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.
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