@blazephoenix/sdk 1.0.0 → 1.1.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 +36 -6
- package/dist/index.cjs +301 -262
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +302 -263
- package/dist/index.js.map +1 -1
- package/llms.txt +56 -0
- package/package.json +6 -3
package/dist/index.cjs
CHANGED
|
@@ -4716,12 +4716,278 @@ function phoenixCheck(pv, tokenIn, tokenOut) {
|
|
|
4716
4716
|
disclaimer: 'Deterministic checks derived from on-chain state, not financial advice. "blocked" is a real answer \u2014 we fail closed rather than guess.'
|
|
4717
4717
|
};
|
|
4718
4718
|
}
|
|
4719
|
+
|
|
4720
|
+
// src/constants.ts
|
|
4721
|
+
var API_BASE = "https://blazephoenix.xyz";
|
|
4722
|
+
var DEPLOYMENTS_URL = `${API_BASE}/api/deployments`;
|
|
4723
|
+
var SUPPORTED_CHAIN_IDS = [8453, 1, 10, 42161, 4663];
|
|
4724
|
+
var CHAINS = {
|
|
4725
|
+
1: {
|
|
4726
|
+
chainId: 1,
|
|
4727
|
+
name: "Ethereum",
|
|
4728
|
+
key: "ETHEREUM",
|
|
4729
|
+
explorer: "https://etherscan.io",
|
|
4730
|
+
weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
4731
|
+
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
4732
|
+
blockTime: 12
|
|
4733
|
+
},
|
|
4734
|
+
8453: {
|
|
4735
|
+
chainId: 8453,
|
|
4736
|
+
name: "Base",
|
|
4737
|
+
key: "BASE",
|
|
4738
|
+
explorer: "https://basescan.org",
|
|
4739
|
+
weth: "0x4200000000000000000000000000000000000006",
|
|
4740
|
+
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
4741
|
+
bzpx: "0x23113e72165a034265Ab8Bf2277CCB7a85Cb7483",
|
|
4742
|
+
staking: "0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77",
|
|
4743
|
+
blockTime: 2
|
|
4744
|
+
},
|
|
4745
|
+
10: {
|
|
4746
|
+
chainId: 10,
|
|
4747
|
+
name: "Optimism",
|
|
4748
|
+
key: "OPTIMISM",
|
|
4749
|
+
explorer: "https://optimistic.etherscan.io",
|
|
4750
|
+
weth: "0x4200000000000000000000000000000000000006",
|
|
4751
|
+
usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
4752
|
+
blockTime: 2
|
|
4753
|
+
},
|
|
4754
|
+
42161: {
|
|
4755
|
+
chainId: 42161,
|
|
4756
|
+
name: "Arbitrum",
|
|
4757
|
+
key: "ARBITRUM",
|
|
4758
|
+
explorer: "https://arbiscan.io",
|
|
4759
|
+
weth: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
4760
|
+
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
4761
|
+
blockTime: 0.25
|
|
4762
|
+
},
|
|
4763
|
+
// Robinhood Chain — the dollar asset is USDG, not USDC; the `usdc` field
|
|
4764
|
+
// carries the chain's canonical dollar token. Explorer is Blockscout.
|
|
4765
|
+
4663: {
|
|
4766
|
+
chainId: 4663,
|
|
4767
|
+
name: "Robinhood",
|
|
4768
|
+
key: "ROBINHOOD",
|
|
4769
|
+
explorer: "https://robinhoodchain.blockscout.com",
|
|
4770
|
+
weth: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
4771
|
+
usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
4772
|
+
blockTime: 2
|
|
4773
|
+
}
|
|
4774
|
+
};
|
|
4775
|
+
var ALIASES = {
|
|
4776
|
+
"1": 1,
|
|
4777
|
+
eth: 1,
|
|
4778
|
+
ethereum: 1,
|
|
4779
|
+
mainnet: 1,
|
|
4780
|
+
"8453": 8453,
|
|
4781
|
+
base: 8453,
|
|
4782
|
+
"10": 10,
|
|
4783
|
+
op: 10,
|
|
4784
|
+
optimism: 10,
|
|
4785
|
+
"42161": 42161,
|
|
4786
|
+
arb: 42161,
|
|
4787
|
+
arbitrum: 42161,
|
|
4788
|
+
"arbitrum-one": 42161,
|
|
4789
|
+
"4663": 4663,
|
|
4790
|
+
rh: 4663,
|
|
4791
|
+
robinhood: 4663,
|
|
4792
|
+
"robinhood-chain": 4663
|
|
4793
|
+
};
|
|
4794
|
+
function resolveChain(chain) {
|
|
4795
|
+
const id = ALIASES[String(chain).trim().toLowerCase()];
|
|
4796
|
+
if (!id) {
|
|
4797
|
+
throw new Error(
|
|
4798
|
+
`Unsupported chain "${chain}" \u2014 use 8453/base, 1/eth, 10/optimism, 42161/arbitrum or 4663/robinhood`
|
|
4799
|
+
);
|
|
4800
|
+
}
|
|
4801
|
+
return id;
|
|
4802
|
+
}
|
|
4803
|
+
function tryResolveChain(chain) {
|
|
4804
|
+
return ALIASES[String(chain).trim().toLowerCase()];
|
|
4805
|
+
}
|
|
4806
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
4807
|
+
var NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
4808
|
+
var PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4809
|
+
var FEE_BPS = 28;
|
|
4810
|
+
var SWAP_TOPIC0 = "0xd6d34547c69c5ee3d2667625c188acf1006abb93e0ee7cf03925c67cf7760413";
|
|
4811
|
+
var SURPLUS_TOPIC0 = "0x1a3afef0f067eb51a6bfec6ab3625a4408dd8c3571b72836483e68feb70d1026";
|
|
4812
|
+
var EXECUTION_PROOF_TOPIC0 = "0xb5a62bcb8753e37706da20862f2a32a16831ef0144567eb15b1da49547e9f84c";
|
|
4813
|
+
var FEE_TOPIC0 = "0x18fa25e22e0f1f39ef2130081363ba86a1259e598e669558683dabfc15bfe731";
|
|
4814
|
+
|
|
4815
|
+
// src/rpc.ts
|
|
4816
|
+
var isTransport = (v) => typeof v === "function";
|
|
4817
|
+
var isEip1193 = (v) => !!v && typeof v === "object" && typeof v.request === "function";
|
|
4818
|
+
var isSource = (v) => typeof v === "string" || Array.isArray(v) || isTransport(v) || isEip1193(v);
|
|
4819
|
+
function isAllowedRpcUrl(url) {
|
|
4820
|
+
let u;
|
|
4821
|
+
try {
|
|
4822
|
+
u = new URL(url.trim());
|
|
4823
|
+
} catch {
|
|
4824
|
+
return false;
|
|
4825
|
+
}
|
|
4826
|
+
if (u.protocol === "https:" || u.protocol === "wss:") return true;
|
|
4827
|
+
if (u.protocol === "http:" || u.protocol === "ws:") {
|
|
4828
|
+
return u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "[::1]";
|
|
4829
|
+
}
|
|
4830
|
+
return false;
|
|
4831
|
+
}
|
|
4832
|
+
function urlTransport(url, o) {
|
|
4833
|
+
if (!isAllowedRpcUrl(url)) {
|
|
4834
|
+
throw new BlazeError("bad_request", `refusing RPC URL "${redact(url)}": use https:// or wss:// (http/ws only for localhost)`);
|
|
4835
|
+
}
|
|
4836
|
+
const u = url.trim();
|
|
4837
|
+
return u.startsWith("ws") ? viem.webSocket(u, { timeout: o.timeoutMs, retryCount: o.retries }) : viem.http(u, { timeout: o.timeoutMs, retryCount: o.retries, batch: o.batch });
|
|
4838
|
+
}
|
|
4839
|
+
function redact(url) {
|
|
4840
|
+
try {
|
|
4841
|
+
const u = new URL(url);
|
|
4842
|
+
const path = u.pathname.length > 1 ? "/\u2026" : "";
|
|
4843
|
+
return `${u.protocol}//${u.host}${path}`;
|
|
4844
|
+
} catch {
|
|
4845
|
+
return "<invalid url>";
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4848
|
+
function scrubUrls(text) {
|
|
4849
|
+
return text.replace(/\b(?:https?|wss?):\/\/[^\s"'<>]+/gi, (m) => redact(m));
|
|
4850
|
+
}
|
|
4851
|
+
function transportFrom(src, opts = {}) {
|
|
4852
|
+
const o = {
|
|
4853
|
+
timeoutMs: opts.timeoutMs ?? 1e4,
|
|
4854
|
+
retries: opts.retries ?? 2,
|
|
4855
|
+
batch: opts.batch ?? false
|
|
4856
|
+
};
|
|
4857
|
+
if (isTransport(src)) return src;
|
|
4858
|
+
if (isEip1193(src)) return viem.custom(src, { retryCount: o.retries });
|
|
4859
|
+
if (typeof src === "string") return urlTransport(src, o);
|
|
4860
|
+
if (Array.isArray(src)) {
|
|
4861
|
+
const list = src.map((u) => u.trim()).filter(Boolean);
|
|
4862
|
+
if (list.length === 0) throw new BlazeError("rpc_required", "empty RPC list");
|
|
4863
|
+
if (list.length === 1) return urlTransport(list[0], o);
|
|
4864
|
+
return viem.fallback(list.map((u) => urlTransport(u, { ...o, retries: 0 })), { retryCount: o.retries });
|
|
4865
|
+
}
|
|
4866
|
+
throw new BlazeError("bad_request", "unrecognised rpc value");
|
|
4867
|
+
}
|
|
4868
|
+
var RPC_HELP = "BlazePhoenix SDK 1.x runs 100% on YOUR RPC \u2014 it ships no endpoints and never reads through our servers. Pass `rpc` (a URL, a list of URLs, a wallet provider or a viem transport), e.g. new BlazePhoenix({ rpc: { base: process.env.BASE_RPC_URL } }). Any provider's free tier is enough.";
|
|
4869
|
+
var RpcRouter = class {
|
|
4870
|
+
perChain = /* @__PURE__ */ new Map();
|
|
4871
|
+
single;
|
|
4872
|
+
opts;
|
|
4873
|
+
constructor(config, opts = {}) {
|
|
4874
|
+
this.opts = opts;
|
|
4875
|
+
if (config === void 0 || config === null || config === "") return;
|
|
4876
|
+
if (isSource(config)) {
|
|
4877
|
+
this.single = { source: config };
|
|
4878
|
+
return;
|
|
4879
|
+
}
|
|
4880
|
+
if (typeof config !== "object") throw new BlazeError("bad_request", "rpc must be a URL, a list, a provider, a transport or a per-chain map");
|
|
4881
|
+
for (const [k, v] of Object.entries(config)) {
|
|
4882
|
+
if (v === void 0 || v === null || v === "") continue;
|
|
4883
|
+
const id = tryResolveChain(k);
|
|
4884
|
+
if (!id) throw new BlazeError("bad_request", `rpc map: unknown chain "${k}"`);
|
|
4885
|
+
if (!isSource(v)) throw new BlazeError("bad_request", `rpc map: bad value for chain "${k}"`);
|
|
4886
|
+
this.perChain.set(id, { source: v, chainId: id });
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
/** Chains with an explicitly configured RPC (a single source reports none). */
|
|
4890
|
+
configuredChains() {
|
|
4891
|
+
return SUPPORTED_CHAIN_IDS.filter((id) => this.perChain.has(id));
|
|
4892
|
+
}
|
|
4893
|
+
hasAny() {
|
|
4894
|
+
return !!this.single || this.perChain.size > 0;
|
|
4895
|
+
}
|
|
4896
|
+
/** The chain to use when a request names none: the only configured one. */
|
|
4897
|
+
async defaultChain() {
|
|
4898
|
+
if (this.perChain.size === 1) return [...this.perChain.keys()][0];
|
|
4899
|
+
if (this.single && this.perChain.size === 0) {
|
|
4900
|
+
const id = await this.verify(this.single);
|
|
4901
|
+
const c = tryResolveChain(id);
|
|
4902
|
+
if (!c) throw new BlazeError("rpc_chain_mismatch", `your RPC serves chain ${id}, which BlazePhoenix is not deployed on`);
|
|
4903
|
+
return c;
|
|
4904
|
+
}
|
|
4905
|
+
throw new BlazeError("bad_request", "this request needs `chain` (several RPCs are configured)");
|
|
4906
|
+
}
|
|
4907
|
+
slotFor(chainId) {
|
|
4908
|
+
const s = this.perChain.get(chainId) ?? this.single;
|
|
4909
|
+
if (!s) {
|
|
4910
|
+
throw new BlazeError(
|
|
4911
|
+
"rpc_required",
|
|
4912
|
+
`no RPC configured for ${CHAINS[chainId].name} (${chainId}). ${RPC_HELP}`,
|
|
4913
|
+
{ details: { chainId } }
|
|
4914
|
+
);
|
|
4915
|
+
}
|
|
4916
|
+
return s;
|
|
4917
|
+
}
|
|
4918
|
+
clientOf(s) {
|
|
4919
|
+
if (!s.client) {
|
|
4920
|
+
s.client = viem.createPublicClient({ transport: transportFrom(s.source, this.opts) });
|
|
4921
|
+
}
|
|
4922
|
+
return s.client;
|
|
4923
|
+
}
|
|
4924
|
+
verify(s) {
|
|
4925
|
+
if (!s.verified) {
|
|
4926
|
+
const c = this.clientOf(s);
|
|
4927
|
+
s.verified = c.getChainId().catch((e) => {
|
|
4928
|
+
s.verified = void 0;
|
|
4929
|
+
throw new BlazeError("rpc_error", `your RPC did not answer eth_chainId: ${scrubUrls(String(e?.message ?? e))}`, { cause: e });
|
|
4930
|
+
});
|
|
4931
|
+
}
|
|
4932
|
+
return s.verified;
|
|
4933
|
+
}
|
|
4934
|
+
/** A PublicClient for `chain`, over YOUR node, after checking it really is that chain. */
|
|
4935
|
+
async client(chain) {
|
|
4936
|
+
const chainId = resolveChain(chain);
|
|
4937
|
+
const s = this.slotFor(chainId);
|
|
4938
|
+
const actual = await this.verify(s);
|
|
4939
|
+
if (actual !== chainId) {
|
|
4940
|
+
throw new BlazeError(
|
|
4941
|
+
"rpc_chain_mismatch",
|
|
4942
|
+
`your RPC serves chain ${actual}, not ${CHAINS[chainId].name} (${chainId}) \u2014 configure one per chain: rpc: { base: "\u2026", eth: "\u2026" }`,
|
|
4943
|
+
{ details: { expected: chainId, actual } }
|
|
4944
|
+
);
|
|
4945
|
+
}
|
|
4946
|
+
return this.clientOf(s);
|
|
4947
|
+
}
|
|
4948
|
+
};
|
|
4949
|
+
function rpcFromEnv(env = globalEnv()) {
|
|
4950
|
+
const split = (v) => {
|
|
4951
|
+
const list = v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
4952
|
+
return list.length === 1 ? list[0] : list;
|
|
4953
|
+
};
|
|
4954
|
+
const map = {};
|
|
4955
|
+
for (const id of SUPPORTED_CHAIN_IDS) {
|
|
4956
|
+
const v = env[`BLAZEPHOENIX_RPC_${CHAINS[id].key}`] ?? env[`BLAZEPHOENIX_RPC_${id}`];
|
|
4957
|
+
if (v && v.trim()) map[String(id)] = split(v);
|
|
4958
|
+
}
|
|
4959
|
+
const single = env.BLAZEPHOENIX_RPC_URL;
|
|
4960
|
+
if (Object.keys(map).length > 0) {
|
|
4961
|
+
if (single && single.trim()) {
|
|
4962
|
+
throw new BlazeError(
|
|
4963
|
+
"bad_request",
|
|
4964
|
+
"set either BLAZEPHOENIX_RPC_URL (one node) or the per-chain BLAZEPHOENIX_RPC_<CHAIN> variables, not both"
|
|
4965
|
+
);
|
|
4966
|
+
}
|
|
4967
|
+
return map;
|
|
4968
|
+
}
|
|
4969
|
+
return single && single.trim() ? split(single) : void 0;
|
|
4970
|
+
}
|
|
4971
|
+
function globalEnv() {
|
|
4972
|
+
const p = globalThis.process;
|
|
4973
|
+
return p?.env ?? {};
|
|
4974
|
+
}
|
|
4975
|
+
|
|
4976
|
+
// src/errors.ts
|
|
4977
|
+
function scrubCause(c, depth = 0) {
|
|
4978
|
+
if (c === void 0 || c === null || depth > 8) return void 0;
|
|
4979
|
+
if (!(c instanceof Error)) return scrubUrls(String(c));
|
|
4980
|
+
const inner = scrubCause(c.cause, depth + 1);
|
|
4981
|
+
const out = new Error(scrubUrls(c.message), inner !== void 0 ? { cause: inner } : void 0);
|
|
4982
|
+
out.name = c.name;
|
|
4983
|
+
return out;
|
|
4984
|
+
}
|
|
4719
4985
|
var BlazeError = class extends Error {
|
|
4720
4986
|
code;
|
|
4721
4987
|
details;
|
|
4722
4988
|
revert;
|
|
4723
4989
|
constructor(code, message, opts = {}) {
|
|
4724
|
-
super(message, opts.cause !== void 0 ? { cause: opts.cause } : void 0);
|
|
4990
|
+
super(message, opts.cause !== void 0 ? { cause: scrubCause(opts.cause) } : void 0);
|
|
4725
4991
|
this.name = "BlazeError";
|
|
4726
4992
|
this.code = code;
|
|
4727
4993
|
this.details = opts.details;
|
|
@@ -4884,101 +5150,6 @@ function minOutFor(netOut, slippageBps, floor) {
|
|
|
4884
5150
|
return bySlip > floor ? bySlip : floor;
|
|
4885
5151
|
}
|
|
4886
5152
|
|
|
4887
|
-
// src/constants.ts
|
|
4888
|
-
var API_BASE = "https://blazephoenix.xyz";
|
|
4889
|
-
var DEPLOYMENTS_URL = `${API_BASE}/api/deployments`;
|
|
4890
|
-
var SUPPORTED_CHAIN_IDS = [8453, 1, 10, 42161, 4663];
|
|
4891
|
-
var CHAINS = {
|
|
4892
|
-
1: {
|
|
4893
|
-
chainId: 1,
|
|
4894
|
-
name: "Ethereum",
|
|
4895
|
-
key: "ETHEREUM",
|
|
4896
|
-
explorer: "https://etherscan.io",
|
|
4897
|
-
weth: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
|
|
4898
|
-
usdc: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
4899
|
-
blockTime: 12
|
|
4900
|
-
},
|
|
4901
|
-
8453: {
|
|
4902
|
-
chainId: 8453,
|
|
4903
|
-
name: "Base",
|
|
4904
|
-
key: "BASE",
|
|
4905
|
-
explorer: "https://basescan.org",
|
|
4906
|
-
weth: "0x4200000000000000000000000000000000000006",
|
|
4907
|
-
usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
4908
|
-
bzpx: "0x23113e72165a034265Ab8Bf2277CCB7a85Cb7483",
|
|
4909
|
-
staking: "0x3f60C7aa0c36a78D200405feBE143d2Cf3fA0c77",
|
|
4910
|
-
blockTime: 2
|
|
4911
|
-
},
|
|
4912
|
-
10: {
|
|
4913
|
-
chainId: 10,
|
|
4914
|
-
name: "Optimism",
|
|
4915
|
-
key: "OPTIMISM",
|
|
4916
|
-
explorer: "https://optimistic.etherscan.io",
|
|
4917
|
-
weth: "0x4200000000000000000000000000000000000006",
|
|
4918
|
-
usdc: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
4919
|
-
blockTime: 2
|
|
4920
|
-
},
|
|
4921
|
-
42161: {
|
|
4922
|
-
chainId: 42161,
|
|
4923
|
-
name: "Arbitrum",
|
|
4924
|
-
key: "ARBITRUM",
|
|
4925
|
-
explorer: "https://arbiscan.io",
|
|
4926
|
-
weth: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1",
|
|
4927
|
-
usdc: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
4928
|
-
blockTime: 0.25
|
|
4929
|
-
},
|
|
4930
|
-
// Robinhood Chain — the dollar asset is USDG, not USDC; the `usdc` field
|
|
4931
|
-
// carries the chain's canonical dollar token. Explorer is Blockscout.
|
|
4932
|
-
4663: {
|
|
4933
|
-
chainId: 4663,
|
|
4934
|
-
name: "Robinhood",
|
|
4935
|
-
key: "ROBINHOOD",
|
|
4936
|
-
explorer: "https://robinhoodchain.blockscout.com",
|
|
4937
|
-
weth: "0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73",
|
|
4938
|
-
usdc: "0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168",
|
|
4939
|
-
blockTime: 2
|
|
4940
|
-
}
|
|
4941
|
-
};
|
|
4942
|
-
var ALIASES = {
|
|
4943
|
-
"1": 1,
|
|
4944
|
-
eth: 1,
|
|
4945
|
-
ethereum: 1,
|
|
4946
|
-
mainnet: 1,
|
|
4947
|
-
"8453": 8453,
|
|
4948
|
-
base: 8453,
|
|
4949
|
-
"10": 10,
|
|
4950
|
-
op: 10,
|
|
4951
|
-
optimism: 10,
|
|
4952
|
-
"42161": 42161,
|
|
4953
|
-
arb: 42161,
|
|
4954
|
-
arbitrum: 42161,
|
|
4955
|
-
"arbitrum-one": 42161,
|
|
4956
|
-
"4663": 4663,
|
|
4957
|
-
rh: 4663,
|
|
4958
|
-
robinhood: 4663,
|
|
4959
|
-
"robinhood-chain": 4663
|
|
4960
|
-
};
|
|
4961
|
-
function resolveChain(chain) {
|
|
4962
|
-
const id = ALIASES[String(chain).trim().toLowerCase()];
|
|
4963
|
-
if (!id) {
|
|
4964
|
-
throw new Error(
|
|
4965
|
-
`Unsupported chain "${chain}" \u2014 use 8453/base, 1/eth, 10/optimism, 42161/arbitrum or 4663/robinhood`
|
|
4966
|
-
);
|
|
4967
|
-
}
|
|
4968
|
-
return id;
|
|
4969
|
-
}
|
|
4970
|
-
function tryResolveChain(chain) {
|
|
4971
|
-
return ALIASES[String(chain).trim().toLowerCase()];
|
|
4972
|
-
}
|
|
4973
|
-
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
4974
|
-
var NATIVE_TOKEN = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
|
|
4975
|
-
var PERMIT2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
|
4976
|
-
var FEE_BPS = 28;
|
|
4977
|
-
var SWAP_TOPIC0 = "0xd6d34547c69c5ee3d2667625c188acf1006abb93e0ee7cf03925c67cf7760413";
|
|
4978
|
-
var SURPLUS_TOPIC0 = "0x1a3afef0f067eb51a6bfec6ab3625a4408dd8c3571b72836483e68feb70d1026";
|
|
4979
|
-
var EXECUTION_PROOF_TOPIC0 = "0xb5a62bcb8753e37706da20862f2a32a16831ef0144567eb15b1da49547e9f84c";
|
|
4980
|
-
var FEE_TOPIC0 = "0x18fa25e22e0f1f39ef2130081363ba86a1259e598e669558683dabfc15bfe731";
|
|
4981
|
-
|
|
4982
5153
|
// src/deployments.ts
|
|
4983
5154
|
var Z = ZERO_ADDRESS;
|
|
4984
5155
|
var pending = () => ({ core: Z, hub: Z, solver: Z, router: Z, quoter: Z });
|
|
@@ -5185,6 +5356,7 @@ var Registry = class {
|
|
|
5185
5356
|
fetchedAt = 0;
|
|
5186
5357
|
lastError;
|
|
5187
5358
|
mode;
|
|
5359
|
+
remoteVersions;
|
|
5188
5360
|
url;
|
|
5189
5361
|
ttlMs;
|
|
5190
5362
|
fetchFn;
|
|
@@ -5193,6 +5365,7 @@ var Registry = class {
|
|
|
5193
5365
|
overrides = /* @__PURE__ */ new Map();
|
|
5194
5366
|
constructor(opts = {}, overrides) {
|
|
5195
5367
|
this.mode = opts.mode ?? "auto";
|
|
5368
|
+
this.remoteVersions = opts.remoteVersions ?? false;
|
|
5196
5369
|
this.url = opts.url ?? DEPLOYMENTS_URL;
|
|
5197
5370
|
this.ttlMs = opts.ttlMs ?? 10 * 6e4;
|
|
5198
5371
|
this.fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
@@ -5324,7 +5497,7 @@ var Registry = class {
|
|
|
5324
5497
|
if (!isValidSelector(selector)) throw new BlazeError("bad_request", `bad version selector "${selector}"`);
|
|
5325
5498
|
const ov = this.overrides.get(chainId);
|
|
5326
5499
|
const sel = ov?.version && selector === "latest" ? ov.version : selector;
|
|
5327
|
-
const candidates = this.doc.versions.filter((v) => matchesSelector(v.version, sel) && isDeployed(v.chains[chainId])).sort((a, b) => compareVersions(b.version, a.version));
|
|
5500
|
+
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));
|
|
5328
5501
|
const pick = candidates[0];
|
|
5329
5502
|
if (ov && (ov.router || ov.quoter)) {
|
|
5330
5503
|
const base = pick ? { ...pick.chains[chainId] } : {
|
|
@@ -5344,9 +5517,10 @@ var Registry = class {
|
|
|
5344
5517
|
}
|
|
5345
5518
|
if (!pick) {
|
|
5346
5519
|
const deployed = this.versionsOn(chainId);
|
|
5520
|
+
const remoteOnly = this.remoteVersions ? [] : deployed.filter((v) => matchesSelector(v, sel) && !this.isPinned(v, chainId));
|
|
5347
5521
|
throw new BlazeError(
|
|
5348
5522
|
"not_deployed",
|
|
5349
|
-
`no BlazePhoenix deployment matching version "${selector}" on chain ${chainId}` + (deployed.length ? ` (deployed: ${deployed.join(", ")})` : ""),
|
|
5523
|
+
`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` : ""),
|
|
5350
5524
|
{ details: { chainId, selector, deployed } }
|
|
5351
5525
|
);
|
|
5352
5526
|
}
|
|
@@ -5355,6 +5529,11 @@ var Registry = class {
|
|
|
5355
5529
|
const source = src("router") === "remote" || src("quoter") === "remote" ? "remote" : "embedded";
|
|
5356
5530
|
return { chainId, version: pick.version, status: pick.status, contracts: { ...set }, source };
|
|
5357
5531
|
}
|
|
5532
|
+
/** Router and Quoter both pinned in this package (not filled by a fetched document). */
|
|
5533
|
+
isPinned(version, chainId) {
|
|
5534
|
+
const src = (k) => this.fieldSource[`${version}|${chainId}|${k}`];
|
|
5535
|
+
return src("router") === "embedded" && src("quoter") === "embedded";
|
|
5536
|
+
}
|
|
5358
5537
|
latestKnownVersion(selector) {
|
|
5359
5538
|
const all = this.doc.versions.map((v) => v.version).sort((a, b) => compareVersions(b, a));
|
|
5360
5539
|
const m = selector ? all.find((v) => matchesSelector(v, selector)) : void 0;
|
|
@@ -5471,162 +5650,6 @@ function fromBaseUnits(v, decimals, maxDp) {
|
|
|
5471
5650
|
frac = frac.replace(/0+$/, "");
|
|
5472
5651
|
return `${neg ? "-" : ""}${whole}${frac ? `.${frac}` : ""}`;
|
|
5473
5652
|
}
|
|
5474
|
-
var isTransport = (v) => typeof v === "function";
|
|
5475
|
-
var isEip1193 = (v) => !!v && typeof v === "object" && typeof v.request === "function";
|
|
5476
|
-
var isSource = (v) => typeof v === "string" || Array.isArray(v) || isTransport(v) || isEip1193(v);
|
|
5477
|
-
function isAllowedRpcUrl(url) {
|
|
5478
|
-
let u;
|
|
5479
|
-
try {
|
|
5480
|
-
u = new URL(url.trim());
|
|
5481
|
-
} catch {
|
|
5482
|
-
return false;
|
|
5483
|
-
}
|
|
5484
|
-
if (u.protocol === "https:" || u.protocol === "wss:") return true;
|
|
5485
|
-
if (u.protocol === "http:" || u.protocol === "ws:") {
|
|
5486
|
-
return u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "[::1]";
|
|
5487
|
-
}
|
|
5488
|
-
return false;
|
|
5489
|
-
}
|
|
5490
|
-
function urlTransport(url, o) {
|
|
5491
|
-
if (!isAllowedRpcUrl(url)) {
|
|
5492
|
-
throw new BlazeError("bad_request", `refusing RPC URL "${redact(url)}": use https:// or wss:// (http/ws only for localhost)`);
|
|
5493
|
-
}
|
|
5494
|
-
const u = url.trim();
|
|
5495
|
-
return u.startsWith("ws") ? viem.webSocket(u, { timeout: o.timeoutMs, retryCount: o.retries }) : viem.http(u, { timeout: o.timeoutMs, retryCount: o.retries, batch: o.batch });
|
|
5496
|
-
}
|
|
5497
|
-
function redact(url) {
|
|
5498
|
-
try {
|
|
5499
|
-
const u = new URL(url);
|
|
5500
|
-
const path = u.pathname.length > 1 ? "/\u2026" : "";
|
|
5501
|
-
return `${u.protocol}//${u.host}${path}`;
|
|
5502
|
-
} catch {
|
|
5503
|
-
return "<invalid url>";
|
|
5504
|
-
}
|
|
5505
|
-
}
|
|
5506
|
-
function transportFrom(src, opts = {}) {
|
|
5507
|
-
const o = {
|
|
5508
|
-
timeoutMs: opts.timeoutMs ?? 1e4,
|
|
5509
|
-
retries: opts.retries ?? 2,
|
|
5510
|
-
batch: opts.batch ?? false
|
|
5511
|
-
};
|
|
5512
|
-
if (isTransport(src)) return src;
|
|
5513
|
-
if (isEip1193(src)) return viem.custom(src, { retryCount: o.retries });
|
|
5514
|
-
if (typeof src === "string") return urlTransport(src, o);
|
|
5515
|
-
if (Array.isArray(src)) {
|
|
5516
|
-
const list = src.map((u) => u.trim()).filter(Boolean);
|
|
5517
|
-
if (list.length === 0) throw new BlazeError("rpc_required", "empty RPC list");
|
|
5518
|
-
if (list.length === 1) return urlTransport(list[0], o);
|
|
5519
|
-
return viem.fallback(list.map((u) => urlTransport(u, { ...o, retries: 0 })), { retryCount: o.retries });
|
|
5520
|
-
}
|
|
5521
|
-
throw new BlazeError("bad_request", "unrecognised rpc value");
|
|
5522
|
-
}
|
|
5523
|
-
var RPC_HELP = "BlazePhoenix SDK 1.x runs 100% on YOUR RPC \u2014 it ships no endpoints and never reads through our servers. Pass `rpc` (a URL, a list of URLs, a wallet provider or a viem transport), e.g. new BlazePhoenix({ rpc: { base: process.env.BASE_RPC_URL } }). Any provider's free tier is enough.";
|
|
5524
|
-
var RpcRouter = class {
|
|
5525
|
-
perChain = /* @__PURE__ */ new Map();
|
|
5526
|
-
single;
|
|
5527
|
-
opts;
|
|
5528
|
-
constructor(config, opts = {}) {
|
|
5529
|
-
this.opts = opts;
|
|
5530
|
-
if (config === void 0 || config === null || config === "") return;
|
|
5531
|
-
if (isSource(config)) {
|
|
5532
|
-
this.single = { source: config };
|
|
5533
|
-
return;
|
|
5534
|
-
}
|
|
5535
|
-
if (typeof config !== "object") throw new BlazeError("bad_request", "rpc must be a URL, a list, a provider, a transport or a per-chain map");
|
|
5536
|
-
for (const [k, v] of Object.entries(config)) {
|
|
5537
|
-
if (v === void 0 || v === null || v === "") continue;
|
|
5538
|
-
const id = tryResolveChain(k);
|
|
5539
|
-
if (!id) throw new BlazeError("bad_request", `rpc map: unknown chain "${k}"`);
|
|
5540
|
-
if (!isSource(v)) throw new BlazeError("bad_request", `rpc map: bad value for chain "${k}"`);
|
|
5541
|
-
this.perChain.set(id, { source: v, chainId: id });
|
|
5542
|
-
}
|
|
5543
|
-
}
|
|
5544
|
-
/** Chains with an explicitly configured RPC (a single source reports none). */
|
|
5545
|
-
configuredChains() {
|
|
5546
|
-
return SUPPORTED_CHAIN_IDS.filter((id) => this.perChain.has(id));
|
|
5547
|
-
}
|
|
5548
|
-
hasAny() {
|
|
5549
|
-
return !!this.single || this.perChain.size > 0;
|
|
5550
|
-
}
|
|
5551
|
-
/** The chain to use when a request names none: the only configured one. */
|
|
5552
|
-
async defaultChain() {
|
|
5553
|
-
if (this.perChain.size === 1) return [...this.perChain.keys()][0];
|
|
5554
|
-
if (this.single && this.perChain.size === 0) {
|
|
5555
|
-
const id = await this.verify(this.single);
|
|
5556
|
-
const c = tryResolveChain(id);
|
|
5557
|
-
if (!c) throw new BlazeError("rpc_chain_mismatch", `your RPC serves chain ${id}, which BlazePhoenix is not deployed on`);
|
|
5558
|
-
return c;
|
|
5559
|
-
}
|
|
5560
|
-
throw new BlazeError("bad_request", "this request needs `chain` (several RPCs are configured)");
|
|
5561
|
-
}
|
|
5562
|
-
slotFor(chainId) {
|
|
5563
|
-
const s = this.perChain.get(chainId) ?? this.single;
|
|
5564
|
-
if (!s) {
|
|
5565
|
-
throw new BlazeError(
|
|
5566
|
-
"rpc_required",
|
|
5567
|
-
`no RPC configured for ${CHAINS[chainId].name} (${chainId}). ${RPC_HELP}`,
|
|
5568
|
-
{ details: { chainId } }
|
|
5569
|
-
);
|
|
5570
|
-
}
|
|
5571
|
-
return s;
|
|
5572
|
-
}
|
|
5573
|
-
clientOf(s) {
|
|
5574
|
-
if (!s.client) {
|
|
5575
|
-
s.client = viem.createPublicClient({ transport: transportFrom(s.source, this.opts) });
|
|
5576
|
-
}
|
|
5577
|
-
return s.client;
|
|
5578
|
-
}
|
|
5579
|
-
verify(s) {
|
|
5580
|
-
if (!s.verified) {
|
|
5581
|
-
const c = this.clientOf(s);
|
|
5582
|
-
s.verified = c.getChainId().catch((e) => {
|
|
5583
|
-
s.verified = void 0;
|
|
5584
|
-
throw new BlazeError("rpc_error", `your RPC did not answer eth_chainId: ${e?.message ?? e}`, { cause: e });
|
|
5585
|
-
});
|
|
5586
|
-
}
|
|
5587
|
-
return s.verified;
|
|
5588
|
-
}
|
|
5589
|
-
/** A PublicClient for `chain`, over YOUR node, after checking it really is that chain. */
|
|
5590
|
-
async client(chain) {
|
|
5591
|
-
const chainId = resolveChain(chain);
|
|
5592
|
-
const s = this.slotFor(chainId);
|
|
5593
|
-
const actual = await this.verify(s);
|
|
5594
|
-
if (actual !== chainId) {
|
|
5595
|
-
throw new BlazeError(
|
|
5596
|
-
"rpc_chain_mismatch",
|
|
5597
|
-
`your RPC serves chain ${actual}, not ${CHAINS[chainId].name} (${chainId}) \u2014 configure one per chain: rpc: { base: "\u2026", eth: "\u2026" }`,
|
|
5598
|
-
{ details: { expected: chainId, actual } }
|
|
5599
|
-
);
|
|
5600
|
-
}
|
|
5601
|
-
return this.clientOf(s);
|
|
5602
|
-
}
|
|
5603
|
-
};
|
|
5604
|
-
function rpcFromEnv(env = globalEnv()) {
|
|
5605
|
-
const split = (v) => {
|
|
5606
|
-
const list = v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
5607
|
-
return list.length === 1 ? list[0] : list;
|
|
5608
|
-
};
|
|
5609
|
-
const map = {};
|
|
5610
|
-
for (const id of SUPPORTED_CHAIN_IDS) {
|
|
5611
|
-
const v = env[`BLAZEPHOENIX_RPC_${CHAINS[id].key}`] ?? env[`BLAZEPHOENIX_RPC_${id}`];
|
|
5612
|
-
if (v && v.trim()) map[String(id)] = split(v);
|
|
5613
|
-
}
|
|
5614
|
-
const single = env.BLAZEPHOENIX_RPC_URL;
|
|
5615
|
-
if (Object.keys(map).length > 0) {
|
|
5616
|
-
if (single && single.trim()) {
|
|
5617
|
-
throw new BlazeError(
|
|
5618
|
-
"bad_request",
|
|
5619
|
-
"set either BLAZEPHOENIX_RPC_URL (one node) or the per-chain BLAZEPHOENIX_RPC_<CHAIN> variables, not both"
|
|
5620
|
-
);
|
|
5621
|
-
}
|
|
5622
|
-
return map;
|
|
5623
|
-
}
|
|
5624
|
-
return single && single.trim() ? split(single) : void 0;
|
|
5625
|
-
}
|
|
5626
|
-
function globalEnv() {
|
|
5627
|
-
const p = globalThis.process;
|
|
5628
|
-
return p?.env ?? {};
|
|
5629
|
-
}
|
|
5630
5653
|
|
|
5631
5654
|
// src/client.ts
|
|
5632
5655
|
var HEX_ADDR2 = /^0x[0-9a-fA-F]{40}$/;
|
|
@@ -5702,6 +5725,8 @@ var BlazePhoenix = class {
|
|
|
5702
5725
|
checks.push({ name: `code:${k}`, ok: !!code && code !== "0x", detail: code && code !== "0x" ? `${(code.length - 2) / 2} bytes` : "no code at address" });
|
|
5703
5726
|
});
|
|
5704
5727
|
if (featuresOf(dep.version).introspection) {
|
|
5728
|
+
if (isZero(c.hub)) checks.push({ name: "hub", ok: false, detail: "zero address" });
|
|
5729
|
+
if (isZero(c.solver)) checks.push({ name: "solver", ok: false, detail: "zero address" });
|
|
5705
5730
|
const eq = (a, b) => a.toLowerCase() === b.toLowerCase();
|
|
5706
5731
|
const read = async (fn) => {
|
|
5707
5732
|
try {
|
|
@@ -5756,16 +5781,19 @@ var BlazePhoenix = class {
|
|
|
5756
5781
|
return { address: c.weth, native: true };
|
|
5757
5782
|
case "WETH":
|
|
5758
5783
|
return { address: c.weth, native: false };
|
|
5784
|
+
// `usdc` holds each chain's one dollar asset: USDG on Robinhood Chain, USDC
|
|
5785
|
+
// elsewhere. The other symbol names a different token there, so it is refused.
|
|
5759
5786
|
case "USDC":
|
|
5760
5787
|
case "USDG":
|
|
5761
|
-
return { address: c.usdc, native: false };
|
|
5788
|
+
if (s.toUpperCase() === (chainId === 4663 ? "USDG" : "USDC")) return { address: c.usdc, native: false };
|
|
5789
|
+
break;
|
|
5762
5790
|
case "BZPX":
|
|
5763
5791
|
if (c.bzpx) return { address: c.bzpx, native: false };
|
|
5764
5792
|
break;
|
|
5765
5793
|
}
|
|
5766
5794
|
throw new BlazeError(
|
|
5767
5795
|
"bad_request",
|
|
5768
|
-
`${side}: "${ref}" is not a 0x address or a built-in symbol (ETH, WETH, USDC, BZPX) on ${c.name} \u2014 pass the token address`
|
|
5796
|
+
`${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`
|
|
5769
5797
|
);
|
|
5770
5798
|
}
|
|
5771
5799
|
async decimalsOf(client, chainId, t) {
|
|
@@ -5912,7 +5940,7 @@ var BlazePhoenix = class {
|
|
|
5912
5940
|
async quoteBatch(reqs) {
|
|
5913
5941
|
const fail = (e) => ({
|
|
5914
5942
|
ok: false,
|
|
5915
|
-
error: { code: e instanceof BlazeError ? e.code : "error", message: e?.message ?? String(e) }
|
|
5943
|
+
error: { code: e instanceof BlazeError ? e.code : "error", message: scrubUrls(e?.message ?? String(e)) }
|
|
5916
5944
|
});
|
|
5917
5945
|
const out = new Array(reqs.length);
|
|
5918
5946
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -6155,6 +6183,12 @@ var BlazePhoenix = class {
|
|
|
6155
6183
|
if (r.status !== "success") throw new BlazeError("reverted", `approval transaction reverted (${approvalHash})`);
|
|
6156
6184
|
}
|
|
6157
6185
|
plan = { ...plan, approval: { ...plan.approval, current: plan.approval.amount } };
|
|
6186
|
+
if (Date.now() - plan.quote.quotedAt > maxAge) {
|
|
6187
|
+
if (!req) {
|
|
6188
|
+
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`);
|
|
6189
|
+
}
|
|
6190
|
+
plan = await this.buildSwap({ ...req, from: req.from ?? account.address });
|
|
6191
|
+
}
|
|
6158
6192
|
}
|
|
6159
6193
|
if (opts.simulate ?? true) {
|
|
6160
6194
|
const sim = await this.simulate(plan, account.address);
|
|
@@ -6203,11 +6237,16 @@ var BlazePhoenix = class {
|
|
|
6203
6237
|
const staking = CHAINS[chainId].staking;
|
|
6204
6238
|
if (!staking) throw new BlazeError("not_deployed", `no BlazePhoenix staking engine on ${CHAINS[chainId].name}`);
|
|
6205
6239
|
const client = await this.rpc.client(chainId);
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
client.
|
|
6209
|
-
|
|
6210
|
-
|
|
6240
|
+
let blockNumber, r, isSolvent;
|
|
6241
|
+
try {
|
|
6242
|
+
blockNumber = await client.getBlockNumber();
|
|
6243
|
+
[r, isSolvent] = await Promise.all([
|
|
6244
|
+
client.readContract({ address: staking, abi: STAKING_SOLVENCY_ABI, functionName: "solvency", blockNumber }),
|
|
6245
|
+
client.readContract({ address: staking, abi: STAKING_SOLVENCY_ABI, functionName: "isSolvent", blockNumber })
|
|
6246
|
+
]);
|
|
6247
|
+
} catch (e) {
|
|
6248
|
+
throw new BlazeError("rpc_error", `solvency read failed on your RPC: ${scrubUrls(String(e?.message ?? e))}`, { cause: e });
|
|
6249
|
+
}
|
|
6211
6250
|
const s = r;
|
|
6212
6251
|
return {
|
|
6213
6252
|
chainId,
|