@unlink-xyz/core 0.1.5 → 0.1.6

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../clients/http.ts","../../utils/async.ts"],"sourcesContent":["import ky, { TimeoutError } from \"ky\";\n\nimport { InitializationError } from \"../errors.js\";\n\nexport type FetchLike = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\n/**\n * Resolve fetch implementation from options or global.\n * @param providedFetch - Optional fetch implementation\n * @returns Fetch function or undefined if not available\n */\nexport function resolveFetch(\n providedFetch?: FetchLike,\n): typeof fetch | undefined {\n return providedFetch ?? (typeof fetch === \"function\" ? fetch : undefined);\n}\n\nexport type JsonHttpDeps = { fetch: FetchLike };\n\nexport type JsonRequestOptions = {\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n query?: Record<string, string | number | boolean | undefined>;\n json?: unknown;\n body?: BodyInit;\n headers?: HeadersInit;\n signal?: AbortSignal;\n};\n\nexport type JsonHttpClient = {\n request<T>(opts: JsonRequestOptions): Promise<T>;\n};\n\nexport class HttpError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n\nasync function readErrorBodySafe(res: Response) {\n try {\n return await res.clone().json();\n } catch {\n const text = await res.text();\n return text || null;\n }\n}\n\nexport function createJsonHttpClient(\n baseUrl: string,\n deps: JsonHttpDeps,\n): JsonHttpClient {\n if (!baseUrl) throw new InitializationError(\"baseUrl is required\");\n if (!deps?.fetch) throw new InitializationError(\"deps.fetch is required\");\n\n // Ensure `fetch` is invoked with a global `this` to avoid \"Illegal invocation\"\n // errors in some browser environments when the function is extracted. // TODO: can we fix this in another way?\n const fetchImpl: FetchLike = (...args) => deps.fetch.apply(globalThis, args);\n\n const api = ky.create({\n prefixUrl: baseUrl.replace(/\\/+$/, \"\"),\n fetch: fetchImpl,\n // Disable ky's automatic error throwing to prevent browser DevTools\n // from logging expected 404s as network errors\n throwHttpErrors: false,\n });\n\n return {\n async request<T>(opts: JsonRequestOptions): Promise<T> {\n let res: Response;\n try {\n res = await api(opts.path.replace(/^\\//, \"\"), {\n method: opts.method,\n searchParams: opts.query,\n json: opts.json,\n body: opts.body,\n headers: opts.headers,\n signal: opts.signal,\n });\n } catch (err) {\n // Only network-level errors reach here (timeout, connection refused, etc.)\n if (err instanceof TimeoutError) {\n throw new HttpError(\"HTTP timeout\", 408, null);\n }\n throw new HttpError(\n err instanceof Error ? err.message : \"Network error\",\n 0,\n null,\n );\n }\n\n // HTTP status errors handled outside try-catch - no redundant catch/rethrow\n if (!res.ok) {\n const body = await readErrorBodySafe(res);\n throw new HttpError(\n `HTTP ${res.status} ${res.statusText}`.trim(),\n res.status,\n body,\n );\n }\n\n return (await res.json()) as T;\n },\n };\n}\n","import { HttpError } from \"../clients/http.js\";\n\n/**\n * Promise-based delay utility.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Type guard for HTTP 404 errors.\n */\nexport function isNotFoundError(err: unknown): err is HttpError {\n return err instanceof HttpError && err.status === 404;\n}\n\n/**\n * Race a promise against a timeout, rejecting if the timeout fires first.\n * Properly cleans up the timeout to avoid memory leaks.\n *\n * @param promise - The promise to race against the timeout\n * @param ms - Timeout in milliseconds\n * @param error - Error to throw on timeout (string message or Error instance)\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n error?: string | Error,\n): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n return Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(\n error instanceof Error\n ? error\n : new Error(error ?? `Operation timed out after ${ms}ms`),\n );\n }, ms);\n }),\n ]).finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n });\n}\n"],"mappings":";AAAA,OAAO,MAAM,oBAAoB;AAoC1B,IAAM,YAAN,cAAwB,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,QAAgB,MAAe;AAC1D,UAAM,OAAO;AACb,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;;;ACxCO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAKO,SAAS,gBAAgB,KAAgC;AAC9D,SAAO,eAAe,aAAa,IAAI,WAAW;AACpD;AAUA,eAAsB,YACpB,SACA,IACA,OACY;AACZ,MAAI;AACJ,SAAO,QAAQ,KAAK;AAAA,IAClB;AAAA,IACA,IAAI,QAAe,CAAC,GAAG,WAAW;AAChC,kBAAY,WAAW,MAAM;AAC3B;AAAA,UACE,iBAAiB,QACb,QACA,IAAI,MAAM,SAAS,6BAA6B,EAAE,IAAI;AAAA,QAC5D;AAAA,MACF,GAAG,EAAE;AAAA,IACP,CAAC;AAAA,EACH,CAAC,EAAE,QAAQ,MAAM;AACf,QAAI,cAAc,OAAW,cAAa,SAAS;AAAA,EACrD,CAAC;AACH;","names":[]}
1
+ {"version":3,"sources":["../../clients/http.ts","../../utils/async.ts"],"sourcesContent":["import ky, { TimeoutError } from \"ky\";\n\nimport { InitializationError } from \"../errors.js\";\n\nexport type FetchLike = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\n/**\n * Resolve fetch implementation from options or global.\n * @param providedFetch - Optional fetch implementation\n * @returns Fetch function or undefined if not available\n */\nexport function resolveFetch(\n providedFetch?: FetchLike,\n): typeof fetch | undefined {\n return providedFetch ?? (typeof fetch === \"function\" ? fetch : undefined);\n}\n\nexport type JsonHttpDeps = { fetch: FetchLike };\n\nexport type JsonRequestOptions = {\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n query?: Record<string, string | number | boolean | undefined>;\n json?: unknown;\n body?: BodyInit;\n headers?: HeadersInit;\n signal?: AbortSignal;\n};\n\nexport type JsonHttpClient = {\n request<T>(opts: JsonRequestOptions): Promise<T>;\n};\n\nexport class HttpError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.status = status;\n this.body = body;\n }\n}\n\nasync function readErrorBodySafe(res: Response) {\n try {\n return await res.clone().json();\n } catch {\n const text = await res.text();\n return text || null;\n }\n}\n\nexport function createJsonHttpClient(\n baseUrl: string,\n deps: JsonHttpDeps,\n): JsonHttpClient {\n if (!baseUrl) throw new InitializationError(\"baseUrl is required\");\n if (!deps?.fetch) throw new InitializationError(\"deps.fetch is required\");\n\n // Ensure `fetch` is invoked with a global `this` to avoid \"Illegal invocation\"\n // errors in some browser environments when the function is extracted. // TODO: can we fix this in another way?\n const fetchImpl: FetchLike = (...args) => deps.fetch.apply(globalThis, args);\n\n const api = ky.create({\n prefixUrl: baseUrl.replace(/\\/+$/, \"\"),\n fetch: fetchImpl,\n // Disable ky's automatic error throwing to prevent browser DevTools\n // from logging expected 404s as network errors\n throwHttpErrors: false,\n retry: 0,\n });\n\n const RETRYABLE_STATUSES = [502, 503, 504];\n const MAX_RETRIES = 3;\n const BASE_DELAY_MS = 500;\n\n return {\n async request<T>(opts: JsonRequestOptions): Promise<T> {\n let res!: Response;\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {\n try {\n res = await api(opts.path.replace(/^\\//, \"\"), {\n method: opts.method,\n searchParams: opts.query,\n json: opts.json,\n body: opts.body,\n headers: opts.headers,\n signal: opts.signal,\n });\n } catch (err) {\n // Only network-level errors reach here (timeout, connection refused, etc.)\n if (err instanceof TimeoutError) {\n throw new HttpError(\"HTTP timeout\", 408, null);\n }\n throw new HttpError(\n err instanceof Error ? err.message : \"Network error\",\n 0,\n null,\n );\n }\n\n if (RETRYABLE_STATUSES.includes(res.status) && attempt < MAX_RETRIES) {\n const delay = BASE_DELAY_MS * 2 ** attempt + Math.random() * 200;\n await new Promise((r) => setTimeout(r, delay));\n continue;\n }\n break;\n }\n\n // HTTP status errors handled outside try-catch - no redundant catch/rethrow\n if (!res.ok) {\n const body = await readErrorBodySafe(res);\n throw new HttpError(\n `HTTP ${res.status} ${res.statusText}`.trim(),\n res.status,\n body,\n );\n }\n\n return (await res.json()) as T;\n },\n };\n}\n","import { HttpError } from \"../clients/http.js\";\n\n/**\n * Promise-based delay utility.\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Type guard for HTTP 404 errors.\n */\nexport function isNotFoundError(err: unknown): err is HttpError {\n return err instanceof HttpError && err.status === 404;\n}\n\n/**\n * Race a promise against a timeout, rejecting if the timeout fires first.\n * Properly cleans up the timeout to avoid memory leaks.\n *\n * @param promise - The promise to race against the timeout\n * @param ms - Timeout in milliseconds\n * @param error - Error to throw on timeout (string message or Error instance)\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n error?: string | Error,\n): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n return Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(\n error instanceof Error\n ? error\n : new Error(error ?? `Operation timed out after ${ms}ms`),\n );\n }, ms);\n }),\n ]).finally(() => {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n });\n}\n"],"mappings":";AAAA,OAAO,MAAM,oBAAoB;AAoC1B,IAAM,YAAN,cAAwB,MAAM;AAAA,EAC1B;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,QAAgB,MAAe;AAC1D,UAAM,OAAO;AACb,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;;;ACxCO,SAAS,MAAM,IAA2B;AAC/C,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAKO,SAAS,gBAAgB,KAAgC;AAC9D,SAAO,eAAe,aAAa,IAAI,WAAW;AACpD;AAUA,eAAsB,YACpB,SACA,IACA,OACY;AACZ,MAAI;AACJ,SAAO,QAAQ,KAAK;AAAA,IAClB;AAAA,IACA,IAAI,QAAe,CAAC,GAAG,WAAW;AAChC,kBAAY,WAAW,MAAM;AAC3B;AAAA,UACE,iBAAiB,QACb,QACA,IAAI,MAAM,SAAS,6BAA6B,EAAE,IAAI;AAAA,QAC5D;AAAA,MACF,GAAG,EAAE;AAAA,IACP,CAAC;AAAA,EACH,CAAC,EAAE,QAAQ,MAAM;AACf,QAAI,cAAc,OAAW,cAAa,SAAS;AAAA,EACrD,CAAC;AACH;","names":[]}
@@ -31,7 +31,7 @@ export type ParsedZkAddress = {
31
31
  chain?: Chain;
32
32
  };
