Docs / Developer API
SDK
@saplingcash/sdk: TypeScript, for Node and the browser. It wraps every endpoint, and checks every transaction on your machine, against your own request and the addresses pinned in the release, before anything signs it.
Install
npm install @saplingcash/sdkIts one runtime dependency is @solana/web3.js. ESM and CommonJS.
The client
import { Sapling } from "@saplingcash/sdk";
const sapling = new Sapling({
apiKey: process.env.SAPLING_API_KEY, // optional: higher limits and usage stats
rpcUrl: process.env.RPC_URL, // your own Solana RPC: verify() reads chain through it
});apiKey: optional. Sent in theAuthorizationheader only.rpcUrlorconnection: whatverify()reads lookup tables and quotes through, and whatsend({ relay: false })sends through. Solana's public endpoint by default; use your own.baseUrl,timeoutMs,fetch: for tests and unusual setups.
In a web page, leave the key out or keep it behind your own server: a key in a page is a key anyone can read. It moves no funds, but it spends your limits.
Reads
await sapling.params();
await sapling.coins.list({ sort: "trending", limit: 20 }); // { data, next }
await sapling.coins.search("owl");
await sapling.coins.get(mint);
await sapling.coins.candles(mint, { interval: "5m" });
await sapling.coins.trades(mint, { cursor });
await sapling.coins.holders(mint);
await sapling.coins.fees(mint);
await sapling.coins.quote(mint, { side: "buy", pay: "ZEC", amount: 25_000_000n });
await sapling.trades();
await sapling.graduations();
await sapling.stats();
await sapling.statsDaily({ days: 30 });
await sapling.rewards.runs({ mint });
await sapling.rewards.run(id);
await sapling.rewards.wallet(address);
await sapling.tx(signature);Answers are the shapes on endpoints, typed. Amounts stay strings in base units.
Actions
Amounts are base units, as a string or a bigint: 25_000_000n is 0.25 ZEC, 200_000_000n lamports is 0.2 SOL. Addresses can be strings or PublicKeys.
const plant = await sapling.plant({ wallet, name: "Night Owl", symbol: "OWL", image, description, payee: "holders", firstBuyZec: 50_000_000n });
const buy = await sapling.buy({ wallet, mint, pay: "SOL", amount: 200_000_000n, slippageBps: 100 });
const sell = await sapling.sell({ wallet, mint, amount: "all", receive: "ZEC" });
const claim = await sapling.claimPayee({ wallet, mint });
const moved = await sapling.setPayee({ wallet, mint, payee: { wallet: newPayee } });plantmakes the coin's mint key on your machine and sends only its public key. It uploadsimage(aBlob, or{ data, type, name }) with the description andlinks, or takes your ownuri.payeeis"me","holders"or{ wallet }. A first buy isfirstBuyZec, in the same transaction, orfirstBuySol, a swap first. The new coin's address isplant.mint.buyandselltrade on whichever venue the coin is on now.pay: "SOL"andreceive: "SOL"swap through Jupiter.amount: "all"sells the whole balance and closes the emptied token account.claimPayeepays a coin's waiting share to its payee. Any wallet can send it; it never pays the sender.setPayeeis signed by the current payee. Holder rewards are permanent, sopayee: "holders"needsconfirmPermanent: true.swap: SOL to ZEC or back, on its own.
verify, sign, send
Each action answers a step with the same four parts:
step.build // the API's answer: transactions, simulation, expected, expiresAt
step.expected // tokens or ZEC out, and the bound your slippage sets
const check = await step.verify(); // { ok, problems, transactions: [{ ok, problems }] }
await step.sign(signer);
const signature = await step.send(); // through the relay; send({ relay: false }) for your own RPCverify()never throws for a failed check: readok, and show the problems if it is false. What it checks: verify before you sign.sign()refuses unlessverify()ran and passed on exactly these bytes.send()sends every transaction of the step in order, each confirmed before the next, and answers the last signature.
The signer
A Keypair for a bot, or a browser wallet from the Solana wallet adapter (anything with publicKey and signTransaction). For a planting, the wallet signs first; the SDK checks the wallet added nothing but a compute budget or Lighthouse assertions, then signs with the mint key.
Two-step actions
A first buy paid in SOL, a SOL buy too large for one transaction, and a sale paid out in SOL take two steps. Send the first, then next() builds the second with what the first was verified to deliver; it answers null when there is nothing after.
let step = await sapling.buy({ wallet, mint, pay: "SOL", amount: 200_000_000n });
while (step) {
const check = await step.verify();
if (!check.ok) throw new Error([...check.problems, ...check.transactions.flatMap((t) => t.problems)].join("; "));
await step.sign(wallet);
await step.send();
step = await step.next();
}Errors
A refused request throws an ApiError with the HTTP status, the API's code, its plain-words message, and retryAfter in seconds on a 429. A request that got no answer has code network or timeout. Show the message to your user as it is. The codes are on keys and limits.