@blazephoenix/sdk 0.5.2 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,203 +3,185 @@
3
3
  Official TypeScript SDK for **[BlazePhoenix](https://blazephoenix.xyz)** — the on-chain
4
4
  DEX aggregator on **Base · Ethereum · Optimism · Arbitrum · Robinhood Chain**.
5
5
 
6
- Every number the API serves is computed **on-chain** by the Quoter contract
7
- (`previewPlan`) — your bot, your frontend and the site itself all read the same truth.
8
- **No API key. No signup. Open CORS.**
9
-
10
- > Live docs & playground: **https://blazephoenix.xyz/?tab=api**
6
+ **1.x runs 100% on YOUR RPC.** Every quote is an `eth_call` from your process to your
7
+ node, against the Quoter contract that settles the swap. There is no BlazePhoenix API
8
+ in the read path, no key, and the package ships no RPC endpoints (a test enforces it).
9
+ We don't pay for your reads, and you don't depend on our servers being up.
11
10
 
12
11
  ```bash
13
- npm i github:blazephoenixxyz-crypto/SDK # builds on install — no registry needed
14
- # (or, once published to npm:) npm i @blazephoenix/sdk
15
- # optional (only for the on-chain module: quoteOnChain / watchFills / getFills):
16
- npm i viem
12
+ npm i @blazephoenix/sdk viem
13
+ # or straight from GitHub (builds on install): npm i github:blazephoenixxyz-crypto/SDK viem
17
14
  ```
18
15
 
19
- ## Quote → approve → send (the whole loop)
16
+ ## Quote → build → execute
20
17
 
21
18
  ```ts
22
- import { BlazePhoenix, buildSwapTx, buildApproveTx, toBaseUnits } from '@blazephoenix/sdk';
23
-
24
- const blaze = new BlazePhoenix();
25
- const amountIn = toBaseUnits('1.5', 18); // "1.5" WETH → 1500000000000000000n
26
-
27
- const q = await blaze.quote({
28
- chain: 'base', // 8453 | 'base' | 1 | 'eth' | 10 | 42161 …
29
- tokenIn: 'WETH', // 0x-address, ETH/WETH/USDC/BZPX — or ANY traded
30
- // symbol ('TOSHI', 'DEGEN'…): resolved to the
31
- // deepest-liquidity token, echoed in q.resolved
32
- tokenOut: 'USDC',
33
- amountIn,
34
- recipient: '0xYOU', // ← makes the API return ready-to-send calldata
35
- slippageBps: 50,
19
+ import { BlazePhoenix } from '@blazephoenix/sdk';
20
+
21
+ const blaze = new BlazePhoenix({
22
+ rpc: { base: process.env.BASE_RPC_URL }, // YOUR node — any provider's free tier is enough
36
23
  });
37
24
 
38
- // once per token: allow the Router to pull tokenIn (exact amount — or MAX_UINT256)
39
- await wallet.sendTransaction(buildApproveTx({ chain: 'base', token: q.tokenIn, amount: amountIn }));
40
- // then execute exactly what was quoted:
41
- await wallet.sendTransaction(buildSwapTx(q)); // { to, data, value }
25
+ // 1) the Quoter's preview, read on your node ("amount" is human units)
26
+ const q = await blaze.quote({ chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amount: '1.5' });
27
+ q.amountOut; // net output after the 0.28% fee (bigint, base units)
28
+ q.checks.verdict; // Phoenix Check: ok | caution | danger | blocked (fails closed)
29
+ q.version; // protocol version that answered ('1.0.0' today, '2.0.0' once deployed)
30
+
31
+ // 2) the swap transaction(s): verified calldata + the approval step if needed
32
+ const plan = await blaze.buildSwap({
33
+ chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amount: '1.5',
34
+ recipient: me, from: me, // `from` enables the allowance check
35
+ slippageBps: 50, // default 50; never below the on-chain floor
36
+ });
37
+ plan.steps; // [approve?, swap] — { chainId, to, data, value }
38
+ plan.minOut; // the minimum the Router will enforce for you
39
+ plan.encodedBy; // 'quoter' on 2.x (the Quoter's own bytes, verified) · 'sdk' on 1.x
40
+
41
+ // 3) optional: dry-run on your node, then execute with YOUR wallet
42
+ await blaze.simulate(plan, me); // { ok, amountOut } or the decoded revert
43
+ const res = await blaze.execute({ wallet, plan }); // approve → simulate → swap → receipt
44
+ res.amountOut; // realised, from the Router's Swap event
42
45
  ```
43
46
 
44
- ## Batch quotes (screeners / arb loops)
47
+ `execute` takes a viem `WalletClient`. The SDK never asks for, stores or touches a key.
45
48
 
46
- ```ts
47
- const { results } = await blaze.quoteBatch([
48
- { chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amountIn: 10n ** 18n },
49
- { chain: 'base', tokenIn: 'WETH', tokenOut: 'BZPX', amountIn: 10n ** 18n },
50
- { chain: 'arbitrum', tokenIn: 'WETH', tokenOut: 'USDC', amountIn: 10n ** 18n },
51
- ]); // one round-trip (max 10), per-item errors inline
52
- ```
53
-
54
- ## Deep links (bot buttons / referral posts)
49
+ ## Your RPC, your way
55
50
 
56
51
  ```ts
57
- import { deepLink } from '@blazephoenix/sdk';
58
-
59
- deepLink({ chain: 'base', tokenIn: 'ETH', tokenOut: 'BZPX', amount: '0.5' });
60
- // → https://blazephoenix.xyz/?tab=swap&chain=8453&in=ETH&out=BZPX&amt=0.5
52
+ new BlazePhoenix({ rpc: 'https://your-base-node.example/KEY' }); // one node — its own chain
53
+ new BlazePhoenix({ rpc: ['https://node-a.example', 'https://node-b.example'] }); // your fallback order
54
+ new BlazePhoenix({ rpc: window.ethereum }); // the user's wallet (EIP-1193)
55
+ new BlazePhoenix({ rpc: http('https://…', { batch: true }) }); // any viem Transport
56
+ new BlazePhoenix({ rpc: { base: '…', eth: ['…', '…'], arbitrum: provider } }); // per chain
57
+ new BlazePhoenix(); // reads BLAZEPHOENIX_RPC_BASE / _ETHEREUM / _OPTIMISM / _ARBITRUM / _ROBINHOOD
58
+ // (comma-separated for fallback) or BLAZEPHOENIX_RPC_URL
61
59
  ```
62
60
 
63
- ## On-chain module (optional `viem` peer)
61
+ - Each node is asked for `eth_chainId` once. Quoting Base contracts on an Ethereum node
62
+ throws `rpc_chain_mismatch` instead of returning garbage.
63
+ - With a single node, `chain` may be omitted: the node's own chain is used.
64
+ - A chain without a configured node throws `rpc_required` — nothing silently falls back
65
+ to someone else's infrastructure.
66
+ - `https://` and `wss://` only (`http://` allowed for `localhost` — your anvil/geth).
67
+ URLs are redacted in every error message and in its `cause` chain, because keys live in them.
64
68
 
65
- Zero middlemen — same numbers, straight from the chain. **RPC optional since
66
- v0.4.0**: omit `rpcUrl` and the SDK falls back across public keyless endpoints
67
- (bring your own node for production throughput). Still zero providers and zero
68
- keys shipped — enforced by tests.
69
+ ## Versions — pick yours
69
70
 
70
- ```ts
71
- import { quoteOnChain, watchFills, getFills } from '@blazephoenix/sdk';
71
+ Protocol versions are deployments, not SDK versions:
72
72
 
73
- const { preview } = await quoteOnChain({
74
- chain: 'base', rpcUrl: RPC,
75
- tokenIn: '0x4200000000000000000000000000000000000006',
76
- tokenOut: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
77
- amountIn: 10n ** 18n,
78
- });
73
+ | version | status | what it is |
74
+ |---|---|---|
75
+ | `1.0.0` | **live** on all five chains | the generation deployed at launch |
76
+ | `2.0.0` | pending | the final Core / Hub / Solver / Quoter / Router from [BlazePhoenix-Dex](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex) (`VERSION()` = `"2.0.0"`) |
79
77
 
80
- const unwatch = await watchFills({
81
- chain: 'base', rpcUrl: RPC,
82
- onFill: (f) => console.log(f.txHash, f.amountIn, '→', f.amountOut),
83
- });
78
+ ```ts
79
+ new BlazePhoenix({ rpc, version: 'latest' }); // default: newest version deployed on each chain
80
+ new BlazePhoenix({ rpc, version: '1' }); // stay on 1.x
81
+ await blaze.quote({ ...req, version: '2.0.0' }); // per call
84
82
  ```
85
83
 
86
- `getFills({ chain, rpcUrl, fromBlock, toBlock })` gives you the raw fill history —
87
- the input for the **quoted-vs-executed verification pattern** documented on the
88
- API page: re-run each fill's quote at its block and compare with the executed
89
- amount the event recorded. Verify us, don't trust us.
90
-
91
- ## Resilient by default (v0.3.0)
84
+ The SDK adapts to what each version can do (`featuresOf(version)`):
92
85
 
93
- Every call ships the same meta-patterns the BlazePhoenix edge runs — no config:
94
-
95
- - identical concurrent calls **coalesce into one request** (singleflight)
96
- - preview quotes ride a **1s micro-cache** (`cacheTtlMs`; never applied to
97
- `recipient`/`exact` requests — execution data stays fresh, always)
98
- - transient failures (network, 429, 502–504) **retry with backoff** and honour
99
- the server's `retry-after` (`retries`, default 2)
86
+ | capability | 1.x | 2.x |
87
+ |---|---|---|
88
+ | preview + Router calldata in ONE call (`previewAndEncode`) | — (encoded by the SDK) | ✅ verified field by field |
89
+ | native ETH in, no pre-wrap (`swapExactInNative`) | wrap first: `buildWrapTx` | ✅ |
90
+ | solve + execute in the same tx (`mode: 'best'` → `swapBestExactIn`) | — | ✅ |
91
+ | on-chain `batchQuote` | parallel previews | ✅ chunked |
92
+ | `ExecutionProof` on every fill (quoted vs realised vs floor) | — | ✅ |
93
+
94
+ ### New deployments reach you without a new SDK release
95
+
96
+ The registry has three layers:
97
+
98
+ 1. **Embedded** — shipped in the package. Non-zero addresses are **pins**: nothing fetched
99
+ at runtime can move them.
100
+ 2. **Remote** — `GET https://blazephoenix.xyz/api/deployments` (static JSON, refreshed every
101
+ 10 min in the background; the first lookup waits at most 3 s). It can only fill what the
102
+ snapshot doesn't know — e.g. the 2.0.0 addresses the day they are deployed — and only for
103
+ versions that can be verified on-chain (2.x+). Every remote-sourced set is **verified on
104
+ your RPC before first use**:
105
+ code at every address, `VERSION()` matches, and the Quoter, Router and Solver all point at
106
+ the same Hub and Solver. Anything else fails closed (`deployment_unverified`).
107
+ 3. **Your overrides** — always win:
100
108
 
