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:

```rust,ignore
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

```sh
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:

```rust,ignore
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 `MintInfo`s, 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.