Skip to content
Merged
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

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions chains/evm/.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,11 @@ LockReleaseTokenPool_releaseOrMint:test_releaseOrMintV2() (gas: 246087)
LockReleaseTokenPool_setRebalancer:test_SetRebalancer() (gas: 21811)
LockReleaseTokenPool_supportsInterface:test_SupportsInterface() (gas: 10100)
LockReleaseTokenPool_transferLiquidity:test_transferLiquidity() (gas: 97332)
LombardTokenPool_constructor:test_constructor() (gas: 3954838)
LombardTokenPool_constructor:test_constructor_ZeroVerifierNotAllowed() (gas: 92001)
LombardTokenPool_getTokenDecimals:test_getTokenDecimals_FallsBackOnRevert() (gas: 12070)
LombardTokenPool_getTokenDecimals:test_getTokenDecimals_UsesTokenDecimals() (gas: 14385)
LombardTokenPool_lockOrBurn:test_lockOrBurn_ForwardsToVerifier() (gas: 247098)
MessageV1Codec__decodeMessageV1:test__decodeMessageV1_EmptyFields() (gas: 46889)
MessageV1Codec__decodeMessageV1:test__decodeMessageV1_MaxLengthFields() (gas: 436236)
MessageV1Codec__decodeMessageV1:test__decodeMessageV1_WithData() (gas: 57857)
Expand Down
7 changes: 6 additions & 1 deletion chains/evm/contracts/onRamp/OnRamp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ contract OnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, Ownable2StepMsgSender
bytes offRamp; // Destination OffRamp address, NOT abi encoded but raw bytes.
}

/// @notice Receipt structure used to record gas limits and fees for verifiers, executors and token transfers.
/// @notice Receipt structure used to record gas limits and fees for message processing.
/// @dev The ordering of receipts in a message is as follows:
/// - Verifier receipts in the order of the CCV list.
/// - Token transfer receipt (if any tokens are being transferred).
/// - Executor receipt.
/// - Network fee receipt.
struct Receipt {
// The address of the entity that issued the receipt. For token receipts this is the token address, not the pool.
// for verifiers and executors, this is the user specified value, even if the call is ultimately handled by some
Expand Down
104 changes: 104 additions & 0 deletions chains/evm/contracts/pools/Lombard/LombardTokenPool.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ICrossChainVerifierResolver} from "../../interfaces/ICrossChainVerifierResolver.sol";
import {ITypeAndVersion} from "@chainlink/contracts/src/v0.8/shared/interfaces/ITypeAndVersion.sol";

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

import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/[email protected]/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";

/// @notice Lombard CCIP token pool.
/// For v2 flows, token movement (burn/mint) is handled by the Lombard verifier,
/// the pool performs validation, rate limiting, accounting and event emission.
/// IPoolV2.lockOrBurn forwards tokens to the verifier.
/// IPoolV2.releaseOrMint does not move tokens, _releaseOrMint is a no-op.
/// TODO: Add explicit V1 support/backwards compatibility.
contract LombardTokenPool is TokenPool, ITypeAndVersion {
using SafeERC20 for IERC20;
using SafeERC20 for IERC20Metadata;

error ZeroVerifierNotAllowed();
error OutboundImplementationNotFoundForVerifier();

event LombardVerifierSet(address indexed verifier);

string public constant override typeAndVersion = "LombardTokenPool 1.7.0-dev";

/// @notice Lombard verifier proxy / resolver address. lockOrBurn fetches the outbound implementation and forwards tokens to it.
address private immutable i_lombardVerifierResolver;
Comment on lines +31 to +32
Copy link
Contributor

Choose a reason for hiding this comment

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

If we change to proxy we have to update this anyways, can just make the comment more explicit.

Suggested change
/// @notice Lombard verifier proxy / resolver address. lockOrBurn fetches the outbound implementation and forwards tokens to it.
address private immutable i_lombardVerifierResolver;
/// @notice Lombard verifier resolver address. lockOrBurn fetches the outbound implementation and forwards tokens to it.
address private immutable i_lombardVerifierResolver;

Copy link
Member Author

Choose a reason for hiding this comment

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

will fix in next PR


constructor(
IERC20Metadata token,
address verifier,
address advancedPoolHooks,
address rmnProxy,
address router,
uint8 fallbackDecimals
) TokenPool(token, _getTokenDecimals(token, fallbackDecimals), advancedPoolHooks, rmnProxy, router) {
if (verifier == address(0)) {
revert ZeroVerifierNotAllowed();
}
i_lombardVerifierResolver = verifier;
emit LombardVerifierSet(verifier);
}

// ================================================================
// │ Lock or Burn │
// ================================================================

/// @notice For IPoolV2.lockOrBurn call, this contract only forwards tokens to the verifier.
/// @dev Forward the net amount to the verifier; actual burn/bridge is done there.
function lockOrBurn(
Pool.LockOrBurnInV1 calldata lockOrBurnIn,
uint16 blockConfirmationRequested,
bytes calldata tokenArgs
) public override returns (Pool.LockOrBurnOutV1 memory lockOrBurnOut, uint256 destTokenAmount) {
address verifierImpl = ICrossChainVerifierResolver(i_lombardVerifierResolver).getOutboundImplementation(
lockOrBurnIn.remoteChainSelector, ""
);
if (verifierImpl == address(0)) {
revert OutboundImplementationNotFoundForVerifier();
}
i_token.safeTransfer(verifierImpl, lockOrBurnIn.amount);
return super.lockOrBurn(lockOrBurnIn, blockConfirmationRequested, tokenArgs);
}

function lockOrBurn(
Pool.LockOrBurnInV1 calldata
) public pure override(TokenPool) returns (Pool.LockOrBurnOutV1 memory lockOrBurnOut) {
// TODO: Implement V1 path for backward compatability with old lanes.
return lockOrBurnOut;
}

// ================================================================
// │ Release or Mint │
// ================================================================

function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata
) public pure override(TokenPool) returns (Pool.ReleaseOrMintOutV1 memory releaseOrMintOut) {
// TODO: Implement V1 path for backward compatability with old lanes.
return releaseOrMintOut;
}

