configId returned by the API is the on-chain routing key.
For EVM chains, see EVM.
How it works
1
Create a config via the API
Use the Configs endpoint to create a config that defines your
charity beneficiaries and weights. You receive an
id object with hex and base58 fields.
Both encode the same 32-byte identifier that routes donations to the correct destinations.2
Submit the Solana transaction
Call a public donate instruction on the Donation Relay program: SPL Token / Token-2022 use
donate_v1 or donate_pubkey_config_id_v1; native SOL uses donate_native_v1 or
donate_native_pubkey_config_id_v1 (see below for accounts and arguments).The Donation Relay program routes donations into a per-config, per-mint
debouncer_v1 PDA. For SPL Token and Token-2022, you transfer from the donor’s token account. For native SOL, dedicated instructions move lamports through an ephemeral wrapped-SOL vault (donate_native_wsol_tmp_v1) inside the program. Donations are accumulated per epoch; the relayer closes epochs and runs distribution (not callable by integrators).
Program ID and IDL
The contract has been audited by Halborn and has gone through multiple rounds of review.
Mapping configId from the API
The API returns config IDs as { "hex": "0x...", "base58": "..." }. Both encode the same 32 bytes.
donate_v1/donate_native_v1: useid.hex. Strip the0xprefix and decode the hex to[u8; 32](left-to-right byte order).donate_pubkey_config_id_v1/donate_native_pubkey_config_id_v1: useid.base58directly as aPubkey(convenient when your stack already usesPublicKeytypes).
Instructions (integrators)
There are four public donate instructions, in two pairs. Within each pair the account layout and semantics are identical; onlyconfig_id encoding in the instruction data differs. On-chain names use snake_case (donate_native_v1, …). Generated TypeScript clients typically expose the native pair as donateNativeV1 and donateNativePubkeyConfigIdV1 (for example UMI) or getDonateNativeV1Instruction / getDonateNativePubkeyConfigIdV1Instruction (Codama-style kits); the SPL instructions follow the same pattern (donate_v1 → donateV1 / getDonateV1Instruction, etc.).
SPL Token and Token-2022 (donate_v1, donate_pubkey_config_id_v1)
Both transfer
amount (base units of mint) from the donor’s token account into the debouncer_v1 vault ATA for (config_id, mint), with tip_bps, message (UTF-8; max 255 bytes), and credited_to.
Native SOL (donate_native_v1, donate_native_pubkey_config_id_v1)
These instructions donate lamports equal to
amount: the program transfers lamports from the donor to an ephemeral donate_native_wsol_tmp_v1 PDA (wrapped-SOL token account), calls sync_native, runs the same donation core as SPL (transfer_checked into the debouncer_v1 vault), then close_account on the temp vault (rent returns to the donor). Arguments tip_bps, message, and credited_to match the SPL donate instructions. mint must be the canonical wrapped SOL mint for the token_program you pass: legacy SPL Token So11111111111111111111111111111111111111112, or Token-2022 9pan9bMn5HatX4EJdBwg9VgCa7Uz5HL8N1m5D3NdXejP.
All four instructions emit DonationMadeV1Event (see Events).
Account metas (order)
Pass accounts in this order (matches the IDL). Several PDAs useinit_if_needed (or init for the native temp vault); the donor (from) pays rent when accounts are created.
PDAs are derived with the relay program ID as the program id. config_id in seeds is always the 32-byte config identifier (for *_pubkey_config_id_* instructions, the same bytes as the Pubkey argument).
donate_v1 and donate_pubkey_config_id_v1
donate_native_v1 and donate_native_pubkey_config_id_v1
Same as SPL, except the donor’s source of funds is donate_native_wsol_tmp_v1 (program-owned PDA, init per instruction) instead of the donor’s ATA.
Minimum SOL and rent
The donate instructions useinit_if_needed on epoch_tracker_v1, debouncer_v1, and the debouncer_v1 vault, with payer = from (the donor signer). Native donate also **init**s donate_native_wsol_tmp_v1 each time. Network transaction fees are additional (often paid by the same key as from, depending on your client).
Rent-exempt lamports come from the cluster Rent sysvar. The table below uses the standard mainnet-style formula (2-year exemption, 3480 lamports per byte-year, 128-byte account overhead). If your RPC or cluster parameters differ, use getMinimumBalanceForRentExemption(dataLength) with the on-chain data length (program account space, or token account size).
First donation for a given (config_id, mint)
These accounts are created once per config + mint pair (WSOL mint for native donate). Sizes match the on-chain program (EpochTrackerV1 / DebouncerV1 account space, standard SPL token account 165 bytes for the debouncer_v1 vault).
Native SOL (donate_native_*) - temporary wrapped-SOL vault
donate_native_wsol_tmp_v1 is a 165-byte token account (same rent as above: ~0.00203928 SOL). It is **init**d at the start of the instruction and **close_account**d at the end, with rent returned to from in the same transaction. It is not an extra long-term cost, but the donor wallet must still have enough lamports at execution time to cover donation amount + this rent (plus any first-time rows above) until the close runs.
After the first donation (same config_id + mint)
No further rent is charged for epoch_tracker_v1, debouncer_v1, or the debouncer_v1 vault. For native donate, each transaction still briefly needs amount + ~0.00203928 SOL (donate_native_wsol_tmp_v1) plus fees. For SPL donate, the donor only needs SPL balance + SOL for fees (and an existing funded from_token_account).
Native SOL (overview)
Usedonate_native_v1 / donate_native_pubkey_config_id_v1 when you want donors to spend native lamports without maintaining their own WSOL ATA balance; the program wraps through donate_native_wsol_tmp_v1 for you.
Alternatively, you can still pre-wrap SOL into the donor’s WSOL ATA and call donate_v1 / donate_pubkey_config_id_v1 with the WSOL mint, same as any other SPL mint.
Mint policy
Mints must either appear on the on-chainmint_whitelist_v1 PDA or satisfy strict safety checks (for example, no active mint/freeze authority; Token-2022 extensions such as transfer fee, transfer hook, and others are validated). Unsupported or unsafe mints fail with errors such as InvalidMint.
If you need a mint allowlisted, coordinate with Donate.gg; updating the whitelist is upsert_mint_whitelist_v1 (relayer-only).
Events
After a successful donation, observeDonationMadeV1Event:
Relayer-only instructions (do not integrate)
These require the relayer signer and are not for end-user wallets:close_donation_epoch_v1distribute_non_swap_output_mint_v1swap_then_distribute_v1upsert_mint_whitelist_v1