Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 96 additions & 97 deletions chains/evm/.gas-snapshot

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions chains/evm/contracts/FeeQuoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ contract FeeQuoter is AuthorizedCallers, IFeeQuoter, ILegacyFeeQuoter, ITypeAndV
bool isEnabled; // ────────────────────╮ Whether this destination chain is enabled.
uint32 maxDataBytes; // │ Maximum data payload size in bytes.
uint32 maxPerMsgGasLimit; // │ Maximum gas limit.
uint32 destGasOverhead; // │ Gas charged on top of the gasLimit to cover destination chain costs.
uint8 destGasPerPayloadByteBase; // │ Default dest-chain gas charged each byte of `data` payload.
uint32 destGasOverhead; // │ LEGACY: Gas charged on top of the gasLimit to cover destination chain costs.
uint8 destGasPerPayloadByteBase; // │ Default dest-chain gas charged each byte of `data` payload, accounting for DA costs.
bytes4 chainFamilySelector; // │ Selector that identifies the destination chain's family. Used to determine the correct validations to perform for the dest chain.
// The following two 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 a token transfer on the destination chain.
uint32 defaultTxGasLimit; // │ Default gas limit for a tx.
uint16 networkFeeUSDCents; // │ Flat network fee to charge for messages, multiples of 0.01 USD.
uint16 networkFeeUSDCents; // │ LEGACY: Flat network fee to charge for messages, multiples of 0.01 USD.
uint8 linkFeeMultiplierPercent; // ────╯ Percentage multiplier to apply when fee is paid in LINK. 90 = 10% discount.
}

