solana, resolve the accounts its manifest names, and build the cook with the Kitchen client or with the instruction builders. The library exports no RPC endpoint: every function that reads the cluster takes your RPC client or rpcUrl.
@solana/kit’s Address, RPC client and TransactionSigner; install the major version the package depends on. @eco-incorp/sauce/svm re-exports the kit functions its own functions take (address, generateKeyPairSigner, getProgramDerivedAddress, AccountRole, getAddressEncoder, blockhash). The program and Kitchen ids come from v12SvmEngineProgramId() and v12SvmKitchenProgramId() in @eco-incorp/sauce/deployments (Deployments).
Compile and keep the account manifest
routes.compileSauceProgram("solana", source) returns the bytecode and the account manifest: the accounts the program needs, in order, with the role each requires. The token methods and venue namespaces add their accounts to main, and derivedAccounts says how each is derived.
sol-compile.mts
entryParams holds main’s values; the account slots are not among them. Encode the values with routes.encodeEntryArguments and append them to the bytecode (Entry arguments).
The program names pot, so its accounts derive from the Pot (pot, pot:USDC) and it runs through the Kitchen only; potMention gives the line and column of that first mention. A program that never names pot derives them from authority (authority:USDC), the account that signs the engine call: the Pot through the Kitchen, the owner on a direct cook, so it runs either way. The two programs below never name pot.
Venue accounts and data
A venue namespace call adds every account its swap takes tomain, and derivedAccounts names each one. This program swaps at Orca’s whirlpool of tick spacing 4 (@sauce/router on Solana):
orca-swap.sauce.ts
orca-compile.mts
@sauce/router function itself needs those accounts in the venue’s own order. routerSvmAccountOrder(venue, options) and routerSvmAccounts(venue, options, refs) from @eco-incorp/sauce/svm list them, with whether the instruction writes each and whether it signs; routerSvmDataTemplate(venue, options) encodes the instruction’s fields once in TypeScript; and ROUTER_SVM_VENUES gives each venue’s program and its …Data and …Cpi functions (Router swap accounts, Router swap data).
resolveSvmAccountName
Resolves one account name, in the languagesauce run --account NAME=<value> takes, to the address a cook attaches.
Import
Usage
sol-accounts.mts
Signature
Names
A token is a registered symbol (
USDC, TSLAx, matched without regard to case), a mint’s base58 address, or @<param>. A registered mint’s program comes from the registry; any other is read through ctx.programOf.
Returns
ResolvedSvmAccountName: value is "pot", "owner" or an address; kind is pot, owner, address, account, mint or program; account carries the mint, holder and token program of a token account.
Throws
SvmAccountNameError for a name that cannot be resolved, such as a slot without ctx.load or an unregistered mint without ctx.programOf.
resolveSvmToken
Reads a mint and resolves its token program, decimals and Token-2022 extensions.Import
Usage
sol-token.mts
Signature
Parameters
load: an owned-account loader, which returns each account’s data and owner.kitBatchAccountLoader(rpc)wraps a kit RPC’sgetMultipleAccounts, andcoalescingOwnedAccountLoaderbatches the loads made in one turn into one request. The owner is the only way to tell an extensionless Token-2022 mint from an SPL Token one.mint: the mint’s address.
Returns
tokenProgram: SPL Token or Token-2022, the program that owns the mint: a checked write’stokenProgramaccount, and the program a token account derives under.decimals: a checked write’sdecimalsargument.extensions:transferFeeandtransferHookwhere set,blocking(non-transferable, frozen by default, paused) andadvisories(permanent delegate, confidential transfers and other flags that do not stop a plain transfer).transferRefusals: why@sauce/token’s checked transfer of this mint would be refused: a transfer hook that names a program, or ablockingextension. Empty when none.svmTokenAccount(token, owner): the owner’s associated token account under the mint’s program. A legacy derivation of a Token-2022 holder’s account gives an address nothing created.svmTokenReceivedandsvmTokenToSend: what a transfer ofamountdelivers atepoch, and what to send for the destination to receive exactlyamount, netting the mint’s transfer fee as Token-2022 computes it.
Throws
resolveSvmToken throws when the account is missing, is owned by neither token program, or is not an initialized mint.
A Token-2022 transfer fee is withheld from what the destination receives, so a program that pays one measures what arrived. In a program, @sauce/token’s transferChecked and approveChecked move either program’s tokens; the legacy transfer, transferFrom and approve are SPL Token only (Names).
The execution fee and a cook built by hand
Every cook pays the Kitchen’s execution fee in lamports, and signs a maximum.fetchExecutionFee reads the fee from the Kitchen’s ["execution_fee"] config account. The instruction builders then assemble a cook for any owner without a signature, which is how you measure or inspect one before a wallet signs it.
Usage
sol-cook-unsigned.mts
Signature
Notes
fetchExecutionFeethrows when the config is absent, is not owned by the Kitchen, or does not decode: every cook is refused on chain then. A cook whosemaxExecutionFeeis below the live fee is refused before anything runs, so read the fee immediately before signing.resolveAccountskeeps manifest order. An entry’ssignerreplaces the manifest’s requirement: the Pot is attached withsigner: falsebecause the Kitchen signs for it during the cook, and a non-payer authority is{ address, signer: transactionSigner }. The reservedpayerreference resolves to the fee payer. A manifest ending inremainingleaves the final tail to you.- Every cook transaction carries
buildHeapFramePrepend()exactly once: the request for the engine’s 262,144-byte heap frame. Without it the engine fails before any instruction of the program runs, and a second ComputeBudget instruction of the same type fails the whole transaction. The client adds it. buildCookInstructionnames the Pot as a readonly account, and the Kitchen validates it withpotSaltand the Pot’s canonical bump, which the builder derives fromkitchen,ownerandpotSalt. PasspotasderivePotPda(kitchen, owner, potSalt).address. The Kitchen signs for the Pot during the cook; a Pot the program writes also belongs intail, unsigned.- The Kitchen’s cook instruction carries the Pot’s bump after its salt. Build every cook with this SDK’s builders.
measureExecuteTransactioncompiles the transaction with no signature and a placeholder blockhash, for its size and accounts. A transaction holds at most 1,232 bytes.
createSauceSvmClient
Builds a client that simulates, builds, signs and sends Kitchen cooks for one payer.Import
Usage
sol-client.mts
Signature
Parameters
Returns
SauceSvmClient:
computeUnitLimit: "auto" simulates first and sets the limit from the units used, padded 20% and capped at 1,400,000. A cook’s own maxExecutionFee option overrides the client’s.
Throws
WhenrpcUrl is missing or via is not "kitchen" or "engine". A simulation failure is a result with ok: false, not a throw.
cookTransaction
The signed cook transaction, built asexecute builds it, for a caller that sends it another way. It is a method of the client above.
Signature
Returns
A kit transaction signed by the client’spayer, with a fresh blockhash, the heap-frame request and, with computeUnitLimit: "auto", a compute limit from a simulation. Nothing is sent. To build a cook with no signature, use the instruction builders in the section above.
When the accounts do not fit
A cook that names about thirty accounts no longer fits one transaction.execute and executeStaged then create an address lookup table holding the accounts a table may hold (all but signers and called programs), wait until it is usable, and send through it. The payer pays the table’s rent and owns it; the result’s lookupTableAddress names it, and passing it as lookupTable to a later cook reuses it. lookupTable: false sends the cook as it is. To simulate such a cook or build it with cookTransaction, get the table first with cookLookupTable and pass its lookupTables.
Stage a large program
A program larger than one transaction is staged into a Kitchen code buffer, then cooked from it. The client has no staging method: build these Kitchen instructions and send them in order.Usage
sol-stage.mts
orca-swap.sauce.ts is the program in Venue accounts and data.
Steps
deriveCodeBufferPda(kitchen, owner, nonce)andderiveCodePda(kitchen, codeBuffer)give the buffer’s metadata and code accounts.buildCreateCodeBufferInstructionallocates them. A capacity aboveGROWTH_STEP(10,240 bytes) needsMath.ceil(capacity / GROWTH_STEP) - 1morebuildGrowCodeBufferInstructioncalls, which can share one transaction.buildWriteCodeBufferInstructionwrites each chunk at its offset.buildFinalizeCodeBufferInstructionseals the buffer with the program’s length and SHA-256 digest. The Kitchen checks the digest and shrinks the code account to that length.- Cook from it: the Kitchen’s
buildCookFromAccountInstruction, the client’ssimulateStagedandexecuteStaged, or on a direct cookbuildCookAndPayFromAccountPinnedInstruction.buildCloseCodeBufferInstructioncloses the buffer and returns its rent.
What the pin checks
- Through the Kitchen,
buildCookFromAccountInstructiontakes apin, and the Kitchen refuses the cook unless the staged bytes hash to it; the Kitchen runs only a finalized buffer the signing owner holds. The client’sexpectedSha256option throws on this path: it is not that pin. - Direct to the engine, the client always pins a staged cook, to
expectedSha256or to thesha256of theStagedBufferyou pass, and refuses a bare buffer address with neither.buildCookAndPayFromAccountInstruction, unpinned, runs whatever the account holds when the transaction lands, with the signer’s authority: use it only for an account no one else can change.
Two ways to cook
The fee and the compiled program are the same either way. A direct cook cannot write a Pot’s storage, and a transfer whose authority is a Pot fails for the missing signature; it leaves the program one more level of calls into other programs.
/svm/engine
The engine’s wire format and instruction builders, without the rest of/svm.
Usage
sol-engine.mts
Exports
- The three
buildCookAndPay…builders are the engine’s paid direct cooks. Each appends the fee payer, the System program and the fee config after the program’stail; the engine takes them off before the program runs. The fee payer must be a System-owned account holding no data. buildExecuteInstructionandbuildExecuteFromAccountInstructionbuild the engine’sexecuteandexecute_from_account, the instructions the Kitchen sends the engine during a cook. The released engine refuses them from anything but the Kitchen.engineFaultName(code)names a failed engine instruction’sCustom(n)code.Custom(0)isRevert, a program’s ownthroworrequire.
/svm/engine-artifacts
File paths to the packaged engine and Kitchen programs and the Kitchen IDL, for a local LiteSVM harness. Node only.Usage
sol-artifacts.mts
Signature
Parameters
cluster: which engine build. Default"devnet".path,paths: an explicit file to use instead of the packaged one.
Returns
Absolute paths. Load each program into LiteSVM or a local validator atv12SvmEngineProgramId() and v12SvmKitchenProgramId(). @eco-incorp/sauce/testing runs programs on such a local Solana from a test (Testing).
Throws
When a file is absent.svmEngineArtifacts throws if any piece is missing, rather than returning a partial set.
Next steps
- Kitchen and Pot: what a Solana cook runs through.
sauce runon Solana: the same steps from the command line.@sauce/routeron Solana: the Solana swap functions.- Distributed swap on Solana and Solana swap.
