> For the complete documentation index, see [llms.txt](https://docs.diversifi.trade/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.diversifi.trade/diversifi-for-partners/integration-guide/on-chain-pricing-overview/price_derivation.ts.md).

# price\_derivation.ts

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

```typescript
import * as borsh from "@coral-xyz/borsh";
import { Connection, PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js";
import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID } from "@solana/spl-token";
import dotenv from "dotenv";

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

// Requirements: npm install @solana/web3.js @solana/spl-token @coral-xyz/borsh dotenv

// --- Constants ---
const DIVERSIFI_PROGRAM_ID = new PublicKey("3vyr9DRfMZb2KvUQdnps7YG3PY38XdguLBQaJ2DFkSxk");
const ADMIN_WALLET = new PublicKey("H1TRD2XtpMy5RJurDNtboDAUuvfzUCUZn8d82oBW3FZd"); // Used purely as a fee payer for simulation
const JUP_API_KEY = process.env.JUP_API_KEY;

// Borsh schema matching the Rust struct returned by the get_vault_balances simulated instruction
const vaultBalancesSchema = borsh.struct([
    borsh.vec(borsh.u64(), "tokenBalances"),
    borsh.vec(borsh.publicKey(), "mints"),
    borsh.u64("indexSupply"),
    borsh.i64("timestamp"),
    borsh.u64("slot"),
]);

const INDEX_ACCOUNT_LAYOUT = borsh.struct([
    borsh.u8("discriminator"),
    borsh.publicKey("manager"),
    borsh.publicKey("tokenMint"),
    borsh.u64("minimumDeposit"),
    borsh.u16("fee"),
    borsh.array(borsh.u8(), 1, "bump"),
    borsh.array(
        borsh.struct([borsh.publicKey("mint"), borsh.u16("target")]),
        5,
        "mints"
    )
]);

export async function fetchAllDTokenMints(connection: Connection): Promise<string[]> {
    console.log("Fetching dToken Mints using dataSlice...");
    const INDEX_ACCOUNT_SIZE = 246;

    // We use getProgramAccounts but strictly limit what the RPC sends back
    const accounts = await connection.getProgramAccounts(DIVERSIFI_PROGRAM_ID, {
        dataSlice: {
            // Note: The structure of the Index struct is:
            // 1 byte discriminator + 32 byte manager pubkey + 32 byte tokenMint pubkey
            offset: 33, // Start pulling exactly at the tokenMint pubkey
            length: 32  // Only pull the 32 byte pubkey and nothing else
        },
        filters: [
            // Ensure we only query valid Index Accounts by checking their layout size
            { dataSize: INDEX_ACCOUNT_SIZE }
        ]
    });

    console.log(`Found ${accounts.length} valid dTokens.`);

    // Convert the raw 32 bytes directly into an array of base-58 strings
    return accounts.map(acc => new PublicKey(acc.account.data).toBase58());
}

export async function fetchVaultBalancesWithInstruction(
    connection: Connection,
    indexPda: PublicKey,
    dTokenMint: PublicKey,
    dTokenDecimals: number,
    vaultAccountsWithMeta: any[]
) {
    // 5. Build and execute atomic on-chain `get_vault_balances` simulation
    const remainingAccounts = vaultAccountsWithMeta.map((v: any) => ({
        pubkey: v.pubkey,
        isSigner: v.isSigner,
        isWritable: v.isWritable
    }));

    // Construct the manual instruction bytes (method discriminator: [138, 38, 110, 31, 116, 102, 223, 172])
    const instructionHex = "8a266e1f7466dfac";
    const dataBuffer = Buffer.from(instructionHex, "hex");

    const ix = new TransactionInstruction({
        programId: DIVERSIFI_PROGRAM_ID,
        keys: [
            { pubkey: indexPda, isSigner: false, isWritable: false },
            { pubkey: dTokenMint, isSigner: false, isWritable: false },
            ...remainingAccounts,
        ],
        data: dataBuffer,
    });

    const tx = new Transaction().add(ix);
    tx.feePayer = ADMIN_WALLET;
    tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;

    // Simulate!
    const simulation = await connection.simulateTransaction(tx);
    if (simulation.value.err) {
        throw new Error(`Simulation failed: ${JSON.stringify(simulation.value.err)}`);
    }

    const result = simulation.value;
    if (!result.returnData || !result.returnData.data) {
        throw new Error("No return data from get_vault_balances");
    }

    // Process the return data payload
    const returnDataBuffer = Buffer.from(result.returnData.data[0], "base64");
    const balances = vaultBalancesSchema.decode(returnDataBuffer) as any;

    const constituentVaults = balances.mints.map((mint: PublicKey, idx: number) => {
        const meta = vaultAccountsWithMeta.find((v) => v.mint.equals(mint));
        const atomicBalance = balances.tokenBalances[idx];
        const uiBalance = Number(atomicBalance.toString()) / Math.pow(10, meta!.decimals);

        return {
            mint,
            balance: uiBalance
        };
    });

    const dTokenSupply = Number(balances.indexSupply.toString()) / Math.pow(10, dTokenDecimals);

    return { constituentVaults, dTokenSupply };
}

export async function fetchVaultBalancesWithRPC(
    connection: Connection,
    dTokenMint: PublicKey,
    dTokenDecimals: number,
    vaultAccountsWithMeta: any[]
) {
    const constituentVaults = await Promise.all(vaultAccountsWithMeta.map(async (meta) => {
        try {
            const balanceInfo = await connection.getTokenAccountBalance(meta.pubkey);
            const uiBalance = balanceInfo.value.uiAmount || 0;
            return {
                mint: meta.mint,
                balance: uiBalance
            };
        } catch (e) {
            // Account might not exist yet if empty
            return {
                mint: meta.mint,
                balance: 0
            };
        }
    }));

    const supplyInfo = await connection.getTokenSupply(dTokenMint);
    const dTokenSupply = supplyInfo.value.uiAmount || 0;

    return { constituentVaults, dTokenSupply };
}

export async function fetchNAV(connection: Connection, dTokenMintStr: string) {
    const dTokenMint = new PublicKey(dTokenMintStr);

    console.log(`Analyzing DiversiFi dToken: ${dTokenMint.toBase58()}`);

    // 1 & 2. Derive the Index Account PDA
    const [indexPda] = PublicKey.findProgramAddressSync(
        [Buffer.from("index"), dTokenMint.toBuffer()],
        DIVERSIFI_PROGRAM_ID
    );

    console.log(`Index PDA: ${indexPda.toBase58()}`);

    // 3. Fetch and Parse the actively configured underlying tokens
    const indexAccountInfo = await connection.getAccountInfo(indexPda);
    if (!indexAccountInfo) {
        throw new Error("Token is not a valid DiversiFi dToken: Index account does not exist.");
    }

    const decodedIndex = INDEX_ACCOUNT_LAYOUT.decode(indexAccountInfo.data);
    const activeMints = decodedIndex.mints.filter((m: any) => !m.mint.equals(PublicKey.default));

    console.log(`Found ${activeMints.length} underlying constituents.`);

    // 4. Fetch token decimals and derive Vault ATAs
    const dTokenMintInfo = await connection.getAccountInfo(dTokenMint);
    if (!dTokenMintInfo) throw new Error("dToken Mint not found");
    const dTokenDecimals = dTokenMintInfo.data[44]; // Offset 44 is the decimal byte

    const vaultAccountsWithMeta = await Promise.all(
        activeMints.map(async (oracleMint: any) => {
            const mintInfo = await connection.getAccountInfo(oracleMint.mint);
            if (!mintInfo) throw new Error(`Mint account not found: ${oracleMint.mint.toBase58()}`);

            const isToken2022 = mintInfo.owner.equals(TOKEN_2022_PROGRAM_ID);
            const tokenProgramId = isToken2022 ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
            const decimals = mintInfo.data[44];

            return {
                pubkey: getAssociatedTokenAddressSync(oracleMint.mint, indexPda, true, tokenProgramId),
                isSigner: false,
                isWritable: false,
                mint: oracleMint.mint,
                decimals
            };
        })
    );

    // 5. Fetch balances and supply using both methods for side-by-side comparison
    const instructionData = await fetchVaultBalancesWithInstruction(
        connection,
        indexPda,
        dTokenMint,
        dTokenDecimals,
        vaultAccountsWithMeta
    );

    const rpcData = await fetchVaultBalancesWithRPC(
        connection,
        dTokenMint,
        dTokenDecimals,
        vaultAccountsWithMeta
    );

    console.log("\n--- Vault Balances Comparison ---");
    const comparisonTable = instructionData.constituentVaults.map((instVault: any) => {
        const rpcVault = rpcData.constituentVaults.find(v => v.mint.equals(instVault.mint));
        return {
            "Token Mint": instVault.mint.toBase58(),
            "Instruction Balance": instVault.balance,
            "RPC Balance": rpcVault ? rpcVault.balance : 0
        };
    });
    console.table(comparisonTable);

    console.log("\n--- Supply Comparison ---");
    console.table([
        { Method: "Instruction", "Total Supply": instructionData.dTokenSupply },
        { Method: "RPC", "Total Supply": rpcData.dTokenSupply }
    ]);

    // Use RPC data for NAV calculation
    const { constituentVaults, dTokenSupply } = rpcData;

    // 6. Demonstrate NAV / Price Calculation
    console.log("--- Pseudo-NAV Calculation ---");
    let totalBasketNAV = 0;

    // Fetch live prices from Jupiter API for accurate NAV calculation
    const mintStr = constituentVaults.map(v => v.mint.toBase58()).join(",");
    // Note: Jupiter V3 now strictly regulates rate limits/auth. Add { headers: { 'x-api-key': 'YOUR_KEY' } } if you have one.
    const fetchOptions: any = JUP_API_KEY ? { headers: { 'x-api-key': JUP_API_KEY } } : {};
    const priceResponse = await fetch(`https://api.jup.ag/price/v3?ids=${mintStr}`, fetchOptions);
    const priceData = await priceResponse.json();

    for (const vault of constituentVaults) {
        const mintId = vault.mint.toBase58();
        const livePriceStr = priceData?.[mintId]?.usdPrice;
        if (!livePriceStr || isNaN(parseFloat(livePriceStr))) {
            console.warn(`  No price available for ${mintId}, skipping`);
            continue;
        }
        const liveOraclePrice = parseFloat(livePriceStr);

        const valueUsd = vault.balance * liveOraclePrice;
        totalBasketNAV += valueUsd;
    }

    const estimatedDTokenPrice = dTokenSupply > 0 ? (totalBasketNAV / dTokenSupply) : 0;

    console.log(`Assessed Network Basket NAV: $${totalBasketNAV.toFixed(4)}`);
    console.log(`Derived dToken Price: $${estimatedDTokenPrice.toFixed(4)}`);
}

// Ensure proper execution
const isMain = typeof require !== 'undefined' && require.main === module || (typeof process !== 'undefined' && process.argv[1]?.endsWith('price_derivation.ts'));
if (isMain) {
    const RPC_URL = process.env.RPC_URL ?? "https://api.mainnet-beta.solana.com";
    const connection = new Connection(RPC_URL);

    (async () => {
        try {
            console.log("\n1. Fetching all dToken Mints...");
            const allMints = await fetchAllDTokenMints(connection);
            console.log("First 3 dToken mints:");
            allMints.slice(0, 3).forEach((mint, i) => console.log(`  ${i + 1}. ${mint}`));

            console.log("\n2. Fetching NAV for a sample dToken...");
            const SAMPLE_DTOKEN = "ADdkaLqDsupDXi3qAEpuv9LXUSjA25ypcKGRseejLXaz";
            await fetchNAV(connection, SAMPLE_DTOKEN);
        } catch (e) {
            console.error(e);
        }
    })();
}
```
