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.
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 (oraddress(0)for native assets like ETH)approvalRequired(): Returnstrueif you need to approve tokens before sending,falsefor 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
quoteSend(_sendParam: SendParam, _payInLzToken: bool)quoteOFT() - Get Detailed Transfer Quote
quoteOFT(_sendParam: SendParam)send() - Transfer Tokens
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:
- Stargate Contract → Debits tokens (lock or burn depending on contract type)
- LayerZero Endpoint → Routes the message to the configured MessageLib
- Message Library (SendUln302) → Requests quotes from DVNs and Executor
- Workers (DVNs + Executor) → Provide fee quotes for verification and execution
- Fee Aggregation → Returns total
nativeFeefrom all workers
This multi-step process is why quoteSend() exists - it aggregates fees from all components in the security stack.
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
});
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
composeMsgto your encoded data - Set
toaddress to your composer contract - Add
addExecutorLzComposeOption()for composer gas - Your composer receives via
lzCompose()and decodes withOFTComposeMsgCodec
// 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:
- OFT Quickstart - Composer - Full examples and code
- Composer Overview - Deep dive on horizontal composability
- Composer Pattern - Architecture and concepts
Finding Stargate Contracts
Method 1: Deployed Contracts Page
- Visit Deployed Contracts
- Search for your chain or asset (e.g., "USDC", "Ethereum")
- Stargate contracts appear at the top of each chain's list
- 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:
- Stargate Finance Concepts - Architecture and how it works
- OFT Standard - Understanding IOFT interface
- Stargate Protocol Docs - Full protocol reference
Build:
- OVault Overview - Build omnichain vaults with Stargate assets
- Composer Overview - Advanced composability patterns
Get Help:
- LayerZero Discord - Technical support
- Stargate Discord - Stargate-specific questions