For developers / SENDIT
SENDIT for developers
SENDIT is a set of contracts on Pickle Chain around one bank, plus two read APIs. Everything a trader does is a transaction you can send yourself, from a wallet or from a registered trading key.
The moving parts
- SenditBank holds every account's pUSD, ETH and launched tokens, and its trading keys. Every other contract is a venue that debits the bank with
pulland credits it withpush. - The perps engine (PepperPerps VERSION 3), its orders book for two-step markets and triggers, and the LP vault.
- The spot router, the pUSD exchange and copy trading, plus pUSD itself.
- The HTTP APIs: dex-api for markets, positions, balances, the feed and tokens; social-api for sessions.
- Building a bot puts it together.
SENDIT runs on the public testnet (chain ID 78270). Its app and APIs are served from https://sendit.picklechain.xyz; JSON-RPC is the chain's, at https://rpc.picklechain.xyz.
Read the manifest
Read the addresses at run time. An engine upgrade deploys new contracts beside the old ones and moves every key, and a call to an address with no code succeeds on an EVM, so a hard-coded address that went stale fails silently.
const ORIGIN = "https://sendit.picklechain.xyz";
// The deployment, as the app itself reads it. Never cached (Cache-Control: no-store).
const manifest = await fetch(`${ORIGIN}/op-deployment/l2-perps.json`).then((r) => r.json());
const { bank, perps, perpOrders, perpVault, spotRouter, copyFactory, pusd, pusdExchange } = manifest;
// Or the same addresses, lower-cased and renamed, from the API:
// { engine, orders, vault, previous, bank, spotRouter, pusdExchange }
const deployment = await fetch(`${ORIGIN}/api/perp/deployment`).then((r) => r.json());Manifest keys
| Key | Type |
|---|---|
| perps | addressThe perps engine, PepperPerps VERSION 3. /api/perp/deployment calls it `engine`. |
| perpOrders | addressThe two-step orders book, PepperPerpOrders. `orders` in /api/perp/deployment. |
| perpVault | addressThe LP vault, PepperPerpVault (pLP). `vault` in /api/perp/deployment. |
| bank | addressSenditBank: every trader's pUSD, ETH and launched tokens, and the trading keys. |
| spotRouter | addressSenditSpotRouter: buy, sell and launch tokens with bank money. |
| copyFactory | addressCopyFactory VERSION 3: one CopyAccount per copier and leader. |
| copyAccountImpl | addressThe CopyAccount implementation every copy account clones (EIP-1167). |
| pusd | addressPickleUSD (pUSD), six decimals. The test dollar every position is margined in. |
| pusdExchange | addressSenditPusdExchange: pUSD for ETH at one dollar. |
| perpsPrevious | arrayEngines that still hold positions: [{ engine, orders, vault, block, version }]. Legacy positions there are closed by the wallet. |
| copyFactoryPrevious | arrayEarlier copy factories: [{ factory, impl, block, version, engine }]. Stop and withdraw old copies there. |
| perpsBlock, bankBlock, copyFactoryBlock, pusdExchangeBlock | numberThe block each contract was deployed in: where an indexer starts reading logs. |
| pusdWethPool, feeRecipient, keeper | addressThe pool the protocol's fee share is sold through, the FeeRouter that receives it, and the keeper's wallet. |
idBase is not in the manifest: read idBase() on the engine.
Current addresses
As served on 2026-10-20. Check them against the manifest; do not hard-code them.
| Key | Address |
|---|---|
| bank | 0x3366f560DC1539B5Db4f889CB603C3BDd369298F |
| perps | 0x57c61D24BaE12D24100ac1FB20950B88904be971VERSION 3, idBase() 10000000: position and request ids start at 10,000,001 |
| perpOrders | 0xcb62fcb714f8aa69d2b2abC8Ba9cCEF2A1E02C84 |
| perpVault | 0x20F4E42E5f56E47e6e058fA137c698C86B0aF459 |
| spotRouter | 0xD6516A4720018AFa6c777F2D686247d715EDa04B |
| copyFactory | 0xf288A5F90Ea3b07b014f52110FC4C7f82206dd95 |
| pusd | 0x47091c58e34bDC4f32A05541C1243710249Dd777 |
| pusdExchange | 0x07b5E6fd6b5A86FBE3685B434988D3b5aFB4385F |
| perpsPrevious[0].engine | 0x8452242B9d7C453556E3a9b0556e48a25ced0ef2VERSION 1, ids from 1, wallet-signed only |
Ids and previous engines
The current engine numbers positions from idBase() + 1, 10,000,001, and its orders book numbers requests from the same base, so an id names one position or request on the whole chain whichever engine holds it. The previous engine (VERSION 1, in perpsPrevious) numbers from 1, predates the bank and is wallet-signed only: positions still open there are closed with its own close from the wallet that opened them. dex-api marks every row from it legacy: true.
The manifest is the only source of truth the app itself uses. If a key is missing (no bank on a network, no exchange yet), the feature is off there; /api/perp/deployment leaves the field out.