Paymaster Module
The Paymaster module enables gasless transactions in your foc.fun applications by sponsoring transaction fees on behalf of users. This dramatically improves user experience by removing the need for users to hold ETH for gas fees.
Paymaster-Only Mode (v0.2.0+)
Starting with foc-engine v0.2.0, you can run foc.fun in paymaster-only mode using the AVNU Paymaster integration. This lightweight mode is perfect when you only need gasless transaction functionality without the full foc-engine stack.
AVNU Fallback Integration
The paymaster module includes automatic fallback to AVNU's paymaster service when the FOC Engine paymaster is unavailable. By providing your AVNU API key to the frontend app, you can interact with AVNU directly without running the foc-engine paymaster server.
// Frontend configuration with AVNU fallback
const engine = new FocEngine({
network: 'sepolia',
paymaster: {
primary: {
type: 'foc-engine',
endpoint: 'https://api.foc.fun/paymaster/sepolia'
},
fallback: {
type: 'avnu',
apiKey: process.env.AVNU_API_KEY // Your AVNU API key
},
fallbackOnError: true
}
});
// Transactions automatically fall back to AVNU if FOC paymaster fails
const tx = await engine.accounts.invokeWithPaymaster({
contractAddress: '0xtoken...',
entrypoint: 'transfer',
calldata: ['0xrecipient...', '1000']
});
Running Paymaster-Only Mode
To start foc-engine with only the paymaster component:
# Run on devnet (default)
foc-engine run --preset paymaster
# Run on Sepolia testnet
foc-engine run --network sepolia --preset paymaster
# Run on mainnet
foc-engine run --network mainnet --preset paymaster
Key Features of Paymaster-Only Mode
- AVNU Paymaster Integration: Leverages AVNU's production-ready paymaster infrastructure
- Minimal Resource Usage: Only runs necessary paymaster components
- Network Flexibility: Supports devnet, Sepolia testnet, and mainnet
- Quick Setup: No complex configuration required for basic gasless transactions
When to Use Paymaster-Only Mode
This mode is ideal when you:
- Want to add gasless transactions to an existing dApp
- Need a lightweight paymaster solution without full foc.fun features
- Are testing paymaster functionality in isolation
- Want to leverage AVNU's paymaster infrastructure directly
- Need reliable fallback when your paymaster server is down
- Prefer direct AVNU integration without server dependencies
Configuration in Paymaster-Only Mode
When using --preset paymaster
, the system uses optimized defaults for AVNU integration. You can still provide a custom configuration file if needed:
foc-engine run --preset paymaster --config custom-paymaster.yml
Overview
The Paymaster module provides:
- Gasless transaction sponsorship
- Flexible sponsorship policies
- Usage quotas and limits
- Multi-token fee payment support
- Analytics and monitoring
- Policy management APIs
How It Works
- User initiates a transaction without ETH
- Paymaster evaluates the transaction against policies
- If approved, Paymaster sponsors the transaction fee
- Transaction executes with Paymaster paying the fee
- Usage is tracked and policies are enforced
Configuration
Configure the Paymaster module in your foc.config.yml
:
modules:
paymaster:
enabled: true
master_account: ${PAYMASTER_ACCOUNT} # Account that pays fees
master_key: ${PAYMASTER_PRIVATE_KEY} # Private key (secure this!)
policies:
default_policy: "whitelist"
max_fee_per_tx: "1000000000000000" # 0.001 ETH
daily_budget: "1000000000000000000" # 1 ETH per day
storage:
type: postgres
url: ${DATABASE_URL}
monitoring:
enabled: true
webhook_url: ${MONITORING_WEBHOOK}
API Reference
Initialize the Module
import { FocEngine } from 'foc-engine';
const engine = new FocEngine();
const paymaster = engine.getModule('paymaster');
Core Methods
sponsor(transaction)
Sponsor a transaction with automatic AVNU fallback:
// Sponsor a transaction (tries FOC Engine first, falls back to AVNU)
const sponsoredTx = await paymaster.sponsor({
account: '0xuser...',
to: '0xcontract...',
selector: 'transfer',
calldata: ['0xrecipient...', '1000']
});
// Execute the sponsored transaction
const result = await sponsoredTx.execute();
// Force AVNU paymaster usage
const avnuSponsoredTx = await paymaster.sponsor({
account: '0xuser...',
to: '0xcontract...',
selector: 'transfer',
calldata: ['0xrecipient...', '1000'],
paymaster: {
provider: 'avnu',
apiKey: process.env.AVNU_API_KEY
}
});
createPolicy(policy)
Create a sponsorship policy:
// Whitelist policy
const whitelistPolicy = await paymaster.createPolicy({
name: 'vip-users',
type: 'whitelist',
config: {
addresses: ['0xuser1...', '0xuser2...'],
contracts: ['0xtoken...'], // Optional: limit to specific contracts
selectors: ['transfer', 'approve'] // Optional: limit to specific functions
}
});
// Quota policy
const quotaPolicy = await paymaster.createPolicy({
name: 'daily-quota',
type: 'quota',
config: {
per_address: {
daily_transactions: 10,
daily_spend: '100000000000000000' // 0.1 ETH
}
}
});
// Time-based policy
const timePolicy = await paymaster.createPolicy({
name: 'weekend-promo',
type: 'time_based',
config: {
start_time: '2024-01-01T00:00:00Z',
end_time: '2024-01-31T23:59:59Z',
days_of_week: [6, 0], // Saturday and Sunday
hours_of_day: [10, 11, 12, 13, 14, 15, 16, 17, 18] // 10 AM to 6 PM
}
});
applyPolicy(address, policyName)
Apply a policy to an address:
// Apply policy to specific user
await paymaster.applyPolicy('0xuser...', 'vip-users');
// Apply policy to all users
await paymaster.applyPolicy('*', 'daily-quota');
// Apply multiple policies
await paymaster.applyPolicies('0xuser...', [
'vip-users',
'weekend-promo'
]);
getUsage(address, options)
Get usage statistics:
// Get current day usage
const todayUsage = await paymaster.getUsage('0xuser...');
// Get usage for date range
const monthlyUsage = await paymaster.getUsage('0xuser...', {
from: '2024-01-01',
to: '2024-01-31'
});
// Returns
{
address: '0xuser...',
period: { from: '2024-01-01', to: '2024-01-31' },
transactions: 45,
totalSpent: '450000000000000000', // 0.45 ETH
byDay: { /* daily breakdown */ },
byContract: { /* per contract breakdown */ }
}
Policy Types
Whitelist Policy
Allow specific addresses:
await paymaster.createPolicy({
name: 'allowed-users',
type: 'whitelist',
config: {
addresses: ['0x123...', '0x456...'],
// Optional restrictions
contracts: ['0xtoken...'],
selectors: ['transfer'],
max_fee_per_tx: '1000000000000000'
}
});
Quota Policy
Limit usage per address:
await paymaster.createPolicy({
name: 'user-limits',
type: 'quota',
config: {
per_address: {
daily_transactions: 100,
daily_spend: '1000000000000000000', // 1 ETH
weekly_transactions: 500,
weekly_spend: '5000000000000000000', // 5 ETH
monthly_transactions: 1000,
monthly_spend: '10000000000000000000' // 10 ETH
},
global: {
daily_spend: '100000000000000000000' // 100 ETH total
}
}
});
Token-Based Policy
Accept alternative tokens as payment:
await paymaster.createPolicy({
name: 'token-payment',
type: 'token_payment',
config: {
accepted_tokens: {
'USDC': {
address: '0xusdc...',
rate: 3000, // 1 ETH = 3000 USDC
max_slippage: 2 // 2% slippage allowed
},
'STRK': {
address: '0xstrk...',
rate: 5000,
max_slippage: 5
}
},
oracle: '0xoracle...' // Optional: use oracle for rates
}
});
Contract-Specific Policy
Sponsor only specific contracts:
await paymaster.createPolicy({
name: 'game-contracts',
type: 'contract_specific',
config: {
contracts: {
'0xgame...': {
selectors: ['play', 'claim_reward'],
max_fee: '500000000000000' // 0.0005 ETH per tx
},
'0xnft...': {
selectors: ['mint'],
max_fee: '2000000000000000', // 0.002 ETH per tx
per_address_daily_limit: 5
}
}
}
});
Composite Policy
Combine multiple conditions:
await paymaster.createPolicy({
name: 'vip-weekend',
type: 'composite',
config: {
all_of: [ // All conditions must be met
{ policy: 'vip-users' },
{ policy: 'weekend-promo' }
],
any_of: [ // At least one must be met
{ policy: 'token-holder' },
{ policy: 'nft-owner' }
]
}
});
AVNU Direct Integration
For applications that prefer direct AVNU integration:
// Configure paymaster to use AVNU only
const engine = new FocEngine({
network: 'sepolia',
paymaster: {
type: 'avnu-only',
apiKey: process.env.AVNU_API_KEY,
endpoint: 'https://paymaster.avnu.fi'
}
});
// All transactions will use AVNU paymaster
const tx = await paymaster.sponsor({
account: '0xuser...',
to: '0xcontract...',
selector: 'transfer',
calldata: ['0xrecipient...', '1000']
});
Fallback Configuration Options
// Advanced fallback configuration
const paymaster = engine.getModule('paymaster', {
fallback: {
enabled: true,
provider: 'avnu',
apiKey: process.env.AVNU_API_KEY,
// Fallback triggers
triggers: {
timeout: 10000, // 10 second timeout
retries: 3, // Try FOC 3 times before fallback
errorCodes: ['503', '504', 'TIMEOUT']
},
// AVNU-specific options
avnuOptions: {
maxFeePercentage: 10, // Max 10% of tx value as fee
validityPeriod: 3600 // 1 hour validity
}
}
});
Advanced Features
Dynamic Policies
Create policies that adapt based on conditions:
// Volume-based discounts
await paymaster.createDynamicPolicy({
name: 'volume-discount',
type: 'dynamic',
evaluate: async (context) => {
const usage = await paymaster.getUsage(context.address, {
from: context.startOfMonth
});
// More usage = higher sponsorship
if (usage.transactions > 100) {
return { sponsor: true, feeMultiplier: 1.0 }; // 100% sponsored
} else if (usage.transactions > 50) {
return { sponsor: true, feeMultiplier: 0.5 }; // 50% sponsored
} else {
return { sponsor: true, feeMultiplier: 0.1 }; // 10% sponsored
}
}
});
Policy Templates
Use pre-built policy templates:
// Gaming policy template
await paymaster.useTemplate('gaming', {
game_contract: '0xgame...',
daily_plays: 10,
reward_claims: 3,
nft_mints: 1
});
// DeFi policy template
await paymaster.useTemplate('defi', {
dex_contract: '0xdex...',
daily_swaps: 5,
max_swap_fee: '2000000000000000',
liquidity_ops: 2
});
Monitoring and Analytics
Track Paymaster performance:
// Get analytics
const analytics = await paymaster.getAnalytics({
period: 'last_30_days',
groupBy: 'day'
});
// Set up alerts
await paymaster.createAlert({
name: 'high-usage',
condition: {
metric: 'daily_spend',
threshold: '10000000000000000000', // 10 ETH
operator: 'greater_than'
},
actions: ['email', 'webhook']
});
// Export data
const report = await paymaster.exportUsageReport({
format: 'csv',
period: 'last_month',
includeDetails: true
});
Fee Strategies
Implement different fee payment strategies:
// Partial sponsorship
await paymaster.setFeeStrategy({
type: 'partial',
config: {
sponsor_percentage: 80, // Sponsor 80% of fee
min_user_balance: '100000000000000' // User needs 0.0001 ETH
}
});
// Tiered sponsorship
await paymaster.setFeeStrategy({
type: 'tiered',
config: {
tiers: [
{ min_transactions: 0, sponsor_percentage: 50 },
{ min_transactions: 10, sponsor_percentage: 75 },
{ min_transactions: 50, sponsor_percentage: 90 },
{ min_transactions: 100, sponsor_percentage: 100 }
]
}
});
Events
Subscribe to Paymaster events:
// Transaction sponsored
paymaster.on('transactionSponsored', (event) => {
console.log('Sponsored:', {
user: event.address,
fee: event.fee,
policy: event.policy
});
});
// Policy violated
paymaster.on('policyViolation', (event) => {
console.log('Violation:', {
user: event.address,
policy: event.policy,
reason: event.reason
});
});
// Budget alert
paymaster.on('budgetAlert', (event) => {
console.log('Budget alert:', {
spent: event.spent,
budget: event.budget,
percentage: event.percentage
});
});
CLI Commands
# Create policy
foc-engine paymaster create-policy \
--name "vip-users" \
--type whitelist \
--addresses 0x123,0x456
# Apply policy
foc-engine paymaster apply-policy \
--address 0x789 \
--policy vip-users
# Check usage
foc-engine paymaster usage 0x789 --period today
# List policies
foc-engine paymaster list-policies
# Monitor spending
foc-engine paymaster monitor --real-time
# Export report
foc-engine paymaster export \
--period last_month \
--format csv \
--output report.csv
Security Considerations
1. Private Key Management
Never expose the Paymaster private key:
// Bad
const paymaster = {
private_key: '0x1234...' // Never hardcode!
};
// Good
const paymaster = {
private_key: process.env.PAYMASTER_PRIVATE_KEY
};
2. Policy Validation
Always validate policies:
// Implement policy limits
await paymaster.createPolicy({
name: 'safe-policy',
type: 'quota',
config: {
per_address: {
daily_spend: '1000000000000000000', // Max 1 ETH
max_fee_per_tx: '10000000000000000' // Max 0.01 ETH per tx
},
global: {
daily_spend: '10000000000000000000', // Max 10 ETH total
emergency_stop: true // Can pause if needed
}
}
});
3. Monitoring
Set up comprehensive monitoring:
// Monitor for abuse
await paymaster.enableMonitoring({
abnormal_usage_detection: true,
rate_limit_per_address: {
transactions_per_minute: 10,
transactions_per_hour: 100
},
alert_channels: ['email', 'slack', 'pagerduty']
});
Best Practices
1. Start Conservative
Begin with restrictive policies:
// Initial launch policy
await paymaster.createPolicy({
name: 'beta-users',
type: 'composite',
config: {
all_of: [
{ type: 'whitelist', addresses: betaUsers },
{ type: 'quota', daily_transactions: 5 },
{ type: 'contract_specific', contracts: [mainContract] }
]
}
});
2. Regular Reviews
Regularly review usage:
// Weekly review automation
const review = await paymaster.weeklyReview();
if (review.anomalies.length > 0) {
await paymaster.pausePolicy(review.anomalies[0].policy);
}
3. Emergency Controls
Implement emergency stops:
// Emergency pause
await paymaster.emergencyPause();
// Gradual resume
await paymaster.resume({
gradual: true,
initial_percentage: 10,
increase_daily: 10
});
Integration Examples
Gaming Integration
// Game-specific Paymaster setup
const gamePaymaster = await paymaster.createGameIntegration({
contract: '0xgame...',
policies: {
new_players: {
free_plays: 10,
tutorial_completion_bonus: 5
},
active_players: {
daily_plays: 5,
achievement_rewards: true
},
vip_players: {
unlimited_plays: true,
premium_features: true
}
}
});
DeFi Integration
// DeFi protocol Paymaster
const defiPaymaster = await paymaster.createDefiIntegration({
protocol: '0xdefi...',
policies: {
swaps: {
small_trades: { max_value: '100', fully_sponsored: true },
medium_trades: { max_value: '1000', sponsor_percentage: 50 },
large_trades: { max_value: 'unlimited', sponsor_percentage: 10 }
},
liquidity: {
add_liquidity: { sponsor_percentage: 100 },
remove_liquidity: { sponsor_percentage: 0 }
}
}
});
Troubleshooting
Transaction Not Sponsored
try {
await paymaster.sponsor(transaction);
} catch (error) {
switch (error.code) {
case 'POLICY_NOT_MET':
console.log('User does not meet policy requirements');
break;
case 'QUOTA_EXCEEDED':
console.log('User has exceeded their quota');
break;
case 'INSUFFICIENT_PAYMASTER_BALANCE':
console.log('Paymaster needs funding');
break;
}
}
Performance Issues
// Enable caching for better performance
const paymaster = engine.getModule('paymaster', {
cache: {
enabled: true,
policy_ttl: 3600, // 1 hour
usage_ttl: 300, // 5 minutes
redis_url: 'redis://localhost:6379'
}
});
Related Code & Resources
Source Code
- Paymaster Module Implementation: foc-engine/modules/paymaster
- JavaScript SDK Paymaster: foc-engine.js/src/modules/paymaster
- Paymaster Core Logic: foc-engine/core/paymaster
Smart Contracts
- Paymaster Contracts: foc-engine/contracts/paymaster
- Universal Paymaster: foc-engine/contracts/paymaster/universal
- Policy Contracts: foc-engine/contracts/paymaster/policies
Examples
- Paymaster Usage Examples: foc-engine/examples/paymaster
- Gaming Paymaster Integration: foc-engine/examples/gaming-paymaster
- DeFi Paymaster Templates: foc-engine/examples/defi-paymaster
Configuration
- Policy Templates: foc-engine/config/paymaster-policies
- Deployment Scripts: foc-engine/scripts/paymaster
Tests
- Paymaster Module Tests: foc-engine/tests/modules/paymaster
- Policy Engine Tests: foc-engine/tests/paymaster
Next Steps
- Explore Events Module for transaction monitoring
- Learn about Registry Module for contract management
- Check Accounts Module for account integration