diff --git a/deployments/ETH_MAINNET.sol b/deployments/ETH_MAINNET.sol new file mode 100644 index 00000000..1568d743 --- /dev/null +++ b/deployments/ETH_MAINNET.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.21; + +contract Addresses { + address public root = address(0x498016d30Cd5f0db50d7ACE329C07313a0420502); + address public investmentManager = address(0xbBF0AB988691dB1892ADaF7F0eF560Ca4c6DD73A); + address public poolManager = address(0x78E9e622A57f70F1E0Ec652A4931E4e278e58142); + address public gateway = address(0x634F036fE66579E901c7bA34e33DF422E37A0037); + address public escrow = address(0xd595E1483c507E74E2E6A3dE8e7D08d8f6F74936); + address public userEscrow= address(0x9fc3A3bcEdc1CaB14EfC1B7ef45dFBDd3d17c9d7); + address public router = address(0x8174D5f12Ce682459864D8C081f9635012Ab51c2); + address public trancheTokenFactory = address(0x4aEFE6CeFEd5D0A30679E41C0B3Fee6cbAa6ADf6); + address public liquidityPoolFactory = address(0x77F48b2c942E6f3ac2232568d560e423c441386a); + address public restrictionManagerFactory = address(0xf4D7F6919eF0B495a2551F7299324961F29aE7aC); + address public pauseAdmin = address(0xce86472007Ea37a5d0208f8C1559A37530c8067C); + address public delayedAdmin = address(0x2559998026796Ca6fd057f3aa66F2d6ecdEd9028); + address public messages = address(0xAf9F6Ac63C057EB7F59b6Fae2c3d447191b58Ea5); + address public deployer = address(0x7270b20603FbB3dF0921381670fbd62b9991aDa4); + address public admin = address(0xD9D30ab47c0f096b0AA67e9B8B1624504a63e7FD); // multisig + + // deployed LPs + address public anemoyToken = address(0x30baA3BA9D7089fD8D020a994Db75D14CF7eC83b); +} \ No newline at end of file diff --git a/test/rpc/RPC.t.sol b/test/rpc/RPC.t.sol new file mode 100644 index 00000000..dc2c4226 --- /dev/null +++ b/test/rpc/RPC.t.sol @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.21; + +import "forge-std/Test.sol"; +import {Addresses} from "deployments/ETH_MAINNET.sol"; +import {Root} from "src/Root.sol"; +import {InvestmentManager} from "src/InvestmentManager.sol"; +import {PoolManager} from "src/PoolManager.sol"; +import {Escrow} from "src/Escrow.sol"; +import {UserEscrow} from "src/UserEscrow.sol"; +import {TrancheToken} from "src/token/Tranche.sol"; +import {Gateway} from "src/gateway/Gateway.sol"; +import {AxelarRouter} from "src/gateway/routers/axelar/Router.sol"; +import {TrancheTokenFactory, LiquidityPoolFactory, RestrictionManagerFactory} from "src/util/Factory.sol"; +import {DelayedAdmin} from "src/admins/DelayedAdmin.sol"; +import {PauseAdmin} from "src/admins/PauseAdmin.sol"; + +contract RPCTest is Test, Addresses { + uint256 mainnetFork; + string MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL"); + + function setUp() public virtual { + mainnetFork = vm.createFork(MAINNET_RPC_URL); // setup ETH mainnet fork + // use fork + vm.selectFork(mainnetFork); + } + + function testContractsWiredCorrectly() public { + // investmentManager + assertEq(address(InvestmentManager(investmentManager).escrow()), escrow); + assertEq(address(InvestmentManager(investmentManager).userEscrow()), userEscrow); + assertEq(address(InvestmentManager(investmentManager).gateway()), gateway); + assertEq(address(InvestmentManager(investmentManager).poolManager()), poolManager); + assertEq(address(Gateway(gateway).investmentManager()), investmentManager); + assertEq(address(PoolManager(poolManager).investmentManager()), investmentManager); + assertEq(InvestmentManager(investmentManager).wards(poolManager), 1); + assertEq(Escrow(escrow).wards(investmentManager), 1); + assertEq(UserEscrow(userEscrow).wards(investmentManager), 1); + assertEq(InvestmentManager(investmentManager).wards(root), 1); + assertEq(InvestmentManager(investmentManager).wards(deployer), 0); // deployer has no permissions + + // PoolManager + assertEq(address(PoolManager(poolManager).gateway()), gateway); + assertEq(address(PoolManager(poolManager).escrow()), escrow); + assertEq(address(PoolManager(poolManager).investmentManager()), investmentManager); + assertEq(address(PoolManager(poolManager).trancheTokenFactory()), trancheTokenFactory); + assertEq(address(PoolManager(poolManager).liquidityPoolFactory()), liquidityPoolFactory); + assertEq(address(PoolManager(poolManager).restrictionManagerFactory()), restrictionManagerFactory); + assertEq(address(Gateway(gateway).poolManager()), poolManager); + assertEq(address(InvestmentManager(investmentManager).poolManager()), poolManager); + assertEq(InvestmentManager(investmentManager).wards(poolManager), 1); + assertEq(Escrow(escrow).wards(poolManager), 1); + assertEq(InvestmentManager(investmentManager).wards(poolManager), 1); + assertEq(PoolManager(poolManager).wards(root), 1); + assertEq(PoolManager(poolManager).wards(deployer), 0); // deployer has no permissions + + // Gateway + assertEq(address(Gateway(gateway).investmentManager()), investmentManager); + assertEq(address(Gateway(gateway).poolManager()), poolManager); + assertEq(address(Gateway(gateway).root()), root); + assertEq(address(InvestmentManager(investmentManager).gateway()), gateway); + assertEq(address(PoolManager(poolManager).gateway()), gateway); + assertEq(address(Gateway(gateway).outgoingRouter()), router); + assertTrue(Gateway(gateway).incomingRouters(router)); + assertEq(Gateway(gateway).wards(root), 1); + assertEq(Root(root).wards(gateway), 1); + assertEq(Gateway(gateway).wards(deployer), 0); // deployer has no permissions + + // Escrow + assertEq(Escrow(escrow).wards(root), 1); + assertEq(Escrow(escrow).wards(deployer), 0); // deployer has no permissions + + // UserEscrow + assertEq(UserEscrow(userEscrow).wards(root), 1); + assertEq(UserEscrow(userEscrow).wards(deployer), 0); // deployer has no permissions + + // router + assertEq(AxelarRouter(router).wards(root), 1); + assertEq(AxelarRouter(router).wards(deployer), 0); // deployer has no permissions + + // trancheTokenFactory + assertEq(address(PoolManager(poolManager).trancheTokenFactory()), trancheTokenFactory); + assertEq(TrancheTokenFactory(trancheTokenFactory).wards(root), 1); + assertEq(TrancheTokenFactory(trancheTokenFactory).wards(deployer), 0); // deployer has no permissions + + // liquidityPoolFactory + assertEq(address(PoolManager(poolManager).liquidityPoolFactory()), liquidityPoolFactory); + assertEq(LiquidityPoolFactory(liquidityPoolFactory).root(), root); + assertEq(LiquidityPoolFactory(liquidityPoolFactory).wards(root), 1); + assertEq(LiquidityPoolFactory(liquidityPoolFactory).wards(poolManager), 1); + assertEq(LiquidityPoolFactory(liquidityPoolFactory).wards(deployer), 0); // deployer has no permissions + + // restrictionMangerFactory + assertEq(address(PoolManager(poolManager).restrictionManagerFactory()), restrictionManagerFactory); + assertEq(RestrictionManagerFactory(restrictionManagerFactory).wards(root), 1); + assertEq(RestrictionManagerFactory(restrictionManagerFactory).wards(deployer), 0); // deployer has no + // permissions + + // delayedAdmin + assertEq(address(DelayedAdmin(delayedAdmin).root()), root); + assertEq(DelayedAdmin(delayedAdmin).wards(admin), 1); + assertEq(Root(root).wards(delayedAdmin), 1); + assertEq(DelayedAdmin(delayedAdmin).wards(root), 0); + assertEq(DelayedAdmin(delayedAdmin).wards(deployer), 0); // deployer has no permissions + + // pauseAdmin + assertEq(address(PauseAdmin(pauseAdmin).root()), root); + assertEq(PauseAdmin(pauseAdmin).wards(delayedAdmin), 1); + // assertEq(PauseAdmin(pauseAdmin).wards(admin), 0); // todo: add once spell executed + assertEq(Root(root).wards(pauseAdmin), 1); + assertEq(PauseAdmin(pauseAdmin).wards(root), 0); + assertEq(PauseAdmin(pauseAdmin).wards(deployer), 0); // deployer has no permissions + } +} diff --git a/test/spells/Spell.sol b/test/spells/Spell.sol new file mode 100644 index 00000000..4fa576c7 --- /dev/null +++ b/test/spells/Spell.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.21; + +import "deployments/ETH_MAINNET.sol"; + +interface RootLike { + function relyContract(address, address) external; +} + +interface FileLike { + function file(bytes32, address) external; +} + +interface AuthLike { + function rely(address) external; + function deny(address) external; +} + +interface PoolManagerLike { + function removeLiquidityPool(uint64 poolId, bytes16 trancheId, address currency) external; + function deployLiquidityPool(uint64 poolId, bytes16 trancheId, address currency) external returns (address); + function getLiquidityPool(uint64 poolId, bytes16 trancheId, address currency) external returns (address); +} + +interface LiquidityPoolLike { + function poolId() external returns (uint64); + function trancheId() external returns (bytes16); + function asset() external returns (address); +} + +// Spell to migrate the LiquidityPool factory contract +contract Spell is Addresses { + bool public done; + string public constant description = "Liquidity Pool Factory migration spell"; + + address public constant LIQUIDITY_POOL_FACTORY_NEW = address(0x8273E36EEcf7A8604BEdEe68FC24Af121B64f165); + address public constant DEPRECATED_LIQUIDITY_POOL = address(0xa0872E8D2975483b2Ab4Afcee729133D8666F6f5); + address public newLiquidityPool; + + address self; + + function cast() public { + require(!done, "spell-already-cast"); + done = true; + execute(); + } + + function execute() internal { + self = address(this); + + // give spell required permissions + RootLike root = RootLike(root); + root.relyContract(LIQUIDITY_POOL_FACTORY_NEW, self); + root.relyContract(poolManager, self); + + // spell magic + migrateLiquidityPoolFactory(); + migrateLiquidityPool(); + + // revoke all permissions from spell + AuthLike(address(root)).deny(self); + AuthLike(LIQUIDITY_POOL_FACTORY_NEW).deny(self); + AuthLike(poolManager).deny(self); + } + + function migrateLiquidityPoolFactory() internal { + AuthLike(LIQUIDITY_POOL_FACTORY_NEW).rely(poolManager); + AuthLike(LIQUIDITY_POOL_FACTORY_NEW).deny(deployer); + FileLike(poolManager).file("liquidityPoolFactory", LIQUIDITY_POOL_FACTORY_NEW); + } + + function migrateLiquidityPool() internal { + LiquidityPoolLike deprectaedLP = LiquidityPoolLike(DEPRECATED_LIQUIDITY_POOL); + address deprectaedLP_ = PoolManagerLike(poolManager).getLiquidityPool( + deprectaedLP.poolId(), deprectaedLP.trancheId(), deprectaedLP.asset() + ); + require(deprectaedLP_ == DEPRECATED_LIQUIDITY_POOL, "SPELL - unknown Liquidity Pool"); + // remove deprectaed pool + PoolManagerLike(poolManager).removeLiquidityPool( + deprectaedLP.poolId(), deprectaedLP.trancheId(), deprectaedLP.asset() + ); + // add new pool + newLiquidityPool = PoolManagerLike(poolManager).deployLiquidityPool( + deprectaedLP.poolId(), deprectaedLP.trancheId(), deprectaedLP.asset() + ); + } +} diff --git a/test/spells/Spell.t.sol b/test/spells/Spell.t.sol new file mode 100644 index 00000000..04ce1596 --- /dev/null +++ b/test/spells/Spell.t.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity 0.8.21; + +import "../rpc/RPC.t.sol"; +import "./Spell.sol"; + +contract SpellTest is RPCTest { + Spell spell; + + function setUp() public override { + super.setUp(); + spell = new Spell(); + + address newLPFactory = spell.LIQUIDITY_POOL_FACTORY_NEW(); + + // rely root on new factory -> to be done manually before spell cast + vm.prank(deployer); + AuthLike(newLPFactory).rely(root); + + liquidityPoolFactory = newLPFactory; // replace liquidityPoolFactory address in the deployment addresses to + // check for correct wiring + castSpell(); + } + + function castSpell() public { + // admin submits a tx to delayedAdmin in order to rely spell -> to be done manually before spell cast + vm.prank(admin); + DelayedAdmin(delayedAdmin).scheduleRely(address(spell)); + // warp to the time when the spell can be cast -> current block + delay + vm.warp(block.timestamp + Root(root).delay()); + Root(root).executeScheduledRely(address(spell)); // --> to be called after delay has passed + spell.cast(); + } + + function testCastSuccessful() public { + address newLP_ = spell.newLiquidityPool(); + address deprecatedLP_ = spell.DEPRECATED_LIQUIDITY_POOL(); + LiquidityPoolLike deprecatedLP = LiquidityPoolLike(deprecatedLP_); + LiquidityPoolLike newLP = LiquidityPoolLike(newLP_); + + assertEq(deprecatedLP.poolId(), newLP.poolId()); + assertEq(deprecatedLP.trancheId(), newLP.trancheId()); + assertEq(deprecatedLP.asset(), newLP.asset()); + + // check if deprectaed pool removed correctly + assertEq(InvestmentManager(investmentManager).wards(deprecatedLP_), 0); + assertEq(TrancheToken(anemoyToken).wards(deprecatedLP_), 0); + assertEq(TrancheToken(anemoyToken).isTrustedForwarder(deprecatedLP_), false); + assertEq(TrancheToken(anemoyToken).allowance(address(escrow), deprecatedLP_), 0); + + // check if new pool added correctly + assertEq(InvestmentManager(investmentManager).wards(newLP_), 1); + assertEq(TrancheToken(anemoyToken).wards(newLP_), 1); + assertEq(TrancheToken(anemoyToken).isTrustedForwarder(newLP_), true); + assertEq(TrancheToken(anemoyToken).allowance(address(escrow), newLP_), UINT256_MAX); + } +}