@blazephoenix/sdk 1.2.0 → 1.3.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 +17 -6
- package/dist/index.cjs +44 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -2
- package/dist/index.d.ts +16 -2
- package/dist/index.js +44 -9
- package/dist/index.js.map +1 -1
- package/llms.txt +3 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ npm i @blazephoenix/sdk viem
|
|
|
15
15
|
|
|
16
16
|
## BlazePhoenix in one sentence
|
|
17
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.
|
|
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. V2, the generation now in development, targets Base, Optimism, Arbitrum, Robinhood Chain and further L2s and is not planned for Ethereum L1. The V1 generation deployed today runs on Base, Ethereum, Optimism, Arbitrum and Robinhood Chain.
|
|
19
19
|
|
|
20
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
21
|
|
|
@@ -53,12 +53,14 @@ q.amountOut; // net output after the 0.28% fee (bigint, base units)
|
|
|
53
53
|
q.checks.verdict; // Phoenix Check: ok | caution | danger | blocked (fails closed)
|
|
54
54
|
q.version; // protocol version that answered ('1.0.0' today, '2.0.0' once deployed)
|
|
55
55
|
|
|
56
|
-
// 2) the swap transaction(s): verified
|
|
56
|
+
// 2) the swap transaction(s): calldata verified against the request (swap math; `simulate: true`
|
|
57
|
+
// asks the Router from `from` whether it settles) + the approval step if needed
|
|
57
58
|
const plan = await blaze.buildSwap({
|
|
58
59
|
chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amount: '1.5',
|
|
59
60
|
recipient: me, from: me, // `from` enables the allowance check
|
|
60
61
|
slippageBps: 50, // default 50; never below the on-chain floor
|
|
61
62
|
exact: true, // optional: minOut from previewPlanExact (one more eth_call)
|
|
63
|
+
simulate: true, // optional: eth_call the swap from `from`; a revert throws not_executable
|
|
62
64
|
});
|
|
63
65
|
plan.steps; // [approve?, swap] — { chainId, to, data, value }
|
|
64
66
|
plan.minOut; // the minimum the Router will enforce for you
|
|
@@ -71,6 +73,12 @@ const res = await blaze.execute({ wallet, plan }); // approve → simulate → s
|
|
|
71
73
|
res.amountOut; // realised, from the Router's Swap event
|
|
72
74
|
```
|
|
73
75
|
|
|
76
|
+
The Quoter's `canExecute` is swap math: it does not see what the tokens do to the payer (a transfer tax,
|
|
77
|
+
a per-account cap, the payer's own balance). Whether the bytes settle is answered only by the Router's own
|
|
78
|
+
call from the account that will send them: `simulate(plan, from)`, `buildSwap({ simulate: true })` or
|
|
79
|
+
`execute`. With `simulate: true` and the approval still missing, no eth_call is made: `plan.simulation` is
|
|
80
|
+
`{ ok: false, error.name: 'allowance' }`; send the approval, then simulate again before the swap.
|
|
81
|
+
|
|
74
82
|
`execute` takes a viem `WalletClient`. The SDK never asks for, stores or touches a key.
|
|
75
83
|
|
|
76
84
|
## Your RPC, your way
|
|
@@ -100,7 +108,7 @@ Protocol versions are deployments, not SDK versions:
|
|
|
100
108
|
| version | status | what it is |
|
|
101
109
|
|---|---|---|
|
|
102
110
|
| `1.0.0` | **live** on all five chains | the generation deployed at launch |
|
|
103
|
-
| `2.0.0` | pending | the
|
|
111
|
+
| `2.0.0` | pending | the generation in development (Router, Solver, Hub, Quoter and Gate, with the Core and Math libraries; Balancer V2/V3 and Curve NG are reached through the Gate), targeting Base, Optimism, Arbitrum, Robinhood Chain and further L2s and not Ethereum L1, from [BlazePhoenix-Dex](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex) (`VERSION()` = `"2.0.0"`) |
|
|
104
112
|
|
|
105
113
|
```ts
|
|
106
114
|
new BlazePhoenix({ rpc, version: 'latest' }); // default: newest version deployed on each chain
|
|
@@ -158,7 +166,9 @@ on-chain floor all match what you asked. Your slippage then only ever **tightens
|
|
|
158
166
|
## More
|
|
159
167
|
|
|
160
168
|
```ts
|
|
161
|
-
await blaze.quoteExact(req); // previewPlanExact: every concentrated leg dry-run on
|
|
169
|
+
await blaze.quoteExact(req); // previewPlanExact: every concentrated leg dry-run on its venue.
|
|
170
|
+
// `exactOut` is net of the protocol fee on every Quoter; `quoterOut` is the
|
|
171
|
+
// scalar exactly as the Quoter returned it (gross of the fee on a 1.x Quoter)
|
|
162
172
|
await blaze.quoteBatch([req1, req2]); // per-item { ok, quote } | { ok:false, error }
|
|
163
173
|
await blaze.getFills({ chain: 'base', lookbackBlocks: 5_000n }); // Swap (+ ExecutionProof on 2.x)
|
|
164
174
|
const stop = await blaze.watchFills({ chain: 'base', onFill: console.log });
|
|
@@ -168,7 +178,8 @@ toJSON(quote); // bigint → string, for HTTP / queues /
|
|
|
168
178
|
```
|
|
169
179
|
|
|
170
180
|
Pure helpers (no RPC): `buildApproveTx`, `buildWrapTx`, `buildUnwrapTx`, `permit2TypedData`,
|
|
171
|
-
`encodeSwapExactInWithPermit2`, `encodeSwapExactIn`, `
|
|
181
|
+
`encodeSwapExactInWithPermit2`, `encodeSwapExactIn`, `encodeSwapExactInNative`, `encodeSwapBestExactIn`,
|
|
182
|
+
`decodeSwapExactIn`, `verifySwapExactIn`, `tightenMinOut`, `minOutFor`, `phoenixCheck`,
|
|
172
183
|
`decodeBlazeError`, `toBaseUnits`, `fromBaseUnits`, `deepLink`.
|
|
173
184
|
|
|
174
185
|
Tokens: `0x` addresses, or the universal symbols `ETH` (native), `WETH`, `USDC` (the chain's
|
|
@@ -186,7 +197,7 @@ Every failure, `solvency()` included, is a `BlazeError` with a stable `code`; pr
|
|
|
186
197
|
| `rpc_chain_mismatch` | the node serves a different chain |
|
|
187
198
|
| `rpc_error` | your node failed (network, rate limit) |
|
|
188
199
|
| `no_route` | the Solver found no executable path (`SolverE(5)`) or the quote reverted |
|
|
189
|
-
| `not_executable` | the Quoter says the route cannot
|
|
200
|
+
| `not_executable` | the Quoter's swap math says the route cannot execute now (`canExecute = false`), or, with `simulate: true`, the Router's eth_call from `from` reverts |
|
|
190
201
|
| `calldata_mismatch` | Quoter bytes did not match your request — refused |
|
|
191
202
|
| `min_implausible` | `buildSwap`: `userMinOut` below the quote net of `slippageBps` (or the on-chain floor) |
|
|
192
203
|
| `min_above_exact` | `buildSwap({ exact: true })`: `userMinOut` above `exactOut` less the 50 bps safety margin |
|
package/dist/index.cjs
CHANGED
|
@@ -4768,7 +4768,8 @@ var CHAINS = {
|
|
|
4768
4768
|
explorer: "https://robinhoodchain.blockscout.com",
|
|
4769
4769
|
weth: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
4770
4770
|
usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
4771
|
-
blockTime:
|
|
4771
|
+
blockTime: 0.104
|
|
4772
|
+
// measured ~0.104 s/block
|
|
4772
4773
|
}
|
|
4773
4774
|
};
|
|
4774
4775
|
var ALIASES = {
|
|
@@ -4833,7 +4834,7 @@ function urlTransport(url, o) {
|
|
|
4833
4834
|
throw new BlazeError("bad_request", `refusing RPC URL "${redact(url)}": use https:// or wss:// (http/ws only for localhost)`);
|
|
4834
4835
|
}
|
|
4835
4836
|
const u = url.trim();
|
|
4836
|
-
return u.startsWith("ws") ? viem.webSocket(u, { timeout: o.timeoutMs, retryCount: o.retries }) : viem.http(u, { timeout: o.timeoutMs, retryCount: o.retries, batch: o.batch });
|
|
4837
|
+
return u.startsWith("ws") ? viem.webSocket(u, { timeout: o.timeoutMs, retryCount: o.retries }) : viem.http(u, { timeout: o.timeoutMs, retryCount: o.retries, batch: o.batch, fetchOptions: { redirect: "error" } });
|
|
4837
4838
|
}
|
|
4838
4839
|
function redact(url) {
|
|
4839
4840
|
try {
|
|
@@ -4981,6 +4982,9 @@ function scrubCause(c, depth = 0) {
|
|
|
4981
4982
|
out.name = c.name;
|
|
4982
4983
|
return out;
|
|
4983
4984
|
}
|
|
4985
|
+
function untrustedText(t, max = 120) {
|
|
4986
|
+
return (t ?? "").replace(/[^\x20-\x7e]+/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
|
|
4987
|
+
}
|
|
4984
4988
|
var BlazeError = class extends Error {
|
|
4985
4989
|
code;
|
|
4986
4990
|
details;
|
|
@@ -5159,9 +5163,9 @@ var EMBEDDED_DEPLOYMENTS = {
|
|
|
5159
5163
|
{
|
|
5160
5164
|
version: "2.0.0",
|
|
5161
5165
|
status: "pending",
|
|
5162
|
-
source: "blazephoenixxyz-crypto/Blaze-Phoenix-Dex@
|
|
5166
|
+
source: "blazephoenixxyz-crypto/Blaze-Phoenix-Dex@f746a48",
|
|
5163
5167
|
notes: "Final generation (Core/Hub/Solver/Quoter/Router). Addresses arrive through the registry the day they are deployed.",
|
|
5164
|
-
chains: { 8453: pending(),
|
|
5168
|
+
chains: { 8453: pending(), 10: pending(), 42161: pending(), 4663: pending() }
|
|
5165
5169
|
},
|
|
5166
5170
|
{
|
|
5167
5171
|
version: "1.0.0",
|
|
@@ -5654,6 +5658,9 @@ function fromBaseUnits(v, decimals, maxDp) {
|
|
|
5654
5658
|
var HEX_ADDR2 = /^0x[0-9a-fA-F]{40}$/;
|
|
5655
5659
|
var INT = /^\d{1,78}$/;
|
|
5656
5660
|
var UINT128_MAX = (1n << 128n) - 1n;
|
|
5661
|
+
function netExactOutput(version, exactOut) {
|
|
5662
|
+
return featuresOf(version).previewAndEncode ? exactOut : exactOut - (exactOut * BigInt(FEE_BPS) + 9999n) / 10000n;
|
|
5663
|
+
}
|
|
5657
5664
|
var BlazePhoenix = class {
|
|
5658
5665
|
registry;
|
|
5659
5666
|
rpc;
|
|
@@ -5812,6 +5819,20 @@ var BlazePhoenix = class {
|
|
|
5812
5819
|
if (typeof req.amountIn === "bigint") v = req.amountIn;
|
|
5813
5820
|
else if (INT.test(String(req.amountIn).trim())) v = BigInt(String(req.amountIn).trim());
|
|
5814
5821
|
else throw new BlazeError("bad_request", 'amountIn must be an integer in base units \u2014 use `amount` for human units ("1.5")');
|
|
5822
|
+
if (req.amount !== void 0 && req.amount !== null && req.amount !== "") {
|
|
5823
|
+
const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
|
|
5824
|
+
throw new BlazeError("bad_request", "amount and amountIn are both set and the decimals could not be read to compare them \u2014 pass only one", { cause: e });
|
|
5825
|
+
});
|
|
5826
|
+
let human;
|
|
5827
|
+
try {
|
|
5828
|
+
human = toBaseUnits(req.amount, d);
|
|
5829
|
+
} catch (e) {
|
|
5830
|
+
throw new BlazeError("bad_request", e.message);
|
|
5831
|
+
}
|
|
5832
|
+
if (human !== v) {
|
|
5833
|
+
throw new BlazeError("bad_request", `amount and amountIn disagree (${human} vs ${v} base units): pass only one`);
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5815
5836
|
} else if (req.amount !== void 0) {
|
|
5816
5837
|
const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
|
|
5817
5838
|
throw new BlazeError("bad_request", `could not read decimals of ${tIn.address} on your RPC \u2014 pass amountIn in base units`, { cause: e });
|
|
@@ -5919,13 +5940,15 @@ var BlazePhoenix = class {
|
|
|
5919
5940
|
args: [tIn.address, tOut.address, amountIn],
|
|
5920
5941
|
...req.blockNumber !== void 0 ? { blockNumber: req.blockNumber } : {}
|
|
5921
5942
|
});
|
|
5943
|
+
const net = netExactOutput(ctx.dep.version, exactOut);
|
|
5922
5944
|
return {
|
|
5923
5945
|
chainId: ctx.chainId,
|
|
5924
5946
|
version: ctx.dep.version,
|
|
5925
5947
|
tokenIn: tIn.address,
|
|
5926
5948
|
tokenOut: tOut.address,
|
|
5927
5949
|
amountIn,
|
|
5928
|
-
exactOut,
|
|
5950
|
+
exactOut: net,
|
|
5951
|
+
quoterOut: exactOut,
|
|
5929
5952
|
route,
|
|
5930
5953
|
quotedAt: Date.now()
|
|
5931
5954
|
};
|
|
@@ -6060,8 +6083,8 @@ var BlazePhoenix = class {
|
|
|
6060
6083
|
if (!samePath(ex[0], route)) {
|
|
6061
6084
|
throw new BlazeError("calldata_mismatch", "previewPlanExact priced a different route than the one to execute");
|
|
6062
6085
|
}
|
|
6063
|
-
exactOut = ex[1];
|
|
6064
|
-
return
|
|
6086
|
+
exactOut = netExactOutput(ctx.dep.version, ex[1]);
|
|
6087
|
+
return exactOut;
|
|
6065
6088
|
};
|
|
6066
6089
|
const settleMin = async (p) => {
|
|
6067
6090
|
let floor = minOutFor(p.netOut, slippageBps, p.ironFloor);
|
|
@@ -6161,7 +6184,7 @@ var BlazePhoenix = class {
|
|
|
6161
6184
|
}
|
|
6162
6185
|
}
|
|
6163
6186
|
const quote = this.buildQuote(ctx, tIn, tOut, amountIn, pv, fb);
|
|
6164
|
-
|
|
6187
|
+
const plan = {
|
|
6165
6188
|
quote,
|
|
6166
6189
|
entry,
|
|
6167
6190
|
encodedBy,
|
|
@@ -6175,6 +6198,18 @@ var BlazePhoenix = class {
|
|
|
6175
6198
|
steps: approval ? [approval.tx, tx] : [tx],
|
|
6176
6199
|
...exactOut !== void 0 ? { exactOut } : {}
|
|
6177
6200
|
};
|
|
6201
|
+
if (!req.simulate) return plan;
|
|
6202
|
+
if (!from) throw new BlazeError("bad_request", "simulate: true needs `from` (the account that will send the swap)");
|
|
6203
|
+
const simulation = await this.simulate(plan, from);
|
|
6204
|
+
if (!simulation.ok && simulation.error?.name !== "allowance") {
|
|
6205
|
+
const err = simulation.error;
|
|
6206
|
+
throw new BlazeError(
|
|
6207
|
+
"not_executable",
|
|
6208
|
+
`the Router refuses these bytes from ${from}: ${err?.name ?? "revert"}${err?.code !== void 0 ? `(${err.code})` : ""} \u2014 ${err?.reason ?? "execution reverted"}`,
|
|
6209
|
+
{ details: { from, error: err } }
|
|
6210
|
+
);
|
|
6211
|
+
}
|
|
6212
|
+
return { ...plan, simulation };
|
|
6178
6213
|
}
|
|
6179
6214
|
/** eth_call the swap from `from` on your RPC: the realised amountOut if it
|
|
6180
6215
|
* would settle now, or the decoded reason it would revert. */
|
|
@@ -6190,7 +6225,7 @@ var BlazePhoenix = class {
|
|
|
6190
6225
|
} catch (e) {
|
|
6191
6226
|
if (isTransportError(e)) throw new BlazeError("rpc_error", `simulation: your RPC failed (${e?.message?.split("\n")[0]})`, { cause: e });
|
|
6192
6227
|
const d = decodeBlazeError(e);
|
|
6193
|
-
return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: e?.message?.split("\n")[0]
|
|
6228
|
+
return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: untrustedText(e?.message?.split("\n")[0]) || "execution reverted" } };
|
|
6194
6229
|
}
|
|
6195
6230
|
}
|
|
6196
6231
|
/**
|