Advanced Transactions
Transaction preview, fee estimation, non-custodial flows, and batch patterns
Advanced Transactions Guide
This guide covers advanced transaction features including preview, fee estimation, non-custodial flows, and batch operations.
Transaction Preview
Preview transactions before sending to show users what will happen and estimate costs.
Basic Preview
import { transactionPreview } from 'cilantro-sdk/transactions';
const preview = await transactionPreview('wallet-id', {
type: 'SEND_SOL',
sendSolParams: {
recipientAddress: 'DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK',
amountLamports: 1000000000
}
});
console.log('Estimated fee:', preview.data.estimatedFee);
console.log('Balance before:', preview.data.balanceBefore);
console.log('Balance after:', preview.data.balanceAfter);
console.log('Risk level:', preview.data.riskLevel);
Preview with User Confirmation
async function sendSOLWithPreview(
walletId: string,
recipientAddress: string,
amountLamports: number
) {
// Step 1: Preview transaction
const preview = await transactionPreview(walletId, {
type: 'SEND_SOL',
sendSolParams: {
recipientAddress,
amountLamports
}
});
// Step 2: Show preview to user
const confirmed = await showTransactionPreview({
recipient: recipientAddress,
amount: amountLamports / 1e9, // Convert to SOL
fee: preview.data.estimatedFee,
balanceAfter: preview.data.balanceAfter,
riskLevel: preview.data.riskLevel
});
if (!confirmed) {
throw new Error('Transaction cancelled by user');
}
// Step 3: Send transaction
const result = await sendSOL(walletId, {
recipientAddress,
amountLamports
});
return result.data;
}
Fee Estimation
Get Fee Estimate
import { feeEstimate, networkFeeInfo } from 'cilantro-sdk/transactions';
// Estimate SOL transfer fee
const estimate = await feeEstimate({
type: 'SOL_TRANSFER',
fromAddress: 'wallet-address',
toAddress: 'recipient-address',
amount: 1.0,
includePriority: true
});
console.log('Base fee:', estimate.data.baseFee);
console.log('Priority fee:', estimate.data.priorityFee);
console.log('Total fee:', estimate.data.totalFee);
Network Fee Information
// Get current network fee information
const feeInfo = await networkFeeInfo();
console.log('Base fee:', feeInfo.data.baseFee);
console.log('Network congestion:', feeInfo.data.congestionLevel);
console.log('Recommended priority fee:', feeInfo.data.recommendedPriorityFee);
Dynamic Fee Adjustment
async function sendSOLWithOptimalFee(
walletId: string,
recipientAddress: string,
amountLamports: number
) {
// Get network fee info
const feeInfo = await networkFeeInfo();
// Adjust priority fee based on congestion
let priorityFee = feeInfo.data.recommendedPriorityFee;
if (feeInfo.data.congestionLevel === 'HIGH') {
priorityFee = priorityFee * 1.5; // Increase by 50% for high congestion
}
// Estimate total fee
const estimate = await feeEstimate({
type: 'SOL_TRANSFER',
fromAddress: 'wallet-address',
toAddress: recipientAddress,
amount: amountLamports / 1e9,
includePriority: true
});
console.log('Estimated total fee:', estimate.data.totalFee);
// Send transaction with estimated fee
const result = await sendSOL(walletId, {
recipientAddress,
amountLamports
});
return result.data;
}
Non-Custodial Transaction Flows
Complete Non-Custodial Flow
import {
prepareTransaction,
submitTransaction
} from 'cilantro-sdk/wallet';
import {
getEmailSignerKeypair,
createLocalStorageAdapter
} from 'cilantro-sdk/helpers';
import { Transaction } from '@solana/web3.js';
import { sign as ed25519Sign } from '@noble/ed25519';
async function sendSOLNonCustodial(
walletId: string,
signerId: string,
recipientAddress: string,
amountLamports: number
) {
// Step 1: Get signer keypair
const storage = createLocalStorageAdapter();
const keypair = await getEmailSignerKeypair(walletId, signerId, {
deviceKeyManager: storage
});
// Step 2: Prepare transaction
const prepared = await prepareTransaction(walletId, {
type: 'SEND_SOL',
signerPubkey: Buffer.from(keypair.publicKey).toString('base64'),
sendSolParams: {
recipientAddress,
amountLamports
}
});
// Step 3: Deserialize and sign
const tx = Transaction.from(
Buffer.from(prepared.data.serializedTransaction, 'base64')
);
const message = tx.serializeMessage();
const privateKey = keypair.secretKey.slice(0, 32);
const signature = await ed25519Sign(message, privateKey);
tx.addSignature(
{ toBuffer: () => Buffer.from(keypair.publicKey) } as any,
Buffer.from(signature)
);
const signedTx = tx.serialize().toString('base64');
// Step 4: Submit
const result = await submitTransaction(walletId, {
signedTransaction: signedTx
});
return result.data;
}
Passkey Non-Custodial Flow
import { sendRawPasskeyTransaction } from 'cilantro-sdk/transactions';
async function sendSOLWithPasskey(
walletId: string,
recipientAddress: string,
amountLamports: number
) {
// Step 1: Build transaction
const transaction = new Transaction();
// ... add transfer instruction
// Step 2: Authenticate with passkey
const authResult = await authenticateWithPasskey(walletId);
// Step 3: Sign and send
const result = await sendRawPasskeyTransaction({
walletId,
transaction: transaction.serialize().toString('base64'),
passkeySignature: authResult.signature
});
return result.data;
}
Batch Operations
Batch Send SOL
import { batchSendSOL } from 'cilantro-sdk/wallet';
async function airdropSOL(
walletId: string,
recipients: Array<{ address: string; amount: number }>
) {
const result = await batchSendSOL(walletId, {
recipients: recipients.map(r => ({
to: r.address,
amount: r.amount
}))
});
console.log('Batch transaction signatures:', result.data.signatures);
// Wait for all confirmations
for (const signature of result.data.signatures) {
await waitForConfirmation(signature);
}
return result.data;
}
// Usage
await airdropSOL('wallet-id', [
{ address: 'addr1...', amount: 0.5 },
{ address: 'addr2...', amount: 1.0 },
{ address: 'addr3...', amount: 0.25 }
]);
Batch Send SPL Tokens
import { batchSendSPL } from 'cilantro-sdk/wallet';
async function airdropTokens(
walletId: string,
mintAddress: string,
recipients: Array<{ address: string; amount: number }>
) {
const result = await batchSendSPL(walletId, {
recipients: recipients.map(r => ({
to: r.address,
amount: r.amount,
mintAddress
}))
});
return result.data;
}
Batch Create Wallets
import { batchCreate } from 'cilantro-sdk/wallet';
async function createMultipleWallets(count: number) {
const wallets = Array.from({ length: count }, (_, i) => ({
label: `Wallet ${i + 1}`,
tags: ['batch-created']
}));
const result = await batchCreate({ wallets });
console.log(`Created ${result.data.wallets.length} wallets`);
return result.data;
}
Transaction Export
Export to CSV
import { exportTransactions } from 'cilantro-sdk/transactions';
async function exportToCSV(walletId: string, startDate: string, endDate: string) {
const csv = await exportTransactions(walletId, {
format: 'CSV',
fromDate: startDate,
toDate: endDate
});
// Download CSV file
const blob = new Blob([csv.data], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `transactions-${walletId}.csv`;
a.click();
}
Export to JSON
async function exportToJSON(walletId: string) {
const json = await exportTransactions(walletId, {
format: 'JSON',
status: 'confirmed'
});
// Process JSON data
const transactions = JSON.parse(json.data);
console.log(`Exported ${transactions.length} transactions`);
return transactions;
}
Best Practices
Always Preview
Preview transactions before sending to show users what will happen
Estimate Fees
Estimate fees before sending to ensure sufficient balance
Handle Errors
Implement proper error handling for all transaction operations
Batch Efficiently
Use batch operations for multiple transactions to save on fees