@blazephoenix/sdk 1.1.0 → 1.2.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
@@ -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(); // the staking engine's proof-of-solvency (Base)
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: "0x23113e72165a034265Ab8Bf2277CCB7a85Cb7483",
4742
- staking: "0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77",
4741
+ bzpx: "0x053D75c00FdD571538492e3B1686Bd963a0911A7",
4743
4742
  blockTime: 2
4744
4743
  },
4745
4744
  10: {
@@ -6035,6 +6034,60 @@ var BlazePhoenix = class {
6035
6034
  let encodedBy = "sdk";
6036
6035
  let entry;
6037
6036
  let minOut;
6037
+ let at;
6038
+ if (req.exact) {
6039
+ try {
6040
+ at = await ctx.client.getBlockNumber();
6041
+ } catch (e) {
6042
+ this.wrapReadError(e, "block number");
6043
+ }
6044
+ }
6045
+ const pin = at !== void 0 ? { blockNumber: at } : {};
6046
+ let exactOut;
6047
+ const exactRead = async (route) => {
6048
+ let ex;
6049
+ try {
6050
+ ex = await ctx.client.readContract({
6051
+ address: ctx.dep.contracts.quoter,
6052
+ abi: QUOTER_ABI,
6053
+ functionName: "previewPlanExact",
6054
+ args: [tIn.address, tOut.address, amountIn],
6055
+ ...pin
6056
+ });
6057
+ } catch (e) {
6058
+ this.wrapReadError(e, "exact quote");
6059
+ }
6060
+ if (!samePath(ex[0], route)) {
6061
+ throw new BlazeError("calldata_mismatch", "previewPlanExact priced a different route than the one to execute");
6062
+ }
6063
+ exactOut = ex[1];
6064
+ return ex[1];
6065
+ };
6066
+ const settleMin = async (p) => {
6067
+ let floor = minOutFor(p.netOut, slippageBps, p.ironFloor);
6068
+ let ceil;
6069
+ if (req.exact) {
6070
+ const ex = await exactRead(p.route);
6071
+ ceil = minOutFor(ex, EXACT_MARGIN_BPS, 0n);
6072
+ floor = maxBig(floor, minOutFor(ceil, slippageBps, 0n));
6073
+ }
6074
+ if (userMinOut === 0n) return floor;
6075
+ if (userMinOut < floor) {
6076
+ throw new BlazeError(
6077
+ "min_implausible",
6078
+ `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`,
6079
+ { details: { userMinOut: userMinOut.toString(), floor: floor.toString() } }
6080
+ );
6081
+ }
6082
+ if (ceil !== void 0 && userMinOut > ceil && userMinOut > floor) {
6083
+ throw new BlazeError(
6084
+ "min_above_exact",
6085
+ `userMinOut is above ${ceil} (exactOut less ${EXACT_MARGIN_BPS} bps): an honest swap at this minimum may revert`,
6086
+ { details: { userMinOut: userMinOut.toString(), ceiling: ceil.toString() } }
6087
+ );
6088
+ }
6089
+ return userMinOut;
6090
+ };
6038
6091
  if (features.previewAndEncode && !tIn.native && mode === "route") {
6039
6092
  let call;
6040
6093
  try {
@@ -6042,19 +6095,21 @@ var BlazePhoenix = class {
6042
6095
  address: ctx.dep.contracts.quoter,
6043
6096
  abi: QUOTER_ABI,
6044
6097
  functionName: "previewAndEncodeWithMinOut",
6045
- args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline]
6098
+ args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline],
6099
+ ...pin
6046
6100
  }) : await ctx.client.readContract({
6047
6101
  address: ctx.dep.contracts.quoter,
6048
6102
  abi: QUOTER_ABI,
6049
6103
  functionName: "previewAndEncode",
6050
- args: [tIn.address, tOut.address, amountIn, recipient, deadline]
6104
+ args: [tIn.address, tOut.address, amountIn, recipient, deadline],
6105
+ ...pin
6051
6106
  });
6052
6107
  [pv, call] = r;
6053
6108
  } catch (e) {
6054
6109
  this.wrapReadError(e, "previewAndEncode");
6055
6110
  }
6056
6111
  assertExecutable(pv, tIn.address, tOut.address, call);
6057
- minOut = maxBig(minOutFor(pv.netOut, slippageBps, pv.effectiveMinOut), userMinOut);
6112
+ minOut = await settleMin(pv);
6058
6113
  verifySwapExactIn(call, {
6059
6114
  tokenIn: tIn.address,
6060
6115
  tokenOut: tOut.address,
@@ -6068,9 +6123,9 @@ var BlazePhoenix = class {
6068
6123
  encodedBy = "quoter";
6069
6124
  entry = "swapExactIn";
6070
6125
  } else {
6071
- ({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut));
6126
+ ({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut, at));
6072
6127
  assertExecutable(pv, tIn.address, tOut.address);
6073
- minOut = maxBig(minOutFor(pv.netOut, slippageBps, pv.effectiveMinOut), userMinOut);
6128
+ minOut = await settleMin(pv);
6074
6129
  if (mode === "best") {
6075
6130
  entry = "swapBestExactIn";
6076
6131
  data = encodeSwapBestExactIn(tIn.address, tOut.address, amountIn, minOut, recipient, deadline);
@@ -6117,7 +6172,8 @@ var BlazePhoenix = class {
6117
6172
  slippageBps,
6118
6173
  approval,
6119
6174
  unwrapAfter: tOut.native,
6120
- steps: approval ? [approval.tx, tx] : [tx]
6175
+ steps: approval ? [approval.tx, tx] : [tx],
6176
+ ...exactOut !== void 0 ? { exactOut } : {}
6121
6177
  };
6122
6178
  }
6123
6179
  /** eth_call the swap from `from` on your RPC: the realised amountOut if it
@@ -6358,6 +6414,17 @@ function checkDeadlineSec(n) {
6358
6414
  return n;
6359
6415
  }
6360
6416
  var maxBig = (a, b) => a > b ? a : b;
6417
+ var EXACT_MARGIN_BPS = 50;
6418
+ function samePath(a, b) {
6419
+ const eqA = (x, y) => x.toLowerCase() === y.toLowerCase();
6420
+ return a.hops.length === b.hops.length && a.hops.every((h, i) => {
6421
+ const g = b.hops[i];
6422
+ return eqA(h.tokenIn, g.tokenIn) && eqA(h.tokenOut, g.tokenOut) && h.legs.length === g.legs.length && h.legs.every((l, j) => {
6423
+ const m = g.legs[j];
6424
+ 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;
6425
+ });
6426
+ });
6427
+ }
6361
6428
  function deepLink(opts = {}) {
6362
6429
  const base = (opts.baseUrl ?? "https://blazephoenix.xyz").replace(/\/+$/, "");
6363
6430
  const sp = new URLSearchParams();