101
109
  ```ts
102
- // a price loop in one line — overlap-safe, stop() when done:
103
- import { pollQuote } from '@blazephoenix/sdk';
104
- const stop = pollQuote(blaze,
105
- { chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amountIn: 10n ** 18n },
106
- (q) => console.log('WETH→USDC', q.amountOut),
107
- { intervalMs: 4000 });
110
+ new BlazePhoenix({
111
+ rpc,
112
+ contracts: { base: { router: '0x…', quoter: '0x…', version: '2.0.0' } }, // your fork / audit target
113
+ registry: { mode: 'embedded' }, // never touch the network for addresses
114
+ });
115
+ await blaze.deployments(); // the chains × versions table this client sees
116
+ await blaze.verifyDeployment({ chain: 'base' }); // run the on-chain checks yourself
108
117
  ```
109
118
 
110
- ## 🔥 Phoenix Bot — zero-custody Telegram bot
111
-
112
- [`examples/phoenix-bot.ts`](examples/phoenix-bot.ts) is a complete Telegram bot
113
- with a difference: **it never holds a key**. It quotes on-chain truth, streams
114
- real fills (`/watch`), and executes by deep-linking users into their OWN wallet.
115
- The famous trading bots custody your funds; this one can't lose what it never
116
- touches.
117
-
118
- ```bash
119
- npm i grammy viem
120
- BOT_TOKEN=... RPC_URL=https://your-base-rpc npx tsx examples/phoenix-bot.ts
121
- ```
119
+ ## Why the calldata can be trusted
122
120
 
