Skip to main content
Version: Endpoint V2

Integrating Stargate Transfers

Stargate protocol contracts (StargatePool and StargateOFT) implement the standard IOFT interface for Omnichain Fungible Tokens (OFTs), making cross-chain transfers straightforward with just two methods: quoteSend() and send().

For architecture and concepts, see Stargate Finance.

Stargate Asset Deployments

View all available Stargate pools and OFT deployments across chains on the OFT Ecosystem & Stargate Assets page. Find contract addresses, supported chains, and asset types for seamless integration.

The IOFT Interface

All Stargate pool and HydraOFT contracts implement the same interface:

interface IStargate is IOFT {
// Get underlying token address
function token() external view returns (address);

// Check if approval is required before sending
function approvalRequired() external view returns (bool);

// Quote the cross-chain transfer fee
function quoteSend(
SendParam calldata _sendParam,
bool _payInLzToken
) external view returns (MessagingFee memory);

// Send tokens cross-chain
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}

Helper Methods:

  • token(): Returns the underlying ERC20 token address (or address(0) for native assets like ETH)
  • approvalRequired(): Returns true if you need to approve tokens before sending, false for mint/burn or native assets

These methods allow you to write generic code that works with any Stargate asset (USDC pools, ETH pools, Hydra OFTs) without hardcoding token addresses or approval logic.

Interactive Interface Methods

quoteSend() - Get Transfer Fees

CALL
quoteSend(_sendParam: SendParam, _payInLzToken: bool)

quoteOFT() - Get Detailed Transfer Quote

CALL
quoteOFT(_sendParam: SendParam)

send() - Transfer Tokens

SEND
send(_sendParam: SendParam, _fee: MessagingFee, _refundAddress: address)

SendParam Structure

The key parameters for Stargate transfers is identical to OFT transfers:

struct SendParam {
uint32 dstEid; // Destination endpoint ID
bytes32 to; // Recipient address
uint256 amountLD; // Amount to send (local decimals)
uint256 minAmountLD; // Minimum amount (slippage protection)
bytes extraOptions; // Execution options for LayerZero
bytes composeMsg; // For composability (Taxi mode only)
bytes oftCmd; // "" for Taxi, bytes(1) for Bus
}

How send() Works

When you call send() on a Stargate contract, it triggers a chain of calls through LayerZero's infrastructure:

Call Flow:

  1. Stargate Contract → Debits tokens (lock or burn depending on contract type)
  2. LayerZero Endpoint → Routes the message to the configured MessageLib
  3. Message Library (SendUln302) → Requests quotes from DVNs and Executor
  4. Workers (DVNs + Executor) → Provide fee quotes for verification and execution
  5. Fee Aggregation → Returns total nativeFee from all workers

This multi-step process is why quoteSend() exists - it aggregates fees from all components in the security stack.

Quote Freshness

Call quoteSend() as close as possible to send() execution. Fee quotes can become stale due to:

  • Changing gas prices on source/destination chains
  • Price feed updates for cross-chain gas estimation
  • DVN fee adjustments

In production, quote and send in the same transaction or block to ensure accurate fees.

Transfer Modes

Stargate supports two transfer modes with different characteristics:

Taxi Mode (Immediate)

Use when: You need immediate transfer or composability

Set: oftCmd: ""

Supports: Composability via composeMsg to trigger actions on destination

SendParam memory sendParam = SendParam({
dstEid: dstEid,
to: bytes32(uint256(uint160(recipient))),
amountLD: amount,
minAmountLD: amount * 995 / 1000, // 0.5% slippage
extraOptions: "", // Or compose options
composeMsg: "", // Or encoded compose message
oftCmd: "" // Empty for Taxi mode
});

Bus Mode (Batched)

Use when: You want gas savings and don't need composability

Set: oftCmd: new bytes(1)

Does NOT support: Composability - no lzCompose() will be triggered

SendParam memory sendParam = SendParam({
dstEid: dstEid,
to: bytes32(uint256(uint160(recipient))),
amountLD: amount,
minAmountLD: amount * 995 / 1000,
extraOptions: new bytes(0),
composeMsg: new bytes(0),
oftCmd: new bytes(1) // bytes(1) for Bus mode
});
Composability Requirement

Composable strategies (e.g., Omnichain Vaults) require Taxi mode. Bus mode will not trigger lzCompose() calls.

Basic Transfer Example

Note: These examples assume you're using existing Stargate contracts (pre-configured by the Stargate team). If deploying your own OFT, you must complete the configuration steps first - see OFT Quickstart - Deployment and Wiring.

Since Stargate implements IOFT, sending tokens works exactly like any OFT - the only Stargate-specific aspect is the oftCmd field for Taxi vs Bus mode:

// Taxi Mode (immediate, supports composability)
SendParam memory sendParam = SendParam({
dstEid: dstEid,
to: bytes32(uint256(uint160(recipient))),
amountLD: amount,
minAmountLD: amount * 995 / 1000,
extraOptions: "",
composeMsg: "",
oftCmd: "" // Empty for Taxi mode
});

// Bus Mode (batched, no composability)
SendParam memory sendParam = SendParam({
dstEid: dstEid,
to: bytes32(uint256(uint160(recipient))),
amountLD: amount,
minAmountLD: amount * 995 / 1000,
extraOptions: new bytes(0),
composeMsg: new bytes(0),
oftCmd: new bytes(1) // bytes(1) for Bus mode
});

// Then call: IOFT(stargateAddress).send(sendParam, fee, refundAddress)

For complete send implementation: See OFT Quickstart - Send Tokens for detailed examples using CLI, Foundry scripts, or Hardhat tasks.

Composability

Taxi mode supports composability - triggering additional actions on the destination chain after Stargate assets arrive.

Key Points:

  • Set composeMsg to your encoded data
  • Set to address to your composer contract
  • Add addExecutorLzComposeOption() for composer gas
  • Your composer receives via lzCompose() and decodes with OFTComposeMsgCodec
// Quick example: Send USDC with compose
bytes memory composeMsg = abi.encode(finalRecipient, action, params);
bytes memory extraOptions = OptionsBuilder.newOptions()
.addExecutorLzComposeOption(0, 200_000, 0);

SendParam memory sendParam = SendParam({
to: bytes32(uint256(uint160(composerAddress))), // Your composer
composeMsg: composeMsg, // Your data
extraOptions: extraOptions, // Compose gas
oftCmd: "" // Must use Taxi mode
// ... other fields
});

For complete composability implementation:

Finding Stargate Contracts

Method 1: Deployed Contracts Page

  1. Visit Deployed Contracts
  2. Search for your chain or asset (e.g., "USDC", "Ethereum")
  3. Stargate contracts appear at the top of each chain's list
  4. Copy the address you need

Method 2: Stargate API

// Mainnet
const response = await fetch('https://mainnet.stargate-api.com/v1/metadata?version=v2');
const data = await response.json();

// Find USDC on Ethereum
const asset = data.data.v2.find((a) => a.chainKey === 'ethereum' && a.token.symbol === 'USDC');

console.log('StargatePool USDC:', asset.address);

Next Steps

Learn More:

Build:

Get Help: