sendfun-sdk 4.0.0

Rust SDK for send.fun programs
Documentation

sendfun-sdk

Rust SDK for the send.fun programs on Solana.

  • launchpad: token creation, bonding curve trades, migration.
  • dex: AMM trades after migration.
  • nexus: staking and partner fees.
  • transfer_hook: Token-2022 transfer-hook resolution.
  • utils: MintInfo.

The SDK builds instructions, decodes accounts and events, and calculates quotes (launchpad::quote, dex::quote). It has no RPC client and does not send transactions.

Fetch accounts with your own client. Compatible: solana-rpc-client 3.x and 4.1+, blocking and nonblocking. Not compatible: 4.0.x.

Decode an account with the from_account function of its type. from_account checks the owner, then the discriminator:

use sendfun_sdk::launchpad::accounts::BondingCurve;

let account = rpc.get_account(&address)?;
let curve = BondingCurve::from_account(&account.owner, &account.data)?;

get_account fails when the account does not exist. For an account that can be missing, use get_account_with_commitment. Its value is None for a missing account. Decode with from_account_maybe. It returns Ok(None) for an uncreated PDA that holds lamports.

Install

cargo add sendfun-sdk

Optional features:

  • serde: Serialize and Deserialize on accounts, events and types.
  • anchor: anchor-lang interop for on-chain callers.

Rules

  • For a trade, read platform_config from the bonding curve or the pool.
  • A partner other than DEFAULT_PARTNER must sign.
  • constants::find_platform_address(slug) returns the key of a platform.
  • Each trade, migrate and create builder takes a MintInfo for the base mint and a MintInfo for the quote mint. For the new base mint of a create, use MintInfo::launch. Load a MintInfo as below. Refresh it on a timer, like any account: load it and fill it again. For WSOL and USDC, you can also use constants::WSOL_MINT_INFO and constants::USDC_MINT_INFO.
  • The trade and migrate builders refuse a base mint that is not a Token-2022 mint.
  • A build can fail with HookError::NotLoaded. See its doc.
  • The SDK refuses a hook list that it cannot resolve before the instruction runs. A transaction can still fail at the hook. Simulate it.
  • Do not edit the generated modules. They come from the program IDLs.

Read the mints and the Clock in one call, then fill each hook:

use sendfun_sdk::transfer_hook::{AccountView, FetchedAccount};
use sendfun_sdk::utils::MintInfo;
use sendfun_sdk::constants::CLOCK_SYSVAR_ID as CLOCK;

// One call, so the fee is the fee at the mint read.
let accounts = rpc.get_multiple_accounts(&[base_mint, quote_mint, CLOCK])?;
let [Some(base), Some(quote), Some(clock)] = accounts.as_slice() else {
    return Err("missing account".into());
};
let clock = AccountView { owner: &clock.owner, data: &clock.data };
let mut base_mint_info = MintInfo::load(
    base_mint,
    AccountView { owner: &base.owner, data: &base.data },
    clock,
)?;
let mut quote_mint_info = MintInfo::load(
    quote_mint,
    AccountView { owner: &quote.owner, data: &quote.data },
    clock,
)?;

for info in [&mut base_mint_info, &mut quote_mint_info] {
    loop {
        let next = info.missing()?;
        if next.is_empty() {
            break;
        }
        for (address, account) in next.iter().zip(rpc.get_multiple_accounts(&next)?) {
            info.insert(*address, account.map(|a| FetchedAccount { owner: a.owner, data: a.data }));
        }
    }
}

A builder quotes the fee at the epoch of the read. Read the mint again when the epoch changes. transfer_fee holds the whole FeeSchedule: fee_at(epoch) gives the fee at another epoch, and newer_epoch the epoch the fee changes. MintInfo::load_at reads a mint at an epoch, with no Clock. hook_program and validation_address() give a hooked mint's hook program and the account that holds its list.

A router that cannot simulate checks each trade direction with transfer_hook::route::trade. It takes both MintInfos, and reads every other account a list reads from the router's own cache. transfer_hook::route::screen checks a direction for any trader, before the router knows the trader. transfer_hook::route::MarketLoad loads and screens a market at each update. It names the accounts to fetch and the accounts to declare for the next update.

Documentation

https://docs.send.fun

License

MIT, see the LICENSE file.