Send transactions across multiple chains in a single request, with gas payment settled on just one chain. This eliminates the need for users to hold gas tokens on every chain.
When using the relayer (without smart account), your transaction must include an ERC-20 transfer to Gelato’s fee collector. Use getCapabilities and getFeeQuote to get the fee collector address and fee amount, then include a token transfer in your transaction payload. This is handled automatically when using the EIP-7702 Smart Account.
Getting Started
Import Dependencies
import { createGelatoEvmRelayerClient } from '@gelatocloud/gasless';
Create Relayer Client
To create an API Key, visit the Gelato App and navigate to Relayer > API Keys.const relayer = createGelatoEvmRelayerClient({
apiKey: process.env.GELATO_API_KEY,
testnet: true // Use false for mainnet
});
Send Multichain Transactions
Submit transactions to multiple chains. One transaction pays with a token, others are sponsored:const taskIds = await relayer.sendTransactionMultichain([
{
// Transaction on Ethereum - pays for all transactions
chainId: 1,
to: '0x55f3a93f544e01ce4378d25e927d7c493b863bd7',
data: '0x29cb0f49',
payment: {
type: 'token',
address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' // USDC
}
},
{
// Transaction on Base - sponsored
chainId: 8453,
to: '0x45f3a93f544e01ce4378d25e927d7c493b863bd7',
data: '0x19ca0f49',
payment: {
type: 'sponsored'
}
},
{
// Transaction on Arbitrum - sponsored
chainId: 42161,
to: '0x35f3a93f544e01ce4378d25e927d7c493b863bd7',
data: '0x39cb0f49',
payment: {
type: 'sponsored'
}
}
]);
console.log('Task IDs:', taskIds);
Track Transaction Status
Each transaction returns its own task ID. Track them independently:const statuses = await Promise.all(
taskIds.map(id => relayer.waitForStatus({ id }))
);
statuses.forEach((status, index) => {
console.log(`Transaction ${index}: ${status.status}`);
if (status.receipt) {
console.log(` Hash: ${status.receipt.transactionHash}`);
}
});
Parameters
Each transaction in the array accepts the following parameters:
| Parameter | Type | Required | Description |
|---|
chainId | string | Yes | Target chain ID |
to | Address | Yes | Target contract address |
data | Hex | Yes | Transaction calldata |
payment | Payment | Yes | Payment configuration (see below) |
context | unknown | No | Optional context data |
authorizationList | Authorization[] | No | EIP-7702 authorizations |
Payment Types
| Type | Format | Description |
|---|
token | { type: 'token', address: '0x...' } | Pay with ERC-20 token at the specified address |
sponsored | { type: 'sponsored' } | Covered by another transaction’s payment |
Exactly one transaction in the batch must have a token payment type. All other transactions must use sponsored. The paying transaction can be at any position in the array.
Response
The method returns an array of task IDs, one for each transaction in the same order as the request:
const taskIds = await relayer.sendTransactionMultichain([...]);
// Returns: ['0x0e670ec6...', '0x0cf041f5...', '0x1ab234c7...']
Each task ID is a unique 32-byte identifier that can be used to track the transaction status.
Tracking Status
Track each transaction independently using its task ID:
// Track all transactions
const statuses = await Promise.all(
taskIds.map(id => relayer.waitForStatus({ id }))
);
// Check results
statuses.forEach((status, index) => {
console.log(`Transaction ${index}: ${status.status}`);
if (status.receipt) {
console.log(` Hash: ${status.receipt.transactionHash}`);
}
});
Additional Resources