Skip to content

Commit 1c82020

Browse files
committed
feat(rollover-vault): finishing rollover vault and refactoring redemptions to be proportional claims from a list
1 parent be58b92 commit 1c82020

15 files changed

Lines changed: 876 additions & 124 deletions

src/FulfillmentVault.sol

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {IWNT} from "./interfaces/IWNT.sol";
1111
import {IFulfillmentVault} from "./interfaces/IFulfillmentVault/IFulfillmentVault.sol";
1212
import {IUSDX} from "@core/interfaces/IUSDX/IUSDX.sol";
1313
import {IOrderPool} from "@core/interfaces/IOrderPool/IOrderPool.sol";
14+
import {IGeneralManager} from "@core/interfaces/IGeneralManager/IGeneralManager.sol";
1415

1516
/**
1617
* @title FulfillmentVault
@@ -21,19 +22,20 @@ contract FulfillmentVault is LiquidityVault, IFulfillmentVault {
2122
using Math for uint256;
2223
using CoreWriterLib for *;
2324

24-
/// @notice Allow the contract to receive ETH (HYPE bridged from Core)
25+
/// @notice Allow the contract to receive network native tokens (HYPE bridged from Core)
2526
receive() external payable {}
2627

2728
/**
2829
* @custom:storage-location erc7201:buttonwood.storage.FulfillmentVault
2930
* @notice The storage for the FulfillmentVault contract
3031
* @param _wrappedNativeToken The address of the wrapped native token
31-
* @param _orderPool The address of the order pool
32+
* @param _generalManager The address of the general manager
3233
* @param _nonce The ongoing nonce that generates distinct cloid values for exchanges on core
3334
*/
3435
struct FulfillmentVaultStorage {
3536
address _wrappedNativeToken;
36-
address _orderPool;
37+
address _generalManager;
38+
address _usdx;
3739
uint128 _nonce;
3840
}
3941

@@ -65,22 +67,25 @@ contract FulfillmentVault is LiquidityVault, IFulfillmentVault {
6567
string memory symbol,
6668
uint8 _decimals,
6769
uint8 _decimalsOffset,
68-
address _usdx,
6970
address _wrappedNativeToken,
70-
address _orderPool
71+
address _generalManager
7172
) internal onlyInitializing {
7273
__ERC20_init_unchained(name, symbol);
73-
__LiquidityVault_init_unchained(_decimals, _decimalsOffset, _usdx, _usdx);
74-
__FulfillmentVault_init_unchained(_wrappedNativeToken, _orderPool);
74+
address[] memory assets = new address[](1);
75+
assets[0] = IGeneralManager(_generalManager).usdx();
76+
__LiquidityVault_init_unchained(_decimals, _decimalsOffset, assets, assets);
77+
__FulfillmentVault_init_unchained(_wrappedNativeToken, _generalManager);
7578
}
7679

