# Pay Gas with ERC-20 Tokens

Enable users to pay gas fees with ERC-20 tokens instead of native tokens using Candide's Token Paymaster.

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

![](/img/network-fees-sponsorship.png)

## Why Use Token Paymaster[​](#why-use-token-paymaster "Direct link to Why Use Token Paymaster")

Token paymasters enable users to pay gas fees with ERC-20 tokens instead of ETH, eliminating the need to hold native tokens for transactions.

* **Better UX**: Users transact with only the tokens they need
* **Cost Calculation**: Calculate exact token amounts for gas costs
* **Supported Tokens**: USDC, USDT, and other [popular ERC-20 tokens](https://docs.candide.dev/wallet/paymaster/tokens-supported.md)

## Quickstart[​](#quickstart "Direct link to Quickstart")

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

[YouTube video player](https://www.youtube.com/embed/3jU_baONS9s?si=OfWuHpqpHWbjOnJD)

### Step 1: Fetch Supported Tokens[​](#step-1-fetch-supported-tokens "Direct link to Step 1: Fetch Supported Tokens")

* index.ts
* .env

Token paymaster implementation

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



const paymasterRPC = process.env.PAYMASTER_RPC as string;

const tokenAddress = process.env.TOKEN_ADDRESS as string;



const paymaster = new Erc7677Paymaster(paymasterRPC);



// Pass any supported token address. Erc7677Paymaster fetches the quote

// and prepends the required ERC-20 approval automatically.

console.log(`Using ${tokenAddress} for gas payment`);
```

Test Tokens

Get ERC-20 faucet tokens (CTT or USDT) from our [dashboard faucet](https://dashboard.candide.dev/faucet) for testing. Ensure your smart account has sufficient ERC-20 tokens to pay for gas.

```
# Paymaster service endpoint

PAYMASTER_RPC=https://api.candide.dev/public/v3/11155111



# Token address for gas payment (CTT on Sepolia for testing)

TOKEN_ADDRESS=0xFa5854FBf9964330d761961F46565AB7326e5a3b
```

### Step2: Get Token Paymaster Data[​](#step2-get-token-paymaster-data "Direct link to Step2: Get Token Paymaster Data")

Get token paymaster data

```
const { userOperation: tokenOp, tokenQuote } = await paymaster.createPaymasterUserOperation(

    smartAccount,

    userOperation,

    bundlerUrl,

    { token: tokenAddress },

);

userOperation = tokenOp;

console.log(`Max token cost: ${tokenQuote?.tokenCost} (smallest units)`);
```

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

Below is a complete example that demonstrates ERC-20 token gas payments:

Full Working Example

* index.ts
* .env

```
import * as dotenv from 'dotenv'



import {

    SafeMultiChainSigAccountV1 as SafeAccount,

    MetaTransaction,

    Erc7677Paymaster,

    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 tokenAddress = process.env.TOKEN_ADDRESS as string

    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

    )



    // Set up token paymaster

    const paymaster = new Erc7677Paymaster(paymasterRPC)

    

    console.log(`Using ${tokenAddress} for gas payment`);



    // Create token paymaster UserOperation

    const { userOperation: tokenOp, tokenQuote } = await paymaster.createPaymasterUserOperation(

        smartAccount,

        userOperation,

        bundlerUrl,

        { token: tokenAddress },

    );

    userOperation = tokenOp;

    console.log(`Max token cost: ${tokenQuote?.tokenCost} (smallest units)`);



    console.log(`Gas cost: ${tokenQuote?.tokenCost} in token smallest units`);

    console.log(`Make sure account has enough tokens`);



    // Sign the UserOperation

    userOperation.signature = smartAccount.signUserOperation(

        userOperation,

        [ownerPrivateKey],

        chainId

    )



    // Submit the UserOperation

    const sendUserOperationResponse = await smartAccount.sendUserOperation(

        userOperation, 

        bundlerUrl

    )



    console.log("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("Transaction successful! Hash:", userOperationReceiptResult.receipt.transactionHash)

        console.log(`Gas paid with token ${tokenAddress}`);

    } else {

        console.log("UserOperation execution failed")

    }

}



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

```
CHAIN_ID=11155111

BUNDLER_URL=https://api.candide.dev/public/v3/11155111

JSON_RPC_NODE_PROVIDER=https://ethereum-sepolia-rpc.publicnode.com



# Paymaster configuration

PAYMASTER_RPC=https://api.candide.dev/public/v3/11155111

TOKEN_ADDRESS=0xFa5854FBf9964330d761961F46565AB7326e5a3b



# Your EOA credentials

PRIVATE_KEY=your_private_key_here

PUBLIC_ADDRESS=your_public_address_here
```
