개발자를 위한 문서 / SENDIT
SENDIT HTTP API
SENDIT의 출처 뒤에는 두 서비스가 있습니다. dex-api는 체인의 읽기 전용 인덱스이고, social-api는 세션, 팔로우, 클랜, 논지를 보관합니다. 어느 쪽도 체인이 아닙니다. 돈이 걸린 판단이라면 컨트랙트를 읽으십시오.
베이스 URL
- dex-api:
https://sendit.picklechain.xyz/api/... - social-api:
https://sendit.picklechain.xyz/api/social/... - 매니페스트:
https://sendit.picklechain.xyz/op-deployment/l2-perps.json
프록시는 dex-api에 쿠키를 보내지 않습니다. dex-api는 GET에만 답하고 다른 출처를 위한 CORS 헤더를 설정하지 않습니다. 다른 사이트의 페이지가 아니라 서버나 봇에서 호출하십시오.
모든 라우트에 공통인 규칙
| 규칙 | 값 |
|---|---|
| Methods | GET onlyA trailing slash is ignored. |
| Errors | 400 {"error":"bad parameter"}A malformed parameter. An unknown path is 404 {"error":"not found"}. An index that is not ready is 503. |
| Busy | 503 {"error":"busy"}Feed, traders and copy routes run at most four reads at once and give up after a few seconds. Retry with a backoff. |
| Caching | no-storeFeed-family answers are memoised for 3 seconds on the server, per argument set. |
| Money | decimal stringspUSD amounts have six decimals ("1040.000000"); base-unit amounts on /bank are integers as strings. Unknown is null, never 0. |
| Addresses | 40 hex digits0x optional. Feed and market ids are 64 hex digits. |
| Feed paging | limit 1-50, before = nextFeed cursors look like <block>.<txIndex>.<logIndex>.<tie>. Each query key may appear once, values at most 100 characters. |
무기한 선물
- Params
- none
- Returns
- { engine, orders, vault, previous: [{ engine, orders, vault, version, bank }], bank, spotRouter, pusdExchange }
The manifest's perps addresses, lower-cased and renamed (perps -> engine, perpOrders -> orders, perpVault -> vault). bank and spotRouter appear only when a bank is deployed, pusdExchange only when one is named. Without a usable manifest every address is null.
{"engine":"0x57c6...e971","orders":"0xcb62...2c84","vault":"0x20f4...f459","previous":[{"engine":"0x8452...0ef2","orders":"0xe728...dae8","vault":"0xc1bd...942d","version":1,"bank":null}],"bank":"0x3366...298f","spotRouter":"0xd651...a04b","pusdExchange":"0x07b5...385f"}- Params
- none
- Returns
- { markets: [{ feedId, symbol, maxLeverage, spreadBps, enabled, inverse, oiLong, oiShort, fundingRatePerHour, fundingSide, closedSpreadBps, engine, legacy, engines: [...] }] }
Every market in listing order, open interest summed over the engines. fundingSide is the side that pays (long, short or null). A market listed only on an old engine shows enabled false and legacy true.
{"markets":[{"feedId":"0x...","symbol":"ETH/USD","maxLeverage":25,"spreadBps":2,"enabled":true,"inverse":false,"oiLong":"3000.000000","oiShort":"1000.000000","fundingRatePerHour":"0.00625%","fundingSide":"long","closedSpreadBps":10,"engine":"0x57c6...","legacy":false,"engines":[...]}]}- Params
- trader (address, required); status open (default) or all; limit 1-500, default 50
- Returns
- { positions: [{ positionId, trader, feedId, isLong, leverage, margin, size, notional, entryPrice, openFee, status, tp, sl, limitPrice, openedAt, closePrice, pnl, funding, closeFee, penalty, payout, badDebt, closedAt, engine, legacy }] }
A trader's positions, newest first. tp, sl and limitPrice are null when unset.
{"positions":[{"positionId":"10000042","status":"liquidated","leverage":25,"margin":"49.375000","openFee":"0.625000","pnl":"-24.673911","penalty":"12.253260","payout":"11.981817","badDebt":"0.000000", "...": "..."}]}- Params
- id (1 to 19 digits, required)
- Returns
- { position: { ...position, closeReason, trades: [...] } }
One position with its last 10 trades. closeReason is closed, take-profit, stop-loss, limit, liquidated or null. An unknown id answers 404 {"error":"There is no such position."}.
{"position":{"positionId":"10000042","closeReason":"liquidated","trades":[{"kind":"liquidation","netPnl":"-38.018183","penalty":"12.253260"}]}}- Params
- trader (address, optional); feed (32-byte feed id, optional); limit 1-500, default 50
- Returns
- { trades: [{ txHash, block, at, positionId, trader, feedId, kind, isLong, price, notional, fee, pricePnl, netPnl, penalty, engine, legacy }] }
Opens, closes and liquidations. kind is open, close or liquidation; pricePnl and penalty are null on opens. Without trader, copy accounts' trades are left out.
{"trades":[{"kind":"close","fee":"0.500000","pricePnl":"0.097399","netPnl":"-0.902401","penalty":"0.000000"}]}- Params
- trader (address, required); limit 1-500, default 50
- Returns
- { requests: [{ requestId, trader, feedId, kind, isLong, leverage, margin, acceptablePrice, positionId, state, cancelReason, fillPrice, createdAt, engine, legacy }] }
Two-step requests. state is pending, executed or cancelled; cancelReason is cancelled, expired, price moved past your limit, market closed, position gone, rejected, liquidatable or null.
{"requests":[{"requestId":"10000007","kind":"open","state":"executed","cancelReason":null}]}- Params
- offset (default 0)
- Returns
- { positions: [...], next }
Every open position with the market state a keeper needs, 500 a page, integers as strings. next is null on the last page.
{"positions":[{"position_id":"10000042","leverage":"10","funding_index":"-4","m_funding_at":"0","engine":"0x57c6...","legacy":false}],"next":null}- Params
- none
- Returns
- { basis, bookAssets, plpSupply, plpBookPrice, apr7d, sharePrice, nav, asOf, vault, engine, legacy, previous: [...] }
The vault's ledger (deposits minus withdrawals plus settlements, no unrealised PnL) and its live sharePrice and nav read from the chain. apr7d is from the last 7 days' settlements.
{"bookAssets":"1100.000000","plpBookPrice":"1.100000","apr7d":33.18,"sharePrice":"1.083412","nav":"1083.412345","previous":[]}- Params
- account (address, required)
- Returns
- { account, deposited, withdrawn, sharesIn, sharesOut, deposits, withdrawals, vault, engine, legacy, previous: [...] }
One LP's flows. previous lists old vaults where the account still holds pLP.
{"account":"0x0a0a...","deposited":"1500.000000","withdrawn":"400.250000","sharesIn":"1490.000000000000000000","sharesOut":"397.000000000000000000","deposits":2,"withdrawals":1}- Params
- feed (required); resolution 60, 300, 900, 3600 (default), 14400 or 86400; from; to
- Returns
- { t: [...], v: [...] }
Traded notional per bucket, at most 5000 buckets.
{"t":[1792537200,1792540800],"v":["12500.000000","8400.000000"]}뱅크
- Params
- account (address, required)
- Returns
- { account, key, keys, keyEpoch, globalEpoch, paused, caps, balances: { pusd, eth, tokens }, indexedBlock }
An account's live keys (newest first, at most 16), epochs, caps and balances in base units. caps comes from keyCapsOf on the chain; when the node does not answer, spent and windowEnds are null.
{"account":"0x0a0a...","key":"0x6b6b...","keys":["0x6b6b...","0x6c6c..."],"keyEpoch":2,"globalEpoch":1,"paused":false,"caps":{"pusdPerDay":"10000000000","ethPerDay":"5000000000000000000","pusdSpent":"0","ethSpent":"0","windowEnds":1790086400},"balances":{"pusd":"900000000","eth":"1000000000000000000","tokens":[{"token":"0xa1a1...","symbol":"CRN","name":"Cornichon","amount":"5000000000000000000"}]},"indexedBlock":40}- Params
- account (required); kind deposit, withdraw, send, gas, key, venue or buy (default: deposit, withdraw, send, gas, buy); limit 1-100, default 30; before (the previous page's next)
- Returns
- { account, flows: [{ id, kind, direction, asset, assetKind, symbol, amount, counterparty, toWallet, venue, by, via, txHash, block, at }], next }
Money in and out of an account. via is key or wallet. A pUSD exchange buy adds ethIn, pusdOut, price and paidFrom (balance or wallet).
{"flows":[{"id":"20.3.2","kind":"buy","direction":"in","assetKind":"pusd","symbol":"pUSD","amount":"4000000000","via":"key","ethIn":"1000000000000000000","pusdOut":"4000000000","price":"400000000000","paidFrom":"balance"}],"next":null}- Params
- key (address, required)
- Returns
- { key, account, since }
The account a key serves, and the block it was registered in; both null when the key is not live.
{"key":"0x6b6b...","account":"0x0a0a...","since":10}- Params
- none
- Returns
- { bank, indexedBlock, assets: [{ asset, indexed, onChain, negativeBalances, ok }], ok }
The index's balances against totalHeld on the chain, per asset.
{"bank":"0x3366...","indexedBlock":9440000,"assets":[{"asset":"0x4709...","indexed":"...","onChain":"...","negativeBalances":0,"ok":true}],"ok":true}피드, 트레이더, 카피 리더
- Params
- token or market (one, optional); limit 1-50, default 30; before
- Returns
- { items: [...], next }
Everything traders did: launches, buys, sells, opens, closes, liquidations.
{"items":[{"id":"...","kind":"close","trader":"0x...","side":"short","leverage":10,"usd":"1040.000000","pnlUsd":"49.500000","pnlPct":49.25,"pnl":"known","market":{"feedId":"0x..."},"token":null}],"next":"9440012.3.1."}- Params
- viewer (required); token or market; limit; before
- Returns
- { items, next, follows }
The accounts a viewer follows (up to 2000). follows is false before social-api has created its tables.
{"items":[],"next":null,"follows":true}- Params
- clan (clan id, 1 to 16 digits, required); token or market; limit; before
- Returns
- { items, next, clans }
What a clan's members did, each counted from when they joined. Members are capped at 201.
{"items":[],"next":null,"clans":true}- Params
- token (required); limit; before
- Returns
- { items, next }
One launched token's activity.
{"items":[{"kind":"buy","usd":"400","pnl":null}],"next":null}- Params
- address (required); limit; before
- Returns
- { items, next }
One trader's activity.
{"items":[],"next":null}- Params
- tokens and/or markets (comma-separated, 100 at most together); limit; before
- Returns
- { items, next }
A watchlist's activity.
{"items":[],"next":null}- Params
- token or market (exactly one); from, to (unix, at most 400 days apart); min 0 or 1000; who all or friends; viewer
- Returns
- { markers: [{ at, trader, side, usd, mine, friend, price, priceEth, eth }], capped }
Trades to draw on a chart, at most 300.
{"markers":[{"at":1792537260,"side":"long","usd":"250.000000","mine":false,"friend":true}],"capped":false}/api/traders
GET- Params
- sort active (default), pnl, volume or new; limit 1-50, default 50; offset (offset + limit at most 1000); addresses (up to 10) or prefix (1-40 hex)
- Returns
- { sort, offset, limit, rows: [{ address, pnlUsd, volumeUsd, trades, firstAt, lastAt }], more, catchingUp, recounting }
The trader directory.
{"sort":"active","offset":0,"limit":50,"rows":[{"address":"0x...","pnlUsd":"49.000000","volumeUsd":"5000.000000","trades":4,"firstAt":10,"lastAt":20}],"more":false,"catchingUp":false,"recounting":[]}- Params
- none
- Returns
- { asOf, trending, trendingBy, copied, copiedBy, returnWindow, rules, stale, catchingUp, recounting, pulse }
The traders page's podium: trending traders (at most 10), the most copied (at most 30) and a 24-hour pulse.
{"pulse":{"window":"24h","traders":2,"volumeUsd":"6250.000000","trades":13,"copies":5},"returnWindow":"30d","trending":[],"copied":[]}- Params
- window 7d, 30d (default) or all; sort copied (default), return, risk or new; addresses or prefix
- Returns
- { asOf, window, sort, rows: [{ address, copiers, newCopiers, capitalUsd, copiersEver, copiersPnlUsd, firstAt, lastAt, trades7d, risk, window }], more, rules, catchingUp, recounting }
Traders to copy, at most 50 rows, with their copiers and a risk score.
{"rows":[{"copiers":2,"newCopiers":1,"capitalUsd":"300.000000","copiersEver":4,"copiersPnlUsd":"28.000000","risk":{"days":7,"trades":3,"avgLeverage":"5.33","score":5},"window":{"closed":2,"wins":1,"liquidated":0,"pnlUsd":"49.000000","baseUsd":"202.000000","returnPct":24.26,"maxDrawdownUsd":"5.000000"}}]}출시된 토큰
- Params
- none
- Returns
- { trending, new, gainers, count, countCapped, ethUsd }
Up to 12 tokens per list. gainers holds only tokens that are up.
{"trending":[...],"new":[...],"gainers":[],"count":1,"countCapped":false,"ethUsd":{"pair":"ETH/USD","price":"...","stale":false}}- Params
- sort trending (default), new, gainers or mcap; limit 1-500, default 50
- Returns
- { tokens: [summary], ethUsd }
Token summaries: identity, price, change, 24-hour activity, liquidity, holders and fees. Dollar fields are null without a fresh ETH/USD price.
{"tokens":[{"address":"0x...","symbol":"CRN","priceUsd":"0.0000123","marketCapUsd":"12300","change24h":12.5,"fees":{"creatorWethWei":"...","creatorTokens":"..."}}]}- Params
- address in the path
- Returns
- { token: summary + creatorBasis, ethUsd }
One token. 404 {"error":"not a SENDIT launch"} for any other address.
{"token":{"address":"0x...","symbol":"CRN","creatorBasis":{"basis":"known"}}}- Params
- creator (required)
- Returns
- { tokens, ethUsd }
Tokens an account launched, newest first.
{"tokens":[]}- Params
- holder (required)
- Returns
- { holder, positions: [...], capped, ethUsd }
What an account holds, with cost basis and PnL, at most 500.
{"holder":"0x...","positions":[{"symbol":"CRN","balance":"5000000000000000000","valueUsd":"61.5","pnlUsd":"11.5","basis":"known"}],"capped":false}- Params
- holder (required)
- Returns
- { holder, tokens: [{ address, launcher, balance, symbol, name, where }], capped, limit, indexedBlock }
Balances only; where is bank or wallet.
{"tokens":[{"symbol":"CRN","balance":"5000000000000000000","where":"bank"}],"capped":false,"limit":500}- Params
- holder, token (required); limit 1-50, default 20; before
- Returns
- { holder, token, position, activity, next, transfersFrom, transfersFromAt, ethUsd }
One holding with its buys, sells and transfers.
{"activity":[{"kind":"buy","usd":"50","tokens":"..."}],"next":null}- Params
- token (required); limit 1-500, default 50
- Returns
- { holders: [{ address, balance, pct, valueUsd, kind }], count }
Holders; kind is pool, burn, creator or holder.
{"holders":[{"kind":"pool","pct":92.1}],"count":14}- Params
- token (required); limit 1-500, default 50
- Returns
- { trades: [{ txHash, logIndex, block, at, trader, isBuy, ethWei, tokens, usd, priceUsd, marketCapUsd, priceEth, marketCapEth }] }
A token's trades, newest first.
{"trades":[{"isBuy":true,"usd":"400"}]}- Params
- token (required); resolution 1, 5, 15 (default), 60, 240 or 1D; from; to; countback (at most 2000); unit usd (default) or eth
- Returns
- { s: "ok", t, o, h, l, c, v, through } or { s: "no_data", nextTime }
TradingView UDF bars.
{"s":"ok","t":[1792537200],"o":["0.0000120"],"h":["0.0000125"],"l":["0.0000119"],"c":["0.0000123"],"v":["400"]}social-api: 나머지
소셜 라우트도 같은 규칙을 따릅니다. 기능별 주요 라우트는 아래와 같고, 한도는 사용자 가이드의 소셜 페이지에 있습니다.
| 기능 | 라우트 |
|---|---|
| 팔로우 | POST /follow { as, address, on }GET /follows, /mutuals, /profile, /people |
| 논지 | POST /theses { txHash, body }GET /theses, /thesis, /replies; POST /likes, /replies |
| 클랜 | POST /clans, /clans/join, /clans/leave, /clans/invite, /clans/apply...GET /clans, /clan?id=, /clans/mine, /clans/list |
| 송금 | POST /sends { txHash, note }이미 체인에 있는 송금을 비공개 메모와 함께 기록; GET /sends?box=in|out |
| 관심 목록 | POST /watchlist { as, kind, target, on }GET /watchlist |
| 카피 설정 | GET /copy/prefs, POST /copy/prefsGET /copies |
social-api: 세션
로그인은 EIP-4361(SIWE)입니다. nonce 메시지를 요청하고,
personal_sign으로 서명한 뒤, 다시 보냅니다. 메시지는 10분 동안 한 번만 유효합니다. SENDIT 트레이딩 키가 있으면 기기는 지갑 없이 로그인합니다.const API = "https://sendit.picklechain.xyz/api/social"; const headers = { "Content-Type": "application/json", "X-Pepper-Social": "1" }; // 1. A nonce message issued for the ACCOUNT (the wallet address), not the key. const { message } = await fetch(`${API}/session/nonce`, { method: "POST", headers, body: JSON.stringify({ address: account }), credentials: "include", }).then((r) => r.json()); // 2. The trading key signs it (personal_sign). const signature = await key.signMessage({ message }); // 3. The server checks bank.accountOf(key) == account on chain, then sets the cookie. await fetch(`${API}/session/key`, { method: "POST", headers, credentials: "include", body: JSON.stringify({ account, key: key.address, message, signature }), });브라우저 밖에서는 마지막 응답의
Set-Cookie값을 보관했다가, 이후 호출에서Cookie헤더로 돌려보내십시오./api/social/session
GETWho this cookie is signed in as.
{"address":"0x0A0a...","expiresAt":1795129600}/api/social/session/nonce
POSTAn EIP-4361 sign-in message for the address, valid for 10 minutes and usable once. 429 after too many sign-ins from one network.
{"message":"sendit.picklechain.xyz wants you to sign in with your Ethereum account:\n0x0A0a...\n\nSign in to SENDIT. ...","expiresAt":1792538400}/api/social/session
POSTThe wallet's personal_sign of that message. Opens a 30-day session. 401 with the reason when the message or signature does not hold.
{"address":"0x0A0a...","expiresAt":1795129600}/api/social/session/key
POSTThe same nonce message, issued for the ACCOUNT, signed by its trading key. The server checks the signature recovers to key and that bank.accountOf(key) is the account on the chain, then opens the same 30-day session: a new device signs in with no wallet prompt. 401 "This key is not the account's trading key", 409 when there is no bank, 503 when the chain could not be read (the nonce is kept; every other refusal spends it).
{"address":"0x0A0a...","expiresAt":1795129600}/api/social/session/end
POSTSigns out this cookie, or every session of the account.
{"address":null}/session/key는 평범한 로그인 메시지에 서명합니다. "Unlock my SENDIT trading key" 메시지는 그와 다른 것입니다. 앱이 키를 도출하는 방법이며, 어떤 서버로도 보내지지 않습니다.