@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/dist/index.d.cts CHANGED
@@ -145,7 +145,8 @@ interface QuoteRequest {
145
145
  amountIn?: bigint | string;
146
146
  /** Input in HUMAN units ("1.5"); decimals are read from the token on your RPC. */
147
147
  amount?: string;
148
- /** Tighten the on-chain floor with your own minimum (base units of tokenOut). */
148
+ /** Your own minimum (base units of tokenOut). In buildSwap it is the signed minimum as given,
149
+ * and it is refused (`min_implausible`) below the quote net of slippageBps or the on-chain floor. */
149
150
  userMinOut?: bigint | string;
150
151
  /** Protocol version for this call (default: the client's, itself 'latest'). */
151
152
  version?: string;
@@ -211,6 +212,15 @@ interface SwapRequest extends QuoteRequest {
211
212
  mode?: 'route' | 'best';
212
213
  /** Allowance to request when approval is needed: 'exact' (default) or 'max'. */
213
214
  approve?: 'exact' | 'max';
215
+ /**
216
+ * true — also read previewPlanExact (every concentrated leg dry-run) in the same block and
217
+ * derive minOut from its exactOut: a fixed 50 bps safety margin first, then slippageBps off
218
+ * that, never below the default minimum. The calldata still executes the route of the default
219
+ * path; the exact read must price that same route, or buildSwap refuses. A userMinOut above
220
+ * exactOut less the margin is refused (`min_above_exact`). One more eth_call; the swap reverts
221
+ * instead of filling short when the pool moves past the tolerance.
222
+ */
223
+ exact?: boolean;
214
224
  }
215
225
  interface ApprovalStep {
216
226
  token: Address;
@@ -237,6 +247,8 @@ interface SwapPlan {
237
247
  unwrapAfter: boolean;
238
248
  /** Ordered transactions to send: [approval?, swap]. */
239
249
  steps: TxRequest[];
250
+ /** With `exact: true`: the previewPlanExact output minOut was derived from. */
251
+ exactOut?: bigint;
240
252
  }
241
253
  interface SimulationResult {
242
254
  ok: boolean;
@@ -4249,7 +4261,7 @@ declare const CAUTION_IMPACT_BPS = 200;
4249
4261
  declare function routeIsConsistent(route: Route, tokenIn: Address, tokenOut: Address): boolean;
4250
4262
  declare function phoenixCheck(pv: Preview, tokenIn: Address, tokenOut: Address): QuoteChecks;
4251
4263
 
4252
- type BlazeErrorCode = 'rpc_required' | 'rpc_chain_mismatch' | 'rpc_error' | 'bad_request' | 'not_deployed' | 'unsupported_by_version' | 'no_route' | 'not_executable' | 'calldata_mismatch' | 'deployment_unverified' | 'reverted' | 'wallet_chain_mismatch';
4264
+ type BlazeErrorCode = 'rpc_required' | 'rpc_chain_mismatch' | 'rpc_error' | 'bad_request' | 'not_deployed' | 'unsupported_by_version' | 'no_route' | 'not_executable' | 'calldata_mismatch' | 'min_implausible' | 'min_above_exact' | 'deployment_unverified' | 'reverted' | 'wallet_chain_mismatch';
4253
4265
  declare class BlazeError extends Error {
4254
4266
  readonly code: BlazeErrorCode;
4255
4267
  readonly details?: Record<string, unknown>;
package/dist/index.d.ts CHANGED
@@ -145,7 +145,8 @@ interface QuoteRequest {
145
145
  amountIn?: bigint | string;
146
146
  /** Input in HUMAN units ("1.5"); decimals are read from the token on your RPC. */
147
147
  amount?: string;
148
- /** Tighten the on-chain floor with your own minimum (base units of tokenOut). */
148
+ /** Your own minimum (base units of tokenOut). In buildSwap it is the signed minimum as given,
149
+ * and it is refused (`min_implausible`) below the quote net of slippageBps or the on-chain floor. */
149
150
  userMinOut?: bigint | string;
150
151
  /** Protocol version for this call (default: the client's, itself 'latest'). */
151
152
  version?: string;
@@ -211,6 +212,15 @@ interface SwapRequest extends QuoteRequest {
211
212
  mode?: 'route' | 'best';
212
213
  /** Allowance to request when approval is needed: 'exact' (default) or 'max'. */
213
214
  approve?: 'exact' | 'max';
215
+ /**
216
+ * true — also read previewPlanExact (every concentrated leg dry-run) in the same block and
217
+ * derive minOut from its exactOut: a fixed 50 bps safety margin first, then slippageBps off
218
+ * that, never below the default minimum. The calldata still executes the route of the default
219
+ * path; the exact read must price that same route, or buildSwap refuses. A userMinOut above
220
+ * exactOut less the margin is refused (`min_above_exact`). One more eth_call; the swap reverts
221
+ * instead of filling short when the pool moves past the tolerance.
222
+ */
223
+ exact?: boolean;
214
224
  }
215
225
  interface ApprovalStep {
216
226
  token: Address;
@@ -237,6 +247,8 @@ interface SwapPlan {
237
247
  unwrapAfter: boolean;
238
248
  /** Ordered transactions to send: [approval?, swap]. */
239
249
  steps: TxRequest[];
250
+ /** With `exact: true`: the previewPlanExact output minOut was derived from. */
251
+ exactOut?: bigint;
240
252
  }
241
253
  interface SimulationResult {
242
254
  ok: boolean;
@@ -4249,7 +4261,7 @@ declare const CAUTION_IMPACT_BPS = 200;
4249
4261
  declare function routeIsConsistent(route: Route, tokenIn: Address, tokenOut: Address): boolean;
4250
4262
  declare function phoenixCheck(pv: Preview, tokenIn: Address, tokenOut: Address): QuoteChecks;
4251
4263
 
4252
- type BlazeErrorCode = 'rpc_required' | 'rpc_chain_mismatch' | 'rpc_error' | 'bad_request' | 'not_deployed' | 'unsupported_by_version' | 'no_route' | 'not_executable' | 'calldata_mismatch' | 'deployment_unverified' | 'reverted' | 'wallet_chain_mismatch';
4264
+ type BlazeErrorCode = 'rpc_required' | 'rpc_chain_mismatch' | 'rpc_error' | 'bad_request' | 'not_deployed' | 'unsupported_by_version' | 'no_route' | 'not_executable' | 'calldata_mismatch' | 'min_implausible' | 'min_above_exact' | 'deployment_unverified' | 'reverted' | 'wallet_chain_mismatch';
4253
4265
  declare class BlazeError extends Error {
4254
4266
  readonly code: BlazeErrorCode;
4255
4267
  readonly details?: Record<string, unknown>;
package/dist/index.js CHANGED
@@ -4736,8 +4736,7 @@ var CHAINS = {
4736
4736
  explorer: "https://basescan.org",
4737
4737
  weth: "0x4200000000000000000000000000000000000006",
4738
4738
  usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
4739
- bzpx: "0x23113e72165a034265Ab8Bf2277CCB7a85Cb7483",
4740
- staking: "0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77",
4739
+ bzpx: "0x053D75c00FdD571538492e3B1686Bd963a0911A7",
4741
4740
  blockTime: 2
4742
4741
  },
4743
4742
  10: {
@@ -6033,6 +6032,60 @@ var BlazePhoenix = class {
6033
6032
  let encodedBy = "sdk";
6034
6033
  let entry;
6035
6034
  let minOut;
6035
+ let at;
6036
+ if (req.exact) {
6037
+ try {
6038
+ at = await ctx.client.getBlockNumber();
6039
+ } catch (e) {
6040
+ this.wrapReadError(e, "block number");
6041
+ }
6042
+ }
6043
+ const pin = at !== void 0 ? { blockNumber: at } : {};
6044
+ let exactOut;
6045
+ const exactRead = async (route) => {
6046
+ let ex;
6047
+ try {
6048
+ ex = await ctx.client.readContract({
6049
+ address: ctx.dep.contracts.quoter,
6050
+ abi: QUOTER_ABI,
6051
+ functionName: "previewPlanExact",
6052
+ args: [tIn.address, tOut.address, amountIn],
6053
+ ...pin
6054
+ });
6055
+ } catch (e) {
6056
+ this.wrapReadError(e, "exact quote");
6057
+ }
6058
+ if (!samePath(ex[0], route)) {
6059
+ throw new BlazeError("calldata_mismatch", "previewPlanExact priced a different route than the one to execute");
6060
+ }
6061
+ exactOut = ex[1];
6062
+ return ex[1];
6063
+ };
6064
+ const settleMin = async (p) => {
6065
+ let floor = minOutFor(p.netOut, slippageBps, p.ironFloor);
6066
+ let ceil;
6067
+ if (req.exact) {
6068
+ const ex = await exactRead(p.route);
6069
+ ceil = minOutFor(ex, EXACT_MARGIN_BPS, 0n);
6070
+ floor = maxBig(floor, minOutFor(ceil, slippageBps, 0n));
6071
+ }
6072
+ if (userMinOut === 0n) return floor;
6073
+ if (userMinOut < floor) {
6074
+ throw new BlazeError(
6075
+ "min_implausible",
6076
+ `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`,
6077
+ { details: { userMinOut: userMinOut.toString(), floor: floor.toString() } }
6078
+ );
6079
+ }
6080
+ if (ceil !== void 0 && userMinOut > ceil && userMinOut > floor) {
6081
+ throw new BlazeError(
6082
+ "min_above_exact",
6083
+ `userMinOut is above ${ceil} (exactOut less ${EXACT_MARGIN_BPS} bps): an honest swap at this minimum may revert`,
6084
+ { details: { userMinOut: userMinOut.toString(), ceiling: ceil.toString() } }
6085
+ );
6086
+ }
6087
+ return userMinOut;
6088
+ };
6036
6089
  if (features.previewAndEncode && !tIn.native && mode === "route") {
6037
6090
  let call;
6038
6091
  try {
@@ -6040,19 +6093,21 @@ var BlazePhoenix = class {
6040
6093
  address: ctx.dep.contracts.quoter,
6041
6094
  abi: QUOTER_ABI,
6042
6095
  functionName: "previewAndEncodeWithMinOut",
6043
- args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline]
6096
+ args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline],
6097
+ ...pin
6044
6098
  }) : await ctx.client.readContract({
6045
6099
  address: ctx.dep.contracts.quoter,
6046
6100
  abi: QUOTER_ABI,
6047
6101
  functionName: "previewAndEncode",
6048
- args: [tIn.address, tOut.address, amountIn, recipient, deadline]
6102
+ args: [tIn.address, tOut.address, amountIn, recipient, deadline],
6103
+ ...pin
6049
6104
  });
6050
6105
  [pv, call] = r;
6051
6106
  } catch (e) {
6052
6107
  this.wrapReadError(e, "previewAndEncode");
6053
6108
  }
6054
6109
  assertExecutable(pv, tIn.address, tOut.address, call);
6055
- minOut = maxBig(minOutFor(pv.netOut, slippageBps, pv.effectiveMinOut), userMinOut);
6110
+ minOut = await settleMin(pv);
6056
6111
  verifySwapExactIn(call, {
6057
6112
  tokenIn: tIn.address,
6058
6113
  tokenOut: tOut.address,
@@ -6066,9 +6121,9 @@ var BlazePhoenix = class {
6066
6121
  encodedBy = "quoter";
6067
6122
  entry = "swapExactIn";
6068
6123
  } else {
6069
- ({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut));
6124
+ ({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut, at));
6070
6125
  assertExecutable(pv, tIn.address, tOut.address);
6071
- minOut = maxBig(minOutFor(pv.netOut, slippageBps, pv.effectiveMinOut), userMinOut);
6126
+ minOut = await settleMin(pv);
6072
6127
  if (mode === "best") {
6073
6128
  entry = "swapBestExactIn";
6074
6129
  data = encodeSwapBestExactIn(tIn.address, tOut.address, amountIn, minOut, recipient, deadline);
@@ -6115,7 +6170,8 @@ var BlazePhoenix = class {
6115
6170
  slippageBps,
6116
6171
  approval,
6117
6172
  unwrapAfter: tOut.native,
6118
- steps: approval ? [approval.tx, tx] : [tx]
6173
+ steps: approval ? [approval.tx, tx] : [tx],
6174
+ ...exactOut !== void 0 ? { exactOut } : {}
6119
6175
  };
6120
6176
  }
6121
6177
  /** eth_call the swap from `from` on your RPC: the realised amountOut if it
@@ -6356,6 +6412,17 @@ function checkDeadlineSec(n) {
6356
6412
  return n;
6357
6413
  }
6358
6414
  var maxBig = (a, b) => a > b ? a : b;
6415
+ var EXACT_MARGIN_BPS = 50;
6416
+ function samePath(a, b) {
6417
+ const eqA = (x, y) => x.toLowerCase() === y.toLowerCase();
6418
+ return a.hops.length === b.hops.length && a.hops.every((h, i) => {
6419
+ const g = b.hops[i];
6420
+ return eqA(h.tokenIn, g.tokenIn) && eqA(h.tokenOut, g.tokenOut) && h.legs.length === g.legs.length && h.legs.every((l, j) => {
6421
+ const m = g.legs[j];
6422
+ 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;
6423
+ });
6424
+ });
6425
+ }
6359
6426
  function deepLink(opts = {}) {
6360
6427
  const base = (opts.baseUrl ?? "https://blazephoenix.xyz").replace(/\/+$/, "");
6361
6428
  const sp = new URLSearchParams();