diff --git a/public/AI_REFERENCE.md b/public/AI_REFERENCE.md index 2692a29..1a753cf 100644 --- a/public/AI_REFERENCE.md +++ b/public/AI_REFERENCE.md @@ -534,6 +534,238 @@ const result = await sdk.documents.get( ); ``` +**Get Document Count** - `documents.count` +*Count documents matching a query. Requires an index declaring countable on the queried properties. Returns a map of group key to count; without groupBy the map has a single aggregate entry.* + +Signature: `count(query: wasm.DocumentsQuery): Promise>` + +Parameters: +- `query`: `wasm.DocumentsQuery` (required) + - Type declarations: [`wasm.DocumentsQuery`](TYPE_REFERENCE.md#type-documentsquery) + - `dataContractId`: `IdentifierLike` (required) + - Data contract identifier. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `documentTypeName`: `string` (required) + - Document type name. + - `where`: `DocumentWhereClause[]` (optional) + - Optional filter clauses expressed as [field, operator, value]. + - Type declarations: [`DocumentWhereClause`](TYPE_REFERENCE.md#type-documentwhereclause) + - `orderBy`: `DocumentOrderByClause[]` (optional) + - Optional sorting clauses expressed as [field, direction]. + - Type declarations: [`DocumentOrderByClause`](TYPE_REFERENCE.md#type-documentorderbyclause) + - `limit`: `number` (optional) + - Maximum number of documents to return. + - `startAfter`: `IdentifierLike` (optional) + - Exclusive document ID to resume from. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `startAt`: `IdentifierLike` (optional) + - Inclusive document ID to start from. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `groupBy`: `string[]` (optional) + - Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors +the v1 wire's `group_by: repeated string` directly. Ignored +by the regular document-fetch path. + +- `[]` or omitted → aggregate count (a single row). +- `[""]` where `` matches an `In` + constraint → per-`In`-value entries (PerInValue). +- `[""]` where `` matches a range + constraint → per-distinct-value entries within the range + (RangeDistinct). +- `["", ""]` for compound `In + range` + queries → compound distinct entries. + +Entry direction comes from the first `orderBy` clause's +direction (which also drives walk order on the materialize + +prove path); set `orderBy: [["", "asc"|"desc"]]` +alongside `groupBy: [""]` to control sort. + +Returns: + +- `Promise>` + +Example: +```javascript +// Requires an index declaring countable on the queried properties. +const result = await sdk.documents.count({ + dataContractId: 'BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP', + documentTypeName: 'review', + where: [["resourceId", "==", "identities-names"]], + orderBy: [["resourceId", "asc"]] +}); +``` + +**Get Document Sum** - `documents.sum` +*Sum a numeric document property across documents matching a query. Requires an index declaring summable for the property. Returns a map of group key to sum.* + +Signature: `sum(query: wasm.DocumentsQuery, sumProperty: string): Promise>` + +Parameters: +- `query`: `wasm.DocumentsQuery` (required) + - Type declarations: [`wasm.DocumentsQuery`](TYPE_REFERENCE.md#type-documentsquery) + - `dataContractId`: `IdentifierLike` (required) + - Data contract identifier. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `documentTypeName`: `string` (required) + - Document type name. + - `where`: `DocumentWhereClause[]` (optional) + - Optional filter clauses expressed as [field, operator, value]. + - Type declarations: [`DocumentWhereClause`](TYPE_REFERENCE.md#type-documentwhereclause) + - `orderBy`: `DocumentOrderByClause[]` (optional) + - Optional sorting clauses expressed as [field, direction]. + - Type declarations: [`DocumentOrderByClause`](TYPE_REFERENCE.md#type-documentorderbyclause) + - `limit`: `number` (optional) + - Maximum number of documents to return. + - `startAfter`: `IdentifierLike` (optional) + - Exclusive document ID to resume from. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `startAt`: `IdentifierLike` (optional) + - Inclusive document ID to start from. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `groupBy`: `string[]` (optional) + - Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors +the v1 wire's `group_by: repeated string` directly. Ignored +by the regular document-fetch path. + +- `[]` or omitted → aggregate count (a single row). +- `[""]` where `` matches an `In` + constraint → per-`In`-value entries (PerInValue). +- `[""]` where `` matches a range + constraint → per-distinct-value entries within the range + (RangeDistinct). +- `["", ""]` for compound `In + range` + queries → compound distinct entries. + +Entry direction comes from the first `orderBy` clause's +direction (which also drives walk order on the materialize + +prove path); set `orderBy: [["", "asc"|"desc"]]` +alongside `groupBy: [""]` to control sort. + +- `sumProperty`: `string` (required) + +Returns: + +- `Promise>` + +Example: +```javascript +// The second argument names the numeric document property to sum. +// Requires an index declaring summable for that property; no known +// testnet contract has one yet, so this call shape is illustrative +// and the server will reject it against this contract. +const result = await sdk.documents.sum({ + dataContractId: 'BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP', + documentTypeName: 'review', + where: [["resourceId", "==", "identities-names"]], + orderBy: [["resourceId", "asc"]] +}, 'rating'); +``` + +**Get Document Average** - `documents.average` +*Aggregate a numeric document property across documents matching a query. Requires an index declaring summable for the property. Returns a map of group key to {count, sum} - divide sum by count to obtain the average.* + +Signature: `average(query: wasm.DocumentsQuery, averageProperty: string): Promise>` + +Parameters: +- `query`: `wasm.DocumentsQuery` (required) + - Type declarations: [`wasm.DocumentsQuery`](TYPE_REFERENCE.md#type-documentsquery) + - `dataContractId`: `IdentifierLike` (required) + - Data contract identifier. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `documentTypeName`: `string` (required) + - Document type name. + - `where`: `DocumentWhereClause[]` (optional) + - Optional filter clauses expressed as [field, operator, value]. + - Type declarations: [`DocumentWhereClause`](TYPE_REFERENCE.md#type-documentwhereclause) + - `orderBy`: `DocumentOrderByClause[]` (optional) + - Optional sorting clauses expressed as [field, direction]. + - Type declarations: [`DocumentOrderByClause`](TYPE_REFERENCE.md#type-documentorderbyclause) + - `limit`: `number` (optional) + - Maximum number of documents to return. + - `startAfter`: `IdentifierLike` (optional) + - Exclusive document ID to resume from. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `startAt`: `IdentifierLike` (optional) + - Inclusive document ID to start from. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `groupBy`: `string[]` (optional) + - Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors +the v1 wire's `group_by: repeated string` directly. Ignored +by the regular document-fetch path. + +- `[]` or omitted → aggregate count (a single row). +- `[""]` where `` matches an `In` + constraint → per-`In`-value entries (PerInValue). +- `[""]` where `` matches a range + constraint → per-distinct-value entries within the range + (RangeDistinct). +- `["", ""]` for compound `In + range` + queries → compound distinct entries. + +Entry direction comes from the first `orderBy` clause's +direction (which also drives walk order on the materialize + +prove path); set `orderBy: [["", "asc"|"desc"]]` +alongside `groupBy: [""]` to control sort. + +- `averageProperty`: `string` (required) + +Returns: + +- `Promise>` + +Example: +```javascript +// Returns { count, sum } per entry - divide sum by count for the average. +// Requires an index declaring summable for the aggregated property; no +// known testnet contract has one yet, so this call shape is illustrative +// and the server will reject it against this contract. +const result = await sdk.documents.average({ + dataContractId: 'BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP', + documentTypeName: 'review', + where: [["resourceId", "==", "identities-names"]], + orderBy: [["resourceId", "asc"]] +}, 'rating'); +``` + +**Get Document History** - `documents.history` +*Fetch historical revisions of a document. Requires a document type with history retention enabled (documentsKeepHistory).* + +Signature: `history(query: wasm.DocumentHistoryQuery): Promise>` + +Parameters: +- `query`: `wasm.DocumentHistoryQuery` (required) + - Type declarations: [`wasm.DocumentHistoryQuery`](TYPE_REFERENCE.md#type-documenthistoryquery) + - `dataContractId`: `IdentifierLike` (required) + - Data contract identifier. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `documentTypeName`: `string` (required) + - Document type name. + - `documentId`: `IdentifierLike` (required) + - Document identifier. + - Type declarations: [`IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + - `startAtMs`: `number` (optional) + - Millisecond timestamp (exclusive) to start after. + - `limit`: `number` (optional) + - Maximum number of entries to return. + - `offset`: `number` (optional) + - Offset for pagination through the document history. + +Returns: + +- `Promise>` + - Type declarations: [`wasm.Document`](TYPE_REFERENCE.md#type-document) + +Example: +```javascript +// Requires a document type with history retention (documentsKeepHistory). +const result = await sdk.documents.history({ + dataContractId: 'BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP', + documentTypeName: 'review', + documentId: '2kWsepFc3PoHEee97xpJQTfbXtaDU9RHB4KdH52wk8f4', + limit: 10 +}); +``` + #### DPNS Queries **Get Primary Username** - `dpns.username` @@ -1191,6 +1423,28 @@ Example: const result = await sdk.tokens.totalSupply('Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv'); ``` +**Get Token Balances for Identity** - `tokens.identityBalances` +*Fetch one identity's balances across multiple tokens. The result map is keyed by token ID (unlike Get Identities Token Balances, which is keyed by identity ID).* + +Signature: `identityBalances(identityId: wasm.IdentifierLike, tokenIds: wasm.IdentifierLikeArray): Promise>` + +Parameters: +- `identityId`: `wasm.IdentifierLike` (required) + - Type declarations: [`wasm.IdentifierLike`](TYPE_REFERENCE.md#type-identifierlike) + +- `tokenIds`: `wasm.IdentifierLikeArray` (required) + - Type declarations: [`wasm.IdentifierLikeArray`](TYPE_REFERENCE.md#type-identifierlikearray) + +Returns: + +- `Promise>` + +Example: +```javascript +// The result map is keyed by token ID. +const result = await sdk.tokens.identityBalances('5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk', ['Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv']); +``` + **Get Token Price by Contract** - `tokens.priceByContract` *Retrieve the price details for a token indexed by contract position.* @@ -1559,6 +1813,99 @@ Example: const result = await sdk.addresses.getMany(['tdash1krt0z5hrcaphyuraxmk2h2ff8nyv5fmncsgf7evf']); ``` +#### Shielded Queries + +**Get Shielded Pool State** - `shielded.poolState` +*Fetch the total balance of the shielded pool in credits. Returns nothing if the pool is empty.* + +Signature: `poolState(): Promise` + +No parameters required. + +Returns: + +- `Promise` + +Example: +```javascript +// Total shielded pool balance in credits (undefined if the pool is empty). +const result = await sdk.shielded.poolState(); +``` + +**Get Shielded Encrypted Notes** - `shielded.encryptedNotes` +*Fetch a page of encrypted shielded notes by global note index.* + +Signature: `encryptedNotes(startIndex: bigint, count: number): Promise` + +Parameters: +- `startIndex`: `bigint` (required) + +- `count`: `number` (required) + +Returns: + +- `Promise` + - Type declarations: [`wasm.ShieldedEncryptedNote`](TYPE_REFERENCE.md#type-shieldedencryptednote) + +Example: +```javascript +const result = await sdk.shielded.encryptedNotes(0n, 10); +``` + +**Get Shielded Anchors** - `shielded.anchors` +*Fetch all valid anchors for building Orchard spend proofs. The SDK returns 32-byte Uint8Array values; this page displays them hex-encoded.* + +Signature: `anchors(): Promise` + +No parameters required. + +Returns: + +- `Promise` + +Example: +```javascript +// Each anchor is a 32-byte Uint8Array. +const result = await sdk.shielded.anchors(); +``` + +**Get Most Recent Shielded Anchor** - `shielded.mostRecentAnchor` +*Fetch the most recent shielded anchor. The SDK returns a 32-byte Uint8Array, or nothing if no anchor exists yet; this page displays it hex-encoded.* + +Signature: `mostRecentAnchor(): Promise` + +No parameters required. + +Returns: + +- `Promise` + +Example: +```javascript +// 32-byte Uint8Array, or undefined if no anchor exists yet. +const result = await sdk.shielded.mostRecentAnchor(); +``` + +**Get Shielded Nullifier Statuses** - `shielded.nullifiers` +*Check whether shielded nullifiers have been spent.* + +Signature: `nullifiers(nullifiers: Uint8Array[]): Promise` + +Parameters: +- `nullifiers`: `Uint8Array[]` (required) + +Returns: + +- `Promise` + - Type declarations: [`wasm.ShieldedNullifierStatus`](TYPE_REFERENCE.md#type-shieldednullifierstatus) + +Example: +```javascript +// Each nullifier must be exactly 32 bytes. +const nullifier = new Uint8Array(32); +return await sdk.shielded.nullifiers([nullifier]); +``` + ## State Transition Operations ### Pattern diff --git a/public/TYPE_REFERENCE.md b/public/TYPE_REFERENCE.md index 7eb8247..c959b0b 100644 --- a/public/TYPE_REFERENCE.md +++ b/public/TYPE_REFERENCE.md @@ -699,6 +699,48 @@ export interface DocumentDeleteOptions { } ``` + +## `DocumentHistoryQuery` + +Source declaration: `wasm-sdk/dist/raw/wasm_sdk.d.ts` + +```typescript +export interface DocumentHistoryQuery { + /** + * Data contract identifier. + */ + dataContractId: IdentifierLike + + /** + * Document type name. + */ + documentTypeName: string; + + /** + * Document identifier. + */ + documentId: IdentifierLike + + /** + * Millisecond timestamp (exclusive) to start after. + * @default 0 + */ + startAtMs?: number; + + /** + * Maximum number of entries to return. + * @default undefined + */ + limit?: number; + + /** + * Offset for pagination through the document history. + * @default undefined + */ + offset?: number; +} +``` + ## `DocumentOrderByClause` @@ -2875,6 +2917,46 @@ export class RewardDistributionMoment { } ``` + +## `ShieldedEncryptedNote` + +Source declaration: `wasm-sdk/dist/raw/wasm_sdk.d.ts` + +```typescript +export class ShieldedEncryptedNote { + private constructor(); + free(): void; + [Symbol.dispose](): void; + static fromJSON(js: object): ShieldedEncryptedNote; + static fromObject(obj: object): ShieldedEncryptedNote; + toJSON(): any; + toObject(): any; + readonly cmx: Uint8Array; + readonly cvNet: Uint8Array; + readonly encryptedNote: Uint8Array; + readonly nullifier: Uint8Array; +} +``` + + +## `ShieldedNullifierStatus` + +Source declaration: `wasm-sdk/dist/raw/wasm_sdk.d.ts` + +```typescript +export class ShieldedNullifierStatus { + private constructor(); + free(): void; + [Symbol.dispose](): void; + static fromJSON(js: object): ShieldedNullifierStatus; + static fromObject(obj: object): ShieldedNullifierStatus; + toJSON(): any; + toObject(): any; + readonly isSpent: boolean; + readonly nullifier: Uint8Array; +} +``` + ## `StateTransitionResult` diff --git a/public/api-definitions.json b/public/api-definitions.json index d947d59..0b4044f 100644 --- a/public/api-definitions.json +++ b/public/api-definitions.json @@ -447,13 +447,13 @@ "name": "startAfter", "type": "text", "label": "Start After", - "help": "Document ID or index value to resume from." + "help": "Exclusive document ID to resume from." }, { "name": "startAt", "type": "text", "label": "Start At", - "help": "Explicit starting index value (JSON string)." + "help": "Inclusive document ID to start from." } ], "sdk_method": "documents.query" @@ -485,6 +485,237 @@ } ], "sdk_method": "documents.get" + }, + "getDocumentCount": { + "label": "Get Document Count", + "description": "Count documents matching a query. Requires an index declaring countable on the queried properties. Returns a map of group key to count; without groupBy the map has a single aggregate entry.", + "inputs": [ + { + "name": "dataContractId", + "type": "text", + "label": "Data Contract ID", + "required": true, + "placeholder": "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP" + }, + { + "name": "documentTypeName", + "type": "text", + "label": "Document Type", + "required": true, + "placeholder": "review" + }, + { + "name": "where", + "type": "json", + "label": "Where Clause (JSON)", + "placeholder": "[[\"resourceId\", \"==\", \"identities-names\"], [\"rating\", \"between\", [1, 5]]]" + }, + { + "name": "orderBy", + "type": "json", + "label": "Order By (JSON)", + "placeholder": "[[\"rating\", \"asc\"]]" + }, + { + "name": "groupBy", + "type": "array", + "label": "Group By", + "placeholder": "[\"rating\"]", + "help": "Optional GROUP BY field list. Omit for a single aggregate row; name a field matched by an In or range constraint for per-value rows." + }, + { + "name": "limit", + "type": "number", + "label": "Limit" + }, + { + "name": "startAfter", + "type": "text", + "label": "Start After", + "help": "Exclusive document ID to resume from." + }, + { + "name": "startAt", + "type": "text", + "label": "Start At", + "help": "Inclusive document ID to start from." + } + ], + "sdk_method": "documents.count" + }, + "getDocumentSum": { + "label": "Get Document Sum", + "description": "Sum a numeric document property across documents matching a query. Requires an index declaring summable for the property. Returns a map of group key to sum.", + "inputs": [ + { + "name": "dataContractId", + "type": "text", + "label": "Data Contract ID", + "required": true, + "placeholder": "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP" + }, + { + "name": "documentTypeName", + "type": "text", + "label": "Document Type", + "required": true, + "placeholder": "review" + }, + { + "name": "sumProperty", + "type": "text", + "label": "Sum Property", + "required": true, + "placeholder": "rating", + "help": "Name of the numeric document property to sum." + }, + { + "name": "where", + "type": "json", + "label": "Where Clause (JSON)", + "placeholder": "[[\"resourceId\", \"==\", \"identities-names\"], [\"rating\", \"between\", [1, 5]]]" + }, + { + "name": "orderBy", + "type": "json", + "label": "Order By (JSON)", + "placeholder": "[[\"rating\", \"asc\"]]" + }, + { + "name": "groupBy", + "type": "array", + "label": "Group By", + "placeholder": "[\"rating\"]", + "help": "Optional GROUP BY field list. Omit for a single aggregate row; name a field matched by an In or range constraint for per-value rows." + }, + { + "name": "limit", + "type": "number", + "label": "Limit" + }, + { + "name": "startAfter", + "type": "text", + "label": "Start After", + "help": "Exclusive document ID to resume from." + }, + { + "name": "startAt", + "type": "text", + "label": "Start At", + "help": "Inclusive document ID to start from." + } + ], + "sdk_method": "documents.sum" + }, + "getDocumentAverage": { + "label": "Get Document Average", + "description": "Aggregate a numeric document property across documents matching a query. Requires an index declaring summable for the property. Returns a map of group key to {count, sum} - divide sum by count to obtain the average.", + "inputs": [ + { + "name": "dataContractId", + "type": "text", + "label": "Data Contract ID", + "required": true, + "placeholder": "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP" + }, + { + "name": "documentTypeName", + "type": "text", + "label": "Document Type", + "required": true, + "placeholder": "review" + }, + { + "name": "averageProperty", + "type": "text", + "label": "Average Property", + "required": true, + "placeholder": "rating", + "help": "Name of the numeric document property to aggregate." + }, + { + "name": "where", + "type": "json", + "label": "Where Clause (JSON)", + "placeholder": "[[\"resourceId\", \"==\", \"identities-names\"], [\"rating\", \"between\", [1, 5]]]" + }, + { + "name": "orderBy", + "type": "json", + "label": "Order By (JSON)", + "placeholder": "[[\"rating\", \"asc\"]]" + }, + { + "name": "groupBy", + "type": "array", + "label": "Group By", + "placeholder": "[\"rating\"]", + "help": "Optional GROUP BY field list. Omit for a single aggregate row; name a field matched by an In or range constraint for per-value rows." + }, + { + "name": "limit", + "type": "number", + "label": "Limit" + }, + { + "name": "startAfter", + "type": "text", + "label": "Start After", + "help": "Exclusive document ID to resume from." + }, + { + "name": "startAt", + "type": "text", + "label": "Start At", + "help": "Inclusive document ID to start from." + } + ], + "sdk_method": "documents.average" + }, + "getDocumentHistory": { + "label": "Get Document History", + "description": "Fetch historical revisions of a document. Requires a document type with history retention enabled (documentsKeepHistory).", + "inputs": [ + { + "name": "dataContractId", + "type": "text", + "label": "Data Contract ID", + "required": true, + "placeholder": "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP" + }, + { + "name": "documentTypeName", + "type": "text", + "label": "Document Type", + "required": true, + "placeholder": "review" + }, + { + "name": "documentId", + "type": "text", + "label": "Document ID", + "required": true, + "placeholder": "2kWsepFc3PoHEee97xpJQTfbXtaDU9RHB4KdH52wk8f4" + }, + { + "name": "startAtMs", + "type": "number", + "label": "Start At (ms)", + "help": "Millisecond timestamp; only revisions strictly after this time are returned." + }, + { + "name": "limit", + "type": "number", + "label": "Limit" + }, + { + "name": "offset", + "type": "number", + "label": "Offset" + } + ], + "sdk_method": "documents.history" } } }, @@ -1109,6 +1340,27 @@ ], "sdk_method": "tokens.totalSupply" }, + "getTokenBalancesForIdentity": { + "label": "Get Token Balances for Identity", + "description": "Fetch one identity's balances across multiple tokens. The result map is keyed by token ID (unlike Get Identities Token Balances, which is keyed by identity ID).", + "inputs": [ + { + "name": "identityId", + "type": "text", + "label": "Identity ID", + "required": true, + "placeholder": "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk" + }, + { + "name": "tokenIds", + "type": "array", + "label": "Token IDs", + "required": true, + "placeholder": "[\"Hqyu7v6mLQTKBLCE6qzquZWLGZapAcnPuYGZDLGoDyxV\"]" + } + ], + "sdk_method": "tokens.identityBalances" + }, "getTokenPriceByContract": { "label": "Get Token Price by Contract", "description": "Retrieve the price details for a token indexed by contract position.", @@ -1471,6 +1723,66 @@ "sdk_method": "addresses.getMany" } } + }, + "shielded": { + "label": "Shielded Queries", + "queries": { + "getShieldedPoolState": { + "label": "Get Shielded Pool State", + "description": "Fetch the total balance of the shielded pool in credits. Returns nothing if the pool is empty.", + "inputs": [], + "sdk_method": "shielded.poolState" + }, + "getShieldedEncryptedNotes": { + "label": "Get Shielded Encrypted Notes", + "description": "Fetch a page of encrypted shielded notes by global note index.", + "inputs": [ + { + "name": "startIndex", + "type": "text", + "label": "Start Index", + "value": "0", + "help": "Global note index to start from (non-negative integer)." + }, + { + "name": "count", + "type": "number", + "label": "Count", + "required": true, + "placeholder": "10", + "help": "Number of notes to fetch." + } + ], + "sdk_method": "shielded.encryptedNotes" + }, + "getShieldedAnchors": { + "label": "Get Shielded Anchors", + "description": "Fetch all valid anchors for building Orchard spend proofs. The SDK returns 32-byte Uint8Array values; this page displays them hex-encoded.", + "inputs": [], + "sdk_method": "shielded.anchors" + }, + "getShieldedMostRecentAnchor": { + "label": "Get Most Recent Shielded Anchor", + "description": "Fetch the most recent shielded anchor. The SDK returns a 32-byte Uint8Array, or nothing if no anchor exists yet; this page displays it hex-encoded.", + "inputs": [], + "sdk_method": "shielded.mostRecentAnchor" + }, + "getShieldedNullifiers": { + "label": "Get Shielded Nullifier Statuses", + "description": "Check whether shielded nullifiers have been spent.", + "inputs": [ + { + "name": "nullifiers", + "type": "array", + "label": "Nullifiers", + "required": true, + "placeholder": "[\"a3f1c2d4e5b60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90\"]", + "help": "Hex-encoded 32-byte nullifiers (64 hex characters each; optional 0x prefix)." + } + ], + "sdk_method": "shielded.nullifiers" + } + } } }, "transitions": { diff --git a/public/docs.css b/public/docs.css index 63cabe8..b147637 100644 --- a/public/docs.css +++ b/public/docs.css @@ -393,6 +393,29 @@ h3 { margin: 5px 0 0; } +.parameter-description code, +.parameter-description-list code { + color: #526684; + font-size: 0.85em; +} + +.parameter-description-list { + color: #71829d; + display: grid; + font-size: 0.88rem; + gap: 5px; + list-style: none; + margin: 7px 0 0 10px; + padding: 0; +} + +.parameter-description-list li { + background: #f7f9fc; + border-left: 2px solid #b9cae3; + border-radius: 0 4px 4px 0; + padding: 6px 10px; +} + .parameter-properties { border-left: 2px solid #e2e9f3; margin: 10px 0 0 8px; diff --git a/public/documentation-check-report.txt b/public/documentation-check-report.txt index c7a2476..717fb07 100644 --- a/public/documentation-check-report.txt +++ b/public/documentation-check-report.txt @@ -1,7 +1,7 @@ ================================================================================ Evo SDK Documentation Check ================================================================================ -Timestamp: 2026-07-23T17:08:43.096666 +Timestamp: 2026-08-27T14:10:15.480441 ✅ All documentation is up to date! ================================================================================ \ No newline at end of file diff --git a/public/sdk-operation-catalog.json b/public/sdk-operation-catalog.json index 1dd9f94..7e38458 100644 --- a/public/sdk-operation-catalog.json +++ b/public/sdk-operation-catalog.json @@ -770,6 +770,374 @@ "wasm.Document" ] }, + { + "key": "getDocumentCount", + "group": "queries", + "category": "document", + "sdkMethod": "documents.count", + "label": "Get Document Count", + "description": "Count documents matching a query. Requires an index declaring countable on the queried properties. Returns a map of group key to count; without groupBy the map has a single aggregate entry.", + "disabled": null, + "signature": "count(query: wasm.DocumentsQuery): Promise>", + "parameters": [ + { + "name": "query", + "type": "wasm.DocumentsQuery", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.DocumentsQuery" + ], + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "where", + "type": "DocumentWhereClause[]", + "optional": true, + "description": "Optional filter clauses expressed as [field, operator, value].", + "references": [ + "DocumentWhereClause" + ] + }, + { + "name": "orderBy", + "type": "DocumentOrderByClause[]", + "optional": true, + "description": "Optional sorting clauses expressed as [field, direction].", + "references": [ + "DocumentOrderByClause" + ] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of documents to return.", + "references": [] + }, + { + "name": "startAfter", + "type": "IdentifierLike", + "optional": true, + "description": "Exclusive document ID to resume from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAt", + "type": "IdentifierLike", + "optional": true, + "description": "Inclusive document ID to start from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "groupBy", + "type": "string[]", + "optional": true, + "description": "Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors\nthe v1 wire's `group_by: repeated string` directly. Ignored\nby the regular document-fetch path.\n\n- `[]` or omitted \u2192 aggregate count (a single row).\n- `[\"\"]` where `` matches an `In`\n constraint \u2192 per-`In`-value entries (PerInValue).\n- `[\"\"]` where `` matches a range\n constraint \u2192 per-distinct-value entries within the range\n (RangeDistinct).\n- `[\"\", \"\"]` for compound `In + range`\n queries \u2192 compound distinct entries.\n\nEntry direction comes from the first `orderBy` clause's\ndirection (which also drives walk order on the materialize +\nprove path); set `orderBy: [[\"\", \"asc\"|\"desc\"]]`\nalongside `groupBy: [\"\"]` to control sort.", + "references": [] + } + ] + } + ], + "returnType": "Promise>", + "references": [] + }, + { + "key": "getDocumentSum", + "group": "queries", + "category": "document", + "sdkMethod": "documents.sum", + "label": "Get Document Sum", + "description": "Sum a numeric document property across documents matching a query. Requires an index declaring summable for the property. Returns a map of group key to sum.", + "disabled": null, + "signature": "sum(query: wasm.DocumentsQuery, sumProperty: string): Promise>", + "parameters": [ + { + "name": "query", + "type": "wasm.DocumentsQuery", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.DocumentsQuery" + ], + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "where", + "type": "DocumentWhereClause[]", + "optional": true, + "description": "Optional filter clauses expressed as [field, operator, value].", + "references": [ + "DocumentWhereClause" + ] + }, + { + "name": "orderBy", + "type": "DocumentOrderByClause[]", + "optional": true, + "description": "Optional sorting clauses expressed as [field, direction].", + "references": [ + "DocumentOrderByClause" + ] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of documents to return.", + "references": [] + }, + { + "name": "startAfter", + "type": "IdentifierLike", + "optional": true, + "description": "Exclusive document ID to resume from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAt", + "type": "IdentifierLike", + "optional": true, + "description": "Inclusive document ID to start from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "groupBy", + "type": "string[]", + "optional": true, + "description": "Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors\nthe v1 wire's `group_by: repeated string` directly. Ignored\nby the regular document-fetch path.\n\n- `[]` or omitted \u2192 aggregate count (a single row).\n- `[\"\"]` where `` matches an `In`\n constraint \u2192 per-`In`-value entries (PerInValue).\n- `[\"\"]` where `` matches a range\n constraint \u2192 per-distinct-value entries within the range\n (RangeDistinct).\n- `[\"\", \"\"]` for compound `In + range`\n queries \u2192 compound distinct entries.\n\nEntry direction comes from the first `orderBy` clause's\ndirection (which also drives walk order on the materialize +\nprove path); set `orderBy: [[\"\", \"asc\"|\"desc\"]]`\nalongside `groupBy: [\"\"]` to control sort.", + "references": [] + } + ] + }, + { + "name": "sumProperty", + "type": "string", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + } + ], + "returnType": "Promise>", + "references": [] + }, + { + "key": "getDocumentAverage", + "group": "queries", + "category": "document", + "sdkMethod": "documents.average", + "label": "Get Document Average", + "description": "Aggregate a numeric document property across documents matching a query. Requires an index declaring summable for the property. Returns a map of group key to {count, sum} - divide sum by count to obtain the average.", + "disabled": null, + "signature": "average(query: wasm.DocumentsQuery, averageProperty: string): Promise>", + "parameters": [ + { + "name": "query", + "type": "wasm.DocumentsQuery", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.DocumentsQuery" + ], + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "where", + "type": "DocumentWhereClause[]", + "optional": true, + "description": "Optional filter clauses expressed as [field, operator, value].", + "references": [ + "DocumentWhereClause" + ] + }, + { + "name": "orderBy", + "type": "DocumentOrderByClause[]", + "optional": true, + "description": "Optional sorting clauses expressed as [field, direction].", + "references": [ + "DocumentOrderByClause" + ] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of documents to return.", + "references": [] + }, + { + "name": "startAfter", + "type": "IdentifierLike", + "optional": true, + "description": "Exclusive document ID to resume from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAt", + "type": "IdentifierLike", + "optional": true, + "description": "Inclusive document ID to start from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "groupBy", + "type": "string[]", + "optional": true, + "description": "Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors\nthe v1 wire's `group_by: repeated string` directly. Ignored\nby the regular document-fetch path.\n\n- `[]` or omitted \u2192 aggregate count (a single row).\n- `[\"\"]` where `` matches an `In`\n constraint \u2192 per-`In`-value entries (PerInValue).\n- `[\"\"]` where `` matches a range\n constraint \u2192 per-distinct-value entries within the range\n (RangeDistinct).\n- `[\"\", \"\"]` for compound `In + range`\n queries \u2192 compound distinct entries.\n\nEntry direction comes from the first `orderBy` clause's\ndirection (which also drives walk order on the materialize +\nprove path); set `orderBy: [[\"\", \"asc\"|\"desc\"]]`\nalongside `groupBy: [\"\"]` to control sort.", + "references": [] + } + ] + }, + { + "name": "averageProperty", + "type": "string", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + } + ], + "returnType": "Promise>", + "references": [] + }, + { + "key": "getDocumentHistory", + "group": "queries", + "category": "document", + "sdkMethod": "documents.history", + "label": "Get Document History", + "description": "Fetch historical revisions of a document. Requires a document type with history retention enabled (documentsKeepHistory).", + "disabled": null, + "signature": "history(query: wasm.DocumentHistoryQuery): Promise>", + "parameters": [ + { + "name": "query", + "type": "wasm.DocumentHistoryQuery", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.DocumentHistoryQuery" + ], + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "documentId", + "type": "IdentifierLike", + "optional": false, + "description": "Document identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAtMs", + "type": "number", + "optional": true, + "description": "Millisecond timestamp (exclusive) to start after.", + "references": [] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of entries to return.", + "references": [] + }, + { + "name": "offset", + "type": "number", + "optional": true, + "description": "Offset for pagination through the document history.", + "references": [] + } + ] + } + ], + "returnType": "Promise>", + "references": [ + "wasm.Document" + ] + }, { "key": "getDpnsUsername", "group": "queries", @@ -1831,6 +2199,42 @@ "wasm.TokenTotalSupply" ] }, + { + "key": "getTokenBalancesForIdentity", + "group": "queries", + "category": "token", + "sdkMethod": "tokens.identityBalances", + "label": "Get Token Balances for Identity", + "description": "Fetch one identity's balances across multiple tokens. The result map is keyed by token ID (unlike Get Identities Token Balances, which is keyed by identity ID).", + "disabled": null, + "signature": "identityBalances(identityId: wasm.IdentifierLike, tokenIds: wasm.IdentifierLikeArray): Promise>", + "parameters": [ + { + "name": "identityId", + "type": "wasm.IdentifierLike", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.IdentifierLike" + ], + "properties": [] + }, + { + "name": "tokenIds", + "type": "wasm.IdentifierLikeArray", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.IdentifierLikeArray" + ], + "properties": [] + } + ], + "returnType": "Promise>", + "references": [] + }, { "key": "getTokenPriceByContract", "group": "queries", @@ -2404,30 +2808,128 @@ ] }, { - "key": "getPlatformAddresses", + "key": "getPlatformAddresses", + "group": "queries", + "category": "address", + "sdkMethod": "addresses.getMany", + "label": "Get Multiple Platform Addresses", + "description": "Fetch information about multiple Platform addresses.", + "disabled": null, + "signature": "getMany(addresses: wasm.PlatformAddressLikeArray): Promise>", + "parameters": [ + { + "name": "addresses", + "type": "wasm.PlatformAddressLikeArray", + "optional": false, + "rest": false, + "description": "- Array of platform addresses to query", + "references": [ + "wasm.PlatformAddressLikeArray" + ], + "properties": [] + } + ], + "returnType": "Promise>", + "references": [ + "wasm.PlatformAddressInfo" + ] + }, + { + "key": "getShieldedPoolState", + "group": "queries", + "category": "shielded", + "sdkMethod": "shielded.poolState", + "label": "Get Shielded Pool State", + "description": "Fetch the total balance of the shielded pool in credits. Returns nothing if the pool is empty.", + "disabled": null, + "signature": "poolState(): Promise", + "parameters": [], + "returnType": "Promise", + "references": [] + }, + { + "key": "getShieldedEncryptedNotes", + "group": "queries", + "category": "shielded", + "sdkMethod": "shielded.encryptedNotes", + "label": "Get Shielded Encrypted Notes", + "description": "Fetch a page of encrypted shielded notes by global note index.", + "disabled": null, + "signature": "encryptedNotes(startIndex: bigint, count: number): Promise", + "parameters": [ + { + "name": "startIndex", + "type": "bigint", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + }, + { + "name": "count", + "type": "number", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + } + ], + "returnType": "Promise", + "references": [ + "wasm.ShieldedEncryptedNote" + ] + }, + { + "key": "getShieldedAnchors", + "group": "queries", + "category": "shielded", + "sdkMethod": "shielded.anchors", + "label": "Get Shielded Anchors", + "description": "Fetch all valid anchors for building Orchard spend proofs. The SDK returns 32-byte Uint8Array values; this page displays them hex-encoded.", + "disabled": null, + "signature": "anchors(): Promise", + "parameters": [], + "returnType": "Promise", + "references": [] + }, + { + "key": "getShieldedMostRecentAnchor", "group": "queries", - "category": "address", - "sdkMethod": "addresses.getMany", - "label": "Get Multiple Platform Addresses", - "description": "Fetch information about multiple Platform addresses.", + "category": "shielded", + "sdkMethod": "shielded.mostRecentAnchor", + "label": "Get Most Recent Shielded Anchor", + "description": "Fetch the most recent shielded anchor. The SDK returns a 32-byte Uint8Array, or nothing if no anchor exists yet; this page displays it hex-encoded.", "disabled": null, - "signature": "getMany(addresses: wasm.PlatformAddressLikeArray): Promise>", + "signature": "mostRecentAnchor(): Promise", + "parameters": [], + "returnType": "Promise", + "references": [] + }, + { + "key": "getShieldedNullifiers", + "group": "queries", + "category": "shielded", + "sdkMethod": "shielded.nullifiers", + "label": "Get Shielded Nullifier Statuses", + "description": "Check whether shielded nullifiers have been spent.", + "disabled": null, + "signature": "nullifiers(nullifiers: Uint8Array[]): Promise", "parameters": [ { - "name": "addresses", - "type": "wasm.PlatformAddressLikeArray", + "name": "nullifiers", + "type": "Uint8Array[]", "optional": false, "rest": false, - "description": "- Array of platform addresses to query", - "references": [ - "wasm.PlatformAddressLikeArray" - ], + "description": "", + "references": [], "properties": [] } ], - "returnType": "Promise>", + "returnType": "Promise", "references": [ - "wasm.PlatformAddressInfo" + "wasm.ShieldedNullifierStatus" ] }, { @@ -5719,7 +6221,250 @@ "description": "", "parameters": [ { - "name": "contractId", + "name": "contractId", + "type": "wasm.IdentifierLike", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.IdentifierLike" + ], + "properties": [] + } + ], + "returnType": "Promise", + "returnReferences": [ + "wasm.DataContract" + ], + "references": [ + "wasm.DataContract" + ], + "variants": [ + { + "key": "getDataContract", + "group": "queries", + "category": "dataContract" + } + ], + "source": "dist/contracts/facade.d.ts" + }, + "contracts.getHistory": { + "sdkMethod": "contracts.getHistory", + "signature": "getHistory(query: wasm.DataContractHistoryQuery): Promise>", + "description": "", + "parameters": [ + { + "name": "query", + "type": "wasm.DataContractHistoryQuery", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.DataContractHistoryQuery" + ], + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of entries to return.", + "references": [] + }, + { + "name": "startAtMs", + "type": "number", + "optional": true, + "description": "Millisecond timestamp (inclusive) to start from.", + "references": [] + } + ] + } + ], + "returnType": "Promise>", + "returnReferences": [ + "wasm.DataContract" + ], + "references": [ + "wasm.DataContract" + ], + "variants": [ + { + "key": "getDataContractHistory", + "group": "queries", + "category": "dataContract" + } + ], + "source": "dist/contracts/facade.d.ts" + }, + "contracts.getMany": { + "sdkMethod": "contracts.getMany", + "signature": "getMany(contractIds: wasm.IdentifierLikeArray): Promise>", + "description": "", + "parameters": [ + { + "name": "contractIds", + "type": "wasm.IdentifierLikeArray", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.IdentifierLikeArray" + ], + "properties": [] + } + ], + "returnType": "Promise>", + "returnReferences": [ + "wasm.DataContract" + ], + "references": [ + "wasm.DataContract" + ], + "variants": [ + { + "key": "getDataContracts", + "group": "queries", + "category": "dataContract" + } + ], + "source": "dist/contracts/facade.d.ts" + }, + "documents.query": { + "sdkMethod": "documents.query", + "signature": "query(query: wasm.DocumentsQuery): Promise>", + "description": "", + "parameters": [ + { + "name": "query", + "type": "wasm.DocumentsQuery", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.DocumentsQuery" + ], + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "where", + "type": "DocumentWhereClause[]", + "optional": true, + "description": "Optional filter clauses expressed as [field, operator, value].", + "references": [ + "DocumentWhereClause" + ] + }, + { + "name": "orderBy", + "type": "DocumentOrderByClause[]", + "optional": true, + "description": "Optional sorting clauses expressed as [field, direction].", + "references": [ + "DocumentOrderByClause" + ] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of documents to return.", + "references": [] + }, + { + "name": "startAfter", + "type": "IdentifierLike", + "optional": true, + "description": "Exclusive document ID to resume from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAt", + "type": "IdentifierLike", + "optional": true, + "description": "Inclusive document ID to start from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "groupBy", + "type": "string[]", + "optional": true, + "description": "Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors\nthe v1 wire's `group_by: repeated string` directly. Ignored\nby the regular document-fetch path.\n\n- `[]` or omitted \u2192 aggregate count (a single row).\n- `[\"\"]` where `` matches an `In`\n constraint \u2192 per-`In`-value entries (PerInValue).\n- `[\"\"]` where `` matches a range\n constraint \u2192 per-distinct-value entries within the range\n (RangeDistinct).\n- `[\"\", \"\"]` for compound `In + range`\n queries \u2192 compound distinct entries.\n\nEntry direction comes from the first `orderBy` clause's\ndirection (which also drives walk order on the materialize +\nprove path); set `orderBy: [[\"\", \"asc\"|\"desc\"]]`\nalongside `groupBy: [\"\"]` to control sort.", + "references": [] + } + ] + } + ], + "returnType": "Promise>", + "returnReferences": [ + "wasm.Document" + ], + "references": [ + "wasm.Document" + ], + "variants": [ + { + "key": "getDocuments", + "group": "queries", + "category": "document" + } + ], + "source": "dist/documents/facade.d.ts" + }, + "documents.get": { + "sdkMethod": "documents.get", + "signature": "get(contractId: wasm.IdentifierLike, type: string, documentId: wasm.IdentifierLike): Promise", + "description": "", + "parameters": [ + { + "name": "contractId", + "type": "wasm.IdentifierLike", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.IdentifierLike" + ], + "properties": [] + }, + { + "name": "type", + "type": "string", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + }, + { + "name": "documentId", "type": "wasm.IdentifierLike", "optional": false, "rest": false, @@ -5730,35 +6475,35 @@ "properties": [] } ], - "returnType": "Promise", + "returnType": "Promise", "returnReferences": [ - "wasm.DataContract" + "wasm.Document" ], "references": [ - "wasm.DataContract" + "wasm.Document" ], "variants": [ { - "key": "getDataContract", + "key": "getDocument", "group": "queries", - "category": "dataContract" + "category": "document" } ], - "source": "dist/contracts/facade.d.ts" + "source": "dist/documents/facade.d.ts" }, - "contracts.getHistory": { - "sdkMethod": "contracts.getHistory", - "signature": "getHistory(query: wasm.DataContractHistoryQuery): Promise>", + "documents.count": { + "sdkMethod": "documents.count", + "signature": "count(query: wasm.DocumentsQuery): Promise>", "description": "", "parameters": [ { "name": "query", - "type": "wasm.DataContractHistoryQuery", + "type": "wasm.DocumentsQuery", "optional": false, "rest": false, "description": "", "references": [ - "wasm.DataContractHistoryQuery" + "wasm.DocumentsQuery" ], "properties": [ { @@ -5770,75 +6515,186 @@ "IdentifierLike" ] }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "where", + "type": "DocumentWhereClause[]", + "optional": true, + "description": "Optional filter clauses expressed as [field, operator, value].", + "references": [ + "DocumentWhereClause" + ] + }, + { + "name": "orderBy", + "type": "DocumentOrderByClause[]", + "optional": true, + "description": "Optional sorting clauses expressed as [field, direction].", + "references": [ + "DocumentOrderByClause" + ] + }, { "name": "limit", "type": "number", "optional": true, - "description": "Maximum number of entries to return.", + "description": "Maximum number of documents to return.", "references": [] }, { - "name": "startAtMs", - "type": "number", + "name": "startAfter", + "type": "IdentifierLike", "optional": true, - "description": "Millisecond timestamp (inclusive) to start from.", + "description": "Exclusive document ID to resume from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAt", + "type": "IdentifierLike", + "optional": true, + "description": "Inclusive document ID to start from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "groupBy", + "type": "string[]", + "optional": true, + "description": "Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors\nthe v1 wire's `group_by: repeated string` directly. Ignored\nby the regular document-fetch path.\n\n- `[]` or omitted \u2192 aggregate count (a single row).\n- `[\"\"]` where `` matches an `In`\n constraint \u2192 per-`In`-value entries (PerInValue).\n- `[\"\"]` where `` matches a range\n constraint \u2192 per-distinct-value entries within the range\n (RangeDistinct).\n- `[\"\", \"\"]` for compound `In + range`\n queries \u2192 compound distinct entries.\n\nEntry direction comes from the first `orderBy` clause's\ndirection (which also drives walk order on the materialize +\nprove path); set `orderBy: [[\"\", \"asc\"|\"desc\"]]`\nalongside `groupBy: [\"\"]` to control sort.", "references": [] } ] } ], - "returnType": "Promise>", - "returnReferences": [ - "wasm.DataContract" - ], - "references": [ - "wasm.DataContract" - ], + "returnType": "Promise>", + "returnReferences": [], + "references": [], "variants": [ { - "key": "getDataContractHistory", + "key": "getDocumentCount", "group": "queries", - "category": "dataContract" + "category": "document" } ], - "source": "dist/contracts/facade.d.ts" + "source": "dist/documents/facade.d.ts" }, - "contracts.getMany": { - "sdkMethod": "contracts.getMany", - "signature": "getMany(contractIds: wasm.IdentifierLikeArray): Promise>", + "documents.sum": { + "sdkMethod": "documents.sum", + "signature": "sum(query: wasm.DocumentsQuery, sumProperty: string): Promise>", "description": "", "parameters": [ { - "name": "contractIds", - "type": "wasm.IdentifierLikeArray", + "name": "query", + "type": "wasm.DocumentsQuery", "optional": false, "rest": false, "description": "", "references": [ - "wasm.IdentifierLikeArray" + "wasm.DocumentsQuery" ], + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "where", + "type": "DocumentWhereClause[]", + "optional": true, + "description": "Optional filter clauses expressed as [field, operator, value].", + "references": [ + "DocumentWhereClause" + ] + }, + { + "name": "orderBy", + "type": "DocumentOrderByClause[]", + "optional": true, + "description": "Optional sorting clauses expressed as [field, direction].", + "references": [ + "DocumentOrderByClause" + ] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of documents to return.", + "references": [] + }, + { + "name": "startAfter", + "type": "IdentifierLike", + "optional": true, + "description": "Exclusive document ID to resume from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAt", + "type": "IdentifierLike", + "optional": true, + "description": "Inclusive document ID to start from.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "groupBy", + "type": "string[]", + "optional": true, + "description": "Count-query knob: SQL-shaped `GROUP BY` field list. Mirrors\nthe v1 wire's `group_by: repeated string` directly. Ignored\nby the regular document-fetch path.\n\n- `[]` or omitted \u2192 aggregate count (a single row).\n- `[\"\"]` where `` matches an `In`\n constraint \u2192 per-`In`-value entries (PerInValue).\n- `[\"\"]` where `` matches a range\n constraint \u2192 per-distinct-value entries within the range\n (RangeDistinct).\n- `[\"\", \"\"]` for compound `In + range`\n queries \u2192 compound distinct entries.\n\nEntry direction comes from the first `orderBy` clause's\ndirection (which also drives walk order on the materialize +\nprove path); set `orderBy: [[\"\", \"asc\"|\"desc\"]]`\nalongside `groupBy: [\"\"]` to control sort.", + "references": [] + } + ] + }, + { + "name": "sumProperty", + "type": "string", + "optional": false, + "rest": false, + "description": "", + "references": [], "properties": [] } ], - "returnType": "Promise>", - "returnReferences": [ - "wasm.DataContract" - ], - "references": [ - "wasm.DataContract" - ], + "returnType": "Promise>", + "returnReferences": [], + "references": [], "variants": [ { - "key": "getDataContracts", + "key": "getDocumentSum", "group": "queries", - "category": "dataContract" + "category": "document" } ], - "source": "dist/contracts/facade.d.ts" + "source": "dist/documents/facade.d.ts" }, - "documents.query": { - "sdkMethod": "documents.query", - "signature": "query(query: wasm.DocumentsQuery): Promise>", + "documents.average": { + "sdkMethod": "documents.average", + "signature": "average(query: wasm.DocumentsQuery, averageProperty: string): Promise>", "description": "", "parameters": [ { @@ -5918,62 +6774,94 @@ "references": [] } ] + }, + { + "name": "averageProperty", + "type": "string", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] } ], - "returnType": "Promise>", - "returnReferences": [ - "wasm.Document" - ], - "references": [ - "wasm.Document" - ], + "returnType": "Promise>", + "returnReferences": [], + "references": [], "variants": [ { - "key": "getDocuments", + "key": "getDocumentAverage", "group": "queries", "category": "document" } ], "source": "dist/documents/facade.d.ts" }, - "documents.get": { - "sdkMethod": "documents.get", - "signature": "get(contractId: wasm.IdentifierLike, type: string, documentId: wasm.IdentifierLike): Promise", + "documents.history": { + "sdkMethod": "documents.history", + "signature": "history(query: wasm.DocumentHistoryQuery): Promise>", "description": "", "parameters": [ { - "name": "contractId", - "type": "wasm.IdentifierLike", - "optional": false, - "rest": false, - "description": "", - "references": [ - "wasm.IdentifierLike" - ], - "properties": [] - }, - { - "name": "type", - "type": "string", - "optional": false, - "rest": false, - "description": "", - "references": [], - "properties": [] - }, - { - "name": "documentId", - "type": "wasm.IdentifierLike", + "name": "query", + "type": "wasm.DocumentHistoryQuery", "optional": false, "rest": false, "description": "", "references": [ - "wasm.IdentifierLike" + "wasm.DocumentHistoryQuery" ], - "properties": [] + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "documentId", + "type": "IdentifierLike", + "optional": false, + "description": "Document identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAtMs", + "type": "number", + "optional": true, + "description": "Millisecond timestamp (exclusive) to start after.", + "references": [] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of entries to return.", + "references": [] + }, + { + "name": "offset", + "type": "number", + "optional": true, + "description": "Offset for pagination through the document history.", + "references": [] + } + ] } ], - "returnType": "Promise", + "returnType": "Promise>", "returnReferences": [ "wasm.Document" ], @@ -5982,7 +6870,7 @@ ], "variants": [ { - "key": "getDocument", + "key": "getDocumentHistory", "group": "queries", "category": "document" } @@ -7184,6 +8072,46 @@ ], "source": "dist/tokens/facade.d.ts" }, + "tokens.identityBalances": { + "sdkMethod": "tokens.identityBalances", + "signature": "identityBalances(identityId: wasm.IdentifierLike, tokenIds: wasm.IdentifierLikeArray): Promise>", + "description": "", + "parameters": [ + { + "name": "identityId", + "type": "wasm.IdentifierLike", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.IdentifierLike" + ], + "properties": [] + }, + { + "name": "tokenIds", + "type": "wasm.IdentifierLikeArray", + "optional": false, + "rest": false, + "description": "", + "references": [ + "wasm.IdentifierLikeArray" + ], + "properties": [] + } + ], + "returnType": "Promise>", + "returnReferences": [], + "references": [], + "variants": [ + { + "key": "getTokenBalancesForIdentity", + "group": "queries", + "category": "token" + } + ], + "source": "dist/tokens/facade.d.ts" + }, "tokens.priceByContract": { "sdkMethod": "tokens.priceByContract", "signature": "priceByContract(contractId: wasm.IdentifierLike, tokenPosition: number): Promise", @@ -7873,6 +8801,128 @@ ], "source": "dist/addresses/facade.d.ts" }, + "shielded.poolState": { + "sdkMethod": "shielded.poolState", + "signature": "poolState(): Promise", + "description": "", + "parameters": [], + "returnType": "Promise", + "returnReferences": [], + "references": [], + "variants": [ + { + "key": "getShieldedPoolState", + "group": "queries", + "category": "shielded" + } + ], + "source": "dist/shielded/facade.d.ts" + }, + "shielded.encryptedNotes": { + "sdkMethod": "shielded.encryptedNotes", + "signature": "encryptedNotes(startIndex: bigint, count: number): Promise", + "description": "", + "parameters": [ + { + "name": "startIndex", + "type": "bigint", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + }, + { + "name": "count", + "type": "number", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + } + ], + "returnType": "Promise", + "returnReferences": [ + "wasm.ShieldedEncryptedNote" + ], + "references": [ + "wasm.ShieldedEncryptedNote" + ], + "variants": [ + { + "key": "getShieldedEncryptedNotes", + "group": "queries", + "category": "shielded" + } + ], + "source": "dist/shielded/facade.d.ts" + }, + "shielded.anchors": { + "sdkMethod": "shielded.anchors", + "signature": "anchors(): Promise", + "description": "", + "parameters": [], + "returnType": "Promise", + "returnReferences": [], + "references": [], + "variants": [ + { + "key": "getShieldedAnchors", + "group": "queries", + "category": "shielded" + } + ], + "source": "dist/shielded/facade.d.ts" + }, + "shielded.mostRecentAnchor": { + "sdkMethod": "shielded.mostRecentAnchor", + "signature": "mostRecentAnchor(): Promise", + "description": "", + "parameters": [], + "returnType": "Promise", + "returnReferences": [], + "references": [], + "variants": [ + { + "key": "getShieldedMostRecentAnchor", + "group": "queries", + "category": "shielded" + } + ], + "source": "dist/shielded/facade.d.ts" + }, + "shielded.nullifiers": { + "sdkMethod": "shielded.nullifiers", + "signature": "nullifiers(nullifiers: Uint8Array[]): Promise", + "description": "Each nullifier must be a `Uint8Array` of exactly 32 bytes; otherwise\nthe underlying call rejects with `InvalidArgument`.", + "parameters": [ + { + "name": "nullifiers", + "type": "Uint8Array[]", + "optional": false, + "rest": false, + "description": "", + "references": [], + "properties": [] + } + ], + "returnType": "Promise", + "returnReferences": [ + "wasm.ShieldedNullifierStatus" + ], + "references": [ + "wasm.ShieldedNullifierStatus" + ], + "variants": [ + { + "key": "getShieldedNullifiers", + "group": "queries", + "category": "shielded" + } + ], + "source": "dist/shielded/facade.d.ts" + }, "identities.create": { "sdkMethod": "identities.create", "signature": "create(options: wasm.IdentityCreateOptions): Promise", @@ -11574,6 +12624,63 @@ ], "source": "wasm-sdk/dist/raw/wasm_sdk.d.ts" }, + "DocumentHistoryQuery": { + "kind": "InterfaceDeclaration", + "anchor": "type-documenthistoryquery", + "declaration": "export interface DocumentHistoryQuery {\n /**\n * Data contract identifier.\n */\n dataContractId: IdentifierLike\n\n /**\n * Document type name.\n */\n documentTypeName: string;\n\n /**\n * Document identifier.\n */\n documentId: IdentifierLike\n\n /**\n * Millisecond timestamp (exclusive) to start after.\n * @default 0\n */\n startAtMs?: number;\n\n /**\n * Maximum number of entries to return.\n * @default undefined\n */\n limit?: number;\n\n /**\n * Offset for pagination through the document history.\n * @default undefined\n */\n offset?: number;\n}", + "properties": [ + { + "name": "dataContractId", + "type": "IdentifierLike", + "optional": false, + "description": "Data contract identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "documentTypeName", + "type": "string", + "optional": false, + "description": "Document type name.", + "references": [] + }, + { + "name": "documentId", + "type": "IdentifierLike", + "optional": false, + "description": "Document identifier.", + "references": [ + "IdentifierLike" + ] + }, + { + "name": "startAtMs", + "type": "number", + "optional": true, + "description": "Millisecond timestamp (exclusive) to start after.", + "references": [] + }, + { + "name": "limit", + "type": "number", + "optional": true, + "description": "Maximum number of entries to return.", + "references": [] + }, + { + "name": "offset", + "type": "number", + "optional": true, + "description": "Offset for pagination through the document history.", + "references": [] + } + ], + "references": [ + "IdentifierLike" + ], + "source": "wasm-sdk/dist/raw/wasm_sdk.d.ts" + }, "DocumentOrderByClause": { "kind": "TypeAliasDeclaration", "anchor": "type-documentorderbyclause", @@ -14690,6 +15797,70 @@ "references": [], "source": "wasm-sdk/dist/raw/wasm_sdk.d.ts" }, + "ShieldedEncryptedNote": { + "kind": "ClassDeclaration", + "anchor": "type-shieldedencryptednote", + "declaration": "export class ShieldedEncryptedNote {\n private constructor();\n free(): void;\n [Symbol.dispose](): void;\n static fromJSON(js: object): ShieldedEncryptedNote;\n static fromObject(obj: object): ShieldedEncryptedNote;\n toJSON(): any;\n toObject(): any;\n readonly cmx: Uint8Array;\n readonly cvNet: Uint8Array;\n readonly encryptedNote: Uint8Array;\n readonly nullifier: Uint8Array;\n}", + "properties": [ + { + "name": "cmx", + "type": "Uint8Array", + "optional": false, + "description": "", + "references": [] + }, + { + "name": "cvNet", + "type": "Uint8Array", + "optional": false, + "description": "", + "references": [] + }, + { + "name": "encryptedNote", + "type": "Uint8Array", + "optional": false, + "description": "", + "references": [] + }, + { + "name": "nullifier", + "type": "Uint8Array", + "optional": false, + "description": "", + "references": [] + } + ], + "references": [ + "ShieldedEncryptedNote" + ], + "source": "wasm-sdk/dist/raw/wasm_sdk.d.ts" + }, + "ShieldedNullifierStatus": { + "kind": "ClassDeclaration", + "anchor": "type-shieldednullifierstatus", + "declaration": "export class ShieldedNullifierStatus {\n private constructor();\n free(): void;\n [Symbol.dispose](): void;\n static fromJSON(js: object): ShieldedNullifierStatus;\n static fromObject(obj: object): ShieldedNullifierStatus;\n toJSON(): any;\n toObject(): any;\n readonly isSpent: boolean;\n readonly nullifier: Uint8Array;\n}", + "properties": [ + { + "name": "isSpent", + "type": "boolean", + "optional": false, + "description": "", + "references": [] + }, + { + "name": "nullifier", + "type": "Uint8Array", + "optional": false, + "description": "", + "references": [] + } + ], + "references": [ + "ShieldedNullifierStatus" + ], + "source": "wasm-sdk/dist/raw/wasm_sdk.d.ts" + }, "StateTransitionResult": { "kind": "ClassDeclaration", "anchor": "type-statetransitionresult", diff --git a/public/src/definitions-data.js b/public/src/definitions-data.js index 07f0ca3..9f53035 100644 --- a/public/src/definitions-data.js +++ b/public/src/definitions-data.js @@ -9,7 +9,7 @@ export const SUPPORTED_QUERIES = new Set([ // Data Contracts 'getDataContract', 'getDataContractHistory', 'getDataContracts', // Documents - 'getDocuments', 'getDocument', + 'getDocuments', 'getDocument', 'getDocumentCount', 'getDocumentSum', 'getDocumentAverage', 'getDocumentHistory', // Epoch 'getEpochsInfo', 'getCurrentEpoch', 'getFinalizedEpochInfos', 'getEvonodesProposedEpochBlocksByIds', 'getEvonodesProposedEpochBlocksByRange', // Voting & Contested Resources @@ -19,7 +19,9 @@ export const SUPPORTED_QUERIES = new Set([ 'getProtocolVersionUpgradeState', 'getProtocolVersionUpgradeVoteStatus', // Tokens 'getTokenStatuses', 'getTokenDirectPurchasePrices', 'getTokenContractInfo', 'getTokenPerpetualDistributionLastClaim', - 'getTokenTotalSupply', 'getTokenPriceByContract', + 'getTokenTotalSupply', 'getTokenBalancesForIdentity', 'getTokenPriceByContract', + // Shielded + 'getShieldedPoolState', 'getShieldedEncryptedNotes', 'getShieldedAnchors', 'getShieldedMostRecentAnchor', 'getShieldedNullifiers', // Groups 'getGroupInfo', 'getGroupInfos', 'getGroupMembers', 'getGroupActions', 'getGroupActionSigners', 'getIdentityGroups', 'getGroupsDataContracts', @@ -136,6 +138,7 @@ export const PROOF_CAPABLE = new Set([ 'getIdentitiesTokenInfos', // Data Contracts & Documents 'getDataContract', 'getDataContractHistory', 'getDataContracts', 'getDocuments', 'getDocument', + 'getDocumentCount', 'getDocumentSum', 'getDocumentAverage', 'getDocumentHistory', // DPNS 'getDpnsUsername', 'getDpnsUsernames', 'getDpnsUsernameByName', // Epoch @@ -149,7 +152,9 @@ export const PROOF_CAPABLE = new Set([ // Tokens 'getTokenStatuses', 'getTokenDirectPurchasePrices', 'getTokenContractInfo', 'getTokenPerpetualDistributionLastClaim', 'getTokenTotalSupply', 'getIdentitiesTokenInfos', 'getIdentityTokenInfos', - 'getIdentitiesTokenBalances', 'getIdentityTokenBalances', + 'getIdentitiesTokenBalances', 'getIdentityTokenBalances', 'getTokenBalancesForIdentity', + // Shielded + 'getShieldedPoolState', 'getShieldedEncryptedNotes', 'getShieldedAnchors', 'getShieldedMostRecentAnchor', 'getShieldedNullifiers', // Groups 'getGroupInfo', 'getGroupInfos', 'getGroupMembers', 'getGroupActions', 'getGroupActionSigners', 'getIdentityGroups', 'getGroupsDataContracts', diff --git a/public/src/operations.js b/public/src/operations.js index efc158a..66c1535 100644 --- a/public/src/operations.js +++ b/public/src/operations.js @@ -39,6 +39,48 @@ export async function callEvo(client, groupKey, itemKey, defs, args, useProof, e } }; + const hexToBytes = (value, label = 'value') => { + const hex = String(value ?? '').trim().replace(/^0x/i, ''); + if (!/^[0-9a-fA-F]{64}$/.test(hex)) { + throw new Error(`Invalid ${label}: expected 64 hexadecimal characters (32 bytes).`); + } + const bytes = new Uint8Array(32); + for (let i = 0; i < 32; i += 1) { + bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16); + } + return bytes; + }; + + const bytesToHex = (bytes) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join(''); + + // Anchors arrive as raw 32-byte values (a single one for mostRecentAnchor, + // an array for anchors); hex-encode them for display while keeping proof + // metadata wrappers intact. + const anchorsToHex = (result) => { + const convert = (data) => { + if (data === undefined || data === null) return data; + return Array.isArray(data) ? data.map(bytesToHex) : bytesToHex(data); + }; + if (result && typeof result === 'object' && 'data' in result && 'metadata' in result && 'proof' in result) { + return { data: convert(result.data), metadata: result.metadata, proof: result.proof }; + } + return convert(result); + }; + + const documentsQueryPayload = () => { + const groupBy = toStringArray(n.groupBy); + return { + dataContractId: n.dataContractId || n.contractId, + documentTypeName: n.documentTypeName || n.documentType, + where: parseJson(n.where, 'Where'), + orderBy: parseJson(n.orderBy, 'Order By'), + limit: n.limit ?? undefined, + startAfter: n.startAfter ?? undefined, + startAt: n.startAt ?? undefined, + ...(groupBy.length ? { groupBy } : {}), + }; + }; + switch (itemKey) { // Identity queries case 'getIdentity': @@ -130,21 +172,42 @@ export async function callEvo(client, groupKey, itemKey, defs, args, useProof, e return useProof ? c.contracts.getManyWithProof(n.ids) : c.contracts.getMany(n.ids); // Documents case 'getDocuments': { - const payload = { - dataContractId: n.dataContractId || n.contractId, - documentTypeName: n.documentTypeName || n.documentType, - where: parseJson(n.where, 'Where'), - orderBy: parseJson(n.orderBy, 'Order By'), - limit: n.limit ?? undefined, - startAfter: n.startAfter ?? undefined, - startAt: n.startAt ?? undefined, - }; + const payload = documentsQueryPayload(); return useProof ? c.documents.queryWithProof(payload) : c.documents.query(payload); } case 'getDocument': return useProof ? c.documents.getWithProof(n.dataContractId || n.contractId, n.documentTypeName || n.documentType, n.documentId) : c.documents.get(n.dataContractId || n.contractId, n.documentTypeName || n.documentType, n.documentId); + case 'getDocumentCount': { + const payload = documentsQueryPayload(); + return useProof ? c.documents.countWithProof(payload) : c.documents.count(payload); + } + case 'getDocumentSum': { + if (!n.sumProperty) { + throw new Error('Sum property is required.'); + } + const payload = documentsQueryPayload(); + return useProof ? c.documents.sumWithProof(payload, n.sumProperty) : c.documents.sum(payload, n.sumProperty); + } + case 'getDocumentAverage': { + if (!n.averageProperty) { + throw new Error('Average property is required.'); + } + const payload = documentsQueryPayload(); + return useProof ? c.documents.averageWithProof(payload, n.averageProperty) : c.documents.average(payload, n.averageProperty); + } + case 'getDocumentHistory': { + const payload = { + dataContractId: n.dataContractId, + documentTypeName: n.documentTypeName, + documentId: n.documentId, + startAtMs: n.startAtMs ?? undefined, + limit: n.limit ?? undefined, + offset: n.offset ?? undefined, + }; + return useProof ? c.documents.historyWithProof(payload) : c.documents.history(payload); + } // DPNS case 'getDpnsUsername': return useProof ? c.dpns.usernameWithProof(n.identityId) : c.dpns.username(n.identityId); @@ -235,6 +298,10 @@ export async function callEvo(client, groupKey, itemKey, defs, args, useProof, e return useProof ? c.tokens.perpetualDistributionLastClaimWithProof(n.identityId, n.tokenId) : c.tokens.perpetualDistributionLastClaim(n.identityId, n.tokenId); case 'getTokenTotalSupply': return useProof ? c.tokens.totalSupplyWithProof(n.tokenId) : c.tokens.totalSupply(n.tokenId); + case 'getTokenBalancesForIdentity': { + const tokenIds = toStringArray(n.tokenIds); + return useProof ? c.tokens.identityBalancesWithProof(n.identityId, tokenIds) : c.tokens.identityBalances(n.identityId, tokenIds); + } case 'getTokenPriceByContract': { const contractId = n.dataContractId || n.contractId; const tokenPosition = toNumber(n.tokenPosition, 0); @@ -436,6 +503,39 @@ export async function callEvo(client, groupKey, itemKey, defs, args, useProof, e return useProof ? c.addresses.getManyWithProof(addresses) : c.addresses.getMany(addresses); } + // Shielded pool queries + case 'getShieldedPoolState': + return useProof ? c.shielded.poolStateWithProof() : c.shielded.poolState(); + case 'getShieldedEncryptedNotes': { + const startIndexRaw = (n.startIndex === undefined || n.startIndex === null || n.startIndex === '') + ? '0' + : String(n.startIndex).trim(); + if (!/^\d+$/.test(startIndexRaw)) { + throw new Error('Start index must be a non-negative integer.'); + } + const count = toNumber(n.count); + if (count === null || !Number.isInteger(count) || count <= 0) { + throw new Error('Count must be a positive integer.'); + } + const startIndex = BigInt(startIndexRaw); + return useProof ? c.shielded.encryptedNotesWithProof(startIndex, count) : c.shielded.encryptedNotes(startIndex, count); + } + case 'getShieldedAnchors': { + const result = useProof ? await c.shielded.anchorsWithProof() : await c.shielded.anchors(); + return anchorsToHex(result); + } + case 'getShieldedMostRecentAnchor': { + const result = useProof ? await c.shielded.mostRecentAnchorWithProof() : await c.shielded.mostRecentAnchor(); + return anchorsToHex(result); + } + case 'getShieldedNullifiers': { + const nullifiers = toStringArray(n.nullifiers).map((value, i) => hexToBytes(value, `nullifier #${i + 1}`)); + if (!nullifiers.length) { + throw new Error('At least one nullifier is required.'); + } + return useProof ? c.shielded.nullifiersWithProof(nullifiers) : c.shielded.nullifiers(nullifiers); + } + default: throw new Error(`Operation ${itemKey} is not supported in the demo UI.`); } diff --git a/scripts/generate_docs.py b/scripts/generate_docs.py index becc7c8..37184ab 100644 --- a/scripts/generate_docs.py +++ b/scripts/generate_docs.py @@ -65,6 +65,12 @@ def rewrite_wasm_wrapper(wasm_file: Path) -> None: 'token_id': 'Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv', 'document_type': 'domain', 'document_id': '7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r', + # DashRate demo contract: review type has countable indices on + # [resourceId] and [resourceId, rating] plus documentsKeepHistory. + 'aggregate_contract_id': 'BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP', + 'aggregate_document_type': 'review', + 'aggregate_resource_id': 'identities-names', + 'history_document_id': '2kWsepFc3PoHEee97xpJQTfbXtaDU9RHB4KdH52wk8f4', 'username': 'alice', 'epoch': 8635, 'platform_address': 'tdash1krt0z5hrcaphyuraxmk2h2ff8nyv5fmncsgf7evf', @@ -296,6 +302,48 @@ def evo_example_for_query(key: str, inputs: List[dict]): '{data['document_id']}' ) """), + 'getDocumentCount': example(f""" + // Requires an index declaring countable on the queried properties. + return await sdk.documents.count({{ + dataContractId: '{data['aggregate_contract_id']}', + documentTypeName: '{data['aggregate_document_type']}', + where: [["resourceId", "==", "{data['aggregate_resource_id']}"]], + orderBy: [["resourceId", "asc"]] + }}) + """), + 'getDocumentSum': example(f""" + // The second argument names the numeric document property to sum. + // Requires an index declaring summable for that property; no known + // testnet contract has one yet, so this call shape is illustrative + // and the server will reject it against this contract. + return await sdk.documents.sum({{ + dataContractId: '{data['aggregate_contract_id']}', + documentTypeName: '{data['aggregate_document_type']}', + where: [["resourceId", "==", "{data['aggregate_resource_id']}"]], + orderBy: [["resourceId", "asc"]] + }}, 'rating') + """), + 'getDocumentAverage': example(f""" + // Returns {{ count, sum }} per entry - divide sum by count for the average. + // Requires an index declaring summable for the aggregated property; no + // known testnet contract has one yet, so this call shape is illustrative + // and the server will reject it against this contract. + return await sdk.documents.average({{ + dataContractId: '{data['aggregate_contract_id']}', + documentTypeName: '{data['aggregate_document_type']}', + where: [["resourceId", "==", "{data['aggregate_resource_id']}"]], + orderBy: [["resourceId", "asc"]] + }}, 'rating') + """), + 'getDocumentHistory': example(f""" + // Requires a document type with history retention (documentsKeepHistory). + return await sdk.documents.history({{ + dataContractId: '{data['aggregate_contract_id']}', + documentTypeName: '{data['aggregate_document_type']}', + documentId: '{data['history_document_id']}', + limit: 10 + }}) + """), 'getDpnsUsername': example(f""" return await sdk.dpns.username('{data['identity_id']}') """), @@ -431,6 +479,10 @@ def evo_example_for_query(key: str, inputs: List[dict]): 'getTokenTotalSupply': example(f""" return await sdk.tokens.totalSupply('{data['token_id']}') """), + 'getTokenBalancesForIdentity': example(f""" + // The result map is keyed by token ID. + return await sdk.tokens.identityBalances('{data['identity_id']}', ['{data['token_id']}']) + """), 'getGroupInfo': example(f""" return await sdk.group.info('{data['group_contract_id']}', 0) """), @@ -510,6 +562,27 @@ def evo_example_for_query(key: str, inputs: List[dict]): 'getIdentityByNonUniquePublicKeyHash': example(f""" return await sdk.identities.byNonUniquePublicKeyHash('{data['public_key_hash_non_unique']}') """), + # Shielded pool + 'getShieldedPoolState': example(""" + // Total shielded pool balance in credits (undefined if the pool is empty). + return await sdk.shielded.poolState() + """), + 'getShieldedEncryptedNotes': example(""" + return await sdk.shielded.encryptedNotes(0n, 10) + """), + 'getShieldedAnchors': example(""" + // Each anchor is a 32-byte Uint8Array. + return await sdk.shielded.anchors() + """), + 'getShieldedMostRecentAnchor': example(""" + // 32-byte Uint8Array, or undefined if no anchor exists yet. + return await sdk.shielded.mostRecentAnchor() + """), + 'getShieldedNullifiers': example(""" + // Each nullifier must be exactly 32 bytes. + const nullifier = new Uint8Array(32); + return await sdk.shielded.nullifiers([nullifier]) + """), } return examples.get(key) @@ -565,6 +638,51 @@ def safe_value(text) -> str: return escape(str(text), quote=False) +def render_description_html(text, css_class: str = 'parameter-description') -> str: + """Render the small Markdown subset used by declaration JSDoc.""" + if not text: + return '' + + def render_inline(value: str) -> str: + escaped = safe_value(value) + return re.sub(r'`([^`]+)`', r'\1', escaped) + + blocks: List[str] = [] + paragraph: List[str] = [] + list_items: List[List[str]] = [] + + def flush_paragraph() -> None: + if paragraph: + blocks.append(f'

