Payment Tokens

The public v0.7 paymaster accepts USDC, USDâ‚®0, or USDG for reimbursement. USDC is the default; select another token explicitly by symbol, alias, or address.

The EIP-7702 v0.8 deployment currently accepts USDC only. Always use the selected deployment’s tokens list as the authority for enabled tokens.

const { quote } = await paymasterClient.quoteUserOperation({
  userOperation,
  token: "USDG",
  maxTokenCost: 100_000n,
});

The Viem adapter accepts the same selection through paymasterContext:

paymasterContext: {
  token: "USDT0", // Alias for the onchain USDâ‚®0 symbol
  maxTokenCost: 100_000n,
}

maxTokenCost and allowances are denominated in the selected token’s smallest unit. All three current tokens use six decimals. Read decimals from deployment metadata rather than assuming that future tokens will do the same.

Canonical Ink addresses

SymbolAddressDecimals
USDC0x2D270e6886d130D724215A266106e6832161EAEd6
USDâ‚®00x0200C29006150606B650577BBE7B6248F58470c16
USDG0xe343167631d89B6Ffc58B88d6b7fB0228795491D6

Token presence in INK_PAYMENT_TOKENS identifies its canonical address. The active deployment’s tokens list remains the authority for what the live paymaster has enabled.

Approvals are per token

Each token has a separate ERC-20 allowance. Before requesting a quote, the smart account must execute approve(paymaster, finiteAllowance) on the token it will use for reimbursement.

import {
  encodePublicPaymasterApproval,
  getPublicPaymasterAccountState,
  INK_PAYMENT_TOKENS,
  INK_PUBLIC_PAYMASTER,
} from "@hellomoon/ink-paymaster";
 
const state = await getPublicPaymasterAccountState(
  publicClient,
  smartAccount.address,
  INK_PUBLIC_PAYMASTER,
  "USDG",
);
 
const approvalCall = {
  to: INK_PAYMENT_TOKENS.USDG.address,
  value: 0n,
  data: encodePublicPaymasterApproval(10_000_000n),
};

The public paymaster does not pay for this approval. Approve from the smart account using an owner-paid UserOperation.

Charging and refunds

The paymaster precharges a bounded amount during validation. After execution, it records the final reimbursement as treasury accrual and makes unused precharge available as pull-based refund credit owned by the smart account.

Read refundCredit with getPublicPaymasterAccountState. Applications may claim it through the smart account using encodePublicPaymasterRefundClaim. Refunds and allowances are independent for each token.

import {
  encodePublicPaymasterRefundClaim,
  INK_PUBLIC_PAYMASTER,
} from "@hellomoon/ink-paymaster";
 
const claim = encodePublicPaymasterRefundClaim(recipient);
const encodedCall = await smartAccount.encodeCalls([
  {
    to: INK_PUBLIC_PAYMASTER.paymaster,
    value: 0n,
    data: claim,
  },
]);

The smart account that owns the credit must execute the claim. The recipient may be a different address.

Made with đź’ś by the Ink team