@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/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
|
-
/**
|
|
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;
|
|
@@ -182,8 +183,10 @@ interface ExactQuote {
|
|
|
182
183
|
tokenIn: Address;
|
|
183
184
|
tokenOut: Address;
|
|
184
185
|
amountIn: bigint;
|
|
185
|
-
/** Execution-grade NET output (every concentrated leg dry-run on the
|
|
186
|
+
/** Execution-grade NET output (every concentrated leg dry-run on its venue), net of the protocol fee. */
|
|
186
187
|
exactOut: bigint;
|
|
188
|
+
/** The scalar exactly as the Quoter returned it (gross of the fee on 1.x Quoters). */
|
|
189
|
+
quoterOut: bigint;
|
|
187
190
|
route: Route;
|
|
188
191
|
quotedAt: number;
|
|
189
192
|
}
|
|
@@ -211,6 +214,15 @@ interface SwapRequest extends QuoteRequest {
|
|
|
211
214
|
mode?: 'route' | 'best';
|
|
212
215
|
/** Allowance to request when approval is needed: 'exact' (default) or 'max'. */
|
|
213
216
|
approve?: 'exact' | 'max';
|
|
217
|
+
/**
|
|
218
|
+
* true — also read previewPlanExact (every concentrated leg dry-run) in the same block and
|
|
219
|
+
* derive minOut from its exactOut: a fixed 50 bps safety margin first, then slippageBps off
|
|
220
|
+
* that, never below the default minimum. The calldata still executes the route of the default
|
|
221
|
+
* path; the exact read must price that same route, or buildSwap refuses. A userMinOut above
|
|
222
|
+
* exactOut less the margin is refused (`min_above_exact`). One more eth_call; the swap reverts
|
|
223
|
+
* instead of filling short when the pool moves past the tolerance.
|
|
224
|
+
*/
|
|
225
|
+
exact?: boolean;
|
|
214
226
|
}
|
|
215
227
|
interface ApprovalStep {
|
|
216
228
|
token: Address;
|
|
@@ -237,6 +249,8 @@ interface SwapPlan {
|
|
|
237
249
|
unwrapAfter: boolean;
|
|
238
250
|
/** Ordered transactions to send: [approval?, swap]. */
|
|
239
251
|
steps: TxRequest[];
|
|
252
|
+
/** With `exact: true`: the previewPlanExact output minOut was derived from. */
|
|
253
|
+
exactOut?: bigint;
|
|
240
254
|
}
|
|
241
255
|
interface SimulationResult {
|
|
242
256
|
ok: boolean;
|
|
@@ -4249,7 +4263,7 @@ declare const CAUTION_IMPACT_BPS = 200;
|
|
|
4249
4263
|
declare function routeIsConsistent(route: Route, tokenIn: Address, tokenOut: Address): boolean;
|
|
4250
4264
|
declare function phoenixCheck(pv: Preview, tokenIn: Address, tokenOut: Address): QuoteChecks;
|
|
4251
4265
|
|
|
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';
|
|
4266
|
+
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
4267
|
declare class BlazeError extends Error {
|
|
4254
4268
|
readonly code: BlazeErrorCode;
|
|
4255
4269
|
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
|
-
/**
|
|
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;
|
|
@@ -182,8 +183,10 @@ interface ExactQuote {
|
|
|
182
183
|
tokenIn: Address;
|
|
183
184
|
tokenOut: Address;
|
|
184
185
|
amountIn: bigint;
|
|
185
|
-
/** Execution-grade NET output (every concentrated leg dry-run on the
|
|
186
|
+
/** Execution-grade NET output (every concentrated leg dry-run on its venue), net of the protocol fee. */
|
|
186
187
|
exactOut: bigint;
|
|
188
|
+
/** The scalar exactly as the Quoter returned it (gross of the fee on 1.x Quoters). */
|
|
189
|
+
quoterOut: bigint;
|
|
187
190
|
route: Route;
|
|
188
191
|
quotedAt: number;
|
|
189
192
|
}
|
|
@@ -211,6 +214,15 @@ interface SwapRequest extends QuoteRequest {
|
|
|
211
214
|
mode?: 'route' | 'best';
|
|
212
215
|
/** Allowance to request when approval is needed: 'exact' (default) or 'max'. */
|
|
213
216
|
approve?: 'exact' | 'max';
|
|
217
|
+
/**
|
|
218
|
+
* true — also read previewPlanExact (every concentrated leg dry-run) in the same block and
|
|
219
|
+
* derive minOut from its exactOut: a fixed 50 bps safety margin first, then slippageBps off
|
|
220
|
+
* that, never below the default minimum. The calldata still executes the route of the default
|
|
221
|
+
* path; the exact read must price that same route, or buildSwap refuses. A userMinOut above
|
|
222
|
+
* exactOut less the margin is refused (`min_above_exact`). One more eth_call; the swap reverts
|
|
223
|
+
* instead of filling short when the pool moves past the tolerance.
|
|
224
|
+
*/
|
|
225
|
+
exact?: boolean;
|
|
214
226
|
}
|
|
215
227
|
interface ApprovalStep {
|
|
216
228
|
token: Address;
|
|
@@ -237,6 +249,8 @@ interface SwapPlan {
|
|
|
237
249
|
unwrapAfter: boolean;
|
|
238
250
|
/** Ordered transactions to send: [approval?, swap]. */
|
|
239
251
|
steps: TxRequest[];
|
|
252
|
+
/** With `exact: true`: the previewPlanExact output minOut was derived from. */
|
|
253
|
+
exactOut?: bigint;
|
|
240
254
|
}
|
|
241
255
|
interface SimulationResult {
|
|
242
256
|
ok: boolean;
|
|
@@ -4249,7 +4263,7 @@ declare const CAUTION_IMPACT_BPS = 200;
|
|
|
4249
4263
|
declare function routeIsConsistent(route: Route, tokenIn: Address, tokenOut: Address): boolean;
|
|
4250
4264
|
declare function phoenixCheck(pv: Preview, tokenIn: Address, tokenOut: Address): QuoteChecks;
|
|
4251
4265
|
|
|
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';
|
|
4266
|
+
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
4267
|
declare class BlazeError extends Error {
|
|
4254
4268
|
readonly code: BlazeErrorCode;
|
|
4255
4269
|
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: "
|
|
4740
|
-
staking: "0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77",
|
|
4739
|
+
bzpx: "0x053D75c00FdD571538492e3B1686Bd963a0911A7",
|
|
4741
4740
|
blockTime: 2
|
|
4742
4741
|
},
|
|
4743
4742
|
10: {
|
|
@@ -4767,7 +4766,8 @@ var CHAINS = {
|
|
|
4767
4766
|
explorer: "https://robinhoodchain.blockscout.com",
|
|
4768
4767
|
weth: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
4769
4768
|
usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
4770
|
-
blockTime:
|
|
4769
|
+
blockTime: 0.104
|
|
4770
|
+
// measured ~0.104 s/block
|
|
4771
4771
|
}
|
|
4772
4772
|
};
|
|
4773
4773
|
var ALIASES = {
|
|
@@ -4832,7 +4832,7 @@ function urlTransport(url, o) {
|
|
|
4832
4832
|
throw new BlazeError("bad_request", `refusing RPC URL "${redact(url)}": use https:// or wss:// (http/ws only for localhost)`);
|
|
4833
4833
|
}
|
|
4834
4834
|
const u = url.trim();
|
|
4835
|
-
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" } });
|
|
4836
4836
|
}
|
|
4837
4837
|
function redact(url) {
|
|
4838
4838
|
try {
|
|
@@ -4980,6 +4980,9 @@ function scrubCause(c, depth = 0) {
|
|
|
4980
4980
|
out.name = c.name;
|
|
4981
4981
|
return out;
|
|
4982
4982
|
}
|
|
4983
|
+
function untrustedText(t, max = 120) {
|
|
4984
|
+
return (t ?? "").replace(/[^\x20-\x7e]+/g, " ").replace(/\s+/g, " ").trim().slice(0, max);
|
|
4985
|
+
}
|
|
4983
4986
|
var BlazeError = class extends Error {
|
|
4984
4987
|
code;
|
|
4985
4988
|
details;
|
|
@@ -5811,6 +5814,20 @@ var BlazePhoenix = class {
|
|
|
5811
5814
|
if (typeof req.amountIn === "bigint") v = req.amountIn;
|
|
5812
5815
|
else if (INT.test(String(req.amountIn).trim())) v = BigInt(String(req.amountIn).trim());
|
|
5813
5816
|
else throw new BlazeError("bad_request", 'amountIn must be an integer in base units \u2014 use `amount` for human units ("1.5")');
|
|
5817
|
+
if (req.amount !== void 0 && req.amount !== null && req.amount !== "") {
|
|
5818
|
+
const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
|
|
5819
|
+
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 });
|
|
5820
|
+
});
|
|
5821
|
+
let human;
|
|
5822
|
+
try {
|
|
5823
|
+
human = toBaseUnits(req.amount, d);
|
|
5824
|
+
} catch (e) {
|
|
5825
|
+
throw new BlazeError("bad_request", e.message);
|
|
5826
|
+
}
|
|
5827
|
+
if (human !== v) {
|
|
5828
|
+
throw new BlazeError("bad_request", `amount and amountIn disagree (${human} vs ${v} base units): pass only one`);
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5814
5831
|
} else if (req.amount !== void 0) {
|
|
5815
5832
|
const d = await this.decimalsOf(client, chainId, tIn).catch((e) => {
|
|
5816
5833
|
throw new BlazeError("bad_request", `could not read decimals of ${tIn.address} on your RPC \u2014 pass amountIn in base units`, { cause: e });
|
|
@@ -5918,13 +5935,15 @@ var BlazePhoenix = class {
|
|
|
5918
5935
|
args: [tIn.address, tOut.address, amountIn],
|
|
5919
5936
|
...req.blockNumber !== void 0 ? { blockNumber: req.blockNumber } : {}
|
|
5920
5937
|
});
|
|
5938
|
+
const net = featuresOf(ctx.dep.version).previewAndEncode ? exactOut : exactOut - (exactOut * BigInt(FEE_BPS) + 9999n) / 10000n;
|
|
5921
5939
|
return {
|
|
5922
5940
|
chainId: ctx.chainId,
|
|
5923
5941
|
version: ctx.dep.version,
|
|
5924
5942
|
tokenIn: tIn.address,
|
|
5925
5943
|
tokenOut: tOut.address,
|
|
5926
5944
|
amountIn,
|
|
5927
|
-
exactOut,
|
|
5945
|
+
exactOut: net,
|
|
5946
|
+
quoterOut: exactOut,
|
|
5928
5947
|
route,
|
|
5929
5948
|
quotedAt: Date.now()
|
|
5930
5949
|
};
|
|
@@ -6033,6 +6052,60 @@ var BlazePhoenix = class {
|
|
|
6033
6052
|
let encodedBy = "sdk";
|
|
6034
6053
|
let entry;
|
|
6035
6054
|
let minOut;
|
|
6055
|
+
let at;
|
|
6056
|
+
if (req.exact) {
|
|
6057
|
+
try {
|
|
6058
|
+
at = await ctx.client.getBlockNumber();
|
|
6059
|
+
} catch (e) {
|
|
6060
|
+
this.wrapReadError(e, "block number");
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
const pin = at !== void 0 ? { blockNumber: at } : {};
|
|
6064
|
+
let exactOut;
|
|
6065
|
+
const exactRead = async (route) => {
|
|
6066
|
+
let ex;
|
|
6067
|
+
try {
|
|
6068
|
+
ex = await ctx.client.readContract({
|
|
6069
|
+
address: ctx.dep.contracts.quoter,
|
|
6070
|
+
abi: QUOTER_ABI,
|
|
6071
|
+
functionName: "previewPlanExact",
|
|
6072
|
+
args: [tIn.address, tOut.address, amountIn],
|
|
6073
|
+
...pin
|
|
6074
|
+
});
|
|
6075
|
+
} catch (e) {
|
|
6076
|
+
this.wrapReadError(e, "exact quote");
|
|
6077
|
+
}
|
|
6078
|
+
if (!samePath(ex[0], route)) {
|
|
6079
|
+
throw new BlazeError("calldata_mismatch", "previewPlanExact priced a different route than the one to execute");
|
|
6080
|
+
}
|
|
6081
|
+
exactOut = ex[1];
|
|
6082
|
+
return ex[1];
|
|
6083
|
+
};
|
|
6084
|
+
const settleMin = async (p) => {
|
|
6085
|
+
let floor = minOutFor(p.netOut, slippageBps, p.ironFloor);
|
|
6086
|
+
let ceil;
|
|
6087
|
+
if (req.exact) {
|
|
6088
|
+
const ex = await exactRead(p.route);
|
|
6089
|
+
ceil = minOutFor(ex, EXACT_MARGIN_BPS, 0n);
|
|
6090
|
+
floor = maxBig(floor, minOutFor(ceil, slippageBps, 0n));
|
|
6091
|
+
}
|
|
6092
|
+
if (userMinOut === 0n) return floor;
|
|
6093
|
+
if (userMinOut < floor) {
|
|
6094
|
+
throw new BlazeError(
|
|
6095
|
+
"min_implausible",
|
|
6096
|
+
`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`,
|
|
6097
|
+
{ details: { userMinOut: userMinOut.toString(), floor: floor.toString() } }
|
|
6098
|
+
);
|
|
6099
|
+
}
|
|
6100
|
+
if (ceil !== void 0 && userMinOut > ceil && userMinOut > floor) {
|
|
6101
|
+
throw new BlazeError(
|
|
6102
|
+
"min_above_exact",
|
|
6103
|
+
`userMinOut is above ${ceil} (exactOut less ${EXACT_MARGIN_BPS} bps): an honest swap at this minimum may revert`,
|
|
6104
|
+
{ details: { userMinOut: userMinOut.toString(), ceiling: ceil.toString() } }
|
|
6105
|
+
);
|
|
6106
|
+
}
|
|
6107
|
+
return userMinOut;
|
|
6108
|
+
};
|
|
6036
6109
|
if (features.previewAndEncode && !tIn.native && mode === "route") {
|
|
6037
6110
|
let call;
|
|
6038
6111
|
try {
|
|
@@ -6040,19 +6113,21 @@ var BlazePhoenix = class {
|
|
|
6040
6113
|
address: ctx.dep.contracts.quoter,
|
|
6041
6114
|
abi: QUOTER_ABI,
|
|
6042
6115
|
functionName: "previewAndEncodeWithMinOut",
|
|
6043
|
-
args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline]
|
|
6116
|
+
args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline],
|
|
6117
|
+
...pin
|
|
6044
6118
|
}) : await ctx.client.readContract({
|
|
6045
6119
|
address: ctx.dep.contracts.quoter,
|
|
6046
6120
|
abi: QUOTER_ABI,
|
|
6047
6121
|
functionName: "previewAndEncode",
|
|
6048
|
-
args: [tIn.address, tOut.address, amountIn, recipient, deadline]
|
|
6122
|
+
args: [tIn.address, tOut.address, amountIn, recipient, deadline],
|
|
6123
|
+
...pin
|
|
6049
6124
|
});
|
|
6050
6125
|
[pv, call] = r;
|
|
6051
6126
|
} catch (e) {
|
|
6052
6127
|
this.wrapReadError(e, "previewAndEncode");
|
|
6053
6128
|
}
|
|
6054
6129
|
assertExecutable(pv, tIn.address, tOut.address, call);
|
|
6055
|
-
minOut =
|
|
6130
|
+
minOut = await settleMin(pv);
|
|
6056
6131
|
verifySwapExactIn(call, {
|
|
6057
6132
|
tokenIn: tIn.address,
|
|
6058
6133
|
tokenOut: tOut.address,
|
|
@@ -6066,9 +6141,9 @@ var BlazePhoenix = class {
|
|
|
6066
6141
|
encodedBy = "quoter";
|
|
6067
6142
|
entry = "swapExactIn";
|
|
6068
6143
|
} else {
|
|
6069
|
-
({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut));
|
|
6144
|
+
({ pv, fb } = await this.readPreview(ctx, tIn.address, tOut.address, amountIn, userMinOut, at));
|
|
6070
6145
|
assertExecutable(pv, tIn.address, tOut.address);
|
|
6071
|
-
minOut =
|
|
6146
|
+
minOut = await settleMin(pv);
|
|
6072
6147
|
if (mode === "best") {
|
|
6073
6148
|
entry = "swapBestExactIn";
|
|
6074
6149
|
data = encodeSwapBestExactIn(tIn.address, tOut.address, amountIn, minOut, recipient, deadline);
|
|
@@ -6115,7 +6190,8 @@ var BlazePhoenix = class {
|
|
|
6115
6190
|
slippageBps,
|
|
6116
6191
|
approval,
|
|
6117
6192
|
unwrapAfter: tOut.native,
|
|
6118
|
-
steps: approval ? [approval.tx, tx] : [tx]
|
|
6193
|
+
steps: approval ? [approval.tx, tx] : [tx],
|
|
6194
|
+
...exactOut !== void 0 ? { exactOut } : {}
|
|
6119
6195
|
};
|
|
6120
6196
|
}
|
|
6121
6197
|
/** eth_call the swap from `from` on your RPC: the realised amountOut if it
|
|
@@ -6132,7 +6208,7 @@ var BlazePhoenix = class {
|
|
|
6132
6208
|
} catch (e) {
|
|
6133
6209
|
if (isTransportError(e)) throw new BlazeError("rpc_error", `simulation: your RPC failed (${e?.message?.split("\n")[0]})`, { cause: e });
|
|
6134
6210
|
const d = decodeBlazeError(e);
|
|
6135
|
-
return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: e?.message?.split("\n")[0]
|
|
6211
|
+
return { ok: false, error: d ?? { contract: "unknown", name: "revert", reason: untrustedText(e?.message?.split("\n")[0]) || "execution reverted" } };
|
|
6136
6212
|
}
|
|
6137
6213
|
}
|
|
6138
6214
|
/**
|
|
@@ -6356,6 +6432,17 @@ function checkDeadlineSec(n) {
|
|
|
6356
6432
|
return n;
|
|
6357
6433
|
}
|
|
6358
6434
|
var maxBig = (a, b) => a > b ? a : b;
|
|
6435
|
+
var EXACT_MARGIN_BPS = 50;
|
|
6436
|
+
function samePath(a, b) {
|
|
6437
|
+
const eqA = (x, y) => x.toLowerCase() === y.toLowerCase();
|
|
6438
|
+
return a.hops.length === b.hops.length && a.hops.every((h, i) => {
|
|
6439
|
+
const g = b.hops[i];
|
|
6440
|
+
return eqA(h.tokenIn, g.tokenIn) && eqA(h.tokenOut, g.tokenOut) && h.legs.length === g.legs.length && h.legs.every((l, j) => {
|
|
6441
|
+
const m = g.legs[j];
|
|
6442
|
+
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;
|
|
6443
|
+
});
|
|
6444
|
+
});
|
|
6445
|
+
}
|
|
6359
6446
|
function deepLink(opts = {}) {
|
|
6360
6447
|
const base = (opts.baseUrl ?? "https://blazephoenix.xyz").replace(/\/+$/, "");
|
|
6361
6448
|
const sp = new URLSearchParams();
|