33
33
  /**
34
- * Parses a Bech32m ZK address (0zk1...) into its components.
34
+ * Parses a Bech32m ZK address (unlink1...) into its components.
35
35
  *
36
36
  * @param value - The Bech32m address string
37
37
  * @returns Parsed address with masterPublicKey, viewingPublicKey, and optional chain
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../wallet/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE1E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzE,OAAO,EAEL,QAAQ,EAGT,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EACV,cAAc,EAEd,kBAAkB,EACnB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAOxE,OAAO,KAAK,EAKV,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAKpB,KAAK,iBAAiB,GAAG,IAAI,CAC3B,UAAU,CAAC,OAAO,uBAAuB,CAAC,EAC1C,aAAa,CACd,CAAC;AAOF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,oBAAoB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,aAAa,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC;IAChD,iBAAiB,CAAC,EAAE,OAAO,eAAe,CAAC;IAC3C,UAAU,CAAC,EAAE,OAAO,QAAQ,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB,CAAC;AAmEF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,kBAAkB,GACvB,gBAAgB,CAuMlB"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../wallet/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAE1E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAKzE,OAAO,EAEL,QAAQ,EAGT,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EACV,cAAc,EAEd,kBAAkB,EACnB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAOxE,OAAO,KAAK,EAKV,gBAAgB,EAEjB,MAAM,YAAY,CAAC;AAKpB,KAAK,iBAAiB,GAAG,IAAI,CAC3B,UAAU,CAAC,OAAO,uBAAuB,CAAC,EAC1C,aAAa,CACd,CAAC;AAOF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,oBAAoB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,aAAa,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC;IAChD,iBAAiB,CAAC,EAAE,OAAO,eAAe,CAAC;IAC3C,UAAU,CAAC,EAAE,OAAO,QAAQ,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB,CAAC;AAmEF,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,kBAAkB,GACvB,gBAAgB,CAkNlB"}
@@ -1827,63 +1827,85 @@ var Runtime = {
1827
1827
 
1828
1828
  // config.ts
1829
1829
  var CONFIG_URL = "https://config.unlink.xyz/networks.json";
1830
- function parseRequiredString(env, field, value) {
1830
+ function parseRequiredString(chain, field, value) {
1831
1831
  if (typeof value !== "string" || value.trim().length === 0) {
1832
1832
  throw new InitializationError(
1833
- `Invalid SDK config for ${env}: ${field} must be a non-empty string`
1833
+ `Invalid SDK config for ${chain}: ${field} must be a non-empty string`
1834
1834
  );
1835
1835
  }
1836
1836
  return value.trim();
1837
1837
  }
1838
- function parseOptionalString(env, field, value) {
1838
+ function parseOptionalString(chain, field, value) {
1839
1839
  if (value === void 0) return void 0;
1840
1840
  if (typeof value !== "string" || value.trim().length === 0) {
1841
1841
  throw new InitializationError(
1842
- `Invalid SDK config for ${env}: ${field} must be a non-empty string when provided`
1842
+ `Invalid SDK config for ${chain}: ${field} must be a non-empty string when provided`
1843
1843
  );
1844
1844
  }
1845
1845
  return value.trim();
1846
1846
  }
1847
- function parseEnvironmentConfig(env, value) {
1847
+ function parseRequiredChainId(chain, value) {
1848
+ if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
1849
+ throw new InitializationError(
1850
+ `Invalid SDK config for ${chain}: chainId must be a positive integer`
1851
+ );
1852
+ }
1853
+ return value;
1854
+ }
1855
+ function parseChainConfig(chain, value) {
1848
1856
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
1849
1857
  throw new InitializationError(
1850
- `Invalid SDK config for ${env}: expected object`
1858
+ `Invalid SDK config for ${chain}: expected object`
1851
1859
  );
1852
1860
  }
1853
1861
  const raw = value;
1862
+ const chainId = parseRequiredChainId(chain, raw.chainId);
1854
1863
  const gatewayUrl = parseRequiredString(
1855
- env,
1864
+ chain,
1856
1865
  "gatewayUrl",
1857
1866
  raw.gatewayUrl
1858
1867
  ).replace(/\/+$/, "");
1859
- const poolAddress = parseRequiredString(env, "poolAddress", raw.poolAddress);
1868
+ const poolAddress = parseRequiredString(
1869
+ chain,
1870
+ "poolAddress",
1871
+ raw.poolAddress
1872
+ );
1860
1873
  const artifactVersion = parseRequiredString(
1861
- env,
1874
+ chain,
1862
1875
  "artifactVersion",
1863
1876
  raw.artifactVersion
1864
1877
  ).replace(/^\/+|\/+$/g, "");
1878
+ const frostUrl = parseOptionalString(
1879
+ chain,
1880
+ "frostUrl",
1881
+ raw.frostUrl
1882
+ )?.replace(/\/+$/, "");
1865
1883
  const artifactBaseUrl = parseOptionalString(
1866
- env,
1884
+ chain,
1867
1885
  "artifactBaseUrl",
1868
1886
  raw.artifactBaseUrl
1869
1887
  )?.replace(/\/+$/, "");
1870
1888
  return {
1889
+ chainId,
1871
1890
  gatewayUrl,
1891
+ ...frostUrl !== void 0 ? { frostUrl } : {},
1872
1892
  poolAddress,
1873
1893
  artifactVersion,
1874
1894
  ...artifactBaseUrl !== void 0 ? { artifactBaseUrl } : { artifactBaseUrl: DEFAULT_ARTIFACT_BASE_URL }
1875
1895
  };
1876
1896
  }
1877
- async function fetchEnvironmentConfig(env) {
1897
+ async function fetchChainConfig(chain) {
1878
1898
  const res = await fetch(CONFIG_URL);
1879
1899
  if (!res.ok) {
1880
1900
  throw new InitializationError(`Failed to fetch SDK config: ${res.status}`);
1881
1901
  }
1882
1902
  const config = await res.json();
1883
- if (!config[env]) {
1884
- throw new InitializationError(`Unknown environment: ${env}`);
1903
+ if (!config[chain]) {
1904
+ throw new InitializationError(
1905
+ `Unknown chain: "${chain}". Supported chains: ${Object.keys(config).join(", ")}`
1906
+ );
1885
1907
  }
1886
- return parseEnvironmentConfig(env, config[env]);
1908
+ return parseChainConfig(chain, config[chain]);
1887
1909
  }
1888
1910
  function createServiceConfig(gatewayUrl) {
1889
1911
  const baseUrl = gatewayUrl.replace(/\/+$/, "");
@@ -2220,9 +2242,9 @@ var Hex = class _Hex {
2220
2242
 
2221
2243
  // keys/address.ts
2222
2244
  var VERSION = 1;
2223
- var LIMIT = 127;
2245
+ var LIMIT = 130;
2224
2246
  var ALL_CHAINS = "ffffffffffffffff";
2225
- var PREFIX = "0zk";
2247
+ var PREFIX = "unlink";
2226
2248
  var SALT = new TextEncoder().encode("unlink");
2227
2249
  function xorWithSalt(hex) {
2228
2250
  const bytes = Hex.toBytes(hex);
@@ -3970,29 +3992,41 @@ function createJsonHttpClient(baseUrl, deps) {
3970
3992
  fetch: fetchImpl,
3971
3993
  // Disable ky's automatic error throwing to prevent browser DevTools
3972
3994
  // from logging expected 404s as network errors
3973
- throwHttpErrors: false
3995
+ throwHttpErrors: false,
3996
+ retry: 0
3974
3997
  });
3998
+ const RETRYABLE_STATUSES = [502, 503, 504];
3999
+ const MAX_RETRIES = 3;
4000
+ const BASE_DELAY_MS = 500;
3975
4001
  return {
3976
4002
  async request(opts) {
3977
4003
  let res;
3978
- try {
3979
- res = await api(opts.path.replace(/^\//, ""), {
3980
- method: opts.method,
3981
- searchParams: opts.query,
3982
- json: opts.json,
3983
- body: opts.body,
3984
- headers: opts.headers,
3985
- signal: opts.signal
3986
- });
3987
- } catch (err) {
3988
- if (err instanceof TimeoutError) {
3989
- throw new HttpError("HTTP timeout", 408, null);
4004
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
4005
+ try {
4006
+ res = await api(opts.path.replace(/^\//, ""), {
4007
+ method: opts.method,
4008
+ searchParams: opts.query,
4009
+ json: opts.json,
4010
+ body: opts.body,
4011
+ headers: opts.headers,
4012
+ signal: opts.signal
4013
+ });
4014
+ } catch (err) {
4015
+ if (err instanceof TimeoutError) {
4016
+ throw new HttpError("HTTP timeout", 408, null);
4017
+ }
4018
+ throw new HttpError(
4019
+ err instanceof Error ? err.message : "Network error",
4020
+ 0,
4021
+ null
4022
+ );
3990
4023
  }
3991
- throw new HttpError(
3992
- err instanceof Error ? err.message : "Network error",
3993
- 0,
3994
- null
3995
- );
4024
+ if (RETRYABLE_STATUSES.includes(res.status) && attempt < MAX_RETRIES) {
4025
+ const delay = BASE_DELAY_MS * 2 ** attempt + Math.random() * 200;
4026
+ await new Promise((r2) => setTimeout(r2, delay));
4027
+ continue;
4028
+ }
4029
+ break;
3996
4030
  }
3997
4031
  if (!res.ok) {
3998
4032
  const body = await readErrorBodySafe(res);
@@ -4210,7 +4244,7 @@ function parseZkAddress(value) {
4210
4244
  };
4211
4245
  } catch (err) {
4212
4246
  throw new ValidationError(
4213
- `Invalid ZK address (expected 0zk1... format): ${err instanceof Error ? err.message : "unknown error"}`
4247
+ `Invalid ZK address (expected unlink1... format): ${err instanceof Error ? err.message : "unknown error"}`
4214
4248
  );
4215
4249
  }
4216
4250
  }
@@ -6206,6 +6240,114 @@ var circuits_default = {
6206
6240
  template: "JoinSplit",
6207
6241
  pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6208
6242
  params: [5, 2, 16]
6243
+ },
6244
+ joinsplit_1x3_16: {
6245
+ file: "joinsplit",
6246
+ template: "JoinSplit",
6247
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6248
+ params: [1, 3, 16]
6249
+ },
6250
+ joinsplit_4x3_16: {
6251
+ file: "joinsplit",
6252
+ template: "JoinSplit",
6253
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6254
+ params: [4, 3, 16]
6255
+ },
6256
+ joinsplit_5x3_16: {
6257
+ file: "joinsplit",
6258
+ template: "JoinSplit",
6259
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6260
+ params: [5, 3, 16]
6261
+ },
6262
+ joinsplit_6x1_16: {
6263
+ file: "joinsplit",
6264
+ template: "JoinSplit",
6265
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6266
+ params: [6, 1, 16]
6267
+ },
6268
+ joinsplit_6x2_16: {
6269
+ file: "joinsplit",
6270
+ template: "JoinSplit",
6271
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6272
+ params: [6, 2, 16]
6273
+ },
6274
+ joinsplit_6x3_16: {
6275
+ file: "joinsplit",
6276
+ template: "JoinSplit",
6277
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6278
+ params: [6, 3, 16]
6279
+ },
6280
+ joinsplit_7x1_16: {
6281
+ file: "joinsplit",
6282
+ template: "JoinSplit",
6283
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6284
+ params: [7, 1, 16]
6285
+ },
6286
+ joinsplit_7x2_16: {
6287
+ file: "joinsplit",
6288
+ template: "JoinSplit",
6289
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6290
+ params: [7, 2, 16]
6291
+ },
6292
+ joinsplit_7x3_16: {
6293
+ file: "joinsplit",
6294
+ template: "JoinSplit",
6295
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6296
+ params: [7, 3, 16]
6297
+ },
6298
+ joinsplit_8x1_16: {
6299
+ file: "joinsplit",
6300
+ template: "JoinSplit",
6301
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6302
+ params: [8, 1, 16]
6303
+ },
6304
+ joinsplit_8x2_16: {
6305
+ file: "joinsplit",
6306
+ template: "JoinSplit",
6307
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6308
+ params: [8, 2, 16]
6309
+ },
6310
+ joinsplit_8x3_16: {
6311
+ file: "joinsplit",
6312
+ template: "JoinSplit",
6313
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6314
+ params: [8, 3, 16]
6315
+ },
6316
+ joinsplit_9x1_16: {
6317
+ file: "joinsplit",
6318
+ template: "JoinSplit",
6319
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6320
+ params: [9, 1, 16]
6321
+ },
6322
+ joinsplit_9x2_16: {
6323
+ file: "joinsplit",
6324
+ template: "JoinSplit",
6325
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6326
+ params: [9, 2, 16]
6327
+ },
6328
+ joinsplit_9x3_16: {
6329
+ file: "joinsplit",
6330
+ template: "JoinSplit",
6331
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6332
+ params: [9, 3, 16]
6333
+ },
6334
+ joinsplit_10x1_16: {
6335
+ file: "joinsplit",
6336
+ template: "JoinSplit",
6337
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6338
+ params: [10, 1, 16]
6339
+ },
6340
+ joinsplit_10x2_16: {
6341
+ file: "joinsplit",
6342
+ template: "JoinSplit",
6343
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6344
+ params: [10, 2, 16]
6345
+ },
6346
+ joinsplit_10x3_16: {
6347
+ file: "joinsplit",
6348
+ template: "JoinSplit",
6349
+ pubs: ["merkleRoot", "boundParamsHash", "nullifiers", "commitmentsOut"],
6350
+ params: [10, 3, 16]
6209
6351
  }
6210
6352
  };
6211
6353
 
@@ -6225,7 +6367,7 @@ function getCircuitConfig(inputs, outputs) {
6225
6367
  }
6226
6368
 
6227
6369
  // prover/prover.ts
6228
- var MAX_ARTIFACT_CACHE_ENTRIES = 8;
6370
+ var MAX_ARTIFACT_CACHE_ENTRIES = 16;
6229
6371
  var artifactCache = /* @__PURE__ */ new Map();