123
- ## Bring your own RPC (optional, v0.5.0)
121
+ On 2.x, `Quoter.previewAndEncode` returns the preview **and** the exact `swapExactIn` bytes that
122
+ execute it. The Quoter's own source says what that is worth — *"a compromised Quoter fools the
123
+ interface"* — so the SDK never forwards those bytes blind. It decodes them with the Router ABI
124
+ and refuses (`calldata_mismatch`) unless the amount, recipient, deadline, the route
125
+ (identical to the previewed one, connecting tokenIn → tokenOut) and a minimum at least the
126
+ on-chain floor all match what you asked. Your slippage then only ever **tightens** that minimum.
124
127
 
125
- Set `rpc` once on the client and every read goes through **your** node instead
126
- of the shared pool:
128
+ ## More
127
129
 
128
130
  ```ts
129
- const blaze = new BlazePhoenix({ rpc: process.env.MY_RPC });
130
- // or per call:
131
- await blaze.quote({ chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amountIn: 10n ** 18n, rpc });
131
+ await blaze.quoteExact(req); // previewPlanExact: every concentrated leg dry-run on the pool
132
+ await blaze.quoteBatch([req1, req2]); // per-item { ok, quote } | { ok:false, error }
133
+ await blaze.getFills({ chain: 'base', lookbackBlocks: 5_000n }); // Swap (+ ExecutionProof on 2.x)
134
+ const stop = await blaze.watchFills({ chain: 'base', onFill: console.log });
135
+ await blaze.tokenInfo('base', '0x…'); // symbol / name / decimals
136
+ await blaze.solvency(); // the staking engine's proof-of-solvency (Base)
137
+ toJSON(quote); // bigint → string, for HTTP / queues / LLMs
132
138
  ```
