diff --git a/chains/evm/contracts/test/fork/AddNonCanonicalUSDC.t.sol b/chains/evm/contracts/test/fork/AddNonCanonicalUSDC.t.sol new file mode 100644 index 0000000000..1a70f95c34 --- /dev/null +++ b/chains/evm/contracts/test/fork/AddNonCanonicalUSDC.t.sol @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.24; + +import {TokenAdminRegistry} from "../../tokenAdminRegistry/TokenAdminRegistry.sol"; +import {HybridLockReleaseUSDCTokenPool} from "../../pools/USDC/HybridLockReleaseUSDCTokenPool.sol"; +import {RateLimiter} from "../../libraries/RateLimiter.sol"; +import {MCMSForkTest} from "./MCMSForkTest.t.sol"; +import {IERC20} from "@openzeppelin/contracts@4.8.3/token/ERC20/IERC20.sol"; + +contract HybridLockReleaseUSDCTokenPoolSetup is MCMSForkTest { + address private s_poolAddress; + address private s_jovayPoolAddress; + address private s_pharosPoolAddress; + address private s_tokenAddress; + address private s_jovayTokenAddress; + address private s_pharosTokenAddress; + address private s_tokenAdminRegistryAddress; + address private s_timelockAddress; + bool private s_shouldUseLockRelease; + uint64 private s_jovayChainSelector; + uint64 private s_pharosChainSelector; + uint256 private s_forkId; + bytes[] private s_payloads; + + function setUp() public { + // Skip test if RPC_URL is not set (e.g., in CI without .env) + string memory rpcUrl = vm.envOr("RPC_URL", string("")); + vm.skip(bytes(rpcUrl).length == 0); + + s_forkId = vm.createFork(rpcUrl); + + // Load payloads dynamically based on PAYLOAD_COUNT + uint256 payloadCount = vm.envUint("PAYLOAD_COUNT"); + s_payloads = new bytes[](payloadCount); + for (uint256 i = 0; i < payloadCount; i++) { + s_payloads[i] = vm.envBytes(string.concat("PAYLOAD_", vm.toString(i + 1))); + } + + s_jovayChainSelector = uint64(vm.envUint("JOVAY_CHAIN_SELECTOR")); + s_pharosChainSelector = uint64(vm.envUint("PHAROS_CHAIN_SELECTOR")); + s_poolAddress = vm.envAddress("POOL_ADDRESS"); + s_jovayPoolAddress = vm.envAddress("JOVAY_POOL_ADDRESS"); + s_pharosPoolAddress = vm.envAddress("PHAROS_POOL_ADDRESS"); + s_tokenAddress = vm.envAddress("TOKEN_ADDRESS"); + s_tokenAdminRegistryAddress = vm.envAddress("TOKEN_ADMIN_REGISTRY_ADDRESS"); + s_timelockAddress = vm.envAddress("TIMELOCK_ADDRESS"); + s_jovayTokenAddress = vm.envAddress("JOVAY_TOKEN_ADDRESS"); + s_pharosTokenAddress = vm.envAddress("PHAROS_TOKEN_ADDRESS"); + } + + function testFork_Migration() public { + vm.selectFork(s_forkId); + + // Apply the payloads + for (uint256 i = 0; i < s_payloads.length; i++) { + _applyPayload(s_timelockAddress, s_payloads[i]); + } + + // Get the Liquidity Provider + address liquidityProviderJovay = HybridLockReleaseUSDCTokenPool(s_poolAddress).getLiquidityProvider(s_jovayChainSelector); + address liquidityProviderPharos = HybridLockReleaseUSDCTokenPool(s_poolAddress).getLiquidityProvider(s_pharosChainSelector); + + // Check the shouldUseLockRelease function + bool shouldUseLockReleaseJovay = HybridLockReleaseUSDCTokenPool(s_poolAddress).shouldUseLockRelease(s_jovayChainSelector); + bool shouldUseLockReleasePharos = HybridLockReleaseUSDCTokenPool(s_poolAddress).shouldUseLockRelease(s_pharosChainSelector); + + // Check the new supported chains + bool isSupportedChainJovay = HybridLockReleaseUSDCTokenPool(s_poolAddress).isSupportedChain(s_jovayChainSelector); + bool isSupportedChainPharos = HybridLockReleaseUSDCTokenPool(s_poolAddress).isSupportedChain(s_pharosChainSelector); + + // Check the new remote pools + bytes[] memory remotePoolsJovay = HybridLockReleaseUSDCTokenPool(s_poolAddress).getRemotePools(s_jovayChainSelector); + bytes[] memory remotePoolsPharos = HybridLockReleaseUSDCTokenPool(s_poolAddress).getRemotePools(s_pharosChainSelector); + + // Check the new remote token + bytes memory remoteTokenJovay = HybridLockReleaseUSDCTokenPool(s_poolAddress).getRemoteToken(s_jovayChainSelector); + bytes memory remoteTokenPharos = HybridLockReleaseUSDCTokenPool(s_poolAddress).getRemoteToken(s_pharosChainSelector); + + // Check the in and outbound rate limits + RateLimiter.TokenBucket memory inboundRateLimitJovay = HybridLockReleaseUSDCTokenPool(s_poolAddress).getCurrentInboundRateLimiterState(s_jovayChainSelector); + RateLimiter.TokenBucket memory outboundRateLimitJovay = HybridLockReleaseUSDCTokenPool(s_poolAddress).getCurrentOutboundRateLimiterState(s_jovayChainSelector); + RateLimiter.TokenBucket memory inboundRateLimitPharos = HybridLockReleaseUSDCTokenPool(s_poolAddress).getCurrentInboundRateLimiterState(s_pharosChainSelector); + RateLimiter.TokenBucket memory outboundRateLimitPharos = HybridLockReleaseUSDCTokenPool(s_poolAddress).getCurrentOutboundRateLimiterState(s_pharosChainSelector); + + // Ensure that the liquidiy provider matches the timelock address -- Jovay + assertEq( + liquidityProviderJovay, + s_timelockAddress, + "Liquidity provider should be set to the timelock address" + ); + + // Ensure that the liquidiy provider matches the timelock address -- Pharos + assertEq( + liquidityProviderPharos, + s_timelockAddress, + "Liquidity provider should be set to the timelock address" + ); + + // Ensure that the shouldUseLockRelease function matches the expected value -- Jovay + assertEq(shouldUseLockReleaseJovay, true, "shouldUseLockRelease should be true"); + + // Ensure that the shouldUseLockRelease function matches the expected value -- Pharos + assertEq(shouldUseLockReleasePharos, true, "shouldUseLockRelease should be true"); + + // Ensure that the isSupportedChain function matches the expected value -- Jovay + assertEq(isSupportedChainJovay, true, "isSupportedChain should be true"); + + // Ensure that the isSupportedChain function matches the expected value -- Pharos + assertEq(isSupportedChainPharos, true, "isSupportedChain should be true"); + + // Ensure that the remote pools array is not empty -- Jovay + assertTrue(remotePoolsJovay.length > 0, "Remote pools array should not be empty for Jovay"); + assertEq(abi.decode(remotePoolsJovay[0], (address)), s_jovayPoolAddress, "First remote pool should be set to the pool address for Jovay"); + + // Ensure that the remote pools array is not empty -- Pharos + assertTrue(remotePoolsPharos.length > 0, "Remote pools array should not be empty for Pharos"); + assertEq(abi.decode(remotePoolsPharos[0], (address)), s_pharosPoolAddress, "First remote pool should be set to the pool address for Pharos"); + + // Ensure that the remote token matches the expected value -- Jovay + assertEq(abi.decode(remoteTokenJovay, (address)), s_jovayTokenAddress, "Remote token should be set to the token address"); + + // Ensure that the remote token matches the expected value -- Pharos + assertEq(abi.decode(remoteTokenPharos, (address)), s_pharosTokenAddress, "Remote token should be set to the token address"); + + // Ensure that the inbound rate limit matches the expected value -- Jovay + assertEq(inboundRateLimitJovay.capacity, 0, "Inbound rate limit should be 0"); + assertEq(inboundRateLimitJovay.rate, 0, "Inbound rate limit should be 0"); + + // Ensure that the outbound rate limit matches the expected value -- Jovay + assertEq(outboundRateLimitJovay.capacity, 0, "Outbound rate limit should be 0"); + assertEq(outboundRateLimitJovay.rate, 0, "Outbound rate limit should be 0"); + + // Ensure that the inbound rate limit matches the expected value -- Pharos + assertEq(inboundRateLimitPharos.capacity, 0, "Inbound rate limit should be 0"); + assertEq(inboundRateLimitPharos.rate, 0, "Inbound rate limit should be 0"); + + // Ensure that the outbound rate limit matches the expected value -- Pharos + assertEq(outboundRateLimitPharos.capacity, 0, "Outbound rate limit should be 0"); + assertEq(outboundRateLimitPharos.rate, 0, "Outbound rate limit should be 0"); + + } +} diff --git a/chains/evm/contracts/test/fork/MCMSForkTest.t.sol b/chains/evm/contracts/test/fork/MCMSForkTest.t.sol new file mode 100644 index 0000000000..bb67d3ddea --- /dev/null +++ b/chains/evm/contracts/test/fork/MCMSForkTest.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.24; + +import {Test} from "forge-std/Test.sol"; + +contract MCMSForkTest is Test { + struct Call { + address target; + uint256 value; + bytes data; + } + + error TransactionReverted(); + + function _applyPayload(address sender, bytes memory payload) internal { + (MCMSForkTest.Call[] memory calls,,,) = abi.decode(payload, (MCMSForkTest.Call[], bytes32, bytes32, uint256)); + for (uint256 i = 0; i < calls.length; ++i) { + MCMSForkTest.Call memory call = calls[i]; + vm.startPrank(sender); + (bool success,) = call.target.call{value: call.value}(call.data); + if (!success) revert TransactionReverted(); + vm.stopPrank(); + } + } +} diff --git a/chains/evm/foundry.toml b/chains/evm/foundry.toml index f23fcf4405..8b7aa2b0e4 100644 --- a/chains/evm/foundry.toml +++ b/chains/evm/foundry.toml @@ -7,6 +7,23 @@ test = 'contracts' out = 'foundry-artifacts' cache_path = 'foundry-cache' libs = ['node_modules'] +auto_detect_remappings = false + +remappings = [ + 'forge-std/=node_modules/@chainlink/contracts/src/v0.8/vendor/forge-std/src/', + '@chainlink/contracts/=node_modules/@chainlink/contracts/', + '@openzeppelin/contracts@4.7.3/=node_modules/@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.7.3/contracts/', + '@openzeppelin/contracts@4.8.3/=node_modules/@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.8.3/contracts/', + '@openzeppelin/contracts@4.9.6/=node_modules/@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v4.9.6/contracts/', + '@openzeppelin/contracts@5.0.2/=node_modules/@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v5.0.2/contracts/', + '@openzeppelin/contracts@5.1.0/=node_modules/@chainlink/contracts/src/v0.8/vendor/openzeppelin-solidity/v5.1.0/contracts/', + '@arbitrum/=node_modules/@arbitrum/', + '@eth-optimism/=node_modules/@eth-optimism/', + '@offchainlabs/=node_modules/@offchainlabs/', + '@scroll-tech/=node_modules/@scroll-tech/', + '@zksync/=node_modules/@zksync/', + 'solady/=node_modules/solady/', +] bytecode_hash = "none" ffi = false diff --git a/chains/evm/remappings.txt b/chains/evm/remappings.txt deleted file mode 100644 index d7e9524dc4..0000000000 --- a/chains/evm/remappings.txt +++ /dev/null @@ -1,2 +0,0 @@ -forge-std/=node_modules/@chainlink/contracts/src/v0.8/vendor/forge-std/src/ -@chainlink/contracts/=node_modules/@chainlink/contracts/