Skip to content

Gas Sponsor (Paymaster)

A paymaster is a smart contract that pays gas fees on behalf of users. This lets you build dapps where users never need to hold DILI for gas -- the sponsor contract covers the cost.

How It Works

  1. Your dapp passes a paymaster address in the TxOptions of any transactional function.
  2. The wallet includes the paymaster address in the signed transaction.
  3. The network charges gas to the paymaster contract instead of the user's account.

The paymaster contract must be pre-funded and must accept the transaction (paymasters can implement allowlists, rate limits, etc.).

Supported Functions

The following wallet functions accept a paymaster option via TxOptions:

Function Description
signPayload Sign a structured payload
sendTransaction Submit a pre-built transaction
callContract Build and submit a contract call
transfer Transfer DILI tokens
shieldedDeposit Deposit into the shielded pool
shieldedWithdraw Withdraw from the shielded pool

Example: Sponsored Transfer

import { connect, transfer, createChainClient } from "@dilithia/browser-sdk";

const SPONSOR = "dili1myappsponsor";

const session = await connect();
const chain = createChainClient(session.rpcUrl);

// User pays no gas -- the sponsor contract covers it
const tx = await transfer("dili1bob", 100, { paymaster: SPONSOR });
const receipt = await chain.waitForReceipt(tx.txHash);

console.log("Status:", receipt.status);
console.log("Fee paid by sponsor:", receipt.feePaid, "DILI");

Example: Sponsored Contract Call

import { callContract, createChainClient, connect } from "@dilithia/browser-sdk";

const SPONSOR = "dili1myappsponsor";

const session = await connect();
const chain = createChainClient(session.rpcUrl);

const tx = await callContract(
  "dili1nftcontract",
  "mint",
  { tokenId: 42, metadata: "ipfs://Qm..." },
  { paymaster: SPONSOR },
);

const receipt = await chain.waitForReceipt(tx.txHash);
console.log("Minted! Block:", receipt.blockHeight);

Example: Sponsored Shielded Deposit

import { shieldedDeposit, createChainClient, connect } from "@dilithia/browser-sdk";

const SPONSOR = "dili1myappsponsor";

const session = await connect();
const chain = createChainClient(session.rpcUrl);

// Deposit into the shielded pool with sponsored gas
const deposit = await shieldedDeposit(500, { paymaster: SPONSOR });
console.log("Commitment:", deposit.commitment);

const receipt = await chain.waitForReceipt(deposit.txHash);
console.log("Deposit confirmed at block", receipt.blockHeight);

Tips

Paymaster funding

Make sure your paymaster contract has enough DILI to cover gas for your users. Monitor its balance with chain.getBalance("dili1myappsponsor").

Combine with shielded withdrawals

You can sponsor shielded withdrawals too. This is useful when the recipient has no public balance to pay gas:

const withdrawal = await shieldedWithdraw(0, 500, "dili1alice", {
  paymaster: "dili1myappsponsor",
});

Paymaster rejection

If the paymaster contract rejects the transaction (e.g., the user is not on its allowlist), the wallet will throw an error. Handle it with try/catch.