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.