Skip to content

Commit

Permalink
refactor: compatibility with IonPool upgrade branch
Browse files Browse the repository at this point in the history
  • Loading branch information
junkim012 committed Apr 28, 2024
1 parent bdcaed5 commit bb543d5
Show file tree
Hide file tree
Showing 6 changed files with 551 additions and 45 deletions.
1 change: 0 additions & 1 deletion src/periphery/IonLens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,6 @@ contract IonLens is IIonLens {
return value;
}

// TODO: RENAME VARIABLE HERE and weth()
/**
* @return The supply cap
*/
Expand Down
25 changes: 13 additions & 12 deletions src/vault/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@ import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { AccessControlDefaultAdminRules } from
"@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
import { ReentrancyGuard } from "openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";

import { console2 } from "forge-std/console2.sol";
/**
* @title Ion Lending Vault
* @author Molecular Labs
* @notice Vault contract that can allocate a single lender asset over various
* isolated lending pairs on Ion Protocol. This contract is a fork of the
* Metamorpho contract licnesed under GPL-2.0 with changes to administrative
* logic, underlying data structures, and applying the lending interactions to
* Ion Protocol.
* logic, underlying data structures, and lending interactions to be made
* compatible with Ion Protocol.
*
* @custom:security-contact [email protected]
*/

contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, ReentrancyGuard {
using EnumerableSet for EnumerableSet.AddressSet;
using Math for uint256;
Expand Down Expand Up @@ -59,7 +60,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
bytes32 public constant ALLOCATOR_ROLE = keccak256("ALLOCATOR_ROLE");

IIonPool constant IDLE = IIonPool(address(uint160(uint256(keccak256("IDLE_ASSET_HOLDINGS")))));
IIonPool public constant IDLE = IIonPool(address(uint160(uint256(keccak256("IDLE_ASSET_HOLDINGS")))));

uint8 public immutable DECIMALS_OFFSET;

Expand Down Expand Up @@ -143,7 +144,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
IIonPool pool = marketsToAdd[i];

if (pool != IDLE) {
if (address(pool.underlying()) != address(baseAsset) || (address(pool) == address(0))) {
if (address(pool.underlying()) != address(baseAsset)) {
revert InvalidSupportedMarkets();
}
baseAsset.approve(address(pool), type(uint256).max);
Expand Down Expand Up @@ -396,7 +397,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
// already on this contract' balance.
toSupply = Math.min(availableRoom, assets);
} else {
uint256 supplyCeil = Math.min(caps[pool], ionLens.wethSupplyCap(pool));
uint256 supplyCeil = Math.min(caps[pool], ionLens.supplyCap(pool));

if (supplyCeil == 0) continue;

Expand Down Expand Up @@ -497,7 +498,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
{
uint256 newTotalAssets = _accrueFee();
shares = _convertToSharesWithTotals(assets, totalSupply(), newTotalAssets, Math.Rounding.Ceil);
_updateLastTotalAssets(newTotalAssets - assets);
_updateLastTotalAssets(_zeroFloorSub(newTotalAssets, assets));

_withdraw(_msgSender(), receiver, owner, assets, shares);
}
Expand Down Expand Up @@ -544,7 +545,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy

/**
* @inheritdoc ERC4626
* @dev Returns the maximum amount of assets that the vault can supply on Morpho.
* @dev Returns the maximum amount of assets that the vault can supply on Ion.
*/
function maxDeposit(address) public view override returns (uint256) {
return _maxDeposit();
Expand Down Expand Up @@ -671,7 +672,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
continue;
}

uint256 supplyCeil = Math.min(caps[pool], ionLens.wethSupplyCap(pool));
uint256 supplyCeil = Math.min(caps[pool], ionLens.supplyCap(pool));
uint256 currentSupplied = pool.getUnderlyingClaimOf(address(this));

uint256 suppliable = _zeroFloorSub(supplyCeil, currentSupplied);
Expand All @@ -690,9 +691,9 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
{
uint256 feeShares;
(feeShares, newTotalAssets) = _accruedFeeShares();
newTotalSupply = totalSupply() + feeShares;

assets =
_convertToAssetsWithTotals(balanceOf(owner), totalSupply() + feeShares, newTotalAssets, Math.Rounding.Floor);
assets = _convertToAssetsWithTotals(balanceOf(owner), newTotalSupply, newTotalAssets, Math.Rounding.Floor);

assets -= _simulateWithdrawIon(assets);
}
Expand Down Expand Up @@ -831,7 +832,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
*/
function _withdrawable(IIonPool pool) internal view returns (uint256) {
uint256 currentSupplied = pool.getUnderlyingClaimOf(address(this));
uint256 availableLiquidity = ionLens.weth(pool);
uint256 availableLiquidity = ionLens.liquidity(pool);
return Math.min(currentSupplied, availableLiquidity);
}
}
11 changes: 3 additions & 8 deletions src/vault/VaultFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity 0.8.21;

import { Vault } from "./Vault.sol";
import { IVault } from "./../interfaces/IVault.sol";
import { IERC20 } from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import { IIonLens } from "./../interfaces/IIonLens.sol";

Expand Down Expand Up @@ -50,14 +49,10 @@ contract VaultFactory {
bytes32 salt
)
external
returns (IVault vault)
returns (Vault vault)
{
vault = IVault(
address(
new Vault{ salt: salt }(
ionLens, baseAsset, feeRecipient, feePercentage, name, symbol, initialDelay, initialDefaultAdmin
)
)
vault = new Vault{ salt: salt }(
ionLens, baseAsset, feeRecipient, feePercentage, name, symbol, initialDelay, initialDefaultAdmin
);

emit CreateVault(address(vault), baseAsset, feeRecipient, feePercentage, name, symbol, initialDefaultAdmin);
Expand Down
38 changes: 27 additions & 11 deletions test/fork/concrete/vault/VaultFactory.t.sol
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.21;

import { Vault } from "./../../../../src/vault/Vault.sol";
import { VaultFactory } from "./../../../../src/vault/VaultFactory.sol";
import { IVault } from "./../../../../src/interfaces/IVault.sol";
import { VaultSharedSetup } from "../../../helpers/VaultSharedSetup.sol";
import { IERC20 } from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import { ERC20PresetMinterPauser } from "../../../helpers/ERC20PresetMinterPauser.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";

contract VaultFactoryTest is VaultSharedSetup {
VaultFactory factory;

address internal owner = address(1);
address internal feeRecipient = address(2);
uint256 internal feePercentage = 0.02e27;
IERC20 internal baseAsset = BASE_ASSET;
Expand All @@ -24,47 +24,63 @@ contract VaultFactoryTest is VaultSharedSetup {

function test_CreateVault() public {
bytes32 salt = keccak256("random salt");
IVault vault = factory.createVault(
Vault vault = factory.createVault(
ionLens, baseAsset, feeRecipient, feePercentage, name, symbol, INITIAL_DELAY, VAULT_ADMIN, salt
);

assertEq(VAULT_ADMIN, vault.defaultAdmin(), "owner");
assertEq(VAULT_ADMIN, vault.defaultAdmin(), "default admin");
assertEq(feeRecipient, vault.feeRecipient(), "fee recipient");
assertEq(address(baseAsset), address(vault.baseAsset()), "base asset");
assertEq(address(ionLens), address(vault.ionLens()), "ion lens");
}

function test_CreateVault_Twice() public {
bytes32 salt = keccak256("first random salt");
IVault vault = factory.createVault(
Vault vault = factory.createVault(
ionLens, baseAsset, feeRecipient, feePercentage, name, symbol, INITIAL_DELAY, VAULT_ADMIN, salt
);

bytes32 salt2 = keccak256("second random salt");
IVault vault2 = factory.createVault(
Vault vault2 = factory.createVault(
ionLens, baseAsset, feeRecipient, feePercentage, name, symbol, INITIAL_DELAY, VAULT_ADMIN, salt2
);

assertEq(owner, vault.owner(), "owner");
assertEq(VAULT_ADMIN, vault.defaultAdmin(), "default admin");
assertEq(feeRecipient, vault.feeRecipient(), "fee recipient");
assertEq(address(baseAsset), address(vault.baseAsset()), "base asset");
assertEq(address(ionLens), address(vault.ionLens()), "ion lens");

assertEq(owner, vault2.owner(), "owner");
assertEq(VAULT_ADMIN, vault2.defaultAdmin(), "default admin");
assertEq(feeRecipient, vault2.feeRecipient(), "fee recipient");
assertEq(address(baseAsset), address(vault2.baseAsset()), "base asset");
assertEq(address(ionLens), address(vault2.ionLens()), "ion lens");
}

function test_Revert_CreateVault_SameSaltTwice() public {
bytes32 salt = keccak256("random salt");
IVault vault = factory.createVault(
Vault vault = factory.createVault(
ionLens, baseAsset, feeRecipient, feePercentage, name, symbol, INITIAL_DELAY, VAULT_ADMIN, salt
);

vm.expectRevert();
IVault vault2 = factory.createVault(
Vault vault2 = factory.createVault(
ionLens, baseAsset, feeRecipient, feePercentage, name, symbol, INITIAL_DELAY, VAULT_ADMIN, salt
);
}

function test_CreateVault_SameSaltDifferentBytecode() public {
bytes32 salt = keccak256("random salt");

Vault vault = factory.createVault(
ionLens, BASE_ASSET, feeRecipient, feePercentage, name, symbol, INITIAL_DELAY, VAULT_ADMIN, salt
);

IERC20 diffBaseAsset = IERC20(address(new ERC20PresetMinterPauser("Another Wrapped Staked ETH", "wstETH2")));

Vault vault2 = factory.createVault(
ionLens, diffBaseAsset, feeRecipient, feePercentage, name, symbol, INITIAL_DELAY, VAULT_ADMIN, salt
);

require(address(vault) != address(vault2), "different deployment address");
}
}
16 changes: 10 additions & 6 deletions test/helpers/VaultSharedSetup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract VaultSharedSetup is IonPoolSharedSetup {
GemJoin rsEthGemJoin;
GemJoin rswEthGemJoin;

IIonPool[] pools;
IIonPool[] markets;

uint256[] ZERO_ALLO_CAPS = new uint256[](3);

Expand All @@ -82,7 +82,7 @@ contract VaultSharedSetup is IonPoolSharedSetup {
// inside `addSupportedMarkets`.
vault.grantRole(vault.ALLOCATOR_ROLE(), ALLOCATOR);

IIonPool[] memory markets = new IIonPool[](3);
markets = new IIonPool[](3);
markets[0] = weEthIonPool;
markets[1] = rsEthIonPool;
markets[2] = rswEthIonPool;
Expand All @@ -94,10 +94,10 @@ contract VaultSharedSetup is IonPoolSharedSetup {

BASE_ASSET.approve(address(vault), type(uint256).max);

pools = new IIonPool[](3);
pools[0] = weEthIonPool;
pools[1] = rsEthIonPool;
pools[2] = rswEthIonPool;
// pools = new IIonPool[](3);
// pools[0] = weEthIonPool;
// pools[1] = rsEthIonPool;
// pools[2] = rswEthIonPool;

weEthGemJoin =
new GemJoin(IonPool(address(weEthIonPool)), IERC20(weEthIonPool.getIlkAddress(0)), 0, address(this));
Expand Down Expand Up @@ -282,4 +282,8 @@ contract VaultSharedSetup is IonPoolSharedSetup {
pool.supply(lender, supplyAmt, emptyProof);
vm.stopPrank();
}

function newAddress(bytes memory str) internal returns (address) {
return address(uint160(uint256(keccak256(str))));
}
}
Loading

0 comments on commit bb543d5

Please sign in to comment.