From 19b0d2177cc31f02ad7ddd81e78b88427a288ca3 Mon Sep 17 00:00:00 2001 From: Jeroen Offerijns Date: Fri, 25 Aug 2023 11:22:45 +0200 Subject: [PATCH] Single tranche infrastructure (#81) * fix deploy script * Remove old factory, update deploy script * Fix restricted token tests * Rename * Update factory test * Format * Work on token manager, support multiple routers, add permissionless router for tests, remove chainbridge router * Fix gateway build * Re organize scripts and remove unused ones * Set up permissionless deployment script * Fix deploy build * Add broadcast * Change deployer to base contract * Rewrite deploy script * Implement token manager * Fix wiring in tests * Format * Fix function signatures * Re-add missing modifiers * Remove unused code * Use auth contract in gateway * Use auth in router * Use auth in all routers * Use auth everywhere * Consistent internal function naming * Re-add token manager tests * Re-add handleTransferTrancheTokens * Add token manager as ward on liquidity pools * Update transfer tranche token message * Fix transferTrancheTokensToEVM * Fix message test * Actually fix message test * Fix message indices * init single tranche infrastructure * fixed approvals and tests * adjust auth in transfers * Fix token permissions * Format * Address review comments * Remove unnecessary deny * Add missing events --------- Co-authored-by: ilinzweilin --- script/Deployer.sol | 6 +- src/InvestmentManager.sol | 120 ++++++++++++++++------------ src/TokenManager.sol | 39 ++++----- src/liquidityPool/Factory.sol | 56 ++++++++++--- src/liquidityPool/LiquidityPool.sol | 111 ++++++++++++++++++++----- src/token/ERC20.sol | 41 +++++++++- src/token/Restricted.sol | 9 +++ test/InvestmentManager.t.sol | 78 +++++++----------- test/LiquidityPool.t.sol | 17 ++-- test/TokenManager.t.sol | 15 ++-- test/liquidityPool/factory.t.sol | 43 +++------- 11 files changed, 333 insertions(+), 202 deletions(-) diff --git a/script/Deployer.sol b/script/Deployer.sol index c22aa085..d57753ae 100644 --- a/script/Deployer.sol +++ b/script/Deployer.sol @@ -8,7 +8,7 @@ import {TokenManager} from "src/TokenManager.sol"; import {Escrow} from "src/Escrow.sol"; import {PauseAdmin} from "src/admin/PauseAdmin.sol"; import {DelayedAdmin} from "src/admin/DelayedAdmin.sol"; -import {LiquidityPoolFactory, MemberlistFactory} from "src/liquidityPool/Factory.sol"; +import {LiquidityPoolFactory, MemberlistFactory, TrancheTokenFactory} from "src/liquidityPool/Factory.sol"; import "forge-std/Script.sol"; interface RouterLike { @@ -33,9 +33,11 @@ contract Deployer is Script { function deployInvestmentManager() public { address liquidityPoolFactory = address(new LiquidityPoolFactory()); + address trancheTokenFactory = address(new TrancheTokenFactory()); address memberlistFactory_ = address(new MemberlistFactory()); escrow = new Escrow(); - investmentManager = new InvestmentManager(address(escrow), liquidityPoolFactory, memberlistFactory_); + investmentManager = + new InvestmentManager(address(escrow), liquidityPoolFactory, trancheTokenFactory, memberlistFactory_); } function wire(address router) public { diff --git a/src/InvestmentManager.sol b/src/InvestmentManager.sol index ce879be2..a2072e73 100644 --- a/src/InvestmentManager.sol +++ b/src/InvestmentManager.sol @@ -2,8 +2,7 @@ pragma solidity ^0.8.18; pragma abicoder v2; -import {LiquidityPoolFactoryLike, MemberlistFactoryLike} from "./liquidityPool/Factory.sol"; -import {ERC20Like} from "./token/Restricted.sol"; +import {TrancheTokenFactoryLike, LiquidityPoolFactoryLike, MemberlistFactoryLike} from "./liquidityPool/Factory.sol"; import {MemberlistLike} from "./token/Memberlist.sol"; import "./auth/auth.sol"; @@ -23,7 +22,6 @@ interface GatewayLike { interface LiquidityPoolLike { function rely(address) external; // restricted token functions - function memberlist() external returns (address); function hasMember(address) external returns (bool); function file(bytes32 what, address data) external; // erc20 functions @@ -32,7 +30,6 @@ interface LiquidityPoolLike { function balanceOf(address) external returns (uint256); function transferFrom(address, address, uint256) external returns (bool); // 4626 functions - function updateTokenPrice(uint128 _tokenPrice) external; function asset() external returns (address); // centrifuge chain info functions function poolId() external returns (uint64); @@ -44,10 +41,24 @@ interface TokenManagerLike { function currencyAddressToId(address addr) external view returns (uint128); } +interface TrancheTokenLike { + function updateTokenPrice(uint128 _tokenPrice) external; + function memberlist() external returns (address); +} + +interface ERC20Like { + function transferFrom(address from, address to, uint256 amount) external returns (bool); + function balanceOf(address) external returns (uint256); +} + interface EscrowLike { function approve(address token, address spender, uint256 value) external; } +interface AuthLike { + function rely(address usr) external; +} + /// @dev centrifuge chain pool struct Pool { uint64 poolId; @@ -57,6 +68,7 @@ struct Pool { /// @dev centrifuge chain tranche struct Tranche { + address token; uint64 poolId; bytes16 trancheId; // important: the decimals of the leading pool currency. Liquidity Pool shares have to be denomatimated with the same precision. @@ -64,7 +76,6 @@ struct Tranche { uint256 createdAt; string tokenName; string tokenSymbol; - address[] liquidityPools; } /// @dev liquidity pool orders and deposit/redemption limits per user @@ -88,6 +99,7 @@ contract InvestmentManager is Auth { // factories for liquidity pool deployments LiquidityPoolFactoryLike public immutable liquidityPoolFactory; + TrancheTokenFactoryLike public immutable trancheTokenFactory; MemberlistFactoryLike public immutable memberlistFactory; uint256 constant MAX_UINT256 = type(uint256).max; @@ -102,26 +114,23 @@ contract InvestmentManager is Auth { event DepositProcessed(address indexed liquidityPool, address indexed user, uint128 indexed currencyAmount); event RedemptionProcessed(address indexed liquidityPool, address indexed user, uint128 indexed trancheTokenAmount); event LiquidityPoolDeployed(uint64 indexed poolId, bytes16 indexed trancheId, address indexed liquidityPoool); - - constructor(address escrow_, address liquidityPoolFactory_, address memberlistFactory_) { + event TrancheTokenDeployed(uint64 indexed poolId, bytes16 indexed trancheId); + + constructor( + address escrow_, + address liquidityPoolFactory_, + address trancheTokenFactory_, + address memberlistFactory_ + ) { escrow = EscrowLike(escrow_); liquidityPoolFactory = LiquidityPoolFactoryLike(liquidityPoolFactory_); + trancheTokenFactory = TrancheTokenFactoryLike(trancheTokenFactory_); memberlistFactory = MemberlistFactoryLike(memberlistFactory_); wards[msg.sender] = 1; emit Rely(msg.sender); } - // --- Getters --- - /// @dev returns all existing liquidity pools for a centrifuge tranche - function getLiquidityPoolsForTranche(uint64 _poolId, bytes16 _trancheId) - public - view - returns (address[] memory lPools) - { - lPools = tranches[_poolId][_trancheId].liquidityPools; - } - /// @dev gateway must be message.sender. permissions check for incoming message handling. modifier onlyGateway() { require(msg.sender == address(gateway), "InvestmentManager/not-the-gateway"); @@ -198,14 +207,14 @@ contract InvestmentManager is Auth { address _liquidityPool = msg.sender; LPValues storage lpValues = orderbook[_user][_liquidityPool]; LiquidityPoolLike lPool = LiquidityPoolLike(_liquidityPool); - ERC20Like currency = ERC20Like(lPool.asset()); + address currency = lPool.asset(); uint128 currencyAmount = _toUint128(_currencyAmount); // check if liquidity pool currency is supported by the centrifuge pool - require(_poolCurrencyCheck(lPool.poolId(), lPool.asset()), "InvestmentManager/currency-not-supported"); + require(_poolCurrencyCheck(lPool.poolId(), currency), "InvestmentManager/currency-not-supported"); // check if user is allowed to hold the restriced liquidity pool tokens require( - _liquidityPoolTokensCheck(lPool.poolId(), lPool.trancheId(), lPool.asset(), _user), + _liquidityPoolTokensCheck(lPool.poolId(), lPool.trancheId(), currency, _user), "InvestmentManager/tranche-tokens-not-supported" ); @@ -227,9 +236,9 @@ contract InvestmentManager is Auth { lpValues.maxRedeem = 0; // transfer the differene between required and locked currency from user to escrow - require(currency.balanceOf(_user) >= transferAmount, "InvestmentManager/insufficient-balance"); + require(ERC20Like(currency).balanceOf(_user) >= transferAmount, "InvestmentManager/insufficient-balance"); require( - currency.transferFrom(_user, address(escrow), transferAmount), + ERC20Like(currency).transferFrom(_user, address(escrow), transferAmount), "InvestmentManager/currency-transfer-failed" ); } @@ -305,24 +314,15 @@ contract InvestmentManager is Auth { function updateTokenPrice(uint64 _poolId, bytes16 _trancheId, uint128 _price) public onlyGateway { Tranche storage tranche = tranches[_poolId][_trancheId]; - require(tranche.createdAt > 0, "InvestmentManager/invalid-pool-or-tranche"); - for (uint256 i = 0; i < tranche.liquidityPools.length; i++) { - address lPool = tranche.liquidityPools[i]; - require(lPool != address(0), "InvestmentManager/invalid-liquidity-pool"); - LiquidityPoolLike(lPool).updateTokenPrice(_price); - } + require(tranche.token != address(0), "InvestmentManager/tranche-not-deployed"); + TrancheTokenLike(tranche.token).updateTokenPrice(_price); } function updateMember(uint64 _poolId, bytes16 _trancheId, address _user, uint64 _validUntil) public onlyGateway { Tranche storage tranche = tranches[_poolId][_trancheId]; - require(tranche.createdAt > 0, "InvestmentManager/invalid-pool-or-tranche"); - for (uint256 i = 0; i < tranche.liquidityPools.length; i++) { - address lPool_ = tranche.liquidityPools[i]; - require(lPool_ != address(0), "InvestmentManager/invalid-liquidity-pool"); - LiquidityPoolLike lPool = LiquidityPoolLike(lPool_); - MemberlistLike memberlist = MemberlistLike(lPool.memberlist()); - memberlist.updateMember(_user, _validUntil); - } + require(tranche.token != address(0), "InvestmentManager/tranche-not-deployed"); + MemberlistLike memberlist = MemberlistLike(TrancheTokenLike(tranche.token).memberlist()); + memberlist.updateMember(_user, _validUntil); } function handleExecutedCollectInvest( @@ -437,7 +437,6 @@ contract InvestmentManager is Auth { (currencyAmount <= orderbook[_user][_liquidityPool].maxDeposit && currencyAmount > 0), "InvestmentManager/amount-exceeds-deposit-limits" ); - (trancheTokenAmount,) = _deposit(0, currencyAmount, _liquidityPool, _user); } @@ -555,38 +554,55 @@ contract InvestmentManager is Auth { require(liquidityPool == address(0), "InvestmentManager/liquidityPool-already-deployed"); require(pools[_poolId].createdAt > 0, "InvestmentManager/pool-does-not-exist"); Tranche storage tranche = tranches[_poolId][_trancheId]; - require(tranche.createdAt != 0, "InvestmentManager/tranche-does-not-exist"); // tranche must have been added + require(tranche.token != address(0), "InvestmentManager/tranche-does-not-exist"); // tranche must have been added require(_poolCurrencyCheck(_poolId, _currency), "InvestmentManager/currency-not-supported"); // currency must be supported by pool uint128 currencyId = tokenManager.currencyAddressToId(_currency); + liquidityPool = liquidityPoolFactory.newLiquidityPool( + _poolId, _trancheId, currencyId, _currency, tranche.token, address(this), address(gateway) + ); + + EscrowLike(escrow).approve(tranche.token, liquidityPool, MAX_UINT256); + liquidityPools[_poolId][_trancheId][_currency] = liquidityPool; + wards[liquidityPool] = 1; + // enable connectors to take the liquidity pool tokens out of escrow in case if investments + AuthLike(tranche.token).rely(liquidityPool); // add liquidityPool as ward on tranche Token + + emit LiquidityPoolDeployed(_poolId, _trancheId, liquidityPool); + return liquidityPool; + } + + function deployTranche(uint64 _poolId, bytes16 _trancheId) public returns (address) { + Tranche storage tranche = tranches[_poolId][_trancheId]; + require(tranche.token == address(0), "InvestmentManager/tranche-already-deployed"); + require(tranche.createdAt > 0, "InvestmentManager/tranche-not-added"); // deploy liquidity pool set gateway as admin on liquidityPool & memberlist address memberlist = memberlistFactory.newMemberlist(address(gateway), address(this)); MemberlistLike(memberlist).updateMember(address(escrow), type(uint256).max); // add escrow to tranche tokens memberlist - liquidityPool = liquidityPoolFactory.newLiquidityPool( + + address token = trancheTokenFactory.newTrancheToken( _poolId, _trancheId, - currencyId, - _currency, address(this), - address(gateway), memberlist, tranche.tokenName, tranche.tokenSymbol, - tranche.decimals + tranche.decimals, + address(gateway) ); - LiquidityPoolLike(liquidityPool).rely(address(tokenManager)); // to be able to mint for incoming transfers + AuthLike(token).rely(address(tokenManager)); // to be able to mint for incoming transfers - liquidityPools[_poolId][_trancheId][_currency] = liquidityPool; - wards[liquidityPool] = 1; - tranche.liquidityPools.push(liquidityPool); - // enable connectors to take the liquidity pool tokens out of escrow in case if investments - EscrowLike(escrow).approve(liquidityPool, address(this), MAX_UINT256); - - emit LiquidityPoolDeployed(_poolId, _trancheId, liquidityPool); - return liquidityPool; + tranche.token = token; + emit TrancheTokenDeployed(_poolId, _trancheId); + return token; } // ------ helper functions + function getTrancheToken(uint64 poolId, bytes16 trancheId) public view returns (address) { + Tranche storage tranche = tranches[poolId][trancheId]; + return tranche.token; + } + // TODO: check rounding function calcDepositPrice(address _user, address _liquidityPool) public diff --git a/src/TokenManager.sol b/src/TokenManager.sol index 1180698b..ca3c8f05 100644 --- a/src/TokenManager.sol +++ b/src/TokenManager.sol @@ -26,17 +26,14 @@ interface GatewayLike { interface InvestmentManagerLike { function liquidityPools(uint64 poolId, bytes16 trancheId, address currency) external returns (address); - function getLiquidityPoolsForTranche(uint64 _poolId, bytes16 _trancheId) - external - view - returns (address[] memory lPools); + function getTrancheToken(uint64 _poolId, bytes16 _trancheId) external view returns (address); } interface EscrowLike { function approve(address token, address spender, uint256 value) external; } -interface LiquidityPoolLike is ERC20Like { +interface RestrictedTokenLike is ERC20Like { function hasMember(address user) external view returns (bool); } @@ -88,16 +85,14 @@ contract TokenManager is Auth { function transferTrancheTokensToCentrifuge( uint64 poolId, bytes16 trancheId, - address currency, // we need this as there is liquidityPool per supported currency bytes32 destinationAddress, uint128 amount ) public { - LiquidityPoolLike liquidityPool = - LiquidityPoolLike(investmentManager.liquidityPools(poolId, trancheId, currency)); - require(address(liquidityPool) != address(0), "TokenManager/unknown-token"); + RestrictedTokenLike trancheToken = RestrictedTokenLike(investmentManager.getTrancheToken(poolId, trancheId)); + require(address(trancheToken) != address(0), "TokenManager/unknown-token"); - require(liquidityPool.balanceOf(msg.sender) >= amount, "TokenManager/insufficient-balance"); - liquidityPool.burn(msg.sender, amount); + require(trancheToken.balanceOf(msg.sender) >= amount, "TokenManager/insufficient-balance"); + trancheToken.burn(msg.sender, amount); gateway.transferTrancheTokensToCentrifuge(poolId, trancheId, msg.sender, destinationAddress, amount); } @@ -109,13 +104,11 @@ contract TokenManager is Auth { address destinationAddress, uint128 amount ) public { - // TODO: this should get the underlying tranche token once the single tranche token change has been merged - LiquidityPoolLike liquidityPool = - LiquidityPoolLike(investmentManager.getLiquidityPoolsForTranche(poolId, trancheId)[0]); - require(address(liquidityPool) != address(0), "TokenManager/unknown-token"); + RestrictedTokenLike trancheToken = RestrictedTokenLike(investmentManager.getTrancheToken(poolId, trancheId)); + require(address(trancheToken) != address(0), "TokenManager/unknown-token"); - require(liquidityPool.balanceOf(msg.sender) >= amount, "TokenManager/insufficient-balance"); - liquidityPool.burn(msg.sender, amount); + require(trancheToken.balanceOf(msg.sender) >= amount, "TokenManager/insufficient-balance"); + trancheToken.burn(msg.sender, amount); gateway.transferTrancheTokensToEVM( poolId, trancheId, msg.sender, destinationChainId, destinationAddress, amount @@ -135,7 +128,7 @@ contract TokenManager is Auth { currencyIdToAddress[currency] = currencyAddress; currencyAddressToId[currencyAddress] = currency; - // enable connectors to take the currency out of escrow in case of redemptions + // enable taking the currency out of escrow in case of redemptions EscrowLike(escrow).approve(currencyAddress, address(investmentManager), type(uint256).max); EscrowLike(escrow).approve(currencyAddress, address(this), type(uint256).max); emit CurrencyAdded(currency, currencyAddress); @@ -156,12 +149,10 @@ contract TokenManager is Auth { public onlyGateway { - // TODO: this should get the underlying tranche token once the single tranche token change has been merged - LiquidityPoolLike liquidityPool = - LiquidityPoolLike(investmentManager.getLiquidityPoolsForTranche(poolId, trancheId)[0]); - require(address(liquidityPool) != address(0), "TokenManager/unknown-token"); + RestrictedTokenLike trancheToken = RestrictedTokenLike(investmentManager.getTrancheToken(poolId, trancheId)); + require(address(trancheToken) != address(0), "TokenManager/unknown-token"); - require(liquidityPool.hasMember(destinationAddress), "TokenManager/not-a-member"); - liquidityPool.mint(destinationAddress, amount); + require(trancheToken.hasMember(destinationAddress), "TokenManager/not-a-member"); + trancheToken.mint(destinationAddress, amount); } } diff --git a/src/liquidityPool/Factory.sol b/src/liquidityPool/Factory.sol index 1375ee25..d9d993a1 100644 --- a/src/liquidityPool/Factory.sol +++ b/src/liquidityPool/Factory.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.18; import {LiquidityPool} from "./LiquidityPool.sol"; +import {RestrictedToken} from "../token/Restricted.sol"; import {Memberlist} from "../token/Memberlist.sol"; interface LiquidityPoolFactoryLike { @@ -10,12 +11,22 @@ interface LiquidityPoolFactoryLike { bytes16 _trancheId, uint128 _currencyId, address _asset, + address _tranche, + address _investmentManager, + address _admin + ) external returns (address); +} + +interface TrancheTokenFactoryLike { + function newTrancheToken( + uint64 _poolId, + bytes16 _trancheId, address _investmentManager, - address _admin, address _memberlist, string memory _name, string memory _symbol, - uint8 _decimals + uint8 _decimals, + address _admin ) external returns (address); } @@ -25,27 +36,22 @@ contract LiquidityPoolFactory { bytes16 _trancheId, uint128 _currencyId, address _asset, + address _tranche, address _investmentManager, - address _admin, - address _memberlist, - string memory _name, - string memory _symbol, - uint8 _decimals + address _admin ) public returns (address) { // Salt is hash(poolId + trancheId + asset), to deploy copies of the liquidity pool contract // on multiple chains with the same address for the same tranche and asset bytes32 salt = keccak256(abi.encodePacked(_poolId, _trancheId, _currencyId)); - LiquidityPool lPool = new LiquidityPool{salt: salt}(_decimals); + LiquidityPool lPool = new LiquidityPool{salt: salt}(); // Name and symbol are not passed on constructor, such that if the same liquidity pool is deployed // on another chain with a different name (it might have changed in between deployments), // then the address remains deterministic. - lPool.file("name", _name); - lPool.file("symbol", _symbol); lPool.file("investmentManager", _investmentManager); lPool.file("asset", _asset); - lPool.file("memberlist", _memberlist); + lPool.file("share", _tranche); lPool.setPoolDetails(_poolId, _trancheId); lPool.deny(msg.sender); @@ -56,6 +62,34 @@ contract LiquidityPoolFactory { } } +contract TrancheTokenFactory { + function newTrancheToken( + uint64 _poolId, + bytes16 _trancheId, + address _investmentManager, + address _memberlist, + string memory _name, + string memory _symbol, + uint8 _decimals, + address _admin + ) public returns (address) { + // Salt is hash(poolId + trancheId ) + // same tracnhe token address on every evm chain + bytes32 salt = keccak256(abi.encodePacked(_poolId, _trancheId)); + + RestrictedToken token = new RestrictedToken{salt: salt}(_decimals); + + token.file("name", _name); + token.file("symbol", _symbol); + token.file("memberlist", _memberlist); + + token.rely(_admin); + token.rely(_investmentManager); // to be able to update tokenPrices + token.deny(address(this)); + return address(token); + } +} + interface MemberlistFactoryLike { function newMemberlist(address _admin, address _investmentManager) external returns (address); } diff --git a/src/liquidityPool/LiquidityPool.sol b/src/liquidityPool/LiquidityPool.sol index daff2880..53df7c05 100644 --- a/src/liquidityPool/LiquidityPool.sol +++ b/src/liquidityPool/LiquidityPool.sol @@ -1,6 +1,34 @@ // SPDX-License-Identifier: AGPL-3.0-only pragma solidity ^0.8.18; +import "./../auth/auth.sol"; + +interface RestrictedTokenERC20 { + // erc20 functions + function mint(address owner, uint256 amount) external; + function burn(address owner, uint256 amount) external; + + function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); + function transfer(address recipient, uint256 amount) external returns (bool); + // function approve(address spender, uint256 amount) external returns (bool); + function approveForOwner(address owner, address spender, uint256 value) external returns (bool); + + function totalSupply() external view returns (uint256); + function balanceOf(address owner) external returns (uint256); + function allowance(address owner, address spender) external returns (uint256); + // function increaseAllowance(address spender, uint256 addedValue) external returns (bool); + // function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); + function increaseAllowanceForOwner(address owner, address spender, uint256 addedValue) external returns (bool); + function decreaseAllowanceForOwner(address owner, address spender, uint256 subtractedValue) + external + returns (bool); + + // restricted token functions + function latestPrice() external view returns (uint256); + function memberlist() external returns (address); + function hasMember(address) external returns (bool); +} + // Liquidity Pool implementation for Centrifuge Pools following the EIP4626 standard. // Each Liquidity Pool is a tokenized vault issuing shares as restricted ERC20 tokens against stable currency deposits based on the current share price. // Liquidity Pool vault: Liquidity Pool asset value. @@ -19,8 +47,6 @@ pragma solidity ^0.8.18; // maple: https://github.com/maple-labs/pool-v2/blob/301f05b4fe5e9202eef988b4c8321310b4e86dc8/contracts/Pool.sol // yearn: https://github.com/yearn/yearn-vaults-v3/blob/master/contracts/VaultV3.vy -import "../token/Restricted.sol"; - interface InvestmentManagerLike { function processDeposit(address _receiver, uint256 _assets) external returns (uint256); function processMint(address _receiver, uint256 _shares) external returns (uint256); @@ -38,13 +64,12 @@ interface InvestmentManagerLike { /// @title LiquidityPool /// @author ilinzweilin -contract LiquidityPool is RestrictedToken { +/// @dev LiquidityPool is compliant with the EIP4626 & ERC20 standards +contract LiquidityPool is Auth { InvestmentManagerLike public investmentManager; address public asset; // underlying stable ERC-20 stable currency - - uint128 public latestPrice; // share price - uint256 public lastPriceUpdate; // timestamp of the latest share price update + RestrictedTokenERC20 public share; // underlying trache Token // ids of the existing centrifuge chain pool and tranche that the liquidity pool belongs to uint64 public poolId; @@ -55,14 +80,18 @@ contract LiquidityPool is RestrictedToken { event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); + event File(bytes32 indexed what, address data); - constructor(uint8 _decimals) RestrictedToken(_decimals) {} + constructor() { + wards[msg.sender] = 1; + emit Rely(msg.sender); + } /// @dev investmentManager and asset address to be filed by the factory on deployment - function file(bytes32 _what, address _data) public override auth { + function file(bytes32 _what, address _data) public auth { if (_what == "investmentManager") investmentManager = InvestmentManagerLike(_data); else if (_what == "asset") asset = _data; - else if (_what == "memberlist") memberlist = MemberlistLike(_data); + else if (_what == "share") share = RestrictedTokenERC20(_data); else revert("LiquidityPool/file-unrecognized-param"); emit File(_what, _data); } @@ -77,17 +106,17 @@ contract LiquidityPool is RestrictedToken { /// @dev The total amount of vault shares /// @return Total amount of the underlying vault assets including accrued interest function totalAssets() public view returns (uint256) { - return totalSupply * latestPrice; + return totalSupply() * latestPrice(); } /// @dev Calculates the amount of shares / tranche tokens that any user would get for the amount of assets provided. The calcultion is based on the token price from the most recent epoch retrieved from Centrifuge chain. function convertToShares(uint256 _assets) public view returns (uint256 shares) { - shares = _assets / latestPrice; + shares = _assets / latestPrice(); } /// @dev Calculates the asset value for an amount of shares / tranche tokens provided. The calcultion is based on the token price from the most recent epoch retrieved from Centrifuge chain. function convertToAssets(uint256 _shares) public view returns (uint256 assets) { - assets = _shares * latestPrice; + assets = _shares * latestPrice(); } /// @return Maximum amount of stable currency that can be deposited into the Tranche by the receiver after the epoch had been executed on Centrifuge chain. @@ -174,12 +203,6 @@ contract LiquidityPool is RestrictedToken { return currencyPayout; } - // auth functions - function updateTokenPrice(uint128 _tokenPrice) public auth { - latestPrice = _tokenPrice; - lastPriceUpdate = block.timestamp; - } - function collectRedeem(address _receiver) public { investmentManager.collectRedeem(poolId, trancheId, _receiver, asset); } @@ -187,4 +210,56 @@ contract LiquidityPool is RestrictedToken { function collectInvest(address _receiver) public { investmentManager.collectInvest(poolId, trancheId, _receiver, asset); } + + // overwrite all ERC20 functions and pass the calls to the shares contract + function totalSupply() public view returns (uint256) { + return share.totalSupply(); + } + + function balanceOf(address _owner) public returns (uint256) { + return share.balanceOf(_owner); + } + + function transfer(address _recipient, uint256 _amount) public auth returns (bool) { + return share.transferFrom(msg.sender, _recipient, _amount); + } + + // test allowance + function transferFrom(address _sender, address _recipient, uint256 _amount) public auth returns (bool) { + return share.transferFrom(_sender, _recipient, _amount); + } + + function approve(address _spender, uint256 _amount) public returns (bool) { + return share.approveForOwner(msg.sender, _spender, _amount); + } + + function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool) { + return share.increaseAllowanceForOwner(msg.sender, _spender, _addedValue); + } + + function decreaseAllowance(address _spender, uint256 _subtractedValue) public returns (bool) { + return share.decreaseAllowanceForOwner(msg.sender, _spender, _subtractedValue); + } + + function mint(address _owner, uint256 _amount) public auth { + share.mint(_owner, _amount); + } + + function burn(address _owner, uint256 _amount) public auth { + share.burn(_owner, _amount); + } + + function allowance(address _owner, address _spender) public returns (uint256) { + return share.allowance(_owner, _spender); + } + + // restricted token functions + + function latestPrice() public view returns (uint256) { + return share.latestPrice(); + } + + function hasMember(address _user) public returns (bool) { + return share.hasMember(_user); + } } diff --git a/src/token/ERC20.sol b/src/token/ERC20.sol index c4fd4907..edba8464 100644 --- a/src/token/ERC20.sol +++ b/src/token/ERC20.sol @@ -35,6 +35,7 @@ contract ERC20 is Auth { constructor(uint8 decimals_) { decimals = decimals_; wards[msg.sender] = 1; + emit Rely(msg.sender); deploymentChainId = block.chainid; _DOMAIN_SEPARATOR = _calculateDomainSeparator(block.chainid); @@ -135,6 +136,44 @@ contract ERC20 is Auth { return true; } + function approveForOwner(address owner, address spender, uint256 value) external auth returns (bool) { + allowance[owner][spender] = value; + + emit Approval(owner, spender, value); + + return true; + } + + function increaseAllowanceForOwner(address owner, address spender, uint256 addedValue) + external + auth + returns (bool) + { + uint256 newValue = allowance[owner][spender] + addedValue; + allowance[owner][spender] = newValue; + + emit Approval(owner, spender, newValue); + + return true; + } + + function decreaseAllowanceForOwner(address owner, address spender, uint256 subtractedValue) + external + auth + returns (bool) + { + uint256 allowed = allowance[owner][spender]; + require(allowed >= subtractedValue, "ERC20/insufficient-allowance"); + unchecked { + allowed = allowed - subtractedValue; + } + allowance[owner][spender] = allowed; + + emit Approval(owner, spender, allowed); + + return true; + } + // --- Mint/Burn --- function mint(address to, uint256 value) public virtual auth { require(to != address(0) && to != address(this), "ERC20/invalid-address"); @@ -146,7 +185,7 @@ contract ERC20 is Auth { emit Transfer(address(0), to, value); } - function burn(address from, uint256 value) external { + function burn(address from, uint256 value) external auth { uint256 balance = balanceOf[from]; require(balance >= value, "ERC20/insufficient-balance"); diff --git a/src/token/Restricted.sol b/src/token/Restricted.sol index 43b952c2..53a9b912 100644 --- a/src/token/Restricted.sol +++ b/src/token/Restricted.sol @@ -30,6 +30,9 @@ interface RestrictedTokenLike is ERC20Like { contract RestrictedToken is ERC20 { MemberlistLike public memberlist; + uint128 public latestPrice; // tokenPrice + uint256 public lastPriceUpdate; // timestamp of the latest share price update + // --- Events --- event File(bytes32 indexed what, address data); @@ -63,4 +66,10 @@ contract RestrictedToken is ERC20 { function mint(address to, uint256 value) public override checkMember(to) { return super.mint(to, value); } + + // auth functions + function updateTokenPrice(uint128 _tokenPrice) public auth { + latestPrice = _tokenPrice; + lastPriceUpdate = block.timestamp; + } } diff --git a/test/InvestmentManager.t.sol b/test/InvestmentManager.t.sol index 90ca1a1a..dd37d8cb 100644 --- a/test/InvestmentManager.t.sol +++ b/test/InvestmentManager.t.sol @@ -6,8 +6,9 @@ import {InvestmentManager, Tranche} from "../src/InvestmentManager.sol"; import {TokenManager} from "../src/TokenManager.sol"; import {Gateway} from "../src/Gateway.sol"; import {Escrow} from "../src/Escrow.sol"; -import {LiquidityPoolFactory, MemberlistFactory} from "../src/liquidityPool/Factory.sol"; +import {LiquidityPoolFactory, TrancheTokenFactory, MemberlistFactory} from "../src/liquidityPool/Factory.sol"; import {LiquidityPool} from "../src/liquidityPool/LiquidityPool.sol"; +import {RestrictedToken} from "../src/token/Restricted.sol"; import {ERC20} from "../src/token/ERC20.sol"; import {MemberlistLike, Memberlist} from "../src/token/Memberlist.sol"; @@ -24,7 +25,7 @@ interface EscrowLike_ { function rely(address usr) external; } -interface AuthLike { +interface AuthLike_ { function wards(address user) external returns (uint256); } @@ -42,9 +43,11 @@ contract InvestmentManagerTest is Test { uint256 gracePeriod = 48 hours; address escrow_ = address(new Escrow()); address liquidityPoolFactory_ = address(new LiquidityPoolFactory()); + address trancheTokenFactory_ = address(new TrancheTokenFactory()); address memberlistFactory_ = address(new MemberlistFactory()); - evmInvestmentManager = new InvestmentManager(escrow_, liquidityPoolFactory_, memberlistFactory_); + evmInvestmentManager = + new InvestmentManager(escrow_, liquidityPoolFactory_, trancheTokenFactory_, memberlistFactory_); evmTokenManager = new TokenManager(escrow_); mockXcmRouter = new MockXcmRouter(address(evmInvestmentManager)); @@ -122,8 +125,8 @@ contract InvestmentManagerTest is Test { (uint64 actualPoolId,,) = evmInvestmentManager.pools(poolId); assertEq(uint256(actualPoolId), uint256(poolId)); homePools.addTranche(poolId, trancheId, tokenName, tokenSymbol, decimals, price); - ( + address token, uint64 poolId_, bytes16 trancheId_, uint8 decimals_, @@ -132,15 +135,12 @@ contract InvestmentManagerTest is Test { string memory tokenSymbol_ ) = evmInvestmentManager.tranches(poolId, trancheId); - address[] memory liquidityPools_ = evmInvestmentManager.getLiquidityPoolsForTranche(poolId, trancheId); - assertEq(poolId, poolId_); assertEq(trancheId, trancheId_); assertEq(block.timestamp, createdAt_); assertEq(bytes32ToString(stringToBytes32(tokenName)), bytes32ToString(stringToBytes32(tokenName_))); assertEq(bytes32ToString(stringToBytes32(tokenSymbol)), bytes32ToString(stringToBytes32(tokenSymbol_))); assertEq(decimals, decimals_); - assertEq(liquidityPools_.length, 0); } function testAddingTrancheMultipleTimesFails( @@ -172,7 +172,7 @@ contract InvestmentManagerTest is Test { for (uint256 i = 0; i < trancheIds.length; i++) { homePools.addTranche(poolId, trancheIds[i], tokenName, tokenSymbol, decimals, price); - (uint64 poolId_, bytes16 trancheId_,,,,) = evmInvestmentManager.tranches(poolId, trancheIds[i]); + (, uint64 poolId_, bytes16 trancheId_,,,,) = evmInvestmentManager.tranches(poolId, trancheIds[i]); assertEq(poolId, poolId_); assertEq(trancheIds[i], trancheId_); @@ -221,37 +221,35 @@ contract InvestmentManagerTest is Test { homePools.addCurrency(currency, address(erc20)); homePools.allowPoolCurrency(poolId, currency); + address trancheToken_ = evmInvestmentManager.deployTranche(poolId, trancheId); address lPoolAddress = evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); address lPool_ = evmInvestmentManager.liquidityPools(poolId, trancheId, address(erc20)); // make sure the pool was stored in connectors - address[] memory liquidityPools = evmInvestmentManager.getLiquidityPoolsForTranche(poolId, trancheId); // make sure the pool was added to the tranche struct assertEq(lPoolAddress, lPool_); - bool lPoolIncluded; - for (uint256 i = 0; i < liquidityPools.length; i++) { - if (liquidityPools[i] == lPool_) { - lPoolIncluded = true; - } - } - assertTrue(lPoolIncluded == true); // check LiquidityPool state LiquidityPool lPool = LiquidityPool(lPool_); + RestrictedToken trancheToken = RestrictedToken(trancheToken_); assertEq(address(lPool.investmentManager()), address(evmInvestmentManager)); assertEq(lPool.asset(), address(erc20)); assertEq(lPool.poolId(), poolId); assertEq(lPool.trancheId(), trancheId); - assertEq(lPool.name(), bytes32ToString(stringToBytes32(tokenName))); - assertEq(lPool.symbol(), bytes32ToString(stringToBytes32(tokenSymbol))); - assertEq(lPool.decimals(), decimals); - - // check wards - assertTrue(lPool.hasMember(address(evmInvestmentManager.escrow()))); assertTrue(lPool.wards(address(gateway)) == 1); assertTrue(lPool.wards(address(evmInvestmentManager)) == 1); assertTrue(lPool.wards(address(this)) == 0); assertTrue(evmInvestmentManager.wards(lPoolAddress) == 1); + + assertEq(trancheToken.name(), bytes32ToString(stringToBytes32(tokenName))); + assertEq(trancheToken.symbol(), bytes32ToString(stringToBytes32(tokenSymbol))); + assertEq(trancheToken.decimals(), decimals); + assertTrue(trancheToken.hasMember(address(evmInvestmentManager.escrow()))); + + assertTrue(trancheToken.wards(address(gateway)) == 1); + assertTrue(trancheToken.wards(address(evmInvestmentManager)) == 1); + assertTrue(trancheToken.wards(lPool_) == 1); + assertTrue(trancheToken.wards(address(this)) == 0); } function testDeployingLiquidityPoolNonExistingTrancheFails( @@ -314,6 +312,7 @@ contract InvestmentManagerTest is Test { ERC20 erc20 = newErc20("X's Dollar", "USDX", 42); homePools.addPool(poolId); // add pool homePools.addTranche(poolId, trancheId, tokenName, tokenSymbol, decimals, price); // add tranche + evmInvestmentManager.deployTranche(poolId, trancheId); homePools.addCurrency(currency, address(erc20)); @@ -337,6 +336,7 @@ contract InvestmentManagerTest is Test { homePools.addCurrency(currency, address(erc20)); homePools.allowPoolCurrency(poolId, currency); + evmInvestmentManager.deployTranche(poolId, trancheId); evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); vm.expectRevert(bytes("InvestmentManager/liquidityPool-already-deployed")); @@ -362,6 +362,7 @@ contract InvestmentManagerTest is Test { homePools.addTranche(poolId, trancheId, tokenName, tokenSymbol, decimals, price); // add tranche homePools.addCurrency(currency, address(erc20)); homePools.allowPoolCurrency(poolId, currency); + evmInvestmentManager.deployTranche(poolId, trancheId); address lPool_ = evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); homePools.updateMember(poolId, trancheId, user, validUntil); @@ -383,18 +384,6 @@ contract InvestmentManagerTest is Test { evmInvestmentManager.updateMember(poolId, trancheId, user, validUntil); } - function testUpdatingMemberForNonExistentPoolFails( - uint64 poolId, - bytes16 trancheId, - address user, - uint64 validUntil - ) public { - vm.assume(validUntil > block.timestamp); - evmInvestmentManager.file("gateway", address(this)); - vm.expectRevert(bytes("InvestmentManager/invalid-pool-or-tranche")); - evmInvestmentManager.updateMember(poolId, trancheId, user, validUntil); - } - function testUpdatingMemberForNonExistentTrancheFails( uint64 poolId, bytes16 trancheId, @@ -404,7 +393,7 @@ contract InvestmentManagerTest is Test { vm.assume(validUntil > block.timestamp); homePools.addPool(poolId); - vm.expectRevert(bytes("InvestmentManager/invalid-pool-or-tranche")); + vm.expectRevert(bytes("InvestmentManager/tranche-not-deployed")); homePools.updateMember(poolId, trancheId, user, validUntil); } @@ -421,13 +410,12 @@ contract InvestmentManagerTest is Test { ERC20 erc20 = newErc20("X's Dollar", "USDX", 42); homePools.addPool(poolId); // add pool homePools.addTranche(poolId, trancheId, tokenName, tokenSymbol, decimals, price); // add tranche - homePools.addCurrency(currency, address(erc20)); - homePools.allowPoolCurrency(poolId, currency); - address lPool_ = evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); + + address tranche_ = evmInvestmentManager.deployTranche(poolId, trancheId); homePools.updateTokenPrice(poolId, trancheId, price); - assertEq(LiquidityPool(lPool_).latestPrice(), price); - assertEq(LiquidityPool(lPool_).lastPriceUpdate(), block.timestamp); + assertEq(RestrictedToken(tranche_).latestPrice(), price); + assertEq(RestrictedToken(tranche_).lastPriceUpdate(), block.timestamp); } function testUpdatingTokenPriceAsNonRouterFails( @@ -445,22 +433,17 @@ contract InvestmentManagerTest is Test { homePools.addTranche(poolId, trancheId, tokenName, tokenSymbol, decimals, price); // add tranche homePools.addCurrency(currency, address(erc20)); homePools.allowPoolCurrency(poolId, currency); + evmInvestmentManager.deployTranche(poolId, trancheId); evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); vm.expectRevert(bytes("InvestmentManager/not-the-gateway")); evmInvestmentManager.updateTokenPrice(poolId, trancheId, price); } - function testUpdatingTokenPriceForNonExistentPoolFails(uint64 poolId, bytes16 trancheId, uint128 price) public { - evmInvestmentManager.file("gateway", address(this)); - vm.expectRevert(bytes("InvestmentManager/invalid-pool-or-tranche")); - evmInvestmentManager.updateTokenPrice(poolId, trancheId, price); - } - function testUpdatingTokenPriceForNonExistentTrancheFails(uint64 poolId, bytes16 trancheId, uint128 price) public { homePools.addPool(poolId); - vm.expectRevert(bytes("InvestmentManager/invalid-pool-or-tranche")); + vm.expectRevert(bytes("InvestmentManager/tranche-not-deployed")); homePools.updateTokenPrice(poolId, trancheId, price); } @@ -480,6 +463,7 @@ contract InvestmentManagerTest is Test { homePools.addCurrency(currency, address(erc20)); homePools.allowPoolCurrency(poolId, currency); + evmInvestmentManager.deployTranche(poolId, trancheId); lPool = evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); } diff --git a/test/LiquidityPool.t.sol b/test/LiquidityPool.t.sol index 71b69a8d..e91427a2 100644 --- a/test/LiquidityPool.t.sol +++ b/test/LiquidityPool.t.sol @@ -6,7 +6,7 @@ import {InvestmentManager, Tranche} from "../src/InvestmentManager.sol"; import {TokenManager} from "../src/TokenManager.sol"; import {Gateway} from "../src/Gateway.sol"; import {Escrow} from "../src/Escrow.sol"; -import {LiquidityPoolFactory, MemberlistFactory} from "../src/liquidityPool/Factory.sol"; +import {LiquidityPoolFactory, TrancheTokenFactory, MemberlistFactory} from "../src/liquidityPool/Factory.sol"; import {LiquidityPool} from "../src/liquidityPool/LiquidityPool.sol"; import {ERC20} from "../src/token/ERC20.sol"; @@ -24,7 +24,7 @@ interface EscrowLike_ { function rely(address usr) external; } -interface AuthLike { +interface AuthLike_ { function wards(address user) external returns (uint256); } @@ -48,8 +48,10 @@ contract LiquidityPoolTest is Test { erc20 = newErc20("X's Dollar", "USDX", 42); address liquidityPoolFactory_ = address(new LiquidityPoolFactory()); address memberlistFactory_ = address(new MemberlistFactory()); + address trancheTokenFactory_ = address(new TrancheTokenFactory()); - evmInvestmentManager = new InvestmentManager(address(escrow), liquidityPoolFactory_, memberlistFactory_); + evmInvestmentManager = + new InvestmentManager(address(escrow), liquidityPoolFactory_, trancheTokenFactory_, memberlistFactory_); evmTokenManager = new TokenManager(address(escrow)); mockXcmRouter = new MockXcmRouter(address(evmInvestmentManager)); @@ -102,7 +104,7 @@ contract LiquidityPoolTest is Test { lPool.requestDeposit(amount); homePools.updateMember(poolId, trancheId, address(this), validUntil); // add user as member - // will fail - user did not give currency allowance to investmentManager + // // will fail - user did not give currency allowance to investmentManager vm.expectRevert(bytes("ERC20/insufficient-allowance")); lPool.requestDeposit(amount); erc20.approve(address(evmInvestmentManager), amount); // add allowance @@ -139,7 +141,7 @@ contract LiquidityPoolTest is Test { assertEq(lPool.balanceOf(address(this)), trancheTokensPayout - lPool.maxMint(address(this))); assertTrue(lPool.balanceOf(address(escrow)) <= 1); assertTrue(lPool.maxMint(address(this)) <= 1); - // assertTrue(lPool.maxDeposit(address(this)) <= 2); // todo: fix rounding + //assertTrue(lPool.maxDeposit(address(this)) <= 2); // todo: fix rounding } function testRedeem( @@ -166,7 +168,7 @@ contract LiquidityPoolTest is Test { // will fail - user did not give tranche token allowance to investmentManager vm.expectRevert(bytes("InvestmentManager/insufficient-balance")); lPool.requestDeposit(amount); - lPool.approve(address(evmInvestmentManager), amount); // add allowance + lPool.approve(address(lPool), amount); // add allowance lPool.requestRedeem(amount); assertEq(lPool.balanceOf(address(escrow)), amount); @@ -220,7 +222,7 @@ contract LiquidityPoolTest is Test { // will fail - user did not give tranche token allowance to investmentManager vm.expectRevert(bytes("InvestmentManager/insufficient-balance")); lPool.requestDeposit(amount); - lPool.approve(address(evmInvestmentManager), amount); // add allowance + lPool.approve(address(lPool), amount); // add allowance lPool.requestRedeem(amount); assertEq(lPool.balanceOf(address(escrow)), amount); @@ -329,6 +331,7 @@ contract LiquidityPoolTest is Test { homePools.addTranche(poolId, trancheId, tokenName, tokenSymbol, decimals, price); // add tranche homePools.addCurrency(currency, address(erc20)); homePools.allowPoolCurrency(poolId, currency); + evmInvestmentManager.deployTranche(poolId, trancheId); address lPoolAddress = evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); return lPoolAddress; diff --git a/test/TokenManager.t.sol b/test/TokenManager.t.sol index 826216ff..62bc060b 100644 --- a/test/TokenManager.t.sol +++ b/test/TokenManager.t.sol @@ -6,7 +6,7 @@ import {InvestmentManager, Tranche} from "../src/InvestmentManager.sol"; import {TokenManager} from "../src/TokenManager.sol"; import {Gateway} from "../src/Gateway.sol"; import {Escrow} from "../src/Escrow.sol"; -import {LiquidityPoolFactory, MemberlistFactory} from "../src/liquidityPool/Factory.sol"; +import {LiquidityPoolFactory, MemberlistFactory, TrancheTokenFactory} from "../src/liquidityPool/Factory.sol"; import {LiquidityPool} from "../src/liquidityPool/LiquidityPool.sol"; import {ERC20} from "../src/token/ERC20.sol"; @@ -24,10 +24,6 @@ interface EscrowLike_ { function rely(address usr) external; } -interface AuthLike { - function wards(address user) external returns (uint256); -} - contract TokenManagerTest is Test { InvestmentManager evmInvestmentManager; TokenManager evmTokenManager; @@ -42,9 +38,11 @@ contract TokenManagerTest is Test { uint256 gracePeriod = 48 hours; address escrow_ = address(new Escrow()); address liquidityPoolFactory_ = address(new LiquidityPoolFactory()); + address trancheTokenFactory_ = address(new TrancheTokenFactory()); address memberlistFactory_ = address(new MemberlistFactory()); - evmInvestmentManager = new InvestmentManager(escrow_, liquidityPoolFactory_, memberlistFactory_); + evmInvestmentManager = + new InvestmentManager(escrow_, liquidityPoolFactory_, trancheTokenFactory_, memberlistFactory_); evmTokenManager = new TokenManager(escrow_); mockXcmRouter = new MockXcmRouter(address(evmInvestmentManager)); @@ -203,9 +201,7 @@ contract TokenManagerTest is Test { // Now send the transfer from EVM -> Cent Chain LiquidityPool(lPool_).approve(address(evmTokenManager), amount); - evmTokenManager.transferTrancheTokensToCentrifuge( - poolId, trancheId, LiquidityPool(lPool_).asset(), centChainAddress, amount - ); + evmTokenManager.transferTrancheTokensToCentrifuge(poolId, trancheId, centChainAddress, amount); assertEq(LiquidityPool(lPool_).balanceOf(address(this)), 0); // Finally, verify the connector called `router.send` @@ -312,6 +308,7 @@ contract TokenManagerTest is Test { homePools.addCurrency(currency, address(erc20)); homePools.allowPoolCurrency(poolId, currency); + evmInvestmentManager.deployTranche(poolId, trancheId); lPool = evmInvestmentManager.deployLiquidityPool(poolId, trancheId, address(erc20)); } diff --git a/test/liquidityPool/factory.t.sol b/test/liquidityPool/factory.t.sol index a3d2b74b..dd33a166 100644 --- a/test/liquidityPool/factory.t.sol +++ b/test/liquidityPool/factory.t.sol @@ -21,28 +21,21 @@ contract FactoryTest is Test { address sender, uint64 poolId, bytes16 trancheId, + address token, uint128 currency, address asset, address investmentManager, - address admin, - address memberlist, - string memory name, - string memory symbol, - uint8 decimals + address admin ) public { vm.selectFork(mainnetFork); LiquidityPoolFactory lpFactory1 = new LiquidityPoolFactory{ salt: SALT }(); - address lp1 = lpFactory1.newLiquidityPool( - poolId, trancheId, currency, asset, investmentManager, admin, memberlist, name, symbol, decimals - ); + address lp1 = lpFactory1.newLiquidityPool(poolId, trancheId, currency, asset, token, investmentManager, admin); vm.selectFork(polygonFork); LiquidityPoolFactory lpFactory2 = new LiquidityPoolFactory{ salt: SALT }(); assertEq(address(lpFactory1), address(lpFactory2)); vm.prank(sender); - address lp2 = lpFactory2.newLiquidityPool( - poolId, trancheId, currency, asset, investmentManager, admin, memberlist, name, symbol, decimals - ); + address lp2 = lpFactory2.newLiquidityPool(poolId, trancheId, currency, asset, token, investmentManager, admin); assertEq(address(lp1), address(lp2)); } @@ -70,12 +63,9 @@ contract FactoryTest is Test { bytes16 trancheId, uint128 currency, address asset, + address token, address investmentManager, - address admin, - address memberlist, - string memory name, - string memory symbol, - uint8 decimals + address admin ) public { LiquidityPoolFactory lpFactory = new LiquidityPoolFactory{ salt: SALT }(); @@ -88,16 +78,14 @@ contract FactoryTest is Test { bytes1(0xff), address(lpFactory), salt, - keccak256(abi.encodePacked(type(LiquidityPool).creationCode, abi.encode(decimals))) + keccak256(abi.encodePacked(type(LiquidityPool).creationCode)) ) ) ) ) ); - address token = lpFactory.newLiquidityPool( - poolId, trancheId, currency, asset, investmentManager, admin, memberlist, name, symbol, decimals - ); + address token = lpFactory.newLiquidityPool(poolId, trancheId, currency, asset, token, investmentManager, admin); assertEq(address(token), predictedAddress); } @@ -107,12 +95,9 @@ contract FactoryTest is Test { bytes16 trancheId, uint128 currency, address asset, + address token, address investmentManager, - address admin, - address memberlist, - string memory name, - string memory symbol, - uint8 decimals + address admin ) public { address predictedAddress = address( uint160( @@ -130,13 +115,9 @@ contract FactoryTest is Test { ); LiquidityPoolFactory lpFactory = new LiquidityPoolFactory{ salt: SALT }(); assertEq(address(lpFactory), predictedAddress); - address lp1 = lpFactory.newLiquidityPool( - poolId, trancheId, currency, asset, investmentManager, admin, memberlist, name, symbol, decimals - ); + address lp1 = lpFactory.newLiquidityPool(poolId, trancheId, currency, asset, token, investmentManager, admin); vm.expectRevert(); - address lp2 = lpFactory.newLiquidityPool( - poolId, trancheId, currency, asset, investmentManager, admin, memberlist, name, symbol, decimals - ); + address lp2 = lpFactory.newLiquidityPool(poolId, trancheId, currency, asset, token, investmentManager, admin); } function testMemberlistFactoryIsDeterministicAcrossChains(address sender, address admin, address investmentManager)