-
Notifications
You must be signed in to change notification settings - Fork 29
/
AffiliateToken.sol
181 lines (154 loc) · 5.53 KB
/
AffiliateToken.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {VaultAPI, BaseWrapper} from "./BaseWrapper.sol";
contract AffiliateToken is ERC20, BaseWrapper {
uint8 public immutable DECIMALS;
/// @notice The EIP-712 typehash for the contract's domain
bytes32 public constant DOMAIN_TYPEHASH =
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
bytes32 public immutable DOMAIN_SEPARATOR;
/// @notice The EIP-712 typehash for the permit struct used by the contract
bytes32 public constant PERMIT_TYPEHASH =
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
);
/// @notice A record of states for signing / validating signatures
mapping(address => uint256) public nonces;
address public affiliate;
address public pendingAffiliate;
modifier onlyAffiliate() {
require(msg.sender == affiliate);
_;
}
constructor(
address _token,
address _registry,
string memory name,
string memory symbol
) BaseWrapper(_token, _registry) ERC20(name, symbol) {
DOMAIN_SEPARATOR = keccak256(
abi.encode(
DOMAIN_TYPEHASH,
keccak256(bytes(name)),
keccak256(bytes("1")),
_getChainId(),
address(this)
)
);
affiliate = msg.sender;
DECIMALS = uint8(ERC20(address(token)).decimals());
}
function _getChainId() internal view returns (uint256) {
uint256 chainId;
assembly {
chainId := chainid()
}
return chainId;
}
function setAffiliate(address _affiliate) external onlyAffiliate {
pendingAffiliate = _affiliate;
}
function acceptAffiliate() external {
require(msg.sender == pendingAffiliate);
affiliate = msg.sender;
}
function _shareValue(uint256 numShares) internal view returns (uint256) {
uint256 totalShares = totalSupply();
if (totalShares > 0) {
return (totalVaultBalance(address(this)) * numShares) / totalShares;
} else {
return numShares;
}
}
function pricePerShare() external view returns (uint256) {
return
(totalVaultBalance(address(this)) * 10**uint256(decimals())) /
totalSupply();
}
function _sharesForValue(uint256 amount) internal view returns (uint256) {
// total wrapper assets before deposit (assumes deposit already occured)
uint256 totalBalance = totalVaultBalance(address(this));
if (totalBalance > amount) {
return (totalSupply() * amount) / (totalBalance - amount);
} else {
return amount;
}
}
function deposit() external returns (uint256) {
return deposit(type(uint256).max); // Deposit everything
}
function deposit(uint256 amount) public returns (uint256 deposited) {
deposited = _deposit(msg.sender, address(this), amount, true); // `true` = pull from `msg.sender`
uint256 shares = _sharesForValue(deposited); // NOTE: Must be calculated after deposit is handled
_mint(msg.sender, shares);
}
function withdraw() external returns (uint256) {
return withdraw(balanceOf(msg.sender));
}
function withdraw(uint256 shares) public returns (uint256 withdrawn) {
withdrawn = _withdraw(
address(this),
msg.sender,
_shareValue(shares),
true
); // `true` = withdraw from `bestVault`
_burn(msg.sender, shares);
}
function migrate() external onlyAffiliate returns (uint256) {
return _migrate(address(this));
}
function migrate(uint256 amount) external onlyAffiliate returns (uint256) {
return _migrate(address(this), amount);
}
function migrate(uint256 amount, uint256 maxMigrationLoss)
external
onlyAffiliate
returns (uint256)
{
return _migrate(address(this), amount, maxMigrationLoss);
}
/**
* @notice Triggers an approval from owner to spends
* @param owner The address to approve from
* @param spender The address to be approved
* @param amount The number of tokens that are approved (2^256-1 means infinite)
* @param deadline The time at which to expire the signature
* @param v The recovery byte of the signature
* @param r Half of the ECDSA signature pair
* @param s Half of the ECDSA signature pair
*/
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external {
require(owner != address(0), "permit: signature");
require(block.timestamp <= deadline, "permit: expired");
bytes32 structHash = keccak256(
abi.encode(
PERMIT_TYPEHASH,
owner,
spender,
amount,
nonces[owner]++,
deadline
)
);
bytes32 digest = keccak256(
abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash)
);
address signatory = ecrecover(digest, v, r, s);
require(signatory == owner, "permit: unauthorized");
_approve(owner, spender, amount);
}
function decimals() public view virtual override returns (uint8) {
return DECIMALS;
}
}