@panoptic-eng/sdk 1.0.68 → 1.0.70
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/cow/index.js +47 -2
- package/dist/cow/index.js.map +1 -1
- package/dist/{cow-CGgPXhY6.js → cow-sGuN0eas.js} +1 -1
- package/dist/{greeks-CGtgLUyh.js → greeks-DNPP24KL.js} +115 -9
- package/dist/index.d.ts +67 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +161 -62
- package/dist/index.js.map +1 -1
- package/dist/{irm-BDxwCFDm.js → irm-6RzXXa1_.js} +927 -3
- package/dist/panoptic/v2/greeks/index.d.ts +7 -4
- package/dist/panoptic/v2/greeks/index.d.ts.map +1 -1
- package/dist/panoptic/v2/greeks/index.js +74 -7
- package/dist/panoptic/v2/greeks/index.js.map +1 -1
- package/dist/panoptic/v2/index.d.ts +244 -6
- package/dist/panoptic/v2/index.d.ts.map +1 -1
- package/dist/panoptic/v2/index.js +2554 -464
- package/dist/panoptic/v2/index.js.map +1 -1
- package/dist/panoptic/v2/react-public.d.ts +258 -9
- package/dist/panoptic/v2/react-public.d.ts.map +1 -1
- package/dist/panoptic/v2/react-public.js +4655 -2909
- package/dist/panoptic/v2/react-public.js.map +1 -1
- package/dist/{providers-DubVMHWU.js → providers-CbcNt27n.js} +2 -1
- package/dist/{rates-BHS-Qiu7.js → rates-DDK4ZimJ.js} +40 -4
- package/dist/{router-BW6TqwOO.js → router-njWsLvZc.js} +969 -39
- package/dist/rpc/index.d.ts +12 -1
- package/dist/rpc/index.d.ts.map +1 -1
- package/dist/rpc/index.js +65 -5
- package/dist/rpc/index.js.map +1 -1
- package/dist/uniswap/index.js +53 -13
- package/dist/uniswap/index.js.map +1 -1
- package/dist/{v2-Dgh6JxG_.js → v2-BrG4T_Gh.js} +681 -32
- package/dist/{writes-CG612Vuk.js → writes-CI6Haer-.js} +34 -3
- package/package.json +1 -1
- package/dist/router-IDtCENST.js +0 -942
package/dist/rpc/index.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ type RpcProviderPreset = {
|
|
|
21
21
|
url: string;
|
|
22
22
|
headers?: Record<string, string>;
|
|
23
23
|
} | undefined;
|
|
24
|
+
/** Websocket endpoint for `chainId`, or undefined when the vendor does not serve one. */
|
|
25
|
+
wsEndpoint?: (chainId: number, apiKey: string) => string | undefined;
|
|
24
26
|
};
|
|
25
27
|
declare const alchemyChains: {
|
|
26
28
|
readonly 1: "eth-mainnet";
|
|
@@ -74,6 +76,12 @@ declare const DEFAULT_RPC_PROVIDERS: readonly RpcProviderName[];
|
|
|
74
76
|
* Blank values count as unset. Throws when nothing resolves.
|
|
75
77
|
*/
|
|
76
78
|
declare function resolveRpcEndpoints(chainId: number, env: RpcEnv, options?: ResolveRpcEndpointsOptions): RpcEndpoint[];
|
|
79
|
+
/**
|
|
80
|
+
* Websocket RPC URL for `chainId`: `RPC_WS_URL_<chainId>`, else the first preset
|
|
81
|
+
* in `RPC_PROVIDERS` (or the defaults) that has an API key and serves websockets.
|
|
82
|
+
* Returns undefined when nothing is configured; throws on a non-ws(s) URL.
|
|
83
|
+
*/
|
|
84
|
+
declare function resolveRpcWsUrl(chainId: number, env: RpcEnv): string | undefined;
|
|
77
85
|
|
|
78
86
|
//#endregion
|
|
79
87
|
//#region src/rpc/transport.d.ts
|
|
@@ -87,6 +95,8 @@ type RpcTransportOptions = RpcHttpConfig & {
|
|
|
87
95
|
* another surfaces as "already known" / "nonce too low" although it was sent.
|
|
88
96
|
*/
|
|
89
97
|
failover?: boolean;
|
|
98
|
+
/** Shared HTTP attempt limit, including retries and failover. Disables HTTP batching. */
|
|
99
|
+
requestsPerSecond?: number;
|
|
90
100
|
};
|
|
91
101
|
/**
|
|
92
102
|
* Transport over resolved endpoints: plain HTTP for one endpoint, otherwise an
|
|
@@ -94,6 +104,7 @@ type RpcTransportOptions = RpcHttpConfig & {
|
|
|
94
104
|
*/
|
|
95
105
|
declare function rpcTransport(endpoints: readonly RpcEndpoint[], {
|
|
96
106
|
failover,
|
|
107
|
+
requestsPerSecond,
|
|
97
108
|
...config
|
|
98
109
|
}?: RpcTransportOptions): Transport;
|
|
99
110
|
/** anvil fork arguments for the primary endpoint (`--fork-header` only when it needs one). */
|
|
@@ -108,5 +119,5 @@ declare function rpcForkArgs(endpoints: readonly RpcEndpoint[]): {
|
|
|
108
119
|
declare function ponderRpc(endpoints: readonly RpcEndpoint[]): string | string[] | Transport;
|
|
109
120
|
|
|
110
121
|
//#endregion
|
|
111
|
-
export { AlchemyChainId, DEFAULT_RPC_PROVIDERS, RPC_PROVIDER_PRESETS, ResolveRpcEndpointsOptions, RpcEndpoint, RpcEnv, RpcHttpConfig, RpcProviderName, RpcProviderPreset, RpcTransportOptions, getAlchemyRpcUrl, getAlchemyWsRpcUrl, ponderRpc, redactRpcSecrets, resolveRpcEndpoints, rpcForkArgs, rpcHttp, rpcTransport };
|
|
122
|
+
export { AlchemyChainId, DEFAULT_RPC_PROVIDERS, RPC_PROVIDER_PRESETS, ResolveRpcEndpointsOptions, RpcEndpoint, RpcEnv, RpcHttpConfig, RpcProviderName, RpcProviderPreset, RpcTransportOptions, getAlchemyRpcUrl, getAlchemyWsRpcUrl, ponderRpc, redactRpcSecrets, resolveRpcEndpoints, resolveRpcWsUrl, rpcForkArgs, rpcHttp, rpcTransport };
|
|
112
123
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/rpc/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":["RpcProviderPreset","Record","alchemyChains","AlchemyChainId","RpcProviderName","RPC_PROVIDER_PRESETS","Readonly","isRpcProviderName","getAlchemyRpcUrl","getAlchemyWsRpcUrl","redactRpcSecrets","RpcProviderName","RpcEnv","Record","Readonly","RpcEndpoint","ResolveRpcEndpointsOptions","DEFAULT_RPC_PROVIDERS","resolveRpcEndpoints","viem1","HttpTransportConfig","Transport","RpcEndpoint","RpcHttpConfig","Omit","rpcHttp","viem0","RpcSchema","HttpTransport","RpcTransportOptions","rpcTransport","failover","rpcForkArgs","ponderRpc"],"sources":["../../src/rpc/providers.d.ts","../../src/rpc/redact.d.ts","../../src/rpc/resolve.d.ts","../../src/rpc/transport.d.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;;;;KAKYA,iBAAAA;;;;;IAAAA,GAAAA,EAAAA,MAAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["RpcProviderPreset","Record","alchemyChains","AlchemyChainId","RpcProviderName","RPC_PROVIDER_PRESETS","Readonly","isRpcProviderName","getAlchemyRpcUrl","getAlchemyWsRpcUrl","redactRpcSecrets","RpcProviderName","RpcEnv","Record","Readonly","RpcEndpoint","ResolveRpcEndpointsOptions","DEFAULT_RPC_PROVIDERS","resolveRpcEndpoints","resolveRpcWsUrl","viem1","HttpTransportConfig","Transport","RpcEndpoint","RpcHttpConfig","Omit","rpcHttp","viem0","RpcSchema","HttpTransport","RpcTransportOptions","rpcTransport","failover","requestsPerSecond","rpcForkArgs","ponderRpc"],"sources":["../../src/rpc/providers.d.ts","../../src/rpc/redact.d.ts","../../src/rpc/resolve.d.ts","../../src/rpc/transport.d.ts"],"sourcesContent":null,"mappings":";;;;;;;;;;;;;;;KAKYA,iBAAAA;;;;;IAAAA,GAAAA,EAAAA,MAAAA;IAWEE,OAAAA,CAAAA,EALIW,MAcjB,CAAA,MAAA,EAAA,MAAA,CAAA;EACWV,CAAAA,GAAAA,SAAAA;EACAQ;EACSN,UAAAA,CAAAA,EAAAA,CAAAA,OAAAA,EAA0E,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,GAAA,MAAA,GAAA,SAAA;CAAA;cAZjFH,aAY6CS,EAAAA;EAAe,SAAEX,CAAAA,EAAAA,aAAAA;EAAiB,SAAzCa,GAAAA,EAAAA,iBAAAA;EAAM,SAAfC,KAAAA,EAAAA,aAAAA;EAAQ,SAAA,IAAA,EAAA,cAAA;EAE3BN,SAAAA,EAAAA,EAAAA,aAAgB;EAChBC,SAAAA,GAAAA,EAAAA,kBAA4BN;;;;KALxCA,cAAAA,gBAA8BD;KAC9BS,eAAAA;cACSN,sBAAsBS,SAASD,OAAOF,iBAAiBX;iBAEpDQ,gBAAAA,UAA0BL;ACzB1BO,iBD0BAD,kBAAAA,CC1BgB,OAAA,ED0BYN,cC1BZ,EAAA,aAAA,EAAA,MAAA,CAAA,EAAA,MAAA,CAAA;;;;;;;iBAAhBO,gBAAAA;;;;KCJZE,MAAAA,GAASE,SAASD;KAClBU,WAAAA;;UAEAZ;;;WAGCE;;KAEDG,0BAAAA;EFJAhB;EAWEE,YAAAA,CAAAA,EAAAA,SASb,MAAA,EAAA;EACWC;EACAQ,gBAAAA,CAAAA,EAAe,SEdKA,eFcL,EAAA;AAC3B,CAAA;AAA+F,cEb1EM,qBFa0E,EAAA,SEb1CN,eFa0C,EAAA;;;;;AAA5C;AAEnD;AACA;;;;;iBEJwBO,mBAAAA,uBAA0CN,kBAAkBI,6BAA6BO;;;;ADtBjH;;iBC4BwBJ,eAAAA,uBAAsCP;;;;KC/BlDY,aAAAA,GAAgBC,KAAKJ;;iBAETK,OAAAA,WAAkBH,sBAAsBC,gBAAqEJ,KAAAA,CAAtCS,cAAlBF,KAAAA,CAA+CC,SAAAA;KAChHE,mBAAAA,GAAsBN;;;;;AHAlC;EAWctB,QAAAA,CAAAA,EAAAA,OASb;EACWC;EACAQ,iBAAAA,CAAe,EAAA,MAAA;AAC3B,CAAA;;;;;AAA2CG,iBGTnBiB,YAAAA,CHSmBjB,SAAAA,EAAAA,SGTcS,WHSdT,EAAAA,EAAAA;EAAAA,QAAAA;EAAAA,iBAAAA;EAAAA,GAAAA;AAAAA,CAAAA,CAAAA,EGT0EgB,mBHS1EhB,CAAAA,EGTgGQ,SHShGR;AAAQ;AAE3BN,iBGTA0B,WAAAA,CHS0B/B,SAAAA,EAAc,SGTRoB,WHSQ,EAAA,CAAA,EAAA;EACxCd,OAAAA,EAAAA,MAAAA;;;;;;;iBGFA0B,SAAAA,qBAA8BZ,oCAAoCD"}
|
package/dist/rpc/index.js
CHANGED
|
@@ -26,7 +26,8 @@ const infuraChains = {
|
|
|
26
26
|
const RPC_PROVIDER_PRESETS = {
|
|
27
27
|
alchemy: {
|
|
28
28
|
apiKeyEnv: () => ["ALCHEMY_API_KEY"],
|
|
29
|
-
endpoint: (chainId, apiKey) => isAlchemyChainId(chainId) ? { url: `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${apiKey}` } : void 0
|
|
29
|
+
endpoint: (chainId, apiKey) => isAlchemyChainId(chainId) ? { url: `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${apiKey}` } : void 0,
|
|
30
|
+
wsEndpoint: (chainId, apiKey) => isAlchemyChainId(chainId) ? getAlchemyWsRpcUrl(chainId, apiKey) : void 0
|
|
30
31
|
},
|
|
31
32
|
infura: {
|
|
32
33
|
apiKeyEnv: () => ["INFURA_API_KEY"],
|
|
@@ -106,6 +107,14 @@ function isHttpUrl(value) {
|
|
|
106
107
|
return false;
|
|
107
108
|
}
|
|
108
109
|
}
|
|
110
|
+
function isWsUrl(value) {
|
|
111
|
+
try {
|
|
112
|
+
const { protocol } = new URL(value);
|
|
113
|
+
return protocol === "ws:" || protocol === "wss:";
|
|
114
|
+
} catch {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
109
118
|
function presetApiKey(name, chainId, env) {
|
|
110
119
|
for (const envName of RPC_PROVIDER_PRESETS[name].apiKeyEnv(chainId)) {
|
|
111
120
|
const key = present(env[envName]);
|
|
@@ -194,6 +203,52 @@ function resolveRpcEndpoints(chainId, env, options = {}) {
|
|
|
194
203
|
...keyEnv
|
|
195
204
|
].join(", ")}`);
|
|
196
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Websocket RPC URL for `chainId`: `RPC_WS_URL_<chainId>`, else the first preset
|
|
208
|
+
* in `RPC_PROVIDERS` (or the defaults) that has an API key and serves websockets.
|
|
209
|
+
* Returns undefined when nothing is configured; throws on a non-ws(s) URL.
|
|
210
|
+
*/
|
|
211
|
+
function resolveRpcWsUrl(chainId, env) {
|
|
212
|
+
const envName = `RPC_WS_URL_${chainId}`;
|
|
213
|
+
const explicit = present(env[envName]);
|
|
214
|
+
if (explicit !== void 0) {
|
|
215
|
+
if (!isWsUrl(explicit)) throw new Error(`${envName} must be a ws(s) URL`);
|
|
216
|
+
return explicit;
|
|
217
|
+
}
|
|
218
|
+
const providers = present(env.RPC_PROVIDERS) ? splitList(env.RPC_PROVIDERS) : DEFAULT_RPC_PROVIDERS;
|
|
219
|
+
for (const name of providers) {
|
|
220
|
+
if (!isRpcProviderName(name)) continue;
|
|
221
|
+
const wsEndpoint = RPC_PROVIDER_PRESETS[name].wsEndpoint;
|
|
222
|
+
const apiKey = presetApiKey(name, chainId, env);
|
|
223
|
+
if (wsEndpoint === void 0 || apiKey === void 0) continue;
|
|
224
|
+
const url = wsEndpoint(chainId, apiKey);
|
|
225
|
+
if (url !== void 0) return url;
|
|
226
|
+
}
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/rpc/rateLimit.ts
|
|
232
|
+
/** Limits HTTP attempts, including retries, across clients sharing this fetch function. */
|
|
233
|
+
function rateLimitedRpcFetch(requestsPerSecond, fetchFn = fetch) {
|
|
234
|
+
if (!Number.isFinite(requestsPerSecond) || requestsPerSecond <= 0) throw new Error("RPC requestsPerSecond must be positive and finite");
|
|
235
|
+
const intervalMs = Math.ceil(1e3 / requestsPerSecond);
|
|
236
|
+
let nextStart = 0;
|
|
237
|
+
let tail = Promise.resolve();
|
|
238
|
+
return (input, init) => {
|
|
239
|
+
const signal = init?.signal ?? (input instanceof Request ? input.signal : void 0);
|
|
240
|
+
const request = tail.then(async () => {
|
|
241
|
+
signal?.throwIfAborted();
|
|
242
|
+
const delay = nextStart - Date.now();
|
|
243
|
+
if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));
|
|
244
|
+
signal?.throwIfAborted();
|
|
245
|
+
nextStart = Date.now() + intervalMs;
|
|
246
|
+
return fetchFn(input, init);
|
|
247
|
+
});
|
|
248
|
+
tail = request.then(() => void 0, () => void 0);
|
|
249
|
+
return request;
|
|
250
|
+
};
|
|
251
|
+
}
|
|
197
252
|
|
|
198
253
|
//#endregion
|
|
199
254
|
//#region src/rpc/transport.ts
|
|
@@ -208,11 +263,16 @@ function rpcHttp(endpoint, config = {}) {
|
|
|
208
263
|
* Transport over resolved endpoints: plain HTTP for one endpoint, otherwise an
|
|
209
264
|
* unranked viem `fallback` in configured order.
|
|
210
265
|
*/
|
|
211
|
-
function rpcTransport(endpoints, { failover = true,...config } = {}) {
|
|
266
|
+
function rpcTransport(endpoints, { failover = true, requestsPerSecond,...config } = {}) {
|
|
267
|
+
const httpConfig = requestsPerSecond === void 0 ? config : {
|
|
268
|
+
...config,
|
|
269
|
+
batch: false,
|
|
270
|
+
fetchFn: rateLimitedRpcFetch(requestsPerSecond, config.fetchFn)
|
|
271
|
+
};
|
|
212
272
|
const [primary, ...rest] = endpoints;
|
|
213
273
|
if (primary === void 0) throw new Error("rpcTransport needs at least one endpoint");
|
|
214
|
-
if (!failover || rest.length === 0) return rpcHttp(primary,
|
|
215
|
-
return fallback(endpoints.map((endpoint) => rpcHttp(endpoint,
|
|
274
|
+
if (!failover || rest.length === 0) return rpcHttp(primary, httpConfig);
|
|
275
|
+
return fallback(endpoints.map((endpoint) => rpcHttp(endpoint, httpConfig)), { rank: false });
|
|
216
276
|
}
|
|
217
277
|
/** anvil fork arguments for the primary endpoint (`--fork-header` only when it needs one). */
|
|
218
278
|
function rpcForkArgs(endpoints) {
|
|
@@ -238,5 +298,5 @@ function ponderRpc(endpoints) {
|
|
|
238
298
|
}
|
|
239
299
|
|
|
240
300
|
//#endregion
|
|
241
|
-
export { DEFAULT_RPC_PROVIDERS, RPC_PROVIDER_PRESETS, getAlchemyRpcUrl, getAlchemyWsRpcUrl, ponderRpc, redactRpcSecrets, resolveRpcEndpoints, rpcForkArgs, rpcHttp, rpcTransport };
|
|
301
|
+
export { DEFAULT_RPC_PROVIDERS, RPC_PROVIDER_PRESETS, getAlchemyRpcUrl, getAlchemyWsRpcUrl, ponderRpc, redactRpcSecrets, resolveRpcEndpoints, resolveRpcWsUrl, rpcForkArgs, rpcHttp, rpcTransport };
|
|
242
302
|
//# sourceMappingURL=index.js.map
|
package/dist/rpc/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["chainId: number","infuraChains: Record<number, string>","RPC_PROVIDER_PRESETS: Readonly<Record<RpcProviderName, RpcProviderPreset>>","name: string","chainId: AlchemyChainId","alchemyApiKey: string","raw: string","text: string | undefined","DEFAULT_RPC_PROVIDERS: readonly RpcProviderName[]","value: string | undefined","envName: string","headers: Record<string, string>","value: string","name: RpcProviderName","chainId: number","env: RpcEnv","legacyUrlEnv: readonly string[]","providers: readonly string[]","endpoints: RpcEndpoint[]","options: ResolveRpcEndpointsOptions","endpoint: RpcEndpoint","config: RpcHttpConfig","endpoints: readonly RpcEndpoint[]"],"sources":["../../src/rpc/providers.ts","../../src/rpc/redact.ts","../../src/rpc/resolve.ts","../../src/rpc/transport.ts"],"sourcesContent":["/**\n * The only place that knows RPC vendor URL formats and credentials. Supporting\n * a new vendor means adding one preset here; services select it through env\n * (see `resolveRpcEndpoints`).\n */\n\nexport type RpcProviderPreset = {\n /** Env vars holding the API key for `chainId`, highest precedence first. */\n apiKeyEnv: (chainId: number) => readonly string[]\n /** Endpoint for `chainId`, or undefined when the vendor does not serve it. */\n endpoint: (\n chainId: number,\n apiKey: string,\n ) => { url: string; headers?: Record<string, string> } | undefined\n}\n\nconst alchemyChains = {\n 1: 'eth-mainnet',\n 137: 'polygon-mainnet',\n 42161: 'arb-mainnet',\n 8453: 'base-mainnet',\n 10: 'opt-mainnet',\n 130: 'unichain-mainnet',\n 4663: 'robinhood-mainnet',\n 11155111: 'eth-sepolia',\n} as const\n\nexport type AlchemyChainId = keyof typeof alchemyChains\n\nfunction isAlchemyChainId(chainId: number): chainId is AlchemyChainId {\n return chainId in alchemyChains\n}\n\nconst infuraChains: Record<number, string> = {\n 1: 'mainnet',\n 10: 'optimism-mainnet',\n 137: 'polygon-mainnet',\n 8453: 'base-mainnet',\n 42161: 'arbitrum-mainnet',\n 43114: 'avalanche-mainnet',\n 11155111: 'sepolia',\n}\n\nexport type RpcProviderName = 'alchemy' | 'infura' | 'uniblock'\n\nexport const RPC_PROVIDER_PRESETS: Readonly<Record<RpcProviderName, RpcProviderPreset>> = {\n alchemy: {\n apiKeyEnv: () => ['ALCHEMY_API_KEY'],\n endpoint: (chainId, apiKey) =>\n isAlchemyChainId(chainId)\n ? { url: `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${apiKey}` }\n : undefined,\n },\n infura: {\n apiKeyEnv: () => ['INFURA_API_KEY'],\n endpoint: (chainId, apiKey) =>\n infuraChains[chainId]\n ? { url: `https://${infuraChains[chainId]}.infura.io/v3/${apiKey}` }\n : undefined,\n },\n uniblock: {\n apiKeyEnv: () => ['UNIBLOCK_API_KEY'],\n // Uniblock accepts the key only as a header, and serves keyless requests\n // from an anonymous tier — callers must never send it without one.\n endpoint: (chainId, apiKey) => ({\n url: `https://api.uniblock.dev/uni/v1/json-rpc?chainId=${chainId}`,\n headers: { 'x-api-key': apiKey },\n }),\n },\n}\n\nexport function isRpcProviderName(name: string): name is RpcProviderName {\n return Object.prototype.hasOwnProperty.call(RPC_PROVIDER_PRESETS, name)\n}\n\nexport function getAlchemyRpcUrl(chainId: AlchemyChainId, alchemyApiKey: string): string {\n if (alchemyChains[chainId]) {\n return `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`\n } else {\n throw new Error(`Unsupported chainId: ${chainId}`)\n }\n}\n\nexport function getAlchemyWsRpcUrl(chainId: AlchemyChainId, alchemyApiKey: string): string {\n if (alchemyChains[chainId]) {\n return `wss://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`\n } else {\n throw new Error(`Unsupported chainId: ${chainId}`)\n }\n}\n","const URL_PATTERN = /\\b(?:https?|wss?):\\/\\/[^\\s\"'<>)]+/giu\n// Unquoted authentication values can contain commas and quotes, so mask through the line boundary.\nconst CREDENTIAL_HEADER_PATTERN =\n /\\b(x-api-key|authorization|proxy-authorization)([\"']?\\s*[:=]\\s*)(?:([\"'])(?:\\\\.|(?!\\3)[^\\\\\\r\\n])*\\3|[^\\r\\n]+)/giu\n\nfunction redactUrl(raw: string): string {\n try {\n const url = new URL(raw)\n const port = url.port ? `:${url.port}` : ''\n return `${url.protocol}//${url.hostname}${port}/<redacted>`\n } catch {\n return '<redacted-url>'\n }\n}\n\n/**\n * Strip RPC credentials from text before it is logged or shown. Providers\n * embed keys in the URL path or query, so every URL keeps only its origin;\n * credential header values are masked too.\n */\nexport function redactRpcSecrets(text: string | undefined): string {\n if (!text) return ''\n return text\n .replace(CREDENTIAL_HEADER_PATTERN, '$1$2$3<redacted>$3')\n .replace(URL_PATTERN, redactUrl)\n}\n","import { type RpcProviderName, isRpcProviderName, RPC_PROVIDER_PRESETS } from './providers'\n\nexport type RpcEnv = Readonly<Record<string, string | undefined>>\n\nexport type RpcEndpoint = {\n /** Preset name, or `'url'` for an explicit URL entry. */\n source: RpcProviderName | 'url'\n url: string\n /** Credentials that must accompany every request to `url`, and only to `url`. */\n headers: Record<string, string>\n}\n\nexport type ResolveRpcEndpointsOptions = {\n /** Service-specific URL variables honoured when `RPC_URL_<chainId>` is unset, in order. */\n legacyUrlEnv?: readonly string[]\n /** Presets tried, in order, when no URL is configured. Overridden by `RPC_PROVIDERS`. */\n defaultProviders?: readonly RpcProviderName[]\n}\n\nexport const DEFAULT_RPC_PROVIDERS: readonly RpcProviderName[] = ['alchemy', 'uniblock']\n\nfunction present(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed ? trimmed : undefined\n}\n\nfunction splitList(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry !== '')\n}\n\nfunction parseHeaders(value: string | undefined, envName: string): Record<string, string> {\n const headers: Record<string, string> = {}\n for (const pair of splitHeaderPairs(value)) {\n const separator = pair.indexOf(':')\n const name = pair.slice(0, separator).trim()\n if (separator <= 0 || name === '') {\n throw new Error(`${envName} must be \"Name: value\" pairs separated by \";\"`)\n }\n headers[name] = pair.slice(separator + 1).trim()\n }\n return headers\n}\n\nfunction splitHeaderPairs(value: string | undefined): string[] {\n return (value ?? '')\n .split(';')\n .map((pair) => pair.trim())\n .filter((pair) => pair !== '')\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const { protocol } = new URL(value)\n return protocol === 'http:' || protocol === 'https:'\n } catch {\n return false\n }\n}\n\nfunction presetApiKey(name: RpcProviderName, chainId: number, env: RpcEnv) {\n for (const envName of RPC_PROVIDER_PRESETS[name].apiKeyEnv(chainId)) {\n const key = present(env[envName])\n if (key !== undefined) return key\n }\n return undefined\n}\n\nfunction presetEndpoint(\n name: RpcProviderName,\n chainId: number,\n env: RpcEnv,\n): RpcEndpoint | undefined {\n const apiKey = presetApiKey(name, chainId, env)\n if (apiKey === undefined) return undefined\n const endpoint = RPC_PROVIDER_PRESETS[name].endpoint(chainId, apiKey)\n return endpoint && { source: name, url: endpoint.url, headers: endpoint.headers ?? {} }\n}\n\nfunction explicitEndpoints(chainId: number, env: RpcEnv): RpcEndpoint[] {\n const listEnv = `RPC_URL_${chainId}`\n return splitList(env[listEnv]).map((entry, index) => {\n if (isHttpUrl(entry)) {\n const headersEnv = `RPC_HEADERS_${chainId}_${index}`\n return { source: 'url', url: entry, headers: parseHeaders(env[headersEnv], headersEnv) }\n }\n if (!isRpcProviderName(entry)) {\n throw new Error(`${listEnv} entry \"${entry}\" is neither an http(s) URL nor a known provider`)\n }\n const endpoint = presetEndpoint(entry, chainId, env)\n if (endpoint === undefined) {\n const keyEnv = RPC_PROVIDER_PRESETS[entry].apiKeyEnv(chainId).join(' or ')\n throw new Error(\n `${listEnv} lists \"${entry}\", which needs ${keyEnv} and must serve chain ${chainId}`,\n )\n }\n return endpoint\n })\n}\n\nfunction legacyEndpoints(env: RpcEnv, legacyUrlEnv: readonly string[]): RpcEndpoint[] {\n return legacyUrlEnv.flatMap((envName) =>\n splitList(env[envName]).map((url): RpcEndpoint => ({ source: 'url', url, headers: {} })),\n )\n}\n\nfunction firstPresetEndpoint(\n chainId: number,\n env: RpcEnv,\n providers: readonly string[],\n): RpcEndpoint | undefined {\n for (const name of providers) {\n if (!isRpcProviderName(name)) {\n throw new Error(`RPC_PROVIDERS entry \"${name}\" is not a known provider`)\n }\n const endpoint = presetEndpoint(name, chainId, env)\n if (endpoint !== undefined) return endpoint\n }\n return undefined\n}\n\nfunction dedupe(endpoints: RpcEndpoint[]): RpcEndpoint[] {\n const seen = new Set<string>()\n return endpoints.filter((endpoint) => {\n const key = `${endpoint.url}\\n${JSON.stringify(endpoint.headers)}`\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n}\n\n/**\n * Ordered RPC endpoints for `chainId` (primary first, the rest failover):\n *\n * 1. `RPC_URL_<chainId>` — comma-separated URLs and/or preset names; entry `n`\n * gets headers from `RPC_HEADERS_<chainId>_<n>` (\"Name: value;Name: value\").\n * 2. Otherwise the service's legacy URL variables (`legacyUrlEnv`).\n * 3. Otherwise the first preset in `RPC_PROVIDERS` (or `defaultProviders`)\n * that has an API key and serves the chain.\n *\n * Blank values count as unset. Throws when nothing resolves.\n */\nexport function resolveRpcEndpoints(\n chainId: number,\n env: RpcEnv,\n options: ResolveRpcEndpointsOptions = {},\n): RpcEndpoint[] {\n const explicit = explicitEndpoints(chainId, env)\n if (explicit.length > 0) return dedupe(explicit)\n\n const legacyUrlEnv = options.legacyUrlEnv ?? []\n const legacy = legacyEndpoints(env, legacyUrlEnv)\n if (legacy.length > 0) return dedupe(legacy)\n\n const providers = present(env.RPC_PROVIDERS)\n ? splitList(env.RPC_PROVIDERS)\n : (options.defaultProviders ?? DEFAULT_RPC_PROVIDERS)\n const preset = firstPresetEndpoint(chainId, env, providers)\n if (preset !== undefined) return [preset]\n\n const keyEnv = [\n ...new Set(\n providers.flatMap((name) =>\n isRpcProviderName(name) ? RPC_PROVIDER_PRESETS[name].apiKeyEnv(chainId) : [],\n ),\n ),\n ]\n throw new Error(\n `No RPC configured for chain ${chainId}: set ${[`RPC_URL_${chainId}`, ...legacyUrlEnv, ...keyEnv].join(', ')}`,\n )\n}\n","import { type HttpTransportConfig, type Transport, fallback, http } from 'viem'\n\nimport type { RpcEndpoint } from './resolve'\n\nexport type RpcHttpConfig = Omit<HttpTransportConfig, 'fetchOptions'>\n\n/** viem HTTP transport for one endpoint, carrying its credential headers. */\nexport function rpcHttp(endpoint: RpcEndpoint, config: RpcHttpConfig = {}) {\n return http(endpoint.url, { ...config, fetchOptions: { headers: endpoint.headers } })\n}\n\nexport type RpcTransportOptions = RpcHttpConfig & {\n /**\n * Fail over to later endpoints. Disable for clients that submit\n * transactions: a tx broadcast through one provider and retried through\n * another surfaces as \"already known\" / \"nonce too low\" although it was sent.\n */\n failover?: boolean\n}\n\n/**\n * Transport over resolved endpoints: plain HTTP for one endpoint, otherwise an\n * unranked viem `fallback` in configured order.\n */\nexport function rpcTransport(\n endpoints: readonly RpcEndpoint[],\n { failover = true, ...config }: RpcTransportOptions = {},\n): Transport {\n const [primary, ...rest] = endpoints\n if (primary === undefined) throw new Error('rpcTransport needs at least one endpoint')\n if (!failover || rest.length === 0) return rpcHttp(primary, config)\n return fallback(\n endpoints.map((endpoint) => rpcHttp(endpoint, config)),\n { rank: false },\n )\n}\n\n/** anvil fork arguments for the primary endpoint (`--fork-header` only when it needs one). */\nexport function rpcForkArgs(endpoints: readonly RpcEndpoint[]): {\n forkUrl: string\n forkHeader?: string\n} {\n const [primary] = endpoints\n if (primary === undefined) throw new Error('rpcForkArgs needs at least one endpoint')\n const headers = Object.entries(primary.headers)\n if (headers.length === 0) return { forkUrl: primary.url }\n if (headers.length > 1) throw new Error('anvil --fork-header supports one header per fork')\n const [[name, value]] = headers\n return { forkUrl: primary.url, forkHeader: `${name}: ${value}` }\n}\n\n/**\n * Value for a Ponder chain's `rpc`. Header-free endpoints stay plain URLs so\n * Ponder keeps its own load balancing; credential headers need a transport.\n */\nexport function ponderRpc(endpoints: readonly RpcEndpoint[]): string | string[] | Transport {\n if (endpoints.some((endpoint) => Object.keys(endpoint.headers).length > 0)) {\n return rpcTransport(endpoints)\n }\n const urls = endpoints.map((endpoint) => endpoint.url)\n return urls.length === 1 ? urls[0] : urls\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["chainId: number","infuraChains: Record<number, string>","RPC_PROVIDER_PRESETS: Readonly<Record<RpcProviderName, RpcProviderPreset>>","name: string","chainId: AlchemyChainId","alchemyApiKey: string","raw: string","text: string | undefined","DEFAULT_RPC_PROVIDERS: readonly RpcProviderName[]","value: string | undefined","envName: string","headers: Record<string, string>","value: string","name: RpcProviderName","chainId: number","env: RpcEnv","legacyUrlEnv: readonly string[]","providers: readonly string[]","endpoints: RpcEndpoint[]","options: ResolveRpcEndpointsOptions","requestsPerSecond: number","fetchFn: NonNullable<HttpTransportConfig['fetchFn']>","endpoint: RpcEndpoint","config: RpcHttpConfig","endpoints: readonly RpcEndpoint[]"],"sources":["../../src/rpc/providers.ts","../../src/rpc/redact.ts","../../src/rpc/resolve.ts","../../src/rpc/rateLimit.ts","../../src/rpc/transport.ts"],"sourcesContent":["/**\n * The only place that knows RPC vendor URL formats and credentials. Supporting\n * a new vendor means adding one preset here; services select it through env\n * (see `resolveRpcEndpoints`).\n */\n\nexport type RpcProviderPreset = {\n /** Env vars holding the API key for `chainId`, highest precedence first. */\n apiKeyEnv: (chainId: number) => readonly string[]\n /** Endpoint for `chainId`, or undefined when the vendor does not serve it. */\n endpoint: (\n chainId: number,\n apiKey: string,\n ) => { url: string; headers?: Record<string, string> } | undefined\n /** Websocket endpoint for `chainId`, or undefined when the vendor does not serve one. */\n wsEndpoint?: (chainId: number, apiKey: string) => string | undefined\n}\n\nconst alchemyChains = {\n 1: 'eth-mainnet',\n 137: 'polygon-mainnet',\n 42161: 'arb-mainnet',\n 8453: 'base-mainnet',\n 10: 'opt-mainnet',\n 130: 'unichain-mainnet',\n 4663: 'robinhood-mainnet',\n 11155111: 'eth-sepolia',\n} as const\n\nexport type AlchemyChainId = keyof typeof alchemyChains\n\nfunction isAlchemyChainId(chainId: number): chainId is AlchemyChainId {\n return chainId in alchemyChains\n}\n\nconst infuraChains: Record<number, string> = {\n 1: 'mainnet',\n 10: 'optimism-mainnet',\n 137: 'polygon-mainnet',\n 8453: 'base-mainnet',\n 42161: 'arbitrum-mainnet',\n 43114: 'avalanche-mainnet',\n 11155111: 'sepolia',\n}\n\nexport type RpcProviderName = 'alchemy' | 'infura' | 'uniblock'\n\nexport const RPC_PROVIDER_PRESETS: Readonly<Record<RpcProviderName, RpcProviderPreset>> = {\n alchemy: {\n apiKeyEnv: () => ['ALCHEMY_API_KEY'],\n endpoint: (chainId, apiKey) =>\n isAlchemyChainId(chainId)\n ? { url: `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${apiKey}` }\n : undefined,\n wsEndpoint: (chainId, apiKey) =>\n isAlchemyChainId(chainId) ? getAlchemyWsRpcUrl(chainId, apiKey) : undefined,\n },\n infura: {\n apiKeyEnv: () => ['INFURA_API_KEY'],\n endpoint: (chainId, apiKey) =>\n infuraChains[chainId]\n ? { url: `https://${infuraChains[chainId]}.infura.io/v3/${apiKey}` }\n : undefined,\n },\n uniblock: {\n apiKeyEnv: () => ['UNIBLOCK_API_KEY'],\n // Uniblock accepts the key only as a header, and serves keyless requests\n // from an anonymous tier — callers must never send it without one.\n endpoint: (chainId, apiKey) => ({\n url: `https://api.uniblock.dev/uni/v1/json-rpc?chainId=${chainId}`,\n headers: { 'x-api-key': apiKey },\n }),\n },\n}\n\nexport function isRpcProviderName(name: string): name is RpcProviderName {\n return Object.prototype.hasOwnProperty.call(RPC_PROVIDER_PRESETS, name)\n}\n\nexport function getAlchemyRpcUrl(chainId: AlchemyChainId, alchemyApiKey: string): string {\n if (alchemyChains[chainId]) {\n return `https://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`\n } else {\n throw new Error(`Unsupported chainId: ${chainId}`)\n }\n}\n\nexport function getAlchemyWsRpcUrl(chainId: AlchemyChainId, alchemyApiKey: string): string {\n if (alchemyChains[chainId]) {\n return `wss://${alchemyChains[chainId]}.g.alchemy.com/v2/${alchemyApiKey}`\n } else {\n throw new Error(`Unsupported chainId: ${chainId}`)\n }\n}\n","const URL_PATTERN = /\\b(?:https?|wss?):\\/\\/[^\\s\"'<>)]+/giu\n// Unquoted authentication values can contain commas and quotes, so mask through the line boundary.\nconst CREDENTIAL_HEADER_PATTERN =\n /\\b(x-api-key|authorization|proxy-authorization)([\"']?\\s*[:=]\\s*)(?:([\"'])(?:\\\\.|(?!\\3)[^\\\\\\r\\n])*\\3|[^\\r\\n]+)/giu\n\nfunction redactUrl(raw: string): string {\n try {\n const url = new URL(raw)\n const port = url.port ? `:${url.port}` : ''\n return `${url.protocol}//${url.hostname}${port}/<redacted>`\n } catch {\n return '<redacted-url>'\n }\n}\n\n/**\n * Strip RPC credentials from text before it is logged or shown. Providers\n * embed keys in the URL path or query, so every URL keeps only its origin;\n * credential header values are masked too.\n */\nexport function redactRpcSecrets(text: string | undefined): string {\n if (!text) return ''\n return text\n .replace(CREDENTIAL_HEADER_PATTERN, '$1$2$3<redacted>$3')\n .replace(URL_PATTERN, redactUrl)\n}\n","import { type RpcProviderName, isRpcProviderName, RPC_PROVIDER_PRESETS } from './providers'\n\nexport type RpcEnv = Readonly<Record<string, string | undefined>>\n\nexport type RpcEndpoint = {\n /** Preset name, or `'url'` for an explicit URL entry. */\n source: RpcProviderName | 'url'\n url: string\n /** Credentials that must accompany every request to `url`, and only to `url`. */\n headers: Record<string, string>\n}\n\nexport type ResolveRpcEndpointsOptions = {\n /** Service-specific URL variables honoured when `RPC_URL_<chainId>` is unset, in order. */\n legacyUrlEnv?: readonly string[]\n /** Presets tried, in order, when no URL is configured. Overridden by `RPC_PROVIDERS`. */\n defaultProviders?: readonly RpcProviderName[]\n}\n\nexport const DEFAULT_RPC_PROVIDERS: readonly RpcProviderName[] = ['alchemy', 'uniblock']\n\nfunction present(value: string | undefined): string | undefined {\n const trimmed = value?.trim()\n return trimmed ? trimmed : undefined\n}\n\nfunction splitList(value: string | undefined): string[] {\n return (value ?? '')\n .split(',')\n .map((entry) => entry.trim())\n .filter((entry) => entry !== '')\n}\n\nfunction parseHeaders(value: string | undefined, envName: string): Record<string, string> {\n const headers: Record<string, string> = {}\n for (const pair of splitHeaderPairs(value)) {\n const separator = pair.indexOf(':')\n const name = pair.slice(0, separator).trim()\n if (separator <= 0 || name === '') {\n throw new Error(`${envName} must be \"Name: value\" pairs separated by \";\"`)\n }\n headers[name] = pair.slice(separator + 1).trim()\n }\n return headers\n}\n\nfunction splitHeaderPairs(value: string | undefined): string[] {\n return (value ?? '')\n .split(';')\n .map((pair) => pair.trim())\n .filter((pair) => pair !== '')\n}\n\nfunction isHttpUrl(value: string): boolean {\n try {\n const { protocol } = new URL(value)\n return protocol === 'http:' || protocol === 'https:'\n } catch {\n return false\n }\n}\n\nfunction isWsUrl(value: string): boolean {\n try {\n const { protocol } = new URL(value)\n return protocol === 'ws:' || protocol === 'wss:'\n } catch {\n return false\n }\n}\n\nfunction presetApiKey(name: RpcProviderName, chainId: number, env: RpcEnv) {\n for (const envName of RPC_PROVIDER_PRESETS[name].apiKeyEnv(chainId)) {\n const key = present(env[envName])\n if (key !== undefined) return key\n }\n return undefined\n}\n\nfunction presetEndpoint(\n name: RpcProviderName,\n chainId: number,\n env: RpcEnv,\n): RpcEndpoint | undefined {\n const apiKey = presetApiKey(name, chainId, env)\n if (apiKey === undefined) return undefined\n const endpoint = RPC_PROVIDER_PRESETS[name].endpoint(chainId, apiKey)\n return endpoint && { source: name, url: endpoint.url, headers: endpoint.headers ?? {} }\n}\n\nfunction explicitEndpoints(chainId: number, env: RpcEnv): RpcEndpoint[] {\n const listEnv = `RPC_URL_${chainId}`\n return splitList(env[listEnv]).map((entry, index) => {\n if (isHttpUrl(entry)) {\n const headersEnv = `RPC_HEADERS_${chainId}_${index}`\n return { source: 'url', url: entry, headers: parseHeaders(env[headersEnv], headersEnv) }\n }\n if (!isRpcProviderName(entry)) {\n throw new Error(`${listEnv} entry \"${entry}\" is neither an http(s) URL nor a known provider`)\n }\n const endpoint = presetEndpoint(entry, chainId, env)\n if (endpoint === undefined) {\n const keyEnv = RPC_PROVIDER_PRESETS[entry].apiKeyEnv(chainId).join(' or ')\n throw new Error(\n `${listEnv} lists \"${entry}\", which needs ${keyEnv} and must serve chain ${chainId}`,\n )\n }\n return endpoint\n })\n}\n\nfunction legacyEndpoints(env: RpcEnv, legacyUrlEnv: readonly string[]): RpcEndpoint[] {\n return legacyUrlEnv.flatMap((envName) =>\n splitList(env[envName]).map((url): RpcEndpoint => ({ source: 'url', url, headers: {} })),\n )\n}\n\nfunction firstPresetEndpoint(\n chainId: number,\n env: RpcEnv,\n providers: readonly string[],\n): RpcEndpoint | undefined {\n for (const name of providers) {\n if (!isRpcProviderName(name)) {\n throw new Error(`RPC_PROVIDERS entry \"${name}\" is not a known provider`)\n }\n const endpoint = presetEndpoint(name, chainId, env)\n if (endpoint !== undefined) return endpoint\n }\n return undefined\n}\n\nfunction dedupe(endpoints: RpcEndpoint[]): RpcEndpoint[] {\n const seen = new Set<string>()\n return endpoints.filter((endpoint) => {\n const key = `${endpoint.url}\\n${JSON.stringify(endpoint.headers)}`\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n}\n\n/**\n * Ordered RPC endpoints for `chainId` (primary first, the rest failover):\n *\n * 1. `RPC_URL_<chainId>` — comma-separated URLs and/or preset names; entry `n`\n * gets headers from `RPC_HEADERS_<chainId>_<n>` (\"Name: value;Name: value\").\n * 2. Otherwise the service's legacy URL variables (`legacyUrlEnv`).\n * 3. Otherwise the first preset in `RPC_PROVIDERS` (or `defaultProviders`)\n * that has an API key and serves the chain.\n *\n * Blank values count as unset. Throws when nothing resolves.\n */\nexport function resolveRpcEndpoints(\n chainId: number,\n env: RpcEnv,\n options: ResolveRpcEndpointsOptions = {},\n): RpcEndpoint[] {\n const explicit = explicitEndpoints(chainId, env)\n if (explicit.length > 0) return dedupe(explicit)\n\n const legacyUrlEnv = options.legacyUrlEnv ?? []\n const legacy = legacyEndpoints(env, legacyUrlEnv)\n if (legacy.length > 0) return dedupe(legacy)\n\n const providers = present(env.RPC_PROVIDERS)\n ? splitList(env.RPC_PROVIDERS)\n : (options.defaultProviders ?? DEFAULT_RPC_PROVIDERS)\n const preset = firstPresetEndpoint(chainId, env, providers)\n if (preset !== undefined) return [preset]\n\n const keyEnv = [\n ...new Set(\n providers.flatMap((name) =>\n isRpcProviderName(name) ? RPC_PROVIDER_PRESETS[name].apiKeyEnv(chainId) : [],\n ),\n ),\n ]\n throw new Error(\n `No RPC configured for chain ${chainId}: set ${[`RPC_URL_${chainId}`, ...legacyUrlEnv, ...keyEnv].join(', ')}`,\n )\n}\n\n/**\n * Websocket RPC URL for `chainId`: `RPC_WS_URL_<chainId>`, else the first preset\n * in `RPC_PROVIDERS` (or the defaults) that has an API key and serves websockets.\n * Returns undefined when nothing is configured; throws on a non-ws(s) URL.\n */\nexport function resolveRpcWsUrl(chainId: number, env: RpcEnv): string | undefined {\n const envName = `RPC_WS_URL_${chainId}`\n const explicit = present(env[envName])\n if (explicit !== undefined) {\n if (!isWsUrl(explicit)) throw new Error(`${envName} must be a ws(s) URL`)\n return explicit\n }\n const providers = present(env.RPC_PROVIDERS)\n ? splitList(env.RPC_PROVIDERS)\n : DEFAULT_RPC_PROVIDERS\n for (const name of providers) {\n if (!isRpcProviderName(name)) continue\n const wsEndpoint = RPC_PROVIDER_PRESETS[name].wsEndpoint\n const apiKey = presetApiKey(name, chainId, env)\n if (wsEndpoint === undefined || apiKey === undefined) continue\n const url = wsEndpoint(chainId, apiKey)\n if (url !== undefined) return url\n }\n return undefined\n}\n","import type { HttpTransportConfig } from 'viem'\n\n/** Limits HTTP attempts, including retries, across clients sharing this fetch function. */\nexport function rateLimitedRpcFetch(\n requestsPerSecond: number,\n fetchFn: NonNullable<HttpTransportConfig['fetchFn']> = fetch,\n): NonNullable<HttpTransportConfig['fetchFn']> {\n if (!Number.isFinite(requestsPerSecond) || requestsPerSecond <= 0) {\n throw new Error('RPC requestsPerSecond must be positive and finite')\n }\n const intervalMs = Math.ceil(1_000 / requestsPerSecond)\n let nextStart = 0\n let tail = Promise.resolve()\n\n return (input, init) => {\n const signal = init?.signal ?? (input instanceof Request ? input.signal : undefined)\n const request = tail.then(async () => {\n signal?.throwIfAborted()\n const delay = nextStart - Date.now()\n if (delay > 0) await new Promise<void>((resolve) => setTimeout(resolve, delay))\n signal?.throwIfAborted()\n nextStart = Date.now() + intervalMs\n return fetchFn(input, init)\n })\n tail = request.then(\n () => undefined,\n () => undefined,\n )\n return request\n }\n}\n","import { type HttpTransportConfig, type Transport, fallback, http } from 'viem'\n\nimport { rateLimitedRpcFetch } from './rateLimit'\nimport type { RpcEndpoint } from './resolve'\n\nexport type RpcHttpConfig = Omit<HttpTransportConfig, 'fetchOptions'>\n\n/** viem HTTP transport for one endpoint, carrying its credential headers. */\nexport function rpcHttp(endpoint: RpcEndpoint, config: RpcHttpConfig = {}) {\n return http(endpoint.url, { ...config, fetchOptions: { headers: endpoint.headers } })\n}\n\nexport type RpcTransportOptions = RpcHttpConfig & {\n /**\n * Fail over to later endpoints. Disable for clients that submit\n * transactions: a tx broadcast through one provider and retried through\n * another surfaces as \"already known\" / \"nonce too low\" although it was sent.\n */\n failover?: boolean\n /** Shared HTTP attempt limit, including retries and failover. Disables HTTP batching. */\n requestsPerSecond?: number\n}\n\n/**\n * Transport over resolved endpoints: plain HTTP for one endpoint, otherwise an\n * unranked viem `fallback` in configured order.\n */\nexport function rpcTransport(\n endpoints: readonly RpcEndpoint[],\n { failover = true, requestsPerSecond, ...config }: RpcTransportOptions = {},\n): Transport {\n const httpConfig =\n requestsPerSecond === undefined\n ? config\n : {\n ...config,\n batch: false as const,\n fetchFn: rateLimitedRpcFetch(requestsPerSecond, config.fetchFn),\n }\n const [primary, ...rest] = endpoints\n if (primary === undefined) throw new Error('rpcTransport needs at least one endpoint')\n if (!failover || rest.length === 0) return rpcHttp(primary, httpConfig)\n return fallback(\n endpoints.map((endpoint) => rpcHttp(endpoint, httpConfig)),\n { rank: false },\n )\n}\n\n/** anvil fork arguments for the primary endpoint (`--fork-header` only when it needs one). */\nexport function rpcForkArgs(endpoints: readonly RpcEndpoint[]): {\n forkUrl: string\n forkHeader?: string\n} {\n const [primary] = endpoints\n if (primary === undefined) throw new Error('rpcForkArgs needs at least one endpoint')\n const headers = Object.entries(primary.headers)\n if (headers.length === 0) return { forkUrl: primary.url }\n if (headers.length > 1) throw new Error('anvil --fork-header supports one header per fork')\n const [[name, value]] = headers\n return { forkUrl: primary.url, forkHeader: `${name}: ${value}` }\n}\n\n/**\n * Value for a Ponder chain's `rpc`. Header-free endpoints stay plain URLs so\n * Ponder keeps its own load balancing; credential headers need a transport.\n */\nexport function ponderRpc(endpoints: readonly RpcEndpoint[]): string | string[] | Transport {\n if (endpoints.some((endpoint) => Object.keys(endpoint.headers).length > 0)) {\n return rpcTransport(endpoints)\n }\n const urls = endpoints.map((endpoint) => endpoint.url)\n return urls.length === 1 ? urls[0] : urls\n}\n"],"mappings":"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"}
|
package/dist/uniswap/index.js
CHANGED
|
@@ -792,7 +792,7 @@ async function getUniswapV3LpPositionState(params) {
|
|
|
792
792
|
|
|
793
793
|
//#endregion
|
|
794
794
|
//#region src/uniswap/lpGreeks.ts
|
|
795
|
-
const Q96$
|
|
795
|
+
const Q96$5 = 1n << 96n;
|
|
796
796
|
const Q192$5 = 1n << 192n;
|
|
797
797
|
/**
|
|
798
798
|
* Compute the token0/token1 amounts backing `liquidity` over `[sqrtA, sqrtB]`
|
|
@@ -803,8 +803,8 @@ const Q192$5 = 1n << 192n;
|
|
|
803
803
|
function getAmountsForLiquidity(sqrtPriceX96, sqrtRatioAX96, sqrtRatioBX96, liquidity) {
|
|
804
804
|
const [sqrtA, sqrtB] = sqrtRatioAX96 <= sqrtRatioBX96 ? [sqrtRatioAX96, sqrtRatioBX96] : [sqrtRatioBX96, sqrtRatioAX96];
|
|
805
805
|
const sqrtC = sqrtPriceX96 < sqrtA ? sqrtA : sqrtPriceX96 > sqrtB ? sqrtB : sqrtPriceX96;
|
|
806
|
-
const amount0 = sqrtC < sqrtB ? liquidity * (sqrtB - sqrtC) * Q96$
|
|
807
|
-
const amount1 = sqrtC > sqrtA ? liquidity * (sqrtC - sqrtA) / Q96$
|
|
806
|
+
const amount0 = sqrtC < sqrtB ? liquidity * (sqrtB - sqrtC) * Q96$5 / (sqrtC * sqrtB) : 0n;
|
|
807
|
+
const amount1 = sqrtC > sqrtA ? liquidity * (sqrtC - sqrtA) / Q96$5 : 0n;
|
|
808
808
|
return {
|
|
809
809
|
amount0,
|
|
810
810
|
amount1
|
|
@@ -833,7 +833,7 @@ function getLpGreeks(input) {
|
|
|
833
833
|
if (assetIndex === 0) {
|
|
834
834
|
const value$1 = amount1 + amount0 * sqrtP2 / Q192$5;
|
|
835
835
|
const delta$1 = amount0;
|
|
836
|
-
const gamma$1 = inRange ? -(liquidity * sqrtP) / (2n * Q96$
|
|
836
|
+
const gamma$1 = inRange ? -(liquidity * sqrtP) / (2n * Q96$5) : 0n;
|
|
837
837
|
return {
|
|
838
838
|
value: value$1,
|
|
839
839
|
delta: delta$1,
|
|
@@ -842,7 +842,7 @@ function getLpGreeks(input) {
|
|
|
842
842
|
}
|
|
843
843
|
const value = amount0 + amount1 * Q192$5 / sqrtP2;
|
|
844
844
|
const delta = amount1;
|
|
845
|
-
const gamma = inRange ? -(liquidity * Q96$
|
|
845
|
+
const gamma = inRange ? -(liquidity * Q96$5) / (2n * sqrtP) : 0n;
|
|
846
846
|
return {
|
|
847
847
|
value,
|
|
848
848
|
delta,
|
|
@@ -1075,6 +1075,9 @@ var NotMarginCalledError = class extends PanopticError {
|
|
|
1075
1075
|
*/
|
|
1076
1076
|
var NotEnoughTokensError = class extends PanopticError {
|
|
1077
1077
|
name = "NotEnoughTokensError";
|
|
1078
|
+
get deficit() {
|
|
1079
|
+
return this.assetsRequested > this.assetBalance ? this.assetsRequested - this.assetBalance : 0n;
|
|
1080
|
+
}
|
|
1078
1081
|
constructor(tokenAddress, assetsRequested, assetBalance, cause) {
|
|
1079
1082
|
super(`Not enough tokens: requested=${assetsRequested}, balance=${assetBalance}, token=${tokenAddress}`, cause);
|
|
1080
1083
|
this.tokenAddress = tokenAddress;
|
|
@@ -4764,6 +4767,39 @@ const panopticErrorsAbi = [
|
|
|
4764
4767
|
}
|
|
4765
4768
|
]
|
|
4766
4769
|
},
|
|
4770
|
+
{
|
|
4771
|
+
type: "error",
|
|
4772
|
+
name: "NotEnoughTokens",
|
|
4773
|
+
inputs: [
|
|
4774
|
+
{
|
|
4775
|
+
name: "tokenAddress",
|
|
4776
|
+
type: "address"
|
|
4777
|
+
},
|
|
4778
|
+
{
|
|
4779
|
+
name: "account",
|
|
4780
|
+
type: "address"
|
|
4781
|
+
},
|
|
4782
|
+
{
|
|
4783
|
+
name: "assetsRequested",
|
|
4784
|
+
type: "uint256"
|
|
4785
|
+
},
|
|
4786
|
+
{
|
|
4787
|
+
name: "assetBalance",
|
|
4788
|
+
type: "uint256"
|
|
4789
|
+
}
|
|
4790
|
+
]
|
|
4791
|
+
},
|
|
4792
|
+
{
|
|
4793
|
+
type: "error",
|
|
4794
|
+
name: "ExecuteError",
|
|
4795
|
+
inputs: [{
|
|
4796
|
+
name: "callIndex",
|
|
4797
|
+
type: "uint256"
|
|
4798
|
+
}, {
|
|
4799
|
+
name: "revertData",
|
|
4800
|
+
type: "bytes"
|
|
4801
|
+
}]
|
|
4802
|
+
},
|
|
4767
4803
|
{
|
|
4768
4804
|
type: "error",
|
|
4769
4805
|
name: "NotGuardian",
|
|
@@ -4905,7 +4941,7 @@ const errorConstructors = {
|
|
|
4905
4941
|
AccountInsolvent: (args, cause) => new AccountInsolventError(args[0], args[1], cause),
|
|
4906
4942
|
NotMarginCalled: (_args, cause) => new NotMarginCalledError(cause),
|
|
4907
4943
|
CreateFail: (_args, cause) => new CreateFailError(cause),
|
|
4908
|
-
NotEnoughTokens: (args, cause) => new NotEnoughTokensError(args[0], args[1], args[2], cause),
|
|
4944
|
+
NotEnoughTokens: (args, cause) => new NotEnoughTokensError(args[0], args[args.length === 4 ? 2 : 1], args[args.length === 4 ? 3 : 2], cause),
|
|
4909
4945
|
NotEnoughLiquidityInChunk: (_args, cause) => new NotEnoughLiquidityInChunkError(cause),
|
|
4910
4946
|
InsufficientCreditLiquidity: (_args, cause) => new InsufficientCreditLiquidityError(cause),
|
|
4911
4947
|
DepositTooLarge: (_args, cause) => new DepositTooLargeError(cause),
|
|
@@ -5295,7 +5331,7 @@ const MAX_UINT256 = (1n << 256n) - 1n;
|
|
|
5295
5331
|
|
|
5296
5332
|
//#endregion
|
|
5297
5333
|
//#region src/uniswap/lpDeposit.ts
|
|
5298
|
-
const Q96$
|
|
5334
|
+
const Q96$4 = 1n << 96n;
|
|
5299
5335
|
const ceilDiv = (n, d) => (n + d - 1n) / d;
|
|
5300
5336
|
/** Uniswap mint principal in raw token units, rounded up per leg, plus 5% headroom. */
|
|
5301
5337
|
function getLpDepositAmounts(ranges, sqrtPriceX96) {
|
|
@@ -5311,8 +5347,8 @@ function getLpDepositBreakdown(ranges, sqrtPriceX96) {
|
|
|
5311
5347
|
const lower = tickToSqrtPriceX96(BigInt(tickLower));
|
|
5312
5348
|
const upper = tickToSqrtPriceX96(BigInt(tickUpper));
|
|
5313
5349
|
const price = sqrtPriceX96 < lower ? lower : sqrtPriceX96 > upper ? upper : sqrtPriceX96;
|
|
5314
|
-
amount0 += ceilDiv(liquidity * Q96$
|
|
5315
|
-
amount1 += ceilDiv(liquidity * (price - lower), Q96$
|
|
5350
|
+
amount0 += ceilDiv(liquidity * Q96$4 * (upper - price), upper * price);
|
|
5351
|
+
amount1 += ceilDiv(liquidity * (price - lower), Q96$4);
|
|
5316
5352
|
}
|
|
5317
5353
|
const buffer = {
|
|
5318
5354
|
amount0: ceilDiv(amount0 * 5n, 100n),
|
|
@@ -5340,7 +5376,7 @@ function getUnhedgedLpRanges({ tokenId, positionSize, tickSpacing }) {
|
|
|
5340
5376
|
const lower = tickToSqrtPriceX96(tickLower);
|
|
5341
5377
|
const upper = tickToSqrtPriceX96(tickUpper);
|
|
5342
5378
|
const amount = positionSize * leg.optionRatio;
|
|
5343
|
-
const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96$
|
|
5379
|
+
const liquidity = leg.asset === 0n ? amount * (lower * upper / Q96$4) / (upper - lower) : amount * Q96$4 / (upper - lower);
|
|
5344
5380
|
return liquidity <= 0n ? [] : [{
|
|
5345
5381
|
tickLower: Number(tickLower),
|
|
5346
5382
|
tickUpper: Number(tickUpper),
|
|
@@ -5361,7 +5397,7 @@ function getLpPositionFunding(params) {
|
|
|
5361
5397
|
});
|
|
5362
5398
|
const breakdown = getLpDepositBreakdown(ranges, sqrtPriceX96);
|
|
5363
5399
|
const priceSquared = valuationSqrtPriceX96 * valuationSqrtPriceX96;
|
|
5364
|
-
const value = ({ amount0, amount1 }) => quoteTokenIndex === 0 ? amount0 + ceilDiv(amount1 * Q96$
|
|
5400
|
+
const value = ({ amount0, amount1 }) => quoteTokenIndex === 0 ? amount0 + ceilDiv(amount1 * Q96$4 * Q96$4, priceSquared) : amount1 + ceilDiv(amount0 * priceSquared, Q96$4 * Q96$4);
|
|
5365
5401
|
const principalInQuote = value(breakdown.principal);
|
|
5366
5402
|
const totalInQuote = value(breakdown.total);
|
|
5367
5403
|
return {
|
|
@@ -5391,8 +5427,8 @@ function getLpFundingDeposit({ funding, availableInQuote, minimumInQuote = 0n, q
|
|
|
5391
5427
|
requiredInQuote,
|
|
5392
5428
|
shortfallInQuote,
|
|
5393
5429
|
defaultQuotePercent,
|
|
5394
|
-
amount0: quoteTokenIndex === 0 ? quoteAmount : ceilDiv(assetValue * Q96$
|
|
5395
|
-
amount1: quoteTokenIndex === 1 ? quoteAmount : ceilDiv(assetValue * priceSquared, Q96$
|
|
5430
|
+
amount0: quoteTokenIndex === 0 ? quoteAmount : ceilDiv(assetValue * Q96$4 * Q96$4, priceSquared),
|
|
5431
|
+
amount1: quoteTokenIndex === 1 ? quoteAmount : ceilDiv(assetValue * priceSquared, Q96$4 * Q96$4)
|
|
5396
5432
|
};
|
|
5397
5433
|
}
|
|
5398
5434
|
/** Largest raw size funded by available collateral, including native-token rounding. */
|
|
@@ -5595,6 +5631,10 @@ function summarizeLpPositionHistory(events) {
|
|
|
5595
5631
|
};
|
|
5596
5632
|
}
|
|
5597
5633
|
|
|
5634
|
+
//#endregion
|
|
5635
|
+
//#region src/panoptic/v2/greeks/legLiquidity.ts
|
|
5636
|
+
const Q96$3 = 1n << 96n;
|
|
5637
|
+
|
|
5598
5638
|
//#endregion
|
|
5599
5639
|
//#region src/panoptic/v2/greeks/positionVolatility.ts
|
|
5600
5640
|
const Precision$1 = Decimal.clone({ precision: 80 });
|