rescue
分析
本题的任务:不小心将10WETH转到了合约masterchef中,我们需要将他归零。
合约中只有swapTokenForPoolToken()
可以调用,它会将一个tokenIn传入,然后对半分换成poolId这个ID对应的池子中的两个代币。其实tokenIn的值只要不为0就可以,因为添加流动性的时候amountDesired设置成了masterchef拥有的最大代币数目,tokenIn的数量不与token0和token1挂钩。
1 | function swapTokenForPoolToken(uint256 poolId, address tokenIn, uint256 amountIn, uint256 minAmountOut) external { |
masterchef添加流动性的时候是将整个合约拥有的代币设置进去,这就意味着,只要我们的token1够多,那么token0就会被归零。(原因是uniswapV2的添加流动性方法中,先是判断token1的数目够不够换token0)
1 | function _addLiquidity(address token0, address token1, uint256 minAmountOut) internal { |
因此,我们选取一个币对池子中token0是WETH的,这样的话,只要我们拥有的token1足够多,就可以将WETH归零。
如果不好理解,那我举个例子:
1 | 由于不知道题目中的池子比例, 我们假设池子里有 WETH 和 USDT 各50个 |
解题
原题目题解如下:
1 | contract Rescue { |
由于比赛已经过了,没有环境给我测试,因此我将写个测试来演绎本题的原理,脚本放在GitHub仓库了
masterchef一开始拥有10WETH,我们需要将它归零,任何人可以调用它的addLiquidity来添加流动性
因此,我们打算用一种叫做COMP的ERC20代币,送给masterchef一定数量的COMP,
然后调用addLiquidity给COMP/WETH池子添加流动性,这样就可以将masterchef的WETH归零
1 | // SPDX-License-Identifier: UNLICENSED |
1 | Logs: |
本例子中无论weth是token0还是token1,结果都是Logs那样。原因如下:1000个COMP可以换的WETH远多于10个,因此不会进入到if分支(此分支是用COMP换WETH),而是进入else分支,else分支则是用WETH换COMP
1 | uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB); |
综上所述,token0和token1的位置不是非要token0是WETH,例子关键是让程序流进入到WETH换COMP的分支即可。回到题目,则是找到一个币对,然后让程序执行流进入到WETH换另外一个token即可,另外一个token需要足够多