@qntx/wallet-engine 0.6.0 → 0.7.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/{contract-D5pguqiZ.mjs → contract-CEsgLme3.mjs} +27 -34
- package/dist/contract-CEsgLme3.mjs.map +1 -0
- package/dist/gateway-contract.d.mts +1 -13
- package/dist/gateway-contract.mjs +1 -1
- package/dist/idb.d.mts +0 -4
- package/dist/idb.mjs +3 -6
- package/dist/idb.mjs.map +1 -1
- package/dist/index.d.mts +310 -163
- package/dist/index.mjs +1469 -641
- package/dist/index.mjs.map +1 -1
- package/dist/{registry-9oeWs_yZ.d.mts → registry-LcFSPrE1.d.mts} +1 -3
- package/package.json +8 -8
- package/dist/contract-D5pguqiZ.mjs.map +0 -1
|
@@ -1,22 +1,5 @@
|
|
|
1
1
|
import * as z from "zod/mini";
|
|
2
|
-
|
|
3
|
-
const INSTALL_ID_HEADER = "x-install-id";
|
|
4
|
-
const PROVIDERS_HEADER = "x-gateway-providers";
|
|
5
|
-
/** 128 random bits as lowercase hex — generated once per install, unrelated to wallet secrets. */
|
|
6
|
-
const installId = z.string().check(z.regex(/^[0-9a-f]{32}$/));
|
|
7
|
-
/** CAIP-2 chain id: `namespace:reference`. */
|
|
8
|
-
const caip2 = z.string().check(z.regex(/^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/));
|
|
9
|
-
const GATEWAY_ERRORS = [
|
|
10
|
-
"invalid-request",
|
|
11
|
-
"unknown-network",
|
|
12
|
-
"not-found",
|
|
13
|
-
"rate-limited",
|
|
14
|
-
"unavailable",
|
|
15
|
-
"internal"
|
|
16
|
-
];
|
|
17
|
-
const gatewayError = z.object({ error: z.enum(GATEWAY_ERRORS) });
|
|
18
|
-
//#endregion
|
|
19
|
-
//#region src/gateway/contract/endpoint.ts
|
|
2
|
+
// src/gateway/contract/endpoint.ts
|
|
20
3
|
/** One gateway route: its verb, versioned path template, and request/response schemas. */
|
|
21
4
|
function endpoint(method, path, request, response) {
|
|
22
5
|
return {
|
|
@@ -35,13 +18,7 @@ function parseWith(schema, value) {
|
|
|
35
18
|
function accepts(schema, value) {
|
|
36
19
|
return z.safeParse(schema, value).success;
|
|
37
20
|
}
|
|
38
|
-
|
|
39
|
-
//#region src/gateway/contract/health.ts
|
|
40
|
-
const healthRequest = z.undefined();
|
|
41
|
-
const healthResponse = z.object({ status: z.literal("ok") });
|
|
42
|
-
const health = endpoint("GET", "/v1/health", healthRequest, healthResponse);
|
|
43
|
-
//#endregion
|
|
44
|
-
//#region src/gateway/contract/rpc.ts
|
|
21
|
+
// src/gateway/contract/rpc.ts
|
|
45
22
|
/** One JSON-RPC call plus nothing else: batches may carry at most this many entries. */
|
|
46
23
|
const RPC_BATCH_MAX = 20;
|
|
47
24
|
/** Read-only EVM methods plus `eth_sendRawTransaction`. Anything not listed is rejected. */
|
|
@@ -135,8 +112,27 @@ const rpcResponse = z.union([jsonRpcResponse, z.array(jsonRpcResponse)]);
|
|
|
135
112
|
const rpc = endpoint("POST", "/v1/rpc/:caip2", rpcRequest, rpcResponse);
|
|
136
113
|
/** The JSON-RPC URL path for one network. */
|
|
137
114
|
const rpcPath = (caip2) => rpc.path.replace(":caip2", caip2);
|
|
138
|
-
|
|
139
|
-
|
|
115
|
+
// src/gateway/contract/primitives.ts
|
|
116
|
+
const INSTALL_ID_HEADER = "x-install-id";
|
|
117
|
+
const PROVIDERS_HEADER = "x-gateway-providers";
|
|
118
|
+
/** 128 random bits as lowercase hex — generated once per install, unrelated to wallet secrets. */
|
|
119
|
+
const installId = z.string().check(z.regex(/^[0-9a-f]{32}$/));
|
|
120
|
+
/** CAIP-2 chain id: `namespace:reference`. */
|
|
121
|
+
const caip2 = z.string().check(z.regex(/^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/));
|
|
122
|
+
const GATEWAY_ERRORS = [
|
|
123
|
+
"invalid-request",
|
|
124
|
+
"unknown-network",
|
|
125
|
+
"not-found",
|
|
126
|
+
"rate-limited",
|
|
127
|
+
"unavailable",
|
|
128
|
+
"internal"
|
|
129
|
+
];
|
|
130
|
+
const gatewayError = z.object({ error: z.enum(GATEWAY_ERRORS) });
|
|
131
|
+
// src/gateway/contract/health.ts
|
|
132
|
+
const healthRequest = z.undefined();
|
|
133
|
+
const healthResponse = z.object({ status: z.literal("ok") });
|
|
134
|
+
const health = endpoint("GET", "/v1/health", healthRequest, healthResponse);
|
|
135
|
+
// src/gateway/contract/esplora.ts
|
|
140
136
|
const HEX64 = "[0-9a-f]{64}";
|
|
141
137
|
/** The Esplora surface the engine uses; every other path is rejected. */
|
|
142
138
|
const ESPLORA_ROUTES = [
|
|
@@ -175,8 +171,7 @@ const esploraGet = endpoint("GET", "/v1/esplora/:caip2/*", z.undefined(), z.unkn
|
|
|
175
171
|
/** The Esplora base path for one network; allow-listed paths are appended to it. */
|
|
176
172
|
const esploraBasePath = (caip2) => esploraGet.path.replace(":caip2", caip2).replace(/\/\*$/, "");
|
|
177
173
|
const esploraPost = endpoint("POST", "/v1/esplora/:caip2/tx", z.string(), z.string());
|
|
178
|
-
|
|
179
|
-
//#region src/networks/registry.ts
|
|
174
|
+
// src/networks/registry.ts
|
|
180
175
|
const entry = (chainId, name, symbol, testnet, _source) => ({
|
|
181
176
|
id: `eip155:${chainId}`,
|
|
182
177
|
chainId,
|
|
@@ -271,14 +266,12 @@ const SOLANA_NETWORKS = [{
|
|
|
271
266
|
family: "solana",
|
|
272
267
|
genesisHash: "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG"
|
|
273
268
|
}];
|
|
274
|
-
|
|
275
|
-
//#region src/gateway/contract/served.ts
|
|
269
|
+
// src/gateway/contract/served.ts
|
|
276
270
|
const servedNetworksRequest = z.undefined();
|
|
277
271
|
/** The networks the gateway actually serves, in its configured order. */
|
|
278
272
|
const servedNetworksResponse = z.object({ networks: z.array(caip2) });
|
|
279
273
|
const servedNetworks = endpoint("GET", "/v1/networks", servedNetworksRequest, servedNetworksResponse);
|
|
280
274
|
const networksPath = servedNetworks.path;
|
|
281
|
-
|
|
282
|
-
export { parseWith as A, rpcResponse as C, healthResponse as D, healthRequest as E, gatewayError as F, installId as I, INSTALL_ID_HEADER as M, PROVIDERS_HEADER as N, accepts as O, caip2 as P, rpcRequest as S, health as T, evmRpcRequest as _, BITCOIN_NETWORKS as a, rpc as b, networkById as c, esploraGet as d, esploraPathAllowed as f, SOLANA_RPC_METHODS as g, RPC_BATCH_MAX as h, servedNetworksResponse as i, GATEWAY_ERRORS as j, endpoint as k, ESPLORA_ROUTES as l, EVM_RPC_METHODS as m, servedNetworks as n, NETWORKS as o, esploraPost as p, servedNetworksRequest as r, SOLANA_NETWORKS as s, networksPath as t, esploraBasePath as u, jsonRpcId as v, solanaRpcRequest as w, rpcPath as x, jsonRpcResponse as y };
|
|
275
|
+
export { rpcPath as A, EVM_RPC_METHODS as C, jsonRpcId as D, evmRpcRequest as E, endpoint as F, parseWith as I, rpcResponse as M, solanaRpcRequest as N, jsonRpcResponse as O, accepts as P, installId as S, SOLANA_RPC_METHODS as T, GATEWAY_ERRORS as _, BITCOIN_NETWORKS as a, caip2 as b, networkById as c, esploraGet as d, esploraPathAllowed as f, healthResponse as g, healthRequest as h, servedNetworksResponse as i, rpcRequest as j, rpc as k, ESPLORA_ROUTES as l, health as m, servedNetworks as n, NETWORKS as o, esploraPost as p, servedNetworksRequest as r, SOLANA_NETWORKS as s, networksPath as t, esploraBasePath as u, INSTALL_ID_HEADER as v, RPC_BATCH_MAX as w, gatewayError as x, PROVIDERS_HEADER as y };
|
|
283
276
|
|
|
284
|
-
//# sourceMappingURL=contract-
|
|
277
|
+
//# sourceMappingURL=contract-CEsgLme3.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-CEsgLme3.mjs","names":[],"sources":["../src/gateway/contract/endpoint.ts","../src/gateway/contract/rpc.ts","../src/gateway/contract/primitives.ts","../src/gateway/contract/health.ts","../src/gateway/contract/esplora.ts","../src/networks/registry.ts","../src/gateway/contract/served.ts"],"sourcesContent":["import * as z from \"zod/mini\";\n\nexport type HttpMethod = \"GET\" | \"POST\" | \"DELETE\";\n\nexport type Endpoint<Req extends z.ZodMiniType, Res extends z.ZodMiniType> = {\n readonly method: HttpMethod;\n readonly path: string;\n readonly request: Req;\n readonly response: Res;\n};\n\n/** One gateway route: its verb, versioned path template, and request/response schemas. */\nexport function endpoint<Req extends z.ZodMiniType, Res extends z.ZodMiniType>(\n method: HttpMethod,\n path: `/v1/${string}`,\n request: Req,\n response: Res,\n): Endpoint<Req, Res> {\n return { method, path, request, response };\n}\n\n/** `value` typed by `schema`, or `undefined` when it does not satisfy it. */\nexport function parseWith<S extends z.ZodMiniType>(\n schema: S,\n value: unknown,\n): z.output<S> | undefined {\n const result = z.safeParse(schema, value);\n return result.success ? result.data : undefined;\n}\n\n/** Whether `value` satisfies `schema` — lets consumers validate without importing zod. */\nexport function accepts(schema: z.ZodMiniType, value: unknown): boolean {\n return z.safeParse(schema, value).success;\n}\n","import * as z from \"zod/mini\";\n\nimport { endpoint } from \"./endpoint.ts\";\n\n/** One JSON-RPC call plus nothing else: batches may carry at most this many entries. */\nexport const RPC_BATCH_MAX = 20;\n\n/** Read-only EVM methods plus `eth_sendRawTransaction`. Anything not listed is rejected. */\nexport const EVM_RPC_METHODS = [\n \"eth_blobBaseFee\",\n \"eth_blockNumber\",\n \"eth_call\",\n \"eth_chainId\",\n \"eth_createAccessList\",\n \"eth_estimateGas\",\n \"eth_feeHistory\",\n \"eth_gasPrice\",\n \"eth_getBalance\",\n \"eth_getBlockByHash\",\n \"eth_getBlockByNumber\",\n \"eth_getBlockReceipts\",\n \"eth_getBlockTransactionCountByHash\",\n \"eth_getBlockTransactionCountByNumber\",\n \"eth_getCode\",\n \"eth_getProof\",\n \"eth_getStorageAt\",\n \"eth_getTransactionByBlockHashAndIndex\",\n \"eth_getTransactionByBlockNumberAndIndex\",\n \"eth_getTransactionByHash\",\n \"eth_getTransactionCount\",\n \"eth_getTransactionReceipt\",\n \"eth_maxPriorityFeePerGas\",\n \"eth_sendRawTransaction\",\n \"net_version\",\n \"web3_clientVersion\",\n] as const;\n\n/** Read-only Solana methods plus `sendTransaction`. Anything not listed is rejected. */\nexport const SOLANA_RPC_METHODS = [\n \"getAccountInfo\",\n \"getBalance\",\n \"getBlock\",\n \"getBlockHeight\",\n \"getBlockTime\",\n \"getEpochInfo\",\n \"getFeeForMessage\",\n \"getGenesisHash\",\n \"getLatestBlockhash\",\n \"getMinimumBalanceForRentExemption\",\n \"getMultipleAccounts\",\n \"getRecentPrioritizationFees\",\n \"getSignatureStatuses\",\n \"getSignaturesForAddress\",\n \"getSlot\",\n \"getTokenAccountBalance\",\n \"getTokenAccountsByOwner\",\n \"getTokenSupply\",\n \"getTransaction\",\n \"getVersion\",\n \"isBlockhashValid\",\n \"sendTransaction\",\n \"simulateTransaction\",\n] as const;\n\nexport type EvmRpcMethod = (typeof EVM_RPC_METHODS)[number];\nexport type SolanaRpcMethod = (typeof SOLANA_RPC_METHODS)[number];\n\nexport const jsonRpcId = z.union([z.string(), z.number(), z.null()]);\n\nconst jsonRpcParams = z.optional(\n z.union([z.array(z.unknown()), z.record(z.string(), z.unknown())]),\n);\n\nconst callSchema = <M extends z.ZodMiniType<string>>(method: M) =>\n z.object({ jsonrpc: z.literal(\"2.0\"), id: jsonRpcId, method, params: jsonRpcParams });\n\nconst bodySchema = <C extends z.ZodMiniType>(call: C) =>\n z.union([call, z.array(call).check(z.minLength(1), z.maxLength(RPC_BATCH_MAX))]);\n\n/** Method-agnostic wire shape of a call or batch (the gateway narrows by family). */\nexport const rpcRequest = bodySchema(callSchema(z.string()));\n\nexport const evmRpcRequest = bodySchema(callSchema(z.enum(EVM_RPC_METHODS)));\nexport const solanaRpcRequest = bodySchema(callSchema(z.enum(SOLANA_RPC_METHODS)));\n\nconst successResponse = z.object({\n jsonrpc: z.literal(\"2.0\"),\n id: jsonRpcId,\n result: z.unknown(),\n});\nconst errorResponse = z.object({\n jsonrpc: z.literal(\"2.0\"),\n id: jsonRpcId,\n error: z.object({ code: z.int(), message: z.string(), data: z.optional(z.unknown()) }),\n});\n\nexport const jsonRpcResponse = z.union([successResponse, errorResponse]);\nexport const rpcResponse = z.union([jsonRpcResponse, z.array(jsonRpcResponse)]);\n\nexport const rpc = endpoint(\"POST\", \"/v1/rpc/:caip2\", rpcRequest, rpcResponse);\n\n/** The JSON-RPC URL path for one network. */\nexport const rpcPath = (caip2: string): string => rpc.path.replace(\":caip2\", caip2);\n","import * as z from \"zod/mini\";\n\nexport const INSTALL_ID_HEADER = \"x-install-id\";\nexport const PROVIDERS_HEADER = \"x-gateway-providers\";\n\n/** 128 random bits as lowercase hex — generated once per install, unrelated to wallet secrets. */\nexport const installId = z.string().check(z.regex(/^[0-9a-f]{32}$/));\n\n/** CAIP-2 chain id: `namespace:reference`. */\nexport const caip2 = z.string().check(z.regex(/^[-a-z0-9]{3,8}:[-_a-zA-Z0-9]{1,32}$/));\n\nexport type Caip2 = z.infer<typeof caip2>;\nexport type InstallId = z.infer<typeof installId>;\n\nexport const GATEWAY_ERRORS = [\n \"invalid-request\",\n \"unknown-network\",\n \"not-found\",\n \"rate-limited\",\n \"unavailable\",\n \"internal\",\n] as const;\n\nexport const gatewayError = z.object({ error: z.enum(GATEWAY_ERRORS) });\n\nexport type GatewayErrorCode = (typeof GATEWAY_ERRORS)[number];\n","import * as z from \"zod/mini\";\n\nimport { endpoint } from \"./endpoint.ts\";\n\nexport const healthRequest = z.undefined();\nexport const healthResponse = z.object({ status: z.literal(\"ok\") });\n\nexport const health = endpoint(\"GET\", \"/v1/health\", healthRequest, healthResponse);\n","import * as z from \"zod/mini\";\n\nimport { endpoint } from \"./endpoint.ts\";\n\nconst HEX64 = \"[0-9a-f]{64}\";\n\ntype EsploraRoute = { readonly method: \"GET\" | \"POST\"; readonly pattern: RegExp };\n\n/** The Esplora surface the engine uses; every other path is rejected. */\nexport const ESPLORA_ROUTES: ReadonlyArray<EsploraRoute> = [\n { method: \"GET\", pattern: /^\\/block-height\\/\\d{1,10}$/ },\n { method: \"GET\", pattern: /^\\/fee-estimates$/ },\n { method: \"GET\", pattern: new RegExp(`^/scripthash/${HEX64}/txs$`) },\n { method: \"GET\", pattern: new RegExp(`^/scripthash/${HEX64}/utxo$`) },\n { method: \"GET\", pattern: new RegExp(`^/tx/${HEX64}/hex$`) },\n { method: \"GET\", pattern: new RegExp(`^/tx/${HEX64}/status$`) },\n { method: \"POST\", pattern: /^\\/tx$/ },\n];\n\n/** `path` is the Esplora path after `/v1/esplora/:caip2`, with a leading slash and no query. */\nexport const esploraPathAllowed = (method: string, path: string): boolean =>\n ESPLORA_ROUTES.some((route) => route.method === method && route.pattern.test(path));\n\nexport const esploraGet = endpoint(\"GET\", \"/v1/esplora/:caip2/*\", z.undefined(), z.unknown());\n\n/** The Esplora base path for one network; allow-listed paths are appended to it. */\nexport const esploraBasePath = (caip2: string): string =>\n esploraGet.path.replace(\":caip2\", caip2).replace(/\\/\\*$/, \"\");\nexport const esploraPost = endpoint(\"POST\", \"/v1/esplora/:caip2/tx\", z.string(), z.string());\n","/**\n * Built-in EVM network registry: the 8 mainnets from chains.mdx plus their main testnets. Each\n * chain id was checked against the chain's official documentation (source URL per entry).\n * Signing-time chainId always comes from this table, never from an RPC endpoint.\n */\n\nexport type NetworkInfo = {\n readonly id: string; // CAIP-2 eip155:<chainId>\n readonly chainId: number;\n readonly name: string;\n readonly symbol: string;\n readonly multicall3?: string;\n /** CAIP-2 family — `solana:<ref>` networks are validated against genesis-hash, not chainId. */\n readonly family: \"evm\" | \"solana\" | \"bitcoin\";\n readonly genesisHash?: string;\n readonly decimals: number;\n readonly testnet: boolean;\n};\n\nconst entry = (\n chainId: number,\n name: string,\n symbol: string,\n testnet: boolean,\n _source: string,\n): NetworkInfo => ({\n id: `eip155:${chainId}`,\n chainId,\n name,\n symbol,\n decimals: 18,\n testnet,\n family: \"evm\",\n // Canonical Multicall3 — https://github.com/mds1/multicall3 deployments (all entries below).\n multicall3: \"0xcA11bde05977b3631167028862bE2a173976CA11\",\n});\n\nexport const NETWORKS: ReadonlyArray<NetworkInfo> = [\n // https://chainlist.org + https://ethereum.org (Ethereum mainnet chainId 1)\n entry(1, \"Ethereum\", \"ETH\", false, \"https://ethereum.org/en/developers/docs/networks/\"),\n // https://docs.base.org — Base Sepolia 84532\n entry(8453, \"Base\", \"ETH\", false, \"https://docs.base.org/docs/network-information\"),\n // https://docs.arbitrum.io — Arbitrum Sepolia 421614\n entry(\n 42161,\n \"Arbitrum One\",\n \"ETH\",\n false,\n \"https://docs.arbitrum.io/for-devs/dev-tools-and-resources/chain-info\",\n ),\n // https://docs.optimism.io — OP Sepolia 11155420\n entry(10, \"OP Mainnet\", \"ETH\", false, \"https://docs.optimism.io/chain/networks\"),\n // https://docs.polygon.technology — Amoy 80002\n entry(\n 137,\n \"Polygon\",\n \"POL\",\n false,\n \"https://docs.polygon.technology/pos/reference/rpc-endpoints/\",\n ),\n // https://docs.bnbchain.org — BSC testnet 97\n entry(\n 56,\n \"BNB Smart Chain\",\n \"BNB\",\n false,\n \"https://docs.bnbchain.org/bnb-smart-chain/developers/json_rpc/\",\n ),\n // https://hyperliquid.gitbook.io — HyperEVM testnet 998\n entry(\n 999,\n \"HyperEVM\",\n \"HYPE\",\n false,\n \"https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm\",\n ),\n // https://build.avax.network — Fuji 43113\n entry(\n 43114,\n \"Avalanche C-Chain\",\n \"AVAX\",\n false,\n \"https://build.avax.network/docs/tooling/rpc-providers\",\n ),\n entry(11155111, \"Sepolia\", \"ETH\", true, \"https://ethereum.org/en/developers/docs/networks/\"),\n entry(84532, \"Base Sepolia\", \"ETH\", true, \"https://docs.base.org/docs/network-information\"),\n entry(\n 421614,\n \"Arbitrum Sepolia\",\n \"ETH\",\n true,\n \"https://docs.arbitrum.io/for-devs/dev-tools-and-resources/chain-info\",\n ),\n entry(11155420, \"OP Sepolia\", \"ETH\", true, \"https://docs.optimism.io/chain/networks\"),\n entry(\n 80002,\n \"Polygon Amoy\",\n \"POL\",\n true,\n \"https://docs.polygon.technology/pos/reference/rpc-endpoints/\",\n ),\n entry(\n 97,\n \"BNB Smart Chain Testnet\",\n \"tBNB\",\n true,\n \"https://docs.bnbchain.org/bnb-smart-chain/developers/json_rpc/\",\n ),\n entry(\n 998,\n \"HyperEVM Testnet\",\n \"HYPE\",\n true,\n \"https://hyperliquid.gitbook.io/hyperliquid-docs/for-developers/hyperevm\",\n ),\n entry(\n 43113,\n \"Avalanche Fuji\",\n \"AVAX\",\n true,\n \"https://build.avax.network/docs/tooling/rpc-providers\",\n ),\n];\n\nexport const networkById = (id: string): NetworkInfo | undefined =>\n [...NETWORKS, ...SOLANA_NETWORKS, ...BITCOIN_NETWORKS].find((n) => n.id === id);\nexport const BITCOIN_NETWORKS: ReadonlyArray<NetworkInfo> = [\n {\n id: \"bip122:000000000019d6689c085ae165831e93\",\n chainId: 0,\n name: \"Bitcoin\",\n symbol: \"BTC\",\n decimals: 8,\n testnet: false,\n family: \"bitcoin\",\n genesisHash: \"000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f\",\n },\n {\n id: \"bip122:00000000da84f2bafbbc53dee25a72ae\",\n chainId: 0,\n name: \"Bitcoin Testnet4\",\n symbol: \"tBTC\",\n decimals: 8,\n testnet: true,\n family: \"bitcoin\",\n genesisHash: \"00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043\",\n },\n {\n id: \"bip122:00000008819873e925422c1ff0f99f7c\",\n chainId: 0,\n name: \"Bitcoin Signet\",\n symbol: \"sBTC\",\n decimals: 8,\n testnet: true,\n family: \"bitcoin\",\n genesisHash: \"00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6\",\n },\n {\n id: \"bip122:0f9188f13cb7b2c71f2a335e3a4fc328\",\n chainId: 0,\n name: \"Bitcoin Regtest\",\n symbol: \"rBTC\",\n decimals: 8,\n testnet: true,\n family: \"bitcoin\",\n genesisHash: \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n },\n];\n\nexport const SOLANA_NETWORKS: ReadonlyArray<NetworkInfo> = [\n {\n id: \"solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp\",\n chainId: 0,\n name: \"Solana\",\n symbol: \"SOL\",\n decimals: 9,\n testnet: false,\n family: \"solana\",\n // https://solana.com/docs/references/clusters (mainnet genesis hash)\n genesisHash: \"5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d\",\n },\n {\n id: \"solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1\",\n chainId: 0,\n name: \"Solana Devnet\",\n symbol: \"SOL\",\n decimals: 9,\n testnet: true,\n family: \"solana\",\n genesisHash: \"EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG\",\n },\n];\n","import * as z from \"zod/mini\";\n\nimport { endpoint } from \"./endpoint.ts\";\nimport { caip2 } from \"./primitives.ts\";\n\nexport const servedNetworksRequest = z.undefined();\n/** The networks the gateway actually serves, in its configured order. */\nexport const servedNetworksResponse = z.object({ networks: z.array(caip2) });\n\nexport const servedNetworks = endpoint(\n \"GET\",\n \"/v1/networks\",\n servedNetworksRequest,\n servedNetworksResponse,\n);\n\nexport const networksPath: string = servedNetworks.path;\n"],"mappings":"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"}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { a as networkById, i as SOLANA_NETWORKS, n as NETWORKS, r as NetworkInfo, t as BITCOIN_NETWORKS } from "./registry-
|
|
1
|
+
import { a as networkById, i as SOLANA_NETWORKS, n as NETWORKS, r as NetworkInfo, t as BITCOIN_NETWORKS } from "./registry-LcFSPrE1.mjs";
|
|
2
2
|
import * as z from "zod/mini";
|
|
3
|
-
//#region src/gateway/contract/primitives.d.ts
|
|
4
3
|
export declare const INSTALL_ID_HEADER = "x-install-id";
|
|
5
4
|
export declare const PROVIDERS_HEADER = "x-gateway-providers";
|
|
6
5
|
/** 128 random bits as lowercase hex — generated once per install, unrelated to wallet secrets. */
|
|
@@ -21,8 +20,6 @@ export declare const gatewayError: z.ZodMiniObject<{
|
|
|
21
20
|
}>;
|
|
22
21
|
}, z.core.$strip>;
|
|
23
22
|
export type GatewayErrorCode = (typeof GATEWAY_ERRORS)[number];
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/gateway/contract/endpoint.d.ts
|
|
26
23
|
export type HttpMethod = "GET" | "POST" | "DELETE";
|
|
27
24
|
export type Endpoint<Req extends z.ZodMiniType, Res extends z.ZodMiniType> = {
|
|
28
25
|
readonly method: HttpMethod;
|
|
@@ -36,8 +33,6 @@ export declare function endpoint<Req extends z.ZodMiniType, Res extends z.ZodMin
|
|
|
36
33
|
export declare function parseWith<S extends z.ZodMiniType>(schema: S, value: unknown): z.output<S> | undefined;
|
|
37
34
|
/** Whether `value` satisfies `schema` — lets consumers validate without importing zod. */
|
|
38
35
|
export declare function accepts(schema: z.ZodMiniType, value: unknown): boolean;
|
|
39
|
-
//#endregion
|
|
40
|
-
//#region src/gateway/contract/health.d.ts
|
|
41
36
|
export declare const healthRequest: z.ZodMiniUndefined;
|
|
42
37
|
export declare const healthResponse: z.ZodMiniObject<{
|
|
43
38
|
status: z.ZodMiniLiteral<"ok">;
|
|
@@ -45,8 +40,6 @@ export declare const healthResponse: z.ZodMiniObject<{
|
|
|
45
40
|
export declare const health: Endpoint<z.ZodMiniUndefined, z.ZodMiniObject<{
|
|
46
41
|
status: z.ZodMiniLiteral<"ok">;
|
|
47
42
|
}, z.core.$strip>>;
|
|
48
|
-
//#endregion
|
|
49
|
-
//#region src/gateway/contract/rpc.d.ts
|
|
50
43
|
/** One JSON-RPC call plus nothing else: batches may carry at most this many entries. */
|
|
51
44
|
export declare const RPC_BATCH_MAX = 20;
|
|
52
45
|
/** Read-only EVM methods plus `eth_sendRawTransaction`. Anything not listed is rejected. */
|
|
@@ -267,8 +260,6 @@ export declare const rpc: Endpoint<z.ZodMiniUnion<readonly [z.ZodMiniObject<{
|
|
|
267
260
|
}, z.core.$strip>]>>]>>;
|
|
268
261
|
/** The JSON-RPC URL path for one network. */
|
|
269
262
|
export declare const rpcPath: (caip2: string) => string;
|
|
270
|
-
//#endregion
|
|
271
|
-
//#region src/gateway/contract/esplora.d.ts
|
|
272
263
|
type EsploraRoute = {
|
|
273
264
|
readonly method: "GET" | "POST";
|
|
274
265
|
readonly pattern: RegExp;
|
|
@@ -281,8 +272,6 @@ export declare const esploraGet: Endpoint<z.ZodMiniUndefined, z.ZodMiniUnknown>;
|
|
|
281
272
|
/** The Esplora base path for one network; allow-listed paths are appended to it. */
|
|
282
273
|
export declare const esploraBasePath: (caip2: string) => string;
|
|
283
274
|
export declare const esploraPost: Endpoint<z.ZodMiniString<string>, z.ZodMiniString<string>>;
|
|
284
|
-
//#endregion
|
|
285
|
-
//#region src/gateway/contract/served.d.ts
|
|
286
275
|
export declare const servedNetworksRequest: z.ZodMiniUndefined;
|
|
287
276
|
/** The networks the gateway actually serves, in its configured order. */
|
|
288
277
|
export declare const servedNetworksResponse: z.ZodMiniObject<{
|
|
@@ -292,6 +281,5 @@ export declare const servedNetworks: Endpoint<z.ZodMiniUndefined, z.ZodMiniObjec
|
|
|
292
281
|
networks: z.ZodMiniArray<z.ZodMiniString<string>>;
|
|
293
282
|
}, z.core.$strip>>;
|
|
294
283
|
export declare const networksPath: string;
|
|
295
|
-
//#endregion
|
|
296
284
|
export { BITCOIN_NETWORKS, NETWORKS, type NetworkInfo, SOLANA_NETWORKS, networkById };
|
|
297
285
|
//# sourceMappingURL=gateway-contract.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { a as BITCOIN_NETWORKS, l as ESPLORA_ROUTES,
|
|
1
|
+
export { a as BITCOIN_NETWORKS, l as ESPLORA_ROUTES, C as EVM_RPC_METHODS, _ as GATEWAY_ERRORS, v as INSTALL_ID_HEADER, o as NETWORKS, y as PROVIDERS_HEADER, w as RPC_BATCH_MAX, s as SOLANA_NETWORKS, T as SOLANA_RPC_METHODS, P as accepts, b as caip2, F as endpoint, u as esploraBasePath, d as esploraGet, f as esploraPathAllowed, p as esploraPost, E as evmRpcRequest, x as gatewayError, m as health, h as healthRequest, g as healthResponse, S as installId, D as jsonRpcId, O as jsonRpcResponse, c as networkById, t as networksPath, I as parseWith, k as rpc, A as rpcPath, j as rpcRequest, M as rpcResponse, n as servedNetworks, r as servedNetworksRequest, i as servedNetworksResponse, N as solanaRpcRequest } from "./contract-CEsgLme3.mjs";
|
package/dist/idb.d.mts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
//#region src/store/port.d.ts
|
|
2
1
|
/**
|
|
3
2
|
* Engine storage port: named collections of byte values. `collection` is synchronous and cheap —
|
|
4
3
|
* the same collection object backs every call, so guards (closed) live on the store.
|
|
@@ -14,8 +13,6 @@ type EngineStore = {
|
|
|
14
13
|
readonly collection: (name: string) => EngineCollection;
|
|
15
14
|
readonly close: () => Promise<void>;
|
|
16
15
|
};
|
|
17
|
-
//#endregion
|
|
18
|
-
//#region src/idb/indexeddb.d.ts
|
|
19
16
|
export type Random = (length: number) => Uint8Array;
|
|
20
17
|
export type IndexedDbEngineStoreOptions = {
|
|
21
18
|
readonly name: string;
|
|
@@ -23,6 +20,5 @@ export type IndexedDbEngineStoreOptions = {
|
|
|
23
20
|
readonly random: Random;
|
|
24
21
|
};
|
|
25
22
|
export declare function openIndexedDbEngineStore(options: IndexedDbEngineStoreOptions): Promise<EngineStore>;
|
|
26
|
-
//#endregion
|
|
27
23
|
export {};
|
|
28
24
|
//# sourceMappingURL=idb.d.mts.map
|
package/dist/idb.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { xchacha20poly1305 } from "@noble/ciphers/chacha.js";
|
|
2
|
-
|
|
2
|
+
// src/errors.ts
|
|
3
3
|
/** Typed engine errors: store codes are `closed` and `integrity` (decrypt or bind failure). */
|
|
4
4
|
var EngineStoreError = class extends Error {
|
|
5
5
|
code;
|
|
@@ -9,14 +9,12 @@ var EngineStoreError = class extends Error {
|
|
|
9
9
|
this.code = code;
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
|
-
|
|
13
|
-
//#region src/store/guard.ts
|
|
12
|
+
// src/store/guard.ts
|
|
14
13
|
/** NUL is the composite-key/AAD separator — a name or key containing it collides. */
|
|
15
14
|
function checkName(kind, value) {
|
|
16
15
|
if (value.includes("\0")) throw new EngineStoreError("invalid-input", `${kind} must not contain NUL`);
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
//#region src/idb/indexeddb.ts
|
|
17
|
+
// src/idb/indexeddb.ts
|
|
20
18
|
/**
|
|
21
19
|
* The Web EngineStore: one IndexedDB database, one `records` object store, values encrypted per
|
|
22
20
|
* record with XChaCha20-Poly1305. Key = the injected 32-byte data key; nonce = 24 random bytes
|
|
@@ -128,7 +126,6 @@ function makeStore(db, key, random) {
|
|
|
128
126
|
}
|
|
129
127
|
};
|
|
130
128
|
}
|
|
131
|
-
//#endregion
|
|
132
129
|
export { openIndexedDbEngineStore };
|
|
133
130
|
|
|
134
131
|
//# sourceMappingURL=idb.mjs.map
|
package/dist/idb.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idb.mjs","names":[],"sources":["../src/errors.ts","../src/store/guard.ts","../src/idb/indexeddb.ts"],"sourcesContent":["export type EngineErrorCode = \"closed\" | \"integrity\" | \"invalid-input\";\n\n/** Typed engine errors: store codes are `closed` and `integrity` (decrypt or bind failure). */\nexport class EngineStoreError extends Error {\n readonly code: EngineErrorCode;\n\n constructor(code: EngineErrorCode, message: string) {\n super(message);\n this.name = \"EngineStoreError\";\n this.code = code;\n }\n}\n","import { EngineStoreError } from \"../errors.ts\";\n\n/** NUL is the composite-key/AAD separator — a name or key containing it collides. */\nexport function checkName(kind: \"collection\" | \"key\", value: string): void {\n if (value.includes(\"\\u0000\")) {\n throw new EngineStoreError(\"invalid-input\", `${kind} must not contain NUL`);\n }\n}\n","/**\n * The Web EngineStore: one IndexedDB database, one `records` object store, values encrypted per\n * record with XChaCha20-Poly1305. Key = the injected 32-byte data key; nonce = 24 random bytes\n * stored as nonce‖ciphertext; AAD = UTF-8 `<collection>\\0<key>` — moving a record to another key or\n * collection fails the MAC, same as a tampered payload. `close()` drops the connection, wipes the\n * module-held key copy and latches every call to `closed`.\n */\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\n\nimport { EngineStoreError } from \"../errors.ts\";\nimport { checkName } from \"../store/guard.ts\";\nimport type { EngineCollection, EngineStore } from \"../store/port.ts\";\n\nexport type Random = (length: number) => Uint8Array;\n\nexport type IndexedDbEngineStoreOptions = {\n readonly name: string;\n readonly dataKey: Uint8Array;\n readonly random: Random;\n};\n\nconst STORE = \"records\";\n\nconst id = (collection: string, recordKey: string): string => `${collection}\\u0000${recordKey}`;\nconst aad = (collection: string, recordKey: string): Uint8Array =>\n new TextEncoder().encode(`${collection}\\u0000${recordKey}`);\n\nasync function request<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.addEventListener(\"success\", () => {\n resolve(req.result);\n });\n req.addEventListener(\"error\", () => {\n reject(new Error(\"indexeddb request failed\", { cause: req.error }));\n });\n });\n}\n\nasync function done(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.addEventListener(\"complete\", () => {\n resolve();\n });\n tx.addEventListener(\"error\", () => {\n reject(new Error(\"indexeddb transaction failed\", { cause: tx.error }));\n });\n tx.addEventListener(\"abort\", () => {\n reject(new Error(\"indexeddb transaction aborted\", { cause: tx.error }));\n });\n });\n}\n\nexport async function openIndexedDbEngineStore(\n options: IndexedDbEngineStoreOptions,\n): Promise<EngineStore> {\n const key = new Uint8Array(options.dataKey);\n return new Promise<EngineStore>((resolve, reject) => {\n const open = indexedDB.open(options.name, 1);\n open.addEventListener(\"upgradeneeded\", () => {\n open.result.createObjectStore(STORE);\n });\n open.addEventListener(\"success\", () => {\n resolve(makeStore(open.result, key, options.random));\n });\n open.addEventListener(\"error\", () => {\n key.fill(0);\n reject(new Error(\"indexeddb open failed\", { cause: open.error }));\n });\n });\n}\n\nfunction makeStore(db: IDBDatabase, key: Uint8Array, random: Random): EngineStore {\n let closed = false;\n\n const guard = (): IDBDatabase => {\n if (closed) {\n throw new EngineStoreError(\"closed\", \"engine store is closed\");\n }\n return db;\n };\n\n const seal = (collection: string, recordKey: string, bytes: Uint8Array): Uint8Array => {\n const nonce = random(24);\n const ciphertext = xchacha20poly1305(key, nonce, aad(collection, recordKey)).encrypt(bytes);\n const record = new Uint8Array(24 + ciphertext.length);\n record.set(nonce, 0);\n record.set(ciphertext, 24);\n return record;\n };\n\n const open = (collection: string, recordKey: string, record: Uint8Array): Uint8Array => {\n if (record.length < 24) {\n throw new EngineStoreError(\"integrity\", \"engine record too short\");\n }\n try {\n return xchacha20poly1305(key, record.slice(0, 24), aad(collection, recordKey)).decrypt(\n record.slice(24),\n );\n } catch {\n throw new EngineStoreError(\"integrity\", \"engine record failed integrity\");\n }\n };\n\n const make = (name: string): EngineCollection => ({\n get: async (recordKey) => {\n const database = guard();\n checkName(\"key\", recordKey);\n const tx = database.transaction(STORE, \"readonly\");\n const raw = tx.objectStore(STORE).get(id(name, recordKey));\n // oxlint-disable-next-line typescript/no-unsafe-argument -- IDB get() is any-typed by spec\n const record = await request<Uint8Array | undefined>(raw);\n return record === undefined ? undefined : open(name, recordKey, record);\n },\n put: async (recordKey, bytes) => {\n const database = guard();\n checkName(\"key\", recordKey);\n const tx = database.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).put(seal(name, recordKey, bytes), id(name, recordKey));\n await done(tx);\n },\n delete: async (recordKey) => {\n const database = guard();\n checkName(\"key\", recordKey);\n const tx = database.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).delete(id(name, recordKey));\n await done(tx);\n },\n list: async () => {\n const database = guard();\n const tx = database.transaction(STORE, \"readonly\");\n const all = tx.objectStore(STORE).getAllKeys();\n const keys = await request<IDBValidKey[]>(all);\n const prefix = `${name}\\u0000`;\n return keys\n .filter((k): k is string => typeof k === \"string\" && k.startsWith(prefix))\n .map((k) => k.slice(prefix.length));\n },\n });\n\n return {\n collection: (name) => {\n checkName(\"collection\", name);\n return make(name);\n },\n close: async () => {\n closed = true;\n db.close();\n key.fill(0);\n await Promise.resolve();\n },\n };\n}\n"],"mappings":";;;AAGA,IAAa,mBAAb,cAAsC,MAAM;CAC1C;CAEA,YAAY,MAAuB,SAAiB;EAClD,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF
|
|
1
|
+
{"version":3,"file":"idb.mjs","names":[],"sources":["../src/errors.ts","../src/store/guard.ts","../src/idb/indexeddb.ts"],"sourcesContent":["export type EngineErrorCode = \"closed\" | \"integrity\" | \"invalid-input\";\n\n/** Typed engine errors: store codes are `closed` and `integrity` (decrypt or bind failure). */\nexport class EngineStoreError extends Error {\n readonly code: EngineErrorCode;\n\n constructor(code: EngineErrorCode, message: string) {\n super(message);\n this.name = \"EngineStoreError\";\n this.code = code;\n }\n}\n","import { EngineStoreError } from \"../errors.ts\";\n\n/** NUL is the composite-key/AAD separator — a name or key containing it collides. */\nexport function checkName(kind: \"collection\" | \"key\", value: string): void {\n if (value.includes(\"\\u0000\")) {\n throw new EngineStoreError(\"invalid-input\", `${kind} must not contain NUL`);\n }\n}\n","/**\n * The Web EngineStore: one IndexedDB database, one `records` object store, values encrypted per\n * record with XChaCha20-Poly1305. Key = the injected 32-byte data key; nonce = 24 random bytes\n * stored as nonce‖ciphertext; AAD = UTF-8 `<collection>\\0<key>` — moving a record to another key or\n * collection fails the MAC, same as a tampered payload. `close()` drops the connection, wipes the\n * module-held key copy and latches every call to `closed`.\n */\nimport { xchacha20poly1305 } from \"@noble/ciphers/chacha.js\";\n\nimport { EngineStoreError } from \"../errors.ts\";\nimport { checkName } from \"../store/guard.ts\";\nimport type { EngineCollection, EngineStore } from \"../store/port.ts\";\n\nexport type Random = (length: number) => Uint8Array;\n\nexport type IndexedDbEngineStoreOptions = {\n readonly name: string;\n readonly dataKey: Uint8Array;\n readonly random: Random;\n};\n\nconst STORE = \"records\";\n\nconst id = (collection: string, recordKey: string): string => `${collection}\\u0000${recordKey}`;\nconst aad = (collection: string, recordKey: string): Uint8Array =>\n new TextEncoder().encode(`${collection}\\u0000${recordKey}`);\n\nasync function request<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.addEventListener(\"success\", () => {\n resolve(req.result);\n });\n req.addEventListener(\"error\", () => {\n reject(new Error(\"indexeddb request failed\", { cause: req.error }));\n });\n });\n}\n\nasync function done(tx: IDBTransaction): Promise<void> {\n return new Promise((resolve, reject) => {\n tx.addEventListener(\"complete\", () => {\n resolve();\n });\n tx.addEventListener(\"error\", () => {\n reject(new Error(\"indexeddb transaction failed\", { cause: tx.error }));\n });\n tx.addEventListener(\"abort\", () => {\n reject(new Error(\"indexeddb transaction aborted\", { cause: tx.error }));\n });\n });\n}\n\nexport async function openIndexedDbEngineStore(\n options: IndexedDbEngineStoreOptions,\n): Promise<EngineStore> {\n const key = new Uint8Array(options.dataKey);\n return new Promise<EngineStore>((resolve, reject) => {\n const open = indexedDB.open(options.name, 1);\n open.addEventListener(\"upgradeneeded\", () => {\n open.result.createObjectStore(STORE);\n });\n open.addEventListener(\"success\", () => {\n resolve(makeStore(open.result, key, options.random));\n });\n open.addEventListener(\"error\", () => {\n key.fill(0);\n reject(new Error(\"indexeddb open failed\", { cause: open.error }));\n });\n });\n}\n\nfunction makeStore(db: IDBDatabase, key: Uint8Array, random: Random): EngineStore {\n let closed = false;\n\n const guard = (): IDBDatabase => {\n if (closed) {\n throw new EngineStoreError(\"closed\", \"engine store is closed\");\n }\n return db;\n };\n\n const seal = (collection: string, recordKey: string, bytes: Uint8Array): Uint8Array => {\n const nonce = random(24);\n const ciphertext = xchacha20poly1305(key, nonce, aad(collection, recordKey)).encrypt(bytes);\n const record = new Uint8Array(24 + ciphertext.length);\n record.set(nonce, 0);\n record.set(ciphertext, 24);\n return record;\n };\n\n const open = (collection: string, recordKey: string, record: Uint8Array): Uint8Array => {\n if (record.length < 24) {\n throw new EngineStoreError(\"integrity\", \"engine record too short\");\n }\n try {\n return xchacha20poly1305(key, record.slice(0, 24), aad(collection, recordKey)).decrypt(\n record.slice(24),\n );\n } catch {\n throw new EngineStoreError(\"integrity\", \"engine record failed integrity\");\n }\n };\n\n const make = (name: string): EngineCollection => ({\n get: async (recordKey) => {\n const database = guard();\n checkName(\"key\", recordKey);\n const tx = database.transaction(STORE, \"readonly\");\n const raw = tx.objectStore(STORE).get(id(name, recordKey));\n // oxlint-disable-next-line typescript/no-unsafe-argument -- IDB get() is any-typed by spec\n const record = await request<Uint8Array | undefined>(raw);\n return record === undefined ? undefined : open(name, recordKey, record);\n },\n put: async (recordKey, bytes) => {\n const database = guard();\n checkName(\"key\", recordKey);\n const tx = database.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).put(seal(name, recordKey, bytes), id(name, recordKey));\n await done(tx);\n },\n delete: async (recordKey) => {\n const database = guard();\n checkName(\"key\", recordKey);\n const tx = database.transaction(STORE, \"readwrite\");\n tx.objectStore(STORE).delete(id(name, recordKey));\n await done(tx);\n },\n list: async () => {\n const database = guard();\n const tx = database.transaction(STORE, \"readonly\");\n const all = tx.objectStore(STORE).getAllKeys();\n const keys = await request<IDBValidKey[]>(all);\n const prefix = `${name}\\u0000`;\n return keys\n .filter((k): k is string => typeof k === \"string\" && k.startsWith(prefix))\n .map((k) => k.slice(prefix.length));\n },\n });\n\n return {\n collection: (name) => {\n checkName(\"collection\", name);\n return make(name);\n },\n close: async () => {\n closed = true;\n db.close();\n key.fill(0);\n await Promise.resolve();\n },\n };\n}\n"],"mappings":"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"}
|