-
Notifications
You must be signed in to change notification settings - Fork 3
/
CollateralTokenAdapter.sol
224 lines (191 loc) · 10.3 KB
/
CollateralTokenAdapter.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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity 0.8.17;
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "../../../interfaces/IBookKeeper.sol";
import "../../../interfaces/ICollateralAdapter.sol";
import "../../../interfaces/ICagable.sol";
import "../../../interfaces/IProxyRegistry.sol";
import "../../../interfaces/IVault.sol";
import "../../../utils/SafeToken.sol";
import "../../../utils/CommonMath.sol";
/// @title CollateralTokenAdapter
/// @dev receives collateral from users and deposit in Vault.
contract CollateralTokenAdapter is CommonMath, ICollateralAdapter, PausableUpgradeable, ReentrancyGuardUpgradeable, ICagable {
using SafeToken for address;
uint256 public live;
bool public flagVault;
address public collateralToken;
IBookKeeper public bookKeeper;
bytes32 public override collateralPoolId;
IVault public vault;
IProxyRegistry public proxyWalletFactory;
/// @dev Total CollateralTokens that has been staked in WAD
uint256 public totalShare;
mapping(address => bool) public whiteListed;
event LogDeposit(uint256 _val);
event LogWithdraw(uint256 _val);
event LogAddToWhitelist(address indexed _user);
event LogRemoveFromWhitelist(address indexed _user);
event LogEmergencyWithdraw(address indexed _caller, address _to);
modifier onlyOwner() {
IAccessControlConfig _accessControlConfig = IAccessControlConfig(bookKeeper.accessControlConfig());
require(_accessControlConfig.hasRole(_accessControlConfig.OWNER_ROLE(), msg.sender), "CollateralTokenAdapter/not-authorized");
_;
}
modifier onlyProxyWalletOrWhiteListed() {
require(IProxyRegistry(proxyWalletFactory).isProxy(msg.sender) || whiteListed[msg.sender], "!ProxyOrWhiteList");
_;
}
modifier onlyOwnerOrGov() {
IAccessControlConfig _accessControlConfig = IAccessControlConfig(bookKeeper.accessControlConfig());
require(
_accessControlConfig.hasRole(_accessControlConfig.OWNER_ROLE(), msg.sender) ||
_accessControlConfig.hasRole(_accessControlConfig.GOV_ROLE(), msg.sender),
"!(ownerRole or govRole)"
);
_;
}
constructor() {
_disableInitializers();
}
function initialize(address _bookKeeper, bytes32 _collateralPoolId, address _collateralToken, address _proxyWalletFactory) external initializer {
// 1. Initialized all dependencies
PausableUpgradeable.__Pausable_init();
ReentrancyGuardUpgradeable.__ReentrancyGuard_init();
require(_bookKeeper != address(0), "CollateralTokenAdapter/zero-book-keeper");
require(_collateralPoolId != bytes32(0), "CollateralTokenAdapter/zero-collateral-pool-id");
require(_collateralToken != address(0), "CollateralTokenAdapter/zero-collateral-token");
require(_proxyWalletFactory != address(0), "CollateralTokenAdapter/zero-proxy-wallet-factory");
live = 1;
collateralPoolId = _collateralPoolId;
collateralToken = _collateralToken;
bookKeeper = IBookKeeper(_bookKeeper);
proxyWalletFactory = IProxyRegistry(_proxyWalletFactory);
}
/// @notice Adds an address to the whitelist, allowing it to interact with the contract
/// @dev Only the contract owner or a governance address can execute this function. The provided address cannot be the zero address.
/// @param _toBeWhitelisted The address to be added to the whitelist
function addToWhitelist(address _toBeWhitelisted) external onlyOwnerOrGov {
require(_toBeWhitelisted != address(0), "CollateralTokenAdapter/whitelist-invalidAdds");
whiteListed[_toBeWhitelisted] = true;
emit LogAddToWhitelist(_toBeWhitelisted);
}
/// @notice Removes an address from the whitelist
/// @dev Only the contract owner or a governance address can execute this function.
/// @param _toBeRemoved The address to be removed from the whitelist
function removeFromWhitelist(address _toBeRemoved) external onlyOwnerOrGov {
require(_toBeRemoved != address(0), "CollateralTokenAdapter/removeFromWL-invalidAdds");
whiteListed[_toBeRemoved] = false;
emit LogRemoveFromWhitelist(_toBeRemoved);
}
/// @dev The `cage` function permanently halts the `collateralTokenAdapter` contract.
/// Please exercise caution when using this function as there is no corresponding `uncage` function.
/// The `cage` function in this contract is unique because it must be called before users can initiate `emergencyWithdraw` in the `collateralTokenAdapter`.
/// It's a must to invoke this function in the `collateralTokenAdapter` during the final phase of an emergency shutdown.
function cage() external override nonReentrant onlyOwner {
if (live == 1) {
live = 0;
emit LogCage();
}
}
/// @dev access: OWNER_ROLE, GOV_ROLE
function pause() external onlyOwnerOrGov {
_pause();
}
/// @dev access: OWNER_ROLE, GOV_ROLE
function unpause() external onlyOwnerOrGov {
_unpause();
}
/// @dev The `setVault` function stores the address of the vault contract that holds the collateral.
/// @param _vault the address of vault smart contract
function setVault(address _vault) external onlyOwner {
require(true != flagVault, "CollateralTokenAdapter/Vault-set-already");
require(_vault != address(0), "CollateralTokenAdapter/zero-vault");
address vaultsAdapter = IVault(_vault).collateralAdapter();
require(vaultsAdapter == address(this), "CollateralTokenAdapter/Adapter-no-match");
IAccessControlConfig _accessControlConfig = IAccessControlConfig(bookKeeper.accessControlConfig());
require(_accessControlConfig.hasRole(_accessControlConfig.ADAPTER_ROLE(), vaultsAdapter), "vaultsAdapter!Adapter");
flagVault = true;
vault = IVault(_vault);
}
/// @param _positionAddress The address that holding states of the position
/// @param _amount The collateral token amount that being used as a collateral and to be staked to AnkrStakingPool
/// @param _data The extra data that may needs to execute the deposit
function deposit(
address _positionAddress,
uint256 _amount,
bytes calldata _data
) external override nonReentrant whenNotPaused onlyProxyWalletOrWhiteListed {
require(_positionAddress != address(0), "CollateralTokenAdapter/deposit-address(0)");
_deposit(_positionAddress, _amount, _data);
}
/// @dev Withdraw collateralToken from Vault
/// @param _usr The address that holding states of the position
/// @param _amount The collateralToken amount in Vault to be returned to proxyWallet and then to user
function withdraw(
address _usr,
uint256 _amount,
bytes calldata /* _data */
) external override nonReentrant whenNotPaused onlyProxyWalletOrWhiteListed {
_withdraw(_usr, _amount);
}
/// @notice Withdraws the collateral from the Vault as the last step for emergency shutdown
/// @dev for excessCollateral withdraw flow of emergency shutdown, please call this fn via proxyWallet
/// @dev for flow that deposits FXD and then withdraw collateral, please call this fn from EOA.
/// @dev EMERGENCY WHEN COLLATERAL TOKEN ADAPTER CAGED ONLY. Withdraw COLLATERAL from VAULT A after redeemStablecoin
function emergencyWithdraw(address _to) external nonReentrant {
require(_to != address(0), "CollateralTokenAdapter/emergency-address(0)");
if (live == 0) {
uint256 _amount = bookKeeper.collateralToken(collateralPoolId, msg.sender);
require(_amount < 2 ** 255, "CollateralTokenAdapter/collateral-overflow");
//deduct totalShare
totalShare -= _amount;
//deduct emergency withdrawal amount of FXD
bookKeeper.addCollateral(collateralPoolId, msg.sender, -int256(_amount));
//withdraw collateralToken from Vault
vault.withdraw(_amount);
//Transfer collateralToken to msg.sender
address(collateralToken).safeTransfer(_to, _amount);
emit LogEmergencyWithdraw(msg.sender, _to);
}
}
/// @dev Lock collateral token in the vault
/// deposit collateral tokens to staking contract, and update BookKeeper
/// @param _positionAddress The position address to be updated
/// @param _amount The amount to be deposited
function _deposit(address _positionAddress, uint256 _amount, bytes calldata /* _data */) private {
require(live == 1, "CollateralTokenAdapter/not-live");
if (_amount > 0) {
// Overflow check for int256(wad) cast below
// Also enforces a non-zero wad
require(int256(_amount) > 0, "CollateralTokenAdapter/amount-overflow");
//transfer collateralToken from proxyWallet to adapter
address(collateralToken).safeTransferFrom(msg.sender, address(this), _amount);
//bookKeeping
bookKeeper.addCollateral(collateralPoolId, _positionAddress, int256(_amount));
totalShare += _amount;
// safeApprove to Vault
address(collateralToken).safeApprove(address(vault), _amount);
//deposit collateralToken to Vault
vault.deposit(_amount);
emit LogDeposit(_amount); // collateralToken
}
}
/// @dev withdraw collateral tokens from staking contract, and update BookKeeper
/// @param _usr The position address to be updated
/// @param _amount The amount to be withdrawn
function _withdraw(address _usr, uint256 _amount) private {
if (_amount > 0) {
require(int256(_amount) > 0, "CollateralTokenAdapter/amount-overflow");
require(bookKeeper.collateralToken(collateralPoolId, msg.sender) >= _amount, "CollateralTokenAdapter/insufficient collateral amount");
bookKeeper.addCollateral(collateralPoolId, msg.sender, -int256(_amount));
totalShare -= _amount;
//withdraw collateralToken from Vault
vault.withdraw(_amount);
//Transfer collateralToken to proxyWallet
collateralToken.safeTransfer(_usr, _amount);
emit LogWithdraw(_amount);
}
}
}