@blazephoenix/sdk 1.1.0 → 1.2.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 +5 -1
- package/dist/index.cjs +99 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -3
- package/dist/index.d.ts +17 -3
- package/dist/index.js +99 -12
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,9 +58,11 @@ const plan = await blaze.buildSwap({
|
|
|
58
58
|
chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amount: '1.5',
|
|
59
59
|
recipient: me, from: me, // `from` enables the allowance check
|
|
60
60
|
slippageBps: 50, // default 50; never below the on-chain floor
|
|
61
|
+
exact: true, // optional: minOut from previewPlanExact (one more eth_call)
|
|
61
62
|
});
|
|
62
63
|
plan.steps; // [approve?, swap] — { chainId, to, data, value }
|
|
63
64
|
plan.minOut; // the minimum the Router will enforce for you
|
|
65
|
+
// (your userMinOut as given, when set; refused below the quote net of slippage)
|
|
64
66
|
plan.encodedBy; // 'quoter' on 2.x (the Quoter's own bytes, verified) · 'sdk' on 1.x
|
|
65
67
|
|
|
66
68
|
// 3) optional: dry-run on your node, then execute with YOUR wallet
|
|
@@ -161,7 +163,7 @@ await blaze.quoteBatch([req1, req2]); // per-item { ok, quote } | { ok:false, er
|
|
|
161
163
|
await blaze.getFills({ chain: 'base', lookbackBlocks: 5_000n }); // Swap (+ ExecutionProof on 2.x)
|
|
162
164
|
const stop = await blaze.watchFills({ chain: 'base', onFill: console.log });
|
|
163
165
|
await blaze.tokenInfo('base', '0x…'); // symbol / name / decimals
|
|
164
|
-
await blaze.solvency(); //
|
|
166
|
+
await blaze.solvency(); // staking proof-of-solvency; `not_deployed` where no engine is registered
|
|
165
167
|
toJSON(quote); // bigint → string, for HTTP / queues / LLMs
|
|
166
168
|
```
|
|
167
169
|
|
|
@@ -186,6 +188,8 @@ Every failure, `solvency()` included, is a `BlazeError` with a stable `code`; pr
|
|
|
186
188
|
| `no_route` | the Solver found no executable path (`SolverE(5)`) or the quote reverted |
|
|
187
189
|
| `not_executable` | the Quoter says the route cannot settle now (`canExecute = false`) |
|
|
188
190
|
| `calldata_mismatch` | Quoter bytes did not match your request — refused |
|
|
191
|
+
| `min_implausible` | `buildSwap`: `userMinOut` below the quote net of `slippageBps` (or the on-chain floor) |
|
|
192
|
+
| `min_above_exact` | `buildSwap({ exact: true })`: `userMinOut` above `exactOut` less the 50 bps safety margin |
|
|
189
193
|
| `deployment_unverified` | a registry deployment failed on-chain verification |
|
|
190
194
|
| `unsupported_by_version` | e.g. native ETH input on a 1.x Router |
|
|
191
195
|
| `not_deployed` / `bad_request` / `wallet_chain_mismatch` / `reverted` | as named |
|
package/dist/index.cjs
CHANGED
|
@@ -4738,8 +4738,7 @@ var CHAINS = {
|
|
|
4738
4738
|
explorer: "https://basescan.org",
|
|
4739
4739
|
weth: "0x4200000000000000000000000000000000000006",
|
|
4740
4740
|
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
4741
|
-
bzpx: "
|
|
4742
|
-
staking: "0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77",
|
|
4741
|
+
bzpx: "0x053D75c00FdD571538492e3B1686Bd963a0911A7",
|
|
4743
4742
|
blockTime: 2
|
|
4744
4743
|
},
|
|
4745
4744
|
10: {
|
|
@@ -4769,7 +4768,8 @@ var CHAINS = {
|
|
|
4769
4768
|
explorer: "https://robinhoodchain.blockscout.com",
|
|
4770
4769
|
weth: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
4771
4770
|
usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
4772
|
-
blockTime:
|
|
4771
|
+
blockTime: 0.104
|
|
4772
|
+
// measured ~0.104 s/block
|
|
4773
4773
|
}
|
|
4774
4774
|
};
|
|
4775
4775
|
var ALIASES = {
|
|
@@ -4834,7 +4834,7 @@ function urlTransport(url, o) {
|
|
|
4834
4834
|
throw new BlazeError("bad_request", `refusing RPC URL "${redact(url)}": use https:// or wss:// (http/ws only for localhost)`);
|
|
4835
4835
|
}
|
|
4836
4836
|
const u = url.trim();
|
|
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 });
|
|
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" } });
|
|
4838
4838
|
}
|
|
4839
4839
|
function redact(url) {
|
|
4840
4840
|
try {
|
|
@@ -4982,6 +4982,9 @@ function scrubCause(c, depth = 0) {
|
|
|
4982
4982
|
out.name = c.name;
|
|
4983
4983
|
return out;
|
|
4984
4984
|
}
|
|
4985
|
+
function untrustedText(t, max = 120) {
|
|
4986
|
+
return (t ?? "").replace(/[^\x20-\x7e]+/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
|
|
4987
|
+
}
|
|
4985
4988
|
var BlazeError = class extends Error {
|
|
4986
4989
|
code;
|
|
4987
4990
|
details;
|
|
@@ -5813,6 +5816,20 @@ var BlazePhoenix = class {
|
|
|
5813
5816
|
if (typeof req.amountIn === "bigint") v = req.amountIn;
|
|
5814
5817
|
else if (INT.test(String(req.amountIn).trim())) v = BigInt(String(req.amountIn).trim());
|
|
5815
5818
|
else throw new BlazeError("bad_request", 'amountIn must be an integer in base units \u2014 use `amount` for human units ("1.5")');
|
|
5819
|
+
if (req.amount !== void 0 && req.amount !== null && req.amount !== "") {
|
|
5820
|
+
const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
|
|
5821
|
+
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 });
|
|
5822
|
+
});
|
|
5823
|
+
let human;
|
|
5824
|
+
try {
|
|
5825
|
+
human = toBaseUnits(req.amount, d);
|
|
5826
|
+
} catch (e) {
|
|
5827
|
+
throw new BlazeError("bad_request", e.message);
|
|
5828
|
+
}
|
|
5829
|
+
if (human !== v) {
|
|
5830
|
+
throw new BlazeError("bad_request", `amount and amountIn disagree (${human} vs ${v} base units): pass only one`);
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5816
5833
|
} else if (req.amount !== void 0) {
|
|
5817
5834
|
const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
|
|
5818
5835
|
throw new BlazeError("bad_request", `could not read decimals of ${tIn.address} on your RPC \u2014 pass amountIn in base units`, { cause: e });
|
|
@@ -5920,13 +5937,15 @@ var BlazePhoenix = class {
|
|
|
5920
5937
|
args: [tIn.address, tOut.address, amountIn],
|
|
5921
5938
|
...req.blockNumber !== void 0 ? { blockNumber: req.blockNumber } : {}
|
|
5922
5939
|
});
|
|
5940
|
+
const net = featuresOf(ctx.dep.version).previewAndEncode ? exactOut : exactOut - (exactOut * BigInt(FEE_BPS) + 9999n) / 10000n;
|
|
5923
5941
|
return {
|
|
5924
5942
|
chainId: ctx.chainId,
|
|
5925
5943
|
version: ctx.dep.version,
|
|
5926
5944
|
tokenIn: tIn.address,
|
|
5927
5945
|
tokenOut: tOut.address,
|
|
5928
5946
|
amountIn,
|
|
5929
|
-
exactOut,
|
|
5947
|
+
exactOut: net,
|
|
5948
|
+
quoterOut: exactOut,
|
|
5930
5949
|
route,
|
|
5931
5950
|
quotedAt: Date.now()
|
|
5932
5951
|
};
|
|
@@ -6035,6 +6054,60 @@ var BlazePhoenix = class {
|
|
|
6035
6054
|
let encodedBy = "sdk";
|
|
6036
6055
|
let entry;
|
|
6037
6056
|
let minOut;
|
|
6057
|
+
let at;
|
|
6058
|
+
if (req.exact) {
|
|
6059
|
+
try {
|
|
6060
|
+
at = await ctx.client.getBlockNumber();
|
|
6061
|
+
} catch (e) {
|
|
6062
|
+
this.wrapReadError(e, "block number");
|
|
6063
|
+
}
|
|
6064
|
+
}
|
|
6065
|
+
const pin = at !== void 0 ? { blockNumber: at } : {};
|
|
6066
|
+
let exactOut;
|
|
6067
|
+
const exactRead = async (route) => {
|
|
6068
|
+
let ex;
|
|
6069
|
+
try {
|
|
6070
|
+
ex = await ctx.client.readContract({
|
|
6071
|
+
address: ctx.dep.contracts.quoter,
|
|
6072
|
+
abi: QUOTER_ABI,
|
|
6073
|
+
functionName: "previewPlanExact",
|
|
6074
|
+
args: [tIn.address, tOut.address, amountIn],
|
|
6075
|
+
...pin
|
|
6076
|
+
});
|
|
6077
|
+
} catch (e) {
|
|
6078
|
+
this.wrapReadError(e, "exact quote");
|
|
6079
|
+
}
|
|
6080
|
+
if (!samePath(ex[0], route)) {
|
|
6081
|
+
throw new BlazeError("calldata_mismatch", "previewPlanExact priced a different route than the one to execute");
|
|
6082
|
+
}
|
|
6083
|
+
exactOut = ex[1];
|
|
6084
|
+
return ex[1];
|
|
6085
|
+
};
|
|
6086
|
+
const settleMin = async (p) => {
|
|
6087
|
+
let floor = minOutFor(p.netOut, slippageBps, p.ironFloor);
|
|
6088
|
+
let ceil;
|
|
6089
|
+
if (req.exact) {
|
|
6090
|
+
const ex = await exactRead(p.route);
|
|
6091
|
+
ceil = minOutFor(ex, EXACT_MARGIN_BPS, 0n);
|
|
6092
|
+
floor = maxBig(floor, minOutFor(ceil, slippageBps, 0n));
|
|
6093
|
+
}
|
|
6094
|
+
if (userMinOut === 0n) return floor;
|
|
6095
|
+
if (userMinOut < floor) {
|
|
6096
|
+
throw new BlazeError(
|
|
6097
|
+
"min_implausible",
|
|
6098
|
+
`userMinOut is below ${floor} (this quote net of slippageBps): set it from a price you trust, or raise slippageBps if a lower minimum is intended`,
|
|
6099
|
+
{ details: { userMinOut: userMinOut.toString(), floor: floor.toString() } }
|
|
6100
|
+
);
|
|
6101
|
+
}
|
|
6102
|
+
if (ceil !== void 0 && userMinOut > ceil && userMinOut > floor) {
|
|
6103
|
+
throw new BlazeError(
|
|
6104
|
+
"min_above_exact",
|
|
6105
|
+
`userMinOut is above ${ceil} (exactOut less ${EXACT_MARGIN_BPS} bps): an honest swap at this minimum may revert`,
|
|
6106
|
+
{ details: { userMinOut: userMinOut.toString(), ceiling: ceil.toString() } }
|
|
6107
|
+
);
|
|
6108
|
+
}
|
|
6109
|
+
return userMinOut;
|
|
6110
|
+
};
|
|
6038
6111
|
if (features.previewAndEncode && !tIn.native && mode === "route") {
|
|
6039
6112
|
let call;
|
|
6040
6113
|
try {
|
|
@@ -6042,19 +6115,21 @@ var BlazePhoenix = class {
|
|
|
6042
6115
|
address: ctx.dep.contracts.quoter,
|
|
6043
6116
|
abi: QUOTER_ABI,
|
|
6044
6117
|
functionName: "previewAndEncodeWithMinOut",
|
|
6045
|
-
args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline]
|
|
6118
|
+
args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline],
|
|
6119
|
+
...pin
|
|
6046
6120
|
}) : await ctx.client.readContract({
|
|
6047
6121
|
address: ctx.dep.contracts.quoter,
|
|
6048
6122
|
abi: QUOTER_ABI,
|
|
6049
6123
|
functionName: "previewAndEncode",
|
|
6050
|
-
args: [tIn.address, tOut.address, amountIn, recipient, deadline]
|
|
6124
|
+
args: [tIn.address, tOut.address, amountIn, recipient, deadline],
|
|
6125
|
+
...pin
|
|
6051
6126
|
});
|
|
6052
6127
|
[pv, call] = r;
|
|
6053
6128
|
} catch (e) {
|
|
6054
6129
|
this.wrapReadError(e, "previewAndEncode");
|
|
6055
6130
|
}
|
|
6056
6131
|
assertExecutable(pv, tIn.address, tOut.address, call);
|
|
6057
|
-
minOut =
|
|
6132
|
+
minOut = await settleMin(pv);
|
|
6058
6133
|
verifySwapExactIn(call, {
|
|
6059
6134
|
tokenIn: tIn.address,
|
|
6060
6135
|
tokenOut: tOut.address,
|
|
@@ -6068,9 +6143,9 @@ var BlazePhoenix = class {
|
|
|
6068
6143
|
encodedBy = "quoter";
|
|
6069
6144
|
entry = "swapExactIn";
|
|
6070
6145
|
} else {
|
|
6071
|
-
({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut));
|
|
6146
|
+
({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut, at));
|
|
6072
6147
|
assertExecutable(pv, tIn.address, tOut.address);
|
|
6073
|
-
minOut =
|
|
6148
|
+
minOut = await settleMin(pv);
|
|
6074
6149
|
if (mode === "best") {
|
|
6075
6150
|
entry = "swapBestExactIn";
|
|
6076
6151
|
data = encodeSwapBestExactIn(tIn.address, tOut.address, amountIn, minOut, recipient, deadline);
|
|
@@ -6117,7 +6192,8 @@ var BlazePhoenix = class {
|
|
|
6117
6192
|
slippageBps,
|
|
6118
6193
|
approval,
|
|
6119
6194
|
unwrapAfter: tOut.native,
|
|
6120
|
-
steps: approval ? [approval.tx, tx] : [tx]
|
|
6195
|
+
steps: approval ? [approval.tx, tx] : [tx],
|
|
6196
|
+
...exactOut !== void 0 ? { exactOut } : {}
|
|
6121
6197
|
};
|
|
6122
6198
|
}
|
|
6123
6199
|
/** eth_call the swap from `from` on your RPC: the realised amountOut if it
|
|
@@ -6134,7 +6210,7 @@ var BlazePhoenix = class {
|
|
|
6134
6210
|
} catch (e) {
|
|
6135
6211
|
if (isTransportError(e)) throw new BlazeError("rpc_error", `simulation: your RPC failed (${e?.message?.split("\n")[0]})`, { cause: e });
|
|
6136
6212
|
const d = decodeBlazeError(e);
|
|
6137
|
-
return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: e?.message?.split("\n")[0]
|
|
6213
|
+
return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: untrustedText(e?.message?.split("\n")[0]) || "execution reverted" } };
|
|
6138
6214
|
}
|
|
6139
6215
|
}
|
|
6140
6216
|
/**
|
|
@@ -6358,6 +6434,17 @@ function checkDeadlineSec(n) {
|
|
|
6358
6434
|
return n;
|
|
6359
6435
|
}
|
|
6360
6436
|
var maxBig = (a, b) => a > b ? a : b;
|
|
6437
|
+
var EXACT_MARGIN_BPS = 50;
|
|
6438
|
+
function samePath(a, b) {
|
|
6439
|
+
const eqA = (x, y) => x.toLowerCase() === y.toLowerCase();
|
|
6440
|
+
return a.hops.length === b.hops.length && a.hops.every((h, i) => {
|
|
6441
|
+
const g = b.hops[i];
|
|
6442
|
+
return eqA(h.tokenIn, g.tokenIn) && eqA(h.tokenOut, g.tokenOut) && h.legs.length === g.legs.length && h.legs.every((l, j) => {
|
|
6443
|
+
const m = g.legs[j];
|
|
6444
|
+
return eqA(l.pool, m.pool) && eqA(l.hooks, m.hooks) && l.kind === m.kind && l.fee === m.fee && l.tickSpacing === m.tickSpacing && l.zeroForOne === m.zeroForOne && l.stable === m.stable;
|
|
6445
|
+
});
|
|
6446
|
+
});
|
|
6447
|
+
}
|
|
6361
6448
|
function deepLink(opts = {}) {
|
|
6362
6449
|
const base = (opts.baseUrl ?? "https://blazephoenix.xyz").replace(/\/+$/, "");
|
|
6363
6450
|
const sp = new URLSearchParams();
|