@blazephoenix/sdk 1.0.1 → 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 +37 -5
- package/dist/index.cjs +96 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -2
- package/dist/index.d.ts +23 -2
- package/dist/index.js +96 -12
- package/dist/index.js.map +1 -1
- package/llms.txt +8 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,6 +13,31 @@ npm i @blazephoenix/sdk viem
|
|
|
13
13
|
# or straight from GitHub (builds on install): npm i github:blazephoenixxyz-crypto/SDK viem
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
+
## BlazePhoenix in one sentence
|
|
17
|
+
|
|
18
|
+
BlazePhoenix is the fully on-chain DEX aggregator — the route is computed inside the transaction that executes it — paired with an oracle-free staking engine that proves its own solvency on every transaction. It runs on Base, Ethereum, Optimism, Arbitrum and Robinhood Chain.
|
|
19
|
+
|
|
20
|
+
**Swap** in the [app](https://blazephoenix.xyz/?tab=swap) · **integrate** with the [SDK](https://github.com/blazephoenixxyz-crypto/SDK) (`npm i @blazephoenix/sdk`) · **connect an agent** to the [MCP server](https://github.com/blazephoenixxyz-crypto/blazephoenix-mcp) (`https://blazephoenix.xyz/mcp`, no key)
|
|
21
|
+
|
|
22
|
+
## Reading guide by level
|
|
23
|
+
|
|
24
|
+
- **New to DeFi:** [BlazePhoenix in five minutes](https://blazephoenix.xyz/learn/blazephoenix-in-five-minutes), then [What is a DEX aggregator](https://blazephoenix.xyz/learn/what-is-a-dex-aggregator) and [How to swap tokens safely](https://blazephoenix.xyz/learn/how-to-swap-safely).
|
|
25
|
+
- **Comfortable with swaps:** [Anatomy of a DEX aggregator](https://blazephoenix.xyz/learn/anatomy-of-an-aggregator) and [BlazePhoenix vs the established aggregators](https://blazephoenix.xyz/learn/how-we-compare).
|
|
26
|
+
- **Engineers and researchers:** the [Technical Whitepaper v2.3](https://blazephoenix.xyz/whitepaper.md) (DOI [10.5281/zenodo.23084091](https://doi.org/10.5281/zenodo.23084091)), the contract source in [Blaze-Phoenix-Dex](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex) and [Blaze-Phoenix-Staking](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Staking), and [On-chain quoting](https://blazephoenix.xyz/learn/on-chain-quoting).
|
|
27
|
+
- **Models and agents:** [https://blazephoenix.xyz/llms.txt](https://blazephoenix.xyz/llms.txt) and the [reading guide](https://blazephoenix.xyz/reading-guide.md).
|
|
28
|
+
|
|
29
|
+
## Trust and verification
|
|
30
|
+
|
|
31
|
+
Each item links to the place where it can be checked. The full list is at [https://blazephoenix.xyz/trust](https://blazephoenix.xyz/trust).
|
|
32
|
+
|
|
33
|
+
- **Verified contracts:** source-verified on Sourcify and the chain explorers; every address per chain at [/api/deployments](https://blazephoenix.xyz/api/deployments).
|
|
34
|
+
- **Source available:** the contract source is public under the [Business Source License 1.1](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex/blob/main/LICENSE), converting to GPL-2.0-or-later on 2030-06-01.
|
|
35
|
+
- **Public CI:** [GitHub Actions](https://github.com/blazephoenixxyz-crypto/Blaze-Phoenix-Dex/actions) on every push, including the Certora Prover and Halmos.
|
|
36
|
+
- **Bug bounty:** a [public programme](https://blazephoenix.xyz/bounty) with [credited researchers](https://blazephoenix.xyz/security/researchers) and [published advisories](https://blazephoenix.xyz/security/advisories).
|
|
37
|
+
- **Whitepaper DOI:** [10.5281/zenodo.23084091](https://doi.org/10.5281/zenodo.23084091); papers timestamped with [OpenTimestamps](https://blazephoenix.xyz/provenance/provenance.json).
|
|
38
|
+
- **Contact:** contact@blazephoenix.xyz ([security.txt](https://blazephoenix.xyz/.well-known/security.txt)); official channels at [/official](https://blazephoenix.xyz/official).
|
|
39
|
+
- **Audit status:** an independent external audit is scheduled before launch.
|
|
40
|
+
|
|
16
41
|
## Quote → build → execute
|
|
17
42
|
|
|
18
43
|
```ts
|
|
@@ -33,9 +58,11 @@ const plan = await blaze.buildSwap({
|
|
|
33
58
|
chain: 'base', tokenIn: 'WETH', tokenOut: 'USDC', amount: '1.5',
|
|
34
59
|
recipient: me, from: me, // `from` enables the allowance check
|
|
35
60
|
slippageBps: 50, // default 50; never below the on-chain floor
|
|
61
|
+
exact: true, // optional: minOut from previewPlanExact (one more eth_call)
|
|
36
62
|
});
|
|
37
63
|
plan.steps; // [approve?, swap] — { chainId, to, data, value }
|
|
38
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)
|
|
39
66
|
plan.encodedBy; // 'quoter' on 2.x (the Quoter's own bytes, verified) · 'sdk' on 1.x
|
|
40
67
|
|
|
41
68
|
// 3) optional: dry-run on your node, then execute with YOUR wallet
|
|
@@ -91,17 +118,20 @@ The SDK adapts to what each version can do (`featuresOf(version)`):
|
|
|
91
118
|
| on-chain `batchQuote` | parallel previews | ✅ chunked |
|
|
92
119
|
| `ExecutionProof` on every fill (quoted vs realised vs floor) | — | ✅ |
|
|
93
120
|
|
|
94
|
-
###
|
|
121
|
+
### Only pinned addresses are routed to by default
|
|
95
122
|
|
|
96
123
|
The registry has three layers:
|
|
97
124
|
|
|
98
125
|
1. **Embedded** — shipped in the package. Non-zero addresses are **pins**: nothing fetched
|
|
99
|
-
at runtime can move them
|
|
126
|
+
at runtime can move them, and by default they are the only Router and Quoter addresses
|
|
127
|
+
a quote or a swap uses. A new deployment reaches you with a new SDK release.
|
|
100
128
|
2. **Remote** — `GET https://blazephoenix.xyz/api/deployments` (static JSON, refreshed every
|
|
101
129
|
10 min in the background; the first lookup waits at most 3 s). It can only fill what the
|
|
102
130
|
snapshot doesn't know — e.g. the 2.0.0 addresses the day they are deployed — and only for
|
|
103
|
-
versions that can be verified on-chain (2.x+).
|
|
104
|
-
|
|
131
|
+
versions that can be verified on-chain (2.x+). Those versions are listed, but routed to
|
|
132
|
+
only with `registry: { remoteVersions: true }`: the on-chain check reads the entry's own
|
|
133
|
+
contracts, so it cannot tell a real deployment from a look-alike. With the opt-in, every
|
|
134
|
+
remote-sourced set is still **verified on your RPC before first use**:
|
|
105
135
|
code at every address, `VERSION()` matches, and the Quoter, Router and Solver all point at
|
|
106
136
|
the same Hub and Solver. Anything else fails closed (`deployment_unverified`).
|
|
107
137
|
3. **Your overrides** — always win:
|
|
@@ -133,7 +163,7 @@ await blaze.quoteBatch([req1, req2]); // per-item { ok, quote } | { ok:false, er
|
|
|
133
163
|
await blaze.getFills({ chain: 'base', lookbackBlocks: 5_000n }); // Swap (+ ExecutionProof on 2.x)
|
|
134
164
|
const stop = await blaze.watchFills({ chain: 'base', onFill: console.log });
|
|
135
165
|
await blaze.tokenInfo('base', '0x…'); // symbol / name / decimals
|
|
136
|
-
await blaze.solvency(); //
|
|
166
|
+
await blaze.solvency(); // staking proof-of-solvency; `not_deployed` where no engine is registered
|
|
137
167
|
toJSON(quote); // bigint → string, for HTTP / queues / LLMs
|
|
138
168
|
```
|
|
139
169
|
|
|
@@ -158,6 +188,8 @@ Every failure, `solvency()` included, is a `BlazeError` with a stable `code`; pr
|
|
|
158
188
|
| `no_route` | the Solver found no executable path (`SolverE(5)`) or the quote reverted |
|
|
159
189
|
| `not_executable` | the Quoter says the route cannot settle now (`canExecute = false`) |
|
|
160
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 |
|
|
161
193
|
| `deployment_unverified` | a registry deployment failed on-chain verification |
|
|
162
194
|
| `unsupported_by_version` | e.g. native ETH input on a 1.x Router |
|
|
163
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: {
|
|
@@ -5356,6 +5355,7 @@ var Registry = class {
|
|
|
5356
5355
|
fetchedAt = 0;
|
|
5357
5356
|
lastError;
|
|
5358
5357
|
mode;
|
|
5358
|
+
remoteVersions;
|
|
5359
5359
|
url;
|
|
5360
5360
|
ttlMs;
|
|
5361
5361
|
fetchFn;
|
|
@@ -5364,6 +5364,7 @@ var Registry = class {
|
|
|
5364
5364
|
overrides = /* @__PURE__ */ new Map();
|
|
5365
5365
|
constructor(opts = {}, overrides) {
|
|
5366
5366
|
this.mode = opts.mode ?? "auto";
|
|
5367
|
+
this.remoteVersions = opts.remoteVersions ?? false;
|
|
5367
5368
|
this.url = opts.url ?? DEPLOYMENTS_URL;
|
|
5368
5369
|
this.ttlMs = opts.ttlMs ?? 10 * 6e4;
|
|
5369
5370
|
this.fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
@@ -5495,7 +5496,7 @@ var Registry = class {
|
|
|
5495
5496
|
if (!isValidSelector(selector)) throw new BlazeError("bad_request", `bad version selector "${selector}"`);
|
|
5496
5497
|
const ov = this.overrides.get(chainId);
|
|
5497
5498
|
const sel = ov?.version && selector === "latest" ? ov.version : selector;
|
|
5498
|
-
const candidates = this.doc.versions.filter((v) => matchesSelector(v.version, sel) && isDeployed(v.chains[chainId])).sort((a, b) => compareVersions(b.version, a.version));
|
|
5499
|
+
const candidates = this.doc.versions.filter((v) => matchesSelector(v.version, sel) && isDeployed(v.chains[chainId])).filter((v) => this.remoteVersions || this.isPinned(v.version, chainId)).sort((a, b) => compareVersions(b.version, a.version));
|
|
5499
5500
|
const pick = candidates[0];
|
|
5500
5501
|
if (ov && (ov.router || ov.quoter)) {
|
|
5501
5502
|
const base = pick ? { ...pick.chains[chainId] } : {
|
|
@@ -5515,9 +5516,10 @@ var Registry = class {
|
|
|
5515
5516
|
}
|
|
5516
5517
|
if (!pick) {
|
|
5517
5518
|
const deployed = this.versionsOn(chainId);
|
|
5519
|
+
const remoteOnly = this.remoteVersions ? [] : deployed.filter((v) => matchesSelector(v, sel) && !this.isPinned(v, chainId));
|
|
5518
5520
|
throw new BlazeError(
|
|
5519
5521
|
"not_deployed",
|
|
5520
|
-
`no BlazePhoenix deployment matching version "${selector}" on chain ${chainId}` + (deployed.length ? ` (deployed: ${deployed.join(", ")})` : ""),
|
|
5522
|
+
`no BlazePhoenix deployment matching version "${selector}" on chain ${chainId}` + (deployed.length ? ` (deployed: ${deployed.join(", ")})` : "") + (remoteOnly.length ? ` \u2014 ${remoteOnly.join(", ")} is known only from the fetched registry; update the SDK, or set registry.remoteVersions after checking the addresses yourself` : ""),
|
|
5521
5523
|
{ details: { chainId, selector, deployed } }
|
|
5522
5524
|
);
|
|
5523
5525
|
}
|
|
@@ -5526,6 +5528,11 @@ var Registry = class {
|
|
|
5526
5528
|
const source = src("router") === "remote" || src("quoter") === "remote" ? "remote" : "embedded";
|
|
5527
5529
|
return { chainId, version: pick.version, status: pick.status, contracts: { ...set }, source };
|
|
5528
5530
|
}
|
|
5531
|
+
/** Router and Quoter both pinned in this package (not filled by a fetched document). */
|
|
5532
|
+
isPinned(version, chainId) {
|
|
5533
|
+
const src = (k) => this.fieldSource[`${version}|${chainId}|${k}`];
|
|
5534
|
+
return src("router") === "embedded" && src("quoter") === "embedded";
|
|
5535
|
+
}
|
|
5529
5536
|
latestKnownVersion(selector) {
|
|
5530
5537
|
const all = this.doc.versions.map((v) => v.version).sort((a, b) => compareVersions(b, a));
|
|
5531
5538
|
const m = selector ? all.find((v) => matchesSelector(v, selector)) : void 0;
|
|
@@ -5773,16 +5780,19 @@ var BlazePhoenix = class {
|
|
|
5773
5780
|
return { address: c.weth, native: true };
|
|
5774
5781
|
case "WETH":
|
|
5775
5782
|
return { address: c.weth, native: false };
|
|
5783
|
+
// `usdc` holds each chain's one dollar asset: USDG on Robinhood Chain, USDC
|
|
5784
|
+
// elsewhere. The other symbol names a different token there, so it is refused.
|
|
5776
5785
|
case "USDC":
|
|
5777
5786
|
case "USDG":
|
|
5778
|
-
return { address: c.usdc, native: false };
|
|
5787
|
+
if (s.toUpperCase() === (chainId === 4663 ? "USDG" : "USDC")) return { address: c.usdc, native: false };
|
|
5788
|
+
break;
|
|
5779
5789
|
case "BZPX":
|
|
5780
5790
|
if (c.bzpx) return { address: c.bzpx, native: false };
|
|
5781
5791
|
break;
|
|
5782
5792
|
}
|
|
5783
5793
|
throw new BlazeError(
|
|
5784
5794
|
"bad_request",
|
|
5785
|
-
`${side}: "${ref}" is not a 0x address or a built-in symbol (ETH, WETH, USDC, BZPX) on ${c.name} \u2014 pass the token address`
|
|
5795
|
+
`${side}: "${ref}" is not a 0x address or a built-in symbol (ETH, WETH, ${chainId === 4663 ? "USDG" : "USDC"}, BZPX) on ${c.name} \u2014 pass the token address`
|
|
5786
5796
|
);
|
|
5787
5797
|
}
|
|
5788
5798
|
async decimalsOf(client, chainId, t) {
|
|
@@ -6024,6 +6034,60 @@ var BlazePhoenix = class {
|
|
|
6024
6034
|
let encodedBy = "sdk";
|
|
6025
6035
|
let entry;
|
|
6026
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
|
+
};
|
|
6027
6091
|
if (features.previewAndEncode && !tIn.native && mode === "route") {
|
|
6028
6092
|
let call;
|
|
6029
6093
|
try {
|
|
@@ -6031,19 +6095,21 @@ var BlazePhoenix = class {
|
|
|
6031
6095
|
address: ctx.dep.contracts.quoter,
|
|
6032
6096
|
abi: QUOTER_ABI,
|
|
6033
6097
|
functionName: "previewAndEncodeWithMinOut",
|
|
6034
|
-
args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline]
|
|
6098
|
+
args: [tIn.address, tOut.address, amountIn, userMinOut, recipient, deadline],
|
|
6099
|
+
...pin
|
|
6035
6100
|
}) : await ctx.client.readContract({
|
|
6036
6101
|
address: ctx.dep.contracts.quoter,
|
|
6037
6102
|
abi: QUOTER_ABI,
|
|
6038
6103
|
functionName: "previewAndEncode",
|
|
6039
|
-
args: [tIn.address, tOut.address, amountIn, recipient, deadline]
|
|
6104
|
+
args: [tIn.address, tOut.address, amountIn, recipient, deadline],
|
|
6105
|
+
...pin
|
|
6040
6106
|
});
|
|
6041
6107
|
[pv, call] = r;
|
|
6042
6108
|
} catch (e) {
|
|
6043
6109
|
this.wrapReadError(e, "previewAndEncode");
|
|
6044
6110
|
}
|
|
6045
6111
|
assertExecutable(pv, tIn.address, tOut.address, call);
|
|
6046
|
-
minOut =
|
|
6112
|
+
minOut = await settleMin(pv);
|
|
6047
6113
|
verifySwapExactIn(call, {
|
|
6048
6114
|
tokenIn: tIn.address,
|
|
6049
6115
|
tokenOut: tOut.address,
|
|
@@ -6057,9 +6123,9 @@ var BlazePhoenix = class {
|
|
|
6057
6123
|
encodedBy = "quoter";
|
|
6058
6124
|
entry = "swapExactIn";
|
|
6059
6125
|
} else {
|
|
6060
|
-
({ 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));
|
|
6061
6127
|
assertExecutable(pv, tIn.address, tOut.address);
|
|
6062
|
-
minOut =
|
|
6128
|
+
minOut = await settleMin(pv);
|
|
6063
6129
|
if (mode === "best") {
|
|
6064
6130
|
entry = "swapBestExactIn";
|
|
6065
6131
|
data = encodeSwapBestExactIn(tIn.address, tOut.address, amountIn, minOut, recipient, deadline);
|
|
@@ -6106,7 +6172,8 @@ var BlazePhoenix = class {
|
|
|
6106
6172
|
slippageBps,
|
|
6107
6173
|
approval,
|
|
6108
6174
|
unwrapAfter: tOut.native,
|
|
6109
|
-
steps: approval ? [approval.tx, tx] : [tx]
|
|
6175
|
+
steps: approval ? [approval.tx, tx] : [tx],
|
|
6176
|
+
...exactOut !== void 0 ? { exactOut } : {}
|
|
6110
6177
|
};
|
|
6111
6178
|
}
|
|
6112
6179
|
/** eth_call the swap from `from` on your RPC: the realised amountOut if it
|
|
@@ -6172,6 +6239,12 @@ var BlazePhoenix = class {
|
|
|
6172
6239
|
if (r.status !== "success") throw new BlazeError("reverted", `approval transaction reverted (${approvalHash})`);
|
|
6173
6240
|
}
|
|
6174
6241
|
plan = { ...plan, approval: { ...plan.approval, current: plan.approval.amount } };
|
|
6242
|
+
if (Date.now() - plan.quote.quotedAt > maxAge) {
|
|
6243
|
+
if (!req) {
|
|
6244
|
+
throw new BlazeError("bad_request", `plan went older than ${maxAge}ms while the approval confirmed${approvalHash ? ` (${approvalHash})` : ""} \u2014 the swap was NOT sent; rebuild the plan, or pass \`request\` so it can be rebuilt`);
|
|
6245
|
+
}
|
|
6246
|
+
plan = await this.buildSwap({ ...req, from: req.from ?? account.address });
|
|
6247
|
+
}
|
|
6175
6248
|
}
|
|
6176
6249
|
if (opts.simulate ?? true) {
|
|
6177
6250
|
const sim = await this.simulate(plan, account.address);
|
|
@@ -6341,6 +6414,17 @@ function checkDeadlineSec(n) {
|
|
|
6341
6414
|
return n;
|
|
6342
6415
|
}
|
|
6343
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
|
+
}
|
|
6344
6428
|
function deepLink(opts = {}) {
|
|
6345
6429
|
const base = (opts.baseUrl ?? "https://blazephoenix.xyz").replace(/\/+$/, "");
|
|
6346
6430
|
const sp = new URLSearchParams();
|