Paymaster
The CandidePaymaster class interacts with Candide's ERC-4337 Paymaster API.
Supports:
- Gas sponsorship through Gas Policies
- ERC-20 Token Sponsorship
- Multi-EntryPoint
Usage
Import
import { CandidePaymaster } from "abstractionkit";
How to Use
Initialize a Paymaster with your RPC URL. Get an API key from the dashboard.
import { CandidePaymaster } from "abstractionkit";
const paymasterRPC = "https://api.candide.dev/public/v3/sepolia";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
Then consume Paymaster methods:
const supportedEntryPoints = await paymaster.getSupportedEntrypoints();
Methods
createSponsorPaymasterUserOperation
Returns the paymaster data if the the userOperation has a Gas Policy. Otherwise it returns an error message. Supports two types of Gas Policies:
- Public Gas Policies: These are gas policies provided by third parties, which do not require a sponsorship policy ID.
- Private Gas Policies: These require a sponsorship policy ID and can be used if no public gas policy matches the user operation.
Usage
- example.ts
- Param Type
- Return Type
import { CandidePaymaster } from "abstractionkit";
const paymasterRPC = "https://api.candide.dev/public/v3/sepolia";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const sponsorshipPolicyId = '1234';
// Use createUserOperation() to help you construct a userOp
const userOperation = smartAccount.createUserOperation(..)
const { userOperation: sponsoredUserOperation, sponsorMetadata } =
await paymaster.createSponsorPaymasterUserOperation(
smartAccount,
userOperation,
bundlerUrl,
sponsorshipPolicyId, // optional
);
| key | type | description | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
smartAccount | SmartAccountWithEntrypoint | The smart account instance. Used to resolve the EntryPoint address. | ||||||||||||
userOperation | UserOperationV6 | UserOperationV7 | UserOperationV8 | UserOperationV9 | UserOperation to Sponsor. Supports EntryPoint v0.6, v0.7, v0.8, and v0.9 | ||||||||||||
bundlerRpc | string | Bundler URL to estimate the gas | ||||||||||||
sponsorshipPolicyId | string? | Optional sponsorship policy id if using a Private Gas Policy | ||||||||||||
context | | Optional additional context to pass to the paymaster RPC (token, signingPhase). | ||||||||||||
overrides | GasPaymasterUserOperationOverrides? | Optional overrides for gas limits, multipliers, entrypoint, and state overrides used during gas estimation. |
| key | type | description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
userOperation | UserOperationV6 | UserOperationV7 | UserOperationV8 | UserOperationV9 | UserOperation with paymaster data included | |||||||||||||||
sponsorMetadata | | Information about the Gas Policy Sponsor |
Source code
createSponsorPaymasterUserOperation
fetchSupportedERC20TokensAndPaymasterMetadata
Returns a promise with the supported erc20 tokens and their exchange rate, along with the paymaster metadata
Usage
- example.ts
- Param Types
- Response Type
import { CandidePaymaster, SafeAccountV0_3_0 as SafeAccount } from "abstractionkit";
const paymasterRPC="https://api.candide.dev/public/v3/sepolia";
const paymaster = new CandidePaymaster(paymasterRPC);
const supportedERC20Tokens = await paymaster.fetchSupportedERC20TokensAndPaymasterMetadata(SafeAccount.DEFAULT_ENTRYPOINT_ADDRESS);
| key | type | description |
|---|---|---|
entrypoint | string? | EntryPoint Address. Defaults to V0.7 |
| key | type | description |
|---|---|---|
SupportedERC20TokensAndMetadataWithExchangeRate | Promise<SupportedERC20TokensAndMetadataV7WithExchangeRate | SupportedERC20TokensAndMetadataV6WithExchangeRate> | A promise with the list of supported erc20 tokens and their exchange rate |
SupportedERC20TokensAndMetadataV7WithExchangeRate
| key | type | description | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
paymasterMetadata | | The Paymaster metadata | ||||||||||||||||||||||||||||||||||||
tokens | | Supported erc20 tokens with Exchange Rate |
SupportedERC20TokensAndMetadataV6WithExchangeRate
| key | type | description | |||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
paymasterMetadata | | The Paymaster metadata | |||||||||||||||||||||
tokens | | Supported erc20 tokens with Exchange Rate |
Example Response
{
tokens: [
{
name: 'USD Coin',
symbol: 'USDC',
address: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831',
decimals: 6,
exchangeRate: 123n
},
{
name: 'Tether USDT',
symbol: 'USDT',
address: '0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9',
decimals: 6,
exchangeRate: 123n
},
{
name: 'Dai Stablecoin',
symbol: 'DAI',
address: '0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1',
decimals: 18,
exchangeRate: 123n
}
],
paymasterMetadata: {
name: 'CANDIDE Paymaster',
description: 'CANDIDE Paymaster a fast, secure and feature-rich 4337 Paymaster',
icons: [],
address: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
sponsoredEventTopic: '0xa050a122b4c0e369e3385eb6b7cccd8019638b2764de67bec0af99130ddf8471',
dummyPaymasterAndData: {
paymaster: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
paymasterVerificationGasLimit: '0xffff',
paymasterPostOpGasLimit: '0xffff',
paymasterData: '0x00010000000000ffff000000000000000000000000000000000000000000000000000000000000ffff010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101011c'
}
}
}
createTokenPaymasterUserOperation
Estimates gas limits and returns the user operation with the paymaster data for ERC-20 Token sponsorship.
Some tokens like USDT do not allow changing a non-zero allowance directly. For known tokens (e.g. USDT on Ethereum mainnet), the SDK automatically prepends an approve(0) call before the actual approval. You can also manually trigger this behavior for other tokens by setting resetApproval: true in the overrides.
The overrides parameter also accepts an entrypoint field, which allows you to manually specify the EntryPoint address instead of relying on auto-detection from the UserOperation structure.
Usage
- example.ts
- Param Types
- Response Type
import { SafeAccountV0_3_0 as SafeAccount, CandidePaymaster } from "abstractionkit";
const paymasterRPC="https://api.candide.dev/public/v3/sepolia";
const erc20TokenAddress = "0xFa5854FBf9964330d761961F46565AB7326e5a3b"; // CTT test token
const bundlerRPC = "https://api.candide.dev/public/v3/sepolia";
const ownerPublicAddress = "0x2Ef844456580b6e1E22e1D584EBbC2467D9298B2"
const smartAccount = SafeAccount.initializeNewAccount([ownerPublicAddress])
// Use createUserOperation() to help you construct a userOp
let userOperation = smartAccount.createUserOperation(..);
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const { userOperation: sponsoredUserOperation, tokenQuote } =
await paymaster.createTokenPaymasterUserOperation(
smartAccount,
userOperation,
erc20TokenAddress,
bundlerRPC,
);
userOperation = sponsoredUserOperation;
| key | type | description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
smartAccount | PrependTokenPaymasterApproveAccount | The smart account object that created the target userOperation. Must implement prependTokenPaymasterApproveToCallData so the paymaster token approval can be prepended to the callData. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
userOperation | UserOperationV6 | UserOperationV7 | UserOperationV8 | UserOperationV9 | The userOperation to sponsor gas with erc-20 paymaster | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
tokenAddress | string | The address of the token to approve. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
bundlerRpc | string | The Bundler RPC to estimate the gas | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
context | | Optional additional context to pass to the paymaster RPC. `token` is always derived from `tokenAddress`, so pass `undefined` unless you need other fields (e.g. `signingPhase`). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
overrides | | Overrides for gas limits, multipliers, entrypoint, and the approval reset behavior. |
| key | type | description | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
userOperation | UserOperationV6 | UserOperationV7 | UserOperationV8 | UserOperationV9 | The userOperation with paymaster data and token approval prepended | ||||||||||||
tokenQuote | | The exchange rate and maximum token cost the paymaster applied to this UserOperation. Populated on the token-payment flow; absent when called under the `signingPhase: "finalize"` path (no cost recomputation). |
Example Response
{
userOperation: {
sender: '0xb8741a449d50ed0dcfe395287f85be152884c8d9',
nonce: 10n,
initCode: '0x',
callData: '0x541d63c8...095ea7b3...',
callGasLimit: 116807n,
verificationGasLimit: 75441n,
preVerificationGas: 50444n,
maxFeePerGas: 66195658616n,
maxPriorityFeePerGas: 120000n,
paymasterAndData: '0x3fE285DcD76BCcE4Ac92d38A6F2F8E964041e020...',
signature: '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
},
tokenQuote: {
token: '0xFa5854FBf9964330d761961F46565AB7326e5a3b',
exchangeRate: 1001219705870085130n,
tokenCost: 3391931975665260n,
},
}
Source code
createTokenPaymasterUserOperation
calculateUserOperationErc20TokenMaxGasCost
Calculates the maximum gas cost in ERC-20 tokens for a given userOperation
Usage
- example.ts
- Param Types
- Return Types
import { CandidePaymaster } from "abstractionkit";
const erc20TokenAddress = "0xFa5854FBf9964330d761961F46565AB7326e5a3b"; // CTT test token
// Use createUserOperation() to help you construct a userOp
const userOperation = smartAccount.createUserOperation(..)
const paymasterRPC="https://api.candide.dev/public/v3/sepolia";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const cost = await paymaster.calculateUserOperationErc20TokenMaxGasCost(
userOperation,
erc20TokenAddress,
);
| key | type | description |
|---|---|---|
userOperation | UserOperationV6 | UserOperationV7 | UserOperationV8 | UserOperationV9 | UserOperation to calculate cost for. Supports EntryPoint v0.6, v0.7, v0.8, and v0.9 |
erc20TokenAddress | string | ERC-20 Token Address |
| key | type | description |
|---|---|---|
cost | Promise<bigint> | Returns maximum amount that the operation will cost in the erc-20 token |
Example Response
3391931975665260
Source code
calculateUserOperationErc20TokenMaxGasCost
Advanced Methods
getPaymasterMetaData
Returns the metadata associated with the Paymaster, along with dummyPaymasterAndData useful for gas estimates
Usage
- example.ts
- Param Type
- Return Type
import { CandidePaymaster, SafeAccountV0_3_0 as SafeAccount } from "abstractionkit";
const paymasterRPC="https://api.candide.dev/public/v3/sepolia";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const paymasterResult = await paymaster.getPaymasterMetaData(SafeAccount.DEFAULT_ENTRYPOINT_ADDRESS);
| key | type | description |
|---|---|---|
entrypoint | string | Target EntryPoint Contract Address |
| key | type | description |
|---|---|---|
paymastermetadata | Promise<PaymasterMetadataV7 | PaymasterMetadataV6 | null> | Returns a promise with the paymaster metadata associated with the target entrypoint |
PaymasterMetadataV7
| key | type | description | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
name | string | The name of the Paymaster. | |||||||||||||||
description | string | A brief description of the Paymaster. | |||||||||||||||
icons | string[] | An array of icon URLs representing the Paymaster's brand or logo. | |||||||||||||||
address | string | The contract address of the Paymaster. | |||||||||||||||
sponsoredEventTopic | string | The event topic that will be emitted when a UserOperation is sponsored by the Paymaster. | |||||||||||||||
dummyPaymasterAndData | | Dummy Paymaster data used for gas estimation. |
PaymasterMetadataV6
| key | type | description |
|---|---|---|
name | string | The name of the Paymaster. |
description | string | A brief description of the Paymaster. |
icons | string[] | An array of icon URLs representing the Paymaster's brand or logo. |
address | string | The contract address of the Paymaster. |
sponsoredEventTopic | string | The event topic that will be emitted when a UserOperation is sponsored by the Paymaster. |
dummyPaymasterAndData | string | Dummy Paymaster data used for gas estimation. |
Example Response
{
name: 'CANDIDE Paymaster',
description: 'CANDIDE Paymaster a fast, secure and feature-rich 4337 Paymaster',
icons: [],
address: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
sponsoredEventTopic: '0xa050a122b4c0e369e3385eb6b7cccd8019638b2764de67bec0af99130ddf8471',
dummyPaymasterAndData: {
paymaster: '0x8b1f6cb5d062aa2ce8d581942bbb960420d875ba',
paymasterVerificationGasLimit: '0xffff',
paymasterPostOpGasLimit: '0xffff',
paymasterData: '0x00010000000000ffff000000000000000000000000000000000000000000000000000000000000ffff010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101011c'
}
}
source code
getSupportedEntrypoints
Returns the supported Entrypoints by the paymaster
Usage
- example.ts
- Return Type
import { CandidePaymaster } from "abstractionkit";
const paymasterRPC="https://api.candide.dev/public/v3/sepolia";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const paymasterResult = await paymaster.getSupportedEntrypoints();
| key | type | description |
|---|---|---|
entrypoint addresses | Promise<string[]> | A promise of a list of entrypoints addresses |
Example Response
[
'0x0000000071727De22E5E9d8BAf0edAc6f37da032',
'0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789'
]
source code
isSupportedERC20Token
Checks if a particular ERC-20 token is accepted as gas payment by the paymaster. Returns a boolean
Usage
- example.ts
- Param Types
- Response Type
import { CandidePaymaster } from "abstractionkit";
const paymasterRPC="https://api.candide.dev/public/v3/sepolia";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const erc20TokenAddress = "0xFa5854FBf9964330d761961F46565AB7326e5a3b"; // CTT on sepolia testnet
const isSupported = await paymaster.isSupportedERC20Token(erc20TokenAddress);
| key | type | description |
|---|---|---|
erc20TokenAddress | string | ERC-20 Token Address |
entrypoint? | string | Target EntryPoint Address. Defaults to ENTRYPOINT_V7 |
| key | type | description |
|---|---|---|
isSupported | boolean | Returns true if the erc20 token is supported |
Example Response
true
Source
getSupportedERC20TokenData
Returns the token data given an erc20 address
Usage
- example.ts
- Param Types
- Return Type
import { CandidePaymaster } from "abstractionkit";
const paymasterRPC = "https://api.candide.dev/public/v3/sepolia";
const erc20TokenAddress = "0xFa5854FBf9964330d761961F46565AB7326e5a3b";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const erc20TokenData = await paymaster.getSupportedERC20TokenData(erc20TokenAddress);
| key | type | description |
|---|---|---|
erc20TokenAddress | string | ERC-20 Token Address |
entrypoint? | string | Target EntryPoint Address. Defaults to ENTRYPOINT_V7 |
| key | type | description |
|---|---|---|
ERC20Token | Promise<ERC20Token | null> | ERC-20 Token |
| key | type | description |
|---|---|---|
name | string | The name of the ERC20 token. |
symbol | string | The symbol representing the ERC20 token. |
address | string | The contract address of the ERC20 token. |
decimals | number | The number of decimal places for the token. |
Example Response
{
name: 'Candide Test Token',
symbol: 'CTT',
address: '0xFa5854FBf9964330d761961F46565AB7326e5a3b',
decimals: 18,
fee: 0n,
exchangeRate: 1001219705870085130n
}
Source code
fetchTokenPaymasterExchangeRate
Fetches the current exchange rate for an ERC-20 token from the paymaster. The exchange rate represents the token cost per unit of native currency, used to calculate how much of the token is needed to cover gas fees.
Usage
- example.ts
- Param Types
- Return Type
import { CandidePaymaster } from "abstractionkit";
const paymasterRPC = "https://api.candide.dev/public/v3/sepolia";
const paymaster: CandidePaymaster = new CandidePaymaster(paymasterRPC);
const erc20TokenAddress = "0xFa5854FBf9964330d761961F46565AB7326e5a3b"; // CTT on sepolia testnet
const exchangeRate = await paymaster.fetchTokenPaymasterExchangeRate(erc20TokenAddress);
| key | type | description |
|---|---|---|
erc20TokenAddress | string | ERC-20 Token Address |
entrypoint | string? | EntryPoint Address that the paymaster is supporting. Defaults to ENTRYPOINT_V7 |
| key | type | description |
|---|---|---|
exchangeRate | bigInt | Returns the exchange rate to the native token |
Example Response
1001219705870085130n
Source code
fetchTokenPaymasterExchangeRate
createPaymasterUserOperation
createPaymasterUserOperation is an internal method. Use createSponsorPaymasterUserOperation for gas sponsorship or createTokenPaymasterUserOperation for ERC-20 token payment instead.
Source code
Erc7677Paymaster
Erc7677Paymaster is a provider-agnostic ERC-7677 paymaster client. It speaks pm_getPaymasterStubData and pm_getPaymasterData, so it works with any compliant provider (Candide, Pimlico, Alchemy, or a self-hosted paymaster). Provider is auto-detected from the RPC URL; for Candide and Pimlico it also fetches exchange rates and paymaster addresses automatically for ERC-20 token flows.
If you are using Candide's paymaster and want features like parallel signing phases, prefer CandidePaymaster above. Use Erc7677Paymaster when you want a single client that can switch providers without code changes.
Usage
Import
import { Erc7677Paymaster } from "abstractionkit";
Sponsored UserOperation
import { Erc7677Paymaster } from "abstractionkit";
const bundlerRpc = "https://api.candide.dev/public/v3/sepolia";
const paymaster = new Erc7677Paymaster(bundlerRpc);
// Use createUserOperation() to help you construct a userOp
let userOperation = await smartAccount.createUserOperation(/* ... */);
const { userOperation: sponsoredOp } = await paymaster.createPaymasterUserOperation(
smartAccount,
userOperation,
bundlerRpc,
{ sponsorshipPolicyId: "sp_..." }, // provider-specific context
);
userOperation = sponsoredOp;
ERC-20 token sponsorship
Passing { token } in the context triggers the ERC-20 gas flow automatically. For Candide and Pimlico the exchange rate is fetched for you; for unknown providers, supply exchangeRate in the context. tokenQuote surfaces the exchange rate and max token cost applied to the UserOperation so you can display the charge to the user without a second round trip.
const bundlerRpc = "https://api.candide.dev/public/v3/sepolia";
const usdcAddress = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"; // USDC on sepolia
// Candide or Pimlico (auto-detected): exchange rate fetched from the RPC
const { userOperation: tokenOp, tokenQuote } = await paymaster.createPaymasterUserOperation(
smartAccount,
userOperation,
bundlerRpc,
{ token: usdcAddress },
);
userOperation = tokenOp;
// Unknown provider: supply exchangeRate (scaled by 1e18)
const { userOperation: tokenOp2 } = await paymaster.createPaymasterUserOperation(
smartAccount,
userOperation,
bundlerRpc,
{ token: usdcAddress, exchangeRate: "1000000000000000000" },
);
userOperation = tokenOp2;