@zill-protocol/client 4.1.2
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/CHANGELOG.md +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/package.json +58 -0
- package/dist/src/NocturneClient.d.ts +68 -0
- package/dist/src/NocturneClient.d.ts.map +1 -0
- package/dist/src/NocturneClient.js +264 -0
- package/dist/src/NocturneClient.js.map +1 -0
- package/dist/src/NocturneDB.d.ts +100 -0
- package/dist/src/NocturneDB.d.ts.map +1 -0
- package/dist/src/NocturneDB.js +525 -0
- package/dist/src/NocturneDB.js.map +1 -0
- package/dist/src/OpTracker.d.ts +13 -0
- package/dist/src/OpTracker.d.ts.map +1 -0
- package/dist/src/OpTracker.js +34 -0
- package/dist/src/OpTracker.js.map +1 -0
- package/dist/src/conversion/converter.d.ts +5 -0
- package/dist/src/conversion/converter.d.ts.map +1 -0
- package/dist/src/conversion/converter.js +15 -0
- package/dist/src/conversion/converter.js.map +1 -0
- package/dist/src/conversion/index.d.ts +3 -0
- package/dist/src/conversion/index.d.ts.map +1 -0
- package/dist/src/conversion/index.js +21 -0
- package/dist/src/conversion/index.js.map +1 -0
- package/dist/src/conversion/mock.d.ts +6 -0
- package/dist/src/conversion/mock.d.ts.map +1 -0
- package/dist/src/conversion/mock.js +14 -0
- package/dist/src/conversion/mock.js.map +1 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +39 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/opRequestGas.d.ts +20 -0
- package/dist/src/opRequestGas.d.ts.map +1 -0
- package/dist/src/opRequestGas.js +321 -0
- package/dist/src/opRequestGas.js.map +1 -0
- package/dist/src/operationRequest/builder.d.ts +40 -0
- package/dist/src/operationRequest/builder.d.ts.map +1 -0
- package/dist/src/operationRequest/builder.js +192 -0
- package/dist/src/operationRequest/builder.js.map +1 -0
- package/dist/src/operationRequest/index.d.ts +3 -0
- package/dist/src/operationRequest/index.d.ts.map +1 -0
- package/dist/src/operationRequest/index.js +6 -0
- package/dist/src/operationRequest/index.js.map +1 -0
- package/dist/src/operationRequest/operationRequest.d.ts +50 -0
- package/dist/src/operationRequest/operationRequest.d.ts.map +1 -0
- package/dist/src/operationRequest/operationRequest.js +16 -0
- package/dist/src/operationRequest/operationRequest.js.map +1 -0
- package/dist/src/prepareOperation.d.ts +21 -0
- package/dist/src/prepareOperation.d.ts.map +1 -0
- package/dist/src/prepareOperation.js +256 -0
- package/dist/src/prepareOperation.js.map +1 -0
- package/dist/src/proveOperation.d.ts +7 -0
- package/dist/src/proveOperation.d.ts.map +1 -0
- package/dist/src/proveOperation.js +79 -0
- package/dist/src/proveOperation.js.map +1 -0
- package/dist/src/signOperation.d.ts +3 -0
- package/dist/src/signOperation.d.ts.map +1 -0
- package/dist/src/signOperation.js +61 -0
- package/dist/src/signOperation.js.map +1 -0
- package/dist/src/snapJsonRpc.d.ts +55 -0
- package/dist/src/snapJsonRpc.d.ts.map +1 -0
- package/dist/src/snapJsonRpc.js +63 -0
- package/dist/src/snapJsonRpc.js.map +1 -0
- package/dist/src/syncSDK.d.ts +17 -0
- package/dist/src/syncSDK.d.ts.map +1 -0
- package/dist/src/syncSDK.js +188 -0
- package/dist/src/syncSDK.js.map +1 -0
- package/dist/src/types.d.ts +60 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +3 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/utils/constants.d.ts +3 -0
- package/dist/src/utils/constants.d.ts.map +1 -0
- package/dist/src/utils/constants.js +20 -0
- package/dist/src/utils/constants.js.map +1 -0
- package/dist/src/utils/index.d.ts +3 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +19 -0
- package/dist/src/utils/index.js.map +1 -0
- package/dist/src/utils/misc.d.ts +13 -0
- package/dist/src/utils/misc.d.ts.map +1 -0
- package/dist/src/utils/misc.js +77 -0
- package/dist/src/utils/misc.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +58 -0
- package/src/NocturneClient.ts +415 -0
- package/src/NocturneDB.ts +761 -0
- package/src/OpTracker.ts +44 -0
- package/src/conversion/converter.ts +22 -0
- package/src/conversion/index.ts +2 -0
- package/src/conversion/mock.ts +11 -0
- package/src/index.ts +14 -0
- package/src/opRequestGas.ts +487 -0
- package/src/operationRequest/builder.ts +359 -0
- package/src/operationRequest/index.ts +16 -0
- package/src/operationRequest/operationRequest.ts +87 -0
- package/src/prepareOperation.ts +420 -0
- package/src/proveOperation.ts +124 -0
- package/src/signOperation.ts +116 -0
- package/src/snapJsonRpc.ts +109 -0
- package/src/syncSDK.ts +285 -0
- package/src/types.ts +83 -0
- package/src/utils/constants.ts +16 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/misc.ts +107 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Address,
|
|
3
|
+
CanonAddrRegistryEntry,
|
|
4
|
+
PreSignOperation,
|
|
5
|
+
SignedOperation,
|
|
6
|
+
} from "@zill-protocol/core";
|
|
7
|
+
import {
|
|
8
|
+
CanonAddress,
|
|
9
|
+
NocturneSignature,
|
|
10
|
+
SpendPk,
|
|
11
|
+
ViewingKey,
|
|
12
|
+
} from "@zill-protocol/crypto";
|
|
13
|
+
import * as JSON from "bigint-json-serialization";
|
|
14
|
+
import { OperationWithMetadata } from "./types";
|
|
15
|
+
|
|
16
|
+
export interface SetSpendKeyMethod {
|
|
17
|
+
method: "nocturne_setSpendKey";
|
|
18
|
+
params: {
|
|
19
|
+
spendKey: string;
|
|
20
|
+
eoaAddress: Address;
|
|
21
|
+
};
|
|
22
|
+
return: string | null; // error string or null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface SignCanonAddrRegistryEntryMethod {
|
|
26
|
+
method: "nocturne_signCanonAddrRegistryEntry";
|
|
27
|
+
params: {
|
|
28
|
+
entry: CanonAddrRegistryEntry;
|
|
29
|
+
chainId: bigint;
|
|
30
|
+
registryAddress: string;
|
|
31
|
+
};
|
|
32
|
+
return: {
|
|
33
|
+
canonAddr: CanonAddress;
|
|
34
|
+
digest: bigint;
|
|
35
|
+
sig: NocturneSignature;
|
|
36
|
+
spendPubkey: SpendPk;
|
|
37
|
+
vkNonce: bigint;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface SignOperationMethod {
|
|
42
|
+
method: "nocturne_signOperation";
|
|
43
|
+
params: OperationWithMetadata<PreSignOperation>;
|
|
44
|
+
return: SignedOperation;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface RequestSpendKeyEoaMethod {
|
|
48
|
+
method: "nocturne_requestSpendKeyEoa";
|
|
49
|
+
params: null;
|
|
50
|
+
return: Address | null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface RequestViewingKeyMethodResponse {
|
|
54
|
+
vk: ViewingKey;
|
|
55
|
+
vkNonce: bigint;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface RequestViewingKeyMethod {
|
|
59
|
+
method: "nocturne_requestViewingKey";
|
|
60
|
+
params: null;
|
|
61
|
+
return: RequestViewingKeyMethodResponse;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type RpcRequestMethod =
|
|
65
|
+
| SetSpendKeyMethod
|
|
66
|
+
| SignCanonAddrRegistryEntryMethod
|
|
67
|
+
| SignOperationMethod
|
|
68
|
+
| RequestViewingKeyMethod
|
|
69
|
+
| RequestSpendKeyEoaMethod;
|
|
70
|
+
|
|
71
|
+
export type SnapRpcRequestHandlerArgs = {
|
|
72
|
+
origin: string;
|
|
73
|
+
request: RpcRequestMethod;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type SnapRpcRequestHandler = (
|
|
77
|
+
args: SnapRpcRequestHandlerArgs
|
|
78
|
+
) => Promise<RpcRequestMethod["return"]>;
|
|
79
|
+
|
|
80
|
+
export function assertAllRpcMethodsHandled(request: never): never {
|
|
81
|
+
// @ts-expect-error on request.method—if this fires at runtime, we want to see the method name
|
|
82
|
+
throw new Error("Snap JSON RPC method not handled: " + request.method);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function parseObjectValues(params: object): object {
|
|
86
|
+
return Object.fromEntries(
|
|
87
|
+
Object.entries(params).map(([key, value]) => {
|
|
88
|
+
const parsedValue = JSON.parse(value);
|
|
89
|
+
if (parsedValue && parsedValue.__primitive) {
|
|
90
|
+
return [key, parsedValue.value ?? undefined];
|
|
91
|
+
} else {
|
|
92
|
+
return [key, parsedValue];
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function stringifyObjectValues(params: object): object {
|
|
99
|
+
return Object.fromEntries(
|
|
100
|
+
Object.entries(params).map(([key, value]) => [
|
|
101
|
+
key,
|
|
102
|
+
JSON.stringify(
|
|
103
|
+
typeof value === "object"
|
|
104
|
+
? value
|
|
105
|
+
: { __primitive: true, value: value ?? null }
|
|
106
|
+
),
|
|
107
|
+
])
|
|
108
|
+
);
|
|
109
|
+
}
|
package/src/syncSDK.ts
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { NocturneViewer } from "@zill-protocol/crypto";
|
|
2
|
+
import { NocturneDB } from "./NocturneDB";
|
|
3
|
+
import { PendingOutputRecord } from "./types";
|
|
4
|
+
import {
|
|
5
|
+
IncludedEncryptedNote,
|
|
6
|
+
IncludedNote,
|
|
7
|
+
IncludedNoteCommitment,
|
|
8
|
+
NoteTrait,
|
|
9
|
+
EncryptedStateDiff,
|
|
10
|
+
StateDiff,
|
|
11
|
+
SDKSyncAdapter,
|
|
12
|
+
TotalEntityIndexTrait,
|
|
13
|
+
decryptNote,
|
|
14
|
+
SparseMerkleProver,
|
|
15
|
+
consecutiveChunks,
|
|
16
|
+
timed,
|
|
17
|
+
timedAsync,
|
|
18
|
+
Histogram,
|
|
19
|
+
} from "@zill-protocol/core";
|
|
20
|
+
|
|
21
|
+
export interface SyncOpts {
|
|
22
|
+
endBlock?: number;
|
|
23
|
+
timeoutSeconds?: number;
|
|
24
|
+
timing?: boolean;
|
|
25
|
+
finalityBlocks?: number;
|
|
26
|
+
debug?: boolean;
|
|
27
|
+
onPendingOutputsConfirmed?: (outputs: PendingOutputRecord[]) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface SyncDeps {
|
|
31
|
+
viewer: NocturneViewer;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Sync SDK, returning last synced merkle index of last state diff
|
|
35
|
+
export async function syncSDK(
|
|
36
|
+
{ viewer }: SyncDeps,
|
|
37
|
+
adapter: SDKSyncAdapter,
|
|
38
|
+
db: NocturneDB,
|
|
39
|
+
merkle: SparseMerkleProver,
|
|
40
|
+
opts?: SyncOpts
|
|
41
|
+
): Promise<number | undefined> {
|
|
42
|
+
const currTotalEntityIndex = await db.currentTotalEntityIndex();
|
|
43
|
+
const startTotalEntityIndex = currTotalEntityIndex
|
|
44
|
+
? currTotalEntityIndex + 1n
|
|
45
|
+
: 0n;
|
|
46
|
+
|
|
47
|
+
const currentBlock = await adapter.getLatestIndexedBlock();
|
|
48
|
+
const endTotalEntityIndex = TotalEntityIndexTrait.fromBlockNumber(
|
|
49
|
+
opts?.endBlock ?? currentBlock
|
|
50
|
+
);
|
|
51
|
+
const range = {
|
|
52
|
+
startTotalEntityIndex,
|
|
53
|
+
endTotalEntityIndex,
|
|
54
|
+
startBlock: TotalEntityIndexTrait.toComponents(startTotalEntityIndex)
|
|
55
|
+
.blockNumber,
|
|
56
|
+
endBlock: currentBlock,
|
|
57
|
+
};
|
|
58
|
+
if (opts?.debug) {
|
|
59
|
+
console.log(
|
|
60
|
+
`[syncSDK] syncing SDK from totalEntityIndex ${startTotalEntityIndex} (block ${range.startBlock}) to ${endTotalEntityIndex} (block ${currentBlock})...`,
|
|
61
|
+
{ range }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const newDiffs = adapter.iterStateDiffs(startTotalEntityIndex, {
|
|
66
|
+
endTotalEntityIndex,
|
|
67
|
+
timing: opts?.timing,
|
|
68
|
+
finalityBlocks: opts?.finalityBlocks,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// decrypt notes and compute nullifiers
|
|
72
|
+
const diffHistogram = opts?.timing
|
|
73
|
+
? new Histogram("decryptStateDiff time (ms) per note")
|
|
74
|
+
: undefined;
|
|
75
|
+
const diffs = newDiffs.map((diff) => {
|
|
76
|
+
const [decrypted, time] = timed(() => decryptStateDiff(viewer, diff));
|
|
77
|
+
diffHistogram?.sample(time / diff.notes.length);
|
|
78
|
+
return decrypted;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
let latestSyncedMerkleIndex: number | undefined =
|
|
82
|
+
await db.latestSyncedMerkleIndex();
|
|
83
|
+
|
|
84
|
+
if (opts?.timeoutSeconds) {
|
|
85
|
+
setTimeout(() => diffs.close(), opts.timeoutSeconds * 1000);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// apply diffs
|
|
89
|
+
const applyStateDiffHistogram = opts?.timing
|
|
90
|
+
? new Histogram("applyStateDiff time (ms) per note or commitment")
|
|
91
|
+
: undefined;
|
|
92
|
+
const updateMerkleHistogram = opts?.timing
|
|
93
|
+
? new Histogram("updateMerkle time (ms) per note or commitment")
|
|
94
|
+
: undefined;
|
|
95
|
+
for await (const diff of diffs.iter) {
|
|
96
|
+
if (opts?.debug) {
|
|
97
|
+
console.log(
|
|
98
|
+
"[syncSDK] diff latestNewlySyncedMerkleIndex",
|
|
99
|
+
diff.latestNewlySyncedMerkleIndex
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
// update notes in DB
|
|
103
|
+
const [nfIndices, nfTime] = await timedAsync(() =>
|
|
104
|
+
db.applyStateDiff(
|
|
105
|
+
diff,
|
|
106
|
+
opts?.onPendingOutputsConfirmed
|
|
107
|
+
? { onPendingOutputsConfirmed: opts.onPendingOutputsConfirmed }
|
|
108
|
+
: undefined
|
|
109
|
+
)
|
|
110
|
+
);
|
|
111
|
+
applyStateDiffHistogram?.sample(nfTime / diff.notesAndCommitments.length);
|
|
112
|
+
latestSyncedMerkleIndex = await db.latestSyncedMerkleIndex();
|
|
113
|
+
|
|
114
|
+
// TODO: deal with case where we have failure between applying state diff to DB and merkle being persisted
|
|
115
|
+
|
|
116
|
+
if (diff.latestCommittedMerkleIndex) {
|
|
117
|
+
const [_, time] = await timedAsync(() =>
|
|
118
|
+
updateMerkle(
|
|
119
|
+
merkle,
|
|
120
|
+
diff.latestCommittedMerkleIndex!,
|
|
121
|
+
diff.notesAndCommitments.map((n) => n.inner),
|
|
122
|
+
nfIndices,
|
|
123
|
+
opts?.debug ?? false
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
updateMerkleHistogram?.sample(time / diff.notesAndCommitments.length);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
diffHistogram?.print();
|
|
131
|
+
applyStateDiffHistogram?.print();
|
|
132
|
+
updateMerkleHistogram?.print();
|
|
133
|
+
|
|
134
|
+
return latestSyncedMerkleIndex;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function updateMerkle(
|
|
138
|
+
merkle: SparseMerkleProver,
|
|
139
|
+
latestCommittedMerkleIndex: number,
|
|
140
|
+
notesAndCommitments: (IncludedNote | IncludedNoteCommitment)[],
|
|
141
|
+
nfIndices: number[],
|
|
142
|
+
logWarnings: boolean
|
|
143
|
+
): Promise<void> {
|
|
144
|
+
const ordered = [...notesAndCommitments].sort(
|
|
145
|
+
(a, b) => a.merkleIndex - b.merkleIndex
|
|
146
|
+
);
|
|
147
|
+
const currentCount = merkle.count();
|
|
148
|
+
const filtered = ordered.filter(
|
|
149
|
+
(noteOrCommitment) => noteOrCommitment.merkleIndex >= currentCount
|
|
150
|
+
);
|
|
151
|
+
if (logWarnings && filtered.length !== ordered.length) {
|
|
152
|
+
console.warn("[syncSDK] skipping stale leaves", {
|
|
153
|
+
currentCount,
|
|
154
|
+
skipped: ordered.length - filtered.length,
|
|
155
|
+
first: ordered[0]?.merkleIndex,
|
|
156
|
+
last: ordered[ordered.length - 1]?.merkleIndex,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// add all new leaves as uncommitted leaves
|
|
161
|
+
const batches = consecutiveChunks(
|
|
162
|
+
filtered,
|
|
163
|
+
(noteOrCommitment) => noteOrCommitment.merkleIndex
|
|
164
|
+
);
|
|
165
|
+
for (const batch of batches) {
|
|
166
|
+
const startIndex = batch[0].merkleIndex;
|
|
167
|
+
const leaves = [];
|
|
168
|
+
const includes = [];
|
|
169
|
+
for (const noteOrCommitment of batch) {
|
|
170
|
+
if (NoteTrait.isCommitment(noteOrCommitment)) {
|
|
171
|
+
leaves.push(
|
|
172
|
+
(noteOrCommitment as IncludedNoteCommitment).noteCommitment
|
|
173
|
+
);
|
|
174
|
+
includes.push(false);
|
|
175
|
+
} else {
|
|
176
|
+
leaves.push(NoteTrait.toCommitment(noteOrCommitment as IncludedNote));
|
|
177
|
+
includes.push(true);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
merkle.insertBatchUncommitted(startIndex, leaves, includes);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// commit up to latest subtree commit
|
|
184
|
+
merkle.commitUpToIndex(latestCommittedMerkleIndex);
|
|
185
|
+
|
|
186
|
+
// mark nullified ones for pruning
|
|
187
|
+
for (const index of nfIndices) {
|
|
188
|
+
try {
|
|
189
|
+
merkle.markForPruning(index);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (logWarnings) {
|
|
192
|
+
console.warn("[syncSDK] skip pruning missing leaf", {
|
|
193
|
+
index,
|
|
194
|
+
error: err instanceof Error ? err.message : String(err),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// persist merkle to underlying KV store
|
|
201
|
+
await merkle.persist();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function decryptStateDiff(
|
|
205
|
+
viewer: NocturneViewer,
|
|
206
|
+
{
|
|
207
|
+
notes,
|
|
208
|
+
nullifiers,
|
|
209
|
+
latestCommittedMerkleIndex,
|
|
210
|
+
latestCommitTei,
|
|
211
|
+
latestNewlySyncedMerkleIndex,
|
|
212
|
+
totalEntityIndex,
|
|
213
|
+
}: EncryptedStateDiff
|
|
214
|
+
): StateDiff {
|
|
215
|
+
const notesAndCommitments = notes.map(({ inner, totalEntityIndex }) => {
|
|
216
|
+
const note = inner;
|
|
217
|
+
const isEncrypted = NoteTrait.isEncryptedNote(note);
|
|
218
|
+
if (isEncrypted) {
|
|
219
|
+
// if it's encrypted, attempt to decrypt.
|
|
220
|
+
// if it succeeds, then return the decrypted note
|
|
221
|
+
// if it fails, assume it's not ours and just the commitment and merkle index
|
|
222
|
+
try {
|
|
223
|
+
const { merkleIndex, commitment, ...encryptedNote } =
|
|
224
|
+
note as IncludedEncryptedNote;
|
|
225
|
+
|
|
226
|
+
// TODO: come up with a way to handle sender mismatches when we implement history proofs
|
|
227
|
+
const includedNote: IncludedNote = {
|
|
228
|
+
...decryptNote(viewer, encryptedNote),
|
|
229
|
+
merkleIndex,
|
|
230
|
+
};
|
|
231
|
+
const nullifier = NoteTrait.createNullifier(viewer, includedNote);
|
|
232
|
+
const res = { ...includedNote, nullifier };
|
|
233
|
+
return {
|
|
234
|
+
inner: res,
|
|
235
|
+
totalEntityIndex,
|
|
236
|
+
};
|
|
237
|
+
} catch (err) {
|
|
238
|
+
const encryptedNote = note as IncludedEncryptedNote;
|
|
239
|
+
const { commitment, merkleIndex } = encryptedNote;
|
|
240
|
+
const nc = {
|
|
241
|
+
noteCommitment: commitment,
|
|
242
|
+
merkleIndex,
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
inner: nc,
|
|
247
|
+
totalEntityIndex,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
// if it's not encrypted, check if it's ours.
|
|
252
|
+
// if it is, then return it
|
|
253
|
+
// if it's not, return only the commitment
|
|
254
|
+
const includedNote = note as IncludedNote;
|
|
255
|
+
const isOwn = viewer.isOwnAddress(includedNote.owner);
|
|
256
|
+
|
|
257
|
+
if (isOwn) {
|
|
258
|
+
const nullifier = NoteTrait.createNullifier(viewer, includedNote);
|
|
259
|
+
const res = { ...includedNote, nullifier };
|
|
260
|
+
return {
|
|
261
|
+
inner: res,
|
|
262
|
+
totalEntityIndex,
|
|
263
|
+
};
|
|
264
|
+
} else {
|
|
265
|
+
const nc = {
|
|
266
|
+
noteCommitment: NoteTrait.toCommitment(includedNote),
|
|
267
|
+
merkleIndex: includedNote.merkleIndex,
|
|
268
|
+
};
|
|
269
|
+
return {
|
|
270
|
+
inner: nc,
|
|
271
|
+
totalEntityIndex,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
notesAndCommitments,
|
|
279
|
+
nullifiers,
|
|
280
|
+
latestCommittedMerkleIndex,
|
|
281
|
+
latestCommitTei,
|
|
282
|
+
latestNewlySyncedMerkleIndex,
|
|
283
|
+
totalEntityIndex,
|
|
284
|
+
};
|
|
285
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Address,
|
|
3
|
+
Asset,
|
|
4
|
+
CanonAddress,
|
|
5
|
+
Operation,
|
|
6
|
+
OperationStatus,
|
|
7
|
+
} from "@zill-protocol/core";
|
|
8
|
+
|
|
9
|
+
export type OpWithMetadata<O> = {
|
|
10
|
+
op: O;
|
|
11
|
+
metadata: OperationMetadata;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export interface OptimisticNFRecord {
|
|
15
|
+
nullifier: bigint;
|
|
16
|
+
expirationDate: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type OpHistoryRecord = {
|
|
20
|
+
digest: bigint;
|
|
21
|
+
metadata: OperationMetadata;
|
|
22
|
+
|
|
23
|
+
spentNoteMerkleIndices: number[];
|
|
24
|
+
|
|
25
|
+
status?: OperationStatus;
|
|
26
|
+
|
|
27
|
+
createdAt: number;
|
|
28
|
+
lastModified: number;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export interface OperationMetadata {
|
|
32
|
+
items: OperationMetadataItem[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type PendingOutputRecord = {
|
|
36
|
+
commitment: bigint;
|
|
37
|
+
asset: Asset;
|
|
38
|
+
value: bigint;
|
|
39
|
+
createdAt: number;
|
|
40
|
+
opDigest?: bigint;
|
|
41
|
+
expectedInsertionIndex?: number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type OperationMetadataItem =
|
|
45
|
+
| ConfidentialPaymentMetadata
|
|
46
|
+
| ActionMetadata;
|
|
47
|
+
|
|
48
|
+
export type ActionMetadata =
|
|
49
|
+
| {
|
|
50
|
+
type: "Action";
|
|
51
|
+
actionType: "Transfer";
|
|
52
|
+
recipientAddress: Address;
|
|
53
|
+
erc20Address: Address;
|
|
54
|
+
amount: bigint;
|
|
55
|
+
}
|
|
56
|
+
| {
|
|
57
|
+
type: "Action";
|
|
58
|
+
actionType: "Weth To Wsteth";
|
|
59
|
+
amount: bigint;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
type: "Action";
|
|
63
|
+
actionType: "Transfer ETH";
|
|
64
|
+
recipientAddress: Address;
|
|
65
|
+
amount: bigint;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export interface ConfidentialPaymentMetadata {
|
|
69
|
+
type: "ConfidentialPayment";
|
|
70
|
+
recipient: CanonAddress;
|
|
71
|
+
asset: Asset;
|
|
72
|
+
amount: bigint;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface OpDigestWithMetadata {
|
|
76
|
+
opDigest: bigint;
|
|
77
|
+
metadata?: OperationMetadata;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface OperationWithMetadata<T extends Operation> {
|
|
81
|
+
op: T;
|
|
82
|
+
metadata?: OperationMetadata;
|
|
83
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function chainIdToNetworkName(chainId: bigint): string {
|
|
2
|
+
switch (chainId) {
|
|
3
|
+
case 1n:
|
|
4
|
+
return "mainnet";
|
|
5
|
+
case 5n:
|
|
6
|
+
return "goerli";
|
|
7
|
+
case 11155111n:
|
|
8
|
+
return "sepolia";
|
|
9
|
+
case 31337n:
|
|
10
|
+
return "localhost";
|
|
11
|
+
default:
|
|
12
|
+
throw new Error(`unsupported chainId: ${chainId}`);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const OPTIMISTIC_RECORD_TTL: number = 30 * 60 * 1000; // 30 minutes
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SignedOperation,
|
|
3
|
+
PreSignOperation,
|
|
4
|
+
PreProofJoinSplit,
|
|
5
|
+
PreSignJoinSplit,
|
|
6
|
+
Note,
|
|
7
|
+
IncludedNote,
|
|
8
|
+
Asset,
|
|
9
|
+
MapWithObjectKeys,
|
|
10
|
+
merklePathToIndex,
|
|
11
|
+
OperationStatus,
|
|
12
|
+
} from "@zill-protocol/core";
|
|
13
|
+
import { JoinSplitRequest } from "../operationRequest";
|
|
14
|
+
|
|
15
|
+
export function sortNotesByValue<T extends Note>(notes: T[]): T[] {
|
|
16
|
+
return notes.sort((a, b) => {
|
|
17
|
+
return Number(a.value - b.value);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getJoinSplitRequestTotalValue(
|
|
22
|
+
joinSplitRequest: JoinSplitRequest
|
|
23
|
+
): bigint {
|
|
24
|
+
let totalVal = joinSplitRequest.unwrapValue;
|
|
25
|
+
if (joinSplitRequest.payment !== undefined) {
|
|
26
|
+
totalVal += joinSplitRequest.payment.value;
|
|
27
|
+
}
|
|
28
|
+
return totalVal;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface NullifierWithMerkleIndex {
|
|
32
|
+
nullifier: bigint;
|
|
33
|
+
merkleIndex: bigint;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function getIncludedNotesFromOp(
|
|
37
|
+
op: PreSignOperation
|
|
38
|
+
): MapWithObjectKeys<Asset, IncludedNote[]> {
|
|
39
|
+
const notesMap: MapWithObjectKeys<Asset, IncludedNote[]> =
|
|
40
|
+
new MapWithObjectKeys();
|
|
41
|
+
|
|
42
|
+
op.joinSplits.forEach((joinSplit) => {
|
|
43
|
+
const { oldNoteA, oldNoteB } = joinSplit;
|
|
44
|
+
const notes = [oldNoteA, oldNoteB];
|
|
45
|
+
|
|
46
|
+
notes.forEach((note) => {
|
|
47
|
+
if (!notesMap.has(note.asset)) {
|
|
48
|
+
notesMap.set(note.asset, []);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const existingNotes = notesMap.get(note.asset)!;
|
|
52
|
+
notesMap.set(note.asset, [...existingNotes, note]);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
return notesMap;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// returns the merkle indices of the notes spent in an op
|
|
60
|
+
export function getMerkleIndicesAndNfsFromOp(
|
|
61
|
+
op: PreSignOperation | SignedOperation
|
|
62
|
+
): NullifierWithMerkleIndex[] {
|
|
63
|
+
return op.joinSplits.flatMap((joinSplit) => {
|
|
64
|
+
// get merkle index out of the path in the merkle proof
|
|
65
|
+
// how we do this depends on which kind of joinSplit it is (which depends on the op)
|
|
66
|
+
let merklePathA: bigint[];
|
|
67
|
+
let merklePathB: bigint[];
|
|
68
|
+
|
|
69
|
+
if (Object.hasOwn(joinSplit, "proofInputs")) {
|
|
70
|
+
// if it has "proofInputs", it's a `PreProofJoinSplit`
|
|
71
|
+
// in this case, we get the merkle path out of the proofInputs
|
|
72
|
+
merklePathA = (joinSplit as PreProofJoinSplit).proofInputs.merkleProofA
|
|
73
|
+
.path;
|
|
74
|
+
merklePathB = (joinSplit as PreProofJoinSplit).proofInputs.merkleProofB
|
|
75
|
+
.path;
|
|
76
|
+
} else {
|
|
77
|
+
// otherwise, it's a `PreSignJoinSplit`, in which case we get it out of the joinsplit itself
|
|
78
|
+
merklePathA = (joinSplit as PreSignJoinSplit).merkleProofA.path;
|
|
79
|
+
merklePathB = (joinSplit as PreSignJoinSplit).merkleProofB.path;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return [
|
|
83
|
+
{
|
|
84
|
+
merkleIndex: merklePathToIndex(merklePathA, "LEAF_TO_ROOT"),
|
|
85
|
+
nullifier: joinSplit.nullifierA,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
merkleIndex: merklePathToIndex(merklePathB, "LEAF_TO_ROOT"),
|
|
89
|
+
nullifier: joinSplit.nullifierB,
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function isFailedOpStatus(status: OperationStatus): boolean {
|
|
96
|
+
return (
|
|
97
|
+
status === OperationStatus.BUNDLE_REVERTED ||
|
|
98
|
+
status === OperationStatus.OPERATION_EXECUTION_FAILED ||
|
|
99
|
+
status === OperationStatus.OPERATION_PROCESSING_FAILED
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function isTerminalOpStatus(status: OperationStatus): boolean {
|
|
104
|
+
return (
|
|
105
|
+
status === OperationStatus.EXECUTED_SUCCESS || isFailedOpStatus(status)
|
|
106
|
+
);
|
|
107
|
+
}
|