For the complete documentation index, see llms.txt. This page is also available as Markdown.

deposit_withdraw_example.ts

view on githhub : https://github.com/SolutioFi-io/diversifi-integration-guide/blob/main/deposit_withdraw_example.ts

/**
 * DiversiFi Deposit & Withdrawal Example
 *
 * Demonstrates how to deposit USDC into a basket and withdraw LP tokens back to USDC
 * using the DiversiFi API. Requires an API key with `position_management` permission.
 *
 * Flow for deposits and withdrawals:
 *   1. Call the API to get an unsigned Solana transaction.
 *   2. Sign it with the user's wallet.
 *   3. Submit it to the Solana network.
 *   4. Call the API confirm endpoint with the resulting signature.
 *
 * Requirements: npm install @solana/web3.js dotenv
 */

import { Connection, Keypair, VersionedTransaction } from "@solana/web3.js";
import dotenv from "dotenv";

dotenv.config({ path: ".env" });

// ---------------------------------------------------------------------------
// Configuration — set these in your .env file
// ---------------------------------------------------------------------------
const API_BASE_URL = process.env.API_BASE_URL ?? "https://api.diversifi.trade";
const API_KEY      = process.env.DIVERSIFI_API_KEY ?? "";           // dfi_...
const RPC_URL      = process.env.RPC_URL ?? "https://api.mainnet-beta.solana.com";

// For a real integration the user signs client-side (e.g. via wallet adapter).
// Here we load a keypair from an environment variable for demonstration.
const USER_SECRET_KEY = process.env.USER_SECRET_KEY
    ? Uint8Array.from(JSON.parse(process.env.USER_SECRET_KEY))
    : null;

// ---------------------------------------------------------------------------
// API client
// ---------------------------------------------------------------------------

async function apiGet<T>(path: string, params: Record<string, string | number>): Promise<T> {
    const url = new URL(path, API_BASE_URL);
    for (const [key, value] of Object.entries(params)) {
        url.searchParams.set(key, String(value));
    }

    const response = await fetch(url.toString(), {
        headers: {
            Authorization: `Bearer ${API_KEY}`,
            "Content-Type": "application/json",
        },
    });

    if (response.status === 429) {
        const retryAfter = response.headers.get("retry-after") ?? "60";
        throw new Error(`Rate limited. Retry after ${retryAfter}s`);
    }

    const body = await response.json() as any;

    if (!response.ok || body.error) {
        throw new Error(body.error ?? body.message ?? `HTTP ${response.status}`);
    }

    return body as T;
}

// ---------------------------------------------------------------------------
// Sign and submit a base64-encoded unsigned transaction
// ---------------------------------------------------------------------------

async function signAndSubmit(
    connection: Connection,
    signer: Keypair,
    base64Tx: string
): Promise<string> {
    const txBytes = Buffer.from(base64Tx, "base64");
    const tx = VersionedTransaction.deserialize(txBytes);
    tx.sign([signer]);
    const signature = await connection.sendRawTransaction(tx.serialize());
    await connection.confirmTransaction(signature, "confirmed");
    return signature;
}

// ---------------------------------------------------------------------------
// Deposit: USDC → LP tokens
// ---------------------------------------------------------------------------

/**
 * Deposits USDC into a DiversiFi basket.
 *
 * @param basketAddress  The basket address / Index PDA (base58)
 * @param usdcAmount     Human-readable USDC amount (e.g. 100.5 for $100.50)
 */
export async function deposit(
    connection: Connection,
    signer: Keypair,
    basketAddress: string,
    usdcAmount: number
): Promise<string> {
    console.log(`Fetching unsigned deposit transaction (${usdcAmount} USDC → ${basketAddress})...`);

    const { data } = await apiGet<{ data: { unsignedTxs: string[] } }>(
        "/v1/positions/open/tx",
        { basket_address: basketAddress, amount: usdcAmount }
    );

    let lastSignature = "";

    for (const unsignedTx of data.unsignedTxs) {
        console.log("Signing and submitting transaction...");
        lastSignature = await signAndSubmit(connection, signer, unsignedTx);
        console.log(`  Submitted: ${lastSignature}`);
    }

    console.log("Confirming with DiversiFi API...");
    await apiGet("/v1/positions/open/confirm", {
        basket_address: basketAddress,
        signature: lastSignature,
    });

    console.log("Deposit confirmed. LP tokens will arrive in your wallet shortly.");
    return lastSignature;
}

