npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cowprotocol/sdk-trading-solana

v0.9.0

Published

CowProtocol Solana trading: Jupiter-sourced quotes and on-chain CreateOrder posting against the Solana settlement program

Readme

Solana Trading SDK

Test coverage

| Statements | Branches | Functions | Lines | | --------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | Statements | Branches | Functions | Lines |

@cowprotocol/sdk-trading-solana is CoW Protocol's Solana settlement client: it turns a quote into an on-chain CreateOrder instruction against the CoW Protocol Solana settlement program.

Experimental. The settlement program (cow-settlement-interface / solana-programs) is a young, actively evolving deployment — expect breaking changes to the intent wire format, PDA seeds and this package's API across minor versions. The currently deployed program id is exported as SOLANA_SETTLEMENT_PROGRAM_ID from @cowprotocol/sdk-config — see it on Solana Explorer.

npm install @cowprotocol/sdk-trading-solana

A runnable end-to-end example lives in examples/nodejs/solana.

Making a trade

Step 0 — approve the settlement program as SPL delegate

The settlement program moves funds out of the seller's token account via a CPI, which SPL Token only allows for an approved delegate. Before any order on a given sell-token account can settle, that account's owner must approve the settlement state PDA (not the program id itself) as delegate, for at least the amount being sold. approveCowProtocol builds that instruction without sending it, deriving the sell-token account and delegate for you:

const sdk = new SolanaTradingSdk() // same env as the SDK instance used for the quote below

const approveInstruction = sdk.approveCowProtocol({
  ownerAddress: owner,
  sellTokenAddress: sellMint,
  approveAmount, // at least the order's sellAmount
})

This approval is not one-time: like any SPL delegate, each settled order decrements the delegate's remaining allowance by the amount actually transferred, so approving once for exactly one order's sellAmount leaves nothing for the next one. Check the sell-token account's current delegate/delegatedAmount before building an order, and reapprove (topping the allowance back up) whenever it's insufficient — approving a new amount replaces the previous allowance rather than adding to it. Separately, because the delegate PDA is derived from the settlement program's major/minor version (getSettlementSeed), a program upgrade that bumps that version changes the delegate address entirely, requiring a fresh approval regardless of remaining allowance. This step can be sent as its own transaction ahead of time, or bundled with the CreateOrder instruction in the same transaction — see Step 2 below.

Step 1 — get a quote

Quoting needs no signer, just the addresses and amount:

import { OrderKind } from '@cowprotocol/sdk-order-book'
import { SolanaTradingSdk } from '@cowprotocol/sdk-trading-solana'

const sdk = new SolanaTradingSdk() // pass `{ env: 'staging' }` to target staging

const { quoteResults, solanaQuote, buildOrder, postSwapOrderFromQuote } = await sdk.getQuote({
  ownerAddress, // the seller's wallet — signs the CreateOrder transaction
  sellTokenAddress,
  sellTokenDecimals,
  buyTokenAddress,
  buyTokenDecimals,
  amount, // bigint — sell-side amount for a SELL order, buy-side amount for a BUY order
  kind: OrderKind.SELL,
})
  • quoteResults mirrors the EVM SDK's QuoteResults (trade parameters, suggested slippage, amounts and costs) so UI code can share formatting logic across chains.
  • solanaQuote is the Solana-specific quote: the order intent that was built from the quote response, its encoded bytes, its uid, the order PDA it will live at, and the raw upstream quote (real amounts/slippage) the intent was derived from.
  • Native SOL is accepted as the sentinel address; it's substituted with wrapped SOL (WSOL) before quoting and building the intent, since Solana has no native-token mint to reference on-chain.
  • Passing sellTokenProgramId / buyTokenProgramId (e.g. TOKEN_2022_PROGRAM_ID) is required for Token-2022 mints, since the associated token account address differs by program.

Step 2 — create the order

Two ways to get the order on-chain, depending on whether you need to bundle it with other instructions:

Let the SDK submit it as its own transaction — pass a signing/sending function at the point of signing:

const result = await postSwapOrderFromQuote(signAndSend)
// result.orderId, result.txHash / result.signature

signAndSend has the shape (instruction: TransactionInstruction) => Promise<{ signature: string }> — build, sign and send the transaction however fits your app (a Connection + Keypair, a wallet adapter, a Squads/Safe-style multisig flow, etc).

Bundle the order with your own instructions — a Solana order is created by a single instruction, so it can share one transaction with, say, the Step 0 delegate approval, a native-SOL wrap, or anything else. buildOrder returns that instruction without sending it:

const { instruction, orderId, orderPda, uid } = await buildOrder()

await sendMyTransaction([approveInstruction, instruction])

This is the recommended path whenever the sell-token account's delegate allowance needs topping up for this trade, since it lets the delegate approval and the order creation land atomically in one transaction.

Both paths accept the same optional advancedSettings (currently quoteRequest.receiver and quoteRequest.validTo) and apply them identically — overriding receiver or validTo re-derives the order's uid and PDA so they still match the intent actually being created:

await postSwapOrderFromQuote(signAndSend, { quoteRequest: { receiver: otherWallet.toBase58() } })

Step 3 — read back the result