133
139
 
134
- The service stays **free and keyless either way** — this exists so sustained
135
- automation carries its own read volume, which is what keeps the free path
136
- viable for callers who cannot bring a node. Your node is tried first and the
137
- public pool remains the fallback, so supplying one can only make calls more
138
- reliable, never less. `meta.rpc` in the response reads `byo` or `shared`.
140
+ Pure helpers (no RPC): `buildApproveTx`, `buildWrapTx`, `buildUnwrapTx`, `permit2TypedData`,
141
+ `encodeSwapExactInWithPermit2`, `encodeSwapExactIn`, `verifySwapExactIn`, `minOutFor`,
142
+ `decodeBlazeError`, `toBaseUnits`, `fromBaseUnits`, `deepLink`.
139
143
 
140
- Any free tier is enough. Lock the key to your domain or IP in the provider
141
- dashboard if it will run in a browser. Full guide:
142
- <https://blazephoenix.xyz/learn/bring-your-own-rpc>
144
+ Tokens: `0x` addresses, or the universal symbols `ETH` (native), `WETH`, `USDC` (the chain's
145
+ dollar asset — USDG on Robinhood), `BZPX`. Anything else: pass the address. The SDK never
146
+ guesses a token from a ticker.
143
147
 
144
148
  ## Errors
145
149
 