7780
/**
7881
* @dev Initializes the FulfillmentVault contract only
7982
*/
8083
// solhint-disable-next-line func-name-mixedcase
81-
function __FulfillmentVault_init_unchained(address _wrappedNativeToken, address _orderPool) internal onlyInitializing {
82-
_getFulfillmentVaultStorage()._wrappedNativeToken = _wrappedNativeToken;
83-
_getFulfillmentVaultStorage()._orderPool = _orderPool;
84+
function __FulfillmentVault_init_unchained(address _wrappedNativeToken, address _generalManager) internal onlyInitializing {
85+
FulfillmentVaultStorage storage $ = _getFulfillmentVaultStorage();
86+
$._wrappedNativeToken = _wrappedNativeToken;
87+
$._generalManager = _generalManager;
88+
$._usdx = IGeneralManager(generalManager()).usdx();
8489
}
8590

8691
/**
@@ -89,20 +94,18 @@ contract FulfillmentVault is LiquidityVault, IFulfillmentVault {
8994
* @param symbol The symbol of the liquidity vault
9095
* @param _decimals The decimals of the liquidity vault
9196
* @param _decimalsOffset The decimals offset for measuring internal precision of shares
92-
* @param _usdx The address of the usdx
9397
* @param _wrappedNativeToken The address of the wrapped native token
94-
* @param _orderPool The address of the order pool
98+
* @param _generalManager The address of the general manager
9599
*/
96100
function initialize(
97101
string memory name,
98102
string memory symbol,
99103
uint8 _decimals,
100104
uint8 _decimalsOffset,
101-
address _usdx,
102105
address _wrappedNativeToken,
103-
address _orderPool
106+
address _generalManager
104107
) external initializer {
105-
__FulfillmentVault_init(name, symbol, _decimals, _decimalsOffset, _usdx, _wrappedNativeToken, _orderPool);
108+
__FulfillmentVault_init(name, symbol, _decimals, _decimalsOffset, _wrappedNativeToken, _generalManager);
106109
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
107110
}
108111

@@ -114,17 +117,27 @@ contract FulfillmentVault is LiquidityVault, IFulfillmentVault {
114117
/// @inheritdoc LiquidityVault
115118
/// @dev Both depositable and redeemable assets are the same asset, so we override totalAssets to return the balance of only the redeemable asset.
116119
function _totalAssets() internal view override returns (uint256) {
117-
return IERC20(redeemableAsset()).balanceOf(address(this));
120+
return IERC20(usdx()).balanceOf(address(this));
118121
}
119122

120123
/// @inheritdoc IFulfillmentVault
121124
function wrappedNativeToken() public view override returns (address) {
122125
return _getFulfillmentVaultStorage()._wrappedNativeToken;
123126
}
124127

128+
/// @inheritdoc IFulfillmentVault
129+
function generalManager() public view override returns (address) {
130+
return _getFulfillmentVaultStorage()._generalManager;
131+
}
132+
125133
/// @inheritdoc IFulfillmentVault
126134
function orderPool() public view override returns (address) {
127-
return _getFulfillmentVaultStorage()._orderPool;
135+
return IGeneralManager(generalManager()).orderPool();
136+
}
137+
138+
/// @inheritdoc IFulfillmentVault
139+
function usdx() public view override returns (address) {
140+
return _getFulfillmentVaultStorage()._usdx;
128141
}
129142

130143
/// @inheritdoc IFulfillmentVault
@@ -152,12 +165,12 @@ contract FulfillmentVault is LiquidityVault, IFulfillmentVault {
152165

153166
/// @inheritdoc IFulfillmentVault
154167
function burnUsdx(uint256 amount) external override onlyRole(KEEPER_ROLE) whenPaused {
155-
IUSDX(redeemableAsset()).burn(amount);
168+
IUSDX(usdx()).burn(amount);
156169
}
157170

158171
// @inheritdoc IFulfillmentVault
159172
function withdrawUsdTokenFromUsdx(address usdToken, uint256 amount) external override onlyRole(KEEPER_ROLE) whenPaused {
160-
IUSDX(redeemableAsset()).withdraw(usdToken, amount);
173+
IUSDX(usdx()).withdraw(usdToken, amount);
161174
}
162175

163176
/// @inheritdoc IFulfillmentVault

src/LiquidityVault.sol

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,19 @@ abstract contract LiquidityVault is
4444
* @notice The storage for the LiquidityVault contract
4545
* @param _decimals The decimals of the vault
4646
* @param _decimalsOffset The decimals offset for measuring internal precision of shares
47-
* @param _depositableAsset The address of the depositable asset
48-
* @param _redeemableAsset The address of the redeemable asset
47+
* @param _depositableAssets The addresses of the depositable assets
48+
* @param _depositableAssetIndex The index of the depositable assets (one-indexed)
49+
* @param _redeemableAssets The addresses of the redeemable assets
50+
* @param _redeemableAssetIndex The index of the redeemable assets (one-indexed)
4951
*/
5052
struct LiquidityVaultStorage {
5153
uint8 _decimals;
5254
uint8 _decimalsOffset;
53-
address _depositableAsset;
54-
address _redeemableAsset;
55+
address[] _depositableAssets;
56+
mapping(address => uint256) _depositableAssetIndex;
57+
address[] _redeemableAssets;
58+
mapping(address => uint256) _redeemableAssetIndex;
59+
5560
bool _whitelistEnforced;
5661
}
5762

@@ -83,11 +88,11 @@ abstract contract LiquidityVault is
8388
string memory symbol,
8489
uint8 _decimals,
8590
uint8 _decimalsOffset,
86-
address _depositableAsset,
87-
address _redeemableAsset
91+
address[] memory _depositableAssets,
92+
address[] memory _redeemableAssets
8893
) internal onlyInitializing {
8994
__ERC20_init_unchained(name, symbol);
90-
__LiquidityVault_init_unchained(_decimals, _decimalsOffset, _depositableAsset, _redeemableAsset);
95+
__LiquidityVault_init_unchained(_decimals, _decimalsOffset, _depositableAssets, _redeemableAssets);
9196
}
9297

9398
/**
@@ -97,14 +102,20 @@ abstract contract LiquidityVault is
97102
function __LiquidityVault_init_unchained(
98103
uint8 _decimals,
99104
uint8 _decimalsOffset,
100-
address _depositableAsset,
101-
address _redeemableAsset
105+
address[] memory _depositableAssets,
106+
address[] memory _redeemableAssets
102107
) internal onlyInitializing {
103108
LiquidityVaultStorage storage $ = _getLiquidityVaultStorage();
104109
$._decimals = _decimals;
105110
$._decimalsOffset = _decimalsOffset;
106-
$._depositableAsset = _depositableAsset;
107-
$._redeemableAsset = _redeemableAsset;
111+
$._depositableAssets = _depositableAssets;
112+
for (uint256 i = 0; i < _depositableAssets.length; i++) {
113+
$._depositableAssetIndex[_depositableAssets[i]] = i + 1;
114+
}
115+
$._redeemableAssets = _redeemableAssets;
116+
for (uint256 i = 0; i < _redeemableAssets.length; i++) {
117+
$._redeemableAssetIndex[_redeemableAssets[i]] = i + 1;
118+
}
108119
$._whitelistEnforced = false;
109120
}
110121

@@ -114,18 +125,18 @@ abstract contract LiquidityVault is
114125
* @param symbol The symbol of the liquidity vault
115126
* @param _decimals The decimals of the liquidity vault
116127
* @param _decimalsOffset The decimals offset for measuring internal precision of shares
117-
* @param _depositableAsset The address of the depositable asset
118-
* @param _redeemableAsset The address of the redeemable asset
128+
* @param _depositableAssets The addresses of the depositable assets
129+
* @param _redeemableAssets The addresses of the redeemable assets
119130
*/
120131
function initialize(
121132
string memory name,
122133
string memory symbol,
123134
uint8 _decimals,
124135
uint8 _decimalsOffset,
125-
address _depositableAsset,
126-
address _redeemableAsset
136+
address[] memory _depositableAssets,
137+
address[] memory _redeemableAssets
127138
) external virtual initializer {
128-
__LiquidityVault_init(name, symbol, _decimals, _decimalsOffset, _depositableAsset, _redeemableAsset);
139+
__LiquidityVault_init(name, symbol, _decimals, _decimalsOffset, _depositableAssets, _redeemableAssets);
129140
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
130141
}
131142

@@ -184,13 +195,13 @@ abstract contract LiquidityVault is
184195
}
185196

186197
/// @inheritdoc ILiquidityVault
187-
function depositableAsset() public view virtual override returns (address) {
188-
return _getLiquidityVaultStorage()._depositableAsset;
198+
function depositableAssets() public view virtual override returns (address[] memory) {
199+
return _getLiquidityVaultStorage()._depositableAssets;
189200
}
190201

191202
/// @inheritdoc ILiquidityVault
192-
function redeemableAsset() public view virtual override returns (address) {
193-
return _getLiquidityVaultStorage()._redeemableAsset;
203+
function redeemableAssets() public view virtual override returns (address[] memory) {
204+
return _getLiquidityVaultStorage()._redeemableAssets;
194205
}
195206

196207
function _totalAssets() internal view virtual returns (uint256);
@@ -206,8 +217,12 @@ abstract contract LiquidityVault is
206217
}
207218

208219
/// @dev Internal conversion function (from shares to assets) with support for rounding direction.
209-
function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256) {
210-
return shares.mulDiv(totalAssets() + 1, totalSupply() + 10 ** decimalsOffset(), rounding);
220+
function _convertToAssets(uint256 shares, Math.Rounding rounding) internal view virtual returns (uint256[] memory) {
221+
uint256[] memory assets = new uint256[](redeemableAssets().length);
222+
for (uint256 i = 0; i < redeemableAssets().length; i++) {
223+
assets[i] = shares.mulDiv(IERC20(redeemableAssets()[i]).balanceOf(address(this)) + 1, totalSupply() + 10 ** decimalsOffset(), rounding);
224+
}
225+
return assets;
211226
}
212227

213228
/// @inheritdoc ILiquidityVault
@@ -220,28 +235,39 @@ abstract contract LiquidityVault is
220235
}
221236

222237
/// @inheritdoc ILiquidityVault
223-
function deposit(uint256 assets) external virtual override whenNotPaused checkWhitelistEnforced {
238+
function deposit(address depositableAsset, uint256 assets) external virtual override whenNotPaused checkWhitelistEnforced {
239+
// Validate the depositable asset is in the depositable assets list
240+
if (_getLiquidityVaultStorage()._depositableAssetIndex[depositableAsset] == 0) {
241+
revert AssetNotDepositable(depositableAsset);
242+
}
224243
// Calculate the corresponding amount of shares for the deposited amount
225244
uint256 shares = _convertToShares(assets, Math.Rounding.Floor);
226245
// Mint the corresponding amount of shares to the sender
227246
_mint(msg.sender, shares);
228247
// Emit the deposited event
229-
emit Deposited(msg.sender, depositableAsset(), assets, shares);
248+
emit Deposited(msg.sender, depositableAsset, assets, shares);
230249
// Transfer the depositable asset from the sender to the vault
231-
IERC20(depositableAsset()).safeTransferFrom(msg.sender, address(this), assets);
250+
IERC20(depositableAsset).safeTransferFrom(msg.sender, address(this), assets);
232251
}
233252

234253
/// @inheritdoc ILiquidityVault
235254
/// @dev No whitelist enforcement for redeeming. This prevents funds from being locked in the vault.
236255
function redeem(uint256 shares) external virtual override whenNotPaused {
237256
// Calculate the corresponding amount of assets for the redeemed shares
238-
uint256 assets = _convertToAssets(shares, Math.Rounding.Floor);
257+
uint256[] memory assetAmounts = _convertToAssets(shares, Math.Rounding.Floor);
239258
// Burn the corresponding amount of shares
240259
_burn(msg.sender, shares);
260+
261+
// Cache the redeemable assets array
262+
address[] memory rAssets = redeemableAssets();
263+
241264
// Emit the redeemed event
242-
emit Redeemed(msg.sender, redeemableAsset(), assets, shares);
243-
// Transfer the redeemable asset from the vault to the sender
244-
IERC20(redeemableAsset()).safeTransfer(msg.sender, assets);
265+
emit Redeemed(msg.sender, rAssets, assetAmounts, shares);
266+
267+
// Transfer the corresponding amount of redeemable assets to the sender
268+
for (uint256 i = 0; i < rAssets.length; i++) {
269+
IERC20(rAssets[i]).safeTransfer(msg.sender, assetAmounts[i]);
270+
}
245271
}
246272
}
247273

0 commit comments

Comments
 (0)