// ================================================================
// │ Internal utils │
// ================================================================

function _getTokenDecimals(IERC20Metadata token, uint8 fallbackDecimals) internal view returns (uint8) {
try token.decimals() returns (uint8 dec) {
return dec;
} catch {
return fallbackDecimals;
}
}

/// @notice Returns the verifier resolver address.
function getVerifierResolver() external view returns (address) {
return i_lombardVerifierResolver;
}
}
19 changes: 19 additions & 0 deletions chains/evm/contracts/test/helpers/LombardTokenPoolHelper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {LombardTokenPool} from "../../pools/Lombard/LombardTokenPool.sol";
import {IERC20Metadata} from "@openzeppelin/[email protected]/token/ERC20/extensions/IERC20Metadata.sol";

contract LombardTokenPoolHelper is LombardTokenPool {
constructor(
IERC20Metadata token,
address verifier,
address rmnProxy,
address router,
uint8 fallbackDecimals
) LombardTokenPool(token, verifier, address(0), rmnProxy, router, fallbackDecimals) {}

function getTokenDecimals(IERC20Metadata token, uint8 fallbackDecimals) external view returns (uint8) {
return _getTokenDecimals(token, fallbackDecimals);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {BaseTest} from "../../BaseTest.t.sol";
import {LombardTokenPoolHelper} from "../../helpers/LombardTokenPoolHelper.sol";
import {MockVerifier} from "../../mocks/MockVerifier.sol";
import {BurnMintERC20} from "@chainlink/contracts/src/v0.8/shared/token/ERC20/BurnMintERC20.sol";

import {IERC20Metadata} from "@openzeppelin/[email protected]/token/ERC20/extensions/IERC20Metadata.sol";

contract LombardTokenPool_getTokenDecimals is BaseTest {
BurnMintERC20 internal s_token;
LombardTokenPoolHelper internal s_helper;
MockVerifier internal s_resolver;
address internal constant VERIFIER = address(0xBEEF);

function setUp() public override {
super.setUp();
s_token = new BurnMintERC20("Lombard", "LBD", 18, 0, 0);
s_resolver = new MockVerifier("");
s_helper =
new LombardTokenPoolHelper(s_token, address(s_resolver), address(s_mockRMNRemote), address(s_sourceRouter), 18);
}

function test_getTokenDecimals_UsesTokenDecimals() public view {
uint8 dec = s_helper.getTokenDecimals(s_token, 6);
assertEq(dec, 18);
}

function test_getTokenDecimals_FallsBackOnRevert() public {
vm.mockCallRevert(address(s_token), abi.encodeWithSelector(IERC20Metadata.decimals.selector), "revert");
uint8 dec = s_helper.getTokenDecimals(IERC20Metadata(address(s_token)), 6);
assertEq(dec, 6);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {Pool} from "../../../libraries/Pool.sol";

import {LombardTokenPool} from "../../../pools/Lombard/LombardTokenPool.sol";
import {LombardTokenPoolSetup} from "./LombardTokenPoolSetup.t.sol";

contract LombardTokenPool_lockOrBurn is LombardTokenPoolSetup {
function setUp() public virtual override {
super.setUp();
vm.startPrank(s_allowedOnRamp);
}

function test_lockOrBurn_ForwardsToVerifier() public {
uint256 amount = 1e18;
deal(address(s_token), address(s_pool), amount);

(Pool.LockOrBurnOutV1 memory out, uint256 destAmount) = s_pool.lockOrBurn(
Pool.LockOrBurnInV1({
receiver: abi.encodePacked(address(0xDEAD)),
remoteChainSelector: DEST_CHAIN_SELECTOR,
originalSender: OWNER,
amount: amount,
localToken: address(s_token)
}),
0,
""
);

assertEq(destAmount, amount);
assertEq(out.destTokenAddress, abi.encode(s_remoteToken));
assertEq(out.destPoolData, abi.encode(uint8(DEFAULT_TOKEN_DECIMALS)));
assertEq(s_token.balanceOf(s_verifierResolver.getOutboundImplementation(DEST_CHAIN_SELECTOR, "")), amount);
assertEq(s_token.balanceOf(address(s_pool)), 0);
}

function test_lockOrBurn_RevertWhen_OutboundImplementationNotFoundForVerifier() public {
uint256 amount = 1e18;
deal(address(s_token), address(s_pool), amount);
vm.mockCall(
address(s_verifierResolver),
abi.encodeCall(s_verifierResolver.getOutboundImplementation, (DEST_CHAIN_SELECTOR, "")),
abi.encode(address(0))
);

vm.expectRevert(LombardTokenPool.OutboundImplementationNotFoundForVerifier.selector);
s_pool.lockOrBurn(
Pool.LockOrBurnInV1({
receiver: abi.encodePacked(address(0xDEAD)),
remoteChainSelector: DEST_CHAIN_SELECTOR,
originalSender: OWNER,
amount: amount,
localToken: address(s_token)
}),
0,
""
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {LombardTokenPool} from "../../../pools/Lombard/LombardTokenPool.sol";
import {MockVerifier} from "../../mocks/MockVerifier.sol";
import {BurnMintERC20} from "@chainlink/contracts/src/v0.8/shared/token/ERC20/BurnMintERC20.sol";
import {Test} from "forge-std/Test.sol";

contract LombardTokenPool_constructor is Test {
BurnMintERC20 internal s_token;
address internal s_resolver;
address internal constant RMN = address(0xAA01);
address internal constant ROUTER = address(0xBB02);

function setUp() public {
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to setup for a single test, can be in the test

Copy link
Member Author

Choose a reason for hiding this comment

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

this file has one more test now

Copy link
Member Author

Choose a reason for hiding this comment

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

s_token = new BurnMintERC20("Lombard", "LBD", 18, 0, 0);
s_resolver = address(new MockVerifier(""));
}

function test_constructor() public {
vm.expectEmit();
emit LombardTokenPool.LombardVerifierSet(s_resolver);
LombardTokenPool pool = new LombardTokenPool(s_token, s_resolver, address(0), RMN, ROUTER, 18);
assertEq(pool.getVerifierResolver(), address(s_resolver));
assertEq(pool.typeAndVersion(), "LombardTokenPool 1.7.0-dev");
}

function test_constructor_ZeroVerifierNotAllowed() public {
vm.expectRevert(LombardTokenPool.ZeroVerifierNotAllowed.selector);
new LombardTokenPool(s_token, address(0), address(0), RMN, ROUTER, 18);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {Router} from "../../../Router.sol";
import {TokenPool} from "../../../pools/TokenPool.sol";
import {LombardTokenPoolHelper} from "../../helpers/LombardTokenPoolHelper.sol";
import {MockVerifier} from "../../mocks/MockVerifier.sol";
import {TokenPoolSetup} from "../TokenPool/TokenPoolSetup.t.sol";

contract LombardTokenPoolSetup is TokenPoolSetup {
LombardTokenPoolHelper internal s_pool;
MockVerifier internal s_verifierResolver;
address internal constant VERIFIER_IMPL = address(0x2345);
address internal s_remotePool = makeAddr("remotePool");
address internal s_remoteToken = makeAddr("remoteToken");

function setUp() public virtual override {
super.setUp();

s_verifierResolver = new MockVerifier("");

s_pool = new LombardTokenPoolHelper(
s_token, address(s_verifierResolver), address(s_mockRMNRemote), address(s_sourceRouter), DEFAULT_TOKEN_DECIMALS
);

// Configure remote chain.
bytes[] memory remotePools = new bytes[](1);
remotePools[0] = abi.encode(s_remotePool);

TokenPool.ChainUpdate[] memory chainUpdate = new TokenPool.ChainUpdate[](1);
chainUpdate[0] = TokenPool.ChainUpdate({
remoteChainSelector: DEST_CHAIN_SELECTOR,
remotePoolAddresses: remotePools,
remoteTokenAddress: abi.encode(s_remoteToken),
outboundRateLimiterConfig: _getOutboundRateLimiterConfig(),
inboundRateLimiterConfig: _getInboundRateLimiterConfig()
});

vm.startPrank(OWNER);
s_pool.applyChainUpdates(new uint64[](0), chainUpdate);

Router.OnRamp[] memory onRampUpdates = new Router.OnRamp[](1);
onRampUpdates[0] = Router.OnRamp({destChainSelector: DEST_CHAIN_SELECTOR, onRamp: s_allowedOnRamp});
Router.OffRamp[] memory offRampUpdates = new Router.OffRamp[](1);
offRampUpdates[0] = Router.OffRamp({sourceChainSelector: DEST_CHAIN_SELECTOR, offRamp: s_allowedOffRamp});
s_sourceRouter.applyRampUpdates(onRampUpdates, new Router.OffRamp[](0), offRampUpdates);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ hybrid_lock_release_usdc_token_pool: ../solc/ccip/HybridLockReleaseUSDCTokenPool
hyper_liquid_compatible_erc20: ../solc/ccip/HyperLiquidCompatibleERC20/HyperLiquidCompatibleERC20.sol/HyperLiquidCompatibleERC20.abi.json ../solc/ccip/HyperLiquidCompatibleERC20/HyperLiquidCompatibleERC20.sol/HyperLiquidCompatibleERC20.bin 1b825f6c2f7ba630ca0dbacb241c6a8cb16a5aa62cc152f5fefe40166c3d96f6
lock_release_token_pool: ../solc/ccip/LockReleaseTokenPool/LockReleaseTokenPool.sol/LockReleaseTokenPool.abi.json ../solc/ccip/LockReleaseTokenPool/LockReleaseTokenPool.sol/LockReleaseTokenPool.bin d1be173acb7fc36d09e800f3bff70a833ebe1439e3f245a8e69cfc3152c07e27
log_message_data_receiver: ../solc/ccip/LogMessageDataReceiver/LogMessageDataReceiver.sol/LogMessageDataReceiver.abi.json ../solc/ccip/LogMessageDataReceiver/LogMessageDataReceiver.sol/LogMessageDataReceiver.bin 6fe60e48711884eae82dd95cabb1c66a5644336719fa1219df1ceceec11e6bce
lombard_token_pool: ../solc/ccip/LombardTokenPool/LombardTokenPool.sol/LombardTokenPool.abi.json ../solc/ccip/LombardTokenPool/LombardTokenPool.sol/LombardTokenPool.bin 452027b48e67b27d17c8c5c2ec3146aba5b4277009618dcbd7cc6e4d26e6df6e
maybe_revert_message_receiver: ../solc/ccip/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.sol/MaybeRevertMessageReceiver.abi.json ../solc/ccip/MaybeRevertMessageReceiver/MaybeRevertMessageReceiver.sol/MaybeRevertMessageReceiver.bin ee264f67a2356cc4eebe839a5a88367cbcdc27a7520cca56263319e9afe97a1a
message_hasher: ../solc/ccip/MessageHasher/MessageHasher.sol/MessageHasher.abi.json ../solc/ccip/MessageHasher/MessageHasher.sol/MessageHasher.bin cb3448514ff88dd019316135cf6c10f6b0d6afcb8ded227884c910efe4274ba1
mock_lbtc_token_pool: ../solc/ccip/MockE2ELBTCTokenPool/MockE2ELBTCTokenPool.sol/MockE2ELBTCTokenPool.abi.json ../solc/ccip/MockE2ELBTCTokenPool/MockE2ELBTCTokenPool.sol/MockE2ELBTCTokenPool.bin 2381c32b701ab9f014003477b594a7e6af2abd9817efb7633804ead43565ac70
Expand Down
1 change: 1 addition & 0 deletions chains/evm/gobindings/go_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ package ccip
//go:generate go run generation/generate/wrap.go ccip USDCTokenPoolCCTPV2 usdc_token_pool_cctp_v2 latest
//go:generate go run generation/generate/wrap.go ccip USDCTokenPoolProxy usdc_token_pool_proxy latest
//go:generate go run generation/generate/wrap.go ccip BurnMintWithLockReleaseFlagTokenPool burn_mint_with_lock_release_flag_token_pool latest
//go:generate go run generation/generate/wrap.go ccip LombardTokenPool lombard_token_pool latest

// Helpers
//go:generate go run generation/generate/wrap.go ccip MaybeRevertMessageReceiver maybe_revert_message_receiver latest
Expand Down
1 change: 1 addition & 0 deletions chains/evm/scripts/compile_all
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ compileContract pools/USDC/SiloedUSDCTokenPool
compileContract pools/USDC/USDCTokenPoolCCTPV2
compileContract pools/USDC/USDCTokenPoolProxy
compileContract pools/USDC/BurnMintWithLockReleaseFlagTokenPool
compileContract pools/Lombard/LombardTokenPool

# Test helpers
compileContract test/helpers/USDCReaderTester
Expand Down
Loading