Pockets & intents

A session can spend from its pocket within the on-chain policy. Apps request outcomes; the chain checks the signed intent, spending limits and atomic delivery.

Try it in the app

The devnet dashboard can create your pocket, renew its trading session, claim 25 tUSDC, review a quote and swap between tUSDC and tSOL. These are test tokens with no monetary value. No SDK setup is required. Keep at least 0.01 devnet SOL in your wallet for registration eligibility; service wallets sponsor setup and trading fees.

New app pockets have a $50 daily cap in oracle-valued test units, a 1% oracle slippage limit and two pinned feeds. The service supports $0.10–$10 per swap with a 0.20% spread. Review the exact amount and short quote expiry before confirming. Reloading loses the in-memory trading key; sign in and choose Enable trading to renew the session. Existing home addresses and policies are preserved. Send everything home uses an owner proof.

Funding

Each pocket lives at PDA(["pocket", app_id]). Users fund it two ways: a Blink (a Solana Actions deposit their own wallet simulates and signs), or a plain address for exchange withdrawals. fundingOptions re-derives the pocket PDA and refuses a substituted address. The reference Actions service also checks that the destination is an initialized pocket before constructing a transaction. Deposits remain public; funding from a main wallet creates an on-chain link to it.

verifyFunding compares GET metadata with the expected address. It does not inspect the transaction later returned by POST. Use a trusted Actions endpoint and check the actual destination, amounts and instructions before signing. The SDK's Solana Pay URL is a transaction-request URL, not a direct-transfer QR containing only the pocket address.

Token accounts: a deposit creates the pocket's account for that mint. The account for a swap's output mint is the app's to create before the first fill (the reference solver does not pay that rent, since it would flow to the user's home on sweep).

Intents, not transactions

const receipt = await gl.submitIntent(pocket, session.signer, {
  nonce: 1n,                            // a counter you persist per pocket
  inMint: USDC, inMax: 25_000_000n,     // spend at most 25 USDC
  outMint: SOL, outMin: 160_000_000n,   // receive at least 0.16 SOL
  deadlineSlot: current + 150n,         // or the offer dies
});

The session key signs SHA-256 of the intent's canonical bytes. A solver — anyone running the open reference implementation — builds one transaction: signature check, fill_begin, its own routing, fill_end. If the pocket's balance of the out-mint hasn't risen by at least outMin by fill_end — measured on the very account fill_begin snapshotted — the entire transaction reverts, the solver's fee included. Nonces are a sliding window of 2,048 values per pocket: use a counter you persist, and the window follows it for the pocket's whole life; anything it leaves behind can never be replayed.

The policy — limits with teeth

RuleConservative defaultEnforced
Daily cap$200on-chain, per pocket
Max slippage vs oracle1%on-chain, every fill
Passkey above$50bigger spends need a passkey session (user-verified assertion)
No-oracle mints (NFTs)$20 cappasskey-signed only
Oracle feedspinned per mint by youapps and solvers can't substitute feeds

Every intent commits to the policy's hash — if anyone tampers with the policy, every fill fails closed while sweep keeps working. The dashboard reads these values from the pocket account itself.

The exits

Before building an owner proof or sweep/revoke signature, read the pocket's current owner_action_nonce. Pass it to sweepDigest(pocket, nonce), revokeDigest(pocket, nonce), or changeDigest/cancelDigest after the change bytes. These digests append the nonce as u64 little-endian. Successful sweep, revoke, propose, cancel and execute operations advance it; rebuild a stale authorization from fresh state. This is separate from the intent nonce window.

When rotating a session, its expiry must exceed the pocket's stored max_session_expiry_slot and stay within the current policy's allowed horizon. Simply reusing the same expiry cannot reopen a revoked session.