@blazephoenix/sdk 1.0.1 → 1.1.0

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
@@ -13,6 +13,31 @@ npm i @blazephoenix/sdk viem
13
13
  # or straight from GitHub (builds on install): npm i github:blazephoenixxyz-crypto/SDK viem
14
14
  ```
15
15
 
16
+ ## BlazePhoenix in one sentence
17
+
18
+ BlazePhoenix is the fully on-chain DEX aggregator — the route is computed inside the transaction that executes it — paired with an oracle-free staking engine that proves its own solvency on every transaction. It runs on Base, Ethereum, Optimism, Arbitrum and Robinhood Chain.
19
+
20
+ **Swap** in the [app](https://blazephoenix.xyz/?tab=swap) · **integrate** with the [SDK](https://github.com/blazephoenixxyz-crypto/SDK) (`npm i @blazephoenix/sdk`) · **connect an agent** to the [MCP server](https://github.com/blazephoenixxyz-crypto/blazephoenix-mcp) (`https://blazephoenix.xyz/mcp`, no key)
21
+
22
+ ## Reading guide by level
23
+
24
+ - **New to DeFi:** [BlazePhoenix in five minutes](https://blazephoenix.xyz/learn/blazephoenix-in-five-minutes), then [What is a DEX aggregator](https://blazephoenix.xyz/learn/what-is-a-dex-aggregator) and [How to swap tokens safely](https://blazephoenix.xyz/learn/how-to-swap-safely).
25
+ - **Comfortable with swaps:** [Anatomy of a DEX aggregator](https://blazephoenix.xyz/learn/anatomy-of-an-aggregator) and [BlazePhoenix vs the established aggregators](https://blazephoenix.xyz/learn/how-we-compare).
26
+ - **Engineers and researchers:** the [Technical Whitepaper v2.3](https://blazephoenix.xyz/whitepaper.md) (DOI [10.5281/zenodo.23084091](https://doi.org/10.5281/zenodo.23084091)), the contract source in [Blaze-Phoenix-Dex](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex) and [Blaze-Phoenix-Staking](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Staking), and [On-chain quoting](https://blazephoenix.xyz/learn/on-chain-quoting).
27
+ - **Models and agents:** [https://blazephoenix.xyz/llms.txt](https://blazephoenix.xyz/llms.txt) and the [reading guide](https://blazephoenix.xyz/reading-guide.md).
28
+
29
+ ## Trust and verification
30
+
31
+ Each item links to the place where it can be checked. The full list is at [https://blazephoenix.xyz/trust](https://blazephoenix.xyz/trust).
32
+
33
+ - **Verified contracts:** source-verified on Sourcify and the chain explorers; every address per chain at [/api/deployments](https://blazephoenix.xyz/api/deployments).
34
+ - **Source available:** the contract source is public under the [Business Source License 1.1](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex/blob/main/LICENSE), converting to GPL-2.0-or-later on 2030-06-01.
35
+ - **Public CI:** [GitHub Actions](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex/actions) on every push, including the Certora Prover and Halmos.
36
+ - **Bug bounty:** a [public programme](https://blazephoenix.xyz/bounty) with [credited researchers](https://blazephoenix.xyz/security/researchers) and [published advisories](https://blazephoenix.xyz/security/advisories).
37
+ - **Whitepaper DOI:** [10.5281/zenodo.23084091](https://doi.org/10.5281/zenodo.23084091); papers timestamped with [OpenTimestamps](https://blazephoenix.xyz/provenance/provenance.json).
38
+ - **Contact:** contact@blazephoenix.xyz ([security.txt](https://blazephoenix.xyz/.well-known/security.txt)); official channels at [/official](https://blazephoenix.xyz/official).
39
+ - **Audit status:** an independent external audit is scheduled before launch.
40
+
16
41
  ## Quote → build → execute
17
42
 
18
43
  ```ts
@@ -91,17 +116,20 @@ The SDK adapts to what each version can do (`featuresOf(version)`):
91
116
  | on-chain `batchQuote` | parallel previews | ✅ chunked |
92
117
  | `ExecutionProof` on every fill (quoted vs realised vs floor) | — | ✅ |
93
118
 
94
- ### New deployments reach you without a new SDK release
119
+ ### Only pinned addresses are routed to by default
95
120
 
96
121
  The registry has three layers:
97
122
 
98
123
  1. **Embedded** — shipped in the package. Non-zero addresses are **pins**: nothing fetched
99
- at runtime can move them.
124
+ at runtime can move them, and by default they are the only Router and Quoter addresses
125
+ a quote or a swap uses. A new deployment reaches you with a new SDK release.
100
126
  2. **Remote** — `GET https://blazephoenix.xyz/api/deployments` (static JSON, refreshed every
101
127
  10 min in the background; the first lookup waits at most 3 s). It can only fill what the
102
128
  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**:
129
+ versions that can be verified on-chain (2.x+). Those versions are listed, but routed to
130
+ only with `registry: { remoteVersions: true }`: the on-chain check reads the entry's own
131
+ contracts, so it cannot tell a real deployment from a look-alike. With the opt-in, every
132
+ remote-sourced set is still **verified on your RPC before first use**:
105
133
  code at every address, `VERSION()` matches, and the Quoter, Router and Solver all point at
106
134
  the same Hub and Solver. Anything else fails closed (`deployment_unverified`).
107
135
  3. **Your overrides** — always win:
package/dist/index.cjs CHANGED
@@ -5356,6 +5356,7 @@ var Registry = class {
5356
5356
  fetchedAt = 0;
5357
5357
  lastError;
5358
5358
  mode;
5359
+ remoteVersions;
5359
5360
  url;
5360
5361
  ttlMs;
5361
5362
  fetchFn;
@@ -5364,6 +5365,7 @@ var Registry = class {
5364
5365
  overrides = /* @__PURE__ */ new Map();
5365
5366
  constructor(opts = {}, overrides) {
5366
5367
  this.mode = opts.mode ?? "auto";
5368
+ this.remoteVersions = opts.remoteVersions ?? false;
5367
5369
  this.url = opts.url ?? DEPLOYMENTS_URL;
5368
5370
  this.ttlMs = opts.ttlMs ?? 10 * 6e4;
5369
5371
  this.fetchFn = opts.fetchFn ?? globalThis.fetch;
@@ -5495,7 +5497,7 @@ var Registry = class {
5495
5497
  if (!isValidSelector(selector)) throw new BlazeError("bad_request", `bad version selector "${selector}"`);
5496
5498
  const ov = this.overrides.get(chainId);
5497
5499
  const sel = ov?.version && selector === "latest" ? ov.version : selector;
5498
- const candidates = this.doc.versions.filter((v) => matchesSelector(v.version, sel) && isDeployed(v.chains[chainId])).sort((a, b) => compareVersions(b.version, a.version));
5500
+ const candidates = this.doc.versions.filter((v) => matchesSelector(v.version, sel) && isDeployed(v.chains[chainId])).filter((v) => this.remoteVersions || this.isPinned(v.version, chainId)).sort((a, b) => compareVersions(b.version, a.version));
5499
5501
  const pick = candidates[0];
5500
5502
  if (ov && (ov.router || ov.quoter)) {
5501
5503
  const base = pick ? { ...pick.chains[chainId] } : {
@@ -5515,9 +5517,10 @@ var Registry = class {
5515
5517
  }
5516
5518
  if (!pick) {
5517
5519
  const deployed = this.versionsOn(chainId);
5520
+ const remoteOnly = this.remoteVersions ? [] : deployed.filter((v) => matchesSelector(v, sel) && !this.isPinned(v, chainId));
5518
5521
  throw new BlazeError(
5519
5522
  "not_deployed",
5520
- `no BlazePhoenix deployment matching version "${selector}" on chain ${chainId}` + (deployed.length ? ` (deployed: ${deployed.join(", ")})` : ""),
5523
+ `no BlazePhoenix deployment matching version "${selector}" on chain ${chainId}` + (deployed.length ? ` (deployed: ${deployed.join(", ")})` : "") + (remoteOnly.length ? ` \u2014 ${remoteOnly.join(", ")} is known only from the fetched registry; update the SDK, or set registry.remoteVersions after checking the addresses yourself` : ""),
5521
5524
  { details: { chainId, selector, deployed } }
5522
5525
  );
5523
5526
  }
@@ -5526,6 +5529,11 @@ var Registry = class {
5526
5529
  const source = src("router") === "remote" || src("quoter") === "remote" ? "remote" : "embedded";
5527
5530
  return { chainId, version: pick.version, status: pick.status, contracts: { ...set }, source };
5528
5531
  }
5532
+ /** Router and Quoter both pinned in this package (not filled by a fetched document). */
5533
+ isPinned(version, chainId) {
5534
+ const src = (k) => this.fieldSource[`${version}|${chainId}|${k}`];
5535
+ return src("router") === "embedded" && src("quoter") === "embedded";
5536
+ }
5529
5537
  latestKnownVersion(selector) {
5530
5538
  const all = this.doc.versions.map((v) => v.version).sort((a, b) => compareVersions(b, a));
5531
5539
  const m = selector ? all.find((v) => matchesSelector(v, selector)) : void 0;
@@ -5773,16 +5781,19 @@ var BlazePhoenix = class {
5773
5781
  return { address: c.weth, native: true };
5774
5782
  case "WETH":
5775
5783
  return { address: c.weth, native: false };
5784
+ // `usdc` holds each chain's one dollar asset: USDG on Robinhood Chain, USDC
5785
+ // elsewhere. The other symbol names a different token there, so it is refused.
5776
5786
  case "USDC":
5777
5787
  case "USDG":
5778
- return { address: c.usdc, native: false };
5788
+ if (s.toUpperCase() === (chainId === 4663 ? "USDG" : "USDC")) return { address: c.usdc, native: false };
5789
+ break;
5779
5790
  case "BZPX":
5780
5791
  if (c.bzpx) return { address: c.bzpx, native: false };
5781
5792
  break;
5782
5793
  }
5783
5794
  throw new BlazeError(
5784
5795
  "bad_request",
5785
- `${side}: "${ref}" is not a 0x address or a built-in symbol (ETH, WETH, USDC, BZPX) on ${c.name} \u2014 pass the token address`
5796
+ `${side}: "${ref}" is not a 0x address or a built-in symbol (ETH, WETH, ${chainId === 4663 ? "USDG" : "USDC"}, BZPX) on ${c.name} \u2014 pass the token address`
5786
5797
  );
5787
5798
  }
5788
5799
  async decimalsOf(client, chainId, t) {
@@ -6172,6 +6183,12 @@ var BlazePhoenix = class {
6172
6183
  if (r.status !== "success") throw new BlazeError("reverted", `approval transaction reverted (${approvalHash})`);
6173
6184
  }
6174
6185
  plan = { ...plan, approval: { ...plan.approval, current: plan.approval.amount } };
6186
+ if (Date.now() - plan.quote.quotedAt > maxAge) {
6187
+ if (!req) {
6188
+ throw new BlazeError("bad_request", `plan went older than ${maxAge}ms while the approval confirmed${approvalHash ? ` (${approvalHash})` : ""} \u2014 the swap was NOT sent; rebuild the plan, or pass \`request\` so it can be rebuilt`);
6189
+ }
6190
+ plan = await this.buildSwap({ ...req, from: req.from ?? account.address });
6191
+ }
6175
6192
  }
6176
6193
  if (opts.simulate ?? true) {
6177
6194
  const sim = await this.simulate(plan, account.address);