Single-token Deposit
Quote, prepare accounts, and deposit one pool token
The helper swaps part of one input token, joins proportionally, then forwards LP tokens and dust. These movements are atomic; prior account setup is separate.
Supported instruction
deposit_single_token uses helper program
7BpdUH1tzTSXLuQNo6YpjJ8Eagw8AkrS6cnkxiJdCFS2. Confirm
cluster/ABI compatibility.
Quote and prepare a deposit
Amounts use BN raw units. client.singleTokenDeposit is a getter exposing
quote, buildTx, and buildTxs. This example batches setup:
import { CubicPoolClient, applySlippage, type BuiltTx } from "@coffer_so/sdk";
import { ComputeBudgetProgram, PublicKey } from "@solana/web3.js";
import BN from "bn.js";
export async function prepareSingleTokenDeposit(
client: CubicPoolClient,
user: PublicKey,
tokenInIndex: number,
amountIn: BN,
helperBalances?: BN[],
) {
const synced = await client.sync();
if (!synced.ok) throw new Error(synced.error.humanMessage);
const slippageHbps = 5_000; // 0.5%; application-selected tolerance
const quote = client.singleTokenDeposit.quote(
tokenInIndex,
amountIn,
slippageHbps,
undefined, // use the Solana Clock timestamp from sync()
helperBalances,
);
if (!quote.ok) throw new Error(quote.error.humanMessage);
const minimumBptAmount = new BN(applySlippage(
BigInt(quote.data.estimatedBpt.toString()),
slippageHbps,
).toString());
if (minimumBptAmount.isZero()) throw new Error("Deposit is too small");
const built = client.singleTokenDeposit.buildTxs({
user,
tokenInIndex,
amountIn,
minimumBptAmount,
});
if (!built.ok) throw new Error(built.error.humanMessage);
// Replace the setup budget and batch idempotent ATA creation.
const creates = (built.data.setup?.instructions ?? []).filter(
ix => !ix.programId.equals(ComputeBudgetProgram.programId),
);
const setup: BuiltTx[] = [];
for (let i = 0; i < creates.length; i += 3) {
const batch = creates.slice(i, i + 3);
const units = 50_000 * batch.length;
setup.push({
instructions: [
ComputeBudgetProgram.setComputeUnitLimit({ units }),
...batch,
],
suggestedCuLimit: units,
});
}
return {
pool: synced.data,
quote: quote.data,
plan: { setup, deposit: built.data.deposit },
};
}helperBalances contains existing helper balances before input, in pool
order; omission assumes zero. sync() does not fetch them. Read/refresh
these accounts to include donations or dust.
Confirm setup first, refresh the quote, and rebuild the deposit with its
final LP floor. Compile using a usable ALT and simulate with wallet-added
instructions. compileBuiltTx neither creates nor extends ALTs. Wrap SOL
in application code. buildTx combines setup/deposit; buildTxs separates
them, but its setup may still need batching. Neither guarantees transaction fit.
How the helper allocates the input
Input must have positive LP-owned actual balance. Zero-balance tokens are sidelined. For live tokens:
W_i = weight_i × min(actualBalance_i, virtualBalance_i) / virtualBalance_i
allocation_i ≈ amountIn × W_i / sum(W)Two fixed-point divisions precede whole-unit allocation; the remainder stays in the input allocation. Actual balances already exclude protocol fees.
Keep the input allocation; swap others in ascending token-index order. Each leg uses updated reserves/windows, pays base/surge fees, and advances the input sell-off window. Independent quotes against starting state are wrong.
Reload pool/helper holdings, take the smallest proportional ratio, then
apply join rounding. Forward LP tokens and leftovers. Quote fields
estimatedBpt, depositedAmounts, refundAmounts estimate that sequence.
Slippage and failure conditions
deposit_single_token(amount_in: u64, token_in_index: u8, minimum_bpt_amount: u64)Positive minimum_bpt_amount protects the whole operation. Internal
swaps use minimum_amount_out = 0; quote minOuts and the slippage
parameter add no on-chain guards. Explicitly build/show the final LP floor.
Disabled pool/swaps, inactive input, insufficient inventory/window capacity, dust, or an unmet floor can reject the deposit. Token movements roll back; network fees and confirmed setup do not. The builder checks extensions on every mint, including sidelined tokens touched by refunds.
Account layout
Helper PDA: ["stld_helper", pool] under the helper program. Fixed accounts:
pool, helper, LP mint, helper LP account, user LP account, user signer,
pool program, LP token program. Remaining accounts are five per token,
in pool order:
[mint_i, user_ata_i, helper_ata_i, vault_i, token_program_i]Derive ATAs with each mint's token program; preserve builder account order.