LostAssets
题目
要求:调用isComplete()返回true
1 | pragma solidity 0.8.10; |
分析
题目包括三个合约:MockWETH.sol,MocksWETH.sol,LostAssets.sol。
项目的意思是:
- MockWETH和MocksWETH的ERC20代币之间具有流动性
用户可以在MockWETH进行存款与提款,也可以将用户在MockWETH的权益转移到MocksWETH。
但是在MocksWETH无法提款,它的提款操作是将权益转回到MockWETH。用户在MockWETH才能真正的提款发送以太
我们的任务:成功调用ClostAssets.sol的isComplete()并且返回true
1.分析题目的目标
isComplete()要求WETH.balanceOf(address(this)) == 0
,也就是将LostAssets合约在MockWETH的余额设置为0
我们设置msg.value=1ETH,在LostAssets合约的构造器中做了以下操作
require(msg.value >= 1 ether, "At least 1 ether");
:msg.value至少为1ETHWETH = new MockWETH();
和sWETH = new MocksWETH(address(WETH));
:新建了MockWETH和MocksWETHWETH.deposit{value: msg.value}();
:msg.sender是LostAssets,所以LostAssets在MockWETH中有1ETHWETH.approve(address(sWETH), type(uint256).max);
:LostAssets授权给sWETH最大值金额,这就意味着,sWETH可以对LostAssets在MoskWETH的金额做任何操作,那么后面只要我们调用TransferFrom可以操作余额sWETH.deposit(msg.value / 2);
:msg.sender是LostAssets,LostAssets将其在WETH的1ETH划走0.5ETH到sWETH,这就体现了流动性
我们的目的是将LostAssets在WETH剩余的0.5ETH也划走,也就是设置为0
2.分析合约
MockWETH合约
这个合约继承了@openzeppelin的ERC20,写了一个存款函数deposit()
和一个提款函数withdraw(uint256 wad)
,并且发现提款函数并没有重入攻击的可能性,并且版本是0.8.10,也没有溢出漏洞的可能性
MocksWETH合约
此合约将MockWETH传入,并且通过using SafeERC20 for IERC20;
,在每一个存款和提款方法使用underlying地址对MockWETH合约进行操作:验证是否在MockWETH合约真的有这么多钱,才给存款;将在MocksWETH合约的余额重新划入MockWETH,然后进一步在MockWETH合约进行提款获取以太
3.分析解题
3.1错误想法
因为LostAssets approve给MocksWETH了很大的金额,因此MocksWETH可以划走剩余的0.5ETH,但是合约不会主动去调用,我们无法直接通过让MocksWETH主动来转走0.5ETH
但是MocksWETH当中有一个depositWithPermit()
函数,意思是可以进行链下签名授权,然后再链上再进行操作,这个是eip-2612和eip-712相关的内容。eip-2612和eip-712的permit是允许一个EOA账户进行链下签名然后链上操作,但题意是想将LostAssets的0.5ETH进行代理,无论是LostAssets还是被授权给的MocksWETH都是一个合约,不是EOA账户,因此无法进行permit签名!【合约虽然也有私钥,理论上可以进行签名,但是eip协议后来又规定合约签名出来的交易无效,并且想要获取一个合约的私钥是不可能的,只能暴力破解】
又因为题目给的合约并没有实现eip-1271,因此无法使用合约链下签名
题目的考点应该是permit(),但是我认为无法通过permit()进行链下操作而解题的。题目我又找不到相关的方法可以进行操作,因为继承了@openzeppelin的ERC20、ERC20Permit,基本上是没什么问题的,只有这么一个permit()可以进行操作,然而这个可能性也没了
我觉得可能是出错题了,我觉得题目可以有如下两种改法:
- 题目从” 将合约拥有的钱设置为0 “改成“ 将EOA拥有的钱设置为0 “,那么才有可能用到permit函数”
- 题目不是实现ERC20Permit,而是实现eip-1271,那么就可以利用合约进行链下签名了,即LostAssets可以进行签名授权0.5ETH
3.2正确想法
1 | function depositWithPermit( |
解题关键还是depositWithPermit()。我们发现,IERC20Permit(underlying).permit
方法,underlying是contract MockWETH is ERC20("Wrapped ETH", "WETH")
,它并没有permit方法,因此会进入fallback,执行deposit函数,但是对我们转移代币没什么作用,只会_mint
记录一下财产情况
IERC20(underlying).safeTransferFrom(target, address(this), value)
是关键:因为using SafeERC20 for IERC20;
且import了相关合约,这个方法会进入方法体执行执行。先前WETH.approve(address(sWETH), type(uint256).max);
LostAssets已经授权最大值给sWETH了,因此这里sWETH可以进行safeTransferFrom来代理LostAssets的财产。于是我们可以将剩余的一半余额进行转移到其他任意地址,这样余额就为0满足题目要求了
注意:我调用MocksWETH,depositWithPermit方法中又IERC20(underlying).safeTransferFrom(target, address(this), value)
进行调用,在safeTransferFrom中,msg.sender不是我,而是MocksWETH
做题
1.部署题目
2.调用depositWithPermit
3.成功修改
总结
本题涉及ERC20,ERC20Permit,相关的内容是eip-1271,eip-712,eip-2612,ecrecover还原signature,ERC20之间的互操作性。实际上是逻辑上的错误,感觉题目并没有太大的意义,先是授权了最大值,然后depositWithPermit当中又转账