Contracts · Solidity

Cross-chain contract patterns

Solidity patterns that call the t3rn executor contract. Each example is production-ready — copy the interface, drop in the pattern, deploy.

IT3rnExecutor interface

Paste this interface into any Solidity file. It gives you submitOrder and isSettled. No library to install.

IT3rnExecutor.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

// Drop this interface in any contract that needs to submit cross-chain intents.
interface IT3rnExecutor {
    struct Order {
        address asset;
        uint256 amount;
        bytes32 destinationChainId; // keccak256 of chain slug, e.g. "base"
        address recipient;
        bytes   calldata_;          // optional calldata to execute on destination
    }

    /// @dev Submit a cross-chain order; returns an intent id.
    function submitOrder(Order calldata order)
        external payable returns (bytes32 intentId);

    /// @dev Returns true once the crosschain proof is anchored.
    function isSettled(bytes32 intentId) external view returns (bool);
}

Perpetual swap rebalancer

Watches for price divergence across chains. When the spread opens, submits a cross-chain order to close it. Demonstrates a simple token transfer with no destination calldata.

PerpRebalancer.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { IT3rnExecutor } from "./IT3rnExecutor.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @notice Auto-rebalancing vault: if ETH price on Base diverges >1% from
/// Ethereum, flash-swap back.  Demonstrates cross-chain calldata.
contract PerpRebalancer {
    IT3rnExecutor public immutable executor;
    address       public immutable usdc;

    constructor(address executor_, address usdc_) {
        executor = IT3rnExecutor(executor_);
        usdc     = usdc_;
    }

    function rebalance(uint256 amount, address recipient) external {
        IERC20(usdc).transferFrom(msg.sender, address(this), amount);
        IERC20(usdc).approve(address(executor), amount);

        IT3rnExecutor.Order memory order = IT3rnExecutor.Order({
            asset:             usdc,
            amount:            amount,
            destinationChainId: keccak256("base"),
            recipient:         recipient,
            calldata_:         ""   // simple transfer — no destination call
        });

        executor.submitOrder{ value: msg.value }(order);
    }
}

NFT mint relay

User pays on Ethereum; the NFT is minted on Base. Shows how to pass destination calldata through the intent.

CrossChainNFTGateway.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { IT3rnExecutor } from "./IT3rnExecutor.sol";

interface INFTMint {
    function mintTo(address to, uint256 tokenId) external;
}

/// @notice Pay on Ethereum, get the NFT minted on Base.
/// calldata_ instructs the destination executor to call mintTo.
contract CrossChainNFTGateway {
    IT3rnExecutor public immutable executor;
    address       public immutable nftContractOnBase;
    uint256       public           nextTokenId;

    constructor(address executor_, address nft_) {
        executor        = IT3rnExecutor(executor_);
        nftContractOnBase = nft_;
    }

    function mint(address recipient) external payable {
        uint256 tokenId = ++nextTokenId;

        bytes memory mintCall = abi.encodeCall(
            INFTMint.mintTo,
            (recipient, tokenId)
        );

        IT3rnExecutor.Order memory order = IT3rnExecutor.Order({
            asset:             address(0),        // ETH
            amount:            msg.value,
            destinationChainId: keccak256("base"),
            recipient:         nftContractOnBase,
            calldata_:         mintCall
        });

        executor.submitOrder{ value: msg.value }(order);
    }
}

Agent treasury sweep

Collect protocol fees locally. Once the balance crosses a threshold, sweep everything cross-chain to the DAO treasury in one atomic intent.

AgentTreasury.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import { IT3rnExecutor } from "./IT3rnExecutor.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/// @notice Agent treasury: collect protocol fees on any chain, sweep them
/// to the DAO treasury on Ethereum once they exceed a threshold.
contract AgentTreasury {
    IT3rnExecutor public immutable executor;
    address       public immutable daoTreasury;   // on Ethereum
    address       public immutable usdc;
    uint256       public constant  SWEEP_THRESHOLD = 1_000e6; // 1,000 USDC

    constructor(address executor_, address daoTreasury_, address usdc_) {
        executor    = IT3rnExecutor(executor_);
        daoTreasury = daoTreasury_;
        usdc        = usdc_;
    }

    /// @dev Called by protocol; sweeps to DAO treasury when threshold reached.
    function depositFees(uint256 amount) external {
        IERC20(usdc).transferFrom(msg.sender, address(this), amount);

        uint256 balance = IERC20(usdc).balanceOf(address(this));
        if (balance < SWEEP_THRESHOLD) return;

        IERC20(usdc).approve(address(executor), balance);

        executor.submitOrder(IT3rnExecutor.Order({
            asset:             usdc,
            amount:            balance,
            destinationChainId: keccak256("ethereum"),
            recipient:         daoTreasury,
            calldata_:         ""
        }));
    }
}
Contract addresses & ABIs

Deployed executor addresses per chain, verified on Etherscan and Basescan.

View on GitHub →