6230
6372
  function selectCircuit(inputs, outputs) {
6231
6373
  const config = getCircuitConfig(inputs, outputs);
@@ -8273,6 +8415,16 @@ function createAdapterService(deps) {
8273
8415
  const reshieldSpecs = params.reshields.map(
8274
8416
  (reshield, i) => normalizeReshieldSpec(reshield, i)
8275
8417
  );
8418
+ const seenReshieldTokens = /* @__PURE__ */ new Set();
8419
+ for (const r2 of reshieldSpecs) {
8420
+ const lower = r2.token.toLowerCase();
8421
+ if (seenReshieldTokens.has(lower)) {
8422
+ throw new AdapterError(
8423
+ `duplicate reshield token ${r2.token}; each reshield must target a unique token`
8424
+ );
8425
+ }
8426
+ seenReshieldTokens.add(lower);
8427
+ }
8276
8428
  const account = overrides?.account ?? await deps.requireActiveAccount();
8277
8429
  const signer = overrides?.signer ?? deps.requireSigner(account);
8278
8430
  const nowSeconds = BigInt(Math.floor(nowImpl() / 1e3));
@@ -9163,28 +9315,31 @@ function createWalletSDK(deps, options) {
9163
9315
  return sdk;
9164
9316
  }
9165
9317
  async function createBrowserWalletSDK(options) {
9318
+ let chainId;
9166
9319
  let gatewayUrl;
9167
9320
  let poolAddress;
9168
9321
  let prover = options.prover;
9169
- if ("gatewayUrl" in options) {
9322
+ if ("chain" in options) {
9323
+ const chainConfig = await fetchChainConfig(options.chain);
9324
+ chainId = chainConfig.chainId;
9325
+ gatewayUrl = chainConfig.gatewayUrl;
9326
+ poolAddress = options.poolAddress ?? chainConfig.poolAddress;
9327
+ prover = {
9328
+ artifactSource: {
9329
+ baseUrl: options.prover?.artifactSource?.baseUrl ?? chainConfig.artifactBaseUrl,
9330
+ version: options.prover?.artifactSource?.version ?? chainConfig.artifactVersion,
9331
+ preferLocalFiles: options.prover?.artifactSource?.preferLocalFiles
9332
+ }
9333
+ };
9334
+ } else {
9335
+ chainId = options.chainId;
9170
9336
  gatewayUrl = options.gatewayUrl;
9171
9337
  poolAddress = options.poolAddress;
9172
9338
  if (typeof window !== "undefined" && !options.prover?.artifactSource?.version) {
9173
9339
  throw new InitializationError(
9174
- "prover.artifactSource.version is required in browser when using explicit gatewayUrl mode. Use environment mode or provide a pinned artifact version."
9340
+ "prover.artifactSource.version is required in browser when using explicit gatewayUrl mode. Use chain mode or provide a pinned artifact version."
9175
9341
  );
9176
9342
  }
9177
- } else {
9178
- const envConfig = await fetchEnvironmentConfig(options.environment);
9179
- gatewayUrl = envConfig.gatewayUrl;
9180
- poolAddress = options.poolAddress ?? envConfig.poolAddress;
9181
- prover = {
9182
- artifactSource: {
9183
- baseUrl: options.prover?.artifactSource?.baseUrl ?? envConfig.artifactBaseUrl,
9184
- version: options.prover?.artifactSource?.version ?? envConfig.artifactVersion,
9185
- preferLocalFiles: options.prover?.artifactSource?.preferLocalFiles
9186
- }
9187
- };
9188
9343
  }
9189
9344
  const storage = createIndexedDbStorage({ name: "unlink-wallet" });
9190
9345
  const rng = (n) => {
@@ -9201,7 +9356,7 @@ async function createBrowserWalletSDK(options) {
9201
9356
  core,
9202
9357
  fetch: globalThis.fetch
9203
9358
  },
9204
- { chainId: options.chainId, gatewayUrl, prover }
9359
+ { chainId, gatewayUrl, prover }
9205
9360
  );
9206
9361
  return {
9207
9362
  sdk,
@@ -9231,34 +9386,37 @@ var UnlinkWallet = class _UnlinkWallet {
9231
9386
  * Create a new UnlinkWallet instance.
9232
9387
  *
9233
9388
  * Handles all initialization internally:
9234
- * - Resolves environment config (if using `environment` instead of explicit URLs)
9389
+ * - Resolves chain config (if using `chain` instead of explicit URLs)
9235
9390
  * - Auto-detects storage (IndexedDB in browser) and rng (crypto.getRandomValues)
9236
9391
  * - Runs schema migration via `initCore()`
9237
9392
  * - Creates the internal SDK
9238
9393
  */
9239
9394
  static async create(config) {
9395
+ let chainId;
9240
9396
  let gatewayUrl;
9241
9397
  let poolAddress;
9242
9398
  let proverConfig = config.prover;
9243
- if ("gatewayUrl" in config) {
9399
+ if ("chain" in config) {
9400
+ const chainConfig = await fetchChainConfig(config.chain);
9401
+ chainId = chainConfig.chainId;
9402
+ gatewayUrl = chainConfig.gatewayUrl;
9403
+ poolAddress = config.poolAddress ?? chainConfig.poolAddress;
9404
+ proverConfig = {
9405
+ artifactSource: {
9406
+ baseUrl: config.prover?.artifactSource?.baseUrl ?? chainConfig.artifactBaseUrl,
9407
+ version: config.prover?.artifactSource?.version ?? chainConfig.artifactVersion,
9408
+ preferLocalFiles: config.prover?.artifactSource?.preferLocalFiles
9409
+ }
9410
+ };
9411
+ } else {
9412
+ chainId = config.chainId;
9244
9413
  gatewayUrl = config.gatewayUrl;
9245
9414
  poolAddress = config.poolAddress;
9246
9415
  if (typeof window !== "undefined" && !config.prover?.artifactSource?.version) {
9247
9416
  throw new InitializationError(
9248
- "prover.artifactSource.version is required in browser when using explicit gatewayUrl mode. Use environment mode or provide a pinned artifact version."
9417
+ "prover.artifactSource.version is required in browser when using explicit gatewayUrl mode. Use chain mode or provide a pinned artifact version."
9249
9418
  );
9250
9419
  }
9251
- } else {
9252
- const envConfig = await fetchEnvironmentConfig(config.environment);
9253
- gatewayUrl = envConfig.gatewayUrl;
9254
- poolAddress = config.poolAddress ?? envConfig.poolAddress;
9255
- proverConfig = {
9256
- artifactSource: {
9257
- baseUrl: config.prover?.artifactSource?.baseUrl ?? envConfig.artifactBaseUrl,
9258
- version: config.prover?.artifactSource?.version ?? envConfig.artifactVersion,
9259
- preferLocalFiles: config.prover?.artifactSource?.preferLocalFiles
9260
- }
9261
- };
9262
9420
  }
9263
9421
  const storage = config.storage ?? detectStorage();
9264
9422
  const rng = config.rng ?? defaultRng;
@@ -9267,14 +9425,14 @@ var UnlinkWallet = class _UnlinkWallet {
9267
9425
  const sdk = createWalletSDK(
9268
9426
  { core, fetch: fetchImpl },
9269
9427
  {
9270
- chainId: config.chainId,
9428
+ chainId,
9271
9429
  gatewayUrl,
9272
9430
  chainRpcUrl: config.chainRpcUrl,
9273
9431
  prover: proverConfig,
9274
9432
  autoSync: config.autoSync
9275
9433
  }
9276
9434
  );
9277
- return new _UnlinkWallet(sdk, config.chainId, poolAddress);
9435
+ return new _UnlinkWallet(sdk, chainId, poolAddress);
9278
9436
  }
9279
9437
  // ===== Seed Lifecycle =====
9280
9438
  /** Seed management (create, import, export, delete mnemonic). */