Skip to main content
LitePart of the Lite plan; Developer includes it too. See plans · Get your key

Multi-action transactions

One transaction, several things in it: buy on one launchpad and sell on another, launch a coin and buy it, buy and move the tokens somewhere else, claim creator fees and burn. Everything in the list lands together or not at all.

POST https://sol.shrine.trade/api/local-actions, with your plan's key in the x-api-key header.

{
"publicKey": "<fee payer, and the wallet for actions without their own>",
"actions": [
{ "action": "buy", "mint": "<mint>", "amount": 0.05, "slippage": 15 },
{ "action": "transfer", "to": "<other wallet>", "mint": "<mint>", "amount": "50%" },
{ "action": "burn", "mint": "<mint>", "amount": "100%" }
],
"priorityFee": 0.0005,
"jitoTip": 0
}

The answer is one unsigned transaction plus the list of keys that must sign it:

{
"tx": "<base64 unsigned transaction>",
"blockhash": "…",
"signers": ["<fee payer>", "<other signing wallets or mint keypairs, in order>"],
"feeLamports": 125000,
"sizeBytes": 1088,
"actions": [
{ "action": "buy", "wallet": "…", "mint": "…", "amount": 0.05, "feeLamports": 125000 },
{ "action": "transfer", "wallet": "…", "mint": "…", "amount": 12345.6, "feeLamports": 0 },
{ "action": "burn", "wallet": "…", "mint": "…", "amount": 12345.6, "feeLamports": 0 }
]
}

Sign with every key in signers and send it through the relay at POST /api/send, exactly like a single trade: the relay broadcasts through our own Solana endpoint and waits for confirmation, so the bot needs no RPC. The full script is under Copy this.

Actions​

Every action takes an optional publicKey. Without one it runs as the top-level wallet; with one, that wallet signs too and appears in signers.

actionFieldsNotes
buymint, amount (SOL, or the coin's quote), slippage, pool, poolAddressAny launchpad the trade endpoint routes.
sellmint, amount (tokens or "100%"), slippage, pool, poolAddress
createname, symbol, then either uri or imageUrl (+ description, twitter, telegram, website), optional mint, mintRef, initialBuy, slippage, creatorFeeAddress, quoteMint and the V2 flagsA pump.fun launch, see create-token. Without uri the metadata is pinned for you; without mint you get an address ending in pump that we sign for.
transferto, amount, mint (omit for SOL)Creates the recipient's token account if needed. Free.
burnmint, amountBurning all of it closes the account and refunds its rent. Free.
claimCreatorFeesSame as claim-creator-fees. Free.
wrapamount (SOL)SOL into the wallet's WSOL account. Free.
unwrapCloses the WSOL account, every lamport comes back. Free.

amount on a sell, transfer or burn may be a number of tokens or a percentage of what the wallet holds. "100%", "50%" and "all" are all understood.

Launch and buy in one go​

A coin created in the transaction has no address yet when you write the request, so give the create a mintRef and refer to it as "$name" afterwards. Generate the mint keypair yourself and pass its public key as mint; it signs the transaction along with the wallet. Or leave mint out: the coin gets an address ending in pump, the transaction comes back already signed by it, and mintSecretKeys in the answer lists each provided mint with its secret. The same holds inside a bundle.

{
"publicKey": "<dev wallet>",
"actions": [
{ "action": "create", "mintRef": "coin", "mint": "<mint keypair public key>", "name": "My Coin", "symbol": "COIN", "uri": "https://…/metadata.json", "initialBuy": 0.5 },
{ "action": "buy", "mint": "$coin", "amount": 0.2, "slippage": 30 }
]
}

Buys on a coin born in the same transaction are quoted from the curve's launch constants, moved along by the SOL already put in before them, so give them a generous slippage.

How much fits​

Solana caps a transaction at 1232 bytes. With the shared lookup table that is about four swaps on one launchpad, fewer across launchpads, and a launch with a dev buy and one more buy. When a set does not fit, the answer is a 400 naming the size; split it across a bundle, where mintRef still resolves from one transaction to the next.

Copy this​

import { Keypair, VersionedTransaction } from "@solana/web3.js";
import bs58 from "bs58";

const wallet = Keypair.fromSecretKey(bs58.decode("<your wallet private key, base58>"));

const res = await fetch("https://sol.shrine.trade/api/local-actions", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
publicKey: wallet.publicKey.toBase58(),
actions: [
{ action: "buy", mint: "<mint>", amount: 0.01, slippage: 15 },
{ action: "transfer", to: "<friend>", mint: "<mint>", amount: "50%" },
],
}),
});
const { tx, signers } = await res.json();

const txObj = VersionedTransaction.deserialize(Buffer.from(tx, "base64"));
txObj.sign([wallet]); // every key listed in `signers`
// Send through the relay; it waits for confirmation.
const sent = await (await fetch("https://sol.shrine.trade/api/send", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ signedTx: Buffer.from(txObj.serialize()).toString("base64") }),
})).json();
console.log(sent.status, `https://solscan.io/tx/${sent.signature}`);

Notes​

  • Atomic. One instruction failing reverts the whole transaction: nothing partial ever lands.
  • Priority fee is one number for the whole transaction. The compute budget is sized from the actions inside.
  • Fees. None: multi-action transactions come with Lite and Developer, which trade at 0%.
  • Unsigned, like every trade endpoint: the API builds, your wallets sign; nothing is signed or sent by us.

LimitsLite 3 req/sDeveloper 10 req/sSee plans →