Skip to main content
Donate.gg uses a config-driven donation pipeline. You interact with our on-chain program using metadata from Donation configs: the 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: use id.hex. Strip the 0x prefix and decode the hex to [u8; 32] (left-to-right byte order).
  • donate_pubkey_config_id_v1 / donate_native_pubkey_config_id_v1: use id.base58 directly as a Pubkey (convenient when your stack already uses PublicKey types).

Instructions (integrators)

There are four public donate instructions, in two pairs. Within each pair the account layout and semantics are identical; only config_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_v1donateV1 / 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 use init_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). 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 use init_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)

Use donate_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-chain mint_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, observe DonationMadeV1Event:

Relayer-only instructions (do not integrate)

These require the relayer signer and are not for end-user wallets:
  • close_donation_epoch_v1
  • distribute_non_swap_output_mint_v1
  • swap_then_distribute_v1
  • upsert_mint_whitelist_v1