@zerc20/wallet-sdk 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +26 -2
  3. package/dist/burnWorker.d.ts +1 -0
  4. package/dist/burnWorker.js +21 -0
  5. package/dist/burnWorker.js.map +1 -0
  6. package/dist/chain.d.ts +359 -0
  7. package/dist/chain.js +155 -0
  8. package/dist/chain.js.map +1 -0
  9. package/dist/context.d.ts +48 -0
  10. package/dist/context.js +40 -0
  11. package/dist/context.js.map +1 -0
  12. package/dist/core.d.ts +43 -0
  13. package/dist/core.js +106 -0
  14. package/dist/core.js.map +1 -0
  15. package/dist/errors.d.ts +38 -0
  16. package/dist/errors.js +26 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +6 -0
  19. package/dist/index.js +19 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/network.d.ts +66 -0
  22. package/dist/network.js +55 -0
  23. package/dist/network.js.map +1 -0
  24. package/dist/pos.d.ts +93 -0
  25. package/dist/pos.js +302 -0
  26. package/dist/pos.js.map +1 -0
  27. package/dist/prove.d.ts +13 -0
  28. package/dist/prove.js +53 -0
  29. package/dist/prove.js.map +1 -0
  30. package/dist/rageExit.d.ts +5 -0
  31. package/dist/rageExit.js +146 -0
  32. package/dist/rageExit.js.map +1 -0
  33. package/dist/scan.d.ts +17 -0
  34. package/dist/scan.js +47 -0
  35. package/dist/scan.js.map +1 -0
  36. package/dist/seed.d.ts +8 -0
  37. package/dist/seed.js +35 -0
  38. package/dist/seed.js.map +1 -0
  39. package/dist/servers.d.ts +60 -0
  40. package/dist/servers.js +117 -0
  41. package/dist/servers.js.map +1 -0
  42. package/dist/services.d.ts +62 -0
  43. package/dist/services.js +131 -0
  44. package/dist/services.js.map +1 -0
  45. package/dist/spend.d.ts +151 -0
  46. package/dist/spend.js +745 -0
  47. package/dist/spend.js.map +1 -0
  48. package/dist/state.d.ts +174 -0
  49. package/dist/state.js +45 -0
  50. package/dist/state.js.map +1 -0
  51. package/dist/sync.d.ts +84 -0
  52. package/dist/sync.js +615 -0
  53. package/dist/sync.js.map +1 -0
  54. package/dist/tree.d.ts +19 -0
  55. package/dist/tree.js +63 -0
  56. package/dist/tree.js.map +1 -0
  57. package/dist/wallet.d.ts +303 -0
  58. package/dist/wallet.js +1002 -0
  59. package/dist/wallet.js.map +1 -0
  60. package/package.json +60 -4
  61. package/src/burnWorker.ts +25 -0
  62. package/src/chain.ts +228 -0
  63. package/src/context.ts +84 -0
  64. package/src/core.ts +147 -0
  65. package/src/errors.ts +63 -0
  66. package/src/index.ts +41 -0
  67. package/src/network.ts +94 -0
  68. package/src/pos.ts +415 -0
  69. package/src/prove.ts +67 -0
  70. package/src/rageExit.ts +168 -0
  71. package/src/scan.ts +62 -0
  72. package/src/seed.ts +46 -0
  73. package/src/servers.ts +172 -0
  74. package/src/services.ts +195 -0
  75. package/src/snarkjs.d.ts +11 -0
  76. package/src/spend.ts +912 -0
  77. package/src/state.ts +211 -0
  78. package/src/sync.ts +698 -0
  79. package/src/tree.ts +108 -0
  80. package/src/wallet.ts +1316 -0
  81. package/wasm/zerc20_wallet_core.d.ts +176 -0
  82. package/wasm/zerc20_wallet_core.js +783 -0
  83. package/wasm/zerc20_wallet_core_bg.wasm +0 -0
  84. package/wasm/zerc20_wallet_core_bg.wasm.d.ts +32 -0
