@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/dist/index.d.cts CHANGED
@@ -183,8 +183,10 @@ interface ExactQuote {
183
183
  tokenIn: Address;
184
184
  tokenOut: Address;
185
185
  amountIn: bigint;
186
- /** Execution-grade NET output (every concentrated leg dry-run on the pool). */
186
+ /** Execution-grade NET output (every concentrated leg dry-run on its venue), net of the protocol fee. */
187
187
  exactOut: bigint;
188
+ /** The scalar exactly as the Quoter returned it (gross of the fee on 1.x Quoters). */
189
+ quoterOut: bigint;
188
190
  route: Route;
189
191
  quotedAt: number;
190
192
  }
@@ -199,6 +201,13 @@ interface SwapRequest extends QuoteRequest {
199
201
  recipient: Address;
200
202
  /** The account that will send the swap — enables the allowance check and simulation. */
201
203
  from?: Address;
204
+ /** With `from`: eth_call the built swap from that account before returning it, and fail closed
205
+ * (`not_executable`, no plan) on a revert. The Quoter's `canExecute` is swap math; whether these
206
+ * bytes settle for the payer is answered only by that call.
207
+ * Exception: while the Router still needs its approval, no eth_call is made. The plan comes back
208
+ * with `simulation: { ok: false, error.name: 'allowance' }`; send the approval, then `simulate()`
209
+ * again from the payer before sending the swap (`execute()` does this). */
210
+ simulate?: boolean;
202
211
  /** Default 50 (0.5%). minOut = netOut − slippage, never below the on-chain floor. */
203
212
  slippageBps?: number;
204
213
  /** Seconds from now (default 120, 10–3600) — or pass an absolute `deadline`. */
@@ -232,6 +241,11 @@ interface ApprovalStep {
232
241
  }
233
242
  interface SwapPlan {
234
243
  quote: Quote;
244
+ /** Present when built with `simulate: true`. Either the Router's eth_call from `from` settled
245
+ * (`ok: true`, `amountOut`), or no eth_call was made because the approval is still missing
246
+ * (`ok: false`, `error.name: 'allowance'`): approve, then `simulate()` again. A revert never
247
+ * reaches here; it is thrown as `not_executable`. */
248
+ simulation?: SimulationResult;
235
249
  entry: 'swapExactIn' | 'swapExactInNative' | 'swapBestExactIn';
236
250
  /** 'quoter': the bytes are the Quoter's own previewAndEncode output (2.x),
237
251
  * verified field-by-field against what you asked; 'sdk': encoded locally. */
@@ -247,7 +261,7 @@ interface SwapPlan {
247
261
  unwrapAfter: boolean;
248
262
  /** Ordered transactions to send: [approval?, swap]. */
249
263
  steps: TxRequest[];
250
- /** With `exact: true`: the previewPlanExact output minOut was derived from. */
264
+ /** With `exact: true`: the NET previewPlanExact output used to derive `minOut`. */
251
265
  exactOut?: bigint;
252
266
  }
253
267
  interface SimulationResult {
package/dist/index.d.ts CHANGED
@@ -183,8 +183,10 @@ interface ExactQuote {
183
183
  tokenIn: Address;
184
184
  tokenOut: Address;
185
185
  amountIn: bigint;
186
- /** Execution-grade NET output (every concentrated leg dry-run on the pool). */
186
+ /** Execution-grade NET output (every concentrated leg dry-run on its venue), net of the protocol fee. */
187
187
  exactOut: bigint;
188
+ /** The scalar exactly as the Quoter returned it (gross of the fee on 1.x Quoters). */
189
+ quoterOut: bigint;
188
190
  route: Route;
189
191
  quotedAt: number;
190
192
  }
@@ -199,6 +201,13 @@ interface SwapRequest extends QuoteRequest {
199
201
  recipient: Address;
200
202
  /** The account that will send the swap — enables the allowance check and simulation. */
201
203
  from?: Address;
204
+ /** With `from`: eth_call the built swap from that account before returning it, and fail closed
205
+ * (`not_executable`, no plan) on a revert. The Quoter's `canExecute` is swap math; whether these
206
+ * bytes settle for the payer is answered only by that call.
207
+ * Exception: while the Router still needs its approval, no eth_call is made. The plan comes back
208
+ * with `simulation: { ok: false, error.name: 'allowance' }`; send the approval, then `simulate()`
209
+ * again from the payer before sending the swap (`execute()` does this). */
210
+ simulate?: boolean;
202
211
  /** Default 50 (0.5%). minOut = netOut − slippage, never below the on-chain floor. */
203
212
  slippageBps?: number;
204
213
  /** Seconds from now (default 120, 10–3600) — or pass an absolute `deadline`. */
@@ -232,6 +241,11 @@ interface ApprovalStep {
232
241
  }
233
242
  interface SwapPlan {
234
243
  quote: Quote;
244
+ /** Present when built with `simulate: true`. Either the Router's eth_call from `from` settled
245
+ * (`ok: true`, `amountOut`), or no eth_call was made because the approval is still missing
246
+ * (`ok: false`, `error.name: 'allowance'`): approve, then `simulate()` again. A revert never
247
+ * reaches here; it is thrown as `not_executable`. */
248
+ simulation?: SimulationResult;
235
249
  entry: 'swapExactIn' | 'swapExactInNative' | 'swapBestExactIn';
236
250
  /** 'quoter': the bytes are the Quoter's own previewAndEncode output (2.x),
237
251
  * verified field-by-field against what you asked; 'sdk': encoded locally. */
@@ -247,7 +261,7 @@ interface SwapPlan {
247
261
  unwrapAfter: boolean;
248
262
  /** Ordered transactions to send: [approval?, swap]. */
249
263
  steps: TxRequest[];
250
- /** With `exact: true`: the previewPlanExact output minOut was derived from. */
264
+ /** With `exact: true`: the NET previewPlanExact output used to derive `minOut`. */
251
265
  exactOut?: bigint;
252
266
  }
253
267
  interface SimulationResult {
package/dist/index.js CHANGED
@@ -4766,7 +4766,8 @@ var CHAINS = {
4766
4766
  explorer: "https://robinhoodchain.blockscout.com",
4767
4767
  weth: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
4768
4768
  usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
4769
- blockTime: 2
4769
+ blockTime: 0.104
4770
+ // measured ~0.104 s/block
4770
4771
  }
4771
4772
  };
4772
4773
  var ALIASES = {
@@ -4831,7 +4832,7 @@ function urlTransport(url, o) {
4831
4832
  throw new BlazeError("bad_request", `refusing RPC URL "${redact(url)}": use https:// or wss:// (http/ws only for localhost)`);
4832
4833
  }
4833
4834
  const u = url.trim();
4834
- return u.startsWith("ws") ? webSocket(u, { timeout: o.timeoutMs, retryCount: o.retries }) : http(u, { timeout: o.timeoutMs, retryCount: o.retries, batch: o.batch });
4835
+ return u.startsWith("ws") ? webSocket(u, { timeout: o.timeoutMs, retryCount: o.retries }) : http(u, { timeout: o.timeoutMs, retryCount: o.retries, batch: o.batch, fetchOptions: { redirect: "error" } });
4835
4836
  }
4836
4837
  function redact(url) {
4837
4838
  try {
@@ -4979,6 +4980,9 @@ function scrubCause(c, depth = 0) {
4979
4980
  out.name = c.name;
4980
4981
  return out;
4981
4982
  }
4983
+ function untrustedText(t, max = 120) {
4984
+ return (t ?? "").replace(/[^\x20-\x7e]+/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
4985
+ }
4982
4986
  var BlazeError = class extends Error {
4983
4987
  code;
4984
4988
  details;
@@ -5157,9 +5161,9 @@ var EMBEDDED_DEPLOYMENTS = {
5157
5161
  {
5158
5162
  version: "2.0.0",
5159
5163
  status: "pending",
5160
- source: "blazephoenixxyz-crypto/Blaze-Phoenix-Dex@07c8563",
5164
+ source: "blazephoenixxyz-crypto/Blaze-Phoenix-Dex@f746a48",
5161
5165
  notes: "Final generation (Core/Hub/Solver/Quoter/Router). Addresses arrive through the registry the day they are deployed.",
5162
- chains: { 8453: pending(), 1: pending(), 10: pending(), 42161: pending(), 4663: pending() }
5166
+ chains: { 8453: pending(), 10: pending(), 42161: pending(), 4663: pending() }
5163
5167
  },
5164
5168
  {
5165
5169
  version: "1.0.0",
@@ -5652,6 +5656,9 @@ function fromBaseUnits(v, decimals, maxDp) {
5652
5656
  var HEX_ADDR2 = /^0x[0-9a-fA-F]{40}$/;
5653
5657
  var INT = /^\d{1,78}$/;
5654
5658
  var UINT128_MAX = (1n << 128n) - 1n;
5659
+ function netExactOutput(version, exactOut) {
5660
+ return featuresOf(version).previewAndEncode ? exactOut : exactOut - (exactOut * BigInt(FEE_BPS) + 9999n) / 10000n;
5661
+ }
5655
5662
  var BlazePhoenix = class {
5656
5663
  registry;
5657
5664
  rpc;
@@ -5810,6 +5817,20 @@ var BlazePhoenix = class {
5810
5817
  if (typeof req.amountIn === "bigint") v = req.amountIn;
5811
5818
  else if (INT.test(String(req.amountIn).trim())) v = BigInt(String(req.amountIn).trim());
5812
5819
  else throw new BlazeError("bad_request", 'amountIn must be an integer in base units \u2014 use `amount` for human units ("1.5")');
5820
+ if (req.amount !== void 0 && req.amount !== null && req.amount !== "") {
5821
+ const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
5822
+ 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 });
5823
+ });
5824
+ let human;
5825
+ try {
5826
+ human = toBaseUnits(req.amount, d);
5827
+ } catch (e) {
5828
+ throw new BlazeError("bad_request", e.message);
5829
+ }
5830
+ if (human !== v) {
5831
+ throw new BlazeError("bad_request", `amount and amountIn disagree (${human} vs ${v} base units): pass only one`);
5832
+ }
5833
+ }
5813
5834
  } else if (req.amount !== void 0) {
5814
5835
  const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
5815
5836
  throw new BlazeError("bad_request", `could not read decimals of ${tIn.address} on your RPC \u2014 pass amountIn in base units`, { cause: e });
@@ -5917,13 +5938,15 @@ var BlazePhoenix = class {
5917
5938
  args: [tIn.address, tOut.address, amountIn],
5918
5939
  ...req.blockNumber !== void 0 ? { blockNumber: req.blockNumber } : {}
5919
5940
  });
5941
+ const net = netExactOutput(ctx.dep.version, exactOut);
5920
5942
  return {
5921
5943
  chainId: ctx.chainId,
5922
5944
  version: ctx.dep.version,
5923
5945
  tokenIn: tIn.address,
5924
5946
  tokenOut: tOut.address,
5925
5947
  amountIn,
5926
- exactOut,
5948
+ exactOut: net,
5949
+ quoterOut: exactOut,
5927
5950
  route,
5928
5951
  quotedAt: Date.now()
5929
5952
  };
@@ -6058,8 +6081,8 @@ var BlazePhoenix = class {
6058
6081
  if (!samePath(ex[0], route)) {
6059
6082
  throw new BlazeError("calldata_mismatch", "previewPlanExact priced a different route than the one to execute");
6060
6083
  }
6061
- exactOut = ex[1];
6062
- return ex[1];
6084
+ exactOut = netExactOutput(ctx.dep.version, ex[1]);
6085
+ return exactOut;
6063
6086
  };
6064
6087
  const settleMin = async (p) => {
6065
6088
  let floor = minOutFor(p.netOut, slippageBps, p.ironFloor);
@@ -6159,7 +6182,7 @@ var BlazePhoenix = class {
6159
6182
  }
6160
6183
  }
6161
6184
  const quote = this.buildQuote(ctx, tIn, tOut, amountIn, pv, fb);
6162
- return {
6185
+ const plan = {
6163
6186
  quote,
6164
6187
  entry,
6165
6188
  encodedBy,
@@ -6173,6 +6196,18 @@ var BlazePhoenix = class {
6173
6196
  steps: approval ? [approval.tx, tx] : [tx],
6174
6197
  ...exactOut !== void 0 ? { exactOut } : {}
6175
6198
  };
6199
+ if (!req.simulate) return plan;
6200
+ if (!from) throw new BlazeError("bad_request", "simulate: true needs `from` (the account that will send the swap)");
6201
+ const simulation = await this.simulate(plan, from);
6202
+ if (!simulation.ok && simulation.error?.name !== "allowance") {
6203
+ const err = simulation.error;
6204
+ throw new BlazeError(
6205
+ "not_executable",
6206
+ `the Router refuses these bytes from ${from}: ${err?.name ?? "revert"}${err?.code !== void 0 ? `(${err.code})` : ""} \u2014 ${err?.reason ?? "execution reverted"}`,
6207
+ { details: { from, error: err } }
6208
+ );
6209
+ }
6210
+ return { ...plan, simulation };
6176
6211
  }
6177
6212
  /** eth_call the swap from `from` on your RPC: the realised amountOut if it
6178
6213
  * would settle now, or the decoded reason it would revert. */
@@ -6188,7 +6223,7 @@ var BlazePhoenix = class {
6188
6223
  } catch (e) {
6189
6224
  if (isTransportError(e)) throw new BlazeError("rpc_error", `simulation: your RPC failed (${e?.message?.split("\n")[0]})`, { cause: e });
6190
6225
  const d = decodeBlazeError(e);
6191
- return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: e?.message?.split("\n")[0] ?? "execution reverted" } };
6226
+ return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: untrustedText(e?.message?.split("\n")[0]) || "execution reverted" } };
6192
6227
  }
6193
6228
  }
6194
6229
  /**