This section covers advanced features of the Flashnet AMM SDK including fund recovery mechanisms, background monitoring, and manual clawback operations.
Automatic Fund Recovery
Good news: The SDK automatically handles fund recovery for most failures. When operations like swaps or liquidity additions fail, the SDK attempts to clawback your funds before throwing an error.
How Auto-Clawback Works
When you execute an operation that sends funds to a pool (swap, route swap, add liquidity, or confirm deposit), the SDK:
- Transfers your funds to the pool
- Executes the operation (signing intents, calling the API)
- If the operation fails, checks if the error requires clawback
- Automatically claws back your funds if needed
- Returns a typed error with recovery status
import { isFlashnetError } from '@flashnet/sdk';
try {
await client.executeSwap({
poolId: "pool-id",
assetInAddress: "btc-address",
assetOutAddress: "usdb-address",
amountIn: "100000000",
minAmountOut: "99000000",
maxSlippageBps: 100,
});
} catch (error) {
if (isFlashnetError(error)) {
// Check auto-clawback results
if (error.wasClawbackAttempted()) {
const summary = error.clawbackSummary!;
console.log(`Recovery: ${summary.successCount}/${summary.totalTransfers} transfers`);
if (error.wereAllTransfersRecovered()) {
console.log('✅ All funds recovered!');
} else {
console.log('⚠️ Some funds may need manual recovery:', summary.unrecoveredTransferIds);
}
}
// Business errors (slippage, liquidity) auto-refund without clawback
if (error.willAutoRefund()) {
console.log('Funds will be returned automatically');
}
}
}
Operations with Auto-Clawback
| Operation | Transfer(s) Protected |
|---|
executeSwap | Input asset transfer |
executeRouteSwap | Initial asset transfer to first pool |
addLiquidity | Both asset A and asset B transfers |
createSingleSidedPool | Initial reserve transfer (during confirm) |
Clawback Monitor
For applications that need continuous fund recovery, the SDK provides a background monitor that automatically recovers any stuck transfers.
Starting the Monitor
const monitor = client.startClawbackMonitor({
intervalMs: 60000, // Poll every 60 seconds
batchSize: 2, // Process 2 clawbacks per batch (rate limit safe)
batchDelayMs: 500, // 500ms between batches
maxTransfersPerPoll: 100,
onClawbackSuccess: (result) => {
console.log(`✅ Recovered: ${result.transferId}`);
},
onClawbackError: (transferId, error) => {
console.error(`❌ Failed to recover ${transferId}:`, error);
},
onPollComplete: (result) => {
if (result.transfersFound > 0) {
console.log(`Poll: ${result.clawbacksSucceeded}/${result.transfersFound} recovered`);
}
},
onPollError: (error) => {
console.error('Poll failed:', error);
},
});
Monitor Options
| Option | Type | Default | Description |
|---|
intervalMs | number | 60000 | Polling interval in milliseconds |
batchSize | number | 2 | Clawbacks per batch (for rate limiting) |
batchDelayMs | number | 500 | Delay between batches |
maxTransfersPerPoll | number | 100 | Max transfers to fetch per poll |
onClawbackSuccess | function | - | Called on successful clawback |
onClawbackError | function | - | Called on failed clawback |
onPollComplete | function | - | Called after each poll cycle |
onPollError | function | - | Called if polling itself fails |
Controlling the Monitor
// Check if running
console.log('Running:', monitor.isRunning());
// Trigger immediate poll
const result = await monitor.pollNow();
console.log('Found:', result.transfersFound);
console.log('Recovered:', result.clawbacksSucceeded);
// Stop gracefully (waits for current poll to complete)
await monitor.stop();
Poll Result Structure
interface ClawbackPollResult {
transfersFound: number; // Clawbackable transfers found
clawbacksAttempted: number; // Clawback attempts made
clawbacksSucceeded: number; // Successful recoveries
clawbacksFailed: number; // Failed attempts
results: ClawbackAttemptResult[]; // Detailed results
}
Manual Clawback
For cases where you need manual control over fund recovery.
Check Clawback Eligibility
Before attempting clawback, verify a transfer is eligible:
const eligibility = await client.checkClawbackEligibility({
sparkTransferId: "transfer-id-here"
});
if (eligibility.accepted) {
console.log('Transfer is eligible for clawback');
} else {
console.log('Cannot clawback:', eligibility.error);
}
List Clawbackable Transfers
Get all your transfers that can be clawed back:
const response = await client.listClawbackableTransfers({
limit: 100,
offset: 0
});
for (const transfer of response.transfers) {
console.log(`Transfer: ${transfer.id}`);
console.log(` Pool: ${transfer.lpIdentityPublicKey}`);
console.log(` Created: ${transfer.createdAt}`);
}
Execute Single Clawback
Recover a specific stuck transfer:
async function recoverTransfer(transferId: string, poolId: string) {
try {
const response = await client.clawback({
sparkTransferId: transferId,
lpIdentityPublicKey: poolId,
});
if (response.accepted) {
console.log('Clawback initiated!');
console.log('Request ID:', response.internalRequestId);
console.log('Tracking ID:', response.sparkStatusTrackingId);
} else {
console.error('Rejected:', response.error);
}
} catch (error) {
console.error('Clawback failed:', error);
}
}
Batch Clawback
Recover multiple transfers at once:
const results = await client.clawbackMultiple(
['transfer-1', 'transfer-2', 'transfer-3'],
'pool-id'
);
for (const result of results) {
if (result.success) {
console.log(`✅ ${result.transferId} recovered`);
} else {
console.log(`❌ ${result.transferId} failed: ${result.error}`);
}
}
When to Use Manual Clawback
Manual clawback is useful when:
- Auto-clawback partially failed - Check
error.getUnrecoveredTransferIds() and retry manually
- Application crashed mid-operation - Use the monitor or
listClawbackableTransfers to find stuck funds
- Debugging - Investigate specific transfers before recovery
- Custom recovery logic - Implement your own retry strategies
Clawback Eligibility Rules
A transfer is eligible for clawback if:
- The authenticated user is the original sender
- The transfer was sent to an LP wallet
- The transfer is not already reserved or spent
- The transfer has not been claimed/settled
- The transfer is less than 23 hours old (for eligibility check) or 10 days old (for listing)
Transfers become ineligible after the time limit expires. Use the clawback monitor for continuous recovery.
Error Recovery Decision Tree
Next Steps