Setup
Copy
import { RebelfiClient, RebelfiError, ErrorCode } from '@rebelfi/sdk';
const client = new RebelfiClient({
apiKey: process.env.REBELFI_API_KEY
});
Step 0: Register the Wallet
Register the user’s wallet before any operations:Copy
const wallet = await client.wallets.register({
walletAddress: userWallet,
blockchain: 'solana',
userId: 'your-user-id' // optional — enables querying by userId
});
const walletId = wallet.walletId;
walletId instead of walletAddress in all subsequent calls.
Step 1: Discover Venues and Strategies
First, find available yield opportunities for the user:Copy
async function getAvailableStrategies(blockchain: 'solana', token: string) {
const { venues, strategyCount } = await client.venues.list({
blockchain,
token
});
// Flatten strategies with venue info
const strategies = venues.flatMap(venue =>
venue.strategies.map(strategy => ({
venueId: venue.id,
venueName: venue.name,
venueIcon: venue.iconUrl,
...strategy
}))
);
// Sort by APY descending
strategies.sort((a, b) => b.apy - a.apy);
return strategies;
}
// Usage
const strategies = await getAvailableStrategies('solana', 'USDC');
console.log('Best APY:', (strategies[0].apy * 100).toFixed(2) + '%');
Step 2: Check Existing Allocations
Before supplying, check what the user already has:Copy
async function getUserAllocations(walletId: number) {
const { allocations, totalValue, totalYieldEarned } = await client.allocations.list({
walletId
});
return {
allocations,
summary: {
totalValue,
totalYieldEarned,
positionCount: allocations.length
}
};
}
// Usage
const { allocations, summary } = await getUserAllocations(walletId);
if (allocations.length > 0) {
console.log(`User has ${summary.positionCount} positions`);
console.log(`Total value: ${formatUSDC(summary.totalValue)}`);
console.log(`Total yield: ${formatUSDC(summary.totalYieldEarned)}`);
}
Step 3: Plan a Supply Operation
Create an operation and get the unsigned transaction:Copy
interface SupplyParams {
walletId: number;
strategyId: number;
amount: string;
tokenAddress: string;
}
async function planSupply(params: SupplyParams) {
const operation = await client.operations.supply(params);
// Validate we got a transaction
if (operation.transactions.length === 0) {
throw new Error('No transactions returned');
}
const tx = operation.transactions[0];
return {
operationId: operation.operationId,
unsignedTransaction: tx.unsignedTransaction,
description: tx.description,
expiresAt: new Date(operation.expiresAt)
};
}
// Usage
const supply = await planSupply({
walletId,
strategyId: strategies[0].strategyId,
amount: '1000000000', // 1000 USDC
tokenAddress: strategies[0].tokenAddress
});
console.log('Transaction ready:', supply.description);
console.log('Expires:', supply.expiresAt.toISOString());
Step 4: User Signs the Transaction
Present the transaction to the user for signing. This example uses@solana/web3.js:
Copy
import { Transaction, Connection, sendAndConfirmRawTransaction } from '@solana/web3.js';
async function signAndBroadcast(
unsignedTxBase64: string,
wallet: { signTransaction: (tx: Transaction) => Promise<Transaction> }
) {
// Decode the unsigned transaction
const txBuffer = Buffer.from(unsignedTxBase64, 'base64');
const transaction = Transaction.from(txBuffer);
// User signs via their wallet
const signedTx = await wallet.signTransaction(transaction);
// Broadcast to Solana
const connection = new Connection('https://api.mainnet-beta.solana.com');
const signature = await sendAndConfirmRawTransaction(
connection,
signedTx.serialize()
);
return signature;
}
// Usage with a wallet adapter
const txHash = await signAndBroadcast(
supply.unsignedTransaction,
walletAdapter
);
console.log('Broadcast:', txHash);
Step 5: Submit Transaction Hash
After broadcasting, notify RebelFi:Copy
async function submitTransaction(operationId: number, txHash: string) {
const result = await client.transactions.submitHash({
operationId,
txHash
});
return result;
}
// Usage
await submitTransaction(supply.operationId, txHash);
console.log('Transaction submitted, waiting for confirmation...');
Alternatively, use
submitSigned to let RebelFi broadcast the transaction for you.Step 6: Poll for Confirmation
Wait for on-chain confirmation:Copy
async function waitForConfirmation(
operationId: number,
options: { timeoutMs?: number; pollIntervalMs?: number } = {}
) {
const { timeoutMs = 60000, pollIntervalMs = 2000 } = options;
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const operation = await client.operations.get(operationId);
if (operation.status === 'confirmed') {
return { success: true, operation };
}
// Note: cancelled operations also appear as 'failed'
if (operation.status === 'failed') {
const tx = operation.transactions[0];
return {
success: false,
error: tx?.error || 'Operation failed or was cancelled',
failureCode: tx?.failureCode,
operation
};
}
// Wait before polling again
await new Promise(resolve => setTimeout(resolve, pollIntervalMs));
}
throw new Error('Timeout waiting for confirmation');
}
// Usage
const result = await waitForConfirmation(supply.operationId);
if (result.success) {
console.log('Supply confirmed!');
} else {
console.error('Supply failed:', result.error);
}
Step 7: View Updated Allocation
After confirmation, the allocation is updated:Copy
// Get the specific allocation
const allocation = await client.allocations.get(
strategies[0].venueId,
userWallet
);
console.log('Current position:');
console.log(` Principal: ${formatUSDC(allocation.principal)}`);
console.log(` Current value: ${formatUSDC(allocation.currentValue)}`);
console.log(` Yield earned: ${formatUSDC(allocation.yieldEarned)}`);
console.log(` APY: ${(allocation.apy * 100).toFixed(2)}%`);
Step 8: Track Earnings Over Time
Get historical earnings data:Copy
const earnings = await client.allocations.earnings({
walletId,
blockchain: 'solana',
token: 'USDC',
days: 30,
includeBreakdown: true
});
console.log(`Period: ${earnings.periodStart} to ${earnings.periodEnd}`);
console.log(`Total yield: ${formatUSDC(earnings.totalYieldEarned)}`);
// Daily breakdown
for (const day of earnings.history) {
console.log(`${day.date}: +${formatUSDC(day.yieldEarned)}`);
}
// Per-venue breakdown
if (earnings.byVenue) {
for (const venue of earnings.byVenue) {
console.log(`${venue.venueName}: ${formatUSDC(venue.totalYieldEarned)}`);
}
}
Step 9: Unwind Position
When the user wants to withdraw:Copy
async function unwind(
walletId: number,
strategyId: number,
amount: string
) {
// Plan the unwind
const operation = await client.operations.unwind({
walletId,
strategyId,
amount
});
return {
operationId: operation.operationId,
unsignedTransaction: operation.transactions[0].unsignedTransaction,
expiresAt: operation.expiresAt
};
}
// Unwind full position
const allocation = await client.allocations.get(strategy.strategyId, walletId);
const unwindOp = await unwind(
walletId,
allocation.strategyId,
allocation.currentValue // Full withdrawal
);
// Sign, broadcast, and wait for confirmation (same as supply)
const txHash = await signAndBroadcast(unwindOp.unsignedTransaction, wallet);
await submitTransaction(unwindOp.operationId, txHash);
await waitForConfirmation(unwindOp.operationId);
console.log('Position unwound successfully');
Complete Service Example
Here’s a complete yield service wrapping all operations:Copy
import { RebelfiClient, RebelfiError, ErrorCode } from '@rebelfi/sdk';
export class YieldService {
private client: RebelfiClient;
constructor(apiKey: string) {
this.client = new RebelfiClient({ apiKey });
}
async getStrategies(token: string = 'USDC') {
const { venues } = await this.client.venues.list({
blockchain: 'solana',
token
});
return venues.flatMap(v =>
v.strategies.map(s => ({ ...s, venueName: v.name, venueId: v.id }))
);
}
async getAllocations(walletAddress: string) {
return this.client.allocations.list({ walletAddress });
}
async getEarnings(walletAddress: string, days: number = 30) {
return this.client.allocations.earnings({
walletAddress,
blockchain: 'solana',
token: 'USDC',
days,
includeBreakdown: true
});
}
async planSupply(
walletAddress: string,
strategyId: number,
amount: string,
tokenAddress: string
) {
return this.client.operations.supply({
walletAddress,
strategyId,
amount,
tokenAddress
});
}
async planUnwind(
walletAddress: string,
strategyId: number,
amount: string
) {
return this.client.operations.unwind({
walletAddress,
strategyId,
amount
});
}
async submitTx(operationId: number, txHash: string) {
return this.client.transactions.submitHash({ operationId, txHash });
}
async waitForConfirmation(operationId: number, timeoutMs = 60000) {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const op = await this.client.operations.get(operationId);
if (op.status === 'confirmed') return { success: true, op };
// Note: cancelled operations also appear as 'failed'
if (op.status === 'failed') {
return { success: false, error: op.transactions[0]?.error || 'Operation failed or was cancelled', op };
}
await new Promise(r => setTimeout(r, 2000));
}
throw new Error('Timeout');
}
async cancelOperation(operationId: number) {
return this.client.operations.cancel(operationId);
}
async recoverTransaction(operationId: number, txHash: string) {
return this.client.transactions.recover(operationId, { txHash });
}
}