Skip to main content
This guide walks through a complete neobank integration with RebelFi — from wallet registration through yield generation and withdrawal. Every step includes working code.

Prerequisites

  • @rebelfi/sdk installed
  • A RebelFi API key
  • A Solana wallet for testing
import { RebelfiClient, RebelfiError, ErrorCode } from '@rebelfi/sdk';

const client = new RebelfiClient({
  apiKey: process.env.REBELFI_API_KEY
});

1. Onboard a User Wallet

Register your user’s wallet. This is idempotent — calling it again with the same address returns the existing wallet.
const wallet = await client.wallets.register({
  walletAddress: 'So11...abc',
  blockchain: 'solana',
  userId: 'neobank-user-123'
});

console.log('Wallet ID:', wallet.walletId);
// Use walletId for all subsequent API calls
The userId field links this wallet to your internal user. You can later query allocations and earnings by userId instead of wallet address.

Update metadata

Attach arbitrary metadata to a wallet:
await client.wallets.update(wallet.walletId, {
  orgMetadata: { plan: 'premium', kycStatus: 'verified' }
});

List wallets

Query wallets for a user or by blockchain:
const { wallets, total } = await client.wallets.list({
  userId: 'neobank-user-123'
});
console.log(`User has ${total} wallets`);

2. Discover Venues and Strategies

Find available yield opportunities:
const { venues, strategyCount } = await client.venues.list({
  blockchain: 'solana',
  token: 'USDC'
});

for (const venue of venues) {
  console.log(`${venue.name} (${venue.protocol})`);
  for (const strategy of venue.strategies) {
    console.log(`  ${strategy.name}: ${(strategy.apy * 100).toFixed(2)}% APY`);
  }
}
Pick a strategy to use:
const venue = venues[0];
const strategy = venue.strategies[0];

3. Check Pre-Supply Allocations

Before supplying, verify the user’s current positions:
const { allocations, totalValue } = await client.allocations.list({
  walletId: wallet.walletId
});

if (allocations.length === 0) {
  console.log('No existing positions — ready to supply');
} else {
  console.log(`${allocations.length} existing positions, total value: ${totalValue}`);
}

4. Supply Flow

Create the operation

const operation = await client.operations.supply({
  walletId: wallet.walletId,
  strategyId: strategy.strategyId,
  amount: '1000000000', // 1000 USDC (6 decimals)
  tokenAddress: strategy.tokenAddress
});

console.log('Operation:', operation.operationId);
console.log('Status:', operation.status); // 'awaiting_signature'
console.log('Expires:', operation.expiresAt);
Operations expire after a short window. Sign and submit promptly.

Sign the transaction

The operation contains an unsigned transaction. Have the user sign it:
import { VersionedTransaction, Connection } from '@solana/web3.js';

const unsignedTxBuffer = Buffer.from(
  operation.transactions[0].unsignedTransaction,
  'base64'
);
const transaction = VersionedTransaction.deserialize(unsignedTxBuffer);

// User signs via their wallet
transaction.sign([userKeypair]);

Broadcast and submit hash

Option A — You broadcast:
const connection = new Connection('https://api.mainnet-beta.solana.com');
const txHash = await connection.sendTransaction(transaction);

await client.transactions.submitHash({
  operationId: operation.operationId,
  txHash
});
Option B — RebelFi broadcasts:
const signedTxBase64 = Buffer.from(transaction.serialize()).toString('base64');

await client.transactions.submitSigned({
  operationId: operation.operationId,
  signedTransaction: signedTxBase64
});

Poll for confirmation

async function waitForConfirmation(operationId: number) {
  const timeout = 60_000;
  const start = Date.now();

  while (Date.now() - start < timeout) {
    const op = await client.operations.get(operationId);

    if (op.status === 'confirmed') return op;
    if (op.status === 'failed') {
      throw new Error(op.transactions[0]?.error || 'Operation failed');
    }

    await new Promise(r => setTimeout(r, 2000));
  }
  throw new Error('Timeout');
}

const confirmed = await waitForConfirmation(operation.operationId);
console.log('Supply confirmed!');

5. Identifier Equivalence

After supply, you can query allocations and earnings using any wallet identifier — they all return the same data:
// All three return identical results:
const byId = await client.allocations.list({ walletId: wallet.walletId });
const byAddr = await client.allocations.list({ walletAddress: 'So11...abc' });
const byUser = await client.allocations.list({ userId: 'neobank-user-123' });
The same applies to the get and earnings methods. Use whichever identifier is most convenient.

6. Monitor Yield

Check allocations

const { allocations } = await client.allocations.list({
  walletId: wallet.walletId
});

for (const alloc of allocations) {
  console.log(`${alloc.venueName}${alloc.strategyName}`);
  console.log(`  Principal: ${alloc.principal}`);
  console.log(`  Current value: ${alloc.currentValue}`);
  console.log(`  Yield earned: ${alloc.yieldEarned}`);
}

Get earnings history

const earnings = await client.allocations.earnings({
  walletId: wallet.walletId,
  blockchain: 'solana',
  token: 'USDC',
  days: 30,
  includeBreakdown: true
});

console.log(`Total yield: ${earnings.totalYieldEarned}`);

for (const day of earnings.history) {
  console.log(`${day.date}: +${day.yieldEarned} (cumulative: ${day.cumulativeYield})`);
}

7. Unwind (Withdraw)

When the user wants to withdraw, create an unwind operation:
// Partial withdrawal
const unwind = await client.operations.unwind({
  walletId: wallet.walletId,
  strategyId: strategy.strategyId,
  amount: '500000000' // 500 USDC
});

// Sign and submit (same flow as supply)
const unsignedTx = Buffer.from(unwind.transactions[0].unsignedTransaction, 'base64');
const tx = VersionedTransaction.deserialize(unsignedTx);
tx.sign([userKeypair]);

const signedBase64 = Buffer.from(tx.serialize()).toString('base64');
await client.transactions.submitSigned({
  operationId: unwind.operationId,
  signedTransaction: signedBase64
});

await waitForConfirmation(unwind.operationId);
console.log('Withdrawal complete');

8. Cancel Flow

Cancel a pending operation that hasn’t been submitted:
const result = await client.operations.cancel(operation.operationId);
console.log(result.status); // 'CANCELLED'
You rarely need to cancel manually. Creating a new operation for the same wallet automatically cancels any pending ones.

9. Error Handling

import { RebelfiError, ErrorCode } from '@rebelfi/sdk';

try {
  await client.operations.supply(request);
} catch (error) {
  if (!(error instanceof RebelfiError)) throw error;

  if (error.is(ErrorCode.INSUFFICIENT_BALANCE)) {
    console.error('Available:', error.details?.available);
  } else if (error.is(ErrorCode.WALLET_NOT_FOUND)) {
    console.error('Register the wallet first');
  } else if (error.is(ErrorCode.OPERATION_IN_PROGRESS)) {
    console.error('Wait for current operation to complete');
  } else {
    console.error(`${error.code}: ${error.message}`);
  }
}
See the Error Handling guide for comprehensive patterns including retry logic.

Next Steps