# Gas Sponsorship with Paymaster

Sponsor gas fees for UserOperations using Candide's Paymaster [InstaGas](https://docs.candide.dev/instagas/overview.md) with public and private gas policies.

> For smart account basics, see the [Getting Started Guide](https://docs.candide.dev/wallet/guides/getting-started.md).

![](/img/gasless-transaction-screen.png)

## Getting Started[​](#getting-started "Direct link to Getting Started")

### Setup Gas Policy[​](#setup-gas-policy "Direct link to Setup Gas Policy")

To create a private gas policy for sponsoring specific transactions:

1. Create an account on the [Candide Dashboard](https://dashboard.candide.dev)
2. Navigate to Gas Policies and create a new private policy
3. Configure sponsorship [rules and conditions](https://docs.candide.dev/instagas/gas-policies.md) for transaction sponsorship
4. Fund your policy by depositing native tokens to cover gas costs
5. Copy the Policy ID for referencing your policy

#### Environment Configuration[​](#environment-configuration "Direct link to Environment Configuration")

Add the paymaster configuration to your `.env`:

.env

```
# Paymaster service endpoint
PAYMASTER_RPC=https://api.candide.dev/public/v3/sepolia

# Optional: Your private gas policy ID (if you created one)
SPONSORSHIP_POLICY_ID=
```

### Getting Paymaster Data[​](#getting-paymaster-data "Direct link to Getting Paymaster Data")

Try public policies first, then fallback to private if no public policy matches

> You can also [fork the complete code](https://github.com/candidelabs/abstractionkit-examples/blob/main/sponsor-gas/sponsor-gas.ts) and follow along.

* index.ts
* .env

Public first, private fallback

```
import { CandidePaymaster } from "abstractionkit";

const paymasterRPC = process.env.PAYMASTER_RPC as string;
const paymaster = new CandidePaymaster(paymasterRPC);
const sponsorshipPolicyId = process.env.SPONSORSHIP_POLICY_ID;

let paymasterUserOperation;
let sponsorMetadata;

try {
  // First, try public gas policies
  [paymasterUserOperation, sponsorMetadata] =
    await paymaster.createSponsorPaymasterUserOperation(
      userOperation,
      bundlerUrl
    );
  console.log("Sponsored by public gas policy!");
} catch (error) {
  try {
    // Fallback to private gas policy
    [paymasterUserOperation, sponsorMetadata] =
      await paymaster.createSponsorPaymasterUserOperation(
        userOperation,
        bundlerUrl,
        sponsorshipPolicyId
      );
    console.log("Sponsored by your private gas policy!");
  } catch (finalError) {
    console.log("No gas sponsorship available");
    throw finalError;
  }
}

userOperation = paymasterUserOperation;
```

```
# Paymaster service endpoint
PAYMASTER_RPC=https://api.candide.dev/public/v3/sepolia

# Optional: Your private gas policy ID (if you created one)
SPONSORSHIP_POLICY_ID=
```

#### Complete Runnable Example[​](#complete-runnable-example "Direct link to Complete Runnable Example")

Below is a complete example that demonstrates gas sponsorship with public and private policy fallback:

Full Working Example

* index.ts
* .env

```
import * as dotenv from 'dotenv'

import {
    SafeAccountV0_3_0 as SafeAccount,
    MetaTransaction,
    CandidePaymaster,
    getFunctionSelector,
    createCallData,
} from "abstractionkit";

async function main(): Promise<void> {
    // Load environment variables
    dotenv.config()
    const chainId = BigInt(process.env.CHAIN_ID as string)
    const bundlerUrl = process.env.BUNDLER_URL as string
    const jsonRpcNodeProvider = process.env.JSON_RPC_NODE_PROVIDER as string
    const paymasterRPC = process.env.PAYMASTER_RPC as string
    const sponsorshipPolicyId = process.env.SPONSORSHIP_POLICY_ID
    const ownerPublicAddress = process.env.PUBLIC_ADDRESS as string
    const ownerPrivateKey = process.env.PRIVATE_KEY as string
    
    // Create smart account
    let smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress])
    console.log("Smart Account Address:", smartAccount.accountAddress)

    // Create example transactions (minting NFTs)
    const nftContractAddress = "0x9a7af758aE5d7B6aAE84fe4C5Ba67c041dFE5336";
    const mintFunctionSignature = 'mint(address)';
    const mintFunctionSelector = getFunctionSelector(mintFunctionSignature);
    const mintTransactionCallData = createCallData(
        mintFunctionSelector, 
        ["address"],
        [smartAccount.accountAddress]
    );
    
    const transaction1: MetaTransaction = {
        to: nftContractAddress,
        value: 0n,
        data: mintTransactionCallData,
    }

    const transaction2: MetaTransaction = {
        to: nftContractAddress,
        value: 0n,
        data: mintTransactionCallData,
    }

    // Create UserOperation
    let userOperation = await smartAccount.createUserOperation(
        [transaction1, transaction2],
        jsonRpcNodeProvider,
        bundlerUrl
    )

    // Add paymaster sponsorship - try public first, fallback to private
    const paymaster = new CandidePaymaster(paymasterRPC)
    
    let paymasterUserOperation;
    let sponsorMetadata;
    
    try {
        // Try public gas policies first
        console.log("🔍 Checking for public gas policies...")
        [paymasterUserOperation, sponsorMetadata] =
            await paymaster.createSponsorPaymasterUserOperation(
                userOperation,
                bundlerUrl
            );
        console.log("✅ Sponsored by public gas policy!")
        
    } catch (error) {
        try {
            // Fallback to private gas policy
            console.log("🔍 Trying private gas policy fallback...")
            [paymasterUserOperation, sponsorMetadata] =
                await paymaster.createSponsorPaymasterUserOperation(
                    userOperation,
                    bundlerUrl,
                    sponsorshipPolicyId
                );
            console.log("✅ Sponsored by private gas policy!")
            
        } catch (finalError) {
            console.log("❌ No gas sponsorship available");
            throw finalError;
        }
    }
    
    userOperation = paymasterUserOperation;
    console.log("💰 Transaction will be gasless for the user!")

    // Sign the UserOperation
    userOperation.signature = smartAccount.signUserOperation(
        userOperation,
        [ownerPrivateKey],
        chainId
    )

    // Submit the sponsored UserOperation
    const sendUserOperationResponse = await smartAccount.sendUserOperation(
        userOperation, 
        bundlerUrl
    )

    console.log("📤 Sponsored UserOperation sent. Waiting for confirmation...")
    
    // Wait for transaction to be included
    let userOperationReceiptResult = await sendUserOperationResponse.included()

    console.log("📋 UserOperation receipt received.")
    console.log(userOperationReceiptResult)
    
    if (userOperationReceiptResult.success) {
        console.log("🎉 Gasless transaction successful! Hash:", userOperationReceiptResult.receipt.transactionHash)
    } else {
        console.log("❌ UserOperation execution failed")
    }
}

main().catch(console.error)
```

```
CHAIN_ID=11155111
BUNDLER_URL=https://api.candide.dev/public/v3/sepolia
JSON_RPC_NODE_PROVIDER=https://ethereum-sepolia-rpc.publicnode.com
PAYMASTER_RPC=https://api.candide.dev/public/v3/sepolia

# Optional: Your private gas policy ID
SPONSORSHIP_POLICY_ID=

# Your EOA credentials
PRIVATE_KEY=your_private_key_here
PUBLIC_ADDRESS=your_public_address_here
```

## Gas Policies Overview[​](#gas-policies-overview "Direct link to Gas Policies Overview")

Candide's Paymaster supports two types of gas policies:

### Public Gas Policies[​](#public-gas-policies "Direct link to Public Gas Policies")

* Set up by external applications - Apps like PoolTogether and Revoke.cash sponsor gas for their users
* Application-specific - Each policy targets specific contract interactions or user flows
* No setup required - Available automatically when your UserOperation matches their criteria
* Best for - Users interacting with supported dApps and protocols

### Private Gas Policies[​](#private-gas-policies "Direct link to Private Gas Policies")

* Your custom rules - You create and fund policies for your specific application
* Full control - Set custom sponsorship conditions, limits, and restrictions
* Your funding - You deposit native tokens to sponsor transactions that match your criteria
* Best for - Wallets wanting to sponsor their users' transactions