{render_inline(" ".join(paragraph))}

') + paragraph.clear() + + def flush_list() -> None: + if list_items: + items = ''.join( + f'
  • {render_inline(" ".join(item))}
  • ' + for item in list_items + ) + blocks.append(f'
      {items}
    ') + list_items.clear() + + for raw_line in str(text).splitlines(): + line = raw_line.strip() + if not line: + flush_paragraph() + flush_list() + elif line.startswith('- '): + flush_paragraph() + list_items.append([line[2:].strip()]) + elif list_items: + list_items[-1].append(line) + else: + paragraph.append(line) + + flush_paragraph() + flush_list() + return '\n'.join(blocks) + + def render_parameter(param: dict) -> str: name = safe_value(param.get('name') or 'Parameter') param_type = str(param.get('type', 'text')) @@ -585,7 +703,7 @@ def render_parameter(param: dict) -> str: description = param.get('description') if description: - lines.append(f'

    {safe_value(description)}

    ') + lines.append(textwrap.indent(render_description_html(description), ' ')) properties = param.get('properties') or [] if properties: @@ -607,7 +725,7 @@ def render_parameter(param: dict) -> str: ' ', ]) if prop.get('description'): - lines.append(f'

    {safe_value(prop["description"])}

    ') + lines.append(textwrap.indent(render_description_html(prop['description']), ' ')) lines.append(' ') lines.append(' ') @@ -646,9 +764,16 @@ def format_example(code: str, header: str, add_return: bool) -> str: body_lines = formatted.split('\n') if add_return and body_lines: - first_line = body_lines[0].lstrip() - if first_line and not first_line.startswith('return'): - body_lines[0] = f'return {first_line}' + # Skip leading comment lines so the return lands on the expression. + first_index = 0 + while first_index < len(body_lines) and ( + not body_lines[first_index].strip() or body_lines[first_index].lstrip().startswith('//') + ): + first_index += 1 + if first_index < len(body_lines): + first_line = body_lines[first_index].lstrip() + if first_line and not first_line.startswith(('return', 'const ', 'let ', 'var ', 'import ')): + body_lines[first_index] = f'return {first_line}' def replace_client(line: str) -> str: return line.replace('client', 'sdk') diff --git a/tests/e2e/fixtures/test-data.js b/tests/e2e/fixtures/test-data.js index b81198b..baa20b6 100644 --- a/tests/e2e/fixtures/test-data.js +++ b/tests/e2e/fixtures/test-data.js @@ -247,6 +247,53 @@ const testData = { documentId: "7NYmEKQsYtniQRUmxwdPGeVcirMoPh5ZPyAKz8BWFy3r" } ] + }, + // DashRate demo contract: review type has countable indices on + // [resourceId] and [resourceId, rating] plus documentsKeepHistory. + getDocumentCount: { + testnet: [ + { + dataContractId: "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP", + documentTypeName: "review", + where: '[["resourceId", "==", "identities-names"]]', + orderBy: '[["resourceId", "asc"]]' + } + ] + }, + // sum/average require an index declaring `summable` for the property; + // no known testnet contract has one (see dashpay/platform#3960), so the + // spec skips these until such a contract exists. + getDocumentSum: { + testnet: [ + { + dataContractId: "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP", + documentTypeName: "review", + where: '[["resourceId", "==", "identities-names"]]', + orderBy: '[["resourceId", "asc"]]', + sumProperty: "rating" + } + ] + }, + getDocumentAverage: { + testnet: [ + { + dataContractId: "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP", + documentTypeName: "review", + where: '[["resourceId", "==", "identities-names"]]', + orderBy: '[["resourceId", "asc"]]', + averageProperty: "rating" + } + ] + }, + getDocumentHistory: { + testnet: [ + { + dataContractId: "BdgTqaTAPYMyhp1WdeWdcvYSgoD7AuJ7tVCaCSXyQgyP", + documentTypeName: "review", + documentId: "2kWsepFc3PoHEee97xpJQTfbXtaDU9RHB4KdH52wk8f4", + limit: 5 + } + ] } }, @@ -445,6 +492,14 @@ const testData = { } ] }, + getTokenBalancesForIdentity: { + testnet: [ + { + identityId: "5DbLwAxGBzUzo81VewMUwn4b5P4bpv9FNFybi25XB5Bk", + tokenIds: ["Hqyu8WcRwXCTwbNxdga4CN5gsVEGc67wng4TFzceyLUv"] + } + ] + }, getTokenPriceByContract: { testnet: [ { @@ -598,6 +653,34 @@ const testData = { } ] } + }, + + shielded: { + getShieldedPoolState: { + testnet: [{}] // No parameters needed + }, + getShieldedEncryptedNotes: { + testnet: [ + { + startIndex: "0", + count: 5 + } + ] + }, + getShieldedAnchors: { + testnet: [{}] + }, + getShieldedMostRecentAnchor: { + testnet: [{}] + }, + getShieldedNullifiers: { + testnet: [ + { + // Any well-formed 32-byte value works; unspent ones return isSpent: false + nullifiers: ["0000000000000000000000000000000000000000000000000000000000000000"] + } + ] + } } }, diff --git a/tests/e2e/queries/query-execution.spec.js b/tests/e2e/queries/query-execution.spec.js index 6de404a..9d4588c 100644 --- a/tests/e2e/queries/query-execution.spec.js +++ b/tests/e2e/queries/query-execution.spec.js @@ -1,6 +1,7 @@ const { test, expect } = require('@playwright/test'); const { EvoSdkPage } = require('../utils/sdk-page'); const { ParameterInjector } = require('../utils/parameter-injector'); +const { getTestParameters } = require('../fixtures/test-data'); /** * Helper function to execute a query with proof toggle enabled @@ -120,8 +121,12 @@ function validateProofContent(resultData) { /** * Helper function to validate result with proof * @param {Object} result - The query result object + * @param {Object} [options] + * @param {boolean} [options.allowNullData] - Accept data: null (for queries + * declared ProofMetadataResponseTyped); proof metadata is still + * validated in full. */ -function validateResultWithProof(result) { +function validateResultWithProof(result, { allowNullData = false } = {}) { expect(result.success).toBe(true); expect(result.result).toBeDefined(); @@ -129,7 +134,9 @@ function validateResultWithProof(result) { const resultData = JSON.parse(result.result); // All proof responses must have a data property expect(resultData).toHaveProperty('data'); - expect(resultData.data).not.toBeNull(); + if (!allowNullData) { + expect(resultData.data).not.toBeNull(); + } expect(resultData.data).toBeDefined(); expect(resultData).toHaveProperty('metadata'); expect(resultData).toHaveProperty('proof'); @@ -637,6 +644,113 @@ test.describe('Evo SDK Query Execution Tests', () => { validateDocumentResult(result.result); } }); + + // Aggregate and history queries run against the DashRate demo contract + // (countable indices on [resourceId] and [resourceId, rating], plus + // documentsKeepHistory on the review type). + const documentAggregateQueries = [ + { + name: 'getDocumentCount', + hasProofSupport: true, + needsParameters: true, + validateFn: (result, isProofMode = false) => { + const parsed = JSON.parse(result); + // Map of group key to count; bigint counts serialize as strings. + // The fixture omits groupBy, so exactly one aggregate entry comes back. + const counts = isProofMode ? parsed.data : parsed; + expect(typeof counts === 'object').toBe(true); + const values = Object.values(counts); + expect(values).toHaveLength(1); + for (const count of values) { + expect(count).toMatch(/^\d+$/); + } + } + }, + { + // sum/average need an index declaring `summable` for the property; no + // known testnet contract has one (see dashpay/platform#3960). + name: 'getDocumentSum', + skip: true, + hasProofSupport: true, + needsParameters: true, + validateFn: () => {} + }, + { + name: 'getDocumentAverage', + skip: true, + hasProofSupport: true, + needsParameters: true, + validateFn: () => {} + }, + { + name: 'getDocumentHistory', + hasProofSupport: true, + needsParameters: true, + validateFn: (result, isProofMode = false) => { + const parsed = JSON.parse(result); + // Map of bigint revision timestamp (stringified) to document + const history = isProofMode ? parsed.data : parsed; + expect(typeof history === 'object').toBe(true); + const keys = Object.keys(history); + expect(keys.length).toBeGreaterThan(0); + for (const key of keys) { + expect(key).toMatch(/^\d+$/); + } + Object.values(history).forEach(doc => validateSingleDocument(doc)); + } + } + ]; + + documentAggregateQueries.forEach(({ name, hasProofSupport, needsParameters, skip, validateFn }) => { + test.describe(`${name} query (parameterized)`, () => { + if (skip) { + test.skip('without proof info', async () => { + // Requires a testnet contract with a summable index + }); + test.skip('with proof info', async () => { + // Requires a testnet contract with a summable index + }); + return; + } + + test('without proof info', async () => { + await evoSdkPage.setupQuery('document', name); + await evoSdkPage.disableProofInfo(); + + if (needsParameters) { + const success = await parameterInjector.injectParameters('document', name, 'testnet'); + expect(success).toBe(true); + } + + const result = await evoSdkPage.executeQueryAndGetResult(); + validateBasicQueryResult(result); + validateResultWithoutProof(result); + validateFn(result.result, false); + }); + + if (hasProofSupport) { + test('with proof info', async () => { + const { result, proofEnabled } = await executeQueryWithProof( + evoSdkPage, + parameterInjector, + 'document', + name, + 'testnet' + ); + + validateBasicQueryResult(result); + + if (proofEnabled) { + validateResultWithProof(result); + validateFn(result.result, true); + } else { + validateResultWithoutProof(result); + validateFn(result.result, false); + } + }); + } + }); + }); }); test.describe('System Queries', () => { @@ -1018,6 +1132,24 @@ test.describe('Evo SDK Query Execution Tests', () => { expect(typeof supplyData === 'object').toBe(true); expect(supplyData).toHaveProperty('totalSupply'); } + }, + { + name: 'getTokenBalancesForIdentity', + hasProofSupport: true, + needsParameters: true, + validateFn: (result) => { + expect(() => JSON.parse(result)).not.toThrow(); + // Map keyed by token ID with bigint balances serialized as strings. + // The requested token must be present (proof-mode keys are base58 too). + const balances = JSON.parse(result); + expect(typeof balances === 'object').toBe(true); + const { tokenIds } = getTestParameters('token', 'getTokenBalancesForIdentity', 'testnet'); + expect(Object.keys(balances)).toContain(tokenIds[0]); + for (const balance of Object.values(balances)) { + expect(typeof balance).toBe('string'); + expect(balance).toMatch(/^\d+$/); + } + } } ]; @@ -1414,6 +1546,144 @@ test.describe('Evo SDK Query Execution Tests', () => { }); }); + test.describe('Shielded Queries', () => { + // Shielded pool data on testnet may be sparse; validators accept legitimate + // empty responses ("Completed (no result returned)") for the nullable queries. + const NO_RESULT = 'Completed (no result returned)'; + // Non-proof scalar results render as the bare formatted string + // (result-format.js returns top-level strings verbatim); proof results are + // a JSON object whose `data` field carries the converted value. Validators + // receive the raw result text plus the mode and assert the exact shape. + const shieldedQueries = [ + { + name: 'getShieldedPoolState', + hasProofSupport: true, + needsParameters: false, + // Declared as ProofMetadataResponseTyped: null when the pool is empty + allowsNullData: true, + validateFn: (result, isProofMode = false) => { + expect(result).toBeDefined(); + if (isProofMode) { + // Balance is a bigint, serialized as a decimal string in data + const parsed = JSON.parse(result); + if (parsed.data === null) return; // empty pool + expect(typeof parsed.data).toBe('string'); + expect(parsed.data).toMatch(/^\d+$/); + } else { + if (result === NO_RESULT) return; // empty pool + expect(result).toMatch(/^\d+$/); + } + } + }, + { + name: 'getShieldedEncryptedNotes', + hasProofSupport: true, + needsParameters: true, + validateFn: (result, isProofMode = false) => { + const parsed = JSON.parse(result); + const notes = isProofMode ? parsed.data : parsed; + expect(Array.isArray(notes)).toBe(true); + for (const note of notes) { + // ShieldedEncryptedNote.toJSON(): base64-encoded byte fields + expect(typeof note.cmx).toBe('string'); + expect(typeof note.nullifier).toBe('string'); + expect(typeof note.cvNet).toBe('string'); + expect(typeof note.encryptedNote).toBe('string'); + } + } + }, + { + name: 'getShieldedAnchors', + hasProofSupport: true, + needsParameters: false, + validateFn: (result, isProofMode = false) => { + const parsed = JSON.parse(result); + const anchors = isProofMode ? parsed.data : parsed; + // The site hex-encodes each 32-byte anchor in both modes + expect(Array.isArray(anchors)).toBe(true); + for (const anchor of anchors) { + expect(anchor).toMatch(/^[0-9a-f]{64}$/); + } + } + }, + { + name: 'getShieldedMostRecentAnchor', + hasProofSupport: true, + needsParameters: false, + // Declared as ProofMetadataResponseTyped: null when no anchor exists + allowsNullData: true, + validateFn: (result, isProofMode = false) => { + expect(result).toBeDefined(); + if (isProofMode) { + const parsed = JSON.parse(result); + if (parsed.data === null) return; // no anchor yet + expect(parsed.data).toMatch(/^[0-9a-f]{64}$/); + } else { + if (result === NO_RESULT) return; // no anchor yet + expect(result).toMatch(/^[0-9a-f]{64}$/); + } + } + }, + { + name: 'getShieldedNullifiers', + hasProofSupport: true, + needsParameters: true, + validateFn: (result, isProofMode = false) => { + const parsed = JSON.parse(result); + const statuses = isProofMode ? parsed.data : parsed; + expect(Array.isArray(statuses)).toBe(true); + expect(statuses.length).toBeGreaterThan(0); + for (const status of statuses) { + // ShieldedNullifierStatus.toJSON(): base64 nullifier + spent flag + expect(typeof status.nullifier).toBe('string'); + expect(typeof status.isSpent).toBe('boolean'); + } + } + } + ]; + + shieldedQueries.forEach(({ name, hasProofSupport, needsParameters, allowsNullData, validateFn }) => { + test.describe(`${name} query (parameterized)`, () => { + test('without proof info', async () => { + await evoSdkPage.setupQuery('shielded', name); + await evoSdkPage.disableProofInfo(); + + if (needsParameters) { + const success = await parameterInjector.injectParameters('shielded', name, 'testnet'); + expect(success).toBe(true); + } + + const result = await evoSdkPage.executeQueryAndGetResult(); + validateBasicQueryResult(result); + validateResultWithoutProof(result); + validateFn(result.result, false); + }); + + if (hasProofSupport) { + test('with proof info', async () => { + const { result, proofEnabled } = await executeQueryWithProof( + evoSdkPage, + parameterInjector, + 'shielded', + name, + 'testnet' + ); + + validateBasicQueryResult(result); + + if (proofEnabled) { + validateResultWithProof(result, { allowNullData: allowsNullData }); + validateFn(result.result, true); + } else { + validateResultWithoutProof(result); + validateFn(result.result, false); + } + }); + } + }); + }); + }); + test.describe('Error Handling', () => { test('should handle invalid identity ID gracefully', async () => { await evoSdkPage.setupQuery('identity', 'getIdentity'); diff --git a/tests/unit/operations-dispatch.test.js b/tests/unit/operations-dispatch.test.js new file mode 100644 index 0000000..eac8f59 --- /dev/null +++ b/tests/unit/operations-dispatch.test.js @@ -0,0 +1,269 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; + +// Focused dispatch coverage for the callEvo() query switch: these operations +// convert form inputs (hex strings, bigint text, JSON clauses) before calling +// the SDK, and that conversion is otherwise only exercised by network-bound +// E2E tests. + +// operations.js transitively imports state.js, which touches `document` at +// import time; stub a minimal shim before dynamically importing (same +// approach as dynamic-handlers.test.js). +let callEvo; + +beforeAll(async () => { + vi.stubGlobal('document', { + getElementById: () => null, + querySelectorAll: () => [], + }); + ({ callEvo } = await import('../../public/src/operations.js')); +}); + +const HEX_A = 'a3f1c2d4e5b60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90'; + +function makeClient() { + return { + documents: { + count: vi.fn().mockResolvedValue('count'), + countWithProof: vi.fn().mockResolvedValue('countProof'), + sum: vi.fn().mockResolvedValue('sum'), + sumWithProof: vi.fn().mockResolvedValue('sumProof'), + average: vi.fn().mockResolvedValue('average'), + averageWithProof: vi.fn().mockResolvedValue('averageProof'), + history: vi.fn().mockResolvedValue('history'), + historyWithProof: vi.fn().mockResolvedValue('historyProof'), + }, + tokens: { + identityBalances: vi.fn().mockResolvedValue('balances'), + identityBalancesWithProof: vi.fn().mockResolvedValue('balancesProof'), + }, + shielded: { + poolState: vi.fn().mockResolvedValue(123n), + poolStateWithProof: vi.fn().mockResolvedValue('poolProof'), + encryptedNotes: vi.fn().mockResolvedValue([]), + encryptedNotesWithProof: vi.fn().mockResolvedValue('notesProof'), + anchors: vi.fn().mockResolvedValue([]), + anchorsWithProof: vi.fn().mockResolvedValue([]), + mostRecentAnchor: vi.fn().mockResolvedValue(undefined), + mostRecentAnchorWithProof: vi.fn().mockResolvedValue(undefined), + nullifiers: vi.fn().mockResolvedValue([]), + nullifiersWithProof: vi.fn().mockResolvedValue([]), + }, + }; +} + +function run(client, itemKey, values = {}, useProof = false) { + return callEvo(client, 'queries', itemKey, [], [], useProof, values); +} + +describe('document aggregate dispatch', () => { + const baseInputs = { + dataContractId: 'contract-id', + documentTypeName: 'domain', + where: '[["normalizedParentDomainName", "==", "dash"]]', + orderBy: '[["normalizedLabel", "asc"]]', + limit: 5, + }; + const expectedPayload = { + dataContractId: 'contract-id', + documentTypeName: 'domain', + where: [['normalizedParentDomainName', '==', 'dash']], + orderBy: [['normalizedLabel', 'asc']], + limit: 5, + startAfter: undefined, + startAt: undefined, + }; + + it('getDocumentCount builds the query payload and dispatches by proof mode', async () => { + const c = makeClient(); + await run(c, 'getDocumentCount', baseInputs); + expect(c.documents.count).toHaveBeenCalledWith(expectedPayload); + await run(c, 'getDocumentCount', baseInputs, true); + expect(c.documents.countWithProof).toHaveBeenCalledWith(expectedPayload); + }); + + it('includes groupBy only when values are provided', async () => { + const c = makeClient(); + await run(c, 'getDocumentCount', { ...baseInputs, groupBy: ['normalizedParentDomainName'] }); + expect(c.documents.count).toHaveBeenCalledWith({ + ...expectedPayload, + groupBy: ['normalizedParentDomainName'], + }); + await run(c, 'getDocumentCount', { ...baseInputs, groupBy: [] }); + expect(c.documents.count).toHaveBeenLastCalledWith(expectedPayload); + }); + + it('getDocumentSum passes the property as a positional argument', async () => { + const c = makeClient(); + await run(c, 'getDocumentSum', { ...baseInputs, sumProperty: 'amount' }); + expect(c.documents.sum).toHaveBeenCalledWith(expectedPayload, 'amount'); + await run(c, 'getDocumentSum', { ...baseInputs, sumProperty: 'amount' }, true); + expect(c.documents.sumWithProof).toHaveBeenCalledWith(expectedPayload, 'amount'); + }); + + it('getDocumentSum requires the property', async () => { + await expect(run(makeClient(), 'getDocumentSum', baseInputs)).rejects.toThrow('Sum property is required'); + }); + + it('getDocumentAverage passes the property as a positional argument', async () => { + const c = makeClient(); + await run(c, 'getDocumentAverage', { ...baseInputs, averageProperty: 'amount' }); + expect(c.documents.average).toHaveBeenCalledWith(expectedPayload, 'amount'); + await run(c, 'getDocumentAverage', { ...baseInputs, averageProperty: 'amount' }, true); + expect(c.documents.averageWithProof).toHaveBeenCalledWith(expectedPayload, 'amount'); + }); + + it('getDocumentAverage requires the property', async () => { + await expect(run(makeClient(), 'getDocumentAverage', baseInputs)).rejects.toThrow('Average property is required'); + }); + + it('rejects invalid where JSON with a labeled error', async () => { + await expect(run(makeClient(), 'getDocumentCount', { ...baseInputs, where: '{not json' })) + .rejects.toThrow('Invalid JSON in Where'); + }); + + it('getDocumentHistory builds its payload with optional fields omitted', async () => { + const c = makeClient(); + await run(c, 'getDocumentHistory', { + dataContractId: 'contract-id', + documentTypeName: 'domain', + documentId: 'doc-id', + }); + expect(c.documents.history).toHaveBeenCalledWith({ + dataContractId: 'contract-id', + documentTypeName: 'domain', + documentId: 'doc-id', + startAtMs: undefined, + limit: undefined, + offset: undefined, + }); + await run(c, 'getDocumentHistory', { + dataContractId: 'contract-id', + documentTypeName: 'domain', + documentId: 'doc-id', + startAtMs: 1700000000000, + limit: 3, + offset: 1, + }, true); + expect(c.documents.historyWithProof).toHaveBeenCalledWith({ + dataContractId: 'contract-id', + documentTypeName: 'domain', + documentId: 'doc-id', + startAtMs: 1700000000000, + limit: 3, + offset: 1, + }); + }); +}); + +describe('getTokenBalancesForIdentity dispatch', () => { + it('passes identity id and filtered token ids in both proof modes', async () => { + const c = makeClient(); + await run(c, 'getTokenBalancesForIdentity', { identityId: 'id-1', tokenIds: ['tok-1', '', null, 'tok-2'] }); + expect(c.tokens.identityBalances).toHaveBeenCalledWith('id-1', ['tok-1', 'tok-2']); + await run(c, 'getTokenBalancesForIdentity', { identityId: 'id-1', tokenIds: ['tok-1'] }, true); + expect(c.tokens.identityBalancesWithProof).toHaveBeenCalledWith('id-1', ['tok-1']); + }); +}); + +describe('shielded dispatch', () => { + it('getShieldedPoolState dispatches by proof mode', async () => { + const c = makeClient(); + await expect(run(c, 'getShieldedPoolState')).resolves.toBe(123n); + await run(c, 'getShieldedPoolState', {}, true); + expect(c.shielded.poolStateWithProof).toHaveBeenCalled(); + }); + + it('getShieldedEncryptedNotes converts startIndex to a BigInt without precision loss', async () => { + const c = makeClient(); + const huge = '18446744073709551615'; // exceeds Number.MAX_SAFE_INTEGER + await run(c, 'getShieldedEncryptedNotes', { startIndex: huge, count: 10 }); + expect(c.shielded.encryptedNotes).toHaveBeenCalledWith(18446744073709551615n, 10); + }); + + it('getShieldedEncryptedNotes defaults a blank startIndex to 0n', async () => { + const c = makeClient(); + await run(c, 'getShieldedEncryptedNotes', { startIndex: '', count: 5 }); + expect(c.shielded.encryptedNotes).toHaveBeenCalledWith(0n, 5); + await run(c, 'getShieldedEncryptedNotes', { count: 5 }, true); + expect(c.shielded.encryptedNotesWithProof).toHaveBeenCalledWith(0n, 5); + }); + + it('getShieldedEncryptedNotes rejects non-integer inputs', async () => { + await expect(run(makeClient(), 'getShieldedEncryptedNotes', { startIndex: '-1', count: 5 })) + .rejects.toThrow('Start index must be a non-negative integer'); + await expect(run(makeClient(), 'getShieldedEncryptedNotes', { startIndex: '1.5', count: 5 })) + .rejects.toThrow('Start index must be a non-negative integer'); + await expect(run(makeClient(), 'getShieldedEncryptedNotes', { count: 0 })) + .rejects.toThrow('Count must be a positive integer'); + await expect(run(makeClient(), 'getShieldedEncryptedNotes', { count: 2.5 })) + .rejects.toThrow('Count must be a positive integer'); + }); + + it('getShieldedNullifiers converts hex strings (with or without 0x) to 32-byte arrays', async () => { + const c = makeClient(); + await run(c, 'getShieldedNullifiers', { nullifiers: [HEX_A, `0x${HEX_A}`] }); + const [nullifiers] = c.shielded.nullifiers.mock.calls[0]; + expect(nullifiers).toHaveLength(2); + for (const bytes of nullifiers) { + expect(bytes).toBeInstanceOf(Uint8Array); + expect(bytes).toHaveLength(32); + expect(bytes[0]).toBe(0xa3); + expect(bytes[31]).toBe(0x90); + } + await run(c, 'getShieldedNullifiers', { nullifiers: [HEX_A] }, true); + expect(c.shielded.nullifiersWithProof).toHaveBeenCalled(); + }); + + it('getShieldedNullifiers rejects invalid input', async () => { + await expect(run(makeClient(), 'getShieldedNullifiers', { nullifiers: ['zz'.repeat(32)] })) + .rejects.toThrow('expected 64 hexadecimal characters'); + await expect(run(makeClient(), 'getShieldedNullifiers', { nullifiers: [HEX_A.slice(0, 62)] })) + .rejects.toThrow('expected 64 hexadecimal characters'); + await expect(run(makeClient(), 'getShieldedNullifiers', { nullifiers: [] })) + .rejects.toThrow('At least one nullifier is required'); + }); + + it('getShieldedAnchors hex-encodes raw results', async () => { + const c = makeClient(); + c.shielded.anchors.mockResolvedValue([ + new Uint8Array(32).fill(0xab), + new Uint8Array(32).fill(0x01), + ]); + await expect(run(c, 'getShieldedAnchors')).resolves.toEqual([ + 'ab'.repeat(32), + '01'.repeat(32), + ]); + }); + + it('getShieldedAnchors converts proof-wrapped data while preserving metadata and proof', async () => { + const c = makeClient(); + const metadata = { height: 1 }; + const proof = { quorumHash: 'abc' }; + c.shielded.anchorsWithProof.mockResolvedValue({ + data: [new Uint8Array(32).fill(0xcd)], + metadata, + proof, + }); + await expect(run(c, 'getShieldedAnchors', {}, true)).resolves.toEqual({ + data: ['cd'.repeat(32)], + metadata, + proof, + }); + }); + + it('getShieldedMostRecentAnchor hex-encodes a single anchor and passes undefined through', async () => { + const c = makeClient(); + c.shielded.mostRecentAnchor.mockResolvedValue(new Uint8Array(32).fill(0x0f)); + await expect(run(c, 'getShieldedMostRecentAnchor')).resolves.toBe('0f'.repeat(32)); + + c.shielded.mostRecentAnchor.mockResolvedValue(undefined); + await expect(run(c, 'getShieldedMostRecentAnchor')).resolves.toBeUndefined(); + + c.shielded.mostRecentAnchorWithProof.mockResolvedValue({ data: null, metadata: {}, proof: {} }); + await expect(run(c, 'getShieldedMostRecentAnchor', {}, true)).resolves.toEqual({ + data: null, + metadata: {}, + proof: {}, + }); + }); +}); diff --git a/tests/unit/result-format.test.js b/tests/unit/result-format.test.js index ca271b1..ffad5b1 100644 --- a/tests/unit/result-format.test.js +++ b/tests/unit/result-format.test.js @@ -70,6 +70,11 @@ describe('formatResult — Map handling', () => { const m = new Map([['bal', 5n]]); expect(formatResult(m)).toBe('{\n "bal": "5"\n}'); }); + + it('stringifies bigint Map keys (documents.history shape)', () => { + const m = new Map([[18446744073709551615n, { revision: 1 }]]); + expect(formatResult(m)).toBe('{\n "18446744073709551615": {\n "revision": 1\n }\n}'); + }); }); describe('formatResult — circular references', () => {