--- title: Sponsoring gas description: Covering the user's gas as an integrator, by sending the deposit from your own wallet, relaying the user's signed deposit yourself, or paying for a gasless relay. --- A user whose wallet holds tokens but no native asset cannot pay for a deposit. As an integrator you can absorb that cost in three ways: send the deposit from a wallet that you control and that holds the funds, relay a deposit the user signed from the user's own wallet, or request a gasless quote and let the operator do the relaying. Gasless in turn has two payers: by default the relay cost is priced out of the user's output, and with `sponsor_fees` it is charged to your partner funding balance instead, so the user gets the full quoted output. `canPayGas(owner)` on a chain adapter tells you whether the wallet could pay for a normal deposit, which is the usual trigger for switching to one of these paths. ## Send from your own wallet Nothing binds the depositor. The quote binds the `recipient` and the `refund_address`; the deposit itself can come from any wallet that holds the tokens and pays its own gas. If you custody the source funds, or move them to a treasury wallet first, deposit from that wallet and put the user as `recipient`. ```ts import { createChainAdapter, quoteOf, TradeType } from "@mayanfinance/hular-sdk"; const response = await hular.getQuote({ srcChain: "arbitrum", dstChain: "solana", srcToken: usdcOnArbitrum, dstToken: usdcOnSolana, tradeType: TradeType.EXACT_INPUT, amount: "25000000", recipient: userSolanaAddress, refundAddress: treasuryAddress, gasDrop: "", }); const quote = quoteOf(response); const adapter = createChainAdapter(client, chainStatus, 42161n); const steps = await adapter.steps(treasuryWallet, quote.quoteHash, quote.orderParams); for (const step of steps) { await step.run(); } ``` Set `refund_address` to your own wallet, not the user's: if the order cannot be fulfilled, the refund returns the deposit on the source chain, and it should come back to the wallet that sent it. This is a normal deposit in every other respect. The entry point, allowance handling, and tracking are exactly as described in [Deposit on EVM](/integration/deposit-evm) and [Deposit on Solana](/integration/deposit-solana), and you pay the source-chain gas directly. ## Relay the deposit yourself When the user holds the tokens but should not send a transaction, you can be the relayer instead of the operator. The router's witness entry points are permissionless: the user signs a permit2 witness signature bound to the quote hash, and any wallet can submit `depositTokenWithPermit2Witness` and pay the gas. Request a normal quote, not a gasless one; the `sponsor_gas` component prices the operator's relay, and here you are relaying. ```ts import { permit2WitnessDepositTypedData, randomPermit2Nonce, permitDeadline, } from "@mayanfinance/hular-sdk"; import { encodeFunctionData } from "viem"; const params = quote.orderParams; const nonce = randomPermit2Nonce(); const deadline = permitDeadline(); const signature = await userWallet.signTypedData( permit2WitnessDepositTypedData( 42161n, params.srcToken, BigInt(params.amountIn), routerAddress, nonce, deadline, quote.quoteHash, ), ); await treasuryWallet.sendTransaction({ to: routerAddress, data: encodeFunctionData({ abi: routerAbi, functionName: "depositTokenWithPermit2Witness", args: [ quote.quoteHash, userAddress, params.srcToken, BigInt(params.amountIn), nonce, deadline, signature, "0x", ], }), }); ``` `routerAddress` is the chain's `router` from `GetChains`, which is both the transaction target and the spender in the typed data. The witness binds the signature to the quote hash, so it authorizes exactly this deposit and nothing else. The user needs a standing permit2 allowance on the token; for EIP-2612 tokens the last argument takes a permit signature approving permit2 instead, collected in the same signing session. When the quote has a source swap (`src_token` differs from `bridge_token_src`), use `permit2WitnessSwapDepositTypedData` and `depositWithSwapAndPermit2Witness` with the aggregator and calldata from `GetSwapInstructions`; the witness additionally binds `bridge_token` and `min_bridge_out`. On Solana there is no signature scheme to relay: build the deposit with your own wallet as fee payer, have the user sign it, co-sign, and send it yourself. ## Gasless through the API When you would rather not run relaying infrastructure, request the quote with `gasless: true`. The user signs an authorization instead of sending a transaction (a permit2 witness signature on EVM, a sponsor-paid transaction on Solana), you submit it through [`SubmitGaslessOrder`](/api-reference/gasless), and an operator wallet lands the deposit and pays its gas. The relay cost is priced into the quote as the `sponsor_gas` fee component, so it comes out of the user's output like any other fee. ```ts const response = await hular.getQuote({ srcChain: "arbitrum", dstChain: "solana", srcToken: usdcOnArbitrum, dstToken: usdcOnSolana, tradeType: TradeType.EXACT_INPUT, amount: "25000000", recipient: userSolanaAddress, refundAddress: userAddress, gasDrop: "", gasless: true, }); const quote = quoteOf(response); const adapter = createChainAdapter(client, chainStatus, 42161n); const steps = await adapter.gaslessSteps(userWallet, quote.quoteHash, quote.orderParams, { sponsorAddress: quote.sponsorAddress, }); for (const step of steps) { await step.run(); } ``` Gasless is available for cross-chain quotes whose source token is not the native asset, on chains where `GetChains` reports `gasless: true`. ## Pay the relay yourself with `sponsor_fees` Add `sponsor_fees: true` to a gasless quote to charge the relay gas to your partner [funding balance](/api-reference/partner#funding) instead of the user's output. The `sponsor_gas` component disappears from the fee breakdown, `amount_out` is what the user actually receives, and each sponsored order debits your prepaid balance. ```ts const response = await hular.getQuote({ srcChain: "arbitrum", dstChain: "solana", srcToken: usdcOnArbitrum, dstToken: usdcOnSolana, tradeType: TradeType.EXACT_INPUT, amount: "25000000", recipient: userSolanaAddress, refundAddress: userAddress, gasDrop: "", gasless: true, sponsorFees: true, }); ``` The submission flow is identical to plain gasless. Two things change on the request side: - The quote must be requested with your `x-api-key`; without one it fails with `sponsor_fees requires an api key`. - Your funding balance must be positive when the order is submitted, or `SubmitGaslessOrder` fails with `insufficient funding balance`. Fund the balance through the router's `fundPartner` entry point on the designated chain and watch it in the [partner panel](/integration/partner-panel) or through [`ListPartnerFundingActivity`](/api-reference/partner#listpartnerfundingactivity), where each sponsored order appears as a debit carrying its `quote_hash`. ## Choosing a path | | Your wallet as sender | You relay | Gasless | Gasless with `sponsor_fees` | | --- | --- | --- | --- | --- | | Who holds the tokens | You | The user | The user | The user | | Who signs | Your wallet | The user, off chain | The user, off chain | The user, off chain | | Who sends the transaction | Your wallet | Your wallet | The operator | The operator | | Who pays the gas | You, directly on chain | You, directly on chain | The user, out of `amount_out` | You, from the funding balance | | Needs an API key | No | No | No | Yes | Sending from your own wallet works on every chain and token, including native deposits. The other three keep custody with the user end to end and need a non-native source token. Relaying yourself gives you control over submission and works from any wallet you run; gasless through the API needs no infrastructure but is limited to source chains where `GetChains` reports `gasless: true`.