// ---------------------------------------------------------------------------
// Withdrawal: LP tokens → USDC
// ---------------------------------------------------------------------------

/**
 * Withdraws LP tokens from a DiversiFi basket back to USDC.
 *
 * @param basketAddress  The basket address / Index PDA (base58)
 * @param lpAmount       Human-readable LP token amount to withdraw
 */
export async function withdraw(
    connection: Connection,
    signer: Keypair,
    basketAddress: string,
    lpAmount: number
): Promise<string> {
    console.log(`Fetching unsigned withdrawal transaction (${lpAmount} LP tokens from ${basketAddress})...`);

    const { data } = await apiGet<{ data: { unsignedTxs: string[] } }>(
        "/v1/positions/close/tx",
        { basket_address: basketAddress, amount: lpAmount }
    );

    let lastSignature = "";

    for (const unsignedTx of data.unsignedTxs) {
        console.log("Signing and submitting transaction...");
        lastSignature = await signAndSubmit(connection, signer, unsignedTx);
        console.log(`  Submitted: ${lastSignature}`);
    }

    console.log("Confirming with DiversiFi API...");
    await apiGet("/v1/positions/close/confirm", {
        basket_address: basketAddress,
        signature: lastSignature,
    });

    console.log("Withdrawal confirmed. USDC will arrive in your wallet shortly.");
    return lastSignature;
}

// ---------------------------------------------------------------------------
// Refund: cancel a stuck deposit or withdrawal
// ---------------------------------------------------------------------------

/**
 * Cancels a pending deposit or withdrawal and reclaims the locked tokens.
 * Only the unprocessed portion is returned.
 *
 * @param basketAddress  The basket address / Index PDA (base58)
 */
export async function refund(
    connection: Connection,
    signer: Keypair,
    basketAddress: string
): Promise<void> {
    // Check what can be refunded
    const { data: eligibility } = await apiGet<{
        data: { canRefund: boolean; refundType: "deposit" | "withdrawal" | null }
    }>("/v1/positions/refund/check", { basket_address: basketAddress });

    if (!eligibility.canRefund || !eligibility.refundType) {
        console.log("No refundable balance found for this basket.");
        return;
    }

    // refund/check returns "withdrawal" but refund/tx expects "withdraw"
    const refundType = eligibility.refundType === "withdrawal" ? "withdraw" : eligibility.refundType;

    console.log(`Refunding ${refundType}...`);

    const { data: refundData } = await apiGet<{ data: { unsignedTx: string } }>(
        "/v1/positions/refund/tx",
        { basket_address: basketAddress, refund_type: refundType }
    );

    const signature = await signAndSubmit(connection, signer, refundData.unsignedTx);
    console.log(`  Submitted refund: ${signature}`);

    await apiGet("/v1/positions/refund/confirm", {
        basket_address: basketAddress,
        refund_signature: signature,
    });

    console.log("Refund confirmed.");
}

// ---------------------------------------------------------------------------
// Demo
// ---------------------------------------------------------------------------

const isMain =
    typeof require !== "undefined" && require.main === module ||
    (typeof process !== "undefined" && process.argv[1]?.endsWith("deposit_withdraw_example.ts"));

if (isMain) {
    if (!API_KEY) {
        console.error("Set DIVERSIFI_API_KEY in your .env file.");
        process.exit(1);
    }
    if (!USER_SECRET_KEY) {
        console.error("Set USER_SECRET_KEY in your .env file (JSON array of 64 bytes).");
        process.exit(1);
    }

    const connection = new Connection(RPC_URL, "confirmed");
    const signer     = Keypair.fromSecretKey(USER_SECRET_KEY);
    const BASKET     = "HfrGnAvWjkNJCbixiVf9zRAbCZP8BKh2tWqtFqsGsdY1"; // example basket address (Index PDA) for dToken ADdkaLqDsupDXi3qAEpuv9LXUSjA25ypcKGRseejLXaz

    (async () => {
        try {
            // Deposit 10 USDC
            await deposit(connection, signer, BASKET, 10);

            // Withdraw 5 LP tokens
            // await withdraw(connection, signer, BASKET, 5);

            // Cancel a stuck operation
            // await refund(connection, signer, BASKET);
        } catch (e) {
            console.error(e);
        }
    })();
}

Last updated