Defined in the Base Account SDK
Most developers should use charge() instead , which handles execution automatically using CDP wallets. This function is for advanced use cases requiring custom transaction handling.
The prepareCharge function prepares the necessary transaction calls to charge a subscription. It returns the array of call data objects to execute the charge through wallet_sendCalls or eth_sendTransaction. This gives you programmatic control over when and how to execute subscription charges.
When to Use This
Use prepareCharge only if you need:
Custom wallet infrastructure (not using CDP)
Manual transaction control
Integration with existing wallet systems
Client-side charging (though this is uncommon)
For standard backend subscription management, use charge() instead.
Parameters
The subscription ID (permission hash) returned from subscribe(). Pattern: ^0x[0-9a-fA-F]{64}$
amount
string | 'max-remaining-charge'
required
Amount to charge (e.g., “10.50”) or ‘max-remaining-charge’ for the full remaining amount in the current period.
Must match the testnet setting used in the original subscribe call. Default: false
Returns
Array of transaction calls to execute the charge. Show PrepareChargeResult properties
The address to call (smart contract address).
The encoded call data for the transaction.
The value to send (always 0x0 for spend permissions).
The returned array contains:
An approval call (if the permission is not yet active)
A spend call to charge the subscription
EOA Owner Wallet
Smart Owner Wallet
import { base } from '@base-org/account' ;
import { createWalletClient , http } from 'viem' ;
import { privateKeyToAccount } from 'viem/accounts' ;
import { base as baseChain } from 'viem/chains' ;
// Initialize wallet client with your subscription owner account
const account = privateKeyToAccount ( '0x...' ); // Your app's private key
const walletClient = createWalletClient ({
account ,
chain: baseChain ,
transport: http ()
});
// Prepare to charge a specific amount
const chargeCalls = await base . subscription . prepareCharge ({
id: '0x71319cd488f8e4f24687711ec5c95d9e0c1bacbf5c1064942937eba4c7cf2984' ,
amount: '9.99' ,
testnet: false
});
// Execute each charge call
const transactionHashes = [];
for ( const call of chargeCalls ) {
const hash = await walletClient . sendTransaction ({
to: call . to ,
data: call . data ,
value: call . value || 0 n
});
transactionHashes . push ( hash );
// Wait for transaction confirmation before next call
await walletClient . waitForTransactionReceipt ({ hash });
}
console . log ( `Charge transactions: ${ transactionHashes . join ( ', ' ) } ` );
Two Calls (Approval + Spend)
Single Call (Spend Only)
[
{
to: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" ,
data: "0x095ea7b3..." ,
value: "0x0"
},
{
to: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" ,
data: "0xa9059cbb..." ,
value: "0x0"
}
]
Error Handling
try {
const chargeCalls = await base . subscription . prepareCharge ({
id: subscriptionId ,
amount: chargeAmount ,
testnet: false
});
// Execute charge
} catch ( error ) {
console . error ( `Failed to prepare charge: ${ error . message } ` );
}
Comparison with charge()
// You manage everything manually
const chargeCalls = await base . subscription . prepareCharge ({
id: subscriptionId ,
amount: 'max-remaining-charge' ,
testnet: false
});
// Initialize wallet client
const walletClient = createWalletClient ({
account: privateKeyToAccount ( '0x...' ),
chain: baseChain ,
transport: http ()
});
// Execute each call sequentially
for ( const call of chargeCalls ) {
const hash = await walletClient . sendTransaction ({
to: call . to ,
data: call . data ,
value: call . value || 0 n
});
await walletClient . waitForTransactionReceipt ({ hash });
}
// Handle gas estimation, nonce management, retries, etc.
Migration to charge()
If you’re currently using prepareCharge in a Node.js backend, consider migrating to charge():
// Old approach - manual execution
const chargeCalls = await base . subscription . prepareCharge ({
id: subscriptionId ,
amount: '9.99' ,
testnet: false
});
const walletClient = createWalletClient ({
account: privateKeyToAccount ( process . env . PRIVATE_KEY ),
chain: baseChain ,
transport: http ()
});
const transactionHashes = [];
for ( const call of chargeCalls ) {
const hash = await walletClient . sendTransaction ({
to: call . to ,
data: call . data ,
value: call . value || 0 n
});
transactionHashes . push ( hash );
await walletClient . waitForTransactionReceipt ({ hash });
}
// New approach - automatic execution with CDP
const result = await base . subscription . charge ({
id: subscriptionId ,
amount: '9.99' ,
testnet: false
});
console . log ( `Charged: ${ result . id } ` );