prividium 0.16.0 → 0.17.1

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.
@@ -2,6 +2,7 @@
2
2
  * Checks if a Response contains a Prividium unauthorized/forbidden error.
3
3
  */
4
4
  export declare function hasPrividiumUnauthorizedError(response: Response): Promise<boolean>;
5
+ export declare function isPrividiumUnauthorizedRpcError(error: unknown): boolean;
5
6
  /**
6
7
  * Extracts a human-readable error string from a failed HTTP response.
7
8
  * Attempts to parse the server's `{ error: { code, message } }` JSON structure,
@@ -17,13 +17,27 @@ const apiErrorSchema = z.object({
17
17
  * Checks if a Response contains a Prividium unauthorized/forbidden error.
18
18
  */
19
19
  export async function hasPrividiumUnauthorizedError(response) {
20
- const clonedResponse = response.clone();
21
- const parsed = jsonRpcErrorSchema.safeParse(await clonedResponse.json());
22
- // { success: false, error: {...} } | { success: true, data: {...} }
23
- if (parsed.success) {
24
- return parsed.data.error.code === UNAUTHORIZED_ERROR_CODE;
20
+ try {
21
+ const clonedResponse = response.clone();
22
+ const parsed = jsonRpcErrorSchema.safeParse(await clonedResponse.json());
23
+ if (parsed.success) {
24
+ return parsed.data.error.code === UNAUTHORIZED_ERROR_CODE;
25
+ }
26
+ return false;
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ }
32
+ export function isPrividiumUnauthorizedRpcError(error) {
33
+ if (!error || typeof error !== 'object') {
34
+ return false;
35
+ }
36
+ const value = error;
37
+ if (value.code === UNAUTHORIZED_ERROR_CODE) {
38
+ return true;
25
39
  }
26
- return false;
40
+ return isPrividiumUnauthorizedRpcError(value.cause);
27
41
  }
28
42
  /**
29
43
  * Extracts a human-readable error string from a failed HTTP response.
@@ -0,0 +1,12 @@
1
+ import { type Address, type Hex, type PublicClient } from 'viem';
2
+ import type { AccountDataDisclosureResult, AccountProperties } from './disclosure-result';
3
+ export declare function encodeAccountProperties(params: AccountProperties): Hex;
4
+ /**
5
+ * Compute the internal bytecode hash: blake2s(code || padding || artifacts)
6
+ * Returns { bytecodeHash, artifactsLen }
7
+ */
8
+ export declare function computeInternalBytecodeHash(codeHex: Hex): {
9
+ bytecodeHash: Hex;
10
+ artifactsLen: number;
11
+ };
12
+ export declare function verifyAccountPropertiesProof(disclosure: AccountDataDisclosureResult, l1Client: PublicClient, diamondAddress: Address, expectedBytecode?: Hex): Promise<boolean>;
@@ -0,0 +1,113 @@
1
+ import { bytesToHex, hexToBigInt, hexToBytes, keccak256, pad } from 'viem';
2
+ import { encodeBatchInfo } from './batch-info';
3
+ import { computeStateCommitment } from './state-commitment';
4
+ import { blake2s256 } from './utils';
5
+ import { calculateStateMerkleRoot } from './verifiy-proofs';
6
+ import { DIAMOND_ABI } from './verify-disclosure';
7
+ const PUSH1 = 0x60;
8
+ const PUSH32 = 0x7f;
9
+ const JUMPDEST = 0x5b;
10
+ const BYTECODE_ALIGNMENT = 8;
11
+ export function encodeAccountProperties(params) {
12
+ const buf = new Uint8Array(124);
13
+ const view = new DataView(buf.buffer);
14
+ view.setBigUint64(0, hexToBigInt(params.versioningData), false);
15
+ view.setBigUint64(8, hexToBigInt(params.nonce), false);
16
+ // Balance as 32 bytes big-endian
17
+ const balanceHex = hexToBigInt(params.balance).toString(16).padStart(64, '0');
18
+ for (let i = 0; i < 32; i++) {
19
+ buf[16 + i] = parseInt(balanceHex.slice(i * 2, i * 2 + 2), 16);
20
+ }
21
+ buf.set(hexToBytes(params.bytecodeHash), 48);
22
+ view.setUint32(80, params.unpaddedCodeLen, false);
23
+ view.setUint32(84, params.artifactsLen, false);
24
+ buf.set(hexToBytes(params.observableBytecodeHash), 88);
25
+ view.setUint32(120, params.observableBytecodeLen, false);
26
+ return bytesToHex(buf);
27
+ }
28
+ /**
29
+ * Compute the internal bytecode hash: blake2s(code || padding || artifacts)
30
+ * Returns { bytecodeHash, artifactsLen }
31
+ */
32
+ export function computeInternalBytecodeHash(codeHex) {
33
+ const code = hexToBytes(codeHex);
34
+ const paddingLen = computeBytecodePaddingLen(code.length);
35
+ const artifacts = computeJumpdestBitmap(code);
36
+ const fullLen = code.length + paddingLen + artifacts.length;
37
+ const buf = new Uint8Array(fullLen); // zero-initialized (padding is zeros)
38
+ buf.set(code, 0);
39
+ buf.set(artifacts, code.length + paddingLen);
40
+ return {
41
+ bytecodeHash: bytesToHex(blake2s256(buf)),
42
+ artifactsLen: artifacts.length
43
+ };
44
+ }
45
+ const ACCOUNT_PROPERTIES_ADDRESS = '0x0000000000000000000000000000000000008003';
46
+ export async function verifyAccountPropertiesProof(disclosure, l1Client, diamondAddress, expectedBytecode) {
47
+ const isEoa = BigInt(disclosure.accountProperties.bytecodeHash) === 0n;
48
+ if (isEoa && expectedBytecode !== undefined && BigInt(expectedBytecode) !== 0n) {
49
+ return false;
50
+ }
51
+ if (!isEoa) {
52
+ if (expectedBytecode === undefined) {
53
+ return false;
54
+ }
55
+ const { bytecodeHash: computedHash } = computeInternalBytecodeHash(expectedBytecode);
56
+ if (computedHash.toLowerCase() !== disclosure.accountProperties.bytecodeHash.toLowerCase()) {
57
+ return false;
58
+ }
59
+ }
60
+ const { treeRoot, value: propertiesHash } = calculateStateMerkleRoot(disclosure.storageProof, ACCOUNT_PROPERTIES_ADDRESS, pad(disclosure.address));
61
+ const encodedProperties = encodeAccountProperties(disclosure.accountProperties);
62
+ const reconstructedHash = blake2s256(hexToBytes(encodedProperties));
63
+ if (bytesToHex(reconstructedHash).toLowerCase() !== propertiesHash?.toLowerCase()) {
64
+ return false;
65
+ }
66
+ // Step 2: Compute state commitment from the tree root + preimage data.
67
+ const stateCommitment = computeStateCommitment(treeRoot, BigInt(disclosure.stateCommitmentPreimage.nextFreeSlot), BigInt(disclosure.stateCommitmentPreimage.blockNumber), disclosure.stateCommitmentPreimage.last256BlockHashesBlake, BigInt(disclosure.stateCommitmentPreimage.lastBlockTimestamp));
68
+ const batchInfoHex = encodeBatchInfo(BigInt(disclosure.batchNumber), stateCommitment, BigInt(disclosure.l1VerificationData.numberOfLayer1Txs), disclosure.l1VerificationData.priorityOperationsHash, disclosure.l1VerificationData.l2ToL1LogsRootHash, disclosure.l1VerificationData.commitment);
69
+ const batchInfoHash = keccak256(batchInfoHex);
70
+ // Step 4: Verify against L1 diamond proxy.
71
+ const storedBatchHash = await l1Client.readContract({
72
+ address: diamondAddress,
73
+ abi: DIAMOND_ABI,
74
+ functionName: 'storedBatchHash',
75
+ args: [BigInt(disclosure.batchNumber)]
76
+ });
77
+ return batchInfoHash.toLowerCase() === storedBatchHash.toLowerCase();
78
+ }
79
+ /**
80
+ * Build the JUMPDEST bitmap matching the ZKsync OS evm_interpreter `analyze()`.
81
+ * The bitmap is stored as u64 words in little-endian byte order.
82
+ */
83
+ function computeJumpdestBitmap(code) {
84
+ // Number of u64 words needed: ceil(code.length / 64)
85
+ const u64Count = Math.ceil(code.length / 64) || 0;
86
+ const bitmap = new Uint8Array(u64Count * 8); // zero-initialized
87
+ let i = 0;
88
+ while (i < code.length) {
89
+ const op = code[i];
90
+ if (op === JUMPDEST) {
91
+ // Set bit at position i in the little-endian u64 bitmap.
92
+ // Word index (in u64 terms): Math.floor(i / 64)
93
+ // Bit within that u64: i % 64
94
+ // Byte within that u64 (little-endian): Math.floor((i % 64) / 8)
95
+ // Bit within that byte: (i % 64) % 8
96
+ const byteIndex = Math.floor(i / 64) * 8 + Math.floor((i % 64) / 8);
97
+ const bitIndex = (i % 64) % 8;
98
+ bitmap[byteIndex] |= 1 << bitIndex;
99
+ i += 1;
100
+ }
101
+ else if (op >= PUSH1 && op <= PUSH32) {
102
+ i += 1 + (op - PUSH1 + 1);
103
+ }
104
+ else {
105
+ i += 1;
106
+ }
107
+ }
108
+ return bitmap;
109
+ }
110
+ function computeBytecodePaddingLen(codeLen) {
111
+ const rem = codeLen % BYTECODE_ALIGNMENT;
112
+ return rem === 0 ? 0 : BYTECODE_ALIGNMENT - rem;
113
+ }
@@ -1,8 +1,25 @@
1
- import { type Account, type Address, type Chain, type Client, type RpcSchema, type Transport } from 'viem';
2
- import type { EthCallDisclosureResult } from './disclosure-result';
1
+ import { type Account, type Address, type BlockTag, type Chain, type Client, type Hex, type RpcSchema, type Transport } from 'viem';
2
+ import type { AccountDataDisclosureResult, EthCallDisclosureResult } from './disclosure-result';
3
+ export type DisclosureBlockNumber = number | bigint | Hex | BlockTag;
3
4
  export type SelectiveDisclosureActions = {
4
- tokenSupplyDisclosure: (tokenAddress: Address, blockNumber: number | bigint) => Promise<EthCallDisclosureResult>;
5
- tokenBalanceDisclosure: (tokenAddress: Address, holderAddress: Address, blockNumber: number | bigint) => Promise<EthCallDisclosureResult>;
5
+ tokenSupplyDisclosure: (tokenAddress: Address, blockNumber: DisclosureBlockNumber) => Promise<EthCallDisclosureResult>;
6
+ tokenBalanceDisclosure: (tokenAddress: Address, holderAddress: Address, blockNumber: DisclosureBlockNumber) => Promise<EthCallDisclosureResult>;
7
+ accountDataDisclosure: (address: Address, blockNumber: DisclosureBlockNumber) => Promise<AccountDataDisclosureResult>;
8
+ };
9
+ export type TokenSupplyDisclosureRpc = {
10
+ Method: 'prividium_tokenSupplyDisclosure';
11
+ Parameters: [tokenAddress: Address, blockNumber: Hex | BlockTag];
12
+ ReturnType: EthCallDisclosureResult;
13
+ };
14
+ export type TokenBalanceDisclosureRpc = {
15
+ Method: 'prividium_tokenBalanceDisclosure';
16
+ Parameters: [tokenAddress: Address, holderAddress: Address, blockNumber: Hex | BlockTag];
17
+ ReturnType: EthCallDisclosureResult;
18
+ };
19
+ export type AccountDataDisclosureRpc = {
20
+ Method: 'prividium_accountDataDisclosure';
21
+ Parameters: [address: Address, blockNumber: Hex | BlockTag];
22
+ ReturnType: AccountDataDisclosureResult;
6
23
  };
7
24
  /**
8
25
  * Viem action extender that adds Prividium selective-disclosure RPC methods
@@ -1,4 +1,7 @@
1
1
  import { numberToHex } from 'viem';
2
+ function encodeBlockNumber(block) {
3
+ return typeof block === 'string' ? block : numberToHex(block);
4
+ }
2
5
  /**
3
6
  * Viem action extender that adds Prividium selective-disclosure RPC methods
4
7
  * to a client. Use with `client.extend(selectiveDisclosureActions)`.
@@ -8,13 +11,19 @@ export function selectiveDisclosureActions(client) {
8
11
  async tokenSupplyDisclosure(tokenAddress, blockNumber) {
9
12
  return client.request({
10
13
  method: 'prividium_tokenSupplyDisclosure',
11
- params: [tokenAddress, numberToHex(blockNumber)]
14
+ params: [tokenAddress, encodeBlockNumber(blockNumber)]
12
15
  });
13
16
  },
14
17
  async tokenBalanceDisclosure(tokenAddress, holderAddress, blockNumber) {
15
18
  return client.request({
16
19
  method: 'prividium_tokenBalanceDisclosure',
17
- params: [tokenAddress, holderAddress, numberToHex(blockNumber)]
20
+ params: [tokenAddress, holderAddress, encodeBlockNumber(blockNumber)]
21
+ });
22
+ },
23
+ async accountDataDisclosure(address, blockNumber) {
24
+ return client.request({
25
+ method: 'prividium_accountDataDisclosure',
26
+ params: [address, encodeBlockNumber(blockNumber)]
18
27
  });
19
28
  }
20
29
  };
@@ -1,4 +1,4 @@
1
- import type { Hex } from 'viem';
1
+ import type { Address, Hex } from 'viem';
2
2
  import type { StorageSlotProof } from './verifiy-proofs';
3
3
  export type StateCommitmentPreimage = {
4
4
  nextFreeSlot: Hex;
@@ -34,3 +34,22 @@ export type EthCallDisclosureResult = {
34
34
  l1VerificationData: L1VerificationData;
35
35
  proofs: AccountStorageProofs[];
36
36
  };
37
+ export type AccountProperties = {
38
+ versioningData: Hex;
39
+ nonce: Hex;
40
+ balance: Hex;
41
+ bytecodeHash: Hex;
42
+ unpaddedCodeLen: number;
43
+ artifactsLen: number;
44
+ observableBytecodeHash: Hex;
45
+ observableBytecodeLen: number;
46
+ };
47
+ export type AccountDataDisclosureResult = {
48
+ accountProperties: AccountProperties;
49
+ address: Address;
50
+ bytecode: Hex;
51
+ batchNumber: number;
52
+ l1VerificationData: L1VerificationData;
53
+ stateCommitmentPreimage: StateCommitmentPreimage;
54
+ storageProof: StorageSlotProof;
55
+ };
@@ -1,8 +1,7 @@
1
+ export * from './account-properties';
1
2
  export * from './actions';
2
3
  export * from './batch-info';
3
4
  export * from './disclosure-result';
4
5
  export * from './state-commitment';
5
- export * from './token-balance-disclosure';
6
- export * from './token-supply-disclosure';
7
6
  export * from './verifiy-proofs';
8
7
  export * from './verify-disclosure';
@@ -1,8 +1,7 @@
1
+ export * from './account-properties';
1
2
  export * from './actions';
2
3
  export * from './batch-info';
3
4
  export * from './disclosure-result';
4
5
  export * from './state-commitment';
5
- export * from './token-balance-disclosure';
6
- export * from './token-supply-disclosure';
7
6
  export * from './verifiy-proofs';
8
7
  export * from './verify-disclosure';
@@ -1,4 +1,4 @@
1
- import { blake2s } from 'blakejs';
1
+ import blake from 'blakejs';
2
2
  export function concatBytes(...arrays) {
3
3
  const totalSize = arrays.map((bytes) => bytes.length).reduce((a, b) => a + b);
4
4
  const result = new Uint8Array(totalSize);
@@ -10,5 +10,5 @@ export function concatBytes(...arrays) {
10
10
  return result;
11
11
  }
12
12
  export function blake2s256(data) {
13
- return blake2s(data, undefined, 32);
13
+ return blake.blake2s(data, undefined, 32);
14
14
  }
@@ -1,5 +1,17 @@
1
1
  import { type Address, type Hex, type PublicClient } from 'viem';
2
2
  import type { EthCallDisclosureResult } from './disclosure-result';
3
+ export declare const DIAMOND_ABI: {
4
+ readonly name: "storedBatchHash";
5
+ readonly type: "function";
6
+ readonly stateMutability: "view";
7
+ readonly inputs: readonly [{
8
+ readonly type: "uint256";
9
+ readonly name: "_batchNumber";
10
+ }];
11
+ readonly outputs: readonly [{
12
+ readonly type: "bytes32";
13
+ }];
14
+ }[];
3
15
  /**
4
16
  * Verifies an ethCall disclosure response end-to-end.
5
17
  *
@@ -2,7 +2,9 @@ import { keccak256, pad, parseAbiItem } from 'viem';
2
2
  import { encodeBatchInfo } from './batch-info';
3
3
  import { computeStateCommitment } from './state-commitment';
4
4
  import { calculateStateMerkleRoot } from './verifiy-proofs';
5
- const diamondAbi = [parseAbiItem('function storedBatchHash(uint256 _batchNumber) external view returns (bytes32)')];
5
+ export const DIAMOND_ABI = [
6
+ parseAbiItem('function storedBatchHash(uint256 _batchNumber) external view returns (bytes32)')
7
+ ];
6
8
  /**
7
9
  * Verifies an ethCall disclosure response end-to-end.
8
10
  *
@@ -51,7 +53,7 @@ export async function verifyEthCallDisclosure(disclosure, l1Client, l2Client, di
51
53
  // Step 4: Verify against L1 diamond proxy.
52
54
  const storedBatchHash = await l1Client.readContract({
53
55
  address: diamondAddress,
54
- abi: diamondAbi,
56
+ abi: DIAMOND_ABI,
55
57
  functionName: 'storedBatchHash',
56
58
  args: [BigInt(batchNumber)]
57
59
  });
@@ -1,6 +1,6 @@
1
1
  import { http } from 'viem';
2
2
  import { buildChainObject, createApiMethods, rpcUrl } from './chain-core.js';
3
- import { extractResponseError, hasPrividiumUnauthorizedError } from './error-utils.js';
3
+ import { extractResponseError, isPrividiumUnauthorizedRpcError } from './error-utils.js';
4
4
  import { PrividiumSessionError } from './errors.js';
5
5
  import { MemoryStorage } from './memory-storage.js';
6
6
  import { SiweAuth } from './siwe-auth.js';
@@ -112,16 +112,12 @@ export function createPrividiumSiweChain(config) {
112
112
  return parsed.data;
113
113
  }
114
114
  // Create transport with auth integration using viem callbacks
115
- const transport = http(rpcUrl(config.prividiumApiBaseUrl), {
115
+ const baseTransport = http(rpcUrl(config.prividiumApiBaseUrl), {
116
116
  batch: false,
117
117
  fetchOptions: {
118
118
  headers: getAuthHeaders() || {}
119
119
  },
120
120
  onFetchRequest(_request, init) {
121
- // Don't throw on expired token — let the request go through
122
- // so onFetchResponse can handle the 401 and trigger reauthentication.
123
- // Throwing here creates a deadlock: no request is sent, so onFetchResponse
124
- // (which is the only reauth trigger for RPC calls) never fires.
125
121
  const authHeaders = getAuthHeaders();
126
122
  if (authHeaders) {
127
123
  init.headers = {
@@ -129,24 +125,32 @@ export function createPrividiumSiweChain(config) {
129
125
  ...authHeaders
130
126
  };
131
127
  }
132
- },
133
- onFetchResponse: async (response) => {
134
- if (await hasPrividiumUnauthorizedError(response)) {
135
- tokenManager.clearToken();
136
- if (autoReauth) {
137
- try {
138
- await reauthenticate();
128
+ }
129
+ });
130
+ const transport = (parameters) => {
131
+ const base = baseTransport(parameters);
132
+ return {
133
+ ...base,
134
+ async request(request) {
135
+ await ensureAuthorized();
136
+ try {
137
+ return await base.request(request);
138
+ }
139
+ catch (error) {
140
+ if (!isPrividiumUnauthorizedRpcError(error)) {
141
+ throw error;
139
142
  }
140
- catch {
143
+ if (!autoReauth) {
141
144
  config.onAuthExpiry?.();
145
+ throw error;
142
146
  }
143
- }
144
- else {
145
- config.onAuthExpiry?.();
147
+ tokenManager.clearToken();
148
+ await reauthenticate();
149
+ return base.request(request);
146
150
  }
147
151
  }
148
- }
149
- });
152
+ };
153
+ };
150
154
  const apiMethods = createApiMethods({ prividiumApiCall, prividiumApiBaseUrl: config.prividiumApiBaseUrl });
151
155
  return {
152
156
  chain: buildChainObject(config),
@@ -1 +1 @@
1
- {"root":["../src/chain-core.ts","../src/create-prividium-client.ts","../src/error-utils.ts","../src/errors.ts","../src/index.ts","../src/memory-storage.ts","../src/popup-auth.ts","../src/prividium-chain.ts","../src/rpc-error-codes.ts","../src/siwe-auth.ts","../src/siwe-chain.ts","../src/siwe.ts","../src/storage.ts","../src/test-utils.ts","../src/token-utils.ts","../src/types.ts","../src/selective-disclosure/actions.ts","../src/selective-disclosure/batch-info.ts","../src/selective-disclosure/disclosure-result.ts","../src/selective-disclosure/index.ts","../src/selective-disclosure/state-commitment.ts","../src/selective-disclosure/token-balance-disclosure.ts","../src/selective-disclosure/token-supply-disclosure.ts","../src/selective-disclosure/utils.ts","../src/selective-disclosure/verifiy-proofs.ts","../src/selective-disclosure/verify-disclosure.ts"],"version":"5.8.3"}
1
+ {"root":["../src/chain-core.ts","../src/create-prividium-client.ts","../src/error-utils.ts","../src/errors.ts","../src/index.ts","../src/memory-storage.ts","../src/popup-auth.ts","../src/prividium-chain.ts","../src/rpc-error-codes.ts","../src/siwe-auth.ts","../src/siwe-chain.ts","../src/siwe.ts","../src/storage.ts","../src/test-utils.ts","../src/token-utils.ts","../src/types.ts","../src/selective-disclosure/account-properties.ts","../src/selective-disclosure/actions.ts","../src/selective-disclosure/batch-info.ts","../src/selective-disclosure/disclosure-result.ts","../src/selective-disclosure/index.ts","../src/selective-disclosure/state-commitment.ts","../src/selective-disclosure/utils.ts","../src/selective-disclosure/verifiy-proofs.ts","../src/selective-disclosure/verify-disclosure.ts"],"version":"5.8.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prividium",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "prividium": "bin/cli.js"
@@ -34,12 +34,12 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@clack/prompts": "^0.11.0",
37
- "@fastify/http-proxy": "^11.3.0",
37
+ "@fastify/http-proxy": "^11.4.4",
38
38
  "@fastify/static": "^9.0.0",
39
39
  "appdirsjs": "^1.2.7",
40
40
  "blakejs": "^1.2.1",
41
41
  "date-fns": "^4.1.0",
42
- "fastify": "^5.7.2",
42
+ "fastify": "^5.8.5",
43
43
  "fastify-type-provider-zod": "^6.1.0",
44
44
  "kleur": "^4.1.5",
45
45
  "open": "^10.1.0",
@@ -1,7 +0,0 @@
1
- import type { Address, Hex } from 'viem';
2
- import type { EthCallDisclosureResult } from './disclosure-result';
3
- export type TokenBalanceDisclosureRpc = {
4
- Method: 'prividium_tokenBalanceDisclosure';
5
- Parameters: [tokenAddress: Address, holderAddress: Address, blockNumber: Hex];
6
- ReturnType: EthCallDisclosureResult;
7
- };
@@ -1,7 +0,0 @@
1
- import type { Address, Hex } from 'viem';
2
- import type { EthCallDisclosureResult } from './disclosure-result';
3
- export type TokenSupplyDisclosureRpc = {
4
- Method: 'prividium_tokenSupplyDisclosure';
5
- Parameters: [tokenAddress: Address, blockNumber: Hex];
6
- ReturnType: EthCallDisclosureResult;
7
- };