@@ -0,0 +1,40 @@
1
+ // Everything an open wallet works with, shared by sync and the operations.
2
+ import { publicActions } from 'viem';
3
+ import { core } from './core.js';
4
+ const keys = new WeakMap();
5
+ /** The key that owns this seed's notes and deposits on every chain and token, or with `code`, the notes paid to that
6
+ * receive code. */
7
+ export function spendKey(ctx, code) {
8
+ let bySeed = keys.get(ctx.seed);
9
+ if (!bySeed)
10
+ keys.set(ctx.seed, (bySeed = new Map()));
11
+ const id = code ? `${code.chainId}:${code.tokenId}:${code.index}` : '';
12
+ let key = bySeed.get(id);
13
+ if (!key) {
14
+ key = (code ? core.codeKey(ctx.seed, code.chainId, code.tokenId, code.index) : core.spendKey(ctx.seed));
15
+ bySeed.set(id, key);
16
+ }
17
+ return key;
18
+ }
19
+ export function chainCtx(ctx, chainId) {
20
+ const c = ctx.chains.find((c) => c.config.chainId === chainId);
21
+ if (!c)
22
+ throw new Error(`chain ${chainId} is not in this network`);
23
+ return c;
24
+ }
25
+ export function chainState(ctx, chainId) {
26
+ let s = ctx.state.chains.find((c) => c.chainId === chainId);
27
+ if (!s)
28
+ ctx.state.chains.push((s = { chainId, samples: [], burnNext: {} }));
29
+ return s;
30
+ }
31
+ /** Waits until the wallet's own node has a transaction mined on ours too: the wallet simulates the next one there, and
32
+ * where blocks are fast that node lags ours now and then, so the next one would show as reverting. A wallet that does
33
+ * not answer is not waited on. */
34
+ export async function walletSees(wc, hash) {
35
+ await wc
36
+ .extend(publicActions)
37
+ .waitForTransactionReceipt({ hash, pollingInterval: 1_000, timeout: 30_000 })
38
+ .catch(() => { });
39
+ }
40
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAG3E,OAAO,EAA+B,aAAa,EAAE,MAAM,MAAM,CAAC;AAElE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAmCjC,MAAM,IAAI,GAAG,IAAI,OAAO,EAAqC,CAAC;AAS9D;mBACmB;AACnB,MAAM,UAAU,QAAQ,CAAC,GAAQ,EAAE,IAAa;IAC9C,IAAI,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,MAAM;QAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC,CAAC,CAAC;IACxE,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACvE,IAAI,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAa,CAAC;QACpH,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,GAAQ,EAAE,OAAe;IAChD,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAC/D,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,SAAS,OAAO,yBAAyB,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAQ,EAAE,OAAe;IAClD,IAAI,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;IAC5D,IAAI,CAAC,CAAC;QAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5E,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;kCAEkC;AAClC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAgB,EAAE,IAAS;IAC1D,MAAM,EAAE;SACL,MAAM,CAAC,aAAa,CAAC;SACrB,yBAAyB,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;SAC5E,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACrB,CAAC"}
package/dist/core.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ import { type Address, type Hex } from 'viem';
2
+ import * as wasm from '../wasm/zerc20_wallet_core.js';
3
+ /** Loads the wasm. A browser (or a bundler) fetches the file next to this module; Node reads it from disk. */
4
+ export declare function initCore(): Promise<void>;
5
+ export declare const core: typeof wasm;
6
+ export interface Burn {
7
+ salt: Hex;
8
+ address: Address;
9
+ }
10
+ /** A seed's `index`-th burn address of a token, with its salt: about 2^16 hashes, up to tens of seconds on a phone.
11
+ * Searched off the main thread (a Web Worker in a browser, a worker thread in Node), so that the app stays
12
+ * responsive; `signal` stops the search. */
13
+ export declare const burnAddress: (seed: Uint8Array, chainId: bigint, tokenId: number, index: number, signal?: AbortSignal) => Promise<Burn>;
14
+ /** The `k`-th burn address a point of sale holding `laneKey` makes for its owner's `publicKey` on a chain and token
15
+ * (`keys::pos_burn`), with its salt: about 2^16 hashes too, searched the same way. */
16
+ export declare const posBurnAddress: (laneKey: Uint8Array, chainId: bigint, tokenId: number, k: number, publicKey: Hex, signal?: AbortSignal) => Promise<Burn>;
17
+ /** A burn address of someone else's `publicKey` (one they published to be paid), with a random salt that has the proof
18
+ * of work: about 2^16 hashes too, searched the same way. Only who holds the salt finds it: the payer, and the owner once
19
+ * told (`planSend` with `burn`). */
20
+ export declare const burnAddressFor: (chainId: bigint, publicKey: Hex, signal?: AbortSignal) => Promise<Burn>;
21
+ export type BurnSearch = {
22
+ seed: Uint8Array;
23
+ chainId: bigint;
24
+ tokenId: number;
25
+ index: number;
26
+ } | {
27
+ laneKey: Uint8Array;
28
+ chainId: bigint;
29
+ tokenId: number;
30
+ k: number;
31
+ publicKey: Hex;
32
+ } | {
33
+ chainId: bigint;
34
+ publicKey: Hex;
35
+ };
36
+ /** A search, where it runs: a seed's index, or a point of sale's counter, derives its salts; someone else's key takes
37
+ * random ones. */
38
+ export declare function searchHere(s: BurnSearch): Burn;
39
+ export type BurnAnswer = {
40
+ burn: Burn;
41
+ } | {
42
+ error: string;
43
+ };
package/dist/core.js ADDED
@@ -0,0 +1,106 @@
1
+ // The wallet's protocol computations (wallet/core: the Rust the circuits, the attester and the backend share,
2
+ // compiled to wasm). `initCore` loads it once; everything else here waits for it.
3
+ import { bytesToHex } from 'viem';
4
+ import init, * as wasm from '../wasm/zerc20_wallet_core.js';
5
+ let ready;
6
+ /** Loads the wasm. A browser (or a bundler) fetches the file next to this module; Node reads it from disk. */
7
+ export function initCore() {
8
+ ready ??= (async () => {
9
+ const url = new URL('../wasm/zerc20_wallet_core_bg.wasm', import.meta.url);
10
+ if (url.protocol !== 'file:')
11
+ return init({ module_or_path: url });
12
+ // a variable, so that bundlers for the browser leave Node's module alone
13
+ const fs = 'node:fs/promises';
14
+ const { readFile } = (await import(/* @vite-ignore */ fs));
15
+ return init({ module_or_path: await readFile(url) });
16
+ })();
17
+ return ready.then(() => undefined);
18
+ }
19
+ export const core = wasm;
20
+ /** A seed's `index`-th burn address of a token, with its salt: about 2^16 hashes, up to tens of seconds on a phone.
21
+ * Searched off the main thread (a Web Worker in a browser, a worker thread in Node), so that the app stays
22
+ * responsive; `signal` stops the search. */
23
+ export const burnAddress = (seed, chainId, tokenId, index, signal) => search({ seed, chainId, tokenId, index }, signal);
24
+ /** The `k`-th burn address a point of sale holding `laneKey` makes for its owner's `publicKey` on a chain and token
25
+ * (`keys::pos_burn`), with its salt: about 2^16 hashes too, searched the same way. */
26
+ export const posBurnAddress = (laneKey, chainId, tokenId, k, publicKey, signal) => search({ laneKey, chainId, tokenId, k, publicKey }, signal);
27
+ /** A burn address of someone else's `publicKey` (one they published to be paid), with a random salt that has the proof
28
+ * of work: about 2^16 hashes too, searched the same way. Only who holds the salt finds it: the payer, and the owner once
29
+ * told (`planSend` with `burn`). */
30
+ export const burnAddressFor = (chainId, publicKey, signal) => search({ chainId, publicKey }, signal);
31
+ /** A search, where it runs: a seed's index, or a point of sale's counter, derives its salts; someone else's key takes
32
+ * random ones. */
33
+ export function searchHere(s) {
34
+ if ('seed' in s)
35
+ return core.burnAddress(s.seed, s.chainId, s.tokenId, s.index);
36
+ if ('laneKey' in s)
37
+ return core.posBurnAddress(s.laneKey, s.chainId, s.tokenId, s.k, s.publicKey);
38
+ for (;;) {
39
+ const bytes = crypto.getRandomValues(new Uint8Array(32));
40
+ bytes[0] = bytes[0] & 0x1f; // below 2^253: a canonical field element
41
+ const salt = bytesToHex(bytes);
42
+ const address = core.burnAddressOf(s.chainId, s.publicKey, salt);
43
+ if (address)
44
+ return { salt, address };
45
+ }
46
+ }
47
+ /** Starts `s` in a worker, which hands `done` its answer, or null if it did not run; returns how to stop it, or null
48
+ * where no worker starts. */
49
+ // ponytail: a worker per search, each compiling the wasm; a pool of availableParallelism() workers if a server makes many
50
+ async function spawn(s, done) {
51
+ if (typeof Worker !== 'undefined') {
52
+ const worker = new Worker(new URL('./burnWorker.js', import.meta.url), { type: 'module' });
53
+ worker.onmessage = ({ data }) => done(data);
54
+ // a worker that does not start (a bundler that left it out) costs responsiveness only
55
+ worker.onerror = (e) => {
56
+ e.preventDefault();
57
+ done(null);
58
+ };
59
+ worker.postMessage(s);
60
+ return () => worker.terminate();
61
+ }
62
+ // variables, so that bundlers for the browser leave Node's module and a second copy of the worker alone
63
+ const [threads, file] = ['node:worker_threads', './burnWorker.js'];
64
+ const { Worker: Thread } = (await import(/* @vite-ignore */ threads).catch(() => ({})));
65
+ if (!Thread)
66
+ return null;
67
+ const thread = new Thread(new URL(file, import.meta.url), { workerData: s });
68
+ thread.once('message', done);
69
+ thread.once('error', () => done(null)); // run from source, as tests are, there is no burnWorker.js
70
+ return () => void thread.terminate();
71
+ }
72
+ async function search(s, signal) {
73
+ signal?.throwIfAborted();
74
+ await initCore();
75
+ return new Promise((resolve, reject) => {
76
+ let stop = null;
77
+ let settled = false;
78
+ const settle = (f) => {
79
+ if (settled)
80
+ return;
81
+ settled = true;
82
+ stop?.();
83
+ signal?.removeEventListener('abort', abort);
84
+ f();
85
+ };
86
+ const here = () => settle(() => {
87
+ try {
88
+ resolve(searchHere(s));
89
+ }
90
+ catch (err) {
91
+ reject(err);
92
+ }
93
+ });
94
+ const abort = () => settle(() => reject(signal.reason));
95
+ signal?.addEventListener('abort', abort, { once: true });
96
+ spawn(s, (a) => a === null ? here() : settle(() => ('burn' in a ? resolve(a.burn) : reject(new Error(a.error))))).then((stopIt) => {
97
+ if (!stopIt)
98
+ here();
99
+ else if (settled)
100
+ stopIt();
101
+ else
102
+ stop = stopIt;
103
+ }, here);
104
+ });
105
+ }
106
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ export type ErrorCode =
2
+ /** Not enough value for the amount and its fees, even counting what is still settling. */
3
+ 'insufficient-funds'
4
+ /** No single JoinSplit pays it, but merging the spendable notes and deposits first would: `consolidate`, then pay.
5
+ * `fees({ kind: 'consolidate' })` tells how many rounds that takes. */
6
+ | 'needs-consolidation'
7
+ /** What is spendable now does not pay it, but notes or deposits are still settling (screening, awaiting
8
+ * a root, or the change of an operation in flight): pay again once `balances()` shows them spendable. */
9
+ | 'funds-settling'
10
+ /** The fees are above the `maxFee` the caller allowed. */
11
+ | 'fee-too-high'
12
+ /** The address never registered for private payments: pay it publicly or ask for a receive code. */
13
+ | 'recipient-not-registered'
14
+ /** The withdrawal's recipient (or a merge's fee recipient) fails the attester's AML screening: withdraw elsewhere. */
15
+ | 'recipient-refused'
16
+ /** A contract account: it cannot sign the messages a wallet needs. */
17
+ | 'unsupported-account'
18
+ /** The user declined a signature or a transaction in their wallet. */
19
+ | 'user-rejected'
20
+ /** The wallet is open in another tab or window: close it there (`close()`), then open it here. */
21
+ | 'wallet-in-use'
22
+ /** This wallet object was closed (`close()`): open the wallet again to go on. */
23
+ | 'wallet-closed'
24
+ /** A server answered something that does not match the chain. */
25
+ | 'indexer-mismatch'
26
+ /** The attester's (or the merge prover's) enclave did not prove it is the published release. */
27
+ | 'attester-unverified'
28
+ /** A service did not answer, or refused. The operation can be resumed. */
29
+ | 'unavailable'
30
+ /** Bad input from the caller. */
31
+ | 'invalid';
32
+ export declare class WalletError extends Error {
33
+ readonly code: ErrorCode;
34
+ constructor(code: ErrorCode, message: string);
35
+ }
36
+ export declare const fail: (code: ErrorCode, message: string) => never;
37
+ /** Maps a declined wallet prompt to `user-rejected`, also where viem wraps it (a transaction's, in an execution error). */
38
+ export declare function guard<T>(p: Promise<T>): Promise<T>;
package/dist/errors.js ADDED
@@ -0,0 +1,26 @@
1
+ // What can go wrong, by code, so that an app can tell the user what to do.
2
+ import { BaseError, UserRejectedRequestError } from 'viem';
3
+ export class WalletError extends Error {
4
+ code;
5
+ constructor(code, message) {
6
+ super(message);
7
+ this.code = code;
8
+ this.name = 'WalletError';
9
+ }
10
+ }
11
+ export const fail = (code, message) => {
12
+ throw new WalletError(code, message);
13
+ };
14
+ const declined = (e) => e instanceof UserRejectedRequestError || e?.code === 4001;
15
+ /** Maps a declined wallet prompt to `user-rejected`, also where viem wraps it (a transaction's, in an execution error). */
16
+ export async function guard(p) {
17
+ try {
18
+ return await p;
19
+ }
20
+ catch (e) {
21
+ if (declined(e) || (e instanceof BaseError && e.walk(declined)))
22
+ throw new WalletError('user-rejected', 'declined in the wallet');
23
+ throw e;
24
+ }
25
+ }
26
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAE3E,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,MAAM,MAAM,CAAC;AAkC3D,MAAM,OAAO,WAAY,SAAQ,KAAK;IAEzB;IADX,YACW,IAAe,EACxB,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHN,SAAI,GAAJ,IAAI,CAAW;QAIxB,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAAe,EAAE,OAAe,EAAS,EAAE;IAC9D,MAAM,IAAI,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAE,EAAE,CAC9B,CAAC,YAAY,wBAAwB,IAAK,CAA8B,EAAE,IAAI,KAAK,IAAI,CAAC;AAE1F,2HAA2H;AAC3H,MAAM,CAAC,KAAK,UAAU,KAAK,CAAI,CAAa;IAC1C,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,CAAC;IACjB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,SAAS,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC7D,MAAM,IAAI,WAAW,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;QACnE,MAAM,CAAC,CAAC;IACV,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { Chain, type Referrer, type Token, MAX_REFERRAL_BPS, NATIVE, UNWRAP_INTENT } from './chain.js';
2
+ export { type ErrorCode, WalletError } from './errors.js';
3
+ export { type AttesterTrust, type ChainConfig, type Circuit, type Network, NITRO_ROOT_SHA256, checkNetwork, networkFromJson, } from './network.js';
4
+ export { type Payment, PointOfSale, type PosToken, type Sale, type SaleStatus } from './pos.js';
5
+ export { seedFromSignature } from './seed.js';
6
+ export { type Activity, type Balance, type CoinInfo, type CoinState, type DepositInfo, type DepositStep, type FeeQuote, type OpenOptions, type Operation, type OperationKind, type OperationStatus, type PendingReason, Wallet, } from './wallet.js';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ // @zerc20/wallet-sdk: a zERC20 wallet for browsers and Node.
2
+ //
3
+ // const wallet = await Wallet.open({ network, walletClient, seed, persist });
4
+ // await wallet.sync(); // or wallet.autoSync(render)
5
+ // wallet.balances(); // spendable, and what is pending and why
6
+ // await (await wallet.deposit({ token, amount })).wait();
7
+ // const q = await wallet.fees({ kind: 'send', token, amount });
8
+ // await (await wallet.send({ to: '0x…', token, amount, maxFee: q.fee })).wait();
9
+ //
10
+ // Nothing secret leaves the device unencrypted: JoinSplit proofs are made here, a merge's by the merge prover, and the
11
+ // attester and the prover get the witness sealed to an enclave whose measurement the wallet checks. Servers are only
12
+ // ever asked for what every wallet downloads.
13
+ export { Chain, MAX_REFERRAL_BPS, NATIVE, UNWRAP_INTENT } from './chain.js';
14
+ export { WalletError } from './errors.js';
15
+ export { NITRO_ROOT_SHA256, checkNetwork, networkFromJson, } from './network.js';
16
+ export { PointOfSale } from './pos.js';
17
+ export { seedFromSignature } from './seed.js';
18
+ export { Wallet, } from './wallet.js';
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAC3E,uFAAuF;AACvF,4DAA4D;AAC5D,kEAAkE;AAClE,mFAAmF;AACnF,EAAE;AACF,uHAAuH;AACvH,qHAAqH;AACrH,8CAA8C;AAE9C,OAAO,EAAE,KAAK,EAA6B,gBAAgB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvG,OAAO,EAAkB,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAKL,iBAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAgB,WAAW,EAA6C,MAAM,UAAU,CAAC;AAChG,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAaL,MAAM,GACP,MAAM,aAAa,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { Address } from 'viem';
2
+ /** A proving key set: the circuit's wasm and zkey, pinned by SHA-256 (from the setup's `setup.json`). */
3
+ export interface Circuit {
4
+ wasm: string;
5
+ zkey: string;
6
+ wasmSha256: string;
7
+ zkeySha256: string;
8
+ }
9
+ /** How to trust an enclave's key: the attester's, or the merge prover's. */
10
+ export type AttesterTrust =
11
+ /** A Nitro Enclave: the document must chain to the root with this SHA-256 and measure this PCR0 (hex). */
12
+ {
13
+ pcr0: string;
14
+ rootSha256: string;
15
+ }
16
+ /** A development attester, which has no document: its key is taken at its word. Never on a real network. */
17
+ | {
18
+ development: true;
19
+ };
20
+ /** The AWS Nitro Enclaves root certificate (G1), SHA-256 over its DER. */
21
+ export declare const NITRO_ROOT_SHA256 = "641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68f79bb5b";
22
+ export interface ChainConfig {
23
+ chainId: bigint;
24
+ /** Used for every read and for waiting on receipts, never the wallet's own RPC. */
25
+ rpcUrl: string;
26
+ verifier: Address;
27
+ depositAddressFactory: Address;
28
+ indexer: string;
29
+ asp: string;
30
+ attester: string;
31
+ attesterTrust: AttesterTrust;
32
+ transactRelayer: string;
33
+ depositRelayer: string;
34
+ /** The native currency, which its native zERC20 is named after; ETH if absent. For a chain an app does not know. */
35
+ nativeCurrency?: {
36
+ name: string;
37
+ symbol: string;
38
+ };
39
+ }
40
+ export interface Network {
41
+ name: string;
42
+ chains: ChainConfig[];
43
+ /** The note storage canister on the Internet Computer. */
44
+ storage: {
45
+ host: string;
46
+ canisterId: string;
47
+ };
48
+ circuits: {
49
+ joinsplit: Circuit;
50
+ rageexit: Circuit;
51
+ };
52
+ /** The merge prover (`backend/merge-prover`), one shared by every chain, and how to trust its enclave, which sees each
53
+ * merge's witness while it proves it. Without it the wallet cannot consolidate. */
54
+ mergeProver?: {
55
+ url: string;
56
+ trust: AttesterTrust;
57
+ };
58
+ }
59
+ /** A `Network` from JSON, where chain ids are strings (JSON has no bigints), as scripts/dev-stack.sh writes it. */
60
+ export declare function networkFromJson(json: string): Network;
61
+ /** The local chain development settings are for. */
62
+ export declare const LOCAL_CHAIN_ID = 31337n;
63
+ /** Throws on settings that only a local development network may have: servers over plain HTTP (anyone on the path
64
+ * could swap a deposit address or a fee), a development attester or merge prover (its host would read every witness,
65
+ * the openings of what it spends included), or a Nitro trust that does not pin the AWS root and a real enclave. */
66
+ export declare function checkNetwork(n: Network): void;
@@ -0,0 +1,55 @@
1
+ // Where a wallet finds everything: per chain, its contracts, servers and artifacts. Apps pass one of these to
2
+ // `Wallet.open`; nothing here is secret.
3
+ import { fail } from './errors.js';
4
+ /** The AWS Nitro Enclaves root certificate (G1), SHA-256 over its DER. */
5
+ export const NITRO_ROOT_SHA256 = '641a0321a3e244efe456463195d606317ed7cdcc3c1756e09893f3c68f79bb5b';
6
+ /** A `Network` from JSON, where chain ids are strings (JSON has no bigints), as scripts/dev-stack.sh writes it. */
7
+ export function networkFromJson(json) {
8
+ const n = JSON.parse(json);
9
+ return { ...n, chains: n.chains.map((c) => ({ ...c, chainId: BigInt(c.chainId) })) };
10
+ }
11
+ /** The local chain development settings are for. */
12
+ export const LOCAL_CHAIN_ID = 31337n;
13
+ const local = (url) => ['localhost', '127.0.0.1', '[::1]'].includes(new URL(url).hostname);
14
+ /** Throws on settings that only a local development network may have: servers over plain HTTP (anyone on the path
15
+ * could swap a deposit address or a fee), a development attester or merge prover (its host would read every witness,
16
+ * the openings of what it spends included), or a Nitro trust that does not pin the AWS root and a real enclave. */
17
+ export function checkNetwork(n) {
18
+ const bad = (what) => fail('invalid', `network ${n.name}: ${what}`);
19
+ const servers = (urls) => {
20
+ for (const u of urls)
21
+ if (new URL(u).protocol !== 'https:' && !local(u))
22
+ bad(`${u} is not https`);
23
+ };
24
+ servers([n.storage.host]);
25
+ // artifacts are pinned by SHA-256, and Node reads them from disk
26
+ for (const c of [n.circuits.joinsplit, n.circuits.rageexit]) {
27
+ for (const u of [c.wasm, c.zkey])
28
+ if (!u.startsWith('file:'))
29
+ servers([u]);
30
+ }
31
+ const trusted = (chains, what, t) => {
32
+ if ('development' in t) {
33
+ for (const c of chains)
34
+ if (c.chainId !== LOCAL_CHAIN_ID)
35
+ bad(`a development ${what} on chain ${c.chainId}`);
36
+ }
37
+ else {
38
+ if (t.rootSha256.toLowerCase() !== NITRO_ROOT_SHA256)
39
+ bad(`the ${what} trust does not pin the AWS Nitro root`);
40
+ // a debug enclave measures all zeros
41
+ const pcr0 = t.pcr0.replace(/^0x/i, '');
42
+ if (!/^[0-9a-f]{96}$/i.test(pcr0) || /^0+$/.test(pcr0))
43
+ bad(`PCR0 ${t.pcr0} is not a release enclave's`);
44
+ }
45
+ };
46
+ for (const c of n.chains) {
47
+ servers([c.rpcUrl, c.indexer, c.asp, c.attester, c.transactRelayer, c.depositRelayer]);
48
+ trusted([c], 'attester', c.attesterTrust);
49
+ }
50
+ if (n.mergeProver) {
51
+ servers([n.mergeProver.url]);
52
+ trusted(n.chains, 'merge prover', n.mergeProver.trust);
53
+ }
54
+ }
55
+ //# sourceMappingURL=network.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../src/network.ts"],"names":[],"mappings":"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"}
package/dist/pos.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ import { type Address, type Hex } from 'viem';
2
+ import { type Token } from './chain.js';
3
+ import { type Network } from './network.js';
4
+ /** What a point of sale needs of a token: where it is, and its id for the burn addresses' derivation and tags. */
5
+ export type PosToken = Pick<Token, 'chainId' | 'tokenId' | 'address'>;
6
+ export interface Sale {
7
+ /** Its burn address, which no other sale has. */
8
+ id: Address;
9
+ chainId: bigint;
10
+ tokenId: number;
11
+ token: Address;
12
+ /** Paid in the token by a transfer here. */
13
+ burnAddress: Address;
14
+ /** Paid in the underlying asset here, which the deposit relayer then wraps into the burn address: once one is
15
+ * registered (`registerDeposit`). */
16
+ depositAddress: Address | null;
17
+ /** What the deposit relayer takes out of each payment to `depositAddress`: ask for `amount` plus it. */
18
+ depositFee: bigint | null;
19
+ /** Where `depositAddress` sends back what it cannot wrap, and what a rage exit returns: the payer's, or the
20
+ * merchant's. On chain for anyone to read once the address is first flushed. */
21
+ refundTo: Address | null;
22
+ /** What was asked, if anything: the point of sale's alone, never sent anywhere. */
23
+ amount: bigint | null;
24
+ createdMs: number;
25
+ }
26
+ export interface Payment {
27
+ amount: bigint;
28
+ /** Who paid the burn address: the payer, or for a payment through `depositAddress` the deposit address itself. */
29
+ sender: Address;
30
+ /** `confirming` until final (a reorg may still drop it), then `screening` until the ASP decides. `rejected` goes back
31
+ * to its sender by the merchant's rage exit, or to `refundTo` from a deposit address. */
32
+ state: 'confirming' | 'screening' | 'approved' | 'rejected';
33
+ /** The transaction that paid the burn address, once the chain shows it: the payer's, or for a payment through
34
+ * `depositAddress` the deposit relayer's. */
35
+ txHash: Hex | null;
36
+ }
37
+ export interface SaleStatus {
38
+ sale: Sale;
39
+ payments: Payment[];
40
+ /** What is final: every payment but the confirming ones. */
41
+ received: bigint;
42
+ }
43
+ export declare class PointOfSale {
44
+ #private;
45
+ private constructor();
46
+ /** Opens the point of sale's saved state. It keeps which addresses it handed out: lose that, and it may hand one out
47
+ * twice. One state, one point of sale: two open on it would hand out the same addresses. */
48
+ static open(opts: {
49
+ network: Network;
50
+ persist: {
51
+ load(): Promise<Uint8Array | null | undefined>;
52
+ save(bytes: Uint8Array): Promise<void>;
53
+ };
54
+ refillWithinMs?: number;
55
+ }): Promise<PointOfSale>;
56
+ get connected(): boolean;
57
+ /** Takes the key `merchant`'s wallet made for it (`Wallet.posKey`), and the public key `merchant` registered in
58
+ * storage, which every address it makes is under: whoever hands it a key cannot point sales at a key of its own. The
59
+ * same key again changes nothing. Another one starts over in that lane; the sales made so far are still followed. */
60
+ connect(merchant: Address, laneKey: Hex): Promise<void>;
61
+ /** Readies addresses of `token` ahead of its sales: an app calls it for each token it takes, as it starts and after
62
+ * sales. Nothing happens while enough are ready, or a refill is under way or due. */
63
+ prepare(token: PosToken): void;
64
+ /** How many addresses of `token` are recorded and wait for a sale. */
65
+ ready(token: PosToken): number;
66
+ /** A new sale of `token`, at an address no sale has had: one recorded ahead, or, when none is left, one recorded
67
+ * now. With `via: 'deposit'` it also registers a deposit address in front of it (`registerDeposit`). */
68
+ next(opts: {
69
+ via: 'burn' | 'deposit';
70
+ token: PosToken;
71
+ amount?: bigint;
72
+ refundTo?: Address;
73
+ }): Promise<Sale>;
74
+ /** Registers a deposit address in front of sale `id`, for the underlying asset; the deposit relayer watches it for 7
75
+ * days. What it cannot wrap (too little, another token) and a rage exit go back to `refundTo`, the merchant's
76
+ * account if absent: name the payer's, so that neither the merchant's account is on chain nor a payment the ASP
77
+ * rejects comes back to the merchant. A sale has one: asked again with the same `refundTo`, it is the same. */
78
+ registerDeposit(id: Address, opts?: {
79
+ refundTo?: Address;
80
+ }): Promise<Sale>;
81
+ sales(): Sale[];
82
+ /** What reached each of sales `ids`: every transfer to its burn address, with where each stands. One pass per chain
83
+ * for them all, reading only what every wallet downloads, so no server learns which addresses it asks about. A chain
84
+ * that fails keeps what was read before, and its error is returned. */
85
+ refresh(ids: Address[]): Promise<{
86
+ sales: SaleStatus[];
87
+ errors: unknown[];
88
+ }>;
89
+ /** What reached sale `id`, and how much is final. */
90
+ status(id: Address): Promise<SaleStatus>;
91
+ /** What reached sale `id` as of the last `refresh`, asking no server. */
92
+ lastStatus(id: Address): SaleStatus;
93
+ }