commish

Docs

Everything needed to call the 402 endpoint, run a relayer, or call the program directly.

Overview

Commish is one Solana program written with Pinocchio, used as a holdback for agent payments. A tool creates a campaign that names a router cut, a refund window and an attestor key (the relayer). An agent pays the tool over HTTP 402 in USDC, straight into the campaign's vault, with a memo naming the router that sent it. The relayer reads that payment on-chain and records it; the program reserves the router's cut in a commission account. After the refund window anyone can release the cut to the payee. Before that, the payee can sell it to a buyer for an agreed price. On-chain the instruction and field names are the original ones: the tool is the brand, the router is the creator, and a paid call is a record_sale.

ProgramCmSHpw9QTwvRSNCCBrQz275ESTCw8D79Z8jjhWmPJfFB
TokenUSDC EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Sourcegithub.com/bryankwandou/commish
Binary9,272 bytes, SBPF v0, built with build-std and panic_immediate_abort
402 endpointhttps://getcommish.vercel.app/api/agent/call

Accounts

AccountAddressSizeHolds
Campaign["campaign", brand, id_le_u64]176brand, attestor, mint, vault, id, hold, reserved, paid, bps
Commission["commission", campaign, order_hash]184campaign, creator, payee, rent_payer, order_hash, amount, release_at, sold flag
VaultATA(campaign, USDC)165The campaign's budget. Owned by the campaign PDA.

Records are #[repr(C)], little-endian, 8-byte aligned. Byte 0 is a tag (1 campaign, 2 commission), byte 1 the version, byte 2 the bump. A commission account is closed when it is paid or cancelled, and its rent returns to rent_payer.

Instructions

Instruction data is an 8-byte header with the tag in byte 0, followed by the arguments as a #[repr(C)] struct. Bumps and the rent for new accounts are passed by the client; the runtime rejects a wrong bump and any new account below the rent-exempt minimum.

TagInstructionSignersAccountsArguments
0create_campaignbrandbrand, campaign, vault, mint, systemid, hold, bps, bump, lamports, attestor
1record_saleattestor, payerattestor, payer, campaign, vault, commission, systemorder_hash, order_amount, creator, lamports, bump
2cancelattestorattestor, campaign, commission, rent_payernone, only before release_at
3releasenonecampaign, vault, commission, payee_token, rent_payer, token_programnone, only after release_at
4withdrawbrandbrand, campaign, vault, dest, token_programamount ≤ vault − reserved
5sellpayee, buyerpayee, buyer, campaign, commission, buyer_token, payee_token, token_programprice, 0 < price ≤ amount, before release_at

Error codes

CodeMeaning
6000Invalid instruction
6001Not enough accounts
6002A required signature is missing
6003Account is not the expected record
6004Signer is not this campaign's attestor
6005Signer is not this campaign's brand
6006An account does not match the campaign
6007Commission rate must be between 0.01% and 50%
6008Refund window must be between 0 and 90 days
6009Order is too small to earn a commission
6010Not enough unreserved budget in the vault
6011The refund window has not closed yet
6012The refund window has already closed
6013Signer or account is not the current payee
6014Price must be above zero and at most the commission
6015Arithmetic overflow
6016Too many accounts
6017Runtime error

Integrating a tool

  1. Create a campaign in the tool console with your relayer key as the attestor, a router cut and a refund window.
  2. Answer unpaid calls with 402 Payment Required: the price, the vault address and the memo format commish:<router wallet>.
  3. When the agent retries with X-Payment: <signature>, read the transaction, check that the vault received at least the price in USDC and that the memo names the ?ref= router, then send record_sale with order hash sha256("commish:x402:" + campaign + ":" + signature).
  4. If the call is refunded inside the window, send cancel. After the window, anyone can send release. The live endpoint does all of this; its source is web/src/app/api/agent/call/route.ts.

The client in web/src/lib/commish/program.ts builds every instruction and derives every address. It depends only on@solana/kit primitives, so it runs in a browser, a server route or a worker.

Security model