Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8117ac5
remove fast transfer pool
0xsuryansh Oct 7, 2025
8ba3bcf
Merge pools
0xsuryansh Oct 7, 2025
c0a1cba
merge tests
0xsuryansh Oct 7, 2025
85a81ed
remove FTF pool BnM impl
0xsuryansh Oct 7, 2025
bc49680
merge test
0xsuryansh Oct 7, 2025
d0300dd
fix remaining
0xsuryansh Oct 7, 2025
e919508
remove ftf pool from wrapper generation
0xsuryansh Oct 7, 2025
ec91525
update wrappers
0xsuryansh Oct 7, 2025
e6a45f5
comments fix
0xsuryansh Oct 7, 2025
ed51697
remove fast transfer wrapper
0xsuryansh Oct 7, 2025
c16168b
coverage fix + remove ftf gen
0xsuryansh Oct 7, 2025
59334ce
Merge branch 'develop' into pool-v1-v2-merge
0xsuryansh Oct 7, 2025
bbde648
snap + cov
0xsuryansh Oct 7, 2025
a444ba9
rename to custom
0xsuryansh Oct 8, 2025
d0c547b
rename in test
0xsuryansh Oct 8, 2025
9ea376c
update wrappers
0xsuryansh Oct 8, 2025
5139868
Merge branch 'develop' into pool-v1-v2-merge
0xsuryansh Oct 8, 2025
08caa35
merge fix
0xsuryansh Oct 8, 2025
b4273a8
Merge branch 'develop' into fast-custom-finality-rename
0xsuryansh Oct 8, 2025
9b41b35
merge fix
0xsuryansh Oct 8, 2025
121ecda
Merge branch 'pool-v1-v2-merge' into fast-custom-finality-rename
0xsuryansh Oct 8, 2025
45497da
merge fix
0xsuryansh Oct 8, 2025
0781acf
Update TokenTransferFeeConfig
0xsuryansh Oct 8, 2025
2da7584
use lib function _assertNoDuplicates
0xsuryansh Oct 8, 2025
9a91b2e
comment fixes
0xsuryansh Oct 8, 2025
c98d1d9
wrappers update
0xsuryansh Oct 8, 2025
383b083
use WAIT_FOR_FINALITY
0xsuryansh Oct 8, 2025
d27d058
add + fix test
0xsuryansh Oct 8, 2025
8ec58b8
fix test name
0xsuryansh Oct 8, 2025
b6ef157
Merge branch 'pool-v1-v2-merge' into fast-custom-finality-rename
0xsuryansh Oct 8, 2025
841d019
fix test name
0xsuryansh Oct 8, 2025
89094ff
Merge branch 'develop' into fast-custom-finality-rename
0xsuryansh Oct 8, 2025
4ac5e29
fix minor comments + wrappers
0xsuryansh Oct 8, 2025
38f1e69
remove maxTransferAmount + minor comment fixes
0xsuryansh Oct 8, 2025
49ed19f
wrappers update
0xsuryansh Oct 8, 2025
5a58413
Merge branch 'develop' into fast-custom-finality-rename
0xsuryansh Oct 8, 2025
604cb2f
comment fix + lint fix + merge fix
0xsuryansh Oct 8, 2025
de95f16
split legacy and new lockOrBurn
0xsuryansh Oct 9, 2025
f48a40e
add placeholder test
0xsuryansh Oct 9, 2025
bb83a65
update wrappers + snap
0xsuryansh Oct 9, 2025
4a80da4
Merge branch 'lockOrBurn-split' into new-token-pool-billing
0xsuryansh Oct 9, 2025
d4fdbb5
merge fix + event rename
0xsuryansh Oct 9, 2025
2cb4a0d
minor fixes
0xsuryansh Oct 9, 2025
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
200 changes: 101 additions & 99 deletions chains/evm/.gas-snapshot

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions chains/evm/contracts/interfaces/IPoolV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import {Pool} from "../libraries/Pool.sol";
/// Each pool type handles a different child token model e.g. lock/release, mint/burn.
interface IPoolV2 is IPoolV1 {
struct TokenTransferFeeConfig {
uint32 destGasOverhead; // ──╮ Gas charged to execute the token transfer on the destination chain.
uint32 destBytesOverhead; // │ Data availability bytes.
uint32 feeUSDCents; // │ Fee to charge per token transfer, multiples of 0.01 USD.
bool isEnabled; // ──────────╯ Whether this token has custom transfer fees.
uint32 destGasOverhead; // ──────────────╮ Gas charged to execute the token transfer on the destination chain.
uint32 destBytesOverhead; // │ Data availability bytes.
uint32 feeUSDCents; // │ Fee to charge per token transfer, multiples of 0.01 USD.
uint16 customFinalityTransferFeeBps; // │ Fee in basis points for custom finality transfers [0-10_000].
uint16 defaultFinalityTransferFeeBps; // │ Fee in basis points for default finality transfers [0-10_000].
bool isEnabled; // ──────────────────────╯ Whether this token has custom transfer fees.
}

/// @notice Lock tokens into the pool or burn the tokens.
Expand Down
104 changes: 53 additions & 51 deletions chains/evm/contracts/pools/TokenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,8 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {

error InvalidDestBytesOverhead(uint32 destBytesOverhead);
error InvalidFinality(uint16 requested, uint16 finalityThreshold);
error AmountExceedsMaxPerRequest(uint256 requested, uint256 maximum);
error TokenTransferFeeConfigNotEnabled(uint64 destChainSelector);
error InvalidFastTransferFeeBps();
error InvalidTransferFeeBps(uint256 bps);
error InvalidFinalityConfig();
error CallerIsNotARampOnRouter(address caller);
error ZeroAddressInvalid();
Expand Down Expand Up @@ -92,13 +91,15 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
event OutboundRateLimitConsumed(uint64 indexed remoteChainSelector, address token, uint256 amount);
event InboundRateLimitConsumed(uint64 indexed remoteChainSelector, address token, uint256 amount);
event CCVConfigUpdated(uint64 indexed remoteChainSelector, address[] outboundCCVs, address[] inboundCCVs);
event FinalityConfigUpdated(uint16 finalityConfig, uint16 fastTransferFeeBps, uint256 maxAmountPerRequest);
event FinalityThresholdUpdated(uint16 finalityConfig);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
event FinalityThresholdUpdated(uint16 finalityConfig);
event FinalityThresholdUpdated(uint16 finalityThreshold);

Also, useful to emit the previous one?

event TokenTransferFeeConfigUpdated(uint64 indexed destChainSelector, TokenTransferFeeConfig tokenTransferFeeConfig);
event TokenTransferFeeConfigDeleted(uint64 indexed destChainSelector);
/// @notice Emitted when pool fees are withdrawn.
event PoolFeeWithdrawn(address indexed recipient, uint256 amount);
event FastTransferOutboundRateLimitConsumed(uint64 indexed remoteChainSelector, address token, uint256 amount);
event FastTransferInboundRateLimitConsumed(uint64 indexed remoteChainSelector, address token, uint256 amount);
event CustomFinalityOutboundRateLimitConsumed(uint64 indexed remoteChainSelector, address token, uint256 amount);
event CustomFinalityTransferInboundRateLimitConsumed(
uint64 indexed remoteChainSelector, address token, uint256 amount
);

struct ChainUpdate {
uint64 remoteChainSelector; // Remote chain selector
Expand All @@ -115,17 +116,15 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
EnumerableSet.Bytes32Set remotePools; // Set of remote pool hashes, ABI encoded in the case of a remote EVM chain.
}

struct FastFinalityConfig {
uint16 finalityThreshold; // ──╮ Minimum block depth on the source chain that token issuers consider sufficiently secure.
// | 0 means the default finality.
uint16 fastTransferFeeBps; // ─╯ Fee in basis points for fast transfers [0-10_000].
uint256 maxAmountPerRequest; // Maximum amount allowed per transfer request.
// Separate buckets isolate fast-finality limits so these transfers cannot deplete the primary pool rate limits.
struct CustomFinalityConfig {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could just have finalityThreshold as a storage variable now.

// 0 means the default finality.
uint16 finalityThreshold; // Minimum block depth on the source chain that token issuers consider sufficiently secure
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't seen this comment pattern before, should we just keep everything in-line?

Suggested change
uint16 finalityThreshold; // Minimum block depth on the source chain that token issuers consider sufficiently secure
uint16 finalityThreshold; // Minimum block depth on the source chain that token issuers consider sufficiently secure (0 means default finality)

// Separate buckets provide isolated rate limits for custom-finality transfers, as their risk profiles differ from default transfers.
mapping(uint64 remoteChainSelector => RateLimiter.TokenBucket tokenBucketOutbound) outboundRateLimiterConfig;
mapping(uint64 remoteChainSelector => RateLimiter.TokenBucket tokenBucketInbound) inboundRateLimiterConfig;
}

struct FastFinalityRateLimitConfigArgs {
struct CustomFinalityRateLimitConfigArgs {
uint64 remoteChainSelector; // Remote chain selector.
RateLimiter.Config outboundRateLimiterConfig; // Outbound rate limiter configuration.
RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limiter configuration.
Expand All @@ -148,8 +147,9 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
TokenTransferFeeConfig tokenTransferFeeConfig; // Token transfer fee configuration.
}

/// @notice The division factor for basis points (BPS). This also represents the maximum BPS fee for fast transfer.
/// @notice The division factor for bps. This also represents the maximum bps fee.
uint256 internal constant BPS_DIVIDER = 10_000;
/// @dev Constant representing the default finality.
uint16 internal constant WAIT_FOR_FINALITY = 0;
/// @dev The bridgeable token that is managed by this pool. Pools could support multiple tokens at the same time if
/// required, but this implementation only supports one token.
Expand Down Expand Up @@ -177,8 +177,8 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
/// @notice The address of the rate limiter admin.
/// @dev Can be address(0) if none is configured.
address internal s_rateLimitAdmin;
// Tracks fast-finality parameters and per-lane rate limit buckets for fast transfers.
FastFinalityConfig internal s_finalityConfig;
// Tracks custom-finality parameters and per-lane rate limit buckets.
CustomFinalityConfig internal s_finalityConfig;
// Stores verifier (CCV) requirements keyed by remote chain selector.
mapping(uint64 remoteChainSelector => CCVConfig ccvConfig) internal s_verifierConfig;
// Optional token-transfer fee overrides keyed by destination chain selector.
Expand Down Expand Up @@ -289,12 +289,25 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
}

/// @inheritdoc IPoolV1
/// @dev calls IPoolV2.lockOrBurn with finality 0 and empty tokenArgs.
/// @dev The _validateLockOrBurn check is an essential security check.
/// @dev _applyFee is not called in this legacy method, so the full amount is locked or burned.
function lockOrBurn(
Pool.LockOrBurnInV1 calldata lockOrBurnIn
) public virtual returns (Pool.LockOrBurnOutV1 memory lockOrBurnOutV1) {
(lockOrBurnOutV1,) = lockOrBurn(lockOrBurnIn, WAIT_FOR_FINALITY, "");
return lockOrBurnOutV1;
_validateLockOrBurn(lockOrBurnIn, WAIT_FOR_FINALITY);
_lockOrBurn(lockOrBurnIn.amount);

emit LockedOrBurned({
remoteChainSelector: lockOrBurnIn.remoteChainSelector,
token: address(i_token),
sender: msg.sender,
amount: lockOrBurnIn.amount
});

return Pool.LockOrBurnOutV1({
destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector),
destPoolData: _encodeLocalDecimals()
});
}

/// @notice Contains the specific lock or burn token logic for a pool.
Expand Down Expand Up @@ -355,7 +368,7 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
/// - RMN curse status
/// - allowlist status
/// - if the sender is a valid onRamp
/// - rate limiting for either normal or fast-transfer lanes.
/// - rate limiting for either default or custom-finality transfer messages.
/// @param lockOrBurnIn The input to validate.
/// @param finality The finality depth requested by the message. A value of zero is used for default finality.
/// @dev This function should always be called before executing a lock or burn. Not doing so would allow
Expand All @@ -366,20 +379,17 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
_checkAllowList(lockOrBurnIn.originalSender);

_onlyOnRamp(lockOrBurnIn.remoteChainSelector);
FastFinalityConfig storage finalityConfig = s_finalityConfig;
CustomFinalityConfig storage finalityConfig = s_finalityConfig;
uint256 amount = lockOrBurnIn.amount;
if (finality != WAIT_FOR_FINALITY && finalityConfig.finalityThreshold != WAIT_FOR_FINALITY) {
if (finality < finalityConfig.finalityThreshold) {
revert InvalidFinality(finality, finalityConfig.finalityThreshold);
}
if (amount > finalityConfig.maxAmountPerRequest) {
revert AmountExceedsMaxPerRequest(amount, finalityConfig.maxAmountPerRequest);
}

finalityConfig.outboundRateLimiterConfig[lockOrBurnIn.remoteChainSelector]._consume(
amount, lockOrBurnIn.localToken
);
emit FastTransferOutboundRateLimitConsumed(lockOrBurnIn.remoteChainSelector, lockOrBurnIn.localToken, amount);
emit CustomFinalityOutboundRateLimitConsumed(lockOrBurnIn.remoteChainSelector, lockOrBurnIn.localToken, amount);
} else {
_consumeOutboundRateLimit(lockOrBurnIn.remoteChainSelector, amount);
}
Expand All @@ -390,7 +400,7 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
/// - RMN curse status
/// - if the sender is a valid offRamp
/// - if the source pool is configured for the remote chain
/// - rate limiting for either normal or fast-transfer lanes.
/// - rate limiting for either default or custom-finality transfer messages.
/// @param releaseOrMintIn The input to validate.
/// @param localAmount The local amount to be released or minted.
/// @param finality The finality depth requested by the message. A value of zero is used for default finality.
Expand All @@ -414,7 +424,7 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
s_finalityConfig.inboundRateLimiterConfig[releaseOrMintIn.remoteChainSelector]._consume(
localAmount, releaseOrMintIn.localToken
);
emit FastTransferInboundRateLimitConsumed(
emit CustomFinalityTransferInboundRateLimitConsumed(
releaseOrMintIn.remoteChainSelector, releaseOrMintIn.localToken, localAmount
);
} else {
Expand Down Expand Up @@ -862,35 +872,28 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
/// @notice Updates the finality configuration for token transfers.
function applyFinalityConfigUpdates(
uint16 finalityThreshold,
uint16 fastTransferFeeBps,
uint256 maxAmountPerRequest,
FastFinalityRateLimitConfigArgs[] calldata rateLimitConfigArgs
CustomFinalityRateLimitConfigArgs[] calldata rateLimitConfigArgs
) external virtual onlyOwner {
FastFinalityConfig storage finalityConfig = s_finalityConfig;
CustomFinalityConfig storage finalityConfig = s_finalityConfig;
finalityConfig.finalityThreshold = finalityThreshold;
if (fastTransferFeeBps >= BPS_DIVIDER) {
revert InvalidFastTransferFeeBps();
}
finalityConfig.fastTransferFeeBps = fastTransferFeeBps;
finalityConfig.maxAmountPerRequest = maxAmountPerRequest;
_setFastFinalityRateLimitConfig(rateLimitConfigArgs);
emit FinalityConfigUpdated(finalityThreshold, fastTransferFeeBps, maxAmountPerRequest);
_setCustomFinalityRateLimitConfig(rateLimitConfigArgs);
emit FinalityThresholdUpdated(finalityThreshold);
}

/// @notice Sets the fast finality based rate limit configurations for specified remote chains.
/// @notice Sets the custom finality based rate limit configurations for specified remote chains.
/// @param rateLimitConfigArgs Array of structs containing remote chain selectors and their rate limiter configs.
function setFastFinalityRateLimitConfig(
FastFinalityRateLimitConfigArgs[] calldata rateLimitConfigArgs
function setCustomFinalityRateLimitConfig(
CustomFinalityRateLimitConfigArgs[] calldata rateLimitConfigArgs
) external virtual onlyOwner {
_setFastFinalityRateLimitConfig(rateLimitConfigArgs);
_setCustomFinalityRateLimitConfig(rateLimitConfigArgs);
}

function _setFastFinalityRateLimitConfig(
FastFinalityRateLimitConfigArgs[] calldata rateLimitConfigArgs
function _setCustomFinalityRateLimitConfig(
CustomFinalityRateLimitConfigArgs[] calldata rateLimitConfigArgs
) internal {
FastFinalityConfig storage finalityConfig = s_finalityConfig;
CustomFinalityConfig storage finalityConfig = s_finalityConfig;
for (uint256 i = 0; i < rateLimitConfigArgs.length; ++i) {
FastFinalityRateLimitConfigArgs calldata configArgs = rateLimitConfigArgs[i];
CustomFinalityRateLimitConfigArgs calldata configArgs = rateLimitConfigArgs[i];
uint64 remoteChainSelector = configArgs.remoteChainSelector;
if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector);

Expand Down Expand Up @@ -1044,12 +1047,11 @@ abstract contract TokenPool is IPoolV2, Ownable2StepMsgSender {
Pool.LockOrBurnInV1 calldata lockOrBurnIn,
uint16 finality
) internal view virtual returns (uint256 destAmount) {
destAmount = lockOrBurnIn.amount;
if (finality != WAIT_FOR_FINALITY) {
// deduct fast transfer fee
destAmount -= (lockOrBurnIn.amount * s_finalityConfig.fastTransferFeeBps) / BPS_DIVIDER;
}
// TODO : normal transfer fee
uint256 feeBps = finality != WAIT_FOR_FINALITY
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WAIT_FOR_FINALITY terminology doesn't seem correct given that we changed the name to "custom finality". Because even using a custom finality, you are still waiting for a measure of finality, just a custom one instead of the default. Would DEFAULT_FINALITY work better? We use the term "default finality" in comments already, so it is clear to understand what this is referring to.

Suggested change
uint256 feeBps = finality != WAIT_FOR_FINALITY
uint256 feeBps = finality != DEFAULT_FINALITY

? s_tokenTransferFeeConfig[lockOrBurnIn.remoteChainSelector].customFinalityTransferFeeBps
: s_tokenTransferFeeConfig[lockOrBurnIn.remoteChainSelector].defaultFinalityTransferFeeBps;

destAmount = lockOrBurnIn.amount - (lockOrBurnIn.amount * feeBps) / BPS_DIVIDER;
return destAmount;
}
}
9 changes: 2 additions & 7 deletions chains/evm/contracts/test/helpers/TokenPoolV2Helper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,8 @@ contract TokenPoolV2Helper is TokenPool {
address router
) TokenPool(token, localTokenDecimals, allowlist, rmnProxy, router) {}

function getFastFinalityConfig()
external
view
returns (uint16 finalityThreshold, uint16 fastTransferFeeBps, uint256 maxAmountPerRequest)
{
FastFinalityConfig storage config = s_finalityConfig;
return (config.finalityThreshold, config.fastTransferFeeBps, config.maxAmountPerRequest);
function getCustomFinalityConfig() external view returns (uint16 finalityThreshold) {
return s_finalityConfig.finalityThreshold;
}

function getFastOutboundBucket(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,28 @@ contract BurnMintTokenPool_lockOrBurn is BurnMintTokenPoolSetup {
assertEq(s_token.balanceOf(address(s_pool)), 0);
}

function test_lockOrBurn_FeeNotApplied_LegacyLockOrBurn() public {
uint16 finalityThreshold = 5;
uint256 amount = 1000e18;

// Apply custom finality config with a fee
vm.startPrank(OWNER);
s_tokenPool.applyFinalityConfigUpdates(finalityThreshold, new TokenPool.CustomFinalityRateLimitConfigArgs[](0));

Pool.LockOrBurnInV1 memory lockOrBurnIn = Pool.LockOrBurnInV1({
originalSender: OWNER,
receiver: bytes(""),
amount: amount,
remoteChainSelector: DEST_CHAIN_SELECTOR,
localToken: address(s_token)
});

vm.startPrank(s_allowedOnRamp);
s_tokenPool.lockOrBurn(lockOrBurnIn);

assertEq(s_tokenPool.getAccumulatedFees(), 0); // No fees should be accumulated
}

// Should not burn tokens if cursed.
function test_lockOrBurn_RevertWhen_CursedByRMN() public {
vm.mockCall(address(s_mockRMNRemote), abi.encodeWithSignature("isCursed(bytes16)"), abi.encode(true));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {IPoolV2} from "../../../interfaces/IPoolV2.sol";

import {Pool} from "../../../libraries/Pool.sol";
import {TokenPool} from "../../../pools/TokenPool.sol";
import {TokenPoolV2Setup} from "./TokenPoolV2Setup.t.sol";

contract TokenPoolV2_applyFee is TokenPoolV2Setup {
function test_applyFee_FastFinality() public {
function test_applyFee_DefaultFinality() public {
uint16 finalityThreshold = 5;
uint16 fastTransferFeeBps = 500;
uint256 maxAmountPerRequest = 1000e18;
uint16 defaultFinalityTransferFeeBps = 200;
uint256 amount = 1000e18;
vm.startPrank(OWNER);
s_tokenPool.applyFinalityConfigUpdates(
finalityThreshold, fastTransferFeeBps, maxAmountPerRequest, new TokenPool.FastFinalityRateLimitConfigArgs[](0)
);
s_tokenPool.applyFinalityConfigUpdates(finalityThreshold, new TokenPool.CustomFinalityRateLimitConfigArgs[](0));
// Set a fee config with default finality fee
_applyTokenTransferFeeConfigUpdates(0, defaultFinalityTransferFeeBps);

Pool.LockOrBurnInV1 memory lockOrBurnIn = Pool.LockOrBurnInV1({
originalSender: s_sender,
receiver: s_receiver,
amount: amount,
remoteChainSelector: DEST_CHAIN_SELECTOR,
localToken: address(s_token)
});

uint256 amountAfterFee = s_tokenPool.applyFee(lockOrBurnIn, 0);
assertEq(amountAfterFee, amount - ((amount * defaultFinalityTransferFeeBps) / BPS_DIVIDER));
}

function test_applyFee_CustomFinality() public {
uint16 finalityThreshold = 5;
uint16 customFinalityTransferFeeBps = 500;
uint256 amount = 1000e18;
vm.startPrank(OWNER);
s_tokenPool.applyFinalityConfigUpdates(finalityThreshold, new TokenPool.CustomFinalityRateLimitConfigArgs[](0));
// Set a fee config with custom finality fee
_applyTokenTransferFeeConfigUpdates(customFinalityTransferFeeBps, 0);

Pool.LockOrBurnInV1 memory lockOrBurnIn = Pool.LockOrBurnInV1({
originalSender: s_sender,
Expand All @@ -25,10 +47,10 @@ contract TokenPoolV2_applyFee is TokenPoolV2Setup {
});

uint256 amountAfterFee = s_tokenPool.applyFee(lockOrBurnIn, finalityThreshold);
assertEq(amountAfterFee, amount - ((amount * fastTransferFeeBps) / BPS_DIVIDER));
assertEq(amountAfterFee, amount - ((amount * customFinalityTransferFeeBps) / BPS_DIVIDER));
}

function test_applyFee_NoFee() public {
function test_applyFee_NoFee() public view {
uint256 amount = 1000e18;
Pool.LockOrBurnInV1 memory lockOrBurnIn = Pool.LockOrBurnInV1({
originalSender: s_sender,
Expand All @@ -41,4 +63,24 @@ contract TokenPoolV2_applyFee is TokenPoolV2Setup {
uint256 amountAfterFee = s_tokenPool.applyFee(lockOrBurnIn, 0);
assertEq(amountAfterFee, amount);
}

function _applyTokenTransferFeeConfigUpdates(
uint16 customFinalityTransferFeeBps,
uint16 defaultFinalityTransferFeeBps
) internal {
IPoolV2.TokenTransferFeeConfig memory feeConfig = IPoolV2.TokenTransferFeeConfig({
destGasOverhead: 50000,
destBytesOverhead: 32,
feeUSDCents: 0,
customFinalityTransferFeeBps: customFinalityTransferFeeBps, // 0.50%
defaultFinalityTransferFeeBps: defaultFinalityTransferFeeBps, // 0.20%
isEnabled: true
});

TokenPool.TokenTransferFeeConfigArgs[] memory feeConfigArgs = new TokenPool.TokenTransferFeeConfigArgs[](1);
feeConfigArgs[0] =
TokenPool.TokenTransferFeeConfigArgs({destChainSelector: DEST_CHAIN_SELECTOR, tokenTransferFeeConfig: feeConfig});

s_tokenPool.applyTokenTransferFeeConfigUpdates(feeConfigArgs, new uint64[](0));
}
}
Loading
Loading