面向开发者 / 应用 API
应用 API
四个第一方服务坐在这条链旁边。它们没有一个是这条链,没有一个有版本号,而且每一个都以自己的方式失败,这正是本分区存在的意义。
这些是什么
这条链的接口是 JSON-RPC。这些页面上的一切,都是某个应用为自己造出来、然后就留在那里可以够到 的东西:一个建在排序器日志流之上的 Postgres 索引、一个持有密钥的进程、一个游戏循环。它们很 方便,它们是第一方的,而它们不是权威。
从它们之一读一次,不等于从链上读一次
这里每一个数字都经过了一个索引器、一个缓存,或者两者。一个停掉的索引器会继续用它最后的 状态作答,而响应里没有任何字段说明这一点;唯一会告诉你的是那些健康路由,而你得去问它们。 如果这个答案要决定钱,就去读链。
A read-only view of what the Pepper indexer has derived from the chain's logs: constant-product pools, concentrated pools, swaps, liquidity events and positions. It reads Postgres rather than the chain, deliberately.
- 技术栈
- Python, standard-library ThreadingHTTPServer, psycopg pool of at most 8 connections, HTTP/1.1
- 挂载于
- /api/ on the Pepper origin
The archive the node deliberately is not. Blocks, mini-blocks, transactions, receipts, logs, call traces, decoded transfers, accounts, tokens, NFTs and the name-service record, all from a Postgres index fed by the sequencer's own log spool.
- 技术栈
- Python, standard-library ThreadingHTTPServer, psycopg pool of at most 12 connections, plus live reads against the node
- 挂载于
- /api/ on the explorer origin
The only first-party service that WRITES to the chain. It holds the faucet operator key and submits the drip on your behalf, which is why the page needs no wallet.
- 技术栈
- Python, standard-library SimpleHTTPRequestHandler serving a static site as well as the API, signing with eth-account
- 挂载于
- its own origin; the home origin re-exposes two of its routes
A WebSocket server for the games, with a small HTTP surface beside it for health and for the figures the site's chrome draws. The socket is the interface; the HTTP routes are a snapshot of it.
- 技术栈
- Node, `ws` in noServer mode behind a plain http server, one event loop for eight game rooms
- 挂载于
- /api/ and /ws/ on the arcade origin
它们在哪里应答
| 服务 | 基础 URL |
|---|---|
| 浏览器 API | https://explorer.picklechain.xyz/api/域名服务站点和工具箱站点也在 /api/ 下挂载了它 |
| Pepper DEX API | https://pepper.picklechain.xyz/api/仅限同源;另一个来源上的浏览器调不了它 |
| 水龙头 | https://faucet.picklechain.xyz主站来源重新暴露了 POST /drip 和 GET /faucet-status |
| Arcade | https://minigame.picklechain.xyz/api/以及 wss://minigame.picklechain.xyz/ws/<room> 上的 socket |
这些服务没有一个有自己的主机名。每一个都是某个前端来源上的一个 /api/ 挂载点, 这让公开的基础地址成为一个 nginx 事实而不是一个服务事实:一次搬动站点的部署会把它的 API 一起 搬走,而里面的路径保持不变。在你自己运行的一套栈上,同样这些服务改在回环地址上应答, 端口由各自那一页点名。
这四个里没有 /v1,没有版本请求头,也没有任何弃用策略。仓库里也没有 OpenAPI 文档,没有 JSON Schema,所以一个字段可以在两次部署之间改变形态,而没有任何东西可以对照。 请防御性地读:忽略你不认识的键,也不要因为一个键消失了就失败。
认证
这四个没有一个要认证,而真正要紧的差别,是它们各自在没有认证的情况下能做什么。
- 浏览器、DEX API 和 Arcade 都不要认证。它们是只读且公开的。浏览器对每一个 POST 都回答
405;DEX API 除 GET 之外没有实现任何动词。 - 水龙头也不要认证,而它会写到链上。这正是它的配额严格、并且在签任何东西 之前就先预留的全部原因。
Arcade socket 没有任何认证,而那个进程持有一把密钥
它前面没有认证层,也不打算有:代理把升级请求转发过去,仅此而已。保护它的是一次连接的每一个 维度都有界,连接数、来源、帧大小、消息速率、缓冲字节数,而每一次溢出都是丢弃而不是排队。 客户端在那个 socket 上说的任何话都不授权任何事;决定谁可以玩的是那些合约。
从浏览器里调用其中一个
CORS 是逐服务设置的,而且它们并不一致,所以第一件要弄清楚的事,是你那个来源上的页面到底能不能 调用你想调的那一个。
- DEX API 不发送任何 CORS 头,也没有 OPTIONS 处理器。从浏览器发起的跨源
fetch会直接失败,而同样的请求从服务器发出却会成功,这正是那种会被赖到网络头上 的 bug。请代理它,或者从后端调它。 - 浏览器和水龙头各自保留一份允许名单。来自不在名单上的来源的预检请求会被以
403拒绝,那至少是一个清楚的答案。 - Arcade 在升级请求上检查 Origin,并在握手之前就以
403拒绝, 所以那个 socket 是根本不打开,而不是打开之后没了动静。
限制与分页
只有浏览器限制请求速率。其余三个靠一个钳制、一份配额或一个连接上限来代替。
| 服务 | 限制 |
|---|---|
| Pepper DEX API | `limit` is clamped to 1..500 on every list route and defaults to 50. There is no request-rate limit in the service. |
| Explorer API | 300 requests per minute per client address, then 429. Request bodies are capped at 600,000 bytes. Live streams are capped at 32 concurrent. |
| Faucet | Quota is reserved before the transaction is signed: 5 a day per address seen, 2 a day per funded address, 1000 a day in total. Request bodies are capped at 4096 bytes. |
| Arcade server | 256 sockets in total and 8 per source address, then the upgrade is refused 429. Inbound frames are 4 KiB at most and rate-limited per socket. A socket more than 512 KiB behind is cut. |
一份被钳制的列表就是一份悄悄变短的列表
向 DEX API 要一千笔兑换,你拿到五百笔,配上一个 200,而没有任何东西说这个答案 被裁过。向浏览器要两百个区块,你拿到一百个。这两个服务在多数列表路由上都不返回总数,所以 没有一个字段可以拿来和你这一页的长度作比较:唯一说明还有更多的信号,就是你拿到的正好是那个 钳制值。
凡是有分页的地方,用的都是 limit 和 offset,不是游标。这意味着一份 在头部增长的列表会在你翻两页之间挪动:你已经见过的行会再次出现,而你没见过的行会被跳过。对任何 必须完整的东西,请按一个你自己控制的键分页,比如一个区块号、一个哈希,而不是按偏移量。
约定
| 约定 | Value |
|---|---|
| Versioning | None of them is versionedno /v1, no version header, no deprecation policy. A field can change shape between two deployments. Read defensively and pin nothing. |
| Schema | There is no OpenAPI documentand no JSON Schema anywhere in the repository. Every shape documented here was read out of a route handler, which is why each record names the line. |
| Errors | {"error": "..."}a human-readable string, occasionally with a second key. There are no error codes and no stable error strings - match on the HTTP status, never on the message. |
| Caching | cache-control: no-storeon every route of all four services, without exception. Nothing here is safe to cache, and nothing offers you an ETag to revalidate against. |
| Content type | JSONwith two exceptions: the explorer's logo route returns raw image bytes, and its live route is an event stream. |
| Units | not uniformtoken amounts and wei are DECIMAL STRINGS, not numbers, because they do not survive a double. Mini-block timestamps are in MICROSECONDS. Concentrated-pool swap amounts are SIGNED. |
金额是字符串,而且其中有些是带符号的
代币数量、wei 和余额自始至终都以十进制字符串回来,因为它们挺不过一个 double。请用大整数类型 解析它们。在集中流动性池的路由上,这些金额还是带符号的,每一笔兑换都有一条腿是负的,而那就是 这笔交易的方向,所以一个不取绝对值的求和会把一整个市场加成零。
// The shape of a careful call against any of these: a timeout, a status check
// before the body, and a big-integer parse of anything that looks like money.
const res = await fetch(`${BASE}/pools`, { signal: AbortSignal.timeout(5000) });
if (!res.ok) throw new Error(`pools: ${res.status}`); // never the message
const { pools } = await res.json(); // an object, not an array
const reserve0 = BigInt(pools[0].reserve0); // a decimal string当其中一个挂了
这四个各自以不同的方式失败,而其中只有一种失败从外面看起来像失败。
- DEX API 回答
503 index not ready,发生在数据库连不上、或者索引器从未运行过因而那些表不存在的时候。那是诚实的情形。不诚实的 那一种,是一个已经停掉的索引器:每一条路由都继续用它到达过的状态作答,而只有/health上的lagBlocks会说出来。 - 浏览器在节点连不上时继续提供索引,而一个答案里需要实时读取的那些部分, 一个余额、一条域名服务记录、费用路由器的待付余额,会回来是 null 或者干脆缺席,而不是报错。
/stats上的indexLag字段是该盯的那个数字。 - 水龙头回答
502,发生在节点不响应的时候,而那时什么都还没签。一个503表示它没钱了,或者那次 滴水 revert 了。 - Arcade 的
/ready在它连到节点的 socket 断掉时回答 503,或者在它最后施加的那个迷你区块超过五秒之前 时回答 503。改用到达时间来衡量的话,一个卡死的进程会在什么都没动的情况下回答 200。
只有水龙头会写,而它返回的一个 502 意味着什么都没提交,那一个重试是安全的。 交易已经发出去之后的一个 503 不是:那次滴水可能已经落地,而配额预留已经被 回滚了,所以请去链上轮询那份回执,而不是再发一次。
哪些东西记录在别处
- The node. Chain state, transactions, logs and subscriptions are JSON-RPC and are documented in this reference's own RPC pages, not here.
- Contracts. What the pools, the faucet and the games actually do on chain - their functions, their events and their revert reasons - is the contract reference.
- Prices. Several answers here carry a dollar figure or a price snapshot. Those fields come from a layer this page does not own; read its own reference for what the number means, how old it is and when it is absent.