Both postSwapOrderFromQuote and buildOrder return an orderId — the uid as the 0x-prefixed hex string the CoW order-book API uses for EVM orders too, so it can be handled uniformly by shared UI/state code. solanaQuote.orderPda is the on-chain account the order lives at once the CreateOrder instruction lands.

The intent model

Every Solana order is, at its core, a SolanaOrderIntent — a fixed-layout struct that is the direct TypeScript counterpart of OrderIntent in cow-settlement-interface (interface/src/data/intent.rs). Unlike an EVM order (an EIP-712 message the owner signs off-chain and the order-book stores), a Solana intent is never signed as data — it's written directly into an on-chain account by an instruction the owner's wallet signs.

interface SolanaOrderIntent {
  owner: PublicKey // signs the CreateOrder transaction
  buyTokenAccount: PublicKey // receives the buy-side proceeds — implicitly encodes the receiver
  buyMint: PublicKey
  sellTokenAccount: PublicKey // funds pulled from here — must be owned by `owner`; implicitly encodes the spender
  sellMint: PublicKey
  sellAmount: bigint
  buyAmount: bigint
  validTo: number // unix timestamp, seconds
  kind: OrderKind // SELL | BUY
  partiallyFillable: boolean
  createdOnChain: boolean // must be true — see "Authentication" below
  appData: Uint8Array // exactly 32 bytes, opaque to the settlement program; no convention defined yet, sent as zeroes
}

Encoding

encodeOrderIntent packs the intent into the exact 213-byte (ENCODED_ORDER_INTENT_SIZE) little-endian layout the settlement program reads (EncodedOrderIntent::from(&OrderIntent)):

| Bytes | Field | | ----- | --------------------------------- | | 32 | owner | | 32 | buyTokenAccount | | 32 | buyMint | | 32 | sellTokenAccount | | 32 | sellMint | | 8 | sellAmount (u64 LE) | | 8 | buyAmount (u64 LE) | | 4 | validTo (u32 LE) | | 1 | flags (bit 0 createdOnChain, bit 1 kind === BUY, bit 2 partiallyFillable) | | 32 | appData |

Identity: uid and the order PDA

hashOrderIntent takes the SHA-256 digest of the encoded intent bytes. That 32-byte digest is both:

  • the order's uid (toOrderId formats it as the 0x-prefixed id used across the SDK), and
  • the middle seed of the order's program-derived address (findOrderPda), alongside a version-embedded settlement seed (getSettlementSeed, "settlement v" + version, padded to a fixed width so seeds from different versions can't collide) and a trailing "order" seed.

Because the uid is a hash of the intent's content, any change to the intent — including an advancedSettings override to receiver or validTo — changes the uid and therefore the PDA the order will be created at. buildSolanaSwapOrder re-derives both whenever advancedSettings actually changes the intent, so the instruction it returns always targets the PDA matching the intent it encodes.

The CreateOrder instruction

buildCreateOrderInstruction builds the instruction that writes the intent on-chain, matching CreateOrder::into::<Instruction>():

  • data: [discriminator = 2, ...213 intent bytes]
  • accounts: owner (readonly signer), createdBy (writable signer — funds the new order PDA's rent; may equal owner), orderPda (writable), the System Program

Authentication: why createdOnChain must be true

cow-settlement-interface supports two ways an intent can be authenticated: created on-chain (the owner signs the CreateOrder transaction themselves) or via an off-chain Ed25519-presigned order that anyone can submit on the owner's behalf. This SDK only implements the former — encodeOrderIntent always sets the on-chain flag, and the settlement program authenticates the order against the transaction's own signature rather than a separate signed payload. This is the Solana counterpart of an EVM PRESIGN order (buildSolanaSwapOrder reports signingScheme: SigningScheme.PRESIGN for exactly this reason) rather than an EIP-712 EIP712 one.

Example app

examples/nodejs/solana is a runnable, end-to-end demo: it gets a quote, approves the settlement program's delegate, builds the CreateOrder instruction, bundles both into one transaction and submits it. It targets mainnet-beta by default and trades real funds — see that example's own README for setup, including how to point it at devnet instead.

Why Solana trading looks different from EVM trading

On EVM chains, TradingSdk gets a quote from the CoW order-book API, has the caller sign an EIP-712 typed order, and POSTs the signed order to the order-book, which later settles it. Solana has neither an order-book API to post to nor an implicit global signer:

  • Quotes don't come from the CoW order-book (yet). SolanaTradingSdk.getQuote currently sources pricing and route discovery from an external quoting API, then builds a CoW Protocol order intent from the result. That API is used for quoting only — orders are never submitted through it, only ever created via the CoW settlement program. This is expected to move onto the CoW order-book API directly as Solana support matures.
  • Orders are created entirely on-chain. There is no signed order body to POST. Creating an order means submitting a single CreateOrder instruction that writes the order intent into a new program-derived account (the order PDA). The transaction signature that lands on-chain is the order's authentication.
  • No bound signer. The EVM SDK gets its signer from a global adapter set once at app startup. This package has no such adapter: quoting needs no signer at all, and a signing/sending function (SolanaSignAndSend) is passed only at the point of actually submitting a transaction. That also means a quote can be turned into a raw TransactionInstruction (via buildOrder) without ever involving a signer, so it can be composed into a larger transaction built and sent by the caller.