# Gelato Developer Documentation - Full Content > This file contains the complete content of all Gelato documentation pages. > It is designed to provide AI systems with comprehensive context about the Gelato platform. Generated: 2026-01-18 ## Quick Links - Documentation: https://docs.gelato.network - Dashboard: https://app.gelato.cloud - GitHub: https://github.com/gelatodigital --- ## Table of Contents ### Root - [Index](#index) - [Migration Guides](#migration-guides) ### Gasless With Relay - [Overview](#gasless-with-relay-gelato-turbo-relayer-overview) - [Quick Start](#gasless-with-relay-gelato-turbo-relayer-quick-start) - [Implementation Paths](#gasless-with-relay-gasless-transactions-evm-implementation-paths) - [Payment Methods](#gasless-with-relay-gasless-transactions-evm-payment-methods) - [Sync Methods](#gasless-with-relay-gasless-transactions-evm-sync-methods) - [Send Multichain](#gasless-with-relay-gasless-transactions-evm-send-multichain) - [7702 Turbobundler](#gasless-with-relay-gasless-transactions-evm-7702-turbobundler) - [Webhooks](#gasless-with-relay-gasless-transactions-evm-webhooks) - [Overview](#gasless-with-relay-gasless-transactions-tron-overview) - [Api Methods](#gasless-with-relay-gasless-transactions-tron-api-methods) - [Code Examples](#gasless-with-relay-gasless-transactions-tron-code-examples) - [Solana](#gasless-with-relay-gasless-transactions-solana-solana) - [Create A Api Key](#gasless-with-relay-how-to-guides-create-a-api-key) - [Send Sync Transactions](#gasless-with-relay-how-to-guides-send-sync-transactions) - [Send Multichain Transactions](#gasless-with-relay-how-to-guides-send-multichain-transactions) - [Overview](#gasless-with-relay-how-to-guides-pay-gas-with-erc20-tokens-overview) - [Overview](#gasless-with-relay-how-to-guides-sponsoredcalls-overview) - [Batching Transactions](#gasless-with-relay-how-to-guides-batching-transactions) - [Estimate Gas](#gasless-with-relay-how-to-guides-estimate-gas) - [Tracking Gelato Request](#gasless-with-relay-how-to-guides-tracking-gelato-request) - [Embedded Wallets](#gasless-with-relay-how-to-guides-embedded-wallets) - [Meta Tx](#gasless-with-relay-how-to-guides-meta-tx) - [Overview](#gasless-with-relay-relayer-api-overview) - [Relayer_Sendtransaction](#gasless-with-relay-relayer-api-endpoints-relayer-relayer_sendtransaction) - [Relayer_Sendtransactionsync](#gasless-with-relay-relayer-api-endpoints-relayer-relayer_sendtransactionsync) - [Relayer_Sendtransactionmultichain](#gasless-with-relay-relayer-api-endpoints-relayer-relayer_sendtransactionmultichain) - [Relayer_Getfeequote](#gasless-with-relay-relayer-api-endpoints-relayer-relayer_getfeequote) - [Relayer_Getstatus](#gasless-with-relay-relayer-api-endpoints-relayer-relayer_getstatus) - [Relayer_Getcapabilities](#gasless-with-relay-relayer-api-endpoints-relayer-relayer_getcapabilities) - [Relayer_Getfeedata](#gasless-with-relay-relayer-api-endpoints-relayer-relayer_getfeedata) - [Supported Networks](#gasless-with-relay-additional-resources-supported-networks) - [Erc20 Payment Tokens](#gasless-with-relay-additional-resources-erc20-payment-tokens) - [Demo](#gasless-with-relay-additional-resources-demo) - [Templates](#gasless-with-relay-additional-resources-templates) - [Erc2771](#gasless-with-relay-additional-resources-migration-to-turbo-relayer-erc2771) - [Syncfee](#gasless-with-relay-additional-resources-migration-to-turbo-relayer-syncfee) ### Paymaster & Bundler - [Overview](#paymaster--bundler-gelato-bundler-paymaster-overview) - [Quick Start](#paymaster--bundler-gelato-bundler-paymaster-quick-start) - [Implementation Paths](#paymaster--bundler-features-implementation-paths) - [Paymnet Methods](#paymaster--bundler-features-paymnet-methods) - [Smart Accounts](#paymaster--bundler-features-smart-accounts) - [Embedded Wallets](#paymaster--bundler-features-embedded-wallets) - [Eip 7702 Support](#paymaster--bundler-features-eip-7702-support) - [Create A Api Key](#paymaster--bundler-how-to-guides-create-a-api-key) - [Sponsor Gas With Gastank](#paymaster--bundler-how-to-guides-sponsor-gas-with-gastank) - [Overview](#paymaster--bundler-how-to-guides-pay-with-erc20-tokens-overview) - [Direct Payment](#paymaster--bundler-how-to-guides-pay-with-erc20-tokens-direct-payment) - [Onchain Paymaster](#paymaster--bundler-how-to-guides-pay-with-erc20-tokens-onchain-paymaster) - [Pay With Native](#paymaster--bundler-how-to-guides-pay-with-native) - [Native Payments](#paymaster--bundler-how-to-guides-native-payments) - [Estimate Gas](#paymaster--bundler-how-to-guides-estimate-gas) - [Tracking Gelato Request](#paymaster--bundler-how-to-guides-tracking-gelato-request) - [Embedded Wallets](#paymaster--bundler-how-to-guides-embedded-wallets) - [Introduction](#paymaster--bundler-gastank-introduction) - [Setting Up Gastank](#paymaster--bundler-gastank-setting-up-gastank) - [Usdc Addresess](#paymaster--bundler-gastank-usdc-addresess) - [Gastank Alerts](#paymaster--bundler-gastank-gastank-alerts) - [Eth_Senduseroperation](#paymaster--bundler-bundler-api-endpoints-bundlers-eth_senduseroperation) - [Eth_Senduseroperationsync](#paymaster--bundler-bundler-api-endpoints-bundlers-eth_senduseroperationsync) - [Eth_Estimateuseroperationgas](#paymaster--bundler-bundler-api-endpoints-bundlers-eth_estimateuseroperationgas) - [Eth_Getuseroperationbyhash](#paymaster--bundler-bundler-api-endpoints-bundlers-eth_getuseroperationbyhash) - [Eth_Getuseroperationreceipt](#paymaster--bundler-bundler-api-endpoints-bundlers-eth_getuseroperationreceipt) - [Eth_Supportedentrypoints](#paymaster--bundler-bundler-api-endpoints-bundlers-eth_supportedentrypoints) - [Eth_Chainid](#paymaster--bundler-bundler-api-endpoints-bundlers-eth_chainid) - [Gelato_Getuseroperationgasprice](#paymaster--bundler-bundler-api-endpoints-bundlers-gelato_getuseroperationgasprice) - [Gelato_Getuseroperationquote](#paymaster--bundler-bundler-api-endpoints-bundlers-gelato_getuseroperationquote) - [Wallet_Getcapabilities](#paymaster--bundler-smart-wallet-endpoints-wallet_getcapabilities) - [Wallet_Preparecalls](#paymaster--bundler-smart-wallet-endpoints-wallet_preparecalls) - [Wallet_Sendpreparedcalls](#paymaster--bundler-smart-wallet-endpoints-wallet_sendpreparedcalls) - [Wallet_Sendtransaction](#paymaster--bundler-smart-wallet-endpoints-wallet_sendtransaction) - [Supported Networks](#paymaster--bundler-additional-resources-supported-networks) - [Erc20 Payment Tokens](#paymaster--bundler-additional-resources-erc20-payment-tokens) - [Faq](#paymaster--bundler-additional-resources-faq) - [Troubleshooting](#paymaster--bundler-additional-resources-troubleshooting) - [Revenue Policies](#paymaster--bundler-monetization-revenue-policies) ### Pricing - [Pricing Plans](#pricing-pricing-plans) - [Compute Units](#pricing-compute-units) - [Compute Units Costs](#pricing-compute-units-costs) ### Private Rpcs - [Introduction](#private-rpcs-introduction) - [Get A Private Rpc](#private-rpcs-how-to-guides-get-a-private-rpc) - [Supported Networks](#private-rpcs-additional-resources-supported-networks) - [Faq](#private-rpcs-additional-resources-faq) ### Relay - [Overview](#relay-introduction-overview) - [What Is Relaying](#relay-introduction-what-is-relaying) - [Overview](#relay-erc2771-recommended-overview) - [Overview](#relay-erc2771-recommended-callwithsyncfee-erc2771-overview) - [Relay Context Contract Erc 2771](#relay-erc2771-recommended-callwithsyncfee-erc2771-relay-context-contract-erc-2771) - [Sponsoredcall Erc2771](#relay-erc2771-recommended-sponsoredcall-erc2771) - [Overview](#relay-non-erc2771-callwithsyncfee-overview) - [Relay Context Contract](#relay-non-erc2771-callwithsyncfee-relay-context-contract) - [Sponsoredcall](#relay-non-erc2771-sponsoredcall) - [Create A Sponsor Api Key](#relay-how-to-guides-create-a-sponsor-api-key) - [Send Sponsored Transactions](#relay-how-to-guides-send-sponsored-transactions) - [Allow Your Users To Pay With Erc20](#relay-how-to-guides-allow-your-users-to-pay-with-erc20) - [Allow Your Target Contract To Pay For Gas](#relay-how-to-guides-allow-your-target-contract-to-pay-for-gas) - [Decode Original Msg.Sender In Target Contract](#relay-how-to-guides-decode-original-msg.sender-in-target-contract) - [Track Your Relay Request](#relay-how-to-guides-track-your-relay-request) - [Gelato'S Fee Oracle](#relay-api--feeoracle-gelato's-fee-oracle) - [Get All The Payment Tokens On A Chain](#relay-api--feeoracle-oracles-get-all-the-payment-tokens-on-a-chain) - [Get List Of Chains Where The Oracle Is Available](#relay-api--feeoracle-oracles-get-list-of-chains-where-the-oracle-is-available) - [Get The Conversion Rate From The Native Token To The Requested Token](#relay-api--feeoracle-oracles-get-the-conversion-rate-from-the-native-token-to-the-requested-token) - [Get The Estimated Fee In Payment Token With Respect To Gas Limit And Priority](#relay-api--feeoracle-oracles-get-the-estimated-fee-in-payment-token-with-respect-to-gas-limit-and-priority) - [Get List Of Chains Where Relay V2 Is Available](#relay-api--feeoracle-relays-v2-get-list-of-chains-where-relay-v2-is-available) - [Place A Relay V2 Callwithsyncfee Request](#relay-api--feeoracle-relays-v2-place-a-relay-v2-callwithsyncfee-request) - [Place A Relay V2 Callwithsyncfeeerc2771 Request](#relay-api--feeoracle-relays-v2-place-a-relay-v2-callwithsyncfeeerc2771-request) - [Place A Relay V2 Sponsoredcall Request](#relay-api--feeoracle-relays-v2-place-a-relay-v2-sponsoredcall-request) - [Place A Relay V2 Sponsoredcallerc2771 Request](#relay-api--feeoracle-relays-v2-place-a-relay-v2-sponsoredcallerc2771-request) - [Get Task Status Of The Relay V2 Task Id](#relay-api--feeoracle-tasks-get-task-status-of-the-relay-v2-task-id) - [Retrieve Debug Information For A Specific Task](#relay-api--feeoracle-tasks-retrieve-debug-information-for-a-specific-task) - [Overview](#relay-security-considerations-overview) - [Erc2771 Delegatecall Vulnerability](#relay-security-considerations-erc2771-delegatecall-vulnerability) - [Supported Networks](#relay-additional-resources-supported-networks) - [Syncfee Payment Tokens](#relay-additional-resources-syncfee-payment-tokens) - [Templates](#relay-additional-resources-templates) - [Erc2771 Migration Guide](#relay-additional-resources-erc2771-migration-guide) ### Rollup As A Service - [Introduction](#rollup-as-a-service-introduction) - [Op](#rollup-as-a-service-rollup-stacks-op) - [Arbitrum Orbit](#rollup-as-a-service-rollup-stacks-arbitrum-orbit) - [Abc](#rollup-as-a-service-rollup-stacks-abc) - [Celestia](#rollup-as-a-service-data-availability-celestia) - [Avail](#rollup-as-a-service-data-availability-avail) - [Eigenda](#rollup-as-a-service-data-availability-eigenda) - [Custom Gas Token](#rollup-as-a-service-customization-custom-gas-token) - [Flashblocks](#rollup-as-a-service-customization-flashblocks) - [Public Testnet](#rollup-as-a-service-customization-public-testnet) - [Verifier Node Package](#rollup-as-a-service-customization-verifier-node-package) - [Deploy Your Own Rollup](#rollup-as-a-service-how-to-guides-deploy-your-own-rollup) - [Run An Op Node](#rollup-as-a-service-how-to-guides-run-an-op-node) - [Run An Orbit Node](#rollup-as-a-service-how-to-guides-run-an-orbit-node) - [Run A Abc Node](#rollup-as-a-service-how-to-guides-run-a-abc-node) - [Run A Verifier Node](#rollup-as-a-service-how-to-guides-run-a-verifier-node) - [Gelato Services](#rollup-as-a-service-marketplace-gelato-services) - [Account Abstraction](#rollup-as-a-service-marketplace-account-abstraction) - [Block Explorers](#rollup-as-a-service-marketplace-block-explorers) - [Bridges](#rollup-as-a-service-marketplace-bridges) - [Data Indexers](#rollup-as-a-service-marketplace-data-indexers) - [Oracles](#rollup-as-a-service-marketplace-oracles) - [Identity & Kyc](#rollup-as-a-service-marketplace-identity--kyc) - [On & Off Ramp](#rollup-as-a-service-marketplace-on--off-ramp) - [Community](#rollup-as-a-service-marketplace-community) - [Others](#rollup-as-a-service-marketplace-others) ### Smart Wallet Sdk - [Overview](#smart-wallet-sdk-introduction-overview) - [Understanding Eip 7702](#smart-wallet-sdk-introduction-understanding-eip-7702) - [Eip 7702 Vs Erc 4337](#smart-wallet-sdk-introduction-eip-7702-vs-erc-4337) - [Overview](#smart-wallet-sdk-embedded-wallets-overview) - [Quickstart](#smart-wallet-sdk-embedded-wallets-quickstart) - [Create Dynamic'S Environment Id](#smart-wallet-sdk-embedded-wallets-create-dynamic's-environment-id) - [Adding Custom Networks With Dynamic](#smart-wallet-sdk-embedded-wallets-adding-custom-networks-with-dynamic) - [Gelato](#smart-wallet-sdk-smart-accounts-gelato) - [Kernel](#smart-wallet-sdk-smart-accounts-other-smart-accounts-kernel) - [Safe](#smart-wallet-sdk-smart-accounts-other-smart-accounts-safe) - [Alchemy](#smart-wallet-sdk-smart-accounts-other-smart-accounts-alchemy) - [Biconomy](#smart-wallet-sdk-smart-accounts-other-smart-accounts-biconomy) - [Coinbase](#smart-wallet-sdk-smart-accounts-other-smart-accounts-coinbase) - [Okx](#smart-wallet-sdk-smart-accounts-other-smart-accounts-okx) - [Thirdweb](#smart-wallet-sdk-smart-accounts-other-smart-accounts-thirdweb) - [Trust](#smart-wallet-sdk-smart-accounts-other-smart-accounts-trust) - [Privy](#smart-wallet-sdk-integration-guides-privy) - [Dynamic](#smart-wallet-sdk-integration-guides-dynamic) - [Web3Auth](#smart-wallet-sdk-integration-guides-web3auth) - [Turnkey](#smart-wallet-sdk-integration-guides-turnkey) - [Para](#smart-wallet-sdk-integration-guides-para) - [Coinbase](#smart-wallet-sdk-integration-guides-coinbase) - [Overview](#smart-wallet-sdk-how-to-guides-overview) - [Create A Api Key](#smart-wallet-sdk-how-to-guides-create-a-api-key) - [Sponsor Gas](#smart-wallet-sdk-how-to-guides-sponsor-gas) - [Allow User To Pay With Erc20](#smart-wallet-sdk-how-to-guides-allow-user-to-pay-with-erc20) - [Allow User To Pay With Native](#smart-wallet-sdk-how-to-guides-allow-user-to-pay-with-native) - [Batching Transactions](#smart-wallet-sdk-how-to-guides-batching-transactions) - [Estimate Gas](#smart-wallet-sdk-how-to-guides-estimate-gas) - [Tracking Gelato Request](#smart-wallet-sdk-how-to-guides-tracking-gelato-request) - [Applied Use Cases Morpho Demo](#smart-wallet-sdk-how-to-guides-applied-use-cases-morpho-demo) - [Server Wallets](#smart-wallet-sdk-applied-use-cases-server-wallets) - [Morpho](#smart-wallet-sdk-applied-use-cases-morpho) - [Wallet_Getcapabilities](#smart-wallet-sdk-smart-wallet-endpoints-smartwallet-wallet_getcapabilities) - [Wallet_Preparecalls](#smart-wallet-sdk-smart-wallet-endpoints-smartwallet-wallet_preparecalls) - [Wallet_Sendpreparedcalls](#smart-wallet-sdk-smart-wallet-endpoints-smartwallet-wallet_sendpreparedcalls) - [Wallet_Sendtransaction](#smart-wallet-sdk-smart-wallet-endpoints-smartwallet-wallet_sendtransaction) - [Demo](#smart-wallet-sdk-additional-resources-demo) - [Supported Networks](#smart-wallet-sdk-additional-resources-supported-networks) - [Erc20 Payment Tokens](#smart-wallet-sdk-additional-resources-erc20-payment-tokens) ### Vrf - [Overview](#vrf-introduction-overview) - [How Gelato Vrf Works](#vrf-introduction-how-gelato-vrf-works) - [Understanding Vrf](#vrf-introduction-understanding-vrf) - [Deploy Your Contract Inheriting Gelato Vrf](#vrf-how-to-guides-deploy-your-contract-inheriting-gelato-vrf) - [Create A Vrf Task](#vrf-how-to-guides-create-a-vrf-task) - [Create A Fallback Vrf](#vrf-how-to-guides-create-a-fallback-vrf) - [Migrate From Chainlink Vrf](#vrf-how-to-guides-migrate-from-chainlink-vrf) - [Security Considerations](#vrf-security-considerations) - [Supported Networks](#vrf-additional-resources-supported-networks) - [Pricing And Rate Limits](#vrf-additional-resources-pricing-and-rate-limits) - [Templates](#vrf-additional-resources-templates) ### Web3 Functions - [Overview](#web3-functions-introduction-overview) - [Typescript Functions](#web3-functions-introduction-typescript-functions) - [Solidity Functions](#web3-functions-introduction-solidity-functions) - [Trigger Types](#web3-functions-introduction-trigger-types) - [Automated Transactions](#web3-functions-introduction-automated-transactions) - [Getting Started](#web3-functions-how-to-guides-write-typescript-functions-getting-started) - [Event Trigger](#web3-functions-how-to-guides-write-typescript-functions-event-trigger) - [Callbacks](#web3-functions-how-to-guides-write-typescript-functions-callbacks) - [Private Typescript Functions](#web3-functions-how-to-guides-write-typescript-functions-private-typescript-functions) - [Write Solidity Functions](#web3-functions-how-to-guides-write-solidity-functions) - [Test Deploy Typescript Functions](#web3-functions-how-to-guides-test-deploy-typescript-functions) - [Test Deploy Solidity Functions](#web3-functions-how-to-guides-test-deploy-solidity-functions) - [Using Ui](#web3-functions-how-to-guides-create-a-web3-functions-task-using-ui) - [Using The Automate Sdk](#web3-functions-how-to-guides-create-a-web3-functions-task-using-the-automate-sdk) - [Using Smart Contract](#web3-functions-how-to-guides-create-a-web3-functions-task-using-smart-contract) - [Using Safe Ui](#web3-functions-how-to-guides-create-a-web3-functions-task-using-safe-ui) - [Initiate An Automated Transactions](#web3-functions-how-to-guides-initiate-an-automated-transactions) - [Dedicated Msg Sender](#web3-functions-security-considerations-dedicated-msg-sender) - [Supported Networks](#web3-functions-additional-resources-supported-networks) - [Analytics And Monitoring](#web3-functions-additional-resources-analytics-and-monitoring) - [Templates And Use Cases](#web3-functions-additional-resources-templates-and-use-cases) - [Transaction Pays For Itself](#web3-functions-additional-resources-transaction-pays-for-itself) --- ## Full Documentation Content ================================================================================ # Root ================================================================================ --- ## Gelato Developer Docs **Path:** /index
Build gasless, seamless onchain experiences. Enable users to transact without holding native tokens.
Provides infrastructure for Account Abstraction, supporting both ERC-4337 and EIP-7702.
Gasless transactions across EVM, TRON, and Solana. Pay with ERC20s
Native EIP-7702 gasless transactions. #1 in gas efficiency and latency.
Send sponsored transactions with Paymaster & Bundler
Send transactions and wait for results in one call
Send transactions across multiple chains
Sponsored, ERC-20, and native token payments
Build smart wallets with EIP-7702 and ERC-4337. Sponsor transactions, batch calls, and integrate with any wallet provider.
Learn more →
Deploy your own L2 rollup in minutes. OP Stack, Arbitrum Orbit, and custom configurations available.
Learn more →
After creating the API Key, navigate to its dashboard to locate your API Key.
Gelato API Keys now supports API key rotation, allowing users to create and delete API keys. This helps prevent unauthorized usage in case an API key is exposed.
`Activate` your API key by allowing access to `all contracts` on a network, or restrict it to `specific contracts` or `specific functions` in policies section.
Here, you can configure different networks. For each network, you can choose to allow access to all target contracts or limit it to selected contracts or specific functions.
Before you can start sponsoring gas with Gas Tank, you need to setup your Gas Tank. Check out our [Guide](/paymaster-&-bundler/gastank/setting-up-gastank) for detailed instructions on setting up your Gas Tank.
For `Sponsorship` purposes, add funds to your Gas Tank account according to your target environment:
- **Mainnets**: Deposit USDC.
- **Testnets**: Deposit Sepolia ETH.
Since Gas Tank is deployed on Polygon, you can deposit USDC in one step, and deposits from other networks are supported via Circle CCTP. Learn [more](/paymaster-&-bundler/gastank/introduction).
---
## Send Sync Transactions
**Path:** /gasless-with-relay/how-to-guides/send-sync-transactions
Send transactions and wait for the final result in a single call. Sync methods are ideal for fast chains where you want immediate confirmation feedback.
## Getting Started
```typescript
import { createGelatoEvmRelayerClient, sponsored, StatusCode } from '@gelatocloud/gasless';
```
To create an API Key, visit the [Gelato App](https://app.gelato.cloud/) and navigate to `Relayer > API Keys`.
```typescript
const relayer = createGelatoEvmRelayerClient({
apiKey: process.env.GELATO_API_KEY,
testnet: true // Use false for mainnet
});
```
Submit a transaction and wait for the final result:
```typescript
const status = await relayer.sendTransactionSync({
chainId: 84532,
to: '0xContractAddress',
data: '0xCalldata',
payment: sponsored(),
timeout: 30000 // Required: max wait time in ms
});
if (status.status === StatusCode.Included) {
console.log('TX hash:', status.receipt.transactionHash);
} else {
console.log('Failed:', status.message);
}
```
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `chainId` | `number` | Yes | Target chain ID |
| `to` | `Address` | Yes | Target contract address |
| `data` | `Hex` | Yes | Transaction calldata |
| `payment` | `Payment` | Yes | `sponsored()`, `token(addr)`, or `native()` |
| `timeout` | `number` | Yes | Max wait time in milliseconds |
| `authorizationList` | `Authorization[]` | No | EIP-7702 authorizations |
| `context` | `unknown` | No | Optional context (e.g., from fee quote) |
## Return Type: TerminalStatus
The sync method returns a `TerminalStatus` object:
```typescript
type TerminalStatus = {
status: StatusCode.Included | StatusCode.Rejected | StatusCode.Reverted;
receipt?: {
transactionHash: Hex;
blockNumber: bigint;
// ... other receipt fields
};
message?: string; // Error message if rejected/reverted
data?: unknown; // Additional error data
};
```
**Status codes:**
| Code | Value | Description |
|------|-------|-------------|
| `Included` | 200 | Transaction successfully included on-chain |
| `Rejected` | 400 | Transaction rejected by relayer |
| `Reverted` | 500 | Transaction reverted on-chain |
## Async vs Sync Comparison
```typescript
// Returns immediately with task ID
const taskId = await relayer.sendTransaction({
chainId: 84532,
to: '0xContract',
data: '0xCalldata',
payment: sponsored()
});
// Poll for status separately
const status = await relayer.waitForStatus({ id: taskId });
```
```typescript
// Waits and returns final status in one call
const status = await relayer.sendTransactionSync({
chainId: 84532,
to: '0xContract',
data: '0xCalldata',
payment: sponsored(),
timeout: 30000
});
```
## Additional Resources
- [Sync Methods Overview](/gasless-with-relay/gasless-transactions-evm/sync-methods) - Feature overview
- [Payment Methods](/gasless-with-relay/gasless-transactions-evm/payment-methods) - Available payment options
- [Tracking Requests](/gasless-with-relay/how-to-guides/tracking-gelato-request) - How to track transaction status
---
## Send Multichain Transactions
**Path:** /gasless-with-relay/how-to-guides/send-multichain-transactions
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
```typescript
import { createGelatoEvmRelayerClient } from '@gelatocloud/gasless';
```
To create an API Key, visit the [Gelato App](https://app.gelato.cloud/) and navigate to `Relayer > API Keys`.
```typescript
const relayer = createGelatoEvmRelayerClient({
apiKey: process.env.GELATO_API_KEY,
testnet: true // Use false for mainnet
});
```
Submit transactions to multiple chains. One transaction pays with a token, others are sponsored:
```typescript
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);
```
Each transaction returns its own task ID. Track them independently:
```typescript
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:
```typescript
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:
```typescript
// 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
- [Multichain Transactions Overview](/gasless-with-relay/gasless-transactions-evm/send-multichain) - Detailed feature documentation
- [relayer_sendTransactionMultichain API](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_sendtransactionmultichain) - Full API reference
- [Tracking Requests](/gasless-with-relay/how-to-guides/tracking-gelato-request) - How to track transaction status
---
## Pay with ERC-20 Tokens
**Path:** /gasless-with-relay/how-to-guides/pay-gas-with-erc20-tokens/overview
Allow users to pay gas fees using ERC-20 tokens like USDC, USDT, or other supported tokens.
## Implementations
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 7702 Smart Account.
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
Check out our [How-To Guide](/gasless-with-relay/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import { createGelatoEvmRelayerClient, StatusCode, token } from '@gelatocloud/gasless';
import { createPublicClient, encodeFunctionData, http } from 'viem';
import { baseSepolia } from 'viem/chains';
const USDC_ADDRESS = '0x036CbD53842c5426634e7929541eC2318f3dCF7e'; // Base Sepolia
const relayer = createGelatoEvmRelayerClient({
apiKey: process.env.GELATO_API_KEY,
testnet: true
});
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http()
});
```
Get the fee collector address for your chain:
```typescript
const capabilities = await relayer.getCapabilities();
const feeCollector = capabilities[baseSepolia.id].feeCollector;
```
Estimate gas and get the fee amount in your payment token:
```typescript
const gasEstimate = await publicClient.estimateGas({
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De',
data: '0xd09de08a'
});
const quote = await relayer.getFeeQuote({
chainId: baseSepolia.id,
gas: gasEstimate,
token: USDC_ADDRESS
});
console.log('Fee:', quote.fee); // Fee amount in token units
```
Build calldata that includes a token transfer to the fee collector:
```typescript
// Encode ERC-20 transfer to fee collector
const transferData = encodeFunctionData({
abi: [{
name: 'transfer',
type: 'function',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' }
],
outputs: [{ type: 'bool' }]
}],
functionName: 'transfer',
args: [feeCollector, quote.fee]
});
// Your main transaction calldata
const targetCalldata = '0xd09de08a'; // increment()
// Use a multicall contract or your own contract to batch:
// 1. Transfer fee to feeCollector
// 2. Execute your target call
const data = encodeMulticall([
{ to: USDC_ADDRESS, data: transferData },
{ to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De', data: targetCalldata }
]);
```
```typescript
const id = await relayer.sendTransaction({
chainId: baseSepolia.id,
data: data,
payment: token(USDC_ADDRESS),
to: multicallContractAddress
});
console.log(`Gelato transaction id: ${id}`);
const status = await relayer.waitForStatus({ id });
if (status.status === StatusCode.Included) {
console.log(`Transaction hash: ${status.receipt.transactionHash}`);
} else {
console.log(`Transaction failed: ${status.message}`);
}
```
When using the 7702 Smart Account, fee payment is handled automatically. You don't need to manually include a transfer to the fee collector.
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
Check out our [How-To Guide](/gasless-with-relay/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import {
createGelatoSmartAccountClient,
toGelatoSmartAccount,
token,
StatusCode,
} from "@gelatocloud/gasless";
import { createPublicClient, http, type Hex } from "viem";
import { baseSepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = toGelatoSmartAccount({
client,
owner,
});
```
```typescript
const relayer = await createGelatoSmartAccountClient({
account,
apiKey: process.env.GELATO_API_KEY,
});
```
```typescript
const tokenAddress = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"; // USDC (Base Sepolia)
const result = await relayer.sendTransactionSync({
payment: token(tokenAddress),
calls: [
{
to: "0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De",
data: "0xd09de08a",
},
],
});
if (result.status === StatusCode.Included) {
console.log(`Transaction hash: ${result.receipt.transactionHash}`);
} else {
console.log(`Failed: ${result.message}`);
}
```
## Supported Tokens
Check out the full list of [ERC-20 Payment Tokens](/gasless-with-relay/additional-resources/erc20-payment-tokens) supported on each network.
## Additional Resources
- [Estimate Gas Costs](/gasless-with-relay/how-to-guides/estimate-gas) - Get fee quotes before sending
- [Sponsor Gas with Gas Tank](/gasless-with-relay/how-to-guides/sponsoredcalls/overview) - Alternative payment method
- [Supported Networks](/gasless-with-relay/additional-resources/supported-networks) - Full list of supported chains
---
## Sponsor Gas with Gas Tank
**Path:** /gasless-with-relay/how-to-guides/sponsoredcalls/overview
Sponsor gas fees for your users using Gelato's cross-chain Gas Tank. Deposit funds once and sponsor transactions across all supported networks.
## Implementations
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
Check out our [How-To Guide](/gasless-with-relay/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import { createGelatoEvmRelayerClient, StatusCode, sponsored } from '@gelatocloud/gasless';
import { baseSepolia } from 'viem/chains';
const relayer = createGelatoEvmRelayerClient({
apiKey: process.env.GELATO_API_KEY,
testnet: baseSepolia.testnet
});
```
Generate the payload for your target contract function:
```typescript
import { encodeFunctionData } from 'viem';
const data = encodeFunctionData({
abi: [{ name: 'increment', type: 'function', inputs: [], outputs: [] }],
functionName: 'increment'
});
```
```typescript
const id = await relayer.sendTransaction({
chainId: baseSepolia.id,
data: data,
payment: sponsored(),
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De'
});
console.log(`Gelato transaction id: ${id}`);
const status = await relayer.waitForStatus({ id });
if (status.status === StatusCode.Included) {
console.log(`Transaction hash: ${status.receipt.transactionHash}`);
} else {
console.log(`Transaction failed: ${status.message}`);
}
```
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
Check out our [How-To Guide](/gasless-with-relay/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import {
createGelatoSmartAccountClient,
toGelatoSmartAccount,
sponsored,
StatusCode,
} from "@gelatocloud/gasless";
import { createPublicClient, http, type Hex } from "viem";
import { baseSepolia } from "viem/chains";
import { privateKeyToAccount } from "viem/accounts";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = toGelatoSmartAccount({
client,
owner,
});
```
```typescript
const relayer = await createGelatoSmartAccountClient({
account,
apiKey: process.env.GELATO_API_KEY,
});
```
```typescript
const result = await relayer.sendTransactionSync({
payment: sponsored(),
calls: [
{
to: "0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De",
data: "0xd09de08a",
},
],
});
if (result.status === StatusCode.Included) {
console.log(`Transaction hash: ${result.receipt.transactionHash}`);
} else {
console.log(`Failed: ${result.message}`);
}
```
Use `https://api.gelato.cloud` for mainnets, or `https://api.t.gelato.cloud` for testnets. Pass the API key in the `X-API-Key` header.
Check out our [How-To Guide](/gasless-with-relay/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
const response = await fetch('https://api.t.gelato.cloud/rpc', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'relayer_sendTransaction',
params: {
chainId: '84532',
to: '0x...',
data: '0x...',
payment: { type: 'sponsored' }
}
})
});
const data = await response.json();
const taskId = data.result;
console.log(`Task ID: ${taskId}`);
```
Poll for the transaction status using `relayer_getStatus`:
```typescript
const statusResponse = await fetch('https://api.t.gelato.cloud/rpc', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': API_KEY
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'relayer_getStatus',
params: { id: taskId }
})
});
const statusData = await statusResponse.json();
const status = statusData.result.status;
// 200 = Included, 400 = Rejected, 500 = Reverted
if (status === 200) {
console.log(`Transaction hash: ${statusData.result.receipt.transactionHash}`);
} else if (status >= 400) {
console.log(`Failed: ${statusData.result.message}`);
}
```
## Sponsor Gas Playground
## Additional Resources
- [Pay with ERC-20 Tokens](/gasless-with-relay/how-to-guides/pay-gas-with-erc20-tokens/overview) - Alternative payment method
- [Supported Networks](/gasless-with-relay/additional-resources/supported-networks) - Full list of supported chains
- [ERC-20 Payment Tokens](/gasless-with-relay/additional-resources/erc20-payment-tokens) - Supported tokens
---
## Batching Transactions
**Path:** /gasless-with-relay/how-to-guides/batching-transactions
Batching transactions combines multiple operations into a single transaction. Instead of staking tokens and claiming rewards separately, users can perform both in one transaction.
## Getting Started
```typescript
import {
createGelatoSmartAccountClient,
toGelatoSmartAccount,
sponsored,
StatusCode,
} from "@gelatocloud/gasless";
import { createPublicClient, http, type Hex, encodeFunctionData } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
```
```typescript
const owner = privateKeyToAccount(
(process.env.PRIVATE_KEY ?? generatePrivateKey()) as Hex
);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = toGelatoSmartAccount({
client,
owner,
});
```
To create an API Key, visit the [Gelato App](https://app.gelato.cloud/) and navigate to `Paymaster & Bundler > API Keys`.
```typescript
const relayer = await createGelatoSmartAccountClient({
account,
apiKey: process.env.GELATO_API_KEY,
});
```
Add multiple operations to the `calls` array:
```typescript
const result = await relayer.sendTransactionSync({
payment: sponsored(),
calls: [
{
to: "
### Steps to Debug
1. Go to the **logs** section and locate your failed relay request.
2. On the right side of the log entry, click the **Debug** button.
3. A new option, **View Debug**, will appear. Click it.
4. This will open a **Tenderly simulation**, which you can use to analyze and debug the failed request.
## Using Status Endpoint
If you call the `relayer_sendTransaction` or `relayer_sendTransactionSync` API endpoints, the returned `id` can also be used to track the status of the transaction through Gelato's infrastructure like this:
```bash
curl --request POST \
--url https://api.gelato.cloud/rpc \
--header 'Content-Type: application/json' \
--header 'X-API-Key: YOUR_API_KEY' \
--data '{
"id": 1,
"jsonrpc": "2.0",
"method": "relayer_getStatus",
"params": {
"id": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
"logs": false
}
}'
```
### Response
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
"status": 200,
"message": "Transaction included",
"receipt": {
"transactionHash": "0x...",
"blockNumber": 12345678,
"gasUsed": "21000"
}
}
}
```
## Status Codes
The `relayer_getStatus` endpoint returns numeric status codes:
| Code | Status | Description |
|------|--------|-------------|
| `100` | Pending | Transaction is queued and waiting to be processed |
| `110` | Submitted | Transaction has been submitted to the network |
| `200` | Included | Transaction was successfully included in a block |
| `400` | Rejected | Transaction was rejected (invalid parameters, insufficient funds, etc.) |
| `500` | Reverted | Transaction was included but execution reverted |
### Using Status Codes in Code
When using the SDK, you can import `StatusCode` for type-safe comparisons:
```typescript
const result = await relayer.sendTransactionSync({ ... });
if (result.status === StatusCode.Included) {
console.log(`Success! Hash: ${result.receipt.transactionHash}`);
} else if (result.status === StatusCode.Reverted) {
console.log(`Transaction reverted: ${result.message}`);
} else if (result.status === StatusCode.Rejected) {
console.log(`Transaction rejected: ${result.message}`);
}
```
When using the API directly, check against the numeric values:
```typescript
const data = await response.json();
const status = data.result.status;
// Terminal states
if (status === 200) {
console.log('Transaction included');
} else if (status === 400) {
console.log('Transaction rejected');
} else if (status === 500) {
console.log('Transaction reverted');
}
// Non-terminal states (keep polling)
else if (status === 100 || status === 110) {
console.log('Transaction pending...');
}
```
---
## Embedded Wallet Integrations
**Path:** /gasless-with-relay/how-to-guides/embedded-wallets
Embedded wallets enable users to interact with your dApp using familiar login methods like email, phone, or social accounts. Gelato Gasless SDK integrates seamlessly with popular embedded wallet providers.
## Quick Start
### Installation
```bash
npm install @gelatocloud/gasless @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @dynamic-labs/wagmi-connector @tanstack/react-query wagmi viem
```
### Setup
1. Create an app at [Dynamic Dashboard](https://app.dynamic.xyz/) and enable `Embedded Wallets`
2. Get your Gelato API Key from [Gelato App](https://app.gelato.cloud/)
```bash
NEXT_PUBLIC_DYNAMIC_APP_ID=your_dynamic_environment_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
### Configure Providers
```typescript
const queryClient = new QueryClient();
export default function Providers({ children }: { children: React.ReactNode }) {
return (
{children} );
}
```
### Create Gelato Smart Account Client
```typescript
const { primaryWallet } = useDynamicContext();
if (!primaryWallet || !isEthereumWallet(primaryWallet)) return;
const connector = primaryWallet.connector;
if (!connector || !isDynamicWaasConnector(connector)) return;
const client = await primaryWallet.getWalletClient();
client.account.signAuthorization = async (parameters) => {
const preparedAuthorization = await prepareAuthorization(client, parameters);
const signedAuthorization = await connector.signAuthorization(preparedAuthorization);
return {
address: preparedAuthorization.address,
chainId: preparedAuthorization.chainId,
nonce: preparedAuthorization.nonce,
r: signedAuthorization.r,
s: signedAuthorization.s,
v: signedAuthorization.v,
yParity: signedAuthorization.yParity,
} as SignAuthorizationReturnType;
};
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const relayer = await createGelatoSmartAccountClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
});
```
[Dynamic Docs](https://docs.dynamic.xyz/)
### Installation
```bash
npm install @gelatocloud/gasless @privy-io/react-auth @privy-io/wagmi @tanstack/react-query viem
```
### Setup
1. Create an app at [Privy Dashboard](https://dashboard.privy.io/) and enable `Embedded Wallets`
2. Get your Gelato API Key from [Gelato App](https://app.gelato.cloud/)
```bash
NEXT_PUBLIC_PRIVY_APP_ID=your_privy_app_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
### Configure Providers
```typescript
const queryClient = new QueryClient();
const wagmiConfig = createConfig({
chains: [baseSepolia],
transports: { [baseSepolia.id]: http() },
});
const privyConfig: PrivyClientConfig = {
embeddedWallets: {
createOnLogin: "users-without-wallets",
requireUserPasswordOnCreate: false,
},
loginMethods: ["email"],
};
export const App = () => (
);
```
### Create Gelato Smart Account Client
```typescript
const { wallets } = useWallets();
const { signAuthorization } = useSign7702Authorization();
const primaryWallet = wallets[0];
const provider = await primaryWallet?.getEthereumProvider();
const client = createWalletClient({
account: primaryWallet.address as Hex,
chain: baseSepolia,
transport: custom(provider),
});
client.account.signAuthorization = async (parameters) => {
const preparedAuthorization = await prepareAuthorization(client, parameters);
return await signAuthorization({
chainId: preparedAuthorization.chainId,
contractAddress: preparedAuthorization.address,
nonce: preparedAuthorization.nonce,
});
};
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const relayer = await createGelatoSmartAccountClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
});
```
[Privy Docs](https://docs.privy.io/)
### Installation
```bash
npm install @gelatocloud/gasless @turnkey/sdk-browser @turnkey/viem viem
```
### Setup
1. Create an organization at [Turnkey Dashboard](https://app.turnkey.com/)
2. Get your Gelato API Key from [Gelato App](https://app.gelato.cloud/)
```bash
NEXT_PUBLIC_TURNKEY_ORG_ID=your_turnkey_org_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
### Initialize Turnkey
```typescript
const turnkey = new Turnkey({
apiBaseUrl: "https://api.turnkey.com",
defaultOrganizationId: process.env.NEXT_PUBLIC_TURNKEY_ORG_ID!,
});
const account = await createAccount({
client: turnkey.apiClient(),
organizationId: process.env.NEXT_PUBLIC_TURNKEY_ORG_ID!,
signWith: walletAddress,
});
const client = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
```
### Create Smart Wallet Client
```typescript
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const relayer = await createGelatoSmartAccountClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
});
```
[Turnkey Docs](https://docs.turnkey.com/)
## Execute Transactions
All providers support the same payment methods:
```typescript
function encodeNonce(key: bigint, seq: bigint): bigint {
// key: up to 192 bits
// seq: up to 64 bits
return (key << 64n) | seq;
}
const results = await relayer.sendTransactionSync({
payment: sponsored(),
calls: [{ to: "0x...", data: "0x...", value: 0n }],
nonce: encodeNonce(BigInt(Date.now()), 0n),
});
```
```typescript
function encodeNonce(key: bigint, seq: bigint): bigint {
// key: up to 192 bits
// seq: up to 64 bits
return (key << 64n) | seq;
}
const tokenAddress = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"; // USDC (Base Sepolia)
const results = await relayer.sendTransactionSync({
payment: token(tokenAddress),
calls: [{ to: "0x...", data: "0x...", value: 0n }],
nonce: encodeNonce(BigInt(Date.now()), 0n),
});
```
```typescript
function encodeNonce(key: bigint, seq: bigint): bigint {
// key: up to 192 bits
// seq: up to 64 bits
return (key << 64n) | seq;
}
const results = await relayer.sendTransactionSync({
nonce: encodeNonce(BigInt(Date.now()), 0n),
payment: native(),
calls: [{ to: "0x...", data: "0x...", value: 0n }],
});
```
## Smart Account Types
All providers support multiple smart account types:
| Type | Description |
| --- | --- |
| `gelato` | Gelato Smart Account (EIP-7702 optimized) |
| `kernel` | Kernel Account (ERC-4337 + optional EIP-7702) |
| `safe` | Safe Account (ERC-4337) |
```typescript
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const relayer = await createGelatoSmartAccountClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
});
```
## Additional Resources
- [Supported Networks](/gasless-with-relay/additional-resources/supported-networks)
- [GitHub Examples](https://github.com/gelatodigital/gasless/tree/master/examples)
---
## EIP-712 Meta-Transactions
**Path:** /gasless-with-relay/how-to-guides/meta-tx
Meta-transactions allow users to interact with smart contracts without paying gas fees. A relayer (like Gelato) pays the gas fees and executes the transaction on behalf of the user through:
1. **EIP-712 Typed Data Signing**: Users sign structured data instead of raw transactions
2. **Contract Inheritance**: Contracts inherit meta-transaction functionality
3. **Signature Verification**: Contracts verify user signatures and execute functions on their behalf
## Contract Conversion
### Original Contract
```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
contract SimpleCounter {
uint256 public counter;
event IncrementCounter(address msgSender, uint256 newCounterValue, uint256 timestamp);
function increment() external {
counter++;
emit IncrementCounter(msg.sender, counter, block.timestamp);
}
}
```
### Meta-Transaction Enabled Contract
To enable meta-transactions:
1. **Inherit from `EIP712MetaTransaction`**
2. **Replace `msg.sender` with `msgSender()`**
```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
contract SimpleCounter is EIP712MetaTransaction("SimpleCounter", "1") {
uint256 public counter;
event IncrementCounter(address msgSender, uint256 newCounterValue, uint256 timestamp);
function increment() external {
counter++;
emit IncrementCounter(msgSender(), counter, block.timestamp);
}
}
```
### Key Changes
| Original | Meta-Transaction Enabled |
| --- | --- |
| `contract SimpleCounter` | `contract SimpleCounter is EIP712MetaTransaction("SimpleCounter", "1")` |
| `msg.sender` | `msgSender()` |
- **Inheritance**: Passes contract name and version for EIP-712 domain separation
- **`msgSender()`**: Returns the original user address (not the relayer address)
### What EIP712MetaTransaction Provides
- `executeMetaTransaction()`: Main function to execute meta-transactions
- `getNonce(address user)`: Get user's nonce for replay protection
- `msgSender()`: Returns the original user address
- EIP-712 domain separation to prevent signature collisions
## Client Implementation
### Setup EIP-712 Types and Domain
```typescript
const types = {
MetaTransaction: [
{ name: 'nonce', type: 'uint256' },
{ name: 'from', type: 'address' },
{ name: 'functionSignature', type: 'bytes' },
],
} as const;
const domain = {
name: 'SimpleCounter',
version: '1',
verifyingContract: simpleCounterAddress as Hex,
salt: pad(toHex(chainId), { size: 32 }),
};
```
### Complete Example
```typescript
createPublicClient,
createWalletClient,
http,
encodeFunctionData,
pad,
toHex,
type Hex
} from 'viem';
const simpleCounterAddress = '0x5115B85246bb32dCEd920dc6a33E2Be6E37fFf6F';
const simpleCounterAbi = [
{ name: 'increment', type: 'function', inputs: [], outputs: [] },
{ name: 'counter', type: 'function', inputs: [], outputs: [{ type: 'uint256' }], stateMutability: 'view' },
{ name: 'getNonce', type: 'function', inputs: [{ name: 'user', type: 'address' }], outputs: [{ type: 'uint256' }], stateMutability: 'view' },
{ name: 'executeMetaTransaction', type: 'function', inputs: [
{ name: 'userAddress', type: 'address' },
{ name: 'functionSignature', type: 'bytes' },
{ name: 'sigR', type: 'bytes32' },
{ name: 'sigS', type: 'bytes32' },
{ name: 'sigV', type: 'uint8' }
], outputs: [{ type: 'bytes' }] }
] as const;
const metaTransactionTypes = {
MetaTransaction: [
{ name: 'nonce', type: 'uint256' },
{ name: 'from', type: 'address' },
{ name: 'functionSignature', type: 'bytes' },
],
} as const;
const executeMetaTransaction = async () => {
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
const relayer = createGelatoEvmRelayerClient({
apiKey: process.env.GELATO_API_KEY,
testnet: true,
});
// Get user nonce from contract
const nonce = await publicClient.readContract({
address: simpleCounterAddress,
abi: simpleCounterAbi,
functionName: 'getNonce',
args: [account.address],
});
// Encode the increment() function call
const functionSignature = encodeFunctionData({
abi: simpleCounterAbi,
functionName: 'increment',
});
// Setup EIP-712 domain (salt is chain ID padded to 32 bytes)
const domain = {
name: 'SimpleCounter',
version: '1',
verifyingContract: simpleCounterAddress as Hex,
salt: pad(toHex(baseSepolia.id), { size: 32 }),
};
// Sign the meta-transaction using EIP-712
const signature = await walletClient.signTypedData({
domain,
types: metaTransactionTypes,
primaryType: 'MetaTransaction',
message: {
nonce: nonce,
from: account.address,
functionSignature: functionSignature,
},
});
// Parse signature into r, s, v components
const r = `0x${signature.slice(2, 66)}` as Hex;
const s = `0x${signature.slice(66, 130)}` as Hex;
const v = parseInt(signature.slice(130, 132), 16);
// Encode executeMetaTransaction call
const metaPayload = encodeFunctionData({
abi: simpleCounterAbi,
functionName: 'executeMetaTransaction',
args: [account.address, functionSignature, r, s, v],
});
// Send via Gelato Relay
const id = await relayer.sendTransaction({
chainId: baseSepolia.id,
to: simpleCounterAddress,
data: metaPayload,
payment: sponsored(),
});
console.log(`Gelato task ID: ${id}`);
// Wait for status
const status = await relayer.waitForStatus({ id });
if (status.status === StatusCode.Included) {
console.log(`Transaction hash: ${status.receipt.transactionHash}`);
} else {
console.log(`Transaction failed: ${status.message}`);
}
};
executeMetaTransaction();
```
## EIP712MetaTransaction.sol
Create this base contract in your project to inherit meta-transaction functionality:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.29;
contract EIP712MetaTransaction {
using ECDSA for bytes32;
bytes32 internal constant EIP712_DOMAIN_TYPEHASH = keccak256(
bytes("EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)")
);
bytes32 internal constant META_TRANSACTION_TYPEHASH = keccak256(
bytes("MetaTransaction(uint256 nonce,address from,bytes functionSignature)")
);
bytes32 internal domainSeparator;
mapping(address => uint256) private nonces;
struct MetaTransaction {
uint256 nonce;
address from;
bytes functionSignature;
}
constructor(string memory name, string memory version) {
domainSeparator = keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256(bytes(name)),
keccak256(bytes(version)),
address(this),
bytes32(block.chainid)
)
);
}
function executeMetaTransaction(
address userAddress,
bytes memory functionSignature,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) public payable returns (bytes memory) {
MetaTransaction memory metaTx = MetaTransaction({
nonce: nonces[userAddress],
from: userAddress,
functionSignature: functionSignature
});
require(verify(userAddress, metaTx, sigR, sigS, sigV), "Invalid signature");
nonces[userAddress]++;
(bool success, bytes memory returnData) = address(this).call(
abi.encodePacked(functionSignature, userAddress)
);
require(success, "Function call failed");
return returnData;
}
function getNonce(address user) external view returns (uint256) {
return nonces[user];
}
function verify(
address user,
MetaTransaction memory metaTx,
bytes32 sigR,
bytes32 sigS,
uint8 sigV
) internal view returns (bool) {
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
domainSeparator,
keccak256(
abi.encode(
META_TRANSACTION_TYPEHASH,
metaTx.nonce,
metaTx.from,
keccak256(metaTx.functionSignature)
)
)
)
);
address recovered = digest.recover(sigV, sigR, sigS);
return recovered == user && recovered != address(0);
}
function msgSender() internal view returns (address sender) {
if (msg.sender == address(this)) {
bytes memory array = msg.data;
uint256 index = msg.data.length;
assembly {
sender := and(mload(add(array, index)), 0xffffffffffffffffffffffffffffffffffffffff)
}
} else {
sender = msg.sender;
}
return sender;
}
}
```
This contract uses OpenZeppelin's `ECDSA` library for signature recovery. Install it with:
```bash
npm install @openzeppelin/contracts
```
---
## Relayer Overview
**Path:** /gasless-with-relay/relayer-api/overview
The Gelato Relayer provides a standardized set of JSON-RPC methods for submitting gasless transactions. These methods support sponsored transactions, token-based fee payments, and transaction status tracking.
## Base URLs
```
https://api.gelato.cloud/rpc
```
```
https://api.t.gelato.cloud/rpc
```
## Available Methods
### Transaction Submission
| Method | Description |
| --- | --- |
| [`relayer_sendTransaction`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_sendtransaction) | Submit a signed transaction for relay |
| [`relayer_sendTransactionSync`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_sendtransactionsync) | Submit and wait for transaction confirmation |
| [`relayer_sendTransactionMultichain`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_sendtransactionmultichain) | Submit transactions to multiple chains |
### Fee Estimation
| Method | Description |
| --- | --- |
| [`relayer_getCapabilities`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_getcapabilities) | Get supported payment tokens and fee collector addresses |
| [`relayer_getFeeData`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_getfeedata) | Get token exchange rates for manual fee calculation |
| [`relayer_getFeeQuote`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_getfeequote) | Get a fee quote for a specific gas amount |
### Status Tracking
| Method | Description |
| --- | --- |
| [`relayer_getStatus`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_getstatus) | Check status with full transaction receipt |
## Payment Types
### Sponsored
The relayer covers gas fees. Requires a valid API key with sufficient Gas Tank balance.
```json
{
"payment": {
"type": "sponsored"
}
}
```
### Token
User pays fees in a supported ERC-20 token. The transaction must include a transfer to the fee collector.
```json
{
"payment": {
"type": "token",
"address": "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
}
}
```
When using token payments, you must first call [`relayer_getCapabilities`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_getcapabilities) to get the fee collector address and either [`relayer_getFeeQuote`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_getfeequote) (recommended) or [`relayer_getFeeData`](/gasless-with-relay/relayer-api-endpoints/relayer/relayer_getfeedata) to get the fee amount. These methods are not required for sponsored transactions.
## Authentication
All requests require an API key passed via the `X-API-Key` header:
```bash
curl -X POST https://api.gelato.cloud/rpc \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{ ... }'
```
```bash
curl -X POST https://api.t.gelato.cloud/rpc \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{ ... }'
```
Create an API Key at the [Gelato App](https://app.gelato.cloud/).
## Status Codes
| Code | Status | Description |
| --- | --- | --- |
| `100` | Pending | Transaction received, not yet submitted |
| `110` | Submitted | Transaction broadcast, awaiting inclusion |
| `200` | Included | Transaction confirmed on-chain |
| `400` | Rejected | Transaction rejected by relayer |
| `500` | Reverted | Transaction reverted on-chain |
## Error Codes
| Code | Message | Description |
| --- | --- | --- |
| `-32602` | Invalid params | Missing or malformed parameters |
| `4100` | Unauthorized | Invalid or missing API key |
| `4200` | Insufficient Payment | Payment amount too low |
| `4201` | Invalid Signature | Signature verification failed |
| `4202` | Unsupported Payment Token | Token not supported |
| `4204` | Quote Expired | Fee quote has expired |
| `4205` | Insufficient Balance | User balance too low |
| `4206` | Unsupported Chain | Chain ID not supported |
| `4208` | Unknown Transaction ID | Task ID not found |
| `4211` | Simulation Failed | Transaction simulation failed |
---
## relayer_sendTransaction
**Path:** /gasless-with-relay/relayer-api-endpoints/relayer/relayer_sendtransaction
---
## relayer_sendTransactionSync
**Path:** /gasless-with-relay/relayer-api-endpoints/relayer/relayer_sendtransactionsync
---
## relayer_sendTransactionMultichain
**Path:** /gasless-with-relay/relayer-api-endpoints/relayer/relayer_sendtransactionmultichain
---
## relayer_getFeeQuote
**Path:** /gasless-with-relay/relayer-api-endpoints/relayer/relayer_getfeequote
---
## relayer_getStatus
**Path:** /gasless-with-relay/relayer-api-endpoints/relayer/relayer_getstatus
---
## relayer_getCapabilities
**Path:** /gasless-with-relay/relayer-api-endpoints/relayer/relayer_getcapabilities
---
## relayer_getFeeData
**Path:** /gasless-with-relay/relayer-api-endpoints/relayer/relayer_getfeedata
---
## Supported Networks
**Path:** /gasless-with-relay/additional-resources/supported-networks
Gelato Relay is supported on the following networks:
{(() => {
const networksData = [
{ network: "Abstract", environment: "Mainnet" },
{ network: "Aleph Zero", environment: "Mainnet, Testnet" },
{ network: "Arbitrum", environment: "Mainnet, Sepolia" },
{ network: "Arena-Z", environment: "Mainnet" },
{ network: "Arbitrum Blueberry", environment: "Testnet" },
{ network: "Avalanche", environment: "Mainnet" },
{ network: "Base", environment: "Mainnet, Sepolia" },
{ network: "Camp", environment: "Mainnet" },
{ network: "Berachain", environment: "Mainnet, Bepolia" },
{ network: "BaseCamp", environment: "Testnet" },
{ network: "Blast", environment: "Mainnet, Sepolia" },
{ network: "BNB", environment: "Mainnet" },
{ network: "Botanix", environment: "Mainnet, Testnet" },
{ network: "Ethernity", environment: "Mainnet, Testnet" },
{ network: "Everclear (prev Connext)", environment: "Mainnet, Testnet" },
{ network: "Ethereum", environment: "Mainnet, Sepolia" },
{ network: "Filecoin", environment: "Mainnet" },
{ network: "Flow", environment: "Mainnet, Testnet" },
{ network: "Gnosis", environment: "Mainnet, Chidao" },
{ network: "HyperEVM", environment: "Mainnet, Testnet" },
{ network: "Ink", environment: "Sepolia, Mainnet" },
{ network: "Katana", environment: "Mainnet" },
{ network: "Linea", environment: "Mainnet" },
{ network: "Lisk", environment: "Mainnet, Sepolia" },
{ network: "Lumia", environment: "Mainnet" },
{ network: "Mantle", environment: "Mainnet" },
{ network: "MegaETH", environment: "Mainnet, Testnet" },
{ network: "Metis", environment: "Mainnet" },
{ network: "Mode", environment: "Mainnet" },
{ network: "Monad", environment: "Mainnet, Testnet" },
{ network: "Open Campus", environment: "Mainnet, Codex" },
{ network: "Optimism", environment: "Mainnet, Sepolia" },
{ network: "Plasma", environment: "Mainnet, Testnet" },
{ network: "Playnance", environment: "Mainnet" },
{ network: "Polygon", environment: "Mainnet, Amoy" },
{ network: "Polygon zkEVM", environment: "Mainnet" },
{ network: "Reya", environment: "Mainnet, Cronos" },
{ network: "Rootstock", environment: "Mainnet, Testnet" },
{ network: "Saigon", environment: "Testnet" },
{ network: "Sonic", environment: "Mainnet" },
{ network: "Story", environment: "Mainnet, Aeneid" },
{ network: "Synfutures ABC", environment: "Testnet" },
{ network: "Tangible", environment: "Real, Unreal" },
{ network: "Thrive", environment: "Testnet" },
{ network: "Unichain", environment: "Mainnet, Sepolia" },
{ network: "Vana", environment: "Moksha, Islander" },
{ network: "Zircuit", environment: "Mainnet" },
{ network: "zkSync Era", environment: "Mainnet" },
{ network: "Zora", environment: "Mainnet" }
];
return (
After creating the API Key, navigate to its dashboard to locate your API Key.
Gelato API Keys now supports API key rotation, allowing users to create and delete API keys. This helps prevent unauthorized usage in case an API key is exposed.
`Activate` your API key by allowing access to `all contracts` on a network, or restrict it to `specific contracts` or `specific functions` in policies section.
Here, you can configure different networks. For each network, you can choose to allow access to all target contracts or limit it to selected contracts or specific functions.
Before you can start sponsoring gas with Gas Tank, you need to setup your Gas Tank. Check out our [Guide](/paymaster-&-bundler/gastank/setting-up-gastank) for detailed instructions on setting up your Gas Tank.
For `Sponsorship` purposes, add funds to your Gas Tank account according to your target environment:
- **Mainnets**: Deposit USDC.
- **Testnets**: Deposit Sepolia ETH.
Since Gas Tank is deployed on Polygon, you can deposit USDC in one step, and deposits from other networks are supported via Circle CCTP. Learn [more](/paymaster-&-bundler/gastank/introduction).
---
## Sponsor Gas with Gas Tank
**Path:** /paymaster-&-bundler/how-to-guides/sponsor-gas-with-gastank
Gelato's Gas Tank is a powerful alternative to traditional onchain paymasters. It acts as a cross-chain gas tank, allowing you to sponsor gas fees across any supported EVM-compatible chain - using just a single balance.
Instead of maintaining balances on multiple chains, you only need to deposit funds in one place, and you're ready to sponsor gas anywhere.
Important: When using Gelato Bundler for `Sponsoring` Transactions with `Gas
Tank`, both `maxFeePerGas` and `maxPriorityFeePerGas` are set to `0`. This
allows transaction fees to be accurately settled post-execution, rather than
upfront via the EntryPoint.
## Implementations
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import { createGelatoBundlerClient, sponsored, toGelatoSmartAccount } from '@gelatocloud/gasless';
import { createPublicClient, http } from 'viem';
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = await toGelatoSmartAccount({
client,
owner,
});
```
```typescript
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.GELATO_API_KEY,
client,
payment: sponsored(),
pollingInterval: 100
});
```
```typescript
const hash = await bundler.sendUserOperation({
calls: [
{
data: '0xd09de08a',
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De'
}
]
});
console.log(`User operation hash: ${hash}`);
const { receipt } = await bundler.waitForUserOperationReceipt({ hash });
console.log(`Transaction hash: ${receipt.transactionHash}`);
```
```bash npm
npm install viem
```
```bash yarn
yarn add viem
```
```bash pnpm
pnpm add viem
```
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
Use `https://api.gelato.cloud` for mainnets, or `https://api.t.gelato.cloud` for testnets. Pass the API key in the `X-API-Key` header via `fetchOptions`.
```typescript
import { createPublicClient, http } from "viem";
import { createBundlerClient } from "viem/account-abstraction";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const bundlerClient = createBundlerClient({
client: publicClient,
transport: http(`https://api.gelato.cloud/rpc/84532?payment=sponsored`, {
fetchOptions: {
headers: {
'X-API-Key': process.env.GELATO_API_KEY
}
}
}),
});
```
To use Gas Tank sponsorship efficiently, set `maxFeePerGas` and `maxPriorityFeePerGas` to `0n`. This allows transaction fees to be settled after execution via Gelato Gas Tank.
```typescript
const hash = await bundlerClient.sendUserOperation({
account,
calls: [
{
data: '0xd09de08a',
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De',
value: 0n,
}
],
maxFeePerGas: 0n,
maxPriorityFeePerGas: 0n,
});
console.log(`User operation hash: ${hash}`);
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash });
console.log(`Transaction hash: ${receipt.receipt.transactionHash}`);
```
```bash npm
npm install permissionless viem
```
```bash yarn
yarn add permissionless viem
```
```bash pnpm
pnpm add permissionless viem
```
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import { createSmartAccountClient } from "permissionless";
import { createPublicClient, http } from "viem";
import { createBundlerClient, toSoladySmartAccount } from "viem/account-abstraction";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = await toSoladySmartAccount({
client,
owner,
});
```
Use `https://api.gelato.cloud` for mainnets, or `https://api.t.gelato.cloud` for testnets. Pass the API key in the `X-API-Key` header via `fetchOptions`.
```typescript
const smartClient = createSmartAccountClient({
account,
chain: baseSepolia,
bundlerTransport: http(`https://api.gelato.cloud/rpc/84532?payment=sponsored`, {
fetchOptions: {
headers: {
'X-API-Key': process.env.GELATO_API_KEY
}
}
}),
});
```
To use Gas Tank sponsorship efficiently, set `maxFeePerGas` and `maxPriorityFeePerGas` to `0n`. This allows transaction fees to be settled after execution via Gelato Gas Tank.
```typescript
const hash = await smartClient.sendUserOperation({
account,
calls: [
{
data: '0xd09de08a',
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De',
value: 0n,
}
],
maxFeePerGas: 0n,
maxPriorityFeePerGas: 0n,
});
console.log(`User operation hash: ${hash}`);
const receipt = await smartClient.waitForUserOperationReceipt({ hash });
console.log(`Transaction hash: ${receipt.receipt.transactionHash}`);
```
Use `https://api.gelato.cloud` for mainnets, or `https://api.t.gelato.cloud` for testnets. Pass the API key in the `X-API-Key` header.
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
To use Gas Tank sponsorship, ensure your UserOperation includes `maxFeePerGas` and `maxPriorityFeePerGas` set to `"0x0"`:
```json
{
"sender": "0x...",
"nonce": "0x...",
"callData": "0x...",
"maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0",
...
}
```
```typescript
const response = await fetch(
`https://api.gelato.cloud/rpc/${chainId}?payment=sponsored`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_sendUserOperation",
params: [userOperation, entryPoint],
id: 1,
}),
}
);
const data = await response.json();
const userOpHash = data.result;
```
```typescript
const response = await fetch(
`https://api.gelato.cloud/rpc/${chainId}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_estimateUserOperationGas",
params: [userOperation, entryPoint],
id: 1,
}),
}
);
const data = await response.json();
const gasEstimate = data.result;
```
```typescript
const response = await fetch(
`https://api.gelato.cloud/rpc/${chainId}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_getUserOperationReceipt",
params: [userOpHash],
id: 1,
}),
}
);
const data = await response.json();
const receipt = data.result;
```
### Available Endpoints
Send a user operation to the bundler
Estimate gas for a user operation
Get receipt for a user operation
Get user operation by hash
---
## Overview
**Path:** /paymaster-&-bundler/how-to-guides/pay-with-erc20-tokens/overview
Allow users to pay gas fees using ERC-20 tokens like USDC, USDT, or other supported tokens instead of native ETH. This significantly improves user experience by removing the need to hold native tokens.
## Payment Approaches
There are two ways to enable ERC-20 gas payments with the Gelato Bundler:
Simplest integration using Gelato's built-in token payment
Use third-party paymasters like Pimlico for token payments
## When to Use Each Approach
### Direct Payment (Recommended)
Use the Gelato Gasless SDK when you want:
- **Fastest integration** - Single SDK handles everything
- **Automatic token handling** - No manual paymaster setup required
- **Gelato-optimized gas costs** - Built-in fee optimization
- **Simple API** - Just specify `payment: token(tokenAddress)`
### On-Chain Paymaster
Use an on-chain paymaster when you need:
- **Custom paymaster logic** - Specific business rules for gas sponsorship
- **Existing paymaster integration** - Already using Pimlico, Alchemy, or other providers
## Additional Resources
- [Sponsor Gas with Gas Tank](/paymaster-&-bundler/how-to-guides/sponsor-gas-with-gastank) - Sponsored transactions alternative
- [Estimate Gas](/paymaster-&-bundler/how-to-guides/estimate-gas) - Get fee quotes before sending
- [Supported Networks](/paymaster-&-bundler/additional-resources/supported-networks) - Network availability
---
## Direct Payment (Gelato SDK)
**Path:** /paymaster-&-bundler/how-to-guides/pay-with-erc20-tokens/direct-payment
The simplest way to enable ERC-20 gas payments. Gelato's SDK handles token payment automatically - just specify the payment token and send your UserOperation.
## Installation
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
## Implementation
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import { createGelatoBundlerClient, token, toGelatoSmartAccount } from '@gelatocloud/gasless';
import { createPublicClient, http, type Hex } from 'viem';
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = await toGelatoSmartAccount({
client,
owner,
});
```
Specify the ERC-20 token address using `token()`:
```typescript
const USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"; // USDC (Base Sepolia)
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.GELATO_API_KEY,
client,
payment: token(USDC_ADDRESS),
});
```
```typescript
const hash = await bundler.sendUserOperation({
calls: [
{
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De',
data: '0xd09de08a', // increment()
}
]
});
console.log(`User operation hash: ${hash}`);
const { receipt } = await bundler.waitForUserOperationReceipt({ hash });
console.log(`Transaction hash: ${receipt.transactionHash}`);
```
## How It Works
When using direct payment with Gelato:
1. **Fee Calculation** - Gelato calculates the gas fee in the specified ERC-20 token
2. **Token Approval** - Ensure your smart account has approved Gelato's paymaster to spend the token
3. **Automatic Deduction** - The fee is automatically deducted from your smart account's token balance
4. **Transaction Execution** - Gelato sponsors the native gas and executes your UserOperation
Your smart account must have sufficient ERC-20 token balance to cover the gas fee. Use `getUserOperationQuote()` to estimate the fee before sending.
## Get Fee Quote
Estimate the fee before sending:
```typescript
const quote = await bundler.getUserOperationQuote({
calls: [
{
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De',
data: '0xd09de08a',
}
]
});
console.log('Fee:', quote.fee); // Fee in token units
console.log('Total gas:', quote.gas); // Total gas
console.log('L1 fee:', quote.l1Fee); // L1 data fee (L2 chains)
```
## Additional Resources
- [On-Chain Paymaster](/paymaster-&-bundler/how-to-guides/pay-with-erc20-tokens/onchain-paymaster) - Alternative approach using third-party paymasters
- [ERC-20 Payment Tokens](/paymaster-&-bundler/additional-resources/erc20-payment-tokens) - Supported tokens
- [Estimate Gas](/paymaster-&-bundler/how-to-guides/estimate-gas) - Gas estimation methods
---
## On-Chain Paymaster
**Path:** /paymaster-&-bundler/how-to-guides/pay-with-erc20-tokens/onchain-paymaster
Use third-party on-chain paymasters for ERC-20 gas payments. This approach gives you flexibility to use different paymaster providers while still benefiting from Gelato's high-performance bundler.
On-chain paymasters are smart contracts that sponsor gas fees by accepting ERC-20 tokens from users. They handle the token-to-ETH conversion on-chain, allowing users to pay gas with tokens they hold.
## Implementations
This example uses [Pimlico's ERC-20 Paymaster](https://docs.pimlico.io/). You can substitute any compatible on-chain paymaster.
```bash npm
npm install permissionless viem
```
```bash yarn
yarn add permissionless viem
```
```bash pnpm
pnpm add permissionless viem
```
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating a Gelato API key.
You'll also need an API key from your paymaster provider (e.g., [Pimlico](https://dashboard.pimlico.io/)).
```typescript
import { createSmartAccountClient } from "permissionless";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { prepareUserOperationForErc20Paymaster } from "permissionless/actions/erc20";
import { createPublicClient, http, type Hex } from "viem";
import { entryPoint07Address, toSoladySmartAccount } from "viem/account-abstraction";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = await toSoladySmartAccount({
client,
owner,
});
```
Set up the on-chain paymaster client. This example uses Pimlico:
```typescript
const pimlicoUrl = `https://api.pimlico.io/v2/${baseSepolia.id}/rpc?apikey=${process.env.PIMLICO_API_KEY}`;
const paymasterClient = createPimlicoClient({
chain: baseSepolia,
transport: http(pimlicoUrl),
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
});
```
Combine Gelato's bundler with the on-chain paymaster:
```typescript
const bundlerUrl = `https://api.t.gelato.cloud/rpc/${baseSepolia.id}`;
const smartClient = createSmartAccountClient({
account,
chain: baseSepolia,
bundlerTransport: http(bundlerUrl, {
fetchOptions: {
headers: {
'X-API-Key': process.env.GELATO_API_KEY
}
}
}),
paymaster: paymasterClient,
userOperation: {
estimateFeesPerGas: async ({ bundlerClient }) => {
const gasPrices = await bundlerClient.request({
method: "eth_getUserOperationGasPrice",
params: [],
});
return {
maxFeePerGas: BigInt(gasPrices.maxFeePerGas),
maxPriorityFeePerGas: BigInt(gasPrices.maxPriorityFeePerGas),
};
},
prepareUserOperation: prepareUserOperationForErc20Paymaster(paymasterClient),
},
});
```
```typescript
const USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"; // USDC (Base Sepolia)
const txHash = await smartClient.sendTransaction({
to: "0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De",
value: 0n,
data: "0xd09de08a", // increment()
paymasterContext: {
token: USDC_ADDRESS,
},
});
console.log(`Transaction hash: ${txHash}`);
```
When using the API directly, you need to obtain paymaster fields from your chosen paymaster provider and include them in the UserOperation.
You'll need:
- A Gelato API key from [app.gelato.cloud](https://app.gelato.cloud)
- A paymaster API key from your provider (e.g., Pimlico, Alchemy)
Request sponsorship data from your paymaster. Example with Pimlico:
```typescript
const USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
const paymasterResponse = await fetch(
`https://api.pimlico.io/v2/${chainId}/rpc?apikey=${PIMLICO_API_KEY}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "pm_getPaymasterStubData",
params: [
{
sender: "0xYourSmartAccountAddress",
nonce: "0x0",
callData: "0xCalldata",
maxFeePerGas: "0x...",
maxPriorityFeePerGas: "0x..."
},
"0x0000000071727De22E5E9d8BAf0edAc6f37da032", // EntryPoint v0.7
chainId.toString(),
{ token: USDC_ADDRESS }
]
})
}
);
const paymasterData = await paymasterResponse.json();
// Returns: paymaster, paymasterData, paymasterVerificationGasLimit, paymasterPostOpGasLimit
```
Include the paymaster fields in your UserOperation:
```typescript
const userOperation = {
sender: "0xYourSmartAccountAddress",
nonce: "0x0",
callData: "0xCalldata",
callGasLimit: "0x...",
verificationGasLimit: "0x...",
preVerificationGas: "0x...",
maxFeePerGas: "0x...",
maxPriorityFeePerGas: "0x...",
signature: "0xSignature",
// Paymaster fields from step 2
paymaster: paymasterData.result.paymaster,
paymasterData: paymasterData.result.paymasterData,
paymasterVerificationGasLimit: paymasterData.result.paymasterVerificationGasLimit,
paymasterPostOpGasLimit: paymasterData.result.paymasterPostOpGasLimit,
};
```
```typescript
const response = await fetch(
`https://api.t.gelato.cloud/rpc/${chainId}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_sendUserOperation",
params: [
userOperation,
"0x0000000071727De22E5E9d8BAf0edAc6f37da032" // EntryPoint v0.7
]
})
}
);
const data = await response.json();
const userOpHash = data.result;
console.log(`UserOperation hash: ${userOpHash}`);
```
```typescript
const response = await fetch(
`https://api.t.gelato.cloud/rpc/${chainId}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_getUserOperationReceipt",
params: [userOpHash]
})
}
);
const data = await response.json();
if (data.result) {
console.log(`Transaction hash: ${data.result.receipt.transactionHash}`);
}
```
## How It Works
```mermaid
sequenceDiagram
participant User
participant SmartAccount
participant Paymaster
participant Bundler as Gelato Bundler
participant EntryPoint
User->>SmartAccount: Sign UserOperation
SmartAccount->>Paymaster: Request sponsorship
Paymaster-->>SmartAccount: Return paymaster fields
SmartAccount->>Bundler: Submit UserOperation
Bundler->>EntryPoint: Bundle & execute
EntryPoint->>Paymaster: Validate & charge tokens
Paymaster->>SmartAccount: Deduct ERC-20 tokens
EntryPoint-->>Bundler: Execution result
Bundler-->>User: Transaction receipt
```
1. **Request Sponsorship** - Your app requests paymaster data from the on-chain paymaster provider
2. **Build UserOperation** - Include the paymaster fields (`paymaster`, `paymasterData`, gas limits)
3. **Submit to Bundler** - Gelato validates and bundles the UserOperation
4. **On-Chain Execution** - The paymaster validates the operation and charges ERC-20 tokens
## Compatible Paymasters
Any ERC-4337 compatible paymaster works with Gelato's bundler:
| Provider | Documentation |
|----------|---------------|
| Pimlico | [docs.pimlico.io](https://docs.pimlico.io/) |
| Alchemy | [docs.alchemy.com](https://docs.alchemy.com/reference/account-abstraction-api-quickstart) |
| Biconomy | [docs.biconomy.io](https://docs.biconomy.io/) |
| StackUp | [docs.stackup.sh](https://docs.stackup.sh/) |
## Additional Resources
- [Direct Payment](/paymaster-&-bundler/how-to-guides/pay-with-erc20-tokens/direct-payment) - Simpler approach using Gelato SDK
- [ERC-20 Payment Tokens](/paymaster-&-bundler/additional-resources/erc20-payment-tokens) - Supported tokens
- [Estimate Gas](/paymaster-&-bundler/how-to-guides/estimate-gas) - Gas estimation methods
---
## Pay with Native Tokens
**Path:** /paymaster-&-bundler/how-to-guides/pay-with-native
## Overview
Users can pay gas fees directly with native tokens from their smart accounts. This provides a familiar payment method while still benefiting from the enhanced features of smart wallets.
## Implementations
## Gelato Gasless SDK
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
### Example: Gelato Smart Account
```typescript
import { createGelatoBundlerClient, toGelatoSmartAccount, token } from '@gelatocloud/gasless';
import { baseSepolia } from "viem/chains";
import { createPublicClient, http } from 'viem';
import { privateKeyToAccount } from "viem/accounts";
```
```typescript
const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = await toGelatoSmartAccount({
client,
owner,
});
```
```typescript
const tokenAddress = "0x0000000000000000000000000000000000000000"; // Native
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.GELATO_API_KEY,
client,
payment: token(tokenAddress),
pollingInterval: 100
});
```
```typescript
const hash = await bundler.sendUserOperation({
calls: [
{
data: '0xd09de08a',
to: '0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De'
}
]
});
console.log(`User operation hash: ${hash}`);
const { receipt } = await bundler.waitForUserOperationReceipt({ hash });
console.log(`Transaction hash: ${receipt.transactionHash}`);
```
## Viem
To start sending transactions while paying gas with native tokens using Viem, follow these steps:
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating a API key.
```typescript
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toSoladySmartAccount } from "viem/account-abstraction";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
```
Any smart account that implements viem's `Account` type can be used here.
Check out other available smart accounts [here](https://viem.sh/account-abstraction/accounts/smart).
```typescript
const publicClient = createPublicClient({ chain: baseSepolia, transport: http() });
const signer = privateKeyToAccount(PRIVATE_KEY as any);
const account = await toSoladySmartAccount({
client: publicClient,
owner: signer,
});
```
Create a `BundlerClient` with the account and publicClient. Use `https://api.gelato.digital/bundlers` as the bundler endpoint.
Pass the API key as a query parameter. Learn more about Bundler Client [here](https://viem.sh/account-abstraction/clients/bundler).
```typescript
const bundlerClient = createBundlerClient({
account,
client: publicClient,
transport: http(`https://api.gelato.digital/bundlers/${chainId}/rpc?apiKey=${process.env.GELATO_API_KEY}`),
userOperation: {
estimateFeesPerGas: async ({ bundlerClient }) => {
const gasPrices = await bundlerClient.request({
method: 'eth_getUserOperationGasPrice',
params: []
});
return {
maxFeePerGas: BigInt(gasPrices.maxFeePerGas),
maxPriorityFeePerGas: BigInt(gasPrices.maxPriorityFeePerGas)
};
}
}
});
```
Send a `UserOperation` with the `bundlerClient`. The smart account must hold native tokens (ETH) to pay for gas fees.
```typescript
const userOperationHash = await bundlerClient.sendUserOperation({
account,
calls: [{ to: account.address, value: 0n, data: "0x" }],
});
console.log("UserOperation Hash: ", userOperationHash);
const receipt = await bundlerClient.waitForUserOperationReceipt({ hash: userOperationHash });
console.log("Transaction Hash: ", receipt.receipt.transactionHash);
```
## Permissionless
```bash npm
npm install permissionless viem
```
```bash yarn
yarn add permissionless viem
```
```bash pnpm
pnpm add permissionless viem
```
## Basic Setup
```typescript
import { createPublicClient, http } from "viem";
import { createBundlerClient, entryPoint07Address } from "viem/account-abstraction";
import { toKernelSmartAccount } from "permissionless/accounts";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
```
```typescript
const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = await toKernelSmartAccount({
client: publicClient,
owners: [owner],
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
version: "0.3.3",
});
```
Use `https://api.gelato.digital/bundlers` as the bundler endpoint. Pass the API key as a query parameter.
Learn more about Bundler Client [here](https://viem.sh/account-abstraction/clients/bundler).
```typescript
const bundlerClient = createBundlerClient({
client: publicClient,
transport: http(`https://api.gelato.digital/bundlers/${chainId}/rpc?apiKey=${process.env.GELATO_API_KEY}`),
userOperation: {
estimateFeesPerGas: async ({ bundlerClient }) => {
const gasPrices = await bundlerClient.request({
method: 'eth_getUserOperationGasPrice',
params: []
});
return {
maxFeePerGas: BigInt(gasPrices.maxFeePerGas),
maxPriorityFeePerGas: BigInt(gasPrices.maxPriorityFeePerGas)
};
}
}
});
```
The smart account must hold native tokens (ETH) to pay for gas fees.
```typescript
const userOpHash = await bundlerClient.sendUserOperation({
account,
calls: [
{
to: "0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De",
data: "0xd09de08a", // increment()
value: 0n,
},
],
});
console.log("UserOperation Hash: ", userOpHash);
const receipt = await bundlerClient.waitForUserOperationReceipt({
hash: userOpHash,
});
console.log(`Transaction successful: ${receipt.receipt.transactionHash}`);
```
### Authentication
Use `https://api.gelato.cloud` for mainnets, or `https://api.t.gelato.cloud` for testnets.
Pass the API key in the `X-API-Key` header.
### Basic Example
```typescript
const tokenAddress = "0x0000000000000000000000000000000000000000"; // Native
const response = await fetch(
`https://api.gelato.cloud/rpc/${chainId}?payment=token&token=${tokenAddress}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_sendUserOperation",
params: [userOperation, entryPoint],
id: 1,
}),
}
);
const data = await response.json();
const userOpHash = data.result;
```
```typescript
const tokenAddress = "0x0000000000000000000000000000000000000000"; // Native
const response = await fetch(
`https://api.gelato.cloud/rpc/${chainId}?payment=token&token=${tokenAddress}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_estimateUserOperationGas",
params: [userOperation, entryPoint],
id: 1,
}),
}
);
const data = await response.json();
const gasEstimate = data.result;
```
```typescript
const response = await fetch(
`https://api.gelato.cloud/rpc/${chainId}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
method: "eth_getUserOperationReceipt",
params: [userOpHash],
id: 1,
}),
}
);
const data = await response.json();
const receipt = data.result;
```
### Available Endpoints
Send a user operation to the bundler
Estimate gas for a user operation
Get receipt for a user operation
Get user operation by hash
## Additional Resources
- [Supported Networks](/paymaster-&-bundler/additional-resources/supported-networks) - Check network availability
---
## Native Payments
**Path:** /paymaster-&-bundler/how-to-guides/native-payments
**Best Practices/Recommendations**:
- We recommend users to rely on the exact values returned by the `eth_getUserOperationGasPrice` endpoint rather than applying buffers, as the values provided by our bundler are designed to handle gas price volatility effectively.
- When sending multiple UserOps back-to-back, querying the nonce from different providers may cause inconsistencies and stale nonces. For high-throughput use cases, we recommend:
- Using parallel nonces so ordering doesn’t matter.
- Managing nonces internally (query once, then increment locally for subsequent UserOps).
This method allows gas fees to be paid using native tokens (e.g., ETH, MATIC, etc.) under the ERC-4337 standard. It does not require a paymaster to be configured.
## Using Viem
To start paying gas fees with native tokens using Viem in ERC-4337 Standard, follow these steps:
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating a API key.
```typescript
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toSoladySmartAccount } from "viem/account-abstraction";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
```
Any smart account that implements viem's `Account` type can be used here.
Check out other available smart accounts [here](https://viem.sh/account-abstraction/accounts/smart).
```typescript
const publicClient = createPublicClient({ chain: mainnet, transport: http() });
const signer = privateKeyToAccount(PRIVATE_KEY as any);
const account = await toSoladySmartAccount({
client: publicClient,
owner: signer,
})
```
Create a `BundlerClient` with the account and publicClient and pass the `apiKey` as query parameter to the transport option. Learn more about Bundler Client [here](https://viem.sh/account-abstraction/clients/bundler).
```typescript
const bundlerClient = createBundlerClient({
account,
client: publicClient,
transport: http(`https://api.gelato.digital/bundlers/${chainID}/rpc?apiKey=${apiKey}`),
userOperation: {
estimateFeesPerGas: async ({ account, bundlerClient, userOperation }) => {
const gasPrices =
await bundlerClient.request({
method: "eth_getUserOperationGasPrice",
params: [],
});
return {
maxFeePerGas: BigInt(gasPrices.maxFeePerGas),
maxPriorityFeePerGas: BigInt(gasPrices.maxPriorityFeePerGas),
};
},
},
})
```
Send a `UserOperation` with the `bundlerClient` and the `account`.
```typescript
const userOperationHash = await bundlerClient.sendUserOperation({
account,
calls: [{ to: account.address, value: 0n, data: "0x" }],
})
console.log("UserOperation Hash: ", userOperationHash);
```
## Using Bundler API Endpoints
This is the standard method where users pay gas fees directly using their native tokens (like ETH, MATIC, etc.).
To use this method, follow these steps:
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating a API key.
Do not include any paymaster-related fields in the UserOperation parameters. This ensures the transaction is treated as a self-sponsored native token payment.
When calling Gelato API endpoints, make sure to include the `apiKey` and `sponsored` set to `false` as a query parameter.
Your Bundler URL will look like this:
```bash
https://api.gelato.digital/bundlers/${chainID}/rpc?apiKey=${apiKey}
```
Use the following endpoints to retrieve gas-related values:
- `maxFeePerGas` and `maxPriorityFeePerGas` can be fetched from `eth_getUserOperationGasPrice` API Endpoint.
- `callGasLimit`, `verificationGasLimit`, `preVerificationGas` can be fetched from `eth_estimateUserOperationGas` API Endpoint.
Check out the required parameters for paying gas with native tokens in the following scenarios:
- [Gas estimation parameters](https://github.com/gelatodigital/how-to-use-bundler-api-endpoints/blob/main/eth_estimateUserOperationGas/Native-Payments/NativeGasPayments.ts)
- [Send UserOperation parameters](https://github.com/gelatodigital/how-to-use-bundler-api-endpoints/blob/main/eth_sendUserOperation/Native-Payments/NativeGasPayments.ts)
---
## Estimate Gas
**Path:** /paymaster-&-bundler/how-to-guides/estimate-gas
**Best Practices/Recommendations**:
- We recommend users to rely on the exact values returned by the gas estimation endpoints rather than applying buffers, as the values provided by our bundler are designed to handle gas price volatility effectively.
- In case you want to estimate gas cost with some state overrides such as balance, code, etc., you can check our [state override](/paymaster-&-bundler/how-to-guides/estimate-gas#state-overrides) section.
## Implementations
```bash npm
npm install @gelatocloud/gasless viem
```
```bash yarn
yarn add @gelatocloud/gasless viem
```
```bash pnpm
pnpm add @gelatocloud/gasless viem
```
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import {
createGelatoBundlerClient,
sponsored,
token,
} from "@gelatocloud/gasless";
import { createPublicClient, http, type Hex } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
import { toSoladySmartAccount } from "viem/account-abstraction";
const owner = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
const account = await toSoladySmartAccount({
client,
owner,
});
const bundler = await createGelatoBundlerClient({
account,
client,
apiKey: process.env.GELATO_API_KEY,
payment: sponsored(),
});
```
Use `estimateUserOperationGas()` to get gas limits for a UserOperation:
```typescript
const gasEstimate = await bundler.estimateUserOperationGas({
calls: [
{
to: "0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De",
data: "0xd09de08a",
},
],
});
console.log("callGasLimit:", gasEstimate.callGasLimit);
console.log("verificationGasLimit:", gasEstimate.verificationGasLimit);
console.log("preVerificationGas:", gasEstimate.preVerificationGas);
```
Use `getUserOperationQuote()` to get gas estimation plus fee in your payment token:
```typescript
const USDC_ADDRESS = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"; // Base Sepolia
const bundlerWithToken = await createGelatoBundlerClient({
account,
client,
apiKey: process.env.GELATO_API_KEY,
payment: token(USDC_ADDRESS),
});
const quote = await bundlerWithToken.getUserOperationQuote({
calls: [
{
to: "0xE27C1359cf02B49acC6474311Bd79d1f10b1f8De",
data: "0xd09de08a",
},
],
});
console.log("Fee:", quote.fee); // Fee in USDC
console.log("Total gas:", quote.gas); // Total gas
console.log("L1 fee:", quote.l1Fee); // L1 data fee (L2 chains)
console.log("callGasLimit:", quote.callGasLimit);
```
Use `getUserOperationGasPrice()` to get current gas prices:
```typescript
const gasPrice = await bundler.getUserOperationGasPrice();
console.log("maxFeePerGas:", gasPrice.maxFeePerGas);
console.log("maxPriorityFeePerGas:", gasPrice.maxPriorityFeePerGas);
```
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
import { createPublicClient, http } from 'viem'
import { createBundlerClient, toSoladySmartAccount } from "viem/account-abstraction";
import { privateKeyToAccount } from "viem/accounts";
import { baseSepolia } from "viem/chains";
```
Any smart account that implements viem's `Account` type can be used here.
Check out other available smart accounts [here](https://viem.sh/account-abstraction/accounts/smart).
You can also use smart accounts from [permissionless](https://docs.pimlico.io/permissionless) like `toKernelSmartAccount`, `toLightSmartAccount`, etc. The bundler client setup and gas estimation work the same way.
```typescript
const publicClient = createPublicClient({
chain: baseSepolia,
transport: http()
});
const signer = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const account = await toSoladySmartAccount({
client: publicClient,
owner: signer,
});
```
Create a `BundlerClient` with the account and publicClient. Use `https://api.gelato.digital/bundlers` as the bundler endpoint.
Pass the API key as a query parameter. Learn more about Bundler Client [here](https://viem.sh/account-abstraction/clients/bundler).
```typescript
const bundlerClient = createBundlerClient({
account,
client: publicClient,
transport: http(`https://api.gelato.digital/bundlers/${baseSepolia.id}/rpc?apiKey=${process.env.GELATO_API_KEY}`),
userOperation: {
estimateFeesPerGas: async ({ bundlerClient }) => {
const gasPrices = await bundlerClient.request({
method: "eth_getUserOperationGasPrice",
params: [],
});
return {
maxFeePerGas: BigInt(gasPrices.maxFeePerGas),
maxPriorityFeePerGas: BigInt(gasPrices.maxPriorityFeePerGas),
};
},
},
})
```
Estimate gas for a `UserOperation` with the `bundlerClient` and the `account`.
```typescript
const results = await bundlerClient.estimateUserOperationGas({
account,
calls: [{ to: account.address, value: 0n, data: "0x" }],
})
console.log("Estimated gas values: ", results);
```
Use `https://api.gelato.cloud` for mainnets, or `https://api.t.gelato.cloud` for testnets. Pass the API key in the `X-API-Key` header.
Check out our [How-To Guide](/paymaster-&-bundler/how-to-guides/create-a-api-key) for detailed instructions on generating an API key.
```typescript
const chainId = 84532; // Base Sepolia
const response = await fetch(
`https://api.t.gelato.cloud/rpc/${chainId}?payment=sponsored`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_getUserOperationGasPrice",
params: []
})
}
);
const data = await response.json();
console.log("maxFeePerGas:", data.result.maxFeePerGas);
console.log("maxPriorityFeePerGas:", data.result.maxPriorityFeePerGas);
```
Configure the UserOperation with:
- Set `maxFeePerGas` and `maxPriorityFeePerGas` to `0` (0x0) for sponsored transactions
- Leave paymaster-related fields empty (`paymaster`, `paymasterData`, etc.)
```typescript
const response = await fetch(
`https://api.t.gelato.cloud/rpc/${chainId}?payment=sponsored`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": process.env.GELATO_API_KEY
},
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method: "eth_estimateUserOperationGas",
params: [
{
sender: "0xYourSmartAccountAddress",
nonce: "0x0",
callData: "0xCalldata",
maxFeePerGas: "0x0",
maxPriorityFeePerGas: "0x0",
signature: "0xDummySignature"
},
"0xEntryPointAddress"
]
})
}
);
const data = await response.json();
console.log("callGasLimit:", data.result.callGasLimit);
console.log("verificationGasLimit:", data.result.verificationGasLimit);
console.log("preVerificationGas:", data.result.preVerificationGas);
```
Check out the required parameters for estimating gas in the following scenarios:
- [Using Gas Tank](https://github.com/gelatodigital/how-to-use-bundler-api-endpoints/blob/main/eth_estimateUserOperationGas/Gas-Tank/SponsoredGas.ts)
- [Using Onchain Paymasters](https://github.com/gelatodigital/how-to-use-bundler-api-endpoints/tree/main/eth_estimateUserOperationGas/OnChain-Paymasters)
- [Using Native Payments](https://github.com/gelatodigital/how-to-use-bundler-api-endpoints/blob/main/eth_estimateUserOperationGas/Native-Payments/NativeGasPayments.ts)
## State Overrides
While estimating gas, you can include state overrides such as balance, code, etc. to test the gas cost with different state values.
```typescript
const results = await bundler.estimateUserOperationGas({
stateOverride: [
{
address: account.address,
balance: 100000000000000000000n,
},
],
calls: [{ to: account.address, value: 0n, data: "0x" }],
});
console.log("Estimated gas values:", results);
```
Include a state override object in your API request:
```typescript
import { toHex } from 'viem';
const stateOverride = {
[account.address]: {
//code: "",
//nonce: "",
balance: toHex(100000000000000000000n),
//state:{},
//stateDiff:{},
},
};
```
Checkout parameters for state overrides [here](https://viem.sh/account-abstraction/actions/bundler/estimateUserOperationGas#stateoverride-optional) and full example code [here](https://github.com/gelatodigital/how-to-use-bundler-api-endpoints/blob/main/eth_estimateUserOperationGas/State-Overrides/stateOverrideExample.ts).
## SDK Methods Reference
| Method | Description | Returns |
|--------|-------------|---------|
| `estimateUserOperationGas()` | Standard ERC-4337 gas estimation | Gas limits only |
| `getUserOperationQuote()` | Gas estimation + fee in payment token | Gas limits + fee |
| `getUserOperationGasPrice()` | Current gas prices | `maxFeePerGas`, `maxPriorityFeePerGas` |
---
## Track & Debug Requests
**Path:** /paymaster-&-bundler/how-to-guides/tracking-gelato-request
We’ve introduced [UI Logs](https://app.gelato.cloud/logs) for Gelato Bundler endpoints!
- You can now track all your requests directly in the dashboard, and easily **debug failed requests** using built-in Tenderly simulations.
- Additionally, you can get info such as **response time, request body, response body**, and more.
## Debugging Failed Requests Using UI Logs
You can use the **UI logs** to debug failed requests directly from the Gelato app. These logs are available in the [Paymaster & Bundler](https://app.gelato.cloud/logs) section of the dashboard.
### Steps to Debug
1. Go to the **logs** section and locate your failed bundler endpoints request.
2. On the right side of the log entry, click the **Debug** button.
3. A new option, **View Debug**, will appear. Click it.
4. This will open a **Tenderly simulation**, which you can use to analyze and debug the failed request.
## Using Status Endpoint
In any of the payment methods, when using `Gelato Bundler`, if you call the `eth_sendUserOperation` API endpoint, the returned `userOpHash` can also be used to track the status of the UserOperation through Gelato's infrastructure like this:
```bash
curl --request POST \
--url https://api.gelato.cloud/rpc \
--header 'Content-Type: application/json' \
--header 'X-API-Key: YOUR_API_KEY' \
--data '{
"id": 1,
"jsonrpc": "2.0",
"method": "relayer_getStatus",
"params": {
"id": "0x0e670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d1527331",
"logs": false
}
}'
```
---
## Embedded Wallet Integrations
**Path:** /paymaster-&-bundler/how-to-guides/embedded-wallets
Embedded wallets enable users to interact with your dApp using familiar login methods like email, phone, or social accounts. Gelato Gasless SDK integrates seamlessly with popular embedded wallet providers.
## Quick Start
### Installation
```bash
npm install @gelatocloud/gasless @dynamic-labs/sdk-react-core @dynamic-labs/ethereum @dynamic-labs/wagmi-connector @tanstack/react-query wagmi viem
```
### Setup
1. Create an app at [Dynamic Dashboard](https://app.dynamic.xyz/) and enable `Embedded Wallets`
2. Get your Gelato API Key from [Gelato App](https://app.gelato.cloud/)
```bash
NEXT_PUBLIC_DYNAMIC_APP_ID=your_dynamic_environment_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
### Configure Providers
```typescript
const queryClient = new QueryClient();
export default function Providers({ children }: { children: React.ReactNode }) {
return (
{children} );
}
```
### Create Gelato Bundler Client
```typescript
const { primaryWallet } = useDynamicContext();
if (!primaryWallet || !isEthereumWallet(primaryWallet)) return;
const connector = primaryWallet.connector;
if (!connector || !isDynamicWaasConnector(connector)) return;
const client = await primaryWallet.getWalletClient();
client.account.signAuthorization = async (parameters) => {
const preparedAuthorization = await prepareAuthorization(client, parameters);
const signedAuthorization = await connector.signAuthorization(preparedAuthorization);
return {
address: preparedAuthorization.address,
chainId: preparedAuthorization.chainId,
nonce: preparedAuthorization.nonce,
r: signedAuthorization.r,
s: signedAuthorization.s,
v: signedAuthorization.v,
yParity: signedAuthorization.yParity,
} as SignAuthorizationReturnType;
};
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
client,
payment: sponsored(),
pollingInterval: 100,
});
```
[Dynamic Docs](https://docs.dynamic.xyz/)
### Installation
```bash
npm install @gelatocloud/gasless @privy-io/react-auth @privy-io/wagmi @tanstack/react-query viem
```
### Setup
1. Create an app at [Privy Dashboard](https://dashboard.privy.io/) and enable `Embedded Wallets`
2. Get your Gelato API Key from [Gelato App](https://app.gelato.cloud/)
```bash
NEXT_PUBLIC_PRIVY_APP_ID=your_privy_app_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
### Configure Providers
```typescript
const queryClient = new QueryClient();
const wagmiConfig = createConfig({
chains: [baseSepolia],
transports: { [baseSepolia.id]: http() },
});
const privyConfig: PrivyClientConfig = {
embeddedWallets: {
createOnLogin: "users-without-wallets",
requireUserPasswordOnCreate: false,
},
loginMethods: ["email"],
};
export const App = () => (
);
```
### Create Gelato Bundler Client
```typescript
const { wallets } = useWallets();
const { signAuthorization } = useSign7702Authorization();
const primaryWallet = wallets[0];
const provider = await primaryWallet?.getEthereumProvider();
const client = createWalletClient({
account: primaryWallet.address as Hex,
chain: baseSepolia,
transport: custom(provider),
});
client.account.signAuthorization = async (parameters) => {
const preparedAuthorization = await prepareAuthorization(client, parameters);
return await signAuthorization({
chainId: preparedAuthorization.chainId,
contractAddress: preparedAuthorization.address,
nonce: preparedAuthorization.nonce,
});
};
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
client,
payment: sponsored(),
pollingInterval: 100,
});
```
[Privy Docs](https://docs.privy.io/)
### Installation
```bash
npm install @gelatocloud/gasless @web3auth/modal @web3auth/ethereum-provider viem
```
### Setup
1. Create an app at [Web3Auth Dashboard](https://dashboard.web3auth.io/)
2. Get your Gelato API Key from [Gelato App](https://app.gelato.cloud/)
```bash
NEXT_PUBLIC_WEB3AUTH_CLIENT_ID=your_web3auth_client_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
### Initialize Web3Auth
```typescript
const chainConfig = {
chainNamespace: "eip155",
chainId: "0x14a34",
rpcTarget: "https://sepolia.base.org",
displayName: "Base Sepolia",
ticker: "ETH",
tickerName: "Ethereum",
};
const privateKeyProvider = new EthereumPrivateKeyProvider({ config: { chainConfig } });
const web3auth = new Web3Auth({
clientId: process.env.NEXT_PUBLIC_WEB3AUTH_CLIENT_ID!,
web3AuthNetwork: "sapphire_devnet",
privateKeyProvider,
});
await web3auth.initModal();
const provider = await web3auth.connect();
```
### Create Gelato Bundler Client
```typescript
const client = createWalletClient({
chain: baseSepolia,
transport: custom(provider),
});
const account = await toKernelSmartAccount({
client,
version: "0.3.1",
entryPoint: {
address: entryPoint07Address,
version: "0.7"
},
owners: [client],
});
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
client,
payment: sponsored(),
pollingInterval: 100,
});
```
[Web3Auth Docs](https://web3auth.io/docs/)
### Installation
```bash
npm install @gelatocloud/gasless @turnkey/sdk-browser @turnkey/viem viem
```
### Setup
1. Create an organization at [Turnkey Dashboard](https://app.turnkey.com/)
2. Get your Gelato API Key from [Gelato App](https://app.gelato.cloud/)
```bash
NEXT_PUBLIC_TURNKEY_ORG_ID=your_turnkey_org_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
### Initialize Turnkey
```typescript
const turnkey = new Turnkey({
apiBaseUrl: "https://api.turnkey.com",
defaultOrganizationId: process.env.NEXT_PUBLIC_TURNKEY_ORG_ID!,
});
const account = await createAccount({
client: turnkey.apiClient(),
organizationId: process.env.NEXT_PUBLIC_TURNKEY_ORG_ID!,
signWith: walletAddress,
});
const client = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
```
### Create Gelato Bundler Client
```typescript
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
client,
payment: sponsored(),
pollingInterval: 100,
});
```
[Turnkey Docs](https://docs.turnkey.com/)
## Execute Transactions
All providers support the same payment methods:
```typescript
const nonce = await publicClient.getTransactionCount({
address: primaryWallet.address as Hex,
});
const authorization = await client.signAuthorization({
address: account.authorization.address,
nonce,
});
const hash = await bundler.sendUserOperation({
account,
calls: [{ to: account.address, value: 0n, data: "0x" }],
authorization,
});
```
```typescript
const nonce = await publicClient.getTransactionCount({
address: primaryWallet.address as Hex,
});
const authorization = await client.signAuthorization({
address: account.authorization.address,
nonce,
});
const tokenAddress = "0x036CbD53842c5426634e7929541eC2318f3dCF7e"; // USDC (Base Sepolia)
const hash = await bundler.sendUserOperation({
account,
calls: [{ to: account.address, value: 0n, data: "0x" }],
authorization,
});
```
```typescript
const nonce = await publicClient.getTransactionCount({
address: primaryWallet.address as Hex,
});
const authorization = await client.signAuthorization({
address: account.authorization.address,
nonce,
});
const hash = await bundler.sendUserOperation({
account,
calls: [{ to: account.address, value: 0n, data: "0x" }],
authorization,
});
```
## Smart Account Types
All providers support multiple smart account types:
| Type | Description |
| --- | --- |
| `gelato` | Gelato Smart Account (EIP-7702 optimized), Also supports ERC-4337 |
| `kernel` | Kernel Account (ERC-4337 + optional EIP-7702) |
| `safe` | Safe Account (ERC-4337) |
```typescript
const account = toGelatoSmartAccount({
client: client,
owner: client.account,
});
const bundler = await createGelatoBundlerClient({
account,
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY as string,
client,
payment: sponsored(),
pollingInterval: 100,
});
```
## Additional Resources
- [Supported Networks](/paymaster-&-bundler/additional-resources/supported-networks)
- [Smart Accounts](/paymaster-&-bundler/features/smart-accounts)
- [GitHub Examples](https://github.com/gelatodigital/gasless/tree/master/examples)
---
## Introduction
**Path:** /paymaster-&-bundler/gastank/introduction
When you use each of Gelato's services there are two costs that you need to pay:
- Gas costs of your executed transactions (or those of your end users if you are sponsoring them)
- Service fees - Gelato subscriptions or % premiums on the gas costs of each transaction
Gas Tank makes it easy for you to pay all of your costs across all the networks that you are using from one single easy-to-manage balance. Regardless of the network you choose for deposits, Gas Tank provides the flexibility to cover costs across multiple networks
Gas Tank now supports cross-chain native USDC deposits powered by Circle’s Cross-Chain Transfer Protocol (CCTP). This enables a seamless and secure transfer of USDC across different blockchain networks.
What is Circle CCTP?
Circle's Cross-Chain Transfer Protocol (CCTP) is a permissionless on-chain utility designed to facilitate the transfer of USDC between blockchains. By leveraging native burning and minting mechanisms, CCTP ensures that USDC transfers are executed with high security and interoperability. [Read more here](https://www.circle.com/en/cross-chain-transfer-protocol).
CCTP deposits using Safes or smart contract wallets is not currently supported. Users may want to consider adding EOA addresses to their team as these can deposit into the team’s Gas Tank account.
## How does Gas Tank work?
- For example, a user can top up their Gelato Gas Tank using USDC on Polygon. This USDC balance will now be used to cover all gas costs and fees for any paymaster & bundler (sponsored with Gas Tank) call, regardless of the underlying chain.

- Therefore, a user could request paymaster & bundler calls on Ethereum mainnet, and Gelato will query their Gas Tank to see if they possess enough equivalent USDC to cover the costs for this call. If the balance is sufficient, Gelato will go ahead and process the message on-chain.
- After the transaction is successful, Gelato can use the transaction receipts to charge you exactly the amount that the transaction costs plus a nominal fee. This makes Gelato Gas Tank much more friendly on your wallet than payment based on a priori gas simulation which can be uncertain at best, and lead to consistent overcharging in the worst case.
## Getting Started
To get started with Gas Tank on mainnets you will need USDC (plus some native token to cover gas costs for depositing).
To see the list of supported chains for CCTP deposits, check out USDC Addresses
1. [Access the Gas Tank in the Gelato app](https://app.gelato.cloud/one-balance)
2. Connect your wallet if you have not already done so
3. Select USDC from the desired chain you would like to deposit from.
4. Click on deposit and then approve USDC into your Gas Tank.
5. After confirming your deposit transaction in your wallet you will see a Pending deposit appear in your deposits history:
Once the required number of confirmations has been reached your deposit will be credited and your Gas Tank is ready for use.
## Depositing from Network other than Polygon
When you deposit USDC into your Gas Tank account from a network other than Polygon, your deposit will undergo a two-step process that ensures your funds are securely bridged and deposited using Circle’s Cross-Chain Transfer Protocol (CCTP).
For this example, we will be making use of USDC on Base:
- We have entered 0.5 USDC that we wish to deposit into our Gas Tank.
- The funds will first undergo a 'Bridging' process, with a clear display of the bridging fee that covers the gas costs
- Confirm the transaction from our wallet, then track the two-step process—Bridging followed by Depositing, culminating in the funds reflecting in our Gas Tank, typically within 15 minutes.
When you make a deposit from a network other than Polygon, you'll notice a small fee applied during the bridging process. It's important to understand that these fees are strictly to cover the CCTP and gas costs. Gas Tank does not charge any additional fees for this service.
## Fees
More details on the fees applied by each service are available:
- [Paymaster & Bundler Fees](/pricing/pricing-plans)
- [Web3 Function Fees](/pricing/pricing-plans)
## Low Balance Alerts
To ensure that your transactions execute as you expect, it is critical that you have sufficient funds deposited in Gas Tank. To help you monitor your balance and receive alerts when it drops below you preferred threshold, we provide a [Gas Tank Alerts service](/paymaster-&-bundler/gastank/gastank-alerts).
---
## Setting up Gas Tank
**Path:** /paymaster-&-bundler/gastank/setting-up-gastank
As we have launched the `Gelato Onchain Cloud`, we have also updated the `Gas Tank` setup, Here are some guides to help you get started.
## Gas Tank with EOA
Before you can start using Gelato services, you’ll need to set up your Gas Tank account. Here’s a guide on how to set up Gas Tank with an EOA:
{item.address}
## Key Benefits
- **Zero code changes** – fully managed from the dashboard
- **Seamless monetization** – earn revenue from ERC-20 flows
- **Weekly USDC payouts** – consistent, automated deposits
- **Flexible configuration** – set margins per chain and update anytime
- **Universal support** – works with all ERC-20 tokens, no restrictions
## How It Works
1. **User pays gas in ERC-20**
- The Paymaster sponsors the transaction, while the user pays for it using any ERC-20 token.
2. **Add a revenue margin**
- Define a percentage margin (e.g., 5%) on top of the actual gas cost.
- Example: If gas costs **1 USD**, and you set a **5%** margin, the total is **1.05 USD**.
- **1 USD** covers the gas
- **0.05 USD** is your revenue
3. **Automated conversion & payout**
- Gelato collects the margin in ERC-20
- Revenue is converted into **USDC weekly**
- Converted USDC is automatically deposited into your **Gas Tank**
## Getting Started
1. Log in to the **[Gelato Cloud Platform](https://app.gelato.cloud/)**
2. Navigate to: **API Key → Policies → Gas Revenue Policy**
3. Select your network
4. Define your gas revenue percentage (e.g., 5%)
5. Save your settings
Your margin is applied immediately, and payouts appear weekly in USDC.
## Monitoring Earnings
Track your earnings in real time under: **Gas Tank → Analytics → Gas Revenue Earnings**
- View daily revenue per chain
- Earnings displayed in USD equivalent using real-time token prices
- For payout details, see **Payouts & Conversion** section below.
## Payouts & Conversion
- **Collection**: Revenue is collected in the ERC-20 tokens users pay with
- **Conversion**: Once per week, Gelato converts accumulated revenue into USDC at the settlement rate
- **Payout**: Converted USDC is automatically deposited into your Gas Tank
- **Variation**: Daily earnings shown in Analytics reflect real-time USD value. Final payouts may vary slightly based on conversion rates
## Supported Networks
Gas Revenue is available on the following chains:
{(() => {
const networksData = [
{ network: "Arbitrum", environment: "Mainnet" },
{ network: "Base", environment: "Mainnet, Sepolia" },
{ network: "Ethereum", environment: "Mainnet, Sepolia" },
{ network: "Ink", environment: "Mainnet" },
];
return (
A dialog box will appear, prompting you to name your new API key.
Choose a descriptive name for your key (e.g., "ProjectName-Mainnet" or
"TestProject-RPC").
After entering the name, click Create to generate the key.
Your new API key will be listed under the My keys section.
You can now click on the key to view its details, including:
- Success rate (last 24 hours)
- Median response time
- Total requests
- CU usage and more
You'll also see the supported networks (HTTP and WebSocket endpoints) for your key at the bottom of the key details page.
---
## Supported Networks
**Path:** /private-rpcs/additional-resources/supported-networks
Gelato Private RPCs are supported on following networks:
{(() => {
const supportedNetworks = [
{ network: "Aleph Zero", environment: "Mainnet" },
{ network: "Arena-Z", environment: "Mainnet" },
{ network: "Ethernity", environment: "Mainnet" },
{ network: "Everclear (prev Connext)", environment: "Mainnet" },
{ network: "Fluence", environment: "Mainnet" },
{ network: "Ink", environment: "Mainnet" },
{ network: "Lisk", environment: "Mainnet" },
{ network: "EDU Chain", environment: "Mainnet" },
{ network: "Playnance", environment: "Mainnet" },
{ network: "Reya", environment: "Mainnet" },
{ network: "SX", environment: "Mainnet" },
{ network: "re.al", environment: "Mainnet" }
];
return (
## Overview of how Gelato Relay works
As requests are submitted to Gelato Relay, a network of decentralised Gelato Executors will execute and get the transactions validated as soon as possible. [EIP-712 signatures](/relay/introduction/what-is-relaying#eip-712-signatures) enforce the integrity of data, while gas fee payments can be handled in any of our supported payment methods. In this way, developers can rely on Gelato's battle-tested blockchain infrastructure improving the UX, costs, security and liveness of their Web3 systems.
## Security Considerations
While Gelato Relay offers very powerful features, improper implementation can introduce vulnerabilities in your contracts. We strongly recommend always using the built-in [ERC-2771](/relay/erc2771-recommended) user signature verification found in our [sponsoredCallERC2771](/relay/erc2771-recommended/sponsoredcall-erc2771) or [callWithSyncFeeERC2771](/relay/erc2771-recommended/callwithsyncfee-erc2771/overview) methods to enhance security.
Please read the [Security Considerations](/relay/security-considerations/overview) section to understand all potential security risks and measures to mitigate them when using a Gelato Relay.
## How can I get started with gasless transactions?
1. Deploy a compatible contract, or use one of ours (see code example links below).
2. Run the code examples found on each SDK method page:
- [sponsoredCallERC2771](/relay/erc2771-recommended/sponsoredcall-erc2771)
- [callWithSyncFeeERC2771](/relay/erc2771-recommended/callwithsyncfee-erc2771/overview)
- [sponsoredCall](/relay/non-erc2771/sponsoredcall)
- [callWithSyncFee](/relay/non-erc2771/callwithsyncfee/overview)
That's all it takes to get started with Gelato Relay and the Gelato Relay SDK!
We hope you have as much fun using Gelato Relay as we did building it! 😄
Any feedback, please [get in touch](https://www.gelato.cloud/contact)!
## API Docs
If your codebase is not JS compatible, you can use the Gelato Relay API directly. Please find the API docs [here](/relay/api-&-feeoracle).
---
## What is Relaying?
**Path:** /relay/introduction/what-is-relaying
Understanding the benefits of relaying and how it can help your dApp thrive
After reading this page:
- You'll understand the context shift between standard transactions and relayed transactions.
- You'll know the ways relaying can help improve UX.
- You'll know exactly why Gelato Relay is the relayer you should integrate with.
- You'll know what meta transactions are and how EIP-712 signatures work.
> **Note:** Please visit our page [Security Considerations](/relay/security-considerations/overview) and read carefully before moving forward.
## Standard transactions
In a standard Ethereum transaction, an ethereum user signs and sends the transaction themselves. This user controls the private key to an [externally owned account (EOA)](https://ethereum.org/en/developers/docs/accounts/#types-of-account)which they can use to sign a transaction and prove they have the right to spend the balance associated with that account address.
For each transaction a user sends, there is an associated transaction fee, known as [gas](https://ethereum.org/en/developers/docs/gas/#top). Since Ethereum executes computation, each unit of computation has an associated gas cost, which deters malicious actors from overloading the network by requiring them to pay heavily for a potential attack. This is excellent news for Ethereum's security and helps keep the network consistent under load, but it comes at a hidden cost for onboarding new users.
## Onboarding issues
How does a new user start interacting with exciting on-chain applications like DeFi, NFTs, or gaming? They will always need the native token to pay for gas on every network, even if the network has very cheap gas fees like Polygon. This requires the user to open an account at a centralised exchange, go through KYC, and buy crypto using fiat. This can be quite a process, even for the most skilled of degens out there, and it can deter new users from being onboarded to a dApp by increasing the latency between their initial excitement and the time it takes to actually get started.
This is where relaying comes in! A relayer can help solve these issues by sending a transaction on behalf of the user.
## What is a relayer?
We allow the user to send a transaction without a native token balance (it turns out relayers can be super nifty in loads of ways, for example, allowing a user who wants to swap a token to pay for the gas using the token being swapped!). Ideally, we would also like to still utilise the excellent security of a user signature, but for the transaction to be sent by a different EOA, one controlled by a relayer, who abstracts gas payment away from the user.
This is a very import context shift to understand. We have shifted from a user signing and sending a transaction themselves, to a user signing a standardised message and passing that on to a relayer. This relayer will, first, verify the user's signature for security, and then pass their message along on-chain. Gelato Relay does exactly this by taking a user's message off-chain and subsequently building a meta-transaction which is executed on chain.
## What is Gelato Relay?
Using Gelato Relay, we relay your user's transactions on-chain, enabling secure gasless transactions for an ultra smooth UX for your app. This allows for a variety of new web3 experiences, as the user can now pay by only signing a message, or their transaction costs can be sponsored by the developer. As long as the gas costs are covered in one of the multiple payment methods that Gelato supports, we handle the rest reliably, quickly and securely.
## What is a meta transaction?
A meta transaction is a regular ethereum transaction which contains the actual message to be delivered on-chain to a target contract within itself, hence the term [meta](https://en.wikipedia.org/wiki/Meta). The outer transaction helps facilitate the first on-chain call which is sent by a relayer. The call is forwarded to the target contract using an intermediate smart contract (Gelato Relay), which in turn forwards the call using the inner transaction to deliver the relayed message.
## EIP-712 signatures
To achieve gasless transactions securely, Gelato Relay makes use of the [EIP-712](https://eips.ethereum.org/EIPS/eip-712) standard. EIP-712 allows for a standardised way to sign and hash typed structured data. This means the user can sign a message using their wallet without incurring a gas cost or interacting with the chain at all, and this signature can be verified on-chain, by the relayer, facilitating a gasless transaction with security built in. This message will include important information such as the transaction signer address, the target contract address, and the calldata payload used to target a specific function.
---
## Overview
**Path:** /relay/erc2771-recommended/overview
# ERC-2771 (Recommended)
Native meta transactions with top notch security.
If you plan to use ERC-2771 with a multicall method or any other method using delegateCall(), please read carefully the section [Avoid ERC-2771-risks](/relay/security-considerations/erc2771-delegatecall-vulnerability).
If you are using `@gelatonetwork/relay-sdk` v3 or contracts from the package `@gelatonetwork/relay-context` v2, please follow this [migration guide](/relay/additional-resources/erc2771-migration-guide) to migrate to the new versions.
After reading this page:
- You'll understand the difference between `sponsoredCallERC2771` and `callWithSyncFeeERC2771`
- You'll understand how to use `sponsoredCallERC2771` and `callWithSyncFeeERC2771` in combination with ERC2771Context to achieve a gasless UX for your app, with secure user signature verification
- You'll understand ERC-2771's core functionality and how it allows for the off-chain sender address to be verified on-chain
## Recommendation for using ERC-2771
As detailed in the Security Considerations section, it's crucial to ensure that your relay implementation is impervious to vulnerabilities when using a relayer. The most secure approach is to utilize our ERC-2771 implementations:
- `sponsoredCallERC2771`
- `callWithSyncFeeERC2771`
When using `sponsoredCallERC2771`, you sponsor your user's gas fees, leveraging 1Balance for payment. In contrast, with `callWithSyncFeeERC2771`, the fees are paid from the target contract.
In both instances, users are prompted to sign their transaction's relay request using their private keys (for instance, through MetaMask). This step is crucial for security purposes. Gelato verifies on-chain that the user's signature corresponds with the required address before forwarding the call.
When relaying a message to a target smart contract function, it's essential for the function to authenticate the message's origin and confirm it was forwarded through the correct relayer. Without these verifications, your target function becomes susceptible to exploitation. ERC-2771 employs sophisticated data encoding to relay the original `_msgSender` from off-chain, and it guarantees that only the `trustedForwarder` is capable of encoding this value. These two parameters, in tandem, safeguard against any potential misconduct, ensuring a secure transmission of information from off-chain to on-chain!
## Why is this important?
In the context of relaying, `msg.sender` loses its usual informational significance. Under normal circumstances, `msg.sender` would denote the user initiating the transaction; however, with off-chain relaying, we lose this valuable piece of information.
Consider this scenario: how does a target smart contract determine who can call a particular function? In this case, `msg.sender` will be the relayer, but merely whitelisting this address is insufficient and still permits others using the same relayer to call your function. This situation can raise significant concerns, particularly when low-level calls are involved.
The optimal solution would be to allow the initiator of the relay call to specify an address and relay this address on-chain. The target smart contract can then authenticate a function call using this address.
The challenge then becomes: how can we successfully transmit information (a specific address) via low-level calldata from off-chain to on-chain without disrupting the calldata's integrity?
## Core Functionality of ERC-2771
Here's where the real magic unfolds. The `trustedForwarder` encodes the `from` address (i.e., the off-chain address) into the calldata by appending it at the end:
```solidity
(bool success, ) = to.call.value(value)(abi.encodePacked(data, from));
```
Now, the target contract can validate the `from` address by decoding the data in the same manner, ensuring that this message has been passed through the `trustedForwarder`.
The necessary target contract function can then confidently confirm that the correct entity signed and requested this payload to be relayed, and only via a trusted forwarder - in our case, the Gelato Relay.
## How does Gelato encode this data?
Let's take as an example relay method `sponsoredCallERC2771`. Method `callWithSyncFeeERC2771` works similarly.
Gelato Relay's `sponsoredCallERC2771` function encodes the user's address, which can then be utilized by the ERC-2771 compatible target smart contract. The most relevant part, where the user address is appended to the calldata, is shown below:
```solidity
// GelatoRelay1BalanceERC2771.sol
_call.target.revertingContractCall(
_encodeERC2771Context(_call.data, _call.user),
"GelatoRelay1BalanceERC2771.sponsoredCallERC2771:"
);
```
where `_encodeERC2771Context` refers to:
```solidity
// GelatoRelayUtils.sol
function _encodeERC2771Context(bytes calldata _data, address _msgSender)
pure
returns (bytes memory)
{
return abi.encodePacked(_data, _msgSender);
}
```
We are encoding the calldata and the user address together by simply appending the user's address to the end as required by ERC-2771.
## How can I modify my smart contract to be ERC-2771 compatible?
Let's take a look at an example using relay method `sponsoredCallERC2771`. For `callWithSyncFeeERC2771` please refer to the steps described [here](/relay/how-to-guides/allow-your-users-to-pay-with-erc20).
### 1. Install Gelato's relay-context package in your contract repo
```bash
npm install --save-dev @gelatonetwork/relay-context
```
or
```bash
yarn add -D @gelatonetwork/relay-context
```
### 2. Import the ERC2771Context contract:
```solidity
ERC2771Context
} from "@gelatonetwork/relay-context/contracts/vendor/ERC2771Context.sol";
```
This contract's main functionality (originally implemented by OpenZeppelin) is to decode the off-chain msg.sender from the encoded calldata using `_msgSender()`.
```solidity
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.9;
/**
* @dev Context variant with ERC2771 support.
*/
abstract contract ERC2771Context is Context {
address private immutable _trustedForwarder;
constructor(address trustedForwarder) {
_trustedForwarder = trustedForwarder;
}
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
return forwarder == _trustedForwarder;
}
function _msgSender() internal view virtual override returns (address sender) {
if (isTrustedForwarder(msg.sender)) {
// The assembly code is more direct than the Solidity version using `abi.decode`.
/// @solidity memory-safe-assembly
assembly {
sender := shr(96, calldataload(sub(calldatasize(), 20)))
}
} else {
return super._msgSender();
}
}
function _msgData() internal view virtual override returns (bytes calldata) {
if (isTrustedForwarder(msg.sender)) {
return msg.data[:msg.data.length - 20];
} else {
return super._msgData();
}
}
}
```
The `trustedForwarder` variable is set in the constructor which allows for setting a trusted party that will relay your message to your target smart contract. In our case, this is `GelatoRelay1BalanceERC2771.sol` which you can find in the contract addresses section.
The `_msgSender()` function encapsulates the main functionality of ERC-2771, by decoding the user address from the last 20 bytes of the calldata.
In Solidity, the logic is equivalent to:
```solidity
abi.decode(
msg.data[msg.data.length - 20:],
(address)
);
```
Gelato's smart contracts handle the encoding of important information to the calldata (see How does Gelato encode this data?). It is the job of your target smart contract function to decode this information using this `_msgSender()` function.
The function `_msgData()` removes the msg.sender from the entire calldata if the contract was called by the trustedForwarder, or otherwise falls back to return the original calldata.
### 3. Replace msg.sender with _msgSender()
Within the function that you would like to be called with Gelato Relay, replace all instances of `msg.sender` with a call to the `_msgSender()` function inherited from ERC2771Context. `_msgSender()` is the off-chain signer of the relay request, allowing for secure whitelisting on your target function.
### 4. (Re)deploy your contract and whitelist GelatoRelay1BalanceERC2771
If your contract is not upgradeable, then you will have to redeploy your contract to set `GelatoRelay1BalanceERC2771.sol` as your trustedForwarder:
`GelatoRelay1BalanceERC2771.sol` is immutable for security reasons. This means that once you set `GelatoRelay1BalanceERC2771.sol` as your trusted forwarder, there is no way for Gelato to change the ERC2771 signature verification scheme and so you can be sure that the intended `_msgSender` is correct and accessible from within your target contract.
Please refer to the contract addresses section to find out which Gelato relay address to use as a trustedForwarder. Use `GelatoRelay1BalanceERC2771.sol` address for `sponsoredCallERC2771`.
---
## Overview
**Path:** /relay/erc2771-recommended/callwithsyncfee-erc2771/overview
# CallWithSyncFeeERC2771
Transactions with on-chain payments and ERC2771 authentication support.
If you plan to use ERC-2771 with a multicall method or any other method using delegateCall(), please read carefully the section [Avoid ERC-2771-risks](/relay/security-considerations/erc2771-delegatecall-vulnerability).
If you are using `@gelatonetwork/relay-sdk` v3 or contracts from the package `@gelatonetwork/relay-context` v2, please follow this [migration guide](/relay/additional-resources/erc2771-migration-guide) to migrate to the new versions.
After reading this page:
- You'll know how to use the `callWithSyncFeeERC2771` SDK method, using the syncFee payment method
- You'll see some code which will help you send a relay request within minutes
- You'll learn how to pay for transactions using the provided values for fee, feeToken and feeCollector
Please proceed to our Security Considerations page and read it thoroughly before advancing with your implementation. It is crucial to understand all potential security risks and measures to mitigate them.
## Overview
The `callWithSyncFeeERC2771` method uses the syncFee payment method with ERC-2771 support.
## Paying for Transactions
When using `callWithSyncFeeERC2771` relay method, the target contract assumes responsibility for transferring the fee to Gelato's fee collector during transaction execution. For this, the target contract needs to know:
- fee: the transfer amount
- feeToken: the token to be transferred
- feeCollector: the destination address for the fee
Fortunately, Gelato provides some useful tools within the Relay Context Contracts:
1. By inheriting the `GelatoRelayContextERC2771` contract in your target contract, you have the ability to transfer the fee through one of two straightforward methods: `_transferRelayFee()` or `_transferRelayFeeCapped(uint256 maxFee)`. In either case, the inherited contract takes care of decoding the fee, feeToken, and feeCollector behind the scenes.
2. The Gelato Relay backend simplifies the process by automatically calculating the fee for you, using Gelato's Fee Oracle to perform the calculations in the background.
Alternatively, you may choose to inherit the `GelatoRelayFeeCollectorERC2771` contract. With this approach, Gelato decodes only the feeCollector. You must provide the fee and feeToken on-chain, either by hardcoding them (which is not recommended) or embedding them within the payload to be executed. The suggested way to handle this is to calculate the fee with Gelato's Fee Oracle.
This modular design ensures a smooth integration with Gelato's fee handling mechanisms, providing a flexible and user-friendly approach to managing transaction fees within your dApps.
## Setting maxFee for Your Transaction
Setting a maximum fee, or maxFee, for your transactions is strongly advised. This practice enables you to ensure that transaction costs remain below a specific limit. The method `_transferRelayFeeCapped(uint256 maxFee)` in the `GelatoRelayContextERC2771` contract provides a convenient way to set the maxFee easily.
If you are utilizing the `GelatoRelayFeeCollectorERC2771` contract, the recommended way to pass the maxFee is by calculating the fee with Gelato's Fee Oracle, which is accessible in the Relay SDK. The `getEstimatedFee()` method is provided to facilitate this calculation.
## SDK Methods
### callWithSyncFeeERC2771
This method initiates the signing of ERC2771 requests with the provided BrowserProvider or Wallet. Once the signature is obtained, the request is forwarded to Gelato.
```typescript
const callWithSyncFeeERC2771 = async (
request: CallWithSyncFeeERC2771Request | CallWithSyncFeeConcurrentERC2771Request,
signerOrProvider: ethers.BrowserProvider | ethers.Signer,
options?: RelayRequestOptions,
apiKey?: string
): Promise```
**Arguments**
- `request`: The body of the request intended for sending
- `signerOrProvider`: a valid provider connected to RPC or a signer
- `options`: an object for specifying optional parameters
- `apiKey`: an optional API key that links your request to your Gelato Relay account. As this call pertains to the syncFee payment method, transaction costs won't be deducted from your Gas Tank account. By using the API key, you can benefit from increased rate limits of your Gelato Relay account
**Response**
```typescript
type RelayResponse = {
taskId: string;
};
```
- `taskId`: a unique task ID which can be used for tracking your request
### getSignatureDataERC2771
This method starts the signing process for ERC2771 requests using the given BrowserProvider or Signer. After capturing the signature, it returns both the signature and the message. This collected data can then be used with the `callWithSyncFeeERC2771WithSignature` method to send the request to Gelato.
```typescript
getSignatureDataERC2771 = (
request: CallWithERC2771Request | CallWithConcurrentERC2771Request,
signerOrProvider: ethers.BrowserProvider | ethers.Signer,
type: ERC2771Type
): Promise```
**Arguments**
- `request`: The body of the request intended for sending
- `signerOrProvider`: a valid provider connected to RPC or a signer
- `type`: CallWithSyncFee for a sequential flow or ConcurrentCallWithSyncFee for a concurrent flow
**Response**
```typescript
type SignatureData = ConcurrentSignatureData | SequentialSignatureData;
type ConcurrentSignatureData = {
struct: CallWithConcurrentERC2771Struct;
signature: string;
};
type SequentialSignatureData = {
struct: CallWithERC2771Struct;
signature: string;
};
```
- `struct`: EIP-712 message data
- `signature`: EIP-712 signature
### getDataToSignERC2771
This method provides the message data intended for external signing along with the EIP-712 typed data. After obtaining the signature, the request can be dispatched using the `callWithSyncFeeERC2771WithSignature` method.
```typescript
getDataToSignERC2771 = (
request: CallWithERC2771Request | CallWithConcurrentERC2771Request,
type: ERC2771Type,
signerOrProvider?: ethers.BrowserProvider | ethers.Signer,
): Promise```
**Arguments**
- `request`: The body of the request intended for sending
- `type`: CallWithSyncFee for a sequential flow or ConcurrentCallWithSyncFee for a concurrent flow
- `signerOrProvider` (optional): A provider needed in a sequential flow to obtain the nonce from the smart contract. If you're providing the nonce within your request or if you're using the concurrent flow, this parameter isn't necessary
**Response**
```typescript
type PayloadToSign = ConcurrentPayloadToSign | SequentialPayloadToSign;
type ConcurrentPayloadToSign = {
struct: CallWithConcurrentERC2771Struct;
typedData: CallWithSyncFeeConcurrentERC2771PayloadToSign;
};
type SequentialPayloadToSign = {
struct: CallWithERC2771Struct;
typedData: CallWithSyncFeeERC2771PayloadToSign;
};
```
- `struct`: EIP-712 message data
- `typedData`: EIP-712 typed data
### callWithSyncFeeERC2771WithSignature
This method sends pre-signed requests to Gelato.
```typescript
const callWithSyncFeeERC2771WithSignature = async (
struct: CallWithERC2771Struct | CallWithConcurrentERC2771Struct;
syncFeeParams: BaseCallWithSyncFeeParams;
signature: string;
options?: RelayRequestOptions;
apiKey?: string
): Promise```
**Arguments**
- `struct`: EIP-712 message data returned from the signing methods
- `syncFeeParams`: the feetoken and isRelayContext params
- `signature`: EIP-712 signature returned after signing the request
- `options`: an object for specifying optional parameters
- `apiKey`: an optional API key that links your request to your Gelato Relay account. As this call pertains to the syncFee payment method, transaction costs won't be deducted from your Gas Tank account. By using the API key, you can benefit from increased rate limits of your Gelato Relay account
**Response**
```typescript
type RelayResponse = {
taskId: string;
};
```
- `taskId`: a unique task ID which can be used for tracking your request
### Optional Parameters
```typescript
type RelayRequestOptions = {
gasLimit?: BigNumberish;
retries?: number;
};
```
- `gasLimit`: the gas limit of the relay call. This effectively sets an upper price limit for the relay call.
If you are using your own custom gas limit, please add a 150k gas buffer on top of the expected gas usage for the transaction. This is for the Gelato Relay execution overhead, and adding this buffer reduces your chance of the task cancelling before it is executed on-chain.
If your contract has any hardcoded requirements about gas usage, please always explicitly pass the gasLimit to the SDK/API, as Gelato will not know what hardcoded gas expectations your contract has. Otherwise, your relay requests might not be executable.
- `retries`: the number of retries that Gelato should attempt before discarding this relay call. This can be useful if the state of the target contract is not fully known and such reverts can not be definitively avoided.
## Sending a Request
As of today, we support two distinct ways of sending `callWithSyncFeeERC2771` requests:
1. **Sequentially**: This approach ensures that each request is ordered and validated against the nonce stored on-chain. You have two options in this method:
- Fetch the current nonce value from the smart contract yourself and include it with your request
- Allow the relay-sdk to fetch the nonce value for you when handling your relay request
2. **Concurrently**: This method enables you to send multiple transactions simultaneously. Replay protection is achieved using a hash-based salt mechanism. Again, you have two options:
- Provide your own salt value
- Allow the relay-sdk to generate a unique salt value for you when processing your relay request
By default `callWithSyncFeeERC2771` requests are using the sequential method.
:::note
Concurrent ERC2771 support has been introduced in the relay-sdk version 5.1.0. Please make sure that your package is up-to-date to start using it.
:::
### Request Body
```typescript
type SequentialERC2771Request = {
chainId: BigNumberish;
target: string;
data: BytesLike;
user: string;
userDeadline?: BigNumberish;
feeToken: string;
isRelayContext?: boolean;
isConcurrent?: false;
userNonce?: BigNumberish;
};
type ConcurrentERC2771Request = {
chainId: BigNumberish;
target: string;
data: BytesLike;
user: string;
userDeadline?: BigNumberish;
feeToken: string;
isRelayContext?: boolean;
isConcurrent: true;
userSalt?: string;
};
```
#### Common Parameters
- `chainId`: the chain ID of the chain where the target smart contract is deployed
- `target`: the address of the target smart contract
- `data`: encoded payload data (usually a function selector plus the required arguments) used to call the required target address
- `user`: the address of the user's EOA
- `userDeadline`: optional, the amount of time in seconds that a user is willing for the relay call to be active in the relay backend before it is dismissed
This way the user knows that if the transaction is not sent within a certain timeframe, it will expire. Without this, an adversary could pick up the transaction in the mempool and send it later. This could transfer money, or change state at a point in time which would be highly undesirable to the user.
- `feeToken`: the address of the token that is to be used for payment. Please visit [SyncFee Payment Tokens](/relay/additional-resources/syncfee-payment-tokens) for the full list of supported payment tokens per network
- `isRelayContext`: an optional boolean (default: true) denoting what data you would prefer appended to the end of the calldata
If set to true (default), Gelato Relay will append the feeCollector address, the feeToken address, and the uint256 fee to the calldata. In this case your target contract should inherit from the `GelatoRelayContextERC2771` contract.
If set to false, Gelato Relay will only append the feeCollector address to the calldata. In this case your target contract should inherit from the `GelatoRelayFeeCollectorERC2771` contract.
#### Parameters For Sequential Requests
- `isConcurrent`: false (default), optional, represents that the users' requests are validated based on a nonce, which enforces them to be processed sequentially
- `userNonce`: optional, this nonce, akin to Ethereum nonces, is stored in a local mapping on the relay contracts. It serves to enforce the nonce ordering of relay calls if the user requires sequential processing. If this parameter is omitted, the relay-sdk will automatically query the current value on-chain
#### Parameters For Concurrent Requests
- `isConcurrent`: true, indicates that the users' requests are validated based on a unique salt, allowing them to be processed concurrently. Replay protection is still ensured by permitting each salt value to be used only once
- `userSalt`: optional, this is a bytes32 hash that is used for replay protection. If the salt is not provided then relay-sdk would generate a unique value based on a random seed and a timestamp
## Example Code (using GelatoRelayContextERC2771)
### 1. Deploy a GelatoRelayContextERC2771 compatible contract
```solidity
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;
GelatoRelayContextERC2771
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContextERC2771.sol";
// Inheriting GelatoRelayContext gives access to:
// 1. _getFeeCollector(): returns the address of Gelato's feeCollector
// 2. _getFeeToken(): returns the address of the fee token
// 3. _getFee(): returns the fee to pay
// 4. _transferRelayFee(): transfers the required fee to Gelato's feeCollector.abi
// 5. _transferRelayFeeCapped(uint256 maxFee): transfers the fee to Gelato
// only if fee < maxFee
// 6. function _getMsgSender(): decodes and returns the user's address from the
// calldata, which can be used to refer to user safely instead of msg.sender
// (which is Gelato Relay in this case).
// 7. _getMsgData(): returns the original msg.data without appended information
// 8. onlyGelatoRelay modifier: allows only Gelato Relay's smart contract
// to call the function
contract CounterRelayContextERC2771 is GelatoRelayContextERC2771 {
using Address for address payable;
mapping(address => uint256) public contextCounter;
// emitting an event for testing purposes
event IncrementCounter(address msgSender);
// `increment` is the target function to call.
// This function increments a counter variable which is
// mapped to every _getMsgSender(), the address of the user.
// This way each user off-chain has their own counter
// variable on-chain.
function increment() external onlyGelatoRelayERC2771 {
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelay modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFee();
// Incrementing the counter mapped to the _getMsgSender()
contextCounter[_getMsgSender()]++;
emit IncrementCounter(_getMsgSender());
}
// `incrementFeeCapped` is the target function to call.
// This function uses `_transferRelayFeeCapped` method to ensure
// better control of gas fees. If gas fees are above the maxFee value
// the transaction will not be executed.
// The maxFee will be passed as an argument to the contract call.
// This function increments a counter variable by 1
// IMPORTANT: with `callWithSyncFee` you need to implement
// your own smart contract security measures, as this
// function can be called by any third party and not only by
// Gelato Relay. If not done properly, funds kept in this
// smart contract can be stolen.
function incrementFeeCapped(uint256 maxFee) external onlyGelatoRelayERC2771 {
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelay modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFeeCapped(maxFee);
// Incrementing the counter mapped to the _getMsgSender()
contextCounter[_getMsgSender()]++;
emit IncrementCounter(_getMsgSender());
}
}
```
### 2. Import GelatoRelaySDK into your front-end .js project
```javascript
```
Once we have imported the GelatoRelay class, when using ERC2771 methods, we must initialize it with the appropriate trustedForwarder. The possible configurations are:
```javascript
contract: {
relay1BalanceERC2771: "trustedForwarder for method sponsoredCallERC2771",
relayERC2771: "trustedForwarder for method callWithSyncFeeERC2771",
relay1BalanceConcurrentERC2771: "trustedForwarder for method concurrent sponsoredCallERC2771",
relayConcurrentERC2771: "trustedForwarder for method concurrent callWithSyncFeeERC2771",
relay1BalanceConcurrentERC2771zkSync: "trustedForwarder for method concurrent sponsoredCallERC2771 on zkSync",
relay1BalanceERC2771zkSync: "trustedForwarder for method sponsoredCallERC2771 on zkSync",
relayConcurrentERC2771zkSync: "trustedForwarder for method concurrent callWithSyncFeeERC2771 on zkSync",
relayERC2771zkSync: "trustedForwarder for method callWithSyncFeeERC2771 on zkSync",
}
```
We will need to go to the [Supported Networks](/relay/additional-resources/supported-networks) and check the network and the contract addresses to identify the trustedForwarder associated with our method.
In the example below, we are using the method `callWithSyncFeeERC2771` on Sepolia, the trustedForwarder associated is `0xb539068872230f20456CF38EC52EF2f91AF4AE49`. We will initialize GelatoRelay with the following config:
```javascript
const relay = new GelatoRelay({
contract: {
relayERC2771: "0xb539068872230f20456CF38EC52EF2f91AF4AE49"
}
});
```
### 3. Send the payload to Gelato
```javascript
// target contract address
const counter = "
After creating the relay app, navigate to its dashboard to locate your Sponsor API Key. This key links your Gelato setup with Gas Tank for gas sponsorship.
Gelato Relay now supports API key rotation, allowing users to create and delete API keys. This helps prevent unauthorized usage in case an API key is exposed.
`Activate` your sponsor API key by allowing access to `all contracts` on a network, or restrict it to `specific contracts` or `specific functions`.
Here, you can configure different networks. For each network, you can choose to allow access to all target contracts or limit it to selected contracts or specific functions.
Add funds to your Gas Tank account according to your target environment:
- **Mainnets**: Deposit USDC.
- **Testnets**: Deposit Sepolia ETH.
Since Gas Tank is deployed on Polygon, you can deposit USDC in one step, and deposits from other networks are supported via Circle CCTP. Learn [more](/paymaster-&-bundler/gastank/introduction).
---
## Send Sponsored Transactions
**Path:** /relay/how-to-guides/send-sponsored-transactions
# Sponsored Calls
Sponsored Calls enable you to cover your users' gas fees, providing a seamless and gasless experience for interacting with your dApp. This is made possible through Gelato's multi-chain unified gas payment system, Gas Tank. Learn more about Gas Tank [here](/paymaster-&-bundler/gastank/introduction).
## Implementation Steps
### 1. Import GelatoRelaySDK into your front-end .js project
Import SponsoredCallRequest for Non ERC2771 sponsored calls with GelatoRelay.
```typescript
const relay = new GelatoRelay();
```
or
If you're using the Viem library in your project, consider importing @gelatonetwork/relay-sdk-viem.
```typescript
const relay = new GelatoRelay();
```
#### For ERC2771 Sponsored Calls (Recommended)
Import CallWithERC2771Request for ERC2771 sponsored calls with GelatoRelay. Learn more about ERC2771 [here](/relay/erc2771-recommended/overview).
```typescript viem
```
```typescript ethers v6
```
Once we have imported the GelatoRelay class, when using ERC2771 methods, we must initialize it with the appropriate trustedForwarder.
The possible configurations are:
```typescript
contract: {
relay1BalanceERC2771: "trustedForwarder for method sponsoredCallERC2771",
relayERC2771: "trustedForwarder for method callWithSyncFeeERC2771",
relay1BalanceConcurrentERC2771: "trustedForwarder for method concurrent sponsoredCallERC2771",
relayConcurrentERC2771:"trustedForwarder for method concurrent callWithSyncFeeERC2771",
relay1BalanceConcurrentERC2771zkSync: "trustedForwarder for method concurrent sponsoredCallERC2771 on zkSync",
relay1BalanceERC2771zkSync: "trustedForwarder for method sponsoredCallERC2771 on zkSync",
relayConcurrentERC2771zkSync: "trustedForwarder for method concurrent callWithSyncFeeERC2771 on zkSync",
relayERC2771zkSync: "trustedForwarder for method callWithSyncFeeERC2771 on zkSync",
}
```
We will need to go to the [Supported Networks](/relay/additional-resources/supported-networks) and check the network and the contract addresses to identify the trustedForwarder associated with our method.
In the example below, we are using the method sponsoredCallERC2771 on Sepolia, the trustedForwarder associated is 0xd8253782c45a12053594b9deB72d8e8aB2Fca54c. We will initialize GelatoRelay with the following config:
```typescript
const relay = new GelatoRelay({
contract: {
relay1BalanceERC2771:"0xd8253782c45a12053594b9deB72d8e8aB2Fca54c"
}
});
```
### 2. Deploy a smart contract
For non-ERC2771 sponsored calls, no modifications are required in your target contract. Below is an example of a target contract.
```solidity
contract TargetContract {
// your logic
function example() external {
// your logic
}
}
```
#### For ERC2771 Sponsored Calls
Import ERC2771Context into your target contract and initialize it in the constructor with the appropriate trusted forwarder based on your use case. checkout list of trusted forwarders [here](/relay/additional-resources/supported-networks).
```solidity
ERC2771Context
} from "@gelatonetwork/relay-context/contracts/vendor/ERC2771Context.sol";
contract TargetContract is ERC2771Context {
// ERC2771Context: setting the immutable trustedForwarder variable
constructor(address trustedForwarder) ERC2771Context(trustedForwarder) {}
function example() external{
// your logic
}
}
```
### 3. Generate a payload for your target contract
```typescript viem
// set up target address and function signature abi
const targetContractAddress = "your target contract address";
const abi = ["function example()"];
const [address] = await window.ethereum!.request({
method: "eth_requestAccounts",
});
// generate payload using front-end provider such as MetaMask
const client = createWalletClient({
account: address,
chain,
transport: custom(window.ethereum!),
});
const chainId = await client.getChainId();
//encode function data
const data = encodeFunctionData({
abi: abi,
functionName: "example",
});
```
```typescript ethers v6
// set up target address and function signature abi
const targetContractAddress = "target contract address";
const abi = ["function example()"];
// generate payload using front-end provider such as MetaMask
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = provider.getSigner();
const user = await signer.getAddress();
const contract = new ethers.Contract(targetContractAddress, abi, signer);
const { data } = await contract.populateTransaction.example();
```
### 4. Send the payload to Gelato
For sending sponsored calls, you need to have a sponsor API key. Learn how to create a sponsor API key [here](/relay/how-to-guides/create-a-sponsor-api-key).
```typescript viem
// Populate a relay request
const request: SponsoredCallRequest = {
chainId: BigInt(chainId),
target: targetContractAddress,
data: data as BytesLike,
};
const relayResponse = await relay.sponsoredCall(
request,
GELATO_RELAY_API_KEY
);
```
```typescript ethers v6
// Populate a relay request
const request: SponsoredCallRequest = {
chainId: (await provider.getNetwork()).chainId,
target: targetContractAddress,
data: data,
};
const relayResponse = await relay.sponsoredCall(request, apiKey);
```
#### For ERC2771 Sponsored Calls
```typescript viem
// Populate a relay request
const request : CallWithERC2771Request = {
user: address,
chainId: BigInt(chainId),
target: targetContractAddress,
data: data as BytesLike,
};
const response = await relay.sponsoredCallERC2771(
request,
client as any,
GELATO_RELAY_API_KEY
);
```
```typescript ethers v6
// Populate a relay request
const request: CallWithERC2771Request = {
chainId: (await provider.getNetwork()).chainId,
target: targetContractAddress;
data: data;
user: user;
};
const relayResponse = await relay.sponsoredCallERC2771(request, signer, apiKey);
```
Learn more about implementation of ERC2771 Sponsored Calls [here](/relay/erc2771-recommended/sponsoredcall-erc2771) and Non ERC2771 Sponsored Calls [here](/relay/non-erc2771/sponsoredcall).
---
## Allow your users to pay with ERC-20
**Path:** /relay/how-to-guides/allow-your-users-to-pay-with-erc20
Non-Sponsored calls, also known as Sync Fee Calls, are the simplest way to pay for relay services. However, they delegate all security (reentrancy/replay protection etc.) and payment logic to the target smart contract. You can use ERC-2771 to achieve out-of-the-box security and authentication. Relay costs are covered in either native or ERC-20 tokens and they are paid synchronously during the relay call.
For ERC-20 tokens that implement the permit function (EIP-2612), you can enable gasless transactions by allowing users to authorize token spending through off-chain signatures instead of requiring an on-chain approval transaction.
## Implementation Steps
### 1. Deploy a GelatoRelayContext compatible contract
Import GelatoRelayContext in your target contract to inherit callWithSyncFee functionalities:
```solidity
GelatoRelayContext
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContext.sol";
contract TargetContract is GelatoRelayContext {
function example() external onlyGelatoRelay {
// _yourAuthenticationLogic()
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelay modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFee();
}
function exampleFeeCapped(uint256 maxFee) external onlyGelatoRelay {
// Remember to authenticate your call since you are not using ERC-2771
// _yourAuthenticationLogic()
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelay modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFeeCapped(maxFee);
}
}
```
### For ERC2771 Sync Fee Calls (Recommended)
Import GelatoRelayContextERC2771 in your target contract to inherit ERC2771 functionalities with callWithSyncFee:
```solidity
GelatoRelayContextERC2771
} from "@gelatonetwork/relay-context/contracts/GelatoRelayContextERC2771.sol";
contract TargetContractRelayContextERC2771 is GelatoRelayContextERC2771 {
mapping (address=>bool) public caller;
function increment() external onlyGelatoRelayERC2771 {
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelayERC2771 modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFee();
// _getMsgSender() will fetch the original user who signed the relay request.
caller[_getMsgSender()] = true;
}
function incrementFeeCapped(uint256 maxFee) external onlyGelatoRelayERC2771 {
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelayERC2771 modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFeeCapped(maxFee);
// _getMsgSender() will fetch the original user who signed the relay request.
caller[_getMsgSender()] = true;
}
// Enhanced functions with ERC-20 permit support for gasless payments
function incrementWithPermit(
address token,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external onlyGelatoRelayERC2771 {
address user = _getMsgSender();
// Execute permit to allow contract to spend user's tokens
// This eliminates the need for a separate approval transaction
IERC20Permit(token).permit(user, address(this), amount, deadline, v, r, s);
// Your business logic here
IERC20(token).transferFrom(user, address(this), amount);
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelayERC2771 modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFee();
// _getMsgSender() will fetch the original user who signed the relay request.
caller[user] = true;
}
function incrementWithPermitFeeCapped(
address token,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s,
uint256 maxFee
) external onlyGelatoRelayERC2771 {
address user = _getMsgSender();
// Execute permit to allow contract to spend user's tokens
IERC20Permit(token).permit(user, address(this), amount, deadline, v, r, s);
// Your business logic here
IERC20(token).transferFrom(user, address(this), amount);
// Payment to Gelato
// NOTE: be very careful here!
// if you do not use the onlyGelatoRelayERC2771 modifier,
// anyone could encode themselves as the fee collector
// in the low-level data and drain tokens from this contract.
_transferRelayFeeCapped(maxFee);
// _getMsgSender() will fetch the original user who signed the relay request.
caller[user] = true;
}
}
```
### 2. Import GelatoRelaySDK into your front-end .js project
```javascript
const relay = new GelatoRelay();
```
Or if you're using the Viem library:
```javascript
const relay = new GelatoRelay();
```
### For ERC2771 Sync Fee Calls
```javascript
```
When using ERC2771 methods, initialize GelatoRelay with the appropriate trustedForwarder. The possible configurations are:
```javascript
contract: {
relay1BalanceERC2771: "trustedForwarder for method sponsoredCallERC2771",
relayERC2771: "trustedForwarder for method callWithSyncFeeERC2771",
relay1BalanceConcurrentERC2771: "trustedForwarder for method concurrent sponsoredCallERC2771",
relayConcurrentERC2771: "trustedForwarder for method concurrent callWithSyncFeeERC2771",
relay1BalanceConcurrentERC2771zkSync: "trustedForwarder for method concurrent sponsoredCallERC2771 on zkSync",
relay1BalanceERC2771zkSync: "trustedForwarder for method sponsoredCallERC2771 on zkSync",
relayConcurrentERC2771zkSync: "trustedForwarder for method concurrent callWithSyncFeeERC2771 on zkSync",
relayERC2771zkSync: "trustedForwarder for method callWithSyncFeeERC2771 on zkSync",
}
```
Check the Supported Networks and contract addresses to identify the trustedForwarder associated with your method.
Example for Sepolia using callWithSyncFeeERC2771:
```javascript
const relay = new GelatoRelay({
contract: {
relayERC2771: "0xb539068872230f20456CF38EC52EF2f91AF4AE49"
}
});
```
### 3. Generate a payload for your target contract
```javascript
// Set up target address and function signature abi
const targetContractAddress = "
#### High Reliability & Multi-Cloud
Gelato's RaaS provides a multi-cloud, globally distributed infrastructure designed for high availability and fault tolerance, ensuring a resilient and robust setup that mitigates service disruptions.
## Dive Deeper into the Gelato RaaS
Follow our simple guide to launch your Layer-2 / Layer-3 Chains with Gelato.
Learn about available execution and data availability solutions.
Explore our turnkey marketplace offering with more than 25+ integrations.
---
## OP Stack
**Path:** /rollup-as-a-service/rollup-stacks/op
The OP Stack is an open-source development framework that, combined with Gelato RaaS, enables developers to deploy their own Optimistic Layer 2 chains on Ethereum. Using optimistic rollups, developers can benefit from low computational overhead, fast throughput, low transaction costs, and improved EVM compatibility.
Optimism Bedrock is the current iteration of the OP Stack. The Bedrock release provides the tools for launching a production-quality Optimistic Rollup blockchain.
## OP Stack Features
### Lower Fees
Achieves 90% lower fees than Ethereum by optimizing data compression and eliminating gas expenses for EVM execution.
### Fast Transactions
Provides a 2-second block time compared to Ethereum's 12-second average, ensuring faster transaction confirmations.
### Enhanced Proof Modularity
Allows rollups to use various proof systems, like fault or validity proofs, providing flexibility for technologies like Cannon to verify correct execution.
### Custom Gas Token
Supports the use of alternative ERC-20 tokens for gas fees, integrating with different app ecosystems.
### Ethereum Equivalence
Offers high compatibility with Ethereum, allowing smart contracts and dApps designed for Ethereum to run without modification and utilizing existing Ethereum tools.
### Improved Node Performance
Executes multiple transactions in a single rollup block and removes technical debt from previous versions, improving node software performance.
## The Superchain
The Superchain is the next major scalability improvement for the OP Stack, following Bedrock. It envisions a network of chains (OP Chains) that share resources like bridging, decentralized governance, upgrades, and communication layers, all built on the OP Stack. By merging OP Mainnet and other chains into a single, unified network, the Superchain aims to bring scalable and decentralized compute to the world. This unified network will enable efficient resource sharing and significantly enhance scalability. To achieve this vision, the OP Stack will undergo several changes to support the new structure. For more detailed information, refer to the [OP Stack Explainer](https://docs.optimism.io/stack/getting-started).
Get more information about [OP Stack](https://docs.optimism.io/stack/getting-started), or Schedule call to set up your
custom Op Stack Gelato L2 testnet.
---
## Arbitrum Orbit
**Path:** /rollup-as-a-service/rollup-stacks/arbitrum-orbit
Arbitrum Orbit is an open-source framework that allows developers to deploy customized Arbitrum Rollup and AnyTrust chains. It is designed for the Ethereum ecosystem, offering high performance, cost efficiency, and Ethereum compatibility.
## Arbitrum Rollup
Arbitrum Rollup, implemented by Arbitrum One, stores raw transaction data on Ethereum's Layer 1 (L1) blockchain, ensuring security through Ethereum's model while improving scalability with off-chain computation.
## Arbitrum Orbit Features
### Enhanced Throughput and Isolation
Orbit chains offer dedicated throughput and traffic isolation, ensuring reliable gas prices and improved performance for end-users. This is especially beneficial for applications demanding high performance or consistent resource availability.
### Versatility and Interoperability
Suitable for hosting a single dApp or an ecosystem of dApps, with the capability to communicate with other Orbit chains.
### Customizable Chain Architecture
Supports both Arbitrum Rollup and AnyTrust protocols, allowing developers to choose the optimal proof system for their needs.
### EVM+ Compatibility
Supports EVM+ via Stylus, enabling smart contract deployment in multiple programming languages.
### Custom Gas Token
Allows the use of alternative ERC-20 tokens for gas fees, integrating seamlessly with different app ecosystems.
### Gas Price Stability
Provides more predictable gas prices through isolated chains.
Get more information about [Arbitrum Orbit](https://arbitrum.io/orbit), or Schedule call to set up your
custom OP Stack Gelato L2 testnet.
---
## ABC Stack
**Path:** /rollup-as-a-service/rollup-stacks/abc
ABC Stack is a next-generation Rollup L1 framework built on top of Celestia's Sovereign Rollup paradigm. Designed for high-throughput, developer flexibility, and complete sovereignty, ABC Stack delivers an execution-first blockchain architecture without the constraints of traditional Layer 2 solutions.
## Why ABC Stack?
### Sovereign by Design
Unlike L2 rollups that rely on enshrined bridges and external settlement layers (like Ethereum), ABC Stack operates as a **sovereign rollup**. This means:
- No dependency on external smart contracts for settlement.
- Full control over execution, consensus, and governance.
- No settlement layer fees or architectural complexity.
### Modular Architecture
ABC Stack cleanly separates the core blockchain functions:
- **Execution:** Handled entirely within the sovereign rollup.
- **Data Availability & Consensus:** Powered by **Celestia**.
- **Bridging:** Modular and customizable — choose when and how to integrate.
---
## Performance & Scalability
### Gigagas Throughput
ABC Stack delivers **1 Gigagas per second** throughput — significantly outperforming L2s — by optimizing execution and removing settlement overhead.
### Low Latency & Fast Finality
- **Sub-100ms block times** through optimized production pipelines.
- **Partial block architecture** enables near-instant (10ms) state updates.
- **No reorgs**, thanks to Celestia's single-slot finality.
---
## Advanced Block Building
- **Custom Block Logic:** Define custom transaction rules and orderings.
- **Rollup Boost Integration:** Leverage external block builders for MEV protection or custom needs.
- **Lazy Block Building:** Blocks are generated on-demand to conserve resources.
- **Dynamic Gas Limits:** Automatically scale block gas capacity with demand.
- **Custom EIP-1559:** Modify fee mechanics to match your app's economy.
---
## Developer Infrastructure
ABC Stack comes with critical smart contract infrastructure preinstalled:
- **Account Abstraction:** Supports EIP-4337 (EntryPoint v0.6.0 & v0.7.0).
- **Safe Contracts:** Includes Safe, SafeL2, MultiSend, and more.
- **Deployment Tools:** Safe Singleton Factory, create2Deployer, Arachnid's CreateX.
- **DeFi Ready:** Permit2, Multicall3, and other utility contracts.
### Cryptographic Precompiles
- **EIP-7212:** Native support for `secp256r1` for WebAuthn keys.
- **EIP-2537:** Efficient BLS12-381 curve ops for ZK and threshold signatures.
- **Prague Hardfork EIPs:** Future-proofed with latest Ethereum tech.
---
## Privacy, UX, and Account Features
- **Gasless & Sponsored Transactions** via EIP-7702.
- **WebAuthn Integration:** Biometric and passwordless onboarding.
- **Call Batching & Session Keys:** Streamlined and secure user flows.
- **Multi-Factor Authentication:** Smart contract-level 2FA.
- **Private Mempools:** Prevent frontrunning with confidential transaction pools.
---
## Modular Bridging & Interop
- **Gelato Hyperlane Cluster:** Connect to 100+ chains with modular messaging.
- **Native or Bridged Tokens:** Choose your token model.
- **Chain Abstraction (WIP):** Use ERC-7683 intents for invisible cross-chain UX.
---
## Security & Identity
- **ZK Privacy:** Confidential transactions and attribute proofs.
- **Flexible Recovery:** Social, OAuth, and multi-path account recovery.
- **ZK Passport Support:** Anonymous verification with integrity.
---
## Conclusion: The Future of Sovereign Blockchain Design
ABC Stack realizes the full potential of the modular blockchain thesis. By eliminating the architectural constraints of L2s, embracing Celestia's modular DA layer, and empowering developers with robust tooling and sovereign control, ABC Stack is the ideal foundation for building scalable, secure, and user-friendly applications — from fully on-chain games to high-throughput DeFi protocols.
> "Where we're going, you won't need settlement layers."
ABC Stack is the future — and it's sovereign.
---
## Celestia
**Path:** /rollup-as-a-service/data-availability/celestia
[Celestia](https://celestia.org/) offers a scalable modular data availability network that securely scales with user numbers, enabled by Data Availability Sampling (DAS). It facilitates the deployment of high-throughput and low-cost validium and sovereign rollups on Gelato. Layer 2 solutions utilize Celestia as a network for publishing transaction data, making it available for download by anyone.
## Celestia DA Layer Key Features
### Dynamic Scaling
Celestia uses data availability sampling to enable scaling that increases with the number of users, ensuring dynamic adaptability to growing demands.
### Virtual Machine Flexibility
Offers the ability to choose any Virtual Machine, facilitating the development of applications with specialized features and diverse use-cases.
### Ease of Deployment
Users can deploy their own L2 Blockchain quickly, with a simplicity comparable to deploying a smart contract.
## Design Principles
### Data Availability Sampling (DAS)
This process allows light nodes in Celestia to verify the availability of block data without downloading the entire block. Light nodes randomly sample small portions of the data, and if these samples are verified, it indicates that the full block's data is likely available. This method ensures efficient and scalable data verification.
### Namespaced Merkle Trees (NMTs)
NMTs are used to organize block data into distinct sections (namespaces), each corresponding to different applications like rollups. They enable applications to download and verify only the data relevant to them, ignoring data from other applications. This system ensures that applications receive all the necessary data for their specific namespace.
## Celestia with Gelato Execution Frameworks
Gelato integrates with Celestia so execution frameworks post the calldata to Celestia rather than directly on Ethereum. This reduces data storage costs significantly and enables higher transaction throughput making the roll-ups more attractive for applications that require high performance and lower fees.
### Celestia x Arbitrum Orbit
Gelato's support for Celestia integration with Arbitrum Orbit provides developers with an alternative data availability layer facilitating the launch of high-throughput, optimistic-powered Ethereum Layer 2 chains. This integration enables the deployment of Arbitrum Rollups using Celestia for data availability instead of Ethereum, scaling securely with numbers of users' with data availability sampling (DAS).
In the case of ERC20 transfer rollup transactions on OP stack with 1M transactions and an average callData size of 120 bytes:
- Expected cost: $122,413
- Using Celestia for callData: $347
- Savings: 99.74%
### Celestia x OP Stack
Gelato's support for Celestia's integration with OP Stack facilitates the launch of high-throughput, optimistic-powered Ethereum Layer 2 chains made possible by Celestia's data availability sampling (DAS). If Celestia experiences downtime or temporary unavailability, L2s can fallback to posting transactions as calldata on Ethereum or another DA layer to maintain data availability.
In the case of ERC20 transfer rollup transactions on OP stack with 1M transactions and an average callData size of 120 bytes:
- Expected cost: $78,558
- Using Celestia for callData: $332
- Savings: 99.61%
## Next Steps
Get more information about [Celestia](https://celestia.org/), or [Schedule a call](https://app.gelato.cloud/rollups/request?type=1click) to set up your custom Op Stack Gelato L2 testnet.
---
## Avail
**Path:** /rollup-as-a-service/data-availability/avail
Gelato RaaS leverages [Avail](https://www.availproject.org/) as a data availability (DA) layer. Avail provides a fast and secure data and consensus layer enabling the creation of hyperefficient L2 blockchains such as Validiums and Plasma, for ultimate control of your state, and execution environment.
## Avail DA Layer Key Features
### Secure Verifiable Data
Avail allows light clients to easily verify data availability through sampling over a peer-to-peer network, and inherit full node-level security and validation directly from the DA layer.
### Simple Blockchain Integration
Avail's modular approach eliminates the need for developers to manage validator sets or understand tokenomics.
### Flexible Execution Environment
Avail's data-agnostic nature accommodates multiple execution environments, such as EVM, WASM, and custom new runtimes, providing a flexible foundation for a wide range of blockchain applications.
## Design Principles
### Erasure Coding
Erasure coding in Avail works by adding layers of redundancy to transaction data to ensure its integrity and reliability. When transactions are processed in Avail, they are split into parts that are duplicated and can be used to reconstruct the full data. This means that even if some parts are lost or corrupted, the complete data can still be recovered.
### Data Availability Sampling (DAS)
Avail's light clients perform data availability sampling by randomly sampling small sections of block data to verify their correctness. Combined with erasure coding and KZG polynomial commitments, this technique enables Avail clients to provide strong guarantees of data availability, nearly 100%, without relying on fraud proofs and with only a minimal number of queries.
## Next Steps
Get more information about [Avail](https://www.availproject.org/), or [Schedule a call](https://app.gelato.cloud/rollups/request?type=1click) to set up your custom Op Stack Gelato L2 testnet.
---
## EigenDA
**Path:** /rollup-as-a-service/data-availability/eigenda
EigenDA is a high-throughput, decentralized data availability (DA) service designed for rollups on the Ethereum blockchain. It uses EigenLayer restaking primitives to ensure a secure and scalable infrastructure for data availability.
## Key Features
### Scalability
EigenDA offers scalable data availability with a throughput of up to 10 MBps, demonstrated in private testing. Plans to scale to 1 GBps are underway, enhancing the performance of blockchain networks significantly.
### Security
EigenDA leverages EigenLayer's restaking primitives to provide a secure DA infrastructure. Techniques like erasure coding and KZG commitments ensure efficient and secure data storage and retrieval.
### Cost Efficiency
EigenDA minimizes the costs associated with data availability by leveraging a shared security model. This approach reduces capital costs of staking and operational costs for operators.
## Design Principles
### Erasure Coding and KZG Commitments
EigenDA uses erasure coding to break data into smaller chunks, which are then distributed across multiple operators. KZG commitments ensure the integrity and availability of these chunks.
### Congestion Management
EigenDA manages congestion through higher throughput capabilities and bandwidth reservation, making data availability more predictable and cost-effective for rollups.
## Components
1. **Operators** :
Run EigenDA node software and are responsible for storing data chunks.
2. **Disperser** :
An untrusted service that encodes blobs, generates KZG commitments and proofs, and registers storage on Ethereum.
3. **Retrievers** :
Query operators for data chunks, verify them, and reconstruct the original blob for users.
## Next Steps
Get more information about EigenDA, or [Schedule a call](https://app.gelato.cloud/rollups/request?type=1click) to set up your custom Op Stack Gelato L2 testnet.
---
## Custom Gas Token
**Path:** /rollup-as-a-service/customization/custom-gas-token
With Gelato Rollups, you have the option to designate an ERC20 token as the native token for your rollup. This section covers the requirements and steps for integrating custom gas tokens in your deployments on the OP Stack and Arbitrum Orbit Stack.
## ERC20 Token Requirements
To use a custom gas token, the ERC20 token must meet the following requirements:
1. **ERC20 Token Smart Contract Address**: Ensure the token contract is deployed and you have the contract address.
2. **Decimals**: The token must have 18 decimal places (dp).
3. **Token Type**: The token must be a non-rebasing token, meaning its total supply does not change due to rebasing mechanisms.
## Arbitrum Orbit Configuration
Arbitrum Orbit requires the custom gas tokens to be utilized as part of the deployment process. Follow these steps to integrate a custom gas token with Arbitrum Orbit:
1. **Verify Token Compatibility**: Ensure your ERC20 token meets the specified requirements.
2. **Configure Deployment Settings**: Set the ERC20 token contract address in your deployment configuration.
3. **Deploy Your Contract**: Proceed with the deployment. The custom gas tokens will be utilized as part of the deployment process.
## Next Steps
Get more information about Custom Gas Tokens, or Schedule a call to set up your custom Op Stack Gelato L2 testnet.
---
## Flashblocks
**Path:** /rollup-as-a-service/customization/flashblocks
## Overview
Flashblocks bring sub-second transaction confirmations to OP Stack rollups, now available through **Gelato RaaS**.
Instead of waiting for full blocks (e.g. 2s on Optimism or 12s on Ethereum), Flashblocks deliver **execution preconfirmations in ~200ms** by streaming lightweight partial blocks.
## Why Flashblocks?
- **Near-instant confirmations**: Users see transaction receipts in milliseconds.
- **No EVM-equivalence trade-offs**: Expensive operations like state root generation are deferred to full blocks.
- **Efficient scaling**: Amortizes computation while supporting higher gas throughput.
- **Better UX**: Feels like Web2 responsiveness, critical for DeFi, gaming, and real-time apps.
## How It Works
- Transactions are executed in **ephemeral partial blocks** every 200ms.
- Heavy computation (state roots, consensus) happens only once per full block.
- RPC nodes can serve flashblock state directly, enabling wallets and dApps to reflect balances or swaps instantly.
## Open Source Infrastructure
Flashblocks are powered by **Flashbots** contributions:
- **op-rbuilder** – Rust-based block builder separating execution from state commitment.
- **Rollup-Boost** – TEE-based, verifiable block building framework.
- **Flashblocks WebSocket Proxy** – Streams flashblocks securely to RPC nodes (contributed by Base).
## Benefits
- Real-time DeFi with more capital-efficient markets.
- Ultra-low latency for trading, payments, and gaming.
- Compatible today with **OP Stack rollups** via Gelato RaaS.
## Get Started
Flashblocks support is production-ready on Gelato RaaS. [Contact](https://app.gelato.cloud/chains) the Gelato team to enable Flashblocks on your OP Stack chain.
---
## Public Testnet
**Path:** /rollup-as-a-service/customization/public-testnet
The launch of fully-integrated testnet is a public good initiative by Gelato and 10+ partners to lower the barriers for developers and help them build and test the next generation of applications in a production-ready environment. Our public testnet is entirely free to use, opening up a world of possibilities for developers, students, and innovators worldwide.
## What is Gelato's Fully-Integrated Public Testnet?
Developers can start building using this public testnet:
### Blueberry ([Arbitrum](/rollup-as-a-service/rollup-stacks/arbitrum-orbit))
This testnet include all web3 services from Gelato as well as fully integrated partner services.
## Gelato Native Web3 Services
At the heart of our testnet is the Gelato web3 services, a powerful toolset designed to automate and enhance your applications:
1. **[Functions](/web3-functions/)** - Smart contract automation
2. **[VRF](/vrf/)** - Verifiable onchain randomness
3. **[Account Abstraction](/smart-wallet-sdk/)** - Programmable Smart Accounts
## Gelato Endpoints
1. **[Relay](/relay/)** - Gasless transactions
2. **[RPCs](/private-rpcs/)** - Reliable Node Infrastructure
## Third Party Services
Our testnet feature a fully integrated web3 service offering with all the web3 infrastructure and tooling you need to build feature-rich applications:
1. **[Goldsky](https://goldsky.com/)** - read, edit, and sync fresh chain data
2. **[Blockscout](https://www.blockscout.com/)** - access essential on-chain data
3. **[Thirdweb](https://thirdweb.com/)** - onboard anyone with flexible sign-in options
4. **[ZeroDev](https://zerodev.app/)** - create smart wallets for your users
5. **[Safe](https://safe.global/)** - use the most secure smart wallet infrastructure
6. **[Tenderly](https://tenderly.co/)** - build, test, monitor, and operate smart contracts
## Arbitrum Blueberry
[Blueberry](https://app.gelato.cloud/rollups/details/public/arb-blueberry) is the first public Arbitrum Anytrust L3 testnet built on Arbitrum Orbit. Anytrust is designed to significantly reduce the cost of transactions and provide high throughput.
### Network Attributes
{(() => {
const networkAttributes = [
{ attribute: "chainID", value: "88153591557" },
{ attribute: "Settlement Layer", value: "Arbitrum Sepolia" },
{ attribute: "RPC URL", value: "https://rpc.arb-blueberry.gelato.digital", isLink: true },
{ attribute: "Explorer", value: "https://arb-blueberry.gelatoscout.com/", isLink: true }
];
return (
{item.value}
)}
### 2. Bridging CGT to Arbitrum Blueberry
After minting CGT on Arb Sepolia, the next step involves bridging these tokens to Arbitrum Blueberry. This process is facilitated through [Arbitrum Blueberry](https://bridge.gelato.network/bridge/arb-blueberry) bridge service designed to seamlessly transfer your CGT, enabling you to use it for transaction fees within the Arbitrum Blueberry environment.
---
## Verifier Node Package
**Path:** /rollup-as-a-service/customization/verifier-node-package
Gelato provides a complete ecosystem for successful node sales. We equip you with node clients and smart contracts, connect you with key Launchpad and Node-as-a-Service partners. This end-to-end solution ensures you have everything needed to run a successful node sale, from technical infrastructure to community engagement.
## Benefits of Gelato Verifier Node
- **Community Engagement** :
Encourage community members to take an active role in securing the rollup,
fostering a sense of ownership and participation. Add value and utility to the
project by requiring Node Licenses to be purchased and held by node operators.
- **Revenue Source** :
Projects can use Verifier Nodes as a revenue source. Successful projects have
raised significant funds through Node Licenses sales, providing a steady
revenue stream over an extended period.
- **Decentralization** :
Verifier Nodes enable projects to decentralize their rollups by allowing users
to verify blocks and secure the network in exchange for rewards, enhancing
network security and robustness.
- **Security and Reliability** :
All smart contracts and the node client provided by Gelato undergo rigorous audits, ensuring maximum security from the start.
## Gelato Verifier Node Package Includes
- **Smart Contracts** :
Essential contracts for running the Node Sale, including the NodeKey NFT,
Referee.sol, and NodeRewards.sol. These contracts serve as the baseline and can
be customized by projects to fit their specific requirements.
- **Node Client** :
A user-friendly client for community members to easily set up and run Verifier
Nodes. The Node Client allows operators to verify batches on everyday hardware
without needing to run a full node, lowering barriers to entry and encouraging
broader participation.
- **Hosted Node Integrations** :
Partnerships with node hosting providers to offer a simplified node running experience for less technically savvy users.
---
## Deploy your own rollup
**Path:** /rollup-as-a-service/how-to-guides/deploy-your-own-rollup
This deployment path is for users who want to deploy their own rollup chain using our 1-click deployment feature. By the end of this guide, you'll have a testnet chain up and running in 30 minutes.
# Steps for Deployment
Head over to the official Gelato [RaaS
dashboard](https://app.gelato.cloud) & click on "Deploy 1-click
Rollup"
Provide a unique name for your rollup. Additionally, you can add a custom
preferred chain ID for your rollup.
Choose the rollup stack, settlement layer, and data availability layer for
your rollup.
Configure the web3 services for your rollup.
Choose the environment you want to deploy your rollup to.
Review the chain settings and click next and proceed to pay for your rollup.
## Dashboard Preview
After deploying, your dashboard should look like the image below. Here, you can access the block explorer, bridge assets, rollup configuration, and more. Users can also check the logs, status, and analytics.
---
## Run OP Node
**Path:** /rollup-as-a-service/how-to-guides/run-an-op-node
This guide will walk you through the process of building a node from source, focusing on the op-node and op-geth implementations. These steps are essential if you want to run a node on a specific architecture or inspect the source code of the node you're running.
## What you're going to Build
### Rollup Node (op-node)
- Responsible for deriving L2 block payloads from L1 data and passing those payloads to the Execution Client
- Analogous to a consensus client in Ethereum
### Execution Client (op-geth)
- Executes the block payloads it receives from the Rollup Node
- Exposes the standard JSON-RPC API used by Ethereum developers
## Software Dependencies
{(() => {
const softwareDependencies = [
{ software: "git", version: "^2", command: "`git --version`" },
{ software: "go", version: "^1.22.6", command: "`go version`" },
{ software: "node", version: "^20", command: "`node --version`" },
{ software: "just", version: "^1.34", command: "`just --version`" },
{ software: "foundry", version: "^0.2.0", command: "`forge --version`" },
{ software: "make", version: "^4", command: "`make --version`" }
];
return (
## Build the Rollup Node
- Clone the Optimism Monorepo
```bash
git clone https://github.com/ethereum-optimism/optimism.git
cd optimism
```
- Check Out the Required Release Tag
```bash
git checkout ddc37daa49558c2fb5c1a92e694eeb7de5942e00
```
- Build op-node
```bash
make build
```
## Build the Execution Client
- Clone op-geth
```bash
git clone https://github.com/ethereum-optimism/op-geth.git
cd op-geth
```
- Check Out the Required Release Tag
```bash
git checkout 7c2819836018bfe0ca07c4e4955754834ffad4e0
```
- Build op-geth
```bash
make geth
```
## Running op-geth
- Create a Data Directory
```bash
cd /path/to/op-rollup-node/op-geth
mkdir data-geth
```
- Create a .env File
Create a `.env` file in the op-geth directory with the following content:
```bash
GENESIS_FILE_PATH=""
SNAPSHOT_FILE_PATH=""
GETH_DATADIR=""
GETH_AUTHRPC_ADDR="0.0.0.0"
GETH_AUTHRPC_PORT=""
GETH_AUTHRPC_JWTSECRET=""
GETH_AUTHRPC_VHOSTS="*"
GETH_METRICS="true"
GETH_METRICS_EXPENSIVE="false"
GETH_METRICS_ADDR="0.0.0.0"
GETH_METRICS_PORT=""
GETH_GCMODE="archive"
GETH_SYNCMODE="full"
GETH_MAXPEERS="0"
GETH_NODISCOVER="true"
GETH_HTTP="true"
GETH_HTTP_ADDR="0.0.0.0"
GETH_HTTP_PORT=""
GETH_HTTP_VHOSTS="*"
GETH_HTTP_CORSDOMAIN="*"
GETH_HTTP_API="web3,debug,eth,txpool,net,engine"
GETH_WS="true"
GETH_WS_ADDR="0.0.0.0"
GETH_WS_PORT=""
GETH_WS_ORIGINS="*"
GETH_WS_API="debug,eth,txpool,net,engine"
GETH_RPC_ALLOW_UNPROTECTED_TXS="false"
```
### Initialize op-geth
Feel free to customize the base configurations provided in the Optimism
documentation to suit your specific requirements. While we will use the
recommended configurations for this guide, you can explore and add additional
flags as needed. Detailed information about execution layer configurations can
be found
[here](https://docs.optimism.io/builders/node-operators/configuration/base-config#working-base-configuration).
Create `init-geth.sh`:
```bash
cd /path/to/op-rollup-node/op-geth
nano init-geth.sh
```
```bash
#!/bin/bash
# Set environment variables
source .env
# Create data directory if it doesn't exist
mkdir -p $DATADIR_PATH
# Initialize geth with the genesis file
echo "Initializing geth with genesis file..."
./build/bin/geth --datadir=$DATADIR_PATH init $GENESIS_PATH
# Generate JWT secret if it doesn't exist
if [ ! -f "$JWT_SECRET_PATH" ]; then
echo "Generating JWT secret..."
openssl rand -hex 32 > $JWT_SECRET_PATH
fi
```
### Run the geth Node
```bash
cd /path/to/op-rollup-node/op-geth
# Load environment variables from the .env file in the root directory
source .env
# Initialize geth
./init-geth.sh
# Run the geth command with the environment variables
./build/bin/geth
```
### Testing the Running Geth Instance
After starting your geth instance, you can test if it's running and confirm the chain ID:
```bash
curl -X POST http://127.0.0.1:8545 \
-H "Content-Type: application/json" \
--data '{"method":"eth_chainId","params":[],"id":1,"jsonrpc":"2.0"}'
```
You should see a response similar to this:
```json
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x2a88" // This is your chain id in hex, in this case its 10888
}
```
## Running op-node
We will utilize the base configurations provided in the Optimism documentation
for the consensus layer. However, you can adjust and expand these
configurations to fit your specific requirements. For a comprehensive
understanding of all available configurations, refer to the detailed
documentation on consensus layer configurations
[here](https://docs.optimism.io/builders/node-operators/configuration/base-config#working-base-configuration-1).
### Create a .env File
Create a `.env` file in the optimism directory with the following content:
```bash
OP_NODE_L1_ETH_RPC=""
OP_NODE_L1_RPC_KIND="standard"
OP_NODE_L2_ENGINE_AUTH=""
OP_NODE_ROLLUP_LOAD_PROTOCOL_VERSIONS="true"
OP_NODE_ROLLUP_HALT="major"
OP_NODE_ROLLUP_CONFIG=""
OP_NODE_SEQUENCER_ENABLED="false"
OP_NODE_SEQUENCER_L1_CONFS="5"
OP_NODE_VERIFIER_L1_CONFS="4"
OP_NODE_LOG_FORMAT="json"
OP_NODE_LOG_LEVEL="info"
OP_NODE_P2P_DISABLE="false"
OP_NODE_P2P_LISTEN_IP="0.0.0.0"
OP_NODE_P2P_LISTEN_TCP_PORT=""
OP_NODE_P2P_LISTEN_UDP_PORT=""
OP_NODE_P2P_PEER_SCORING="none"
OP_NODE_P2P_PEER_BANNING="false"
OP_NODE_P2P_PEER_BANNING_DURATION="0h1m0s"
OP_NODE_P2P_BOOTNODES=""
OP_NODE_P2P_ADVERTISE_TCP=""
OP_NODE_P2P_ADVERTISE_UDP=""
OP_NODE_P2P_ADVERTISE_IP=""
OP_NODE_P2P_SYNC_REQ_RESP="true"
OP_NODE_P2P_STATIC=""
OP_NODE_P2P_PRIV_RAW=""
OP_NODE_RPC_ADDR="0.0.0.0"
OP_NODE_RPC_PORT=""
OP_NODE_RPC_ENABLE_ADMIN="true"
OP_NODE_SNAPSHOT_LOG=""
OP_NODE_METRICS_ENABLED="true"
OP_NODE_METRICS_ADDR="0.0.0.0"
OP_NODE_METRICS_PORT=""
OP_NODE_PPROF_ENABLED="true"
OP_NODE_L1_BEACON=""
OP_NODE_L2_ENGINE_RPC=""
```
Ensure that op-node P2P ports (`` and ``) are accessible externally via ``. This allows the node to communicate with other peers on the network.
Add the sequencer node's multiaddr to `` in your configuration. This helps establish a direct connection with the sequencer, ensuring smooth operation and synchronization.
### Run the op-node
```bash
cd /path/to/op-rollup-node/optimism
# Load environment variables from the .env file in the root directory
source .env
# Run the op-node command with the environment variables
./op-node/bin/op-node
```
---
## Run a Full Orbit Node
**Path:** /rollup-as-a-service/how-to-guides/run-an-orbit-node
This guide provides step-by-step instructions for running a Arbitrum Orbit node on your local machine. You can also use this as a basis for running nodes of arbitrary orbit rollups on Gelato.
## Prerequisites
Before you begin, ensure you have the latest Docker image as mentioned in the Arbitrum Docs:
Arbitrum
Get the `chain-info.json` from the dashboard by heading over to [https://app.gelato.cloud/chains](https://app.gelato.cloud/chains) and then downloading the "Genesis File" from the "Details" tab.
## Minimum Hardware Requirements
{(() => {
const hardwareRequirements = [
{ component: "CPU", requirement: "2-4 core CPU (For AWS: t3 xLarge)" },
{ component: "RAM", requirement: "8-16 GB" },
{ component: "Disk", requirement: "Depends on traffic volume" }
];
return (
After creating the Wallet Project, navigate to its dashboard to find your Environment ID.
Click on View Config, then either copy the Dynamic's Environment ID or the entire configuration code, depending on your needs.
Keep your Environment ID secure as it's essential for configuring wallet providers in your application.
By default, Ethereum Mainnet is selected as the network. However, you can specify multiple networks in the Network section based on your requirements.
You can check the list of supported networks [here](/smart-wallet-sdk/additional-resources/supported-networks).
## Next Steps
Now that you've created your Environment ID, you can proceed to integrate it into your code to configure wallet providers in your application. Learn how to use Dynamic as a wallet provider [here](/smart-wallet-sdk/embedded-wallets/use-dynamic-signers).
---
## Adding Custom Networks With Dynamic
**Path:** /smart-wallet-sdk/embedded-wallets/adding-custom-networks-with-dynamic
If your desired network is not listed in the network list on the [Gelato Dashboard](https://app.gelato.cloud/wallets) or [Dynamic Dashboard](https://app.dynamic.xyz/dashboard/chains-and-networks#evm), but is included in the [supported networks](/smart-wallet-sdk/additional-resources/supported-networks) section, you can manually add it when using the Gelato Smart Wallet React SDK with Dynamic as the wallet provider.
Follow the steps below to configure custom networks with Dynamic.
```typescript
const customNetwork = {
chainId: 1,
chainName: "Custom Network",
name: "Custom Network",
iconUrls: ["https://customnetwork.com/icon.png"],
nativeCurrency: {
name: "Custom Network",
symbol: "CNT",
decimals: 18,
iconUrl: "https://customnetwork.com/icon.png",
},
networkId: 1,
rpcUrls: ["https://rpc.customnetwork.com"],
blockExplorerUrls: ["https://explorer.customnetwork.com"],
vanityName: "Custom Network",
};
```
```typescript
{children}
```
---
Now that you’ve successfully added custom networks with Dynamic, you can proceed to integrate various transaction methods using the Gelato Smart Wallet React SDK [here](/smart-wallet-sdk/embedded-wallets/use-dynamic-signers)
---
## Gelato Smart Account
**Path:** /smart-wallet-sdk/smart-accounts/gelato
Gelato Smart Accounts are fully optimized and compatible with EIP-7702.
See how we rank `#1` in gas efficiency and latency performance compared to other leading smart accounts and bundlers in our latest benchmark report: [Read the full blog](https://www.gelato.cloud/blog/performance-benchmarks-across-top-5-evm-paymaster-and-bundler-providers)
### Features
- Sponsored transactions.
- ERC20 Gas Payments.
- Low latency.
- Low gas fees.
- High security.
## Installation
```bash npm
npm install @gelatonetwork/smartwallet viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet viem
```
## Integration Steps
```ts
createGelatoSmartWalletClient,
sponsored,
} from "@gelatonetwork/smartwallet";
```
```ts main.ts
const account = await gelato({
owner,
client,
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
const smartWalletClient = await createGelatoSmartWalletClient(walletClient, {
apiKey: sponsorApiKey,
});
```
```ts
const response = await smartWalletClient.execute({
payment: sponsored(sponsorApiKey),
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
console.log(`userOpHash: ${response.id}`);
const txHash = await response.wait();
console.log(`Transaction successful: ${txHash}`);
```
---
Next, you can also explore other smart accounts compatible with the Gelato Bundler and Paymaster, enabling seamless integration of Gelato’s features without requiring user migration.
---
## Zerodev (Kernel)
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/kernel
## Installation
```bash npm
npm install @gelatonetwork/smartwallet viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet viem
```
## Integration Steps
```ts
createGelatoSmartWalletClient,
sponsored,
} from "@gelatonetwork/smartwallet";
```
```ts main.ts
const account = await kernel({
owner,
client,
eip7702: false, // Set to true if you want to use EIP-7702
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
const smartWalletClient = await createGelatoSmartWalletClient(walletClient, {
apiKey: sponsorApiKey,
});
```
```ts
const response = await smartWalletClient.execute({
payment: sponsored(sponsorApiKey),
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
console.log(`userOpHash: ${response.id}`);
const txHash = await response.wait();
console.log(`Transaction successful: ${txHash}`);
```
---
## Safe Smart Account
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/safe
## Installation
```bash npm
npm install @gelatonetwork/smartwallet viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet viem
```
## Integration Steps
```ts
createGelatoSmartWalletClient,
sponsored,
} from "@gelatonetwork/smartwallet";
```
```ts main.ts
const account = await safe({
client,
owners: [owner],
version: "1.4.1",
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
const smartWalletClient = await createGelatoSmartWalletClient(walletClient, {
apiKey: sponsorApiKey,
});
```
```ts
const response = await smartWalletClient.execute({
payment: sponsored(sponsorApiKey),
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
console.log(`userOpHash: ${response.id}`);
const txHash = await response.wait();
console.log(`Transaction successful: ${txHash}`);
```
---
## Alchemy (Light Account)
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/alchemy
To implement Alchemy's [Light Account](https://github.com/alchemyplatform/light-account), you can use the [toLightSmartAccount](https://docs.pimlico.io/references/permissionless/reference/accounts/toLightSmartAccount) module from `permissionless.js`.
For using Gelato Bundler with Alchemy's Light Account, you can extend the `createSmartAccountClient` module from `permissionless.js` with the `gelatoBundlerActions` module from `@gelatonetwork/smartwallet`.
## Installation
```bash npm
npm install @gelatonetwork/smartwallet permissionless viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet permissionless viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet permissionless viem
```
## Integration Steps
```ts
```
```ts main.ts
const account = await toLightSmartAccount({
client,
owner,
entryPoint: {
address: entryPoint07Address,
version: "0.7",
},
version: "2.0.0",
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const smartClient = createSmartAccountClient({
account,
chain: baseSepolia,
// Important: Chain transport (chain rpc) must be passed here instead of bundler transport
bundlerTransport: http(),
}).extend(
gelatoBundlerActions({
payment: sponsored(sponsorApiKey),
// payment: erc20(paymentToken),
// payment: native(),
encoding: WalletEncoding.LightAccount,
})
);
```
```ts
const userOpHash = await smartClient.sendUserOperation({
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
const receipt = await smartClient.waitForUserOperationReceipt({
hash: userOpHash,
});
console.log(`Transaction successful: ${receipt.receipt.transactionHash}`);
```
---
## Biconomy (Nexus Account)
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/biconomy
To implement Biconomy's [Nexus Account](https://github.com/bcnmy/nexus), you can use the [toNexusSmartAccount](https://docs.pimlico.io/references/permissionless/reference/accounts/toNexusSmartAccount) module from `permissionless.js`.
For using Gelato Bundler with Biconomy's Nexus Account, you can extend the `createSmartAccountClient` module from `permissionless.js` with the `gelatoBundlerActions` module from `@gelatonetwork/smartwallet`.
## Installation
```bash npm
npm install @gelatonetwork/smartwallet permissionless viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet permissionless viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet permissionless viem
```
## Integration Steps
```ts
```
```ts main.ts
const account = await toNexusSmartAccount({
client,
owners: [owner],
version: "1.0.0",
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const smartClient = createSmartAccountClient({
account,
chain: baseSepolia,
// Important: Chain transport (chain rpc) must be passed here instead of bundler transport
bundlerTransport: http(),
}).extend(
gelatoBundlerActions({
payment: sponsored(sponsorApiKey),
// payment: erc20(paymentToken),
// payment: native(),
encoding: WalletEncoding.LightAccount,
})
);
```
```ts
const userOpHash = await smartClient.sendUserOperation({
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
const receipt = await smartClient.waitForUserOperationReceipt({
hash: userOpHash,
});
console.log(`Transaction successful: ${receipt.receipt.transactionHash}`);
```
---
## Coinbase Smart Wallet
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/coinbase
The `toCoinbaseSmartAccount` implementation references the [Coinbase Smart Wallet](https://github.com/coinbase/smart-wallet) contract.
For using Gelato Bundler with Coinbase's Smart Account, you can extend the `createBundlerClient` module from `viem` with the `gelatoBundlerActions` module from `@gelatonetwork/smartwallet`.
## Installation
```bash npm
npm install @gelatonetwork/smartwallet viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet viem
```
## Integration Steps
```ts
```
```ts main.ts
const account = await toCoinbaseSmartAccount({
client,
owners: [owner],
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const bundler = createBundlerClient({
account,
client,
// Important: Chain transport (chain rpc) must be passed here instead of bundler transport
transport: http(),
}).extend(
gelatoBundlerActions({
payment: sponsored(sponsorApiKey),
encoding: WalletEncoding.ERC7579,
})
);
```
```ts
const userOpHash = await bundler.sendUserOperation({
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
const receipt = await bundler.waitForUserOperationReceipt({ hash: userOpHash });
console.log(`Transaction successful: ${receipt.receipt.transactionHash}`);
```
---
## OKX Smart Account
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/okx
## Installation
```bash npm
npm install @gelatonetwork/smartwallet viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet viem
```
## Integration Steps
```ts
createGelatoSmartWalletClient,
sponsored,
} from "@gelatonetwork/smartwallet";
```
```ts main.ts
const account = await okx({
owner,
client,
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
const smartWalletClient = await createGelatoSmartWalletClient(walletClient, {
apiKey: sponsorApiKey,
});
```
```ts
const response = await smartWalletClient.execute({
payment: sponsored(sponsorApiKey),
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
console.log(`userOpHash: ${response.id}`);
const txHash = await response.wait();
console.log(`Transaction successful: ${txHash}`);
```
---
## Thirdweb Smart Account
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/thirdweb
To implement Thirdweb's [Smart Account](https://portal.thirdweb.com/), you can use the [toThirdwebSmartAccount](https://github.com/pimlicolabs/permissionless.js/blob/main/packages/permissionless/accounts/thirdweb/toThirdwebSmartAccount.ts) module from `permissionless.js`.
For using Gelato Bundler with Thirdweb's Smart Account, you can extend the `createSmartAccountClient` module from `permissionless.js` with the `gelatoBundlerActions` module from `@gelatonetwork/smartwallet`.
## Installation
```bash npm
npm install @gelatonetwork/smartwallet permissionless viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet permissionless viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet permissionless viem
```
## Integration Steps
```ts
```
```ts main.ts
const account = await toThirdwebSmartAccount({
client,
owner,
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const smartClient = createSmartAccountClient({
account,
chain: baseSepolia,
// Important: Chain transport (chain rpc) must be passed here instead of bundler transport
bundlerTransport: http(),
}).extend(
gelatoBundlerActions({
payment: sponsored(sponsorApiKey),
// payment: erc20(paymentToken),
// payment: native(),
encoding: WalletEncoding.LightAccount,
})
);
```
```ts
const userOpHash = await smartClient.sendUserOperation({
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
const receipt = await smartClient.waitForUserOperationReceipt({
hash: userOpHash,
});
console.log(`Transaction successful: ${receipt.receipt.transactionHash}`);
```
---
## Trust Wallet
**Path:** /smart-wallet-sdk/smart-accounts/other-smart-accounts/trust
## Installation
```bash npm
npm install @gelatonetwork/smartwallet viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet viem
```
```bash pnpm
pnpm add @gelatonetwork/smartwallet viem
```
## Integration Steps
```ts
createGelatoSmartWalletClient,
sponsored,
} from "@gelatonetwork/smartwallet";
```
```ts main.ts
const account = await trustWallet({
owner,
client,
});
```
```ts config.ts
export const owner = privateKeyToAccount(process.env.PRIVATE_KEY);
export const client = createPublicClient({
chain: baseSepolia,
transport: http(),
});
```
```ts
const walletClient = createWalletClient({
account,
chain: baseSepolia,
transport: http(),
});
const smartWalletClient = await createGelatoSmartWalletClient(walletClient, {
apiKey: sponsorApiKey,
});
```
```ts
const response = await smartWalletClient.execute({
payment: sponsored(sponsorApiKey),
calls: [
{
to: zeroAddress,
data: "0x",
value: 0n,
},
],
});
console.log(`userOpHash: ${response.id}`);
const txHash = await response.wait();
console.log(`Transaction successful: ${txHash}`);
```
---
## Privy
**Path:** /smart-wallet-sdk/integration-guides/privy
## Installation
```bash npm
npm install @gelatonetwork/smartwallet @privy-io/react-auth @privy-io/wagmi @tanstack/react-query viem
```
```bash yarn
yarn add @gelatonetwork/smartwallet @privy-io/react-auth @privy-io/wagmi @tanstack/react-query viem
```
```bash pnpm
pnpm install @gelatonetwork/smartwallet @privy-io/react-auth @privy-io/wagmi @tanstack/react-query viem
```
## Setup Instructions
1. Visit the [Privy Dashboard](https://dashboard.privy.io/)
2. Create a new app or select an existing one
3. Enable the `Embedded Wallets` feature
4. Copy your App ID from the dashboard
1. Visit the [Gelato App](https://app.gelato.cloud/)
2. Navigate to `Paymaster & Bundler > API Keys`
3. Create a new API Key and select your required networks
4. Copy the generated API Key
Create a `.env.local` file in your project root:
```bash
NEXT_PUBLIC_PRIVY_APP_ID=your_privy_app_id
NEXT_PUBLIC_GELATO_API_KEY=your_gelato_api_key
```
## Implementation
```typescript
createGelatoSmartWalletClient,
sponsored,
} from "@gelatonetwork/smartwallet";
PrivyClientConfig,
PrivyProvider,
useSignAuthorization,
useWallets,
} from "@privy-io/react-auth";
Chain,
Transport,
Account,
http,
zeroAddress,
custom,
createWalletClient,
Hex,
} from "viem";
prepareAuthorization,
PrepareAuthorizationParameters,
} from "viem/actions";
```
```typescript
const queryClient = new QueryClient();
const wagmiConfig = createConfig({
chains: [baseSepolia],
transports: {
[baseSepolia.id]: http(),
},
});
const privyConfig: PrivyClientConfig = {
embeddedWallets: {
createOnLogin: "users-without-wallets",
requireUserPasswordOnCreate: false,
},
loginMethods: ["email"],
appearance: {
showWalletLoginFirst: false,
},
};
export const App = () => {
return (
);
};
```
```typescript
const App = () => {
const { wallets } = useWallets();
const { signAuthorization } = useSignAuthorization();
const sendTransaction = async () => {
const primaryWallet = wallets[0];
const chain = baseSepolia;
const provider = await primaryWallet?.getEthereumProvider();
const client = createWalletClient({
account: primaryWallet.address as Hex,
chain,
transport: custom(provider),
});
(
client.account as SmartAccount & {
signAuthorization: typeof client.signAuthorization;
}
).signAuthorization = async (
parameters: PrepareAuthorizationParameters ) => {
const preparedAuthorization = await prepareAuthorization(
client,
parameters
);
const signedAuthorization = await signAuthorization({
chainId: preparedAuthorization.chainId,
contractAddress: preparedAuthorization.address,
nonce: preparedAuthorization.nonce,
});
return signedAuthorization;
};
const smartWalletClient = await createGelatoSmartWalletClient<
Transport,
Chain,
Account
>(client, {
apiKey: process.env.NEXT_PUBLIC_GELATO_API_KEY,
scw: { type: "gelato" }, // use gelato, kernel, safe, or custom
});
};
return (
Status: {status}
Loading: {loading ? "Yes" : "No"}
Is Connected: {isConnected ? "Yes" : "No"}
Error: {error ? error.message : "No error"}
Connected: {wallet?.address}
After creating the API Key, navigate to its dashboard to locate your API Key.
Gelato API Keys now supports API key rotation, allowing users to create and delete API keys. This helps prevent unauthorized usage in case an API key is exposed.
`Activate` your API key by allowing access to `all contracts` on a network, or restrict it to `specific contracts` or `specific functions` in policies section.
Here, you can configure different networks. For each network, you can choose to allow access to all target contracts or limit it to selected contracts or specific functions.
Before you can start sponsoring gas with Gas Tank, you need to setup your Gas Tank. Check out our [Guide](/paymaster-&-bundler/gastank/setting-up-gastank) for detailed instructions on setting up your Gas Tank.
For `Sponsorship` purposes, add funds to your Gas Tank account according to your target environment:
- **Mainnets**: Deposit USDC.
- **Testnets**: Deposit Sepolia ETH.
Since Gas Tank is deployed on Polygon, you can deposit USDC in one step, and deposits from other networks are supported via Circle CCTP. Learn [more](/paymaster-&-bundler/gastank/introduction).
---
## Sponsor Gas for your users
**Path:** /smart-wallet-sdk/how-to-guides/sponsor-gas
Sponsoring gas for users is one of the most effective ways to enhance the user experience in dApps. With the Gelato Gasless SDK, developers can easily set up sponsored transactions for their applications in just a few simple steps, enabling seamless onboarding and interaction without requiring users to hold native tokens.
## Getting Started
```typescript
import { createGelatoSmartWalletClient, sponsored } from "@gelatonetwork/smartwallet";
import { gelato, kernel, safe, metamask } from "@gelatonetwork/smartwallet/accounts";
import { createWalletClient, createPublicClient, http, type Hex } from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
```
You can set up a Smart Account as per your needs. In the case of ```Gelato```, the Gelato Smart Account address will be the same as your EOA, enabling EIP-7702 features.
This debug feature is currently available only for `wallet_preparedCalls` endpoint of Gasless SDK.
### Steps to Debug
1. Go to the **logs** section and locate your failed `wallet_preparedCalls` request.
2. On the right side of the log entry, click the **Debug** button.
3. A new option, **View Debug**, will appear. Click it.
4. This will open a **Tenderly simulation**, which you can use to analyze and debug the failed request.
## Using Status API
In any of the payment methods, when using `Gasless SDK`, if you call the `wallet_sendPreparedCalls` API endpoint, the returned `Id` can also be used to track the status of the request through Gelato’s infrastructure like this:
```bash
curl -X GET https://api.gelato.digital/tasks/status/{Id}
```
Additionally, you can debug the request using the `status` API on Tenderly like this:
You can make use of the debug endpoint by adding the following parameters:
- `tenderlyUsername`
- `tenderlyProjectName`
The request URL should look like this:
```bash
curl -X GET https://api.gelato.digital/tasks/status/{Id}/debug?tenderlyUsername={yourUserName}&tenderlyProjectName={yourProjectName}
```
After running the above command, you can use the link of tenderly simulation in the response to debug the UserOperation.
## Using WebSocket API
Additionally, you can also use the `WebSocket` API to subscribe to the status updates of the request like this:
You can interact with the websocket API directly by connecting to this endpoint:
```
wss://api.gelato.digital/tasks/ws/status
```
Once connected, you can subscribe to updates using Id of your submitted requests by sending messages like this:
```json
{
"action": "subscribe",
"taskId": "0x..." // paste your Id here
}
```
To unsubscribe from updates:
```json
{
"action": "unsubscribe",
"taskId": "0x..." // paste your Id here
}
```
---
## Applied Use Cases: Morpho Demo
**Path:** /smart-wallet-sdk/how-to-guides/applied-use-cases-morpho-demo
Integrate crypto-backed loans into your app using Gelato’s Gasless SDK and Morpho’s lending protocol. This guide walks you through setting up embedded wallet onboarding and implementing both the Supply/Earn and Borrow flows.
This guide assumes you're using React + TypeScript.
## Smart Wallet Onboarding Setup
Before users can supply or borrow assets, they must onboard into a Smart Wallet via a frictionless embedded flow. Powered by Gelato’s SDK and Dynamic, users can create smart accounts using just their email, social login, or passkeys — no browser extensions or seed phrases needed.
### Key Features of Embedded Wallets
- EIP-7702 & ERC-4337 compliant
- Supports gasless transactions (sponsored execution)
- Runs across 50+ EVM chains
- Session-based login — no need to reconnect every time
### Code Snippet
Embed the following setup within your app to configure the provider:
```typescript
{children}
```
This context enables smart wallet access throughout your app, setting up:
- Wallet creation and session
- Transaction preparation and sending
- Provider availability across your components
```typescript
const { gelato: { client }, logout} = useGelatoSmartWalletProviderContext();
```
## Part 1: Supply & Earn
Allow users to supply assets (e.g., USDC) and earn yield in Morpho’s vaults — fully onchain, without user signatures or gas fees.
### Flow Overview
- Approve vault to spend USDC
- Deposit USDC to Morpho vault
- Optionally, record stats to external tracking contract
### Code Snippet
```typescript
const calls = [
{
to: USDC_ADDRESS,
data: encodeFunctionData({
abi: tokenABI,
functionName: "approve",
args: [MORPHO_VAULT_ADDRESS, parseUnits(amount, 6)],
}),
},
{
to: MORPHO_VAULT_ADDRESS,
data: encodeFunctionData({
abi: morphoVaultABI,
functionName: "deposit",
args: [parseUnits(amount, 6), smartWallet.address],
}),
},
{
to: VAULT_STATS_ADDRESS,
data: encodeFunctionData({
abi: vaultStatsABI,
functionName: "deposit",
args: [parseUnits(amount, 6), userAssets, totalAssets],
}),
},
];
const response = await smartWalletClient.execute({
payment: sponsored(GELATO_API_KEY),
calls,
});
console.log("userOp Hash", response.id);
const txHash = await response.wait();
```
Users never sign a transaction or pay gas. All logic executes onchain using their smart account.
## Part 2: Borrow
Let users borrow stablecoins (like USDC) using crypto collateral (cbBTC) — trustless, non-custodial, and instant.
### Flow Overview
- Approve Morpho to move collateral
- Supply collateral to Morpho
- Borrow USDC
### Code Snippet
```typescript
const approveCall = {
to: COLLATERAL_TOKEN_ADDRESS,
data: encodeFunctionData({
abi: tokenABI,
functionName: "approve",
args: [MORPHO_MARKET_ADDRESS, collateralAmount],
}),
};
const supplyCollateralCall = {
to: MORPHO_MARKET_ADDRESS,
data: encodeFunctionData({
abi: morphoABI,
functionName: "supplyCollateral",
args: [marketParams, collateralAmount, smartWallet.address, "0x"],
}),
};
const borrowCall = {
to: MORPHO_MARKET_ADDRESS,
data: encodeFunctionData({
abi: morphoABI,
functionName: "borrow",
args: [
marketParams,
borrowAmount,
BigInt(0),
smartWallet.address,
smartWallet.address,
],
}),
};
const response = await smartWalletClient.execute({
payment: sponsored(GELATO_API_KEY),
calls: [approveCall, supplyCollateralCall, borrowCall]
});
console.log("userOp Hash", response.id);
const txHash = await response.wait();
```
## Summary
{(() => {
const featuresData = [
{
feature: "Smart wallet onboarding (social/email)",
available: "Yes"
},
{
feature: "Embedded supply to Morpho vaults",
available: "Yes"
},
{
feature: "Embedded borrow against crypto",
available: "Yes"
},
{
feature: "Gasless transactions via Gelato",
available: "Yes"
},
{
feature: "Fully onchain, no custody",
available: "Yes"
}
];
return (
### Code Snippet
Embed the following setup within your app to configure the provider:
```typescript
{children}
```
This context enables smart wallet access throughout your app, setting up:
- Wallet creation and session
- Transaction preparation and sending
- Provider availability across your components
```typescript
const { gelato: { client }, logout} = useGelatoSmartWalletProviderContext();
```
## Part 1: Supply & Earn
Allow users to supply assets (e.g., USDC) and earn yield in Morpho’s vaults — fully onchain, without user signatures or gas fees.
### Flow Overview
- Approve vault to spend USDC
- Deposit USDC to Morpho vault
- Optionally, record stats to external tracking contract
### Code Snippet
```typescript
const calls = [
{
to: USDC_ADDRESS,
data: encodeFunctionData({
abi: tokenABI,
functionName: "approve",
args: [MORPHO_VAULT_ADDRESS, parseUnits(amount, 6)],
}),
},
{
to: MORPHO_VAULT_ADDRESS,
data: encodeFunctionData({
abi: morphoVaultABI,
functionName: "deposit",
args: [parseUnits(amount, 6), smartWallet.address],
}),
},
{
to: VAULT_STATS_ADDRESS,
data: encodeFunctionData({
abi: vaultStatsABI,
functionName: "deposit",
args: [parseUnits(amount, 6), userAssets, totalAssets],
}),
},
];
const response = await smartWalletClient.execute({
payment: sponsored(GELATO_API_KEY),
calls,
});
console.log("userOp Hash", response.id);
const txHash = await response.wait();
```
Users never sign a transaction or pay gas. All logic executes onchain using their smart account.
## Part 2: Borrow
Let users borrow stablecoins (like USDC) using crypto collateral (cbBTC) — trustless, non-custodial, and instant.
### Flow Overview
- Approve Morpho to move collateral
- Supply collateral to Morpho
- Borrow USDC
### Code Snippet
```typescript
const approveCall = {
to: COLLATERAL_TOKEN_ADDRESS,
data: encodeFunctionData({
abi: tokenABI,
functionName: "approve",
args: [MORPHO_MARKET_ADDRESS, collateralAmount],
}),
};
const supplyCollateralCall = {
to: MORPHO_MARKET_ADDRESS,
data: encodeFunctionData({
abi: morphoABI,
functionName: "supplyCollateral",
args: [marketParams, collateralAmount, smartWallet.address, "0x"],
}),
};
const borrowCall = {
to: MORPHO_MARKET_ADDRESS,
data: encodeFunctionData({
abi: morphoABI,
functionName: "borrow",
args: [
marketParams,
borrowAmount,
BigInt(0),
smartWallet.address,
smartWallet.address,
],
}),
};
const response = await smartWalletClient.execute({
payment: sponsored(GELATO_API_KEY),
calls: [approveCall, supplyCollateralCall, borrowCall]
});
console.log("userOp Hash", response.id);
const txHash = await response.wait();
```
## Summary
{(() => {
const featuresData = [
{
feature: "Smart wallet onboarding (social/email)",
available: "Yes"
},
{
feature: "Embedded supply to Morpho vaults",
available: "Yes"
},
{
feature: "Embedded borrow against crypto",
available: "Yes"
},
{
feature: "Gasless transactions via Gelato",
available: "Yes"
},
{
feature: "Fully onchain, no custody",
available: "Yes"
}
];
return (
2. A transaction prompt will appear asking you to confirm the upgrade. This process will create a new task and simultaneously pause the old one.
3. After confirming the transaction, you will notice that the task label updates to "VRF v1.2," indicating that the upgrade is complete.
## 1. Gather Necessary Addresses
Before you begin, make sure you have the address of your requester contract at hand. This will be essential for the deployment process.
Head over to the [VRF Quick Start guide](/vrf/how-to-guides/deploy-your-contract-inheriting-gelato-vrf) to learn how to prepare your contract for requesting randomness.
## 2. Access the VRF Deployment Portal
Navigate to the [Gelato app](https://app.gelato.cloud).
## 3. Choose your VRF Type
When prompted to select the VRF type, opt for "Gelato VRF". If you previously deployed a Chainlink consumer contract and wish to transition, refer to the [Migrate from Chainlink VRF](/vrf/how-to-guides/migrate-from-chainlink-vrf) section.
Or, if you already have a VRF task and want to cover all the missing events, you can set up a fallback task to ensure completeness, refer to the [Create a Fallback VRF](/vrf/how-to-guides/create-a-fallback-vrf) section.
## 4. Select Deployment Network
Ensure you choose the same network where both your VRF requester and receiver contracts are deployed.

## 5. Specify the Request Contract
You'll be asked to provide the address of the Request Contract to which the Gelato nodes should respond. Enter the address you gathered in step 1.
## 6. Launch your VRF Instance
Once all details are correctly entered, go ahead and launch your Gelato VRF instance.
---
## Create a Fallback VRF
**Path:** /vrf/how-to-guides/create-a-fallback-vrf
Fallback VRF is a mechanism designed to fulfill missing or unfulfilled VRF requests. By creating a fallback task alongside your main VRF task, you can ensure that all requests are fulfilled in any scenario.
Setting up a fallback task for VRF is a straightforward process. Here's a step-by-step guide to help you create one.
## 1. Deploy VRF Compatible Contract
Before deploying Fallback VRF, ensure that your contract is compatible with VRF. You can quickly set up one by heading over to the [quick start guide](/vrf/how-to-guides/deploy-your-contract-inheriting-gelato-vrf).
## 2. Select Deployment Network
Navigate to the [Gelato app](https://app.gelato.cloud) and ensure you choose the same network where both your Main VRF task and VRF compatible contracts are deployed.

## 3. Enter Fallback Parameters
Fallback VRF has three main parameters:
- **From Block**: The block number from which the fallback will start analyzing missing events. It is recommended to use the block number of your VRF-compatible contract deployment.
- **Time**: The interval at which the fallback task checks for missing events.
- **Contract Address**: The address of your deployed VRF-compatible contract.
## 4. Launch your Fallback VRF Instance
Once all details are correctly entered, go ahead and launch your Fallback Gelato VRF instance.
---
## Migrate from Chainlink VRF
**Path:** /vrf/how-to-guides/migrate-from-chainlink-vrf
Already using Chainlink VRF? Here's how you can quickly and easily migrate to GelatoVRF.
## Understand the Implications
While this migration option is available, be aware that it can lead to higher gas costs and added development intricacies. We advise this route only if:
- You've already deployed a Chainlink VRF Consumer.
- Your Chainlink VRF Consumer has the capability to update its Coordinator address.
Otherwise, for new integrations, we recommend directly implementing the Gelato VRF.
## Initiate Migration
If you're set on migrating an existing Chainlink VRF Consumer:
1. Begin by creating your VRF task. In the "VRF Type" selection window, opt for "VRF Compatibility".
2. Choose the blockchain network for deployment.

3. You will then be asked to deploy your Adapter contract:

Once deployed, the app will show you the address to which the adapter contract was deployed. You now need to replace the old Coordinator address in your contract by this address.
## Updating the VRF Coordinator Address
Here's a brief example of how you might implement such a function to update the vrfCoordinator in your Consumer Contract:
```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VRFConsumer is VRFConsumerBase {
...
function setVRFCoordinator(address _vrfCoordinator) external onlyOwner {
vrfCoordinator = _vrfCoordinator;
}
...
}
```
To facilitate the update of the Chainlink VRF Coordinator address in your smart contracts, you can reference the function from this [deployed contract on Polygon](https://polygonscan.com/address/0x81e4e7977310308271082fc5285039e613d47d51#code).
---
## Overview
**Path:** /vrf/security-considerations
After reading this page you will:
- Understand the importance of security measures when implementing Gelato VRF in your dApp.
- Recognize the need for state locking to prevent front-running and maintain the integrity of the randomization process.
- Learn the benefits of using RNGLib to ensure the randomness you receive is unique and secure, particularly when handling multiple requests simultaneously.
### Important Note
Contrary to some other VRF providers, Gelato VRF is verifiable off-chain but not on-chain. This is due to the nature of the BLS signatures used by Drand network, which are not yet supported at EVM level. With the upcoming [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) release, adding BLS precompile for BLS12-381 curve, we aim to add support for on-chain randomness verification in a near future on all networks that will include this precompile.
## Security Precautions
When integrating with GelatoVRF, it's essential to take several precautions to ensure the safety and reliability of your application. Here are key considerations:
### 1. State Locking and Front-Running Prevention
After you initiate a request for randomness and before the random number gets delivered, it's essential to lock the relevant application state in your consumer contract. This step minimizes the risk of front-running attacks.
In essence, front-running involves gaining an unfair advantage by making transactions based on foreknowledge of pending transactions. By locking the state, you add an additional layer of security against such tactics.
### 2. Usage of RNGLib
Instead of using the received randomness directly, consider integrating it with our RNGLib. This approach:
- Enables dynamic fetching of random values as required.
- Offers protection against certain bet arbitrage attacks, especially when multiple applications operate simultaneously.
By inheriting from [GelatoVRFConsumerBase.sol](https://github.com/gelatodigital/vrf-contracts/blob/main/contracts/GelatoVRFConsumerBase.sol), your contract will automatically benefit from enhanced security. All fulfilled randomness requests will be dynamically derived from the drand randomness using a pseudo-random number generator (RNG). This is crucial to ensure the uniqueness of values, particularly for concurrent requests, and adds another layer of protection against potential vulnerabilities.
---
## Supported Networks
**Path:** /vrf/additional-resources/supported-networks
Networks you can request randomness on!
The following networks are supported:
{(() => {
const networksData = [
{ network: "Abstract", environment: "Mainnet" },
{ network: "Aleph Zero", environment: "Mainnet, Testnet" },
{ network: "Arbitrum", environment: "Mainnet, Sepolia" },
{ network: "Arbitrum Blueberry", environment: "Testnet" },
{ network: "Avalanche", environment: "Mainnet" },
{ network: "Base", environment: "Mainnet, Sepolia" },
{ network: "Berachain", environment: "Mainnet, Bepolia" },
{ network: "Binance Smart Chain", environment: "Mainnet" },
{ network: "Blast", environment: "Mainnet, Testnet" },
{ network: "Camp Network", environment: "Testnet" },
{ network: "Corn", environment: "Maizenet" },
{ network: "Ethereum", environment: "Mainnet, Sepolia" },
{ network: "Everclear (prev Connext)", environment: "Mainnet, Testnet" },
{ network: "Fantom", environment: "Mainnet" },
{ network: "Gnosis", environment: "Mainnet, Testnet" },
{ network: "HyperEVM", environment: "Mainnet" },
{ network: "Incentiv", environment: "Mainnet, Testnet" },
{ network: "Ink", environment: "Mainnet, Testnet" },
{ network: "Linea", environment: "Mainnet" },
{ network: "Lisk", environment: "Mainnet, Sepolia" },
{ network: "MegaETH", environment: "Timothy Testnet" },
{ network: "Metis", environment: "Mainnet" },
{ network: "Mode", environment: "Mainnet" },
{ network: "Monad", environment: "Testnet" },
{ network: "Optimism", environment: "Mainnet, Sepolia" },
{ network: "Playblock", environment: "Mainnet" },
{ network: "Polygon", environment: "Mainnet, Amoy" },
{ network: "Polygonzk", environment: "Mainnet" },
{ network: "Reya", environment: "Mainnet, Cronos" },
{ network: "Shape", environment: "Mainnet" },
{ network: "Singularity Finance", environment: "Testnet" },
{ network: "Sonic", environment: "Mainnet" },
{ network: "Story", environment: "Mainnet" },
{ network: "Tangible", environment: "re.al, unreal" },
{ network: "zkSync Era", environment: "Mainnet" }
];
return (
Copy in the `log.json` file the raw data of the event you want to test:
### event/log.json
```json
{
"blockNumber": 48758053,
"blockHash": "0x6794a56583329794f184d50862019ecf7b6d8ba6b3210f68ca4b91a8fa81817d",
"transactionIndex": 29,
"removed": false,
"address": "0xb74de3F91e04d0920ff26Ac28956272E8d67404D",
"data": "0x",
"topics": [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x000000000000000000000000eec2ba9b9f0202c63bba29ea9a4ce5c23f9865fd",
"0x0000000000000000000000000000000000000000000000000000000000001099"
],
"transactionHash": "0x2c500a55f5c24d587e73805975d91395634a971dca5939f43d34d774d0f7147b",
"logIndex": 343
}
```
The data in `log.json` will be injected in your event context in local runs via CLI:
```bash
npx w3f test event/index.ts --logs
```
```bash
Web3Function running logs:
> Transfer of NFT #4249 from 0x0000000000000000000000000000000000000000 to 0xeeC2ba9B9F0202c63bba29Ea9A4Ce5c23f9865FD detected
Web3Function Result:
✓ Return value: {"canExec":false,"message":"Event processed 0x2c500a55f5c24d587e73805975d9"}
---
## Callbacks
**Path:** /web3-functions/how-to-guides/write-typescript-functions/callbacks
Callbacks can be used to manage the outcome of your transaction submission. This advanced feature enables your functions to adapt based on the execution status, whether successful or not, thus providing a robust way to handle different scenarios that may occur during task execution. Let's explore the two types of callbacks available:
## Callback Function Example:
```typescript
Web3Function,
Web3FunctionContext,
Web3FunctionFailContext,
Web3FunctionSuccessContext,
} from "@gelatonetwork/web3-functions-sdk";
const ORACLE_ABI = [
"function lastUpdated() external view returns(uint256)",
"function updatePrice(uint256)",
];
// Callback for successful execution
Web3Function.onSuccess(async (context: Web3FunctionSuccessContext) => {
const { transactionHash } = context;
//onSuccess Logic goes here
});
// Callback for handling failures
Web3Function.onFail(async (context: Web3FunctionFailContext) => {
const { reason, transactionHash, callData } = context;
//onFail Logic goes here
});
// Main function logic
Web3Function.onRun(async (context: Web3FunctionContext) => {
const { userArgs, multiChainProvider } = context;
// Core logic goes here to prepare callData
return {
canExec: false,
message: "Nothing to execute yet"
};
});
```
## Types of Callbacks
### onSuccess Callback
This callback gets invoked after a successful on-chain execution. It's especially useful for tracking successful transactions or for further processing after a task completes.
```typescript
Web3Function.onSuccess(async (context: Web3FunctionSuccessContext) => {
const { transactionHash } = context;
console.log("onSuccess: txHash: ", transactionHash);
});
```
The `Web3FunctionSuccessContext` offers access to the `transactionHash`, allowing you to reference and track the successful transaction within your application.
### onFail Callback
Triggered when an on-chain execution encounters issues such as:
- **InsufficientFunds**: When the account executing the function does not have enough balance to cover the transaction fees.
- **SimulationFailed**: If the execution simulation (a pre-run of the transaction) fails, indicating that the actual transaction might also fail.
- **ExecutionReverted**: When the actual transaction is executed on the blockchain but is reverted due to a condition in the smart contract code or because it runs out of gas.
This callback is crucial for handling errors and implementing fallback logic.
```typescript
Web3Function.onFail(async (context: Web3FunctionFailContext) => {
const { reason } = context;
if (reason === "ExecutionReverted") {
console.log(`onFail: ${reason} txHash: ${context.transactionHash}`);
} else if (reason === "SimulationFailed") {
console.log(
`onFail: ${reason} callData: ${JSON.stringify(context.callData)}`
);
} else {
console.log(`onFail: ${reason}`);
}
});
```
In the context of the `onFail` callback:
- `reason`: This is a string indicating why the failure occurred.
- `transactionHash`: Provided when the reason for failure is `ExecutionReverted`, this is the unique identifier of the reverted transaction.
- `callData`: Available when the reason is `SimulationFailed`, this is the data that was used during the function run, which can be useful for debugging the failure.
## Testing Your Callbacks
You can test your callbacks locally using specific flags during the test execution. This helps in ensuring that your callbacks function as intended before deployment.
### For onFail Callback:
```bash
yarn test src/web3-functions/callbacks/index.ts --logs --onFail
```
```bash
Web3Function building...
Web3Function Build result:
✓ Schema: web3-functions\oracle-callback\schema.json
✓ Built file: C:\Users\aniru\OneDrive\Desktop\Gelato_internship\w3f-example\web3-functions-hardhat-template\.tmp\index.js
✓ File size: 0.64mb
✓ Build time: 150.48ms
Web3Function user args validation:
✓ currency: ethereum
✓ oracle: 0x71B9B0F6C999CBbB0FeF9c92B80D54e4973214da
Web3Function running logs:
> userArgs: undefined
> onFail: SimulationFailed callData: [{"to":"0x0000000000000000000000000000000000000000","data":"0x00000000"}]
Web3Function onFail result:
✓ Success
Web3Function Runtime stats:
✓ Duration: 0.20s
✓ Memory: 0.00mb
✓ Storage: 0.04kb
✓ Network: 0 req [ DL: 0.00kb / UL: 0.00kb]
✓ Rpc calls: 0
Done in 1.33s.
```
### For onSuccess Callback:
```bash
npx test src/web3-functions/callbacks/index.ts --logs --onSuccess
```
```bash
Web3Function Build result:
✓ Schema: web3-functions\oracle-callback\schema.json
✓ Built file: C:\Users\aniru\OneDrive\Desktop\Gelato_internship\w3f-example\web3-functions-hardhat-template\.tmp\index.js
✓ File size: 0.64mb
✓ Build time: 143.00ms
Web3Function user args validation:
✓ currency: ethereum
✓ oracle: 0x71B9B0F6C999CBbB0FeF9c92B80D54e4973214da
Web3Function running logs:
> userArgs: undefined
> onSuccess: txHash: undefined
Web3Function onSuccess result:
✓ Success
Web3Function Runtime stats:
✓ Duration: 0.19s
✓ Memory: 0.00mb
✓ Storage: 0.04kb
✓ Network: 0 req [ DL: 0.00kb / UL: 0.00kb]
✓ Rpc calls: 0
Done in 1.47s.
```
---
## Private Typescript Functions
**Path:** /web3-functions/how-to-guides/write-typescript-functions/private-typescript-functions
When you deploy a Typescript Function the code is stored and pinned on IPFS making it accessible to everyone. If you would prefer to conceal your code, one approach is to store your code in a private Github Gist. Subsequently, this code can be retrieved and executed through a Web3 Function.
This approach introduces a dependency on Github's availability. We aim to directly support private Web3 Function deployments in the future.
## Private Typescript Function example
This Typescript Function fetches `onRun.js` (Github gist containing concealed code) with its gist id and executes it during runtime. Check out the example on [GitHub](https://github.com/gelatodigital/web3-functions-template/blob/master/web3-functions/private/README.md) here.
The code in `onRun.js` must be in JavaScript
```typescript private-w3f/index.ts
Web3Function,
Web3FunctionContext,
Web3FunctionResult,
} from "@gelatonetwork/web3-functions-sdk";
// import dependencies used in onRun.js
Web3Function.onRun(async (context: Web3FunctionContext) => {
const { secrets } = context;
const gistId = (await secrets.get("GIST_ID")) as string;
const octokit = new Octokit();
let onRunScript: string | undefined;
// fetch onRun.js from private github gist
try {
const gistDetails = await octokit.rest.gists.get({
gist_id: gistId,
});
const files = gistDetails.data.files;
if (!files) throw new Error(`No files in gist`);
for (const file of Object.values(files)) {
if (file?.filename === "onRun.js" && file.content) {
onRunScript = file.content;
break;
}
}
if (!onRunScript) throw new Error(`No onRun.js`);
} catch (err) {
return {
canExec: false,
message: `Error fetching gist: ${err.message}`,
};
}
// run onRun.js
try {
/**
* context are passed into onRun.js.
* onRun.js will have access to all userArgs, secrets & storage
*/
const onRunFunction = new Function("context", "ky", "ethers", onRunScript);
const onRunResult: Web3FunctionResult = await onRunFunction(
context,
ky,
ethers
);
if (onRunResult) {
return onRunResult;
} else {
return { canExec: false, message: `No result returned` };
}
} catch (err) {
console.log(err);
return {
canExec: false,
message: `Error running gist: ${err.message}`,
};
}
});
```
## Writing onRun.js
Check out an example of a GitHub gist with `onRun.js` [here](https://gist.github.com/brandonchuah/0c58ee8ce55bc7af5f42a2d75c27433c).
### 1. onRun.js file structure
`onRun.js` should return a promise.
```javascript onRun.js
return (async () => {
// ... your code here
})();
```
### 2. Using dependencies
Dependencies that are used in `onRun.js` should be imported into the Web3 Function `index.ts` file, not in `onRun.js`.
```typescript
// import dependencies used in onRun.js
```
### 3. Accessing Web3 Function Context
Web3 Function context which includes, secrets, userArgs, multiChainProvider can be accessed normally in `onRun.js`.
```javascript
return (async () => {
const {secrets, userArgs, multiChainProvider} = context
})();
```
### 4. Return Web3 Function result
Results returned in `onRun.js` will be bubbled up and returned in the private Web3 Function.
```javascript
return {
canExec: true,
callData: [
{
to: oracleAddress,
data: oracle.interface.encodeFunctionData("updatePrice", [price]),
},
],
}
```
## Creating private Typescript Function task
### Secrets (strict)
- `GIST_ID` (Github gist id to fetch `onRun.js` from)
Make sure to store your GitHub gist id as a secret.
### Arguments (not strict)
Since GitHub gists are editable, you can have a userArgs to be a JSON string so that arguments can be editable without re-deploying a web3 function with a different schema.
```json private-w3f/schema.json
{
"web3FunctionVersion": "2.0.0",
"runtime": "js-1.0",
"memory": 128,
"timeout": 30,
"userArgs": {
"args": "string"
}
}
```
Example args when creating your task:
```json
{
"args": "{\"currency\":\"ethereum\",\"oracle\":\"0x71B9B0F6C999CBbB0FeF9c92B80D54e4973214da\"}"
}
```
---
## Write Solidity Functions
**Path:** /web3-functions/how-to-guides/write-solidity-functions
## 1. Understand the Role of a Checker
A Checker acts as a bridge between conditions and smart contract executions. Its purpose? To check conditions and determine whether a task should be executed by Gelato. Every Checker returns two main things:
- `canExec` (Boolean): Indicates if Gelato should execute the task.
- `execData` (Bytes): Contains the data that executors will use during execution.
Solidity functions must adhere to the block gas limit for checker calls; exceeding it will cause the call to fail.
## 2. Solidity Function Example
Before we delve into complexities, let's understand the structure of a simple Checker:
```solidity
contract CounterChecker{
ICounter public immutable counter;
constructor(ICounter _counter) {
counter = _counter;
}
function checker()
external
view
returns (bool canExec, bytes memory execPayload)
{
uint256 lastExecuted = counter.lastExecuted();
canExec = (block.timestamp - lastExecuted) > 180;
execPayload = abi.encodeCall(ICounter.increaseCount, (1));
}
}
```
In the above, the checker checks the state of a counter and prompts Gelato to execute if 3 minutes (180 seconds) have elapsed since its last execution.
## 3. Making your Checker Reusable
Avoid hardcoding addresses. Instead, allow the passing of arguments to your checker. This lets you reuse the checker for multiple tasks:
```solidity
function checker(address _counter)
external
view
returns (bool canExec, bytes memory execPayload)
{
uint256 lastExecuted = ICounter(_counter).lastExecuted();
canExec = (block.timestamp - lastExecuted) > 180;
execPayload = abi.encodeCall(ICounter.increaseCount, (1));
}
```
## 4. Advanced: Checking Multiple Functions
Suppose you're automating tasks across different pools. Instead of creating multiple tasks, iterate through your list of pools within a single checker:
```solidity
function checker()
external
view
returns (bool canExec, bytes memory execPayload)
{
uint256 delay = harvester.delay();
for (uint256 i = 0; i < vaults.length(); i++) {
IVault vault = IVault(getVault(i));
canExec = block.timestamp >= vault.lastDistribution().add(delay);
execPayload = abi.encodeWithSelector(
IHarvester.harvestVault.selector,
address(vault)
);
if (canExec) return(true, execPayload);
}
return(false, bytes("No vaults to harvest"));
}
```
## 5. Incorporating Feedback with Logs
With the Gelato Web3 Functions UI, you can use custom return messages to pinpoint where your checker might be "stuck":
```solidity
function checker()
external
view
returns (bool canExec, bytes memory execPayload)
{
uint256 lastExecuted = counter.lastExecuted();
if(block.timestamp - lastExecuted < 180) return(false, bytes("Time not elapsed"));
execPayload = abi.encodeCall(ICounter.increaseCount, (1));
return(true, execPayload);
}
```
## 6. Limit the Gas Price of your execution
On networks such as Ethereum, gas will get expensive at certain times. If what you are automating is not time-sensitive and don't mind having your transaction mined at a later point, you can limit the gas price used in your execution in your checker.
```solidity
function checker()
external
view
returns (bool canExec, bytes memory execPayload)
{
// condition here
if(tx.gasprice > 80 gwei) return (false, bytes("Gas price too high"));
}
```
This way, Gelato will not execute your transaction if the gas price is higher than 80 GWEI.
---
## Test & Deploy Typescript Functions
**Path:** /web3-functions/how-to-guides/test-deploy-typescript-functions
## Testing Typescript Functions
To test your Typescript Function locally, run:
```bash
npx w3f test path/to/web3-functions/index.ts --logs
```
or
```bash
npx hardhat w3f-run W3FNAME --logs
```
Example:
```bash
npx w3f test oracle/index.ts --logs
```
### Optional flags:
- `--logs` Show internal Web3 Function logs
- `--debug` Show Runtime debug messages
- `--chain-id [NETWORK]` Set the default runtime network & provider.
Example:
```bash
npx w3f test path/to/web3-functions/index.ts --logs --chain-id=
1. **Selection of Function**
- Navigate to the What to trigger section.
- Within the Typescript Function subsection, find the IPFS CID input box.
2. **Function Details Input**
- Input the CID you secured after deploying your Typescript function.
- Upon entry, you should see a message like "Typescript Function code imported," signifying a successful connection.
3. **Network Configuration**
- Scroll to the Network dropdown menu.
- Choose the blockchain network where the Typescript function should work, e.g., "Sepolia."
4. **Task Configuration**
- If your Typescript function needs secret variables or API keys, securely enter them in the Task Secrets section. For every secret:
- **Key**: Define the name of the variable or key, e.g., "API_KEY".
- **Value**: Enter the associated secret value.
- Click Save after each input to guarantee its safe storage.
---
## Test & Deploy Solidity Functions
**Path:** /web3-functions/how-to-guides/test-deploy-solidity-functions
## Testing Solidity Functions
The purpose of this repo is to showcase unit tests examples of using Gelato Solidity Functions in a Hardhat environment.
A repository showcasing unit tests examples of using Gelato Solidity Functions in a Hardhat environment.
It's advised to conduct tests by impersonating Gelato's address within a Hardhat environment to ensure accurate simulation of Gelato Solidity Function executions.
## Deploying Solidity Functions
To deploy your Solidity functions, please proceed with deploying your contract to the network. Once deployed, ensure you verify your contract on Etherscan to enable automatic ABI fetching within our app.
## Creating Solidity Function Tasks
Before creating solidity function tasks, familiarize yourself with the available [Trigger Types](/web3-functions/introduction/trigger-types)!
1. **Selection of Function**
- Navigate to the What to trigger section.
- Choose the Solidity Function option
2. **Network Configuration**
- Locate the Network dropdown.
- Select your desired blockchain network where the contract is deployed, e.g., "Göerli."
3. **Function Details Input**
- Under the Solidity Function section, find the input labeled Contract Address.
- Enter the Ethereum address of your deployed Solidity contract. Ensure accuracy as this determines where your functions will interact.
- Once the contract address is entered, the ABI (Application Binary Interface) should automatically populate. If using a custom ABI, select the Custom ABI option and input it accordingly.
4. **Task Configuration**
- A checker function evaluates conditions before triggering the main function. From the Checker Function dropdown, choose the specific function you want as a condition checker.
- Enter the Target Contract where the automated function call should be sent. From the subsequent dropdown, select the specific function you wish to automate.
---
## Using UI
**Path:** /web3-functions/how-to-guides/create-a-web3-functions-task/using-ui
This method is ideal for developers or operators who prefer a graphical interface for task configuration. For instance, if you're a developer who wants to quickly test a function or a non-technical person looking to schedule a recurring job without writing any code.
In order to create a task that will automatically run your Web3 Function on your behalf, go to https://app.gelato.cloud/ and click on the "Create Task" button.
- To learn about creating Typescript function tasks from the UI: [Creating Typescript Function Task](/web3-functions/how-to-guides/write-typescript-functions)
- To learn about creating Solidity function tasks from the UI: [Creating Solidity Function Tasks](/web3-functions/how-to-guides/write-solidity-functions)
- To learn about creating Automated Transaction Tasks from the UI: [Initiate an Automated Transaction](/web3-functions/how-to-guides/initiate-an-automated-transactions)
## Single Execution Task
If you want to have Gelato call your function only once. If so, you can open up the Advanced Settings panel when creating a new task and select Single execution task
The task will still be cancelled if the execution reverts on-chain
---
## Using the Automate SDK
**Path:** /web3-functions/how-to-guides/create-a-web3-functions-task/using-the-automate-sdk
The SDK is suitable when you need to integrate task creation into your development environment or automated scripts. It's also useful for complex setups that require conditional logic before task submission.
Use the `automate-sdk` to easily create a new task:
```bash
yarn install @gelatonetwork/automate-sdk
```
Typescript Functions are still in private beta, make sure to use the beta version of the `automate-sdk` to have access to it
## Typescript Function
Import the sdk and create task, passing your typescript function CID & arguments:
```typescript
const automate = new AutomateSDK(chainId, deployer);
const { taskId, tx } = await automate.createBatchExecTask({
name: "Web3Function - Eth Oracle",
web3FunctionHash: cid,
web3FunctionArgs: {
oracle: oracle.address,
currency: "ethereum",
},
trigger: {
// Run every minutes
type: TriggerType.TIME,
interval: 60 * 1000,
},
});
await tx.wait();
```
You can specify `cron` trigger this way:
```typescript
trigger: {
type: TriggerType.CRON,
cron: "0 8 * * *", // Run every day at 8:00
}
```
`event` trigger like this:
```typescript
trigger: {
type: TriggerType.EVENT,
filter: {
// Listen to PriceUpdated event on Oracle contract
address: oracle.address,
topics: [[oracle.getEventTopic("PriceUpdated")]],
},
blockConfirmations: 0, // Trigger immediately
},
```
And `block` trigger this way:
```typescript
trigger: {
type: TriggerType.BLOCK,
}
```
If your task utilizes secrets, you can set them after the task has been created.
```typescript
const web3Function = new Web3Function(chainId, deployer);
const secrets = {
API_KEY: "..." // Set your secret environment variables
}
await web3Function.secrets.set(secrts, taskId);
```
## Solidity Function
Repeat the installation step as shown above, then import and instantiate the SDK:
```typescript
const automate = new AutomateSDK(chainId, signer);
```
Use `createTask` to automate your function calls:
```typescript
interface CreateTaskOptions {
name: string; // your task name
// Function to execute
execAddress: string; // address of your target smart contract
execSelector: string; // function selector to execute on your target smart contract
execAbi?: string; // ABI of your target smart contract
// Proxy caller
dedicatedMsgSender: boolean; // task will be called via a dedicated msg.sender which you can whitelist (recommended: true)
// Optional: Pre-defined / static target smart contract inputs
execData?: string; // exec call data
// Optional: Dynamic target smart contract inputs (using a resolver)
resolverAddress?: string; // resolver contract address
resolverData?: string; // resolver call data (encoded data with function selector)
resolverAbi?: string; // your resolver contract ABI
// Optional: Time based task params
interval?: number; // execution interval in seconds
startTime?: number; // start timestamp in seconds or 0 to start immediately (default: 0)
// Optional: Single execution task
singleExec?: boolean; // task cancels itself after 1 execution if true.
// Optional: Payment params
useTreasury?: boolean; // use false if your task is self-paying (default: true)
}
const params: CreateTaskOptions = {
name,
execAddress,
execSelector,
interval
};
const { taskId, tx }: TaskTransaction = await automate.createTask(params);
```
### Examples
#### Deploy a contract & automate your function call:
```typescript
// Deploying Counter contract
const counterFactory = await hre.ethers.getContractFactory("Counter");
const counter = await counterFactory.deploy(GELATO_ADDRESSES[chainId].automate);
await counter.deployed();
// Call Counter.increaseCount(42) every 10 minutes
const { taskId, tx }: TaskTransaction = await automate.createTask({
execAddress: counter.address,
execSelector: counter.interface.getSighash("increaseCount(uint256)"),
execData: counter.interface.encodeFunctionData("increaseCount", [42]),
execAbi: counter.interface.format("json") as string,
interval: 10 * 60, // execute every 10 minutes
name: "Automated counter every 10min",
dedicatedMsgSender: true
});
```
#### Use a Checker to automate your function call:
If you need more configurable execution condition and/or dynamic input data, you can create a task using Checker
```typescript
// Prepare Task data to automate
const counter = new Contract(COUNTER_ADDRESSES, counterAbi, signer);
const resolver = new Contract(COUNTER_RESOLVER_ADDRESSES, counterResolverAbi, signer);
const selector = counter.interface.getSighash("increaseCount(uint256)");
const resolverData = resolver.interface.getSighash("checker()");
// Create task
const { taskId, tx }: TaskTransaction = await automate.createTask({
execAddress: counter.address,
execSelector: selector,
resolverAddress: resolver.address,
resolverData: resolverData,
name: "Automated counter using resolver",
dedicatedMsgSender: true
});
```
## Automated Transaction
The `CreateTaskOptions` interface is used for configuring Automated Transactions with the same structure and options as defined above.
The only difference is you need to configure your automated transaction without the need for a checker function.
Example:
```typescript
interface CreateTaskOptions {
name: string; // Name your task
execAddress: string; // Address of your target smart contract
execSelector: string; // Function selector to call on your target smart contract
execAbi?: string; // ABI of your target smart contract (optional)
execData?: string; // Call data for the function execution (optional)
}
```
## Single Execution Task
If you want to have Gelato call your function only once. If so, set `singleExec` flag to true when calling `createTask`.
```typescript
const { taskId, tx }: TaskTransaction = await automate.createTask({
execAddress: counter.address,
execSelector: selector,
resolverAddress: counter.address,
resolverData: resolverData,
dedicatedMsgSender: true,
name: "Automated counter using resolver",
dedicatedMsgSender: true,
singleExec: true
});
```
---
## Using Smart Contract
**Path:** /web3-functions/how-to-guides/create-a-web3-functions-task/using-smart-contract
You can create a task that uses Web3 Function from your smart contract as well.
If your project involves complex interactions and you need the task creation to be a part of an on-chain transaction, you would directly interact with a smart contract.
Web3 Function secrets are not available for smart contract created tasks.
To create a Web3 Function task with your smart contract, you can inherit [AutomateTaskCreator](https://github.com/gelatodigital/automate/blob/master/contracts/integrations/AutomateTaskCreator.sol) which has helper functions to easily create your task.
Pass the `Module.PROXY` & `Module.WEB3_FUNCTION` as modules in `ModuleData`
```solidity
ModuleData memory moduleData = ModuleData({
modules: new Module[](2),
args: new bytes[](2)
});
moduleData.modules[0] = Module.PROXY;
moduleData.modules[1] = Module.WEB3_FUNCTION;
```
Use `_web3FunctionModuleArg` to encode arguments for `WEB3_FUNCTION` module.
```solidity
function _web3FunctionModuleArg(
string memory _web3FunctionHash, // IPFS CID of deployed web3Function
bytes calldata _web3FunctionArgsHex // Abi encoded web3 function arguments
)
```
Here is how you can encode your Web3Function arguments to get `web3FunctionArgsHex`.
In this example, the Web3Function has 2 arguments, `counterW3fAddress` & `count`.
```json schema.json
{
"web3FunctionVersion": "2.0.0",
"runtime": "js-1.0",
"memory": 128,
"timeout": 30,
"userArgs": {
"counterW3fAddress": "string",
"count": "number"
}
}
```
In your contract, you would encode the arguments according to the sequence defined in `schema.json`.
```solidity
function _getWeb3FunctionArgsHex(
address counterW3fAddress,
uint256 count
)
internal
pure
returns (bytes memory web3FunctionArgsHex) {
web3FunctionArgsHex = abi.encode(counterW3fAddress, count)
}
```
The full code can be found [here](https://github.com/gelatodigital/automate/blob/master/contracts/integrations/examples/contractCreator/withTreasury/CounterWeb3Function.sol).
```solidity
function createTask(
string memory _web3FunctionHash,
bytes calldata _web3FunctionArgsHex
) external {
require(taskId == bytes32(""), "Already started task");
bytes memory execData = abi.encodeCall(this.increaseCount, (1));
ModuleData memory moduleData = ModuleData({
modules: new Module[](2),
args: new bytes[](2)
});
moduleData.modules[0] = Module.PROXY;
moduleData.modules[1] = Module.WEB3_FUNCTION;
moduleData.args[0] = _proxyModuleArg();
moduleData.args[1] = _web3FunctionModuleArg(
_web3FunctionHash,
_web3FunctionArgsHex
);
bytes32 id = _createTask(
address(this),
execData,
moduleData,
address(0)
);
taskId = id;
emit CounterTaskCreated(id);
}
```
## Additional Info
- Tasks created via this route cannot be named
- Smart Contracts can also create and cancel tasks
- You can find a list of example smart contracts [here](https://github.com/gelatodigital/automate/tree/master/contracts/integrations/examples)
## Functions Exposed by `AutomateTaskCreator`
### `_createTask()`
Interacts and creates a task on the Gelato Automate smart contract.
```solidity
function _createTask(
address execAddress,
bytes memory execDataOrSelector,
ModuleData memory moduleData,
address feeToken
) internal returns (bytes32 taskId);
```
- `execAddress` - Address of the contract which Gelato will call
- `execDataOrSelector` - Signature of function which Gelato will call / execution data (If Resolver Module is not used. More about modules below)
- `moduleData` - Modules that are enabled for the task. (More about ModuleData below)
- `feeToken` - Use address(0) if using Gelato Gas Tank. Use 0xeeeeee... for ETH or native tokens.
### `ModuleData`
```solidity
struct ModuleData {
Module[] modules;
bytes[] args;
}
```
Modules are conditions / specifications about your task. These are the current available Modules.
```solidity
enum Module {
RESOLVER,
PROXY,
SINGLE_EXEC,
WEB3_FUNCTION,
TRIGGER
}
```
- `RESOLVER` - Define dynamic conditions and execution data
- `TIME` - Repeated execution at a specific time and interval. (in ms)
- `PROXY` - Your function will be called by a dedicated msg.sender
- `SINGLE_EXEC` - Task is cancelled after one execution
- `WEB3_FUNCTION` - Define a Typescript function to get off-chain execution data
- `TRIGGER` - Define your execution trigger (Time interval, Event, every block, ...)
Each Module would require additional arguments which is an encoded data.
Including `Module.Proxy` in `moduleData` is mandatory, otherwise task creation will fail.
You can use these helper functions to get the arguments for each Module.
```solidity
function _resolverModuleArg(address _resolverAddress, bytes memory _resolverData)
function _proxyModuleArg()
function _singleExecModuleArg()
function _timeTriggerModuleArg(uint128 _start, uint128 _interval)
function _cronTriggerModuleArg(string memory _expression)
function _blockTriggerModuleArg()
function _eventTriggerModuleArg(
address _address,
bytes32[][] memory _topics,
uint256 _blockConfirmations
) internal pure returns (bytes memory)
```
Crafting `ModuleData` will look like this if we want to create a task which utilise `RESOLVER`, `PROXY` & `SINGLE_EXEC` Module.
```solidity
ModuleData memory moduleData = ModuleData({
modules: new Module[](3),
args: new bytes[](3)
});
moduleData.modules[0] = Module.RESOLVER;
moduleData.modules[1] = Module.PROXY;
moduleData.modules[2] = Module.SINGLE_EXEC
moduleData.args[0] = _resolverModuleArg(
address(this),
abi.encodeCall(this.checker, ())
);
moduleData.args[1] = _proxyModuleArg();
moduleData.args[2] = _singleExecModuleArg();
```
`Module[]` must follow the order `RESOLVER`, `PROXY`, `SINGLE_EXEC`, `WEB3_FUNCTION`, `TRIGGER`
### `_cancelTask()`
Cancels a task owned by the smart contract.
```solidity
function _cancelTask(bytes32 _taskId) internal
```
### `onlyDedicatedMsgSender`
Function modifier to restrict msg.sender to only task executions created by taskCreator (defined in constructor). Learn more about it at [Security Considerations](/web3-functions/security-considerations/dedicated-msg-sender)
```solidity
modifier onlyDedicatedMsgSender() {
require(msg.sender == dedicatedMsgSender, "Only dedicated msg.sender");
_;
}
```
### `_depositFunds1Balance()`
```solidity
function _depositFunds1Balance(
uint256 _amount,
address _token,
address _sponsor
)
```
Deposit funds into the Gelato 1balance contract.
The `_depositFunds1Balance` method is only available on Polygon
## Single Execution Task
If you want to have Gelato call your function only once. If so, you can Include SingleExec module in ModuleData.modules. Check out the full code [here](https://github.com/gelatodigital/automate/blob/f6c45c81971c36e414afc31276481c47e202bdbf/contracts/integrations/examples/contractCreator/withTreasury/CounterSingleExecTaskCreator.sol).
```solidity
ModuleData memory moduleData = ModuleData({
modules: new Module[](2),
args: new bytes[](2)
});
moduleData.modules[0] = Module.PROXY;
moduleData.modules[1] = Module.SINGLE_EXEC;
moduleData.args[0] = _proxyModuleArg();
moduleData.args[1] = _singleExecModuleArg();
bytes32 id = _createTask(
address(this),
execData,
moduleData,
address(0)
);
```
---
## Using Safe UI
**Path:** /web3-functions/how-to-guides/create-a-web3-functions-task/using-safe-ui
We recommend using our custom app within Safe for a more reliable connection and smoother user experience. Wallet Connect does sometimes encounter issues and is not recommended.
## Setting up Custom Gelato App in Safe
Access the official Gnosis Safe website: https://safe.global/
Once there, locate and click on the "Launch Wallet" button, highlighted in green and situated in the top right corner.
If you're already using Safe, access your dashboard
Under the Apps tab, search for Gelato and click on the app. You will then be re-directed to the Gelato app login screen, where you should be able to sign-in.
After completing these steps, you'll be required to sign in through the Safe UI:
If your Safe requires multiple signatures (multi-sig) the sign-in process will need approvals from the minimum number of signers set for your Safe before it can complete
And that's it! You now have full access to the Gelato app!
---
## Initiate an Automated Transaction
**Path:** /web3-functions/how-to-guides/initiate-an-automated-transactions
An execution attempt in Gelato allows you to pre-define the inputs for a function. By doing so, every time Gelato calls the function, it uses the same arguments, ensuring consistent behavior in your automated tasks.
## 1. Prepare Your Smart Contract for Automation
- **Identify Your Smart Contract**: Ensure you have the correct smart contract address and know the specific function you want to automate.
- **Function Restrictions**: Some functions may not be compatible with Gelato due to certain restrictions. Familiarize yourself with these to ensure seamless automation.
## 2. Set Your Trigger Condition
- **Choosing Your Trigger**: Gelato allows you to set specific conditions to determine when your function is called. This could be at regular intervals (time interval) or based on specific events (cron expression).
- **No Custom Code Required**: For initiating tranaction tasks, there's no need for you to write code. Simply set your desired trigger and move forward.
For initiating tranaction tasks, there's no need for you to write code. Simply set your desired trigger and move forward.
Finally, Click on "Create Task" button! Its just that simple!
---
## Dedicated msg.sender
**Path:** /web3-functions/security-considerations/dedicated-msg-sender
For security reasons, during task creation, you will see an address which will be the `msg.sender` for your task executions.
If you are the owner of the target contract in question, it's recommended to implement a msg.sender restriction within your smart contract. This involves whitelisting a dedicated msg.sender address. Such a measure ensures that only tasks you have created can call your function, significantly elevating the security posture of your operations. For a hands-on guide and to manage your dedicated msg.sender settings, please connect to the app and visit your own Settings page.
Remember that your dedicated msg.sender can vary across different blockchain networks. You can view the dedicated msg.sender for each network through the provided settings link.
`msg.sender` restrictions should be added to the function that Gelato will call during execution, not the checker function. Learn more about it here: [Writing Solidity Functions](/web3-functions/how-to-guides/write-solidity-functions#1-understand-the-role-of-a-checker)
You can have this restriction by inheriting [AutomateReady](https://github.com/gelatodigital/automate/blob/master/contracts/integrations/AutomateReady.sol).
```AutomateReady``` exposes a modifier ```onlyDedicatedMsgSender``` which restricts ```msg.sender``` to only task executions created by ```taskCreator``` defined in the constructor.
```solidity
modifier onlyDedicatedMsgSender() {
require(msg.sender == dedicatedMsgSender, "Only dedicated msg.sender");
_;
}
```
If you would like to have additional callers for your function. You can implement a whitelist like so.
```solidity
mapping(address => bool) public whitelisted;
modifier onlyWhitelisted() {
require(
whitelisted[msg.sender] || msg.sender == dedicatedMsgSender,
"Only whitelisted"
);
_;
}
```
---
## Supported Networks
**Path:** /web3-functions/additional-resources/supported-networks
The following networks are supported:
{(() => {
const networksData = [
{ network: "Abstract", environment: "Mainnet", notes: [{ text: "Abstract Mainnet", href: "#abstract-mainnet" }] },
{ network: "Aleph Zero", environment: "Mainnet, Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Arbitrum", environment: "Mainnet, Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Arbitrum Blueberry", environment: "Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Avalanche", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Base", environment: "Mainnet, Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Berachain", environment: "Mainnet, Bepolia", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Binance Smart Chain", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Blast", environment: "Mainnet, Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Botanix", environment: "Mainnet, Testnet", notes: [{ text: "Botanix Mainnet", href: "#botanix-mainnet" },{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Camp Network", environment: "Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Corn", environment: "Maizenet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "EDU Chain", environment: "Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Everclear (prev Connext)", environment: "Mainnet, Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Ethereum", environment: "Mainnet, Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Etherlink", environment: "Mainnet, Ghostnet, Shadownet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }, { text: "Group B Deployments", href: "#group-b-deployments" },{ text: "Group C Deployments", href: "#group-c-deployments" }] },
{ network: "Fantom", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Filecoin", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Gnosis", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "HyperEVM", environment: "Mainnet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Incentiv", environment: "Mainnet, Testnet", notes: [{ text: "Group C Deployments", href: "#group-c-deployments" }] },
{ network: "Ink", environment: "Mainnet, Sepolia", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Linea", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Lisk", environment: "Mainnet, Sepolia", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Lumia", environment: "Mainnet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "MegaETH", environment: "Timothy Testnet", notes: [{ text: "Group C Deployments", href: "#group-c-deployments" }] },
{ network: "Metis", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Mitosis", environment: "Mainnet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Mode", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Monad", environment: "Testnet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Nibiru", environment: "Mainnet, Testnet", notes: [{ text: "Nibiru Mainnet", href: "#nibiru-mainnet" }] },
{ network: "Optimism", environment: "Mainnet, Testnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Plasma", environment: "Mainnet, Testnet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Playblock", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Polygon", environment: "Mainnet, Amoy", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Polygon zkEVM", environment: "Mainnet", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "Reya", environment: "Mainnet, Cronos", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{
network: "Rootstock",
environment: "Mainnet, Testnet",
notes: [
{ text: "Group A Deployments", href: "#group-a-deployments" },
{ text: "Group B Deployments", href: "#group-b-deployments" }
]
},
{ network: "Singularity Finance", environment: "Testnet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Sonic", environment: "Mainnet", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" }] },
{ network: "Story", environment: "Mainnet, Aeneid", notes: [{ text: "Group B Deployments", href: "#group-b-deployments" },{ text: "Group C Deployments", href: "#group-c-deployments" }] },
{ network: "Tangible", environment: "re.al, unreal", notes: [{ text: "Group A Deployments", href: "#group-a-deployments" }] },
{ network: "zkSync Era", environment: "Mainnet", notes: [{ text: "ZkSync Era", href: "#zksync-era" }] }
];
return (
{item.address}
{item.address}
{item.address}
{item.address}
{item.address}
{item.address}
{item.address}
{item.address}
## Logs & Status
Besides the task logs available in the UI, Gelato Web3 Functions offer a more detailed and granular monitoring system providing status and logs APIs.
### Task Status URL
Provided the `ChainId` and `taskId`, this API will return the current Task status
```
https://api.gelato.digital/tasks/web3functions/networks/{chainId}/tasks/{taskId}/status
```
For example, if your chainId, taskId are:
```
chainId: 137
taskId: 0xdeaeee394c952d8b23c86eacc704adf7b605d89d992cec9a5fc86e4a517f053b
```
Then the URL to go to is:
```
https://api.gelato.digital/tasks/web3functions/networks/137/tasks/0xdeaeee394c952d8b23c86eacc704adf7b605d89d992cec9a5fc86e4a517f053b/status
```
For this taskId, here is the returned task information:
```json
{
"task":{
"chainId":137,
"taskId": "0xdeaeee394c952d8b23c86eacc704adf7b605d89d992cec9a5fc86e4a517f053b",
"taskState":"CheckPending",
"creationDate":"2023-06-01T20:10:39.985Z",
"lastCheckDate":"2023-06-09T06:22:44.966Z",
"lastCheckMessage":"Fail to run Web3Function: Web3Function exited with code=1",
"lastExecDate":"2023-06-09T08:15:11.883Z",
"lastExecTransactionHash": "0xc2e57f5b56bf24ae77eca31fbe76ecf16cd30cb0fc5592207bb567addff62402"
}
}
```
The first thing to look at is the taskState key:
#### Task states:
For the taskState key, these are the possible values:
- **CheckPending**: the task is pending simulation.
- **ExecPending**: the task is executable and is awaiting inclusion into the blockchain.
- **WaitingForConfirmation**: the task was included into the blockchain but is still awaiting the required amount of blocks confirmations.
- **ExecSuccess**: the task has been successfully executed.
- **Cancelled**: the task has been canceled by the owner
- **ExecReverted**: the task transaction has been reverted.
### Task Logs URL
Provided the ChainId and taskId, this API will return the logs in the last 24 hours.
Query Parameters:
- `limit` (optional): The number of log entries to return, ranging from 1 to 100.
- `page` (optional): Specifies the page number of logs to fetch, ranging from 1 to 100.
```
https://api.gelato.digital/tasks/web3functions/networks/{chainId}/tasks/{taskId}/logs?limit=NrLogs&page=PageNr
```
For example, if your chainId, taskId, NrLogs and PageNr are:
```
chainId: 137
taskId: 0xdeaeee394c952d8b23c86eacc704adf7b605d89d992cec9a5fc86e4a517f053b
NrLogs:2
PageNr: 1
```
Then the URL to go to is:
```
https://api.gelato.digital/tasks/web3functions/networks/137/tasks/0xdeaeee394c952d8b23c86eacc704adf7b605d89d992cec9a5fc86e4a517f053b/logs?limit=2&page=1
```
For this taskId, here is the returned task information:
```json
{
"logs": [
{
"date":"2023-06-09T08:43:52.404Z",
"state":"WaitingForConfirmation",
"type":"WaitingForConfirmation",
"message":"txHash: 0x788726ed95f2f916a47cae0c6cdfbea91e1c8e3756f91e0efc08fa501daed8f0"
},
{
"date":"2023-06-09T08:43:51.835Z",
"state":"ExecPending",
"type":"ExecPendingCheck",
"message":"Task submitted for execution",
"web3FunctionLogs":[
"Text generated: ",
"Chaffinches are small, colourful birds which feed on seeds and insects. They have buff-coloured breasts streaked with brown markings, bright pinkish-red faces and wings marked with white bars. In summer they breed in woodlands; in winter many move south to warmer areas.",
"Text generated: ",
"Chameleons are lizards known for their ability to change color, excellent vision and long, sticky tongues used to catch prey."
]
}
]
}
```
## Alerts
Web3 Functions provides an alerting service to enable you to get notified about your task executions, problems or when your balance is getting low. Alerting currently supports notifications in Telegram, with other channels on the way.
### Types of notifications
{(() => {
const notificationTypes = [
{
type: "Balance",
description: "If your task uses Gelato Balance you need to ensure that you always have enough funds deposited, otherwise your executions will stop executing until you top-up. Set a balance alert to be notified when your funds are running low - you can use our default levels or set your own threshold."
}
];
return (
---
## Summary
- Total pages processed: 216
- Errors: 0
---
For the concise version of this documentation, see llms.txt
For section-specific summaries, see the /summaries directory