diff --git a/contracts/src/v0.8/ccip/test/helpers/interfaces/IEVM2EVMOffRamp.sol b/contracts/src/v0.8/ccip/test/helpers/interfaces/IEVM2EVMOffRamp.sol new file mode 100644 index 0000000000..6b370fbcee --- /dev/null +++ b/contracts/src/v0.8/ccip/test/helpers/interfaces/IEVM2EVMOffRamp.sol @@ -0,0 +1,17 @@ +pragma solidity ^0.8.0; + +import {ITypeAndVersion} from "../../../../shared/interfaces/ITypeAndVersion.sol"; +import {IAny2EVMOffRamp} from "../../../interfaces/IAny2EVMOffRamp.sol"; +import {Internal} from "../../../libraries/Internal.sol"; + +interface IEVM2EVMOffRamp_1_2 is IAny2EVMOffRamp, ITypeAndVersion { + function executeSingleMessage(Internal.EVM2EVMMessage memory message, bytes[] memory offchainTokenData) external; +} + +interface IEVM2EVMOffRamp_1_5 is IAny2EVMOffRamp, ITypeAndVersion { + function executeSingleMessage( + Internal.EVM2EVMMessage calldata message, + bytes[] calldata offchainTokenData, + uint32[] memory tokenGasOverrides + ) external; +} diff --git a/contracts/src/v0.8/ccip/test/helpers/interfaces/IEVM2EVMOnRamp.sol b/contracts/src/v0.8/ccip/test/helpers/interfaces/IEVM2EVMOnRamp.sol new file mode 100644 index 0000000000..4fa742dae4 --- /dev/null +++ b/contracts/src/v0.8/ccip/test/helpers/interfaces/IEVM2EVMOnRamp.sol @@ -0,0 +1,46 @@ +pragma solidity ^0.8.0; + +import {ITypeAndVersion} from "../../../../shared/interfaces/ITypeAndVersion.sol"; +import {IEVM2AnyOnRamp} from "../../../interfaces/IEVM2AnyOnRamp.sol"; + +interface IEVM2EVMOnRamp_1_2 is IEVM2AnyOnRamp, ITypeAndVersion {} + +interface IEVM2EVMOnRamp_1_5 is IEVM2AnyOnRamp, ITypeAndVersion { + struct TokenTransferFeeConfig { + uint32 minFeeUSDCents; // ──────────╮ Minimum fee to charge per token transfer, multiples of 0.01 USD + uint32 maxFeeUSDCents; // │ Maximum fee to charge per token transfer, multiples of 0.01 USD + uint16 deciBps; // │ Basis points charged on token transfers, multiples of 0.1bps, or 1e-5 + uint32 destGasOverhead; // │ Gas charged to execute the token transfer on the destination chain + // │ Extra data availability bytes that are returned from the source pool and sent + uint32 destBytesOverhead; // │ to the destination pool. Must be >= Pool.CCIP_LOCK_OR_BURN_V1_RET_BYTES + bool aggregateRateLimitEnabled; // │ Whether this transfer token is to be included in Aggregate Rate Limiting + bool isEnabled; // ─────────────────╯ Whether this token has custom transfer fees + } + + struct DynamicConfig { + address router; // ──────────────────────────╮ Router address + uint16 maxNumberOfTokensPerMsg; // │ Maximum number of distinct ERC20 token transferred per message + uint32 destGasOverhead; // │ Gas charged on top of the gasLimit to cover destination chain costs + uint16 destGasPerPayloadByte; // │ Destination chain gas charged for passing each byte of `data` payload to receiver + uint32 destDataAvailabilityOverheadGas; // ──╯ Extra data availability gas charged on top of the message, e.g. for OCR + uint16 destGasPerDataAvailabilityByte; // ───╮ Amount of gas to charge per byte of message data that needs availability + uint16 destDataAvailabilityMultiplierBps; // │ Multiplier for data availability gas, multiples of bps, or 0.0001 + address priceRegistry; // │ Price registry address + uint32 maxDataBytes; // │ Maximum payload data size in bytes + uint32 maxPerMsgGasLimit; // ────────────────╯ Maximum gas limit for messages targeting EVMs + // │ + // The following three properties are defaults, they can be overridden by setting the TokenTransferFeeConfig for a token + uint16 defaultTokenFeeUSDCents; // ──────────╮ Default token fee charged per token transfer + uint32 defaultTokenDestGasOverhead; // │ Default gas charged to execute the token transfer on the destination chain + bool enforceOutOfOrder; // ──────────────────╯ Whether to enforce the allowOutOfOrderExecution extraArg value to be true. + } + + /// @notice Gets the transfer fee config for a given token. + function getTokenTransferFeeConfig( + address token + ) external view returns (TokenTransferFeeConfig memory tokenTransferFeeConfig); + + /// @notice Returns the dynamic onRamp config. + /// @return dynamicConfig the configuration. + function getDynamicConfig() external view returns (DynamicConfig memory dynamicConfig); +} diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol index 5e8fc50f0d..045c94d313 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/ForkBase.t.sol @@ -10,6 +10,8 @@ import {IAny2EVMOffRamp} from "../../../../../interfaces/IAny2EVMOffRamp.sol"; import {IRouter as IRouterBase} from "../../../../../interfaces/IRouter.sol"; import {Client} from "../../../../../libraries/Client.sol"; import {Internal} from "../../../../../libraries/Internal.sol"; +import {IEVM2EVMOffRamp_1_2, IEVM2EVMOffRamp_1_5} from "../../../../helpers/interfaces/IEVM2EVMOffRamp.sol"; +import {IEVM2EVMOnRamp_1_2, IEVM2EVMOnRamp_1_5} from "../../../../helpers/interfaces/IEVM2EVMOnRamp.sol"; import {UpgradeableLockReleaseTokenPool_Sepolia} from "./LegacyTestnetTokenPools/UpgradeableLockReleaseTokenPool_Sepolia.sol"; import {UpgradeableBurnMintTokenPool_ArbSepolia} from "./LegacyTestnetTokenPools/UpgradeableBurnMintTokenPool_ArbSepolia.sol"; @@ -23,18 +25,6 @@ interface IRouter is IRouterClient, IRouterBase { function getOffRamps() external view returns (OffRamp[] memory); } -interface IEVM2EVMOffRamp_1_2 is IAny2EVMOffRamp, ITypeAndVersion { - function executeSingleMessage(Internal.EVM2EVMMessage memory message, bytes[] memory offchainTokenData) external; -} - -interface IEVM2EVMOffRamp_1_5 is IAny2EVMOffRamp, ITypeAndVersion { - function executeSingleMessage( - Internal.EVM2EVMMessage calldata message, - bytes[] calldata offchainTokenData, - uint32[] memory tokenGasOverrides - ) external; -} - struct SourceTokenData { bytes sourcePoolAddress; bytes destTokenAddress; @@ -55,8 +45,8 @@ contract ForkBase is Test { UpgradeableLockReleaseTokenPool_Sepolia tokenPool; IRouter router; IERC20 token; - IEVM2AnyOnRamp EVM2EVMOnRamp1_2; - IEVM2AnyOnRamp EVM2EVMOnRamp1_5; + IEVM2EVMOnRamp_1_2 EVM2EVMOnRamp1_2; + IEVM2EVMOnRamp_1_5 EVM2EVMOnRamp1_5; IEVM2EVMOffRamp_1_2 EVM2EVMOffRamp1_2; IEVM2EVMOffRamp_1_5 EVM2EVMOffRamp1_5; address proxyPool; @@ -68,8 +58,8 @@ contract ForkBase is Test { UpgradeableBurnMintTokenPool_ArbSepolia tokenPool; IRouter router; IERC20 token; - IEVM2AnyOnRamp EVM2EVMOnRamp1_2; - IEVM2AnyOnRamp EVM2EVMOnRamp1_5; + IEVM2EVMOnRamp_1_2 EVM2EVMOnRamp1_2; + IEVM2EVMOnRamp_1_5 EVM2EVMOnRamp1_5; IEVM2EVMOffRamp_1_2 EVM2EVMOffRamp1_2; IEVM2EVMOffRamp_1_5 EVM2EVMOffRamp1_5; address proxyPool; @@ -96,8 +86,8 @@ contract ForkBase is Test { l1.router = IRouter(l1.tokenPool.getRouter()); l2.chainSelector = l1.tokenPool.getSupportedChains()[0]; l1.token = l1.tokenPool.getToken(); - l1.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e); // legacy on ramp - l1.EVM2EVMOnRamp1_5 = IEVM2AnyOnRamp(l1.router.getOnRamp(l2.chainSelector)); + l1.EVM2EVMOnRamp1_2 = IEVM2EVMOnRamp_1_2(0xe4Dd3B16E09c016402585a8aDFdB4A18f772a07e); // legacy on ramp + l1.EVM2EVMOnRamp1_5 = IEVM2EVMOnRamp_1_5(l1.router.getOnRamp(l2.chainSelector)); l1.EVM2EVMOffRamp1_2 = IEVM2EVMOffRamp_1_2(0xF18896AB20a09A29e64fdEbA99FDb8EC328f43b1); l1.EVM2EVMOffRamp1_5 = IEVM2EVMOffRamp_1_5(0xD2f5edfD4561d6E7599F6c6888Bd353cAFd0c55E); vm.prank(alice); @@ -111,8 +101,8 @@ contract ForkBase is Test { l2.router = IRouter(l2.tokenPool.getRouter()); l1.chainSelector = l2.tokenPool.getSupportedChains()[0]; l2.token = l2.tokenPool.getToken(); - l2.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9); // legacy on ramp - l2.EVM2EVMOnRamp1_5 = IEVM2AnyOnRamp(l2.router.getOnRamp(l1.chainSelector)); + l2.EVM2EVMOnRamp1_2 = IEVM2EVMOnRamp_1_2(0x4205E1Ca0202A248A5D42F5975A8FE56F3E302e9); // legacy on ramp + l2.EVM2EVMOnRamp1_5 = IEVM2EVMOnRamp_1_5(l2.router.getOnRamp(l1.chainSelector)); l2.EVM2EVMOffRamp1_2 = IEVM2EVMOffRamp_1_2(0x1c71f141b4630EBE52d6aF4894812960abE207eB); l2.EVM2EVMOffRamp1_5 = IEVM2EVMOffRamp_1_5(0xBed6e9131916d724418C8a6FE810F727302a5c00); vm.prank(alice); @@ -129,8 +119,8 @@ contract ForkBase is Test { assertEq(l1.token.balanceOf(alice), 1000e18); assertEq(ITypeAndVersion(address(l1.router)).typeAndVersion(), "Router 1.2.0"); assertEq(ITypeAndVersion(l1.proxyPool).typeAndVersion(), "LockReleaseTokenPoolAndProxy 1.5.0"); - assertEq(ITypeAndVersion(address(l1.EVM2EVMOnRamp1_2)).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); - assertEq(ITypeAndVersion(address(l1.EVM2EVMOnRamp1_5)).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); + assertEq(l1.EVM2EVMOnRamp1_2.typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(l1.EVM2EVMOnRamp1_5.typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); assertEq(l1.EVM2EVMOffRamp1_2.typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); assertEq(l1.EVM2EVMOffRamp1_5.typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); assertTrue(l1.router.isOffRamp(l2.chainSelector, address(l1.EVM2EVMOffRamp1_2))); @@ -142,8 +132,8 @@ contract ForkBase is Test { assertEq(l2.token.balanceOf(alice), 1000e18); assertEq(ITypeAndVersion(address(l2.router)).typeAndVersion(), "Router 1.2.0"); assertEq(ITypeAndVersion(l2.proxyPool).typeAndVersion(), "BurnMintTokenPoolAndProxy 1.5.0"); - assertEq(ITypeAndVersion(address(l2.EVM2EVMOnRamp1_2)).typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); - assertEq(ITypeAndVersion(address(l2.EVM2EVMOnRamp1_5)).typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); + assertEq(l2.EVM2EVMOnRamp1_2.typeAndVersion(), "EVM2EVMOnRamp 1.2.0"); + assertEq(l2.EVM2EVMOnRamp1_5.typeAndVersion(), "EVM2EVMOnRamp 1.5.0"); assertEq(l2.EVM2EVMOffRamp1_2.typeAndVersion(), "EVM2EVMOffRamp 1.2.0"); assertEq(l2.EVM2EVMOffRamp1_5.typeAndVersion(), "EVM2EVMOffRamp 1.5.0"); assertTrue(l2.router.isOffRamp(l1.chainSelector, address(l2.EVM2EVMOffRamp1_2))); @@ -168,7 +158,7 @@ contract ForkBase is Test { function _messageToEvent( Client.EVM2AnyMessage memory message, - IEVM2AnyOnRamp onRamp, + address onRamp, uint256 feeTokenAmount, address originalSender, bool isL1 @@ -180,10 +170,10 @@ contract ForkBase is Test { } Client.EVMExtraArgsV1 memory extraArgs = abi.decode(args, (Client.EVMExtraArgsV1)); Internal.EVM2EVMMessage memory messageEvent = Internal.EVM2EVMMessage({ - sequenceNumber: onRamp.getExpectedNextSequenceNumber(), + sequenceNumber: IEVM2AnyOnRamp(onRamp).getExpectedNextSequenceNumber(), feeTokenAmount: feeTokenAmount, sender: originalSender, - nonce: onRamp.getSenderNonce(originalSender) + 1, + nonce: IEVM2AnyOnRamp(onRamp).getSenderNonce(originalSender) + 1, gasLimit: extraArgs.gasLimit, strict: false, sourceChainSelector: isL1 ? l1.chainSelector : l2.chainSelector, @@ -202,7 +192,7 @@ contract ForkBase is Test { sourcePoolAddress: abi.encode(isL1 ? l1.proxyPool : l2.proxyPool), destTokenAddress: abi.encode(address(isL1 ? l2.token : l1.token)), extraData: "", - destGasAmount: 90000 + destGasAmount: _getDestGasAmount(onRamp, message.tokenAmounts[i].token) }) ); } @@ -211,6 +201,16 @@ contract ForkBase is Test { return messageEvent; } + function _getDestGasAmount(address onRamp, address token) internal view returns (uint32) { + IEVM2EVMOnRamp_1_5.TokenTransferFeeConfig memory config = IEVM2EVMOnRamp_1_5(onRamp).getTokenTransferFeeConfig( + token + ); + return + config.isEnabled + ? config.destGasOverhead + : IEVM2EVMOnRamp_1_5(onRamp).getDynamicConfig().defaultTokenDestGasOverhead; + } + function _generateMetadataHash(uint64 sourceChainSelector, IEVM2AnyOnRamp onRamp) internal view returns (bytes32) { uint64 destChainSelector = sourceChainSelector == l1.chainSelector ? l2.chainSelector : l1.chainSelector; return @@ -286,7 +286,7 @@ contract ForkPoolBeforeMigration is ForkBase { l1.router = IRouter(l1.tokenPool.getRouter()); l2.chainSelector = l1.tokenPool.getSupportedChains()[0]; l1.token = l1.tokenPool.getToken(); - l1.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(l1.router.getOnRamp(l2.chainSelector)); + l1.EVM2EVMOnRamp1_2 = IEVM2EVMOnRamp_1_2(l1.router.getOnRamp(l2.chainSelector)); l1.EVM2EVMOffRamp1_2 = IEVM2EVMOffRamp_1_2(0xdb92e73d1D630B5B7aC96840c4df0c591c7Ad23E); vm.prank(alice); l1.token.approve(address(l1.router), type(uint256).max); @@ -298,7 +298,7 @@ contract ForkPoolBeforeMigration is ForkBase { l2.router = IRouter(l2.tokenPool.getRouter()); l1.chainSelector = l2.tokenPool.getSupportedChains()[0]; l2.token = l2.tokenPool.getToken(); - l2.EVM2EVMOnRamp1_2 = IEVM2AnyOnRamp(l2.router.getOnRamp(l1.chainSelector)); + l2.EVM2EVMOnRamp1_2 = IEVM2EVMOnRamp_1_2(l2.router.getOnRamp(l1.chainSelector)); l2.EVM2EVMOffRamp1_2 = IEVM2EVMOffRamp_1_2(0xFf5e1c597c5DFfC896Ab8c7b9d876D513518c4b7); vm.prank(alice); l2.token.approve(address(l2.router), type(uint256).max); @@ -396,7 +396,7 @@ contract ForkPoolBeforeMigration is ForkBase { uint256 feeTokenAmount = l2.router.getFee(l1.chainSelector, message); Internal.EVM2EVMMessage memory eventArg = _messageToEvent( message, - l2.EVM2EVMOnRamp1_2, + address(l2.EVM2EVMOnRamp1_2), feeTokenAmount, alice, false @@ -423,7 +423,7 @@ contract ForkPoolBeforeMigration is ForkBase { uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); Internal.EVM2EVMMessage memory eventArg = _messageToEvent( message, - l1.EVM2EVMOnRamp1_2, + address(l1.EVM2EVMOnRamp1_2), feeTokenAmount, alice, true diff --git a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol index 1f302227ae..a649167773 100644 --- a/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol +++ b/contracts/src/v0.8/ccip/test/pools/GHO/fork/GhoTokenPoolMigrate1_4To1_5/TokenPoolsUpgrade.t.sol @@ -43,7 +43,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { // router uses 1_5 onRamp assertEq(l1.router.getOnRamp(l2.chainSelector), address(l1.EVM2EVMOnRamp1_5)); vm.expectEmit(); - emit CCIPSendRequested(_messageToEvent(message, l1.EVM2EVMOnRamp1_5, feeTokenAmount, alice, true)); + emit CCIPSendRequested(_messageToEvent(message, address(l1.EVM2EVMOnRamp1_5), feeTokenAmount, alice, true)); vm.prank(alice); l1.router.ccipSend{value: feeTokenAmount}(l2.chainSelector, message); } @@ -57,7 +57,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { // router uses 1_5 onRamp assertEq(l2.router.getOnRamp(l1.chainSelector), address(l2.EVM2EVMOnRamp1_5)); vm.expectEmit(); - emit CCIPSendRequested(_messageToEvent(message, l2.EVM2EVMOnRamp1_5, feeTokenAmount, alice, false)); + emit CCIPSendRequested(_messageToEvent(message, address(l2.EVM2EVMOnRamp1_5), feeTokenAmount, alice, false)); vm.prank(alice); l2.router.ccipSend{value: feeTokenAmount}(l1.chainSelector, message); } @@ -91,7 +91,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { uint256 feeTokenAmount = l2.router.getFee(l1.chainSelector, message); Internal.EVM2EVMMessage memory eventArg = _messageToEvent( message, - l2.EVM2EVMOnRamp1_5, + address(l2.EVM2EVMOnRamp1_5), feeTokenAmount, alice, false @@ -118,7 +118,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); Internal.EVM2EVMMessage memory eventArg = _messageToEvent( message, - l1.EVM2EVMOnRamp1_5, + address(l1.EVM2EVMOnRamp1_5), feeTokenAmount, alice, true @@ -150,7 +150,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { uint256 feeTokenAmount = l2.router.getFee(l1.chainSelector, message); Internal.EVM2EVMMessage memory eventArg = _messageToEvent( message, - l2.EVM2EVMOnRamp1_5, + address(l2.EVM2EVMOnRamp1_5), feeTokenAmount, alice, false @@ -179,7 +179,7 @@ contract ForkPoolUpgradeAfterMigration is ForkBase { uint256 feeTokenAmount = l1.router.getFee(l2.chainSelector, message); Internal.EVM2EVMMessage memory eventArg = _messageToEvent( message, - l1.EVM2EVMOnRamp1_5, + address(l1.EVM2EVMOnRamp1_5), feeTokenAmount, alice, true