Skip to main content
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:
  1. Transfers your funds to the pool
  2. Executes the operation (signing intents, calling the API)
  3. If the operation fails, checks if the error requires clawback
  4. Automatically claws back your funds if needed
  5. 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

OperationTransfer(s) Protected
executeSwapInput asset transfer
executeRouteSwapInitial asset transfer to first pool
addLiquidityBoth asset A and asset B transfers
createSingleSidedPoolInitial 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

OptionTypeDefaultDescription
intervalMsnumber60000Polling interval in milliseconds
batchSizenumber2Clawbacks per batch (for rate limiting)
batchDelayMsnumber500Delay between batches
maxTransfersPerPollnumber100Max transfers to fetch per poll
onClawbackSuccessfunction-Called on successful clawback
onClawbackErrorfunction-Called on failed clawback
onPollCompletefunction-Called after each poll cycle
onPollErrorfunction-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:
  1. Auto-clawback partially failed - Check error.getUnrecoveredTransferIds() and retry manually
  2. Application crashed mid-operation - Use the monitor or listClawbackableTransfers to find stuck funds
  3. Debugging - Investigate specific transfers before recovery
  4. 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