WDK logoWDK documentation
USD₮0 bridgeFrom SolanaGuides

Bridge with a Gasless Account

Bridge USD₮ from Solana without holding SOL, using WalletAccountSolanaGasless and a Kora paymaster.

This guide covers prerequisites, how to create a gasless account, read the fees, bridge with paymaster options, and what the Kora node must allow.

Prerequisites

Install the bridge and the gasless wallet
npm install @tetherto/wdk-protocol-bridge-usdt0-solana @tetherto/wdk-wallet-solana-gasless

Create a gasless account

Create a WalletAccountSolanaGasless and wrap it with the same bridge class. The bridge recognizes the gasless flow from the account's paymasterAddress:

Bridge from a gasless account
import { WalletAccountSolanaGasless } from '@tetherto/wdk-wallet-solana-gasless'
import Usdt0ProtocolSolana from '@tetherto/wdk-protocol-bridge-usdt0-solana'

const account = new WalletAccountSolanaGasless(process.env.SEED_PHRASE, "0'/0'", {
  provider: 'https://api.mainnet-beta.solana.com',
  paymasterUrl: 'https://your-kora-paymaster.example',
  paymasterAddress: process.env.PAYMASTER_ADDRESS,
  paymasterToken: { address: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' }
})

const bridgeProtocol = new Usdt0ProtocolSolana(account)

The account needs USD₮ for the amount and paymaster tokens for the fee. It does not need SOL. For quotes without signing, use WalletAccountReadOnlySolanaGasless with the same configuration. The paymaster is the simulated fee payer, so the source address does not have to hold SOL.

Read the fees

The paymaster pays the Solana transaction, fronts the LayerZero fee, and charges both back in the paymaster token through the wallet's payment instruction. The two fields returned by quoteBridge() and bridge() therefore use different units:

FieldUnitMeaningCapped by
feePaymaster token base unitsThe payment to the paymaster, which includes the LayerZero feetransactionMaxFee
bridgeFeeLamportsThe LayerZero fee the paymaster frontsbridgeMaxFee

Do not add fee and bridgeFee. They are different currencies, and fee already includes the cost of bridgeFee.

Bridge with paymaster options

You can pass overrides as the second argument of bridge():

Bridge with paymaster overrides
const options = {
  targetChain: 'arbitrum',
  recipient: process.env.ARBITRUM_RECIPIENT,
  token: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
  amount: 1000000n
}

const quote = await bridgeProtocol.quoteBridge(options)

const result = await bridgeProtocol.bridge(options, {
  bridgeMaxFee: 2_000_000n, // Lamports, checked before contacting the paymaster
  transactionMaxFee: 1_000_000n, // Paymaster token base units
  paymasterToken: { address: 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB' }
})

console.log('Solana transaction hash:', result.hash)
console.log('Paid to the paymaster:', result.fee)
console.log('LayerZero fee fronted:', result.bridgeFee, 'lamports')

quoteBridge() accepts only the paymasterToken override.

Kora node requirements

The bridge needs more from the node than an ordinary sponsored transfer. Your Kora node must have:

  • Margin pricing, so the LayerZero fee it pays is charged back.
  • fee_payer_policy.system.allow_transfer = true, because the LayerZero message library pays its workers from the fee payer.
  • max_allowed_lamports above the LayerZero fee, which follows destination gas prices. See the sampled fees. TRON was above 0.1 SOL when sampled.
  • The programs below on its allowlist.
ProgramAddress
Legacy Mesh OFTFuww9mfc8ntAwxPUzFia7VJFAdvLppyZwhPJoXySZXf7
LayerZero endpoint76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6
LayerZero ULN7a4WjyR8VZ7yZz5XJAKm39BUGn5iT9CKcv2pmG9tdXVH
Executor and DVNsResolved on-chain for each destination

The endpoint is read from the OFT store, and the executor and DVN programs from the LayerZero send configuration of each route. They can change on-chain, so test each route you enable against your node.

Next Steps

Harden integrations with Handle errors, or review the gasless wallet module.


Need Help?

On this page