Gasless Transactions
The ClawPaymaster enables gasless transactions for agent wallets on Abstract Chain. Agents can execute on-chain operations without needing to hold or manage ETH for gas fees — the paymaster covers it.
Overview
| Property | Value |
|---|---|
| Contract | 0x7BBBBbDaCE3EA19Fe317e620CbD89F1040F2ddAf |
| Default Mode | Free Tier |
| Default Daily Gas Limit | 0.1 ETH |
| Sponsorship Modes | Free Tier, Revenue Funded, Token Paid |
How Paymasters Work on Abstract
Abstract Chain is built on zkSync ERA, where paymasters are a native protocol feature. Unlike Ethereum L1 where account abstraction requires bundlers and entry points, zkSync ERA supports paymasters at the protocol level:
- A transaction includes a paymaster address and paymaster input data
- The sequencer calls the paymaster's validation hooks
- If the paymaster approves, it pays the gas — the user pays nothing
- After execution, the paymaster's post-transaction hook runs for accounting
ClawPaymaster manages the sponsorship logic and accounting — deciding which wallets get gas sponsorship and tracking usage.
Sponsorship Modes
enum SponsorshipMode {
FREE_TIER, // Fully sponsored up to rate limit
TOKEN_PAID, // User pays in ERC-20 token
REVENUE_FUNDED // Funded by protocol revenue
}
Free Tier (Default)
Wallets receive fully sponsored gas up to a configurable daily limit:
- Default daily limit: 0.1 ETH worth of gas
- Rate limiting: Resets at midnight UTC
- Condition: Paymaster must have sufficient ETH balance
This is ideal for onboarding new agents and low-volume operations.
Revenue Funded
For revenue-generating agents that contribute to the protocol, higher (or unlimited) gas limits:
- No daily gas cap enforcement
- Only requires paymaster to have sufficient balance
- Set per-wallet via
setWalletConfig()
Token Paid
Agents pay for gas using ERC-20 tokens instead of ETH. The paymaster logic validates the token payment — the actual gas is still paid in ETH by the paymaster contract.
Sponsorship Validation Flow
Agent submits transaction
│
▼
ClawPaymaster.validateSponsorship(wallet, gasAmount)
│
├── Determine mode (wallet-specific or default)
│
├── FREE_TIER:
│ ├── Check daily limit: usedToday + gasAmount <= dailyLimit?
│ ├── Check balance: paymaster.balance >= gasAmount?
│ └── Return true/false
│
├── REVENUE_FUNDED:
│ ├── Check balance: paymaster.balance >= gasAmount?
│ └── Return true
│
└── TOKEN_PAID:
└── Return true (caller handles token payment)
After execution:
ClawPaymaster.recordSponsorship(wallet, gasUsed)
│
├── Reset daily counter if new day
├── Add gasUsed to dailyGasUsed
├── Add gasUsed to totalSponsored
└── Emit WalletSponsored event
Configuration
Per-Wallet Configuration
The paymaster owner can set custom configurations for individual wallets:
function setWalletConfig(
address wallet,
SponsorshipMode mode,
uint256 dailyGasLimit
) external onlyOwner;
// Give a wallet revenue-funded mode with higher limits
await paymaster.setWalletConfig(
walletAddress,
1, // SponsorshipMode.REVENUE_FUNDED
ethers.parseEther('1.0'), // 1 ETH daily limit
);
Wallet Config Structure
struct WalletConfig {
SponsorshipMode mode; // Sponsorship mode for this wallet
uint256 dailyGasLimit; // Custom daily gas limit
uint256 dailyGasUsed; // Gas used today
uint256 lastResetDay; // Day number of last reset
bool approved; // Whether custom config is active
}
Global Settings
// Set default mode for all wallets without custom config
function setDefaultMode(SponsorshipMode mode) external onlyOwner;
// Set default daily gas limit
function setDefaultDailyGasLimit(uint256 limit) external onlyOwner;
Funding the Paymaster
The paymaster needs ETH to sponsor gas. Anyone can fund it:
// Fund the paymaster
await paymaster.fund({ value: ethers.parseEther('1.0') });
// Or just send ETH directly
await wallet.sendTransaction({
to: PAYMASTER_ADDRESS,
value: ethers.parseEther('1.0'),
});
// Check balance
const balance = await paymaster.getBalance();
console.log('Paymaster balance:', ethers.formatEther(balance), 'ETH');
The owner can also withdraw funds:
function defund(address payable to, uint256 amount) external onlyOwner;
Checking Gas Availability
// Check if a specific wallet can be sponsored
const canSponsor = await paymaster.validateSponsorship(
walletAddress,
ethers.parseEther('0.001'), // estimated gas cost
);
console.log('Can sponsor:', canSponsor);
// Check remaining daily gas for a wallet
const remaining = await paymaster.getRemainingDailyGas(walletAddress);
console.log('Remaining daily gas:', ethers.formatEther(remaining), 'ETH');
// Check wallet config
const config = await paymaster.getWalletConfig(walletAddress);
console.log('Mode:', config.mode);
console.log('Daily limit:', ethers.formatEther(config.dailyGasLimit));
console.log('Used today:', ethers.formatEther(config.dailyGasUsed));
Staking PINCH for Gas
In the broader ClawWallet ecosystem, staking $PINCH tokens helps fund paymaster operations:
- Stake PINCH in the
PinchStakingcontract - Staking rewards accrue from protocol fees
- Protocol uses a portion of staking yield to fund the paymaster
- This creates a sustainable gas sponsorship flywheel
See Staking for details on staking tiers and rewards.
Integration Example
Here's how a dApp integrates ClawPaymaster for gasless agent operations:
import { ethers } from 'ethers';
const PAYMASTER = '0x7BBBBbDaCE3EA19Fe317e620CbD89F1040F2ddAf';
const paymasterABI = [
'function validateSponsorship(address wallet, uint256 gasAmount) external view returns (bool)',
'function getRemainingDailyGas(address wallet) external view returns (uint256)',
'function getBalance() external view returns (uint256)',
'function getWalletConfig(address wallet) external view returns (tuple(uint8 mode, uint256 dailyGasLimit, uint256 dailyGasUsed, uint256 lastResetDay, bool approved))',
];
async function checkGasEligibility(walletAddress: string) {
const provider = new ethers.JsonRpcProvider('https://api.mainnet.abs.xyz');
const paymaster = new ethers.Contract(PAYMASTER, paymasterABI, provider);
const remaining = await paymaster.getRemainingDailyGas(walletAddress);
const balance = await paymaster.getBalance();
return {
remainingDailyGas: ethers.formatEther(remaining),
paymasterBalance: ethers.formatEther(balance),
canSponsor: remaining > 0n && balance > 0n,
};
}
Events
| Event | Description |
|---|---|
WalletSponsored(wallet, gasUsed, mode) | Gas was sponsored for a wallet |
PaymasterFunded(funder, amount) | ETH deposited into paymaster |
PaymasterDefunded(to, amount) | ETH withdrawn from paymaster |
WalletConfigUpdated(wallet, mode, limit) | Wallet config changed |
DefaultModeUpdated(mode) | Default sponsorship mode changed |
Statistics
// Total gas sponsored across all wallets (lifetime)
const totalSponsored = await paymaster.totalSponsored();
console.log('Total sponsored:', ethers.formatEther(totalSponsored), 'ETH');