Docs / Developer API
Verify before you sign
A transaction from the API is one someone else built. Check it on your side, against your own request, before your user signs it. The SDK does this in one call.
What verify() checks
verify() runs on your machine. It trusts neither the network between you and the API nor the API itself. It answers ok and the problems it found, and sign() refuses to sign anything that did not pass it.
- Lookup tables come from chain. A v0 transaction names accounts through lookup tables. verify() reads each table from chain by its address, never from the answer, so a swapped table cannot make the same bytes mean other accounts.
- Every program and address is the pinned one. pump.fun, PumpSwap, Jupiter, Sapling's vault program, the ZEC mint and the treasury are compared in full, every character, with the addresses pinned in the SDK release you installed.
- The payee is the one you chose. A planting declares exactly the payee in your request: your wallet, the wallet you named, or holders.
- The bounds are yours. The least you receive and the most you pay are worked out from your own amount and
slippageBps, not from the answer'sintentorexpected. - Nothing extra. Only the instructions the action needs: a compute budget within the cap, the pump.fun or PumpSwap instruction for this coin and this wallet, the wallet's own token accounts, Jupiter for a swap, and for a planting the launch fee and the declaration. No other transfer, no token approval, no change of authority, no closing of an account the wallet still uses.
const plant = await sapling.plant({ wallet, name, symbol, image, payee: "holders" });
const check = await plant.verify();
if (!check.ok) {
// in plain words: what is not what you asked for
throw new Error([...check.problems, ...check.transactions.flatMap((t) => t.problems)].join("; "));
}
await plant.sign(wallet);
await plant.send();Why the payee matters
Whoever builds a planting chooses its payee, the wallet that receives the deployer's 40% of every trade's creator fee. Once the coin lands, only that payee can change it. If a builder put its own wallet there instead of the one your user chose, it would take that share for good.
A wallet's preview does not catch it. Declaring a payee moves no funds at signing time, so there is no balance change to warn about. The only place to see it is the transaction itself, and verify() checks it.
On sapling.cash, a coin whose payee is a wallet other than the one that planted it says so on its page, with the payee's full address. In the API, payee.isPlanter is false for such a coin.
Pinned addresses
The addresses verify() holds a transaction to, as this site runs them. Check the full address, every character: look-alike addresses copy only the first and last few.
Without the SDK
Make the same checks before you ask for a signature: resolve the lookup tables from chain, decompile the message, and hold every instruction to the shape of the action. The planting's exact shape is on build it yourself.