Expand Down
20 changes: 13 additions & 7 deletions chains/evm/contracts/ccvs/components/BaseVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ abstract contract BaseVerifier is ICrossChainVerifierV1, ITypeAndVersion {
event RemoteChainConfigSet(uint64 indexed remoteChainSelector, address router, bool allowlistEnabled);
event AllowListSendersAdded(uint64 indexed destChainSelector, address[] senders);
event AllowListSendersRemoved(uint64 indexed destChainSelector, address[] senders);
event AllowListStateChanged(uint64 indexed destChainSelector, bool allowlistEnabled);
event StorageLocationsUpdated(string[] oldLocations, string[] newLocations);

struct RemoteChainConfig {
Expand Down Expand Up @@ -201,12 +202,23 @@ abstract contract BaseVerifier is ICrossChainVerifierV1, ITypeAndVersion {
AllowlistConfigArgs memory allowlistConfigArgs = allowlistConfigArgsItems[i];

RemoteChainConfig storage remoteChainConfig = s_remoteChainConfigs[allowlistConfigArgs.destChainSelector];
remoteChainConfig.allowlistEnabled = allowlistConfigArgs.allowlistEnabled;

if (remoteChainConfig.allowlistEnabled != allowlistConfigArgs.allowlistEnabled) {
remoteChainConfig.allowlistEnabled = allowlistConfigArgs.allowlistEnabled;

emit AllowListStateChanged(allowlistConfigArgs.destChainSelector, allowlistConfigArgs.allowlistEnabled);
}

for (uint256 j = 0; j < allowlistConfigArgs.removedAllowlistedSenders.length; ++j) {
remoteChainConfig.allowedSendersList.remove(allowlistConfigArgs.removedAllowlistedSenders[j]);
}

if (allowlistConfigArgs.removedAllowlistedSenders.length > 0) {
emit AllowListSendersRemoved(
allowlistConfigArgs.destChainSelector, allowlistConfigArgs.removedAllowlistedSenders
);
}

if (allowlistConfigArgs.addedAllowlistedSenders.length > 0) {
if (allowlistConfigArgs.allowlistEnabled) {
for (uint256 j = 0; j < allowlistConfigArgs.addedAllowlistedSenders.length; ++j) {
Expand All @@ -222,12 +234,6 @@ abstract contract BaseVerifier is ICrossChainVerifierV1, ITypeAndVersion {
revert InvalidAllowListRequest(allowlistConfigArgs.destChainSelector);
}
}

if (allowlistConfigArgs.removedAllowlistedSenders.length > 0) {
emit AllowListSendersRemoved(
allowlistConfigArgs.destChainSelector, allowlistConfigArgs.removedAllowlistedSenders
);
}
}
}

Expand Down
3 changes: 2 additions & 1 deletion chains/evm/contracts/onRamp/OnRamp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,8 @@ contract OnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, Ownable2StepMsgSender
destBytesOverhead: uint32(
MessageV1Codec.MESSAGE_V1_EVM_SOURCE_BASE_SIZE + dataLength + extraArgs.executorArgs.length
+ (MessageV1Codec.MESSAGE_V1_REMOTE_CHAIN_ADDRESSES * remoteChainAddressLengthBytes)
+ (numberOfTokens * (MessageV1Codec.TOKEN_TRANSFER_V1_EVM_SOURCE_BASE_SIZE + remoteChainAddressLengthBytes))
+ (numberOfTokens
* (MessageV1Codec.TOKEN_TRANSFER_V1_EVM_SOURCE_BASE_SIZE + remoteChainAddressLengthBytes * 2))
),
// Only bill a flat fee when automated execution is enabled.
feeTokenAmount: extraArgs.executor == Client.NO_EXECUTION_ADDRESS
Expand Down
38 changes: 23 additions & 15 deletions chains/evm/contracts/pools/TokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ abstract contract TokenPool is IPoolV1V2, Ownable2StepMsgSender {

emit LockedOrBurned({
remoteChainSelector: lockOrBurnIn.remoteChainSelector,
token: address(i_token),
token: lockOrBurnIn.localToken,
sender: msg.sender,
amount: destTokenAmount
});
Expand All @@ -318,7 +318,7 @@ abstract contract TokenPool is IPoolV1V2, Ownable2StepMsgSender {

emit LockedOrBurned({
remoteChainSelector: lockOrBurnIn.remoteChainSelector,
token: address(i_token),
token: lockOrBurnIn.localToken,
sender: msg.sender,
amount: lockOrBurnIn.amount
});
Expand Down Expand Up @@ -360,7 +360,7 @@ abstract contract TokenPool is IPoolV1V2, Ownable2StepMsgSender {

emit ReleasedOrMinted({
remoteChainSelector: releaseOrMintIn.remoteChainSelector,
token: address(i_token),
token: releaseOrMintIn.localToken,
sender: msg.sender,
recipient: releaseOrMintIn.receiver,
amount: localAmount
Expand Down Expand Up @@ -429,9 +429,11 @@ abstract contract TokenPool is IPoolV1V2, Ownable2StepMsgSender {
if (blockConfirmationRequested < minBlockConfirmationConfigured) {
revert InvalidMinBlockConfirmation(blockConfirmationRequested, minBlockConfirmationConfigured);
}
_consumeCustomBlockConfirmationOutboundRateLimit(lockOrBurnIn.remoteChainSelector, amount);
_consumeCustomBlockConfirmationOutboundRateLimit(
lockOrBurnIn.localToken, lockOrBurnIn.remoteChainSelector, amount
);
} else {
_consumeOutboundRateLimit(lockOrBurnIn.remoteChainSelector, amount);
_consumeOutboundRateLimit(lockOrBurnIn.localToken, lockOrBurnIn.remoteChainSelector, amount);
}

_preflightCheck(lockOrBurnIn, blockConfirmationRequested, tokenArgs, amount);
Expand Down Expand Up @@ -483,9 +485,11 @@ abstract contract TokenPool is IPoolV1V2, Ownable2StepMsgSender {
revert InvalidSourcePoolAddress(releaseOrMintIn.sourcePoolAddress);
}
if (blockConfirmationRequested != WAIT_FOR_FINALITY) {
_consumeCustomBlockConfirmationInboundRateLimit(releaseOrMintIn.remoteChainSelector, localAmount);
_consumeCustomBlockConfirmationInboundRateLimit(
releaseOrMintIn.localToken, releaseOrMintIn.remoteChainSelector, localAmount
);
} else {
_consumeInboundRateLimit(releaseOrMintIn.remoteChainSelector, localAmount);
_consumeInboundRateLimit(releaseOrMintIn.localToken, releaseOrMintIn.remoteChainSelector, localAmount);
}

_postflightCheck(releaseOrMintIn, localAmount, blockConfirmationRequested);
Expand Down Expand Up @@ -772,51 +776,55 @@ abstract contract TokenPool is IPoolV1V2, Ownable2StepMsgSender {
/// @param remoteChainSelector The remote chain selector.
/// @param amount The amount of tokens consumed.
function _consumeOutboundRateLimit(
address token,
uint64 remoteChainSelector,
uint256 amount
) internal virtual {
s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._consume(amount, address(i_token));
s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._consume(amount, token);

emit OutboundRateLimitConsumed({token: address(i_token), remoteChainSelector: remoteChainSelector, amount: amount});
emit OutboundRateLimitConsumed({token: token, remoteChainSelector: remoteChainSelector, amount: amount});
}

/// @notice Consumes inbound rate limiting capacity in this pool.
/// @param remoteChainSelector The remote chain selector.
/// @param amount The amount of tokens consumed.
function _consumeInboundRateLimit(
address token,
uint64 remoteChainSelector,
uint256 amount
) internal virtual {
s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._consume(amount, address(i_token));
s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._consume(amount, token);

emit InboundRateLimitConsumed({token: address(i_token), remoteChainSelector: remoteChainSelector, amount: amount});
emit InboundRateLimitConsumed({token: token, remoteChainSelector: remoteChainSelector, amount: amount});
}

/// @notice Consumes custom block confirmation outbound rate limiting capacity in this pool.
/// @param remoteChainSelector The remote chain selector.
/// @param amount The amount of tokens consumed.
function _consumeCustomBlockConfirmationOutboundRateLimit(
address token,
uint64 remoteChainSelector,
uint256 amount
) internal virtual {
s_outboundRateLimiterConfig[remoteChainSelector]._consume(amount, address(i_token));
s_outboundRateLimiterConfig[remoteChainSelector]._consume(amount, token);

emit CustomBlockConfirmationOutboundRateLimitConsumed({
token: address(i_token), remoteChainSelector: remoteChainSelector, amount: amount
token: token, remoteChainSelector: remoteChainSelector, amount: amount
});
}

/// @notice Consumes custom block confirmation inbound rate limiting capacity in this pool.
/// @param remoteChainSelector The remote chain selector.
/// @param amount The amount of tokens consumed.
function _consumeCustomBlockConfirmationInboundRateLimit(
address token,
uint64 remoteChainSelector,
uint256 amount
) internal virtual {
s_inboundRateLimiterConfig[remoteChainSelector]._consume(amount, address(i_token));
s_inboundRateLimiterConfig[remoteChainSelector]._consume(amount, token);

emit CustomBlockConfirmationInboundRateLimitConsumed({
token: address(i_token), remoteChainSelector: remoteChainSelector, amount: amount
token: token, remoteChainSelector: remoteChainSelector, amount: amount
});
}

Expand Down
109 changes: 68 additions & 41 deletions chains/evm/contracts/pools/USDC/USDCTokenPoolProxy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,34 @@ contract USDCTokenPoolProxy is Ownable2StepMsgSender, IPoolV1V2, ITypeAndVersion
function lockOrBurn(
Pool.LockOrBurnInV1 calldata lockOrBurnIn
) public virtual override returns (Pool.LockOrBurnOutV1 memory) {
(Pool.LockOrBurnOutV1 memory lockOrBurnOut,) = lockOrBurn(lockOrBurnIn, WAIT_FOR_FINALITY, "");
return lockOrBurnOut;
// Since this contract does not inherit from the TokenPool contract, it must manually validate the caller as an onRamp.
if (i_router.getOnRamp(lockOrBurnIn.remoteChainSelector) != msg.sender) {
revert CallerIsNotARampOnRouter(msg.sender);
}
LockOrBurnMechanism mechanism = s_lockOrBurnMechanism[lockOrBurnIn.remoteChainSelector];

// The child pool which will perform the lock/burn operation.
address pool;

// For a IPoolV2 call, only CCTP v1/v2 and Lock/Release are supported.
if (mechanism == LockOrBurnMechanism.CCTP_V2) {
pool = s_cctpV2Pool;
} else if (mechanism == LockOrBurnMechanism.CCTP_V1) {
pool = s_cctpV1Pool;
} else if (mechanism == LockOrBurnMechanism.LOCK_RELEASE) {
pool = s_siloedLockReleasePool;
} else {
revert InvalidLockOrBurnMechanism(mechanism);
}

if (pool == address(0)) {
revert NoLockOrBurnMechanismSet(lockOrBurnIn.remoteChainSelector);
}

// Transfer the tokens to the correct address, as this contract is only a proxy and will not perform the lock/burn itself.
i_token.safeTransfer(pool, lockOrBurnIn.amount);

return IPoolV1(pool).lockOrBurn(lockOrBurnIn);
}

/// @inheritdoc IPoolV2
Expand All @@ -149,10 +175,12 @@ contract USDCTokenPoolProxy is Ownable2StepMsgSender, IPoolV1V2, ITypeAndVersion
revert InvalidLockOrBurnMechanism(mechanism);
}

address pool;

// For a IPoolV2 call, only CCTP with CCV and Lock/Release are supported.
if (mechanism == LockOrBurnMechanism.CCV) {
// CCV-compatible lockOrBurn path is completed within this if statement to avoid redundant checks.
address ccvPool = s_cctpV2PoolWithCCV;
if (ccvPool == address(0)) {
pool = s_cctpV2PoolWithCCV;
if (pool == address(0)) {
revert NoLockOrBurnMechanismSet(lockOrBurnIn.remoteChainSelector);
}
// If using the CCTP verifier, transfer funds to the verifier instead of the pool.
Expand All @@ -162,48 +190,24 @@ contract USDCTokenPoolProxy is Ownable2StepMsgSender, IPoolV1V2, ITypeAndVersion
revert ChainNotSupportedByVerifier(lockOrBurnIn.remoteChainSelector);
}
i_token.safeTransfer(verifierImpl, lockOrBurnIn.amount);

return IPoolV2(ccvPool).lockOrBurn(lockOrBurnIn, blockConfirmationRequested, tokenArgs);
}

// The child pool which will perform the lock/burn operation.
address childPool;

if (mechanism == LockOrBurnMechanism.CCTP_V2) {
childPool = s_cctpV2Pool;
} else if (mechanism == LockOrBurnMechanism.CCTP_V1) {
childPool = s_cctpV1Pool;
} else if (mechanism == LockOrBurnMechanism.LOCK_RELEASE) {
childPool = s_siloedLockReleasePool;
}

// If the destination pool is the zero address, then no mechanism has been configured for the outgoing tokens
// and thus the destination chain is not supported and should revert.
if (childPool == address(0)) {
revert NoLockOrBurnMechanismSet(lockOrBurnIn.remoteChainSelector);
pool = s_siloedLockReleasePool;
if (pool == address(0)) {
revert NoLockOrBurnMechanismSet(lockOrBurnIn.remoteChainSelector);
}
i_token.safeTransfer(pool, lockOrBurnIn.amount);
} else {
revert InvalidLockOrBurnMechanism(mechanism);
}

// Transfer the tokens to the correct address, as this contract is only a proxy and will not perform the lock/burn itself.
i_token.safeTransfer(childPool, lockOrBurnIn.amount);

return (IPoolV1(childPool).lockOrBurn(lockOrBurnIn), lockOrBurnIn.amount);
return IPoolV2(pool).lockOrBurn(lockOrBurnIn, blockConfirmationRequested, tokenArgs);
}

/// @inheritdoc IPoolV1
/// @param releaseOrMintIn Encoded data fields for the processing of tokens on the destination chain.
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
) public virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
return releaseOrMint(releaseOrMintIn, WAIT_FOR_FINALITY);
}

/// @inheritdoc IPoolV2
/// @param releaseOrMintIn Encoded data fields for the processing of tokens on the destination chain.
/// @param blockConfirmationRequested Requested block confirmation.
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn,
uint16 blockConfirmationRequested
) public virtual returns (Pool.ReleaseOrMintOutV1 memory) {
// Since this proxy does not inherit from the TokenPool contract, it must manually validate the caller as an offRamp.
if (!i_router.isOffRamp(releaseOrMintIn.remoteChainSelector, msg.sender)) {
revert CallerIsNotARampOnRouter(msg.sender);
Expand All @@ -212,7 +216,6 @@ contract USDCTokenPoolProxy is Ownable2StepMsgSender, IPoolV1V2, ITypeAndVersion
// The first 4 bytes of source pool data are the version which can be extracted directly and cast into a uint32.
bytes4 version = bytes4(releaseOrMintIn.sourcePoolData[:4]);

// If the source pool data is the lock release flag, use the lock release pool set for the remote chain selector.
if (version == USDCSourcePoolDataCodec.LOCK_RELEASE_FLAG) {
return IPoolV1(s_siloedLockReleasePool).releaseOrMint(releaseOrMintIn);
}
Expand All @@ -222,9 +225,6 @@ contract USDCTokenPoolProxy is Ownable2StepMsgSender, IPoolV1V2, ITypeAndVersion
if (version == USDCSourcePoolDataCodec.CCTP_VERSION_2_TAG) {
return IPoolV1(s_cctpV2Pool).releaseOrMint(releaseOrMintIn);
}
if (version == USDCSourcePoolDataCodec.CCTP_VERSION_2_CCV_TAG) {
return IPoolV2(s_cctpV2PoolWithCCV).releaseOrMint(releaseOrMintIn, blockConfirmationRequested);
}

// In previous versions of the USDC Token Pool, the sourcePoolData only contained abi encoded (uint64, uint32).
// This means that a message originating from a previous version of the pool will have a sourcePoolData that is 64
Expand All @@ -241,6 +241,32 @@ contract USDCTokenPoolProxy is Ownable2StepMsgSender, IPoolV1V2, ITypeAndVersion
revert InvalidMessageVersion(version);
}

/// @inheritdoc IPoolV2
/// @param releaseOrMintIn Encoded data fields for the processing of tokens on the destination chain.
/// @param blockConfirmationRequested Requested block confirmation.
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn,
uint16 blockConfirmationRequested
) public virtual returns (Pool.ReleaseOrMintOutV1 memory) {
// Since this proxy does not inherit from the TokenPool contract, it must manually validate the caller as an offRamp.
if (!i_router.isOffRamp(releaseOrMintIn.remoteChainSelector, msg.sender)) {
revert CallerIsNotARampOnRouter(msg.sender);
}

// The first 4 bytes of source pool data are the version which can be extracted directly and cast into a uint32.
bytes4 version = bytes4(releaseOrMintIn.sourcePoolData[:4]);

// If the source pool data is the lock release flag, use the lock release pool set for the remote chain selector.
if (version == USDCSourcePoolDataCodec.LOCK_RELEASE_FLAG) {
return IPoolV2(s_siloedLockReleasePool).releaseOrMint(releaseOrMintIn, blockConfirmationRequested);
}
if (version == USDCSourcePoolDataCodec.CCTP_VERSION_2_CCV_TAG) {
return IPoolV2(s_cctpV2PoolWithCCV).releaseOrMint(releaseOrMintIn, blockConfirmationRequested);
}

revert InvalidMessageVersion(version);
}

function updatePoolAddresses(
PoolAddresses calldata pools
) external onlyOwner {
Expand Down Expand Up @@ -271,6 +297,7 @@ contract USDCTokenPoolProxy is Ownable2StepMsgSender, IPoolV1V2, ITypeAndVersion
if (
pools.siloedLockReleasePool != address(0)
&& !pools.siloedLockReleasePool.supportsInterface(type(IPoolV1).interfaceId)
&& !pools.siloedLockReleasePool.supportsInterface(type(IPoolV2).interfaceId)
) {
revert TokenPoolUnsupported(pools.siloedLockReleasePool);
}
Expand Down
Loading
Loading