@zerc20/wallet-sdk 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +24 -0
- package/README.md +26 -2
- package/dist/burnWorker.d.ts +1 -0
- package/dist/burnWorker.js +21 -0
- package/dist/burnWorker.js.map +1 -0
- package/dist/chain.d.ts +345 -0
- package/dist/chain.js +149 -0
- package/dist/chain.js.map +1 -0
- package/dist/context.d.ts +48 -0
- package/dist/context.js +40 -0
- package/dist/context.js.map +1 -0
- package/dist/core.d.ts +43 -0
- package/dist/core.js +106 -0
- package/dist/core.js.map +1 -0
- package/dist/errors.d.ts +36 -0
- package/dist/errors.js +26 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +66 -0
- package/dist/network.js +55 -0
- package/dist/network.js.map +1 -0
- package/dist/pos.d.ts +93 -0
- package/dist/pos.js +302 -0
- package/dist/pos.js.map +1 -0
- package/dist/prove.d.ts +13 -0
- package/dist/prove.js +53 -0
- package/dist/prove.js.map +1 -0
- package/dist/rageExit.d.ts +5 -0
- package/dist/rageExit.js +146 -0
- package/dist/rageExit.js.map +1 -0
- package/dist/scan.d.ts +17 -0
- package/dist/scan.js +47 -0
- package/dist/scan.js.map +1 -0
- package/dist/seed.d.ts +8 -0
- package/dist/seed.js +35 -0
- package/dist/seed.js.map +1 -0
- package/dist/servers.d.ts +60 -0
- package/dist/servers.js +117 -0
- package/dist/servers.js.map +1 -0
- package/dist/services.d.ts +62 -0
- package/dist/services.js +128 -0
- package/dist/services.js.map +1 -0
- package/dist/spend.d.ts +151 -0
- package/dist/spend.js +745 -0
- package/dist/spend.js.map +1 -0
- package/dist/state.d.ts +174 -0
- package/dist/state.js +45 -0
- package/dist/state.js.map +1 -0
- package/dist/sync.d.ts +84 -0
- package/dist/sync.js +615 -0
- package/dist/sync.js.map +1 -0
- package/dist/tree.d.ts +19 -0
- package/dist/tree.js +63 -0
- package/dist/tree.js.map +1 -0
- package/dist/wallet.d.ts +301 -0
- package/dist/wallet.js +992 -0
- package/dist/wallet.js.map +1 -0
- package/package.json +60 -4
- package/src/burnWorker.ts +25 -0
- package/src/chain.ts +205 -0
- package/src/context.ts +84 -0
- package/src/core.ts +147 -0
- package/src/errors.ts +61 -0
- package/src/index.ts +41 -0
- package/src/network.ts +94 -0
- package/src/pos.ts +415 -0
- package/src/prove.ts +67 -0
- package/src/rageExit.ts +168 -0
- package/src/scan.ts +62 -0
- package/src/seed.ts +46 -0
- package/src/servers.ts +172 -0
- package/src/services.ts +184 -0
- package/src/snarkjs.d.ts +11 -0
- package/src/spend.ts +912 -0
- package/src/state.ts +211 -0
- package/src/sync.ts +698 -0
- package/src/tree.ts +108 -0
- package/src/wallet.ts +1297 -0
- package/wasm/zerc20_wallet_core.d.ts +176 -0
- package/wasm/zerc20_wallet_core.js +783 -0
- package/wasm/zerc20_wallet_core_bg.wasm +0 -0
- package/wasm/zerc20_wallet_core_bg.wasm.d.ts +32 -0
package/dist/seed.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Hex, type WalletClient } from 'viem';
|
|
2
|
+
/** The seed, a fingerprint to check later derivations against (store it; it reveals nothing about the seed), and the
|
|
3
|
+
* canonical signature it comes from, which proves the seed is this account's. */
|
|
4
|
+
export declare function seedFromSignature(walletClient: WalletClient, expectedFingerprint?: Hex): Promise<{
|
|
5
|
+
seed: Uint8Array;
|
|
6
|
+
fingerprint: Hex;
|
|
7
|
+
signature: Hex;
|
|
8
|
+
}>;
|
package/dist/seed.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// A seed from the wallet's own signature, so the account alone restores it. It holds only for wallets whose
|
|
2
|
+
// signatures are deterministic (RFC 6979, as MetaMask's and hardware wallets' are): keep the fingerprint, and if a
|
|
3
|
+
// later derivation gives another one, the wallet signs differently each time and the seed must be backed up itself.
|
|
4
|
+
// Anything that gets the user to sign the same message gets the seed.
|
|
5
|
+
import { secp256k1 } from '@noble/curves/secp256k1.js';
|
|
6
|
+
import { bytesToHex, hexToBytes, keccak256 } from 'viem';
|
|
7
|
+
import { fail, guard } from './errors.js';
|
|
8
|
+
const message = (address) => [
|
|
9
|
+
'zERC20 wallet seed',
|
|
10
|
+
'WARNING: signing this gives control of your private zERC20 funds.',
|
|
11
|
+
'Sign it only in an app you trust to hold them.',
|
|
12
|
+
`address: ${address.toLowerCase()}`,
|
|
13
|
+
'tag: zerc20/wallet/seed/v1',
|
|
14
|
+
].join('\n');
|
|
15
|
+
/** The seed, a fingerprint to check later derivations against (store it; it reveals nothing about the seed), and the
|
|
16
|
+
* canonical signature it comes from, which proves the seed is this account's. */
|
|
17
|
+
export async function seedFromSignature(walletClient, expectedFingerprint) {
|
|
18
|
+
const account = walletClient.account ?? fail('invalid', 'the wallet client has no account');
|
|
19
|
+
const raw = hexToBytes(await guard(walletClient.signMessage({ account, message: message(account.address) })));
|
|
20
|
+
// canonical form: low s and v = 27/28, so equivalent signatures give one seed
|
|
21
|
+
let sig = secp256k1.Signature.fromBytes(raw.subarray(0, 64), 'compact');
|
|
22
|
+
let v = raw[64] >= 27 ? raw[64] - 27 : raw[64];
|
|
23
|
+
if (sig.hasHighS()) {
|
|
24
|
+
sig = new secp256k1.Signature(sig.r, secp256k1.Point.CURVE().n - sig.s);
|
|
25
|
+
v ^= 1;
|
|
26
|
+
}
|
|
27
|
+
const canonical = new Uint8Array([...sig.toBytes('compact'), 27 + v]);
|
|
28
|
+
const seed = hexToBytes(keccak256(canonical));
|
|
29
|
+
const fingerprint = keccak256(new Uint8Array([...new TextEncoder().encode('zerc20/wallet/seed-fingerprint/v1'), ...seed])).slice(0, 18);
|
|
30
|
+
if (expectedFingerprint && expectedFingerprint !== fingerprint) {
|
|
31
|
+
fail('invalid', 'this wallet signs differently each time, so its signature cannot stand in for a seed: back the seed up instead');
|
|
32
|
+
}
|
|
33
|
+
return { seed, fingerprint, signature: bytesToHex(canonical) };
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=seed.js.map
|
package/dist/seed.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../src/seed.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,mHAAmH;AACnH,oHAAoH;AACpH,sEAAsE;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAA+B,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACtF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAE1C,MAAM,OAAO,GAAG,CAAC,OAAY,EAAE,EAAE,CAC/B;IACE,oBAAoB;IACpB,mEAAmE;IACnE,gDAAgD;IAChD,YAAY,OAAO,CAAC,WAAW,EAAE,EAAE;IACnC,4BAA4B;CAC7B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEf;iFACiF;AACjF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,YAA0B,EAC1B,mBAAyB;IAEzB,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,IAAI,IAAI,CAAC,SAAS,EAAE,kCAAkC,CAAC,CAAC;IAC5F,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9G,8EAA8E;IAC9E,IAAI,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;IACxE,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/C,IAAI,GAAG,CAAC,QAAQ,EAAE,EAAE,CAAC;QACnB,GAAG,GAAG,IAAI,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACxE,CAAC,IAAI,CAAC,CAAC;IACT,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,UAAU,CAAC,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,UAAU,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;IAC9C,MAAM,WAAW,GAAG,SAAS,CAC3B,IAAI,UAAU,CAAC,CAAC,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,mCAAmC,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,CAC5F,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAQ,CAAC;IACtB,IAAI,mBAAmB,IAAI,mBAAmB,KAAK,WAAW,EAAE,CAAC;QAC/D,IAAI,CACF,SAAS,EACT,gHAAgH,CACjH,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type Hex } from 'viem';
|
|
2
|
+
export declare const PAGE = 4096;
|
|
3
|
+
/** A tree's size and root, with the roots of its partial chunk and page (null where the size ends on a
|
|
4
|
+
* boundary): what a path against that root needs besides the wallet's own chunk. */
|
|
5
|
+
export interface RootEntry {
|
|
6
|
+
size: number;
|
|
7
|
+
root: Hex;
|
|
8
|
+
partialChunk: Hex | null;
|
|
9
|
+
partialPage: Hex | null;
|
|
10
|
+
}
|
|
11
|
+
export interface IndexerStatus {
|
|
12
|
+
commitments: number;
|
|
13
|
+
commitmentsFinal: number;
|
|
14
|
+
nullifiers: number;
|
|
15
|
+
nullifiersFinal: number;
|
|
16
|
+
/** The latest proved root the indexer derives itself, before any forged one: what a proof uses. */
|
|
17
|
+
latestRoot: RootEntry | null;
|
|
18
|
+
/** The first proved root since the protocol last recovered that the indexer does not derive: a forged one. No root
|
|
19
|
+
* is proved after it until the protocol recovers. */
|
|
20
|
+
mismatch: {
|
|
21
|
+
size: number;
|
|
22
|
+
root: Hex;
|
|
23
|
+
} | null;
|
|
24
|
+
/** The newest block whose entries cannot reorg, the one the final counts stand at. */
|
|
25
|
+
finalBlock: number | null;
|
|
26
|
+
}
|
|
27
|
+
export declare class Indexer {
|
|
28
|
+
readonly url: string;
|
|
29
|
+
constructor(url: string);
|
|
30
|
+
status(): Promise<IndexerStatus>;
|
|
31
|
+
/** Page `p` of a list of fixed-size entries: 4096 entries, fewer while it fills. */
|
|
32
|
+
page(list: 'tags' | 'nullifiers' | 'blocks' | 'times' | 'chunk-roots' | 'marks', p: number): Promise<Uint8Array>;
|
|
33
|
+
/** Page `p` of commitments: the ledger's hash chain before it (32 bytes), then 4 fields of 32 bytes each. */
|
|
34
|
+
commitments(p: number): Promise<{
|
|
35
|
+
hashChainBefore: Hex;
|
|
36
|
+
entries: Uint8Array;
|
|
37
|
+
}>;
|
|
38
|
+
pageRoots(): Promise<Uint8Array>;
|
|
39
|
+
/** Page `p` of proved roots, in the order they were proved. */
|
|
40
|
+
roots(p: number): Promise<(RootEntry & {
|
|
41
|
+
matches: boolean;
|
|
42
|
+
})[]>;
|
|
43
|
+
}
|
|
44
|
+
export interface AspStatus {
|
|
45
|
+
decided: number;
|
|
46
|
+
latestRoot: RootEntry | null;
|
|
47
|
+
/** How old a deposit's block must be before it is screened. */
|
|
48
|
+
delaySecs: number;
|
|
49
|
+
}
|
|
50
|
+
export declare class Asp {
|
|
51
|
+
readonly url: string;
|
|
52
|
+
constructor(url: string);
|
|
53
|
+
status(): Promise<AspStatus>;
|
|
54
|
+
/** 512 bytes: position `c * 4096 + j` is approved if bit `j % 8` of byte `j / 8` is set. */
|
|
55
|
+
bitmap(c: number): Promise<Uint8Array>;
|
|
56
|
+
chunkRoots(p: number): Promise<Uint8Array>;
|
|
57
|
+
pageRoots(): Promise<Uint8Array>;
|
|
58
|
+
/** The recorded root whose value is `root`, searched from the newest page back. */
|
|
59
|
+
rootEntry(root: Hex): Promise<RootEntry | null>;
|
|
60
|
+
}
|
package/dist/servers.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// The indexer and the ASP, read in whole pages that every wallet downloads alike: a wallet never names a
|
|
2
|
+
// leaf, an address or a nullifier to a server. Layouts are in backend/indexer/README.md and backend/asp/README.md.
|
|
3
|
+
import { bytesToHex } from 'viem';
|
|
4
|
+
import { fail } from './errors.js';
|
|
5
|
+
export const PAGE = 4096;
|
|
6
|
+
const TIMEOUT_MS = 20_000;
|
|
7
|
+
async function fetchBytes(url) {
|
|
8
|
+
let res;
|
|
9
|
+
try {
|
|
10
|
+
res = await fetch(url, { signal: AbortSignal.timeout(TIMEOUT_MS) });
|
|
11
|
+
}
|
|
12
|
+
catch (e) {
|
|
13
|
+
return fail('unavailable', `${url}: ${e.message}`);
|
|
14
|
+
}
|
|
15
|
+
if (!res.ok)
|
|
16
|
+
return fail('unavailable', `${url}: HTTP ${res.status}`);
|
|
17
|
+
return new Uint8Array(await res.arrayBuffer());
|
|
18
|
+
}
|
|
19
|
+
async function fetchJson(url) {
|
|
20
|
+
return JSON.parse(new TextDecoder().decode(await fetchBytes(url)));
|
|
21
|
+
}
|
|
22
|
+
const ZERO = `0x${'0'.repeat(64)}`;
|
|
23
|
+
const orNull = (h) => (h === ZERO ? null : h);
|
|
24
|
+
/** Root entries back to back: size(8) ‖ root ‖ partial chunk ‖ partial page (zero: none) [‖ matches(1)]. */
|
|
25
|
+
function rootEntries(bytes, width) {
|
|
26
|
+
const out = [];
|
|
27
|
+
for (let at = 0; at + width <= bytes.length; at += width) {
|
|
28
|
+
const e = bytes.subarray(at, at + width);
|
|
29
|
+
out.push({
|
|
30
|
+
size: Number(new DataView(e.buffer, e.byteOffset).getBigUint64(0, false)),
|
|
31
|
+
root: bytesToHex(e.subarray(8, 40)),
|
|
32
|
+
partialChunk: orNull(bytesToHex(e.subarray(40, 72))),
|
|
33
|
+
partialPage: orNull(bytesToHex(e.subarray(72, 104))),
|
|
34
|
+
matches: width === 104 || e[104] === 1,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
export class Indexer {
|
|
40
|
+
url;
|
|
41
|
+
constructor(url) {
|
|
42
|
+
this.url = url;
|
|
43
|
+
}
|
|
44
|
+
async status() {
|
|
45
|
+
const s = await fetchJson(`${this.url}/status`);
|
|
46
|
+
const r = s.latest_root;
|
|
47
|
+
return {
|
|
48
|
+
commitments: s.commitments,
|
|
49
|
+
commitmentsFinal: s.commitments_final,
|
|
50
|
+
nullifiers: s.nullifiers,
|
|
51
|
+
nullifiersFinal: s.nullifiers_final,
|
|
52
|
+
latestRoot: r && r.matches
|
|
53
|
+
? { size: r.index, root: r.root, partialChunk: r.partial_chunk_root, partialPage: r.partial_page_root }
|
|
54
|
+
: null,
|
|
55
|
+
mismatch: s.mismatch ? { size: s.mismatch.index, root: s.mismatch.root } : null,
|
|
56
|
+
finalBlock: s.indexer.final_block,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Page `p` of a list of fixed-size entries: 4096 entries, fewer while it fills. */
|
|
60
|
+
page(list, p) {
|
|
61
|
+
return fetchBytes(`${this.url}/${list}/${p}`);
|
|
62
|
+
}
|
|
63
|
+
/** Page `p` of commitments: the ledger's hash chain before it (32 bytes), then 4 fields of 32 bytes each. */
|
|
64
|
+
async commitments(p) {
|
|
65
|
+
const body = await fetchBytes(`${this.url}/commitments/${p}`);
|
|
66
|
+
return { hashChainBefore: bytesToHex(body.subarray(0, 32)), entries: body.subarray(32) };
|
|
67
|
+
}
|
|
68
|
+
pageRoots() {
|
|
69
|
+
return fetchBytes(`${this.url}/page-roots`);
|
|
70
|
+
}
|
|
71
|
+
/** Page `p` of proved roots, in the order they were proved. */
|
|
72
|
+
async roots(p) {
|
|
73
|
+
return rootEntries(await fetchBytes(`${this.url}/roots/${p}`), 105);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export class Asp {
|
|
77
|
+
url;
|
|
78
|
+
constructor(url) {
|
|
79
|
+
this.url = url;
|
|
80
|
+
}
|
|
81
|
+
async status() {
|
|
82
|
+
const s = await fetchJson(`${this.url}/status`);
|
|
83
|
+
const r = s.latest_root;
|
|
84
|
+
return {
|
|
85
|
+
decided: s.decided,
|
|
86
|
+
latestRoot: r
|
|
87
|
+
? { size: r.index, root: r.root, partialChunk: r.partial_chunk_root, partialPage: r.partial_page_root }
|
|
88
|
+
: null,
|
|
89
|
+
delaySecs: s.delay_secs,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/** 512 bytes: position `c * 4096 + j` is approved if bit `j % 8` of byte `j / 8` is set. */
|
|
93
|
+
bitmap(c) {
|
|
94
|
+
return fetchBytes(`${this.url}/bitmap/${c}`);
|
|
95
|
+
}
|
|
96
|
+
chunkRoots(p) {
|
|
97
|
+
return fetchBytes(`${this.url}/chunk-roots/${p}`);
|
|
98
|
+
}
|
|
99
|
+
pageRoots() {
|
|
100
|
+
return fetchBytes(`${this.url}/page-roots`);
|
|
101
|
+
}
|
|
102
|
+
/** The recorded root whose value is `root`, searched from the newest page back. */
|
|
103
|
+
async rootEntry(root) {
|
|
104
|
+
const latest = (await this.status()).latestRoot;
|
|
105
|
+
if (latest?.root === root)
|
|
106
|
+
return latest;
|
|
107
|
+
for (let p = 0;; p++) {
|
|
108
|
+
const entries = rootEntries(await fetchBytes(`${this.url}/roots/${p}`), 104);
|
|
109
|
+
const hit = entries.find((e) => e.root === root);
|
|
110
|
+
if (hit)
|
|
111
|
+
return hit;
|
|
112
|
+
if (entries.length < PAGE)
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=servers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"servers.js","sourceRoot":"","sources":["../src/servers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type Address, type Hex } from 'viem';
|
|
2
|
+
import type { Chain } from './chain.js';
|
|
3
|
+
import { WalletError } from './errors.js';
|
|
4
|
+
import { type AttesterTrust, type ChainConfig, type Network } from './network.js';
|
|
5
|
+
/** The key the attester's enclave opens witnesses with, once its attestation checks out. */
|
|
6
|
+
export declare const attesterKey: (c: ChainConfig) => Promise<Uint8Array>;
|
|
7
|
+
/** The key an enclave (the attester's, the merge prover's) opens what is sealed to it with, from `url`, once its
|
|
8
|
+
* attestation document checks out against `trust`. */
|
|
9
|
+
export declare function enclaveKey(c: ChainConfig, url: string, trust: AttesterTrust, what: string): Promise<Uint8Array>;
|
|
10
|
+
/** The attester's signature over `statement`, once its enclave has checked `witness` (JSON): a JoinSplit's, or with
|
|
11
|
+
* `inputs`, a merge's of that many inputs. */
|
|
12
|
+
export declare function attest(c: ChainConfig, statement: object, witness: string, inputs?: number): Promise<Hex>;
|
|
13
|
+
export interface RelayerFees {
|
|
14
|
+
relayer: Address;
|
|
15
|
+
/** The relayer's note key, which owns its fee notes whatever their token. */
|
|
16
|
+
publicKey: Hex;
|
|
17
|
+
fees: Map<number, {
|
|
18
|
+
fee: bigint;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
export declare function relayerFees(c: ChainConfig): Promise<RelayerFees>;
|
|
22
|
+
export interface Relayed {
|
|
23
|
+
txHash: Hex;
|
|
24
|
+
noteIndex1: number | null;
|
|
25
|
+
noteIndex2: number | null;
|
|
26
|
+
}
|
|
27
|
+
/** What `relay` reports when it cannot say whether the transaction landed. */
|
|
28
|
+
export declare class RelayUnknown extends WalletError {
|
|
29
|
+
}
|
|
30
|
+
/** Has the relayer submit `request`. A 400 means the call reverts, which includes "already landed". */
|
|
31
|
+
export declare const relay: (c: ChainConfig, request: object, feeNote?: {
|
|
32
|
+
value: bigint;
|
|
33
|
+
salt: Hex;
|
|
34
|
+
}) => Promise<Relayed | {
|
|
35
|
+
reverted: string;
|
|
36
|
+
}>;
|
|
37
|
+
/** Has the relayer submit a merge's `request`, as `relay` does a JoinSplit's. Answers the merged note's index. */
|
|
38
|
+
export declare const relayMerge: (c: ChainConfig, request: object) => Promise<{
|
|
39
|
+
txHash: Hex;
|
|
40
|
+
noteIndex: number | null;
|
|
41
|
+
} | {
|
|
42
|
+
reverted: string;
|
|
43
|
+
}>;
|
|
44
|
+
export type MergeProver = NonNullable<Network['mergeProver']>;
|
|
45
|
+
/** Sends a merge's witness (JSON, of `inputs` inputs) sealed to the merge prover's enclave, to prove `statement` (the
|
|
46
|
+
* attester's `Merge`). Proving takes minutes: `proofOf` tells when it is done. */
|
|
47
|
+
export declare function requestProof(c: ChainConfig, p: MergeProver, statement: object, witness: string, inputs: number): Promise<void>;
|
|
48
|
+
/** Where the proof of the merge that outputs `noteHash` stands: `null` when the prover does not have it (it never
|
|
49
|
+
* did, or lost it in a restart), so it is to be sent again. `proof` is the ABI-encoded `NovaDeciderProof`. */
|
|
50
|
+
export declare function proofOf(p: MergeProver, noteHash: Hex): Promise<{
|
|
51
|
+
status: 'queued' | 'proving' | 'done' | 'failed';
|
|
52
|
+
proof?: Hex;
|
|
53
|
+
error?: string;
|
|
54
|
+
} | null>;
|
|
55
|
+
/** A deposit address that flushes whatever reaches it into `burnAddress`, less `fee` paid to `feeRecipient` each time;
|
|
56
|
+
* refunds go to `refundAddress`. What the relayer says is checked against the factory: an address of its own would take
|
|
57
|
+
* the funds, and the fee it names is the one the address takes. */
|
|
58
|
+
export declare function registerDeposit(chain: Chain, token: Address, burnAddress: Hex, refundAddress: Address): Promise<{
|
|
59
|
+
depositAddress: Address;
|
|
60
|
+
feeRecipient: Address;
|
|
61
|
+
fee: bigint;
|
|
62
|
+
}>;
|
package/dist/services.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// The attester, the merge prover and the relayers. Each sees only what it must: the attester's and the prover's hosts
|
|
2
|
+
// see the public statement and a witness sealed to their enclave, the transact-relayer a finished request, the
|
|
3
|
+
// deposit-relayer a burn address.
|
|
4
|
+
import { bytesToHex, hexToBytes } from 'viem';
|
|
5
|
+
import { core } from './core.js';
|
|
6
|
+
import { WalletError, fail } from './errors.js';
|
|
7
|
+
import { LOCAL_CHAIN_ID } from './network.js';
|
|
8
|
+
/** A transact witness is sealed padded to this, and a merge's to `MERGE_PAD` per input, so its size does not leak the
|
|
9
|
+
* amounts' digits (`backend/attester/README.md`). */
|
|
10
|
+
const WITNESS_PAD = 16 * 1024;
|
|
11
|
+
const MERGE_PAD = 8 * 1024;
|
|
12
|
+
const json = (v) => JSON.stringify(v, (_, x) => (typeof x === 'bigint' ? x.toString() : x));
|
|
13
|
+
async function call(url, init) {
|
|
14
|
+
let res;
|
|
15
|
+
try {
|
|
16
|
+
res = await fetch(url, { ...init, signal: AbortSignal.timeout(init?.timeoutMs ?? 30_000) });
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
return fail('unavailable', `${url}: ${e.message}`);
|
|
20
|
+
}
|
|
21
|
+
const text = await res.text();
|
|
22
|
+
if (!res.ok)
|
|
23
|
+
return { status: res.status, body: text };
|
|
24
|
+
return { status: res.status, body: JSON.parse(text) };
|
|
25
|
+
}
|
|
26
|
+
const post = (url, body, timeoutMs) => call(url, { method: 'POST', headers: { 'content-type': 'application/json' }, body: json(body), timeoutMs });
|
|
27
|
+
const base64 = (s) => Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
|
|
28
|
+
/** The key the attester's enclave opens witnesses with, once its attestation checks out. */
|
|
29
|
+
export const attesterKey = (c) => enclaveKey(c, `${c.attester}/attestation`, c.attesterTrust, 'attester');
|
|
30
|
+
/** The key an enclave (the attester's, the merge prover's) opens what is sealed to it with, from `url`, once its
|
|
31
|
+
* attestation document checks out against `trust`. */
|
|
32
|
+
export async function enclaveKey(c, url, trust, what) {
|
|
33
|
+
const r = await call(url);
|
|
34
|
+
if (typeof r.body === 'string')
|
|
35
|
+
return fail('unavailable', `${what}: ${r.body}`);
|
|
36
|
+
if ('development' in trust) {
|
|
37
|
+
// its host would read everything sealed to it, every input's opening included: a local chain only, whatever the
|
|
38
|
+
// flag says
|
|
39
|
+
if (trust.development !== true || c.chainId !== LOCAL_CHAIN_ID)
|
|
40
|
+
return fail('attester-unverified', `a development ${what} off the local chain`);
|
|
41
|
+
return hexToBytes(r.body.publicKey);
|
|
42
|
+
}
|
|
43
|
+
if (!r.body.attestation)
|
|
44
|
+
return fail('attester-unverified', `the ${what} sent no attestation document`);
|
|
45
|
+
try {
|
|
46
|
+
// from the document, never the `publicKey` beside it
|
|
47
|
+
return core.verifyAttestation(base64(r.body.attestation), trust.rootSha256, trust.pcr0, BigInt(Math.floor(Date.now() / 1000)));
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return fail('attester-unverified', `the ${what}'s enclave does not check out: ${e.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The attester's signature over `statement`, once its enclave has checked `witness` (JSON): a JoinSplit's, or with
|
|
54
|
+
* `inputs`, a merge's of that many inputs. */
|
|
55
|
+
export async function attest(c, statement, witness, inputs) {
|
|
56
|
+
const [path, pad] = inputs === undefined ? ['transact', WITNESS_PAD] : ['merge', MERGE_PAD * inputs];
|
|
57
|
+
for (let attempt = 0;; attempt++) {
|
|
58
|
+
const sealed = bytesToHex(core.sealWitness(await attesterKey(c), witness, pad));
|
|
59
|
+
const r = await post(`${c.attester}/${path}`, { statement, witness: sealed }, 60_000);
|
|
60
|
+
if (typeof r.body !== 'string')
|
|
61
|
+
return r.body.attestation;
|
|
62
|
+
// the enclave restarted with a new key between the fetch and the request
|
|
63
|
+
if (r.body.includes('fetch /attestation again') && attempt === 0)
|
|
64
|
+
continue;
|
|
65
|
+
// a root its indexer has not derived yet is a 400 too, but one that passes
|
|
66
|
+
const later = r.status !== 400 || r.body.includes('retry once');
|
|
67
|
+
return fail(later ? 'unavailable' : 'invalid', `attester: ${r.body}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export async function relayerFees(c) {
|
|
71
|
+
const r = await call(`${c.transactRelayer}/fee`);
|
|
72
|
+
if (typeof r.body === 'string')
|
|
73
|
+
return fail('unavailable', `transact-relayer: ${r.body}`);
|
|
74
|
+
const fees = new Map(Object.entries(r.body.fees).map(([t, f]) => [Number(t), { fee: BigInt(f.fee) }]));
|
|
75
|
+
return { relayer: r.body.relayer, publicKey: r.body.publicKey, fees };
|
|
76
|
+
}
|
|
77
|
+
/** What `relay` reports when it cannot say whether the transaction landed. */
|
|
78
|
+
export class RelayUnknown extends WalletError {
|
|
79
|
+
}
|
|
80
|
+
/** Has the relayer submit `request`. A 400 means the call reverts, which includes "already landed". */
|
|
81
|
+
export const relay = (c, request, feeNote) => relayed(`${c.transactRelayer}/transact`, { request, feeNote });
|
|
82
|
+
/** Has the relayer submit a merge's `request`, as `relay` does a JoinSplit's. Answers the merged note's index. */
|
|
83
|
+
export const relayMerge = (c, request) => relayed(`${c.transactRelayer}/merge`, { request });
|
|
84
|
+
async function relayed(url, body) {
|
|
85
|
+
// the relayer answers once mined, after fee bumping if need be
|
|
86
|
+
const r = await post(url, body, 15 * 60_000).catch((e) => {
|
|
87
|
+
throw new RelayUnknown('unavailable', `transact-relayer: ${e.message}`);
|
|
88
|
+
});
|
|
89
|
+
if (typeof r.body !== 'string')
|
|
90
|
+
return r.body;
|
|
91
|
+
if (r.status === 400)
|
|
92
|
+
return { reverted: r.body };
|
|
93
|
+
throw new RelayUnknown('unavailable', `transact-relayer: HTTP ${r.status}: ${r.body}`);
|
|
94
|
+
}
|
|
95
|
+
/** Sends a merge's witness (JSON, of `inputs` inputs) sealed to the merge prover's enclave, to prove `statement` (the
|
|
96
|
+
* attester's `Merge`). Proving takes minutes: `proofOf` tells when it is done. */
|
|
97
|
+
export async function requestProof(c, p, statement, witness, inputs) {
|
|
98
|
+
const key = await enclaveKey(c, `${p.url}/enclave`, p.trust, 'merge prover');
|
|
99
|
+
const sealed = bytesToHex(core.sealMergeWitness(key, witness, MERGE_PAD * inputs));
|
|
100
|
+
const r = await post(`${p.url}/proofs`, { statement, witness: sealed }, 60_000);
|
|
101
|
+
if (typeof r.body === 'string')
|
|
102
|
+
fail(r.status === 400 ? 'invalid' : 'unavailable', `merge prover: ${r.body}`);
|
|
103
|
+
}
|
|
104
|
+
/** Where the proof of the merge that outputs `noteHash` stands: `null` when the prover does not have it (it never
|
|
105
|
+
* did, or lost it in a restart), so it is to be sent again. `proof` is the ABI-encoded `NovaDeciderProof`. */
|
|
106
|
+
export async function proofOf(p, noteHash) {
|
|
107
|
+
const r = await call(`${p.url}/proofs/${noteHash}`);
|
|
108
|
+
if (r.status === 404)
|
|
109
|
+
return null;
|
|
110
|
+
if (typeof r.body === 'string')
|
|
111
|
+
return fail('unavailable', `merge prover: ${r.body}`);
|
|
112
|
+
return r.body;
|
|
113
|
+
}
|
|
114
|
+
/** A deposit address that flushes whatever reaches it into `burnAddress`, less `fee` paid to `feeRecipient` each time;
|
|
115
|
+
* refunds go to `refundAddress`. What the relayer says is checked against the factory: an address of its own would take
|
|
116
|
+
* the funds, and the fee it names is the one the address takes. */
|
|
117
|
+
export async function registerDeposit(chain, token, burnAddress, refundAddress) {
|
|
118
|
+
const r = await post(`${chain.config.depositRelayer}/register`, { token, burnAddress, refundAddress });
|
|
119
|
+
if (typeof r.body === 'string')
|
|
120
|
+
return fail('unavailable', `deposit-relayer: ${r.body}`);
|
|
121
|
+
const { depositAddress, feeRecipient } = r.body;
|
|
122
|
+
const fee = BigInt(r.body.fee);
|
|
123
|
+
const expected = await chain.predictDepositAddress(token, burnAddress, refundAddress, feeRecipient, fee);
|
|
124
|
+
if (depositAddress.toLowerCase() !== expected.toLowerCase())
|
|
125
|
+
fail('indexer-mismatch', `the deposit relayer gave ${depositAddress}, not the factory's ${expected}`);
|
|
126
|
+
return { depositAddress, feeRecipient, fee };
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=services.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"services.js","sourceRoot":"","sources":["../src/services.ts"],"names":[],"mappings":"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"}
|
package/dist/spend.d.ts
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { type Recipient } from '@zerc20/storage-sdk';
|
|
2
|
+
import { type Address, type Hex } from 'viem';
|
|
3
|
+
import { type Ctx } from './context.js';
|
|
4
|
+
import type { OperationRecord, Position, SpendOperation } from './state.js';
|
|
5
|
+
/** Something spendable. */
|
|
6
|
+
export interface Coin {
|
|
7
|
+
kind: 'note' | 'burn';
|
|
8
|
+
index: number;
|
|
9
|
+
value: bigint;
|
|
10
|
+
/** For a note paid to a receive code, the code's index: the code's own key owns it. Otherwise the wallet's key does. */
|
|
11
|
+
keyIndex?: number;
|
|
12
|
+
/** A note's salt. */
|
|
13
|
+
salt?: Hex;
|
|
14
|
+
/** A deposit's burn salt and sender. */
|
|
15
|
+
burnSalt?: Hex;
|
|
16
|
+
sender?: Hex;
|
|
17
|
+
}
|
|
18
|
+
export interface Output {
|
|
19
|
+
publicKey: Hex;
|
|
20
|
+
value: bigint;
|
|
21
|
+
salt: Hex;
|
|
22
|
+
role: 'recipient' | 'change' | 'relayer';
|
|
23
|
+
}
|
|
24
|
+
/** One JoinSplit and how far it got. */
|
|
25
|
+
export interface Step {
|
|
26
|
+
inputs: Coin[];
|
|
27
|
+
outputs: [Output, Output];
|
|
28
|
+
/** `unwrap` pays it out as the underlying asset rather than zERC20. */
|
|
29
|
+
withdraw: {
|
|
30
|
+
recipient: Address;
|
|
31
|
+
amount: bigint;
|
|
32
|
+
unwrap?: boolean;
|
|
33
|
+
} | null;
|
|
34
|
+
entropy: Hex;
|
|
35
|
+
stage: 'planned' | 'noted' | 'proved' | 'attested' | 'relayed';
|
|
36
|
+
witness?: string;
|
|
37
|
+
statement?: Record<string, string>;
|
|
38
|
+
request?: object;
|
|
39
|
+
/** Reverts with no sign of it on chain: proved again with the same outputs a few times, then failed. */
|
|
40
|
+
attempts: number;
|
|
41
|
+
/** Once relayed: the indexer's nullifier count just after. Final nullifiers past it, and the input's still not among
|
|
42
|
+
* them, means a reorg dropped the transaction. */
|
|
43
|
+
relayedAt?: number;
|
|
44
|
+
}
|
|
45
|
+
/** The most inputs one merge spends: the attester takes requests up to 2 MB, at 8 KiB of sealed witness per input
|
|
46
|
+
* sent as hex, and the prover folds them in one job. */
|
|
47
|
+
export declare const MAX_MERGE_INPUTS = 100;
|
|
48
|
+
/** One Nova merge of a consolidation and how far it got. */
|
|
49
|
+
export interface MergeStep {
|
|
50
|
+
inputs: Coin[];
|
|
51
|
+
/** The note it makes: the inputs less `fee`. */
|
|
52
|
+
output: Output;
|
|
53
|
+
/** Paid publicly to `feeRecipient`, the relayer, grossed up for the protocol's cut. */
|
|
54
|
+
fee: bigint;
|
|
55
|
+
feeRecipient: Address;
|
|
56
|
+
stage: 'planned' | 'noted' | 'attested' | 'proving' | 'proved' | 'relayed';
|
|
57
|
+
/** Only until the prover has it: it holds the inputs' openings. */
|
|
58
|
+
witness?: string;
|
|
59
|
+
/** The attester's `Merge`, and from `attested` on the `MergeRequest` short of its proof. */
|
|
60
|
+
statement?: Record<string, unknown>;
|
|
61
|
+
request?: Record<string, unknown>;
|
|
62
|
+
/** Refused by the prover, or reverting with no sign of it on chain: built again with the same output a few times,
|
|
63
|
+
* then failed. */
|
|
64
|
+
attempts: number;
|
|
65
|
+
relayedAt?: number;
|
|
66
|
+
}
|
|
67
|
+
/** Where a send or withdrawal goes; `self` merges the wallet's own notes and nothing leaves. */
|
|
68
|
+
export type Destination = {
|
|
69
|
+
kind: 'address';
|
|
70
|
+
recipient: Recipient;
|
|
71
|
+
} | {
|
|
72
|
+
kind: 'code';
|
|
73
|
+
code: string;
|
|
74
|
+
} | {
|
|
75
|
+
kind: 'public';
|
|
76
|
+
address: Address;
|
|
77
|
+
unwrap?: boolean;
|
|
78
|
+
} | {
|
|
79
|
+
kind: 'self';
|
|
80
|
+
};
|
|
81
|
+
export interface SpendData {
|
|
82
|
+
to: Destination;
|
|
83
|
+
/** The largest total fee the caller allowed. */
|
|
84
|
+
maxFee: bigint;
|
|
85
|
+
step: Step | MergeStep | null;
|
|
86
|
+
/** Fees paid so far. */
|
|
87
|
+
paid: bigint;
|
|
88
|
+
/** A consolidation of only these notes and deposits (commitment indices). */
|
|
89
|
+
chosen?: number[];
|
|
90
|
+
/** The notes to self its steps made (a payment's change, a consolidation's merged notes), by salt. */
|
|
91
|
+
changes?: Hex[];
|
|
92
|
+
/** The transactions relayed for it. */
|
|
93
|
+
txHashes?: Hex[];
|
|
94
|
+
}
|
|
95
|
+
/** The notes and deposits an operation in flight spends: no other operation may take them. */
|
|
96
|
+
export declare const inputsOf: (o: OperationRecord) => Coin[];
|
|
97
|
+
/** Final, unspent, under the latest proved root of size `rootSize`, and not taken by another operation. */
|
|
98
|
+
export declare function coins(ctx: Ctx, chainId: bigint, tokenId: number, rootSize: number): Coin[];
|
|
99
|
+
/** Whether any of `tokenId`'s notes or deposits is on chain but not spendable yet: not final or confirmed, not
|
|
100
|
+
* screened, or not under the proved root of size `rootSize`. A note only announced does not count: it may never land. */
|
|
101
|
+
export declare function settling(ctx: Ctx, chainId: bigint, tokenId: number, rootSize: number): boolean;
|
|
102
|
+
/** The inputs of one JoinSplit worth at least `need`: one coin, or a coin plus a note (a deposit can only be the
|
|
103
|
+
* first input). The smallest total wins. O(n log n), for wallets holding many notes. */
|
|
104
|
+
export declare function pick(all: Coin[], need: bigint): Coin[] | null;
|
|
105
|
+
/** A consolidation's next merge: up to `MAX_MERGE_INPUTS` coins, the largest first. `null` when a merge would leave
|
|
106
|
+
* nothing better: no coin, a lone note, or no more than the relayer's `fee`.
|
|
107
|
+
* ponytail: one merge per root; merge disjoint sets in parallel if consolidations of thousands get slow. */
|
|
108
|
+
export declare function mergeRound(all: Coin[], fee: bigint): Coin[] | null;
|
|
109
|
+
/** How many merges consolidate `all` into one note: each spends up to `MAX_MERGE_INPUTS`, the one before's note among
|
|
110
|
+
* them. A lone deposit takes one, to become a note. */
|
|
111
|
+
export declare function roundsToMerge(all: Coin[]): number;
|
|
112
|
+
/** What a consolidation merges: everything, or only the chosen notes and deposits (by commitment index) and the
|
|
113
|
+
* notes its earlier rounds made of them. */
|
|
114
|
+
export declare function mergePool(all: Coin[], chosen: number[] | undefined, changes?: Hex[]): Coin[];
|
|
115
|
+
/** The inputs of the one JoinSplit that pays `need` (see `pick`), or why there is none: merging everything worth it
|
|
116
|
+
* into one note first would pay it, coins `onTheWay` might, or nothing will. A payment never waits or merges itself:
|
|
117
|
+
* that is the app's call. */
|
|
118
|
+
export declare function payWith(all: Coin[], need: bigint, fee: bigint, onTheWay: boolean): Coin[];
|
|
119
|
+
/** The smallest public withdrawal that still pays `net` after the protocol's cut. */
|
|
120
|
+
export declare function grossed(ctx: Ctx, chainId: bigint, tokenId: number, net: bigint): Promise<bigint>;
|
|
121
|
+
/** A payment's one public withdrawal, and the protocol's cut of it, which the payer pays on top: a withdrawal's pays
|
|
122
|
+
* its recipient `amount` (and the relayer by a note), a private payment's pays the relayer its `fee`. */
|
|
123
|
+
export declare function withdrawalOf(ctx: Ctx, chainId: bigint, tokenId: number, isPublic: boolean, amount: bigint, fee: bigint): Promise<{
|
|
124
|
+
withdrawn: bigint;
|
|
125
|
+
protocol: bigint;
|
|
126
|
+
}>;
|
|
127
|
+
/** The most a payment out of `all` pays its recipient, the relayer's `fee` and the protocol's cut on top
|
|
128
|
+
* (`withdrawalOf`): a withdrawal of what the relayer leaves, less the cut on it, or a private payment of what the
|
|
129
|
+
* relayer's grossed fee leaves. Coins one JoinSplit cannot spend together (`pick`) are merged first, each merge
|
|
130
|
+
* paying the relayer's grossed fee, as a consolidation's quote does. */
|
|
131
|
+
export declare function maxPayment(ctx: Ctx, chainId: bigint, tokenId: number, isPublic: boolean, all: Coin[], fee: bigint): Promise<bigint>;
|
|
132
|
+
/** Plans the next step of `op`: a payment's only JoinSplit (or throws why there is none), or a consolidation's next
|
|
133
|
+
* merge. `null`: wait for the last merge's note to become spendable. `'done'`: nothing left worth merging. */
|
|
134
|
+
export declare function planStep(ctx: Ctx, op: SpendOperation): Promise<Step | MergeStep | null | 'done'>;
|
|
135
|
+
/** Whether any of a consolidation's chosen notes and deposits (commitment indices) is still settling: not among the
|
|
136
|
+
* spendable `all` yet. Throws for one that never merges: a deposit the ASP rejected. A quote and every merge go by
|
|
137
|
+
* this one rule. */
|
|
138
|
+
export declare function chosenSettling(ctx: Ctx, chainId: bigint, chosen: number[], all: Coin[]): boolean;
|
|
139
|
+
export declare const codeOf: (code: string) => import("@zerc20/storage-sdk").ReceiveCode;
|
|
140
|
+
/** What `step` paid besides `op`'s amount, the relayer's fee and the protocol's cut: what its inputs hold beyond the
|
|
141
|
+
* amount and the change. */
|
|
142
|
+
export declare const feeOf: (op: SpendOperation, step: Step) => bigint;
|
|
143
|
+
/** Where a JoinSplit relayed when the indexer had `relayedAt` nullifiers stands, by its first input `f` as sync last
|
|
144
|
+
* saw it: spent, or the final nullifiers passed it without its own (a reorg dropped it, or the relayer never sent it),
|
|
145
|
+
* or neither yet. */
|
|
146
|
+
export declare function relayedState(f: Position | undefined, relayedAt: number): 'landed' | 'dropped' | 'waiting';
|
|
147
|
+
/** Whether `op`'s payment or withdrawal is on chain: done, or relayed, which the relayer answers once it is mined. Until
|
|
148
|
+
* it is final a reorg may still drop it, and the operation relays it again. */
|
|
149
|
+
export declare function landed(op: SpendOperation): boolean;
|
|
150
|
+
/** Takes `op` as far as it can go now, saving before each step that leaves the device. */
|
|
151
|
+
export declare function advanceSpend(ctx: Ctx, op: SpendOperation): Promise<void>;
|