@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,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./constants"), exports);
|
|
18
|
+
__exportStar(require("./misc"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B;AAC5B,yCAAuB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SignedOperation, PreSignOperation, Note, IncludedNote, Asset, MapWithObjectKeys, OperationStatus } from "@zill-protocol/core";
|
|
2
|
+
import { JoinSplitRequest } from "../operationRequest";
|
|
3
|
+
export declare function sortNotesByValue<T extends Note>(notes: T[]): T[];
|
|
4
|
+
export declare function getJoinSplitRequestTotalValue(joinSplitRequest: JoinSplitRequest): bigint;
|
|
5
|
+
export interface NullifierWithMerkleIndex {
|
|
6
|
+
nullifier: bigint;
|
|
7
|
+
merkleIndex: bigint;
|
|
8
|
+
}
|
|
9
|
+
export declare function getIncludedNotesFromOp(op: PreSignOperation): MapWithObjectKeys<Asset, IncludedNote[]>;
|
|
10
|
+
export declare function getMerkleIndicesAndNfsFromOp(op: PreSignOperation | SignedOperation): NullifierWithMerkleIndex[];
|
|
11
|
+
export declare function isFailedOpStatus(status: OperationStatus): boolean;
|
|
12
|
+
export declare function isTerminalOpStatus(status: OperationStatus): boolean;
|
|
13
|
+
//# sourceMappingURL=misc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misc.d.ts","sourceRoot":"","sources":["../../../src/utils/misc.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,gBAAgB,EAGhB,IAAI,EACJ,YAAY,EACZ,KAAK,EACL,iBAAiB,EAEjB,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAIhE;AAED,wBAAgB,6BAA6B,CAC3C,gBAAgB,EAAE,gBAAgB,GACjC,MAAM,CAMR;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,gBAAgB,GACnB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC,CAmB1C;AAGD,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,gBAAgB,GAAG,eAAe,GACrC,wBAAwB,EAAE,CA+B5B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAMjE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAInE"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sortNotesByValue = sortNotesByValue;
|
|
4
|
+
exports.getJoinSplitRequestTotalValue = getJoinSplitRequestTotalValue;
|
|
5
|
+
exports.getIncludedNotesFromOp = getIncludedNotesFromOp;
|
|
6
|
+
exports.getMerkleIndicesAndNfsFromOp = getMerkleIndicesAndNfsFromOp;
|
|
7
|
+
exports.isFailedOpStatus = isFailedOpStatus;
|
|
8
|
+
exports.isTerminalOpStatus = isTerminalOpStatus;
|
|
9
|
+
const core_1 = require("@zill-protocol/core");
|
|
10
|
+
function sortNotesByValue(notes) {
|
|
11
|
+
return notes.sort((a, b) => {
|
|
12
|
+
return Number(a.value - b.value);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function getJoinSplitRequestTotalValue(joinSplitRequest) {
|
|
16
|
+
let totalVal = joinSplitRequest.unwrapValue;
|
|
17
|
+
if (joinSplitRequest.payment !== undefined) {
|
|
18
|
+
totalVal += joinSplitRequest.payment.value;
|
|
19
|
+
}
|
|
20
|
+
return totalVal;
|
|
21
|
+
}
|
|
22
|
+
function getIncludedNotesFromOp(op) {
|
|
23
|
+
const notesMap = new core_1.MapWithObjectKeys();
|
|
24
|
+
op.joinSplits.forEach((joinSplit) => {
|
|
25
|
+
const { oldNoteA, oldNoteB } = joinSplit;
|
|
26
|
+
const notes = [oldNoteA, oldNoteB];
|
|
27
|
+
notes.forEach((note) => {
|
|
28
|
+
if (!notesMap.has(note.asset)) {
|
|
29
|
+
notesMap.set(note.asset, []);
|
|
30
|
+
}
|
|
31
|
+
const existingNotes = notesMap.get(note.asset);
|
|
32
|
+
notesMap.set(note.asset, [...existingNotes, note]);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
return notesMap;
|
|
36
|
+
}
|
|
37
|
+
// returns the merkle indices of the notes spent in an op
|
|
38
|
+
function getMerkleIndicesAndNfsFromOp(op) {
|
|
39
|
+
return op.joinSplits.flatMap((joinSplit) => {
|
|
40
|
+
// get merkle index out of the path in the merkle proof
|
|
41
|
+
// how we do this depends on which kind of joinSplit it is (which depends on the op)
|
|
42
|
+
let merklePathA;
|
|
43
|
+
let merklePathB;
|
|
44
|
+
if (Object.hasOwn(joinSplit, "proofInputs")) {
|
|
45
|
+
// if it has "proofInputs", it's a `PreProofJoinSplit`
|
|
46
|
+
// in this case, we get the merkle path out of the proofInputs
|
|
47
|
+
merklePathA = joinSplit.proofInputs.merkleProofA
|
|
48
|
+
.path;
|
|
49
|
+
merklePathB = joinSplit.proofInputs.merkleProofB
|
|
50
|
+
.path;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// otherwise, it's a `PreSignJoinSplit`, in which case we get it out of the joinsplit itself
|
|
54
|
+
merklePathA = joinSplit.merkleProofA.path;
|
|
55
|
+
merklePathB = joinSplit.merkleProofB.path;
|
|
56
|
+
}
|
|
57
|
+
return [
|
|
58
|
+
{
|
|
59
|
+
merkleIndex: (0, core_1.merklePathToIndex)(merklePathA, "LEAF_TO_ROOT"),
|
|
60
|
+
nullifier: joinSplit.nullifierA,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
merkleIndex: (0, core_1.merklePathToIndex)(merklePathB, "LEAF_TO_ROOT"),
|
|
64
|
+
nullifier: joinSplit.nullifierB,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
function isFailedOpStatus(status) {
|
|
70
|
+
return (status === core_1.OperationStatus.BUNDLE_REVERTED ||
|
|
71
|
+
status === core_1.OperationStatus.OPERATION_EXECUTION_FAILED ||
|
|
72
|
+
status === core_1.OperationStatus.OPERATION_PROCESSING_FAILED);
|
|
73
|
+
}
|
|
74
|
+
function isTerminalOpStatus(status) {
|
|
75
|
+
return (status === core_1.OperationStatus.EXECUTED_SUCCESS || isFailedOpStatus(status));
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=misc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"misc.js","sourceRoot":"","sources":["../../../src/utils/misc.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["../index.ts","../src/nocturneclient.ts","../src/nocturnedb.ts","../src/optracker.ts","../src/index.ts","../src/oprequestgas.ts","../src/prepareoperation.ts","../src/proveoperation.ts","../src/signoperation.ts","../src/snapjsonrpc.ts","../src/syncsdk.ts","../src/types.ts","../src/conversion/converter.ts","../src/conversion/index.ts","../src/conversion/mock.ts","../src/operationrequest/builder.ts","../src/operationrequest/index.ts","../src/operationrequest/operationrequest.ts","../src/utils/constants.ts","../src/utils/index.ts","../src/utils/misc.ts","../package.json"],"version":"5.9.3"}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zill-protocol/client",
|
|
3
|
+
"version": "4.1.2",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*",
|
|
9
|
+
"src/**/*"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "yarn clean; tsc --build",
|
|
13
|
+
"check": "tsc --noEmit",
|
|
14
|
+
"clean": "rm -rf .turbo dist",
|
|
15
|
+
"gen-test-cases:operationhash": "npx ts-node ./scripts/genOperationHashTestCase.ts",
|
|
16
|
+
"gen-test-cases:registryentry": "npx ts-node ./scripts/genCanonAddrRegistryEntryHashTestCase.ts",
|
|
17
|
+
"gen-test-cases:poseidonext": "npx ts-node ./scripts/genPoseidonExtTestCase.ts",
|
|
18
|
+
"lint": "eslint --fix src --ext .ts",
|
|
19
|
+
"prettier:check": "prettier --check ./src ./test",
|
|
20
|
+
"prettier:write": "prettier --write ./src ./test",
|
|
21
|
+
"publish": "yarn build; yarn npm publish --access public",
|
|
22
|
+
"test:unit": "mocha --require ts-node/register test/** --timeout 45000 --exit"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@zill-protocol/config": "^1.7.4",
|
|
26
|
+
"@zill-protocol/contracts": "^3.0.0",
|
|
27
|
+
"@zill-protocol/core": "^3.3.1",
|
|
28
|
+
"@zill-protocol/crypto": "^0.4.3",
|
|
29
|
+
"async-mutex": "^0.4.0",
|
|
30
|
+
"big-integer": "^1.6.42",
|
|
31
|
+
"bigint-json-serialization": "^1.0.1",
|
|
32
|
+
"ethers": "^5.7.2",
|
|
33
|
+
"js-sha256": "^0.9.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/chai": "^4.3.3",
|
|
37
|
+
"@types/chai-as-promised": "^7.1.5",
|
|
38
|
+
"@types/mocha": "^10.0.0",
|
|
39
|
+
"@types/node": "^25.0.1",
|
|
40
|
+
"@types/sinon": "^10.0.13",
|
|
41
|
+
"@types/sinon-chai": "^3.2.9",
|
|
42
|
+
"@typescript-eslint/eslint-plugin": "^8.49.0",
|
|
43
|
+
"@typescript-eslint/parser": "^8.49.0",
|
|
44
|
+
"chai": "^4.3.6",
|
|
45
|
+
"chai-as-promised": "^7.1.1",
|
|
46
|
+
"eslint": "^9.39.2",
|
|
47
|
+
"eslint-config-prettier": "^10.1.8",
|
|
48
|
+
"mocha": "^10.1.0",
|
|
49
|
+
"prettier": "^3.7.4",
|
|
50
|
+
"sinon": "^15.0.1",
|
|
51
|
+
"sinon-chai": "^3.7.0",
|
|
52
|
+
"ts-node": "^10.9.2",
|
|
53
|
+
"typescript": "^5.9.3"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { NocturneConfig, loadNocturneConfig } from "@zill-protocol/config";
|
|
2
|
+
import { Handler, Handler__factory } from "@zill-protocol/contracts";
|
|
3
|
+
import { NocturneViewer, StealthAddressTrait } from "@zill-protocol/crypto";
|
|
4
|
+
import ethers from "ethers";
|
|
5
|
+
import { GetNotesOpts, NocturneDB } from "./NocturneDB";
|
|
6
|
+
import { OpTracker } from "./OpTracker";
|
|
7
|
+
import { EthToTokenConverter } from "./conversion";
|
|
8
|
+
import { handleGasForOperationRequest } from "./opRequestGas";
|
|
9
|
+
import {
|
|
10
|
+
OperationRequest,
|
|
11
|
+
ensureOpRequestChainInfo,
|
|
12
|
+
} from "./operationRequest/operationRequest";
|
|
13
|
+
import { prepareOperation } from "./prepareOperation";
|
|
14
|
+
import { SyncOpts, syncSDK } from "./syncSDK";
|
|
15
|
+
import { OpHistoryRecord, OperationMetadata, PendingOutputRecord } from "./types";
|
|
16
|
+
import {
|
|
17
|
+
getJoinSplitRequestTotalValue,
|
|
18
|
+
getMerkleIndicesAndNfsFromOp,
|
|
19
|
+
} from "./utils";
|
|
20
|
+
|
|
21
|
+
import {
|
|
22
|
+
Asset,
|
|
23
|
+
AssetTrait,
|
|
24
|
+
AssetWithBalance,
|
|
25
|
+
IncludedNote,
|
|
26
|
+
MapWithObjectKeys,
|
|
27
|
+
Note,
|
|
28
|
+
NoteTrait,
|
|
29
|
+
OperationStatus,
|
|
30
|
+
OperationTrait,
|
|
31
|
+
PreSignOperation,
|
|
32
|
+
SDKSyncAdapter,
|
|
33
|
+
SignedOperation,
|
|
34
|
+
SparseMerkleProver,
|
|
35
|
+
TotalEntityIndex,
|
|
36
|
+
TotalEntityIndexTrait,
|
|
37
|
+
decryptNote,
|
|
38
|
+
maxArray,
|
|
39
|
+
} from "@zill-protocol/core";
|
|
40
|
+
|
|
41
|
+
const PRUNE_OPTIMISTIC_NFS_TIMER = 60 * 1000; // 1 minute
|
|
42
|
+
|
|
43
|
+
export class NocturneClient {
|
|
44
|
+
protected provider: ethers.providers.Provider;
|
|
45
|
+
protected config: NocturneConfig;
|
|
46
|
+
protected handlerContract: Handler;
|
|
47
|
+
protected merkleProver: SparseMerkleProver;
|
|
48
|
+
protected db: NocturneDB;
|
|
49
|
+
protected syncAdapter: SDKSyncAdapter;
|
|
50
|
+
protected tokenConverter: EthToTokenConverter;
|
|
51
|
+
protected opTracker: OpTracker;
|
|
52
|
+
protected pendingOutputsListeners: Set<
|
|
53
|
+
(outputs: PendingOutputRecord[]) => void
|
|
54
|
+
> = new Set();
|
|
55
|
+
|
|
56
|
+
readonly viewer: NocturneViewer;
|
|
57
|
+
readonly gasAssets: Map<string, Asset>;
|
|
58
|
+
|
|
59
|
+
constructor(
|
|
60
|
+
viewer: NocturneViewer,
|
|
61
|
+
provider: ethers.providers.Provider,
|
|
62
|
+
configOrNetworkName: NocturneConfig | string,
|
|
63
|
+
merkleProver: SparseMerkleProver,
|
|
64
|
+
db: NocturneDB,
|
|
65
|
+
syncAdapter: SDKSyncAdapter,
|
|
66
|
+
tokenConverter: EthToTokenConverter,
|
|
67
|
+
nulliferChecker: OpTracker
|
|
68
|
+
) {
|
|
69
|
+
if (typeof configOrNetworkName == "string") {
|
|
70
|
+
this.config = loadNocturneConfig(configOrNetworkName);
|
|
71
|
+
} else {
|
|
72
|
+
this.config = configOrNetworkName;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
this.provider = provider;
|
|
76
|
+
|
|
77
|
+
this.gasAssets = new Map(
|
|
78
|
+
Array.from(this.config.erc20s.entries())
|
|
79
|
+
.filter(([_, config]) => config.isGasAsset)
|
|
80
|
+
.map(([ticker, config]) => {
|
|
81
|
+
return [ticker, AssetTrait.erc20AddressToAsset(config.address)];
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
this.viewer = viewer;
|
|
86
|
+
console.log(`Canonical address: `, this.viewer.canonicalAddress());
|
|
87
|
+
this.handlerContract = Handler__factory.connect(
|
|
88
|
+
this.config.handlerAddress,
|
|
89
|
+
provider
|
|
90
|
+
);
|
|
91
|
+
this.merkleProver = merkleProver;
|
|
92
|
+
this.db = db;
|
|
93
|
+
this.syncAdapter = syncAdapter;
|
|
94
|
+
this.tokenConverter = tokenConverter;
|
|
95
|
+
this.opTracker = nulliferChecker;
|
|
96
|
+
|
|
97
|
+
// set an interval to prune optimistic nfs to ensure they don't get stuck
|
|
98
|
+
const prune = async () => {
|
|
99
|
+
await this.pruneOptimisticNullifiers();
|
|
100
|
+
setTimeout(prune, PRUNE_OPTIMISTIC_NFS_TIMER);
|
|
101
|
+
};
|
|
102
|
+
void prune();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async clearDb(): Promise<void> {
|
|
106
|
+
await this.db.kv.clear();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Sync SDK, returning last synced merkle index of last state diff
|
|
110
|
+
async sync(opts?: SyncOpts): Promise<number | undefined> {
|
|
111
|
+
const hasListeners = this.pendingOutputsListeners.size > 0;
|
|
112
|
+
const shouldNotify = hasListeners || opts?.onPendingOutputsConfirmed;
|
|
113
|
+
const onPendingOutputsConfirmed = shouldNotify
|
|
114
|
+
? (outputs: PendingOutputRecord[]) => {
|
|
115
|
+
if (!outputs.length) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (hasListeners) {
|
|
119
|
+
for (const listener of this.pendingOutputsListeners) {
|
|
120
|
+
try {
|
|
121
|
+
listener(outputs);
|
|
122
|
+
} catch (err) {
|
|
123
|
+
console.warn("[sync] pending output listener failed", err);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
opts?.onPendingOutputsConfirmed?.(outputs);
|
|
128
|
+
}
|
|
129
|
+
: undefined;
|
|
130
|
+
|
|
131
|
+
const baseOpts = opts
|
|
132
|
+
? {
|
|
133
|
+
...opts,
|
|
134
|
+
finalityBlocks: opts.finalityBlocks ?? this.config.finalityBlocks,
|
|
135
|
+
}
|
|
136
|
+
: undefined;
|
|
137
|
+
const mergedOpts = onPendingOutputsConfirmed
|
|
138
|
+
? { ...(baseOpts ?? {}), onPendingOutputsConfirmed }
|
|
139
|
+
: baseOpts;
|
|
140
|
+
|
|
141
|
+
const latestSyncedMerkleIndex = await syncSDK(
|
|
142
|
+
{ viewer: this.viewer },
|
|
143
|
+
this.syncAdapter,
|
|
144
|
+
this.db,
|
|
145
|
+
this.merkleProver,
|
|
146
|
+
mergedOpts
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
return latestSyncedMerkleIndex;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async prepareOperation(
|
|
153
|
+
opRequest: OperationRequest,
|
|
154
|
+
gasMultiplier: number
|
|
155
|
+
): Promise<PreSignOperation> {
|
|
156
|
+
opRequest = await ensureOpRequestChainInfo(opRequest, this.provider);
|
|
157
|
+
|
|
158
|
+
const deps = {
|
|
159
|
+
db: this.db,
|
|
160
|
+
gasAssets: this.gasAssets,
|
|
161
|
+
tokenConverter: this.tokenConverter,
|
|
162
|
+
handlerContract: this.handlerContract,
|
|
163
|
+
merkle: this.merkleProver,
|
|
164
|
+
viewer: this.viewer,
|
|
165
|
+
};
|
|
166
|
+
const gasAccountedOpRequest = await handleGasForOperationRequest(
|
|
167
|
+
deps,
|
|
168
|
+
opRequest,
|
|
169
|
+
gasMultiplier
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return await prepareOperation(deps, gasAccountedOpRequest);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async getAllAssetBalances(opts?: GetNotesOpts): Promise<AssetWithBalance[]> {
|
|
176
|
+
const notes = await this.db.getAllNotes(opts);
|
|
177
|
+
return Array.from(notes.entries()).map(([assetString, notes]) => {
|
|
178
|
+
const asset = NocturneDB.parseAssetKey(assetString);
|
|
179
|
+
const balance = notes.reduce((a, b) => a + b.value, 0n);
|
|
180
|
+
return {
|
|
181
|
+
asset,
|
|
182
|
+
balance,
|
|
183
|
+
numNotes: notes.length,
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async getBalanceForAsset(asset: Asset, opts?: GetNotesOpts): Promise<bigint> {
|
|
189
|
+
return await this.db.getBalanceForAsset(asset, opts);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async getNotesForAsset(
|
|
193
|
+
asset: Asset,
|
|
194
|
+
opts?: GetNotesOpts
|
|
195
|
+
): Promise<IncludedNote[]> {
|
|
196
|
+
return await this.db.getNotesForAsset(asset, opts);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async getPrivateBalanceBreakdown(asset: Asset): Promise<{
|
|
200
|
+
confirmed: bigint;
|
|
201
|
+
pendingUncommitted: bigint;
|
|
202
|
+
pendingLocal: bigint;
|
|
203
|
+
pending: bigint;
|
|
204
|
+
total: bigint;
|
|
205
|
+
}> {
|
|
206
|
+
const [confirmed, totalUncommitted, pendingLocal] = await Promise.all([
|
|
207
|
+
this.db.getBalanceForAsset(asset),
|
|
208
|
+
this.db.getBalanceForAsset(asset, { includeUncommitted: true }),
|
|
209
|
+
this.db.getPendingBalanceForAsset(asset),
|
|
210
|
+
]);
|
|
211
|
+
const pendingUncommitted =
|
|
212
|
+
totalUncommitted > confirmed ? totalUncommitted - confirmed : 0n;
|
|
213
|
+
const pending = pendingUncommitted + pendingLocal;
|
|
214
|
+
return {
|
|
215
|
+
confirmed,
|
|
216
|
+
pendingUncommitted,
|
|
217
|
+
pendingLocal,
|
|
218
|
+
pending,
|
|
219
|
+
total: confirmed + pending,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async getPendingBalanceForAsset(asset: Asset): Promise<bigint> {
|
|
224
|
+
return await this.db.getPendingBalanceForAsset(asset);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async getPendingOutputs(
|
|
228
|
+
opts?: { asset?: Asset }
|
|
229
|
+
): Promise<PendingOutputRecord[]> {
|
|
230
|
+
return await this.db.getPendingOutputs(opts);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async listPendingNotes(
|
|
234
|
+
opts?: { asset?: Asset }
|
|
235
|
+
): Promise<PendingOutputRecord[]> {
|
|
236
|
+
return await this.db.getPendingOutputs(opts);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async addPendingOutputs(outputs: PendingOutputRecord[]): Promise<void> {
|
|
240
|
+
await this.db.addPendingOutputs(outputs);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async addPendingOutputsFromOperation(
|
|
244
|
+
op: PreSignOperation | SignedOperation
|
|
245
|
+
): Promise<void> {
|
|
246
|
+
const senderOwner = StealthAddressTrait.fromCanonAddress(
|
|
247
|
+
this.viewer.canonicalAddress()
|
|
248
|
+
);
|
|
249
|
+
const senderKey = StealthAddressTrait.toString(senderOwner);
|
|
250
|
+
const digest = OperationTrait.computeDigest(op);
|
|
251
|
+
const outputs: PendingOutputRecord[] = [];
|
|
252
|
+
|
|
253
|
+
for (const joinSplit of op.joinSplits) {
|
|
254
|
+
let notes: Note[] = [];
|
|
255
|
+
if ("newNoteA" in joinSplit) {
|
|
256
|
+
notes = [joinSplit.newNoteA, joinSplit.newNoteB];
|
|
257
|
+
} else {
|
|
258
|
+
const decryptedNotes = [
|
|
259
|
+
joinSplit.newNoteAEncrypted,
|
|
260
|
+
joinSplit.newNoteBEncrypted,
|
|
261
|
+
]
|
|
262
|
+
.map((encryptedNote): Note | null => {
|
|
263
|
+
try {
|
|
264
|
+
const { sender, ...note } = decryptNote(this.viewer, encryptedNote);
|
|
265
|
+
return note;
|
|
266
|
+
} catch (err) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
.filter((note): note is Note => note !== null);
|
|
271
|
+
notes = decryptedNotes;
|
|
272
|
+
}
|
|
273
|
+
for (const note of notes) {
|
|
274
|
+
if (note.value <= 0n) {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
const ownerKey = StealthAddressTrait.toString(note.owner);
|
|
278
|
+
if (ownerKey !== senderKey) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
outputs.push({
|
|
282
|
+
commitment: NoteTrait.toCommitment(note),
|
|
283
|
+
asset: note.asset,
|
|
284
|
+
value: note.value,
|
|
285
|
+
createdAt: Date.now(),
|
|
286
|
+
opDigest: digest,
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
await this.db.addPendingOutputs(outputs);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async removePendingOutputsByOpDigest(opDigest: bigint): Promise<void> {
|
|
295
|
+
await this.db.removePendingOutputsByOpDigest(opDigest);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
onPendingNoteConfirmed(
|
|
299
|
+
listener: (outputs: PendingOutputRecord[]) => void
|
|
300
|
+
): () => void {
|
|
301
|
+
this.pendingOutputsListeners.add(listener);
|
|
302
|
+
return () => {
|
|
303
|
+
this.pendingOutputsListeners.delete(listener);
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async getLatestSyncedMerkleIndex(): Promise<number | undefined> {
|
|
308
|
+
return await this.db.latestSyncedMerkleIndex();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async getLatestCommittedMerkleIndex(): Promise<number | undefined> {
|
|
312
|
+
return await this.db.latestCommittedMerkleIndex();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Force-nullify notes locally by nullifier. This is useful as a mitigation
|
|
317
|
+
* when the sync source cannot supply nullifiers (e.g. Mirror down) but the
|
|
318
|
+
* bundler reports the nullifier already exists on-chain.
|
|
319
|
+
*/
|
|
320
|
+
async forceNullifyNotes(nullifiers: bigint[]): Promise<number[]> {
|
|
321
|
+
return await this.db.nullifyNotes(nullifiers);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
getCurrentTreeRoot(): bigint {
|
|
325
|
+
return this.merkleProver.getRoot();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async hasEnoughBalanceForOperationRequest(
|
|
329
|
+
opRequest: OperationRequest
|
|
330
|
+
): Promise<boolean> {
|
|
331
|
+
const assetRequestedAmounts = new MapWithObjectKeys<Asset, bigint>();
|
|
332
|
+
for (const joinSplitRequest of opRequest.joinSplitRequests) {
|
|
333
|
+
const asset = joinSplitRequest.asset;
|
|
334
|
+
let currentAmount = assetRequestedAmounts.get(asset) || 0n;
|
|
335
|
+
currentAmount += getJoinSplitRequestTotalValue(joinSplitRequest);
|
|
336
|
+
assetRequestedAmounts.set(asset, currentAmount);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
for (const [asset, requestedAmount] of assetRequestedAmounts.entries()) {
|
|
340
|
+
const balance = await this.db.getBalanceForAsset(asset);
|
|
341
|
+
if (balance < requestedAmount) {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async getCreationBlockOfNewestNoteInOp(
|
|
350
|
+
op: PreSignOperation | SignedOperation
|
|
351
|
+
): Promise<number> {
|
|
352
|
+
const totalEntityIndex = await getTotalEntityIndexOfNewestNoteInOp(
|
|
353
|
+
this.db,
|
|
354
|
+
op
|
|
355
|
+
);
|
|
356
|
+
return Number(
|
|
357
|
+
TotalEntityIndexTrait.toComponents(totalEntityIndex).blockNumber
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
async addOpToHistory(
|
|
362
|
+
op: PreSignOperation | SignedOperation,
|
|
363
|
+
metadata: OperationMetadata
|
|
364
|
+
): Promise<void> {
|
|
365
|
+
await this.db.addOpToHistory(op, metadata);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async removeOpFromHistory(digest: bigint): Promise<void> {
|
|
369
|
+
await this.db.removeOpFromHistory(digest);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
async getOpHistory(includePending?: boolean): Promise<OpHistoryRecord[]> {
|
|
373
|
+
return await this.db.getHistory(includePending);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async getOpHistoryRecord(
|
|
377
|
+
digest: bigint
|
|
378
|
+
): Promise<OpHistoryRecord | undefined> {
|
|
379
|
+
return await this.db.getHistoryRecord(digest);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async setOpStatusInHistory(
|
|
383
|
+
digest: bigint,
|
|
384
|
+
status: OperationStatus
|
|
385
|
+
): Promise<void> {
|
|
386
|
+
await this.db.setStatusForOp(digest, status);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async pruneOptimisticNullifiers(): Promise<void> {
|
|
390
|
+
await this.db.pruneOptimisticNFs();
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export async function getTotalEntityIndexOfNewestNoteInOp(
|
|
395
|
+
db: NocturneDB,
|
|
396
|
+
op: PreSignOperation | SignedOperation
|
|
397
|
+
): Promise<TotalEntityIndex> {
|
|
398
|
+
// get the max merkle index of any note in any joinsplit in the op
|
|
399
|
+
const maxMerkleIndex = maxArray(
|
|
400
|
+
getMerkleIndicesAndNfsFromOp(op).map(({ merkleIndex }) => merkleIndex)
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
// get the corresponding TotalEntityIndex
|
|
404
|
+
const totalEntityIndex = await db.getTotalEntityIndexForMerkleIndex(
|
|
405
|
+
Number(maxMerkleIndex)
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
if (totalEntityIndex === undefined) {
|
|
409
|
+
throw new Error(
|
|
410
|
+
`totalEntityIndex not found for newest note with merkle index ${maxMerkleIndex}`
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
return totalEntityIndex;
|
|
415
|
+
}
|