@provablehq/sdk
Version:
A Software Development Kit (SDK) for Zero-Knowledge Transactions
375 lines (374 loc) • 19.3 kB
text/typescript
import { TransportFunction } from "./utils/utils.js";
import { EncryptedRecord } from "./models/record-provider/encryptedRecord.js";
import { CryptoBoxPubKey } from "./models/cryptoBoxPubkey.js";
import { OwnedFilter } from "./models/record-scanner/ownedFilter.js";
import { OwnedRecord } from "./models/record-provider/ownedRecord.js";
import { RecordProvider } from "./record-provider.js";
import { Field, ViewKey } from "./wasm.js";
import { RecordsFilter } from "./models/record-scanner/recordsFilter.js";
import { RegisterResult } from "./models/record-scanner/registrationResult.js";
import { RevokeResult } from "./models/record-scanner/revokeResult.js";
import { TagsResult } from "./models/record-scanner/tagsResult.js";
import { SerialNumbersResult } from "./models/record-scanner/serialNumbersResult.js";
import { StatusResult } from "./models/record-scanner/statusResult.js";
import { OwnedRecordsResult } from "./models/record-scanner/ownedRecordsResult.js";
import { EncryptedRecordsResult } from "./models/record-scanner/encryptedRecordsResult.js";
import { Account } from "./account.js";
/**
* JWT data for optional authentication with the record scanning service (e.g. Provable API).
*
* @property {string} jwt The JWT token string.
* @property {number} expiration Expiration time as a Unix timestamp (e.g. in milliseconds).
*/
export interface RecordScannerJWTData {
jwt: string;
expiration: number;
}
/**
* Configuration for the record scanner.
*
* @property {string} url Base URL of the record scanning service (network path is appended by the SDK).
* @property {string | { header: string, value: string }} [apiKey] API key as a string or as a custom header name and value.
* @property {string} [consumerId] Required for JWT refresh when using authenticated record scanner (e.g. Provable API).
* @property {RecordScannerJWTData} [jwtData] Optional JWT for auth. If omitted and apiKey + consumerId are set, JWT is refreshed when needed.
* @property {ViewKey[]} [viewKeys] Optional view keys to use for local scanning and decryption.
* @property {Account} [account] Optional account to use for local scanning and decryption.
* @property {boolean} [cacheViewKeysOnRegister] Cache view keys in memory for faster scanning upon register.
* @property {boolean} [autoReRegister] If true, on 422 from /owned attempt one re-register via registerEncrypted (when a view key is in viewKeys or account) and retry once. Default false.
* @property {boolean} [decryptEnabled] If true, enable decryption of owned records (e.g. for use with the decrypt method). This is REQUIRED for findCreditsRecord/findCreditsRecords to work properly. Further the ViewKey matching the UUID must be stored in the RecordScanner object to perform decryption. Default false.
*/
export interface RecordScannerOptions {
url: string;
apiKey?: string | {
header: string;
value: string;
};
consumerId?: string;
jwtData?: RecordScannerJWTData;
viewKeys?: ViewKey[];
account?: Account;
cacheViewKeysOnRegister?: boolean;
autoReRegister?: boolean;
decryptEnabled?: boolean;
transport?: TransportFunction;
}
/**
* RecordScanner is a RecordProvider implementation that uses Provable's confidential record scanning service to find
* records.
*
* @example
* const account = new Account({ privateKey: 'APrivateKey1...' });
*
* const recordScanner = new RecordScanner({ url: "https://record-scanner.aleo.org" });
* recordScanner.setAccount(account);
* recordScanner.setApiKey("example-api-key");
* const result = await recordScanner.registerEncrypted(viewKey, 0);
* if (result.ok) { const uuid = result.data.uuid; }
*
* const filter = {
* uuid,
* filter: {
* program: "credits.aleo",
* records: ["credits"],
* },
* responseFilter: {
* commitment: true,
* owner: true,
* tag: true,
* tag?: boolean;
* sender: true,
* spent: true,
* record_ciphertext: true,
* block_height: true;
* block_timestamp: true;
* output_index: true;
* record_name: true;
* function_name: true;
* program_name: true;
* transition_id: true;
* transaction_id: true;
* transaction_index: true;
* transition_index: true;
* },
* unspent: true,
* };
*
* const records = await recordScanner.findRecords(filter);
*/
declare class RecordScanner implements RecordProvider {
readonly cacheViewKeysOnRegister?: boolean;
readonly url: string;
private readonly baseUrl;
private apiKey?;
private consumerId?;
private jwtData?;
private uuid?;
private viewKeys?;
private autoReRegister?;
private decryptEnabled?;
transport: TransportFunction;
account?: Account | undefined;
/**
* @param {RecordScannerOptions} options Configuration for the record scanner.
*/
constructor(options: RecordScannerOptions);
/**
* Set the API key to use for the record scanner.
*
* @param {string | { header: string, value: string }} apiKey The API key to use for the record scanner.
*/
setApiKey(apiKey: string | {
header: string;
value: string;
}): void;
/**
* Set the consumer ID used for JWT refresh when using authenticated record scanner (e.g. Provable API).
*
* @param {string} consumerId The consumer ID to use for JWT refresh.
*/
setConsumerId(consumerId: string): void;
/**
* Set JWT data for authentication. Optional; when not set, JWT can be refreshed from apiKey + consumerId if provided.
*
* @param {RecordScannerJWTData | undefined} jwtData The JWT data to use, or undefined to clear.
*/
setJwtData(jwtData: RecordScannerJWTData | undefined): void;
/**
* Set whether /owned should automatically re-register on 422 (when a view key for the UUID is in viewKeys or account) and retry once.
*
* @param {boolean} enabled Whether to enable auto re-register on 422.
*/
setAutoReRegister(enabled: boolean): void;
/**
* Set whether decryption of owned records is enabled (e.g. for use with the decrypt method).
*
* @param {boolean} enabled Whether to enable decryption of owned records received from the scanner using the `owned` or any `findRecords` methods.
*/
setDecryptEnabled(enabled: boolean): void;
/**
* Add a view key to the record scanner for usage in local decryption. This is REQUIRED for findCreditsRecord/findCreditsRecords to work properly.
*
* @param {ViewKey} viewKey The view key to add.
*/
addViewKey(viewKey: ViewKey): void;
/**
* Remove a view key from the record scanner.
*
* @param {string} uuid The uuid of the view key to remove.
*/
removeViewKey(uuid: string): void;
/**
* Return the view key for the given record-scanner UUID if one is configured
* (in viewKeys or as the account's view key). Used to decide if re-registration on 422 is possible.
*
* @param {string} uuid The record-scanner UUID to look up.
* @returns {ViewKey | undefined} The view key for that UUID, or undefined.
*/
private getViewKeyForUuid;
/**
* Set the primary account for the record scanner.
*
* @param {Account} account The account to set as the primary account.
*/
setAccount(account: Account): void;
/**
* Refreshes the JWT by making a POST request to /jwts/{consumer_id}. Used when authentication is required.
*
* @param {string} apiKey The API key to use for the refresh request.
* @param {string} consumerId The consumer ID for the JWT endpoint.
* @returns {Promise<RecordScannerJWTData>} The new JWT data.
*/
private refreshJwt;
/**
* Returns auth headers (e.g. Authorization with JWT). Refreshes JWT if expired and apiKey + consumerId are set. Empty when auth is not configured.
*
* @returns {Promise<Record<string, string>>} Auth headers to add to requests, or empty object when not configured.
*/
private getAuthHeaders;
/**
* Set the UUID for the record scanner.
*
* @param {Field | ViewKey} keyMaterial The UUID to use for the record scanner. If a ViewKey is provided, the UUID will be computed from the key.
*/
setUuid(keyMaterial: Field | ViewKey): void;
/**
* If the error is a RecordScannerRequestError (from request()), return a RecordScannerFailure result;
* otherwise re-throw the error.
*
* @param {unknown} err The error from a failed request (e.g. from request() or from a catch after calling it).
* @returns {RecordScannerFailure} When err is RecordScannerRequestError.
* @throws Re-throws err when it is not a RecordScannerRequestError.
*/
private handleRequestError;
/**
* Fetches an ephemeral public key from the record scanning service for use with registerEncrypted.
* Follows the same pattern as the delegated proving service /pubkey endpoint.
*
* @returns {Promise<CryptoBoxPubKey>} The service's ephemeral public key and key_id.
*/
getPubkey(): Promise<CryptoBoxPubKey>;
/**
* Registers the account with the record scanning service using the encrypted flow.
* Alias of {@link registerEncrypted} — preserved so existing callers using the
* previous unencrypted `register(viewKey, startBlock)` API continue to work
* unchanged while transparently using the encrypted endpoint.
*
* @param {ViewKey} viewKey The view key to register.
* @param {number} startBlock The block height to start scanning from.
* @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
*/
register(viewKey: ViewKey, startBlock: number): Promise<RegisterResult>;
/**
* Registers the account with the record scanning service using the encrypted flow: 1. fetches an ephemeral public key from /pubkey - 2. encrypts the registration request (view key + start block) - 3. POSTs to /register/encrypted. Does not HTTP error on a proper error response from the record scanner; returns a result object instead.
*
* @param {ViewKey} viewKey The view key to register.
* @param {number} startBlock The block height to start scanning from.
* @returns {Promise<RegisterResult>} `{ ok: true, data }` on success, or `{ ok: false, status, error }` on failure.
*/
registerEncrypted(viewKey: ViewKey, startBlock: number): Promise<RegisterResult>;
/**
* Remove all local scanner state associated with the given UUID (stored uuid, viewKeys entry, account if it matches).
* Called after a successful revoke so the scanner does not retain view keys or account for a revoked registration.
*/
private clearLocalStateForUuid;
/**
* Revoke the account registration with the record scanning service (POST /revoke). On success, also removes
* all local state for that UUID: the stored UUID (if it matches), the view key for that UUID, and the
* account (if its view key corresponds to that UUID).
*
* @param {string | Field | undefined} uuid The UUID to revoke. If omitted, uses the UUID configured on the scanner.
* @returns {Promise<RevokeResult>} `{ ok: true, data: { status } }` on success, or `{ ok: false, status, error }` on failure.
* @throws {UUIDError} When no UUID is configured and none provided, or when the UUID string is invalid.
*/
revoke(uuid?: string | Field): Promise<RevokeResult>;
/**
* Get encrypted records from the record scanning service. This is a safe variant of /records/encrypted that returns
* a result instead of throwing on HTTP error.
*
* @param {RecordsFilter} recordsFilter The filter to use to find the records and filter the response.
* @returns {Promise<EncryptedRecordsResult>} The encrypted records or an error if the request failed.
*/
encrypted(recordsFilter: RecordsFilter): Promise<EncryptedRecordsResult>;
/**
* Get encrypted records from the record scanning service.
*
* @param {RecordsFilter} recordsFilter The filter to use to find the records and filter the response.
* @returns {Promise<EncryptedRecord[]>} The encrypted records.
*/
encryptedRecords(recordsFilter: RecordsFilter): Promise<EncryptedRecord[]>;
/**
* Check if serial numbers appear in any record inputs on-chain, indicating that the records they belong to have been spent. This is a safe variant of /records/sns that returns a result instead of throwing on HTTP error.
*
* @param {string[]} serialNumbers The serial numbers to check.
* @returns {Promise<SerialNumbersResult>} Map of Aleo Record serial numbers and whether they appeared in any inputs on chain. If a boolean corresponding to the Serial Number has a true value, that Record is considered spent by the Aleo Network.
*/
serialNumbers(serialNumbers: string[]): Promise<SerialNumbersResult>;
/**
* Check if serial numbers appear in any record inputs on-chain, indicating that the records they belong to have been spent.
*
* @param {string[]} serialNumbers The serial numbers to check.
* @returns {Promise<Record<string, boolean>>} Map of Aleo Record serial numbers and whether they appeared in any inputs on chain. If boolean corresponding to the Serial Number has a true value, that Record is considered spent by the Aleo Network.
*/
checkSerialNumbers(serialNumbers: string[]): Promise<Record<string, boolean>>;
/**
* Check if tags appear in any record inputs on-chain, indicating that the records they belong to have been spent. This is a safe variant of /records/tags that returns a result instead of throwing on HTTP error.
*
* *
* @param {string[]} tags The tags to check.
* @returns {Promise<TagsResult>} Map of Aleo Record tags and whether they appeared in any inputs on chain. If a boolean corresponding to the tag has a true value, that Record is considered spent by the Aleo Network.
*/
tags(tags: string[]): Promise<TagsResult>;
/**
* Check if tags appear in any record inputs on-chain, indicating that the records they belong to have been spent.
*
* @param {string[]} tags The tags to check.
* @returns {Promise<Record<string, boolean>>} Map of Aleo Record tags and whether they appeared in any inputs on chain. If boolean corresponding to the tag has a true value, that Record is considered spent by the Aleo Network.
*/
checkTags(tags: string[]): Promise<Record<string, boolean>>;
/**
* Check the scan completion job status for a specific UUID.
*
* @param {string | Field | undefined} uuid The UUID of the job to check. If no UUID is provided as input, the UUID configured for the scanner will be used.
* @returns {Promise<StatusResult>} The status of the job or an error if the job could not be found.
*/
status(uuid?: string | Field): Promise<StatusResult>;
/**
* Find a record in the record scanning service.
*
* @param {OwnedFilter} searchParameters The filter to use to find the record.
* @returns {Promise<OwnedRecord>} The record.
*/
findRecord(searchParameters: OwnedFilter): Promise<OwnedRecord>;
/**
* Get owned records. Throws if the UUID passed in the OwnedFilter is invalid or is not configured in the record scanner otherwise returns the RESTFUL response from the record scanner.
*
* @param {OwnedFilter} filter The OwnedFilter used to specify the subset of owned records to select.
* @returns {Promise<OwnedRecordsResult>} Record belonging to the uuid passed in the filter or set on the Record Scanner.
*/
owned(filter: OwnedFilter): Promise<OwnedRecordsResult>;
/**
* Find records using the record scanning service.
*
* @param {OwnedFilter} searchParameters The filter to use to find the records.
* @returns {Promise<OwnedRecord[]>} The records.
*/
findRecords(searchParameters: OwnedFilter): Promise<OwnedRecord[]>;
/**
* Get RecordPlaintext from an OwnedRecord by parsing record_plaintext (trimmed). Returns null if missing or parse fails. Does not decrypt; decryption is handled only in owned().
*/
private getPlaintext;
/**
* For each owned record provided, attempt to decrypt with the given view key. On success, sets record_plaintext on that record to the decrypted plaintext string. Records that fail to decrypt (e.g. wrong view key) or have no record_ciphertext are left unchanged.
*
* @param {ViewKey} viewKey The view key to use for decryption.
* @param {OwnedRecord[]} records The owned records to decrypt (mutated in place).
*/
decrypt(viewKey: ViewKey, records: OwnedRecord[]): void;
/**
* Find a credits.aleo record in the record scanning service.
*
* @param {number} microcredits The amount of microcredits to find.
* @param {OwnedFilter} searchParameters The filter to use to find the record.
* @returns {Promise<OwnedRecord>} The record.
*/
findCreditsRecord(microcredits: number, searchParameters: OwnedFilter): Promise<OwnedRecord>;
/**
* Find credits records greater than or equal to the specified amounts using the record scanning service.
*
* @param {number[]} microcreditAmounts The amounts of microcredits to find.
* @param {OwnedFilter} searchParameters The filter to use to find the records.
* @returns {Promise<OwnedRecord[]>} The records
*/
findCreditsRecords(microcreditAmounts: number[], searchParameters: OwnedFilter): Promise<OwnedRecord[]>;
/**
* Wrapper function to make a request to the record scanning service and handle any errors. Optionally adds JWT Authorization header when consumerId/jwtData (or apiKey+consumerId) are configured.
*
* @param {Request} req The request to make.
* @returns {Promise<Response>} The response when the request succeeds.
* @throws {RecordScannerRequestError} When the server returns a non-2xx status (e.g. 4xx, 5xx).
* @throws Re-throws any error from fetch (e.g. network failure) or from getAuthHeaders().
*/
private request;
/**
* Compute the record scanner UUID for a view key.
*
* @param {ViewKey} viewKey The view key to compute the UUID for.
* @returns {Field} The computed UUID corresponding to the view key.
*/
computeUUID(viewKey: ViewKey): Field;
/**
* Validate a UUID string to ensure it represents a valid Aleo Record Scanner UUID.
*
* @param {string} uuid The UUID to validate.
* @returns {boolean} Whether the UUID is valid.
*/
uuidIsValid(uuid: string): boolean;
/**
* Get the uuid for the filter, first by extracting the UUID from the filter, then falling back to the uuid configured within the record scanner.
*
* @param {OwnedFilter} filter The filter to extract the UUID from.
* @returns {string | undefined} The UUID for the filter, or undefined if the filter does not contain a UUID.
*/
private getUUID;
}
export { RecordScanner };