forked from crytic/echidna-spearbit-demo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SomeDeFi.sol
125 lines (104 loc) · 3.84 KB
/
SomeDeFi.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
pragma solidity ^0.8.1;
// This is code from OpenZeppelin to simulate ERC20 tokens
contract ERC20 {
mapping(address => uint256) private _balances;
uint256 private _totalSupply;
event Transfer(address indexed from, address indexed to, uint256 value);
constructor(uint256 minted) {
_mint(msg.sender, minted);
}
function totalSupply() public view virtual returns (uint256) { return _totalSupply; }
function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; }
function transfer(address to, uint256 amount) public virtual returns (bool) {
address owner = msg.sender;
_transfer(owner, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
_transfer(from, to, amount);
return true;
}
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
}
_balances[to] += amount;
emit Transfer(from, to, amount);
}
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_totalSupply += amount;
_balances[account] += amount;
emit Transfer(address(0), account, amount);
}
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
}
_totalSupply -= amount;
emit Transfer(account, address(0), amount);
}
}
contract SomeDefi is ERC20 {
ERC20 public token;
constructor() public ERC20(0) {}
function mintShares(uint256 tokens) public {
token.transferFrom(msg.sender, address(this), tokens);
if (totalSupply() == 0)
_mint(msg.sender, tokens);
else
_mint(msg.sender, 10 ** 18 * tokens / totalSupply());
}
function withdrawShares(uint256 shares) public {
require(sharesOf(msg.sender) >= shares);
uint256 oldTotalSupply = totalSupply();
_burn(msg.sender, shares);
if (oldTotalSupply == shares)
token.transfer(msg.sender, shares);
else
token.transfer(msg.sender, shares * totalSupply() / 10 ** 18);
}
function sharesOf(address user) public returns (uint256) {
return balanceOf(user);
}
}
contract TestSomeDefi is SomeDefi {
constructor() public {
token = new ERC20(type(uint256).max);
token.transfer(address(0x10000), type(uint256).max / 2);
token.transfer(address(0x20000), type(uint256).max / 2);
}
function testMintShares() public {
require(totalSupply() == 0);
uint256 oldBalance = token.balanceOf(msg.sender);
assert(oldBalance >= 1000);
mintShares(1000);
assert(sharesOf(msg.sender) == 1000);
assert(token.balanceOf(msg.sender) == oldBalance - 1000);
}
function testWithdrawShares() public {
require(totalSupply() == 0);
uint256 oldBalance = token.balanceOf(msg.sender);
assert(oldBalance >= 1000);
mintShares(1000);
assert(sharesOf(msg.sender) == 1000);
assert(token.balanceOf(msg.sender) == oldBalance - 1000);
withdrawShares(1000);
assert(token.balanceOf(msg.sender) == oldBalance);
}
}