prividium 0.20.0 → 0.21.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/bin/cli.js +8 -1
- package/dist/cli/base-cli.d.ts +5 -0
- package/dist/cli/base-cli.js +11 -0
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/doctor/clients/browser-auth.d.ts +1 -0
- package/dist/cli/commands/doctor/clients/http.d.ts +3 -0
- package/dist/cli/commands/doctor/clients/rpc.d.ts +19 -0
- package/dist/cli/commands/doctor/clients/wallet-api.d.ts +7 -0
- package/dist/cli/commands/doctor/constants.d.ts +1 -0
- package/dist/cli/commands/doctor/probes/authentication/authentication.d.ts +4 -0
- package/dist/cli/commands/doctor/probes/authentication/wallet-preconditions.d.ts +2 -0
- package/dist/cli/commands/doctor/probes/authentication.d.ts +2 -0
- package/dist/cli/commands/doctor/probes/bridging/bridging.d.ts +5 -0
- package/dist/cli/commands/doctor/probes/bridging.d.ts +2 -0
- package/dist/cli/commands/doctor/probes/global/input-validation.d.ts +3 -0
- package/dist/cli/commands/doctor/probes/global/reachability.d.ts +5 -0
- package/dist/cli/commands/doctor/probes/global.d.ts +2 -0
- package/dist/cli/commands/doctor/probes/wallet/authenticated-rpc.d.ts +3 -0
- package/dist/cli/commands/doctor/probes/wallet/authorization-and-wallet-rpc.d.ts +5 -0
- package/dist/cli/commands/doctor/probes/wallet-api.d.ts +2 -0
- package/dist/cli/commands/doctor/probes/wallet.d.ts +2 -0
- package/dist/cli/commands/doctor/profile.d.ts +12 -0
- package/dist/cli/commands/doctor/report/build.d.ts +2 -0
- package/dist/cli/commands/doctor/report/render.d.ts +2 -0
- package/dist/cli/commands/doctor/stages.d.ts +2 -0
- package/dist/cli/commands/doctor/types.d.ts +90 -0
- package/dist/cli/commands/doctor/utils.d.ts +18 -0
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/proxy.d.ts +2 -0
- package/dist/cli/{cli/commands → commands}/proxy.js +1 -3
- package/dist/cli/commands/utils/schemas.d.ts +3 -0
- package/dist/cli/commands/utils/schemas.js +3 -0
- package/dist/cli/commands/utils/show-prividium-header.d.ts +2 -0
- package/dist/cli/commands/utils/url-config.d.ts +18 -0
- package/dist/cli/commands/verify/account-data-cmd.d.ts +14 -0
- package/dist/cli/commands/verify/account-data-cmd.js +102 -0
- package/dist/cli/commands/verify/erc20-balance-cmd.d.ts +17 -0
- package/dist/cli/commands/verify/erc20-balance-cmd.js +111 -0
- package/dist/cli/commands/verify/erc20-supply-cmd.d.ts +16 -0
- package/dist/cli/commands/verify/erc20-supply-cmd.js +102 -0
- package/dist/cli/commands/verify/utils.d.ts +5 -0
- package/dist/cli/commands/verify/utils.js +38 -0
- package/dist/cli/commands/verify/verify-workflow.d.ts +8 -0
- package/dist/cli/commands/verify/verify-workflow.js +41 -0
- package/dist/cli/commands/verify.d.ts +18 -0
- package/dist/cli/commands/verify.js +43 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +11 -0
- package/dist/cli/server/config-file.d.ts +62 -0
- package/dist/cli/server/config-file.js +167 -0
- package/dist/cli/server/connection-workflow.d.ts +12 -0
- package/dist/cli/server/server.d.ts +28 -0
- package/dist/sdk/create-prividium-client.d.ts +1 -1
- package/dist/sdk/create-prividium-client.js +1 -1
- package/dist/sdk/selective-disclosure/account-properties.d.ts +7 -1
- package/dist/sdk/selective-disclosure/account-properties.js +22 -10
- package/dist/sdk/selective-disclosure/utils.d.ts +2 -0
- package/dist/sdk/selective-disclosure/utils.js +3 -0
- package/dist/sdk/selective-disclosure/verify-disclosure.d.ts +35 -12
- package/dist/sdk/selective-disclosure/verify-disclosure.js +60 -25
- package/dist/tsconfig.cli.tsbuildinfo +1 -0
- package/dist/tsconfig.sdk.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/dist/cli/cli/base-cli.js +0 -9
- package/dist/cli/cli/index.js +0 -13
- package/dist/cli/cli/server/config-file.js +0 -78
- package/dist/cli/src/error-utils.js +0 -73
- package/dist/cli/src/memory-storage.js +0 -12
- package/dist/cli/src/rpc-error-codes.js +0 -28
- package/dist/cli/src/siwe-auth.js +0 -70
- package/dist/cli/src/storage.js +0 -142
- package/dist/cli/src/types.js +0 -46
- package/dist/cli/tsconfig.cli.tsbuildinfo +0 -1
- /package/dist/cli/cli/{static/callback.html → callback.html} +0 -0
- /package/dist/cli/cli/{static/start.html → start.html} +0 -0
- /package/dist/cli/{cli/commands → commands}/config.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/clients/browser-auth.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/clients/http.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/clients/rpc.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/clients/wallet-api.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/constants.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/authentication/authentication.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/authentication/wallet-preconditions.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/authentication.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/bridging/bridging.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/bridging.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/global/input-validation.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/global/reachability.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/global.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/wallet/authenticated-rpc.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/wallet/authorization-and-wallet-rpc.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/wallet-api.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/probes/wallet.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/profile.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/report/build.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/report/render.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/stages.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/types.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor/utils.js +0 -0
- /package/dist/cli/{cli/commands → commands}/doctor.js +0 -0
- /package/dist/cli/{cli/commands → commands}/utils/show-prividium-header.js +0 -0
- /package/dist/cli/{cli/commands → commands}/utils/url-config.js +0 -0
- /package/dist/cli/{cli/server → server}/connection-workflow.js +0 -0
- /package/dist/cli/{cli/server → server}/server.js +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { selectiveDisclosureActions, verifyEthCallDisclosure } from '@repo/prividium-sdk';
|
|
2
|
+
import { createPublicClient, getAddress, hexToBigInt, http } from 'viem';
|
|
3
|
+
import { ConfigFile } from '../../server/config-file.js';
|
|
4
|
+
import { loadBytecodes, parseBlockNumber } from './utils.js';
|
|
5
|
+
import { VerifyWorkflow } from './verify-workflow.js';
|
|
6
|
+
export async function erc20SupplyCmd(opts) {
|
|
7
|
+
const wf = new VerifyWorkflow();
|
|
8
|
+
const configFile = new ConfigFile(opts.configPath);
|
|
9
|
+
wf.start();
|
|
10
|
+
try {
|
|
11
|
+
const bytecodes = loadBytecodes(opts.bytecodesFile, opts.bytecodes);
|
|
12
|
+
const { apiUrl, l1RpcUrl, localZksyncOsRpcUrl, diamondAddress } = await configFile.readAndUpdatedRequiredFields({
|
|
13
|
+
apiUrl: opts.apiUrl,
|
|
14
|
+
l1RpcUrl: opts.l1Rpc,
|
|
15
|
+
localZksyncOsRpcUrl: opts.zkSyncOsRpc,
|
|
16
|
+
diamondAddress: opts.diamondAddress
|
|
17
|
+
});
|
|
18
|
+
const prividiumRpcUrl = new URL('/rpc', apiUrl).toString();
|
|
19
|
+
const l1Client = createPublicClient({ transport: http(l1RpcUrl) });
|
|
20
|
+
const l2Client = createPublicClient({ transport: http(localZksyncOsRpcUrl) });
|
|
21
|
+
const prividiumRpc = createPublicClient({ transport: http(prividiumRpcUrl) }).extend(selectiveDisclosureActions);
|
|
22
|
+
const disclosure = await wf
|
|
23
|
+
.step('Gathering proofs', 'Proofs fetched ✅', () => prividiumRpc.tokenSupplyDisclosure(getAddress(opts.tokenAddr), parseBlockNumber(opts.blockNumber)))
|
|
24
|
+
.catch((e) => {
|
|
25
|
+
const detail = e instanceof Error ? e.message : 'unexpected error';
|
|
26
|
+
wf.error(`Error fetching proofs: ${detail}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
});
|
|
29
|
+
if (opts.verbose) {
|
|
30
|
+
wf.message(JSON.stringify(disclosure, null, 2));
|
|
31
|
+
}
|
|
32
|
+
const res = await wf
|
|
33
|
+
.step('Verifying proofs', 'Proofs verified ✅', () => verifyEthCallDisclosure({
|
|
34
|
+
disclosure,
|
|
35
|
+
l1Client,
|
|
36
|
+
l2Client,
|
|
37
|
+
diamondAddress: getAddress(diamondAddress),
|
|
38
|
+
contractBytecodes: bytecodes,
|
|
39
|
+
batchNumber: disclosure.batchNumber
|
|
40
|
+
}))
|
|
41
|
+
.catch((e) => {
|
|
42
|
+
const detail = e instanceof Error ? e.message : 'unexpected error';
|
|
43
|
+
wf.error(`Verification failed: ${detail}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
46
|
+
if (res.success) {
|
|
47
|
+
wf.success(`Supply: ${hexToBigInt(disclosure.result)}`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
wf.error(`Verification failed: ${res.errorMsg}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
if (typeof e === 'object' && e && 'message' in e && typeof e.message === 'string') {
|
|
56
|
+
wf.error(e.message);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
wf.error('Unexpected error');
|
|
60
|
+
}
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export const addErc20SupplyCmd = (cli) => cli.command('erc20-total-supply <tokenAddr>', 'Verify an ERC-20 total supply using proofs from L1', (yargs) => yargs
|
|
65
|
+
.positional('tokenAddr', {
|
|
66
|
+
description: 'Address of the token',
|
|
67
|
+
type: 'string',
|
|
68
|
+
demandOption: true
|
|
69
|
+
})
|
|
70
|
+
.option('bytecodesFile', {
|
|
71
|
+
alias: 'bytecodes-file',
|
|
72
|
+
description: 'JSON file with contracts bytecodes. The file should be a simple object using the addresses as keys with the respective bytecodes as values',
|
|
73
|
+
type: 'string',
|
|
74
|
+
demandOption: false
|
|
75
|
+
})
|
|
76
|
+
.option('bytecodes', {
|
|
77
|
+
alias: ['address-bytecode', 'addressBytecode', 'contractBytecode', 'contract-bytecode'],
|
|
78
|
+
description: "Contract bytecode in '0x<address>:0x<bytecode>' format. Repeat the flag for multiple contracts. Alternatively use --bytecodes-file to supply a JSON map.",
|
|
79
|
+
array: true,
|
|
80
|
+
demandOption: true,
|
|
81
|
+
default: [],
|
|
82
|
+
type: 'string'
|
|
83
|
+
})
|
|
84
|
+
.option('verbose', {
|
|
85
|
+
alias: ['v'],
|
|
86
|
+
description: 'Verbose',
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
default: false
|
|
89
|
+
}), async (yargs) => {
|
|
90
|
+
await erc20SupplyCmd({
|
|
91
|
+
tokenAddr: yargs.tokenAddr,
|
|
92
|
+
blockNumber: yargs.blockNumber,
|
|
93
|
+
diamondAddress: yargs.diamondAddress,
|
|
94
|
+
l1Rpc: yargs.l1Rpc,
|
|
95
|
+
zkSyncOsRpc: yargs.zkSyncOsRpc,
|
|
96
|
+
apiUrl: yargs.apiUrl,
|
|
97
|
+
bytecodesFile: yargs.bytecodesFile,
|
|
98
|
+
bytecodes: yargs.bytecodes,
|
|
99
|
+
configPath: yargs.configPath,
|
|
100
|
+
verbose: yargs.verbose
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Hex } from 'viem';
|
|
2
|
+
export declare function parseBlockNumber(str: string): bigint;
|
|
3
|
+
export declare function intoAddressCodePair(strings: string[]): [Hex, Hex];
|
|
4
|
+
export declare function parseBytecodes(raw: string[]): Record<Hex, Hex>;
|
|
5
|
+
export declare function loadBytecodes(filePath: string | undefined, raw: string[]): Record<Hex, Hex>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { hexSchema } from '../utils/schemas.js';
|
|
4
|
+
export function parseBlockNumber(str) {
|
|
5
|
+
try {
|
|
6
|
+
return BigInt(str);
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
throw new Error('Invalid block number provided');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const hexRegex = /0x[a-fA-F0-9]+/;
|
|
13
|
+
export function intoAddressCodePair(strings) {
|
|
14
|
+
const [addr, code] = strings.filter((part) => hexRegex.test(part));
|
|
15
|
+
if (addr === undefined || code === undefined) {
|
|
16
|
+
throw new Error('Wrong bytecode format. Only 1 colon is allowed');
|
|
17
|
+
}
|
|
18
|
+
if (addr.length !== 42) {
|
|
19
|
+
throw new Error(`Invalid value. Expected address received: ${addr}`);
|
|
20
|
+
}
|
|
21
|
+
return [addr, code];
|
|
22
|
+
}
|
|
23
|
+
export function parseBytecodes(raw) {
|
|
24
|
+
const res = {};
|
|
25
|
+
for (const elem of raw) {
|
|
26
|
+
const parts = elem.split(':').map((part) => part.trim());
|
|
27
|
+
const [addr, code] = intoAddressCodePair(parts);
|
|
28
|
+
res[addr] = code;
|
|
29
|
+
}
|
|
30
|
+
return res;
|
|
31
|
+
}
|
|
32
|
+
export function loadBytecodes(filePath, raw) {
|
|
33
|
+
if (filePath !== undefined) {
|
|
34
|
+
const data = JSON.parse(readFileSync(filePath).toString());
|
|
35
|
+
return z.record(hexSchema, hexSchema).parse(data);
|
|
36
|
+
}
|
|
37
|
+
return parseBytecodes(raw);
|
|
38
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class VerifyWorkflow {
|
|
2
|
+
start(): void;
|
|
3
|
+
step<T>(waitMsg: string, successMessage: string, task: () => Promise<T>): Promise<T>;
|
|
4
|
+
success(msg: string): void;
|
|
5
|
+
error(msg: string): void;
|
|
6
|
+
message(msg: string): void;
|
|
7
|
+
userConfirmation(msg: string): Promise<boolean>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { setTimeout } from 'node:timers/promises';
|
|
2
|
+
import { confirm, intro, isCancel, log, outro, spinner } from '@clack/prompts';
|
|
3
|
+
import { showPrividiumHeader } from '../utils/show-prividium-header.js';
|
|
4
|
+
export class VerifyWorkflow {
|
|
5
|
+
start() {
|
|
6
|
+
showPrividiumHeader();
|
|
7
|
+
intro('Starting Prividium™ verification tool');
|
|
8
|
+
}
|
|
9
|
+
async step(waitMsg, successMessage, task) {
|
|
10
|
+
const spin = spinner({
|
|
11
|
+
indicator: 'dots'
|
|
12
|
+
});
|
|
13
|
+
spin.start(waitMsg);
|
|
14
|
+
const [res] = await Promise.all([task(), setTimeout(1500)]).catch((e) => {
|
|
15
|
+
spin.stop(`❌ ${waitMsg}`);
|
|
16
|
+
throw e;
|
|
17
|
+
});
|
|
18
|
+
spin.stop(successMessage);
|
|
19
|
+
return res;
|
|
20
|
+
}
|
|
21
|
+
success(msg) {
|
|
22
|
+
outro(msg);
|
|
23
|
+
}
|
|
24
|
+
error(msg) {
|
|
25
|
+
log.error(msg);
|
|
26
|
+
outro('❌ error');
|
|
27
|
+
}
|
|
28
|
+
message(msg) {
|
|
29
|
+
log.message(msg);
|
|
30
|
+
}
|
|
31
|
+
async userConfirmation(msg) {
|
|
32
|
+
const res = await confirm({
|
|
33
|
+
message: msg,
|
|
34
|
+
initialValue: true
|
|
35
|
+
});
|
|
36
|
+
if (isCancel(res)) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return res;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { BaseCli, DefCommand } from '../base-cli.js';
|
|
2
|
+
declare function addVerifyOptions(cli: BaseCli): import("yargs").Argv<{
|
|
3
|
+
configPath: string | undefined;
|
|
4
|
+
} & {
|
|
5
|
+
l1Rpc: string | undefined;
|
|
6
|
+
} & {
|
|
7
|
+
zkSyncOsRpc: string | undefined;
|
|
8
|
+
} & {
|
|
9
|
+
apiUrl: string | undefined;
|
|
10
|
+
} & {
|
|
11
|
+
blockNumber: string;
|
|
12
|
+
} & {
|
|
13
|
+
diamondAddress: string | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export type VerifyCli = ReturnType<typeof addVerifyOptions>;
|
|
16
|
+
export type VerifyDefCommand = (args: VerifyCli) => VerifyCli;
|
|
17
|
+
export declare const addVerify: DefCommand;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { addVerifyAccountDataCmd } from './verify/account-data-cmd.js';
|
|
2
|
+
import { addErc20BalanceCmd } from './verify/erc20-balance-cmd.js';
|
|
3
|
+
import { addErc20SupplyCmd } from './verify/erc20-supply-cmd.js';
|
|
4
|
+
function addVerifyOptions(cli) {
|
|
5
|
+
return cli
|
|
6
|
+
.option('l1Rpc', {
|
|
7
|
+
alias: ['l1-rpc', 'l1'],
|
|
8
|
+
description: 'Url for l1 rpc',
|
|
9
|
+
demandOption: false,
|
|
10
|
+
type: 'string'
|
|
11
|
+
})
|
|
12
|
+
.option('zkSyncOsRpc', {
|
|
13
|
+
alias: ['zksync-os-rpc', 'zksync-os'],
|
|
14
|
+
description: 'ZKsync OS RPC used to replay the disclosed EVM call locally with proof-derived state overrides. Does not need historical state — a freshly started node works.',
|
|
15
|
+
demandOption: false,
|
|
16
|
+
type: 'string'
|
|
17
|
+
})
|
|
18
|
+
.option('apiUrl', {
|
|
19
|
+
alias: ['api-url', 'rpc-url', 'r', 'prividium-rpc', 'prividium'],
|
|
20
|
+
description: 'Specifies target Prividium™ API url',
|
|
21
|
+
demandOption: false,
|
|
22
|
+
type: 'string'
|
|
23
|
+
})
|
|
24
|
+
.option('blockNumber', {
|
|
25
|
+
alias: ['block-number', 'height'],
|
|
26
|
+
description: 'block number in hex',
|
|
27
|
+
demandOption: true,
|
|
28
|
+
type: 'string'
|
|
29
|
+
})
|
|
30
|
+
.option('diamondAddress', {
|
|
31
|
+
alias: ['diamond-address'],
|
|
32
|
+
description: 'Address for the diamond in l1',
|
|
33
|
+
type: 'string'
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export const addVerify = (cli) => {
|
|
37
|
+
return cli
|
|
38
|
+
.command('verify', 'Verify proofs of data from a prividium chain', (cli) => {
|
|
39
|
+
const withVerifyOpts = addVerifyOptions(cli);
|
|
40
|
+
return [addErc20SupplyCmd, addErc20BalanceCmd, addVerifyAccountDataCmd].reduce((a, b) => b(a), withVerifyOpts);
|
|
41
|
+
})
|
|
42
|
+
.demandCommand();
|
|
43
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createBaseCli } from './base-cli.js';
|
|
2
|
+
import { addConfig } from './commands/config.js';
|
|
3
|
+
import { addDoctor } from './commands/doctor.js';
|
|
4
|
+
import { addProxy } from './commands/proxy.js';
|
|
5
|
+
import { addVerify } from './commands/verify.js';
|
|
6
|
+
const actions = [addProxy, addDoctor, addConfig, addVerify];
|
|
7
|
+
export function buildCli() {
|
|
8
|
+
const base = createBaseCli();
|
|
9
|
+
const composed = actions.reduce((argv, applyAction) => applyAction(argv), base);
|
|
10
|
+
return composed.help().strict().demandCommand();
|
|
11
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const schemas: {
|
|
3
|
+
readonly latest: z.ZodObject<{
|
|
4
|
+
userPanelUrl: z.ZodOptional<z.ZodString>;
|
|
5
|
+
apiUrl: z.ZodOptional<z.ZodString>;
|
|
6
|
+
l1RpcUrl: z.ZodOptional<z.ZodString>;
|
|
7
|
+
localZksyncOsRpcUrl: z.ZodOptional<z.ZodString>;
|
|
8
|
+
diamondAddress: z.ZodOptional<z.ZodString>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
readonly v0_17: z.ZodObject<{
|
|
11
|
+
prividiumRpcUrl: z.ZodString;
|
|
12
|
+
userPanelUrl: z.ZodString;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
};
|
|
15
|
+
declare const PROMPTS_FOR_CONFIGS: {
|
|
16
|
+
readonly userPanelUrl: {
|
|
17
|
+
readonly msg: "Please insert your Prividium™ User Panel url";
|
|
18
|
+
readonly retry: "Please provide a valid url";
|
|
19
|
+
readonly schema: z.ZodURL;
|
|
20
|
+
};
|
|
21
|
+
readonly apiUrl: {
|
|
22
|
+
readonly msg: "Please insert your Prividium™ api url";
|
|
23
|
+
readonly retry: "Please provide a valid url";
|
|
24
|
+
readonly schema: z.ZodURL;
|
|
25
|
+
};
|
|
26
|
+
readonly diamondAddress: {
|
|
27
|
+
readonly msg: "Please provide the address of the ZKsync OS main contract in l1";
|
|
28
|
+
readonly retry: "Please provide a valid address";
|
|
29
|
+
readonly schema: z.ZodTemplateLiteral<`0x${string}`>;
|
|
30
|
+
};
|
|
31
|
+
readonly l1RpcUrl: {
|
|
32
|
+
readonly msg: "Please provide an rpc url for the l1 chain";
|
|
33
|
+
readonly retry: "Please provide a valid url";
|
|
34
|
+
readonly schema: z.ZodURL;
|
|
35
|
+
};
|
|
36
|
+
readonly localZksyncOsRpcUrl: {
|
|
37
|
+
readonly msg: "Please provide an url for a local and trusted zksyncos rpc";
|
|
38
|
+
readonly retry: "Please provide a valid url";
|
|
39
|
+
readonly schema: z.ZodURL;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
type MsgKeys = keyof typeof PROMPTS_FOR_CONFIGS;
|
|
43
|
+
export type CliConfig = z.infer<typeof schemas.latest>;
|
|
44
|
+
type NoUndefinedFields<T> = {
|
|
45
|
+
[K in keyof T]-?: Exclude<T[K], undefined>;
|
|
46
|
+
};
|
|
47
|
+
type WithUndefinedFields<T> = {
|
|
48
|
+
[K in keyof T]?: T[K] | null | undefined;
|
|
49
|
+
};
|
|
50
|
+
export declare class ConfigFile {
|
|
51
|
+
private filePath;
|
|
52
|
+
constructor(filePath?: string);
|
|
53
|
+
read(): Partial<CliConfig>;
|
|
54
|
+
write(config: CliConfig): void;
|
|
55
|
+
partialWrite(newConfig: Partial<CliConfig>): void;
|
|
56
|
+
remove(): void;
|
|
57
|
+
print(): void;
|
|
58
|
+
printPath(): void;
|
|
59
|
+
readAndUpdatedRequiredFields<K extends keyof CliConfig>(given: WithUndefinedFields<Pick<CliConfig, K>>): Promise<NoUndefinedFields<Pick<CliConfig, K>>>;
|
|
60
|
+
prompt(key: MsgKeys): Promise<string>;
|
|
61
|
+
}
|
|
62
|
+
export {};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { confirm, log, text } from '@clack/prompts';
|
|
4
|
+
import appDirs from 'appdirsjs';
|
|
5
|
+
import color from 'kleur';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import { hexSchema } from '../commands/utils/schemas.js';
|
|
8
|
+
// biome-ignore lint/suspicious/noExplicitAny: CJS/ESM interop
|
|
9
|
+
const appDirsFn = (appDirs.default ?? appDirs);
|
|
10
|
+
const dirs = appDirsFn({ appName: 'prividium-proxy' });
|
|
11
|
+
// Source of truth for the on-disk config-file shapes the SDK accepts.
|
|
12
|
+
// `latest` is what `write()` produces today.
|
|
13
|
+
// Any other entry is keyed by the last SDK version that wrote that shape;
|
|
14
|
+
// it exists only so older config files keep working after a user upgrades.
|
|
15
|
+
// To add a new legacy entry: copy the previous `latest` into a new `vX_Y`
|
|
16
|
+
// key, then change `latest` to the new shape and add a branch in `migrate()`.
|
|
17
|
+
const schemas = {
|
|
18
|
+
latest: z.object({
|
|
19
|
+
userPanelUrl: z.string().optional(),
|
|
20
|
+
apiUrl: z.string().optional(),
|
|
21
|
+
l1RpcUrl: z.string().optional(),
|
|
22
|
+
localZksyncOsRpcUrl: z.string().optional(),
|
|
23
|
+
diamondAddress: z.string().optional()
|
|
24
|
+
}),
|
|
25
|
+
v0_17: z.object({
|
|
26
|
+
prividiumRpcUrl: z.string(),
|
|
27
|
+
userPanelUrl: z.string()
|
|
28
|
+
})
|
|
29
|
+
};
|
|
30
|
+
const ENV_VARS_FOR_CONFIG = {
|
|
31
|
+
apiUrl: 'PRIVIDIUM_API_URL',
|
|
32
|
+
l1RpcUrl: 'L1_RPC_URL',
|
|
33
|
+
diamondAddress: 'ZSYNCOS_L1_DIAMOND',
|
|
34
|
+
userPanelUrl: 'USER_PANEL_URL',
|
|
35
|
+
localZksyncOsRpcUrl: 'LOCAL_ZKSYNCOS_RPC_URL'
|
|
36
|
+
};
|
|
37
|
+
const PROMPTS_FOR_CONFIGS = {
|
|
38
|
+
userPanelUrl: {
|
|
39
|
+
msg: 'Please insert your Prividium™ User Panel url',
|
|
40
|
+
retry: 'Please provide a valid url',
|
|
41
|
+
schema: z.url()
|
|
42
|
+
},
|
|
43
|
+
apiUrl: {
|
|
44
|
+
msg: 'Please insert your Prividium™ api url',
|
|
45
|
+
retry: 'Please provide a valid url',
|
|
46
|
+
schema: z.url()
|
|
47
|
+
},
|
|
48
|
+
diamondAddress: {
|
|
49
|
+
msg: 'Please provide the address of the ZKsync OS main contract in l1',
|
|
50
|
+
retry: 'Please provide a valid address',
|
|
51
|
+
schema: hexSchema
|
|
52
|
+
},
|
|
53
|
+
l1RpcUrl: {
|
|
54
|
+
msg: 'Please provide an rpc url for the l1 chain',
|
|
55
|
+
retry: 'Please provide a valid url',
|
|
56
|
+
schema: z.url()
|
|
57
|
+
},
|
|
58
|
+
localZksyncOsRpcUrl: {
|
|
59
|
+
msg: 'Please provide an url for a local and trusted zksyncos rpc',
|
|
60
|
+
retry: 'Please provide a valid url',
|
|
61
|
+
schema: z.url()
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
function migrate(json) {
|
|
65
|
+
const latest = schemas.latest.safeParse(json);
|
|
66
|
+
if (latest.success)
|
|
67
|
+
return latest.data;
|
|
68
|
+
const v0_17 = schemas.v0_17.safeParse(json);
|
|
69
|
+
if (v0_17.success)
|
|
70
|
+
return { apiUrl: v0_17.data.prividiumRpcUrl };
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
export class ConfigFile {
|
|
74
|
+
filePath;
|
|
75
|
+
constructor(filePath) {
|
|
76
|
+
if (filePath) {
|
|
77
|
+
this.filePath = filePath;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
this.filePath = path.join(dirs.config, 'config.json');
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
read() {
|
|
84
|
+
if (!existsSync(this.filePath)) {
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
const data = readFileSync(this.filePath).toString();
|
|
89
|
+
try {
|
|
90
|
+
const json = JSON.parse(data);
|
|
91
|
+
return migrate(json) ?? {};
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
rmSync(this.filePath);
|
|
95
|
+
log.warn('Corrupted configuration file');
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
write(config) {
|
|
101
|
+
const dir = path.parse(this.filePath).dir;
|
|
102
|
+
mkdirSync(dir, { recursive: true });
|
|
103
|
+
writeFileSync(this.filePath, JSON.stringify({ latest: config }));
|
|
104
|
+
}
|
|
105
|
+
partialWrite(newConfig) {
|
|
106
|
+
const old = this.read();
|
|
107
|
+
const dir = path.parse(this.filePath).dir;
|
|
108
|
+
mkdirSync(dir, { recursive: true });
|
|
109
|
+
writeFileSync(this.filePath, JSON.stringify({ latest: { ...old, ...newConfig } }));
|
|
110
|
+
}
|
|
111
|
+
remove() {
|
|
112
|
+
if (existsSync(this.filePath)) {
|
|
113
|
+
rmSync(this.filePath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
print() {
|
|
117
|
+
const { apiUrl } = this.read();
|
|
118
|
+
console.log(`${color.bold('Prividium™ API url')}: ${apiUrl}`);
|
|
119
|
+
}
|
|
120
|
+
printPath() {
|
|
121
|
+
console.log(this.filePath);
|
|
122
|
+
}
|
|
123
|
+
async readAndUpdatedRequiredFields(given) {
|
|
124
|
+
const res = {};
|
|
125
|
+
const existing = this.read();
|
|
126
|
+
for (const key in given) {
|
|
127
|
+
if (given[key] !== null && given[key] !== undefined) {
|
|
128
|
+
res[key] = given[key];
|
|
129
|
+
}
|
|
130
|
+
else if (key in existing) {
|
|
131
|
+
res[key] = existing[key];
|
|
132
|
+
}
|
|
133
|
+
else if (ENV_VARS_FOR_CONFIG[key] && ENV_VARS_FOR_CONFIG[key] in process.env) {
|
|
134
|
+
res[key] = process.env[ENV_VARS_FOR_CONFIG[key]];
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
res[key] = await this.prompt(key);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return res;
|
|
141
|
+
}
|
|
142
|
+
async prompt(key) {
|
|
143
|
+
const response = await text({
|
|
144
|
+
message: PROMPTS_FOR_CONFIGS[key].msg,
|
|
145
|
+
validate(value) {
|
|
146
|
+
const parsed = PROMPTS_FOR_CONFIGS[key].schema.safeParse(value);
|
|
147
|
+
if (!parsed.success) {
|
|
148
|
+
return PROMPTS_FOR_CONFIGS[key].retry;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
if (typeof response === 'symbol') {
|
|
153
|
+
throw new Error('Canceled by the user');
|
|
154
|
+
}
|
|
155
|
+
const save = await confirm({
|
|
156
|
+
message: 'Do you want to save this value for future usages?',
|
|
157
|
+
initialValue: true
|
|
158
|
+
});
|
|
159
|
+
if (typeof save === 'symbol') {
|
|
160
|
+
throw new Error('Canceled by the user');
|
|
161
|
+
}
|
|
162
|
+
if (save) {
|
|
163
|
+
this.partialWrite({ [key]: response });
|
|
164
|
+
}
|
|
165
|
+
return response;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare class CreationWorkflow {
|
|
2
|
+
private submitCallback;
|
|
3
|
+
private host;
|
|
4
|
+
private port;
|
|
5
|
+
constructor(host: string, port: number);
|
|
6
|
+
start(): void;
|
|
7
|
+
waitForAuthentication(url: string): Promise<void>;
|
|
8
|
+
private openBrowser;
|
|
9
|
+
onSubmit(): Promise<void>;
|
|
10
|
+
onMessage(msg: string): void;
|
|
11
|
+
onError(err: Error): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ZodTypeProvider } from 'fastify-type-provider-zod';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
declare function fastifyApp(): import("fastify").FastifyInstance<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, ZodTypeProvider>;
|
|
4
|
+
type WebServer = ReturnType<typeof fastifyApp>;
|
|
5
|
+
type Config = {
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
userPanelUrl?: string;
|
|
8
|
+
host: string;
|
|
9
|
+
port: number;
|
|
10
|
+
onSubmit: () => Promise<void>;
|
|
11
|
+
onCall: (methodName: string) => void;
|
|
12
|
+
onReAuthNeeded: () => void;
|
|
13
|
+
onError: (err: Error) => void;
|
|
14
|
+
initialToken?: {
|
|
15
|
+
accessToken: string;
|
|
16
|
+
expiresAt: Date;
|
|
17
|
+
};
|
|
18
|
+
onReAuth?: () => Promise<{
|
|
19
|
+
accessToken: string;
|
|
20
|
+
expiresAt: Date;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
export declare const sessionSchema: z.ZodObject<{
|
|
24
|
+
type: z.ZodString;
|
|
25
|
+
expiresAt: z.ZodCoercedDate<unknown>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
export declare function buildServer(config: Config): WebServer;
|
|
28
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Account, type Chain, type ClientConfig, type PublicClient, type PublicClientConfig, type RpcSchema, type Transport } from 'viem';
|
|
2
|
-
import { type SelectiveDisclosureActions } from './selective-disclosure/
|
|
2
|
+
import { type SelectiveDisclosureActions } from './selective-disclosure/index.js';
|
|
3
3
|
type PrividiumClientConfig<transport extends Transport = Transport, chain extends Chain | undefined = Chain | undefined, accountOrAddress extends Account | undefined = undefined, rpcSchema extends RpcSchema | undefined = undefined> = PublicClientConfig<transport, chain, accountOrAddress, rpcSchema> & Pick<ClientConfig<transport, chain, accountOrAddress, rpcSchema>, 'account'>;
|
|
4
4
|
/**
|
|
5
5
|
* Creates a Prividium specific Public Client. This client extends the standard Viem Public Client
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createClient, publicActions } from 'viem';
|
|
2
|
-
import { selectiveDisclosureActions } from './selective-disclosure/
|
|
2
|
+
import { selectiveDisclosureActions } from './selective-disclosure/index.js';
|
|
3
3
|
function prividiumActions(client) {
|
|
4
4
|
return {
|
|
5
5
|
call: (args) => {
|
|
@@ -9,4 +9,10 @@ export declare function computeInternalBytecodeHash(codeHex: Hex): {
|
|
|
9
9
|
bytecodeHash: Hex;
|
|
10
10
|
artifactsLen: number;
|
|
11
11
|
};
|
|
12
|
-
export
|
|
12
|
+
export type VerifyAccountPropertiesResult = {
|
|
13
|
+
success: true;
|
|
14
|
+
} | {
|
|
15
|
+
success: false;
|
|
16
|
+
errorMsg: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function verifyAccountPropertiesProof(disclosure: AccountDataDisclosureResult, l1Client: PublicClient, diamondAddress: Address, expectedBytecode?: Hex, expectedAddress?: Hex): Promise<VerifyAccountPropertiesResult>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { bytesToHex, hexToBigInt, hexToBytes, keccak256, pad } from 'viem';
|
|
1
|
+
import { bytesToHex, getAddress, hexToBigInt, hexToBytes, keccak256, pad } from 'viem';
|
|
2
2
|
import { encodeBatchInfo } from './batch-info.js';
|
|
3
3
|
import { computeStateCommitment } from './state-commitment.js';
|
|
4
|
-
import { blake2s256 } from './utils.js';
|
|
4
|
+
import { areHexEqual, blake2s256 } from './utils.js';
|
|
5
5
|
import { calculateStateMerkleRoot } from './verifiy-proofs.js';
|
|
6
6
|
import { DIAMOND_ABI } from './verify-disclosure.js';
|
|
7
7
|
const PUSH1 = 0x60;
|
|
@@ -43,25 +43,31 @@ export function computeInternalBytecodeHash(codeHex) {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
const ACCOUNT_PROPERTIES_ADDRESS = '0x0000000000000000000000000000000000008003';
|
|
46
|
-
export async function verifyAccountPropertiesProof(disclosure, l1Client, diamondAddress, expectedBytecode) {
|
|
46
|
+
export async function verifyAccountPropertiesProof(disclosure, l1Client, diamondAddress, expectedBytecode, expectedAddress) {
|
|
47
47
|
const isEoa = BigInt(disclosure.accountProperties.bytecodeHash) === 0n;
|
|
48
48
|
if (isEoa && expectedBytecode !== undefined && BigInt(expectedBytecode) !== 0n) {
|
|
49
|
-
return false;
|
|
49
|
+
return { success: false, errorMsg: 'EOA should not have bytecode' };
|
|
50
50
|
}
|
|
51
51
|
if (!isEoa) {
|
|
52
52
|
if (expectedBytecode === undefined) {
|
|
53
|
-
return false;
|
|
53
|
+
return { success: false, errorMsg: 'Missing expected bytecode for contract' };
|
|
54
54
|
}
|
|
55
55
|
const { bytecodeHash: computedHash } = computeInternalBytecodeHash(expectedBytecode);
|
|
56
|
-
if (computedHash
|
|
57
|
-
return false;
|
|
56
|
+
if (!areHexEqual(computedHash, disclosure.accountProperties.bytecodeHash)) {
|
|
57
|
+
return { success: false, errorMsg: "Bytecode hash doesn't match" };
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
+
if (expectedAddress !== undefined && !areHexEqual(expectedAddress, disclosure.address)) {
|
|
61
|
+
return {
|
|
62
|
+
success: false,
|
|
63
|
+
errorMsg: `Disclosure address does not match with expected address. Expected ${getAddress(expectedAddress)}, included in disclosure payload: ${getAddress(disclosure.address)} `
|
|
64
|
+
};
|
|
65
|
+
}
|
|
60
66
|
const { treeRoot, value: propertiesHash } = calculateStateMerkleRoot(disclosure.storageProof, ACCOUNT_PROPERTIES_ADDRESS, pad(disclosure.address));
|
|
61
67
|
const encodedProperties = encodeAccountProperties(disclosure.accountProperties);
|
|
62
68
|
const reconstructedHash = blake2s256(hexToBytes(encodedProperties));
|
|
63
|
-
if (bytesToHex(reconstructedHash)
|
|
64
|
-
return false;
|
|
69
|
+
if (propertiesHash === null || !areHexEqual(bytesToHex(reconstructedHash), propertiesHash)) {
|
|
70
|
+
return { success: false, errorMsg: "Account properties hash doesn't match the storage proof value" };
|
|
65
71
|
}
|
|
66
72
|
// Step 2: Compute state commitment from the tree root + preimage data.
|
|
67
73
|
const stateCommitment = computeStateCommitment(treeRoot, BigInt(disclosure.stateCommitmentPreimage.nextFreeSlot), BigInt(disclosure.stateCommitmentPreimage.blockNumber), disclosure.stateCommitmentPreimage.last256BlockHashesBlake, BigInt(disclosure.stateCommitmentPreimage.lastBlockTimestamp));
|
|
@@ -74,7 +80,13 @@ export async function verifyAccountPropertiesProof(disclosure, l1Client, diamond
|
|
|
74
80
|
functionName: 'storedBatchHash',
|
|
75
81
|
args: [BigInt(disclosure.batchNumber)]
|
|
76
82
|
});
|
|
77
|
-
|
|
83
|
+
const success = areHexEqual(batchInfoHash, storedBatchHash);
|
|
84
|
+
if (success) {
|
|
85
|
+
return { success: true };
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
return { success: false, errorMsg: 'Storage proof verification failed.' };
|
|
89
|
+
}
|
|
78
90
|
}
|
|
79
91
|
/**
|
|
80
92
|
* Build the JUMPDEST bitmap matching the ZKsync OS evm_interpreter `analyze()`.
|