146
- HTTP-level failures throw `BlazeApiError` with a stable `code`:
150
+ Every failure, `solvency()` included, is a `BlazeError` with a stable `code`; protocol reverts carry `revert`
151
+ (`RouterE` / `QuoterE` / `SolverE` / `HubE` + the reason from the contract's own source):
147
152
 
148
153
  | code | meaning |
149
154
  |---|---|
150
- | `bad_*` (400) | invalid parameter — message names the field |
151
- | `no_route` (422) | Quoter reverted: no executable route for this pair/size |
152
- | `rpc_unreachable` (502) | all upstream RPCs failed — retry shortly |
155
+ | `rpc_required` | no node configured for that chain |
156
+ | `rpc_chain_mismatch` | the node serves a different chain |
157
+ | `rpc_error` | your node failed (network, rate limit) |
158
+ | `no_route` | the Solver found no executable path (`SolverE(5)`) or the quote reverted |
159
+ | `not_executable` | the Quoter says the route cannot settle now (`canExecute = false`) |
160
+ | `calldata_mismatch` | Quoter bytes did not match your request — refused |
161
+ | `deployment_unverified` | a registry deployment failed on-chain verification |
162
+ | `unsupported_by_version` | e.g. native ETH input on a 1.x Router |
163
+ | `not_deployed` / `bad_request` / `wallet_chain_mismatch` / `reverted` | as named |
153
164
 
154
- ## Reference
165
+ ## MCP (AI agents)
155
166
 
156
- - REST endpoint: `GET https://blazephoenix.xyz/api/quote`
157
- - Batch: `POST https://blazephoenix.xyz/api/quote/batch`
158
- - Manifest (contracts / token / event topics): `GET https://blazephoenix.xyz/api/manifest`
159
- - Examples: [`examples/`](examples) — plain quote, Telegram bot, fill watcher
160
- - Protocol fee: 0.28% on quoted output; **execution surplus is fee-exempt → user**
167
+ [`@blazephoenix/mcp`](https://github.com/blazephoenixxyz-crypto/blazephoenix-mcp) wraps this SDK as
168
+ a local MCP server — the agent's tools run on your machine, on your RPC.
161
169
 
162
- ## Deployed contracts (verified)
170
+ For agents, [`llms.txt`](llms.txt) describes this package in the [llmstxt.org](https://llmstxt.org) format.
163
171
 
164
- ![solvency](https://img.shields.io/endpoint?url=https%3A%2F%2Fblazephoenix.xyz%2Fapi%2Fbadge)
165
- — live `isSolvent()` read from the Base staking contract ·
166
- [full proof-of-solvency report](https://blazephoenix.xyz/solvency)
172
+ ## Examples
167
173
 
168
- | Chain | Contract | Address |
169
- |---|---|---|
170
- | Base (8453) | Router | [`0x2a779f9Be49aac57495A8B6467Cc325a8a47Eb9f`](https://basescan.org/address/0x2a779f9Be49aac57495A8B6467Cc325a8a47Eb9f) |
171
- | Base (8453) | Quoter | [`0x4cEF0615614B212895F45Aa1D4833B16666E18d3`](https://basescan.org/address/0x4cEF0615614B212895F45Aa1D4833B16666E18d3) |
172
- | Base (8453) | Staking | [`0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77`](https://basescan.org/address/0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77) |
173
- | Base (8453) | BZPX token | [`0x23113e72165a034265Ab8Bf2277CCB7a85Cb7483`](https://basescan.org/address/0x23113e72165a034265Ab8Bf2277CCB7a85Cb7483) |
174
- | Ethereum (1) | Router | [`0xE1aE5f49013920CF71De8CED4043e14C4d63416b`](https://etherscan.io/address/0xE1aE5f49013920CF71De8CED4043e14C4d63416b) |
175
- | Ethereum (1) | Quoter | [`0x4a20AA0912388ff7A9221Ab6BFC224cc20Baa0c3`](https://etherscan.io/address/0x4a20AA0912388ff7A9221Ab6BFC224cc20Baa0c3) |
176
- | Optimism (10) | Router | [`0x7262e7483ab6f0db7b8f90eC3a9de3B02Ab36F6A`](https://optimistic.etherscan.io/address/0x7262e7483ab6f0db7b8f90eC3a9de3B02Ab36F6A) |
177
- | Optimism (10) | Quoter | [`0xfB18EF6f62A0278A273Af4b7A46b454F9E482dc2`](https://optimistic.etherscan.io/address/0xfB18EF6f62A0278A273Af4b7A46b454F9E482dc2) |
178
- | Arbitrum (42161) | Router | [`0x7262e7483ab6f0db7b8f90eC3a9de3B02Ab36F6A`](https://arbiscan.io/address/0x7262e7483ab6f0db7b8f90eC3a9de3B02Ab36F6A) |
179
- | Arbitrum (42161) | Quoter | [`0xfB18EF6f62A0278A273Af4b7A46b454F9E482dc2`](https://arbiscan.io/address/0xfB18EF6f62A0278A273Af4b7A46b454F9E482dc2) |
180
-
181
- Always cross-check against the live manifest: `https://blazephoenix.xyz/api/manifest`.
182
-
183
- ## Learn the engineering
184
-
185
- The protocol is documented from the deployed bytecode — every article ends with
186
- a command to reproduce its claims:
187
-
188
- - **The mathematics** (every formula; the originals are ours): https://blazephoenix.xyz/learn/the-mathematics
189
- - **Integrate any bot** (keyless / keyed / ethers / viem / Python / AI-agent): https://blazephoenix.xyz/learn/integrate-any-bot
190
- - **Web 2.5 vs Web 3.0** (the decision on-chain, not just settlement): https://blazephoenix.xyz/learn/web25-vs-web30
191
- - **Machine-readable measured cases**: https://blazephoenix.xyz/measured-cases.json
192
- - **Agent guide**: https://blazephoenix.xyz/llms.txt
193
-
194
- ## Citing BlazePhoenix
195
-
196
- This repository ships a `CITATION.cff`, so GitHub shows a **“Cite this
197
- repository”** button. Please cite with attribution and a link — the protocol's
198
- original constructions (the Iron Law Φ, the Vitality Field Ψ, the
199
- Capital-Anchored Filter, the Master Conservation Identity) are © 2026
200
- BlazePhoenix.
174
+ [`examples/`](examples): `quote.ts`, `watch-fills.ts`, `telegram-bot.ts`, and
175
+ [`phoenix-bot.ts`](examples/phoenix-bot.ts) — a complete zero-custody Telegram bot on your own nodes.
176
+
177
+ ## Regenerating the ABIs
178
+
179
+ The protocol ABIs are compiled from the Solidity sources, never written by hand:
180
+
181
+ ```bash
182
+ npm i --no-save solc@0.8.36 && npm run gen:abis # expects ../Blaze-Phoenix-Dex
183
+ ```
201
184
 
202
185
  ## License
203
186
 
204
- MIT — the SDK is deliberately permissive so anyone can integrate.
205
- (The BlazePhoenix protocol and site carry their own licenses.)
187
+ MIT — deliberately permissive so anyone can integrate. The protocol contracts are BUSL-1.1.