From b40442a1776f57b719d29c64102743ca2d3424ff Mon Sep 17 00:00:00 2001 From: Cameron Marshall Date: Fri, 13 Mar 2026 11:35:30 -0400 Subject: [PATCH] rebase --- .../provable-sdk-tutorial-docs/SKILL.md | 177 +- .claude/CLAUDE.md | 99 +- .claude/commands/wrap-snarkvm.md | 57 +- .codecov.yml | 8 +- .github/ISSUE_TEMPLATE.md | 3 +- .github/ISSUE_TEMPLATE/config.yml | 8 +- .github/ISSUE_TEMPLATE/documentation.md | 2 +- .github/ISSUE_TEMPLATE/feature.md | 7 +- .github/ISSUE_TEMPLATE/proposal.md | 2 +- .github/actions/setup-rust/action.yml | 26 +- .github/actions/setup-yarn/action.yml | 18 +- .github/actions/use-build/action.yml | 12 +- .github/dependabot.yml | 108 +- .github/workflows/sdk.yml | 479 ++-- .github/workflows/staging-website.yml | 62 +- .github/workflows/update-snarkvm.yml | 286 +- .github/workflows/website.yml | 62 +- .prettierignore | 1 + LICENSE.md | 869 ++++--- PUBLISH.md | 2 +- README.md | 73 +- create-leo-app/README.md | 11 +- create-leo-app/build.config.ts | 24 +- create-leo-app/index.js | 2 +- create-leo-app/package.json | 84 +- create-leo-app/src/index.ts | 547 ++-- .../package.json | 34 +- .../rollup.config.js | 2 +- .../src/index.ts | 21 +- .../tsconfig.json | 24 +- create-leo-app/template-devnode-js/README.md | 34 +- create-leo-app/template-devnode-js/index.js | 53 +- .../template-devnode-js/package.json | 20 +- create-leo-app/template-extension/README.md | 3 +- .../template-extension/package.json | 26 +- .../template-extension/src/service_worker.js | 5 +- .../template-extension/src/worker.js | 16 +- .../template-extension/static/manifest.json | 26 +- .../template-extension/static/offscreen.html | 14 +- create-leo-app/template-nextjs-ts/README.md | 2 +- .../template-nextjs-ts/next.config.js | 34 +- .../template-nextjs-ts/package.json | 44 +- .../template-nextjs-ts/src/app/globals.css | 183 +- .../template-nextjs-ts/src/app/layout.tsx | 28 +- .../src/app/page.module.css | 228 +- .../template-nextjs-ts/src/app/page.tsx | 2 +- .../template-nextjs-ts/src/app/worker.ts | 66 +- .../template-nextjs-ts/tsconfig.json | 50 +- .../README.md | 4 +- .../src/index.ts | 56 +- .../README.md | 21 +- .../loyalty_rewards/build/abi.json | 536 ++-- .../build/imports/loyalty_token.abi.json | 842 +++--- .../loyalty_rewards/build/program.json | 14 +- .../loyalty_rewards/program.json | 26 +- .../loyalty_token/build/abi.json | 842 +++--- .../loyalty_token/build/program.json | 14 +- .../src/index.ts | 577 ++-- create-leo-app/template-node-ts/package.json | 34 +- .../template-node-ts/rollup.config.js | 2 +- create-leo-app/template-node-ts/src/index.ts | 99 +- create-leo-app/template-node-ts/tsconfig.json | 24 +- create-leo-app/template-node/index.js | 73 +- create-leo-app/template-node/package.json | 20 +- .../README.md | 53 +- .../package.json | 34 +- .../rollup.config.js | 2 +- .../src/helpers.ts | 40 +- .../src/index.ts | 155 +- .../tsconfig.json | 24 +- .../template-private-transaction-ts/README.md | 5 +- .../package.json | 34 +- .../rollup.config.js | 2 +- .../src/index.ts | 23 +- .../tsconfig.json | 24 +- .../README.md | 9 +- .../src/App.tsx | 20 +- .../src/workers/worker.ts | 57 +- .../template-react-leo/.eslintrc.cjs | 36 +- create-leo-app/template-react-leo/README.md | 52 +- .../template-react-leo/helloworld/README.md | 2 + create-leo-app/template-react-leo/index.html | 156 +- .../template-react-leo/package.json | 84 +- create-leo-app/template-react-leo/src/App.css | 42 +- create-leo-app/template-react-leo/src/App.jsx | 172 +- .../template-react-leo/src/index.css | 86 +- .../template-react-leo/src/main.jsx | 6 +- .../src/workers/AleoWorker.js | 4 +- .../template-react-leo/src/workers/worker.js | 92 +- .../template-react-leo/vite.config.js | 20 +- .../template-react-leo/webpack.config.js | 175 +- .../README.md | 31 +- .../index.html | 20 +- .../loyalty_rewards/build/abi.json | 536 ++-- .../build/imports/loyalty_token.abi.json | 842 +++--- .../loyalty_rewards/build/program.json | 14 +- .../loyalty_rewards/program.json | 28 +- .../loyalty_token/build/abi.json | 842 +++--- .../loyalty_token/build/program.json | 14 +- .../package.json | 96 +- .../src/App.css | 499 ++-- .../src/App.tsx | 1025 ++++---- .../src/index.css | 38 +- .../src/main.tsx | 6 +- .../src/vite-env.d.ts | 28 +- .../src/workers/AleoWorker.ts | 54 +- .../src/workers/worker.ts | 2309 +++++++++-------- .../tsconfig.json | 30 +- .../vite.config.ts | 20 +- .../webpack.config.ts | 178 +- .../.eslintrc.cjs | 36 +- .../template-react-managed-worker/README.md | 3 +- .../template-react-managed-worker/index.html | 156 +- .../package.json | 78 +- .../template-react-managed-worker/src/App.css | 42 +- .../template-react-managed-worker/src/App.jsx | 129 +- .../src/index.css | 86 +- .../src/main.jsx | 6 +- .../vite.config.js | 26 +- .../webpack.config.js | 146 +- .../template-react-ts/.eslintrc.cjs | 36 +- create-leo-app/template-react-ts/README.md | 52 +- .../template-react-ts/helloworld/README.md | 2 + create-leo-app/template-react-ts/index.html | 156 +- create-leo-app/template-react-ts/package.json | 94 +- create-leo-app/template-react-ts/src/App.css | 42 +- create-leo-app/template-react-ts/src/App.tsx | 172 +- .../template-react-ts/src/custom.d.ts | 18 +- .../template-react-ts/src/index.css | 86 +- create-leo-app/template-react-ts/src/main.tsx | 6 +- .../src/workers/AleoWorker.ts | 4 +- .../template-react-ts/src/workers/worker.ts | 92 +- .../template-react-ts/tsconfig.json | 28 +- .../template-react-ts/vite.config.ts | 20 +- .../template-react-ts/webpack.config.ts | 192 +- create-leo-app/template-vanilla/main.js | 2 +- .../template-vanilla/vite.config.js | 4 +- create-leo-app/template-vanilla/worker.js | 85 +- create-leo-app/tsconfig.json | 26 +- docs/00_overview.md | 69 +- docs/create-leo-app/01_intro.md | 64 +- docs/create-leo-app/02_react_js_tutorial.md | 52 +- docs/examples/01_transfer_public.md | 28 +- docs/examples/02_transfer_private.md | 56 +- docs/examples/03_deploy_program.md | 19 +- docs/examples/04_execute_program.md | 19 +- docs/guide/01_intro.md | 66 +- docs/guide/02_setup.md | 60 +- docs/guide/03_creating_accounts.md | 69 +- docs/guide/04_programs.md | 131 +- docs/guide/05_transfers.md | 194 +- docs/guide/06_executing_programs.md | 194 +- docs/guide/07_deploying_programs.md | 109 +- docs/guide/08_public_program_state.md | 115 +- docs/guide/09_private_program_state.md | 156 +- docs/guide/10_finding_records.md | 147 +- docs/guide/11_working_with_chain_state.md | 297 ++- e2e/dynamic/index.js | 40 +- e2e/dynamic/package.json | 20 +- e2e/mainnet/index.js | 48 +- e2e/mainnet/package.json | 20 +- e2e/nodenext/index.ts | 8 +- e2e/nodenext/tsconfig.json | 12 +- e2e/testnet/index.js | 48 +- e2e/testnet/package.json | 20 +- package.json | 76 +- scripts/change-version.js | 57 +- sdk/.eslintrc.json | 28 +- sdk/README.md | 22 +- sdk/docs/index.html | 1332 +++++++++- sdk/jsdoc.json | 67 +- sdk/package.json | 162 +- sdk/rollup.config.js | 56 +- sdk/rollup.test.js | 11 +- sdk/src/account.ts | 891 ++++--- sdk/src/browser.ts | 95 +- sdk/src/constants.ts | 20 +- sdk/src/integrations/sealance/merkle-tree.ts | 362 +-- sdk/src/keys/keystore/error.ts | 2 +- sdk/src/keys/keystore/file.ts | 57 +- sdk/src/keys/keystore/interface.ts | 10 +- sdk/src/keys/provider/interface.ts | 2 +- sdk/src/keys/provider/memory.ts | 20 +- sdk/src/keys/provider/offline.ts | 354 ++- sdk/src/keys/verifier/interface.ts | 7 +- sdk/src/keys/verifier/memory.ts | 37 +- sdk/src/models/authorization.ts | 2 +- sdk/src/models/blockJSON.ts | 12 +- sdk/src/models/confirmed_transaction.ts | 2 +- sdk/src/models/cryptoBoxPubkey.ts | 6 +- sdk/src/models/deployment/deploymentJSON.ts | 8 +- sdk/src/models/deployment/deploymentObject.ts | 6 +- sdk/src/models/encryptedProvingRequest.ts | 6 +- sdk/src/models/finalizeJSON.ts | 8 +- sdk/src/models/functionInput.ts | 12 +- sdk/src/models/functionObject.ts | 10 +- sdk/src/models/imports.ts | 2 +- sdk/src/models/input/inputJSON.ts | 2 +- sdk/src/models/input/inputObject.ts | 8 +- sdk/src/models/inputID.ts | 2 +- sdk/src/models/keyPair.ts | 2 +- sdk/src/models/output/outputObject.ts | 14 +- sdk/src/models/owner/ownerJSON.ts | 2 +- sdk/src/models/plaintext/array.ts | 5 +- sdk/src/models/plaintext/literal.ts | 2 +- sdk/src/models/plaintext/plaintext.ts | 6 +- sdk/src/models/plaintext/struct.ts | 2 +- sdk/src/models/provingRequest.ts | 2 +- sdk/src/models/provingResponse.ts | 12 +- sdk/src/models/ratification.ts | 2 +- .../models/record-provider/encryptedRecord.ts | 6 +- sdk/src/models/record-provider/ownedRecord.ts | 2 +- .../record-provider/recordSearchParams.ts | 4 +- .../record-scanner/encryptedRecordsResult.ts | 5 +- sdk/src/models/record-scanner/error.ts | 1 - sdk/src/models/record-scanner/ownedFilter.ts | 6 +- .../ownedRecordsResponseFilter.ts | 4 +- .../record-scanner/ownedRecordsResult.ts | 1 - .../models/record-scanner/recordsFilter.ts | 4 +- .../record-scanner/recordsResponseFilter.ts | 4 +- .../record-scanner/registrationRequest.ts | 4 +- .../record-scanner/registrationResponse.ts | 8 +- .../record-scanner/serialNumbersResult.ts | 1 - .../models/record-scanner/statusResponse.ts | 4 +- sdk/src/models/record-scanner/statusResult.ts | 1 - sdk/src/models/record-scanner/tagsResult.ts | 1 - sdk/src/models/request.ts | 4 +- sdk/src/models/solution.ts | 2 +- .../models/transaction/transactionObject.ts | 17 +- sdk/src/models/transition/transitionJSON.ts | 4 +- sdk/src/models/transition/transitionObject.ts | 4 +- sdk/src/network-client.ts | 194 +- sdk/src/polyfill/fetch.ts | 10 +- sdk/src/polyfill/worker.ts | 32 +- sdk/src/polyfill/xmlhttprequest.ts | 6 +- sdk/src/program-manager.ts | 511 ++-- sdk/src/record-provider.ts | 148 +- sdk/src/record-scanner.ts | 203 +- sdk/src/security.ts | 26 +- sdk/src/utils.ts | 29 +- sdk/src/wasm.ts | 4 +- sdk/tests/account.test.ts | 268 +- sdk/tests/algorithm.test.ts | 268 +- sdk/tests/arithmetic.test.ts | 130 +- sdk/tests/data/account-data.ts | 105 +- sdk/tests/data/algebra.ts | 158 +- sdk/tests/data/program.ts | 27 +- sdk/tests/data/proving.ts | 14 +- sdk/tests/data/records.ts | 440 ++-- sdk/tests/key-provider.test.ts | 566 +++- sdk/tests/network-client.integration.ts | 99 +- sdk/tests/network-client.test.ts | 141 +- sdk/tests/program-manager.test.ts | 403 ++- sdk/tests/record-provider.integration.ts | 29 +- sdk/tests/record-provider.test.ts | 22 +- sdk/tests/record-scanner-integration.spec.ts | 22 +- sdk/tests/record-scanner.test.ts | 322 ++- sdk/tests/sealance-merkle-tree.test.ts | 334 +-- sdk/tests/wasm.test.ts | 531 ++-- sdk/tsconfig.json | 52 +- sdk/tsconfig.test.json | 8 +- wasm/LICENSE.md | 869 ++++--- wasm/README.md | 54 +- wasm/build.js | 153 +- wasm/package.json | 98 +- wasm/test.js | 10 +- website/.eslintrc.cjs | 2 +- website/README.md | 12 +- website/index.html | 53 +- website/src/App.css | 12 +- website/src/components/WasmLoadingMessage.jsx | 11 +- website/src/index.jsx | 6 +- website/src/main.jsx | 49 +- website/src/pages/Homepage.css | 5 +- website/src/pages/Homepage.jsx | 52 +- website/src/pages/PrivacyPolicy.css | 11 +- website/src/pages/PrivacyPolicy.jsx | 140 +- website/src/pages/TermsOfUse.jsx | 402 +-- website/src/routing.jsx | 5 +- .../tabs/account/AccountFromPrivateKey.jsx | 12 +- .../src/tabs/account/AddressFromViewKey.jsx | 11 +- website/src/tabs/account/NewAccount.jsx | 7 +- website/src/tabs/account/SignMessage.jsx | 14 +- website/src/tabs/account/VerifyMessage.jsx | 5 +- website/src/tabs/advanced/DecryptAccount.jsx | 18 +- website/src/tabs/advanced/EncryptAccount.jsx | 7 +- website/src/tabs/algebra/FieldArithmetic.jsx | 91 +- website/src/tabs/algebra/GroupArithmetic.jsx | 122 +- website/src/tabs/algebra/HashFunctions.jsx | 443 +++- website/src/tabs/develop/Deploy.jsx | 69 +- website/src/tabs/develop/ExecuteLegacy.jsx | 8 +- website/src/tabs/develop/Join.jsx | 24 +- website/src/tabs/develop/Split.jsx | 12 +- website/src/tabs/develop/Transfer.jsx | 8 +- website/src/tabs/develop/execute/index.jsx | 455 ++-- website/src/tabs/protocol/DecryptRecord.css | 2 +- website/src/tabs/protocol/DecryptRecord.jsx | 12 +- website/src/tabs/protocol/TransactionInfo.jsx | 1237 +++++---- website/src/tabs/protocol/transactions.js | 6 +- website/src/tabs/rest/GetBlockByHash.jsx | 9 +- website/src/tabs/rest/GetBlockByHeight.jsx | 9 +- website/src/tabs/rest/GetLatestBlock.jsx | 16 +- .../src/tabs/rest/GetLatestBlockHeight.jsx | 20 +- website/src/tabs/rest/GetMappingNames.jsx | 7 +- website/src/tabs/rest/GetMappingValue.jsx | 12 +- website/src/tabs/rest/GetProgram.jsx | 9 +- website/src/tabs/rest/GetTransaction.jsx | 13 +- website/src/workers/worker.js | 246 +- website/vercel.json | 44 +- website/vite.config.js | 2 +- website/webpack.config.js | 22 +- 311 files changed, 19417 insertions(+), 13524 deletions(-) create mode 100644 .prettierignore diff --git a/.agents/skills/provable-sdk-tutorial-docs/SKILL.md b/.agents/skills/provable-sdk-tutorial-docs/SKILL.md index 999f4940e..3d033e254 100644 --- a/.agents/skills/provable-sdk-tutorial-docs/SKILL.md +++ b/.agents/skills/provable-sdk-tutorial-docs/SKILL.md @@ -1,6 +1,9 @@ --- name: provable-sdk-tutorial-docs -description: Use when writing tutorial documentation or create-leo-app examples for the Provable SDK (@provablehq/sdk). Covers how to structure runnable templates, write tutorial-style docs, and handle Node.js vs web runtime differences. +description: + Use when writing tutorial documentation or create-leo-app examples for the + Provable SDK (@provablehq/sdk). Covers how to structure runnable templates, + write tutorial-style docs, and handle Node.js vs web runtime differences. --- # Provable SDK Tutorial Docs @@ -8,10 +11,12 @@ description: Use when writing tutorial documentation or create-leo-app examples ## Overview This skill guides writing two interconnected artifacts for each SDK feature set: + 1. A runnable **create-leo-app template** demonstrating the features 2. **Tutorial documentation** that walks readers through the template -The template and tutorial are the same content expressed two ways — step numbers, section names, and code structure must correspond exactly. +The template and tutorial are the same content expressed two ways — step +numbers, section names, and code structure must correspond exactly. --- @@ -19,39 +24,56 @@ The template and tutorial are the same content expressed two ways — step numbe Require the developer to specify: -| Input | Description | -|-------|-------------| -| **Feature bucket** | Explicit list of SDK features to demonstrate (e.g. "key caching, offline execution, deployment") | -| **Target runtimes** | `node`, `web`, or `both` | -| **Highlight areas** | What concepts readers should leave understanding — not just what the code does, but WHY | -| **Complexity level** | beginner / intermediate / advanced — determines how much Aleo/ZK background to assume | +| Input | Description | +| -------------------- | ------------------------------------------------------------------------------------------------ | +| **Feature bucket** | Explicit list of SDK features to demonstrate (e.g. "key caching, offline execution, deployment") | +| **Target runtimes** | `node`, `web`, or `both` | +| **Highlight areas** | What concepts readers should leave understanding — not just what the code does, but WHY | +| **Complexity level** | beginner / intermediate / advanced — determines how much Aleo/ZK background to assume | --- ## SDK Runtime Context -Tutorials must open with context about why someone would use the SDK in this environment. Use the framing below as a guide. +Tutorials must open with context about why someone would use the SDK in this +environment. Use the framing below as a guide. ### Node.js The SDK runs natively in Node — no WASM worker setup needed. Common use cases: -- **Backend servers** that execute web3 functions on behalf of users (e.g. building and submitting transactions server-side) -- **Hardware wallets** that execute transactions in an offline setting on behalf of users -- **Command-line tools** and scripts for account management, transaction creation, or record or chain data inspection, or specialized functions such as MPC computations run by web3 service providers -- **Local testing and prototyping** local testing of dapps or web3 service prototypes +- **Backend servers** that execute web3 functions on behalf of users (e.g. + building and submitting transactions server-side) +- **Hardware wallets** that execute transactions in an offline setting on behalf + of users +- **Command-line tools** and scripts for account management, transaction + creation, or record or chain data inspection, or specialized functions such as + MPC computations run by web3 service providers +- **Local testing and prototyping** local testing of dapps or web3 service + prototypes ### Web (Browser) -In browser contexts, the SDK is most commonly used to **supplement features that wallet adapters don't yet provide**. The [aleo-wallet-adapter](https://github.com/ProvableHQ/aleo-dev-toolkit/tree/master/packages/aleo-wallet-adaptor) handles account management and basic transaction signing — developers reach for the SDK directly when they need: +In browser contexts, the SDK is most commonly used to **supplement features that +wallet adapters don't yet provide**. The +[aleo-wallet-adapter](https://github.com/ProvableHQ/aleo-dev-toolkit/tree/master/packages/aleo-wallet-adaptor) +handles account management and basic transaction signing — developers reach for +the SDK directly when they need: -- Cryptographic hashing (BHP, Pedersen, Poseidon) and mathematics (cryptographic math using the Aleo finite field and groups) -- Program and program data introspection (parsing program source, inspecting program functions, records and mappings) -- Arbitrary encryption, decryption and signature operations not exposed by the wallet. +- Cryptographic hashing (BHP, Pedersen, Poseidon) and mathematics (cryptographic + math using the Aleo finite field and groups) +- Program and program data introspection (parsing program source, inspecting + program functions, records and mappings) +- Arbitrary encryption, decryption and signature operations not exposed by the + wallet. -**Web tutorials must recommend using `aleo-wallet-adapter` alongside the SDK** for account/signing concerns, transaction execution and should show how the two integrate where relevant. Do not suggest the SDK replaces the wallet adapter on the web. +**Web tutorials must recommend using `aleo-wallet-adapter` alongside the SDK** +for account/signing concerns, transaction execution and should show how the two +integrate where relevant. Do not suggest the SDK replaces the wallet adapter on +the web. -WASM runs in a Web Worker in browsers, this is optional but highly recommend. Every web template should at least suggestion the worker pattern. +WASM runs in a Web Worker in browsers, this is optional but highly recommend. +Every web template should at least suggestion the worker pattern. --- @@ -66,33 +88,42 @@ package.json # yarn start runs src/index.ts .env.example # Any required secrets or endpoints ``` -- Use top-level `await`, initialize thread pool at the top: `await initThreadPool();` +- Use top-level `await`, initialize thread pool at the top: + `await initThreadPool();` - Organize into named async functions, one per feature. - Use section headers matching tutorial step numbers: - ```ts // --- STEP 1: Initialize key provider and cache keys. --- // ``` -- `yarn start` must run successfully without modification (offline features) or with only `.env` filled in (network features) +- `yarn start` must run successfully without modification (offline features) or + with only `.env` filled in (network features) ### Web templates (`template-react-*-ts`) The wallet adapter has this API here: -* https://github.com/ProvableHQ/aleo-dev-toolkit/blob/master/packages/aleo-wallet-adaptor/core/src/adapter.ts -* https://github.com/ProvableHQ/aleo-dev-toolkit/blob/master/packages/aleo-wallet-adaptor/core/src/account.ts + +- https://github.com/ProvableHQ/aleo-dev-toolkit/blob/master/packages/aleo-wallet-adaptor/core/src/adapter.ts +- https://github.com/ProvableHQ/aleo-dev-toolkit/blob/master/packages/aleo-wallet-adaptor/core/src/account.ts and for web examples, this should be used to do the following. + 1. Executing transactions (via the `executeTransaction` method) 2. Creating new accounts (via the `createAccount` method) 3. Polling transaction status (via the `transactionStatus` method) -4. Decrypting ciphertexts from transition inputs and outputs (via the `decrypt` method). Note this does not do arbitrary encryption and decryption schemes, only decryption of transition inputs and outputs. +4. Decrypting ciphertexts from transition inputs and outputs (via the `decrypt` + method). Note this does not do arbitrary encryption and decryption schemes, + only decryption of transition inputs and outputs. 5. Record scans via the (via the `requestRecords` method) 6. Executing deployments (via the `executeDeployment` method) -React environments should look here for context on how to invoke wallets via react: https://github.com/ProvableHQ/aleo-dev-toolkit/blob/master/packages/aleo-wallet-adaptor/react/src/WalletProvider.tsx +React environments should look here for context on how to invoke wallets via +react: +https://github.com/ProvableHQ/aleo-dev-toolkit/blob/master/packages/aleo-wallet-adaptor/react/src/WalletProvider.tsx -For most create leo app examples (for now) there should be the ability to execute any of the above functions optionally -via the wallet AND via native SDK methods (where the wallet is preferred). +For most create leo app examples (for now) there should be the ability to +execute any of the above functions optionally via the wallet AND via native SDK +methods (where the wallet is preferred). ``` src/ @@ -103,22 +134,29 @@ vite.config.ts .env.example ``` -- All `@provablehq/sdk` imports and calls live in `AleoWorker.ts` — never in `App.tsx` -- Expose worker methods via Comlink; `App.tsx` or any other file calls them like async functions +- All `@provablehq/sdk` imports and calls live in `AleoWorker.ts` — never in + `App.tsx` +- Expose worker methods via Comlink; `App.tsx` or any other file calls them like + async functions - `initThreadPool()` runs inside the worker, not in the main thread -- Show loading/progress state in the UI for any proving operation (these take time) -- Use `aleo-wallet-adapter` for account connection; the SDK worker handles the supplemental operations +- Show loading/progress state in the UI for any proving operation (these take + time) +- Use `aleo-wallet-adapter` for account connection; the SDK worker handles the + supplemental operations ### Both runtimes -- Imports are always network-specific: `@provablehq/sdk/testnet.js` or `/mainnet.js` — never the bare package -- Each feature in the bucket gets its own clearly named function — no interleaved logic -- Error handling must be explicit; ZK errors are opaque, surface them with context: - ```ts - } catch (e) { - throw new KeySynthesisError(`Failed to synthesize keys: ${e}`); - } - ``` +- Imports are always network-specific: `@provablehq/sdk/testnet.js` or + `/mainnet.js` — never the bare package +- Each feature in the bucket gets its own clearly named function — no + interleaved logic +- Error handling must be explicit; ZK errors are opaque, surface them with + context: + ```ts + } catch (e) { + throw new KeySynthesisError(`Failed to synthesize keys: ${e}`); + } + ``` --- @@ -127,9 +165,12 @@ vite.config.ts For each feature in the bucket: 1. Show the **minimal working example** first, then advanced usage -2. Cover **both node and web** unless the feature is inherently platform-specific -3. State the **expected output** — what does `yarn start` print? What does the UI show? -4. Explain **why** non-obvious SDK calls exist (see Code-Doc Correspondence below) +2. Cover **both node and web** unless the feature is inherently + platform-specific +3. State the **expected output** — what does `yarn start` print? What does the + UI show? +4. Explain **why** non-obvious SDK calls exist (see Code-Doc Correspondence + below) --- @@ -138,46 +179,69 @@ For each feature in the bucket: Every tutorial follows this shape: ### 1. Overview (1 paragraph) -What the reader will build. Which features from the bucket are covered. Link to the template. + +What the reader will build. Which features from the bucket are covered. Link to +the template. ### 2. Runtime context (1–2 sentences) -Why a developer would use the SDK in this runtime for this use case. Reference the framing from the Runtime Context section above. For web tutorials, mention `aleo-wallet-adapter` and its relationship to the SDK. + +Why a developer would use the SDK in this runtime for this use case. Reference +the framing from the Runtime Context section above. For web tutorials, mention +`aleo-wallet-adapter` and its relationship to the SDK. ### 3. Prerequisites + - Node version - Aleo credits (if network calls are involved) - Required `.env` values and where to get them - For web: note that `aleo-wallet-adapter` is needed for account connection ### 4. Concepts -Plain-English explanation of each feature before showing code. This is where Highlight Areas from the developer's inputs go. Assume only the complexity level specified — don't assume ZK/cryptography knowledge for beginner tutorials. + +Plain-English explanation of each feature before showing code. This is where +Highlight Areas from the developer's inputs go. Assume only the complexity level +specified — don't assume ZK/cryptography knowledge for beginner tutorials. ### 5. Step-by-step walkthrough -One section per step, numbered to match the template's section headers. For each step: + +One section per step, numbered to match the template's section headers. For each +step: + - What the code does - **Why** it does it (not just what) - The relevant code snippet ### 6. Running it + Exact commands. Expected output copied from an actual run. ### 7. Next steps -Links to related templates, the wallet adapter docs (web only), and relevant SDK API docs. + +Links to related templates, the wallet adapter docs (web only), and relevant SDK +API docs. ### Tone -Third person replacing "you" with appropriate 3rd party nouns, tutorial style: "callers should notice...", "this tells the SDK to...", "here the keys are cached because...". Not API reference style. Not first person. + +Third person replacing "you" with appropriate 3rd party nouns, tutorial style: +"callers should notice...", "this tells the SDK to...", "here the keys are +cached because...". Not API reference style. Not first person. --- ## Code-Doc Correspondence Rules -- Step numbers in template comments **must match** step numbers in the tutorial — they are the same document expressed two ways -- Every non-obvious SDK call in the template requires a "why" explanation in the tutorial. Examples of calls that always need a "why": - - `initThreadPool()` — why it exists, what happens without it - - `keyProvider.cacheKeys(...)` — why caching matters (proving key synthesis is slow) - - `OfflineQuery` — what it replaces and when to use it - - `buildDeploymentTransaction` vs `deploy` — the distinction between building and submitting -- **No unexplained magic**: if a call is in the template, it is explained in the tutorial +- Step numbers in template comments **must match** step numbers in the tutorial + — they are the same document expressed two ways +- Every non-obvious SDK call in the template requires a "why" explanation in the + tutorial. Examples of calls that always need a "why": + - `initThreadPool()` — why it exists, what happens without it + - `keyProvider.cacheKeys(...)` — why caching matters (proving key synthesis + is slow) + - `OfflineQuery` — what it replaces and when to use it + - `buildDeploymentTransaction` vs `deploy` — the distinction between + building and submitting +- **No unexplained magic**: if a call is in the template, it is explained in the + tutorial --- @@ -185,7 +249,8 @@ Third person replacing "you" with appropriate 3rd party nouns, tutorial style: " Before the tutorial is done: -- [ ] Template runs with `yarn start` without modification (or only `.env` needed) +- [ ] Template runs with `yarn start` without modification (or only `.env` + needed) - [ ] Every feature in the bucket is implemented in each target runtime - [ ] Step numbers align between template comments and tutorial sections - [ ] Expected output is shown in the tutorial (copied from a real run) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 7c9d7409b..bdd0911ec 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -1,12 +1,15 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file provides guidance to Claude Code (claude.ai/code) when working with +code in this repository. ## Repository Overview -Yarn monorepo for the **Provable SDK** — a TypeScript/Rust SDK for building zero-knowledge applications on the Aleo -blockchain. The SDK re-exports core protocol objects from SnarkVM to allow users to perform operations related to their -accounts and execute Aleo programs. Main published packages: `@provablehq/sdk` and `@provablehq/wasm` (both v0.9.18). +Yarn monorepo for the **Provable SDK** — a TypeScript/Rust SDK for building +zero-knowledge applications on the Aleo blockchain. The SDK re-exports core +protocol objects from SnarkVM to allow users to perform operations related to +their accounts and execute Aleo programs. Main published packages: +`@provablehq/sdk` and `@provablehq/wasm` (both v0.9.18). **Workspaces:** `sdk`, `wasm`, `create-leo-app` @@ -46,73 +49,111 @@ yarn test:sdk # cd sdk && yarn test cd sdk && rimraf tmp && rollup -c rollup.test.js && mocha tmp/account.test.js --timeout 60000 ``` -**Test framework:** Mocha + Chai + Sinon. Tests in `sdk/tests/` are bundled by `rollup.test.js` into `sdk/tmp/` before Mocha runs them. Network integration tests (`.integration.ts` files) are skipped by default. Test data lives in `sdk/tests/data/`. +**Test framework:** Mocha + Chai + Sinon. Tests in `sdk/tests/` are bundled by +`rollup.test.js` into `sdk/tmp/` before Mocha runs them. Network integration +tests (`.integration.ts` files) are skipped by default. Test data lives in +`sdk/tests/data/`. ## Architecture ### Package: `@provablehq/wasm` (`/wasm`) -Rust crate compiled to WebAssembly via `wasm-bindgen`. Wraps [snarkvm](https://github.com/AleoHQ/snarkvm) to expose Aleo cryptographic primitives to JavaScript. Built by `node build.js` (Rollup + `@wasm-tool/rollup-plugin-rust`). Outputs `dist/testnet/` and `dist/mainnet/`. +Rust crate compiled to WebAssembly via `wasm-bindgen`. Wraps +[snarkvm](https://github.com/AleoHQ/snarkvm) to expose Aleo cryptographic +primitives to JavaScript. Built by `node build.js` (Rollup + +`@wasm-tool/rollup-plugin-rust`). Outputs `dist/testnet/` and `dist/mainnet/`. Key Rust modules in `wasm/src/`: -- `account/` — Account objects PrivateKey, ViewKey, ComputeKey, Address, Signature, Encryptor -- `programs/manager/` — builds execution proofs, authorizations, proving requests and new aleo program deployments + +- `account/` — Account objects PrivateKey, ViewKey, ComputeKey, Address, + Signature, Encryptor +- `programs/manager/` — builds execution proofs, authorizations, proving + requests and new aleo program deployments - `programs/` — Program, execution, keypair, offline_query - `synthesizer/` — Authorization, ProvingRequest -- `types/` — Aleo scalar types (Field, Group, Scalar, I8–I128, U8–U128, Boolean) + type re-exports from SnarkVM +- `types/` — Aleo scalar types (Field, Group, Scalar, I8–I128, U8–U128, + Boolean) + type re-exports from SnarkVM - `algorithms/` — BHP256/512/768/1024, Pedersen64/128, Poseidon2/4/8 - `record/` — RecordPlaintext, RecordCiphertext - `ledger/` — Transaction, Transition ### Package: `@provablehq/sdk` (`/sdk`) -TypeScript SDK wrapping `@provablehq/wasm`. Bundled with Rollup into three network variants (`testnet`, `mainnet`, `dynamic`), each with `browser.js` and `node.js` exports. +TypeScript SDK wrapping `@provablehq/wasm`. Bundled with Rollup into three +network variants (`testnet`, `mainnet`, `dynamic`), each with `browser.js` and +`node.js` exports. Key source files in `sdk/src/`: -- `wasm.ts` — Re-exports all WASM types; the bridge between Rust types and TS. Uses `%%NETWORK%%` in the import path. -- `account.ts` — `Account` class: key derivation, signing, ciphertext import/export -- `network-client.ts` — `AleoNetworkClient`: REST calls to Aleo nodes + DPS integration (JWT auth, encrypted proving) -- `program-manager.ts` — Orchestrates deploy, execute, transfer; wraps `WasmProgramManager` -- `record-provider.ts` — `NetworkRecordProvider`: fetching and decrypting user records -- `record-scanner.ts` — Record scanning service integration (registration, revocation, filtering) -- `security.ts` — libsodium-based encryption for DPS and RSS (`crypto_box_seal` for Authorization, ProvingRequest, ViewKey) + +- `wasm.ts` — Re-exports all WASM types; the bridge between Rust types and TS. + Uses `%%NETWORK%%` in the import path. +- `account.ts` — `Account` class: key derivation, signing, ciphertext + import/export +- `network-client.ts` — `AleoNetworkClient`: REST calls to Aleo nodes + DPS + integration (JWT auth, encrypted proving) +- `program-manager.ts` — Orchestrates deploy, execute, transfer; wraps + `WasmProgramManager` +- `record-provider.ts` — `NetworkRecordProvider`: fetching and decrypting user + records +- `record-scanner.ts` — Record scanning service integration (registration, + revocation, filtering) +- `security.ts` — libsodium-based encryption for DPS and RSS (`crypto_box_seal` + for Authorization, ProvingRequest, ViewKey) - `constants.ts` — Credit program keys, transfer types, timing constants - `utils.ts` — `retryWithBackoff`, HTTP helpers, environment detection - `browser.ts` / `node.ts` — Runtime entry points (set up polyfills, re-export) **Key subdirectories in `sdk/src/`:** -- `keys/provider/` — `FunctionKeyProvider` interface, `AleoKeyProvider` (memory/network), `OfflineKeyProvider` + +- `keys/provider/` — `FunctionKeyProvider` interface, `AleoKeyProvider` + (memory/network), `OfflineKeyProvider` - `keys/keystore/` — Key storage interface and file-based implementation - `keys/verifier/` — Verifying key interface and memory implementation -- `models/` — TypeScript types for transactions, deployments, executions, records, DPS, record scanner, plaintext -- `models/record-scanner/` — Registration, filtering, and result types for the record scanner service +- `models/` — TypeScript types for transactions, deployments, executions, + records, DPS, record scanner, plaintext +- `models/record-scanner/` — Registration, filtering, and result types for the + record scanner service - `integrations/sealance/` — Sealance Merkle tree integration ### Network Variants & Build System -`rollup.config.js` builds `testnet` and `mainnet` separately via `@rollup/plugin-replace`, substituting: -- `%%NETWORK%%` → `testnet` or `mainnet` (used in `wasm.ts` import and test files) +`rollup.config.js` builds `testnet` and `mainnet` separately via +`@rollup/plugin-replace`, substituting: + +- `%%NETWORK%%` → `testnet` or `mainnet` (used in `wasm.ts` import and test + files) - `%%VERSION%%` → package version -The `dynamic` variant is **auto-generated** by `buildRuntimes()` in `rollup.config.js` — it writes `dist/dynamic/browser.js` and `dist/dynamic/node.js` that dynamically import either network at runtime via `loadNetwork(name)`. This generation runs before the Rollup export. +The `dynamic` variant is **auto-generated** by `buildRuntimes()` in +`rollup.config.js` — it writes `dist/dynamic/browser.js` and +`dist/dynamic/node.js` that dynamically import either network at runtime via +`loadNetwork(name)`. This generation runs before the Rollup export. ### Delegated Proving Service (DPS) -Programs can offload expensive proof generation to a remote prover instead of running locally: +Programs can offload expensive proof generation to a remote prover instead of +running locally: + 1. `ProgramManager` builds an `Authorization` or `ProvingRequest` locally -2. `security.ts` encrypts proving requests and record scanner registration requests with each respective service's X25519 public key (libsodium `crypto_box_seal`) -3. `AleoNetworkClient` submits via `DelegatedProvingParams` with optional API key + JWT auth +2. `security.ts` encrypts proving requests and record scanner registration + requests with each respective service's X25519 public key (libsodium + `crypto_box_seal`) +3. `AleoNetworkClient` submits via `DelegatedProvingParams` with optional API + key + JWT auth 4. Response contains the completed transaction -Configure by passing `proverUri` (and optionally `recordScannerUri`) to `AleoNetworkClientOptions`. +Configure by passing `proverUri` (and optionally `recordScannerUri`) to +`AleoNetworkClientOptions`. ### Package: `create-leo-app` (`/create-leo-app`) -Interactive CLI scaffolding (`npm create leo-app`). Templates in `create-leo-app/template-*/`. Built with `unbuild`. +Interactive CLI scaffolding (`npm create leo-app`). Templates in +`create-leo-app/template-*/`. Built with `unbuild`. ### Package: `website` (`/website`) -Demo app at provable.tools. React 19 + Vite + Ant Design + CodeMirror. Not published to npm. +Demo app at provable.tools. React 19 + Vite + Ant Design + CodeMirror. Not +published to npm. ## Key Conventions diff --git a/.claude/commands/wrap-snarkvm.md b/.claude/commands/wrap-snarkvm.md index 2822692fb..d771685ec 100644 --- a/.claude/commands/wrap-snarkvm.md +++ b/.claude/commands/wrap-snarkvm.md @@ -6,11 +6,13 @@ Automates wrapping a SnarkVM type in wasm-bindgen bindings for the Provable SDK. Ask the user for the following before proceeding. Do not guess. -1. **SnarkVM type path** — fully-qualified Rust path, e.g. `snarkvm_ledger_block::Transaction` +1. **SnarkVM type path** — fully-qualified Rust path, e.g. + `snarkvm_ledger_block::Transaction` 2. **SnarkVM source** — one of: - A GitHub branch name, e.g. `staging` (will fetch via `gh api`) - A local path to a SnarkVM checkout, e.g. `~/dev/snarkvm` -3. **Wrapper destination** — where the new `.rs` wrapper file should live, e.g. `wasm/src/ledger/transaction.rs` +3. **Wrapper destination** — where the new `.rs` wrapper file should live, e.g. + `wasm/src/ledger/transaction.rs` 4. **Methods to expose** — either "all public methods" or a specific list --- @@ -18,16 +20,24 @@ Ask the user for the following before proceeding. Do not guess. ## Step 1: Fetch the SnarkVM type definition **If GitHub branch:** + - Derive the file path from the crate name. For example: - - `snarkvm_ledger_block::Transaction` → crate `snarkvm-ledger-block` → look in `ledger/block/src/` - - `snarkvm_console::program::Plaintext` → crate `snarkvm-console` → look in `console/src/program/` -- Use `gh api "repos/ProvableHQ/snarkVM/contents/PATH?ref=BRANCH"` and decode the base64 content -- If the exact file path is unclear, use `gh api "repos/ProvableHQ/snarkVM/git/trees/BRANCH?recursive=1"` to list the tree and locate the right file + - `snarkvm_ledger_block::Transaction` → crate `snarkvm-ledger-block` → look + in `ledger/block/src/` + - `snarkvm_console::program::Plaintext` → crate `snarkvm-console` → look in + `console/src/program/` +- Use `gh api "repos/ProvableHQ/snarkVM/contents/PATH?ref=BRANCH"` and decode + the base64 content +- If the exact file path is unclear, use + `gh api "repos/ProvableHQ/snarkVM/git/trees/BRANCH?recursive=1"` to list the + tree and locate the right file **If local path:** + - Read the file directly using the path derived from the crate/module structure -Read the type definition thoroughly — note all public methods, their signatures, and any trait bounds. +Read the type definition thoroughly — note all public methods, their signatures, +and any trait bounds. --- @@ -35,10 +45,14 @@ Read the type definition thoroughly — note all public methods, their signature This step is always the same regardless of wrapper destination. -1. Add the `use` import from the appropriate SnarkVM crate to the existing import block. Match the grouping style — account types together, ledger types together, etc. -2. Add a `pub type TypeNameNative = TypeName;` alias in the appropriate section. +1. Add the `use` import from the appropriate SnarkVM crate to the existing + import block. Match the grouping style — account types together, ledger types + together, etc. +2. Add a `pub type TypeNameNative = TypeName;` alias in the + appropriate section. Example — adding `Transaction` from `snarkvm_ledger_block`: + ```rust // In the use block (already present or add to existing ledger group): use snarkvm_ledger_block::{..., Transaction}; @@ -47,7 +61,8 @@ use snarkvm_ledger_block::{..., Transaction}; pub type TransactionNative = Transaction; ``` -Do not add a new `use` statement if the crate is already imported — extend the existing one. +Do not add a new `use` statement if the crate is already imported — extend the +existing one. Ensure all imports are in alphabetical order. @@ -79,7 +94,7 @@ impl TypeName { // - to_string gets #[wasm_bindgen(js_name = "toString")] and #[allow(clippy::inherent_to_string)] // - from_str / constructors get #[wasm_bindgen(js_name = "fromString")] etc. // - Fallible methods return Result — map errors with .map_err(|e| e.to_string()) - // - Methods should have to_string(), from_string(), to_bytes_le(), from_bytes_le(), to_field(), and to_fields(), from_fields(), to_bits_le(), and from_bits_le() if the SnarkVM object implements any of those. + // - Methods should have to_string(), from_string(), to_bytes_le(), from_bytes_le(), to_field(), and to_fields(), from_fields(), to_bits_le(), and from_bits_le() if the SnarkVM object implements any of those. } // Always generate all four two-way conversions between TypeName and TypeNameNative. @@ -122,7 +137,8 @@ impl From<&TypeName> for TypeNameNative { ## Step 4: Write wasm bindgen tests to test out any methods that were written. -Write tests similar to this for records (feel free to read wasm/src/record/record_ciphertext.rs for the object implementation) +Write tests similar to this for records (feel free to read +wasm/src/record/record_ciphertext.rs for the object implementation) ```rust #[cfg(test)] @@ -301,7 +317,8 @@ mod tests { ## Step 5: Wire up the module -Find the `mod.rs` (or equivalent) in the parent directory of the wrapper destination and add: +Find the `mod.rs` (or equivalent) in the parent directory of the wrapper +destination and add: ```rust pub mod type_name; @@ -315,19 +332,22 @@ Match the style of existing entries in that file. ## Step 6: Verify the wasm tests Run this from the sdk base directory. + ```bash yarn test:wasm ``` Fix any errors before declaring done. Common issues: + - Missing imports in the wrapper file - Trait bounds not satisfied (check what traits the native type requires) -- Method signatures that need adjustment for wasm-bindgen compatibility (e.g. no generic parameters, no lifetimes on return types) +- Method signatures that need adjustment for wasm-bindgen compatibility (e.g. no + generic parameters, no lifetimes on return types) ## Step 7: Write JS tests of the wasm object and export it in `browser.ts`. - -Write JS tests of the wasm object and its methods in the JS sdk in the appropriate test file in `sdk/tests/wasm.test.ts`. +Write JS tests of the wasm object and its methods in the JS sdk in the +appropriate test file in `sdk/tests/wasm.test.ts`. An example of how to write such tests are below. @@ -373,11 +393,14 @@ An example of how to write such tests are below. }; ``` -Verify these tests work by running `yarn build:wasm && yarn build:sdk && yarn test:sdk` from the root of the sdk directory. +Verify these tests work by running +`yarn build:wasm && yarn build:sdk && yarn test:sdk` from the root of the sdk +directory. ## Step 8: Cleanup Run cargo fmt --all run the following in the `wasm` directory + ```bash cargo fmt --all ``` diff --git a/.codecov.yml b/.codecov.yml index 0516e58a7..2dcf24f24 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,5 +1,5 @@ coverage: - status: - project: - default: - threshold: 4% + status: + project: + default: + threshold: 4% diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index d70564903..d03e9b170 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,3 +1,4 @@ ## 👉 [Please follow one of these issue templates](https://github.com/ProvableHQ/sdk/issues/new/choose) 👈 -Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates. +Note: to keep the backlog clean and actionable, issues may be immediately closed +if they do not follow one of the above issue templates. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 17610f64a..a4ed4c7fc 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,7 @@ blank_issues_enabled: true contact_links: - - name: ❓ Q&A Technical Support Channel - url: https://discord.gg/aleo - about: For quick questions or technical troubleshooting, please ask them on our dedicated Discord channel. + - name: ❓ Q&A Technical Support Channel + url: https://discord.gg/aleo + about: + For quick questions or technical troubleshooting, please ask them on + our dedicated Discord channel. diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md index 1198d2720..023620cae 100644 --- a/.github/ISSUE_TEMPLATE/documentation.md +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -2,7 +2,7 @@ name: 📚 Documentation about: Report an issue related to documentation title: "[Docs]" -labels: 'documentation' +labels: "documentation" --- ## 📚 Documentation diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/feature.md index e348f96f9..12db88495 100644 --- a/.github/ISSUE_TEMPLATE/feature.md +++ b/.github/ISSUE_TEMPLATE/feature.md @@ -1,5 +1,5 @@ --- -name: 🚀 Feature +name: 🚀 Feature about: Submit a new feature request title: "[Feature]" labels: feature @@ -18,7 +18,7 @@ labels: feature @@ -33,4 +33,5 @@ labels: feature How should this feature be implemented? --> -**Are you willing to open a pull request?** (See [CONTRIBUTING](../../CONTRIBUTING.md)) +**Are you willing to open a pull request?** (See +[CONTRIBUTING](../../CONTRIBUTING.md)) diff --git a/.github/ISSUE_TEMPLATE/proposal.md b/.github/ISSUE_TEMPLATE/proposal.md index b1baeb9a6..dc1ff1a48 100644 --- a/.github/ISSUE_TEMPLATE/proposal.md +++ b/.github/ISSUE_TEMPLATE/proposal.md @@ -2,7 +2,7 @@ name: 💥 Proposal about: Propose a non-trivial change to the Provable SDK title: "[Proposal]" -labels: 'proposal' +labels: "proposal" --- ## 💥 Proposal diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index c7366e327..b80366298 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -1,17 +1,17 @@ name: Setup Rust description: Sets up Rust runs: - using: "composite" - steps: - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - target - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + using: "composite" + steps: + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - name: Initialize Rustup - shell: bash - run: | - rustup show + - name: Initialize Rustup + shell: bash + run: | + rustup show diff --git a/.github/actions/setup-yarn/action.yml b/.github/actions/setup-yarn/action.yml index 47bf3340b..697ab5052 100644 --- a/.github/actions/setup-yarn/action.yml +++ b/.github/actions/setup-yarn/action.yml @@ -1,13 +1,13 @@ name: Setup Yarn description: Sets up Yarn runs: - using: "composite" - steps: - - uses: actions/setup-node@v4 - with: - cache: 'yarn' + using: "composite" + steps: + - uses: actions/setup-node@v4 + with: + cache: "yarn" - - name: yarn install - shell: bash - run: | - yarn install --immutable --check-cache + - name: yarn install + shell: bash + run: | + yarn install --immutable --check-cache diff --git a/.github/actions/use-build/action.yml b/.github/actions/use-build/action.yml index 6f7521d98..ea0eb4cc0 100644 --- a/.github/actions/use-build/action.yml +++ b/.github/actions/use-build/action.yml @@ -1,9 +1,9 @@ name: Use build description: Uses the build artifacts runs: - using: "composite" - steps: - - name: Download build - uses: actions/download-artifact@v4 - with: - name: build + using: "composite" + steps: + - name: Download build + uses: actions/download-artifact@v4 + with: + name: build diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6b17bb24c..168d35b27 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,56 +1,56 @@ version: 2 updates: -- package-ecosystem: cargo - directory: "/" - schedule: - interval: daily - time: "10:00" - open-pull-requests-limit: 10 - ignore: - - dependency-name: wiremock - versions: - - 0.5.2 - - dependency-name: wasm-bindgen - versions: - - 0.2.71 - - 0.2.72 - - dependency-name: snarkvm-utilities - versions: - - 0.0.3 - - 0.0.4 - - 0.0.6 - - 0.2.0 - - 0.2.1 - - dependency-name: serde_json - versions: - - 1.0.63 - - 1.0.64 - - dependency-name: self_update - versions: - - 0.24.0 - - dependency-name: thiserror - versions: - - 1.0.24 - - dependency-name: snarkvm-storage - versions: - - 0.0.3 - - 0.0.4 - - dependency-name: snarkvm-dpc - versions: - - 0.0.3 - - 0.0.4 - - dependency-name: snarkvm-errors - versions: - - 0.0.3 - - 0.0.4 - - dependency-name: rand_chacha - versions: - - 0.3.0 - - dependency-name: snarkvm-objects - versions: - - 0.0.3 - - 0.0.4 - - dependency-name: snarkvm-models - versions: - - 0.0.3 - - 0.0.4 + - package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + time: "10:00" + open-pull-requests-limit: 10 + ignore: + - dependency-name: wiremock + versions: + - 0.5.2 + - dependency-name: wasm-bindgen + versions: + - 0.2.71 + - 0.2.72 + - dependency-name: snarkvm-utilities + versions: + - 0.0.3 + - 0.0.4 + - 0.0.6 + - 0.2.0 + - 0.2.1 + - dependency-name: serde_json + versions: + - 1.0.63 + - 1.0.64 + - dependency-name: self_update + versions: + - 0.24.0 + - dependency-name: thiserror + versions: + - 1.0.24 + - dependency-name: snarkvm-storage + versions: + - 0.0.3 + - 0.0.4 + - dependency-name: snarkvm-dpc + versions: + - 0.0.3 + - 0.0.4 + - dependency-name: snarkvm-errors + versions: + - 0.0.3 + - 0.0.4 + - dependency-name: rand_chacha + versions: + - 0.3.0 + - dependency-name: snarkvm-objects + versions: + - 0.0.3 + - 0.0.4 + - dependency-name: snarkvm-models + versions: + - 0.0.3 + - 0.0.4 diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index d604d6476..3119811b1 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -1,247 +1,246 @@ name: SDK on: - pull_request: - push: - branches: - - mainnet - - testnet + pull_request: + push: + branches: + - mainnet + - testnet env: - CARGO_TERM_COLOR: always - PUZZLE_PK: ${{ secrets.PUZZLE_PK }} - PUZZLE_VK: ${{ secrets.PUZZLE_VK }} - RECORD_SCANNER_URL: ${{ secrets.RECORD_SCANNER_URL }} - KONG_API_KEY: ${{ secrets.ALEO_DPS_API_KEY }} - CONSUMER_ID: ${{ secrets.ALEO_CONSUMER_ID }} - + CARGO_TERM_COLOR: always + PUZZLE_PK: ${{ secrets.PUZZLE_PK }} + PUZZLE_VK: ${{ secrets.PUZZLE_VK }} + RECORD_SCANNER_URL: ${{ secrets.RECORD_SCANNER_URL }} + KONG_API_KEY: ${{ secrets.ALEO_DPS_API_KEY }} + CONSUMER_ID: ${{ secrets.ALEO_CONSUMER_ID }} jobs: - build: - name: build - runs-on: ubuntu-latest-m - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/setup-rust - - - run: | - yarn build:all - - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: build - path: | - sdk/dist/ - wasm/dist/ - create-leo-app/dist/ - - - test-wasm: - name: test wasm - runs-on: ubuntu-latest-m - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - - run: | - yarn test:wasm - - - test-sdk: - name: test sdk - runs-on: ubuntu-latest - needs: build - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - run: | - yarn test:sdk - - - e2e: - name: "e2e" - runs-on: ubuntu-latest-m - needs: build - strategy: - fail-fast: false - matrix: - network: [testnet, mainnet, dynamic] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - working-directory: e2e/${{ matrix.network }} - run: | - yarn start - - - create-leo-app-cli: - name: "create-leo-app CLI" - runs-on: ubuntu-latest-m - needs: build - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - working-directory: create-leo-app - run: | - npm i -D tsx - npx tsx src/index.ts test-app --template react-leo - - - create-leo-app-build: - name: "create-leo-app build" - runs-on: ubuntu-latest-m - needs: build - strategy: - fail-fast: false - matrix: - template: - - extension - - nextjs-ts - - offline-public-transaction-ts - - react-credits-aleo-functions-ts - - react-leo - #- react-managed-worker - - react-ts - - vanilla - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - working-directory: create-leo-app/template-${{ matrix.template }} - run: | - yarn build - - - create-leo-app-run: - name: "create-leo-app run" - runs-on: ubuntu-latest-m - needs: build - strategy: - fail-fast: false - matrix: - template: - - node - - node-credits-aleo-functions-ts - - node-ts - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - working-directory: create-leo-app/template-${{ matrix.template }} - run: | - yarn start - - - create-leo-app-loyalty-program-local: - name: "create-leo-app loyalty-program (local)" - runs-on: ubuntu-latest-m - needs: build - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - working-directory: create-leo-app/template-node-loyalty-program-ts - run: | - yarn local - - - create-leo-app-loyalty-program-delegated: - name: "create-leo-app loyalty-program (delegated + scanner)" - runs-on: ubuntu-latest-m - needs: build - env: - # Consumer ID (used for both DPS and RSS) - ALEO_CONSUMER_ID: ${{ secrets.ALEO_CONSUMER_ID }} - # DPS configuration - ALEO_DPS_URL: https://api.provable.com/prove/testnet - ALEO_DPS_API_KEY: ${{ secrets.ALEO_DPS_API_KEY }} - # RSS configuration - ALEO_RSS_URL: https://api.provable.com/scanner - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - name: Run delegated mode (creates on-chain records) - working-directory: create-leo-app/template-node-loyalty-program-ts - run: | - yarn delegated - - - name: Run scanner mode (finds on-chain records) - working-directory: create-leo-app/template-node-loyalty-program-ts - run: | - yarn scanner - - - clippy: - name: "clippy" - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - network: [testnet, mainnet] - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-rust - - - name: "cargo clippy" - working-directory: wasm - run: | - cargo clippy --features ${{ matrix.network }} - - - rustfmt: - name: "rustfmt" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-rust - - - name: "cargo fmt" - working-directory: wasm - run: | - cargo fmt --all --check - - - website: - name: website - runs-on: ubuntu-latest - needs: build - - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/use-build - - - name: Build Website - working-directory: website - run: | - yarn build - - - name: Test Deploy - id: unmodified - uses: JamesIves/github-pages-deploy-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: gh-pages - folder: website/dist - clean-exclude: '["dev"]' - dry-run: true - - - name: Check step output - run: | - [[ \ - ${{steps.unmodified.outputs.deployment-status}} = skipped || \ - ${{steps.unmodified.outputs.deployment-status}} = success \ - ]] + build: + name: build + runs-on: ubuntu-latest-m + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/setup-rust + + - run: | + yarn build:all + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build + path: | + sdk/dist/ + wasm/dist/ + create-leo-app/dist/ + + test-wasm: + name: test wasm + runs-on: ubuntu-latest-m + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + + - run: | + yarn test:wasm + + test-sdk: + name: test sdk + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - run: | + yarn test:sdk + + e2e: + name: "e2e" + runs-on: ubuntu-latest-m + needs: build + strategy: + fail-fast: false + matrix: + network: [testnet, mainnet, dynamic] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - working-directory: e2e/${{ matrix.network }} + run: | + yarn start + + create-leo-app-cli: + name: "create-leo-app CLI" + runs-on: ubuntu-latest-m + needs: build + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - working-directory: create-leo-app + run: | + npm i -D tsx + npx tsx src/index.ts test-app --template react-leo + + create-leo-app-build: + name: "create-leo-app build" + runs-on: ubuntu-latest-m + needs: build + strategy: + fail-fast: false + matrix: + template: + - extension + - nextjs-ts + - offline-public-transaction-ts + - react-credits-aleo-functions-ts + - react-leo + #- react-managed-worker + - react-ts + - vanilla + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - working-directory: create-leo-app/template-${{ matrix.template }} + run: | + yarn build + + create-leo-app-run: + name: "create-leo-app run" + runs-on: ubuntu-latest-m + needs: build + strategy: + fail-fast: false + matrix: + template: + - node + - node-credits-aleo-functions-ts + - node-ts + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - working-directory: create-leo-app/template-${{ matrix.template }} + run: | + yarn start + + create-leo-app-loyalty-program-local: + name: "create-leo-app loyalty-program (local)" + runs-on: ubuntu-latest-m + needs: build + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - working-directory: create-leo-app/template-node-loyalty-program-ts + run: | + yarn local + + create-leo-app-loyalty-program-delegated: + name: "create-leo-app loyalty-program (delegated + scanner)" + runs-on: ubuntu-latest-m + needs: build + env: + # Consumer ID (used for both DPS and RSS) + ALEO_CONSUMER_ID: ${{ secrets.ALEO_CONSUMER_ID }} + # DPS configuration + ALEO_DPS_URL: https://api.provable.com/prove/testnet + ALEO_DPS_API_KEY: ${{ secrets.ALEO_DPS_API_KEY }} + # RSS configuration + ALEO_RSS_URL: https://api.provable.com/scanner + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - name: Run delegated mode (creates on-chain records) + working-directory: create-leo-app/template-node-loyalty-program-ts + run: | + yarn delegated + + - name: Run scanner mode (finds on-chain records) + working-directory: create-leo-app/template-node-loyalty-program-ts + run: | + yarn scanner + + clippy: + name: "clippy" + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + network: [testnet, mainnet] + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-rust + + - name: "cargo clippy" + working-directory: wasm + run: | + cargo clippy --features ${{ matrix.network }} + + rustfmt: + name: "rustfmt" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-rust + + - name: "cargo fmt" + working-directory: wasm + run: | + cargo fmt --all --check + + prettier: + name: "prettier" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + + - name: "prettier --check" + run: | + yarn lint + + website: + name: website + runs-on: ubuntu-latest + needs: build + + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/use-build + + - name: Build Website + working-directory: website + run: | + yarn build + + - name: Test Deploy + id: unmodified + uses: JamesIves/github-pages-deploy-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: website/dist + clean-exclude: '["dev"]' + dry-run: true + + - name: Check step output + run: | + [[ \ + ${{steps.unmodified.outputs.deployment-status}} = skipped || \ + ${{steps.unmodified.outputs.deployment-status}} = success \ + ]] diff --git a/.github/workflows/staging-website.yml b/.github/workflows/staging-website.yml index 81e147f9c..de79b738a 100644 --- a/.github/workflows/staging-website.yml +++ b/.github/workflows/staging-website.yml @@ -1,38 +1,38 @@ name: Staging Website Deploy on: - push: - branches: - - staging + push: + branches: + - staging jobs: - deploy: - name: SDK Website - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/setup-rust + deploy: + name: SDK Website + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/setup-rust - - name: Install and Build - run: | - yarn build:all - cd website - yarn build - env: - CI: "" + - name: Install and Build + run: | + yarn build:all + cd website + yarn build + env: + CI: "" - - name: Deploy - id: modified - uses: JamesIves/github-pages-deploy-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: staging.provable.tools - folder: website/dist - clean-exclude: '["dev"]' + - name: Deploy + id: modified + uses: JamesIves/github-pages-deploy-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: staging.provable.tools + folder: website/dist + clean-exclude: '["dev"]' - - name: Check step output - run: | - [[ \ - ${{steps.modified.outputs.deployment-status}} = skipped || \ - ${{steps.modified.outputs.deployment-status}} = success \ - ]] + - name: Check step output + run: | + [[ \ + ${{steps.modified.outputs.deployment-status}} = skipped || \ + ${{steps.modified.outputs.deployment-status}} = success \ + ]] diff --git a/.github/workflows/update-snarkvm.yml b/.github/workflows/update-snarkvm.yml index 1dbddbcd2..c5fcbf6f6 100644 --- a/.github/workflows/update-snarkvm.yml +++ b/.github/workflows/update-snarkvm.yml @@ -1,150 +1,150 @@ name: Update snarkVM dependency on: - schedule: - # Run once every day at midnight (UTC) - - cron: '0 0 * * *' - push: - branches: - - 'ci/snarkvm-update' + schedule: + # Run once every day at midnight (UTC) + - cron: "0 0 * * *" + push: + branches: + - "ci/snarkvm-update" env: - RUST_BACKTRACE: 1 + RUST_BACKTRACE: 1 jobs: - update-snarkvm-staging: - name: Update snarkVM to latest staging - runs-on: ubuntu-22.04 - permissions: - contents: write - pull-requests: write - steps: - - name: Checkout SDK - uses: actions/checkout@v5 - with: - ref: mainnet - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Get current snarkVM commit from Cargo.toml - id: current-commit - run: | - # Extract the first snarkVM rev found in the Cargo.toml - CURRENT_COMMIT=$(grep -E 'snarkvm-[a-zA-Z0-9_-]+ = .*rev\s*=' wasm/Cargo.toml | head -n 1 | sed -E 's/.*rev\s*=\s*"([a-f0-9]+)".*/\1/') - echo "Current snarkVM commit: $CURRENT_COMMIT" - echo "current_commit=$CURRENT_COMMIT" >> $GITHUB_OUTPUT - - - name: Get latest snarkVM staging commit - id: snarkvm-commit - uses: actions/github-script@v7 - with: - script: | - // Get the latest commit hash from snarkVM staging branch - const { data: branch } = await github.rest.repos.getBranch({ - owner: 'ProvableHQ', - repo: 'snarkVM', - branch: 'staging' - }); - - const latestCommit = branch.commit.sha; - console.log('Latest snarkVM staging commit:', latestCommit); - core.setOutput('latest_commit', latestCommit); - core.setOutput('update_needed', 'true'); - - - name: Setup Git - if: steps.snarkvm-commit.outputs.update_needed == 'true' - run: | - git config --global user.name "github-actions[bot]" - git config --global user.email "github-actions[bot]@users.noreply.github.com" - - - name: Create new update-snarkvm-staging branch from staging - if: steps.snarkvm-commit.outputs.update_needed == 'true' - run: | - git fetch origin staging:staging - # Always create a fresh branch from staging - echo "Creating new update-snarkvm-staging branch from staging" - git checkout -B update-snarkvm-staging origin/staging - echo "Created fresh update-snarkvm-staging branch from staging" - - - name: Update snarkVM dependency - if: steps.snarkvm-commit.outputs.update_needed == 'true' - run: | - # Validate the commit hash format (should be 40 chars hex or 7+ chars) - LATEST_COMMIT="${{ steps.snarkvm-commit.outputs.latest_commit }}" - if [[ ! "$LATEST_COMMIT" =~ ^[a-f0-9]{7,40}$ ]]; then - echo "Error: Invalid commit hash format: $LATEST_COMMIT" - exit 1 - fi - - # First, update the rev in Cargo.toml to point to the new commit - cd wasm - cargo add snarkvm-algorithms --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-circuit-network --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-console --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-ledger-block --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-ledger-query --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-ledger-store --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-parameters --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-synthesizer-program --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-synthesizer --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - cargo add snarkvm-wasm --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT - - echo "✅ Successfully updated snarkVM dependency to $LATEST_COMMIT" - - - name: Commit and push changes - if: steps.snarkvm-commit.outputs.update_needed == 'true' - run: | - git add wasm/Cargo.toml wasm/Cargo.lock - git commit --no-verify -m "Update snarkVM to latest staging commit ${{ steps.snarkvm-commit.outputs.latest_commit }} - - Previous commit: ${{ steps.snarkvm-commit.outputs.current_commit }} - Latest commit: ${{ steps.snarkvm-commit.outputs.latest_commit }} - - This update was performed automatically by the snarkVM update workflow." - - # Make sure the remote branch exists and is up to date - git fetch origin update-snarkvm-staging || true - - # Push forcefully (safe because only CI touches this branch) - git push --force origin update-snarkvm-staging - - - name: Create or update PR - if: steps.snarkvm-commit.outputs.update_needed == 'true' - uses: actions/github-script@v7 - with: - script: | - // Check if a PR already exists from update-snarkvm-staging to staging - const { data: prs } = await github.rest.pulls.list({ - owner: 'ProvableHQ', - repo: 'sdk', - head: 'ProvableHQ:update-snarkvm-staging', - base: 'staging', - state: 'open' - }); - - if (prs.length === 0) { - // No PR exists, create one - const { data: pr } = await github.rest.pulls.create({ - owner: 'ProvableHQ', - repo: 'sdk', - title: 'Update snarkVM to latest staging commit', - head: 'update-snarkvm-staging', - base: 'staging', - body: 'This PR updates the snarkVM dependency to track the latest staging commit.\n\nLatest commit: ${{ steps.snarkvm-commit.outputs.latest_commit }}\nPrevious commit: ${{ steps.snarkvm-commit.outputs.current_commit }}\n\nThis PR was created automatically by the snarkVM update workflow.' - }); - console.log(`Created PR #${pr.number}: ${pr.html_url}`); - } else { - console.log(`PR already exists: ${prs[0].html_url}`); - } - - - name: Summary - if: always() - run: | - echo "## Workflow Summary (staging)" >> $GITHUB_STEP_SUMMARY - echo "- Current snarkVM commit: ${{ steps.snarkvm-commit.outputs.current_commit }}" >> $GITHUB_STEP_SUMMARY - echo "- Latest snarkVM commit: ${{ steps.snarkvm-commit.outputs.latest_commit }}" >> $GITHUB_STEP_SUMMARY - echo "- Update needed: ${{ steps.snarkvm-commit.outputs.update_needed }}" >> $GITHUB_STEP_SUMMARY - if [ "${{ steps.snarkvm-commit.outputs.update_needed }}" = "true" ]; then - echo "- ✅ snarkVM dependency updated successfully" >> $GITHUB_STEP_SUMMARY - else - echo "- ℹ️ No update required" >> $GITHUB_STEP_SUMMARY - fi + update-snarkvm-staging: + name: Update snarkVM to latest staging + runs-on: ubuntu-22.04 + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout SDK + uses: actions/checkout@v5 + with: + ref: mainnet + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Get current snarkVM commit from Cargo.toml + id: current-commit + run: | + # Extract the first snarkVM rev found in the Cargo.toml + CURRENT_COMMIT=$(grep -E 'snarkvm-[a-zA-Z0-9_-]+ = .*rev\s*=' wasm/Cargo.toml | head -n 1 | sed -E 's/.*rev\s*=\s*"([a-f0-9]+)".*/\1/') + echo "Current snarkVM commit: $CURRENT_COMMIT" + echo "current_commit=$CURRENT_COMMIT" >> $GITHUB_OUTPUT + + - name: Get latest snarkVM staging commit + id: snarkvm-commit + uses: actions/github-script@v7 + with: + script: | + // Get the latest commit hash from snarkVM staging branch + const { data: branch } = await github.rest.repos.getBranch({ + owner: 'ProvableHQ', + repo: 'snarkVM', + branch: 'staging' + }); + + const latestCommit = branch.commit.sha; + console.log('Latest snarkVM staging commit:', latestCommit); + core.setOutput('latest_commit', latestCommit); + core.setOutput('update_needed', 'true'); + + - name: Setup Git + if: steps.snarkvm-commit.outputs.update_needed == 'true' + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + + - name: Create new update-snarkvm-staging branch from staging + if: steps.snarkvm-commit.outputs.update_needed == 'true' + run: | + git fetch origin staging:staging + # Always create a fresh branch from staging + echo "Creating new update-snarkvm-staging branch from staging" + git checkout -B update-snarkvm-staging origin/staging + echo "Created fresh update-snarkvm-staging branch from staging" + + - name: Update snarkVM dependency + if: steps.snarkvm-commit.outputs.update_needed == 'true' + run: | + # Validate the commit hash format (should be 40 chars hex or 7+ chars) + LATEST_COMMIT="${{ steps.snarkvm-commit.outputs.latest_commit }}" + if [[ ! "$LATEST_COMMIT" =~ ^[a-f0-9]{7,40}$ ]]; then + echo "Error: Invalid commit hash format: $LATEST_COMMIT" + exit 1 + fi + + # First, update the rev in Cargo.toml to point to the new commit + cd wasm + cargo add snarkvm-algorithms --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-circuit-network --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-console --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-ledger-block --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-ledger-query --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-ledger-store --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-parameters --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-synthesizer-program --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-synthesizer --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + cargo add snarkvm-wasm --git https://github.com/ProvableHQ/snarkVM --rev $LATEST_COMMIT + + echo "✅ Successfully updated snarkVM dependency to $LATEST_COMMIT" + + - name: Commit and push changes + if: steps.snarkvm-commit.outputs.update_needed == 'true' + run: | + git add wasm/Cargo.toml wasm/Cargo.lock + git commit --no-verify -m "Update snarkVM to latest staging commit ${{ steps.snarkvm-commit.outputs.latest_commit }} + + Previous commit: ${{ steps.snarkvm-commit.outputs.current_commit }} + Latest commit: ${{ steps.snarkvm-commit.outputs.latest_commit }} + + This update was performed automatically by the snarkVM update workflow." + + # Make sure the remote branch exists and is up to date + git fetch origin update-snarkvm-staging || true + + # Push forcefully (safe because only CI touches this branch) + git push --force origin update-snarkvm-staging + + - name: Create or update PR + if: steps.snarkvm-commit.outputs.update_needed == 'true' + uses: actions/github-script@v7 + with: + script: | + // Check if a PR already exists from update-snarkvm-staging to staging + const { data: prs } = await github.rest.pulls.list({ + owner: 'ProvableHQ', + repo: 'sdk', + head: 'ProvableHQ:update-snarkvm-staging', + base: 'staging', + state: 'open' + }); + + if (prs.length === 0) { + // No PR exists, create one + const { data: pr } = await github.rest.pulls.create({ + owner: 'ProvableHQ', + repo: 'sdk', + title: 'Update snarkVM to latest staging commit', + head: 'update-snarkvm-staging', + base: 'staging', + body: 'This PR updates the snarkVM dependency to track the latest staging commit.\n\nLatest commit: ${{ steps.snarkvm-commit.outputs.latest_commit }}\nPrevious commit: ${{ steps.snarkvm-commit.outputs.current_commit }}\n\nThis PR was created automatically by the snarkVM update workflow.' + }); + console.log(`Created PR #${pr.number}: ${pr.html_url}`); + } else { + console.log(`PR already exists: ${prs[0].html_url}`); + } + + - name: Summary + if: always() + run: | + echo "## Workflow Summary (staging)" >> $GITHUB_STEP_SUMMARY + echo "- Current snarkVM commit: ${{ steps.snarkvm-commit.outputs.current_commit }}" >> $GITHUB_STEP_SUMMARY + echo "- Latest snarkVM commit: ${{ steps.snarkvm-commit.outputs.latest_commit }}" >> $GITHUB_STEP_SUMMARY + echo "- Update needed: ${{ steps.snarkvm-commit.outputs.update_needed }}" >> $GITHUB_STEP_SUMMARY + if [ "${{ steps.snarkvm-commit.outputs.update_needed }}" = "true" ]; then + echo "- ✅ snarkVM dependency updated successfully" >> $GITHUB_STEP_SUMMARY + else + echo "- ℹ️ No update required" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.github/workflows/website.yml b/.github/workflows/website.yml index aff78e453..023797bf9 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/website.yml @@ -1,38 +1,38 @@ name: Website on: - push: - branches: - - mainnet + push: + branches: + - mainnet jobs: - deploy: - name: SDK Website - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/setup-yarn - - uses: ./.github/actions/setup-rust + deploy: + name: SDK Website + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup-yarn + - uses: ./.github/actions/setup-rust - - name: Install and Build - run: | - yarn build:all - cd website - yarn build - env: - CI: "" + - name: Install and Build + run: | + yarn build:all + cd website + yarn build + env: + CI: "" - - name: Deploy - id: modified - uses: JamesIves/github-pages-deploy-action@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - branch: gh-pages - folder: website/dist - clean-exclude: '["dev"]' + - name: Deploy + id: modified + uses: JamesIves/github-pages-deploy-action@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages + folder: website/dist + clean-exclude: '["dev"]' - - name: Check step output - run: | - [[ \ - ${{steps.modified.outputs.deployment-status}} = skipped || \ - ${{steps.modified.outputs.deployment-status}} = success \ - ]] + - name: Check step output + run: | + [[ \ + ${{steps.modified.outputs.deployment-status}} = skipped || \ + ${{steps.modified.outputs.deployment-status}} = success \ + ]] diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..58ccb2d35 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +docs/api_reference/ diff --git a/LICENSE.md b/LICENSE.md index b95c626e2..bea2ca68d 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,5 +1,4 @@ -GNU General Public License -========================== +# GNU General Public License Version 3, 29 June 2007 @@ -10,59 +9,62 @@ document, but changing it is not allowed. ## Preamble -The GNU General Public License is a free, copyleft license for software and other -kinds of works. - -The licenses for most software and other practical works are designed to take away -your freedom to share and change the works. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change all versions of a -program--to make sure it remains free software for all its users. We, the Free -Software Foundation, use the GNU General Public License for most of our software; it -applies also to any other work released this way by its authors. You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General -Public Licenses are designed to make sure that you have the freedom to distribute -copies of free software (and charge for them if you wish), that you receive source -code or can get it if you want it, that you can change the software or use pieces of -it in new free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you these rights or -asking you to surrender the rights. Therefore, you have certain responsibilities if -you distribute copies of the software, or if you modify it: responsibilities to -respect the freedom of others. - -For example, if you distribute copies of such a program, whether gratis or for a fee, -you must pass on to the recipients the same freedoms that you received. You must make -sure that they, too, receive or can get the source code. And you must show them these -terms so they know their rights. - -Developers that use the GNU GPL protect your rights with two steps: **(1)** assert -copyright on the software, and **(2)** offer you this License giving you legal permission -to copy, distribute and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains that there is -no warranty for this free software. For both users' and authors' sake, the GPL -requires that modified versions be marked as changed, so that their problems will not -be attributed erroneously to authors of previous versions. - -Some devices are designed to deny users access to install or run modified versions of -the software inside them, although the manufacturer can do so. This is fundamentally -incompatible with the aim of protecting users' freedom to change the software. The -systematic pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we have designed -this version of the GPL to prohibit the practice for those products. If such problems -arise substantially in other domains, we stand ready to extend this provision to -those domains in future versions of the GPL, as needed to protect the freedom of -users. - -Finally, every program is threatened constantly by software patents. States should -not allow patents to restrict development and use of software on general-purpose -computers, but in those that do, we wish to avoid the special danger that patents -applied to a free program could make it effectively proprietary. To prevent this, the -GPL assures that patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and modification follow. +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most of +our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can do +these things. + +To protect your rights, we need to prevent others from denying you these rights +or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a +fee, you must pass on to the recipients the same freedoms that you received. You +must make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: **(1)** +assert copyright on the software, and **(2)** offer you this License giving you +legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there +is no warranty for this free software. For both users' and authors' sake, the +GPL requires that modified versions be marked as changed, so that their problems +will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to +change the software. The systematic pattern of such abuse occurs in the area of +products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. ## TERMS AND CONDITIONS @@ -70,70 +72,70 @@ The precise terms and conditions for copying, distribution and modification foll “This License” refers to version 3 of the GNU General Public License. -“Copyright” also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. +“Copyright” also means copyright-like laws that apply to other kinds of works, +such as semiconductor masks. -“The Program” refers to any copyrightable work licensed under this -License. Each licensee is addressed as “you”. “Licensees” and -“recipients” may be individuals or organizations. +“The Program” refers to any copyrightable work licensed under this License. Each +licensee is addressed as “you”. “Licensees” and “recipients” may be individuals +or organizations. -To “modify” a work means to copy from or adapt all or part of the work in -a fashion requiring copyright permission, other than the making of an exact copy. The -resulting work is called a “modified version” of the earlier work or a -work “based on” the earlier work. +To “modify” a work means to copy from or adapt all or part of the work in a +fashion requiring copyright permission, other than the making of an exact copy. +The resulting work is called a “modified version” of the earlier work or a work +“based on” the earlier work. -A “covered work” means either the unmodified Program or a work based on -the Program. +A “covered work” means either the unmodified Program or a work based on the +Program. -To “propagate” a work means to do anything with it that, without -permission, would make you directly or secondarily liable for infringement under -applicable copyright law, except executing it on a computer or modifying a private -copy. Propagation includes copying, distribution (with or without modification), +To “propagate” a work means to do anything with it that, without permission, +would make you directly or secondarily liable for infringement under applicable +copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. -To “convey” a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through a computer -network, with no transfer of a copy, is not conveying. +To “convey” a work means any kind of propagation that enables other parties to +make or receive copies. Mere interaction with a user through a computer network, +with no transfer of a copy, is not conveying. -An interactive user interface displays “Appropriate Legal Notices” to the -extent that it includes a convenient and prominently visible feature that **(1)** -displays an appropriate copyright notice, and **(2)** tells the user that there is no -warranty for the work (except to the extent that warranties are provided), that -licensees may convey the work under this License, and how to view a copy of this -License. If the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. +An interactive user interface displays “Appropriate Legal Notices” to the extent +that it includes a convenient and prominently visible feature that **(1)** +displays an appropriate copyright notice, and **(2)** tells the user that there +is no warranty for the work (except to the extent that warranties are provided), +that licensees may convey the work under this License, and how to view a copy of +this License. If the interface presents a list of user commands or options, such +as a menu, a prominent item in the list meets this criterion. ### 1. Source Code -The “source code” for a work means the preferred form of the work for -making modifications to it. “Object code” means any non-source form of a -work. - -A “Standard Interface” means an interface that either is an official -standard defined by a recognized standards body, or, in the case of interfaces -specified for a particular programming language, one that is widely used among -developers working in that language. - -The “System Libraries” of an executable work include anything, other than -the work as a whole, that **(a)** is included in the normal form of packaging a Major -Component, but which is not part of that Major Component, and **(b)** serves only to -enable use of the work with that Major Component, or to implement a Standard -Interface for which an implementation is available to the public in source code form. -A “Major Component”, in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system (if any) on which -the executable work runs, or a compiler used to produce the work, or an object code -interpreter used to run it. - -The “Corresponding Source” for a work in object code form means all the -source code needed to generate, install, and (for an executable work) run the object -code and to modify the work, including scripts to control those activities. However, -it does not include the work's System Libraries, or general-purpose tools or -generally available free programs which are used unmodified in performing those -activities but which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for the work, and -the source code for shared libraries and dynamically linked subprograms that the work -is specifically designed to require, such as by intimate data communication or -control flow between those subprograms and other parts of the work. +The “source code” for a work means the preferred form of the work for making +modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard +defined by a recognized standards body, or, in the case of interfaces specified +for a particular programming language, one that is widely used among developers +working in that language. + +The “System Libraries” of an executable work include anything, other than the +work as a whole, that **(a)** is included in the normal form of packaging a +Major Component, but which is not part of that Major Component, and **(b)** +serves only to enable use of the work with that Major Component, or to implement +a Standard Interface for which an implementation is available to the public in +source code form. A “Major Component”, in this context, means a major essential +component (kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to produce the +work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose +tools or generally available free programs which are used unmodified in +performing those activities but which are not part of the work. For example, +Corresponding Source includes interface definition files associated with source +files for the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, such as by +intimate data communication or control flow between those subprograms and other +parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. @@ -142,328 +144,341 @@ The Corresponding Source for a work in source code form is that same work. ### 2. Basic Permissions -All rights granted under this License are granted for the term of copyright on the -Program, and are irrevocable provided the stated conditions are met. This License -explicitly affirms your unlimited permission to run the unmodified Program. The -output from running a covered work is covered by this License only if the output, -given its content, constitutes a covered work. This License acknowledges your rights -of fair use or other equivalent, as provided by copyright law. +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This +License explicitly affirms your unlimited permission to run the unmodified +Program. The output from running a covered work is covered by this License only +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. You may make, run and propagate covered works that you do not convey, without -conditions so long as your license otherwise remains in force. You may convey covered -works to others for the sole purpose of having them make modifications exclusively -for you, or provide you with facilities for running those works, provided that you -comply with the terms of this License in conveying all material for which you do not -control copyright. Those thus making or running the covered works for you must do so -exclusively on your behalf, under your direction and control, on terms that prohibit -them from making any copies of your copyrighted material outside their relationship -with you. +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, +provided that you comply with the terms of this License in conveying all +material for which you do not control copyright. Those thus making or running +the covered works for you must do so exclusively on your behalf, under your +direction and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law -No covered work shall be deemed part of an effective technological measure under any -applicable law fulfilling obligations under article 11 of the WIPO copyright treaty -adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention -of such measures. +No covered work shall be deemed part of an effective technological measure under +any applicable law fulfilling obligations under article 11 of the WIPO copyright +treaty adopted on 20 December 1996, or similar laws prohibiting or restricting +circumvention of such measures. -When you convey a covered work, you waive any legal power to forbid circumvention of -technological measures to the extent such circumvention is effected by exercising -rights under this License with respect to the covered work, and you disclaim any -intention to limit operation or modification of the work as a means of enforcing, -against the work's users, your or third parties' legal rights to forbid circumvention -of technological measures. +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention is +effected by exercising rights under this License with respect to the covered +work, and you disclaim any intention to limit operation or modification of the +work as a means of enforcing, against the work's users, your or third parties' +legal rights to forbid circumvention of technological measures. ### 4. Conveying Verbatim Copies -You may convey verbatim copies of the Program's source code as you receive it, in any -medium, provided that you conspicuously and appropriately publish on each copy an -appropriate copyright notice; keep intact all notices stating that this License and -any non-permissive terms added in accord with section 7 apply to the code; keep -intact all notices of the absence of any warranty; and give all recipients a copy of -this License along with the Program. +You may convey verbatim copies of the Program's source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on each +copy an appropriate copyright notice; keep intact all notices stating that this +License and any non-permissive terms added in accord with section 7 apply to the +code; keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. -You may charge any price or no price for each copy that you convey, and you may offer -support or warranty protection for a fee. +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. ### 5. Conveying Modified Source Versions -You may convey a work based on the Program, or the modifications to produce it from -the Program, in the form of source code under the terms of section 4, provided that -you also meet all of these conditions: - -* **a)** The work must carry prominent notices stating that you modified it, and giving a -relevant date. -* **b)** The work must carry prominent notices stating that it is released under this -License and any conditions added under section 7. This requirement modifies the -requirement in section 4 to “keep intact all notices”. -* **c)** You must license the entire work, as a whole, under this License to anyone who -comes into possession of a copy. This License will therefore apply, along with any -applicable section 7 additional terms, to the whole of the work, and all its parts, -regardless of how they are packaged. This License gives no permission to license the -work in any other way, but it does not invalidate such permission if you have -separately received it. -* **d)** If the work has interactive user interfaces, each must display Appropriate Legal -Notices; however, if the Program has interactive interfaces that do not display -Appropriate Legal Notices, your work need not make them do so. - -A compilation of a covered work with other separate and independent works, which are -not by their nature extensions of the covered work, and which are not combined with -it such as to form a larger program, in or on a volume of a storage or distribution -medium, is called an “aggregate” if the compilation and its resulting -copyright are not used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work in an aggregate -does not cause this License to apply to the other parts of the aggregate. +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: + +- **a)** The work must carry prominent notices stating that you modified it, and + giving a relevant date. +- **b)** The work must carry prominent notices stating that it is released under + this License and any conditions added under section 7. This requirement + modifies the requirement in section 4 to “keep intact all notices”. +- **c)** You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will therefore apply, + along with any applicable section 7 additional terms, to the whole of the + work, and all its parts, regardless of how they are packaged. This License + gives no permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- **d)** If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive interfaces + that do not display Appropriate Legal Notices, your work need not make them do + so. + +A compilation of a covered work with other separate and independent works, which +are not by their nature extensions of the covered work, and which are not +combined with it such as to form a larger program, in or on a volume of a +storage or distribution medium, is called an “aggregate” if the compilation and +its resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a +covered work in an aggregate does not cause this License to apply to the other +parts of the aggregate. ### 6. Conveying Non-Source Forms -You may convey a covered work in object code form under the terms of sections 4 and -5, provided that you also convey the machine-readable Corresponding Source under the -terms of this License, in one of these ways: - -* **a)** Convey the object code in, or embodied in, a physical product (including a -physical distribution medium), accompanied by the Corresponding Source fixed on a -durable physical medium customarily used for software interchange. -* **b)** Convey the object code in, or embodied in, a physical product (including a -physical distribution medium), accompanied by a written offer, valid for at least -three years and valid for as long as you offer spare parts or customer support for -that product model, to give anyone who possesses the object code either **(1)** a copy of -the Corresponding Source for all the software in the product that is covered by this -License, on a durable physical medium customarily used for software interchange, for -a price no more than your reasonable cost of physically performing this conveying of -source, or **(2)** access to copy the Corresponding Source from a network server at no -charge. -* **c)** Convey individual copies of the object code with a copy of the written offer to -provide the Corresponding Source. This alternative is allowed only occasionally and -noncommercially, and only if you received the object code with such an offer, in -accord with subsection 6b. -* **d)** Convey the object code by offering access from a designated place (gratis or for -a charge), and offer equivalent access to the Corresponding Source in the same way -through the same place at no further charge. You need not require recipients to copy -the Corresponding Source along with the object code. If the place to copy the object -code is a network server, the Corresponding Source may be on a different server -(operated by you or a third party) that supports equivalent copying facilities, -provided you maintain clear directions next to the object code saying where to find -the Corresponding Source. Regardless of what server hosts the Corresponding Source, -you remain obligated to ensure that it is available for as long as needed to satisfy -these requirements. -* **e)** Convey the object code using peer-to-peer transmission, provided you inform -other peers where the object code and Corresponding Source of the work are being -offered to the general public at no charge under subsection 6d. +You may convey a covered work in object code form under the terms of sections 4 +and 5, provided that you also convey the machine-readable Corresponding Source +under the terms of this License, in one of these ways: + +- **a)** Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the Corresponding + Source fixed on a durable physical medium customarily used for software + interchange. +- **b)** Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a written offer, + valid for at least three years and valid for as long as you offer spare parts + or customer support for that product model, to give anyone who possesses the + object code either **(1)** a copy of the Corresponding Source for all the + software in the product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no more than + your reasonable cost of physically performing this conveying of source, or + **(2)** access to copy the Corresponding Source from a network server at no + charge. +- **c)** Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed only + occasionally and noncommercially, and only if you received the object code + with such an offer, in accord with subsection 6b. +- **d)** Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the Corresponding + Source in the same way through the same place at no further charge. You need + not require recipients to copy the Corresponding Source along with the object + code. If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by you or a third + party) that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the Corresponding + Source, you remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. +- **e)** Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the work + are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. -A “User Product” is either **(1)** a “consumer product”, which -means any tangible personal property which is normally used for personal, family, or -household purposes, or **(2)** anything designed or sold for incorporation into a -dwelling. In determining whether a product is a consumer product, doubtful cases -shall be resolved in favor of coverage. For a particular product received by a -particular user, “normally used” refers to a typical or common use of -that class of product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected to use, the -product. A product is a consumer product regardless of whether the product has -substantial commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - -“Installation Information” for a User Product means any methods, -procedures, authorization keys, or other information required to install and execute -modified versions of a covered work in that User Product from a modified version of -its Corresponding Source. The information must suffice to ensure that the continued -functioning of the modified object code is in no case prevented or interfered with -solely because modification has been made. - -If you convey an object code work under this section in, or with, or specifically for -use in, a User Product, and the conveying occurs as part of a transaction in which -the right of possession and use of the User Product is transferred to the recipient -in perpetuity or for a fixed term (regardless of how the transaction is -characterized), the Corresponding Source conveyed under this section must be -accompanied by the Installation Information. But this requirement does not apply if -neither you nor any third party retains the ability to install modified object code -on the User Product (for example, the work has been installed in ROM). - -The requirement to provide Installation Information does not include a requirement to -continue to provide support service, warranty, or updates for a work that has been -modified or installed by the recipient, or for the User Product in which it has been -modified or installed. Access to a network may be denied when the modification itself -materially and adversely affects the operation of the network or violates the rules -and protocols for communication across the network. - -Corresponding Source conveyed, and Installation Information provided, in accord with -this section must be in a format that is publicly documented (and with an +A “User Product” is either **(1)** a “consumer product”, which means any +tangible personal property which is normally used for personal, family, or +household purposes, or **(2)** anything designed or sold for incorporation into +a dwelling. In determining whether a product is a consumer product, doubtful +cases shall be resolved in favor of coverage. For a particular product received +by a particular user, “normally used” refers to a typical or common use of that +class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, +the product. A product is a consumer product regardless of whether the product +has substantial commercial, industrial or non-consumer uses, unless such uses +represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified version +of its Corresponding Source. The information must suffice to ensure that the +continued functioning of the modified object code is in no case prevented or +interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as part of a +transaction in which the right of possession and use of the User Product is +transferred to the recipient in perpetuity or for a fixed term (regardless of +how the transaction is characterized), the Corresponding Source conveyed under +this section must be accompanied by the Installation Information. But this +requirement does not apply if neither you nor any third party retains the +ability to install modified object code on the User Product (for example, the +work has been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates for a +work that has been modified or installed by the recipient, or for the User +Product in which it has been modified or installed. Access to a network may be +denied when the modification itself materially and adversely affects the +operation of the network or violates the rules and protocols for communication +across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord +with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. ### 7. Additional Terms -“Additional permissions” are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. Additional -permissions that are applicable to the entire Program shall be treated as though they -were included in this License, to the extent that they are valid under applicable -law. If additional permissions apply only to part of the Program, that part may be -used separately under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. +“Additional permissions” are terms that supplement the terms of this License by +making exceptions from one or more of its conditions. Additional permissions +that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may +be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional -permissions may be written to require their own removal in certain cases when you -modify the work.) You may place additional permissions on material, added by you to a -covered work, for which you have or can give appropriate copyright permission. +permissions may be written to require their own removal in certain cases when +you modify the work.) You may place additional permissions on material, added by +you to a covered work, for which you have or can give appropriate copyright +permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: -* **a)** Disclaiming warranty or limiting liability differently from the terms of -sections 15 and 16 of this License; or -* **b)** Requiring preservation of specified reasonable legal notices or author -attributions in that material or in the Appropriate Legal Notices displayed by works -containing it; or -* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that -modified versions of such material be marked in reasonable ways as different from the -original version; or -* **d)** Limiting the use for publicity purposes of names of licensors or authors of the -material; or -* **e)** Declining to grant rights under trademark law for use of some trade names, -trademarks, or service marks; or -* **f)** Requiring indemnification of licensors and authors of that material by anyone -who conveys the material (or modified versions of it) with contractual assumptions of -liability to the recipient, for any liability that these contractual assumptions -directly impose on those licensors and authors. - -All other non-permissive additional terms are considered “further -restrictions” within the meaning of section 10. If the Program as you received -it, or any part of it, contains a notice stating that it is governed by this License -along with a term that is a further restriction, you may remove that term. If a -license document contains a further restriction but permits relicensing or conveying -under this License, you may add to a covered work material governed by the terms of -that license document, provided that the further restriction does not survive such -relicensing or conveying. - -If you add terms to a covered work in accord with this section, you must place, in -the relevant source files, a statement of the additional terms that apply to those -files, or a notice indicating where to find the applicable terms. +- **a)** Disclaiming warranty or limiting liability differently from the terms + of sections 15 and 16 of this License; or +- **b)** Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices displayed by + works containing it; or +- **c)** Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in reasonable ways + as different from the original version; or +- **d)** Limiting the use for publicity purposes of names of licensors or + authors of the material; or +- **e)** Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or +- **f)** Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with contractual + assumptions of liability to the recipient, for any liability that these + contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” +within the meaning of section 10. If the Program as you received it, or any part +of it, contains a notice stating that it is governed by this License along with +a term that is a further restriction, you may remove that term. If a license +document contains a further restriction but permits relicensing or conveying +under this License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does not survive +such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, +in the relevant source files, a statement of the additional terms that apply to +those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a -separately written license, or stated as exceptions; the above requirements apply -either way. +separately written license, or stated as exceptions; the above requirements +apply either way. ### 8. Termination -You may not propagate or modify a covered work except as expressly provided under -this License. Any attempt otherwise to propagate or modify it is void, and will -automatically terminate your rights under this License (including any patent licenses -granted under the third paragraph of section 11). +You may not propagate or modify a covered work except as expressly provided +under this License. Any attempt otherwise to propagate or modify it is void, and +will automatically terminate your rights under this License (including any +patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a -particular copyright holder is reinstated **(a)** provisionally, unless and until the -copyright holder explicitly and finally terminates your license, and **(b)** permanently, -if the copyright holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. +particular copyright holder is reinstated **(a)** provisionally, unless and +until the copyright holder explicitly and finally terminates your license, and +**(b)** permanently, if the copyright holder fails to notify you of the +violation by some reasonable means prior to 60 days after the cessation. -Moreover, your license from a particular copyright holder is reinstated permanently -if the copyright holder notifies you of the violation by some reasonable means, this -is the first time you have received notice of violation of this License (for any -work) from that copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. +Moreover, your license from a particular copyright holder is reinstated +permanently if the copyright holder notifies you of the violation by some +reasonable means, this is the first time you have received notice of violation +of this License (for any work) from that copyright holder, and you cure the +violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your -rights have been terminated and not permanently reinstated, you do not qualify to -receive new licenses for the same material under section 10. +rights have been terminated and not permanently reinstated, you do not qualify +to receive new licenses for the same material under section 10. ### 9. Acceptance Not Required for Having Copies -You are not required to accept this License in order to receive or run a copy of the -Program. Ancillary propagation of a covered work occurring solely as a consequence of -using peer-to-peer transmission to receive a copy likewise does not require -acceptance. However, nothing other than this License grants you permission to -propagate or modify any covered work. These actions infringe copyright if you do not -accept this License. Therefore, by modifying or propagating a covered work, you -indicate your acceptance of this License to do so. +You are not required to accept this License in order to receive or run a copy of +the Program. Ancillary propagation of a covered work occurring solely as a +consequence of using peer-to-peer transmission to receive a copy likewise does +not require acceptance. However, nothing other than this License grants you +permission to propagate or modify any covered work. These actions infringe +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to do +so. ### 10. Automatic Licensing of Downstream Recipients -Each time you convey a covered work, the recipient automatically receives a license -from the original licensors, to run, modify and propagate that work, subject to this -License. You are not responsible for enforcing compliance by third parties with this -License. +Each time you convey a covered work, the recipient automatically receives a +license from the original licensors, to run, modify and propagate that work, +subject to this License. You are not responsible for enforcing compliance by +third parties with this License. An “entity transaction” is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an organization, or -merging organizations. If propagation of a covered work results from an entity -transaction, each party to that transaction who receives a copy of the work also -receives whatever licenses to the work the party's predecessor in interest had or -could give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if the predecessor -has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the rights granted or -affirmed under this License. For example, you may not impose a license fee, royalty, -or other charge for exercise of rights granted under this License, and you may not -initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging -that any patent claim is infringed by making, using, selling, offering for sale, or -importing the Program or any portion of it. +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered work results +from an entity transaction, each party to that transaction who receives a copy +of the work also receives whatever licenses to the work the party's predecessor +in interest had or could give under the previous paragraph, plus a right to +possession of the Corresponding Source of the work from the predecessor in +interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights +granted or affirmed under this License. For example, you may not impose a +license fee, royalty, or other charge for exercise of rights granted under this +License, and you may not initiate litigation (including a cross-claim or +counterclaim in a lawsuit) alleging that any patent claim is infringed by +making, using, selling, offering for sale, or importing the Program or any +portion of it. ### 11. Patents -A “contributor” is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The work thus -licensed is called the contributor's “contributor version”. +A “contributor” is a copyright holder who authorizes use under this License of +the Program or a work on which the Program is based. The work thus licensed is +called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or -controlled by the contributor, whether already acquired or hereafter acquired, that -would be infringed by some manner, permitted by this License, of making, using, or -selling its contributor version, but do not include claims that would be infringed -only as a consequence of further modification of the contributor version. For -purposes of this definition, “control” includes the right to grant patent -sublicenses in a manner consistent with the requirements of this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free patent license -under the contributor's essential patent claims, to make, use, sell, offer for sale, -import and otherwise run, modify and propagate the contents of its contributor -version. - -In the following three paragraphs, a “patent license” is any express -agreement or commitment, however denominated, not to enforce a patent (such as an -express permission to practice a patent or covenant not to sue for patent -infringement). To “grant” such a patent license to a party means to make -such an agreement or commitment not to enforce a patent against the party. +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would +be infringed only as a consequence of further modification of the contributor +version. For purposes of this definition, “control” includes the right to grant +patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent +license under the contributor's essential patent claims, to make, use, sell, +offer for sale, import and otherwise run, modify and propagate the contents of +its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement +or commitment, however denominated, not to enforce a patent (such as an express +permission to practice a patent or covenant not to sue for patent infringement). +To “grant” such a patent license to a party means to make such an agreement or +commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the -Corresponding Source of the work is not available for anyone to copy, free of charge -and under the terms of this License, through a publicly available network server or -other readily accessible means, then you must either **(1)** cause the Corresponding -Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the -patent license for this particular work, or **(3)** arrange, in a manner consistent with -the requirements of this License, to extend the patent license to downstream -recipients. “Knowingly relying” means you have actual knowledge that, but -for the patent license, your conveying the covered work in a country, or your -recipient's use of the covered work in a country, would infringe one or more -identifiable patents in that country that you have reason to believe are valid. +Corresponding Source of the work is not available for anyone to copy, free of +charge and under the terms of this License, through a publicly available network +server or other readily accessible means, then you must either **(1)** cause the +Corresponding Source to be so available, or **(2)** arrange to deprive yourself +of the benefit of the patent license for this particular work, or **(3)** +arrange, in a manner consistent with the requirements of this License, to extend +the patent license to downstream recipients. “Knowingly relying” means you have +actual knowledge that, but for the patent license, your conveying the covered +work in a country, or your recipient's use of the covered work in a country, +would infringe one or more identifiable patents in that country that you have +reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you -convey, or propagate by procuring conveyance of, a covered work, and grant a patent -license to some of the parties receiving the covered work authorizing them to use, -propagate, modify or convey a specific copy of the covered work, then the patent -license you grant is automatically extended to all recipients of the covered work and -works based on it. - -A patent license is “discriminatory” if it does not include within the -scope of its coverage, prohibits the exercise of, or is conditioned on the -non-exercise of one or more of the rights that are specifically granted under this -License. You may not convey a covered work if you are a party to an arrangement with -a third party that is in the business of distributing software, under which you make -payment to the third party based on the extent of your activity of conveying the -work, and under which the third party grants, to any of the parties who would receive -the covered work from you, a discriminatory patent license **(a)** in connection with -copies of the covered work conveyed by you (or copies made from those copies), or **(b)** -primarily for and in connection with specific products or compilations that contain -the covered work, unless you entered into that arrangement, or that patent license -was granted, prior to 28 March 2007. +convey, or propagate by procuring conveyance of, a covered work, and grant a +patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, +then the patent license you grant is automatically extended to all recipients of +the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of +its coverage, prohibits the exercise of, or is conditioned on the non-exercise +of one or more of the rights that are specifically granted under this License. +You may not convey a covered work if you are a party to an arrangement with a +third party that is in the business of distributing software, under which you +make payment to the third party based on the extent of your activity of +conveying the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory patent +license **(a)** in connection with copies of the covered work conveyed by you +(or copies made from those copies), or **(b)** primarily for and in connection +with specific products or compilations that contain the covered work, unless you +entered into that arrangement, or that patent license was granted, prior to 28 +March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you @@ -471,51 +486,55 @@ under applicable patent law. ### 12. No Surrender of Others' Freedom -If conditions are imposed on you (whether by court order, agreement or otherwise) -that contradict the conditions of this License, they do not excuse you from the -conditions of this License. If you cannot convey a covered work so as to satisfy -simultaneously your obligations under this License and any other pertinent -obligations, then as a consequence you may not convey it at all. For example, if you -agree to terms that obligate you to collect a royalty for further conveying from -those to whom you convey the Program, the only way you could satisfy both those terms -and this License would be to refrain entirely from conveying the Program. +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not excuse +you from the conditions of this License. If you cannot convey a covered work so +as to satisfy simultaneously your obligations under this License and any other +pertinent obligations, then as a consequence you may not convey it at all. For +example, if you agree to terms that obligate you to collect a royalty for +further conveying from those to whom you convey the Program, the only way you +could satisfy both those terms and this License would be to refrain entirely +from conveying the Program. ### 13. Use with the GNU Affero General Public License -Notwithstanding any other provision of this License, you have permission to link or -combine any covered work with a work licensed under version 3 of the GNU Affero -General Public License into a single combined work, and to convey the resulting work. -The terms of this License will continue to apply to the part which is the covered -work, but the special requirements of the GNU Affero General Public License, section -13, concerning interaction through a network will apply to the combination as such. +Notwithstanding any other provision of this License, you have permission to link +or combine any covered work with a work licensed under version 3 of the GNU +Affero General Public License into a single combined work, and to convey the +resulting work. The terms of this License will continue to apply to the part +which is the covered work, but the special requirements of the GNU Affero +General Public License, section 13, concerning interaction through a network +will apply to the combination as such. ### 14. Revised Versions of this License The Free Software Foundation may publish revised and/or new versions of the GNU -General Public License from time to time. Such new versions will be similar in spirit -to the present version, but may differ in detail to address new problems or concerns. +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. -Each version is given a distinguishing version number. If the Program specifies that -a certain numbered version of the GNU General Public License “or any later +Each version is given a distinguishing version number. If the Program specifies +that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and -conditions either of that numbered version or of any later version published by the -Free Software Foundation. If the Program does not specify a version number of the GNU -General Public License, you may choose any version ever published by the Free -Software Foundation. - -If the Program specifies that a proxy can decide which future versions of the GNU -General Public License can be used, that proxy's public statement of acceptance of a -version permanently authorizes you to choose that version for the Program. +conditions either of that numbered version or of any later version published by +the Free Software Foundation. If the Program does not specify a version number +of the GNU General Public License, you may choose any version ever published by +the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the +GNU General Public License can be used, that proxy's public statement of +acceptance of a version permanently authorizes you to choose that version for +the Program. -Later license versions may give you additional or different permissions. However, no -additional obligations are imposed on any author or copyright holder as a result of -your choosing to follow a later version. +Later license versions may give you additional or different permissions. +However, no additional obligations are imposed on any author or copyright holder +as a result of your choosing to follow a later version. ### 15. Disclaimer of Warranty THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER +PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE @@ -526,32 +545,32 @@ DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, -INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE -OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE -WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### 17. Interpretation of Sections 15 and 16 -If the disclaimer of warranty and limitation of liability provided above cannot be -given local legal effect according to their terms, reviewing courts shall apply local -law that most closely approximates an absolute waiver of all civil liability in -connection with the Program, unless a warranty or assumption of liability accompanies -a copy of the Program in return for a fee. +If the disclaimer of warranty and limitation of liability provided above cannot +be given local legal effect according to their terms, reviewing courts shall +apply local law that most closely approximates an absolute waiver of all civil +liability in connection with the Program, unless a warranty or assumption of +liability accompanies a copy of the Program in return for a fee. _END OF TERMS AND CONDITIONS_ ## How to Apply These Terms to Your New Programs -If you develop a new program, and you want it to be of the greatest possible use to -the public, the best way to achieve this is to make it free software which everyone -can redistribute and change under these terms. +If you develop a new program, and you want it to be of the greatest possible use +to the public, the best way to achieve this is to make it free software which +everyone can redistribute and change under these terms. -To do so, attach the following notices to the program. It is safest to attach them -to the start of each source file to most effectively state the exclusion of warranty; -and each file should have at least the “copyright” line and a pointer to -where the full notice is found. +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion of +warranty; and each file should have at least the “copyright” line and a pointer +to where the full notice is found. Copyright (C) @@ -571,26 +590,26 @@ where the full notice is found. Also add information on how to contact you by electronic and paper mail. -If the program does terminal interaction, make it output a short notice like this -when it starts in an interactive mode: +If the program does terminal interaction, make it output a short notice like +this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. -The hypothetical commands `show w` and `show c` should show the appropriate parts of -the General Public License. Of course, your program's commands might be different; -for a GUI interface, you would use an “about box”. +The hypothetical commands `show w` and `show c` should show the appropriate +parts of the General Public License. Of course, your program's commands might be +different; for a GUI interface, you would use an “about box”. -You should also get your employer (if you work as a programmer) or school, if any, to -sign a “copyright disclaimer” for the program, if necessary. For more +You should also get your employer (if you work as a programmer) or school, if +any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <>. The GNU General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may consider it -more useful to permit linking proprietary applications with the library. If this is -what you want to do, use the GNU Lesser General Public License instead of this -License. But first, please read +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. But first, please read <>. diff --git a/PUBLISH.md b/PUBLISH.md index 9c2994c11..513e924b1 100644 --- a/PUBLISH.md +++ b/PUBLISH.md @@ -10,4 +10,4 @@ cd ../sdk && npm publish --access public cd ../create-leo-app & npm publish --access public git tag vX.X.X git push origin vX.X.X -``` \ No newline at end of file +``` diff --git a/README.md b/README.md index 65b4fae60..cce53094c 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,12 @@

- # Zero-Knowledge Web App SDK -The [Provable SDK](https://github.com/ProvableHQ/sdk) provides tools for building zero-knowledge applications. It consists of -several TypeScript & JavaScript libraries which provide the following functionality: +The [Provable SDK](https://github.com/ProvableHQ/sdk) provides tools for +building zero-knowledge applications. It consists of several TypeScript & +JavaScript libraries which provide the following functionality: + 1. [Aleo account management](https://provable.tools/account) 2. [Web-based program execution and deployment](https://provable.tools/develop) 3. [Aleo credit transfers](https://provable.tools/transfer) @@ -16,61 +17,79 @@ several TypeScript & JavaScript libraries which provide the following functional 5. [Communication with the Aleo network](https://provable.tools/rest) 6. [Aleo Cryptographic Primitives](https://provable.tools/algebra) -All of this functionality is demonstrated on [Provable.tools](https://provable.tools). - +All of this functionality is demonstrated on +[Provable.tools](https://provable.tools). The Provable SDK is divided into three TypeScript/JavaScript packages: ## 1. Provable SDK - Build Zero-Knowledge Web Apps - Provable SDK + +Provable SDK -The official Provable SDK providing JavaScript/TypeScript tools for creating zero-knowledge applications. +The official Provable SDK providing JavaScript/TypeScript tools for creating +zero-knowledge applications. ### ⚡ Build your own app -Start here with the [Provable SDK Readme](https://github.com/ProvableHQ/sdk#readme) to get started building your -first zero-knowledge web app. +Start here with the +[Provable SDK Readme](https://github.com/ProvableHQ/sdk#readme) to get started +building your first zero-knowledge web app. #### Source: [`Provable SDK`](https://www.npmjs.com/package/@provablehq/sdk) ## 2. Create-Leo-App - Zero-Knowledge Web App Examples - Create Leo App -Create-leo-app provides zero-knowledge web app examples in common web frameworks such as React. Developers looking to -start with working examples should start here. + +Create Leo App + +Create-leo-app provides zero-knowledge web app examples in common web frameworks +such as React. Developers looking to start with working examples should start +here. #### Source: [`sdk/create-leo-app`](https://github.com/ProvableHQ/sdk/tree/mainnet/create-leo-app) ## 3. Aleo Wasm - Zero-Knowledge Algorithms in JavaScript + WebAssembly - Create Leo App -Aleo Wasm is a Rust crate which compiles the Aleo source code responsible for creating and executing zero-knowledge programs into -WebAssembly. + +Create Leo App -When compiled with `wasm-pack`, JavaScript bindings are generated for the WebAssembly allowing Aleo zero-knowledge programs to be used in the browser and Node.js. This package is available on NPM (linked above). The Aleo Wasm -readme provides instructions for compiling this crate and using it in web projects for those interested in building from -source. +Aleo Wasm is a Rust crate which compiles the Aleo source code responsible for +creating and executing zero-knowledge programs into WebAssembly. + +When compiled with `wasm-pack`, JavaScript bindings are generated for the +WebAssembly allowing Aleo zero-knowledge programs to be used in the browser and +Node.js. This package is available on NPM (linked above). The Aleo Wasm readme +provides instructions for compiling this crate and using it in web projects for +those interested in building from source. Source: [Aleo Wasm](https://www.npmjs.com/package/@provablehq/wasm) ## 📚 Documentation #### [API Documentation](https://developer.aleo.org/sdk/overview) -API Documentation and tutorials for the Provable SDK can be found on the [SDK Developer Docs](https://developer.aleo.org/sdk/overview) page. -Documentation on how to build Leo and Aleo Instructions programs can be found on the [Leo Developer Docs](https://docs.leo-lang.org/leo) page. + +API Documentation and tutorials for the Provable SDK can be found on the +[SDK Developer Docs](https://developer.aleo.org/sdk/overview) page. +Documentation on how to build Leo and Aleo Instructions programs can be found on +the [Leo Developer Docs](https://docs.leo-lang.org/leo) page. #### [SDK Readme](https://github.com/ProvableHQ/sdk/tree/mainnet/sdk#readme) -The SDK readme provides concepts core to executing zero-knowledge programs in the web and several detailed examples of -how to use the SDK to build web apps using Aleo. + +The SDK readme provides concepts core to executing zero-knowledge programs in +the web and several detailed examples of how to use the SDK to build web apps +using Aleo. #### [Aleo Wasm Readme](https://github.com/ProvableHQ/sdk/tree/mainnet/wasm#readme) -The Aleo Wasm readme provides instructions for compiling the Aleo Wasm crate and using it in web projects. Those who -want to build from source or create their own WebAssembly bindings should start here. + +The Aleo Wasm readme provides instructions for compiling the Aleo Wasm crate and +using it in web projects. Those who want to build from source or create their +own WebAssembly bindings should start here. ## ❤️ Contributors -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): +Thanks goes to these wonderful people +([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -126,4 +145,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! +This project follows the +[all-contributors](https://github.com/all-contributors/all-contributors) +specification. Contributions of any kind welcome! diff --git a/create-leo-app/README.md b/create-leo-app/README.md index 1ed4b669d..2a9567756 100644 --- a/create-leo-app/README.md +++ b/create-leo-app/README.md @@ -2,8 +2,8 @@ ## Scaffolding Your First Aleo Project -> **Compatibility Note:** -> Please use [Node.js](https://nodejs.org/en/) version 18+ +> **Compatibility Note:** Please use [Node.js](https://nodejs.org/en/) version +> 18+ With NPM: @@ -13,7 +13,9 @@ npm create leo-app@latest Then follow the prompts! -You can also directly specify the project name and template you want to use via additional command line options. For example, to scaffold a Leo project using the Vanilla JavaScript template, run: +You can also directly specify the project name and template you want to use via +additional command line options. For example, to scaffold a Leo project using +the Vanilla JavaScript template, run: ```bash # npm 6.x @@ -33,4 +35,5 @@ You can use `.` for the project name to scaffold in the current directory. ## More Information -Based off of create-vite: https://github.com/vitejs/vite/tree/main/packages/create-vite +Based off of create-vite: +https://github.com/vitejs/vite/tree/main/packages/create-vite diff --git a/create-leo-app/build.config.ts b/create-leo-app/build.config.ts index 178866390..45b7aa8ed 100644 --- a/create-leo-app/build.config.ts +++ b/create-leo-app/build.config.ts @@ -1,15 +1,15 @@ -import { defineBuildConfig } from 'unbuild' +import { defineBuildConfig } from "unbuild"; export default defineBuildConfig({ - entries: ['src/index'], - clean: true, - rollup: { - inlineDependencies: true, - esbuild: { - minify: true, + entries: ["src/index"], + clean: true, + rollup: { + inlineDependencies: true, + esbuild: { + minify: true, + }, }, - }, - alias: { - prompts: 'prompts/lib/index.js', - }, -}) + alias: { + prompts: "prompts/lib/index.js", + }, +}); diff --git a/create-leo-app/index.js b/create-leo-app/index.js index f5e8e064a..f7fcb1920 100755 --- a/create-leo-app/index.js +++ b/create-leo-app/index.js @@ -1,3 +1,3 @@ #!/usr/bin/env node -import './dist/index.mjs' +import "./dist/index.mjs"; diff --git a/create-leo-app/package.json b/create-leo-app/package.json index 428cff6ab..c176cb6c8 100644 --- a/create-leo-app/package.json +++ b/create-leo-app/package.json @@ -1,44 +1,44 @@ { - "name": "create-leo-app", - "version": "0.9.18", - "type": "module", - "license": "GPL-3.0", - "collaborators": [ - "The Provable Team" - ], - "bin": "index.js", - "files": [ - "index.js", - "template-extension", - "template-nextjs-ts", - "template-node-ts", - "template-build-and-execute-authorization-ts", - "template-node", - "template-offline-public-transaction-ts", - "template-node-credits-aleo-functions-ts", - "template-react-credits-aleo-functions-ts", - "template-react-leo", - "template-react-managed-worker", - "template-react-ts", - "template-vanilla", - "dist" - ], - "scripts": { - "dev": "unbuild --stub", - "build": "unbuild", - "typecheck": "tsc --noEmit" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "devDependencies": { - "@types/minimist": "^1.2.5", - "@types/prompts": "^2.4.9", - "kolorist": "^1.8.0", - "minimist": "^1.2.8", - "prompts": "^2.4.2", - "ts-node": "^10.9.2", - "typescript": "^5.8.2", - "unbuild": "^3.3.1" - } + "name": "create-leo-app", + "version": "0.9.18", + "type": "module", + "license": "GPL-3.0", + "collaborators": [ + "The Provable Team" + ], + "bin": "index.js", + "files": [ + "index.js", + "template-extension", + "template-nextjs-ts", + "template-node-ts", + "template-build-and-execute-authorization-ts", + "template-node", + "template-offline-public-transaction-ts", + "template-node-credits-aleo-functions-ts", + "template-react-credits-aleo-functions-ts", + "template-react-leo", + "template-react-managed-worker", + "template-react-ts", + "template-vanilla", + "dist" + ], + "scripts": { + "dev": "unbuild --stub", + "build": "unbuild", + "typecheck": "tsc --noEmit" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "devDependencies": { + "@types/minimist": "^1.2.5", + "@types/prompts": "^2.4.9", + "kolorist": "^1.8.0", + "minimist": "^1.2.8", + "prompts": "^2.4.2", + "ts-node": "^10.9.2", + "typescript": "^5.8.2", + "unbuild": "^3.3.1" + } } diff --git a/create-leo-app/src/index.ts b/create-leo-app/src/index.ts index 4f1e371e7..010935db8 100755 --- a/create-leo-app/src/index.ts +++ b/create-leo-app/src/index.ts @@ -3,333 +3,358 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import minimist from "minimist"; import prompts from "prompts"; -import {lightGreen, cyan, red, reset, yellow, lightGray, green} from "kolorist"; +import { + lightGreen, + cyan, + red, + reset, + yellow, + lightGray, + green, +} from "kolorist"; const argv = minimist<{ - t?: string; - template?: string; + t?: string; + template?: string; }>(process.argv.slice(2), { string: ["_"] }); const cwd = process.cwd(); type ColorFunc = (str: string | number) => string; type Framework = { - name: string; - display: string; - color: ColorFunc; - variants: FrameworkVariant[]; + name: string; + display: string; + color: ColorFunc; + variants: FrameworkVariant[]; }; type FrameworkVariant = { - name: string; - display: string; - color: ColorFunc; - customCommand?: string; + name: string; + display: string; + color: ColorFunc; + customCommand?: string; }; const FRAMEWORKS: Framework[] = [ - { - name: 'vanilla', - display: 'Vanilla', - color: yellow, - variants: [ - { - name: 'vanilla', - display: 'JavaScript', + { + name: "vanilla", + display: "Vanilla", color: yellow, - }, - ], - }, - { - name: "react", - display: "React", - color: cyan, - variants: [ - { - name: "react-leo", - display: "JavaScript + Leo", - color: lightGreen, - }, - { - name: 'react-ts', - display: "React + TypeScript", - color: red - }, - { - name: "nextjs-ts", - display: "TypeScript + Next.js", - color: lightGray, - }, - ], - }, - { - name: 'node', - display: 'Node.js', - color: green, - variants: [ - { - name: 'node', - display: 'Node.js', + variants: [ + { + name: "vanilla", + display: "JavaScript", + color: yellow, + }, + ], + }, + { + name: "react", + display: "React", + color: cyan, + variants: [ + { + name: "react-leo", + display: "JavaScript + Leo", + color: lightGreen, + }, + { + name: "react-ts", + display: "React + TypeScript", + color: red, + }, + { + name: "nextjs-ts", + display: "TypeScript + Next.js", + color: lightGray, + }, + ], + }, + { + name: "node", + display: "Node.js", color: green, - customCommand: 'start', - }, - ], - }, + variants: [ + { + name: "node", + display: "Node.js", + color: green, + customCommand: "start", + }, + ], + }, ]; const TEMPLATES = FRAMEWORKS.map( - (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name], + (f) => (f.variants && f.variants.map((v) => v.name)) || [f.name], ).reduce((a, b) => a.concat(b), []); const renameFiles: Record = { - _gitignore: ".gitignore", + _gitignore: ".gitignore", }; const defaultTargetDir = "aleo-project"; async function init() { - const argTargetDir = formatTargetDir(argv._[0]); - const argTemplate = argv.template || argv.t; - - let targetDir = argTargetDir || defaultTargetDir; - const getProjectName = () => - targetDir === "." ? path.basename(path.resolve()) : targetDir; - - let selectedFramework: Framework | undefined; - let selectedVariant: string | undefined; - let selectedVariantObj: FrameworkVariant | undefined; - - if (argTemplate && TEMPLATES.includes(argTemplate)) { - for (const framework of FRAMEWORKS) { - const match = framework.variants.find((v) => v.name === argTemplate); - if (match) { - selectedFramework = framework; - selectedVariant = match.name; - selectedVariantObj = match; - break; - } - } - } - - let result: prompts.Answers< - "projectName" | "overwrite" | "packageName" | "framework" | "variant" - >; - - try { - result = await prompts( - [ - { - type: argTargetDir ? null : "text", - name: "projectName", - message: reset("Project name:"), - initial: defaultTargetDir, - onState: (state) => { - targetDir = formatTargetDir(state.value) || defaultTargetDir; - }, - }, - { - type: () => - !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : "confirm", - name: "overwrite", - message: () => - (targetDir === "." - ? "Current directory" - : `Target directory "${targetDir}"`) + - ` is not empty. Remove existing files and continue?`, - }, - { - type: (_, { overwrite }: { overwrite?: boolean }) => { - if (overwrite === false) { - throw new Error(red("✖") + " Operation cancelled"); + const argTargetDir = formatTargetDir(argv._[0]); + const argTemplate = argv.template || argv.t; + + let targetDir = argTargetDir || defaultTargetDir; + const getProjectName = () => + targetDir === "." ? path.basename(path.resolve()) : targetDir; + + let selectedFramework: Framework | undefined; + let selectedVariant: string | undefined; + let selectedVariantObj: FrameworkVariant | undefined; + + if (argTemplate && TEMPLATES.includes(argTemplate)) { + for (const framework of FRAMEWORKS) { + const match = framework.variants.find( + (v) => v.name === argTemplate, + ); + if (match) { + selectedFramework = framework; + selectedVariant = match.name; + selectedVariantObj = match; + break; } - return null; - }, - name: "overwriteChecker", - }, - { - type: () => (isValidPackageName(getProjectName()) ? null : "text"), - name: "packageName", - message: reset("Package name:"), - initial: () => toValidPackageName(getProjectName()), - validate: (dir) => - isValidPackageName(dir) || "Invalid package.json name", - }, - { - type: selectedFramework ? null : "select", - name: "framework", - message: - typeof argTemplate === "string" && !TEMPLATES.includes(argTemplate) - ? reset( - `"${argTemplate}" isn't a valid template. Please choose from below: `, - ) - : reset("Select a framework:"), - initial: 0, - choices: FRAMEWORKS.map((framework) => { - const frameworkColor = framework.color; - return { - title: frameworkColor(framework.display || framework.name), - value: framework, - }; - }), - }, - { - type: selectedVariant ? null : "select", - name: "variant", - message: reset("Select a variant:"), - choices: (framework: Framework) => - framework.variants.map((variant) => { - const variantColor = variant.color; - return { - title: variantColor(variant.display || variant.name), - value: variant.name, - }; - }), - }, - ], - { - onCancel: () => { - throw new Error(red("✖") + " Operation cancelled"); - }, - }, - ); - } catch (cancelled: any) { - console.log(cancelled.message); - return; - } + } + } + + let result: prompts.Answers< + "projectName" | "overwrite" | "packageName" | "framework" | "variant" + >; + + try { + result = await prompts( + [ + { + type: argTargetDir ? null : "text", + name: "projectName", + message: reset("Project name:"), + initial: defaultTargetDir, + onState: (state) => { + targetDir = + formatTargetDir(state.value) || defaultTargetDir; + }, + }, + { + type: () => + !fs.existsSync(targetDir) || isEmpty(targetDir) + ? null + : "confirm", + name: "overwrite", + message: () => + (targetDir === "." + ? "Current directory" + : `Target directory "${targetDir}"`) + + ` is not empty. Remove existing files and continue?`, + }, + { + type: (_, { overwrite }: { overwrite?: boolean }) => { + if (overwrite === false) { + throw new Error(red("✖") + " Operation cancelled"); + } + return null; + }, + name: "overwriteChecker", + }, + { + type: () => + isValidPackageName(getProjectName()) ? null : "text", + name: "packageName", + message: reset("Package name:"), + initial: () => toValidPackageName(getProjectName()), + validate: (dir) => + isValidPackageName(dir) || "Invalid package.json name", + }, + { + type: selectedFramework ? null : "select", + name: "framework", + message: + typeof argTemplate === "string" && + !TEMPLATES.includes(argTemplate) + ? reset( + `"${argTemplate}" isn't a valid template. Please choose from below: `, + ) + : reset("Select a framework:"), + initial: 0, + choices: FRAMEWORKS.map((framework) => { + const frameworkColor = framework.color; + return { + title: frameworkColor( + framework.display || framework.name, + ), + value: framework, + }; + }), + }, + { + type: selectedVariant ? null : "select", + name: "variant", + message: reset("Select a variant:"), + choices: (framework: Framework) => + framework.variants.map((variant) => { + const variantColor = variant.color; + return { + title: variantColor( + variant.display || variant.name, + ), + value: variant.name, + }; + }), + }, + ], + { + onCancel: () => { + throw new Error(red("✖") + " Operation cancelled"); + }, + }, + ); + } catch (cancelled: any) { + console.log(cancelled.message); + return; + } - // user choice associated with prompts - const { framework, overwrite, packageName, variant } = result; + // user choice associated with prompts + const { framework, overwrite, packageName, variant } = result; - const root = path.join(cwd, targetDir); + const root = path.join(cwd, targetDir); - // Find the selected variant object to get customCommand - if (!selectedVariantObj && framework) { - selectedVariantObj = framework.variants.find((v: FrameworkVariant) => v.name === variant); - } + // Find the selected variant object to get customCommand + if (!selectedVariantObj && framework) { + selectedVariantObj = framework.variants.find( + (v: FrameworkVariant) => v.name === variant, + ); + } - if (overwrite) { - emptyDir(root); - } else if (!fs.existsSync(root)) { - fs.mkdirSync(root, { recursive: true }); - } + if (overwrite) { + emptyDir(root); + } else if (!fs.existsSync(root)) { + fs.mkdirSync(root, { recursive: true }); + } - // determine template - let template: string = variant || framework?.name || argTemplate; + // determine template + let template: string = variant || framework?.name || argTemplate; - const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent); - const pkgManager = pkgInfo ? pkgInfo.name : "npm"; + const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent); + const pkgManager = pkgInfo ? pkgInfo.name : "npm"; - console.log(`\nScaffolding project in ${root}...`); + console.log(`\nScaffolding project in ${root}...`); - const templateDir = path.resolve( - fileURLToPath(import.meta.url), - "../..", - `template-${template}`, - ); + const templateDir = path.resolve( + fileURLToPath(import.meta.url), + "../..", + `template-${template}`, + ); - const write = (file: string, content?: string) => { - const targetPath = path.join(root, renameFiles[file] ?? file); - if (content) { - fs.writeFileSync(targetPath, content); - } else { - copy(path.join(templateDir, file), targetPath); + const write = (file: string, content?: string) => { + const targetPath = path.join(root, renameFiles[file] ?? file); + if (content) { + fs.writeFileSync(targetPath, content); + } else { + copy(path.join(templateDir, file), targetPath); + } + }; + + const files = fs.readdirSync(templateDir); + for (const file of files.filter((f) => f !== "package.json")) { + write(file); } - }; - - const files = fs.readdirSync(templateDir); - for (const file of files.filter((f) => f !== "package.json")) { - write(file); - } - const pkg = JSON.parse( - fs.readFileSync(path.join(templateDir, `package.json`), "utf-8"), - ); + const pkg = JSON.parse( + fs.readFileSync(path.join(templateDir, `package.json`), "utf-8"), + ); - pkg.name = packageName || getProjectName(); + pkg.name = packageName || getProjectName(); - write("package.json", JSON.stringify(pkg, null, 2) + "\n"); + write("package.json", JSON.stringify(pkg, null, 2) + "\n"); - const cdProjectName = path.relative(cwd, root); - console.log(`\nDone. Now run:\n`); - if (root !== cwd) { - console.log( - ` cd ${ - cdProjectName.includes(" ") ? `"${cdProjectName}"` : cdProjectName - }`, - ); - } - const runCommand = selectedVariantObj?.customCommand || 'dev'; - switch (pkgManager) { - default: - console.log(` ${pkgManager} install`); - console.log(` ${pkgManager} ${runCommand === 'dev' ? 'run dev' : runCommand}`); - break; - } - console.log(); + const cdProjectName = path.relative(cwd, root); + console.log(`\nDone. Now run:\n`); + if (root !== cwd) { + console.log( + ` cd ${ + cdProjectName.includes(" ") + ? `"${cdProjectName}"` + : cdProjectName + }`, + ); + } + const runCommand = selectedVariantObj?.customCommand || "dev"; + switch (pkgManager) { + default: + console.log(` ${pkgManager} install`); + console.log( + ` ${pkgManager} ${runCommand === "dev" ? "run dev" : runCommand}`, + ); + break; + } + console.log(); } function formatTargetDir(targetDir: string | undefined) { - return targetDir?.trim().replace(/\/+$/g, ""); + return targetDir?.trim().replace(/\/+$/g, ""); } function copy(src: string, dest: string) { - const stat = fs.statSync(src); - if (stat.isDirectory()) { - copyDir(src, dest); - } else { - fs.copyFileSync(src, dest); - } + const stat = fs.statSync(src); + if (stat.isDirectory()) { + copyDir(src, dest); + } else { + fs.copyFileSync(src, dest); + } } function isValidPackageName(projectName: string) { - return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test( - projectName, - ); + return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test( + projectName, + ); } function toValidPackageName(projectName: string) { - return projectName - .trim() - .toLowerCase() - .replace(/\s+/g, "-") - .replace(/^[._]/, "") - .replace(/[^a-z\d\-~]+/g, "-"); + return projectName + .trim() + .toLowerCase() + .replace(/\s+/g, "-") + .replace(/^[._]/, "") + .replace(/[^a-z\d\-~]+/g, "-"); } function copyDir(srcDir: string, destDir: string) { - fs.mkdirSync(destDir, { recursive: true }); - for (const file of fs.readdirSync(srcDir)) { - const srcFile = path.resolve(srcDir, file); - const destFile = path.resolve(destDir, file); - copy(srcFile, destFile); - } + fs.mkdirSync(destDir, { recursive: true }); + for (const file of fs.readdirSync(srcDir)) { + const srcFile = path.resolve(srcDir, file); + const destFile = path.resolve(destDir, file); + copy(srcFile, destFile); + } } function isEmpty(path: string) { - const files = fs.readdirSync(path); - return files.length === 0 || (files.length === 1 && files[0] === ".git"); + const files = fs.readdirSync(path); + return files.length === 0 || (files.length === 1 && files[0] === ".git"); } function emptyDir(dir: string) { - if (!fs.existsSync(dir)) { - return; - } - for (const file of fs.readdirSync(dir)) { - if (file === ".git") { - continue; + if (!fs.existsSync(dir)) { + return; + } + for (const file of fs.readdirSync(dir)) { + if (file === ".git") { + continue; + } + fs.rmSync(path.resolve(dir, file), { recursive: true, force: true }); } - fs.rmSync(path.resolve(dir, file), { recursive: true, force: true }); - } } function pkgFromUserAgent(userAgent: string | undefined) { - if (!userAgent) return undefined; - const pkgSpec = userAgent.split(" ")[0]; - const pkgSpecArr = pkgSpec.split("/"); - return { - name: pkgSpecArr[0], - version: pkgSpecArr[1], - }; + if (!userAgent) return undefined; + const pkgSpec = userAgent.split(" ")[0]; + const pkgSpecArr = pkgSpec.split("/"); + return { + name: pkgSpecArr[0], + version: pkgSpecArr[1], + }; } init().catch((e) => { - console.error(e); + console.error(e); }); diff --git a/create-leo-app/template-build-and-execute-authorization-ts/package.json b/create-leo-app/template-build-and-execute-authorization-ts/package.json index 07a12ef36..e7ed9faab 100644 --- a/create-leo-app/template-build-and-execute-authorization-ts/package.json +++ b/create-leo-app/template-build-and-execute-authorization-ts/package.json @@ -1,19 +1,19 @@ { - "name": "node-build-and-execute-authorization-ts-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "build": "rimraf dist/js && rollup --config", - "dev": "npm run build && node dist/index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - }, - "devDependencies": { - "rimraf": "^6.0.1", - "rollup": "^4.59.0", - "rollup-plugin-typescript2": "^0.36.0", - "typescript": "^5.7.3" - } + "name": "node-build-and-execute-authorization-ts-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "rimraf dist/js && rollup --config", + "dev": "npm run build && node dist/index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + }, + "devDependencies": { + "rimraf": "^6.0.1", + "rollup": "^4.59.0", + "rollup-plugin-typescript2": "^0.36.0", + "typescript": "^5.7.3" + } } diff --git a/create-leo-app/template-build-and-execute-authorization-ts/rollup.config.js b/create-leo-app/template-build-and-execute-authorization-ts/rollup.config.js index 259a970aa..baeeb52f7 100644 --- a/create-leo-app/template-build-and-execute-authorization-ts/rollup.config.js +++ b/create-leo-app/template-build-and-execute-authorization-ts/rollup.config.js @@ -9,7 +9,7 @@ export default { format: "es", sourcemap: true, }, - external: ['@provablehq/sdk'], + external: ["@provablehq/sdk"], plugins: [ typescript({ tsconfig: "tsconfig.json", diff --git a/create-leo-app/template-build-and-execute-authorization-ts/src/index.ts b/create-leo-app/template-build-and-execute-authorization-ts/src/index.ts index 655c1f067..c4a646021 100644 --- a/create-leo-app/template-build-and-execute-authorization-ts/src/index.ts +++ b/create-leo-app/template-build-and-execute-authorization-ts/src/index.ts @@ -1,4 +1,9 @@ -import { AleoKeyProvider, PrivateKey, initThreadPool, ProgramManager } from "@provablehq/sdk"; +import { + AleoKeyProvider, + PrivateKey, + initThreadPool, + ProgramManager, +} from "@provablehq/sdk"; await initThreadPool(); @@ -7,7 +12,10 @@ const keyProvider = new AleoKeyProvider(); keyProvider.useCache(true); // Initialize a program manager with the key provider to automatically fetch keys for executions. -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, +); // Build the `Authorization`. const privateKey = new PrivateKey(); // Change this to a private key that has an aleo credit balance. @@ -33,7 +41,7 @@ const baseFeeMicrocredits = await programManager.estimateFeeForAuthorization({ programName: "credits.aleo", authorization, }); -const baseFeeCredits = Number(baseFeeMicrocredits)/1000000; +const baseFeeCredits = Number(baseFeeMicrocredits) / 1000000; console.log("Building fee authorization"); @@ -41,7 +49,7 @@ console.log("Building fee authorization"); const feeAuthorization = await programManager.buildFeeAuthorization({ deploymentOrExecutionId: executionId, baseFeeCredits, - privateKey + privateKey, }); console.log("Executing authorizations"); @@ -58,7 +66,8 @@ await programManager.networkClient.submitTransaction(tx.toString()); // Verify the transaction was successful. setTimeout(async () => { - const transaction = await programManager.networkClient.getTransaction(tx.id()); + const transaction = await programManager.networkClient.getTransaction( + tx.id(), + ); console.log(transaction); }, 10000); - diff --git a/create-leo-app/template-build-and-execute-authorization-ts/tsconfig.json b/create-leo-app/template-build-and-execute-authorization-ts/tsconfig.json index 052bb2cc1..17db7b13e 100644 --- a/create-leo-app/template-build-and-execute-authorization-ts/tsconfig.json +++ b/create-leo-app/template-build-and-execute-authorization-ts/tsconfig.json @@ -1,15 +1,15 @@ { - "compilerOptions": { - /* Basic Options */ - "target": "es2017", - "module": "esnext", + "compilerOptions": { + /* Basic Options */ + "target": "es2017", + "module": "esnext", - /* Module Resolution Options */ - "moduleResolution": "bundler", - "esModuleInterop": true, + /* Module Resolution Options */ + "moduleResolution": "bundler", + "esModuleInterop": true, - /* Advanced Options */ - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - } -} \ No newline at end of file + /* Advanced Options */ + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/create-leo-app/template-devnode-js/README.md b/create-leo-app/template-devnode-js/README.md index f6d94bc5d..b264f02cc 100644 --- a/create-leo-app/template-devnode-js/README.md +++ b/create-leo-app/template-devnode-js/README.md @@ -1,20 +1,30 @@ # Aleo Local Development Server + Node.js ## Initializing a Leo Devnode server -Note: Prior to the release of Leo 4.4.0, the Leo CLI should be installed from the following commit in order to use the Devnode tool: [5baf94e](https://github.com/ProvableHQ/leo/pull/28982/commits/5baf94e491189b89dca7c981c2b79dfc6af1d108) -The Leo Devnode server is designed to enable developers to rapidly iterate on their Aleo program design. Deployment transactions do not require key synthesis or certificate generation and execution transitions do not require proofs. +Note: Prior to the release of Leo 4.4.0, the Leo CLI should be installed from +the following commit in order to use the Devnode tool: +[5baf94e](https://github.com/ProvableHQ/leo/pull/28982/commits/5baf94e491189b89dca7c981c2b79dfc6af1d108) + +The Leo Devnode server is designed to enable developers to rapidly iterate on +their Aleo program design. Deployment transactions do not require key synthesis +or certificate generation and execution transitions do not require proofs. To initialize a Devnode server, run the following command: + ```bash leo devnode start --private-key APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH ``` -with optional `--verbosity` feature flag with settings `0, 1, and 2`. -There is an additional command which lets users "fast-forward" a specified number of blocks: +with optional `--verbosity` feature flag with settings `0, 1, and 2`. + +There is an additional command which lets users "fast-forward" a specified +number of blocks: + ``` leo devnode advance N ``` + for an integer `N`. The default setting will initialize the server to the latest Consensus Version. @@ -22,13 +32,17 @@ The default setting will initialize the server to the latest Consensus Version. To terminate the Devnode, simply use `ctrl + c`. ## Example -The example code in the `index.js` file deploys a sample program to the Devnode, then submits an upgrade transaction which adds a new method to the existing program, and then submits an execution transaction that uses the new method. -The code can be run using: + +The example code in the `index.js` file deploys a sample program to the Devnode, +then submits an upgrade transaction which adds a new method to the existing +program, and then submits an execution transaction that uses the new method. The +code can be run using: + ```bash yarn dev ``` -Note: The Devnode support programs with dependencies, but the SDK's Devnode deploy method does not currently support nested deployments. Each dependency must be deployed independently using the `buildDevnodeDeploymentTransaction` method. - - - +Note: The Devnode support programs with dependencies, but the SDK's Devnode +deploy method does not currently support nested deployments. Each dependency +must be deployed independently using the `buildDevnodeDeploymentTransaction` +method. diff --git a/create-leo-app/template-devnode-js/index.js b/create-leo-app/template-devnode-js/index.js index 3312402e1..475801918 100644 --- a/create-leo-app/template-devnode-js/index.js +++ b/create-leo-app/template-devnode-js/index.js @@ -1,5 +1,12 @@ #!/usr/bin/env ts-node -import { Account, ProgramManager, initThreadPool, NetworkRecordProvider, AleoNetworkClient, getOrInitConsensusVersionTestHeights } from '@provablehq/sdk'; +import { + Account, + ProgramManager, + initThreadPool, + NetworkRecordProvider, + AleoNetworkClient, + getOrInitConsensusVersionTestHeights, +} from "@provablehq/sdk"; const program = `program test_program.aleo; @@ -32,32 +39,44 @@ constructor: async function main() { // Initialize multi-threading to allow WASM execution. await initThreadPool(); - const heights = getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12"); + const heights = getOrInitConsensusVersionTestHeights( + "0,1,2,3,4,5,6,7,8,9,10,11,12", + ); - const privateKey = "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH"; - const account = new Account({privateKey}); + const privateKey = + "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH"; + const account = new Account({ privateKey }); const networkClient = new AleoNetworkClient("http://localhost:3030"); const recordProvider = new NetworkRecordProvider(account, networkClient); - - const programManager = new ProgramManager("http://localhost:3030", recordProvider ); - programManager.setAccount(account); + const programManager = new ProgramManager( + "http://localhost:3030", + recordProvider, + ); + programManager.setAccount(account); - const tx_1 = await programManager.buildDevnodeDeploymentTransaction({program: program, priorityFee: 0, privateFee: false}); + const tx_1 = await programManager.buildDevnodeDeploymentTransaction({ + program: program, + priorityFee: 0, + privateFee: false, + }); console.log("Program deployed - response:", tx_1.toString()); await programManager.networkClient.submitTransaction(tx_1); - const tx_2 = await programManager.buildDevnodeUpgradeTransaction({program: upgraded_program, priorityFee: 0, privateFee: false}); + const tx_2 = await programManager.buildDevnodeUpgradeTransaction({ + program: upgraded_program, + priorityFee: 0, + privateFee: false, + }); await programManager.networkClient.submitTransaction(tx_2); console.log("Program deployed - response:", tx_2); - const tx_3 = await programManager.buildDevnodeExecutionTransaction({ privateKey: account.privateKey(), programName: "test_program.aleo", functionName: "new_transition", privateFee: false, - inputs: ["2u32", "4u32"], + inputs: ["2u32", "4u32"], priorityFee: 0.0, edition: 0, skipProof: true, @@ -66,11 +85,9 @@ async function main() { console.log("Transaction submitted - response:", tx_3.toString()); } - main() - .then(_ => { - }) - .catch(err => { - console.log(err) - process.exit(1) - }); \ No newline at end of file + .then((_) => {}) + .catch((err) => { + console.log(err); + process.exit(1); + }); diff --git a/create-leo-app/template-devnode-js/package.json b/create-leo-app/template-devnode-js/package.json index dd55e0188..440902a8b 100644 --- a/create-leo-app/template-devnode-js/package.json +++ b/create-leo-app/template-devnode-js/package.json @@ -1,12 +1,12 @@ { - "name": "devnode-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "node index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - } + "name": "devnode-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "node index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + } } diff --git a/create-leo-app/template-extension/README.md b/create-leo-app/template-extension/README.md index 158cbc144..8fcde2bf8 100644 --- a/create-leo-app/template-extension/README.md +++ b/create-leo-app/template-extension/README.md @@ -1,4 +1,3 @@ # Chrome Extension + Aleo -> > [!NOTE] -> This is an experimental template not recommended for use \ No newline at end of file +> > [!NOTE] This is an experimental template not recommended for use diff --git a/create-leo-app/template-extension/package.json b/create-leo-app/template-extension/package.json index 695f8ea6e..4c0e262a4 100644 --- a/create-leo-app/template-extension/package.json +++ b/create-leo-app/template-extension/package.json @@ -1,15 +1,15 @@ { - "name": "extension-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "build": "rimraf static/js && rollup --config" - }, - "devDependencies": { - "@provablehq/sdk": "^0.9.18", - "@web/rollup-plugin-import-meta-assets": "^2.2.1", - "rimraf": "^6.0.1", - "rollup": "^4.59.0" - } + "name": "extension-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "rimraf static/js && rollup --config" + }, + "devDependencies": { + "@provablehq/sdk": "^0.9.18", + "@web/rollup-plugin-import-meta-assets": "^2.2.1", + "rimraf": "^6.0.1", + "rollup": "^4.59.0" + } } diff --git a/create-leo-app/template-extension/src/service_worker.js b/create-leo-app/template-extension/src/service_worker.js index 8c1e35045..10a8b5c03 100644 --- a/create-leo-app/template-extension/src/service_worker.js +++ b/create-leo-app/template-extension/src/service_worker.js @@ -3,7 +3,7 @@ async function createOffscreen(path) { const existingContexts = await chrome.runtime.getContexts({ contextTypes: ["OFFSCREEN_DOCUMENT"], - documentUrls: [offscreenUrl] + documentUrls: [offscreenUrl], }); if (existingContexts.length > 0) { @@ -13,7 +13,8 @@ async function createOffscreen(path) { await chrome.offscreen.createDocument({ url: offscreenUrl, reasons: ["WORKERS"], - justification: "Top-level await and Workers cannot be used in service workers, but they are necessary to use the Provable SDK.", + justification: + "Top-level await and Workers cannot be used in service workers, but they are necessary to use the Provable SDK.", }); } diff --git a/create-leo-app/template-extension/src/worker.js b/create-leo-app/template-extension/src/worker.js index 4e0904ee1..852a5821c 100644 --- a/create-leo-app/template-extension/src/worker.js +++ b/create-leo-app/template-extension/src/worker.js @@ -1,15 +1,20 @@ -import {Account, initThreadPool, PrivateKey, ProgramManager,} from "@provablehq/sdk"; +import { + Account, + initThreadPool, + PrivateKey, + ProgramManager, +} from "@provablehq/sdk"; await initThreadPool(); -const hello_hello_program =` +const hello_hello_program = ` program hello_hello.aleo; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; - output r2 as u32.private;` + output r2 as u32.private;`; async function localProgramExecution(program, aleoFunction, inputs) { const programManager = new ProgramManager(); @@ -29,6 +34,9 @@ async function localProgramExecution(program, aleoFunction, inputs) { const start = Date.now(); console.log("Starting execute!"); -const result = await localProgramExecution(hello_hello_program, "hello", ["5u32", "5u32"]); +const result = await localProgramExecution(hello_hello_program, "hello", [ + "5u32", + "5u32", +]); console.log(result); console.log("Execute finished!", Date.now() - start); diff --git a/create-leo-app/template-extension/static/manifest.json b/create-leo-app/template-extension/static/manifest.json index d215c62d3..be7885f6e 100644 --- a/create-leo-app/template-extension/static/manifest.json +++ b/create-leo-app/template-extension/static/manifest.json @@ -1,16 +1,14 @@ { - "manifest_version": 3, - "name": "extension-starter", - "version": "0.1.0", - "description": "Example for using Aleo in a Chrome / Firefox extension", - "background": { - "service_worker": "./js/service_worker.js", - "type": "module" - }, - "permissions": [ - "offscreen" - ], - "content_security_policy": { - "extension_pages": "default-src 'self' 'wasm-unsafe-eval'; connect-src https://s3-us-west-1.amazonaws.com/mainnet.parameters/powers-of-beta-15.usrs.eb7040c" - } + "manifest_version": 3, + "name": "extension-starter", + "version": "0.1.0", + "description": "Example for using Aleo in a Chrome / Firefox extension", + "background": { + "service_worker": "./js/service_worker.js", + "type": "module" + }, + "permissions": ["offscreen"], + "content_security_policy": { + "extension_pages": "default-src 'self' 'wasm-unsafe-eval'; connect-src https://s3-us-west-1.amazonaws.com/mainnet.parameters/powers-of-beta-15.usrs.eb7040c" + } } diff --git a/create-leo-app/template-extension/static/offscreen.html b/create-leo-app/template-extension/static/offscreen.html index b9fbf5a7a..53ca73a8d 100644 --- a/create-leo-app/template-extension/static/offscreen.html +++ b/create-leo-app/template-extension/static/offscreen.html @@ -1,9 +1,9 @@ - + - - - - - - + + + + + + diff --git a/create-leo-app/template-nextjs-ts/README.md b/create-leo-app/template-nextjs-ts/README.md index 2759ae850..a0e27155a 100644 --- a/create-leo-app/template-nextjs-ts/README.md +++ b/create-leo-app/template-nextjs-ts/README.md @@ -16,4 +16,4 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `app/page.tsx`. The page -auto-updates as you edit the file. \ No newline at end of file +auto-updates as you edit the file. diff --git a/create-leo-app/template-nextjs-ts/next.config.js b/create-leo-app/template-nextjs-ts/next.config.js index 9568d4014..3377c38f2 100644 --- a/create-leo-app/template-nextjs-ts/next.config.js +++ b/create-leo-app/template-nextjs-ts/next.config.js @@ -1,32 +1,38 @@ -const TerserPlugin = require("terser-webpack-plugin") +const TerserPlugin = require("terser-webpack-plugin"); /** @type {import('next').NextConfig} */ const nextConfig = { async headers() { return [ { - source: '/:path*', + source: "/:path*", headers: [ { - key: 'Cross-Origin-Opener-Policy', - value: 'same-origin', + key: "Cross-Origin-Opener-Policy", + value: "same-origin", }, { - key: 'Cross-Origin-Embedder-Policy', - value: 'require-corp', + key: "Cross-Origin-Embedder-Policy", + value: "require-corp", }, ], }, - ] + ]; }, webpack: (config) => { config.experiments = { ...config.experiments, topLevelAwait: true }; - config.optimization = { ...config.optimization, minimize: true, minimizer: [new TerserPlugin({ - terserOptions: { - module: true, - } - })], } + config.optimization = { + ...config.optimization, + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + module: true, + }, + }), + ], + }; return config; }, -} +}; -module.exports = nextConfig +module.exports = nextConfig; diff --git a/create-leo-app/template-nextjs-ts/package.json b/create-leo-app/template-nextjs-ts/package.json index 838444f6b..c751c0ec3 100644 --- a/create-leo-app/template-nextjs-ts/package.json +++ b/create-leo-app/template-nextjs-ts/package.json @@ -1,24 +1,24 @@ { - "name": "template-nextjs", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "lint": "next lint" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18", - "next": "15.5.14", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "terser-webpack-plugin": "^5.3.11" - }, - "devDependencies": { - "@types/node": "^22.12.0", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "typescript": "^5.7.3" - } + "name": "template-nextjs", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18", + "next": "15.5.14", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "terser-webpack-plugin": "^5.3.11" + }, + "devDependencies": { + "@types/node": "^22.12.0", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "typescript": "^5.7.3" + } } diff --git a/create-leo-app/template-nextjs-ts/src/app/globals.css b/create-leo-app/template-nextjs-ts/src/app/globals.css index 700756b14..b054f81a4 100644 --- a/create-leo-app/template-nextjs-ts/src/app/globals.css +++ b/create-leo-app/template-nextjs-ts/src/app/globals.css @@ -1,126 +1,129 @@ :root { - --max-width: 1100px; - --border-radius: 12px; - --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', - 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', - 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; + --max-width: 1100px; + --border-radius: 12px; + --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono", + "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro", + "Fira Mono", "Droid Sans Mono", "Courier New", monospace; - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; - --primary-glow: conic-gradient( - from 180deg at 50% 50%, - #16abff33 0deg, - #0885ff33 55deg, - #54d6ff33 120deg, - #0071ff33 160deg, - transparent 360deg - ); - --secondary-glow: radial-gradient( - rgba(255, 255, 255, 1), - rgba(255, 255, 255, 0) - ); + --primary-glow: conic-gradient( + from 180deg at 50% 50%, + #16abff33 0deg, + #0885ff33 55deg, + #54d6ff33 120deg, + #0071ff33 160deg, + transparent 360deg + ); + --secondary-glow: radial-gradient( + rgba(255, 255, 255, 1), + rgba(255, 255, 255, 0) + ); - --tile-start-rgb: 239, 245, 249; - --tile-end-rgb: 228, 232, 233; - --tile-border: conic-gradient( - #00000080, - #00000040, - #00000030, - #00000020, - #00000010, - #00000010, - #00000080 - ); + --tile-start-rgb: 239, 245, 249; + --tile-end-rgb: 228, 232, 233; + --tile-border: conic-gradient( + #00000080, + #00000040, + #00000030, + #00000020, + #00000010, + #00000010, + #00000080 + ); - --callout-rgb: 238, 240, 241; - --callout-border-rgb: 172, 175, 176; - --card-rgb: 180, 185, 188; - --card-border-rgb: 131, 134, 135; + --callout-rgb: 238, 240, 241; + --callout-border-rgb: 172, 175, 176; + --card-rgb: 180, 185, 188; + --card-border-rgb: 131, 134, 135; } button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; } button:hover { - border-color: #646cff; + border-color: #646cff; } button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; - --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); - --secondary-glow: linear-gradient( - to bottom right, - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0.3) - ); + --primary-glow: radial-gradient( + rgba(1, 65, 255, 0.4), + rgba(1, 65, 255, 0) + ); + --secondary-glow: linear-gradient( + to bottom right, + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0.3) + ); - --tile-start-rgb: 2, 13, 46; - --tile-end-rgb: 2, 5, 19; - --tile-border: conic-gradient( - #ffffff80, - #ffffff40, - #ffffff30, - #ffffff20, - #ffffff10, - #ffffff10, - #ffffff80 - ); + --tile-start-rgb: 2, 13, 46; + --tile-end-rgb: 2, 5, 19; + --tile-border: conic-gradient( + #ffffff80, + #ffffff40, + #ffffff30, + #ffffff20, + #ffffff10, + #ffffff10, + #ffffff80 + ); - --callout-rgb: 20, 20, 20; - --callout-border-rgb: 108, 108, 108; - --card-rgb: 100, 100, 100; - --card-border-rgb: 200, 200, 200; - } + --callout-rgb: 20, 20, 20; + --callout-border-rgb: 108, 108, 108; + --card-rgb: 100, 100, 100; + --card-border-rgb: 200, 200, 200; + } } * { - box-sizing: border-box; - padding: 0; - margin: 0; + box-sizing: border-box; + padding: 0; + margin: 0; } html, body { - max-width: 100vw; - overflow-x: hidden; + max-width: 100vw; + overflow-x: hidden; } body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); + color: rgb(var(--foreground-rgb)); + background: linear-gradient( + to bottom, + transparent, + rgb(var(--background-end-rgb)) + ) + rgb(var(--background-start-rgb)); } a { - color: inherit; - text-decoration: none; + color: inherit; + text-decoration: none; } @media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } + html { + color-scheme: dark; + } } diff --git a/create-leo-app/template-nextjs-ts/src/app/layout.tsx b/create-leo-app/template-nextjs-ts/src/app/layout.tsx index ae8456212..ba8e40241 100644 --- a/create-leo-app/template-nextjs-ts/src/app/layout.tsx +++ b/create-leo-app/template-nextjs-ts/src/app/layout.tsx @@ -1,22 +1,22 @@ -import './globals.css' -import type { Metadata } from 'next' -import { Inter } from 'next/font/google' +import "./globals.css"; +import type { Metadata } from "next"; +import { Inter } from "next/font/google"; -const inter = Inter({ subsets: ['latin'] }) +const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { - title: 'Create Next App', - description: 'Generated by create next app', -} + title: "Create Next App", + description: "Generated by create next app", +}; export default function RootLayout({ - children, + children, }: { - children: React.ReactNode + children: React.ReactNode; }) { - return ( - - {children} - - ) + return ( + + {children} + + ); } diff --git a/create-leo-app/template-nextjs-ts/src/app/page.module.css b/create-leo-app/template-nextjs-ts/src/app/page.module.css index 1ceb3b9c9..22bef0fc3 100644 --- a/create-leo-app/template-nextjs-ts/src/app/page.module.css +++ b/create-leo-app/template-nextjs-ts/src/app/page.module.css @@ -1,160 +1,160 @@ .main { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: center; - padding: 15rem; - min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: center; + padding: 15rem; + min-height: 100vh; } .description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - z-index: 2; - font-family: var(--font-mono); + display: inherit; + justify-content: inherit; + align-items: inherit; + font-size: 0.85rem; + max-width: var(--max-width); + z-index: 2; + font-family: var(--font-mono); } .description a { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; } .description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); + position: relative; + margin: 0; + padding: 1rem; + background-color: rgba(var(--callout-rgb), 0.5); + border: 1px solid rgba(var(--callout-border-rgb), 0.3); + border-radius: var(--border-radius); } .code { - font-weight: 700; - font-family: var(--font-mono); + font-weight: 700; + font-family: var(--font-mono); } .center { - display: flex; - justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; + display: flex; + justify-content: center; + align-items: center; + position: relative; + padding: 4rem 0; } .center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; + background: var(--secondary-glow); + border-radius: 50%; + width: 480px; + height: 360px; + margin-left: -400px; } .center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; + background: var(--primary-glow); + width: 240px; + height: 180px; + z-index: -1; } .center::before, .center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); + content: ""; + left: 50%; + position: absolute; + filter: blur(45px); + transform: translateZ(0); } .logo { - position: relative; + position: relative; } /* Mobile */ @media (max-width: 700px) { - .content { - padding: 4rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } + .content { + padding: 4rem; + } + + .center { + padding: 8rem 0 6rem; + } + + .center::before { + transform: none; + height: 300px; + } + + .description { + font-size: 0.8rem; + } + + .description a { + padding: 1rem; + } + + .description p, + .description div { + display: flex; + justify-content: center; + position: fixed; + width: 100%; + } + + .description p { + align-items: center; + inset: 0 0 auto; + padding: 2rem 1rem 1.4rem; + border-radius: 0; + border: none; + border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); + background: linear-gradient( + to bottom, + rgba(var(--background-start-rgb), 1), + rgba(var(--callout-rgb), 0.5) + ); + background-clip: padding-box; + backdrop-filter: blur(24px); + } + + .description div { + align-items: flex-end; + pointer-events: none; + inset: auto 0 0; + padding: 2rem; + height: 200px; + background: linear-gradient( + to bottom, + transparent 0%, + rgb(var(--background-end-rgb)) 40% + ); + z-index: 1; + } } @media (prefers-color-scheme: dark) { - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } + .logo { + filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); + } } @keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } + from { + transform: rotate(360deg); + } + to { + transform: rotate(0deg); + } } .card { - text-align: center; + text-align: center; } .card p { - padding: 1em; + padding: 1em; } diff --git a/create-leo-app/template-nextjs-ts/src/app/page.tsx b/create-leo-app/template-nextjs-ts/src/app/page.tsx index 35aff34ce..8401f6ba9 100644 --- a/create-leo-app/template-nextjs-ts/src/app/page.tsx +++ b/create-leo-app/template-nextjs-ts/src/app/page.tsx @@ -27,7 +27,7 @@ export default function Home() { useEffect(() => { workerRef.current = new Worker(new URL("worker.ts", import.meta.url)); workerRef.current.onmessage = ( - event: MessageEvent + event: MessageEvent, ) => { if (event.data.type === "key") { setAccount(event.data.result); diff --git a/create-leo-app/template-nextjs-ts/src/app/worker.ts b/create-leo-app/template-nextjs-ts/src/app/worker.ts index 87bb908cf..b025f8e29 100644 --- a/create-leo-app/template-nextjs-ts/src/app/worker.ts +++ b/create-leo-app/template-nextjs-ts/src/app/worker.ts @@ -1,47 +1,55 @@ import { - Account, - initThreadPool, - PrivateKey, - ProgramManager, + Account, + initThreadPool, + PrivateKey, + ProgramManager, } from "@provablehq/sdk"; await initThreadPool(); -const hello_hello_program =` +const hello_hello_program = ` program hello_hello.aleo; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; - output r2 as u32.private;` - -async function localProgramExecution(program: string, aleoFunction: string, inputs: string[]) { - const programManager = new ProgramManager(); - - // Create a temporary account for the execution of the program - const account = new Account(); - programManager.setAccount(account); - - const executionResponse = await programManager.run( - program, - aleoFunction, - inputs, - false, - ); - return executionResponse.getOutputs(); + output r2 as u32.private;`; + +async function localProgramExecution( + program: string, + aleoFunction: string, + inputs: string[], +) { + const programManager = new ProgramManager(); + + // Create a temporary account for the execution of the program + const account = new Account(); + programManager.setAccount(account); + + const executionResponse = await programManager.run( + program, + aleoFunction, + inputs, + false, + ); + return executionResponse.getOutputs(); } function getPrivateKey() { - return new PrivateKey().to_string(); + return new PrivateKey().to_string(); } onmessage = async function (e) { - if (e.data === "execute") { - const result = await localProgramExecution(hello_hello_program, "hello", ["5u32", "5u32"]); - postMessage({type: "execute", result: result}); - } else if (e.data === "key") { - const result = getPrivateKey(); - postMessage({type: "key", result: result}); - } + if (e.data === "execute") { + const result = await localProgramExecution( + hello_hello_program, + "hello", + ["5u32", "5u32"], + ); + postMessage({ type: "execute", result: result }); + } else if (e.data === "key") { + const result = getPrivateKey(); + postMessage({ type: "key", result: result }); + } }; diff --git a/create-leo-app/template-nextjs-ts/tsconfig.json b/create-leo-app/template-nextjs-ts/tsconfig.json index 54698a489..39f0ab62b 100644 --- a/create-leo-app/template-nextjs-ts/tsconfig.json +++ b/create-leo-app/template-nextjs-ts/tsconfig.json @@ -1,27 +1,27 @@ { - "compilerOptions": { - "target": "es2017", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "strict": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "incremental": true, - "plugins": [ - { - "name": "next" - } - ], - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "compilerOptions": { + "target": "es2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] } diff --git a/create-leo-app/template-node-credits-aleo-functions-ts/README.md b/create-leo-app/template-node-credits-aleo-functions-ts/README.md index 8001d1e2a..f7d44f5aa 100644 --- a/create-leo-app/template-node-credits-aleo-functions-ts/README.md +++ b/create-leo-app/template-node-credits-aleo-functions-ts/README.md @@ -1,7 +1,7 @@ # Node.js Credits.aleo Functions Example -This example builds execution transactions for all 6 credits.aleo functions using -the ProgramManager: `transfer_public`, `transfer_public_to_private`, +This example builds execution transactions for all 6 credits.aleo functions +using the ProgramManager: `transfer_public`, `transfer_public_to_private`, `transfer_private`, `transfer_private_to_public`, `join`, and `split`. Run all 6 functions: diff --git a/create-leo-app/template-node-credits-aleo-functions-ts/src/index.ts b/create-leo-app/template-node-credits-aleo-functions-ts/src/index.ts index 4d1f103e4..bad9ad3af 100644 --- a/create-leo-app/template-node-credits-aleo-functions-ts/src/index.ts +++ b/create-leo-app/template-node-credits-aleo-functions-ts/src/index.ts @@ -41,7 +41,10 @@ class Credits { * @param account - The Aleo account to use for transactions * @param apiUrl - The API endpoint (defaults to https://api.provable.com/v2) */ - constructor(account: Account, apiUrl: string = "https://api.provable.com/v2") { + constructor( + account: Account, + apiUrl: string = "https://api.provable.com/v2", + ) { this._account = account; this.programManager = new ProgramManager(apiUrl); this.programManager.setAccount(account); @@ -63,7 +66,10 @@ class Credits { /** * Execute a credits.aleo function. */ - private async execute(functionName: string, inputs: string[]): Promise { + private async execute( + functionName: string, + inputs: string[], + ): Promise { const start = Date.now(); console.log(`Starting ${functionName} execution`); @@ -140,8 +146,14 @@ class Credits { * @example * await credits.transferPublicToPrivate("aleo1abc...xyz", 500000); */ - async transferPublicToPrivate(recipient: string, amount: number): Promise { - return this.execute("transfer_public_to_private", [recipient, `${amount}u64`]); + async transferPublicToPrivate( + recipient: string, + amount: number, + ): Promise { + return this.execute("transfer_public_to_private", [ + recipient, + `${amount}u64`, + ]); } /** @@ -155,8 +167,16 @@ class Credits { * @example * await credits.transferPrivate(myRecord, "aleo1abc...xyz", 100000); */ - async transferPrivate(record: string, recipient: string, amount: number): Promise { - return this.execute("transfer_private", [record, recipient, `${amount}u64`]); + async transferPrivate( + record: string, + recipient: string, + amount: number, + ): Promise { + return this.execute("transfer_private", [ + record, + recipient, + `${amount}u64`, + ]); } /** @@ -170,8 +190,16 @@ class Credits { * @example * await credits.transferPrivateToPublic(myRecord, "aleo1abc...xyz", 50000); */ - async transferPrivateToPublic(record: string, recipient: string, amount: number): Promise { - return this.execute("transfer_private_to_public", [record, recipient, `${amount}u64`]); + async transferPrivateToPublic( + record: string, + recipient: string, + amount: number, + ): Promise { + return this.execute("transfer_private_to_public", [ + record, + recipient, + `${amount}u64`, + ]); } /** @@ -216,7 +244,8 @@ const account = Account.fromCiphertext(accountCiphertext, "provablealeo1"); const credits = new Credits(account); // Specify the recipient -const recipient = "aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3"; +const recipient = + "aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3"; // NOTE: These records exist on testnet and are used to build transactions. // Since we only build (not broadcast) the transactions, they remain unspent. @@ -237,9 +266,12 @@ const joinRecord = `{ const functions: Record Promise> = { transfer_public: () => credits.transferPublic(recipient, 50000), - transfer_public_to_private: () => credits.transferPublicToPrivate(recipient, 50000), - transfer_private: () => credits.transferPrivate(sendRecord, recipient, 100000), - transfer_private_to_public: () => credits.transferPrivateToPublic(sendRecord, recipient, 50000), + transfer_public_to_private: () => + credits.transferPublicToPrivate(recipient, 50000), + transfer_private: () => + credits.transferPrivate(sendRecord, recipient, 100000), + transfer_private_to_public: () => + credits.transferPrivateToPublic(sendRecord, recipient, 50000), join: () => credits.join(sendRecord, joinRecord), split: () => credits.split(sendRecord, 250000), }; diff --git a/create-leo-app/template-node-loyalty-program-ts/README.md b/create-leo-app/template-node-loyalty-program-ts/README.md index de506b3f5..ada1796e1 100644 --- a/create-leo-app/template-node-loyalty-program-ts/README.md +++ b/create-leo-app/template-node-loyalty-program-ts/README.md @@ -1,14 +1,15 @@ # Loyalty Program - Node.js Example -A Node.js example demonstrating the Aleo SDK with a multi-program loyalty points system. +A Node.js example demonstrating the Aleo SDK with a multi-program loyalty points +system. ## Features This example showcases: - **Multi-Program Architecture**: Two Leo programs that work together - - `loyalty_token.aleo`: Manages loyalty cards and points - - `loyalty_rewards.aleo`: Handles voucher redemption (imports loyalty_token) + - `loyalty_token.aleo`: Manages loyalty cards and points + - `loyalty_rewards.aleo`: Handles voucher redemption (imports loyalty_token) - **Record Operations**: Minting, consuming, and transferring records - **Hash Functions**: BHP256 for generating unique card/voucher IDs - **Program Imports**: Cross-program execution with imports @@ -74,7 +75,7 @@ const updatedCard = await loyalty.addPoints(card, 500); const { card: newCard, voucher } = await loyalty.redeemForVoucher( updatedCard, RewardType.Discount, - 500 + 500, ); // => { card: LoyaltyCard, voucher: RewardVoucher } @@ -107,18 +108,18 @@ loyalty_rewards.aleo (Imports loyalty_token) ## Tier Thresholds | Tier | Points Required | -|--------|-----------------| +| ------ | --------------- | | Bronze | 0 - 999 | | Silver | 1,000 - 9,999 | | Gold | 10,000+ | ## Reward Types -| Type | Value | Description | -|----------|-------|------------------| -| Discount | 1 | Percentage off | -| Freebie | 2 | Free item | -| Upgrade | 3 | Service upgrade | +| Type | Value | Description | +| -------- | ----- | --------------- | +| Discount | 1 | Percentage off | +| Freebie | 2 | Free item | +| Upgrade | 3 | Service upgrade | ## Learn More diff --git a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/abi.json b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/abi.json index ed3f2b7f8..1d2d311a5 100644 --- a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/abi.json +++ b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/abi.json @@ -1,303 +1,285 @@ { - "program": "loyalty_rewards.aleo", - "structs": [], - "records": [ - { - "path": [ - "RewardVoucher" - ], - "fields": [ + "program": "loyalty_rewards.aleo", + "structs": [], + "records": [ { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "None" - }, - { - "name": "voucher_id", - "ty": { - "Primitive": "Field" - }, - "mode": "None" - }, - { - "name": "reward_type", - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "None" - }, - { - "name": "amount", - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "None" - } - ] - } - ], - "mappings": [ - { - "name": "voucher_exists", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "voucher_used", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "redemptions_by_type", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "approved_upgrades", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - } - ], - "storage_variables": [], - "transitions": [ - { - "name": "approve_upgrade", - "is_async": true, - "inputs": [ - { - "name": "checksum", - "ty": { - "Plaintext": { - "Array": { - "element": { - "Primitive": { - "UInt": "U8" - } + "path": ["RewardVoucher"], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "None" }, - "length": 32 - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": "Future", - "mode": "None" + { + "name": "voucher_id", + "ty": { + "Primitive": "Field" + }, + "mode": "None" + }, + { + "name": "reward_type", + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "None" + }, + { + "name": "amount", + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "None" + } + ] } - ] - }, - { - "name": "redeem_points_for_voucher", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, + ], + "mappings": [ { - "name": "reward_type", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U8" - } + "name": "voucher_exists", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "name": "points_to_spend", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "voucher_used", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" + "name": "redemptions_by_type", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "use_voucher", - "is_async": true, - "inputs": [ - { - "name": "voucher", - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" + "name": "approved_upgrades", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" } - ], - "outputs": [ + ], + "storage_variables": [], + "transitions": [ { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "check_voucher", - "is_async": false, - "inputs": [ - { - "name": "voucher", - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" + "name": "approve_upgrade", + "is_async": true, + "inputs": [ + { + "name": "checksum", + "ty": { + "Plaintext": { + "Array": { + "element": { + "Primitive": { + "UInt": "U8" + } + }, + "length": 32 + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U8" - } - } - }, - "mode": "None" + "name": "redeem_points_for_voucher", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "reward_type", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U8" + } + } + }, + "mode": "None" + }, + { + "name": "points_to_spend", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ] - }, - { - "name": "transfer_voucher", - "is_async": true, - "inputs": [ - { - "name": "voucher", - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" + "name": "use_voucher", + "is_async": true, + "inputs": [ + { + "name": "voucher", + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "name": "new_owner", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" + "name": "check_voucher", + "is_async": false, + "inputs": [ + { + "name": "voucher", + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U8" + } + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" + "name": "transfer_voucher", + "is_async": true, + "inputs": [ + { + "name": "voucher", + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "name": "new_owner", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] } - ] - } - ] -} \ No newline at end of file + ] +} diff --git a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json index 6666de6c5..1de7f4108 100644 --- a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json +++ b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json @@ -1,466 +1,434 @@ { - "program": "loyalty_token.aleo", - "structs": [], - "records": [ - { - "path": [ - "LoyaltyCard" - ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "None" - }, - { - "name": "card_id", - "ty": { - "Primitive": "Field" - }, - "mode": "None" - }, - { - "name": "points", - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "None" - }, - { - "name": "tier", - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "None" - } - ] - } - ], - "mappings": [ - { - "name": "card_exists", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "total_cards", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "total_points_issued", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "approved_upgrades", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - } - ], - "storage_variables": [], - "transitions": [ - { - "name": "approve_upgrade", - "is_async": true, - "inputs": [ - { - "name": "checksum", - "ty": { - "Plaintext": { - "Array": { - "element": { - "Primitive": { - "UInt": "U8" - } + "program": "loyalty_token.aleo", + "structs": [], + "records": [ + { + "path": ["LoyaltyCard"], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "None" }, - "length": 32 - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "mint_card", - "is_async": true, - "inputs": [ - { - "name": "recipient", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - }, - { - "name": "initial_points", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" + { + "name": "card_id", + "ty": { + "Primitive": "Field" + }, + "mode": "None" + }, + { + "name": "points", + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "None" + }, + { + "name": "tier", + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "None" + } + ] } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + ], + "mappings": [ + { + "name": "card_exists", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "add_points", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "total_cards", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" }, { - "name": "points_earned", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } + "name": "total_points_issued", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "check_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "approved_upgrades", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + ], + "storage_variables": [], + "transitions": [ + { + "name": "approve_upgrade", + "is_async": true, + "inputs": [ + { + "name": "checksum", + "ty": { + "Plaintext": { + "Array": { + "element": { + "Primitive": { + "UInt": "U8" + } + }, + "length": 32 + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ] - }, - { - "name": "transfer_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "new_owner", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "mint_card", + "is_async": true, + "inputs": [ + { + "name": "recipient", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + }, + { + "name": "initial_points", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "add_points", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_earned", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card_v2", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "check_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ] }, { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "transfer_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "new_owner", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "spend_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card_v2", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "name": "points_to_spend", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "spend_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_spend", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ] } - ] - } - ] -} \ No newline at end of file + ] +} diff --git a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/program.json b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/program.json index 945f8f429..90526bc7b 100644 --- a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/program.json +++ b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/build/program.json @@ -1,9 +1,9 @@ { - "program": "loyalty_rewards.aleo", - "version": "0.1.0", - "description": "", - "license": "", - "leo": "3.4.0", - "dependencies": null, - "dev_dependencies": null + "program": "loyalty_rewards.aleo", + "version": "0.1.0", + "description": "", + "license": "", + "leo": "3.4.0", + "dependencies": null, + "dev_dependencies": null } diff --git a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/program.json b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/program.json index b67efaed6..16e74e93b 100644 --- a/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/program.json +++ b/create-leo-app/template-node-loyalty-program-ts/loyalty_rewards/program.json @@ -1,15 +1,15 @@ { - "program": "loyalty_rewards.aleo", - "version": "0.0.0", - "description": "Loyalty rewards program for voucher redemption - imports loyalty_token.aleo", - "license": "MIT", - "leo": "3.4.0", - "dependencies": [ - { - "name": "loyalty_token.aleo", - "location": "local", - "path": "../loyalty_token" - } - ], - "dev_dependencies": null + "program": "loyalty_rewards.aleo", + "version": "0.0.0", + "description": "Loyalty rewards program for voucher redemption - imports loyalty_token.aleo", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "loyalty_token.aleo", + "location": "local", + "path": "../loyalty_token" + } + ], + "dev_dependencies": null } diff --git a/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/abi.json b/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/abi.json index 6666de6c5..1de7f4108 100644 --- a/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/abi.json +++ b/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/abi.json @@ -1,466 +1,434 @@ { - "program": "loyalty_token.aleo", - "structs": [], - "records": [ - { - "path": [ - "LoyaltyCard" - ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "None" - }, - { - "name": "card_id", - "ty": { - "Primitive": "Field" - }, - "mode": "None" - }, - { - "name": "points", - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "None" - }, - { - "name": "tier", - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "None" - } - ] - } - ], - "mappings": [ - { - "name": "card_exists", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "total_cards", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "total_points_issued", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "approved_upgrades", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - } - ], - "storage_variables": [], - "transitions": [ - { - "name": "approve_upgrade", - "is_async": true, - "inputs": [ - { - "name": "checksum", - "ty": { - "Plaintext": { - "Array": { - "element": { - "Primitive": { - "UInt": "U8" - } + "program": "loyalty_token.aleo", + "structs": [], + "records": [ + { + "path": ["LoyaltyCard"], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "None" }, - "length": 32 - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "mint_card", - "is_async": true, - "inputs": [ - { - "name": "recipient", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - }, - { - "name": "initial_points", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" + { + "name": "card_id", + "ty": { + "Primitive": "Field" + }, + "mode": "None" + }, + { + "name": "points", + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "None" + }, + { + "name": "tier", + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "None" + } + ] } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + ], + "mappings": [ + { + "name": "card_exists", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "add_points", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "total_cards", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" }, { - "name": "points_earned", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } + "name": "total_points_issued", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "check_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "approved_upgrades", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + ], + "storage_variables": [], + "transitions": [ + { + "name": "approve_upgrade", + "is_async": true, + "inputs": [ + { + "name": "checksum", + "ty": { + "Plaintext": { + "Array": { + "element": { + "Primitive": { + "UInt": "U8" + } + }, + "length": 32 + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ] - }, - { - "name": "transfer_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "new_owner", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "mint_card", + "is_async": true, + "inputs": [ + { + "name": "recipient", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + }, + { + "name": "initial_points", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "add_points", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_earned", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card_v2", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "check_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ] }, { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "transfer_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "new_owner", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "spend_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card_v2", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "name": "points_to_spend", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "spend_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_spend", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ] } - ] - } - ] -} \ No newline at end of file + ] +} diff --git a/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/program.json b/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/program.json index b5da64ffc..bb2838f69 100644 --- a/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/program.json +++ b/create-leo-app/template-node-loyalty-program-ts/loyalty_token/build/program.json @@ -1,9 +1,9 @@ { - "program": "loyalty_token.aleo", - "version": "0.1.0", - "description": "", - "license": "", - "leo": "3.4.0", - "dependencies": null, - "dev_dependencies": null + "program": "loyalty_token.aleo", + "version": "0.1.0", + "description": "", + "license": "", + "leo": "3.4.0", + "dependencies": null, + "dev_dependencies": null } diff --git a/create-leo-app/template-node-loyalty-program-ts/src/index.ts b/create-leo-app/template-node-loyalty-program-ts/src/index.ts index c0fce8013..5e2e68330 100644 --- a/create-leo-app/template-node-loyalty-program-ts/src/index.ts +++ b/create-leo-app/template-node-loyalty-program-ts/src/index.ts @@ -121,61 +121,110 @@ const COLORS = { function logHeader(title: string): void { console.log(`\n${COLORS.cyan}╔${"═".repeat(58)}╗${COLORS.reset}`); - console.log(`${COLORS.cyan}║${COLORS.bright} ${title.padEnd(56)}${COLORS.reset}${COLORS.cyan}║${COLORS.reset}`); + console.log( + `${COLORS.cyan}║${COLORS.bright} ${title.padEnd(56)}${COLORS.reset}${COLORS.cyan}║${COLORS.reset}`, + ); console.log(`${COLORS.cyan}╚${"═".repeat(58)}╝${COLORS.reset}`); } -function logConfig(provingMode: ProvingMode, hasRecordScanner: boolean, dpsPrivacy: boolean, rssPrivacy: boolean): void { - console.log(`${COLORS.dim}┌──────────────────────────────────────────────────────────┐${COLORS.reset}`); - const provingModeStr = provingMode === ProvingMode.Local - ? `${COLORS.green}LOCAL${COLORS.reset}` - : `${COLORS.magenta}DELEGATED${COLORS.reset}${dpsPrivacy ? ` ${COLORS.cyan}(encrypted)${COLORS.reset}` : ''}`; - console.log(`${COLORS.dim}│${COLORS.reset} Proving Mode: ${provingModeStr}`.padEnd(78) + `${COLORS.dim}│${COLORS.reset}`); +function logConfig( + provingMode: ProvingMode, + hasRecordScanner: boolean, + dpsPrivacy: boolean, + rssPrivacy: boolean, +): void { + console.log( + `${COLORS.dim}┌──────────────────────────────────────────────────────────┐${COLORS.reset}`, + ); + const provingModeStr = + provingMode === ProvingMode.Local + ? `${COLORS.green}LOCAL${COLORS.reset}` + : `${COLORS.magenta}DELEGATED${COLORS.reset}${dpsPrivacy ? ` ${COLORS.cyan}(encrypted)${COLORS.reset}` : ""}`; + console.log( + `${COLORS.dim}│${COLORS.reset} Proving Mode: ${provingModeStr}`.padEnd( + 78, + ) + `${COLORS.dim}│${COLORS.reset}`, + ); const scannerStr = hasRecordScanner - ? `${COLORS.magenta}RSS${COLORS.reset}${rssPrivacy ? ` ${COLORS.cyan}(encrypted)${COLORS.reset}` : ''}` + ? `${COLORS.magenta}RSS${COLORS.reset}${rssPrivacy ? ` ${COLORS.cyan}(encrypted)${COLORS.reset}` : ""}` : `${COLORS.yellow}DISABLED${COLORS.reset}`; - console.log(`${COLORS.dim}│${COLORS.reset} Record Scanner: ${scannerStr}`.padEnd(78) + `${COLORS.dim}│${COLORS.reset}`); - console.log(`${COLORS.dim}└──────────────────────────────────────────────────────────┘${COLORS.reset}`); + console.log( + `${COLORS.dim}│${COLORS.reset} Record Scanner: ${scannerStr}`.padEnd( + 78, + ) + `${COLORS.dim}│${COLORS.reset}`, + ); + console.log( + `${COLORS.dim}└──────────────────────────────────────────────────────────┘${COLORS.reset}`, + ); if (provingMode === ProvingMode.Delegated) { - console.log(`${COLORS.yellow}⚠ Note: Delegated proving requires programs to be deployed on-chain.${COLORS.reset}`); - console.log(`${COLORS.yellow} Use ALEO_PROVING_MODE=local for undeployed programs.${COLORS.reset}`); + console.log( + `${COLORS.yellow}⚠ Note: Delegated proving requires programs to be deployed on-chain.${COLORS.reset}`, + ); + console.log( + `${COLORS.yellow} Use ALEO_PROVING_MODE=local for undeployed programs.${COLORS.reset}`, + ); } } function logModeChange(mode: ProvingMode): void { const modeStr = mode === ProvingMode.Local ? "LOCAL" : "DELEGATED"; const color = mode === ProvingMode.Local ? COLORS.green : COLORS.magenta; - console.log(`\n${COLORS.yellow}🔄 Switching to ${color}${modeStr}${COLORS.reset}${COLORS.yellow} proving mode${COLORS.reset}`); + console.log( + `\n${COLORS.yellow}🔄 Switching to ${color}${modeStr}${COLORS.reset}${COLORS.yellow} proving mode${COLORS.reset}`, + ); if (mode === ProvingMode.Delegated) { - console.log(` ${COLORS.dim}└─ Proving requests will be sent to a remote prover${COLORS.reset}`); + console.log( + ` ${COLORS.dim}└─ Proving requests will be sent to a remote prover${COLORS.reset}`, + ); } else { - console.log(` ${COLORS.dim}└─ Proving will be performed locally on this machine${COLORS.reset}`); + console.log( + ` ${COLORS.dim}└─ Proving will be performed locally on this machine${COLORS.reset}`, + ); } } function logOperation(operation: string, mode: ProvingMode): void { const modeStr = mode === ProvingMode.Local ? "LOCAL" : "DELEGATED"; const color = mode === ProvingMode.Local ? COLORS.green : COLORS.magenta; - console.log(`\n${COLORS.blue}⏱ ${operation}${COLORS.reset} ${COLORS.dim}(${color}${modeStr}${COLORS.reset}${COLORS.dim} proving)${COLORS.reset}`); + console.log( + `\n${COLORS.blue}⏱ ${operation}${COLORS.reset} ${COLORS.dim}(${color}${modeStr}${COLORS.reset}${COLORS.dim} proving)${COLORS.reset}`, + ); } -function logOperationComplete(operation: string, durationMs: number, mode: ProvingMode): void { +function logOperationComplete( + operation: string, + durationMs: number, + mode: ProvingMode, +): void { const modeStr = mode === ProvingMode.Local ? "local" : "delegated"; - console.log(` ${COLORS.dim}└─${COLORS.reset} ${COLORS.green}✓${COLORS.reset} ${operation} completed in ${COLORS.bright}${(durationMs / 1000).toFixed(1)}s${COLORS.reset} ${COLORS.dim}(${modeStr})${COLORS.reset}`); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} ${COLORS.green}✓${COLORS.reset} ${operation} completed in ${COLORS.bright}${(durationMs / 1000).toFixed(1)}s${COLORS.reset} ${COLORS.dim}(${modeStr})${COLORS.reset}`, + ); } -function logScanStart(program: string, recordType: string, startHeight: number, endHeight?: number): void { +function logScanStart( + program: string, + recordType: string, + startHeight: number, + endHeight?: number, +): void { const range = endHeight ? `${startHeight}-${endHeight}` : `${startHeight}+`; - console.log(`\n${COLORS.blue}🔍 Scanning for ${COLORS.bright}${recordType}${COLORS.reset}${COLORS.blue} records${COLORS.reset}`); + console.log( + `\n${COLORS.blue}🔍 Scanning for ${COLORS.bright}${recordType}${COLORS.reset}${COLORS.blue} records${COLORS.reset}`, + ); console.log(` ${COLORS.dim}├─ Program: ${program}${COLORS.reset}`); console.log(` ${COLORS.dim}└─ Block range: ${range}${COLORS.reset}`); } function logScanResults(recordType: string, count: number): void { if (count > 0) { - console.log(` ${COLORS.green}✓${COLORS.reset} Found ${COLORS.bright}${count}${COLORS.reset} ${recordType} record${count !== 1 ? "s" : ""}`); + console.log( + ` ${COLORS.green}✓${COLORS.reset} Found ${COLORS.bright}${count}${COLORS.reset} ${recordType} record${count !== 1 ? "s" : ""}`, + ); } else { - console.log(` ${COLORS.yellow}○${COLORS.reset} No ${recordType} records found`); + console.log( + ` ${COLORS.yellow}○${COLORS.reset} No ${recordType} records found`, + ); } } @@ -264,7 +313,10 @@ class LoyaltyProgram { // Configure RecordScanner if URL is provided. if (config?.recordScannerUrl) { const apiKeyConfig = config.recordScannerApiKey?.startsWith("eyJ") - ? { header: "Authorization", value: `Bearer ${config.recordScannerApiKey}` } + ? { + header: "Authorization", + value: `Bearer ${config.recordScannerApiKey}`, + } : config.recordScannerApiKey; this._recordScanner = new RecordScanner({ url: config.recordScannerUrl, @@ -336,7 +388,9 @@ class LoyaltyProgram { */ setRecordScanner(scanner: RecordScanner): void { this._recordScanner = scanner; - console.log(`${COLORS.green}✓${COLORS.reset} Record Scanner configured`); + console.log( + `${COLORS.green}✓${COLORS.reset} Record Scanner configured`, + ); } /** @@ -365,18 +419,34 @@ class LoyaltyProgram { * const cards = await loyalty.findMyCards(0); * console.log(`Found ${cards.length} cards`); */ - async findMyCards(startHeight: number = 0, endHeight?: number): Promise { + async findMyCards( + startHeight: number = 0, + endHeight?: number, + ): Promise { if (!this._recordScanner) { - throw new Error("No record scanner configured. Use setRecordScanner() to configure."); + throw new Error( + "No record scanner configured. Use setRecordScanner() to configure.", + ); } - logScanStart(this.TOKEN_PROGRAM_ID, "LoyaltyCard", startHeight, endHeight); + logScanStart( + this.TOKEN_PROGRAM_ID, + "LoyaltyCard", + startHeight, + endHeight, + ); // Register with the scanner. if (this._rssPrivacy) { - await this._recordScanner.registerEncrypted(this._account.viewKey(), startHeight); + await this._recordScanner.registerEncrypted( + this._account.viewKey(), + startHeight, + ); } else { - await this._recordScanner.register(this._account.viewKey(), startHeight); + await this._recordScanner.register( + this._account.viewKey(), + startHeight, + ); } const records = await this._recordScanner.findRecords({ @@ -391,12 +461,18 @@ class LoyaltyProgram { }); const cards = records - .filter((r) => r.record_name === "LoyaltyCard" && (r.record_plaintext || r.record_ciphertext)) + .filter( + (r) => + r.record_name === "LoyaltyCard" && + (r.record_plaintext || r.record_ciphertext), + ) .map((r) => { if (r.record_plaintext) { return this.parseCard(r.record_plaintext); } - const ciphertext = RecordCiphertext.fromString(r.record_ciphertext!); + const ciphertext = RecordCiphertext.fromString( + r.record_ciphertext!, + ); const plaintext = ciphertext.decrypt(this._account.viewKey()); return this.parseCard(plaintext.toString()); }); @@ -416,18 +492,34 @@ class LoyaltyProgram { * const vouchers = await loyalty.findMyVouchers(0); * console.log(`Found ${vouchers.length} vouchers`); */ - async findMyVouchers(startHeight: number = 0, endHeight?: number): Promise { + async findMyVouchers( + startHeight: number = 0, + endHeight?: number, + ): Promise { if (!this._recordScanner) { - throw new Error("No record scanner configured. Use setRecordScanner() to configure."); + throw new Error( + "No record scanner configured. Use setRecordScanner() to configure.", + ); } - logScanStart(this.REWARDS_PROGRAM_ID, "RewardVoucher", startHeight, endHeight); + logScanStart( + this.REWARDS_PROGRAM_ID, + "RewardVoucher", + startHeight, + endHeight, + ); // Register with the scanner. if (this._rssPrivacy) { - await this._recordScanner.registerEncrypted(this._account.viewKey(), startHeight); + await this._recordScanner.registerEncrypted( + this._account.viewKey(), + startHeight, + ); } else { - await this._recordScanner.register(this._account.viewKey(), startHeight); + await this._recordScanner.register( + this._account.viewKey(), + startHeight, + ); } const records = await this._recordScanner.findRecords({ @@ -442,12 +534,18 @@ class LoyaltyProgram { }); const vouchers = records - .filter((r) => r.record_name === "RewardVoucher" && (r.record_plaintext || r.record_ciphertext)) + .filter( + (r) => + r.record_name === "RewardVoucher" && + (r.record_plaintext || r.record_ciphertext), + ) .map((r) => { if (r.record_plaintext) { return this.parseVoucher(r.record_plaintext); } - const ciphertext = RecordCiphertext.fromString(r.record_ciphertext!); + const ciphertext = RecordCiphertext.fromString( + r.record_ciphertext!, + ); const plaintext = ciphertext.decrypt(this._account.viewKey()); return this.parseVoucher(plaintext.toString()); }); @@ -475,15 +573,20 @@ class LoyaltyProgram { async mintCard( recipient: string, initialPoints: number, - nonce?: string + nonce?: string, ): Promise { - const actualNonce = nonce ?? Math.floor(Math.random() * 1000000000).toString(); - const inputs = [recipient, `${initialPoints}u64`, `${actualNonce}field`]; + const actualNonce = + nonce ?? Math.floor(Math.random() * 1000000000).toString(); + const inputs = [ + recipient, + `${initialPoints}u64`, + `${actualNonce}field`, + ]; const outputs = await this.execute( this.tokenProgram, "mint_card", - inputs + inputs, ); return this.parseCard(outputs[0]); @@ -501,13 +604,16 @@ class LoyaltyProgram { * const updatedCard = await loyalty.addPoints(card, 500); * console.log(`New balance: ${updatedCard.points}, Tier: ${CardTier[updatedCard.tier]}`); */ - async addPoints(card: LoyaltyCard, pointsToAdd: number): Promise { + async addPoints( + card: LoyaltyCard, + pointsToAdd: number, + ): Promise { const inputs = [card.raw, `${pointsToAdd}u64`]; const outputs = await this.execute( this.tokenProgram, "add_points", - inputs + inputs, ); return this.parseCard(outputs[0]); @@ -519,13 +625,15 @@ class LoyaltyProgram { * @param card - The card to check. * @returns The same card (for chaining) and points value. */ - async checkPoints(card: LoyaltyCard): Promise<{ card: LoyaltyCard; points: number }> { + async checkPoints( + card: LoyaltyCard, + ): Promise<{ card: LoyaltyCard; points: number }> { const inputs = [card.raw]; const outputs = await this.execute( this.tokenProgram, "check_points", - inputs + inputs, ); return { @@ -541,13 +649,16 @@ class LoyaltyProgram { * @param newOwner - The address of the new owner. * @returns The transferred card with new owner. */ - async transferCard(card: LoyaltyCard, newOwner: string): Promise { + async transferCard( + card: LoyaltyCard, + newOwner: string, + ): Promise { const inputs = [card.raw, newOwner]; const outputs = await this.execute( this.tokenProgram, "transfer_card", - inputs + inputs, ); return this.parseCard(outputs[0]); @@ -566,10 +677,13 @@ class LoyaltyProgram { * const { keptCard, splitCard } = await loyalty.splitCardV2(card, 3000); * console.log(`Kept: ${keptCard.points}, Split: ${splitCard.points}`); */ - async splitCardV2(card: LoyaltyCard, pointsToKeep: number): Promise { + async splitCardV2( + card: LoyaltyCard, + pointsToKeep: number, + ): Promise { if (pointsToKeep >= card.points) { throw new Error( - `pointsToKeep (${pointsToKeep}) must be less than card points (${card.points})` + `pointsToKeep (${pointsToKeep}) must be less than card points (${card.points})`, ); } @@ -578,7 +692,7 @@ class LoyaltyProgram { const outputs = await this.execute( this.tokenProgram, "split_card_v2", - inputs + inputs, ); return { @@ -611,11 +725,11 @@ class LoyaltyProgram { async redeemForVoucher( card: LoyaltyCard, rewardType: RewardType, - pointsCost: number + pointsCost: number, ): Promise { if (card.points < pointsCost) { throw new Error( - `Insufficient points: have ${card.points}, need ${pointsCost}` + `Insufficient points: have ${card.points}, need ${pointsCost}`, ); } @@ -625,7 +739,7 @@ class LoyaltyProgram { this.rewardsProgram, "redeem_points_for_voucher", inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } + { [this.TOKEN_PROGRAM_ID]: this.tokenProgram }, ); return { @@ -643,12 +757,9 @@ class LoyaltyProgram { async useVoucher(voucher: RewardVoucher): Promise { const inputs = [voucher.raw]; - await this.execute( - this.rewardsProgram, - "use_voucher", - inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } - ); + await this.execute(this.rewardsProgram, "use_voucher", inputs, { + [this.TOKEN_PROGRAM_ID]: this.tokenProgram, + }); } /** @@ -657,16 +768,18 @@ class LoyaltyProgram { * @param voucher - The voucher to check. * @returns The voucher with its type and value. */ - async checkVoucher( - voucher: RewardVoucher - ): Promise<{ voucher: RewardVoucher; rewardType: RewardType; value: number }> { + async checkVoucher(voucher: RewardVoucher): Promise<{ + voucher: RewardVoucher; + rewardType: RewardType; + value: number; + }> { const inputs = [voucher.raw]; const outputs = await this.execute( this.rewardsProgram, "check_voucher", inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } + { [this.TOKEN_PROGRAM_ID]: this.tokenProgram }, ); return { @@ -685,7 +798,7 @@ class LoyaltyProgram { */ async transferVoucher( voucher: RewardVoucher, - newOwner: string + newOwner: string, ): Promise { const inputs = [voucher.raw, newOwner]; @@ -693,7 +806,7 @@ class LoyaltyProgram { this.rewardsProgram, "transfer_voucher", inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } + { [this.TOKEN_PROGRAM_ID]: this.tokenProgram }, ); return this.parseVoucher(outputs[0]); @@ -707,7 +820,7 @@ class LoyaltyProgram { program: string, functionName: string, inputs: string[], - imports?: Record + imports?: Record, ): Promise { logOperation(functionName, this._provingMode); const start = Date.now(); @@ -715,12 +828,26 @@ class LoyaltyProgram { let outputs: string[]; if (this._provingMode === ProvingMode.Delegated) { - outputs = await this.executeDelegated(program, functionName, inputs, imports); + outputs = await this.executeDelegated( + program, + functionName, + inputs, + imports, + ); } else { - outputs = await this.executeLocal(program, functionName, inputs, imports); + outputs = await this.executeLocal( + program, + functionName, + inputs, + imports, + ); } - logOperationComplete(functionName, Date.now() - start, this._provingMode); + logOperationComplete( + functionName, + Date.now() - start, + this._provingMode, + ); return outputs; } @@ -728,25 +855,25 @@ class LoyaltyProgram { program: string, functionName: string, inputs: string[], - imports?: Record + imports?: Record, ): Promise { // Use OfflineQuery with mock state to prevent network lookups for locally-created records. const offlineQuery = new OfflineQuery( 0, - "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" + "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu", ); const executionResponse = await this.programManager.run( program, functionName, inputs, - false, // proveExecution + false, // proveExecution imports, - undefined, // keySearchParams - undefined, // provingKey - undefined, // verifyingKey - undefined, // privateKey - offlineQuery + undefined, // keySearchParams + undefined, // provingKey + undefined, // verifyingKey + undefined, // privateKey + offlineQuery, ); return executionResponse.getOutputs(); @@ -762,19 +889,27 @@ class LoyaltyProgram { const startTime = Date.now(); const shortId = txId.slice(0, 16); - console.log(` ${COLORS.dim}├─ Broadcasting: ${shortId}...${COLORS.reset}`); + console.log( + ` ${COLORS.dim}├─ Broadcasting: ${shortId}...${COLORS.reset}`, + ); // Use explorer API for transaction lookups (not DPS). - const explorerClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); + const explorerClient = new AleoNetworkClient( + "https://api.explorer.provable.com/v1", + ); while (Date.now() - startTime < this.TX_TIMEOUT_MS) { try { const tx = await explorerClient.getTransaction(txId); if (tx) { - const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); + const elapsed = ((Date.now() - startTime) / 1000).toFixed( + 1, + ); // Clear the "Waiting for confirmation..." line before printing process.stdout.write("\r\x1b[K"); - console.log(` ${COLORS.dim}├─ Confirmed in ${elapsed}s${COLORS.reset}`); + console.log( + ` ${COLORS.dim}├─ Confirmed in ${elapsed}s${COLORS.reset}`, + ); return tx; } } catch { @@ -783,24 +918,30 @@ class LoyaltyProgram { // Log progress every poll. const elapsed = Math.round((Date.now() - startTime) / 1000); - process.stdout.write(` ${COLORS.dim}├─ Waiting for confirmation... (${elapsed}s)\r${COLORS.reset}`); + process.stdout.write( + ` ${COLORS.dim}├─ Waiting for confirmation... (${elapsed}s)\r${COLORS.reset}`, + ); - await new Promise(resolve => setTimeout(resolve, this.TX_POLL_INTERVAL_MS)); + await new Promise((resolve) => + setTimeout(resolve, this.TX_POLL_INTERVAL_MS), + ); } - throw new Error(`Transaction ${txId} not confirmed within ${this.TX_TIMEOUT_MS / 1000}s`); + throw new Error( + `Transaction ${txId} not confirmed within ${this.TX_TIMEOUT_MS / 1000}s`, + ); } private async executeDelegated( program: string, functionName: string, inputs: string[], - imports?: Record + imports?: Record, ): Promise { if (!this._dpsUrl) { throw new Error( "Delegated proving requires dpsUrl to be configured. " + - "Pass dpsUrl in the config or use setProvingMode(ProvingMode.Local)." + "Pass dpsUrl in the config or use setProvingMode(ProvingMode.Local).", ); } @@ -825,7 +966,9 @@ class LoyaltyProgram { } if (this._dpsPrivacy) { - console.log(` ${COLORS.dim}├─ Using encrypted DPS flow (TEE-protected)${COLORS.reset}`); + console.log( + ` ${COLORS.dim}├─ Using encrypted DPS flow (TEE-protected)${COLORS.reset}`, + ); } const response = await this.networkClient.submitProvingRequest({ @@ -846,7 +989,13 @@ class LoyaltyProgram { return this.extractOutputsFromTransaction(confirmedTx); } - private extractOutputsFromTransaction(transaction: { execution?: { transitions: Array<{ outputs?: Array<{ type?: string; value?: string }> }> } }): string[] { + private extractOutputsFromTransaction(transaction: { + execution?: { + transitions: Array<{ + outputs?: Array<{ type?: string; value?: string }>; + }>; + }; + }): string[] { // Extract and decrypt record outputs from transaction execution. const outputs: string[] = []; @@ -856,11 +1005,20 @@ class LoyaltyProgram { for (const output of transition.outputs) { if (output.value) { // Check if this is an encrypted record (starts with "record1"). - if (output.type === "record" && output.value.startsWith("record1")) { - const ciphertext = RecordCiphertext.fromString(output.value); + if ( + output.type === "record" && + output.value.startsWith("record1") + ) { + const ciphertext = RecordCiphertext.fromString( + output.value, + ); // Only decrypt records owned by this account (e.g. transfers produce records for other owners). - if (ciphertext.isOwner(this._account.viewKey())) { - const plaintext = ciphertext.decrypt(this._account.viewKey()); + if ( + ciphertext.isOwner(this._account.viewKey()) + ) { + const plaintext = ciphertext.decrypt( + this._account.viewKey(), + ); outputs.push(plaintext.toString()); } } else { @@ -898,7 +1056,7 @@ class LoyaltyProgram { owner: this.cleanAddress(fields.owner), voucherId: this.cleanField(fields.voucher_id), rewardType: this.parseU8(fields.reward_type) as RewardType, - value: this.parseU64(fields.amount), // Leo record uses 'amount' field. + value: this.parseU64(fields.amount), // Leo record uses 'amount' field. raw: recordString, }; } @@ -944,7 +1102,10 @@ const __dirname = dirname(__filename); // Load the Leo programs. const tokenProgramPath = join(__dirname, "../loyalty_token/build/main.aleo"); -const rewardsProgramPath = join(__dirname, "../loyalty_rewards/build/main.aleo"); +const rewardsProgramPath = join( + __dirname, + "../loyalty_rewards/build/main.aleo", +); const tokenProgram = readFileSync(tokenProgramPath, "utf-8").trim(); const rewardsProgram = readFileSync(rewardsProgramPath, "utf-8").trim(); @@ -953,15 +1114,25 @@ const rewardsProgram = readFileSync(rewardsProgramPath, "utf-8").trim(); console.log("\nValidating programs..."); try { const parsedToken = Program.fromString(tokenProgram); - console.log(` ${COLORS.green}✓${COLORS.reset} Token program: ${parsedToken.id()}`); + console.log( + ` ${COLORS.green}✓${COLORS.reset} Token program: ${parsedToken.id()}`, + ); } catch (e) { - console.error(` ${COLORS.yellow}✗${COLORS.reset} Failed to parse token program:`, e); + console.error( + ` ${COLORS.yellow}✗${COLORS.reset} Failed to parse token program:`, + e, + ); } try { const parsedRewards = Program.fromString(rewardsProgram); - console.log(` ${COLORS.green}✓${COLORS.reset} Rewards program: ${parsedRewards.id()}`); + console.log( + ` ${COLORS.green}✓${COLORS.reset} Rewards program: ${parsedRewards.id()}`, + ); } catch (e) { - console.error(` ${COLORS.yellow}✗${COLORS.reset} Failed to parse rewards program:`, e); + console.error( + ` ${COLORS.yellow}✗${COLORS.reset} Failed to parse rewards program:`, + e, + ); } // Read configuration from environment. @@ -976,9 +1147,10 @@ try { // ALEO_RSS_API_KEY - JWT token for RSS authentication (optional if using ALEO_CONSUMER_ID) // ALEO_RSS_PRIVACY - "true" to enable encrypted RSS flow (TEE-protected) const consumerId = process.env.ALEO_CONSUMER_ID; -const provingMode = process.env.ALEO_PROVING_MODE === "delegated" - ? ProvingMode.Delegated - : ProvingMode.Local; +const provingMode = + process.env.ALEO_PROVING_MODE === "delegated" + ? ProvingMode.Delegated + : ProvingMode.Local; const dpsUrl = process.env.ALEO_DPS_URL; const dpsApiKey = process.env.ALEO_DPS_API_KEY; const dpsPrivacy = process.env.ALEO_DPS_PRIVACY === "true"; @@ -1019,28 +1191,44 @@ async function fetchRssJwt(): Promise { if (!response.ok) { const status = response.status; if (status === 401 || status === 403) { - console.error(`${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: Invalid API key`); - console.error(` ${COLORS.dim}Check that ALEO_DPS_API_KEY is correct${COLORS.reset}`); + console.error( + `${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: Invalid API key`, + ); + console.error( + ` ${COLORS.dim}Check that ALEO_DPS_API_KEY is correct${COLORS.reset}`, + ); } else if (status === 404) { - console.error(`${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: Consumer ID not found`); - console.error(` ${COLORS.dim}Check that ALEO_CONSUMER_ID is correct${COLORS.reset}`); + console.error( + `${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: Consumer ID not found`, + ); + console.error( + ` ${COLORS.dim}Check that ALEO_CONSUMER_ID is correct${COLORS.reset}`, + ); } else { - console.error(`${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: HTTP ${status}`); + console.error( + `${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: HTTP ${status}`, + ); } return recordScannerApiKey; } // JWT is returned in the Authorization header as "Bearer ". const authHeader = response.headers.get("authorization"); if (!authHeader) { - console.error(`${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: No token in response`); + console.error( + `${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: No token in response`, + ); return recordScannerApiKey; } const jwt = authHeader.replace(/^Bearer\s+/i, ""); console.log(`${COLORS.green}✓${COLORS.reset} Fetched RSS JWT token`); return jwt; } catch (error) { - console.error(`${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: ${error}`); - console.error(` ${COLORS.dim}Check your network connection${COLORS.reset}`); + console.error( + `${COLORS.yellow}⚠${COLORS.reset} Failed to fetch RSS JWT: ${error}`, + ); + console.error( + ` ${COLORS.dim}Check your network connection${COLORS.reset}`, + ); return recordScannerApiKey; } } @@ -1073,7 +1261,12 @@ const address = account.address().to_string(); // Display configuration. logHeader("Aleo Loyalty Program Demo"); -logConfig(loyalty.provingMode, loyalty.hasRecordScanner, dpsPrivacy, rssPrivacy); +logConfig( + loyalty.provingMode, + loyalty.hasRecordScanner, + dpsPrivacy, + rssPrivacy, +); // Demo functions. const functions: Record Promise> = { @@ -1081,11 +1274,21 @@ const functions: Record Promise> = { mint_card: async () => { logHeader("Minting Loyalty Card"); const card = await loyalty.mintCard(address, 1000); - console.log(`\n${COLORS.green}✓${COLORS.reset} Card minted successfully!`); - console.log(` ${COLORS.dim}├─${COLORS.reset} Owner: ${card.owner.slice(0, 20)}...`); - console.log(` ${COLORS.dim}├─${COLORS.reset} Card ID: ${card.cardId.slice(0, 20)}...`); - console.log(` ${COLORS.dim}├─${COLORS.reset} Points: ${COLORS.bright}${card.points}${COLORS.reset}`); - console.log(` ${COLORS.dim}└─${COLORS.reset} Tier: ${COLORS.bright}${CardTier[card.tier]}${COLORS.reset}`); + console.log( + `\n${COLORS.green}✓${COLORS.reset} Card minted successfully!`, + ); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Owner: ${card.owner.slice(0, 20)}...`, + ); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Card ID: ${card.cardId.slice(0, 20)}...`, + ); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Points: ${COLORS.bright}${card.points}${COLORS.reset}`, + ); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Tier: ${COLORS.bright}${CardTier[card.tier]}${COLORS.reset}`, + ); }, // Add points to a card (mints a new card first for demo). @@ -1093,12 +1296,18 @@ const functions: Record Promise> = { logHeader("Adding Points to Card"); console.log("\nStep 1: Minting initial card with 500 points..."); const card = await loyalty.mintCard(address, 500); - console.log(` ${COLORS.dim}└─${COLORS.reset} Initial points: ${card.points}`); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Initial points: ${card.points}`, + ); console.log("\nStep 2: Adding 600 points..."); const updatedCard = await loyalty.addPoints(card, 600); - console.log(` ${COLORS.dim}├─${COLORS.reset} New points: ${COLORS.bright}${updatedCard.points}${COLORS.reset}`); - console.log(` ${COLORS.dim}└─${COLORS.reset} Tier changed: ${CardTier[card.tier]} → ${COLORS.bright}${CardTier[updatedCard.tier]}${COLORS.reset}`); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} New points: ${COLORS.bright}${updatedCard.points}${COLORS.reset}`, + ); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Tier changed: ${CardTier[card.tier]} → ${COLORS.bright}${CardTier[updatedCard.tier]}${COLORS.reset}`, + ); }, // Redeem points for a voucher. @@ -1111,14 +1320,22 @@ const functions: Record Promise> = { const result = await loyalty.redeemForVoucher( card, RewardType.Discount, - 500 + 500, ); console.log(`\n${COLORS.green}✓${COLORS.reset} Redemption successful!`); - console.log(` ${COLORS.dim}├─${COLORS.reset} Card points remaining: ${COLORS.bright}${result.card.points}${COLORS.reset}`); - console.log(` ${COLORS.dim}├─${COLORS.reset} Voucher type: ${COLORS.bright}${RewardType[result.voucher.rewardType]}${COLORS.reset}`); - console.log(` ${COLORS.dim}├─${COLORS.reset} Voucher value: ${COLORS.bright}${result.voucher.value}${COLORS.reset}`); - console.log(` ${COLORS.dim}└─${COLORS.reset} Voucher ID: ${result.voucher.voucherId.slice(0, 20)}...`); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Card points remaining: ${COLORS.bright}${result.card.points}${COLORS.reset}`, + ); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Voucher type: ${COLORS.bright}${RewardType[result.voucher.rewardType]}${COLORS.reset}`, + ); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Voucher value: ${COLORS.bright}${result.voucher.value}${COLORS.reset}`, + ); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Voucher ID: ${result.voucher.voucherId.slice(0, 20)}...`, + ); }, // Full flow: mint -> add points -> redeem -> use voucher. @@ -1127,55 +1344,83 @@ const functions: Record Promise> = { console.log("\n1. Minting initial card with 100 points..."); let card = await loyalty.mintCard(address, 100); - console.log(` ${COLORS.dim}└─${COLORS.reset} Points: ${card.points}, Tier: ${CardTier[card.tier]}`); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Points: ${card.points}, Tier: ${CardTier[card.tier]}`, + ); console.log("\n2. Adding 900 points (should upgrade to Silver)..."); card = await loyalty.addPoints(card, 900); - console.log(` ${COLORS.dim}└─${COLORS.reset} Points: ${COLORS.bright}${card.points}${COLORS.reset}, Tier: ${COLORS.bright}${CardTier[card.tier]}${COLORS.reset}`); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Points: ${COLORS.bright}${card.points}${COLORS.reset}, Tier: ${COLORS.bright}${CardTier[card.tier]}${COLORS.reset}`, + ); console.log("\n3. Adding 9900 points (should upgrade to Gold)..."); card = await loyalty.addPoints(card, 9900); - console.log(` ${COLORS.dim}└─${COLORS.reset} Points: ${COLORS.bright}${card.points}${COLORS.reset}, Tier: ${COLORS.bright}${CardTier[card.tier]}${COLORS.reset}`); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Points: ${COLORS.bright}${card.points}${COLORS.reset}, Tier: ${COLORS.bright}${CardTier[card.tier]}${COLORS.reset}`, + ); console.log("\n4. Splitting card (keep 3000, split off 7900)..."); const { keptCard, splitCard } = await loyalty.splitCardV2(card, 3000); - console.log(` ${COLORS.dim}├─${COLORS.reset} Kept card: ${COLORS.bright}${keptCard.points}${COLORS.reset} points, Tier: ${COLORS.bright}${CardTier[keptCard.tier]}${COLORS.reset}`); - console.log(` ${COLORS.dim}└─${COLORS.reset} Split card: ${COLORS.bright}${splitCard.points}${COLORS.reset} points, Tier: ${COLORS.bright}${CardTier[splitCard.tier]}${COLORS.reset}`); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Kept card: ${COLORS.bright}${keptCard.points}${COLORS.reset} points, Tier: ${COLORS.bright}${CardTier[keptCard.tier]}${COLORS.reset}`, + ); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Split card: ${COLORS.bright}${splitCard.points}${COLORS.reset} points, Tier: ${COLORS.bright}${CardTier[splitCard.tier]}${COLORS.reset}`, + ); - console.log("\n5. Redeeming 2000 points from kept card for Upgrade voucher..."); + console.log( + "\n5. Redeeming 2000 points from kept card for Upgrade voucher...", + ); const { card: updatedCard, voucher } = await loyalty.redeemForVoucher( keptCard, RewardType.Upgrade, - 2000 + 2000, + ); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Card points remaining: ${COLORS.bright}${updatedCard.points}${COLORS.reset}`, + ); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Voucher: ${RewardType[voucher.rewardType]}, value: ${voucher.value}`, ); - console.log(` ${COLORS.dim}├─${COLORS.reset} Card points remaining: ${COLORS.bright}${updatedCard.points}${COLORS.reset}`); - console.log(` ${COLORS.dim}└─${COLORS.reset} Voucher: ${RewardType[voucher.rewardType]}, value: ${voucher.value}`); console.log("\n6. Using the voucher..."); await loyalty.useVoucher(voucher); - console.log(` ${COLORS.green}✓${COLORS.reset} Voucher consumed successfully!`); + console.log( + ` ${COLORS.green}✓${COLORS.reset} Voucher consumed successfully!`, + ); }, // Demonstrate proving mode switching. demo_modes: async () => { logHeader("Proving Mode Demonstration"); - console.log("\nCurrently using:", loyalty.provingMode.toUpperCase(), "proving"); + console.log( + "\nCurrently using:", + loyalty.provingMode.toUpperCase(), + "proving", + ); // Local mode demo. loyalty.setProvingMode(ProvingMode.Local); console.log("\nMinting card with LOCAL proving..."); const localCard = await loyalty.mintCard(address, 500); - console.log(` ${COLORS.dim}└─${COLORS.reset} Card minted: ${localCard.points} points`); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Card minted: ${localCard.points} points`, + ); // Note: Delegated mode requires DPS configuration. if (dpsUrl) { loyalty.setProvingMode(ProvingMode.Delegated); console.log("\nMinting card with DELEGATED proving..."); const delegatedCard = await loyalty.mintCard(address, 500); - console.log(` ${COLORS.dim}└─${COLORS.reset} Card minted: ${delegatedCard.points} points`); + console.log( + ` ${COLORS.dim}└─${COLORS.reset} Card minted: ${delegatedCard.points} points`, + ); } else { - console.log(`\n${COLORS.yellow}⚠${COLORS.reset} Delegated proving skipped (ALEO_DPS_URL not set)`); + console.log( + `\n${COLORS.yellow}⚠${COLORS.reset} Delegated proving skipped (ALEO_DPS_URL not set)`, + ); } // Reset to original mode. @@ -1187,24 +1432,36 @@ const functions: Record Promise> = { logHeader("Record Scanner Demonstration"); if (!loyalty.hasRecordScanner) { - console.log(`\n${COLORS.yellow}⚠${COLORS.reset} Record Scanner not configured`); - console.log(` ${COLORS.dim}Set ALEO_RSS_URL to enable this feature${COLORS.reset}`); + console.log( + `\n${COLORS.yellow}⚠${COLORS.reset} Record Scanner not configured`, + ); + console.log( + ` ${COLORS.dim}Set ALEO_RSS_URL to enable this feature${COLORS.reset}`, + ); return; } - console.log(`\nScanning from block ${COLORS.bright}${scanStartHeight}${COLORS.reset}...`); - console.log(` ${COLORS.dim}(Set ALEO_SCAN_START_HEIGHT to change)${COLORS.reset}`); + console.log( + `\nScanning from block ${COLORS.bright}${scanStartHeight}${COLORS.reset}...`, + ); + console.log( + ` ${COLORS.dim}(Set ALEO_SCAN_START_HEIGHT to change)${COLORS.reset}`, + ); console.log("\nSearching for your LoyaltyCard records..."); const cards = await loyalty.findMyCards(scanStartHeight); for (const card of cards) { - console.log(` ${COLORS.dim}├─${COLORS.reset} Card: ${card.points} points (${CardTier[card.tier]})`); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Card: ${card.points} points (${CardTier[card.tier]})`, + ); } console.log("\nSearching for your RewardVoucher records..."); const vouchers = await loyalty.findMyVouchers(scanStartHeight); for (const voucher of vouchers) { - console.log(` ${COLORS.dim}├─${COLORS.reset} Voucher: ${RewardType[voucher.rewardType]}, value: ${voucher.value}`); + console.log( + ` ${COLORS.dim}├─${COLORS.reset} Voucher: ${RewardType[voucher.rewardType]}, value: ${voucher.value}`, + ); } }, @@ -1223,12 +1480,18 @@ const functions: Record Promise> = { // Requires: ALEO_DPS_URL, ALEO_DPS_API_KEY delegated: async () => { if (!dpsUrl) { - console.error(`\n${COLORS.yellow}⚠${COLORS.reset} Delegated proving requires ALEO_DPS_URL`); - console.error(` Example: ALEO_DPS_URL=https://api.provable.com/prove/testnet npm run delegated\n`); + console.error( + `\n${COLORS.yellow}⚠${COLORS.reset} Delegated proving requires ALEO_DPS_URL`, + ); + console.error( + ` Example: ALEO_DPS_URL=https://api.provable.com/prove/testnet npm run delegated\n`, + ); process.exit(1); } if (!dpsApiKey) { - console.error(`\n${COLORS.yellow}⚠${COLORS.reset} Delegated proving requires ALEO_DPS_API_KEY`); + console.error( + `\n${COLORS.yellow}⚠${COLORS.reset} Delegated proving requires ALEO_DPS_API_KEY`, + ); console.error(` Get an API key from https://provable.com\n`); process.exit(1); } @@ -1240,13 +1503,21 @@ const functions: Record Promise> = { // Requires: ALEO_RSS_URL + (ALEO_CONSUMER_ID or ALEO_RSS_API_KEY) scanner: async () => { if (!recordScannerUrl) { - console.error(`\n${COLORS.yellow}⚠${COLORS.reset} RSS scanner requires ALEO_RSS_URL`); - console.error(` Example: ALEO_RSS_URL=https://api.provable.com/scanner npm run scanner\n`); + console.error( + `\n${COLORS.yellow}⚠${COLORS.reset} RSS scanner requires ALEO_RSS_URL`, + ); + console.error( + ` Example: ALEO_RSS_URL=https://api.provable.com/scanner npm run scanner\n`, + ); process.exit(1); } if (!recordScannerApiKey && !consumerId) { - console.error(`\n${COLORS.yellow}⚠${COLORS.reset} RSS scanner requires ALEO_CONSUMER_ID or ALEO_RSS_API_KEY`); - console.error(` Set ALEO_CONSUMER_ID to fetch JWT automatically\n`); + console.error( + `\n${COLORS.yellow}⚠${COLORS.reset} RSS scanner requires ALEO_CONSUMER_ID or ALEO_RSS_API_KEY`, + ); + console.error( + ` Set ALEO_CONSUMER_ID to fetch JWT automatically\n`, + ); process.exit(1); } await functions.demo_scanner(); @@ -1262,7 +1533,9 @@ async function main() { process.exit(1); } - const toRun = selected ? { [selected]: functions[selected] } : { full_flow: functions.full_flow }; + const toRun = selected + ? { [selected]: functions[selected] } + : { full_flow: functions.full_flow }; for (const [, fn] of Object.entries(toRun)) { await fn(); diff --git a/create-leo-app/template-node-ts/package.json b/create-leo-app/template-node-ts/package.json index ffb38645f..a32d421c9 100644 --- a/create-leo-app/template-node-ts/package.json +++ b/create-leo-app/template-node-ts/package.json @@ -1,19 +1,19 @@ { - "name": "node-ts-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "build": "rimraf dist/js && rollup --config", - "start": "npm run build && node dist/index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - }, - "devDependencies": { - "rimraf": "^6.0.1", - "rollup": "^4.59.0", - "rollup-plugin-typescript2": "^0.36.0", - "typescript": "^5.7.3" - } + "name": "node-ts-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "rimraf dist/js && rollup --config", + "start": "npm run build && node dist/index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + }, + "devDependencies": { + "rimraf": "^6.0.1", + "rollup": "^4.59.0", + "rollup-plugin-typescript2": "^0.36.0", + "typescript": "^5.7.3" + } } diff --git a/create-leo-app/template-node-ts/rollup.config.js b/create-leo-app/template-node-ts/rollup.config.js index 259a970aa..baeeb52f7 100644 --- a/create-leo-app/template-node-ts/rollup.config.js +++ b/create-leo-app/template-node-ts/rollup.config.js @@ -9,7 +9,7 @@ export default { format: "es", sourcemap: true, }, - external: ['@provablehq/sdk'], + external: ["@provablehq/sdk"], plugins: [ typescript({ tsconfig: "tsconfig.json", diff --git a/create-leo-app/template-node-ts/src/index.ts b/create-leo-app/template-node-ts/src/index.ts index 72e9dfa53..41ab0c140 100644 --- a/create-leo-app/template-node-ts/src/index.ts +++ b/create-leo-app/template-node-ts/src/index.ts @@ -12,7 +12,7 @@ import { await initThreadPool(); function generateHelloHelloSource(programName: string) { - const hello_hello_program =` + const hello_hello_program = ` program ${programName}; function hello: @@ -38,20 +38,33 @@ const keyProvider = new AleoKeyProvider(); keyProvider.useCache(true); programManager.setKeyProvider(keyProvider); -async function localProgramExecution(program: string, programName: string, aleoFunction: string, inputs: string[]) { +async function localProgramExecution( + program: string, + programName: string, + aleoFunction: string, + inputs: string[], +) { // Pre-synthesize the program keys and then cache them in memory using the key provider. try { - const keyPair = await programManager.synthesizeKeys(program, aleoFunction, inputs); - - programManager.keyProvider.cacheKeys(`${programName}:${aleoFunction}`, keyPair); - + const keyPair = await programManager.synthesizeKeys( + program, + aleoFunction, + inputs, + ); + + programManager.keyProvider.cacheKeys( + `${programName}:${aleoFunction}`, + keyPair, + ); } catch (e) { throw new Error(`Failed to synthesize keys: ${e.message}`); } // Specify parameters for the key provider to use search for program keys. In particular specify the cache key // that was used to cache the keys in the previous step. - const keyProviderParams = new AleoKeyProviderParams({cacheKey: `${programName}:${aleoFunction}`}); + const keyProviderParams = new AleoKeyProviderParams({ + cacheKey: `${programName}:${aleoFunction}`, + }); // Execute once using the key provider params defined above. This will use the cached proving keys and make // execution significantly faster. @@ -63,13 +76,16 @@ async function localProgramExecution(program: string, programName: string, aleoF undefined, keyProviderParams, ); - console.log("hello_hello/hello executed - result:", executionResponse.getOutputs()); + console.log( + "hello_hello/hello executed - result:", + executionResponse.getOutputs(), + ); // Verify the execution using the verifying key that was generated earlier. if (programManager.verifyExecution(executionResponse, 9_000_000)) { console.log("hello_hello/hello execution verified!"); } else { - throw("Execution failed verification!"); + throw "Execution failed verification!"; } } @@ -82,31 +98,47 @@ async function run(online: boolean = false) { const inputs = ["5u32", "5u32"]; console.log(""); - console.log("// --- STEP 1: Execute the program offline to test it gives the expected results. --- //"); + console.log( + "// --- STEP 1: Execute the program offline to test it gives the expected results. --- //", + ); // Execute the program locally. console.log(`Executing ${programName}/hello offline`); let start = Date.now(); - const result = await localProgramExecution(hello_hello_program, programName, functionName, inputs); - console.log(`✅ Local execute finished in ${(Date.now() - start)/1000}s`); + const result = await localProgramExecution( + hello_hello_program, + programName, + functionName, + inputs, + ); + console.log(`✅ Local execute finished in ${(Date.now() - start) / 1000}s`); console.log(""); console.log("// --- STEP 2: Build the deployment transaction. --- //"); start = Date.now(); programName = `hello_hello_${Math.floor(Math.random() * 65536)}.aleo`; hello_hello_program = generateHelloHelloSource(programName); - const deploymentTx: Transaction = await programManager.buildDeploymentTransaction( - hello_hello_program, - 0, - false, - ) - console.log(`✅ Deployment transaction built in ${(Date.now() - start)/1000}s`); + const deploymentTx: Transaction = + await programManager.buildDeploymentTransaction( + hello_hello_program, + 0, + false, + ); + console.log( + `✅ Deployment transaction built in ${(Date.now() - start) / 1000}s`, + ); // If the deployment flag is set to true, deploy the program on testnet (requires aleo credits). if (online) { - const txId: string = await programManager.networkClient.submitTransaction(deploymentTx); - const confirmedTx: ConfirmedTransactionJSON = await programManager.networkClient.waitForTransactionConfirmation(txId); + const txId: string = + await programManager.networkClient.submitTransaction(deploymentTx); + const confirmedTx: ConfirmedTransactionJSON = + await programManager.networkClient.waitForTransactionConfirmation( + txId, + ); if (txId === confirmedTx.transaction.id) { - console.log(`Program ${programName} deployed to Aleo Testnet successfully!`); + console.log( + `Program ${programName} deployed to Aleo Testnet successfully!`, + ); } } else { programName = `hello_hello.aleo`; @@ -120,24 +152,31 @@ async function run(online: boolean = false) { // program with the same logic. console.log(`Executing ${programName}/hello online on the aleo network`); start = Date.now(); - const keySearchParams = new AleoKeyProviderParams({cacheKey: `${programName}:${functionName}`}); - const executionTx: Transaction = await programManager.buildExecutionTransaction( - { + const keySearchParams = new AleoKeyProviderParams({ + cacheKey: `${programName}:${functionName}`, + }); + const executionTx: Transaction = + await programManager.buildExecutionTransaction({ programName, functionName, priorityFee: 0, privateFee: false, inputs: inputs, keySearchParams, - program: hello_hello_program - } - ) - console.log(`✅ Online execution of ${programName} built in ${(Date.now() - start)/1000}s`); + program: hello_hello_program, + }); + console.log( + `✅ Online execution of ${programName} built in ${(Date.now() - start) / 1000}s`, + ); // If the online option is specified, submit the transaction to the network. if (online) { - const txId: string = await programManager.networkClient.submitTransaction(executionTx); - const confirmedTx: ConfirmedTransactionJSON = await programManager.networkClient.waitForTransactionConfirmation(txId); + const txId: string = + await programManager.networkClient.submitTransaction(executionTx); + const confirmedTx: ConfirmedTransactionJSON = + await programManager.networkClient.waitForTransactionConfirmation( + txId, + ); if (txId === confirmedTx.transaction.id) { console.log(`Program ${programName}/hello executed successfully!`); } diff --git a/create-leo-app/template-node-ts/tsconfig.json b/create-leo-app/template-node-ts/tsconfig.json index 052bb2cc1..17db7b13e 100644 --- a/create-leo-app/template-node-ts/tsconfig.json +++ b/create-leo-app/template-node-ts/tsconfig.json @@ -1,15 +1,15 @@ { - "compilerOptions": { - /* Basic Options */ - "target": "es2017", - "module": "esnext", + "compilerOptions": { + /* Basic Options */ + "target": "es2017", + "module": "esnext", - /* Module Resolution Options */ - "moduleResolution": "bundler", - "esModuleInterop": true, + /* Module Resolution Options */ + "moduleResolution": "bundler", + "esModuleInterop": true, - /* Advanced Options */ - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - } -} \ No newline at end of file + /* Advanced Options */ + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/create-leo-app/template-node/index.js b/create-leo-app/template-node/index.js index 1dbd129b5..5f849c9ce 100644 --- a/create-leo-app/template-node/index.js +++ b/create-leo-app/template-node/index.js @@ -1,21 +1,33 @@ -import {Account, AleoKeyProvider, AleoKeyProviderParams, initThreadPool, ProgramManager, PrivateKey} from "@provablehq/sdk"; +import { + Account, + AleoKeyProvider, + AleoKeyProviderParams, + initThreadPool, + ProgramManager, + PrivateKey, +} from "@provablehq/sdk"; import * as process from "node:process"; // await initThreadPool(); -const programName = "hello_hello.aleo" +const programName = "hello_hello.aleo"; -const hello_hello_program =` +const hello_hello_program = ` program ${programName}; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; - output r2 as u32.private;` - -async function localProgramExecution(program, programName, functionName, inputs) { + output r2 as u32.private;`; + +async function localProgramExecution( + program, + programName, + functionName, + inputs, +) { const programManager = new ProgramManager(); // Create a temporary account for the execution of the program @@ -29,12 +41,21 @@ async function localProgramExecution(program, programName, functionName, inputs) // Pre-synthesize the program keys and then cache them in memory using key provider. console.log("Synthesizing hello_hello/hello keys"); - const keyPair = await programManager.synthesizeKeys(hello_hello_program, functionName, inputs); - programManager.keyProvider.cacheKeys(`${programName}:${functionName}`, keyPair); + const keyPair = await programManager.synthesizeKeys( + hello_hello_program, + functionName, + inputs, + ); + programManager.keyProvider.cacheKeys( + `${programName}:${functionName}`, + keyPair, + ); // Specify parameters for the key provider to use search for program keys. In particular specify the cache key // that was used to cache the keys in the previous step. - const keyProviderParams = new AleoKeyProviderParams({cacheKey: `${programName}:${functionName}`}); + const keyProviderParams = new AleoKeyProviderParams({ + cacheKey: `${programName}:${functionName}`, + }); // Execute once using the key provider params defined above. This will use the cached proving keys and make // execution significantly faster. @@ -47,13 +68,16 @@ async function localProgramExecution(program, programName, functionName, inputs) undefined, keyProviderParams, ); - console.log("hello_hello/hello executed - result:", executionResponse.getOutputs()); + console.log( + "hello_hello/hello executed - result:", + executionResponse.getOutputs(), + ); // Verify the execution using the verifying key that was generated earlier. if (programManager.verifyExecution(executionResponse, 9_000_000)) { console.log("hello_hello/hello execution verified!"); } else { - throw("Execution failed verification!"); + throw "Execution failed verification!"; } } @@ -63,19 +87,20 @@ async function remoteProgramExecution(programName, functionName, inputs) { const keyProvider = new AleoKeyProvider(); keyProvider.useCache(true); - const programManager = new ProgramManager("http://34.169.215.4:3030", keyProvider); - - const tx = await programManager.buildExecutionTransaction( - { - programName, - functionName, - priorityFee: 0, - privateFee: false, - inputs, - privateKey: PrivateKey.from_string(process.env["PUZZLE_PK"]) - } + const programManager = new ProgramManager( + "http://34.169.215.4:3030", + keyProvider, ); - console.log("Resulting transaction") + + const tx = await programManager.buildExecutionTransaction({ + programName, + functionName, + priorityFee: 0, + privateFee: false, + inputs, + privateKey: PrivateKey.from_string(process.env["PUZZLE_PK"]), + }); + console.log("Resulting transaction"); } // const start = Date.now(); @@ -86,4 +111,4 @@ async function remoteProgramExecution(programName, functionName, inputs) { // console.log("Starting remote execute"); // const auctionTicket = "{\n owner: aleo12a4wll9ax6w5355jph0dr5wt2vla5sss2t4cnch0tc3vzh643v8qcfvc7a.private,\n auction: {\n starting_bid: 1000u64.private,\n name: 35399035103896773146887283777field.private,\n item: {\n id: 2711777270856651361584090827715149911900945757872672230578290769243507539617field.private,\n offchain_data: [\n 988474637487226873250021955104421895943605632761729320744454284792013483886field.private,\n 1018595503607749325560812785092303652308909717269501712857428145700763090203field.private,\n 1866354748676879328546224733327549476838379876255164483333908854380501015560field.private,\n 17649382157field.private\n ]\n }\n },\n auction_id: 4494702806735512695876583707511065751304542460719196393147692059573188109243field.private,\n settings: {\n auction_privacy: 1field.private,\n bid_types_accepted: 2field.private\n },\n _nonce: 3369967065799891136255379173673996324404175734426175774361522329924029135340group.public,\n _version: 0u8.public\n}"; // const privateBid = "{\n owner: aleo12a4wll9ax6w5355jph0dr5wt2vla5sss2t4cnch0tc3vzh643v8qcfvc7a.private,\n bid: {\n amount: 50000u64.private,\n auction_id: 4494702806735512695876583707511065751304542460719196393147692059573188109243field.private,\n bid_public_key: 7957235921075215080384898776027711008106448988910535634014947882222019778701group.private\n },\n bid_id: 7560059211950188901208146469854635725646381155467709838136796808753178551929field.private,\n _nonce: 6603986437928263590097393830337419611438422585243442121397081002092267314422group.public,\n _version: 0u8.public\n}"; -// await remoteProgramExecution("private_auction_test_3.aleo", "select_winner_private", [auctionTicket, privateBid]); \ No newline at end of file +// await remoteProgramExecution("private_auction_test_3.aleo", "select_winner_private", [auctionTicket, privateBid]); diff --git a/create-leo-app/template-node/package.json b/create-leo-app/template-node/package.json index ba43e9fd7..4fadc520a 100644 --- a/create-leo-app/template-node/package.json +++ b/create-leo-app/template-node/package.json @@ -1,12 +1,12 @@ { - "name": "node-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "start": "node index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - } + "name": "node-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + } } diff --git a/create-leo-app/template-offline-public-transaction-ts/README.md b/create-leo-app/template-offline-public-transaction-ts/README.md index 66bd6e32f..4c6cf7866 100644 --- a/create-leo-app/template-offline-public-transaction-ts/README.md +++ b/create-leo-app/template-offline-public-transaction-ts/README.md @@ -1,33 +1,38 @@ -# Offline Transaction Builder +# Offline Transaction Builder ## 1. Overview + ### 1.1 Proving Keys for Zero-Knowledge Function Execution -To achieve zero-knowledge execution, all Aleo functions require a `ProvingKey` and `VerifyingKey` in order to build a -zero-knowledge zk-SNARK proof of execution. If a user does not possess these keys for a function, they are normally -downloaded from the internet when the function is called. + +To achieve zero-knowledge execution, all Aleo functions require a `ProvingKey` +and `VerifyingKey` in order to build a zero-knowledge zk-SNARK proof of +execution. If a user does not possess these keys for a function, they are +normally downloaded from the internet when the function is called. ### 1.2 Key Providers -They `KeyProvider` interface is designed to allow users to provide their own implementations for providing key material -to Aleo function executions. + +They `KeyProvider` interface is designed to allow users to provide their own +implementations for providing key material to Aleo function executions. ### 1.3 Building Transactions Offline -The `OfflineKeyProvider` enables Transaction Building without connection to the internet. +The `OfflineKeyProvider` enables Transaction Building without connection to the +internet. -The `OfflineKeyProvider` and `OfflineSearchParams` are concrete implementations of the `KeyProvider` and `KeySearchParams` -interfaces. They are designed to fetch proving key material for Aleo functions from a local machine instead of contacting -the internet for it. This provides a way to build Aleo execution transactions without being connected to the internet. +The `OfflineKeyProvider` and `OfflineSearchParams` are concrete implementations +of the `KeyProvider` and `KeySearchParams` interfaces. They are designed to +fetch proving key material for Aleo functions from a local machine instead of +contacting the internet for it. This provides a way to build Aleo execution +transactions without being connected to the internet. -This pathway is suitable for use-cases such as hardware wallets or air-gapped machines used -for building secure transactions. +This pathway is suitable for use-cases such as hardware wallets or air-gapped +machines used for building secure transactions. ### 1.4 Transaction Types Several types of transactions can be built and executed using this template: -`bond_public` -`unbond_public` -`claim_unbond_public` +`bond_public` `unbond_public` `claim_unbond_public` ## 2. Usage @@ -35,13 +40,17 @@ Several types of transactions can be built and executed using this template: `npm run dev` -Once this command is run, all proving keys for the `transfer_public`, `bond_public`, `unbond_public`, and -`claim_unbond_public` functions will be downloaded to the `dist/keys` folder. The machine can then be disconnected from -the internet and the `OfflineKeyProvider` will search this directory for the function proving keys when building the -transaction instead of connecting to the internet. Alternatively you can skip the online step entirely by adding the proving key creating this directory manually and -adding the key material yourself. - -Once the keys are downloaded to your local machine, the offline transactions will be built without requiring an internet connection. +Once this command is run, all proving keys for the `transfer_public`, +`bond_public`, `unbond_public`, and `claim_unbond_public` functions will be +downloaded to the `dist/keys` folder. The machine can then be disconnected from +the internet and the `OfflineKeyProvider` will search this directory for the +function proving keys when building the transaction instead of connecting to the +internet. Alternatively you can skip the online step entirely by adding the +proving key creating this directory manually and adding the key material +yourself. + +Once the keys are downloaded to your local machine, the offline transactions +will be built without requiring an internet connection. ## 3. Notes diff --git a/create-leo-app/template-offline-public-transaction-ts/package.json b/create-leo-app/template-offline-public-transaction-ts/package.json index 558359386..e71970545 100644 --- a/create-leo-app/template-offline-public-transaction-ts/package.json +++ b/create-leo-app/template-offline-public-transaction-ts/package.json @@ -1,19 +1,19 @@ { - "name": "node-offline-transaction-example", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "build": "rimraf dist/js && rollup --config", - "dev": "npm run build && node --trace-uncaught dist/index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - }, - "devDependencies": { - "rimraf": "^6.0.1", - "rollup": "^4.59.0", - "rollup-plugin-typescript2": "^0.36.0", - "typescript": "^5.7.3" - } + "name": "node-offline-transaction-example", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "rimraf dist/js && rollup --config", + "dev": "npm run build && node --trace-uncaught dist/index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + }, + "devDependencies": { + "rimraf": "^6.0.1", + "rollup": "^4.59.0", + "rollup-plugin-typescript2": "^0.36.0", + "typescript": "^5.7.3" + } } diff --git a/create-leo-app/template-offline-public-transaction-ts/rollup.config.js b/create-leo-app/template-offline-public-transaction-ts/rollup.config.js index 259a970aa..baeeb52f7 100644 --- a/create-leo-app/template-offline-public-transaction-ts/rollup.config.js +++ b/create-leo-app/template-offline-public-transaction-ts/rollup.config.js @@ -9,7 +9,7 @@ export default { format: "es", sourcemap: true, }, - external: ['@provablehq/sdk'], + external: ["@provablehq/sdk"], plugins: [ typescript({ tsconfig: "tsconfig.json", diff --git a/create-leo-app/template-offline-public-transaction-ts/src/helpers.ts b/create-leo-app/template-offline-public-transaction-ts/src/helpers.ts index 104c196e8..8b0924d72 100644 --- a/create-leo-app/template-offline-public-transaction-ts/src/helpers.ts +++ b/create-leo-app/template-offline-public-transaction-ts/src/helpers.ts @@ -4,7 +4,7 @@ import path from "path"; import { fileURLToPath } from "url"; async function downloadAndSaveKey(keyData, keysDirPath) { - const locatorParts = keyData.locator.split('/'); + const locatorParts = keyData.locator.split("/"); const fileName = locatorParts.pop(); const dirPath = path.join(keysDirPath, ...locatorParts); await fsPromises.mkdir(dirPath, { recursive: true }); @@ -16,7 +16,7 @@ async function downloadAndSaveKey(keyData, keysDirPath) { } catch { const res = await fetch(keyData.prover); const buffer = await res.arrayBuffer(); - writeFileSync(filePath, new Uint8Array(buffer), { flag: 'wx' }); + writeFileSync(filePath, new Uint8Array(buffer), { flag: "wx" }); console.log(`Downloaded ${keyData.locator}.prover to ${filePath}`); return filePath; } @@ -28,11 +28,19 @@ async function preDownloadTransferKeys() { const keysDirPath = path.join(__dirname, "keys"); await fsPromises.mkdir(keysDirPath, { recursive: true }); - for (const keyData of [CREDITS_PROGRAM_KEYS.transfer_public, CREDITS_PROGRAM_KEYS.fee_public, CREDITS_PROGRAM_KEYS.transfer_public_as_signer, CREDITS_PROGRAM_KEYS.inclusion]) { + for (const keyData of [ + CREDITS_PROGRAM_KEYS.transfer_public, + CREDITS_PROGRAM_KEYS.fee_public, + CREDITS_PROGRAM_KEYS.transfer_public_as_signer, + CREDITS_PROGRAM_KEYS.inclusion, + ]) { try { - keyPaths[keyData.locator] = await downloadAndSaveKey(keyData, keysDirPath); + keyPaths[keyData.locator] = await downloadAndSaveKey( + keyData, + keysDirPath, + ); } catch (error) { - throw(`Failed to download ${keyData.locator} - ${error}`); + throw `Failed to download ${keyData.locator} - ${error}`; } } @@ -45,11 +53,20 @@ async function preDownloadBondingKeys() { const keysDirPath = path.join(__dirname, "keys"); await fsPromises.mkdir(keysDirPath, { recursive: true }); - for (const keyData of [CREDITS_PROGRAM_KEYS.bond_public, CREDITS_PROGRAM_KEYS.fee_public, CREDITS_PROGRAM_KEYS.unbond_public, CREDITS_PROGRAM_KEYS.claim_unbond_public, CREDITS_PROGRAM_KEYS.inclusion]) { + for (const keyData of [ + CREDITS_PROGRAM_KEYS.bond_public, + CREDITS_PROGRAM_KEYS.fee_public, + CREDITS_PROGRAM_KEYS.unbond_public, + CREDITS_PROGRAM_KEYS.claim_unbond_public, + CREDITS_PROGRAM_KEYS.inclusion, + ]) { try { - keyPaths[keyData.locator] = await downloadAndSaveKey(keyData, keysDirPath); + keyPaths[keyData.locator] = await downloadAndSaveKey( + keyData, + keysDirPath, + ); } catch (error) { - throw(`Failed to download ${keyData.locator} - ${error}`); + throw `Failed to download ${keyData.locator} - ${error}`; } } @@ -67,4 +84,9 @@ async function getLocalKey(filePath: string): Promise { } } -export { downloadAndSaveKey, getLocalKey, preDownloadBondingKeys, preDownloadTransferKeys }; \ No newline at end of file +export { + downloadAndSaveKey, + getLocalKey, + preDownloadBondingKeys, + preDownloadTransferKeys, +}; diff --git a/create-leo-app/template-offline-public-transaction-ts/src/index.ts b/create-leo-app/template-offline-public-transaction-ts/src/index.ts index b68b19d56..633c00750 100644 --- a/create-leo-app/template-offline-public-transaction-ts/src/index.ts +++ b/create-leo-app/template-offline-public-transaction-ts/src/index.ts @@ -9,14 +9,23 @@ import { OfflineSearchParams, ProvingKey, Transaction, - VerifyingKey + VerifyingKey, } from "@provablehq/sdk"; -import { getLocalKey, preDownloadBondingKeys, preDownloadTransferKeys } from "./helpers"; +import { + getLocalKey, + preDownloadBondingKeys, + preDownloadTransferKeys, +} from "./helpers"; await initThreadPool(); /// Build transfer public transaction without connection to the internet -async function buildTransferPublicTxOffline(recipientAddress: Address, amount: number, latestStateRoot: string, keyPaths: {}): Promise { +async function buildTransferPublicTxOffline( + recipientAddress: Address, + amount: number, + latestStateRoot: string, + keyPaths: {}, +): Promise { // Create an offline program manager const programManager = new ProgramManager(); @@ -26,15 +35,21 @@ async function buildTransferPublicTxOffline(recipientAddress: Address, amount: n // Create the proving keys from the key bytes on the offline machine console.log("Creating proving keys from local key files"); - const feePublicKeyBytes = await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.fee_public.locator]); + const feePublicKeyBytes = await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.fee_public.locator], + ); const feePublicProvingKey = ProvingKey.fromBytes(feePublicKeyBytes); const transferPublicProvingKey = ProvingKey.fromBytes( - await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.transfer_public.locator]) + await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.transfer_public.locator], + ), ); const inclusionKey = ProvingKey.fromBytes( - await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.inclusion.locator]) + await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.inclusion.locator], + ), ); - + // Create an offline key provider console.log("Creating offline key provider"); const offlineKeyProvider = new OfflineKeyProvider(); @@ -43,7 +58,7 @@ async function buildTransferPublicTxOffline(recipientAddress: Address, amount: n // keys into the key manager. console.log("Inserting proving keys into key provider"); offlineKeyProvider.insertFeePublicKeys(feePublicProvingKey); - offlineKeyProvider.insertInclusionKeys(inclusionKey) + offlineKeyProvider.insertInclusionKeys(inclusionKey); try { offlineKeyProvider.insertTransferPublicKeys(transferPublicProvingKey); @@ -79,11 +94,11 @@ async function buildTransferPublicTxOffline(recipientAddress: Address, amount: n /// Build bonding and unbonding transactions without connection to the internet async function buildBondingTxOffline( - validatorAddress: Address, - withdrawalAddress: Address, - amount: number, - latestStateRoot: string, - keyPaths: {} + validatorAddress: Address, + withdrawalAddress: Address, + amount: number, + latestStateRoot: string, + keyPaths: {}, ): Promise { // Create an offline program manager const programManager = new ProgramManager(); @@ -94,15 +109,27 @@ async function buildBondingTxOffline( // Create the proving keys from the key bytes on the offline machine console.log("Creating proving keys from local key files"); - const feePublicKeyBytes = await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.fee_public.locator]); - const bondPublicKeyBytes = await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.bond_public.locator]); - const unbondPublicKeyBytes = await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.unbond_public.locator]); - const claimUnbondPublicKeyBytes = await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.claim_unbond_public.locator]); - const inclusionKeys = await getLocalKey(keyPaths[CREDITS_PROGRAM_KEYS.inclusion.locator]); + const feePublicKeyBytes = await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.fee_public.locator], + ); + const bondPublicKeyBytes = await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.bond_public.locator], + ); + const unbondPublicKeyBytes = await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.unbond_public.locator], + ); + const claimUnbondPublicKeyBytes = await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.claim_unbond_public.locator], + ); + const inclusionKeys = await getLocalKey( + keyPaths[CREDITS_PROGRAM_KEYS.inclusion.locator], + ); const feePublicProvingKey = ProvingKey.fromBytes(feePublicKeyBytes); const bondPublicProvingKey = ProvingKey.fromBytes(bondPublicKeyBytes); const unBondPublicProvingKey = ProvingKey.fromBytes(unbondPublicKeyBytes); - const claimUnbondPublicProvingKey = ProvingKey.fromBytes(claimUnbondPublicKeyBytes); + const claimUnbondPublicProvingKey = ProvingKey.fromBytes( + claimUnbondPublicKeyBytes, + ); const inclusionProvingKey = ProvingKey.fromBytes(inclusionKeys); // Create an offline key provider to fetch keys without connection to the internet @@ -129,40 +156,46 @@ async function buildBondingTxOffline( const bondPublicOptions = { keySearchParams: OfflineSearchParams.bondPublicKeyParams(), - offlineQuery: new OfflineQuery(9233665, latestStateRoot) + offlineQuery: new OfflineQuery(9233665, latestStateRoot), }; console.log("Build bond public transaction"); - const bondTx = await programManager.buildBondPublicTransaction( - validatorAddress.to_string(), - withdrawalAddress.to_string(), - amount, - bondPublicOptions + const bondTx = ( + await programManager.buildBondPublicTransaction( + validatorAddress.to_string(), + withdrawalAddress.to_string(), + amount, + bondPublicOptions, + ) ); console.log("\nbond_public transaction built!\n"); const unbondPublicOptions = { keySearchParams: OfflineSearchParams.unbondPublicKeyParams(), - offlineQuery: new OfflineQuery(9233665, latestStateRoot) + offlineQuery: new OfflineQuery(9233665, latestStateRoot), }; - const unBondTx = await programManager.buildUnbondPublicTransaction( - stakerAddress.to_string(), - amount, - unbondPublicOptions + const unBondTx = ( + await programManager.buildUnbondPublicTransaction( + stakerAddress.to_string(), + amount, + unbondPublicOptions, + ) ); console.log("\nunbond_public transaction built!\n"); console.log("Building a claim_unbond_public transaction offline"); const claimUnbondPublicOptions = { keySearchParams: OfflineSearchParams.claimUnbondPublicKeyParams(), - offlineQuery: new OfflineQuery(9233665, latestStateRoot) + offlineQuery: new OfflineQuery(9233665, latestStateRoot), }; - const claimUnbondTx = await programManager.buildClaimUnbondPublicTransaction( - stakerAddress.to_string(), - claimUnbondPublicOptions + const claimUnbondTx = ( + await programManager.buildClaimUnbondPublicTransaction( + stakerAddress.to_string(), + claimUnbondPublicOptions, + ) ); console.log("\nclaim_unbond_public transaction built!\n"); return [bondTx, unBondTx, claimUnbondTx]; @@ -185,28 +218,56 @@ async function main() { // ------------------OFFLINE COMPONENT--------------------- // (Do this part on an offline machine) // Get the latest state root from an online machine and enter it into an offline machine - const latestStateRoot = "sr18kzmy5fw3rwr8gldzfg2deemvyapgkuahte372shyz7phtwtsypqngg0ml"; + const latestStateRoot = + "sr18kzmy5fw3rwr8gldzfg2deemvyapgkuahte372shyz7phtwtsypqngg0ml"; // // // Build a transfer_public transaction - const transferTx = await buildTransferPublicTxOffline(stakerAddress, 10000, latestStateRoot, transferKeyPaths); + const transferTx = await buildTransferPublicTxOffline( + stakerAddress, + 10000, + latestStateRoot, + transferKeyPaths, + ); console.log("Transfer transaction built offline!"); - console.log(`\n---------------transfer_public transaction---------------\n${transferTx}`); - console.log(`---------------------------------------------------------`); + console.log( + `\n---------------transfer_public transaction---------------\n${transferTx}`, + ); + console.log( + `---------------------------------------------------------`, + ); function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } await sleep(15000); // Build bonding & unbonding transactions - const bondTransactions = await buildBondingTxOffline(validatorAddress, withdrawalAddress, 100, latestStateRoot, bondingKeyPaths); + const bondTransactions = await buildBondingTxOffline( + validatorAddress, + withdrawalAddress, + 100, + latestStateRoot, + bondingKeyPaths, + ); console.log("Bonding transactions built offline!"); - console.log(`\n-----------------bond_public transaction-----------------\n${bondTransactions[0]}`); - console.log(`---------------------------------------------------------`); - console.log(`\n----------------unbond_public transaction:---------------\n${bondTransactions[1]}`); - console.log(`---------------------------------------------------------`); - console.log(`\n-----------------claim_unbond_public transaction:---------------\n${bondTransactions[2]}`); - console.log(`---------------------------------------------------------`); + console.log( + `\n-----------------bond_public transaction-----------------\n${bondTransactions[0]}`, + ); + console.log( + `---------------------------------------------------------`, + ); + console.log( + `\n----------------unbond_public transaction:---------------\n${bondTransactions[1]}`, + ); + console.log( + `---------------------------------------------------------`, + ); + console.log( + `\n-----------------claim_unbond_public transaction:---------------\n${bondTransactions[2]}`, + ); + console.log( + `---------------------------------------------------------`, + ); //--------------------------------------------------------- } catch (e) { console.log(e); @@ -221,4 +282,4 @@ await main(); // ONLINE COMPONENT (Uncomment this part to send the transaction to the Aleo Network on an internet connected machine) // Submit the transaction to the network // const transferTxId = await networkClient.submitTransaction(transferTx); -//--------------------------------------------------------- \ No newline at end of file +//--------------------------------------------------------- diff --git a/create-leo-app/template-offline-public-transaction-ts/tsconfig.json b/create-leo-app/template-offline-public-transaction-ts/tsconfig.json index add1c57a8..0a16a3d1f 100644 --- a/create-leo-app/template-offline-public-transaction-ts/tsconfig.json +++ b/create-leo-app/template-offline-public-transaction-ts/tsconfig.json @@ -1,15 +1,15 @@ { - "compilerOptions": { - /* Basic Options */ - "target": "es2017", - "module": "esnext", + "compilerOptions": { + /* Basic Options */ + "target": "es2017", + "module": "esnext", - /* Module Resolution Options */ - "moduleResolution": "node", - "esModuleInterop": true, + /* Module Resolution Options */ + "moduleResolution": "node", + "esModuleInterop": true, - /* Advanced Options */ - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - } -} \ No newline at end of file + /* Advanced Options */ + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/create-leo-app/template-private-transaction-ts/README.md b/create-leo-app/template-private-transaction-ts/README.md index 7d6c1c3a9..e0b34dca6 100644 --- a/create-leo-app/template-private-transaction-ts/README.md +++ b/create-leo-app/template-private-transaction-ts/README.md @@ -1,15 +1,18 @@ # Node.js Private Transfer Example -This example shows how to build a transfer_private transaction using the ProgramManager. +This example shows how to build a transfer_private transaction using the +ProgramManager. This example can be run with the following #### Yarn + ```bash yarn start ``` #### NPM + ```bash npm start ``` diff --git a/create-leo-app/template-private-transaction-ts/package.json b/create-leo-app/template-private-transaction-ts/package.json index f3f471a2a..e055e6ec5 100644 --- a/create-leo-app/template-private-transaction-ts/package.json +++ b/create-leo-app/template-private-transaction-ts/package.json @@ -1,19 +1,19 @@ { - "name": "node-ts-transfer-private-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "build": "rimraf dist/js && rollup --config", - "start": "npm run build && node dist/index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - }, - "devDependencies": { - "rimraf": "^6.0.1", - "rollup": "^4.59.0", - "rollup-plugin-typescript2": "^0.36.0", - "typescript": "^5.7.3" - } + "name": "node-ts-transfer-private-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "build": "rimraf dist/js && rollup --config", + "start": "npm run build && node dist/index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + }, + "devDependencies": { + "rimraf": "^6.0.1", + "rollup": "^4.59.0", + "rollup-plugin-typescript2": "^0.36.0", + "typescript": "^5.7.3" + } } diff --git a/create-leo-app/template-private-transaction-ts/rollup.config.js b/create-leo-app/template-private-transaction-ts/rollup.config.js index 259a970aa..baeeb52f7 100644 --- a/create-leo-app/template-private-transaction-ts/rollup.config.js +++ b/create-leo-app/template-private-transaction-ts/rollup.config.js @@ -9,7 +9,7 @@ export default { format: "es", sourcemap: true, }, - external: ['@provablehq/sdk'], + external: ["@provablehq/sdk"], plugins: [ typescript({ tsconfig: "tsconfig.json", diff --git a/create-leo-app/template-private-transaction-ts/src/index.ts b/create-leo-app/template-private-transaction-ts/src/index.ts index 1f7652d69..3f2c53ea2 100644 --- a/create-leo-app/template-private-transaction-ts/src/index.ts +++ b/create-leo-app/template-private-transaction-ts/src/index.ts @@ -1,18 +1,27 @@ -import {Account, initThreadPool, ProgramManager, AleoKeyProvider} from "@provablehq/sdk/testnet.js"; +import { + Account, + initThreadPool, + ProgramManager, + AleoKeyProvider, +} from "@provablehq/sdk/testnet.js"; import { CREDITS_PROGRAM_KEYS } from "@provablehq/sdk/testnet.js"; // Initialize the threadpool to speed up proving. await initThreadPool(); // Specify the record to send. -const sendRecord = "{\n owner: aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3.private,\n microcredits: 500000u64.private,\n _nonce: 2128807984625485873765840993868794284062894954530194503954279385341936659546group.public,\n _version: 1u8.public\n}"; +const sendRecord = + "{\n owner: aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3.private,\n microcredits: 500000u64.private,\n _nonce: 2128807984625485873765840993868794284062894954530194503954279385341936659546group.public,\n _version: 1u8.public\n}"; // Specify the fee record to use for the transaction. -const feeRecord = "{\n owner: aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3.private,\n microcredits: 50000u64.private,\n _nonce: 8327477210335641151082470829879168522735279120730137538049818239556464339772group.public,\n _version: 1u8.public\n}"; +const feeRecord = + "{\n owner: aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3.private,\n microcredits: 50000u64.private,\n _nonce: 8327477210335641151082470829879168522735279120730137538049818239556464339772group.public,\n _version: 1u8.public\n}"; // Import the account. -const accountCiphertext = "ciphertext1qvq283j7ujnhz59d4rnu772rfmvf94039x9ekhk2lzuutteqzlghsr3g9824qgw97a79mmdymqdt0ulqdkahq39vnerw2tl7thvvnnunq386jzjnw29e0ghnq7unphgdzw637q3fgvvlkrcywsc5jukkdhss5qq3njp"; +const accountCiphertext = + "ciphertext1qvq283j7ujnhz59d4rnu772rfmvf94039x9ekhk2lzuutteqzlghsr3g9824qgw97a79mmdymqdt0ulqdkahq39vnerw2tl7thvvnnunq386jzjnw29e0ghnq7unphgdzw637q3fgvvlkrcywsc5jukkdhss5qq3njp"; const account = Account.fromCiphertext(accountCiphertext, "provablealeo1"); // Specify the recipient. -const recipient = "aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3"; +const recipient = + "aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3"; // Create a program manager with the account desired. const programManager = new ProgramManager(); @@ -42,6 +51,8 @@ await programManager.buildExecutionTransaction({ privateFee: true, inputs: [sendRecord, recipient, "500000u64"], feeRecord, - keySearchParams: { "cacheKey" : CREDITS_PROGRAM_KEYS.getKey("transfer_private").locator} + keySearchParams: { + cacheKey: CREDITS_PROGRAM_KEYS.getKey("transfer_private").locator, + }, }); console.log(`transfer_private Execute finished in ${Date.now() - start}ms`); diff --git a/create-leo-app/template-private-transaction-ts/tsconfig.json b/create-leo-app/template-private-transaction-ts/tsconfig.json index 052bb2cc1..17db7b13e 100644 --- a/create-leo-app/template-private-transaction-ts/tsconfig.json +++ b/create-leo-app/template-private-transaction-ts/tsconfig.json @@ -1,15 +1,15 @@ { - "compilerOptions": { - /* Basic Options */ - "target": "es2017", - "module": "esnext", + "compilerOptions": { + /* Basic Options */ + "target": "es2017", + "module": "esnext", - /* Module Resolution Options */ - "moduleResolution": "bundler", - "esModuleInterop": true, + /* Module Resolution Options */ + "moduleResolution": "bundler", + "esModuleInterop": true, - /* Advanced Options */ - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true - } -} \ No newline at end of file + /* Advanced Options */ + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + } +} diff --git a/create-leo-app/template-react-credits-aleo-functions-ts/README.md b/create-leo-app/template-react-credits-aleo-functions-ts/README.md index 1c551faf5..57834aee9 100644 --- a/create-leo-app/template-react-credits-aleo-functions-ts/README.md +++ b/create-leo-app/template-react-credits-aleo-functions-ts/README.md @@ -1,7 +1,8 @@ # React Credits.aleo Functions Template This template demonstrates building execution transactions for 6 credits.aleo -functions that demonstrate value transfers and record manipulation functions in a React application. +functions that demonstrate value transfers and record manipulation functions in +a React application. ## Functions Demonstrated @@ -44,7 +45,8 @@ unspent and can be reused. ### Credits Records -Private credits in the credits.aleo program (the official representation of value on the Aleo Network) are stored as Aleo records with this structure: +Private credits in the credits.aleo program (the official representation of +value on the Aleo Network) are stored as Aleo records with this structure: ``` { @@ -64,7 +66,8 @@ microcredits as plain numbers (e.g., `50000`). - Network connectivity is required for this app. - Public function execution takes ~10-15 seconds. -- Private function execution takes ~30-120 seconds (depending on whether keys are locally cached or not). +- Private function execution takes ~30-120 seconds (depending on whether keys + are locally cached or not). - Sample records are pre-filled for testing. - First execution downloads proving keys (~40-100MB). diff --git a/create-leo-app/template-react-credits-aleo-functions-ts/src/App.tsx b/create-leo-app/template-react-credits-aleo-functions-ts/src/App.tsx index 31f3e9c74..ed3e995cc 100644 --- a/create-leo-app/template-react-credits-aleo-functions-ts/src/App.tsx +++ b/create-leo-app/template-react-credits-aleo-functions-ts/src/App.tsx @@ -198,7 +198,9 @@ function App() { type="text" value={publicAmount} onChange={(e) => - setPublicAmount(Number(e.target.value) || 0) + setPublicAmount( + Number(e.target.value) || 0, + ) } placeholder="50000" /> @@ -260,7 +262,9 @@ function App() { type="text" value={pubToPrivAmount} onChange={(e) => - setPubToPrivAmount(Number(e.target.value) || 0) + setPubToPrivAmount( + Number(e.target.value) || 0, + ) } placeholder="50000" /> @@ -343,7 +347,9 @@ function App() { type="text" value={privateAmount} onChange={(e) => - setPrivateAmount(Number(e.target.value) || 0) + setPrivateAmount( + Number(e.target.value) || 0, + ) } placeholder="50000" /> @@ -416,7 +422,9 @@ function App() { type="text" value={privToPubAmount} onChange={(e) => - setPrivToPubAmount(Number(e.target.value) || 0) + setPrivToPubAmount( + Number(e.target.value) || 0, + ) } placeholder="50000" /> @@ -547,7 +555,9 @@ function App() { type="text" value={splitAmount} onChange={(e) => - setSplitAmount(Number(e.target.value) || 0) + setSplitAmount( + Number(e.target.value) || 0, + ) } placeholder="250000" /> diff --git a/create-leo-app/template-react-credits-aleo-functions-ts/src/workers/worker.ts b/create-leo-app/template-react-credits-aleo-functions-ts/src/workers/worker.ts index 50e87f372..e8f9237e1 100644 --- a/create-leo-app/template-react-credits-aleo-functions-ts/src/workers/worker.ts +++ b/create-leo-app/template-react-credits-aleo-functions-ts/src/workers/worker.ts @@ -29,7 +29,10 @@ class Credits { private creditsProgram: string; private _account: Account; - constructor(account: Account, apiUrl: string = "https://api.provable.com/v2") { + constructor( + account: Account, + apiUrl: string = "https://api.provable.com/v2", + ) { this._account = account; this.programManager = new ProgramManager(apiUrl); this.programManager.setAccount(account); @@ -48,7 +51,10 @@ class Credits { /** * Execute a credits.aleo function. */ - private async execute(functionName: string, inputs: string[]): Promise { + private async execute( + functionName: string, + inputs: string[], + ): Promise { const keyParams = new AleoKeyProviderParams({ cacheKey: CREDITS_PROGRAM_KEYS.getKey(functionName).locator, }); @@ -95,16 +101,38 @@ class Credits { return this.execute("transfer_public", [recipient, `${amount}u64`]); } - async transferPublicToPrivate(recipient: string, amount: number): Promise { - return this.execute("transfer_public_to_private", [recipient, `${amount}u64`]); + async transferPublicToPrivate( + recipient: string, + amount: number, + ): Promise { + return this.execute("transfer_public_to_private", [ + recipient, + `${amount}u64`, + ]); } - async transferPrivate(record: string, recipient: string, amount: number): Promise { - return this.execute("transfer_private", [record, recipient, `${amount}u64`]); + async transferPrivate( + record: string, + recipient: string, + amount: number, + ): Promise { + return this.execute("transfer_private", [ + record, + recipient, + `${amount}u64`, + ]); } - async transferPrivateToPublic(record: string, recipient: string, amount: number): Promise { - return this.execute("transfer_private_to_public", [record, recipient, `${amount}u64`]); + async transferPrivateToPublic( + record: string, + recipient: string, + amount: number, + ): Promise { + return this.execute("transfer_private_to_public", [ + record, + recipient, + `${amount}u64`, + ]); } async join(record1: string, record2: string): Promise { @@ -137,12 +165,13 @@ const workerMethods = { credits.transferPublicToPrivate(recipient, amount), transferPrivate: (record: string, recipient: string, amount: number) => credits.transferPrivate(record, recipient, amount), - transferPrivateToPublic: (record: string, recipient: string, amount: number) => - credits.transferPrivateToPublic(record, recipient, amount), - join: (record1: string, record2: string) => - credits.join(record1, record2), - split: (record: string, amount: number) => - credits.split(record, amount), + transferPrivateToPublic: ( + record: string, + recipient: string, + amount: number, + ) => credits.transferPrivateToPublic(record, recipient, amount), + join: (record1: string, record2: string) => credits.join(record1, record2), + split: (record: string, amount: number) => credits.split(record, amount), getPrivateKey, }; diff --git a/create-leo-app/template-react-leo/.eslintrc.cjs b/create-leo-app/template-react-leo/.eslintrc.cjs index 4dcb43901..619e7374d 100644 --- a/create-leo-app/template-react-leo/.eslintrc.cjs +++ b/create-leo-app/template-react-leo/.eslintrc.cjs @@ -1,20 +1,20 @@ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", ], - }, -} + ignorePatterns: ["dist", ".eslintrc.cjs"], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, +}; diff --git a/create-leo-app/template-react-leo/README.md b/create-leo-app/template-react-leo/README.md index f1964bffb..71b5cb9a3 100644 --- a/create-leo-app/template-react-leo/README.md +++ b/create-leo-app/template-react-leo/README.md @@ -2,7 +2,8 @@ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/ProvableHQ/sdk/tree/mainnet/create-leo-app/template-react-leo) -This template provides a minimal setup to get React and Aleo working in Webpack or Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React and Aleo working in Webpack +or Vite with HMR and some ESLint rules. This template includes a Leo program that is loaded by the web app located in the `helloworld` directory. @@ -20,43 +21,50 @@ Your app should be running on http://localhost:5173/ 1. Copy the `helloworld/.env.example` to `helloworld/.env` (this will be ignored by Git): - ```bash - cd helloworld - cp .env.example .env - ``` + ```bash + cd helloworld + cp .env.example .env + ``` 2. Replace `PRIVATE_KEY=user1PrivateKey` in the `.env` with your own key (you - can use an existing one or generate your own at https://provable.tools/account) + can use an existing one or generate your own at + https://provable.tools/account) 3. Follow instructions to install Leo here: https://github.com/ProvableHQ/leo -4. You can edit `helloworld/src/main.leo` and run `leo run` to compile and update the - Aleo instructions under `build` which are loaded by the web app. +4. You can edit `helloworld/src/main.leo` and run `leo run` to compile and + update the Aleo instructions under `build` which are loaded by the web app. ## Deploy program from web app -> [!WARNING] -> This is for demonstration purposes or local testing only, in production applications you -> should avoid building a public facing web app with private key information +> [!WARNING] This is for demonstration purposes or local testing only, in +> production applications you should avoid building a public facing web app with +> private key information -Information on generating a private key, seeding a wallet with funds, and finding a spendable record can be found here -if you are unfamiliar: https://docs.leo-lang.org/testnet/getting_started/deploy_execute_demo +Information on generating a private key, seeding a wallet with funds, and +finding a spendable record can be found here if you are unfamiliar: +https://docs.leo-lang.org/testnet/getting_started/deploy_execute_demo -Aleo programs deployed require unique names, make sure to edit the program's name to something unique in `helloworld/src/main.leo`, `helloworld/program.json`, rename `helloworld/inputs/helloworld.in` and rebuild. +Aleo programs deployed require unique names, make sure to edit the program's +name to something unique in `helloworld/src/main.leo`, +`helloworld/program.json`, rename `helloworld/inputs/helloworld.in` and rebuild. 1. In the `worker.js` file modify the privateKey to be an account with available funds - ```js - // Use existing account with funds - const account = new Account({ - privateKey: "user1PrivateKey", - }); - ``` + ```js + // Use existing account with funds + const account = new Account({ + privateKey: "user1PrivateKey", + }); + ``` -2. (Optional) Provide a fee record manually (located in commented code within `worker.js`) +2. (Optional) Provide a fee record manually (located in commented code within + `worker.js`) - If you do not provide a manual fee record, the SDK will attempt to scan for a record starting at the latest block. A simple way to speed this up would be to make a public transaction to this account right before deploying. + If you do not provide a manual fee record, the SDK will attempt to scan for + a record starting at the latest block. A simple way to speed this up would + be to make a public transaction to this account right before deploying. 3. Run the web app and hit the deploy button diff --git a/create-leo-app/template-react-leo/helloworld/README.md b/create-leo-app/template-react-leo/helloworld/README.md index 2456ef654..94d976167 100644 --- a/create-leo-app/template-react-leo/helloworld/README.md +++ b/create-leo-app/template-react-leo/helloworld/README.md @@ -3,11 +3,13 @@ ## Build Guide To compile this Aleo program, run: + ```bash snarkvm build ``` To execute this Aleo program, run: + ```bash snarkvm run hello ``` diff --git a/create-leo-app/template-react-leo/index.html b/create-leo-app/template-react-leo/index.html index 57aa8244e..2711a8db3 100644 --- a/create-leo-app/template-react-leo/index.html +++ b/create-leo-app/template-react-leo/index.html @@ -1,88 +1,88 @@ - - - - - Aleo + React - - - -
-
-
-
-
-
- - + @keyframes sk-bounce { + 0%, + 100% { + transform: scale(0); + -webkit-transform: scale(0); + } + 50% { + transform: scale(1); + -webkit-transform: scale(1); + } + } + + + +
+
+
+
+
+
+ + diff --git a/create-leo-app/template-react-leo/package.json b/create-leo-app/template-react-leo/package.json index 49f021c6c..90704e237 100644 --- a/create-leo-app/template-react-leo/package.json +++ b/create-leo-app/template-react-leo/package.json @@ -1,44 +1,44 @@ { - "name": "aleo-react-leo-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "webpack --config webpack.config.js", - "build:vite": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "install-leo": "./install.sh" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18", - "comlink": "^4.4.2", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@babel/core": "^7.26.7", - "@babel/preset-env": "^7.26.7", - "@babel/preset-react": "^7.26.3", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "@vitejs/plugin-react": "^4.3.4", - "babel-loader": "^9.2.1", - "copy-webpack-plugin": "^12.0.2", - "css-loader": "^7.1.2", - "eslint": "^9.19.0", - "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.18", - "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.6.3", - "raw-loader": "^4.0.2", - "style-loader": "^4.0.0", - "svg-url-loader": "^8.0.0", - "vite": "^6.3.6", - "webpack": "^5.97.1", - "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.0" - } + "name": "aleo-react-leo-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "webpack --config webpack.config.js", + "build:vite": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "install-leo": "./install.sh" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18", + "comlink": "^4.4.2", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@babel/core": "^7.26.7", + "@babel/preset-env": "^7.26.7", + "@babel/preset-react": "^7.26.3", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "babel-loader": "^9.2.1", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^7.1.2", + "eslint": "^9.19.0", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.18", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.6.3", + "raw-loader": "^4.0.2", + "style-loader": "^4.0.0", + "svg-url-loader": "^8.0.0", + "vite": "^6.3.6", + "webpack": "^5.97.1", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.0" + } } diff --git a/create-leo-app/template-react-leo/src/App.css b/create-leo-app/template-react-leo/src/App.css index b9d355df2..f44fb79ad 100644 --- a/create-leo-app/template-react-leo/src/App.css +++ b/create-leo-app/template-react-leo/src/App.css @@ -1,42 +1,42 @@ #root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; } .logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; } .logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); + filter: drop-shadow(0 0 2em #646cffaa); } .logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); + filter: drop-shadow(0 0 2em #61dafbaa); } @keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } } .card { - padding: 2em; + padding: 2em; } .read-the-docs { - color: #888; + color: #888; } diff --git a/create-leo-app/template-react-leo/src/App.jsx b/create-leo-app/template-react-leo/src/App.jsx index 62af591e8..564e4ebd2 100644 --- a/create-leo-app/template-react-leo/src/App.jsx +++ b/create-leo-app/template-react-leo/src/App.jsx @@ -7,97 +7,101 @@ import { AleoWorker } from "./workers/AleoWorker.js"; const aleoWorker = AleoWorker(); function App() { - const [count, setCount] = useState(0); - const [account, setAccount] = useState(null); - const [executing, setExecuting] = useState(false); - const [deploying, setDeploying] = useState(false); + const [count, setCount] = useState(0); + const [account, setAccount] = useState(null); + const [executing, setExecuting] = useState(false); + const [deploying, setDeploying] = useState(false); - const generateAccount = async () => { - const key = await aleoWorker.getPrivateKey(); - setAccount(await key.to_string()); - }; + const generateAccount = async () => { + const key = await aleoWorker.getPrivateKey(); + setAccount(await key.to_string()); + }; - async function execute() { - setExecuting(true); - const result = await aleoWorker.localProgramExecution( - helloworld_program, - "main", - ["5u32", "5u32"], - ); - setExecuting(false); + async function execute() { + setExecuting(true); + const result = await aleoWorker.localProgramExecution( + helloworld_program, + "main", + ["5u32", "5u32"], + ); + setExecuting(false); - alert(JSON.stringify(result)); - } + alert(JSON.stringify(result)); + } - async function deploy() { - setDeploying(true); - try { - const result = await aleoWorker.deployProgram(helloworld_program); - console.log("Transaction:") - console.log("https://explorer.provable.com/transaction/" + result) - alert("Transaction ID: " + result); - } catch (e) { - console.log(e) - alert("Error with deployment, please check console for details"); + async function deploy() { + setDeploying(true); + try { + const result = await aleoWorker.deployProgram(helloworld_program); + console.log("Transaction:"); + console.log("https://explorer.provable.com/transaction/" + result); + alert("Transaction ID: " + result); + } catch (e) { + console.log(e); + alert("Error with deployment, please check console for details"); + } + setDeploying(false); } - setDeploying(false); - } - return ( - <> - -

Aleo + React

-
- -

- -

-

- -

-

- Edit src/App.jsx and save to test HMR -

-
+ return ( + <> + +

Aleo + React

+
+ +

+ +

+

+ +

+

+ Edit src/App.jsx and save to test HMR +

+
- {/* Advanced Section */} -
-

Advanced Actions

-

- Deployment on Aleo requires certain prerequisites like seeding your - wallet with credits and retrieving a fee record. Check README for more - details. -

-

- -

-
-

- Click on the Aleo and React logos to learn more -

- - ); + {/* Advanced Section */} +
+

Advanced Actions

+

+ Deployment on Aleo requires certain prerequisites like + seeding your wallet with credits and retrieving a fee + record. Check README for more details. +

+

+ +

+
+

+ Click on the Aleo and React logos to learn more +

+ + ); } export default App; diff --git a/create-leo-app/template-react-leo/src/index.css b/create-leo-app/template-react-leo/src/index.css index 2c3fac689..8da4c0ffa 100644 --- a/create-leo-app/template-react-leo/src/index.css +++ b/create-leo-app/template-react-leo/src/index.css @@ -1,69 +1,69 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } a:hover { - color: #535bf2; + color: #535bf2; } body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; } h1 { - font-size: 3.2em; - line-height: 1.1; + font-size: 3.2em; + line-height: 1.1; } button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; } button:hover { - border-color: #646cff; + border-color: #646cff; } button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } } diff --git a/create-leo-app/template-react-leo/src/main.jsx b/create-leo-app/template-react-leo/src/main.jsx index 1a70efb28..e259b09b7 100644 --- a/create-leo-app/template-react-leo/src/main.jsx +++ b/create-leo-app/template-react-leo/src/main.jsx @@ -4,7 +4,7 @@ import App from "./App.jsx"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root")).render( - - - , + + + , ); diff --git a/create-leo-app/template-react-leo/src/workers/AleoWorker.js b/create-leo-app/template-react-leo/src/workers/AleoWorker.js index 2ed06307b..12a36c76e 100644 --- a/create-leo-app/template-react-leo/src/workers/AleoWorker.js +++ b/create-leo-app/template-react-leo/src/workers/AleoWorker.js @@ -8,7 +8,7 @@ const AleoWorker = () => { type: "module", }); - worker.onerror = function(event) { + worker.onerror = function (event) { console.error("Error in worker: " + event?.message); }; @@ -17,4 +17,4 @@ const AleoWorker = () => { return singletonWorker; }; -export { AleoWorker }; \ No newline at end of file +export { AleoWorker }; diff --git a/create-leo-app/template-react-leo/src/workers/worker.js b/create-leo-app/template-react-leo/src/workers/worker.js index 544ed7d53..874688788 100644 --- a/create-leo-app/template-react-leo/src/workers/worker.js +++ b/create-leo-app/template-react-leo/src/workers/worker.js @@ -1,71 +1,71 @@ import { - Account, - ProgramManager, - PrivateKey, - initThreadPool, - AleoKeyProvider, - AleoNetworkClient, - NetworkRecordProvider, + Account, + ProgramManager, + PrivateKey, + initThreadPool, + AleoKeyProvider, + AleoNetworkClient, + NetworkRecordProvider, } from "@provablehq/sdk"; import { expose, proxy } from "comlink"; await initThreadPool(); async function localProgramExecution(program, aleoFunction, inputs) { - const programManager = new ProgramManager(); - - // Create a temporary account for the execution of the program - const account = new Account(); - programManager.setAccount(account); - - const executionResponse = await programManager.run( - program, - aleoFunction, - inputs, - false, - ); - return executionResponse.getOutputs(); + const programManager = new ProgramManager(); + + // Create a temporary account for the execution of the program + const account = new Account(); + programManager.setAccount(account); + + const executionResponse = await programManager.run( + program, + aleoFunction, + inputs, + false, + ); + return executionResponse.getOutputs(); } async function getPrivateKey() { - const key = new PrivateKey(); - return proxy(key); + const key = new PrivateKey(); + return proxy(key); } async function deployProgram(program) { - const keyProvider = new AleoKeyProvider(); - keyProvider.useCache(true); + const keyProvider = new AleoKeyProvider(); + keyProvider.useCache(true); - // Create a record provider that will be used to find records and transaction data for Aleo programs - const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); + // Create a record provider that will be used to find records and transaction data for Aleo programs + const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); - // Use existing account with funds - const account = new Account({ - privateKey: "user1PrivateKey", - }); + // Use existing account with funds + const account = new Account({ + privateKey: "user1PrivateKey", + }); - const recordProvider = new NetworkRecordProvider(account, networkClient); + const recordProvider = new NetworkRecordProvider(account, networkClient); - // Initialize a program manager to talk to the Aleo network with the configured key and record providers - const programManager = new ProgramManager( - "https://api.provable.com/v2", - keyProvider, - recordProvider, - ); + // Initialize a program manager to talk to the Aleo network with the configured key and record providers + const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, + ); - programManager.setAccount(account); + programManager.setAccount(account); - // Define a fee to pay to deploy the program - const fee = 1.9; // 1.9 Aleo credits + // Define a fee to pay to deploy the program + const fee = 1.9; // 1.9 Aleo credits - // Deploy the program to the Aleo network - const tx_id = await programManager.deploy(program, fee); + // Deploy the program to the Aleo network + const tx_id = await programManager.deploy(program, fee); - // Optional: Pass in fee record manually to avoid long scan times - // const feeRecord = "{ owner: aleo1xxx...xxx.private, microcredits: 2000000u64.private, _nonce: 123...789group.public}"; - // const tx_id = await programManager.deploy(program, fee, undefined, feeRecord); + // Optional: Pass in fee record manually to avoid long scan times + // const feeRecord = "{ owner: aleo1xxx...xxx.private, microcredits: 2000000u64.private, _nonce: 123...789group.public}"; + // const tx_id = await programManager.deploy(program, fee, undefined, feeRecord); - return tx_id; + return tx_id; } const workerMethods = { localProgramExecution, getPrivateKey, deployProgram }; diff --git a/create-leo-app/template-react-leo/vite.config.js b/create-leo-app/template-react-leo/vite.config.js index 9c0aa4959..7a0b0e1c0 100644 --- a/create-leo-app/template-react-leo/vite.config.js +++ b/create-leo-app/template-react-leo/vite.config.js @@ -3,15 +3,15 @@ import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], - assetsInclude: ['**/*.wasm'], - optimizeDeps: { - exclude: ["@provablehq/wasm"], - }, - server: { - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + plugins: [react()], + assetsInclude: ["**/*.wasm"], + optimizeDeps: { + exclude: ["@provablehq/wasm"], + }, + server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, }, - }, }); diff --git a/create-leo-app/template-react-leo/webpack.config.js b/create-leo-app/template-react-leo/webpack.config.js index 236e019db..1eb2f1f83 100644 --- a/create-leo-app/template-react-leo/webpack.config.js +++ b/create-leo-app/template-react-leo/webpack.config.js @@ -5,98 +5,103 @@ import HtmlWebpackPlugin from "html-webpack-plugin"; import path from "path"; const appConfig = { - mode: "production", - entry: { - index: "./src/main.jsx", - }, - output: { - path: path.resolve("dist"), - filename: "[name].bundle.js", - }, - resolve: { - extensions: [".js", ".wasm", ".jsx"], - }, - devServer: { - port: 3000, - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + mode: "production", + entry: { + index: "./src/main.jsx", }, - client: { - overlay: false, + output: { + path: path.resolve("dist"), + filename: "[name].bundle.js", }, - }, - module: { - rules: [ - { - test: /\.(js|jsx)$/, - exclude: /nodeModules/, - use: { - loader: "babel-loader", + resolve: { + extensions: [".js", ".wasm", ".jsx"], + }, + devServer: { + port: 3000, + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, + client: { + overlay: false, }, - }, - { - test: /\.css$/, - use: ["style-loader", "css-loader"], - }, - { - test: /\.(png|jpe?g|gif)$/i, - use: [ - { - loader: "file-loader", - options: { - name: "[path][name].[ext]", + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, + exclude: /nodeModules/, + use: { + loader: "babel-loader", + }, }, - }, - ], - }, - { - test: /\.svg$/, - use: [ - { - loader: "svg-url-loader", - options: { - limit: 8192, - noquotes: true, + { + test: /\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: "file-loader", + options: { + name: "[path][name].[ext]", + }, + }, + ], + }, + { + test: /\.svg$/, + use: [ + { + loader: "svg-url-loader", + options: { + limit: 8192, + noquotes: true, + }, + }, + ], + }, + { + test: /\.aleo$/i, + use: "raw-loader", }, - }, ], - }, - { - test: /\.aleo$/i, - use: 'raw-loader', - }, + }, + plugins: [ + new CopyPlugin({ + patterns: [ + { from: "public", to: "public" }, + { from: "_headers", to: "." }, + ], + }), + new HtmlWebpackPlugin({ + template: "./index.html", + }), ], - }, - plugins: [ - new CopyPlugin({ - patterns: [{ from: "public", to: "public" }, { from: "_headers", to: "." }], - }), - new HtmlWebpackPlugin({ - template: "./index.html", - }), - ], - performance: { - hints: false, - maxAssetSize: 13 * 1024 * 1024, // 12 MiB - maxEntrypointSize: 13 * 1024 * 1024, // 12 MiB - }, - optimization: { - minimize: true, - minimizer: [new TerserPlugin({ - terserOptions: { - module: true, - } - })], - }, - stats: { - warnings: false, - }, - experiments: { - asyncWebAssembly: true, - topLevelAwait: true, - }, - devtool: "source-map", + performance: { + hints: false, + maxAssetSize: 13 * 1024 * 1024, // 12 MiB + maxEntrypointSize: 13 * 1024 * 1024, // 12 MiB + }, + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + module: true, + }, + }), + ], + }, + stats: { + warnings: false, + }, + experiments: { + asyncWebAssembly: true, + topLevelAwait: true, + }, + devtool: "source-map", }; export default [appConfig]; diff --git a/create-leo-app/template-react-loyalty-program-ts/README.md b/create-leo-app/template-react-loyalty-program-ts/README.md index c38d27779..5e0d6d317 100644 --- a/create-leo-app/template-react-loyalty-program-ts/README.md +++ b/create-leo-app/template-react-loyalty-program-ts/README.md @@ -34,7 +34,8 @@ Voucher redemption system (imports loyalty_token): Records: `RewardVoucher { owner, voucher_id, reward_type, value }` -Mappings: `voucher_exists`, `voucher_used`, `redemptions_by_type`, `rewards_catalog` +Mappings: `voucher_exists`, `voucher_used`, `redemptions_by_type`, +`rewards_catalog` ## Getting Started @@ -72,28 +73,32 @@ npm run build:vite ## SDK Features Demonstrated 1. **Record Operations** - - Creating records with `cast` - - Consuming records as inputs - - Multiple record outputs + + - Creating records with `cast` + - Consuming records as inputs + - Multiple record outputs 2. **Hash Functions** - - `BHP256::hash_to_field` for unique ID generation - - Adds proving complexity for parameter download testing + + - `BHP256::hash_to_field` for unique ID generation + - Adds proving complexity for parameter download testing 3. **Mapping Operations** - - `Mapping::set` - Write to mappings - - `Mapping::get` - Read from mappings - - `Mapping::get_or_use` - Read with default value - - `Mapping::contains` - Check key existence + + - `Mapping::set` - Write to mappings + - `Mapping::get` - Read from mappings + - `Mapping::get_or_use` - Read with default value + - `Mapping::contains` - Check key existence 4. **Multi-Program** - - Import statements - - Cross-program record types - - Calling functions from imported programs + - Import statements + - Cross-program record types + - Calling functions from imported programs ## Tier System Points determine card tier: + - **Bronze**: 0-999 points - **Silver**: 1,000-9,999 points - **Gold**: 10,000+ points diff --git a/create-leo-app/template-react-loyalty-program-ts/index.html b/create-leo-app/template-react-loyalty-program-ts/index.html index 6d903f687..7857aae5e 100644 --- a/create-leo-app/template-react-loyalty-program-ts/index.html +++ b/create-leo-app/template-react-loyalty-program-ts/index.html @@ -1,13 +1,13 @@ - - - - - Aleo Loyalty Program Demo - - -
- - + + + + + Aleo Loyalty Program Demo + + +
+ + diff --git a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/abi.json b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/abi.json index ed3f2b7f8..1d2d311a5 100644 --- a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/abi.json +++ b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/abi.json @@ -1,303 +1,285 @@ { - "program": "loyalty_rewards.aleo", - "structs": [], - "records": [ - { - "path": [ - "RewardVoucher" - ], - "fields": [ + "program": "loyalty_rewards.aleo", + "structs": [], + "records": [ { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "None" - }, - { - "name": "voucher_id", - "ty": { - "Primitive": "Field" - }, - "mode": "None" - }, - { - "name": "reward_type", - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "None" - }, - { - "name": "amount", - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "None" - } - ] - } - ], - "mappings": [ - { - "name": "voucher_exists", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "voucher_used", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "redemptions_by_type", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "approved_upgrades", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - } - ], - "storage_variables": [], - "transitions": [ - { - "name": "approve_upgrade", - "is_async": true, - "inputs": [ - { - "name": "checksum", - "ty": { - "Plaintext": { - "Array": { - "element": { - "Primitive": { - "UInt": "U8" - } + "path": ["RewardVoucher"], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "None" }, - "length": 32 - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": "Future", - "mode": "None" + { + "name": "voucher_id", + "ty": { + "Primitive": "Field" + }, + "mode": "None" + }, + { + "name": "reward_type", + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "None" + }, + { + "name": "amount", + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "None" + } + ] } - ] - }, - { - "name": "redeem_points_for_voucher", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, + ], + "mappings": [ { - "name": "reward_type", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U8" - } + "name": "voucher_exists", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "name": "points_to_spend", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "voucher_used", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" + "name": "redemptions_by_type", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "use_voucher", - "is_async": true, - "inputs": [ - { - "name": "voucher", - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" + "name": "approved_upgrades", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" } - ], - "outputs": [ + ], + "storage_variables": [], + "transitions": [ { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "check_voucher", - "is_async": false, - "inputs": [ - { - "name": "voucher", - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" + "name": "approve_upgrade", + "is_async": true, + "inputs": [ + { + "name": "checksum", + "ty": { + "Plaintext": { + "Array": { + "element": { + "Primitive": { + "UInt": "U8" + } + }, + "length": 32 + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U8" - } - } - }, - "mode": "None" + "name": "redeem_points_for_voucher", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "reward_type", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U8" + } + } + }, + "mode": "None" + }, + { + "name": "points_to_spend", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ] - }, - { - "name": "transfer_voucher", - "is_async": true, - "inputs": [ - { - "name": "voucher", - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" + "name": "use_voucher", + "is_async": true, + "inputs": [ + { + "name": "voucher", + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "name": "new_owner", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "RewardVoucher" - ], - "program": "loyalty_rewards" - } - }, - "mode": "None" + "name": "check_voucher", + "is_async": false, + "inputs": [ + { + "name": "voucher", + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U8" + } + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" + "name": "transfer_voucher", + "is_async": true, + "inputs": [ + { + "name": "voucher", + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "name": "new_owner", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["RewardVoucher"], + "program": "loyalty_rewards" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] } - ] - } - ] -} \ No newline at end of file + ] +} diff --git a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json index 6666de6c5..1de7f4108 100644 --- a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json +++ b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/imports/loyalty_token.abi.json @@ -1,466 +1,434 @@ { - "program": "loyalty_token.aleo", - "structs": [], - "records": [ - { - "path": [ - "LoyaltyCard" - ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "None" - }, - { - "name": "card_id", - "ty": { - "Primitive": "Field" - }, - "mode": "None" - }, - { - "name": "points", - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "None" - }, - { - "name": "tier", - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "None" - } - ] - } - ], - "mappings": [ - { - "name": "card_exists", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "total_cards", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "total_points_issued", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "approved_upgrades", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - } - ], - "storage_variables": [], - "transitions": [ - { - "name": "approve_upgrade", - "is_async": true, - "inputs": [ - { - "name": "checksum", - "ty": { - "Plaintext": { - "Array": { - "element": { - "Primitive": { - "UInt": "U8" - } + "program": "loyalty_token.aleo", + "structs": [], + "records": [ + { + "path": ["LoyaltyCard"], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "None" }, - "length": 32 - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "mint_card", - "is_async": true, - "inputs": [ - { - "name": "recipient", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - }, - { - "name": "initial_points", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" + { + "name": "card_id", + "ty": { + "Primitive": "Field" + }, + "mode": "None" + }, + { + "name": "points", + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "None" + }, + { + "name": "tier", + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "None" + } + ] } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + ], + "mappings": [ + { + "name": "card_exists", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "add_points", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "total_cards", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" }, { - "name": "points_earned", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } + "name": "total_points_issued", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "check_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "approved_upgrades", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + ], + "storage_variables": [], + "transitions": [ + { + "name": "approve_upgrade", + "is_async": true, + "inputs": [ + { + "name": "checksum", + "ty": { + "Plaintext": { + "Array": { + "element": { + "Primitive": { + "UInt": "U8" + } + }, + "length": 32 + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ] - }, - { - "name": "transfer_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "new_owner", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "mint_card", + "is_async": true, + "inputs": [ + { + "name": "recipient", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + }, + { + "name": "initial_points", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "add_points", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_earned", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card_v2", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "check_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ] }, { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "transfer_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "new_owner", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "spend_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card_v2", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "name": "points_to_spend", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "spend_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_spend", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ] } - ] - } - ] -} \ No newline at end of file + ] +} diff --git a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/program.json b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/program.json index 945f8f429..90526bc7b 100644 --- a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/program.json +++ b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/build/program.json @@ -1,9 +1,9 @@ { - "program": "loyalty_rewards.aleo", - "version": "0.1.0", - "description": "", - "license": "", - "leo": "3.4.0", - "dependencies": null, - "dev_dependencies": null + "program": "loyalty_rewards.aleo", + "version": "0.1.0", + "description": "", + "license": "", + "leo": "3.4.0", + "dependencies": null, + "dev_dependencies": null } diff --git a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/program.json b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/program.json index 4b5770591..0a4f9ee47 100644 --- a/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/program.json +++ b/create-leo-app/template-react-loyalty-program-ts/loyalty_rewards/program.json @@ -1,16 +1,16 @@ { - "program": "loyalty_rewards.aleo", - "version": "0.0.0", - "description": "Loyalty rewards program for voucher redemption - imports loyalty_token.aleo", - "license": "MIT", - "leo": "3.4.0", - "dependencies": [ - { - "name": "loyalty_token.aleo", - "location": "local", - "path": "../loyalty_token", - "edition": null - } - ], - "dev_dependencies": null + "program": "loyalty_rewards.aleo", + "version": "0.0.0", + "description": "Loyalty rewards program for voucher redemption - imports loyalty_token.aleo", + "license": "MIT", + "leo": "3.4.0", + "dependencies": [ + { + "name": "loyalty_token.aleo", + "location": "local", + "path": "../loyalty_token", + "edition": null + } + ], + "dev_dependencies": null } diff --git a/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/abi.json b/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/abi.json index 6666de6c5..1de7f4108 100644 --- a/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/abi.json +++ b/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/abi.json @@ -1,466 +1,434 @@ { - "program": "loyalty_token.aleo", - "structs": [], - "records": [ - { - "path": [ - "LoyaltyCard" - ], - "fields": [ - { - "name": "owner", - "ty": { - "Primitive": "Address" - }, - "mode": "None" - }, - { - "name": "card_id", - "ty": { - "Primitive": "Field" - }, - "mode": "None" - }, - { - "name": "points", - "ty": { - "Primitive": { - "UInt": "U64" - } - }, - "mode": "None" - }, - { - "name": "tier", - "ty": { - "Primitive": { - "UInt": "U8" - } - }, - "mode": "None" - } - ] - } - ], - "mappings": [ - { - "name": "card_exists", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - }, - { - "name": "total_cards", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "total_points_issued", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": { - "UInt": "U64" - } - } - }, - { - "name": "approved_upgrades", - "key": { - "Primitive": "Field" - }, - "value": { - "Primitive": "Boolean" - } - } - ], - "storage_variables": [], - "transitions": [ - { - "name": "approve_upgrade", - "is_async": true, - "inputs": [ - { - "name": "checksum", - "ty": { - "Plaintext": { - "Array": { - "element": { - "Primitive": { - "UInt": "U8" - } + "program": "loyalty_token.aleo", + "structs": [], + "records": [ + { + "path": ["LoyaltyCard"], + "fields": [ + { + "name": "owner", + "ty": { + "Primitive": "Address" + }, + "mode": "None" }, - "length": 32 - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "mint_card", - "is_async": true, - "inputs": [ - { - "name": "recipient", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - }, - { - "name": "initial_points", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" + { + "name": "card_id", + "ty": { + "Primitive": "Field" + }, + "mode": "None" + }, + { + "name": "points", + "ty": { + "Primitive": { + "UInt": "U64" + } + }, + "mode": "None" + }, + { + "name": "tier", + "ty": { + "Primitive": { + "UInt": "U8" + } + }, + "mode": "None" + } + ] } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + ], + "mappings": [ + { + "name": "card_exists", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "add_points", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "total_cards", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" }, { - "name": "points_earned", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } + "name": "total_points_issued", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": { + "UInt": "U64" + } } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "check_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" + "name": "approved_upgrades", + "key": { + "Primitive": "Field" + }, + "value": { + "Primitive": "Boolean" } - }, - "mode": "None" } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + ], + "storage_variables": [], + "transitions": [ + { + "name": "approve_upgrade", + "is_async": true, + "inputs": [ + { + "name": "checksum", + "ty": { + "Plaintext": { + "Array": { + "element": { + "Primitive": { + "UInt": "U8" + } + }, + "length": 32 + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ] - }, - { - "name": "transfer_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "new_owner", - "ty": { - "Plaintext": { - "Primitive": "Address" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" - }, - { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - }, - { - "name": "nonce", - "ty": { - "Plaintext": { - "Primitive": "Field" - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "mint_card", + "is_async": true, + "inputs": [ + { + "name": "recipient", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + }, + { + "name": "initial_points", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "add_points", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_earned", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "split_card_v2", - "is_async": true, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "check_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ] }, { - "name": "points_to_keep", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "transfer_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "new_owner", + "ty": { + "Plaintext": { + "Primitive": "Address" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + }, + { + "name": "nonce", + "ty": { + "Plaintext": { + "Primitive": "Field" + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "ty": "Future", - "mode": "None" - } - ] - }, - { - "name": "spend_points", - "is_async": false, - "inputs": [ - { - "name": "card", - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "split_card_v2", + "is_async": true, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_keep", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "ty": "Future", + "mode": "None" + } + ] }, { - "name": "points_to_spend", - "ty": { - "Plaintext": { - "Primitive": { - "UInt": "U64" - } - } - }, - "mode": "None" - } - ], - "outputs": [ - { - "ty": { - "Record": { - "path": [ - "LoyaltyCard" - ], - "program": "loyalty_token" - } - }, - "mode": "None" + "name": "spend_points", + "is_async": false, + "inputs": [ + { + "name": "card", + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + }, + { + "name": "points_to_spend", + "ty": { + "Plaintext": { + "Primitive": { + "UInt": "U64" + } + } + }, + "mode": "None" + } + ], + "outputs": [ + { + "ty": { + "Record": { + "path": ["LoyaltyCard"], + "program": "loyalty_token" + } + }, + "mode": "None" + } + ] } - ] - } - ] -} \ No newline at end of file + ] +} diff --git a/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/program.json b/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/program.json index b5da64ffc..bb2838f69 100644 --- a/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/program.json +++ b/create-leo-app/template-react-loyalty-program-ts/loyalty_token/build/program.json @@ -1,9 +1,9 @@ { - "program": "loyalty_token.aleo", - "version": "0.1.0", - "description": "", - "license": "", - "leo": "3.4.0", - "dependencies": null, - "dev_dependencies": null + "program": "loyalty_token.aleo", + "version": "0.1.0", + "description": "", + "license": "", + "leo": "3.4.0", + "dependencies": null, + "dev_dependencies": null } diff --git a/create-leo-app/template-react-loyalty-program-ts/package.json b/create-leo-app/template-react-loyalty-program-ts/package.json index e314ad2af..c3e1f183f 100644 --- a/create-leo-app/template-react-loyalty-program-ts/package.json +++ b/create-leo-app/template-react-loyalty-program-ts/package.json @@ -1,50 +1,50 @@ { - "name": "template-react-loyalty-program-ts", - "private": true, - "version": "0.0.0", - "description": "Multi-program loyalty system demonstrating records, hashes, and mappings", - "scripts": { - "dev": "vite", - "build": "webpack", - "dev-webpack": "webpack serve", - "build:vite": "vite build", - "lint": "eslint . --ext js,jsx,ts,tsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "install-leo": "./install.sh" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.15", - "comlink": "^4.4.2", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@babel/core": "^7.26.7", - "@babel/preset-env": "^7.26.7", - "@babel/preset-react": "^7.26.3", - "@babel/preset-typescript": "^7.26.0", - "@types/babel__generator": "^7.6.8", - "@types/node": "^22.12.0", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "@vitejs/plugin-react": "^4.3.4", - "babel-loader": "^9.2.1", - "copy-webpack-plugin": "^12.0.2", - "css-loader": "^7.1.2", - "eslint": "^9.19.0", - "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.18", - "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.6.3", - "raw-loader": "^4.0.2", - "style-loader": "^4.0.0", - "svg-url-loader": "^8.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.7.3", - "vite": "^6.3.6", - "webpack": "^5.97.1", - "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.0" - } + "name": "template-react-loyalty-program-ts", + "private": true, + "version": "0.0.0", + "description": "Multi-program loyalty system demonstrating records, hashes, and mappings", + "scripts": { + "dev": "vite", + "build": "webpack", + "dev-webpack": "webpack serve", + "build:vite": "vite build", + "lint": "eslint . --ext js,jsx,ts,tsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "install-leo": "./install.sh" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.15", + "comlink": "^4.4.2", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@babel/core": "^7.26.7", + "@babel/preset-env": "^7.26.7", + "@babel/preset-react": "^7.26.3", + "@babel/preset-typescript": "^7.26.0", + "@types/babel__generator": "^7.6.8", + "@types/node": "^22.12.0", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "babel-loader": "^9.2.1", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^7.1.2", + "eslint": "^9.19.0", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.18", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.6.3", + "raw-loader": "^4.0.2", + "style-loader": "^4.0.0", + "svg-url-loader": "^8.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.7.3", + "vite": "^6.3.6", + "webpack": "^5.97.1", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.0" + } } diff --git a/create-leo-app/template-react-loyalty-program-ts/src/App.css b/create-leo-app/template-react-loyalty-program-ts/src/App.css index bcf8aec20..6a916059e 100644 --- a/create-leo-app/template-react-loyalty-program-ts/src/App.css +++ b/create-leo-app/template-react-loyalty-program-ts/src/App.css @@ -1,459 +1,470 @@ #root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; } .logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; } .logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); + filter: drop-shadow(0 0 2em #646cffaa); } .logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); + filter: drop-shadow(0 0 2em #61dafbaa); } @keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } } h1 { - font-size: 2.5em; - margin-bottom: 0.5em; + font-size: 2.5em; + margin-bottom: 0.5em; } .subtitle { - color: #888; - margin-bottom: 2em; + color: #888; + margin-bottom: 2em; } .card { - padding: 2em; - margin: 1em 0; - background: #1a1a1a; - border-radius: 8px; - text-align: left; + padding: 2em; + margin: 1em 0; + background: #1a1a1a; + border-radius: 8px; + text-align: left; } .card h2 { - margin-top: 0; - color: #fff; - font-size: 1.3em; + margin-top: 0; + color: #fff; + font-size: 1.3em; } .info-card ul { - text-align: left; - padding-left: 1.5em; + text-align: left; + padding-left: 1.5em; } .info-card li { - margin: 0.5em 0; - color: #ccc; + margin: 0.5em 0; + color: #ccc; } /* Status messages */ .loading { - background: #2196f3; - color: white; - padding: 1em; - border-radius: 4px; - margin: 1em 0; - animation: pulse 2s infinite; + background: #2196f3; + color: white; + padding: 1em; + border-radius: 4px; + margin: 1em 0; + animation: pulse 2s infinite; } @keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.7; } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.7; + } } .status { - background: #333; - color: #4caf50; - padding: 1em; - border-radius: 4px; - margin: 1em 0; - font-family: monospace; + background: #333; + color: #4caf50; + padding: 1em; + border-radius: 4px; + margin: 1em 0; + font-family: monospace; } /* Account section */ .account-info { - margin-top: 1em; - padding: 1em; - background: #2a2a2a; - border-radius: 4px; + margin-top: 1em; + padding: 1em; + background: #2a2a2a; + border-radius: 4px; } .account-info code { - background: #333; - padding: 0.2em 0.4em; - border-radius: 3px; - font-size: 0.9em; + background: #333; + padding: 0.2em 0.4em; + border-radius: 3px; + font-size: 0.9em; } .account-mode { - color: #888; - font-size: 0.85em; - margin-top: 0.5em; + color: #888; + font-size: 0.85em; + margin-top: 0.5em; } /* Card selector tabs (shown when multiple cards from splits) */ .card-selector { - display: flex; - gap: 0.5em; - flex-wrap: wrap; - justify-content: center; - margin-bottom: 0.5em; + display: flex; + gap: 0.5em; + flex-wrap: wrap; + justify-content: center; + margin-bottom: 0.5em; } .card-tab { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.15em; - padding: 0.4em 0.8em; - border: 2px solid #444; - border-radius: 6px; - background: #2a2a2a; - cursor: pointer; - transition: border-color 0.2s, background-color 0.2s; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.15em; + padding: 0.4em 0.8em; + border: 2px solid #444; + border-radius: 6px; + background: #2a2a2a; + cursor: pointer; + transition: + border-color 0.2s, + background-color 0.2s; } .card-tab:hover:not(:disabled) { - background: #333; + background: #333; } .card-tab.active { - background: #1a2a1a; + background: #1a2a1a; } .card-tab-points { - font-weight: bold; - font-size: 1em; - color: #fff; + font-weight: bold; + font-size: 1em; + color: #fff; } .card-tab-tier { - font-size: 0.7em; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; + font-size: 0.7em; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; } /* Loyalty card display */ .card-display { - display: flex; - flex-direction: column; - align-items: center; - gap: 1em; + display: flex; + flex-direction: column; + align-items: center; + gap: 1em; } .loyalty-card { - width: 280px; - height: 160px; - background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); - border-radius: 12px; - border: 3px solid; - padding: 1.5em; - display: flex; - flex-direction: column; - justify-content: space-between; - position: relative; + width: 280px; + height: 160px; + background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); + border-radius: 12px; + border: 3px solid; + padding: 1.5em; + display: flex; + flex-direction: column; + justify-content: space-between; + position: relative; } .tier-badge { - position: absolute; - top: 1em; - right: 1em; - padding: 0.3em 0.8em; - border-radius: 4px; - font-weight: bold; - font-size: 0.8em; - color: #000; + position: absolute; + top: 1em; + right: 1em; + padding: 0.3em 0.8em; + border-radius: 4px; + font-weight: bold; + font-size: 0.8em; + color: #000; } .points-display { - display: flex; - flex-direction: column; - align-items: flex-start; + display: flex; + flex-direction: column; + align-items: flex-start; } .points-value { - font-size: 3em; - font-weight: bold; - color: #fff; + font-size: 3em; + font-weight: bold; + color: #fff; } .points-label { - color: #888; - font-size: 0.9em; - text-transform: uppercase; - letter-spacing: 2px; + color: #888; + font-size: 0.9em; + text-transform: uppercase; + letter-spacing: 2px; } .points-controls { - display: flex; - gap: 0.5em; - align-items: center; + display: flex; + gap: 0.5em; + align-items: center; } .points-controls input { - width: 100px; - padding: 0.5em; - border: 1px solid #444; - border-radius: 4px; - background: #2a2a2a; - color: #fff; + width: 100px; + padding: 0.5em; + border: 1px solid #444; + border-radius: 4px; + background: #2a2a2a; + color: #fff; } /* Redeem section */ .redeem-controls { - display: flex; - flex-wrap: wrap; - gap: 1em; - align-items: flex-end; + display: flex; + flex-wrap: wrap; + gap: 1em; + align-items: flex-end; } .input-group { - display: flex; - flex-direction: column; - gap: 0.3em; + display: flex; + flex-direction: column; + gap: 0.3em; } .input-group label { - font-size: 0.8em; - color: #888; + font-size: 0.8em; + color: #888; } .input-group select, .input-group input { - padding: 0.5em; - border: 1px solid #444; - border-radius: 4px; - background: #2a2a2a; - color: #fff; - min-width: 120px; + padding: 0.5em; + border: 1px solid #444; + border-radius: 4px; + background: #2a2a2a; + color: #fff; + min-width: 120px; } /* Voucher display */ .voucher-display { - margin-top: 1.5em; - padding-top: 1.5em; - border-top: 1px solid #333; + margin-top: 1.5em; + padding-top: 1.5em; + border-top: 1px solid #333; } .voucher { - display: inline-block; - background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%); - border-radius: 8px; - padding: 1.5em 2em; - margin: 1em 0; - color: white; + display: inline-block; + background: linear-gradient(135deg, #4caf50 0%, #2e7d32 100%); + border-radius: 8px; + padding: 1.5em 2em; + margin: 1em 0; + color: white; } .voucher-type { - font-size: 1.2em; - font-weight: bold; + font-size: 1.2em; + font-weight: bold; } .voucher-value { - font-size: 0.9em; - opacity: 0.9; - margin-top: 0.5em; + font-size: 0.9em; + opacity: 0.9; + margin-top: 0.5em; } .voucher-id { - font-size: 0.75em; - opacity: 0.7; - margin-top: 0.5em; - font-family: monospace; + font-size: 0.75em; + opacity: 0.7; + margin-top: 0.5em; + font-family: monospace; } .vouchers-list { - display: flex; - flex-wrap: wrap; - gap: 1em; + display: flex; + flex-wrap: wrap; + gap: 1em; } .voucher-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.5em; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.5em; } .voucher-item .voucher { - margin: 0; + margin: 0; } .voucher-item button { - width: 100%; + width: 100%; } /* Card ID */ .card-id { - font-size: 0.7em; - color: #666; - font-family: monospace; - margin-top: auto; + font-size: 0.7em; + color: #666; + font-family: monospace; + margin-top: auto; } /* Code block */ .code-block { - background: #0d0d0d; - border: 1px solid #333; - border-radius: 6px; - padding: 1em; - font-size: 0.85em; - text-align: left; - overflow-x: auto; - color: #e0e0e0; - margin: 1em 0; + background: #0d0d0d; + border: 1px solid #333; + border-radius: 6px; + padding: 1em; + font-size: 0.85em; + text-align: left; + overflow-x: auto; + color: #e0e0e0; + margin: 1em 0; } .api-example { - margin-bottom: 0.5em; - color: #888; + margin-bottom: 0.5em; + color: #888; } /* Buttons */ button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - color: #e0e0e0; - cursor: pointer; - transition: border-color 0.25s, color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + color: #e0e0e0; + cursor: pointer; + transition: + border-color 0.25s, + color 0.25s; } button:hover:not(:disabled) { - border-color: #646cff; - color: #fff; + border-color: #646cff; + color: #fff; } button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } button:disabled { - opacity: 0.5; - cursor: not-allowed; + opacity: 0.5; + cursor: not-allowed; } /* Info text */ .info { - color: #888; - font-size: 0.9em; - margin-bottom: 1em; + color: #888; + font-size: 0.9em; + margin-bottom: 1em; } .mapping-result { - margin-top: 1em; - padding: 1em; - background: #2a2a2a; - border-radius: 4px; - font-family: monospace; - color: #4caf50; + margin-top: 1em; + padding: 1em; + background: #2a2a2a; + border-radius: 4px; + font-family: monospace; + color: #4caf50; } .read-the-docs { - color: #888; + color: #888; } /* Scanner section */ .scanner-info { - color: #888; - font-size: 0.9em; - margin-bottom: 1em; + color: #888; + font-size: 0.9em; + margin-bottom: 1em; } .scanned-records { - margin-top: 1.5em; + margin-top: 1.5em; } .scanned-section { - margin-bottom: 1em; + margin-bottom: 1em; } .scanned-section h3 { - font-size: 1em; - color: #888; - margin-bottom: 0.5em; + font-size: 1em; + color: #888; + margin-bottom: 0.5em; } .scanned-list { - display: flex; - flex-wrap: wrap; - gap: 0.5em; + display: flex; + flex-wrap: wrap; + gap: 0.5em; } .scanned-item { - display: flex; - gap: 0.5em; - padding: 0.5em 1em; - background: #2a2a2a; - border: 1px solid #444; - border-radius: 6px; - cursor: pointer; - transition: border-color 0.2s, background-color 0.2s; + display: flex; + gap: 0.5em; + padding: 0.5em 1em; + background: #2a2a2a; + border: 1px solid #444; + border-radius: 6px; + cursor: pointer; + transition: + border-color 0.2s, + background-color 0.2s; } .scanned-item:hover { - border-color: #646cff; - background: #333; + border-color: #646cff; + background: #333; } .scanned-item.selected { - border-color: #4caf50; - background: #1a3a1a; + border-color: #4caf50; + background: #1a3a1a; } .scanned-tier { - font-weight: bold; + font-weight: bold; } .scanned-points { - color: #888; + color: #888; } .scanned-type { - font-weight: bold; - color: #4caf50; + font-weight: bold; + color: #4caf50; } .scanned-value { - color: #888; + color: #888; } /* Responsive */ @media (max-width: 600px) { - .redeem-controls { - flex-direction: column; - align-items: stretch; - } - - .loyalty-card { - width: 100%; - max-width: 280px; - } + .redeem-controls { + flex-direction: column; + align-items: stretch; + } + + .loyalty-card { + width: 100%; + max-width: 280px; + } } diff --git a/create-leo-app/template-react-loyalty-program-ts/src/App.tsx b/create-leo-app/template-react-loyalty-program-ts/src/App.tsx index 0d6df9292..90b6c6be7 100644 --- a/create-leo-app/template-react-loyalty-program-ts/src/App.tsx +++ b/create-leo-app/template-react-loyalty-program-ts/src/App.tsx @@ -8,491 +8,585 @@ import { getAleoWorker } from "./workers/AleoWorker"; // Read configuration from environment variables (Vite loads .env automatically) const getConfig = () => { - const provingMode = import.meta.env.VITE_ALEO_PROVING_MODE || "local"; - - return { - provingMode: provingMode as "local" | "delegated", - // Consumer ID (used for both DPS and RSS) - consumerId: import.meta.env.VITE_ALEO_CONSUMER_ID, - // DPS configuration - dpsUrl: import.meta.env.VITE_ALEO_DPS_URL, - dpsApiKey: import.meta.env.VITE_ALEO_DPS_API_KEY, - dpsPrivacy: import.meta.env.VITE_ALEO_DPS_PRIVACY === "true", - // RSS configuration - recordScannerUrl: import.meta.env.VITE_ALEO_RSS_URL, - recordScannerApiKey: import.meta.env.VITE_ALEO_RSS_API_KEY, - rssPrivacy: import.meta.env.VITE_ALEO_RSS_PRIVACY === "true", - }; + const provingMode = import.meta.env.VITE_ALEO_PROVING_MODE || "local"; + + return { + provingMode: provingMode as "local" | "delegated", + // Consumer ID (used for both DPS and RSS) + consumerId: import.meta.env.VITE_ALEO_CONSUMER_ID, + // DPS configuration + dpsUrl: import.meta.env.VITE_ALEO_DPS_URL, + dpsApiKey: import.meta.env.VITE_ALEO_DPS_API_KEY, + dpsPrivacy: import.meta.env.VITE_ALEO_DPS_PRIVACY === "true", + // RSS configuration + recordScannerUrl: import.meta.env.VITE_ALEO_RSS_URL, + recordScannerApiKey: import.meta.env.VITE_ALEO_RSS_API_KEY, + rssPrivacy: import.meta.env.VITE_ALEO_RSS_PRIVACY === "true", + }; }; // Type definitions (matching worker types) interface LoyaltyCard { - owner: string; - cardId: string; - points: number; - tier: number; - raw: string; + owner: string; + cardId: string; + points: number; + tier: number; + raw: string; } interface RewardVoucher { - owner: string; - voucherId: string; - rewardType: number; - value: number; - raw: string; + owner: string; + voucherId: string; + rewardType: number; + value: number; + raw: string; } interface SplitResult { - keptCard: LoyaltyCard; - splitCard: LoyaltyCard; + keptCard: LoyaltyCard; + splitCard: LoyaltyCard; } // Enums (matching worker enums) const RewardType = { - Discount: 1, - Freebie: 2, - Upgrade: 3, + Discount: 1, + Freebie: 2, + Upgrade: 3, } as const; const CardTier = { - Bronze: 0, - Silver: 1, - Gold: 2, + Bronze: 0, + Silver: 1, + Gold: 2, } as const; // Reward type labels const REWARD_TYPE_NAMES: Record = { - [RewardType.Discount]: "Discount", - [RewardType.Freebie]: "Freebie", - [RewardType.Upgrade]: "Upgrade", + [RewardType.Discount]: "Discount", + [RewardType.Freebie]: "Freebie", + [RewardType.Upgrade]: "Upgrade", }; // Tier names const TIER_NAMES: Record = { - [CardTier.Bronze]: "Bronze", - [CardTier.Silver]: "Silver", - [CardTier.Gold]: "Gold", + [CardTier.Bronze]: "Bronze", + [CardTier.Silver]: "Silver", + [CardTier.Gold]: "Gold", }; function App() { - // Initialization state - const [initialized, setInitialized] = useState(false); - - // Account state - const [account, setAccount] = useState<{ - privateKey: string; - viewKey: string; - address: string; - } | null>(null); - - // Card state - array to support multiple cards from splits - const [cards, setCards] = useState([]); - const [selectedCardIndex, setSelectedCardIndex] = useState(0); - - // Derived selected card (null when no cards exist) - const selectedCard = cards.length > 0 ? cards[selectedCardIndex] : null; - - // Voucher state - array to support multiple vouchers - const [vouchers, setVouchers] = useState([]); - - // UI state - const [loading, setLoading] = useState(""); - const [status, setStatus] = useState(""); - - // Input state - const [pointsToAdd, setPointsToAdd] = useState(500); - const [pointsToKeep, setPointsToKeep] = useState(50); - const [selectedRewardType, setSelectedRewardType] = useState(RewardType.Discount); - const [pointsCost, setPointsCost] = useState(100); - - // Config state (for conditional UI) - const [provingMode, setProvingMode] = useState<"local" | "delegated">("local"); - - // Initialize the loyalty program on mount - useEffect(() => { - const init = async () => { - try { - // Wait for worker to be ready - console.log("[App] Waiting for worker to be ready..."); - const aleoWorker = await getAleoWorker(); - console.log("[App] Worker ready!"); - - // Test comlink connectivity first - console.log("[App] Testing comlink with ping..."); - const pong = await aleoWorker.ping(); - console.log("[App] Ping response:", pong); - - const config = getConfig(); - console.log("[App] Config:", config); - console.log("[App] Calling initLoyaltyProgram..."); - await aleoWorker.initLoyaltyProgram( - loyalty_token_program, - loyalty_rewards_program, - config + // Initialization state + const [initialized, setInitialized] = useState(false); + + // Account state + const [account, setAccount] = useState<{ + privateKey: string; + viewKey: string; + address: string; + } | null>(null); + + // Card state - array to support multiple cards from splits + const [cards, setCards] = useState([]); + const [selectedCardIndex, setSelectedCardIndex] = useState(0); + + // Derived selected card (null when no cards exist) + const selectedCard = cards.length > 0 ? cards[selectedCardIndex] : null; + + // Voucher state - array to support multiple vouchers + const [vouchers, setVouchers] = useState([]); + + // UI state + const [loading, setLoading] = useState(""); + const [status, setStatus] = useState(""); + + // Input state + const [pointsToAdd, setPointsToAdd] = useState(500); + const [pointsToKeep, setPointsToKeep] = useState(50); + const [selectedRewardType, setSelectedRewardType] = useState( + RewardType.Discount, + ); + const [pointsCost, setPointsCost] = useState(100); + + // Config state (for conditional UI) + const [provingMode, setProvingMode] = useState<"local" | "delegated">( + "local", + ); + + // Initialize the loyalty program on mount + useEffect(() => { + const init = async () => { + try { + // Wait for worker to be ready + console.log("[App] Waiting for worker to be ready..."); + const aleoWorker = await getAleoWorker(); + console.log("[App] Worker ready!"); + + // Test comlink connectivity first + console.log("[App] Testing comlink with ping..."); + const pong = await aleoWorker.ping(); + console.log("[App] Ping response:", pong); + + const config = getConfig(); + console.log("[App] Config:", config); + console.log("[App] Calling initLoyaltyProgram..."); + await aleoWorker.initLoyaltyProgram( + loyalty_token_program, + loyalty_rewards_program, + config, + ); + console.log("[App] initLoyaltyProgram completed!"); + setProvingMode(config.provingMode); + + // Auto-load account based on proving mode + // - Delegated: use pre-funded demo account (same as Node template) + // - Local: generate a random account (no funds needed) + console.log("[App] Loading account..."); + const loadedAccount = + config.provingMode === "delegated" + ? await aleoWorker.loadDemoAccount() + : await aleoWorker.createAccount(); + setAccount(loadedAccount); + console.log("[App] Account loaded:", loadedAccount.address); + + setInitialized(true); + const modeLabel = + config.provingMode === "delegated" + ? "delegated (demo account)" + : "local"; + setStatus(`Ready! Using ${modeLabel} mode`); + } catch (error) { + console.error("[App] Init error:", error); + setStatus(`Initialization error: ${error}`); + } + }; + init(); + }, []); + + // Mint a new loyalty card - clean API! + const handleMintCard = async () => { + if (!account) { + setStatus("Please generate an account first"); + return; + } + + setLoading( + "Minting loyalty card (this may take a moment due to hash operations)...", ); - console.log("[App] initLoyaltyProgram completed!"); - setProvingMode(config.provingMode); - - // Auto-load account based on proving mode - // - Delegated: use pre-funded demo account (same as Node template) - // - Local: generate a random account (no funds needed) - console.log("[App] Loading account..."); - const loadedAccount = config.provingMode === "delegated" - ? await aleoWorker.loadDemoAccount() - : await aleoWorker.createAccount(); - setAccount(loadedAccount); - console.log("[App] Account loaded:", loadedAccount.address); - - setInitialized(true); - const modeLabel = config.provingMode === "delegated" ? "delegated (demo account)" : "local"; - setStatus(`Ready! Using ${modeLabel} mode`); - } catch (error) { - console.error("[App] Init error:", error); - setStatus(`Initialization error: ${error}`); - } + try { + const aleoWorker = await getAleoWorker(); + // Clean API: just pass recipient and points + const newCard = await aleoWorker.mintCard(account.address, 100); + + setCards((prev) => [...prev, newCard]); + setSelectedCardIndex(cards.length); // Select the newly minted card + setStatus( + `Loyalty card minted! You have ${newCard.points} points (${TIER_NAMES[newCard.tier]} tier)`, + ); + } catch (error) { + setStatus(`Error minting card: ${error}`); + } + setLoading(""); }; - init(); - }, []); - - // Mint a new loyalty card - clean API! - const handleMintCard = async () => { - if (!account) { - setStatus("Please generate an account first"); - return; - } - setLoading("Minting loyalty card (this may take a moment due to hash operations)..."); - try { - const aleoWorker = await getAleoWorker(); - // Clean API: just pass recipient and points - const newCard = await aleoWorker.mintCard(account.address, 100); - - setCards(prev => [...prev, newCard]); - setSelectedCardIndex(cards.length); // Select the newly minted card - setStatus(`Loyalty card minted! You have ${newCard.points} points (${TIER_NAMES[newCard.tier]} tier)`); - } catch (error) { - setStatus(`Error minting card: ${error}`); - } - setLoading(""); - }; - - // Helper to replace the selected card in the array - const updateSelectedCard = (updatedCard: LoyaltyCard) => { - setCards(prev => prev.map((c, i) => i === selectedCardIndex ? updatedCard : c)); - }; - - // Add points to the card - clean API! - const handleAddPoints = async () => { - if (!selectedCard) { - setStatus("Please mint a card first"); - return; - } + // Helper to replace the selected card in the array + const updateSelectedCard = (updatedCard: LoyaltyCard) => { + setCards((prev) => + prev.map((c, i) => (i === selectedCardIndex ? updatedCard : c)), + ); + }; - setLoading(`Adding ${pointsToAdd} points (computing hash for audit trail)...`); - try { - const aleoWorker = await getAleoWorker(); - // Clean API: pass the card object directly - const updatedCard = await aleoWorker.addPoints(selectedCard, pointsToAdd); - - updateSelectedCard(updatedCard); - setStatus( - `Added ${pointsToAdd} points! New balance: ${updatedCard.points} (${TIER_NAMES[updatedCard.tier]} tier)` - ); - } catch (error) { - setStatus(`Error adding points: ${error}`); - } - setLoading(""); - }; - - // Split a card into two - clean API! - const handleSplitCard = async () => { - if (!selectedCard) { - setStatus("Please mint a card first"); - return; - } + // Add points to the card - clean API! + const handleAddPoints = async () => { + if (!selectedCard) { + setStatus("Please mint a card first"); + return; + } - if (pointsToKeep >= selectedCard.points) { - setStatus(`Points to keep must be less than card balance (${selectedCard.points})`); - return; - } + setLoading( + `Adding ${pointsToAdd} points (computing hash for audit trail)...`, + ); + try { + const aleoWorker = await getAleoWorker(); + // Clean API: pass the card object directly + const updatedCard = await aleoWorker.addPoints( + selectedCard, + pointsToAdd, + ); + + updateSelectedCard(updatedCard); + setStatus( + `Added ${pointsToAdd} points! New balance: ${updatedCard.points} (${TIER_NAMES[updatedCard.tier]} tier)`, + ); + } catch (error) { + setStatus(`Error adding points: ${error}`); + } + setLoading(""); + }; - const splitPoints = selectedCard.points - pointsToKeep; - setLoading(`Splitting card (keep ${pointsToKeep}, split off ${splitPoints})...`); - try { - const aleoWorker = await getAleoWorker(); - const result: SplitResult = await aleoWorker.splitCardV2(selectedCard, pointsToKeep); - - // Replace the current card with keptCard, append splitCard - setCards(prev => { - const updated = [...prev]; - updated[selectedCardIndex] = result.keptCard; - updated.push(result.splitCard); - return updated; - }); - setStatus( - `Card split! Kept: ${result.keptCard.points} points (${TIER_NAMES[result.keptCard.tier]}), ` + - `Split off: ${result.splitCard.points} points (${TIER_NAMES[result.splitCard.tier]})` - ); - } catch (error) { - setStatus(`Error splitting card: ${error}`); - } - setLoading(""); - }; - - // Redeem points for a voucher - clean API! - const handleRedeem = async () => { - if (!selectedCard) { - setStatus("Please mint a card first"); - return; - } + // Split a card into two - clean API! + const handleSplitCard = async () => { + if (!selectedCard) { + setStatus("Please mint a card first"); + return; + } + + if (pointsToKeep >= selectedCard.points) { + setStatus( + `Points to keep must be less than card balance (${selectedCard.points})`, + ); + return; + } + + const splitPoints = selectedCard.points - pointsToKeep; + setLoading( + `Splitting card (keep ${pointsToKeep}, split off ${splitPoints})...`, + ); + try { + const aleoWorker = await getAleoWorker(); + const result: SplitResult = await aleoWorker.splitCardV2( + selectedCard, + pointsToKeep, + ); + + // Replace the current card with keptCard, append splitCard + setCards((prev) => { + const updated = [...prev]; + updated[selectedCardIndex] = result.keptCard; + updated.push(result.splitCard); + return updated; + }); + setStatus( + `Card split! Kept: ${result.keptCard.points} points (${TIER_NAMES[result.keptCard.tier]}), ` + + `Split off: ${result.splitCard.points} points (${TIER_NAMES[result.splitCard.tier]})`, + ); + } catch (error) { + setStatus(`Error splitting card: ${error}`); + } + setLoading(""); + }; - if (selectedCard.points < pointsCost) { - setStatus(`Not enough points! You have ${selectedCard.points}, need ${pointsCost}`); - return; - } + // Redeem points for a voucher - clean API! + const handleRedeem = async () => { + if (!selectedCard) { + setStatus("Please mint a card first"); + return; + } + + if (selectedCard.points < pointsCost) { + setStatus( + `Not enough points! You have ${selectedCard.points}, need ${pointsCost}`, + ); + return; + } + + setLoading("Redeeming points for voucher (multi-program execution)..."); + try { + const aleoWorker = await getAleoWorker(); + // Clean API: pass card, reward type, and cost + const result = await aleoWorker.redeemForVoucher( + selectedCard, + selectedRewardType, + pointsCost, + ); + + updateSelectedCard(result.card); + setVouchers((prev) => [...prev, result.voucher]); + setStatus( + `Redeemed ${pointsCost} points for a ${REWARD_TYPE_NAMES[result.voucher.rewardType]} voucher (value: ${result.voucher.value})!`, + ); + } catch (error) { + setStatus(`Error redeeming: ${error}`); + } + setLoading(""); + }; - setLoading("Redeeming points for voucher (multi-program execution)..."); - try { - const aleoWorker = await getAleoWorker(); - // Clean API: pass card, reward type, and cost - const result = await aleoWorker.redeemForVoucher( - selectedCard, - selectedRewardType, - pointsCost - ); - - updateSelectedCard(result.card); - setVouchers(prev => [...prev, result.voucher]); - setStatus( - `Redeemed ${pointsCost} points for a ${REWARD_TYPE_NAMES[result.voucher.rewardType]} voucher (value: ${result.voucher.value})!` - ); - } catch (error) { - setStatus(`Error redeeming: ${error}`); - } - setLoading(""); - }; - - // Use (burn) a voucher - clean API! - const handleUseVoucher = async (voucherToUse: RewardVoucher) => { - setLoading("Using voucher (consuming record)..."); - try { - const aleoWorker = await getAleoWorker(); - // Clean API: just pass the voucher - await aleoWorker.useVoucher(voucherToUse); - - // Remove the used voucher from the array (use raw record string for uniqueness) - setVouchers(prev => prev.filter(v => v.raw !== voucherToUse.raw)); - setStatus("Voucher used successfully! It has been consumed."); - } catch (error) { - setStatus(`Error using voucher: ${error}`); - } - setLoading(""); - }; - - // Get tier badge color - const getTierColor = (tier: number) => { - switch (tier) { - case CardTier.Gold: - return "#FFD700"; - case CardTier.Silver: - return "#C0C0C0"; - default: - return "#CD7F32"; + // Use (burn) a voucher - clean API! + const handleUseVoucher = async (voucherToUse: RewardVoucher) => { + setLoading("Using voucher (consuming record)..."); + try { + const aleoWorker = await getAleoWorker(); + // Clean API: just pass the voucher + await aleoWorker.useVoucher(voucherToUse); + + // Remove the used voucher from the array (use raw record string for uniqueness) + setVouchers((prev) => + prev.filter((v) => v.raw !== voucherToUse.raw), + ); + setStatus("Voucher used successfully! It has been consumed."); + } catch (error) { + setStatus(`Error using voucher: ${error}`); + } + setLoading(""); + }; + + // Get tier badge color + const getTierColor = (tier: number) => { + switch (tier) { + case CardTier.Gold: + return "#FFD700"; + case CardTier.Silver: + return "#C0C0C0"; + default: + return "#CD7F32"; + } + }; + + if (!initialized) { + return
Initializing loyalty program...
; } - }; - if (!initialized) { return ( -
- Initializing loyalty program... -
- ); - } - - return ( - <> - -

Loyalty Program Demo

-

- Multi-program example with records, hashes, and mappings -

- - {/* Status display */} - {loading &&
{loading}
} - {status &&
{status}
} - - {/* Account Section */} -
-

1. Account

- {account && ( -
-

- Address:{" "} - {account.address.slice(0, 20)}... -

-

- {provingMode === "delegated" - ? "Using pre-funded demo account" - : "Using generated account (local mode)"} + <> +

+

Loyalty Program Demo

+

+ Multi-program example with records, hashes, and mappings

-
- )} -
- - {/* Loyalty Card Section */} -
-

2. Loyalty Card

- {cards.length === 0 ? ( - - ) : ( -
- {/* Card selector tabs - shown when multiple cards exist */} - {cards.length > 1 && ( -
- {cards.map((c, i) => ( - - ))} -
- )} - - {selectedCard && ( - <> -
-
- {TIER_NAMES[selectedCard.tier]} -
-
- {selectedCard.points} - Points -
-
- ID: {selectedCard.cardId.slice(0, 12)}... -
-
-
- setPointsToAdd(Number(e.target.value))} - min="1" - max="10000" - /> - -
-
- setPointsToKeep(Number(e.target.value))} - min="1" - max={selectedCard.points - 1} - /> - -
- - )} -
- )} -
- - {/* Rewards Section */} -
-

3. Redeem Rewards

- {selectedCard && ( -
-
- - + + {/* Status display */} + {loading &&
{loading}
} + {status &&
{status}
} + + {/* Account Section */} +
+

1. Account

+ {account && ( +
+

+ Address:{" "} + {account.address.slice(0, 20)}... +

+

+ {provingMode === "delegated" + ? "Using pre-funded demo account" + : "Using generated account (local mode)"} +

+
+ )}
-
- - setPointsCost(Number(e.target.value))} - min="10" - max={selectedCard.points} - /> + + {/* Loyalty Card Section */} +
+

2. Loyalty Card

+ {cards.length === 0 ? ( + + ) : ( +
+ {/* Card selector tabs - shown when multiple cards exist */} + {cards.length > 1 && ( +
+ {cards.map((c, i) => ( + + ))} +
+ )} + + {selectedCard && ( + <> +
+
+ {TIER_NAMES[selectedCard.tier]} +
+
+ + {selectedCard.points} + + + Points + +
+
+ ID: {selectedCard.cardId.slice(0, 12)} + ... +
+
+
+ + setPointsToAdd( + Number(e.target.value), + ) + } + min="1" + max="10000" + /> + +
+
+ + setPointsToKeep( + Number(e.target.value), + ) + } + min="1" + max={selectedCard.points - 1} + /> + +
+ + )} +
+ )}
- -
- )} - - {vouchers.length > 0 && ( -
-

Your Vouchers ({vouchers.length})

-
- {vouchers.map((v) => ( -
-
-
- {REWARD_TYPE_NAMES[v.rewardType]} + + {/* Rewards Section */} +
+

3. Redeem Rewards

+ {selectedCard && ( +
+
+ + +
+
+ + + setPointsCost(Number(e.target.value)) + } + min="10" + max={selectedCard.points} + /> +
+
-
Value: {v.value}
-
- ID: {v.voucherId.slice(0, 12)}... + )} + + {vouchers.length > 0 && ( +
+

Your Vouchers ({vouchers.length})

+
+ {vouchers.map((v) => ( +
+
+
+ {REWARD_TYPE_NAMES[v.rewardType]} +
+
+ Value: {v.value} +
+
+ ID: {v.voucherId.slice(0, 12)}... +
+
+ +
+ ))} +
-
- -
- ))} + )}
-
- )} -
- - {/* Program Info */} -
-

About This Demo

-

- Clean API Example: -

-
-{`// Initialize once
+
+            {/* Program Info */}
+            
+

About This Demo

+

+ Clean API Example: +

+
+                    {`// Initialize once
 await loyalty.initLoyaltyProgram(tokenProgram, rewardsProgram);
 
 // Mint a card
@@ -513,31 +607,32 @@ const { card, voucher } = await loyalty.redeemForVoucher(
 
 // Use voucher
 await loyalty.useVoucher(voucher);`}
-        
-
    -
  • - Multi-Program: Uses loyalty_token.aleo and - loyalty_rewards.aleo -
  • -
  • - Hash Functions: BHP256 generates unique card/voucher - IDs -
  • -
  • - Typed Records: LoyaltyCard and RewardVoucher - interfaces -
  • -
  • - Enums: RewardType.Discount, CardTier.Gold, etc. -
  • -
-
- -

- Click on the Aleo and React logos to learn more -

- - ); +
+
    +
  • + Multi-Program: Uses loyalty_token.aleo + and loyalty_rewards.aleo +
  • +
  • + Hash Functions: BHP256 generates unique + card/voucher IDs +
  • +
  • + Typed Records: LoyaltyCard and + RewardVoucher interfaces +
  • +
  • + Enums: RewardType.Discount, + CardTier.Gold, etc. +
  • +
+
+ +

+ Click on the Aleo and React logos to learn more +

+ + ); } export default App; diff --git a/create-leo-app/template-react-loyalty-program-ts/src/index.css b/create-leo-app/template-react-loyalty-program-ts/src/index.css index e0cce1292..5de620dd6 100644 --- a/create-leo-app/template-react-loyalty-program-ts/src/index.css +++ b/create-leo-app/template-react-loyalty-program-ts/src/index.css @@ -1,31 +1,31 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } a:hover { - color: #535bf2; + color: #535bf2; } body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; } diff --git a/create-leo-app/template-react-loyalty-program-ts/src/main.tsx b/create-leo-app/template-react-loyalty-program-ts/src/main.tsx index 9b67590a0..642466342 100644 --- a/create-leo-app/template-react-loyalty-program-ts/src/main.tsx +++ b/create-leo-app/template-react-loyalty-program-ts/src/main.tsx @@ -4,7 +4,7 @@ import App from "./App"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root")!).render( - - - + + + , ); diff --git a/create-leo-app/template-react-loyalty-program-ts/src/vite-env.d.ts b/create-leo-app/template-react-loyalty-program-ts/src/vite-env.d.ts index 95abec821..27211ab09 100644 --- a/create-leo-app/template-react-loyalty-program-ts/src/vite-env.d.ts +++ b/create-leo-app/template-react-loyalty-program-ts/src/vite-env.d.ts @@ -1,24 +1,24 @@ /// interface ImportMetaEnv { - // Consumer ID (used for both DPS and RSS) - readonly VITE_ALEO_CONSUMER_ID?: string; + // Consumer ID (used for both DPS and RSS) + readonly VITE_ALEO_CONSUMER_ID?: string; - // Proving mode - readonly VITE_ALEO_PROVING_MODE?: "local" | "delegated"; + // Proving mode + readonly VITE_ALEO_PROVING_MODE?: "local" | "delegated"; - // DPS (Delegated Proving Service) - readonly VITE_ALEO_DPS_URL?: string; - readonly VITE_ALEO_DPS_API_KEY?: string; - readonly VITE_ALEO_DPS_PRIVACY?: string; + // DPS (Delegated Proving Service) + readonly VITE_ALEO_DPS_URL?: string; + readonly VITE_ALEO_DPS_API_KEY?: string; + readonly VITE_ALEO_DPS_PRIVACY?: string; - // RSS scanner - readonly VITE_ALEO_RSS_URL?: string; - readonly VITE_ALEO_RSS_API_KEY?: string; - readonly VITE_ALEO_RSS_PRIVACY?: string; - readonly VITE_ALEO_SCAN_START_HEIGHT?: string; + // RSS scanner + readonly VITE_ALEO_RSS_URL?: string; + readonly VITE_ALEO_RSS_API_KEY?: string; + readonly VITE_ALEO_RSS_PRIVACY?: string; + readonly VITE_ALEO_SCAN_START_HEIGHT?: string; } interface ImportMeta { - readonly env: ImportMetaEnv; + readonly env: ImportMetaEnv; } diff --git a/create-leo-app/template-react-loyalty-program-ts/src/workers/AleoWorker.ts b/create-leo-app/template-react-loyalty-program-ts/src/workers/AleoWorker.ts index 276fb7c69..978d1c0a6 100644 --- a/create-leo-app/template-react-loyalty-program-ts/src/workers/AleoWorker.ts +++ b/create-leo-app/template-react-loyalty-program-ts/src/workers/AleoWorker.ts @@ -5,40 +5,40 @@ let singletonWorker = null; let workerReady: Promise | null = null; const AleoWorker = () => { - if (!singletonWorker) { - const worker = new Worker(new URL("worker", import.meta.url), { - type: "module", - }); + if (!singletonWorker) { + const worker = new Worker(new URL("worker", import.meta.url), { + type: "module", + }); - worker.onerror = function (event) { - console.error("Error in worker: " + event?.message); - }; + worker.onerror = function (event) { + console.error("Error in worker: " + event?.message); + }; - // Wait for worker to signal it's ready before wrapping - workerReady = new Promise((resolve) => { - const handleMessage = (event: MessageEvent) => { - if (event.data === "ready") { - console.log("[AleoWorker] Worker signaled ready"); - worker.removeEventListener("message", handleMessage); - resolve(); - } - }; - worker.addEventListener("message", handleMessage); - }); + // Wait for worker to signal it's ready before wrapping + workerReady = new Promise((resolve) => { + const handleMessage = (event: MessageEvent) => { + if (event.data === "ready") { + console.log("[AleoWorker] Worker signaled ready"); + worker.removeEventListener("message", handleMessage); + resolve(); + } + }; + worker.addEventListener("message", handleMessage); + }); - // Wrap after ready signal - workerReady.then(() => { - singletonWorker = wrap(worker); - }); - } - return singletonWorker; + // Wrap after ready signal + workerReady.then(() => { + singletonWorker = wrap(worker); + }); + } + return singletonWorker; }; // Get a promise that resolves to the worker when it's ready const getAleoWorker = async () => { - AleoWorker(); // Initialize if needed - await workerReady; - return singletonWorker; + AleoWorker(); // Initialize if needed + await workerReady; + return singletonWorker; }; export { AleoWorker, getAleoWorker }; diff --git a/create-leo-app/template-react-loyalty-program-ts/src/workers/worker.ts b/create-leo-app/template-react-loyalty-program-ts/src/workers/worker.ts index 4c9580334..ca48bf304 100644 --- a/create-leo-app/template-react-loyalty-program-ts/src/workers/worker.ts +++ b/create-leo-app/template-react-loyalty-program-ts/src/workers/worker.ts @@ -12,16 +12,16 @@ * - Delegated proving (optional). */ import { - Account, - ProgramManager, - Program, - PrivateKey, - initThreadPool, - AleoKeyProvider, - AleoNetworkClient, - RecordScanner, - RecordCiphertext, - OfflineQuery, + Account, + ProgramManager, + Program, + PrivateKey, + initThreadPool, + AleoKeyProvider, + AleoNetworkClient, + RecordScanner, + RecordCiphertext, + OfflineQuery, } from "@provablehq/sdk"; import { expose, proxy } from "comlink"; @@ -37,115 +37,115 @@ console.log("[Worker] Thread pool initialized!"); * Proving mode for transactions */ export enum ProvingMode { - Local = "local", - Delegated = "delegated", + Local = "local", + Delegated = "delegated", } /** * Reward types available for redemption */ export enum RewardType { - Discount = 1, - Freebie = 2, - Upgrade = 3, + Discount = 1, + Freebie = 2, + Upgrade = 3, } /** * Card tier levels based on points */ export enum CardTier { - Bronze = 0, - Silver = 1, - Gold = 2, + Bronze = 0, + Silver = 1, + Gold = 2, } /** * Parsed LoyaltyCard record. */ export interface LoyaltyCard { - owner: string; - cardId: string; - points: number; - tier: CardTier; - raw: string; // Original record string for passing to functions. + owner: string; + cardId: string; + points: number; + tier: CardTier; + raw: string; // Original record string for passing to functions. } /** * Parsed RewardVoucher record. */ export interface RewardVoucher { - owner: string; - voucherId: string; - rewardType: RewardType; - value: number; - raw: string; // Original record string for passing to functions. + owner: string; + voucherId: string; + rewardType: RewardType; + value: number; + raw: string; // Original record string for passing to functions. } /** * Result of splitting a card into two. */ export interface SplitResult { - keptCard: LoyaltyCard; - splitCard: LoyaltyCard; + keptCard: LoyaltyCard; + splitCard: LoyaltyCard; } /** * Result of redeeming points for a voucher. */ export interface RedeemResult { - card: LoyaltyCard; - voucher: RewardVoucher; + card: LoyaltyCard; + voucher: RewardVoucher; } /** * Loyalty program statistics from mappings. */ export interface ProgramStats { - totalCards: number; - totalPointsIssued: number; - redemptionsByType: Record; + totalCards: number; + totalPointsIssued: number; + redemptionsByType: Record; } /** * Configuration for program execution. */ export interface ProgramConfig { - provingMode?: ProvingMode; - /** Provable API consumer ID (used for both DPS and RSS). */ - consumerId?: string; - /** RecordScanner service URL. */ - recordScannerUrl?: string; - /** API key or JWT for RecordScanner. */ - recordScannerApiKey?: string; - dpsUrl?: string; - dpsApiKey?: string; - /** Enable encrypted DPS flow (TEE-protected proving). */ - dpsPrivacy?: boolean; - /** Enable encrypted RSS flow (TEE-protected record scanning). */ - rssPrivacy?: boolean; + provingMode?: ProvingMode; + /** Provable API consumer ID (used for both DPS and RSS). */ + consumerId?: string; + /** RecordScanner service URL. */ + recordScannerUrl?: string; + /** API key or JWT for RecordScanner. */ + recordScannerApiKey?: string; + dpsUrl?: string; + dpsApiKey?: string; + /** Enable encrypted DPS flow (TEE-protected proving). */ + dpsPrivacy?: boolean; + /** Enable encrypted RSS flow (TEE-protected record scanning). */ + rssPrivacy?: boolean; } /** * Status event types for UI feedback */ export type StatusEventType = - | "mode_changed" - | "operation_start" - | "operation_complete" - | "scan_start" - | "scan_complete" - | "error"; + | "mode_changed" + | "operation_start" + | "operation_complete" + | "scan_start" + | "scan_complete" + | "error"; /** * Status event for UI feedback */ export interface StatusEvent { - type: StatusEventType; - operation?: string; - mode?: ProvingMode; - duration?: number; - count?: number; - error?: string; + type: StatusEventType; + operation?: string; + mode?: ProvingMode; + duration?: number; + count?: number; + error?: string; } // ============================================================================ @@ -190,979 +190,1073 @@ export interface StatusEvent { * }); */ class LoyaltyProgram { - private programManager: ProgramManager; - private keyProvider: AleoKeyProvider; - private networkClient: AleoNetworkClient | null = null; - private account: Account | null = null; - - // Program sources (for local execution) - private tokenProgram: string = ""; - private rewardsProgram: string = ""; - - // Program IDs - private readonly TOKEN_PROGRAM_ID = "loyalty_token.aleo"; - private readonly REWARDS_PROGRAM_ID = "loyalty_rewards.aleo"; - - // Feature configuration - private _provingMode: ProvingMode = ProvingMode.Local; - private _recordScanner: RecordScanner | null = null; - private _dpsUrl?: string; - private _dpsApiKey?: string; - private _consumerId?: string; - private _dpsPrivacy: boolean = false; - private _rssPrivacy: boolean = false; - - // Transaction confirmation polling configuration - private readonly TX_POLL_INTERVAL_MS = 5000; - private readonly TX_TIMEOUT_MS = 300000; // 5 minutes - - // Status callback for UI updates - private _onStatus: ((event: StatusEvent) => void) | null = null; - - /** - * Create a new LoyaltyProgram instance. - * - * @param account - Optional account for network operations - * @param config - Optional configuration for proving mode and record scanner - */ - constructor(account?: Account, config?: ProgramConfig) { - // Always use the standard API endpoint for ProgramManager state queries - // (edition lookups, inclusion proofs, etc.). The DPS URL is only needed for - // submitProvingRequest() — passing it here doubles the /testnet/ path segment. - const hostUrl = "https://api.provable.com/v2"; - this.programManager = new ProgramManager(hostUrl); - - this.keyProvider = new AleoKeyProvider(); - this.keyProvider.useCache(true); - this.programManager.setKeyProvider(this.keyProvider); - - if (account) { - this.account = account; - this.programManager.setAccount(account); + private programManager: ProgramManager; + private keyProvider: AleoKeyProvider; + private networkClient: AleoNetworkClient | null = null; + private account: Account | null = null; + + // Program sources (for local execution) + private tokenProgram: string = ""; + private rewardsProgram: string = ""; + + // Program IDs + private readonly TOKEN_PROGRAM_ID = "loyalty_token.aleo"; + private readonly REWARDS_PROGRAM_ID = "loyalty_rewards.aleo"; + + // Feature configuration + private _provingMode: ProvingMode = ProvingMode.Local; + private _recordScanner: RecordScanner | null = null; + private _dpsUrl?: string; + private _dpsApiKey?: string; + private _consumerId?: string; + private _dpsPrivacy: boolean = false; + private _rssPrivacy: boolean = false; + + // Transaction confirmation polling configuration + private readonly TX_POLL_INTERVAL_MS = 5000; + private readonly TX_TIMEOUT_MS = 300000; // 5 minutes + + // Status callback for UI updates + private _onStatus: ((event: StatusEvent) => void) | null = null; + + /** + * Create a new LoyaltyProgram instance. + * + * @param account - Optional account for network operations + * @param config - Optional configuration for proving mode and record scanner + */ + constructor(account?: Account, config?: ProgramConfig) { + // Always use the standard API endpoint for ProgramManager state queries + // (edition lookups, inclusion proofs, etc.). The DPS URL is only needed for + // submitProvingRequest() — passing it here doubles the /testnet/ path segment. + const hostUrl = "https://api.provable.com/v2"; + this.programManager = new ProgramManager(hostUrl); + + this.keyProvider = new AleoKeyProvider(); + this.keyProvider.useCache(true); + this.programManager.setKeyProvider(this.keyProvider); + + if (account) { + this.account = account; + this.programManager.setAccount(account); + } + + // Store network client for delegated proving (created on demand) + if (config?.dpsUrl) { + this._dpsUrl = config.dpsUrl; + } + + // Apply configuration + if (config?.provingMode) { + this._provingMode = config.provingMode; + } + + // Configure RecordScanner if URL is provided. + if (config?.recordScannerUrl) { + const apiKeyConfig = config.recordScannerApiKey?.startsWith("eyJ") + ? { + header: "Authorization", + value: `Bearer ${config.recordScannerApiKey}`, + } + : config.recordScannerApiKey; + this._recordScanner = new RecordScanner({ + url: config.recordScannerUrl, + apiKey: apiKeyConfig, + }); + } + + if (config?.dpsUrl) { + this._dpsUrl = config.dpsUrl; + } + if (config?.dpsApiKey) { + this._dpsApiKey = config.dpsApiKey; + } + if (config?.consumerId) { + this._consumerId = config.consumerId; + } + if (config?.dpsPrivacy) { + this._dpsPrivacy = config.dpsPrivacy; + } + if (config?.rssPrivacy) { + this._rssPrivacy = config.rssPrivacy; + } } - // Store network client for delegated proving (created on demand) - if (config?.dpsUrl) { - this._dpsUrl = config.dpsUrl; + /** + * Get the current proving mode. + */ + get provingMode(): ProvingMode { + return this._provingMode; } - // Apply configuration - if (config?.provingMode) { - this._provingMode = config.provingMode; + /** + * Check if a record scanner is configured. + */ + get hasRecordScanner(): boolean { + return this._recordScanner !== null; } - // Configure RecordScanner if URL is provided. - if (config?.recordScannerUrl) { - const apiKeyConfig = config.recordScannerApiKey?.startsWith("eyJ") - ? { header: "Authorization", value: `Bearer ${config.recordScannerApiKey}` } - : config.recordScannerApiKey; - this._recordScanner = new RecordScanner({ - url: config.recordScannerUrl, - apiKey: apiKeyConfig, - }); + /** + * Set a callback for status events (for UI feedback). + */ + onStatus(callback: (event: StatusEvent) => void): void { + this._onStatus = callback; } - if (config?.dpsUrl) { - this._dpsUrl = config.dpsUrl; + private emitStatus(event: StatusEvent): void { + if (this._onStatus) { + this._onStatus(event); + } + // Also log to console for debugging. + console.log(`[LoyaltyProgram] ${event.type}:`, event); } - if (config?.dpsApiKey) { - this._dpsApiKey = config.dpsApiKey; + + /** + * Set the proving mode (local or delegated). + * + * @param mode - The proving mode to use + */ + setProvingMode(mode: ProvingMode): void { + if (mode !== this._provingMode) { + this._provingMode = mode; + this.emitStatus({ type: "mode_changed", mode }); + } } - if (config?.consumerId) { - this._consumerId = config.consumerId; + + /** + * Configure the RSS record scanner for discovering records on-chain. + * + * @param url - The record scanner service URL + * @param apiKey - Optional API key or JWT token (JWT tokens start with "eyJ") + */ + setRecordScanner(url: string, apiKey?: string): void { + // JWT tokens (start with "eyJ") use Authorization header. + // Plain API keys use X-Provable-API-Key header. + const apiKeyConfig = apiKey?.startsWith("eyJ") + ? { header: "Authorization", value: `Bearer ${apiKey}` } + : apiKey; + this._recordScanner = new RecordScanner({ url, apiKey: apiKeyConfig }); } - if (config?.dpsPrivacy) { - this._dpsPrivacy = config.dpsPrivacy; + + /** + * Set the program sources for local execution. + * Required before calling any execution methods in local mode. + * + * @param tokenProgram - The loyalty_token.aleo program source + * @param rewardsProgram - The loyalty_rewards.aleo program source + */ + setPrograms(tokenProgram: string, rewardsProgram: string): void { + this.tokenProgram = tokenProgram; + this.rewardsProgram = rewardsProgram; } - if (config?.rssPrivacy) { - this._rssPrivacy = config.rssPrivacy; + + /** + * Set the account for transactions. + * + * @param account - The Aleo account to use + */ + setAccount(account: Account): void { + this.account = account; + this.programManager.setAccount(account); } - } - - /** - * Get the current proving mode. - */ - get provingMode(): ProvingMode { - return this._provingMode; - } - - /** - * Check if a record scanner is configured. - */ - get hasRecordScanner(): boolean { - return this._recordScanner !== null; - } - - /** - * Set a callback for status events (for UI feedback). - */ - onStatus(callback: (event: StatusEvent) => void): void { - this._onStatus = callback; - } - - private emitStatus(event: StatusEvent): void { - if (this._onStatus) { - this._onStatus(event); + + // ========================================================================== + // Record Discovery (using RecordScanner) + // ========================================================================== + + /** + * Find LoyaltyCard records owned by this account using the RecordScanner service. + * + * @param startHeight - The block height to start scanning from + * @param endHeight - Optional end block height (defaults to latest) + * @returns Array of LoyaltyCard records + */ + async findMyCards( + startHeight: number = 0, + endHeight?: number, + ): Promise { + if (!this._recordScanner) { + throw new Error( + "No record scanner configured. Use setRecordScanner() to configure.", + ); + } + if (!this.account) { + throw new Error("Account not set. Use setAccount() first."); + } + + this.emitStatus({ + type: "scan_start", + operation: "findMyCards", + }); + + // Register with the scanner. + if (this._rssPrivacy) { + await this._recordScanner.registerEncrypted( + this.account.viewKey(), + startHeight, + ); + } else { + await this._recordScanner.register( + this.account.viewKey(), + startHeight, + ); + } + + const records = await this._recordScanner.findRecords({ + decrypt: true, + unspent: true, + filter: { + start: startHeight, + end: endHeight, + program: this.TOKEN_PROGRAM_ID, + record: "LoyaltyCard", + }, + }); + + const cards = records + .filter( + (r) => + r.record_name === "LoyaltyCard" && + (r.record_plaintext || r.record_ciphertext), + ) + .map((r) => { + if (r.record_plaintext) { + return this.parseCard(r.record_plaintext); + } + const ciphertext = RecordCiphertext.fromString( + r.record_ciphertext!, + ); + const plaintext = ciphertext.decrypt(this.account!.viewKey()); + return this.parseCard(plaintext.toString()); + }); + + this.emitStatus({ + type: "scan_complete", + operation: "findMyCards", + count: cards.length, + }); + + return cards; } - // Also log to console for debugging. - console.log(`[LoyaltyProgram] ${event.type}:`, event); - } - - /** - * Set the proving mode (local or delegated). - * - * @param mode - The proving mode to use - */ - setProvingMode(mode: ProvingMode): void { - if (mode !== this._provingMode) { - this._provingMode = mode; - this.emitStatus({ type: "mode_changed", mode }); + + /** + * Find RewardVoucher records owned by this account. + * + * @param startHeight - The block height to start scanning from + * @param endHeight - Optional end block height (defaults to latest) + * @returns Array of RewardVoucher records + */ + async findMyVouchers( + startHeight: number = 0, + endHeight?: number, + ): Promise { + if (!this._recordScanner) { + throw new Error( + "No record scanner configured. Use setRecordScanner() to configure.", + ); + } + if (!this.account) { + throw new Error("Account not set. Use setAccount() first."); + } + + this.emitStatus({ + type: "scan_start", + operation: "findMyVouchers", + }); + + // Register with the scanner. + if (this._rssPrivacy) { + await this._recordScanner.registerEncrypted( + this.account.viewKey(), + startHeight, + ); + } else { + await this._recordScanner.register( + this.account.viewKey(), + startHeight, + ); + } + + const records = await this._recordScanner.findRecords({ + decrypt: true, + unspent: true, + filter: { + start: startHeight, + end: endHeight, + program: this.REWARDS_PROGRAM_ID, + record: "RewardVoucher", + }, + }); + + const vouchers = records + .filter( + (r) => + r.record_name === "RewardVoucher" && + (r.record_plaintext || r.record_ciphertext), + ) + .map((r) => { + if (r.record_plaintext) { + return this.parseVoucher(r.record_plaintext); + } + const ciphertext = RecordCiphertext.fromString( + r.record_ciphertext!, + ); + const plaintext = ciphertext.decrypt(this.account!.viewKey()); + return this.parseVoucher(plaintext.toString()); + }); + + this.emitStatus({ + type: "scan_complete", + operation: "findMyVouchers", + count: vouchers.length, + }); + + return vouchers; } - } - - /** - * Configure the RSS record scanner for discovering records on-chain. - * - * @param url - The record scanner service URL - * @param apiKey - Optional API key or JWT token (JWT tokens start with "eyJ") - */ - setRecordScanner(url: string, apiKey?: string): void { - // JWT tokens (start with "eyJ") use Authorization header. - // Plain API keys use X-Provable-API-Key header. - const apiKeyConfig = apiKey?.startsWith("eyJ") - ? { header: "Authorization", value: `Bearer ${apiKey}` } - : apiKey; - this._recordScanner = new RecordScanner({ url, apiKey: apiKeyConfig }); - } - - /** - * Set the program sources for local execution. - * Required before calling any execution methods in local mode. - * - * @param tokenProgram - The loyalty_token.aleo program source - * @param rewardsProgram - The loyalty_rewards.aleo program source - */ - setPrograms(tokenProgram: string, rewardsProgram: string): void { - this.tokenProgram = tokenProgram; - this.rewardsProgram = rewardsProgram; - } - - /** - * Set the account for transactions. - * - * @param account - The Aleo account to use - */ - setAccount(account: Account): void { - this.account = account; - this.programManager.setAccount(account); - } - - // ========================================================================== - // Record Discovery (using RecordScanner) - // ========================================================================== - - /** - * Find LoyaltyCard records owned by this account using the RecordScanner service. - * - * @param startHeight - The block height to start scanning from - * @param endHeight - Optional end block height (defaults to latest) - * @returns Array of LoyaltyCard records - */ - async findMyCards(startHeight: number = 0, endHeight?: number): Promise { - if (!this._recordScanner) { - throw new Error("No record scanner configured. Use setRecordScanner() to configure."); + + // ========================================================================== + // Card Operations (loyalty_token.aleo) + // ========================================================================== + + /** + * Mint a new loyalty card with hash-generated unique ID. + * + * @param recipient - The address to receive the card + * @param initialPoints - Starting points on the card + * @param nonce - Optional nonce for uniqueness (auto-generated if not provided) + * @returns The newly minted LoyaltyCard + * + * @example + * const card = await loyalty.mintCard("aleo1abc...xyz", 1000); + * console.log(`Card minted with ${card.points} points`); + */ + async mintCard( + recipient: string, + initialPoints: number, + nonce?: string, + ): Promise { + const actualNonce = + nonce ?? Math.floor(Math.random() * 1000000000).toString(); + const inputs = [ + recipient, + `${initialPoints}u64`, + `${actualNonce}field`, + ]; + + const outputs = await this.execute( + this.tokenProgram, + "mint_card", + inputs, + ); + + return this.parseCard(outputs[0]); } - if (!this.account) { - throw new Error("Account not set. Use setAccount() first."); + + /** + * Add points to an existing loyalty card. + * Consumes the old card and creates a new one with updated points. + * + * @param card - The card to add points to + * @param pointsToAdd - Number of points to add + * @returns The updated LoyaltyCard with new points and potentially upgraded tier + * + * @example + * const updatedCard = await loyalty.addPoints(card, 500); + * console.log(`New balance: ${updatedCard.points}, Tier: ${CardTier[updatedCard.tier]}`); + */ + async addPoints( + card: LoyaltyCard, + pointsToAdd: number, + ): Promise { + console.log("[LoyaltyProgram] addPoints called with card:", card); + console.log("[LoyaltyProgram] card.raw:", card.raw); + const inputs = [card.raw, `${pointsToAdd}u64`]; + console.log("[LoyaltyProgram] inputs:", inputs); + + const outputs = await this.execute( + this.tokenProgram, + "add_points", + inputs, + ); + + return this.parseCard(outputs[0]); } - this.emitStatus({ - type: "scan_start", - operation: "findMyCards", - }); + /** + * Check card points without consuming the record. + * + * @param card - The card to check + * @returns The same card (for chaining) and points value + * + * @example + * const { card, points } = await loyalty.checkPoints(myCard); + */ + async checkPoints( + card: LoyaltyCard, + ): Promise<{ card: LoyaltyCard; points: number }> { + const inputs = [card.raw]; + + const outputs = await this.execute( + this.tokenProgram, + "check_points", + inputs, + ); + + return { + card: this.parseCard(outputs[0]), + points: this.parseU64(outputs[1]), + }; + } - // Register with the scanner. - if (this._rssPrivacy) { - await this._recordScanner.registerEncrypted(this.account.viewKey(), startHeight); - } else { - await this._recordScanner.register(this.account.viewKey(), startHeight); + /** + * Transfer a loyalty card to a new owner. + * + * @param card - The card to transfer + * @param newOwner - The address of the new owner + * @returns The transferred card with new owner + * + * @example + * const transferredCard = await loyalty.transferCard(card, "aleo1new...owner"); + */ + async transferCard( + card: LoyaltyCard, + newOwner: string, + ): Promise { + const inputs = [card.raw, newOwner]; + + const outputs = await this.execute( + this.tokenProgram, + "transfer_card", + inputs, + ); + + return this.parseCard(outputs[0]); } - const records = await this._recordScanner.findRecords({ - decrypt: true, - unspent: true, - filter: { - start: startHeight, - end: endHeight, - program: this.TOKEN_PROGRAM_ID, - record: "LoyaltyCard", - }, - }); - - const cards = records - .filter((r) => r.record_name === "LoyaltyCard" && (r.record_plaintext || r.record_ciphertext)) - .map((r) => { - if (r.record_plaintext) { - return this.parseCard(r.record_plaintext); + /** + * Split a loyalty card into two cards with a deterministic nonce. + * Uses BHP256 hash of the record for the split card's ID, preventing + * caller-provided nonce manipulation. + * + * @param card - The card to split. + * @param pointsToKeep - Points to retain on the original card. + * @returns Object containing the kept card and the new split card. + * + * @example + * const { keptCard, splitCard } = await loyalty.splitCardV2(card, 3000); + */ + async splitCardV2( + card: LoyaltyCard, + pointsToKeep: number, + ): Promise { + if (pointsToKeep >= card.points) { + throw new Error( + `pointsToKeep (${pointsToKeep}) must be less than card points (${card.points})`, + ); } - const ciphertext = RecordCiphertext.fromString(r.record_ciphertext!); - const plaintext = ciphertext.decrypt(this.account!.viewKey()); - return this.parseCard(plaintext.toString()); - }); - - this.emitStatus({ - type: "scan_complete", - operation: "findMyCards", - count: cards.length, - }); - - return cards; - } - - /** - * Find RewardVoucher records owned by this account. - * - * @param startHeight - The block height to start scanning from - * @param endHeight - Optional end block height (defaults to latest) - * @returns Array of RewardVoucher records - */ - async findMyVouchers(startHeight: number = 0, endHeight?: number): Promise { - if (!this._recordScanner) { - throw new Error("No record scanner configured. Use setRecordScanner() to configure."); - } - if (!this.account) { - throw new Error("Account not set. Use setAccount() first."); - } - this.emitStatus({ - type: "scan_start", - operation: "findMyVouchers", - }); + const inputs = [card.raw, `${pointsToKeep}u64`]; - // Register with the scanner. - if (this._rssPrivacy) { - await this._recordScanner.registerEncrypted(this.account.viewKey(), startHeight); - } else { - await this._recordScanner.register(this.account.viewKey(), startHeight); + const outputs = await this.execute( + this.tokenProgram, + "split_card_v2", + inputs, + ); + + return { + keptCard: this.parseCard(outputs[0]), + splitCard: this.parseCard(outputs[1]), + }; } - const records = await this._recordScanner.findRecords({ - decrypt: true, - unspent: true, - filter: { - start: startHeight, - end: endHeight, - program: this.REWARDS_PROGRAM_ID, - record: "RewardVoucher", - }, - }); - - const vouchers = records - .filter((r) => r.record_name === "RewardVoucher" && (r.record_plaintext || r.record_ciphertext)) - .map((r) => { - if (r.record_plaintext) { - return this.parseVoucher(r.record_plaintext); + // ========================================================================== + // Voucher Operations (loyalty_rewards.aleo) + // ========================================================================== + + /** + * Redeem points for a reward voucher. + * This is a cross-program operation that consumes points and creates a voucher. + * + * @param card - The card to redeem points from + * @param rewardType - Type of reward (Discount, Freebie, or Upgrade) + * @param pointsCost - Number of points to spend + * @returns Object containing updated card and new voucher + * + * @example + * const { card, voucher } = await loyalty.redeemForVoucher( + * myCard, + * RewardType.Discount, + * 500 + * ); + * console.log(`Voucher value: ${voucher.value}`); + */ + async redeemForVoucher( + card: LoyaltyCard, + rewardType: RewardType, + pointsCost: number, + ): Promise { + if (card.points < pointsCost) { + throw new Error( + `Insufficient points: have ${card.points}, need ${pointsCost}`, + ); } - const ciphertext = RecordCiphertext.fromString(r.record_ciphertext!); - const plaintext = ciphertext.decrypt(this.account!.viewKey()); - return this.parseVoucher(plaintext.toString()); - }); - - this.emitStatus({ - type: "scan_complete", - operation: "findMyVouchers", - count: vouchers.length, - }); - - return vouchers; - } - - // ========================================================================== - // Card Operations (loyalty_token.aleo) - // ========================================================================== - - /** - * Mint a new loyalty card with hash-generated unique ID. - * - * @param recipient - The address to receive the card - * @param initialPoints - Starting points on the card - * @param nonce - Optional nonce for uniqueness (auto-generated if not provided) - * @returns The newly minted LoyaltyCard - * - * @example - * const card = await loyalty.mintCard("aleo1abc...xyz", 1000); - * console.log(`Card minted with ${card.points} points`); - */ - async mintCard( - recipient: string, - initialPoints: number, - nonce?: string - ): Promise { - const actualNonce = nonce ?? Math.floor(Math.random() * 1000000000).toString(); - const inputs = [recipient, `${initialPoints}u64`, `${actualNonce}field`]; - - const outputs = await this.execute( - this.tokenProgram, - "mint_card", - inputs - ); - return this.parseCard(outputs[0]); - } - - /** - * Add points to an existing loyalty card. - * Consumes the old card and creates a new one with updated points. - * - * @param card - The card to add points to - * @param pointsToAdd - Number of points to add - * @returns The updated LoyaltyCard with new points and potentially upgraded tier - * - * @example - * const updatedCard = await loyalty.addPoints(card, 500); - * console.log(`New balance: ${updatedCard.points}, Tier: ${CardTier[updatedCard.tier]}`); - */ - async addPoints(card: LoyaltyCard, pointsToAdd: number): Promise { - console.log("[LoyaltyProgram] addPoints called with card:", card); - console.log("[LoyaltyProgram] card.raw:", card.raw); - const inputs = [card.raw, `${pointsToAdd}u64`]; - console.log("[LoyaltyProgram] inputs:", inputs); - - const outputs = await this.execute( - this.tokenProgram, - "add_points", - inputs - ); + const inputs = [card.raw, `${rewardType}u8`, `${pointsCost}u64`]; - return this.parseCard(outputs[0]); - } - - /** - * Check card points without consuming the record. - * - * @param card - The card to check - * @returns The same card (for chaining) and points value - * - * @example - * const { card, points } = await loyalty.checkPoints(myCard); - */ - async checkPoints(card: LoyaltyCard): Promise<{ card: LoyaltyCard; points: number }> { - const inputs = [card.raw]; - - const outputs = await this.execute( - this.tokenProgram, - "check_points", - inputs - ); + // Multi-program execution requires imports. + const outputs = await this.execute( + this.rewardsProgram, + "redeem_points_for_voucher", + inputs, + { [this.TOKEN_PROGRAM_ID]: this.tokenProgram }, + ); - return { - card: this.parseCard(outputs[0]), - points: this.parseU64(outputs[1]), - }; - } - - /** - * Transfer a loyalty card to a new owner. - * - * @param card - The card to transfer - * @param newOwner - The address of the new owner - * @returns The transferred card with new owner - * - * @example - * const transferredCard = await loyalty.transferCard(card, "aleo1new...owner"); - */ - async transferCard(card: LoyaltyCard, newOwner: string): Promise { - const inputs = [card.raw, newOwner]; - - const outputs = await this.execute( - this.tokenProgram, - "transfer_card", - inputs - ); + return { + card: this.parseCard(outputs[0]), + voucher: this.parseVoucher(outputs[1]), + }; + } - return this.parseCard(outputs[0]); - } - - /** - * Split a loyalty card into two cards with a deterministic nonce. - * Uses BHP256 hash of the record for the split card's ID, preventing - * caller-provided nonce manipulation. - * - * @param card - The card to split. - * @param pointsToKeep - Points to retain on the original card. - * @returns Object containing the kept card and the new split card. - * - * @example - * const { keptCard, splitCard } = await loyalty.splitCardV2(card, 3000); - */ - async splitCardV2(card: LoyaltyCard, pointsToKeep: number): Promise { - if (pointsToKeep >= card.points) { - throw new Error( - `pointsToKeep (${pointsToKeep}) must be less than card points (${card.points})` - ); + /** + * Use (burn) a voucher. + * The voucher record is consumed and cannot be used again. + * + * @param voucher - The voucher to use + * + * @example + * await loyalty.useVoucher(myVoucher); + * console.log("Voucher redeemed!"); + */ + async useVoucher(voucher: RewardVoucher): Promise { + const inputs = [voucher.raw]; + + await this.execute(this.rewardsProgram, "use_voucher", inputs, { + [this.TOKEN_PROGRAM_ID]: this.tokenProgram, + }); } - const inputs = [card.raw, `${pointsToKeep}u64`]; + /** + * Check voucher details without consuming. + * + * @param voucher - The voucher to check + * @returns The voucher with its type and value + */ + async checkVoucher(voucher: RewardVoucher): Promise<{ + voucher: RewardVoucher; + rewardType: RewardType; + value: number; + }> { + const inputs = [voucher.raw]; + + const outputs = await this.execute( + this.rewardsProgram, + "check_voucher", + inputs, + { [this.TOKEN_PROGRAM_ID]: this.tokenProgram }, + ); + + return { + voucher: this.parseVoucher(outputs[0]), + rewardType: this.parseU8(outputs[1]) as RewardType, + value: this.parseU64(outputs[2]), + }; + } - const outputs = await this.execute( - this.tokenProgram, - "split_card_v2", - inputs - ); + /** + * Transfer a voucher to a new owner. + * + * @param voucher - The voucher to transfer + * @param newOwner - The address of the new owner + * @returns The transferred voucher + */ + async transferVoucher( + voucher: RewardVoucher, + newOwner: string, + ): Promise { + const inputs = [voucher.raw, newOwner]; + + const outputs = await this.execute( + this.rewardsProgram, + "transfer_voucher", + inputs, + { [this.TOKEN_PROGRAM_ID]: this.tokenProgram }, + ); + + return this.parseVoucher(outputs[0]); + } - return { - keptCard: this.parseCard(outputs[0]), - splitCard: this.parseCard(outputs[1]), - }; - } - - // ========================================================================== - // Voucher Operations (loyalty_rewards.aleo) - // ========================================================================== - - /** - * Redeem points for a reward voucher. - * This is a cross-program operation that consumes points and creates a voucher. - * - * @param card - The card to redeem points from - * @param rewardType - Type of reward (Discount, Freebie, or Upgrade) - * @param pointsCost - Number of points to spend - * @returns Object containing updated card and new voucher - * - * @example - * const { card, voucher } = await loyalty.redeemForVoucher( - * myCard, - * RewardType.Discount, - * 500 - * ); - * console.log(`Voucher value: ${voucher.value}`); - */ - async redeemForVoucher( - card: LoyaltyCard, - rewardType: RewardType, - pointsCost: number - ): Promise { - if (card.points < pointsCost) { - throw new Error( - `Insufficient points: have ${card.points}, need ${pointsCost}` - ); + // ========================================================================== + // Mapping Reads (requires network client) + // ========================================================================== + + /** + * Get total number of cards minted. + * + * @returns Total cards count, or null if not available + */ + async getTotalCards(): Promise { + return this.readMappingU64( + this.TOKEN_PROGRAM_ID, + "total_cards", + "0field", + ); } - const inputs = [card.raw, `${rewardType}u8`, `${pointsCost}u64`]; + /** + * Get total points issued across all cards. + * + * @returns Total points issued, or null if not available + */ + async getTotalPointsIssued(): Promise { + return this.readMappingU64( + this.TOKEN_PROGRAM_ID, + "total_points_issued", + "0field", + ); + } - // Multi-program execution requires imports. - const outputs = await this.execute( - this.rewardsProgram, - "redeem_points_for_voucher", - inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } - ); + /** + * Check if a card ID exists in the registry. + * + * @param cardId - The card ID to check + * @returns true if exists, false otherwise + */ + async cardExists(cardId: string): Promise { + const value = await this.readMapping( + this.TOKEN_PROGRAM_ID, + "card_exists", + cardId, + ); + return value === "true"; + } - return { - card: this.parseCard(outputs[0]), - voucher: this.parseVoucher(outputs[1]), - }; - } - - /** - * Use (burn) a voucher. - * The voucher record is consumed and cannot be used again. - * - * @param voucher - The voucher to use - * - * @example - * await loyalty.useVoucher(myVoucher); - * console.log("Voucher redeemed!"); - */ - async useVoucher(voucher: RewardVoucher): Promise { - const inputs = [voucher.raw]; - - await this.execute( - this.rewardsProgram, - "use_voucher", - inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } - ); - } - - /** - * Check voucher details without consuming. - * - * @param voucher - The voucher to check - * @returns The voucher with its type and value - */ - async checkVoucher( - voucher: RewardVoucher - ): Promise<{ voucher: RewardVoucher; rewardType: RewardType; value: number }> { - const inputs = [voucher.raw]; - - const outputs = await this.execute( - this.rewardsProgram, - "check_voucher", - inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } - ); + /** + * Check if a voucher has been used. + * + * @param voucherId - The voucher ID to check + * @returns true if used, false otherwise + */ + async isVoucherUsed(voucherId: string): Promise { + const value = await this.readMapping( + this.REWARDS_PROGRAM_ID, + "voucher_used", + voucherId, + ); + return value === "true"; + } - return { - voucher: this.parseVoucher(outputs[0]), - rewardType: this.parseU8(outputs[1]) as RewardType, - value: this.parseU64(outputs[2]), - }; - } - - /** - * Transfer a voucher to a new owner. - * - * @param voucher - The voucher to transfer - * @param newOwner - The address of the new owner - * @returns The transferred voucher - */ - async transferVoucher( - voucher: RewardVoucher, - newOwner: string - ): Promise { - const inputs = [voucher.raw, newOwner]; - - const outputs = await this.execute( - this.rewardsProgram, - "transfer_voucher", - inputs, - { [this.TOKEN_PROGRAM_ID]: this.tokenProgram } - ); + /** + * Get redemption count for a specific reward type. + * + * @param rewardType - The reward type to query + * @returns Number of redemptions, or null if not available + */ + async getRedemptionCount(rewardType: RewardType): Promise { + return this.readMappingU64( + this.REWARDS_PROGRAM_ID, + "redemptions_by_type", + `${rewardType}field`, + ); + } - return this.parseVoucher(outputs[0]); - } - - // ========================================================================== - // Mapping Reads (requires network client) - // ========================================================================== - - /** - * Get total number of cards minted. - * - * @returns Total cards count, or null if not available - */ - async getTotalCards(): Promise { - return this.readMappingU64(this.TOKEN_PROGRAM_ID, "total_cards", "0field"); - } - - /** - * Get total points issued across all cards. - * - * @returns Total points issued, or null if not available - */ - async getTotalPointsIssued(): Promise { - return this.readMappingU64( - this.TOKEN_PROGRAM_ID, - "total_points_issued", - "0field" - ); - } - - /** - * Check if a card ID exists in the registry. - * - * @param cardId - The card ID to check - * @returns true if exists, false otherwise - */ - async cardExists(cardId: string): Promise { - const value = await this.readMapping( - this.TOKEN_PROGRAM_ID, - "card_exists", - cardId - ); - return value === "true"; - } - - /** - * Check if a voucher has been used. - * - * @param voucherId - The voucher ID to check - * @returns true if used, false otherwise - */ - async isVoucherUsed(voucherId: string): Promise { - const value = await this.readMapping( - this.REWARDS_PROGRAM_ID, - "voucher_used", - voucherId - ); - return value === "true"; - } - - /** - * Get redemption count for a specific reward type. - * - * @param rewardType - The reward type to query - * @returns Number of redemptions, or null if not available - */ - async getRedemptionCount(rewardType: RewardType): Promise { - return this.readMappingU64( - this.REWARDS_PROGRAM_ID, - "redemptions_by_type", - `${rewardType}field` - ); - } - - /** - * Get all program statistics. - * - * @returns Combined stats from all mappings - */ - async getStats(): Promise { - const [totalCards, totalPoints, discounts, freebies, upgrades] = - await Promise.all([ - this.getTotalCards(), - this.getTotalPointsIssued(), - this.getRedemptionCount(RewardType.Discount), - this.getRedemptionCount(RewardType.Freebie), - this.getRedemptionCount(RewardType.Upgrade), - ]); + /** + * Get all program statistics. + * + * @returns Combined stats from all mappings + */ + async getStats(): Promise { + const [totalCards, totalPoints, discounts, freebies, upgrades] = + await Promise.all([ + this.getTotalCards(), + this.getTotalPointsIssued(), + this.getRedemptionCount(RewardType.Discount), + this.getRedemptionCount(RewardType.Freebie), + this.getRedemptionCount(RewardType.Upgrade), + ]); + + return { + totalCards: totalCards ?? 0, + totalPointsIssued: totalPoints ?? 0, + redemptionsByType: { + [RewardType.Discount]: discounts ?? 0, + [RewardType.Freebie]: freebies ?? 0, + [RewardType.Upgrade]: upgrades ?? 0, + }, + }; + } - return { - totalCards: totalCards ?? 0, - totalPointsIssued: totalPoints ?? 0, - redemptionsByType: { - [RewardType.Discount]: discounts ?? 0, - [RewardType.Freebie]: freebies ?? 0, - [RewardType.Upgrade]: upgrades ?? 0, - }, - }; - } - - // ========================================================================== - // Private Execution Methods - // ========================================================================== - - private async execute( - program: string, - functionName: string, - inputs: string[], - imports?: Record - ): Promise { - this.emitStatus({ - type: "operation_start", - operation: functionName, - mode: this._provingMode, - }); - - const start = Date.now(); - let outputs: string[]; - - try { - if (this._provingMode === ProvingMode.Delegated) { - outputs = await this.executeDelegated(program, functionName, inputs, imports); - } else { - outputs = await this.executeLocal(program, functionName, inputs, imports); - } - - this.emitStatus({ - type: "operation_complete", - operation: functionName, - mode: this._provingMode, - duration: Date.now() - start, - }); - - return outputs; - } catch (error: any) { - this.emitStatus({ - type: "error", - operation: functionName, - error: error.message, - }); - throw error; + // ========================================================================== + // Private Execution Methods + // ========================================================================== + + private async execute( + program: string, + functionName: string, + inputs: string[], + imports?: Record, + ): Promise { + this.emitStatus({ + type: "operation_start", + operation: functionName, + mode: this._provingMode, + }); + + const start = Date.now(); + let outputs: string[]; + + try { + if (this._provingMode === ProvingMode.Delegated) { + outputs = await this.executeDelegated( + program, + functionName, + inputs, + imports, + ); + } else { + outputs = await this.executeLocal( + program, + functionName, + inputs, + imports, + ); + } + + this.emitStatus({ + type: "operation_complete", + operation: functionName, + mode: this._provingMode, + duration: Date.now() - start, + }); + + return outputs; + } catch (error: any) { + this.emitStatus({ + type: "error", + operation: functionName, + error: error.message, + }); + throw error; + } } - } - - /** - * Execute a function locally using run() with OfflineQuery. - * Uses OfflineQuery to prevent network state lookups for locally-created records. - */ - private async executeLocal( - program: string, - functionName: string, - inputs: string[], - imports?: Record - ): Promise { - console.log(`Starting ${functionName} execution (LOCAL)`); - - // Create temporary account if none set. - if (!this.account) { - const tempAccount = new Account(); - this.account = tempAccount; - this.programManager.setAccount(tempAccount); + + /** + * Execute a function locally using run() with OfflineQuery. + * Uses OfflineQuery to prevent network state lookups for locally-created records. + */ + private async executeLocal( + program: string, + functionName: string, + inputs: string[], + imports?: Record, + ): Promise { + console.log(`Starting ${functionName} execution (LOCAL)`); + + // Create temporary account if none set. + if (!this.account) { + const tempAccount = new Account(); + this.account = tempAccount; + this.programManager.setAccount(tempAccount); + } + + // Create OfflineQuery with mock state to prevent network lookups. + // This allows local execution with locally-created records. + const offlineQuery = new OfflineQuery( + 0, // block height + "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu", // mock state root + ); + + // Execute locally using run() with offlineQuery - no network lookups. + // Keys will be synthesized internally by the WASM using the offline query. + const executionResponse = await this.programManager.run( + program, + functionName, + inputs, + false, // proveExecution - false for local demo + imports, + undefined, // keySearchParams + undefined, // provingKey + undefined, // verifyingKey + undefined, // privateKey + offlineQuery, + ); + + const outputs = executionResponse.getOutputs(); + console.log("Outputs:", outputs); + + return outputs; } - // Create OfflineQuery with mock state to prevent network lookups. - // This allows local execution with locally-created records. - const offlineQuery = new OfflineQuery( - 0, // block height - "sr1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gk0xu" // mock state root - ); + /** + * Wait for a transaction to be confirmed on-chain. + * + * @param txId - The transaction ID to wait for + * @returns The confirmed transaction + */ + private async waitForTransaction(txId: string): Promise { + const startTime = Date.now(); + const shortId = txId.slice(0, 16); + + console.log(`[LoyaltyProgram] Broadcasting: ${shortId}...`); + + // Use explorer API for transaction lookups (not DPS). + const explorerClient = new AleoNetworkClient( + "https://api.explorer.provable.com/v1", + ); + + while (Date.now() - startTime < this.TX_TIMEOUT_MS) { + try { + const tx = await explorerClient.getTransaction(txId); + if (tx) { + const elapsed = ((Date.now() - startTime) / 1000).toFixed( + 1, + ); + console.log(`[LoyaltyProgram] Confirmed in ${elapsed}s`); + return tx; + } + } catch { + // Transaction not found yet, continue polling. + } - // Execute locally using run() with offlineQuery - no network lookups. - // Keys will be synthesized internally by the WASM using the offline query. - const executionResponse = await this.programManager.run( - program, - functionName, - inputs, - false, // proveExecution - false for local demo - imports, - undefined, // keySearchParams - undefined, // provingKey - undefined, // verifyingKey - undefined, // privateKey - offlineQuery - ); + // Log progress every poll. + const elapsed = Math.round((Date.now() - startTime) / 1000); + console.log( + `[LoyaltyProgram] Waiting for confirmation... (${elapsed}s)`, + ); - const outputs = executionResponse.getOutputs(); - console.log("Outputs:", outputs); - - return outputs; - } - - /** - * Wait for a transaction to be confirmed on-chain. - * - * @param txId - The transaction ID to wait for - * @returns The confirmed transaction - */ - private async waitForTransaction(txId: string): Promise { - const startTime = Date.now(); - const shortId = txId.slice(0, 16); - - console.log(`[LoyaltyProgram] Broadcasting: ${shortId}...`); - - // Use explorer API for transaction lookups (not DPS). - const explorerClient = new AleoNetworkClient("https://api.explorer.provable.com/v1"); - - while (Date.now() - startTime < this.TX_TIMEOUT_MS) { - try { - const tx = await explorerClient.getTransaction(txId); - if (tx) { - const elapsed = ((Date.now() - startTime) / 1000).toFixed(1); - console.log(`[LoyaltyProgram] Confirmed in ${elapsed}s`); - return tx; + await new Promise((resolve) => + setTimeout(resolve, this.TX_POLL_INTERVAL_MS), + ); } - } catch { - // Transaction not found yet, continue polling. - } - - // Log progress every poll. - const elapsed = Math.round((Date.now() - startTime) / 1000); - console.log(`[LoyaltyProgram] Waiting for confirmation... (${elapsed}s)`); - await new Promise(resolve => setTimeout(resolve, this.TX_POLL_INTERVAL_MS)); + throw new Error( + `Transaction ${txId} not confirmed within ${this.TX_TIMEOUT_MS / 1000}s`, + ); } - throw new Error(`Transaction ${txId} not confirmed within ${this.TX_TIMEOUT_MS / 1000}s`); - } - - /** - * Execute a function using delegated proving. - */ - private async executeDelegated( - program: string, - functionName: string, - inputs: string[], - imports?: Record - ): Promise { - if (!this._dpsUrl) { - throw new Error( - "Delegated proving requires dpsUrl to be configured. " + - "Pass dpsUrl in the config or use setProvingMode(ProvingMode.Local)." - ); - } + /** + * Execute a function using delegated proving. + */ + private async executeDelegated( + program: string, + functionName: string, + inputs: string[], + imports?: Record, + ): Promise { + if (!this._dpsUrl) { + throw new Error( + "Delegated proving requires dpsUrl to be configured. " + + "Pass dpsUrl in the config or use setProvingMode(ProvingMode.Local).", + ); + } - console.log(`Starting ${functionName} execution (DELEGATED)`); - - // Get program name from source. - const programName = Program.fromString(program).id(); - - // Build the proving request with broadcast: true to submit to chain. - const provingRequest = await this.programManager.provingRequest({ - programName, - programSource: program, - programImports: imports, - functionName, - inputs, - priorityFee: 0, - privateFee: false, - broadcast: true, - }); - - // Submit to DPS (with optional encryption via dpsPrivacy flag). - if (!this.networkClient) { - this.networkClient = new AleoNetworkClient(this._dpsUrl); - } + console.log(`Starting ${functionName} execution (DELEGATED)`); + + // Get program name from source. + const programName = Program.fromString(program).id(); + + // Build the proving request with broadcast: true to submit to chain. + const provingRequest = await this.programManager.provingRequest({ + programName, + programSource: program, + programImports: imports, + functionName, + inputs, + priorityFee: 0, + privateFee: false, + broadcast: true, + }); + + // Submit to DPS (with optional encryption via dpsPrivacy flag). + if (!this.networkClient) { + this.networkClient = new AleoNetworkClient(this._dpsUrl); + } - if (this._dpsPrivacy) { - console.log("[LoyaltyProgram] Using encrypted DPS flow (TEE-protected)"); - } + if (this._dpsPrivacy) { + console.log( + "[LoyaltyProgram] Using encrypted DPS flow (TEE-protected)", + ); + } + + const response = await this.networkClient.submitProvingRequest({ + provingRequest, + url: this._dpsUrl, + apiKey: this._dpsApiKey, + consumerId: this._consumerId, + dpsPrivacy: this._dpsPrivacy, + }); + + // Wait for transaction confirmation and extract outputs. + const txId = response.transaction?.id; + if (!txId) { + throw new Error("DPS response did not contain a transaction ID"); + } - const response = await this.networkClient.submitProvingRequest({ - provingRequest, - url: this._dpsUrl, - apiKey: this._dpsApiKey, - consumerId: this._consumerId, - dpsPrivacy: this._dpsPrivacy, - }); - - // Wait for transaction confirmation and extract outputs. - const txId = response.transaction?.id; - if (!txId) { - throw new Error("DPS response did not contain a transaction ID"); + const confirmedTx = await this.waitForTransaction(txId); + return this.extractOutputsFromTransaction(confirmedTx); } - const confirmedTx = await this.waitForTransaction(txId); - return this.extractOutputsFromTransaction(confirmedTx); - } - - private extractOutputsFromTransaction(transaction: { execution?: { transitions: Array<{ outputs?: Array<{ type?: string; value?: string }> }> } }): string[] { - // Extract and decrypt record outputs from transaction execution. - const outputs: string[] = []; - - if (transaction.execution?.transitions) { - for (const transition of transaction.execution.transitions) { - if (transition.outputs) { - for (const output of transition.outputs) { - if (output.value) { - // Check if this is an encrypted record (starts with "record1"). - if (output.type === "record" && output.value.startsWith("record1") && this.account) { - const ciphertext = RecordCiphertext.fromString(output.value); - // Only decrypt records owned by this account (e.g. transfers produce records for other owners). - if (ciphertext.isOwner(this.account.viewKey())) { - const plaintext = ciphertext.decrypt(this.account.viewKey()); - outputs.push(plaintext.toString()); + private extractOutputsFromTransaction(transaction: { + execution?: { + transitions: Array<{ + outputs?: Array<{ type?: string; value?: string }>; + }>; + }; + }): string[] { + // Extract and decrypt record outputs from transaction execution. + const outputs: string[] = []; + + if (transaction.execution?.transitions) { + for (const transition of transaction.execution.transitions) { + if (transition.outputs) { + for (const output of transition.outputs) { + if (output.value) { + // Check if this is an encrypted record (starts with "record1"). + if ( + output.type === "record" && + output.value.startsWith("record1") && + this.account + ) { + const ciphertext = RecordCiphertext.fromString( + output.value, + ); + // Only decrypt records owned by this account (e.g. transfers produce records for other owners). + if ( + ciphertext.isOwner(this.account.viewKey()) + ) { + const plaintext = ciphertext.decrypt( + this.account.viewKey(), + ); + outputs.push(plaintext.toString()); + } + } else { + outputs.push(output.value); + } + } + } } - } else { - outputs.push(output.value); - } } - } } - } + + return outputs; } - return outputs; - } - - /** - * Read a mapping value from the network. - */ - private async readMapping( - programId: string, - mappingName: string, - key: string - ): Promise { - if (!this.networkClient) { - console.warn("No network client configured for mapping reads"); - return null; + /** + * Read a mapping value from the network. + */ + private async readMapping( + programId: string, + mappingName: string, + key: string, + ): Promise { + if (!this.networkClient) { + console.warn("No network client configured for mapping reads"); + return null; + } + + try { + const value = await this.networkClient.getProgramMappingValue( + programId, + mappingName, + key, + ); + return value; + } catch (error) { + console.log(`Mapping value not found: ${error}`); + return null; + } } - try { - const value = await this.networkClient.getProgramMappingValue( - programId, - mappingName, - key - ); - return value; - } catch (error) { - console.log(`Mapping value not found: ${error}`); - return null; + /** + * Read a u64 mapping value from the network. + */ + private async readMappingU64( + programId: string, + mappingName: string, + key: string, + ): Promise { + const value = await this.readMapping(programId, mappingName, key); + if (!value) return null; + return this.parseU64(value); } - } - - /** - * Read a u64 mapping value from the network. - */ - private async readMappingU64( - programId: string, - mappingName: string, - key: string - ): Promise { - const value = await this.readMapping(programId, mappingName, key); - if (!value) return null; - return this.parseU64(value); - } - - // ========================================================================== - // Parsing Helpers - // ========================================================================== - - /** - * Parse a LoyaltyCard record from its string representation. - */ - private parseCard(recordString: string): LoyaltyCard { - const fields = this.parseRecordFields(recordString); - return { - owner: this.cleanAddress(fields.owner), - cardId: this.cleanField(fields.card_id), - points: this.parseU64(fields.points), - tier: this.parseU8(fields.tier) as CardTier, - raw: recordString, - }; - } + // ========================================================================== + // Parsing Helpers + // ========================================================================== + + /** + * Parse a LoyaltyCard record from its string representation. + */ + private parseCard(recordString: string): LoyaltyCard { + const fields = this.parseRecordFields(recordString); + + return { + owner: this.cleanAddress(fields.owner), + cardId: this.cleanField(fields.card_id), + points: this.parseU64(fields.points), + tier: this.parseU8(fields.tier) as CardTier, + raw: recordString, + }; + } - /** - * Parse a RewardVoucher record from its string representation. - */ - private parseVoucher(recordString: string): RewardVoucher { - const fields = this.parseRecordFields(recordString); + /** + * Parse a RewardVoucher record from its string representation. + */ + private parseVoucher(recordString: string): RewardVoucher { + const fields = this.parseRecordFields(recordString); + + return { + owner: this.cleanAddress(fields.owner), + voucherId: this.cleanField(fields.voucher_id), + rewardType: this.parseU8(fields.reward_type) as RewardType, + value: this.parseU64(fields.amount), // Leo record uses 'amount' field. + raw: recordString, + }; + } - return { - owner: this.cleanAddress(fields.owner), - voucherId: this.cleanField(fields.voucher_id), - rewardType: this.parseU8(fields.reward_type) as RewardType, - value: this.parseU64(fields.amount), // Leo record uses 'amount' field. - raw: recordString, - }; - } - - /** - * Parse record fields from string representation. - */ - private parseRecordFields(recordString: string): Record { - const fields: Record = {}; - const matches = recordString.matchAll(/(\w+):\s*([^,}]+)/g); - - for (const match of matches) { - const [, key, value] = match; - fields[key.trim()] = value.trim(); + /** + * Parse record fields from string representation. + */ + private parseRecordFields(recordString: string): Record { + const fields: Record = {}; + const matches = recordString.matchAll(/(\w+):\s*([^,}]+)/g); + + for (const match of matches) { + const [, key, value] = match; + fields[key.trim()] = value.trim(); + } + + return fields; } - return fields; - } - - /** - * Parse a u64 value from string (e.g., "1000u64" -> 1000). - */ - private parseU64(value: string): number { - const match = value.match(/(\d+)u64/); - return match ? parseInt(match[1], 10) : 0; - } - - /** - * Parse a u8 value from string (e.g., "2u8" -> 2). - */ - private parseU8(value: string): number { - const match = value.match(/(\d+)u8/); - return match ? parseInt(match[1], 10) : 0; - } - - /** - * Clean address string (remove .private suffix). - */ - private cleanAddress(value: string): string { - return value.replace(/\.private$/, "").trim(); - } - - /** - * Clean field string (remove field suffix). - */ - private cleanField(value: string): string { - return value.replace(/field\.private$/, "field").trim(); - } + /** + * Parse a u64 value from string (e.g., "1000u64" -> 1000). + */ + private parseU64(value: string): number { + const match = value.match(/(\d+)u64/); + return match ? parseInt(match[1], 10) : 0; + } + + /** + * Parse a u8 value from string (e.g., "2u8" -> 2). + */ + private parseU8(value: string): number { + const match = value.match(/(\d+)u8/); + return match ? parseInt(match[1], 10) : 0; + } + + /** + * Clean address string (remove .private suffix). + */ + private cleanAddress(value: string): string { + return value.replace(/\.private$/, "").trim(); + } + + /** + * Clean field string (remove field suffix). + */ + private cleanField(value: string): string { + return value.replace(/field\.private$/, "field").trim(); + } } // ============================================================================ @@ -1174,7 +1268,7 @@ class LoyaltyProgram { * This account is pre-funded on testnet for demo purposes. */ const DEMO_ACCOUNT_CIPHERTEXT = - "ciphertext1qvq283j7ujnhz59d4rnu772rfmvf94039x9ekhk2lzuutteqzlghsr3g9824qgw97a79mmdymqdt0ulqdkahq39vnerw2tl7thvvnnunq386jzjnw29e0ghnq7unphgdzw637q3fgvvlkrcywsc5jukkdhss5qq3njp"; + "ciphertext1qvq283j7ujnhz59d4rnu772rfmvf94039x9ekhk2lzuutteqzlghsr3g9824qgw97a79mmdymqdt0ulqdkahq39vnerw2tl7thvvnnunq386jzjnw29e0ghnq7unphgdzw637q3fgvvlkrcywsc5jukkdhss5qq3njp"; const DEMO_ACCOUNT_PASSWORD = "provablealeo1"; /** @@ -1182,51 +1276,54 @@ const DEMO_ACCOUNT_PASSWORD = "provablealeo1"; * This is the same account used in the Node template. */ async function loadDemoAccount(): Promise<{ - privateKey: string; - viewKey: string; - address: string; + privateKey: string; + viewKey: string; + address: string; }> { - const account = Account.fromCiphertext(DEMO_ACCOUNT_CIPHERTEXT, DEMO_ACCOUNT_PASSWORD); - getLoyaltyProgram().setAccount(account); - return { - privateKey: account.privateKey().to_string(), - viewKey: account.viewKey().to_string(), - address: account.address().to_string(), - }; + const account = Account.fromCiphertext( + DEMO_ACCOUNT_CIPHERTEXT, + DEMO_ACCOUNT_PASSWORD, + ); + getLoyaltyProgram().setAccount(account); + return { + privateKey: account.privateKey().to_string(), + viewKey: account.viewKey().to_string(), + address: account.address().to_string(), + }; } /** * Create a new account, set it on the LoyaltyProgram, and return its details. */ async function createAccount(): Promise<{ - privateKey: string; - viewKey: string; - address: string; + privateKey: string; + viewKey: string; + address: string; }> { - const account = new Account(); - // Set this account on the LoyaltyProgram so record operations work - getLoyaltyProgram().setAccount(account); - return { - privateKey: account.privateKey().to_string(), - viewKey: account.viewKey().to_string(), - address: account.address().to_string(), - }; + const account = new Account(); + // Set this account on the LoyaltyProgram so record operations work + getLoyaltyProgram().setAccount(account); + return { + privateKey: account.privateKey().to_string(), + viewKey: account.viewKey().to_string(), + address: account.address().to_string(), + }; } /** * Generate a new private key. */ async function getPrivateKey(): Promise { - const key = new PrivateKey(); - return proxy(key); + const key = new PrivateKey(); + return proxy(key); } /** * Get the current block height from the network. */ async function getBlockHeight(apiUrl: string): Promise { - const networkClient = new AleoNetworkClient(apiUrl); - return networkClient.getLatestHeight(); + const networkClient = new AleoNetworkClient(apiUrl); + return networkClient.getLatestHeight(); } /** @@ -1236,14 +1333,14 @@ async function getBlockHeight(apiUrl: string): Promise { * Get tier name from tier value. */ function getTierName(tier: CardTier): string { - return CardTier[tier] ?? "Unknown"; + return CardTier[tier] ?? "Unknown"; } /** * Get reward type name from type value. */ function getRewardTypeName(type: RewardType): string { - return RewardType[type] ?? "Unknown"; + return RewardType[type] ?? "Unknown"; } // ============================================================================ @@ -1254,15 +1351,14 @@ function getRewardTypeName(type: RewardType): string { * Create a LoyaltyProgram instance configured for local execution. */ function createLocalLoyaltyProgram( - tokenProgram: string, - rewardsProgram: string + tokenProgram: string, + rewardsProgram: string, ): LoyaltyProgram { - const loyalty = new LoyaltyProgram(); - loyalty.setPrograms(tokenProgram, rewardsProgram); - return loyalty; + const loyalty = new LoyaltyProgram(); + loyalty.setPrograms(tokenProgram, rewardsProgram); + return loyalty; } - // ============================================================================ // Worker Instance // ============================================================================ @@ -1274,113 +1370,140 @@ let loyaltyInstance: LoyaltyProgram | null = null; * Initialize the loyalty program with program sources. */ function initLoyaltyProgram( - tokenProgram: string, - rewardsProgram: string, - config?: ProgramConfig + tokenProgram: string, + rewardsProgram: string, + config?: ProgramConfig, ): boolean { - console.log("[Worker] initLoyaltyProgram called with config:", config); - if (config) { - console.log("[Worker] Creating account..."); - const account = new Account(); - console.log("[Worker] Creating LoyaltyProgram instance..."); - loyaltyInstance = new LoyaltyProgram(account, config); - console.log("[Worker] Setting programs..."); - loyaltyInstance.setPrograms(tokenProgram, rewardsProgram); - } else { - console.log("[Worker] Creating local LoyaltyProgram..."); - loyaltyInstance = createLocalLoyaltyProgram(tokenProgram, rewardsProgram); - } - console.log("[Worker] initLoyaltyProgram complete!"); - return true; + console.log("[Worker] initLoyaltyProgram called with config:", config); + if (config) { + console.log("[Worker] Creating account..."); + const account = new Account(); + console.log("[Worker] Creating LoyaltyProgram instance..."); + loyaltyInstance = new LoyaltyProgram(account, config); + console.log("[Worker] Setting programs..."); + loyaltyInstance.setPrograms(tokenProgram, rewardsProgram); + } else { + console.log("[Worker] Creating local LoyaltyProgram..."); + loyaltyInstance = createLocalLoyaltyProgram( + tokenProgram, + rewardsProgram, + ); + } + console.log("[Worker] initLoyaltyProgram complete!"); + return true; } /** * Get the current loyalty program instance. */ function getLoyaltyProgram(): LoyaltyProgram { - if (!loyaltyInstance) { - throw new Error("Loyalty program not initialized. Call initLoyaltyProgram first."); - } - return loyaltyInstance; + if (!loyaltyInstance) { + throw new Error( + "Loyalty program not initialized. Call initLoyaltyProgram first.", + ); + } + return loyaltyInstance; } /** * Get the current proving mode. */ function getProvingMode(): ProvingMode { - return getLoyaltyProgram().provingMode; + return getLoyaltyProgram().provingMode; } /** * Set the proving mode. */ function setProvingMode(mode: ProvingMode): void { - getLoyaltyProgram().setProvingMode(mode); + getLoyaltyProgram().setProvingMode(mode); } /** * Check if record scanner is configured. */ function hasRecordScanner(): boolean { - return getLoyaltyProgram().hasRecordScanner; + return getLoyaltyProgram().hasRecordScanner; } /** * Configure the RSS record scanner. */ function setRecordScanner(url: string, apiKey?: string): void { - getLoyaltyProgram().setRecordScanner(url, apiKey); + getLoyaltyProgram().setRecordScanner(url, apiKey); } // Wrapper functions that use the shared instance async function mintCard( - recipient: string, - initialPoints: number, - nonce?: string + recipient: string, + initialPoints: number, + nonce?: string, ): Promise { - const card = await getLoyaltyProgram().mintCard(recipient, initialPoints, nonce); - console.log("[Worker] mintCard returning card:", JSON.stringify(card)); - return card; + const card = await getLoyaltyProgram().mintCard( + recipient, + initialPoints, + nonce, + ); + console.log("[Worker] mintCard returning card:", JSON.stringify(card)); + return card; } -async function addPoints(card: LoyaltyCard, pointsToAdd: number): Promise { - console.log("[Worker] addPoints wrapper called with card:", JSON.stringify(card)); - return getLoyaltyProgram().addPoints(card, pointsToAdd); +async function addPoints( + card: LoyaltyCard, + pointsToAdd: number, +): Promise { + console.log( + "[Worker] addPoints wrapper called with card:", + JSON.stringify(card), + ); + return getLoyaltyProgram().addPoints(card, pointsToAdd); } async function redeemForVoucher( - card: LoyaltyCard, - rewardType: RewardType, - pointsCost: number + card: LoyaltyCard, + rewardType: RewardType, + pointsCost: number, ): Promise { - return getLoyaltyProgram().redeemForVoucher(card, rewardType, pointsCost); + return getLoyaltyProgram().redeemForVoucher(card, rewardType, pointsCost); } async function useVoucher(voucher: RewardVoucher): Promise { - return getLoyaltyProgram().useVoucher(voucher); + return getLoyaltyProgram().useVoucher(voucher); } -async function splitCardV2(card: LoyaltyCard, pointsToKeep: number): Promise { - return getLoyaltyProgram().splitCardV2(card, pointsToKeep); +async function splitCardV2( + card: LoyaltyCard, + pointsToKeep: number, +): Promise { + return getLoyaltyProgram().splitCardV2(card, pointsToKeep); } -async function transferCard(card: LoyaltyCard, newOwner: string): Promise { - return getLoyaltyProgram().transferCard(card, newOwner); +async function transferCard( + card: LoyaltyCard, + newOwner: string, +): Promise { + return getLoyaltyProgram().transferCard(card, newOwner); } async function transferVoucher( - voucher: RewardVoucher, - newOwner: string + voucher: RewardVoucher, + newOwner: string, ): Promise { - return getLoyaltyProgram().transferVoucher(voucher, newOwner); + return getLoyaltyProgram().transferVoucher(voucher, newOwner); } -async function findMyCards(startHeight?: number, endHeight?: number): Promise { - return getLoyaltyProgram().findMyCards(startHeight, endHeight); +async function findMyCards( + startHeight?: number, + endHeight?: number, +): Promise { + return getLoyaltyProgram().findMyCards(startHeight, endHeight); } -async function findMyVouchers(startHeight?: number, endHeight?: number): Promise { - return getLoyaltyProgram().findMyVouchers(startHeight, endHeight); +async function findMyVouchers( + startHeight?: number, + endHeight?: number, +): Promise { + return getLoyaltyProgram().findMyVouchers(startHeight, endHeight); } // ============================================================================ @@ -1391,54 +1514,54 @@ async function findMyVouchers(startHeight?: number, endHeight?: number): Promise * Simple ping function to test comlink connectivity. */ function ping(): string { - console.log("[Worker] ping called!"); - return "pong"; + console.log("[Worker] ping called!"); + return "pong"; } const workerMethods = { - // Test - ping, - - // Initialization - initLoyaltyProgram, - - // Configuration - getProvingMode, - setProvingMode, - hasRecordScanner, - setRecordScanner, - - // Card operations - mintCard, - addPoints, - splitCardV2, - transferCard, - - // Voucher operations - redeemForVoucher, - useVoucher, - transferVoucher, - - // Record discovery - findMyCards, - findMyVouchers, - - // Account utilities - loadDemoAccount, - createAccount, - getPrivateKey, - - // Network utilities - getBlockHeight, - - // Helpers - getTierName, - getRewardTypeName, - - // Enums (for UI) - ProvingMode, - RewardType, - CardTier, + // Test + ping, + + // Initialization + initLoyaltyProgram, + + // Configuration + getProvingMode, + setProvingMode, + hasRecordScanner, + setRecordScanner, + + // Card operations + mintCard, + addPoints, + splitCardV2, + transferCard, + + // Voucher operations + redeemForVoucher, + useVoucher, + transferVoucher, + + // Record discovery + findMyCards, + findMyVouchers, + + // Account utilities + loadDemoAccount, + createAccount, + getPrivateKey, + + // Network utilities + getBlockHeight, + + // Helpers + getTierName, + getRewardTypeName, + + // Enums (for UI) + ProvingMode, + RewardType, + CardTier, }; console.log("[Worker] Exposing worker methods via comlink..."); diff --git a/create-leo-app/template-react-loyalty-program-ts/tsconfig.json b/create-leo-app/template-react-loyalty-program-ts/tsconfig.json index ec1200272..988bf7dc5 100644 --- a/create-leo-app/template-react-loyalty-program-ts/tsconfig.json +++ b/create-leo-app/template-react-loyalty-program-ts/tsconfig.json @@ -1,17 +1,17 @@ { - "include": ["src", "__tests__", "src/vite-env.d.ts"], - "compilerOptions": { - "outDir": "dist", - "target": "ES2020", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "declaration": false, - "sourceMap": false, - "noUnusedLocals": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "jsx": "react-jsx" - } + "include": ["src", "__tests__", "src/vite-env.d.ts"], + "compilerOptions": { + "outDir": "dist", + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "declaration": false, + "sourceMap": false, + "noUnusedLocals": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "jsx": "react-jsx" + } } diff --git a/create-leo-app/template-react-loyalty-program-ts/vite.config.ts b/create-leo-app/template-react-loyalty-program-ts/vite.config.ts index 159a5c129..b331a2322 100644 --- a/create-leo-app/template-react-loyalty-program-ts/vite.config.ts +++ b/create-leo-app/template-react-loyalty-program-ts/vite.config.ts @@ -3,15 +3,15 @@ import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ - assetsInclude: ['**/*.wasm'], - plugins: [react()], - optimizeDeps: { - exclude: ["@provablehq/wasm"], - }, - server: { - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + assetsInclude: ["**/*.wasm"], + plugins: [react()], + optimizeDeps: { + exclude: ["@provablehq/wasm"], + }, + server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, }, - }, }); diff --git a/create-leo-app/template-react-loyalty-program-ts/webpack.config.ts b/create-leo-app/template-react-loyalty-program-ts/webpack.config.ts index 34301b6fa..cd99fe21d 100644 --- a/create-leo-app/template-react-loyalty-program-ts/webpack.config.ts +++ b/create-leo-app/template-react-loyalty-program-ts/webpack.config.ts @@ -6,101 +6,103 @@ import HtmlWebpackPlugin from "html-webpack-plugin"; import path from "path"; const appConfig = { - mode: "production", - entry: { - index: "./src/main.tsx", - }, - output: { - path: path.resolve("dist"), - filename: "[name].bundle.js", - }, - resolve: { - extensions: [".ts", ".tsx", ".js", ".wasm", ".jsx"], - }, - devServer: { - port: 3000, - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + mode: "production", + entry: { + index: "./src/main.tsx", }, - client: { - overlay: false, + output: { + path: path.resolve("dist"), + filename: "[name].bundle.js", }, - }, - module: { - rules: [ - { - test: /\.(ts|tsx|js|jsx)$/, - use: { - loader: "babel-loader", + resolve: { + extensions: [".ts", ".tsx", ".js", ".wasm", ".jsx"], + }, + devServer: { + port: 3000, + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, + client: { + overlay: false, }, - }, - { - test: /\.css$/, - use: ["style-loader", "css-loader"], - }, - { - test: /\.(png|jpe?g|gif)$/i, - use: [ - { - loader: "file-loader", - options: { - name: "[path][name].[ext]", + }, + module: { + rules: [ + { + test: /\.(ts|tsx|js|jsx)$/, + use: { + loader: "babel-loader", + }, }, - }, - ], - }, - { - test: /\.svg$/, - use: [ - { - loader: "svg-url-loader", - options: { - limit: 8192, - noquotes: true, + { + test: /\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: "file-loader", + options: { + name: "[path][name].[ext]", + }, + }, + ], + }, + { + test: /\.svg$/, + use: [ + { + loader: "svg-url-loader", + options: { + limit: 8192, + noquotes: true, + }, + }, + ], + }, + { + test: /\.aleo$/i, + use: "raw-loader", }, - }, ], - }, - { - test: /\.aleo$/i, - use: 'raw-loader', - }, + }, + plugins: [ + new CopyPlugin({ + patterns: [ + { from: "public", to: "public" }, + { from: "_headers", to: "." }, + { from: "src/assets", to: "dist/assets" }, + ], + }), + new HtmlWebpackPlugin({ + template: "./index.html", + }), ], - }, - plugins: [ - new CopyPlugin({ - patterns: [ - { from: "public", to: "public" }, - { from: "_headers", to: "." }, - { from: 'src/assets', to: 'dist/assets' } - ], - }), - new HtmlWebpackPlugin({ - template: "./index.html", - }), - ], - performance: { - hints: false, - maxAssetSize: 13 * 1024 * 1024, // 13 MiB - maxEntrypointSize: 13 * 1024 * 1024, // 13 MiB - }, - optimization: { - minimize: true, - minimizer: [new TerserPlugin({ - terserOptions: { - module: true, - } - })], - }, - stats: { - warnings: false, - }, - experiments: { - asyncWebAssembly: true, - topLevelAwait: true, - }, - devtool: "source-map", + performance: { + hints: false, + maxAssetSize: 13 * 1024 * 1024, // 13 MiB + maxEntrypointSize: 13 * 1024 * 1024, // 13 MiB + }, + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + module: true, + }, + }), + ], + }, + stats: { + warnings: false, + }, + experiments: { + asyncWebAssembly: true, + topLevelAwait: true, + }, + devtool: "source-map", }; export default [appConfig]; diff --git a/create-leo-app/template-react-managed-worker/.eslintrc.cjs b/create-leo-app/template-react-managed-worker/.eslintrc.cjs index 4dcb43901..619e7374d 100644 --- a/create-leo-app/template-react-managed-worker/.eslintrc.cjs +++ b/create-leo-app/template-react-managed-worker/.eslintrc.cjs @@ -1,20 +1,20 @@ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", ], - }, -} + ignorePatterns: ["dist", ".eslintrc.cjs"], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, +}; diff --git a/create-leo-app/template-react-managed-worker/README.md b/create-leo-app/template-react-managed-worker/README.md index d081142b2..bb1b17fc2 100644 --- a/create-leo-app/template-react-managed-worker/README.md +++ b/create-leo-app/template-react-managed-worker/README.md @@ -3,4 +3,5 @@ > [!NOTE] > This is an experimental template not recommended for use -This template provides a minimal setup to get React and Aleo working in Webpack or Vite with HMR and some ESLint rules. \ No newline at end of file +This template provides a minimal setup to get React and Aleo working in Webpack +or Vite with HMR and some ESLint rules. diff --git a/create-leo-app/template-react-managed-worker/index.html b/create-leo-app/template-react-managed-worker/index.html index 57aa8244e..2711a8db3 100644 --- a/create-leo-app/template-react-managed-worker/index.html +++ b/create-leo-app/template-react-managed-worker/index.html @@ -1,88 +1,88 @@ - - - - - Aleo + React - - - -
-
-
-
-
-
- - + @keyframes sk-bounce { + 0%, + 100% { + transform: scale(0); + -webkit-transform: scale(0); + } + 50% { + transform: scale(1); + -webkit-transform: scale(1); + } + } + + + +
+
+
+
+
+
+ + diff --git a/create-leo-app/template-react-managed-worker/package.json b/create-leo-app/template-react-managed-worker/package.json index 38162b841..c80d6c760 100644 --- a/create-leo-app/template-react-managed-worker/package.json +++ b/create-leo-app/template-react-managed-worker/package.json @@ -1,41 +1,41 @@ { - "name": "aleo-react-managed-worker-starter", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "webpack --config webpack.config.js", - "build:vite": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@babel/core": "^7.26.7", - "@babel/preset-env": "^7.26.7", - "@babel/preset-react": "^7.26.3", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "@vitejs/plugin-react": "^4.3.4", - "babel-loader": "^9.2.1", - "copy-webpack-plugin": "^12.0.2", - "css-loader": "^7.1.2", - "eslint": "^9.19.0", - "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.18", - "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.6.3", - "style-loader": "^4.0.0", - "svg-url-loader": "^8.0.0", - "vite": "^6.3.6", - "webpack": "^5.97.1", - "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.0" - } + "name": "aleo-react-managed-worker-starter", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "webpack --config webpack.config.js", + "build:vite": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@babel/core": "^7.26.7", + "@babel/preset-env": "^7.26.7", + "@babel/preset-react": "^7.26.3", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "babel-loader": "^9.2.1", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^7.1.2", + "eslint": "^9.19.0", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.18", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.6.3", + "style-loader": "^4.0.0", + "svg-url-loader": "^8.0.0", + "vite": "^6.3.6", + "webpack": "^5.97.1", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.0" + } } diff --git a/create-leo-app/template-react-managed-worker/src/App.css b/create-leo-app/template-react-managed-worker/src/App.css index b9d355df2..f44fb79ad 100644 --- a/create-leo-app/template-react-managed-worker/src/App.css +++ b/create-leo-app/template-react-managed-worker/src/App.css @@ -1,42 +1,42 @@ #root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; } .logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; } .logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); + filter: drop-shadow(0 0 2em #646cffaa); } .logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); + filter: drop-shadow(0 0 2em #61dafbaa); } @keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } } .card { - padding: 2em; + padding: 2em; } .read-the-docs { - color: #888; + color: #888; } diff --git a/create-leo-app/template-react-managed-worker/src/App.jsx b/create-leo-app/template-react-managed-worker/src/App.jsx index f7238fad7..a643b2fae 100644 --- a/create-leo-app/template-react-managed-worker/src/App.jsx +++ b/create-leo-app/template-react-managed-worker/src/App.jsx @@ -7,72 +7,79 @@ import { createAleoWorker } from "@provablehq/sdk"; const aleoWorker = createAleoWorker(); function App() { - const [count, setCount] = useState(0); - const [account, setAccount] = useState(null); - const [loading, setLoading] = useState(false); + const [count, setCount] = useState(0); + const [account, setAccount] = useState(null); + const [loading, setLoading] = useState(false); - const generateAccount = async () => { - const privateKey = await aleoWorker.getPrivateKey(); - setAccount(privateKey); - }; + const generateAccount = async () => { + const privateKey = await aleoWorker.getPrivateKey(); + setAccount(privateKey); + }; - async function execute() { - const hello_hello_program = - "program hello_hello.aleo;\n" + - "\n" + - "function hello:\n" + - " input r0 as u32.public;\n" + - " input r1 as u32.private;\n" + - " add r0 r1 into r2;\n" + - " output r2 as u32.private;\n"; + async function execute() { + const hello_hello_program = + "program hello_hello.aleo;\n" + + "\n" + + "function hello:\n" + + " input r0 as u32.public;\n" + + " input r1 as u32.private;\n" + + " add r0 r1 into r2;\n" + + " output r2 as u32.private;\n"; - setLoading(true); - const result = await aleoWorker.run(hello_hello_program,"hello", ["5u32", "5u32"], "APrivateKey1zkp778oUFSck3PZA5xppgp4trFwkkD6xnUXtxcBCfsq4URJ") - setLoading(false); + setLoading(true); + const result = await aleoWorker.run( + hello_hello_program, + "hello", + ["5u32", "5u32"], + "APrivateKey1zkp778oUFSck3PZA5xppgp4trFwkkD6xnUXtxcBCfsq4URJ", + ); + setLoading(false); - alert(JSON.stringify(result)); - } + alert(JSON.stringify(result)); + } - return ( - <> - -

Aleo + React

-
- -

- -

-

- -

-

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Aleo and React logos to learn more -

- - ); + return ( + <> + +

Aleo + React

+
+ +

+ +

+

+ +

+

+ Edit src/App.jsx and save to test HMR +

+
+

+ Click on the Aleo and React logos to learn more +

+ + ); } export default App; diff --git a/create-leo-app/template-react-managed-worker/src/index.css b/create-leo-app/template-react-managed-worker/src/index.css index 2c3fac689..8da4c0ffa 100644 --- a/create-leo-app/template-react-managed-worker/src/index.css +++ b/create-leo-app/template-react-managed-worker/src/index.css @@ -1,69 +1,69 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } a:hover { - color: #535bf2; + color: #535bf2; } body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; } h1 { - font-size: 3.2em; - line-height: 1.1; + font-size: 3.2em; + line-height: 1.1; } button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; } button:hover { - border-color: #646cff; + border-color: #646cff; } button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } } diff --git a/create-leo-app/template-react-managed-worker/src/main.jsx b/create-leo-app/template-react-managed-worker/src/main.jsx index 1a70efb28..e259b09b7 100644 --- a/create-leo-app/template-react-managed-worker/src/main.jsx +++ b/create-leo-app/template-react-managed-worker/src/main.jsx @@ -4,7 +4,7 @@ import App from "./App.jsx"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root")).render( - - - , + + + , ); diff --git a/create-leo-app/template-react-managed-worker/vite.config.js b/create-leo-app/template-react-managed-worker/vite.config.js index ff78648f9..79745371b 100644 --- a/create-leo-app/template-react-managed-worker/vite.config.js +++ b/create-leo-app/template-react-managed-worker/vite.config.js @@ -3,19 +3,19 @@ import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ - assetsInclude: ['**/*.wasm'], - plugins: [react()], - optimizeDeps: { - exclude: ["@provablehq/wasm"], - }, - server: { - // Needed if you are linking local packages for development - fs: { - allow: [searchForWorkspaceRoot(process.cwd()), "../../sdk"], + assetsInclude: ["**/*.wasm"], + plugins: [react()], + optimizeDeps: { + exclude: ["@provablehq/wasm"], }, - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + server: { + // Needed if you are linking local packages for development + fs: { + allow: [searchForWorkspaceRoot(process.cwd()), "../../sdk"], + }, + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, }, - }, }); diff --git a/create-leo-app/template-react-managed-worker/webpack.config.js b/create-leo-app/template-react-managed-worker/webpack.config.js index ea0472dd5..0804c0726 100644 --- a/create-leo-app/template-react-managed-worker/webpack.config.js +++ b/create-leo-app/template-react-managed-worker/webpack.config.js @@ -5,86 +5,86 @@ import HtmlWebpackPlugin from "html-webpack-plugin"; import path from "path"; const appConfig = { - mode: "production", - entry: { - index: "./src/main.jsx", - }, - output: { - path: path.resolve("dist"), - filename: "[name].bundle.js", - }, - resolve: { - extensions: [".js", ".wasm", ".jsx"], - }, - devServer: { - port: 3000, - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + mode: "production", + entry: { + index: "./src/main.jsx", }, - client: { - overlay: false, + output: { + path: path.resolve("dist"), + filename: "[name].bundle.js", }, - }, - module: { - rules: [ - { - test: /\.(js|jsx)$/, - exclude: /nodeModules/, - use: { - loader: "babel-loader", + resolve: { + extensions: [".js", ".wasm", ".jsx"], + }, + devServer: { + port: 3000, + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, + client: { + overlay: false, }, - }, - { - test: /\.css$/, - use: ["style-loader", "css-loader"], - }, - { - test: /\.(png|jpe?g|gif)$/i, - use: [ - { - loader: "file-loader", - options: { - name: "[path][name].[ext]", + }, + module: { + rules: [ + { + test: /\.(js|jsx)$/, + exclude: /nodeModules/, + use: { + loader: "babel-loader", + }, }, - }, - ], - }, - { - test: /\.svg$/, - use: [ - { - loader: "svg-url-loader", - options: { - limit: 8192, - noquotes: true, + { + test: /\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: "file-loader", + options: { + name: "[path][name].[ext]", + }, + }, + ], + }, + { + test: /\.svg$/, + use: [ + { + loader: "svg-url-loader", + options: { + limit: 8192, + noquotes: true, + }, + }, + ], }, - }, ], - }, + }, + plugins: [ + new CopyPlugin({ + patterns: [{ from: "public", to: "public" }], + }), + new HtmlWebpackPlugin({ + template: "./index.html", + }), ], - }, - plugins: [ - new CopyPlugin({ - patterns: [{ from: "public", to: "public" }], - }), - new HtmlWebpackPlugin({ - template: "./index.html", - }), - ], - performance: { - hints: false, - maxAssetSize: 13 * 1024 * 1024, // 12 MiB - maxEntrypointSize: 13 * 1024 * 1024, // 12 MiB - }, - stats: { - warnings: false, - }, - experiments: { - asyncWebAssembly: true, - topLevelAwait: true, - }, - devtool: "source-map", + performance: { + hints: false, + maxAssetSize: 13 * 1024 * 1024, // 12 MiB + maxEntrypointSize: 13 * 1024 * 1024, // 12 MiB + }, + stats: { + warnings: false, + }, + experiments: { + asyncWebAssembly: true, + topLevelAwait: true, + }, + devtool: "source-map", }; export default [appConfig]; diff --git a/create-leo-app/template-react-ts/.eslintrc.cjs b/create-leo-app/template-react-ts/.eslintrc.cjs index 4dcb43901..619e7374d 100644 --- a/create-leo-app/template-react-ts/.eslintrc.cjs +++ b/create-leo-app/template-react-ts/.eslintrc.cjs @@ -1,20 +1,20 @@ module.exports = { - root: true, - env: { browser: true, es2020: true }, - extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', - ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], - rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, + root: true, + env: { browser: true, es2020: true }, + extends: [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", ], - }, -} + ignorePatterns: ["dist", ".eslintrc.cjs"], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], + rules: { + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true }, + ], + }, +}; diff --git a/create-leo-app/template-react-ts/README.md b/create-leo-app/template-react-ts/README.md index 5d82d71fd..443851b3d 100644 --- a/create-leo-app/template-react-ts/README.md +++ b/create-leo-app/template-react-ts/README.md @@ -2,7 +2,8 @@ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/ProvableHQ/sdk/tree/mainnet/create-leo-app/template-react) -This template provides a minimal setup to get React and Aleo working in Webpack or Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React and Aleo working in Webpack +or Vite with HMR and some ESLint rules. This template includes a Leo program that is loaded by the web app located in the `helloworld` directory. @@ -20,43 +21,50 @@ Your app should be running on http://localhost:5173/ 1. Copy the `helloworld/.env.example` to `helloworld/.env` (this will be ignored by Git): - ```bash - cd helloworld - cp .env.example .env - ``` + ```bash + cd helloworld + cp .env.example .env + ``` 2. Replace `PRIVATE_KEY=user1PrivateKey` in the `.env` with your own key (you - can use an existing one or generate your own at https://provable.tools/account) + can use an existing one or generate your own at + https://provable.tools/account) 3. Follow instructions to install Leo here: https://github.com/ProvableHQ/leo -4. You can edit `helloworld/src/main.leo` and run `leo run` to compile and update the - Aleo instructions under `build` which are loaded by the web app. +4. You can edit `helloworld/src/main.leo` and run `leo run` to compile and + update the Aleo instructions under `build` which are loaded by the web app. ## Deploy program from web app -> [!WARNING] -> This is for demonstration purposes or local testing only, in production applications you -> should avoid building a public facing web app with private key information +> [!WARNING] This is for demonstration purposes or local testing only, in +> production applications you should avoid building a public facing web app with +> private key information -Information on generating a private key, seeding a wallet with funds, and finding a spendable record can be found here -if you are unfamiliar: https://docs.leo-lang.org/testnet/getting_started/deploy_execute_demo +Information on generating a private key, seeding a wallet with funds, and +finding a spendable record can be found here if you are unfamiliar: +https://docs.leo-lang.org/testnet/getting_started/deploy_execute_demo -Aleo programs deployed require unique names, make sure to edit the program's name to something unique in `helloworld/src/main.leo`, `helloworld/program.json`, rename `helloworld/inputs/helloworld.in` and rebuild. +Aleo programs deployed require unique names, make sure to edit the program's +name to something unique in `helloworld/src/main.leo`, +`helloworld/program.json`, rename `helloworld/inputs/helloworld.in` and rebuild. 1. In the `worker.js` file modify the privateKey to be an account with available funds - ```js - // Use existing account with funds - const account = new Account({ - privateKey: "user1PrivateKey", - }); - ``` + ```js + // Use existing account with funds + const account = new Account({ + privateKey: "user1PrivateKey", + }); + ``` -2. (Optional) Provide a fee record manually (located in commented code within `worker.js`) +2. (Optional) Provide a fee record manually (located in commented code within + `worker.js`) - If you do not provide a manual fee record, the SDK will attempt to scan for a record starting at the latest block. A simple way to speed this up would be to make a public transaction to this account right before deploying. + If you do not provide a manual fee record, the SDK will attempt to scan for + a record starting at the latest block. A simple way to speed this up would + be to make a public transaction to this account right before deploying. 3. Run the web app and hit the deploy button diff --git a/create-leo-app/template-react-ts/helloworld/README.md b/create-leo-app/template-react-ts/helloworld/README.md index 2456ef654..94d976167 100644 --- a/create-leo-app/template-react-ts/helloworld/README.md +++ b/create-leo-app/template-react-ts/helloworld/README.md @@ -3,11 +3,13 @@ ## Build Guide To compile this Aleo program, run: + ```bash snarkvm build ``` To execute this Aleo program, run: + ```bash snarkvm run hello ``` diff --git a/create-leo-app/template-react-ts/index.html b/create-leo-app/template-react-ts/index.html index 4da12f5b5..20480b096 100644 --- a/create-leo-app/template-react-ts/index.html +++ b/create-leo-app/template-react-ts/index.html @@ -1,88 +1,88 @@ - - - - - Aleo + React - - - -
-
-
-
-
-
- - + @keyframes sk-bounce { + 0%, + 100% { + transform: scale(0); + -webkit-transform: scale(0); + } + 50% { + transform: scale(1); + -webkit-transform: scale(1); + } + } + + + +
+
+
+
+
+
+ + diff --git a/create-leo-app/template-react-ts/package.json b/create-leo-app/template-react-ts/package.json index 190881e50..9bd5e715c 100644 --- a/create-leo-app/template-react-ts/package.json +++ b/create-leo-app/template-react-ts/package.json @@ -1,49 +1,49 @@ { - "name": "aleo-react-typescript-starter", - "private": true, - "version": "0.0.0", - "scripts": { - "dev": "vite", - "build": "webpack", - "dev-webpack": "webpack serve", - "build:vite": "vite build", - "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", - "preview": "vite preview", - "install-leo": "./install.sh" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18", - "comlink": "^4.4.2", - "react": "^19.0.0", - "react-dom": "^19.0.0" - }, - "devDependencies": { - "@babel/core": "^7.26.7", - "@babel/preset-env": "^7.26.7", - "@babel/preset-react": "^7.26.3", - "@babel/preset-typescript": "^7.26.0", - "@types/babel__generator": "^7.6.8", - "@types/node": "^22.12.0", - "@types/react": "^19.0.8", - "@types/react-dom": "^19.0.3", - "@vitejs/plugin-react": "^4.3.4", - "babel-loader": "^9.2.1", - "copy-webpack-plugin": "^12.0.2", - "css-loader": "^7.1.2", - "eslint": "^9.19.0", - "eslint-plugin-react": "^7.37.4", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.18", - "file-loader": "^6.2.0", - "html-webpack-plugin": "^5.6.3", - "raw-loader": "^4.0.2", - "style-loader": "^4.0.0", - "svg-url-loader": "^8.0.0", - "ts-node": "^10.9.2", - "typescript": "^5.7.3", - "vite": "^6.3.6", - "webpack": "^5.97.1", - "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.0" - } + "name": "aleo-react-typescript-starter", + "private": true, + "version": "0.0.0", + "scripts": { + "dev": "vite", + "build": "webpack", + "dev-webpack": "webpack serve", + "build:vite": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview", + "install-leo": "./install.sh" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18", + "comlink": "^4.4.2", + "react": "^19.0.0", + "react-dom": "^19.0.0" + }, + "devDependencies": { + "@babel/core": "^7.26.7", + "@babel/preset-env": "^7.26.7", + "@babel/preset-react": "^7.26.3", + "@babel/preset-typescript": "^7.26.0", + "@types/babel__generator": "^7.6.8", + "@types/node": "^22.12.0", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "@vitejs/plugin-react": "^4.3.4", + "babel-loader": "^9.2.1", + "copy-webpack-plugin": "^12.0.2", + "css-loader": "^7.1.2", + "eslint": "^9.19.0", + "eslint-plugin-react": "^7.37.4", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.18", + "file-loader": "^6.2.0", + "html-webpack-plugin": "^5.6.3", + "raw-loader": "^4.0.2", + "style-loader": "^4.0.0", + "svg-url-loader": "^8.0.0", + "ts-node": "^10.9.2", + "typescript": "^5.7.3", + "vite": "^6.3.6", + "webpack": "^5.97.1", + "webpack-cli": "^6.0.1", + "webpack-dev-server": "^5.2.0" + } } diff --git a/create-leo-app/template-react-ts/src/App.css b/create-leo-app/template-react-ts/src/App.css index b9d355df2..f44fb79ad 100644 --- a/create-leo-app/template-react-ts/src/App.css +++ b/create-leo-app/template-react-ts/src/App.css @@ -1,42 +1,42 @@ #root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; } .logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; } .logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); + filter: drop-shadow(0 0 2em #646cffaa); } .logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); + filter: drop-shadow(0 0 2em #61dafbaa); } @keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } } .card { - padding: 2em; + padding: 2em; } .read-the-docs { - color: #888; + color: #888; } diff --git a/create-leo-app/template-react-ts/src/App.tsx b/create-leo-app/template-react-ts/src/App.tsx index a07c00f0b..fa37dd904 100644 --- a/create-leo-app/template-react-ts/src/App.tsx +++ b/create-leo-app/template-react-ts/src/App.tsx @@ -7,97 +7,101 @@ import { AleoWorker } from "./workers/AleoWorker"; const aleoWorker = AleoWorker(); function App() { - const [count, setCount] = useState(0); - const [account, setAccount] = useState(null); - const [executing, setExecuting] = useState(false); - const [deploying, setDeploying] = useState(false); + const [count, setCount] = useState(0); + const [account, setAccount] = useState(null); + const [executing, setExecuting] = useState(false); + const [deploying, setDeploying] = useState(false); - const generateAccount = async () => { - const key = await aleoWorker.getPrivateKey(); - setAccount(await key.to_string()); - }; + const generateAccount = async () => { + const key = await aleoWorker.getPrivateKey(); + setAccount(await key.to_string()); + }; - async function execute() { - setExecuting(true); - const result = await aleoWorker.localProgramExecution( - helloworld_program, - "main", - ["5u32", "5u32"], - ); - setExecuting(false); + async function execute() { + setExecuting(true); + const result = await aleoWorker.localProgramExecution( + helloworld_program, + "main", + ["5u32", "5u32"], + ); + setExecuting(false); - alert(JSON.stringify(result)); - } + alert(JSON.stringify(result)); + } - async function deploy() { - setDeploying(true); - try { - const result = await aleoWorker.deployProgram(helloworld_program); - console.log("Transaction:") - console.log("https://explorer.provable.com/transaction/" + result) - alert("Transaction ID: " + result); - } catch (e) { - console.log(e) - alert("Error with deployment, please check console for details"); + async function deploy() { + setDeploying(true); + try { + const result = await aleoWorker.deployProgram(helloworld_program); + console.log("Transaction:"); + console.log("https://explorer.provable.com/transaction/" + result); + alert("Transaction ID: " + result); + } catch (e) { + console.log(e); + alert("Error with deployment, please check console for details"); + } + setDeploying(false); } - setDeploying(false); - } - return ( - <> - -

Aleo + React

-
- -

- -

-

- -

-

- Edit src/App.tsx and save to test HMR -

-
+ return ( + <> + +

Aleo + React

+
+ +

+ +

+

+ +

+

+ Edit src/App.tsx and save to test HMR +

+
- {/* Advanced Section */} -
-

Advanced Actions

-

- Deployment on Aleo requires certain prerequisites like seeding your - wallet with credits and retrieving a fee record. Check README for more - details. -

-

- -

-
-

- Click on the Aleo and React logos to learn more -

- - ); + {/* Advanced Section */} +
+

Advanced Actions

+

+ Deployment on Aleo requires certain prerequisites like + seeding your wallet with credits and retrieving a fee + record. Check README for more details. +

+

+ +

+
+

+ Click on the Aleo and React logos to learn more +

+ + ); } export default App; diff --git a/create-leo-app/template-react-ts/src/custom.d.ts b/create-leo-app/template-react-ts/src/custom.d.ts index 3aa72e286..076556b5f 100644 --- a/create-leo-app/template-react-ts/src/custom.d.ts +++ b/create-leo-app/template-react-ts/src/custom.d.ts @@ -1,16 +1,14 @@ -declare module '*.svg' { +declare module "*.svg" { const content: any; export default content; - } - - declare module '*.aleo' { +} + +declare module "*.aleo" { const content: string; export default content; - } - - declare module '*?raw' { +} + +declare module "*?raw" { const content: string; export default content; - } - - \ No newline at end of file +} diff --git a/create-leo-app/template-react-ts/src/index.css b/create-leo-app/template-react-ts/src/index.css index 2c3fac689..8da4c0ffa 100644 --- a/create-leo-app/template-react-ts/src/index.css +++ b/create-leo-app/template-react-ts/src/index.css @@ -1,69 +1,69 @@ :root { - font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - -webkit-text-size-adjust: 100%; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; } a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; + font-weight: 500; + color: #646cff; + text-decoration: inherit; } a:hover { - color: #535bf2; + color: #535bf2; } body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; } h1 { - font-size: 3.2em; - line-height: 1.1; + font-size: 3.2em; + line-height: 1.1; } button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; } button:hover { - border-color: #646cff; + border-color: #646cff; } button:focus, button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; + outline: 4px auto -webkit-focus-ring-color; } @media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } } diff --git a/create-leo-app/template-react-ts/src/main.tsx b/create-leo-app/template-react-ts/src/main.tsx index e2aaf9ff6..c224cf5a6 100644 --- a/create-leo-app/template-react-ts/src/main.tsx +++ b/create-leo-app/template-react-ts/src/main.tsx @@ -4,7 +4,7 @@ import App from "./App"; import "./index.css"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - , + + + , ); diff --git a/create-leo-app/template-react-ts/src/workers/AleoWorker.ts b/create-leo-app/template-react-ts/src/workers/AleoWorker.ts index 218021ba4..9d6465b32 100644 --- a/create-leo-app/template-react-ts/src/workers/AleoWorker.ts +++ b/create-leo-app/template-react-ts/src/workers/AleoWorker.ts @@ -9,7 +9,7 @@ const AleoWorker = () => { type: "module", }); - worker.onerror = function(event) { + worker.onerror = function (event) { console.error("Error in worker: " + event?.message); }; @@ -18,4 +18,4 @@ const AleoWorker = () => { return singletonWorker; }; -export { AleoWorker }; \ No newline at end of file +export { AleoWorker }; diff --git a/create-leo-app/template-react-ts/src/workers/worker.ts b/create-leo-app/template-react-ts/src/workers/worker.ts index 1f5a5a0dd..b7be80368 100644 --- a/create-leo-app/template-react-ts/src/workers/worker.ts +++ b/create-leo-app/template-react-ts/src/workers/worker.ts @@ -1,72 +1,72 @@ //@ts-nocheck import { - Account, - ProgramManager, - PrivateKey, - initThreadPool, - AleoKeyProvider, - AleoNetworkClient, - NetworkRecordProvider, + Account, + ProgramManager, + PrivateKey, + initThreadPool, + AleoKeyProvider, + AleoNetworkClient, + NetworkRecordProvider, } from "@provablehq/sdk"; import { expose, proxy } from "comlink"; await initThreadPool(); async function localProgramExecution(program, aleoFunction, inputs) { - const programManager = new ProgramManager(); - - // Create a temporary account for the execution of the program - const account = new Account(); - programManager.setAccount(account); - - const executionResponse = await programManager.run( - program, - aleoFunction, - inputs, - false, - ); - return executionResponse.getOutputs(); + const programManager = new ProgramManager(); + + // Create a temporary account for the execution of the program + const account = new Account(); + programManager.setAccount(account); + + const executionResponse = await programManager.run( + program, + aleoFunction, + inputs, + false, + ); + return executionResponse.getOutputs(); } async function getPrivateKey() { - const key = new PrivateKey(); - return proxy(key); + const key = new PrivateKey(); + return proxy(key); } async function deployProgram(program) { - const keyProvider = new AleoKeyProvider(); - keyProvider.useCache(true); + const keyProvider = new AleoKeyProvider(); + keyProvider.useCache(true); - // Create a record provider that will be used to find records and transaction data for Aleo programs - const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); + // Create a record provider that will be used to find records and transaction data for Aleo programs + const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); - // Use existing account with funds - const account = new Account({ - privateKey: "user1PrivateKey", - }); + // Use existing account with funds + const account = new Account({ + privateKey: "user1PrivateKey", + }); - const recordProvider = new NetworkRecordProvider(account, networkClient); + const recordProvider = new NetworkRecordProvider(account, networkClient); - // Initialize a program manager to talk to the Aleo network with the configured key and record providers - const programManager = new ProgramManager( - "https://api.provable.com/v2", - keyProvider, - recordProvider, - ); + // Initialize a program manager to talk to the Aleo network with the configured key and record providers + const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, + ); - programManager.setAccount(account); + programManager.setAccount(account); - // Define a fee to pay to deploy the program - const fee = 1.9; // 1.9 Aleo credits + // Define a fee to pay to deploy the program + const fee = 1.9; // 1.9 Aleo credits - // Deploy the program to the Aleo network - const tx_id = await programManager.deploy(program, fee); + // Deploy the program to the Aleo network + const tx_id = await programManager.deploy(program, fee); - // Optional: Pass in fee record manually to avoid long scan times - // const feeRecord = "{ owner: aleo1xxx...xxx.private, microcredits: 2000000u64.private, _nonce: 123...789group.public}"; - // const tx_id = await programManager.deploy(program, fee, undefined, feeRecord); + // Optional: Pass in fee record manually to avoid long scan times + // const feeRecord = "{ owner: aleo1xxx...xxx.private, microcredits: 2000000u64.private, _nonce: 123...789group.public}"; + // const tx_id = await programManager.deploy(program, fee, undefined, feeRecord); - return tx_id; + return tx_id; } const workerMethods = { localProgramExecution, getPrivateKey, deployProgram }; diff --git a/create-leo-app/template-react-ts/tsconfig.json b/create-leo-app/template-react-ts/tsconfig.json index a5b674b1c..c9ba72a1d 100644 --- a/create-leo-app/template-react-ts/tsconfig.json +++ b/create-leo-app/template-react-ts/tsconfig.json @@ -1,16 +1,16 @@ { - "include": ["src", "__tests__", "src/custom.d.ts"], - "compilerOptions": { - "outDir": "dist", - "target": "ES2015", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "declaration": false, - "sourceMap": false, - "noUnusedLocals": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "jsx":"react-jsx", - } + "include": ["src", "__tests__", "src/custom.d.ts"], + "compilerOptions": { + "outDir": "dist", + "target": "ES2015", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "declaration": false, + "sourceMap": false, + "noUnusedLocals": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "jsx": "react-jsx" + } } diff --git a/create-leo-app/template-react-ts/vite.config.ts b/create-leo-app/template-react-ts/vite.config.ts index 159a5c129..b331a2322 100644 --- a/create-leo-app/template-react-ts/vite.config.ts +++ b/create-leo-app/template-react-ts/vite.config.ts @@ -3,15 +3,15 @@ import react from "@vitejs/plugin-react"; // https://vitejs.dev/config/ export default defineConfig({ - assetsInclude: ['**/*.wasm'], - plugins: [react()], - optimizeDeps: { - exclude: ["@provablehq/wasm"], - }, - server: { - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + assetsInclude: ["**/*.wasm"], + plugins: [react()], + optimizeDeps: { + exclude: ["@provablehq/wasm"], + }, + server: { + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, }, - }, }); diff --git a/create-leo-app/template-react-ts/webpack.config.ts b/create-leo-app/template-react-ts/webpack.config.ts index f6d23cb6f..71404f93a 100644 --- a/create-leo-app/template-react-ts/webpack.config.ts +++ b/create-leo-app/template-react-ts/webpack.config.ts @@ -6,107 +6,111 @@ import HtmlWebpackPlugin from "html-webpack-plugin"; import path from "path"; const appConfig = { - mode: "production", - entry: { - index: "./src/main.tsx", - }, - output: { - path: path.resolve("dist"), - filename: "[name].bundle.js", - }, - resolve: { - extensions: [".ts", ".tsx", ".js", ".wasm", ".jsx"], - }, - devServer: { - port: 3000, - headers: { - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + mode: "production", + entry: { + index: "./src/main.tsx", }, - client: { - overlay: false, + output: { + path: path.resolve("dist"), + filename: "[name].bundle.js", }, - }, - module: { - rules: [ - { - test: /\.(ts|tsx|js|jsx)$/, - //exclude: /node_modules/, - use: { - loader: "babel-loader", - // options: { - // presets: [ - // '@babel/preset-env', - // '@babel/preset-react', - // '@babel/preset-typescript', - // ], - // }, + resolve: { + extensions: [".ts", ".tsx", ".js", ".wasm", ".jsx"], + }, + devServer: { + port: 3000, + headers: { + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "require-corp", + }, + client: { + overlay: false, }, - }, - { - test: /\.css$/, - use: ["style-loader", "css-loader"], - }, - { - test: /\.(png|jpe?g|gif)$/i, - use: [ - { - loader: "file-loader", - options: { - name: "[path][name].[ext]", + }, + module: { + rules: [ + { + test: /\.(ts|tsx|js|jsx)$/, + //exclude: /node_modules/, + use: { + loader: "babel-loader", + // options: { + // presets: [ + // '@babel/preset-env', + // '@babel/preset-react', + // '@babel/preset-typescript', + // ], + // }, + }, }, - }, - ], - }, - { - test: /\.svg$/, - use: [ - { - loader: "svg-url-loader", - options: { - limit: 8192, - noquotes: true, + { + test: /\.css$/, + use: ["style-loader", "css-loader"], + }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: "file-loader", + options: { + name: "[path][name].[ext]", + }, + }, + ], + }, + { + test: /\.svg$/, + use: [ + { + loader: "svg-url-loader", + options: { + limit: 8192, + noquotes: true, + }, + }, + ], + }, + { + test: /\.aleo$/i, + use: "raw-loader", }, - }, ], - }, - { - test: /\.aleo$/i, - use: 'raw-loader', - }, - ], - }, - plugins: [ - new CopyPlugin({ - patterns: [{ from: "public", to: "public" }, { from: "_headers", to: "." }, - { from: 'src/assets', to: 'dist/assets' } + }, + plugins: [ + new CopyPlugin({ + patterns: [ + { from: "public", to: "public" }, + { from: "_headers", to: "." }, + { from: "src/assets", to: "dist/assets" }, + ], + }), + new HtmlWebpackPlugin({ + template: "./index.html", + }), ], - }), - new HtmlWebpackPlugin({ - template: "./index.html", - }), - ], - performance: { - hints: false, - maxAssetSize: 13 * 1024 * 1024, // 12 MiB - maxEntrypointSize: 13 * 1024 * 1024, // 12 MiB - }, - optimization: { - minimize: true, - minimizer: [new TerserPlugin({ - terserOptions: { - module: true, - } - })], - }, - stats: { - warnings: false, - }, - experiments: { - asyncWebAssembly: true, - topLevelAwait: true, - }, - devtool: "source-map", + performance: { + hints: false, + maxAssetSize: 13 * 1024 * 1024, // 12 MiB + maxEntrypointSize: 13 * 1024 * 1024, // 12 MiB + }, + optimization: { + minimize: true, + minimizer: [ + new TerserPlugin({ + terserOptions: { + module: true, + }, + }), + ], + }, + stats: { + warnings: false, + }, + experiments: { + asyncWebAssembly: true, + topLevelAwait: true, + }, + devtool: "source-map", }; export default [appConfig]; diff --git a/create-leo-app/template-vanilla/main.js b/create-leo-app/template-vanilla/main.js index 28f844464..47e41d125 100644 --- a/create-leo-app/template-vanilla/main.js +++ b/create-leo-app/template-vanilla/main.js @@ -21,7 +21,7 @@ window.key = () => { }; window.deploy = () => { - worker.postMessage("deploy"); + worker.postMessage("deploy"); }; document.querySelector("#app").innerHTML = ` diff --git a/create-leo-app/template-vanilla/vite.config.js b/create-leo-app/template-vanilla/vite.config.js index f19a55032..b2376c549 100644 --- a/create-leo-app/template-vanilla/vite.config.js +++ b/create-leo-app/template-vanilla/vite.config.js @@ -1,9 +1,9 @@ import { defineConfig } from "vite"; export default defineConfig({ - assetsInclude: ['**/*.wasm'], + assetsInclude: ["**/*.wasm"], optimizeDeps: { - exclude: ["@provablehq/wasm",], + exclude: ["@provablehq/wasm"], }, server: { headers: { diff --git a/create-leo-app/template-vanilla/worker.js b/create-leo-app/template-vanilla/worker.js index f25a1f1a1..abb7b4525 100644 --- a/create-leo-app/template-vanilla/worker.js +++ b/create-leo-app/template-vanilla/worker.js @@ -1,24 +1,24 @@ import { - Account, - ProgramManager, - PrivateKey, - initThreadPool, - AleoKeyProvider, - AleoNetworkClient, - NetworkRecordProvider, + Account, + ProgramManager, + PrivateKey, + initThreadPool, + AleoKeyProvider, + AleoNetworkClient, + NetworkRecordProvider, } from "@provablehq/sdk"; await initThreadPool(); // A program name must be unique. To deploy the program, you should change the name from hello_hello.aleo to something else that is unique. -const hello_hello_program =` +const hello_hello_program = ` program hello_hello.aleo; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; - output r2 as u32.private;` + output r2 as u32.private;`; async function localProgramExecution(program, aleoFunction, inputs) { const programManager = new ProgramManager(); @@ -41,55 +41,58 @@ function getPrivateKey() { } async function deployProgram(program) { - const keyProvider = new AleoKeyProvider(); - keyProvider.useCache(true); + const keyProvider = new AleoKeyProvider(); + keyProvider.useCache(true); - // Create a record provider that will be used to find records and transaction data for Aleo programs - const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); + // Create a record provider that will be used to find records and transaction data for Aleo programs + const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); - // Use existing account with funds - const account = new Account({ - privateKey: "user1PrivateKey", - }); + // Use existing account with funds + const account = new Account({ + privateKey: "user1PrivateKey", + }); - const recordProvider = new NetworkRecordProvider(account, networkClient); + const recordProvider = new NetworkRecordProvider(account, networkClient); - // Initialize a program manager to talk to the Aleo network with the configured key and record providers - const programManager = new ProgramManager( - "https://api.provable.com/v2", - keyProvider, - recordProvider, - ); + // Initialize a program manager to talk to the Aleo network with the configured key and record providers + const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, + ); - programManager.setAccount(account); + programManager.setAccount(account); - // Define a fee to pay to deploy the program - const fee = 1.9; // 1.9 Aleo credits + // Define a fee to pay to deploy the program + const fee = 1.9; // 1.9 Aleo credits - // Deploy the program to the Aleo network - const tx_id = await programManager.deploy(program, fee); + // Deploy the program to the Aleo network + const tx_id = await programManager.deploy(program, fee); - // Optional: Pass in fee record manually to avoid long scan times - // const feeRecord = "{ owner: aleo1xxx...xxx.private, microcredits: 2000000u64.private, _nonce: 123...789group.public}"; - // const tx_id = await programManager.deploy(program, fee, undefined, feeRecord); + // Optional: Pass in fee record manually to avoid long scan times + // const feeRecord = "{ owner: aleo1xxx...xxx.private, microcredits: 2000000u64.private, _nonce: 123...789group.public}"; + // const tx_id = await programManager.deploy(program, fee, undefined, feeRecord); - return tx_id; + return tx_id; } onmessage = async function (e) { if (e.data === "execute") { - const result = await localProgramExecution(hello_hello_program, "hello", ["5u32", "5u32"]); + const result = await localProgramExecution( + hello_hello_program, + "hello", + ["5u32", "5u32"], + ); postMessage(result); } else if (e.data === "key") { const result = getPrivateKey(); postMessage(result); } else if (e.data === "deploy") { - try { - const result = await deployProgram(hello_hello_program); - postMessage(result); - } catch (error) { - postMessage({ error: error.message }); + try { + const result = await deployProgram(hello_hello_program); + postMessage(result); + } catch (error) { + postMessage({ error: error.message }); + } } - } - }; diff --git a/create-leo-app/tsconfig.json b/create-leo-app/tsconfig.json index 65fffe745..e5b716ca9 100644 --- a/create-leo-app/tsconfig.json +++ b/create-leo-app/tsconfig.json @@ -1,15 +1,15 @@ { - "include": ["build.config.ts", "src", "__tests__"], - "compilerOptions": { - "outDir": "dist", - "target": "ES2020", - "module": "ES2020", - "moduleResolution": "bundler", - "strict": true, - "skipLibCheck": true, - "declaration": false, - "sourceMap": false, - "noUnusedLocals": true, - "esModuleInterop": true - } + "include": ["build.config.ts", "src", "__tests__"], + "compilerOptions": { + "outDir": "dist", + "target": "ES2020", + "module": "ES2020", + "moduleResolution": "bundler", + "strict": true, + "skipLibCheck": true, + "declaration": false, + "sourceMap": false, + "noUnusedLocals": true, + "esModuleInterop": true + } } diff --git a/docs/00_overview.md b/docs/00_overview.md index d9bf86994..854379968 100644 --- a/docs/00_overview.md +++ b/docs/00_overview.md @@ -16,22 +16,28 @@ sidebar_label: Overview Provable SDK

- -The [Provable SDK](https://github.com/ProvableHQ/sdk) is an open source TypeScript/JavaScript SDK for building private full stack web applications. The -SDK provides an easy API for using Aleo's core suite of cryptographic primitives, zk-SNARKs and private program execution -protocol directly within Javascript. It also provides APIs for interacting with nodes and applications on the Aleo Network. +The [Provable SDK](https://github.com/ProvableHQ/sdk) is an open source +TypeScript/JavaScript SDK for building private full stack web applications. The +SDK provides an easy API for using Aleo's core suite of cryptographic +primitives, zk-SNARKs and private program execution protocol directly within +Javascript. It also provides APIs for interacting with nodes and applications on +the Aleo Network. Applications built with the Provable SDK include: -* **Private DeFi** -* **Private Data Custody Systems** -* **Zk Games & Gaming Toolkits** -* **ZkML** -* **Zero-Knowledge Identity Management** -* **Aleo Wallets** + +- **Private DeFi** +- **Private Data Custody Systems** +- **Zk Games & Gaming Toolkits** +- **ZkML** +- **Zero-Knowledge Identity Management** +- **Aleo Wallets** ## Features -The Provable SDK provides the following features. Follow the links below for live demonstrations of these features on + +The Provable SDK provides the following features. Follow the links below for +live demonstrations of these features on [provable.tools](https://provable.tools) + 1. [Aleo account management](https://provable.tools/account) 2. [Aleo cryptographic primitives](https://provable.tools/algebra) 3. [Web based zero-knowledge program execution and deployment](https://provable.tools/develop) @@ -39,31 +45,42 @@ The Provable SDK provides the following features. Follow the links below for liv 5. [Management of program state](https://provable.tools/protocol) 6. [Communication with the Aleo network](https://provable.tools/rest) - ## Start Building -Developers interested in using the SDK to build private web applications can get started with the following resources. +Developers interested in using the SDK to build private web applications can get +started with the following resources. ### [The SDK Guide](https://docs.explorer.provable.com/docs/sdk/6p7047svvq2ox-intro-to-aleo) - Provable SDK -A step-by-step guide to building private web applications with the Provable SDK. The guide covers creating Aleo accounts, -transferring and receiving aleo credits, executing/deploying/interacting with Aleo programs, managing private and public -state, and best practices for building private web applications. + +Provable SDK + +A step-by-step guide to building private web applications with the Provable SDK. +The guide covers creating Aleo accounts, transferring and receiving aleo +credits, executing/deploying/interacting with Aleo programs, managing private +and public state, and best practices for building private web applications. ### [Create-Leo-App](https://docs.leo-lang.org/sdk/create-leo-app/tutorial) - Create Leo App + +Create Leo App -A suite of examples and templates for building private web applications in a variety of Javascript frameworks. These -examples can be easily installed and run using the `create-leo-app` command line tool. The examples cover a wide range -of use cases such as Private NFTs, offline transaction signing, and usage of the SDK in React, Next, and VanillaJS. +A suite of examples and templates for building private web applications in a +variety of Javascript frameworks. These examples can be easily installed and run +using the `create-leo-app` command line tool. The examples cover a wide range of +use cases such as Private NFTs, offline transaction signing, and usage of the +SDK in React, Next, and VanillaJS. ### [SDK API Documentation](https://docs.explorer.provable.com/docs/sdk/0qgi3uyhotv62-account) -For developers who prefer to dive straight into the code, the SDK API documentation provides a comprehensive reference -for all the SDK's classes, methods, and interfaces. The documentation is generated from the SDK's TypeScript source code. + +For developers who prefer to dive straight into the code, the SDK API +documentation provides a comprehensive reference for all the SDK's classes, +methods, and interfaces. The documentation is generated from the SDK's +TypeScript source code. ### [Leo Language Documentation](https://docs.leo-lang.org) -For developers who want to build their own zero-knowledge programs, the Leo Language provides an easy-to-use imperative -programming language for writing zero-knowledge programs on Aleo. In conjuction with the SDK, private programs built -using Leo can be turned into fully functional private web applications. \ No newline at end of file + +For developers who want to build their own zero-knowledge programs, the Leo +Language provides an easy-to-use imperative programming language for writing +zero-knowledge programs on Aleo. In conjuction with the SDK, private programs +built using Leo can be turned into fully functional private web applications. diff --git a/docs/create-leo-app/01_intro.md b/docs/create-leo-app/01_intro.md index fb065fdb5..b7d118009 100644 --- a/docs/create-leo-app/01_intro.md +++ b/docs/create-leo-app/01_intro.md @@ -1,38 +1,52 @@ # **Create Leo App** -Easily scaffold an Aleo project with `create-leo-app`. This tool helps developers quickly set up a project with best practices and pre-configured templates for working with Aleo smart contracts, zero-knowledge transactions, and frontend integrations. +Easily scaffold an Aleo project with `create-leo-app`. This tool helps +developers quickly set up a project with best practices and pre-configured +templates for working with Aleo smart contracts, zero-knowledge transactions, +and frontend integrations. -Create-leo-app is based on create-vite and follows a similar project structure. It helps developers bootstrap zero-knowledge applications on the Aleo network. +Create-leo-app is based on create-vite and follows a similar project structure. +It helps developers bootstrap zero-knowledge applications on the Aleo network. ## Getting Started -*Prerequisites* -Ensure you have: +_Prerequisites_ -* Node.js version 18+ installed. -* NPM 6.x+ or Yarn. +Ensure you have: +- Node.js version 18+ installed. +- NPM 6.x+ or Yarn. ### Scaffold Your First Aleo Project! + To create a new Aleo project, run: `npm create leo-app@latest` -Follow the prompts to select your preferred template. Each template provides pre-configured dependencies and example code to get started. We recommend that you explore the generated project files to understand the structure. +Follow the prompts to select your preferred template. Each template provides +pre-configured dependencies and example code to get started. We recommend that +you explore the generated project files to understand the structure. -***Note**: you will need to replace the placeholder private key in the templates with an actual private key in order to actually interact with the network. Be sure to follow the best practices at the end of this doc to handle your private key securely.* +**\*Note**: you will need to replace the placeholder private key in the +templates with an actual private key in order to actually interact with the +network. Be sure to follow the best practices at the end of this doc to handle +your private key securely.\* -Read more [here](https://docs.explorer.provable.com/docs/sdk/ghweubjq0or1x-creating-accounts) about account creation and deriving an address from your newly generated private key. +Read more +[here](https://docs.explorer.provable.com/docs/sdk/ghweubjq0or1x-creating-accounts) +about account creation and deriving an address from your newly generated private +key. We currently support the following templates: ### 1. Vanilla JavaScript -A minimalistic web app that allows users to engage with the Aleo network. +A minimalistic web app that allows users to engage with the Aleo network. ### 2. React with JavaScript -A React-based frontend that interacts with Aleo smart contracts using JavaScript. +A React-based frontend that interacts with Aleo smart contracts using +JavaScript. ### 3. React with TypeScript @@ -44,23 +58,25 @@ A Next.js template for building Aleo-powered web applications. ### 5. Node.js -A backend template for building Aleo transactions and interacting with the Aleo blockchain programmatically. - - - +A backend template for building Aleo transactions and interacting with the Aleo +blockchain programmatically. -------- +--- -After scaffolding your project, modify and extend it to interact with your Aleo program. +After scaffolding your project, modify and extend it to interact with your Aleo +program. -Learn more about writing Leo programs in our [official Leo language docs](https://docs.leo-lang.org/leo). +Learn more about writing Leo programs in our +[official Leo language docs](https://docs.leo-lang.org/leo). ### 🚨KEEP YOUR PRIVATE KEY SAFE!🚨 -Do not simply paste your private key into your source code! Here are some basic safety tips. +Do not simply paste your private key into your source code! Here are some basic +safety tips. -* Never hardcode your private key in your codebase (e.g., in your `worker.js`). -* Do not commit your private key to Git or any public repository. -* Use environment variables instead of storing your key in source files. -* Consider a secure key management service for production use. -* Always double-check your clipboard before pasting a private key. Malware can swap copied values. \ No newline at end of file +- Never hardcode your private key in your codebase (e.g., in your `worker.js`). +- Do not commit your private key to Git or any public repository. +- Use environment variables instead of storing your key in source files. +- Consider a secure key management service for production use. +- Always double-check your clipboard before pasting a private key. Malware can + swap copied values. diff --git a/docs/create-leo-app/02_react_js_tutorial.md b/docs/create-leo-app/02_react_js_tutorial.md index f1964bffb..71b5cb9a3 100644 --- a/docs/create-leo-app/02_react_js_tutorial.md +++ b/docs/create-leo-app/02_react_js_tutorial.md @@ -2,7 +2,8 @@ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/fork/github/ProvableHQ/sdk/tree/mainnet/create-leo-app/template-react-leo) -This template provides a minimal setup to get React and Aleo working in Webpack or Vite with HMR and some ESLint rules. +This template provides a minimal setup to get React and Aleo working in Webpack +or Vite with HMR and some ESLint rules. This template includes a Leo program that is loaded by the web app located in the `helloworld` directory. @@ -20,43 +21,50 @@ Your app should be running on http://localhost:5173/ 1. Copy the `helloworld/.env.example` to `helloworld/.env` (this will be ignored by Git): - ```bash - cd helloworld - cp .env.example .env - ``` + ```bash + cd helloworld + cp .env.example .env + ``` 2. Replace `PRIVATE_KEY=user1PrivateKey` in the `.env` with your own key (you - can use an existing one or generate your own at https://provable.tools/account) + can use an existing one or generate your own at + https://provable.tools/account) 3. Follow instructions to install Leo here: https://github.com/ProvableHQ/leo -4. You can edit `helloworld/src/main.leo` and run `leo run` to compile and update the - Aleo instructions under `build` which are loaded by the web app. +4. You can edit `helloworld/src/main.leo` and run `leo run` to compile and + update the Aleo instructions under `build` which are loaded by the web app. ## Deploy program from web app -> [!WARNING] -> This is for demonstration purposes or local testing only, in production applications you -> should avoid building a public facing web app with private key information +> [!WARNING] This is for demonstration purposes or local testing only, in +> production applications you should avoid building a public facing web app with +> private key information -Information on generating a private key, seeding a wallet with funds, and finding a spendable record can be found here -if you are unfamiliar: https://docs.leo-lang.org/testnet/getting_started/deploy_execute_demo +Information on generating a private key, seeding a wallet with funds, and +finding a spendable record can be found here if you are unfamiliar: +https://docs.leo-lang.org/testnet/getting_started/deploy_execute_demo -Aleo programs deployed require unique names, make sure to edit the program's name to something unique in `helloworld/src/main.leo`, `helloworld/program.json`, rename `helloworld/inputs/helloworld.in` and rebuild. +Aleo programs deployed require unique names, make sure to edit the program's +name to something unique in `helloworld/src/main.leo`, +`helloworld/program.json`, rename `helloworld/inputs/helloworld.in` and rebuild. 1. In the `worker.js` file modify the privateKey to be an account with available funds - ```js - // Use existing account with funds - const account = new Account({ - privateKey: "user1PrivateKey", - }); - ``` + ```js + // Use existing account with funds + const account = new Account({ + privateKey: "user1PrivateKey", + }); + ``` -2. (Optional) Provide a fee record manually (located in commented code within `worker.js`) +2. (Optional) Provide a fee record manually (located in commented code within + `worker.js`) - If you do not provide a manual fee record, the SDK will attempt to scan for a record starting at the latest block. A simple way to speed this up would be to make a public transaction to this account right before deploying. + If you do not provide a manual fee record, the SDK will attempt to scan for + a record starting at the latest block. A simple way to speed this up would + be to make a public transaction to this account right before deploying. 3. Run the web app and hit the deploy button diff --git a/docs/examples/01_transfer_public.md b/docs/examples/01_transfer_public.md index be0190c97..9bbb9c3b8 100644 --- a/docs/examples/01_transfer_public.md +++ b/docs/examples/01_transfer_public.md @@ -1,5 +1,5 @@ ```typescript -import { Account, ProgramManager, initThreadPool } from '@provable.sdk'; +import { Account, ProgramManager, initThreadPool } from "@provable.sdk"; // Initialize multi-threading to allow WASM execution. await initThreadPoool(); @@ -14,7 +14,11 @@ keyProvider.useCache(true); const recordProvider = new NetworkRecordProvider(account, networkClient); // Create program manager using the KeyProvider and NetworkProvider. -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); // Set the account as the program caller. programManager.setAccount(account); @@ -23,15 +27,15 @@ const recipient = new Account(); // Build a transfer_public transaction. // Publicly send 5 microcredits to the recipient -const transaction = await programManager - .buildTransferPublicTransaction( - 5, // The amount to be transferred in credits (not microcredits) - recipient // The address of the recipient. - .address() - .to_string(), - 0.0 // The optional priority fee amount. - ); +const transaction = await programManager.buildTransferPublicTransaction( + 5, // The amount to be transferred in credits (not microcredits) + recipient // The address of the recipient. + .address() + .to_string(), + 0.0, // The optional priority fee amount. +); // Broadcast the transaction to the Aleo network. -const result = await programManager.networkClient.submitTransaction(transaction); -``` \ No newline at end of file +const result = + await programManager.networkClient.submitTransaction(transaction); +``` diff --git a/docs/examples/02_transfer_private.md b/docs/examples/02_transfer_private.md index d3301eaad..1272b9d62 100644 --- a/docs/examples/02_transfer_private.md +++ b/docs/examples/02_transfer_private.md @@ -1,5 +1,5 @@ ```typescript -import { Account, ProgramManager, initThreadPool } from '@provable.sdk'; +import { Account, ProgramManager, initThreadPool } from "@provable.sdk"; // Initialize multi-threading to allow WASM execution. await initThreadPoool(); @@ -10,26 +10,29 @@ const account = new Account(); // Create a new NetworkClient, KeyProvider, and RecordProvider using official Aleo record, key, and network providers const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const keyProvider = new AleoKeyProvider(); -keyProvider.useCache(true);; +keyProvider.useCache(true); const recordProvider = new NetworkRecordProvider(account, networkClient); // Create program manager using the KeyProvider and NetworkProvider. -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); // Set the account as the program caller. programManager.setAccount(account); // Build a transfer_public_to_private transaction. // Create a credits record for the sender. -const transaction = await programManager - .buildTransferTransaction( - 5, // The amount to be transferred in credits (not microcredits) - account // The address of the recipient (In this case, your own address). - .address() - .to_string(), - "publicToPrivate", // The transfer type. - 0.0, // The optional priority fee amount. - false, // Indicates whether or not the fee will be private. - ); +const transaction = await programManager.buildTransferTransaction( + 5, // The amount to be transferred in credits (not microcredits) + account // The address of the recipient (In this case, your own address). + .address() + .to_string(), + "publicToPrivate", // The transfer type. + 0.0, // The optional priority fee amount. + false, // Indicates whether or not the fee will be private. +); // Broadcast the transaction to the Aleo network. let result = await programManager.networkClient.submitTransaction(transaction); @@ -38,7 +41,8 @@ let transactionFound = false; // Loop until the transaction has been Accepted while (!transactionFound) { try { - transactionObj = await programManager.networkClient.getTransactionObject(result); + transactionObj = + await programManager.networkClient.getTransactionObject(result); transactionFound = true; } catch (e) { console.error(e); @@ -55,17 +59,17 @@ const recipient = new Account(); // Build a transfer_private transaction. // Privately send 5 microcredits to the recipient from the sender's record -const transaction2 = await programManager - .buildTransferTransaction( - 5, // The amount to be transferred in credits (not microcredits) - recipient // The address of the recipient. - .address() - .to_string(), - "private", // The transfer type. - 0.0, // The optional priority fee amount. - false, // Indicates whether or not the fee will be private. - ); +const transaction2 = await programManager.buildTransferTransaction( + 5, // The amount to be transferred in credits (not microcredits) + recipient // The address of the recipient. + .address() + .to_string(), + "private", // The transfer type. + 0.0, // The optional priority fee amount. + false, // Indicates whether or not the fee will be private. +); // Broadcast the transaction to the Aleo network. -const result2 = await programManager.networkClient.submitTransaction(transaction2); +const result2 = + await programManager.networkClient.submitTransaction(transaction2); // -``` \ No newline at end of file +``` diff --git a/docs/examples/03_deploy_program.md b/docs/examples/03_deploy_program.md index 9c710aa31..156c5ff6b 100644 --- a/docs/examples/03_deploy_program.md +++ b/docs/examples/03_deploy_program.md @@ -1,5 +1,5 @@ ```typescript -import { Account, ProgramManager, initThreadPool } from '@provablehq/sdk'; +import { Account, ProgramManager, initThreadPool } from "@provablehq/sdk"; // Initialize multi-threading to allow WASM execution. await initThreadPoool(); @@ -14,7 +14,11 @@ keyProvider.useCache(true); const recordProvider = new NetworkRecordProvider(account, networkClient); // Create program manager using the KeyProvider and NetworkProvider. -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); // Set the account as the program caller. programManager.setAccount(account); @@ -30,7 +34,12 @@ function addition: `; // Create a deployment transaction using the declared source code. -const transaction = await programManager.buildDeploymentTransaction(program, 0.0, false); +const transaction = await programManager.buildDeploymentTransaction( + program, + 0.0, + false, +); // Broadcast the transaction to the Aleo network. -const result = await programManager.networkClient.submitTransaction(transaction); -``` \ No newline at end of file +const result = + await programManager.networkClient.submitTransaction(transaction); +``` diff --git a/docs/examples/04_execute_program.md b/docs/examples/04_execute_program.md index 36a4bf0bc..146ad22b5 100644 --- a/docs/examples/04_execute_program.md +++ b/docs/examples/04_execute_program.md @@ -1,6 +1,8 @@ -In this example we will be executing the program that was deployed in [the previous example](03_deploy_program.md) +In this example we will be executing the program that was deployed in +[the previous example](03_deploy_program.md) + ```typescript -import { Account, ProgramManager, initThreadPool } from '@provablehq/sdk'; +import { Account, ProgramManager, initThreadPool } from "@provablehq/sdk"; // Initialize multi-threading to allow WASM execution. await initThreadPoool(); @@ -15,7 +17,11 @@ keyProvider.useCache(true); const recordProvider = new NetworkRecordProvider(account, networkClient); // Create program manager using the KeyProvider and NetworkProvider. -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); // Set the account as the program caller. programManager.setAccount(account); @@ -30,7 +36,7 @@ const transaction = await programManager.buildExecutionTransaction({ priorityFee: 0.0, privateFee: false, inputs: [`${input1}u32`, `${input2}u32`], - keySearchParams: { "cacheKey": "addition_demo:addition" }, + keySearchParams: { cacheKey: "addition_demo:addition" }, }); // Broadcast the transaction to the Aleo network. @@ -41,7 +47,8 @@ let transactionFound = false; // Loop until the transaction has been Accepted while (!transactionFound) { try { - transactionObj = await programManager.networkClient.getTransactionObject(result); + transactionObj = + await programManager.networkClient.getTransactionObject(result); transactionFound = true; } catch (e) { console.error(e); @@ -51,4 +58,4 @@ while (!transactionFound) { let transition = transactionObj.transitions()[0]; // Get the output from the execution transition, which should be `42u32` let output = transition.outputs(true)[0]; -``` \ No newline at end of file +``` diff --git a/docs/guide/01_intro.md b/docs/guide/01_intro.md index 43254ea51..86c876817 100644 --- a/docs/guide/01_intro.md +++ b/docs/guide/01_intro.md @@ -4,21 +4,26 @@ title: Intro to Aleo via the Provable SDK sidebar_label: Intro --- -Building private web applications with the SDK requires a few fundamental pieces of knowledge about how Aleo enables -privacy. This guide introduces these concepts and provides on overview of the core building blocks of a "private app". +Building private web applications with the SDK requires a few fundamental pieces +of knowledge about how Aleo enables privacy. This guide introduces these +concepts and provides on overview of the core building blocks of a "private +app". # How does Aleo Create Privacy? -Aleo enables private apps through the use of `function privacy`. Functions executed on Aleo can have private inputs or -outputs and generate persistent private state. +Aleo enables private apps through the use of `function privacy`. Functions +executed on Aleo can have private inputs or outputs and generate persistent +private state. -Program functions can have private inputs or outputs that are only visible to the caller of the function (and in some -cases, an intended receiver) and encrypted for everyone else. This paradigm allows for building privacy preserving -protocls such as private transfers of assets, lending approvals which do not require the lender to know the borrower's -assets, private machine learning inferences, and more. +Program functions can have private inputs or outputs that are only visible to +the caller of the function (and in some cases, an intended receiver) and +encrypted for everyone else. This paradigm allows for building privacy +preserving protocls such as private transfers of assets, lending approvals which +do not require the lender to know the borrower's assets, private machine +learning inferences, and more. ```mermaid -graph +graph subgraph PrivateValue["Private Value Transfers"] Sender(["Sender(Private)"]) Amount(["Amount(Private)"]) @@ -48,6 +53,7 @@ graph LR style PrivateLending fill:#ffdbf0,stroke:#f229e0,stroke-width:2px,color:#000; linkStyle default stroke:#f229e0,stroke-width:2px; ``` + ```mermaid graph LR subgraph ZkML["ZkML"] @@ -70,15 +76,20 @@ graph LR # What is Aleo? -Aleo is composed of two main components which are used and operated by the `Aleo` community. +Aleo is composed of two main components which are used and operated by the +`Aleo` community. ## SnarkVM - The Aleo zkVM -A zkVM called `snarkVM` which provides private Program execution via the Varuna zkSnark as well and several libraries -of cryptographic primitives such as hash functions, field & elliptic curve arithmetic, and symmetric encryption tools. -`snarkVM` supports running executable programs that can have private inputs or outputs, allowing programs to be run -privately, producing a proof that the execution was run correctly without revealing the private inputs or outputs. -These proofs can be verified by any party, with the most common verifier being the Aleo Blockchain. +A zkVM called `snarkVM` which provides private Program execution via the Varuna +zkSnark as well and several libraries of cryptographic primitives such as hash +functions, field & elliptic curve arithmetic, and symmetric encryption tools. + +`snarkVM` supports running executable programs that can have private inputs or +outputs, allowing programs to be run privately, producing a proof that the +execution was run correctly without revealing the private inputs or outputs. +These proofs can be verified by any party, with the most common verifier being +the Aleo Blockchain. ```mermaid graph @@ -113,18 +124,25 @@ end ## The Aleo Blockchain -A blockchain network operated through community operated `SnarkOS` nodes which provide a decentralized network for -executing private programs. The network is secured by independent `validators` via a proof of stake consensus mechanism -called `Bullshark` that allows anyone to join or leave the validator set. An audit and description of Aleo Bullshark -can found in the [Aleo Bullshark Audit](https://docs.leo-lang.org/sdk/audit/bullshark) by zkSecurity. +A blockchain network operated through community operated `SnarkOS` nodes which +provide a decentralized network for executing private programs. The network is +secured by independent `validators` via a proof of stake consensus mechanism +called `Bullshark` that allows anyone to join or leave the validator set. An +audit and description of Aleo Bullshark can found in the +[Aleo Bullshark Audit](https://docs.leo-lang.org/sdk/audit/bullshark) by +zkSecurity. # Building Private Programs on Aleo ### Build & Use Private Programs with the Leo Language and Provable SDK -Developers can build their own private programs and deploy them to the `Aleo` network through the usage of the -[Leo language](https://docs.leo-lang.org/leo). Leo is a high-level, developer-friendly language for developing zero-knowledge programs. +Developers can build their own private programs and deploy them to the `Aleo` +network through the usage of the [Leo language](https://docs.leo-lang.org/leo). +Leo is a high-level, developer-friendly language for developing zero-knowledge +programs. -Developers can execute -and interact with private programs via the `Provable SDK`. The `Provable SDK` provides methods for executing programs -and interacting with public and private state on the `Aleo Network`. This guide provides a detailed overview of how to use the `Provable SDK` to build private full stack web applications. +Developers can execute and interact with private programs via the +`Provable SDK`. The `Provable SDK` provides methods for executing programs and +interacting with public and private state on the `Aleo Network`. This guide +provides a detailed overview of how to use the `Provable SDK` to build private +full stack web applications. diff --git a/docs/guide/02_setup.md b/docs/guide/02_setup.md index b322201cd..c69a16043 100644 --- a/docs/guide/02_setup.md +++ b/docs/guide/02_setup.md @@ -22,41 +22,56 @@ yarn add @provablehq/sdk ## Network Selection -The Provable SDK contains modules for interacting with both the `mainnet` and `testnet` networks. The `mainnet` and -`testnet` networks are **NOT** interoperable so it is required to explicitly select the desired network. Any -transactions built for the `mainnet` network will not be valid on the `testnet` network and vice versa. +The Provable SDK contains modules for interacting with both the `mainnet` and +`testnet` networks. The `mainnet` and `testnet` networks are **NOT** +interoperable so it is required to explicitly select the desired network. Any +transactions built for the `mainnet` network will not be valid on the `testnet` +network and vice versa. The following import syntax is used to select the desired network: ### Mainnet + ```typescript -import { Account, ProgramManager, initThreadPool } from '@provablehq/sdk/mainnet.js'; +import { + Account, + ProgramManager, + initThreadPool, +} from "@provablehq/sdk/mainnet.js"; ``` ### Testnet + ```typescript -import { Account, ProgramManager, initThreadPool } from '@provablehq/sdk/testnet.js'; +import { + Account, + ProgramManager, + initThreadPool, +} from "@provablehq/sdk/testnet.js"; ``` If no network is explicitly selected, the SDK defaults to the `testnet` network. ```typescript -import { Account, ProgramManager, initThreadPool } from '@provablehq/sdk'; +import { Account, ProgramManager, initThreadPool } from "@provablehq/sdk"; ``` - ## WebAssembly Initialization -When the SDK is imported, single-threaded `WebAssembly` is enabled by default. However, it is recommended to enable -multithreaded `WebAssembly` as it is much more performant and eliminates the possibility of a computationally expensive +When the SDK is imported, single-threaded `WebAssembly` is enabled by default. +However, it is recommended to enable multithreaded `WebAssembly` as it is much +more performant and eliminates the possibility of a computationally expensive operation blocking the main thread. -Multi-threaded `WebAssembly` is enabled by calling the `initThreadPool` function at the beginning of the application. -This starts multiple `WebWorker` threads and provides access to the `WebAssembly` instance and memory to each thread. +Multi-threaded `WebAssembly` is enabled by calling the `initThreadPool` function +at the beginning of the application. This starts multiple `WebWorker` threads +and provides access to the `WebAssembly` instance and memory to each thread. + +**This function only needs to be called once and should be called before any +other SDK functions.** -**This function only needs to be called once and should be called before any other SDK functions.** ```typescript -import { Account, initThreadPool } from '@provablehq/sdk/mainnet.js'; +import { Account, initThreadPool } from "@provablehq/sdk/mainnet.js"; // Enables multithreading await initThreadPool(); @@ -71,10 +86,12 @@ const account = new Account(); ### Top-Level Await -Top level await is a feature that allows you to use the `await` keyword outside of an `async` function. -This feature is necessary for the Provable SDK to function correctly. +Top level await is a feature that allows you to use the `await` keyword outside +of an `async` function. This feature is necessary for the Provable SDK to +function correctly. -In webpack this is enabled with the following options within `webpack.config.js`: +In webpack this is enabled with the following options within +`webpack.config.js`: ```json experiments: { @@ -85,10 +102,13 @@ experiments: { ### NodeJS Version (Node.JS Projects Only) -The Provable SDK requires a minimum of Node.js version 20 and recommends using node version 22+ for best performance. +The Provable SDK requires a minimum of Node.js version 20 and recommends using +node version 22+ for best performance. ### Framework Specific Configuration -The npm package [create-leo-app](https://www.npmjs.com/package/create-leo-app) offers several templates for building zero-knowledge JavaScript apps using several -popular frameworks including React, Next.js, and Node. Examining the configuration of these templates can provide -additional guidance on how to configure your project. +The npm package [create-leo-app](https://www.npmjs.com/package/create-leo-app) +offers several templates for building zero-knowledge JavaScript apps using +several popular frameworks including React, Next.js, and Node. Examining the +configuration of these templates can provide additional guidance on how to +configure your project. diff --git a/docs/guide/03_creating_accounts.md b/docs/guide/03_creating_accounts.md index 0d5afa53f..d5693e883 100644 --- a/docs/guide/03_creating_accounts.md +++ b/docs/guide/03_creating_accounts.md @@ -4,27 +4,37 @@ title: Creating and Managing Aleo Accounts sidebar_label: Creating and Managing Aleo Accounts --- -An Aleo account is defined as a Private Key, and several other cryptographic keys derived from the private key. +An Aleo account is defined as a Private Key, and several other cryptographic +keys derived from the private key. ## Account Keys ### Private Key -The `Private Key` can be thought of as the core identity of a user. It is the most sensitive of the keys within an Aleo -account and is used to derive all other keys. It is used to sign and create new program executions, to encrypt & decrypt -private data within a zero-knowledge function execution, and to generate signatures, commitments, and other key material -used in zero-knowledge proofs. + +The `Private Key` can be thought of as the core identity of a user. It is the +most sensitive of the keys within an Aleo account and is used to derive all +other keys. It is used to sign and create new program executions, to encrypt & +decrypt private data within a zero-knowledge function execution, and to generate +signatures, commitments, and other key material used in zero-knowledge proofs. ### View Key -The `View Key` is derived from the private key and can be used to both decrypt encrypted data owned by a user and prove -ownership of that data. Specific usages of this key include, determining ownership of records, decrypting records, -and decrypting private inputs or outputs of a zero-knowledge program generated by the owner of the private key. + +The `View Key` is derived from the private key and can be used to both decrypt +encrypted data owned by a user and prove ownership of that data. Specific usages +of this key include, determining ownership of records, decrypting records, and +decrypting private inputs or outputs of a zero-knowledge program generated by +the owner of the private key. ### Compute Key -The `Compute Key` can be used to trustlessly run applications and generate transactions on a user's behalf. + +The `Compute Key` can be used to trustlessly run applications and generate +transactions on a user's behalf. ### Address -The `Address` is a user's unique public identifier. It serves as an address for a user to receive both Aleo credits and -data from other zero-knowledge Aleo programs. + +The `Address` is a user's unique public identifier. It serves as an address for +a user to receive both Aleo credits and data from other zero-knowledge Aleo +programs. ```mermaid flowchart TD @@ -46,7 +56,7 @@ flowchart TD private data it owns and prove ownership of data. - + ) B --> E( Compute Key: @@ -63,15 +73,15 @@ flowchart TD linkStyle default stroke:#f229e0,stroke-width:2px; ``` -Please note that all keys are considered sensitive information and -should be stored securely. +Please note that all keys are considered sensitive information and should be +stored securely. ## Creating an Account -All keys can be created using the account object. +All keys can be created using the account object. ```typescript -import { Account } from '@provablehq/sdk'; +import { Account } from "@provablehq/sdk"; const account = new Account(); @@ -83,39 +93,44 @@ const address = account.address(); ``` Alternatively, an account can be created with an existing private key: + ```typescript -import { Account } from '@provablehq/sdk'; -import { PrivateKey } from './wasm'; +import { Account } from "@provablehq/sdk"; +import { PrivateKey } from "./wasm"; // From a newly generated private key const privateKey = new PrivateKey(); const account = new Account({ privateKey }); // From a private key derived from its string representation -const privateKey2 = PrivateKey.fromString('APrivateKey1...'); +const privateKey2 = PrivateKey.fromString("APrivateKey1..."); const account2 = new Account({ privateKey: privateKey2, }); // From a private key string const account3 = new Account({ - privateKey: 'APrivateKey1...', + privateKey: "APrivateKey1...", }); ``` Or an encrypted ciphertext of the private key: + ```typescript -import { Account } from '@provablehq/sdk'; -import { PrivateKey } from './wasm'; +import { Account } from "@provablehq/sdk"; +import { PrivateKey } from "./wasm"; // From a newly generated encrypted private key -const password = 'password'; +const password = "password"; const ciphertext = PrivateKey.newEncrypted(password); const account = Account.fromCiphertext(ciphertext, password); // From the encryption of an existing private key -const privateKey = PrivateKey.fromString('APrivateKey1...'); -const existingPassword = 'existingPassword'; +const privateKey = PrivateKey.fromString("APrivateKey1..."); +const existingPassword = "existingPassword"; const existingCiphertext = privateKey.toCiphertext(existingPassword); -const existingAccount = Account.fromCiphertext(existingCiphertext, existingPassword); -``` \ No newline at end of file +const existingAccount = Account.fromCiphertext( + existingCiphertext, + existingPassword, +); +``` diff --git a/docs/guide/04_programs.md b/docs/guide/04_programs.md index ef844a13a..36270e01d 100644 --- a/docs/guide/04_programs.md +++ b/docs/guide/04_programs.md @@ -4,9 +4,11 @@ title: Aleo Programs and Transactions sidebar_label: Programs and Transactions --- -Programs lie at the core of the Aleo protocol. All transactions either execute program functions or deploy new programs. -Program function executions are the primary method of updating the state of the Aleo Blockchain. This section -introduces the core ideass behind programs, the structure of a program, and the lifecycle of an Execution transaction. +Programs lie at the core of the Aleo protocol. All transactions either execute +program functions or deploy new programs. Program function executions are the +primary method of updating the state of the Aleo Blockchain. This section +introduces the core ideass behind programs, the structure of a program, and the +lifecycle of an Execution transaction. ```mermaid graph TD @@ -27,7 +29,7 @@ graph TD credential_store.aleo"] end BlockN --> batchProposal --> BlockN+1 - + classDef default fill:#fff3e0,stroke:#ff9800,stroke-width:2px,color:#000; style BlockN fill:#ffdbf0,stroke:#f229e0,stroke-width:2px,color:#000; style BlockN+1 fill:#ffdbf0,stroke:#f229e0,stroke-width:2px,color:#000; @@ -37,20 +39,22 @@ graph TD ## Function Privacy -Every program on Aleo is open source and has one or more functions that can be executed. When a function is executed -and included within a transaction that is accepted into a block, it causes a state change on the Aleo network. The -designers of a function can choose to make some or all of this state change private - +Every program on Aleo is open source and has one or more functions that can be +executed. When a function is executed and included within a transaction that is +accepted into a block, it causes a state change on the Aleo network. The +designers of a function can choose to make some or all of this state change +private ### Private Inputs and Outputs -Program functions can have private inputs or outputs that are only visible to the caller of the function (and in some -cases, intended receivers) and encrypted for everyone else. This paradigm allows for privacy preserving actions such as -private transfers of assets, lending approvals which do not require the lender to know the borrower's assets, -private machine learning inferences, and more. +Program functions can have private inputs or outputs that are only visible to +the caller of the function (and in some cases, intended receivers) and encrypted +for everyone else. This paradigm allows for privacy preserving actions such as +private transfers of assets, lending approvals which do not require the lender +to know the borrower's assets, private machine learning inferences, and more. ```mermaid -graph +graph subgraph PrivateValue["Private Value Transfers"] Sender(["Sender(Private)"]) Amount(["Amount(Private)"]) @@ -80,6 +84,7 @@ graph LR style PrivateLending fill:#ffdbf0,stroke:#f229e0,stroke-width:2px,color:#000; linkStyle default stroke:#f229e0,stroke-width:2px; ``` + ```mermaid graph LR subgraph ZkML["ZkML"] @@ -102,22 +107,28 @@ graph LR ### Structure of a Program -Programs are collections of functions, private records, data structure definitions and on-chain public data-stores. +Programs are collections of functions, private records, data structure +definitions and on-chain public data-stores. + +When a function within a program is executed with any mix of private or public +inputs, the output is an `Execution` object that contains: -When a function within a program is executed with any mix of private or public inputs, the output is an `Execution` -object that contains: 1. A proof that the function was executed correctly. 2. A list of `Transitions` which enumerate the following - - **Public Inputs/Outputs:** A list of public inputs/outputs - - **Encryped Private Input/Outputs:** A list of encrypted private inputs/outputs that is - only decryptable by the holder of private key (or view key) of the user who executed the program. - - **Records:** Special, encrypted UTXO-like structs that store longterm private state. - - **Futures:** Any optional code marked as a future to be executed on chain later - -The `Transitions` provides a transcript of the function's inputs and outputs and the proof provides certainty that the -function was executed correctly. This allows outside verifiers to trust that the private inputs and outputs are correct -without the need to see them. This is the core of the Aleo protocol's privacy guarantees as it allows for fully private -execution of programs. + - **Public Inputs/Outputs:** A list of public inputs/outputs + - **Encryped Private Input/Outputs:** A list of encrypted private + inputs/outputs that is only decryptable by the holder of private key (or + view key) of the user who executed the program. + - **Records:** Special, encrypted UTXO-like structs that store longterm + private state. + - **Futures:** Any optional code marked as a future to be executed on chain + later + +The `Transitions` provides a transcript of the function's inputs and outputs and +the proof provides certainty that the function was executed correctly. This +allows outside verifiers to trust that the private inputs and outputs are +correct without the need to see them. This is the core of the Aleo protocol's +privacy guarantees as it allows for fully private execution of programs. ```mermaid graph LR @@ -139,7 +150,7 @@ graph LR (UTXO-like structs)"} end subgraph Data - Structs["Structs + Structs["Structs (user-defined)"] Arrays Literals["Literals"] @@ -169,17 +180,23 @@ graph LR ### Aleo Programming Languages Programs on Aleo are written in one of two languages: -1. [Leo](https://docs.leo-lang.org/leo): A high-level, developer-friendly language for developing zero-knowledge programs. -2. [Aleo Instructions](https://developer.aleo.org/guides/aleo/aleo/): A low-level language that provides developers with fine-grained control over the execution - flow of zero-knowledge programs. Leo code is compiled into Aleo Instructions under the hood. +1. [Leo](https://docs.leo-lang.org/leo): A high-level, developer-friendly + language for developing zero-knowledge programs. + +2. [Aleo Instructions](https://developer.aleo.org/guides/aleo/aleo/): A + low-level language that provides developers with fine-grained control over + the execution flow of zero-knowledge programs. Leo code is compiled into Aleo + Instructions under the hood. -Documentation for both languages can be found at [docs.leo-lang.org](https://docs.leo-lang.org/leo). +Documentation for both languages can be found at +[docs.leo-lang.org](https://docs.leo-lang.org/leo). -Those interested in attempting to build programs immediately should visit the Leo Playground at -[play.leo-lang.org](https://play.leo-lang.org/). +Those interested in attempting to build programs immediately should visit the +Leo Playground at [play.leo-lang.org](https://play.leo-lang.org/). #### "Hello World" in Leo + ``` // A simple program adding two numbers together program helloworld.aleo { @@ -191,6 +208,7 @@ program helloworld.aleo { ``` #### "Hello World" in Aleo Instructions + ``` program helloworld.aleo; @@ -204,32 +222,43 @@ function hello: ## Transactions -Transactions are the primary method of updating the state of the Aleo Network. There are two types of transactions in Aleo: +Transactions are the primary method of updating the state of the Aleo Network. +There are two types of transactions in Aleo: ### Execution Transactions -As discussed in the Programs section above, an Execution Transaction contains the following -* A proof of execution that one or more Aleo Programs were executed correct -* A set of `Transitions` which list the inputs and outputs of the function executions -* A fee that is paid to the network for the transaction. -Execution transactions update the state of the ledger, and update the internal state of the programs that were executed. +As discussed in the Programs section above, an Execution Transaction contains +the following + +- A proof of execution that one or more Aleo Programs were executed correct +- A set of `Transitions` which list the inputs and outputs of the function + executions +- A fee that is paid to the network for the transaction. + +Execution transactions update the state of the ledger, and update the internal +state of the programs that were executed. State is updated in one of two ways: -1. **Public State:** If a function contains a **future**, the future is executed on-chain by the validators and the -public mappings (an on-chain key-value store associated with a program). -2. **Private State:** If a function takes a record as input, that record is "spent". If a function returns a record as -output, a new record is created and stored in the Block and can be used as input in future transactions. + +1. **Public State:** If a function contains a **future**, the future is executed + on-chain by the validators and the public mappings (an on-chain key-value + store associated with a program). +2. **Private State:** If a function takes a record as input, that record is + "spent". If a function returns a record as output, a new record is created + and stored in the Block and can be used as input in future transactions. ### Deployment Transactions -Transactions which add a new program to the Aleo chain (with blank state). Once programs are deployed, execution -transactions can be sent that change the state of the program and the Aleo ledger. + +Transactions which add a new program to the Aleo chain (with blank state). Once +programs are deployed, execution transactions can be sent that change the state +of the program and the Aleo ledger. ### Lifecycle of a Transaction The following diagram shows the lifecycle of a transaction in the Aleo network. ```mermaid -graph +graph subgraph Transaction["Execute Transaction"] subgraph Execution Proof@{ shape: document, label: "Proof" } @@ -272,7 +301,9 @@ graph ## Building Transactions with the SDK -The SDK provides the ability to both build transactions and submit them to the Aleo network. It also allows for the -inspection of the data in existing transactions or programs so that data on-chain can be used within a front or backend -application. The following sections will provide an overview of how to build both transfers of both Aleo credits, -arbitrary programs, and how to build and deploy new programs. \ No newline at end of file +The SDK provides the ability to both build transactions and submit them to the +Aleo network. It also allows for the inspection of the data in existing +transactions or programs so that data on-chain can be used within a front or +backend application. The following sections will provide an overview of how to +build both transfers of both Aleo credits, arbitrary programs, and how to build +and deploy new programs. diff --git a/docs/guide/05_transfers.md b/docs/guide/05_transfers.md index b6d7b37da..0de61fde8 100644 --- a/docs/guide/05_transfers.md +++ b/docs/guide/05_transfers.md @@ -6,16 +6,21 @@ sidebar_label: Aleo Credits Transfers ## Credits.aleo -The official currency of Aleo Network are called `Aleo Credits`. All fees paid for transactions and rewards for staking -and mining on the Aleo Network are transacted in Aleo Credits. - -Unlike other popular Blockchains like Ethereum, there is no special `transfer` transaction type. Instead, a native -program called [credits.aleo](https://explorer.provable.com/program/credits.aleo) governs transfers, usage, and ownership -of Aleo Credits. All value transfers on the Aleo Network are done by calling functions in the `credits.aleo` program -via `Execute` transactions. This enables users to send Aleo Credits privately, publicly, or a mix of both as well as -initiate staking and other advanced on-chain operations with Aleo credits. - -A small selection of the credit transfer functions available in credits.aleo is visualized below: +The official currency of Aleo Network are called `Aleo Credits`. All fees paid +for transactions and rewards for staking and mining on the Aleo Network are +transacted in Aleo Credits. + +Unlike other popular Blockchains like Ethereum, there is no special `transfer` +transaction type. Instead, a native program called +[credits.aleo](https://explorer.provable.com/program/credits.aleo) governs +transfers, usage, and ownership of Aleo Credits. All value transfers on the Aleo +Network are done by calling functions in the `credits.aleo` program via +`Execute` transactions. This enables users to send Aleo Credits privately, +publicly, or a mix of both as well as initiate staking and other advanced +on-chain operations with Aleo credits. + +A small selection of the credit transfer functions available in credits.aleo is +visualized below: ```mermaid graph @@ -43,32 +48,40 @@ style credits fill:#ffdbf0,stroke:#f229e0,stroke-width:2px,color:#000; linkStyle default stroke:#f229e0,stroke-width:2px; ``` -## Aleo credits +## Aleo credits -Aleo Credits are used to pay all fees on the network. They are also used to initiate staking, to bond in new validators, -and are used as the currency to used to pay staking and mining rewards. +Aleo Credits are used to pay all fees on the network. They are also used to +initiate staking, to bond in new validators, and are used as the currency to +used to pay staking and mining rewards. There are two main ways to hold Aleo credits within credits.aleo: -### 1 - Private balances via `credits.aleo` records -The first method is owning a `credits` record which enables a participant in the Aleo -network to hold a private balance of Aleo credits. +### 1 - Private balances via `credits.aleo` records + +The first method is owning a `credits` record which enables a participant in the +Aleo network to hold a private balance of Aleo credits. + ``` record credits: owner as address.private; microcredits as u64.private; ``` -A user's total private credits balance will consist of all unspent `credits` records owned by the user with a non-zero -`microcredits` value. These records are analogous to UTXOs in Bitcoin. It is generally the responsibility of a wallet -application to scan the chain for records that belong to a user and determine which are spent and unspent in order -to calculate the user's total private balance and private transaction history. +A user's total private credits balance will consist of all unspent `credits` +records owned by the user with a non-zero `microcredits` value. These records +are analogous to UTXOs in Bitcoin. It is generally the responsibility of a +wallet application to scan the chain for records that belong to a user and +determine which are spent and unspent in order to calculate the user's total +private balance and private transaction history. ### 2 - Public balances via `credits.aleo` account mappings -The second method is by holding a `balance` in the `account` mapping in the `credits.aleo` program on the Aleo network. -This mapping is an on-chain key-value store associated with the `credits.aleo` program that is maintained and updated -by Aleo validators at each block. This public balance is visible to all participants in the network and is analogous to -the account balances in Ethereum. + +The second method is by holding a `balance` in the `account` mapping in the +`credits.aleo` program on the Aleo network. This mapping is an on-chain +key-value store associated with the `credits.aleo` program that is maintained +and updated by Aleo validators at each block. This public balance is visible to +all participants in the network and is analogous to the account balances in +Ethereum. ``` mapping account: @@ -76,18 +89,22 @@ mapping account: value microcredits as u64.public; ``` -The total public credits balance of a user is the value of the account mapping at the user's address. Users can hold both private and public balances simultaneously. +The total public credits balance of a user is the value of the account mapping +at the user's address. Users can hold both private and public balances +simultaneously. ## Transferring Aleo credits -The `ProgramManager` allows transfers of Aleo credits via the `transfer` method. This function executes the `credits.aleo` -program under the hood. + +The `ProgramManager` allows transfers of Aleo credits via the `transfer` method. +This function executes the `credits.aleo` program under the hood. There are four transfer functions available. ### 1. `transfer_private` -Takes a `credits` record owned by the sender, subtracts an amount from it, and adds that amount -to a new record owned by the receiver. This function is 100% private and does not affect the `account` mapping. +Takes a `credits` record owned by the sender, subtracts an amount from it, and +adds that amount to a new record owned by the receiver. This function is 100% +private and does not affect the `account` mapping. ```mermaid graph LR @@ -102,9 +119,10 @@ graph LR ### 2. `transfer_private_to_public` -Takes a `credits` record owned by the sender, subtracts an amount from it, and adds -that amount to the `account` mapping of the receiver. This function is 50% private and 50% public. It consumes a record -as a private input and generates a public balance in the `account` mapping entry belonging to the receiver. +Takes a `credits` record owned by the sender, subtracts an amount from it, and +adds that amount to the `account` mapping of the receiver. This function is 50% +private and 50% public. It consumes a record as a private input and generates a +public balance in the `account` mapping entry belonging to the receiver. ```mermaid graph LR @@ -114,16 +132,17 @@ graph LR user1--record3 \n owner: user2address \n balance: 4000u64-->t1[transfer_private_to_public] t1-.record4 \n owner: user2address \n amount: 1000u64.->user1 t1--amount 3000u64-->m1 - + classDef default fill:#fff3e0,stroke:#ff9800,stroke-width:2px,color:#000; linkStyle default stroke:#f229e0,stroke-width:2px; ``` ### 3. `transfer_public` -Subtracts an amount of `credits` stored in the `account` mapping of the `credits.aleo` program, and -adds that amount to the `account` mapping of the receiver. This function is 100% public and does not consume or generate -any records. +Subtracts an amount of `credits` stored in the `account` mapping of the +`credits.aleo` program, and adds that amount to the `account` mapping of the +receiver. This function is 100% public and does not consume or generate any +records. ```mermaid graph LR @@ -145,10 +164,11 @@ graph LR ### 4. `transfer_public_to_private` -Subtracts an amount `credits` stored in the `account` mapping of the `credits.aleo program` -and adds that amount to a new private record owned by the receiver. This function is 50% private and 50% public. -It publicly consumes a balance in the `account` mapping entry belonging to the sender and generates a private record -as a private output. +Subtracts an amount `credits` stored in the `account` mapping of the +`credits.aleo program` and adds that amount to a new private record owned by the +receiver. This function is 50% private and 50% public. It publicly consumes a +balance in the `account` mapping entry belonging to the sender and generates a +private record as a private output. ```mermaid graph LR @@ -168,62 +188,112 @@ graph LR linkStyle default stroke:#f229e0,stroke-width:2px; ``` -All four of these functions can be used to transfer credits between users via the `transfer` function in the -`ProgramManager` by specifying the transfer type as the third argument. +All four of these functions can be used to transfer credits between users via +the `transfer` function in the `ProgramManager` by specifying the transfer type +as the third argument. ```typescript -import { Account, ProgramManager, AleoKeyProvider, NetworkRecordProvider, AleoNetworkClient } from '@provablehq/sdk'; +import { + Account, + ProgramManager, + AleoKeyProvider, + NetworkRecordProvider, + AleoNetworkClient, +} from "@provablehq/sdk"; // Create a new NetworkClient, KeyProvider, and RecordProvider -const account = Account.from_string({privateKey: "user1PrivateKey"}); +const account = Account.from_string({ privateKey: "user1PrivateKey" }); const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const keyProvider = new AleoKeyProvider(); const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions const myAddress = account.address(); -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); programManager.setAccount(account); // Send a private transfer to yourself -const tx_id = await programManager.transfer(1, myAddress, "transfer_private", 0.2); +const tx_id = await programManager.transfer( + 1, + myAddress, + "transfer_private", + 0.2, +); // Update or initialize a public balance in your own account mapping -const tx_id_2 = await programManager.transfer(1, myAddress, "transfer_private_to_public", 0.2); +const tx_id_2 = await programManager.transfer( + 1, + myAddress, + "transfer_private_to_public", + 0.2, +); // Check the value of your public balance -let public_balance = programManager.networkClient.getMappingValue("credits.aleo", myAddress); -assert(public_balance === 0.2*1_000_000); +let public_balance = programManager.networkClient.getMappingValue( + "credits.aleo", + myAddress, +); +assert(public_balance === 0.2 * 1_000_000); /// Send public transfer to another user const USER_2_ADDRESS = "user2Address"; -const tx_id_3 = await programManager.transfer(1, USER_2_ADDRESS, "transfer_public", 0.1); +const tx_id_3 = await programManager.transfer( + 1, + USER_2_ADDRESS, + "transfer_public", + 0.1, +); // Check the value of the public balance and assert that it has been updated -public_balance = programManager.networkClient.getMappingValue("credits.aleo", myAddress); -const user2_public_balance = programManager.networkClient.getMappingValue("credits.aleo", myAddress); -assert(public_balance === 0.1*1_000_000); -assert(user2_public_balance === 0.1*1_000_000); +public_balance = programManager.networkClient.getMappingValue( + "credits.aleo", + myAddress, +); +const user2_public_balance = programManager.networkClient.getMappingValue( + "credits.aleo", + myAddress, +); +assert(public_balance === 0.1 * 1_000_000); +assert(user2_public_balance === 0.1 * 1_000_000); /// Create a private record from a public balance -const tx_id_4 = await programManager.transfer(1, myAddress, "transfer_public_to_private", 0.1); +const tx_id_4 = await programManager.transfer( + 1, + myAddress, + "transfer_public_to_private", + 0.1, +); // Check the value of the public balance and assert that it has been updated -public_balance = programManager.networkClient.getMappingValue("credits.aleo", myAddress); +public_balance = programManager.networkClient.getMappingValue( + "credits.aleo", + myAddress, +); assert(public_balance === 0); ``` ## Public balances -The `account` mapping of `credits.aleo` contains the public balances of all addresses on the Aleo network. A public -balance of any address can be checked with `getMappingValue` function of the `NetworkClient`. + +The `account` mapping of `credits.aleo` contains the public balances of all +addresses on the Aleo network. A public balance of any address can be checked +with `getMappingValue` function of the `NetworkClient`. ```typescript const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const USER_1_ADDRESS = "user1Address"; -const public_balance = networkClient.getMappingValue("credits.aleo", USER_1_ADDRESS); +const public_balance = networkClient.getMappingValue( + "credits.aleo", + USER_1_ADDRESS, +); ``` ## Private Balances -The private balance of an address is the sum of all unspent `credits` records owned by the address. A full article on -how to calculate the private balance of an address is available in the [finding records](./10_finding_records.md) of -the documentation. \ No newline at end of file + +The private balance of an address is the sum of all unspent `credits` records +owned by the address. A full article on how to calculate the private balance of +an address is available in the [finding records](./10_finding_records.md) of the +documentation. diff --git a/docs/guide/06_executing_programs.md b/docs/guide/06_executing_programs.md index 4ff7f4059..fbbc89e84 100644 --- a/docs/guide/06_executing_programs.md +++ b/docs/guide/06_executing_programs.md @@ -4,16 +4,20 @@ title: Executing Aleo Programs sidebar_label: Executing Aleo Programs --- -The Provable SDK provides the ability to build `Execution transactions` locally and submit them to the Aleo Network. +The Provable SDK provides the ability to build `Execution transactions` locally +and submit them to the Aleo Network. -The `ProgramManager` class encapsulates the functionality for executing programs and building `Execution Transactions`. -The ProgramManager calls code from [snarkVM](https://github.com/ProvableHQ/snarkVM) that has been compiled into WebAssembly. -This code runs the execution, creates the resulting transitions and the zk-SNARK proof of the execution within the -local WebAssembly environment and returns an `Execution Transaction` when it is finished. Once the transaction is built, -it is submitted to the Aleo network using the `NetworkClient` class. +The `ProgramManager` class encapsulates the functionality for executing programs +and building `Execution Transactions`. The ProgramManager calls code from +[snarkVM](https://github.com/ProvableHQ/snarkVM) that has been compiled into +WebAssembly. This code runs the execution, creates the resulting transitions and +the zk-SNARK proof of the execution within the local WebAssembly environment and +returns an `Execution Transaction` when it is finished. Once the transaction is +built, it is submitted to the Aleo network using the `NetworkClient` class. -This process is visualized within the following diagram. The necessary steps to perform these actions within JavaScript -or Typescript are explained in this guide. +This process is visualized within the following diagram. The necessary steps to +perform these actions within JavaScript or Typescript are explained in this +guide. ```mermaid graph @@ -48,12 +52,14 @@ graph ``` ## 1. WebAssembly Initialization + Before executing a function within the Browser or Node environment. This is done -by calling `initThreadPool` function which initializes `wasm` memory and creates a `wasm` instance which can take -advantage of multiple available threads on the host machine. +by calling `initThreadPool` function which initializes `wasm` memory and creates +a `wasm` instance which can take advantage of multiple available threads on the +host machine. ```typescript -import { Account, initThreadPool } from '@provablehq/sdk/mainnet.js'; +import { Account, initThreadPool } from "@provablehq/sdk/mainnet.js"; // Enables multithreading await initThreadPool(); @@ -62,16 +68,20 @@ await initThreadPool(); const account = new Account(); // Perform further program logic... -```` +``` This step MUST -1. Be performed before any other operations are executed. -2. Be performed only once per for the entire application lifecycle. **Do not call `initThreadPool` multiple times.** + +1. Be performed before any other operations are executed. +2. Be performed only once per for the entire application lifecycle. **Do not + call `initThreadPool` multiple times.** ## 2. Building an Execution Transaction -The `ProgramManager` class has multiple convenience methods for building `Execution` and `Deployment` transactions. -Shown below is a partial class diagram of the `ProgramManager` class introducing the methods that are used to build -general execution and deployment transactions. + +The `ProgramManager` class has multiple convenience methods for building +`Execution` and `Deployment` transactions. Shown below is a partial class +diagram of the `ProgramManager` class introducing the methods that are used to +build general execution and deployment transactions. ```mermaid classDiagram @@ -88,27 +98,36 @@ classDiagram // Builds a Deployment Transaction and immediately sends it to the Aleo Network () +deploy(options: DeploymentOptions): Promise // Builds an Execution Transaction and immediately sends it to the Aleo Network () - +execute(options: ExecuteOptions): Promise // Builds + +execute(options: ExecuteOptions): Promise // Builds ...other methods() } ``` -There are two main methods for building general execution transactions: `execute` and `buildExecutionTransaction`. -Calling `execute` will build and submit the transaction to the Aleo network, while `buildExecutionTransaction` will -only build the transaction and return it to the caller within Javascript. +There are two main methods for building general execution transactions: +`execute` and `buildExecutionTransaction`. Calling `execute` will build and +submit the transaction to the Aleo network, while `buildExecutionTransaction` +will only build the transaction and return it to the caller within Javascript. -The example below illustrates how to instantiate a `ProgramManager`, build an execution transaction, and submit it to -the Aleo network (note: Ensure that your project supports `top-level await`). +The example below illustrates how to instantiate a `ProgramManager`, build an +execution transaction, and submit it to the Aleo network (note: Ensure that your +project supports `top-level await`). ```typescript -import { Account, AleoNetworkClient, initThreadPool, NetworkRecordProvider, ProgramManager, AleoKeyProvider } from '@provablehq/sdk/mainnet.js'; - -// If the threadpool has not been initialized, do so (this step can be skipped if it's been initialized elsewhere). +import { + Account, + AleoNetworkClient, + initThreadPool, + NetworkRecordProvider, + ProgramManager, + AleoKeyProvider, +} from "@provablehq/sdk/mainnet.js"; + +// If the threadpool has not been initialized, do so (this step can be skipped if it's been initialized elsewhere). await initThreadPool(); // Create an account from the desired private key. -const account = new Account({ privateKey: 'APrivateKey1...'}); +const account = new Account({ privateKey: "APrivateKey1..." }); // Create a network client to connect to the Aleo network. const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); @@ -134,55 +153,73 @@ try { functionName: "stake_public", priorityFee: 0.0, privateFee: false, // Assuming a value for privateFee - inputs: ["aleo17x23al8k9scqe0qqdppzcehlu8vm0ap0j5mukskdq56lsa25lv8qz5cz3g", "50000000u64"], // Example inputs matching the function definition + inputs: [ + "aleo17x23al8k9scqe0qqdppzcehlu8vm0ap0j5mukskdq56lsa25lv8qz5cz3g", + "50000000u64", + ], // Example inputs matching the function definition keySearchParams: keySearchParams, - privateKey: account.privateKey() // Set the private key + privateKey: account.privateKey(), // Set the private key }); - + // Submit the program to the network. - const transaction_id = await programManager.networkClient.submitTransaction(tx); + const transaction_id = + await programManager.networkClient.submitTransaction(tx); console.log("Transaction details: ", transaction); } catch (error) { console.error("Error executing program:", error); } -// Generally the transaction will need 1-3 blocks (3-9 seconds) to be confirmed on the network. When that time has +// Generally the transaction will need 1-3 blocks (3-9 seconds) to be confirmed on the network. When that time has // elapsed the following function can be used to get the transaction details. const transaction = await programManager.networkClient.getTransaction(tx_id); ``` ### Key Provider: Finding Proving and Verifying Keys -A reader of the above example may notice `KeyProvider` class. +A reader of the above example may notice `KeyProvider` class. -Since each function in a program has a proof associated with it, each function in a program has something called a -`ProvingKey` and `VerifyingKey`. These keys are cryptographic material that uniquely identifies the structure of the -function and are required to build the proof and verify the proof respectively. A unique `ProvingKey` and `VerifyingKey` -is generated for each function in a program. +Since each function in a program has a proof associated with it, each function +in a program has something called a `ProvingKey` and `VerifyingKey`. These keys +are cryptographic material that uniquely identifies the structure of the +function and are required to build the proof and verify the proof respectively. +A unique `ProvingKey` and `VerifyingKey` is generated for each function in a +program. -If an execution in the SDK does not have the keys, it will generate them. However, generating them is a computationally -expensive process, and significantly slows down the execution process if they need. It is wise for developers to store -them for re-use when possible. The SDK provides an interface called the `KeyProvider` to enable developers to define -easy ways to retrieve these keys. +If an execution in the SDK does not have the keys, it will generate them. +However, generating them is a computationally expensive process, and +significantly slows down the execution process if they need. It is wise for +developers to store them for re-use when possible. The SDK provides an interface +called the `KeyProvider` to enable developers to define easy ways to retrieve +these keys. -The default implementation of the `KeyProvider` interface is the `AleoKeyProvider`. This `KeyProvider` implementation -allows users to specify an optional HTTP url where the keys may be found and an in-memory cache for proving and -verifying keys. However, developers can implement their own `KeyProvider` to store keys in places such as CDNs, -databases, local file systems, etc. +The default implementation of the `KeyProvider` interface is the +`AleoKeyProvider`. This `KeyProvider` implementation allows users to specify an +optional HTTP url where the keys may be found and an in-memory cache for proving +and verifying keys. However, developers can implement their own `KeyProvider` to +store keys in places such as CDNs, databases, local file systems, etc. ```typescript -import { AleoKeyProvider, VerifyingKey, ProvingKey } from '@provablehq/sdk/mainnet.js'; +import { + AleoKeyProvider, + VerifyingKey, + ProvingKey, +} from "@provablehq/sdk/mainnet.js"; // The Aleo key provider is the default implementation of the key provider. const keyProvider = new AleoKeyProvider(); -// This flag enables the cache for the key provider. If the cache is enabled, the key provider will store the keys in +// This flag enables the cache for the key provider. If the cache is enabled, the key provider will store the keys in // memory after being fetched for the first time. keyProvider.useCache(true); -// The key provider allows specification of HTTP uris where proving keys and verifying keys can be found and a cache +// The key provider allows specification of HTTP uris where proving keys and verifying keys can be found and a cache // key for storing them. -const keySearchParams = { "proverUri":"http://mykeylocation.com/prover", "verifierUri":"http://mykeylocation.com/verifier", "cacheKey": "myProgram:myFunction" }; -const [provingKey, verifyingKey] = await keyProvider.functionKeys(keySearchParams); +const keySearchParams = { + proverUri: "http://mykeylocation.com/prover", + verifierUri: "http://mykeylocation.com/verifier", + cacheKey: "myProgram:myFunction", +}; +const [provingKey, verifyingKey] = + await keyProvider.functionKeys(keySearchParams); // If the keys are not located in remote locations (perhaps they are stored locally), the cache key can be used alone // to store the keys in memory. @@ -190,31 +227,41 @@ const [provingKey, verifyingKey] = await keyProvider.functionKeys(keySearchParam // Create a local proving key and verifying key. const localProvingKey = ProvingKey.fromString(".."); const localVerifyingKey = VerifyingKey.fromString(".."); -keyProvider.cacheKeys("myProgram.aleo:myFunction", [localProvingKey, localVerifyingKey]); +keyProvider.cacheKeys("myProgram.aleo:myFunction", [ + localProvingKey, + localVerifyingKey, +]); // Retrieve the keys from the in-memory cache. -const [storedProvingKey, storedVerifyingKey] = await keyProvider.functionKeys({ "cacheKey": "myProgram:myFunction" }); +const [storedProvingKey, storedVerifyingKey] = await keyProvider.functionKeys({ + cacheKey: "myProgram:myFunction", +}); ``` ## 3. Local program execution -It is also possible to simply execute a program locally without sending a transaction to the Aleo network. This can be -useful if a developer wants to use the SDK to use Aleo's zk-SNARKs outside of the Blockchain network or run a test -execution of a program while developing. For this purpose the `ProgramManager` class has a method called `run` that can -be used to execute a program locally. +It is also possible to simply execute a program locally without sending a +transaction to the Aleo network. This can be useful if a developer wants to use +the SDK to use Aleo's zk-SNARKs outside of the Blockchain network or run a test +execution of a program while developing. For this purpose the `ProgramManager` +class has a method called `run` that can be used to execute a program locally. ### Development - Running Locally without a Proof -When the developer sees fit to simply see the output of a function without generating a proof, the `run` method of -`ProgramManager` can be used. It simply needs the program, the function name, and the inputs to the function. -When run in this fashion, the program will execute and return the outputs of the function without generating a proof. -This can be useful for testing a function in development. +When the developer sees fit to simply see the output of a function without +generating a proof, the `run` method of `ProgramManager` can be used. It simply +needs the program, the function name, and the inputs to the function. + +When run in this fashion, the program will execute and return the outputs of the +function without generating a proof. This can be useful for testing a function +in development. ```typescript -import { Account, ProgramManager } from '@provablehq/sdk/mainnet.js'; +import { Account, ProgramManager } from "@provablehq/sdk/mainnet.js"; /// Create the source for the "hello world" program -const program = "program helloworld.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const program = + "program helloworld.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; const programManager = new ProgramManager(); /// Create a temporary account for the execution of the program @@ -222,18 +269,25 @@ const account = new Account(); programManager.setAccount(account); /// Get the response and ensure that the program executed correctly -const executionResponse = await programManager.run(program, "hello", ["5u32", "5u32"]); +const executionResponse = await programManager.run(program, "hello", [ + "5u32", + "5u32", +]); const result = executionResponse.getOutputs(); -assert.deepStrictEqual(result, ['10u32']); +assert.deepStrictEqual(result, ["10u32"]); ``` ### Offchain Proving - Running Locally WITH a Proof -If the developer wants to generate a proof for a program execution without sending it to the Aleo network, the `run` -method can be used with the `generateProof` flag set to true. This will generate an `ExecutionResponse` object that -includes the proof of the execution which can be verified offchain by the `verifyFunctionExecution` method by anyone who -has the function's proving and verifying keys. -Note: This approach is **will not work** for any function that has an async future defined within it. +If the developer wants to generate a proof for a program execution without +sending it to the Aleo network, the `run` method can be used with the +`generateProof` flag set to true. This will generate an `ExecutionResponse` +object that includes the proof of the execution which can be verified offchain +by the `verifyFunctionExecution` method by anyone who has the function's proving +and verifying keys. + +Note: This approach is **will not work** for any function that has an async +future defined within it. ```typescript import { Account, AleoKeyProvider, ProgramManager, ProvingKey, VerifyingKey } from '@provablehq/sdk/mainnet.js'; @@ -266,4 +320,4 @@ const executionResponse = await programManager.run(program, "hello", ["5u32", "5 /// Verify the proof of the execution const proofIsValid = await programManager.verifyFunctionExecution(executionResponse.getExecution(), "helloworld.aleo:main", program, "main"); -``` \ No newline at end of file +``` diff --git a/docs/guide/07_deploying_programs.md b/docs/guide/07_deploying_programs.md index c455f391c..2087fcfb6 100644 --- a/docs/guide/07_deploying_programs.md +++ b/docs/guide/07_deploying_programs.md @@ -4,45 +4,65 @@ title: Deploying Programs sidebar_label: Deploying Programs --- -Developers of Apps on the Aleo Chain will often need to deploy their own program to implement the logic of their Dapp. -This section provides an overview of how to deploy a program to the Aleo Network and the languages that can be used to +Developers of Apps on the Aleo Chain will often need to deploy their own program +to implement the logic of their Dapp. This section provides an overview of how +to deploy a program to the Aleo Network and the languages that can be used to develop programs. ## Developing an Aleo Program + Programs on Aleo are written in one of two languages: ### Leo Language -Leo is a high-level, developer-friendly language for developing zero-knowledge programs. The -[Leo Playground](https://play.leo-lang.org/) provides a web IDE that allows developers to build, test and deploy -new programs for. Documentation and tutorials on the Leo Language can be found at [docs.leo-lang.org](https://docs.leo-lang.org/). + +Leo is a high-level, developer-friendly language for developing zero-knowledge +programs. The [Leo Playground](https://play.leo-lang.org/) provides a web IDE +that allows developers to build, test and deploy new programs for. Documentation +and tutorials on the Leo Language can be found at +[docs.leo-lang.org](https://docs.leo-lang.org/). ### Aleo Instructions -Aleo instructions is a lower level language that provides developers with fine-grained control over the execution -flow of zero-knowledge programs. It is written to be syntactically similar to the R1CS constraint systems that Aleo + +Aleo instructions is a lower level language that provides developers with +fine-grained control over the execution flow of zero-knowledge programs. It is +written to be syntactically similar to the R1CS constraint systems that Aleo programs compile into. A full guide to this language can be found at [docs.leo-lang.org/aleo/language](https://docs.leo-lang.org/aleo/language). ## Deploying a Program ### How to Create a Program Deployment -Programs are deployed by building a `Deployment Transaction`. This is done by calling the SDK `deploy` or -`buildDeploymentTransaction` method. Under the hood these methods execute and prove each function in the Aleo program to -derive verifying keys. These keys are stored in a `Deployment Transaction`and sent to the Aleo Network. -If the program name is available and the fee is sufficient, the program will be stored on the Aleo Network. Once a -is deployed, its functions can be executed via `Execution Transactions` by any party. +Programs are deployed by building a `Deployment Transaction`. This is done by +calling the SDK `deploy` or `buildDeploymentTransaction` method. Under the hood +these methods execute and prove each function in the Aleo program to derive +verifying keys. These keys are stored in a `Deployment Transaction`and sent to +the Aleo Network. + +If the program name is available and the fee is sufficient, the program will be +stored on the Aleo Network. Once a is deployed, its functions can be executed +via `Execution Transactions` by any party. -Programs can be deployed to either the Aleo Testnet or Mainnet. It is highly recommended that developers test their -programs on the Testnet before deploying them to Aleo Mainnet. +Programs can be deployed to either the Aleo Testnet or Mainnet. It is highly +recommended that developers test their programs on the Testnet before deploying +them to Aleo Mainnet. ### Deploying a Program with the Provable SDK -When ready to deploy a program, the `Aleo Instructions` source code must be imported into the JS/TS environment as a -`string`. If the program is written in `Leo` it must first be compiled to `Aleo Instructions`. Once the source code is -available with JS/TS, it can be deployed using the ProgramManager. The following code snippet demonstrates how to deploy -a program using the Provable SDK: +When ready to deploy a program, the `Aleo Instructions` source code must be +imported into the JS/TS environment as a `string`. If the program is written in +`Leo` it must first be compiled to `Aleo Instructions`. Once the source code is +available with JS/TS, it can be deployed using the ProgramManager. The following +code snippet demonstrates how to deploy a program using the Provable SDK: + ```typescript -import { Account, AleoNetworkClient, NetworkRecordProvider, ProgramManager, AleoKeyProvider} from '@provablehq/sdk/testnet.js'; +import { + Account, + AleoNetworkClient, + NetworkRecordProvider, + ProgramManager, + AleoKeyProvider, +} from "@provablehq/sdk/testnet.js"; // Create a key provider that will be used to find public proving & verifying keys for Aleo programs const keyProvider = new AleoKeyProvider(); @@ -57,17 +77,25 @@ const account = new Account({ }); // Initialize a program manager to talk to the Aleo network with the configured key and record providers -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider); -programManager.setAccount(account) +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, +); +programManager.setAccount(account); // Define an Aleo program to deploy -const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const program = + "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; // Set the priority fee to pay to deploy the program const priorityFee = 0.0; // Build a deployment transaction for the program. -const tx = await programManager.buildDeploymentTransaction(program, priorityFee, false); +const tx = await programManager.buildDeploymentTransaction( + program, + priorityFee, + false, +); // Send the transaction to the network. const tx_id = await programManager.networkClient.submitTransaction(tx); @@ -75,27 +103,34 @@ const tx_id = await programManager.networkClient.submitTransaction(tx); // Verify the transaction was successful const transaction = await programManager.networkClient.getTransaction(tx_id); ``` -Once a program has been deployed, developers can check to see its deployment status and monitor its activity using the + +Once a program has been deployed, developers can check to see its deployment +status and monitor its activity using the [Provable Explorer](https://explorer.provable.com/programs). ### Deployment Fees -A fee must be paid to the Aleo Network for deployment. This fee can be paid publicly using a public balance or privately -using an `Credits` record. The fee for deploying any program can be calculated with the static `estimateDeploymentFee` -method of the `ProgramManager` class. +A fee must be paid to the Aleo Network for deployment. This fee can be paid +publicly using a public balance or privately using an `Credits` record. The fee +for deploying any program can be calculated with the static +`estimateDeploymentFee` method of the `ProgramManager` class. + ```typescript -const program = "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const program = + "program hello_hello.aleo;\n\nfunction hello:\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; const fee = await ProgramManager.estimateDeploymentFee(program); ``` -Deployment fees are calculated based on the following formulas. The cost of deploying a program is proportional to the -amount of opcodes used in a program and the complexity of the operations it performs. More computationally expensive -opcode usage such as hash functions will cost more than simple opcodes such as arithmetic or boolean opcodes. - -| Cost Component | Cost (Microcredits) | -|--------------------|--------------------------| -| **Synthesis Cost** | 25*#Constraints | -| **Storage Cost** | 1000*#Bytes | -| **Namespace Cost** | 10^(10 - num_characters) | +Deployment fees are calculated based on the following formulas. The cost of +deploying a program is proportional to the amount of opcodes used in a program +and the complexity of the operations it performs. More computationally expensive +opcode usage such as hash functions will cost more than simple opcodes such as +arithmetic or boolean opcodes. + +| Cost Component | Cost (Microcredits) | +| ------------------ | ------------------------------- | +| **Synthesis Cost** | 25\*#Constraints | +| **Storage Cost** | 1000\*#Bytes | +| **Namespace Cost** | 10^(10 - num_characters) | | **Total Cost** | Synthesis + Storage + Namespace | diff --git a/docs/guide/08_public_program_state.md b/docs/guide/08_public_program_state.md index d7059f730..bb0f2a12a 100644 --- a/docs/guide/08_public_program_state.md +++ b/docs/guide/08_public_program_state.md @@ -4,24 +4,26 @@ title: Mappings - Persistent Public Program State sidebar_label: Public Program State --- -Mappings are simple key-value stores defined in a program. Mappings within programs are identified by the `mapping` -identifier. Any program where this keyword appears contains an on-chain mapping. Each mapping has a key and value, each -with an Aleo type specified by the author of the program. +Mappings are simple key-value stores defined in a program. Mappings within +programs are identified by the `mapping` identifier. Any program where this +keyword appears contains an on-chain mapping. Each mapping has a key and value, +each with an Aleo type specified by the author of the program. ``` -// Account mapping in credits.aleo. This mapping +// Account mapping in credits.aleo. This mapping // stores all public Aleo credits balances on-chain mapping account: key owner as address.public; value microcredits as u64.public; ``` -When program `Deployment Transaction` is accepted into a -block, these key-value stores are initialized within RocksDB stores in the Aleo Ledger. After deployment program -mappings can be updated by executing Aleo functions that contain a `finalize` block. +When program `Deployment Transaction` is accepted into a block, these key-value +stores are initialized within RocksDB stores in the Aleo Ledger. After +deployment program mappings can be updated by executing Aleo functions that +contain a `finalize` block. ```mermaid -graph +graph subgraph Ledger subgraph BlockStore subgraph BlockN @@ -45,7 +47,7 @@ graph end end DeployTx-.Create Program + Datastores.->zParty.aleo - ExecuteTx-.Modify + ExecuteTx-.Modify Public Data.->zPartyStore ExecuteTx2-.Modify Public Data.->zPartyStore @@ -62,8 +64,10 @@ graph ``` ## Initializing & updating mappings -Updating mappings is done by executing a program function on the Aleo network which has a finalize block that updates the -program's mapping. For instance, the `transfer_public` function in the `credits.aleo` program updates the `account` + +Updating mappings is done by executing a program function on the Aleo network +which has a finalize block that updates the program's mapping. For instance, the +`transfer_public` function in the `credits.aleo` program updates the `account` mapping (and thus a user's balance) when called. ``` @@ -91,62 +95,105 @@ finalize transfer_public: set r6 into account[r1]; ``` -The `finalize` identifier is used to identify a portion of a function's code that should be executed by nodes on the -Aleo network. Program mappings are updated exclusively by code run by nodes on the Aleo network written in `finalize` -blocks. +The `finalize` identifier is used to identify a portion of a function's code +that should be executed by nodes on the Aleo network. Program mappings are +updated exclusively by code run by nodes on the Aleo network written in +`finalize` blocks. ### Updating Mappings with the SDK -Updating mappings requires executing an Aleo function that has a `finalize` block which updates the mapping. If the -inputs to the function are valid and the correct fee is paid, the network will execute the function and update the mapping. -If function inputs are invalid or an invalid fee is paid, the network will return an error, but the fee paid for the -transaction will still be consumed. Therefore, it is important to ensure the fee and inputs are correct before executing. +Updating mappings requires executing an Aleo function that has a `finalize` +block which updates the mapping. If the inputs to the function are valid and the +correct fee is paid, the network will execute the function and update the +mapping. If function inputs are invalid or an invalid fee is paid, the network +will return an error, but the fee paid for the transaction will still be +consumed. Therefore, it is important to ensure the fee and inputs are correct +before executing. -A simple example of a mapping update can be shown by simply executing `transfer_public` as shown below. +A simple example of a mapping update can be shown by simply executing +`transfer_public` as shown below. ```typescript -import { Account, ProgramManager, AleoKeyProvider, NetworkRecordProvider, AleoNetworkClient } from '@provablehq/sdk'; +import { + Account, + ProgramManager, + AleoKeyProvider, + NetworkRecordProvider, + AleoNetworkClient, +} from "@provablehq/sdk"; // Create a new NetworkClient, KeyProvider, and RecordProvider -const account = Account.from_string({privateKey: process.env.PRIVATE_KEY}); +const account = Account.from_string({ privateKey: process.env.PRIVATE_KEY }); const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const keyProvider = new AleoKeyProvider(); const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions const RECIPIENT_ADDRESS = "aleo1address..."; -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); programManager.setAccount(account); // Update or initialize a public balance -const tx_id = await programManager.transfer(1, RECIPIENT_ADDRESS, "transfer_private_to_public", 0.2); +const tx_id = await programManager.transfer( + 1, + RECIPIENT_ADDRESS, + "transfer_private_to_public", + 0.2, +); ``` ## Reading mappings -Any state within a program mapping is public and can be read by any participant in the Aleo network. + +Any state within a program mapping is public and can be read by any participant +in the Aleo network. ### Discovering available mappings -The `NetworkClient` class provides the `getProgramMappingNames` method to read the public mappings available within a program. + +The `NetworkClient` class provides the `getProgramMappingNames` method to read +the public mappings available within a program. ```typescript -import { AleoNetworkClient } from '@provablehq/sdk'; +import { AleoNetworkClient } from "@provablehq/sdk"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const creditsMappings = networkClient.getProgramMappingNames("credits.aleo"); -assert(creditsMappings === ["committee", "delegated", "metadata", "bonded", "unbonding", "account", "withdraw"]); - -const publicCredits = networkClient.getMapping("credits.aleo", "[a valid aleo account with zero balance]"); +assert( + creditsMappings === + [ + "committee", + "delegated", + "metadata", + "bonded", + "unbonding", + "account", + "withdraw", + ], +); + +const publicCredits = networkClient.getMapping( + "credits.aleo", + "[a valid aleo account with zero balance]", +); assert(publicCredits === "0u64"); ``` ### Reading a mapping -The `getProgramMappingValue` method of the `NetworkClient` can be used to read the value of a key in a mapping. The method -returns the value associated with the specified key within the mapping or an `Error` if the key does not exist. + +The `getProgramMappingValue` method of the `NetworkClient` can be used to read +the value of a key in a mapping. The method returns the value associated with +the specified key within the mapping or an `Error` if the key does not exist. ```typescript -import { AleoNetworkClient } from '@provablehq/sdk'; +import { AleoNetworkClient } from "@provablehq/sdk"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); -const publicCredits = networkClient.getProgramMappingValue("credits.aleo", "aleo1address..."); +const publicCredits = networkClient.getProgramMappingValue( + "credits.aleo", + "aleo1address...", +); assert(publicCredits === "437059431396u64"); -``` \ No newline at end of file +``` diff --git a/docs/guide/09_private_program_state.md b/docs/guide/09_private_program_state.md index d8438a19d..1e3a79628 100644 --- a/docs/guide/09_private_program_state.md +++ b/docs/guide/09_private_program_state.md @@ -5,19 +5,25 @@ sidebar_label: Records and Private Program State --- ## Records -Records are analogous to the Bitcoin concept of [UTXOs](https://en.wikipedia.org/wiki/Unspent_transaction_output). When a record is -created by a program, it can then be consumed later by the same program as an input to a function. Once a record is used -as an input, it is considered consumed and cannot be used again. In many cases a new record will be created from the output -of the function. Records are private by default and are associated with a single Aleo program and a single private key -representing a user. + +Records are analogous to the Bitcoin concept of +[UTXOs](https://en.wikipedia.org/wiki/Unspent_transaction_output). When a record +is created by a program, it can then be consumed later by the same program as an +input to a function. Once a record is used as an input, it is considered +consumed and cannot be used again. In many cases a new record will be created +from the output of the function. Records are private by default and are +associated with a single Aleo program and a single private key representing a +user. ### Record usage example: private value transfers -A straightforward example of a usage of records in a program can be demonstrated by explaining the process of private -value transfers of Aleo credits on the Aleo network. +A straightforward example of a usage of records in a program can be demonstrated +by explaining the process of private value transfers of Aleo credits on the Aleo +network. -Aleo credits are used for all on-chain execution and deployment fees. Credits can be public or private. Private credits -are represented by the `credits` record in the [credits.aleo](https://explorer.provable.com/program/credits.aleo) +Aleo credits are used for all on-chain execution and deployment fees. Credits +can be public or private. Private credits are represented by the `credits` +record in the [credits.aleo](https://explorer.provable.com/program/credits.aleo) program. ``` @@ -26,14 +32,18 @@ record credits: microcredits as u64.private; ``` -Credits records contain an `owner` field representing the address which owns the record and a `microcredits` field -representing the amount of microcredits in the record. 1 credit is equal to 1,000,000 microcredits. +Credits records contain an `owner` field representing the address which owns the +record and a `microcredits` field representing the amount of microcredits in the +record. 1 credit is equal to 1,000,000 microcredits. -An example of an Aleo function that both takes a record as input and outputs a record is the `transfer_private` function -of the `credits.aleo` program. This function takes a private `credits` record as input and outputs two new private `credits` -records as output (one that sends the credits to the recipient and one that sends the remaining credits to the sender). +An example of an Aleo function that both takes a record as input and outputs a +record is the `transfer_private` function of the `credits.aleo` program. This +function takes a private `credits` record as input and outputs two new private +`credits` records as output (one that sends the credits to the recipient and one +that sends the remaining credits to the sender). The source code for the `transfer_private` is: + ``` function transfer_private: input r0 as credits.record; @@ -46,10 +56,12 @@ function transfer_private: output r5 as credits.record; ``` -The `transfer_private` function can be graphically represented by the graph below. In the graph the first record, Record 1, -is consumed and can never be used again. From the data in Record 1, two more records are created. One containing -the intended amount for the recipient which is now owned by the recipient and another containing the remaining credits -which are sent back to the sender. +The `transfer_private` function can be graphically represented by the graph +below. In the graph the first record, Record 1, is consumed and can never be +used again. From the data in Record 1, two more records are created. One +containing the intended amount for the recipient which is now owned by the +recipient and another containing the remaining credits which are sent back to +the sender. ```mermaid graph LR @@ -61,11 +73,13 @@ graph LR p1--Credits Record 3-->R1[Recipient Address] ``` -This chain of ownership is tracked by the Aleo blockchain by storing the encrypted input and output records within -the ledger. This allows other users who receive records to receive scan the chain for new record outputs from functions. +This chain of ownership is tracked by the Aleo blockchain by storing the +encrypted input and output records within the ledger. This allows other users +who receive records to receive scan the chain for new record outputs from +functions. -This allows a chain of private state to be created between users. For private Aleo Credit transfers, a chain of state -might look like the following: +This allows a chain of private state to be created between users. For private +Aleo Credit transfers, a chain of state might look like the following: ```mermaid graph LR @@ -78,40 +92,67 @@ graph LR ``` The above state chain would be executed in the following way using the SDK: + #### Step 1 - User 1 sends a private value transfer to User 2 + ```typescript // USER 1 -import { Account, ProgramManager, AleoKeyProvider, NetworkRecordProvider, AleoNetworkClient } from '@provablehq/sdk'; +import { + Account, + ProgramManager, + AleoKeyProvider, + NetworkRecordProvider, + AleoNetworkClient, +} from "@provablehq/sdk"; // Create a new NetworkClient, KeyProvider, and RecordProvider -const account = Account.from_string({privateKey: "user1PrivateKey"}); +const account = Account.from_string({ privateKey: "user1PrivateKey" }); const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const keyProvider = new AleoKeyProvider(); const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions const USER_2_ADDRESS = "user2Address"; -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); programManager.setAccount(account); /// Send private transfer to User 2 -const tx_id = await programManager.transfer(1, USER_2_ADDRESS, "transfer_private", 0.2); +const tx_id = await programManager.transfer( + 1, + USER_2_ADDRESS, + "transfer_private", + 0.2, +); ``` #### Step 2 - User 2 receives the transaction ID and fetches the credits record they received from User 1 from the network. They then send it to User 3 ```typescript // USER 2 -import { Account, ProgramManager, AleoKeyProvider, NetworkRecordProvider, AleoNetworkClient } from '@provablehq/sdk'; +import { + Account, + ProgramManager, + AleoKeyProvider, + NetworkRecordProvider, + AleoNetworkClient, +} from "@provablehq/sdk"; // Create a new NetworkClient, KeyProvider, and RecordProvider -const account = Account.from_string({privateKey: "user2PrivateKey"}); +const account = Account.from_string({ privateKey: "user2PrivateKey" }); const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const keyProvider = new AleoKeyProvider(); const recordProvider_User2 = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); programManager.setAccount(account); // Fetch the transaction from the network that user 1 sent @@ -120,18 +161,28 @@ const record = transaction.execution.transitions[0].outputs[0].value; // Decrypt the record with the user's view key const recordCiphertext = RecordCiphertext.fromString(record); -const recordPlaintext = recordCiphertext.decrypt(account.viewKey()); +const recordPlaintext = ( + recordCiphertext.decrypt(account.viewKey()) +); // Send a transfer to user 3 using the record found above const USER_3_ADDRESS = "user3Address"; -const tx_id = await programManager.transfer(1, USER_3_ADDRESS, "transfer_private", 0.2, undefined, recordPlaintext); +const tx_id = await programManager.transfer( + 1, + USER_3_ADDRESS, + "transfer_private", + 0.2, + undefined, + recordPlaintext, +); ``` -When an execution such as `transfer_private` consumes or generates a record, the record inputs and outputs are visible -in encrypted form within the transitions of the execution transaction `transfer_private` occured within. +When an execution such as `transfer_private` consumes or generates a record, the +record inputs and outputs are visible in encrypted form within the transitions +of the execution transaction `transfer_private` occured within. -Because the record inputs and ouputs are encrypted, no details are revealed about the sender or receiver of the -transaction. +Because the record inputs and ouputs are encrypted, no details are revealed +about the sender or receiver of the transaction.
`transfer_private` Execution Transcript @@ -187,40 +238,46 @@ transaction. } ], ``` +
## Record Decryption -If a user receives a private record from a program execution, they can use the SDK to decrypt encrypted records with -their view keys and view their contents. Only records that are owned by the user can be decrypted. Decryption of records +If a user receives a private record from a program execution, they can use the +SDK to decrypt encrypted records with their view keys and view their contents. +Only records that are owned by the user can be decrypted. Decryption of records that are not owned by the user will fail. -Record decryption and ownership verification can be done in the SDK using the following code: +Record decryption and ownership verification can be done in the SDK using the +following code: ```typescript -import { Account, RecordCiphertext, RecordPlaintext } from '@provablehq/sdk'; +import { Account, RecordCiphertext, RecordPlaintext } from "@provablehq/sdk"; // Create an account from an existing private key -const account = Account.from_string({privateKey: "existingPrivateKey"}); +const account = Account.from_string({ privateKey: "existingPrivateKey" }); // Record value received as a string from program output or found on the Aleo network -const record = "record1qyqsq4r7mcd3ystjvjqda0v2a6dxnyzg9mk2daqjh0wwh359h396k7c9qyxx66trwfhkxun9v35hguerqqpqzqzshsw8dphxlzn5frh8pknsm5zlvhhee79xnhfesu68nkw75dt2qgrye03xqm4zf5xg5n6nscmmzh7ztgptlrzxq95syrzeaqaqu3vpzqf03s6"; +const record = + "record1qyqsq4r7mcd3ystjvjqda0v2a6dxnyzg9mk2daqjh0wwh359h396k7c9qyxx66trwfhkxun9v35hguerqqpqzqzshsw8dphxlzn5frh8pknsm5zlvhhee79xnhfesu68nkw75dt2qgrye03xqm4zf5xg5n6nscmmzh7ztgptlrzxq95syrzeaqaqu3vpzqf03s6"; const recordCiphertext = RecordCiphertext.fromString(record); // Check ownership of the record. If the account is the owner, decrypt the record if (RecordCiphertext.is_owner(account.viewKey())) { - // Decrypt the record with the account's view key - const recordPlaintext = recordCiphertext.decrypt(account.viewKey()); + // Decrypt the record with the account's view key + const recordPlaintext = recordCiphertext.decrypt(account.viewKey()); - // View the record data - console.log(recordPlaintext.toString()); + // View the record data + console.log(recordPlaintext.toString()); } ``` -A record can be decrypted by a non-owner using a record view key for use cases requiring inspection of the data contained within a -record by a third party. This approach enables a user to maintain privacy over the rest of their records and ciphertext data, -ensuring that only the desired record can be decrypted a third party. +A record can be decrypted by a non-owner using a record view key for use cases +requiring inspection of the data contained within a record by a third party. +This approach enables a user to maintain privacy over the rest of their records +and ciphertext data, ensuring that only the desired record can be decrypted a +third party. ```typescript import {Account, EncryptionToolkit, Field, RecordCiphertext, RecordPlaintext} from '@provablehq/sdk'; @@ -243,4 +300,5 @@ const recordViewKeyAlt = EncryptionToolkit::gnerateRecordViewKey(accoutn.viewKey const recordPlaintext = recordciphertext.decryptWithRecordViewKey(recordViewKey); // Alternatively, the record can be decrypted using a method from the EncryptionToolkit object -const recordPlaintextAlt = EncryptionToolkit::decryptRecordWithRVk(recordViewKeyAlt, reocrdCiphertext); \ No newline at end of file +const recordPlaintextAlt = EncryptionToolkit::decryptRecordWithRVk(recordViewKeyAlt, reocrdCiphertext); +``` diff --git a/docs/guide/10_finding_records.md b/docs/guide/10_finding_records.md index 8ab9c8387..26b366d48 100644 --- a/docs/guide/10_finding_records.md +++ b/docs/guide/10_finding_records.md @@ -4,12 +4,16 @@ title: Finding Records sidebar_label: Finding Records --- -Finding records is similar to finding UTXOs in Bitcoin. Records are stored as outputs of transitions contained within -execution transactions. To find records, implementors of web apps must: -* Scan the Aleo network for transactions that include transitions that contain records. -* Check any found records to see if the desired user is the owner of the record. -* Check to see if the record is "spent" or "unspent" by checking if the record has appeared in any function inputs. -* Optionally decrypt the record if the data within it is desired. +Finding records is similar to finding UTXOs in Bitcoin. Records are stored as +outputs of transitions contained within execution transactions. To find records, +implementors of web apps must: + +- Scan the Aleo network for transactions that include transitions that contain + records. +- Check any found records to see if the desired user is the owner of the record. +- Check to see if the record is "spent" or "unspent" by checking if the record + has appeared in any function inputs. +- Optionally decrypt the record if the data within it is desired. ```mermaid graph TD @@ -33,7 +37,7 @@ graph TD end subgraph Scanner subgraph Checks - CheckOwner[["Check Ownership + CheckOwner[["Check Ownership (View Key)"]] CheckSpent[["Check Unspent"]] end @@ -41,7 +45,7 @@ graph TD CheckOwner -.->Owned end BlockN-->BlockN+1-->BlockN+2 - Scanner <-."Scan Records + Scanner <-."Scan Records (Records Found!)" .-> Transition1 Scanner -.Scan For Records.-> Transition2 @@ -61,26 +65,38 @@ graph TD ## Finding Records Using the SDK -Although the process described above seems complicated, much of the complexity is encapsulated within the SDK. +Although the process described above seems complicated, much of the complexity +is encapsulated within the SDK. ### Finding Records with The `AleoNetworkClient` Convenience Methods -The `AleoNetworkClient` provides the `findRecords` methods fo finding records. This method allows records to be -searched for between specified block heights. + +The `AleoNetworkClient` provides the `findRecords` methods fo finding records. +This method allows records to be searched for between specified block heights. It also optionally allows users to specify: -* The option to search exclusively for unspent records. -* One or more programs to find records for. -* A list of nonces (i.e. the unique ID of a record) to exclude from the search. -If `credits.aleo`records are being searched for, users can also optionally specify: -* A list of amounts to find. -* A maximum cumulative amount to find between all records. +- The option to search exclusively for unspent records. +- One or more programs to find records for. +- A list of nonces (i.e. the unique ID of a record) to exclude from the search. + +If `credits.aleo`records are being searched for, users can also optionally +specify: + +- A list of amounts to find. +- A maximum cumulative amount to find between all records. ```typescript -import { AleoNetworkClient } from '@provablehq/sdk'; +import { AleoNetworkClient } from "@provablehq/sdk"; -const account = new Account.fromCiphertext(process.env.cipherText, process.env.secret); -const networkClient = new AleoNetworkClient("https://api.provable.com/v2", undefined, account); +const account = new Account.fromCiphertext( + process.env.cipherText, + process.env.secret, +); +const networkClient = new AleoNetworkClient( + "https://api.provable.com/v2", + undefined, + account, +); // Find all records from an account within a block range. const allRecords = networkClient.findRecords( @@ -99,24 +115,35 @@ const unspentRrecords = networkClient.findRecords( ); ``` -This method provides a linear search through the block range specified. It is most useful for finding records -in smaller block ranges where the app invoking the method can expect to find desired records. For larger ranges of -blocks this method may be infeasible to use. +This method provides a linear search through the block range specified. It is +most useful for finding records in smaller block ranges where the app invoking +the method can expect to find desired records. For larger ranges of blocks this +method may be infeasible to use. ### Implementing the `RecordProvider` interface. -In order to conveniently find records during execution, the implementations of `RecordProvider` can be used. This -interface allows developers to implement an efficient search strategy for finding new records. A default implementation -of the `RecordProvider` interface is provided by the `NetworkRecordProvider` class, but developers can use the -`RecordProvider` interface to implement their own search strategies. -When a `RecordProvider` is provided within the constructor of a `ProgramManager` object and `RecordSearchParameters` are -provided to a function that executes a function, and a private fee is specified, the `ProgramManager` will automatically -search for an appropriate record to pay the fee. +In order to conveniently find records during execution, the implementations of +`RecordProvider` can be used. This interface allows developers to implement an +efficient search strategy for finding new records. A default implementation of +the `RecordProvider` interface is provided by the `NetworkRecordProvider` class, +but developers can use the `RecordProvider` interface to implement their own +search strategies. + +When a `RecordProvider` is provided within the constructor of a `ProgramManager` +object and `RecordSearchParameters` are provided to a function that executes a +function, and a private fee is specified, the `ProgramManager` will +automatically search for an appropriate record to pay the fee. + +A usage example of the `RecordProvider` is shown below using the +`NetworkRecordProvider` implementation of the `RecordProvider` interface. -A usage example of the `RecordProvider` is shown below using the `NetworkRecordProvider` implementation of the -`RecordProvider` interface. ```typescript -import { AleoNetworkClient, AleoKeyProvider, NetworkRecordProvider, ProgramManager } from '@provablehq/sdk'; +import { + AleoNetworkClient, + AleoKeyProvider, + NetworkRecordProvider, + ProgramManager, +} from "@provablehq/sdk"; // Create a new NetworkClient, KeyProvider, and RecordProvider using official Aleo record, key, and network providers const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); @@ -125,7 +152,11 @@ keyProvider.useCache(true); const recordProvider = new NetworkRecordProvider(account, networkClient); // Initialize a program manager with the key provider to automatically fetch keys for executions -const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); +const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + recordProvider, +); // Find a record to pay the fee for the transaction let inputRecordSearchParameters = { @@ -133,11 +164,11 @@ let inputRecordSearchParameters = { amounts: [10_000_000], // Find the amount desired to be transferred. startHeight: 4370000, // Specify the start of a block range where unspent records are likely to be found. endHeight: 4371000, // Specify the end of a block height range where unspent records are likely to be found. -} +}; const record = await programManager.recordProvider.findRecords( - true, // Find only unspent records. - undefined, // No nonces need to be excluded because only one record is being searched for. - inputRecordSearchParameters, + true, // Find only unspent records. + undefined, // No nonces need to be excluded because only one record is being searched for. + inputRecordSearchParameters, ); // Record the nonce of the found record so it's not selected again. @@ -148,32 +179,40 @@ const feeRecordSearchParameters = { startHeight: 4370000, // Specify the start height. endHeight: 4371000, // Specify the end height. nonces: [nonce], // Exclude the nonce of the record found for the transfer. -} +}; const transaction = await programManager.buildExecutionTransaction({ - programName: "credits.aleo", - functionName: "transfer_private", - fee: 0.040, - privateFee: true, - inputs: ["aleoAddress1..", "10000000u64"], - recordSearchParams: feeRecordSearchParameters, // Specify the record search parameters for the fee record. + programName: "credits.aleo", + functionName: "transfer_private", + fee: 0.04, + privateFee: true, + inputs: ["aleoAddress1..", "10000000u64"], + recordSearchParams: feeRecordSearchParameters, // Specify the record search parameters for the fee record. }); -const result = await programManager.networkClient.submitTransaction(transaction); +const result = + await programManager.networkClient.submitTransaction(transaction); ``` ## Optimizing web search -Using naive approaches such as scanning the entire Blockchain history can be a time-consuming process and degrade the -experience of a web app. Fortunately, strategies can be used to optimize the process. + +Using naive approaches such as scanning the entire Blockchain history can be a +time-consuming process and degrade the experience of a web app. Fortunately, +strategies can be used to optimize the process. #### Searching for Records After the User Account Creation -If the user a web app has created an Aleo account after a known block, the search can be optimized to search for records -by only scanning the records from the block height after which the account was created. + +If the user a web app has created an Aleo account after a known block, the +search can be optimized to search for records by only scanning the records from +the block height after which the account was created. #### Searching for A Specific Program's Records -If the records you are searching for are from a specific program, you can optimize the search by only scanning the -records for a specific program. + +If the records you are searching for are from a specific program, you can +optimize the search by only scanning the records for a specific program. #### Storing Records Created by Your Web App -If your web app has created a transaction, you have access to the records produced by that transaction and can store -them in a database for easy retrieval later. \ No newline at end of file + +If your web app has created a transaction, you have access to the records +produced by that transaction and can store them in a database for easy retrieval +later. diff --git a/docs/guide/11_working_with_chain_state.md b/docs/guide/11_working_with_chain_state.md index 453547d27..185aaac38 100644 --- a/docs/guide/11_working_with_chain_state.md +++ b/docs/guide/11_working_with_chain_state.md @@ -4,21 +4,27 @@ title: Working with Chain State sidebar_label: Working with Chain State --- -The Provable SDK provides methods for querying and inspecting state on the Aleo Network. This guide will cover what -stateful objects exist on the Aleo blockchain and how to query them. +The Provable SDK provides methods for querying and inspecting state on the Aleo +Network. This guide will cover what stateful objects exist on the Aleo +blockchain and how to query them. # Blocks -Blocks are formed by Aleo Validators and represent the canonical unit of state change. They contain all `Execution` and -`Deployment` transactions, `Solutions` to the Aleo Puzzle, metadata about the block (such as height, current coinbase -target, etc.) and identifying cryptographic information such as the block's merkle roots and validator signatures. +Blocks are formed by Aleo Validators and represent the canonical unit of state +change. They contain all `Execution` and `Deployment` transactions, `Solutions` +to the Aleo Puzzle, metadata about the block (such as height, current coinbase +target, etc.) and identifying cryptographic information such as the block's +merkle roots and validator signatures. -Blocks can be queried by the `AleoNetworkClient` which will return the block in a human readable JSON format. The -returned `JSON` mirrors the Block's [typescript interface](https://github.com/ProvableHQ/sdk/blob/mainnet/sdk/src/models/blockJSON.ts). +Blocks can be queried by the `AleoNetworkClient` which will return the block in +a human readable JSON format. The returned `JSON` mirrors the Block's +[typescript interface](https://github.com/ProvableHQ/sdk/blob/mainnet/sdk/src/models/blockJSON.ts). + +The following `typescript` snippet shows how to extract most of the important +information from a block. -The following `typescript` snippet shows how to extract most of the important information from a block. ```typescript -import { AleoNetworkClient } from "@provablehq/sdk/testnet.js" +import { AleoNetworkClient } from "@provablehq/sdk/testnet.js"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); const block = await networkClient.getBlock(1); @@ -38,7 +44,7 @@ for (let i = 0; i < transactions.length; i++) { const transaction = transactions[i]; // Get the transaction's ID. const transactionID = transaction.id; - + // Get the transaction's type. const transactionType = transaction.type; if (transactionType === "execute") { @@ -55,63 +61,77 @@ for (let i = 0; i < transactions.length; i++) { } } else if (transactionType === "deploy") { // Get the transaction's deployment data. - const deploymentData = transaction.transaction.deployment; + const deploymentData = ( + transaction.transaction.deployment + ); // Get the program's name. const programName = deploymentData.program; // Get the program's verifying keys. const verifyingKeys = deploymentData.verifying_keys; } - + // Get the block and puzzle reward (this information is important to calculating staking and mining rewards). const ratificationsJSON = block.ratifications; const blockReward = ratificationsJSON[0].amount; const puzzleReward = ratificationsJSON[1].amount; - + // Get the block's puzzle solutions. - const solutions = block.solutions + const solutions = block.solutions; } ``` -While blocks contain most relevant information, they are large and may represent more data than is necessary for -your application. An application may only be interested in specific transactions and the `AleoNetworkClient` provides -methods to query transactions by their unique ID. +While blocks contain most relevant information, they are large and may represent +more data than is necessary for your application. An application may only be +interested in specific transactions and the `AleoNetworkClient` provides methods +to query transactions by their unique ID. # Transactions and Transitions ## Transaction Objects - `Transactions` that contain either `Deployments` of new programs or `Executions` existing programs that change chain - state. Each `Execution` transactions contain one or more `Transitions` that list all inputs and outputs of the executed -functions including any `Records` produced or `Futures` created by functions with `finalize` statements. -After a program function relevant to an app has been executed, it is often useful to query transaction objects to -visualize, store, or use the state changes produced in the transaction within the app. +`Transactions` that contain either `Deployments` of new programs or `Executions` +existing programs that change chain state. Each `Execution` transactions contain +one or more `Transitions` that list all inputs and outputs of the executed +functions including any `Records` produced or `Futures` created by functions +with `finalize` statements. + +After a program function relevant to an app has been executed, it is often +useful to query transaction objects to visualize, store, or use the state +changes produced in the transaction within the app. ## Querying and Inspecting a Transaction -Each transaction has a unique ID with the bech32 prefix `at`. When a transaction is executed and submitted by the -`deploy` or `execute` methods of the `ProgramManager` or submitted manually via the `submitTransaction` method of -the `AleoNetworkClient`, the transaction id is returned as a string. +Each transaction has a unique ID with the bech32 prefix `at`. When a transaction +is executed and submitted by the `deploy` or `execute` methods of the +`ProgramManager` or submitted manually via the `submitTransaction` method of the +`AleoNetworkClient`, the transaction id is returned as a string. -This transaction id can be used to query the transaction data from the Aleo Network. +This transaction id can be used to query the transaction data from the Aleo +Network. -The `AleoNetworkClient` provides a method for transaction information back in the format -of a `wasm` or `json` object. The `json` representation will provide the full structure of the `Transaction` in human -read-able format that can be parsed using typical `JSON` parsing tools. +The `AleoNetworkClient` provides a method for transaction information back in +the format of a `wasm` or `json` object. The `json` representation will provide +the full structure of the `Transaction` in human read-able format that can be +parsed using typical `JSON` parsing tools. -The `wasm` representation will provide the raw `SnarkVM` object, which has several convenience methods for extracting -the objects such as `inputs`, `outputs`, and `records` without the need for traditional JSON parsing. The choice of -representation is up to the developer's personal preference in ergonomics. +The `wasm` representation will provide the raw `SnarkVM` object, which has +several convenience methods for extracting the objects such as `inputs`, +`outputs`, and `records` without the need for traditional JSON parsing. The +choice of representation is up to the developer's personal preference in +ergonomics. Usage of both methods however are illustrated below. ```typescript -import { AleoNetworkClient, Transition } from '@provablehq/sdk/testnet.js'; +import { AleoNetworkClient, Transition } from "@provablehq/sdk/testnet.js"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); // Get a transaction by id and get its inputs and outputs from the JSON representation. let jsonRecords = []; -const transactionJSON = await networkClient.getTransaction('at1659war3z5t4wppr9h5rck3kpf5gmzf80xpud2hz8yuv3ds286u8s5lxh7c'); +const transactionJSON = await networkClient.getTransaction( + "at1659war3z5t4wppr9h5rck3kpf5gmzf80xpud2hz8yuv3ds286u8s5lxh7c", +); const transitions = transactionJSON["execution"]["transitions"]; for (let i = 0; i < transitions.length; i++) { const transition = transitions[i]; @@ -126,7 +146,9 @@ for (let i = 0; i < transitions.length; i++) { } // Get a transaction by id and get its inputs and outputs from the Wasm representation. -const transactionWasm = await networkClient.getTransactionObject(`at1659war3z5t4wppr9h5rck3kpf5gmzf80xpud2hz8yuv3ds286u8s5lxh7c`); +const transactionWasm = await networkClient.getTransactionObject( + `at1659war3z5t4wppr9h5rck3kpf5gmzf80xpud2hz8yuv3ds286u8s5lxh7c`, +); const transitionsWasm = transactionWasm.transitions(); for (let i = 0; i < transitionsWasm.length; i++) { const transition = transitionsWasm[i]; @@ -143,99 +165,130 @@ const transactionRecords = transactionWasm.records(); ``` # Programs -It is often key to use data both about a program's structure and it's internal data within apps. The `AleoNetworkClient` -provides several methods to query and inspect programs on the Aleo Network. + +It is often key to use data both about a program's structure and it's internal +data within apps. The `AleoNetworkClient` provides several methods to query and +inspect programs on the Aleo Network. ## Querying Public Program State. -The public state of a program exists within its `mappings`. These mappings often contain information such as public -balances, validator stake, token information and more. This information will often change from block to block, and at -any given block it can be queried using the `AleoNetworkClient`. + +The public state of a program exists within its `mappings`. These mappings often +contain information such as public balances, validator stake, token information +and more. This information will often change from block to block, and at any +given block it can be queried using the `AleoNetworkClient`. ### Querying Mappings -The list of mappings within a program can be queried using the `getProgramMappingNames` method of the -`AleoNetworkClient`. +The list of mappings within a program can be queried using the +`getProgramMappingNames` method of the `AleoNetworkClient`. + ```typescript -import { AleoNetworkClient } from '@provablehq/sdk/testnet.js'; +import { AleoNetworkClient } from "@provablehq/sdk/testnet.js"; import { networkInterfaces } from "node:os"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); // Get the list of program mappings in credits.aleo. const expectedMappings = [ - "committee", - "delegated", - "metadata", - "bonded", - "unbonding", - "account", - "withdraw" + "committee", + "delegated", + "metadata", + "bonded", + "unbonding", + "account", + "withdraw", ]; const creditsMappings = networkClient.getProgramMappingNames("credits.aleo"); assert.deepStrictEqual(creditsMappings, expectedMappings); ``` -To get the value from a mapping, one must know the type of the mapping's keys. When this is known, the `getMappingValue` -method can be used. +To get the value from a mapping, one must know the type of the mapping's keys. +When this is known, the `getMappingValue` method can be used. + ```typescript -import { AleoNetworkClient } from '@provablehq/sdk/testnet.js'; +import { AleoNetworkClient } from "@provablehq/sdk/testnet.js"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); // Get the value of the `committee` mapping in credits.aleo for an Aleo validator. -const committee = await networkClient.getProgramMappingValue("credits.aleo", "committee", "aleo1q3vx8pet0h7739hx5xlekfxh9kus6qdlxhx9qdkxhh9rnva8q5gsskve3t"); +const committee = await networkClient.getProgramMappingValue( + "credits.aleo", + "committee", + "aleo1q3vx8pet0h7739hx5xlekfxh9kus6qdlxhx9qdkxhh9rnva8q5gsskve3t", +); const expectedCommittee = "{\n is_open: true,\n commission: 10u8\n}"; assert.equal(committee, expectedCommittee); // Get the balance of an account in the `account` mapping in credits.aleo. -const account = await networkClient.getProgramMappingValue("credits.aleo", "account", "aleo1q3vx8pet0h7739hx5xlekfxh9kus6qdlxhx9qdkxhh9rnva8q5gsskve3t"); +const account = await networkClient.getProgramMappingValue( + "credits.aleo", + "account", + "aleo1q3vx8pet0h7739hx5xlekfxh9kus6qdlxhx9qdkxhh9rnva8q5gsskve3t", +); const expectedBalance = "66138284723u64"; assert.equal(account, expectedBalance); // Get a token value of the `registered_tokens` mapping in token_registry.aleo. -const tokenInfo = await networkClient.getProgramMappingValue("token_registry.aleo", "registered_tokens", "1381601714105276218895759962490543360839827276760458984912661726715051428034field"); -const expectedTokenInfo = "{\n token_id: 1381601714105276218895759962490543360839827276760458984912661726715051428034field,\n name: 1447384136u128,\n symbol: 1984255048u128,\n decimals: 18u8,\n supply: 171170000000000000u128,\n max_supply: 340282366920938463463374607431768211455u128,\n admin: aleo1yuxemtvtkdv9u0d4pewn8g6rjuzlcpv734ayqyh5pnrgsu6xaspsm6dgpd,\n external_authorization_required: false,\n external_authorization_party: aleo1ywd9h0gql58sqcxlvy6m5vjg2wm9h56umyaudazwmw5cjjv2sygq9yr6he\n}" +const tokenInfo = await networkClient.getProgramMappingValue( + "token_registry.aleo", + "registered_tokens", + "1381601714105276218895759962490543360839827276760458984912661726715051428034field", +); +const expectedTokenInfo = + "{\n token_id: 1381601714105276218895759962490543360839827276760458984912661726715051428034field,\n name: 1447384136u128,\n symbol: 1984255048u128,\n decimals: 18u8,\n supply: 171170000000000000u128,\n max_supply: 340282366920938463463374607431768211455u128,\n admin: aleo1yuxemtvtkdv9u0d4pewn8g6rjuzlcpv734ayqyh5pnrgsu6xaspsm6dgpd,\n external_authorization_required: false,\n external_authorization_party: aleo1ywd9h0gql58sqcxlvy6m5vjg2wm9h56umyaudazwmw5cjjv2sygq9yr6he\n}"; assert.equal(tokenInfo, expectedTokenInfo); ``` -Often the returned value from a mapping will be a `struct` or `array`. When returned as a string, this can be difficult -to parse. The `AleoNetworkClient` provides a method to return the value as a `wasm` object called a `Plaintext`, which -has several convenience methods for inspecting the returned value. The `toObject` method can be used to convert the -`wasm` object to a JavaScript object. +Often the returned value from a mapping will be a `struct` or `array`. When +returned as a string, this can be difficult to parse. The `AleoNetworkClient` +provides a method to return the value as a `wasm` object called a `Plaintext`, +which has several convenience methods for inspecting the returned value. The +`toObject` method can be used to convert the `wasm` object to a JavaScript +object. -This is very useful for extracting complicated struct or array data returned from a mapping. +This is very useful for extracting complicated struct or array data returned +from a mapping. ```typescript -import { AleoNetworkClient } from '@provablehq/sdk/testnet.js'; +import { AleoNetworkClient } from "@provablehq/sdk/testnet.js"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); // Get a token value of the `registered_tokens` mapping in token_registry.aleo. -const tokenStruct = await networkClient.getProgramMappingPlaintext("token_registry.aleo", "registered_tokens", "1381601714105276218895759962490543360839827276760458984912661726715051428034field"); +const tokenStruct = await networkClient.getProgramMappingPlaintext( + "token_registry.aleo", + "registered_tokens", + "1381601714105276218895759962490543360839827276760458984912661726715051428034field", +); const tokenObject = tokenInfoStruct.toObject(); const expectedTokenObject = { - token_id: "1381601714105276218895759962490543360839827276760458984912661726715051428034field", - name: BigInt(1447384136), - symbol: BigInt(1984255048), - decimals: BigInt(18), - supply: BigInt(171170000000000000), - max_supply: BigInt(340282366920938463463374607431768211455), - admin: "aleo1yuxemtvtkdv9u0d4pewn8g6rjuzlcpv734ayqyh5pnrgsu6xaspsm6dgpd", - external_authorization_required: false, - external_authorization_party: "aleo1ywd9h0gql58sqcxlvy6m5vjg2wm9h56umyaudazwmw5cjjv2sygq9yr6he" + token_id: + "1381601714105276218895759962490543360839827276760458984912661726715051428034field", + name: BigInt(1447384136), + symbol: BigInt(1984255048), + decimals: BigInt(18), + supply: BigInt(171170000000000000), + max_supply: BigInt(340282366920938463463374607431768211455), + admin: "aleo1yuxemtvtkdv9u0d4pewn8g6rjuzlcpv734ayqyh5pnrgsu6xaspsm6dgpd", + external_authorization_required: false, + external_authorization_party: + "aleo1ywd9h0gql58sqcxlvy6m5vjg2wm9h56umyaudazwmw5cjjv2sygq9yr6he", }; ``` ## Querying Program Structure -Apps often need to know information about a program's structure such as its source code, the functions it contains, -function inputs and their types, and the mappings, records, and other programs it imports. The guide below shows how to + +Apps often need to know information about a program's structure such as its +source code, the functions it contains, function inputs and their types, and the +mappings, records, and other programs it imports. The guide below shows how to query this information. ### Querying Program Source Code -To get the source code of a program, the `AleoNetworkClient` provides a method to query the program by its unique ID. +To get the source code of a program, the `AleoNetworkClient` provides a method +to query the program by its unique ID. ```typescript -import { AleoNetworkClient } from '@provablehq/sdk/testnet.js'; +import { AleoNetworkClient } from "@provablehq/sdk/testnet.js"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); @@ -247,50 +300,50 @@ const token_registry = await networkClient.getProgram("token_registry.aleo"); ### Querying Programs as Objects -The `SnarkVM` representation of a program can be queried from the Aleo Network by its unique ID. The returned object -representation has several convenience methods for extracting a list of program's `functions`, `inputs` and `input types` -, `mappings`, `records`, and `address`. This is useful because these data are difficult to parse from the source code -directly. +The `SnarkVM` representation of a program can be queried from the Aleo Network +by its unique ID. The returned object representation has several convenience +methods for extracting a list of program's `functions`, `inputs` and +`input types` , `mappings`, `records`, and `address`. This is useful because +these data are difficult to parse from the source code directly. ```typescript -import { AleoNetworkClient } from '@provablehq/sdk/testnet.js'; +import { AleoNetworkClient } from "@provablehq/sdk/testnet.js"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); - - // Get credits.aleo as a program object. const credits = await networkClient.getProgramObject("credits.aleo"); // Get all functions in the program. const functions = credits.getFunctions(); // Get all inputs for the `transfer_private` function. -const transfer_function_inputs = credits_program.getFunctionInputs("transfer_private"); -// Inputs will be an array of objects with the following structure, this an be used to build web forms or other UI +const transfer_function_inputs = + credits_program.getFunctionInputs("transfer_private"); +// Inputs will be an array of objects with the following structure, this an be used to build web forms or other UI // elements. An example of an Aleo function input form can be seen at https://provable.tools/develop. const expected_inputs = [ { - type:"record", - visibility:"private", - record:"credits", - members:[ - { - name:"microcredits", - type:"u64", - visibility:"private" - } - ], - register:"r0" + type: "record", + visibility: "private", + record: "credits", + members: [ + { + name: "microcredits", + type: "u64", + visibility: "private", + }, + ], + register: "r0", }, { - type:"address", - visibility:"private", - register:"r1" + type: "address", + visibility: "private", + register: "r1", }, { - type:"u64", - visibility:"private", - register:"r2" - } + type: "u64", + visibility: "private", + register: "r2", + }, ]; console.log(transfer_function_inputs === expected_inputs); // Output should be "true" @@ -298,36 +351,46 @@ console.log(transfer_function_inputs === expected_inputs); // Output should be " const mappings = credits.getMappings(); // Get all programs the program imports. -const records = credits.getImports() +const records = credits.getImports(); ``` ### Querying Program Imports and Mappings -The following example shows how to query the mappings within a program and the other programs it imports. +The following example shows how to query the mappings within a program and the +other programs it imports. ```typescript -import { AleoNetworkClient, Program } from '@provablehq/sdk/testnet.js'; +import { AleoNetworkClient, Program } from "@provablehq/sdk/testnet.js"; const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); // Get the program's import names. -const programImportsNames = await networkClient.getProgramImportNames("token_registry.aleo"); +const programImportsNames = await networkClient.getProgramImportNames( + "token_registry.aleo", +); const expectedImportsNames = ["credits.aleo"]; assert.deepStrictEqual(programImportsNames, expectedImportsNames); // Get the source code of all imported programs. -const programImports = await networkClient.getProgramImports("token_registry.aleo"); +const programImports = await networkClient.getProgramImports( + "token_registry.aleo", +); // Get the source code of credits.aleo as a wasm object. -assert.deepStrictEqual(programImports["credits.aleo"], Program.getCreditsProgram().toString()); +assert.deepStrictEqual( + programImports["credits.aleo"], + Program.getCreditsProgram().toString(), +); // Get the list of all program mappings. -const programMappings = await networkClient.getProgramMappingNames("token_registry.aleo"); +const programMappings = await networkClient.getProgramMappingNames( + "token_registry.aleo", +); const expectedMappings = [ - "registered_tokens", - "balances", - "authorized_balances", - "allowances", - "roles" + "registered_tokens", + "balances", + "authorized_balances", + "allowances", + "roles", ]; assert.deepStrictEqual(programMappings, expectedMappings); -``` \ No newline at end of file +``` diff --git a/e2e/dynamic/index.js b/e2e/dynamic/index.js index fdb80832e..9024fce73 100644 --- a/e2e/dynamic/index.js +++ b/e2e/dynamic/index.js @@ -4,18 +4,23 @@ const mainnet = await loadNetwork("mainnet"); await mainnet.initThreadPool(); -const programName = "hello_hello.aleo" +const programName = "hello_hello.aleo"; -const hello_hello_program =` +const hello_hello_program = ` program ${programName}; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; - output r2 as u32.private;` + output r2 as u32.private;`; -async function localProgramExecution(program, programName, aleoFunction, inputs) { +async function localProgramExecution( + program, + programName, + aleoFunction, + inputs, +) { const programManager = new mainnet.ProgramManager(); // Create a temporary account for the execution of the program @@ -28,12 +33,21 @@ async function localProgramExecution(program, programName, aleoFunction, inputs) programManager.setKeyProvider(keyProvider); // Pre-synthesize the program keys and then cache them in memory using key provider - const keyPair = await programManager.synthesizeKeys(hello_hello_program, aleoFunction, inputs); - programManager.keyProvider.cacheKeys(`${programName}:${aleoFunction}`, keyPair); + const keyPair = await programManager.synthesizeKeys( + hello_hello_program, + aleoFunction, + inputs, + ); + programManager.keyProvider.cacheKeys( + `${programName}:${aleoFunction}`, + keyPair, + ); // Specify parameters for the key provider to use search for program keys. In particular specify the cache key // that was used to cache the keys in the previous step. - const keyProviderParams = new mainnet.AleoKeyProviderParams({cacheKey: `${programName}:${aleoFunction}`}); + const keyProviderParams = new mainnet.AleoKeyProviderParams({ + cacheKey: `${programName}:${aleoFunction}`, + }); // Execute once using the key provider params defined above. This will use the cached proving keys and make // execution significantly faster. @@ -45,18 +59,24 @@ async function localProgramExecution(program, programName, aleoFunction, inputs) undefined, keyProviderParams, ); - console.log("hello_hello/hello executed - result:", executionResponse.getOutputs()); + console.log( + "hello_hello/hello executed - result:", + executionResponse.getOutputs(), + ); // Verify the execution using the verifying key that was generated earlier. const blockHeight = 9_000_000; if (programManager.verifyExecution(executionResponse, blockHeight)) { console.log("hello_hello/hello execution verified!"); } else { - throw("Execution failed verification!"); + throw "Execution failed verification!"; } } const start = Date.now(); console.log("Starting execute!"); -await localProgramExecution(hello_hello_program, programName, "hello", ["5u32", "5u32"]); +await localProgramExecution(hello_hello_program, programName, "hello", [ + "5u32", + "5u32", +]); console.log("Execute finished!", Date.now() - start); diff --git a/e2e/dynamic/package.json b/e2e/dynamic/package.json index 2ef0d67ba..313964818 100644 --- a/e2e/dynamic/package.json +++ b/e2e/dynamic/package.json @@ -1,12 +1,12 @@ { - "name": "e2e-dynamic", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "start": "node index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - } + "name": "e2e-dynamic", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + } } diff --git a/e2e/mainnet/index.js b/e2e/mainnet/index.js index b769379c9..ea5ec81fc 100644 --- a/e2e/mainnet/index.js +++ b/e2e/mainnet/index.js @@ -1,19 +1,30 @@ -import {Account, initThreadPool, ProgramManager, AleoKeyProvider, AleoKeyProviderParams} from "@provablehq/sdk/mainnet.js"; +import { + Account, + initThreadPool, + ProgramManager, + AleoKeyProvider, + AleoKeyProviderParams, +} from "@provablehq/sdk/mainnet.js"; await initThreadPool(); -const programName = "hello_hello.aleo" +const programName = "hello_hello.aleo"; -const hello_hello_program =` +const hello_hello_program = ` program ${programName}; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; - output r2 as u32.private;` + output r2 as u32.private;`; -async function localProgramExecution(program, programName, aleoFunction, inputs) { +async function localProgramExecution( + program, + programName, + aleoFunction, + inputs, +) { const programManager = new ProgramManager(); // Create a temporary account for the execution of the program @@ -26,12 +37,21 @@ async function localProgramExecution(program, programName, aleoFunction, inputs) programManager.setKeyProvider(keyProvider); // Pre-synthesize the program keys and then cache them in memory using key provider - const keyPair = await programManager.synthesizeKeys(hello_hello_program, aleoFunction, inputs); - programManager.keyProvider.cacheKeys(`${programName}:${aleoFunction}`, keyPair); + const keyPair = await programManager.synthesizeKeys( + hello_hello_program, + aleoFunction, + inputs, + ); + programManager.keyProvider.cacheKeys( + `${programName}:${aleoFunction}`, + keyPair, + ); // Specify parameters for the key provider to use search for program keys. In particular specify the cache key // that was used to cache the keys in the previous step. - const keyProviderParams = new AleoKeyProviderParams({cacheKey: `${programName}:${aleoFunction}`}); + const keyProviderParams = new AleoKeyProviderParams({ + cacheKey: `${programName}:${aleoFunction}`, + }); // Execute once using the key provider params defined above. This will use the cached proving keys and make // execution significantly faster. @@ -43,18 +63,24 @@ async function localProgramExecution(program, programName, aleoFunction, inputs) undefined, keyProviderParams, ); - console.log("hello_hello/hello executed - result:", executionResponse.getOutputs()); + console.log( + "hello_hello/hello executed - result:", + executionResponse.getOutputs(), + ); // Verify the execution using the verifying key that was generated earlier. const blockHeight = 9_000_000; if (programManager.verifyExecution(executionResponse, blockHeight)) { console.log("hello_hello/hello execution verified!"); } else { - throw("Execution failed verification!"); + throw "Execution failed verification!"; } } const start = Date.now(); console.log("Starting execute!"); -await localProgramExecution(hello_hello_program, programName, "hello", ["5u32", "5u32"]); +await localProgramExecution(hello_hello_program, programName, "hello", [ + "5u32", + "5u32", +]); console.log("Execute finished!", Date.now() - start); diff --git a/e2e/mainnet/package.json b/e2e/mainnet/package.json index 57d467b3f..326af18e6 100644 --- a/e2e/mainnet/package.json +++ b/e2e/mainnet/package.json @@ -1,12 +1,12 @@ { - "name": "e2e-mainnet", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "start": "node index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - } + "name": "e2e-mainnet", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + } } diff --git a/e2e/nodenext/index.ts b/e2e/nodenext/index.ts index 1015a0eb7..f4b6c704c 100644 --- a/e2e/nodenext/index.ts +++ b/e2e/nodenext/index.ts @@ -1 +1,7 @@ -import { Account, initThreadPool, ProgramManager, AleoKeyProvider, AleoKeyProviderParams } from "@provablehq/sdk"; +import { + Account, + initThreadPool, + ProgramManager, + AleoKeyProvider, + AleoKeyProviderParams, +} from "@provablehq/sdk"; diff --git a/e2e/nodenext/tsconfig.json b/e2e/nodenext/tsconfig.json index aedbe4100..93c839297 100644 --- a/e2e/nodenext/tsconfig.json +++ b/e2e/nodenext/tsconfig.json @@ -1,8 +1,8 @@ { - "compilerOptions": { - "outDir": "./dist", - "module": "nodenext", - "moduleResolution": "nodenext", - "esModuleInterop": true, - } + "compilerOptions": { + "outDir": "./dist", + "module": "nodenext", + "moduleResolution": "nodenext", + "esModuleInterop": true + } } diff --git a/e2e/testnet/index.js b/e2e/testnet/index.js index 663d62272..9beffe1bc 100644 --- a/e2e/testnet/index.js +++ b/e2e/testnet/index.js @@ -1,19 +1,30 @@ -import {Account, initThreadPool, ProgramManager, AleoKeyProvider, AleoKeyProviderParams} from "@provablehq/sdk/testnet.js"; +import { + Account, + initThreadPool, + ProgramManager, + AleoKeyProvider, + AleoKeyProviderParams, +} from "@provablehq/sdk/testnet.js"; await initThreadPool(); -const programName = "hello_hello.aleo" +const programName = "hello_hello.aleo"; -const hello_hello_program =` +const hello_hello_program = ` program ${programName}; function hello: input r0 as u32.public; input r1 as u32.private; add r0 r1 into r2; - output r2 as u32.private;` + output r2 as u32.private;`; -async function localProgramExecution(program, programName, aleoFunction, inputs) { +async function localProgramExecution( + program, + programName, + aleoFunction, + inputs, +) { const programManager = new ProgramManager(); // Create a temporary account for the execution of the program @@ -26,12 +37,21 @@ async function localProgramExecution(program, programName, aleoFunction, inputs) programManager.setKeyProvider(keyProvider); // Pre-synthesize the program keys and then cache them in memory using key provider - const keyPair = await programManager.synthesizeKeys(hello_hello_program, aleoFunction, inputs); - programManager.keyProvider.cacheKeys(`${programName}:${aleoFunction}`, keyPair); + const keyPair = await programManager.synthesizeKeys( + hello_hello_program, + aleoFunction, + inputs, + ); + programManager.keyProvider.cacheKeys( + `${programName}:${aleoFunction}`, + keyPair, + ); // Specify parameters for the key provider to use search for program keys. In particular specify the cache key // that was used to cache the keys in the previous step. - const keyProviderParams = new AleoKeyProviderParams({cacheKey: `${programName}:${aleoFunction}`}); + const keyProviderParams = new AleoKeyProviderParams({ + cacheKey: `${programName}:${aleoFunction}`, + }); // Execute once using the key provider params defined above. This will use the cached proving keys and make // execution significantly faster. @@ -43,17 +63,23 @@ async function localProgramExecution(program, programName, aleoFunction, inputs) undefined, keyProviderParams, ); - console.log("hello_hello/hello executed - result:", executionResponse.getOutputs()); + console.log( + "hello_hello/hello executed - result:", + executionResponse.getOutputs(), + ); // Verify the execution using the verifying key that was generated earlier. if (programManager.verifyExecution(executionResponse, 9_000_000)) { console.log("hello_hello/hello execution verified!"); } else { - throw("Execution failed verification!"); + throw "Execution failed verification!"; } } const start = Date.now(); console.log("Starting execute!"); -await localProgramExecution(hello_hello_program, programName, "hello", ["5u32", "5u32"]); +await localProgramExecution(hello_hello_program, programName, "hello", [ + "5u32", + "5u32", +]); console.log("Execute finished!", Date.now() - start); diff --git a/e2e/testnet/package.json b/e2e/testnet/package.json index c13b79ee2..cff4f2c41 100644 --- a/e2e/testnet/package.json +++ b/e2e/testnet/package.json @@ -1,12 +1,12 @@ { - "name": "e2e-testnet", - "private": true, - "version": "0.0.0", - "type": "module", - "scripts": { - "start": "node index.js" - }, - "dependencies": { - "@provablehq/sdk": "^0.9.18" - } + "name": "e2e-testnet", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "dependencies": { + "@provablehq/sdk": "^0.9.18" + } } diff --git a/package.json b/package.json index fc265d205..716c04385 100644 --- a/package.json +++ b/package.json @@ -1,40 +1,40 @@ { - "name": "sdk-root", - "private": true, - "version": "0.0.0", - "workspaces": [ - "sdk", - "wasm", - "website", - "create-leo-app", - "create-leo-app/*", - "e2e", - "e2e/*" - ], - "type": "module", - "scripts": { - "build:wasm": "rustup show active-toolchain && cd wasm && yarn build", - "build:sdk": "cd sdk && yarn build", - "build:sdk-docs": "sh scripts/jsdoc.sh", - "build:create-leo-app": "cd create-leo-app && yarn build", - "build:all": "yarn build:wasm && yarn build:sdk && yarn build:create-leo-app", - "start:website": "cd website && yarn dev", - "test:wasm": "cd wasm && yarn test", - "test:sdk": "cd sdk && yarn test", - "test": "yarn test:wasm && yarn test:sdk", - "change-version": "node scripts/change-version.js", - "deploy:wasm": "cd wasm && npm publish --access=public", - "deploy:sdk": "cd sdk && npm publish --access=public", - "deploy:create-leo-app": "cd create-leo-app && npm publish --access=public", - "deploy": "yarn build:all && yarn deploy:wasm && yarn deploy:sdk && yarn deploy:create-leo-app", - "lint": "prettier . --check", - "pretty": "prettier . --write" - }, - "optionalDependencies": { - "glob": "^11.0.1" - }, - "devDependencies": { - "prettier": "3.4.2", - "wasm-pack": "^0.13.1" - } + "name": "sdk-root", + "private": true, + "version": "0.0.0", + "workspaces": [ + "sdk", + "wasm", + "website", + "create-leo-app", + "create-leo-app/*", + "e2e", + "e2e/*" + ], + "type": "module", + "scripts": { + "build:wasm": "rustup show active-toolchain && cd wasm && yarn build", + "build:sdk": "cd sdk && yarn build", + "build:sdk-docs": "sh scripts/jsdoc.sh", + "build:create-leo-app": "cd create-leo-app && yarn build", + "build:all": "yarn build:wasm && yarn build:sdk && yarn build:create-leo-app", + "start:website": "cd website && yarn dev", + "test:wasm": "cd wasm && yarn test", + "test:sdk": "cd sdk && yarn test", + "test": "yarn test:wasm && yarn test:sdk", + "change-version": "node scripts/change-version.js", + "deploy:wasm": "cd wasm && npm publish --access=public", + "deploy:sdk": "cd sdk && npm publish --access=public", + "deploy:create-leo-app": "cd create-leo-app && npm publish --access=public", + "deploy": "yarn build:all && yarn deploy:wasm && yarn deploy:sdk && yarn deploy:create-leo-app", + "lint": "prettier . --check", + "pretty": "prettier . --write" + }, + "optionalDependencies": { + "glob": "^11.0.1" + }, + "devDependencies": { + "prettier": "3.4.2", + "wasm-pack": "^0.13.1" + } } diff --git a/scripts/change-version.js b/scripts/change-version.js index 604c5092a..77ec1f5ff 100644 --- a/scripts/change-version.js +++ b/scripts/change-version.js @@ -1,64 +1,75 @@ import { readFile, writeFile } from "node:fs/promises"; import { glob } from "glob"; - // Updates the version in the `package.json` file async function updateVersion(path, newVersion) { const json = await readFile(path, { encoding: "utf8" }); - const replaced = json.replace(/"version": *"[^"]+"/, `"version": "${newVersion}"`); + const replaced = json.replace( + /"version": *"[^"]+"/, + `"version": "${newVersion}"`, + ); await writeFile(path, replaced); } - // Updates the `package.json` file so it uses the correct // version of `@provablehq/wasm` and `@provablehq/sdk` async function updateDependency(path, newVersion) { const json = await readFile(path, { encoding: "utf8" }); const replaced = json - .replace(/"@provablehq\/sdk": *"[^"]+"/g, `"@provablehq/sdk": "^${newVersion}"`) - .replace(/"@provablehq\/wasm": *"[^"]+"/g, `"@provablehq/wasm": "^${newVersion}"`); + .replace( + /"@provablehq\/sdk": *"[^"]+"/g, + `"@provablehq/sdk": "^${newVersion}"`, + ) + .replace( + /"@provablehq\/wasm": *"[^"]+"/g, + `"@provablehq/wasm": "^${newVersion}"`, + ); await writeFile(path, replaced); } - // Updates the version of the published `package.json` files async function updateVersions(newVersion) { - await Promise.all([ - "create-leo-app/package.json", - "sdk/package.json", - "wasm/package.json", - ].map(async (file) => { - await updateVersion(file, newVersion); - })); + await Promise.all( + [ + "create-leo-app/package.json", + "sdk/package.json", + "wasm/package.json", + ].map(async (file) => { + await updateVersion(file, newVersion); + }), + ); } - // Updates the version in `Cargo.toml` async function updateCargo(newVersion) { const toml = await readFile("wasm/Cargo.toml", { encoding: "utf8" }); - const replaced = toml - .replace(/(\[package\]\s+name *= *"aleo-wasm"\s+version *= *)"[^"]+"/, `$1"${newVersion}"`); + const replaced = toml.replace( + /(\[package\]\s+name *= *"aleo-wasm"\s+version *= *)"[^"]+"/, + `$1"${newVersion}"`, + ); await writeFile("wasm/Cargo.toml", replaced); } - // Updates all of the `package.json` files so they use the correct // version of `@provablehq/wasm` and `@provablehq/sdk` async function updateDependencies(newVersion) { - const files = await glob("**/package.json", { ignore: "**/node_modules/**" }); - - await Promise.all(files.map(async (file) => { - await updateDependency(file, newVersion); - })); + const files = await glob("**/package.json", { + ignore: "**/node_modules/**", + }); + + await Promise.all( + files.map(async (file) => { + await updateDependency(file, newVersion); + }), + ); } - const newVersion = process.argv[2]; await updateVersions(newVersion); diff --git a/sdk/.eslintrc.json b/sdk/.eslintrc.json index e277b4481..567c4f899 100644 --- a/sdk/.eslintrc.json +++ b/sdk/.eslintrc.json @@ -1,16 +1,16 @@ { - "env": { - "browser": true, - "es2021": true - }, - "extends": ["plugin:@typescript-eslint/recommended", "prettier"], - "overrides": [], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": "latest", - "sourceType": "module" - }, - "plugins": ["@typescript-eslint"], - "root": true, - "rules": {} + "env": { + "browser": true, + "es2021": true + }, + "extends": ["plugin:@typescript-eslint/recommended", "prettier"], + "overrides": [], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "plugins": ["@typescript-eslint"], + "root": true, + "rules": {} } diff --git a/sdk/README.md b/sdk/README.md index 888e8cc62..d71aa8d04 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -12,22 +12,24 @@ Provable SDK

- The [Provable SDK](https://github.com/ProvableHQ/sdk) is an open source TypeScript/JavaScript SDK for building private full stack web applications. The SDK provides an easy API for using Aleo's core suite of cryptographic primitives, zk-SNARKs and private program execution protocol directly within Javascript. It also provides APIs for interacting with nodes and applications on the Aleo Network. Applications built with the Provable SDK include: -* **Private DeFi** -* **Private Data Custody Systems** -* **Zk Games & Gaming Toolkits** -* **ZkML** -* **Zero-Knowledge Identity Management** -* **Aleo Wallets** + +- **Private DeFi** +- **Private Data Custody Systems** +- **Zk Games & Gaming Toolkits** +- **ZkML** +- **Zero-Knowledge Identity Management** +- **Aleo Wallets** ## Features + The Provable SDK provides the following features. Follow the links below for live demonstrations of these features on [provable.tools](https://provable.tools) + 1. [Aleo account management](https://provable.tools/account) 2. [Aleo cryptographic primitives](https://provable.tools/algebra) 3. [Web based zero-knowledge program execution and deployment](https://provable.tools/develop) @@ -35,12 +37,12 @@ The Provable SDK provides the following features. Follow the links below for liv 5. [Management of program state](https://provable.tools/protocol) 6. [Communication with the Aleo network](https://provable.tools/rest) - ## Start Building Developers interested in using the SDK to build private web applications can get started with the following resources. ### [The SDK Guide](https://docs.explorer.provable.com/docs/sdk/6p7047svvq2ox-intro-to-aleo) + Provable SDK A step-by-step guide to building private web applications with the Provable SDK. The guide covers creating Aleo accounts, @@ -56,10 +58,12 @@ examples can be easily installed and run using the `create-leo-app` command line of use cases such as Private NFTs, offline transaction signing, and usage of the SDK in React, Next, and VanillaJS. ### [SDK API Documentation](https://docs.explorer.provable.com/docs/sdk/0qgi3uyhotv62-account) + For developers who prefer to dive straight into the code, the SDK API documentation provides a comprehensive reference for all the SDK's classes, methods, and interfaces. The documentation is generated from the SDK's TypeScript source code. ### [Leo Language Documentation](https://docs.leo-lang.org) + For developers who want to build their own zero-knowledge programs, the Leo Language provides an easy-to-use imperative programming language for writing zero-knowledge programs on Aleo. In conjuction with the SDK, private programs built -using Leo can be turned into fully functional private web applications. \ No newline at end of file +using Leo can be turned into fully functional private web applications. diff --git a/sdk/docs/index.html b/sdk/docs/index.html index bf4ca1fa6..25a03f1f4 100644 --- a/sdk/docs/index.html +++ b/sdk/docs/index.html @@ -1,3 +1,1329 @@ -Provable SDK
On this page

Website

Zero-Knowledge Web App SDK

The Provable SDK provides tools for building zero-knowledge applications. It consists of several TypeScript & JavaScript libraries which provide the following functionality:

  1. Aleo account management
  2. Web-based program execution and deployment
  3. Aleo credit transfers
  4. Management of program state and data
  5. Communication with the Aleo network

All of this functionality is demonstrated on Provable.tools.

The Provable SDK is divided into three TypeScript/JavaScript packages:

1. Provable SDK - Build Zero-Knowledge Web Apps

Provable SDK

The official Provable SDK providing JavaScript/TypeScript tools for creating zero-knowledge applications.

⚡ Build your own app

Start here with the Provable SDK Readme to get started building your first zero-knowledge web app.

Source: Provable SDK

2. Create-Leo-App - Zero-Knowledge Web App Examples

Create Leo App

Create-leo-app provides zero-knowledge web app examples in common web frameworks such as React. Developers looking to start with working examples should start here.

Source: sdk/create-leo-app

3. Aleo Wasm - Zero-Knowledge Algorithms in JavaScript + WebAssembly

Create Leo AppCreate Leo AppAleo-Wasm

Aleo Wasm is a Rust crate which compiles the Aleo source code responsible for creating and executing zero-knowledge programs into WebAssembly.

When compiled with wasm-pack, JavaScript bindings are generated for the WebAssembly allowing Aleo zero-knowledge programs to be used in the browser and Node.js. This package is available on NPM (linked above). The Aleo Wasm readme provides instructions for compiling this crate and using it in web projects for those interested in building from source.

❗ Currently, program execution is only available in web browsers. However, account, program, and data management within NodeJS is functional.

Source: Aleo Wasm

📚 Documentation

API Documentation

API Documentation, tutorials for the Provable SDK, and documentation on how to build Leo and Aleo Instructions programs can be found on the Leo Developer Docs page.

SDK Readme

The SDK readme provides concepts core to executing zero-knowledge programs in the web and several detailed examples of how to use the SDK to build web apps using Aleo.

Aleo Wasm Readme

The Aleo Wasm readme provides instructions for compiling the Aleo Wasm crate and using it in web projects. Those who want to build from source or create their own WebAssembly bindings should start here.

❤️ Contributors

Thanks goes to these wonderful people (emoji key):

Mike Turner
Mike Turner

💻 🚧 💬 👀
Brent C
Brent C

💻 🚧 💬 👀
Collin Chin
Collin Chin

💻 🚧 💬 👀
Howard Wu
Howard Wu

💻 🤔 🔬 👀
Raymond Chu
Raymond Chu

💻 🤔 🔬 👀
d0cd
d0cd

💻 🤔 🔬 👀
Alessandro Coglio
Alessandro Coglio

📖 🔬 👀
a h
a h

💻 📖
Anthony DiPrinzio
Anthony DiPrinzio

💻
Ali Mousa
Ali Mousa

💻
Ivan Litteri
Ivan Litteri

💻
Nacho Avecilla
Nacho Avecilla

💻
ljedrz
ljedrz

💻
Facundo Olano
Facundo Olano

💻
Nicolas Continanza
Nicolas Continanza

💻
Mike
Mike

💻
Javier Rodríguez Chatruc
Javier Rodríguez Chatruc

💻
Pablo Deymonnaz
Pablo Deymonnaz

💻
Bob Niu
Bob Niu

💻
sptg
sptg

💻
Hamza Khchichine
Hamza Khchichine

💻
Kendrick
Kendrick

💻
Dependabot
Dependabot

💻
All Contributors
All Contributors

📖
Add your contributions

This project follows the all-contributors specification. Contributions of any kind welcome!

\ No newline at end of file + + + + + + Provable SDK + + + + + + + + + + + + + +
+
+ On this page +
+
+
+
+
+
+
+
+

+ Website +

+

+ Zero-Knowledge Web App SDK +

+

+ The + Provable SDK + provides tools for building zero-knowledge + applications. It consists of several TypeScript + & JavaScript libraries which provide the + following functionality: +

+
    +
  1. + Aleo account management +
  2. +
  3. + Web-based program execution and + deployment +
  4. +
  5. + Aleo credit transfers +
  6. +
  7. + Management of program state and data +
  8. +
  9. + Communication with the Aleo network +
  10. +
+

+ All of this functionality is demonstrated on + Provable.tools. +

+

+ The Provable SDK is divided into three + TypeScript/JavaScript packages: +

+

+ 1. Provable SDK - Build Zero-Knowledge Web Apps +

+

+ Provable SDK +

+

+ The official Provable SDK providing + JavaScript/TypeScript tools for creating + zero-knowledge applications. +

+

+ ⚡ Build your own app +

+

+ Start here with the + Provable SDK Readme + to get started building your first + zero-knowledge web app. +

+

+ Source: + Provable SDK +

+

+ 2. Create-Leo-App - Zero-Knowledge Web App + Examples +

+

+ Create Leo App +

+

+ Create-leo-app provides zero-knowledge web app + examples in common web frameworks such as React. + Developers looking to start with working + examples should start here. +

+

+ Source: + sdk/create-leo-app +

+

+ 3. Aleo Wasm - Zero-Knowledge Algorithms in + JavaScript + WebAssembly +

+

+ Create Leo AppCreate Leo AppAleo-Wasm +

+

+ Aleo Wasm is a Rust crate which compiles the + Aleo source code responsible for creating and + executing zero-knowledge programs into + WebAssembly. +

+

+ When compiled with wasm-pack, + JavaScript bindings are generated for the + WebAssembly allowing Aleo zero-knowledge + programs to be used in the browser and Node.js. + This package is available on NPM (linked above). + The Aleo Wasm readme provides instructions for + compiling this crate and using it in web + projects for those interested in building from + source. +

+

+ ❗ Currently, program execution is only + available in web browsers. However, account, + program, and data management within NodeJS is + functional. +

+

+ Source: + Aleo Wasm +

+

+ 📚 Documentation +

+

+ API Documentation +

+

+ API Documentation, tutorials for the Provable + SDK, and documentation on how to build Leo and + Aleo Instructions programs can be found on the + Leo Developer Docs + page. +

+

+ SDK Readme +

+

+ The SDK readme provides concepts core to + executing zero-knowledge programs in the web and + several detailed examples of how to use the SDK + to build web apps using Aleo. +

+

+ Aleo Wasm Readme +

+

+ The Aleo Wasm readme provides instructions for + compiling the Aleo Wasm crate and using it in + web projects. Those who want to build from + source or create their own WebAssembly bindings + should start here. +

+

+ ❤️ Contributors +

+

+ Thanks goes to these wonderful people (emoji key): +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Mike Turner
Mike Turner

💻 + 🚧 + 💬 + 👀 +
+ Brent C
Brent C

💻 + 🚧 + 💬 + 👀 +
+ Collin Chin
Collin Chin

💻 + 🚧 + 💬 + 👀 +
+ Howard Wu
Howard Wu

💻 + 🤔 + 🔬 + 👀 +
+ Raymond Chu
Raymond Chu

💻 + 🤔 + 🔬 + 👀 +
+ d0cd
d0cd

💻 + 🤔 + 🔬 + 👀 +
+ Alessandro Coglio
Alessandro Coglio

📖 + 🔬 + 👀 +
+ a h
a h

💻 + 📖 +
+ Anthony DiPrinzio
Anthony DiPrinzio

💻 +
+ Ali Mousa
Ali Mousa

💻 +
+ Ivan Litteri
Ivan Litteri

💻 +
+ Nacho Avecilla
Nacho Avecilla

💻 +
+ ljedrz
ljedrz

💻 +
+ Facundo Olano
Facundo Olano

💻 +
+ Nicolas Continanza
Nicolas Continanza

💻 +
+ Mike
Mike

💻 +
+ Javier Rodríguez Chatruc
Javier Rodríguez + Chatruc

💻 +
+ Pablo Deymonnaz
Pablo Deymonnaz

💻 +
+ Bob Niu
Bob Niu

💻 +
+ sptg
sptg

💻 +
+ Hamza Khchichine
Hamza Khchichine

💻 +
+ Kendrick
Kendrick

💻 +
+ Dependabot
Dependabot

💻 +
+ All Contributors
All Contributors

📖 +
+ + Add your contributions +
+

+ This project follows the + all-contributors + specification. Contributions of any kind + welcome! +

+
+
+
+
+
+ +
+ +
+
+
+ + +
+ + + +
+
+
+ + + + + + diff --git a/sdk/jsdoc.json b/sdk/jsdoc.json index 45b449048..42611e6c2 100644 --- a/sdk/jsdoc.json +++ b/sdk/jsdoc.json @@ -1,32 +1,43 @@ { - "source": { - "include": ["sdk/src/account.ts", "sdk/src/function-key-provider.ts", "sdk/src/network-client.ts", "sdk/src/offline-key-provider.ts", "sdk/src/program-manager.ts", "sdk/src/record-provider.ts", "sdk/src/wasm.ts"], - "includePattern": ".+\\.ts?$" - }, - "tags": { - "allowUnknownTags": ["optional"] - }, - "plugins": ["node_modules/better-docs/typescript", "node_modules/kis-jsdoc-plugin"], - "templates": { + "source": { + "include": [ + "sdk/src/account.ts", + "sdk/src/function-key-provider.ts", + "sdk/src/network-client.ts", + "sdk/src/offline-key-provider.ts", + "sdk/src/program-manager.ts", + "sdk/src/record-provider.ts", + "sdk/src/wasm.ts" + ], + "includePattern": ".+\\.ts?$" + }, + "tags": { + "allowUnknownTags": ["optional"] + }, + "plugins": [ + "node_modules/better-docs/typescript", + "node_modules/kis-jsdoc-plugin" + ], + "templates": { + "markdown": { + "tocfilename": "toc.md" + } + }, + "opts": { + "encoding": "utf8", + "readme": "./README.md", + "destination": "docs/api_reference", + "recurse": true, + "verbose": true, + "template": "node_modules/kis-jsdoc-plugin/templates/markdown", + "theme_opts": { + "default_theme": "dark", + "static_dir": ["sdk/docs/public"], + "homepageTitle": "Provable SDK" + } + }, "markdown": { - "tocfilename": "toc.md" + "hardwrap": false, + "idInHeadings": true } - }, - "opts": { - "encoding": "utf8", - "readme": "./README.md", - "destination": "docs/api_reference", - "recurse": true, - "verbose": true, - "template": "node_modules/kis-jsdoc-plugin/templates/markdown", - "theme_opts": { - "default_theme": "dark", - "static_dir": ["sdk/docs/public"], - "homepageTitle": "Provable SDK" - } - }, - "markdown": { - "hardwrap": false, - "idInHeadings": true - } } diff --git a/sdk/package.json b/sdk/package.json index 847d992a2..1335c686c 100644 --- a/sdk/package.json +++ b/sdk/package.json @@ -1,87 +1,87 @@ { - "name": "@provablehq/sdk", - "version": "0.9.18", - "description": "A Software Development Kit (SDK) for Zero-Knowledge Transactions", - "collaborators": [ - "The Provable Team" - ], - "license": "GPL-3.0", - "type": "module", - "main": "./dist/testnet/node.js", - "browser": "./dist/testnet/browser.js", - "exports": { - ".": { - "node": "./dist/testnet/node.js", - "default": "./dist/testnet/browser.js" + "name": "@provablehq/sdk", + "version": "0.9.18", + "description": "A Software Development Kit (SDK) for Zero-Knowledge Transactions", + "collaborators": [ + "The Provable Team" + ], + "license": "GPL-3.0", + "type": "module", + "main": "./dist/testnet/node.js", + "browser": "./dist/testnet/browser.js", + "exports": { + ".": { + "node": "./dist/testnet/node.js", + "default": "./dist/testnet/browser.js" + }, + "./testnet.js": { + "node": "./dist/testnet/node.js", + "default": "./dist/testnet/browser.js" + }, + "./mainnet.js": { + "node": "./dist/mainnet/node.js", + "default": "./dist/mainnet/browser.js" + }, + "./dynamic.js": { + "node": "./dist/dynamic/node.js", + "default": "./dist/dynamic/browser.js" + } }, - "./testnet.js": { - "node": "./dist/testnet/node.js", - "default": "./dist/testnet/browser.js" + "files": [ + "dist", + "LICENSE", + "README.md" + ], + "scripts": { + "build": "rimraf dist && rollup -c rollup.config.js", + "test": "rimraf tmp && rollup -c rollup.test.js && mocha tmp/**/*.test.js --timeout 60000 && RUN_SKIPPED=true mocha tmp/**/wasm.test.js --timeout 60000 --grep Consensus" }, - "./mainnet.js": { - "node": "./dist/mainnet/node.js", - "default": "./dist/mainnet/browser.js" + "repository": { + "type": "git", + "url": "git+https://github.com/ProvableHQ/sdk.git" }, - "./dynamic.js": { - "node": "./dist/dynamic/node.js", - "default": "./dist/dynamic/browser.js" + "keywords": [ + "Aleo", + "Blockchain", + "Zero-Knowledge", + "ZK" + ], + "bugs": { + "url": "https://github.com/ProvableHQ/sdk/issues" + }, + "homepage": "https://github.com/ProvableHQ/sdk#readme", + "dependencies": { + "@provablehq/wasm": "^0.9.18", + "@scure/base": "^2.0.0", + "comlink": "^4.4.2", + "core-js": "^3.40.0", + "mime": "^4.0.6", + "libsodium-wrappers": "^0.8.2", + "xmlhttprequest-ssl": "^4.0.0" + }, + "devDependencies": { + "@rollup/plugin-replace": "^6.0.2", + "@types/chai": "^5.0.1", + "@types/mocha": "^10.0.10", + "@types/sinon": "^17.0.3", + "@typescript-eslint/eslint-plugin": "^8.22.0", + "@typescript-eslint/parser": "^8.22.0", + "better-docs": "^2.7.3", + "chai": "^5.1.2", + "clean-jsdoc-theme": "^4.3.0", + "cpr": "^3.0.1", + "eslint": "^9.19.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-import": "^2.31.0", + "glob": "^11.0.1", + "jsdoc": "^4.0.4", + "kis-jsdoc-plugin": "^2.2.1", + "mocha": "^11.1.0", + "prettier": "3.4.2", + "rimraf": "^6.0.1", + "rollup": "^4.59.0", + "rollup-plugin-typescript2": "^0.36.0", + "sinon": "^19.0.2", + "typescript": "^5.7.3" } - }, - "files": [ - "dist", - "LICENSE", - "README.md" - ], - "scripts": { - "build": "rimraf dist && rollup -c rollup.config.js", - "test": "rimraf tmp && rollup -c rollup.test.js && mocha tmp/**/*.test.js --timeout 60000 && RUN_SKIPPED=true mocha tmp/**/wasm.test.js --timeout 60000 --grep Consensus" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/ProvableHQ/sdk.git" - }, - "keywords": [ - "Aleo", - "Blockchain", - "Zero-Knowledge", - "ZK" - ], - "bugs": { - "url": "https://github.com/ProvableHQ/sdk/issues" - }, - "homepage": "https://github.com/ProvableHQ/sdk#readme", - "dependencies": { - "@provablehq/wasm": "^0.9.18", - "@scure/base": "^2.0.0", - "comlink": "^4.4.2", - "core-js": "^3.40.0", - "mime": "^4.0.6", - "libsodium-wrappers": "^0.8.2", - "xmlhttprequest-ssl": "^4.0.0" - }, - "devDependencies": { - "@rollup/plugin-replace": "^6.0.2", - "@types/chai": "^5.0.1", - "@types/mocha": "^10.0.10", - "@types/sinon": "^17.0.3", - "@typescript-eslint/eslint-plugin": "^8.22.0", - "@typescript-eslint/parser": "^8.22.0", - "better-docs": "^2.7.3", - "chai": "^5.1.2", - "clean-jsdoc-theme": "^4.3.0", - "cpr": "^3.0.1", - "eslint": "^9.19.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-import": "^2.31.0", - "glob": "^11.0.1", - "jsdoc": "^4.0.4", - "kis-jsdoc-plugin": "^2.2.1", - "mocha": "^11.1.0", - "prettier": "3.4.2", - "rimraf": "^6.0.1", - "rollup": "^4.59.0", - "rollup-plugin-typescript2": "^0.36.0", - "sinon": "^19.0.2", - "typescript": "^5.7.3" - } } diff --git a/sdk/rollup.config.js b/sdk/rollup.config.js index 2c38ab0b1..a77873160 100644 --- a/sdk/rollup.config.js +++ b/sdk/rollup.config.js @@ -4,22 +4,16 @@ import typescript from "rollup-plugin-typescript2"; import replace from "@rollup/plugin-replace"; import $package from "./package.json" with { type: "json" }; -const networks = [ - "testnet", - "mainnet", -]; +const networks = ["testnet", "mainnet"]; -const runtimes = [ - "browser", - "node", -]; +const runtimes = ["browser", "node"]; function buildNetwork(network) { return { input: { "node-polyfill": "./src/node-polyfill.ts", - "browser": "./src/browser.ts", - "node": "./src/node.ts", + browser: "./src/browser.ts", + node: "./src/node.ts", }, output: { dir: `dist/${network}`, @@ -44,10 +38,10 @@ function buildNetwork(network) { plugins: [ replace({ preventAssignment: true, - delimiters: ['', ''], + delimiters: ["", ""], values: { - '%%VERSION%%': $package.version, - '%%NETWORK%%': network, + "%%VERSION%%": $package.version, + "%%NETWORK%%": network, }, }), typescript({ @@ -59,31 +53,49 @@ function buildNetwork(network) { } async function buildRuntimes() { - await Promise.all(runtimes.map(async (runtime) => { - await $fs.mkdir(`dist/dynamic`, { recursive: true }); + await Promise.all( + runtimes.map(async (runtime) => { + await $fs.mkdir(`dist/dynamic`, { recursive: true }); - const loading = networks.map((network) => `"${network}": () => import("../${network}/${runtime}.js"),`).join("\n "); + const loading = networks + .map( + (network) => + `"${network}": () => import("../${network}/${runtime}.js"),`, + ) + .join("\n "); - const typings = networks.map((network) => `"${network}": typeof import("../${network}/${runtime}"),`).join("\n "); + const typings = networks + .map( + (network) => + `"${network}": typeof import("../${network}/${runtime}"),`, + ) + .join("\n "); - await $fs.writeFile(`dist/dynamic/${runtime}.js`, `const networks = { + await $fs.writeFile( + `dist/dynamic/${runtime}.js`, + `const networks = { ${loading} }; export function loadNetwork(name) { return networks[name](); } -`); +`, + ); - await $fs.writeFile(`dist/dynamic/${runtime}.d.ts`, `interface Networks { + await $fs.writeFile( + `dist/dynamic/${runtime}.d.ts`, + `interface Networks { ${typings} } export { Networks }; export declare function loadNetwork(name: Key): Promise; -`); - })); +`, + ); + }), + ); } await buildRuntimes(); diff --git a/sdk/rollup.test.js b/sdk/rollup.test.js index 487707588..e0943362e 100644 --- a/sdk/rollup.test.js +++ b/sdk/rollup.test.js @@ -3,10 +3,7 @@ import replace from "@rollup/plugin-replace"; import { globSync } from "glob"; import $package from "./package.json" with { type: "json" }; -const networks = [ - "testnet", - "mainnet", -]; +const networks = ["testnet", "mainnet"]; function inputs() { const files = {}; @@ -49,10 +46,10 @@ export default networks.map((network) => { plugins: [ replace({ preventAssignment: true, - delimiters: ['', ''], + delimiters: ["", ""], values: { - '%%VERSION%%': $package.version, - '%%NETWORK%%': network, + "%%VERSION%%": $package.version, + "%%NETWORK%%": network, }, }), typescript({ diff --git a/sdk/src/account.ts b/sdk/src/account.ts index 7860829b7..9a740815a 100644 --- a/sdk/src/account.ts +++ b/sdk/src/account.ts @@ -1,21 +1,21 @@ import { - Address, - ComputeKey, - EncryptionToolkit, - Field, - Group, - PrivateKey, - Signature, - ViewKey, - PrivateKeyCiphertext, - RecordCiphertext, - RecordPlaintext, + Address, + ComputeKey, + EncryptionToolkit, + Field, + Group, + PrivateKey, + Signature, + ViewKey, + PrivateKeyCiphertext, + RecordCiphertext, + RecordPlaintext, } from "./wasm.js"; import { zeroizeBytes } from "./security.js"; interface AccountParam { - privateKey?: string | PrivateKey; - seed?: Uint8Array; + privateKey?: string | PrivateKey; + seed?: Uint8Array; } /** @@ -55,449 +55,482 @@ interface AccountParam { * myRandomAccount.destroy(); */ export class Account { - _privateKey: PrivateKey; - _viewKey: ViewKey; - _computeKey: ComputeKey; - _address: Address; - private _destroyed = false; + _privateKey: PrivateKey; + _viewKey: ViewKey; + _computeKey: ComputeKey; + _address: Address; + private _destroyed = false; - constructor(params: AccountParam = {}) { - try { - this._privateKey = this.privateKeyFromParams(params); - } catch (e) { - console.error("Wrong parameter", e); - throw new Error("Wrong Parameter"); + constructor(params: AccountParam = {}) { + try { + this._privateKey = this.privateKeyFromParams(params); + } catch (e) { + console.error("Wrong parameter", e); + throw new Error("Wrong Parameter"); + } + this._viewKey = ViewKey.from_private_key(this._privateKey); + this._computeKey = ComputeKey.from_private_key(this._privateKey); + this._address = Address.from_private_key(this._privateKey); } - this._viewKey = ViewKey.from_private_key(this._privateKey); - this._computeKey = ComputeKey.from_private_key(this._privateKey); - this._address = Address.from_private_key(this._privateKey); - } - /** - * Attempts to create an account from a private key ciphertext - * @param {PrivateKeyCiphertext | string} ciphertext The encrypted private key ciphertext or its string representation - * @param {string} password The password used to decrypt the private key ciphertext - * @returns {Account} A new Account instance created from the decrypted private key - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * // Create an account object from a previously encrypted ciphertext and password. - * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); - */ - public static fromCiphertext(ciphertext: PrivateKeyCiphertext | string, password: string): Account { - try { - ciphertext = (typeof ciphertext === "string") ? PrivateKeyCiphertext.fromString(ciphertext) : ciphertext; - const _privateKey = PrivateKey.fromPrivateKeyCiphertext(ciphertext, password); - try { - return new Account({ privateKey: _privateKey }); - } finally { - _privateKey.free(); // Zeroize + free the temporary; Account owns its own clone - } - } catch(e) { - throw new Error("Wrong password or invalid ciphertext"); + /** + * Attempts to create an account from a private key ciphertext + * @param {PrivateKeyCiphertext | string} ciphertext The encrypted private key ciphertext or its string representation + * @param {string} password The password used to decrypt the private key ciphertext + * @returns {Account} A new Account instance created from the decrypted private key + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * // Create an account object from a previously encrypted ciphertext and password. + * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); + */ + public static fromCiphertext( + ciphertext: PrivateKeyCiphertext | string, + password: string, + ): Account { + try { + ciphertext = + typeof ciphertext === "string" + ? PrivateKeyCiphertext.fromString(ciphertext) + : ciphertext; + const _privateKey = PrivateKey.fromPrivateKeyCiphertext( + ciphertext, + password, + ); + try { + return new Account({ privateKey: _privateKey }); + } finally { + _privateKey.free(); // Zeroize + free the temporary; Account owns its own clone + } + } catch (e) { + throw new Error("Wrong password or invalid ciphertext"); + } } - } - /** - * Validates whether the given input is a valid Aleo address. - * @param {string | Uint8Array} address The address to validate, either as a string or bytes - * @returns {boolean} True if the address is valid, false otherwise - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * const isValid = Account.isValidAddress("aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"); - * console.log(isValid); // true - * - * const isInvalid = Account.isValidAddress("invalid_address"); - * console.log(isInvalid); // false - */ - public static isValidAddress(address: string | Uint8Array): boolean { - return Address.isValid(address); - } - - /** - * Creates a PrivateKey from the provided parameters. - * @param {AccountParam} params The parameters containing either a private key string, PrivateKey object, or a seed - * @returns {PrivateKey} A PrivateKey instance derived from the provided parameters - */ - private privateKeyFromParams(params: AccountParam): PrivateKey { - if (params.seed) { - return PrivateKey.from_seed_unchecked(params.seed); - } - if (params.privateKey) { - if (typeof params.privateKey === 'string') { - return PrivateKey.from_string(params.privateKey); - } - // Clone the PrivateKey WASM object via byte serialization to avoid - // creating an immutable JS string of the private key. - const bytes = params.privateKey.toBytesLe(); - try { - return PrivateKey.fromBytesLe(bytes); - } finally { - zeroizeBytes(bytes); - } + /** + * Validates whether the given input is a valid Aleo address. + * @param {string | Uint8Array} address The address to validate, either as a string or bytes + * @returns {boolean} True if the address is valid, false otherwise + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * const isValid = Account.isValidAddress("aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"); + * console.log(isValid); // true + * + * const isInvalid = Account.isValidAddress("invalid_address"); + * console.log(isInvalid); // false + */ + public static isValidAddress(address: string | Uint8Array): boolean { + return Address.isValid(address); } - return new PrivateKey(); - } - /** - * Throws an error if this account has been destroyed. - */ - private assertNotDestroyed(): void { - if (this._destroyed) { - throw new Error("Account has been destroyed. Create a new Account instance."); + /** + * Creates a PrivateKey from the provided parameters. + * @param {AccountParam} params The parameters containing either a private key string, PrivateKey object, or a seed + * @returns {PrivateKey} A PrivateKey instance derived from the provided parameters + */ + private privateKeyFromParams(params: AccountParam): PrivateKey { + if (params.seed) { + return PrivateKey.from_seed_unchecked(params.seed); + } + if (params.privateKey) { + if (typeof params.privateKey === "string") { + return PrivateKey.from_string(params.privateKey); + } + // Clone the PrivateKey WASM object via byte serialization to avoid + // creating an immutable JS string of the private key. + const bytes = params.privateKey.toBytesLe(); + try { + return PrivateKey.fromBytesLe(bytes); + } finally { + zeroizeBytes(bytes); + } + } + return new PrivateKey(); } - } - - /** - * Returns the PrivateKey associated with the account. - * @returns {PrivateKey} The private key of the account - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * const account = new Account(); - * const privateKey = account.privateKey(); - */ - privateKey(): PrivateKey { - this.assertNotDestroyed(); - return this._privateKey; - } - /** - * Returns the ViewKey associated with the account. - * @returns {ViewKey} The view key of the account - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * const account = new Account(); - * const viewKey = account.viewKey(); - */ - viewKey(): ViewKey { - this.assertNotDestroyed(); - return this._viewKey; - } + /** + * Throws an error if this account has been destroyed. + */ + private assertNotDestroyed(): void { + if (this._destroyed) { + throw new Error( + "Account has been destroyed. Create a new Account instance.", + ); + } + } - /** - * Returns the ComputeKey associated with the account. - * @returns {ComputeKey} The compute key of the account - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * const account = new Account(); - * const computeKey = account.computeKey(); - */ - computeKey(): ComputeKey { - this.assertNotDestroyed(); - return this._computeKey; - } + /** + * Returns the PrivateKey associated with the account. + * @returns {PrivateKey} The private key of the account + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * const account = new Account(); + * const privateKey = account.privateKey(); + */ + privateKey(): PrivateKey { + this.assertNotDestroyed(); + return this._privateKey; + } - /** - * Returns the Aleo address associated with the account. - * @returns {Address} The public address of the account - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * const account = new Account(); - * const address = account.address(); - */ - address(): Address { - this.assertNotDestroyed(); - return this._address; - } + /** + * Returns the ViewKey associated with the account. + * @returns {ViewKey} The view key of the account + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * const account = new Account(); + * const viewKey = account.viewKey(); + */ + viewKey(): ViewKey { + this.assertNotDestroyed(); + return this._viewKey; + } - /** - * Deep clones the Account via byte serialization of the private key, - * avoiding creation of immutable JS string representations of the private key. - * @returns {Account} A new Account instance with the same private key - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * const account = new Account(); - * const clonedAccount = account.clone(); - */ - clone(): Account { - this.assertNotDestroyed(); - return new Account({ privateKey: this._privateKey }); - } + /** + * Returns the ComputeKey associated with the account. + * @returns {ComputeKey} The compute key of the account + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * const account = new Account(); + * const computeKey = account.computeKey(); + */ + computeKey(): ComputeKey { + this.assertNotDestroyed(); + return this._computeKey; + } - /** - * Returns the address of the account in a string representation. - * - * @returns {string} The string representation of the account address - */ - toString(): string { - this.assertNotDestroyed(); - return this.address().to_string() - } + /** + * Returns the Aleo address associated with the account. + * @returns {Address} The public address of the account + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * const account = new Account(); + * const address = account.address(); + */ + address(): Address { + this.assertNotDestroyed(); + return this._address; + } - /** - * Encrypts the account's private key with a password. - * - * @param {string} password Password to encrypt the private key. - * @returns {PrivateKeyCiphertext} The encrypted private key ciphertext - * - * @example - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * const account = new Account(); - * const ciphertext = account.encryptAccount("password"); - * process.env.ciphertext = ciphertext.toString(); - */ - encryptAccount(password: string): PrivateKeyCiphertext { - this.assertNotDestroyed(); - return this._privateKey.toCiphertext(password); - } + /** + * Deep clones the Account via byte serialization of the private key, + * avoiding creation of immutable JS string representations of the private key. + * @returns {Account} A new Account instance with the same private key + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * const account = new Account(); + * const clonedAccount = account.clone(); + */ + clone(): Account { + this.assertNotDestroyed(); + return new Account({ privateKey: this._privateKey }); + } - /** - * Decrypts an encrypted record string into a plaintext record object. - * - * @param {string} ciphertext A string representing the ciphertext of a record. - * @returns {RecordPlaintext} The decrypted record plaintext - * - * @example - * // Import the AleoNetworkClient and Account classes - * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; - * - * // Create a connection to the Aleo network and an account - * const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); - * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); - * - * // Get the record ciphertexts from a transaction. - * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); - * const records = transaction.records(); - * - * // Decrypt any records the account owns. - * const decryptedRecords = []; - * for (const record of records) { - * if (account.decryptRecord(record)) { - * decryptedRecords.push(record); - * } - * } - */ - decryptRecord(ciphertext: string): RecordPlaintext { - this.assertNotDestroyed(); - return this._viewKey.decrypt(ciphertext); - } + /** + * Returns the address of the account in a string representation. + * + * @returns {string} The string representation of the account address + */ + toString(): string { + this.assertNotDestroyed(); + return this.address().to_string(); + } - /** - * Decrypts an array of Record ciphertext strings into an array of record plaintext objects. - * - * @param {string[]} ciphertexts An array of strings representing the ciphertexts of records. - * @returns {RecordPlaintext[]} An array of decrypted record plaintexts - * - * @example - * // Import the AleoNetworkClient and Account classes - * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; - * - * // Create a connection to the Aleo network and an account - * const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); - * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); - * - * // Get the record ciphertexts from a transaction. - * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); - * const records = transaction.records(); - * - * // Decrypt any records the account owns. If the account owns no records, the array will be empty. - * const decryptedRecords = account.decryptRecords(records); - */ - decryptRecords(ciphertexts: string[]): RecordPlaintext[] { - this.assertNotDestroyed(); - return ciphertexts.map((ciphertext) => this._viewKey.decrypt(ciphertext)); - } + /** + * Encrypts the account's private key with a password. + * + * @param {string} password Password to encrypt the private key. + * @returns {PrivateKeyCiphertext} The encrypted private key ciphertext + * + * @example + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * const account = new Account(); + * const ciphertext = account.encryptAccount("password"); + * process.env.ciphertext = ciphertext.toString(); + */ + encryptAccount(password: string): PrivateKeyCiphertext { + this.assertNotDestroyed(); + return this._privateKey.toCiphertext(password); + } - /** - * Generates a record view key from the account owner's view key and the record ciphertext. - * This key can be used to decrypt the record without revealing the account's view key. - * @param {RecordCiphertext | string} recordCiphertext The record ciphertext to generate the view key for - * @returns {Field} The record view key - * - * @example - * // Import the Account class - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * // Create an account object from a previously encrypted ciphertext and password. - * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); - * - * // Generate a record view key from the account's view key and a record ciphertext - * const recordCiphertext = RecordCiphertext.fromString("your_record_ciphertext_here"); - * const recordViewKey = account.generateRecordViewKey(recordCiphertext); - */ - generateRecordViewKey(recordCiphertext: RecordCiphertext | string): Field { - this.assertNotDestroyed(); - if (typeof recordCiphertext === 'string') { - recordCiphertext = RecordCiphertext.fromString(recordCiphertext); + /** + * Decrypts an encrypted record string into a plaintext record object. + * + * @param {string} ciphertext A string representing the ciphertext of a record. + * @returns {RecordPlaintext} The decrypted record plaintext + * + * @example + * // Import the AleoNetworkClient and Account classes + * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; + * + * // Create a connection to the Aleo network and an account + * const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); + * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); + * + * // Get the record ciphertexts from a transaction. + * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); + * const records = transaction.records(); + * + * // Decrypt any records the account owns. + * const decryptedRecords = []; + * for (const record of records) { + * if (account.decryptRecord(record)) { + * decryptedRecords.push(record); + * } + * } + */ + decryptRecord(ciphertext: string): RecordPlaintext { + this.assertNotDestroyed(); + return this._viewKey.decrypt(ciphertext); } - if (!(recordCiphertext.isOwner(this._viewKey))) { - throw new Error("The record ciphertext does not belong to this account"); + + /** + * Decrypts an array of Record ciphertext strings into an array of record plaintext objects. + * + * @param {string[]} ciphertexts An array of strings representing the ciphertexts of records. + * @returns {RecordPlaintext[]} An array of decrypted record plaintexts + * + * @example + * // Import the AleoNetworkClient and Account classes + * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; + * + * // Create a connection to the Aleo network and an account + * const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); + * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); + * + * // Get the record ciphertexts from a transaction. + * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); + * const records = transaction.records(); + * + * // Decrypt any records the account owns. If the account owns no records, the array will be empty. + * const decryptedRecords = account.decryptRecords(records); + */ + decryptRecords(ciphertexts: string[]): RecordPlaintext[] { + this.assertNotDestroyed(); + return ciphertexts.map((ciphertext) => + this._viewKey.decrypt(ciphertext), + ); } - return EncryptionToolkit.generateRecordViewKey(this._viewKey, recordCiphertext); - } - /** - * Generates a transition view key from the account owner's view key and the transition public key. - * This key can be used to decrypt the private inputs and outputs of a the transition without - * revealing the account's view key. - * @param {string | Group} tpk The transition public key - * @returns {Field} The transition view key - * - * @example - * // Import the Account class - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * // Generate a transition view key from the account's view key and a transition public key - * const tpk = Group.fromString("your_transition_public_key_here"); - * - * const transitionViewKey = account.generateTransitionViewKey(tpk); - */ - generateTransitionViewKey(tpk: string | Group): Field { - this.assertNotDestroyed(); - if (typeof tpk === 'string') { - tpk = Group.fromString(tpk); + /** + * Generates a record view key from the account owner's view key and the record ciphertext. + * This key can be used to decrypt the record without revealing the account's view key. + * @param {RecordCiphertext | string} recordCiphertext The record ciphertext to generate the view key for + * @returns {Field} The record view key + * + * @example + * // Import the Account class + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * // Create an account object from a previously encrypted ciphertext and password. + * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); + * + * // Generate a record view key from the account's view key and a record ciphertext + * const recordCiphertext = RecordCiphertext.fromString("your_record_ciphertext_here"); + * const recordViewKey = account.generateRecordViewKey(recordCiphertext); + */ + generateRecordViewKey(recordCiphertext: RecordCiphertext | string): Field { + this.assertNotDestroyed(); + if (typeof recordCiphertext === "string") { + recordCiphertext = RecordCiphertext.fromString(recordCiphertext); + } + if (!recordCiphertext.isOwner(this._viewKey)) { + throw new Error( + "The record ciphertext does not belong to this account", + ); + } + return EncryptionToolkit.generateRecordViewKey( + this._viewKey, + recordCiphertext, + ); } - return EncryptionToolkit.generateTvk(this._viewKey, tpk); - } - /** - * Determines whether the account owns a ciphertext record. - * @param {RecordCiphertext | string} ciphertext The record ciphertext to check ownership of - * @returns {boolean} True if the account owns the record, false otherwise - * - * @example - * // Import the AleoNetworkClient and Account classes - * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; - * - * // Create a connection to the Aleo network and an account - * const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); - * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); - * - * // Get the record ciphertexts from a transaction and check ownership of them. - * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); - * const records = transaction.records(); - * - * // Check if the account owns any of the record ciphertexts present in the transaction. - * const ownedRecords = []; - * for (const record of records) { - * if (account.ownsRecordCiphertext(record)) { - * ownedRecords.push(record); - * } - * } - */ - ownsRecordCiphertext(ciphertext: RecordCiphertext | string): boolean { - this.assertNotDestroyed(); - if (typeof ciphertext === 'string') { - try { - const ciphertextObject = RecordCiphertext.fromString(ciphertext); - return ciphertextObject.isOwner(this._viewKey); - } - catch (e) { - return false; - } + /** + * Generates a transition view key from the account owner's view key and the transition public key. + * This key can be used to decrypt the private inputs and outputs of a the transition without + * revealing the account's view key. + * @param {string | Group} tpk The transition public key + * @returns {Field} The transition view key + * + * @example + * // Import the Account class + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * // Generate a transition view key from the account's view key and a transition public key + * const tpk = Group.fromString("your_transition_public_key_here"); + * + * const transitionViewKey = account.generateTransitionViewKey(tpk); + */ + generateTransitionViewKey(tpk: string | Group): Field { + this.assertNotDestroyed(); + if (typeof tpk === "string") { + tpk = Group.fromString(tpk); + } + return EncryptionToolkit.generateTvk(this._viewKey, tpk); } - else { - return ciphertext.isOwner(this._viewKey); + + /** + * Determines whether the account owns a ciphertext record. + * @param {RecordCiphertext | string} ciphertext The record ciphertext to check ownership of + * @returns {boolean} True if the account owns the record, false otherwise + * + * @example + * // Import the AleoNetworkClient and Account classes + * import { AleoNetworkClient, Account } from "@provablehq/sdk/testnet.js"; + * + * // Create a connection to the Aleo network and an account + * const networkClient = new AleoNetworkClient("https://api.provable.com/v2"); + * const account = Account.fromCiphertext(process.env.ciphertext!, process.env.password!); + * + * // Get the record ciphertexts from a transaction and check ownership of them. + * const transaction = await networkClient.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); + * const records = transaction.records(); + * + * // Check if the account owns any of the record ciphertexts present in the transaction. + * const ownedRecords = []; + * for (const record of records) { + * if (account.ownsRecordCiphertext(record)) { + * ownedRecords.push(record); + * } + * } + */ + ownsRecordCiphertext(ciphertext: RecordCiphertext | string): boolean { + this.assertNotDestroyed(); + if (typeof ciphertext === "string") { + try { + const ciphertextObject = + RecordCiphertext.fromString(ciphertext); + return ciphertextObject.isOwner(this._viewKey); + } catch (e) { + return false; + } + } else { + return ciphertext.isOwner(this._viewKey); + } } - } - /** - * Signs a message with the account's private key. - * Returns a Signature. - * - * @param {Uint8Array} message Message to be signed. - * @returns {Signature} Signature over the message in bytes. - * - * @example - * // Import the Account class - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * // Create a connection to the Aleo network and an account - * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); - * - * // Create an account and a message to sign. - * const account = new Account(); - * const message = Uint8Array.from([104, 101, 108, 108, 111 119, 111, 114, 108, 100]) - * const signature = account.sign(message); - * - * // Verify the signature. - * assert(account.verify(message, signature)); - */ - sign(message: Uint8Array): Signature { - this.assertNotDestroyed(); - return this._privateKey.sign(message); - } + /** + * Signs a message with the account's private key. + * Returns a Signature. + * + * @param {Uint8Array} message Message to be signed. + * @returns {Signature} Signature over the message in bytes. + * + * @example + * // Import the Account class + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * // Create a connection to the Aleo network and an account + * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); + * + * // Create an account and a message to sign. + * const account = new Account(); + * const message = Uint8Array.from([104, 101, 108, 108, 111 119, 111, 114, 108, 100]) + * const signature = account.sign(message); + * + * // Verify the signature. + * assert(account.verify(message, signature)); + */ + sign(message: Uint8Array): Signature { + this.assertNotDestroyed(); + return this._privateKey.sign(message); + } - /** - * Verifies the Signature on a message. - * - * @param {Uint8Array} message Message in bytes to be signed. - * @param {Signature} signature Signature to be verified. - * @returns {boolean} True if the signature is valid, false otherwise. - * - * @example - * // Import the Account class - * import { Account } from "@provablehq/sdk/testnet.js"; - * - * // Create a connection to the Aleo network and an account - * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); - * - * // Sign a message. - * const message = Uint8Array.from([104, 101, 108, 108, 111 119, 111, 114, 108, 100]) - * const signature = account.sign(message); - * - * // Verify the signature. - * assert(account.verify(message, signature)); - */ - verify(message: Uint8Array, signature: Signature): boolean { - this.assertNotDestroyed(); - return this._address.verify(message, signature); - } + /** + * Verifies the Signature on a message. + * + * @param {Uint8Array} message Message in bytes to be signed. + * @param {Signature} signature Signature to be verified. + * @returns {boolean} True if the signature is valid, false otherwise. + * + * @example + * // Import the Account class + * import { Account } from "@provablehq/sdk/testnet.js"; + * + * // Create a connection to the Aleo network and an account + * const account = Account.fromCiphertext(process.env.ciphertext, process.env.password); + * + * // Sign a message. + * const message = Uint8Array.from([104, 101, 108, 108, 111 119, 111, 114, 108, 100]) + * const signature = account.sign(message); + * + * // Verify the signature. + * assert(account.verify(message, signature)); + */ + verify(message: Uint8Array, signature: Signature): boolean { + this.assertNotDestroyed(); + return this._address.verify(message, signature); + } - /** - * Securely destroys the account by zeroizing and freeing all sensitive key material - * from WASM memory. After calling this method, the account object should not be used. - * - * This triggers the Rust-level zeroizing Drop implementation which overwrites private key, - * view key, and compute key bytes with zeros in WASM linear memory before deallocation. - * - * Note: If destroy() is never called, the FinalizationRegistry (set up by wasm-bindgen) - * will eventually trigger cleanup via GC, but the timing is non-deterministic. For security- - * sensitive applications, always call destroy() explicitly when the account is no longer needed. - * - * @example - * const account = new Account(); - * // ... use account ... - * account.destroy(); // Securely cleans up key material - */ - destroy(): void { - if (this._destroyed) return; - this._destroyed = true; + /** + * Securely destroys the account by zeroizing and freeing all sensitive key material + * from WASM memory. After calling this method, the account object should not be used. + * + * This triggers the Rust-level zeroizing Drop implementation which overwrites private key, + * view key, and compute key bytes with zeros in WASM linear memory before deallocation. + * + * Note: If destroy() is never called, the FinalizationRegistry (set up by wasm-bindgen) + * will eventually trigger cleanup via GC, but the timing is non-deterministic. For security- + * sensitive applications, always call destroy() explicitly when the account is no longer needed. + * + * @example + * const account = new Account(); + * // ... use account ... + * account.destroy(); // Securely cleans up key material + */ + destroy(): void { + if (this._destroyed) return; + this._destroyed = true; - // Free sensitive WASM objects (triggers Rust Drop -> zeroization). - // try/catch guards against double-free if FinalizationRegistry already ran. - try { this._privateKey.free(); } catch (_) { /* already freed */ } - try { this._viewKey.free(); } catch (_) { /* already freed */ } - try { this._computeKey.free(); } catch (_) { /* already freed */ } - // Address is public data but free it for completeness. - try { this._address.free(); } catch (_) { /* already freed */ } - } + // Free sensitive WASM objects (triggers Rust Drop -> zeroization). + // try/catch guards against double-free if FinalizationRegistry already ran. + try { + this._privateKey.free(); + } catch (_) { + /* already freed */ + } + try { + this._viewKey.free(); + } catch (_) { + /* already freed */ + } + try { + this._computeKey.free(); + } catch (_) { + /* already freed */ + } + // Address is public data but free it for completeness. + try { + this._address.free(); + } catch (_) { + /* already freed */ + } + } - /** - * Implements the Disposable interface for use with `using` declarations (ES2024+). - * Calls {@link destroy} to securely clean up key material. - * - * @example - * { - * using account = new Account(); - * // ... use account ... - * } // account is automatically destroyed here - */ - [Symbol.dispose](): void { - this.destroy(); - } + /** + * Implements the Disposable interface for use with `using` declarations (ES2024+). + * Calls {@link destroy} to securely clean up key material. + * + * @example + * { + * using account = new Account(); + * // ... use account ... + * } // account is automatically destroyed here + */ + [Symbol.dispose](): void { + this.destroy(); + } } diff --git a/sdk/src/browser.ts b/sdk/src/browser.ts index 45569648c..bfba6dcdf 100644 --- a/sdk/src/browser.ts +++ b/sdk/src/browser.ts @@ -6,12 +6,21 @@ import { BlockJSON, Header, Metadata } from "./models/blockJSON.js"; import { CachedKeyPair, FunctionKeyPair } from "./models/keyPair.js"; import { ConfirmedTransactionJSON } from "./models/confirmed_transaction.js"; import { CryptoBoxPubKey } from "./models/cryptoBoxPubkey.js"; -import { DeploymentJSON, VerifyingKeys } from "./models/deployment/deploymentJSON.js"; +import { + DeploymentJSON, + VerifyingKeys, +} from "./models/deployment/deploymentJSON.js"; import { DeploymentObject } from "./models/deployment/deploymentObject.js"; import { EncryptedProvingRequest } from "./models/encryptedProvingRequest.js"; import { EncryptedRecord } from "./models/record-provider/encryptedRecord.js"; -import { ExecutionJSON, FeeExecutionJSON } from "./models/execution/executionJSON.js"; -import { ExecutionObject, FeeExecutionObject } from "./models/execution/executionObject.js"; +import { + ExecutionJSON, + FeeExecutionJSON, +} from "./models/execution/executionJSON.js"; +import { + ExecutionObject, + FeeExecutionObject, +} from "./models/execution/executionObject.js"; import { FinalizeJSON } from "./models/finalizeJSON.js"; import { FunctionInput } from "./models/functionInput"; import { FunctionObject } from "./models/functionObject.js"; @@ -23,15 +32,29 @@ import { OutputObject } from "./models/output/outputObject.js"; import { OwnedFilter } from "./models/record-scanner/ownedFilter.js"; import { OwnedRecord } from "./models/record-provider/ownedRecord.js"; import { OwnerJSON } from "./models/owner/ownerJSON.js"; -import { PlaintextArray} from "./models/plaintext/array.js"; -import { PlaintextLiteral} from "./models/plaintext/literal.js"; +import { PlaintextArray } from "./models/plaintext/array.js"; +import { PlaintextLiteral } from "./models/plaintext/literal.js"; import { PlaintextObject } from "./models/plaintext/plaintext.js"; -import { PlaintextStruct} from "./models/plaintext/struct.js"; +import { PlaintextStruct } from "./models/plaintext/struct.js"; import { ProvingRequestJSON } from "./models/provingRequest.js"; -import { ProvingResponse, BroadcastResponse, BroadcastResult, ProvingResult, ProvingFailure, ProvingSuccess, ProveApiErrorBody, ProvingRequestError, isProvingResponse, isProveApiErrorBody } from "./models/provingResponse.js"; +import { + ProvingResponse, + BroadcastResponse, + BroadcastResult, + ProvingResult, + ProvingFailure, + ProvingSuccess, + ProveApiErrorBody, + ProvingRequestError, + isProvingResponse, + isProveApiErrorBody, +} from "./models/provingResponse.js"; import { RatificationJSON } from "./models/ratification.js"; import { EncryptedRegistrationRequest } from "./models/record-scanner/encryptedRegistrationRequest.js"; -import { EncryptedRecordsResult, EncryptedRecordsSuccess } from "./models/record-scanner/encryptedRecordsResult.js"; +import { + EncryptedRecordsResult, + EncryptedRecordsSuccess, +} from "./models/record-scanner/encryptedRecordsResult.js"; import { DecryptionNotEnabledError, RecordNotFoundError, @@ -41,20 +64,40 @@ import { UUIDError, ViewKeyNotStoredError, } from "./models/record-scanner/error.js"; -import { OwnedRecordsResult, OwnedRecordsSuccess } from "./models/record-scanner/ownedRecordsResult.js"; +import { + OwnedRecordsResult, + OwnedRecordsSuccess, +} from "./models/record-scanner/ownedRecordsResult.js"; import { OwnedRecordsResponseFilter } from "./models/record-scanner/ownedRecordsResponseFilter.js"; -import { RegisterResult, RegisterSuccess } from "./models/record-scanner/registrationResult.js"; +import { + RegisterResult, + RegisterSuccess, +} from "./models/record-scanner/registrationResult.js"; import { RegistrationRequest } from "./models/record-scanner/registrationRequest.js"; import { RegistrationResponse } from "./models/record-scanner/registrationResponse.js"; -import { RevokeResult, RevokeSuccess, RevokeResponse } from "./models/record-scanner/revokeResult.js"; +import { + RevokeResult, + RevokeSuccess, + RevokeResponse, +} from "./models/record-scanner/revokeResult.js"; import { RecordsFilter } from "./models/record-scanner/recordsFilter.js"; import { RecordsResponseFilter } from "./models/record-scanner/recordsResponseFilter.js"; -import { SerialNumbersResult, SerialNumbersSuccess } from "./models/record-scanner/serialNumbersResult.js"; +import { + SerialNumbersResult, + SerialNumbersSuccess, +} from "./models/record-scanner/serialNumbersResult.js"; import { StatusResponse } from "./models/record-scanner/statusResponse.js"; -import { StatusResult, StatusSuccess } from "./models/record-scanner/statusResult.js"; +import { + StatusResult, + StatusSuccess, +} from "./models/record-scanner/statusResult.js"; import { TagsResult, TagsSuccess } from "./models/record-scanner/tagsResult.js"; import { RecordSearchParams } from "./models/record-provider/recordSearchParams.js"; -import { SolutionsJSON, SolutionJSON, PartialSolutionJSON } from "./models/solution.js"; +import { + SolutionsJSON, + SolutionJSON, + PartialSolutionJSON, +} from "./models/solution.js"; import { TransactionJSON } from "./models/transaction/transactionJSON.js"; import { TransactionObject } from "./models/transaction/transactionObject.js"; import { TransitionJSON } from "./models/transition/transitionJSON.js"; @@ -84,14 +127,18 @@ import { } from "./keys/keystore/interface.js"; import { OfflineKeyProvider, - OfflineSearchParams + OfflineSearchParams, } from "./keys/provider/offline.js"; import { BlockHeightSearch, NetworkRecordProvider, RecordProvider, } from "./record-provider.js"; -import { RecordScanner, RecordScannerJWTData, RecordScannerOptions } from "./record-scanner.js"; +import { + RecordScanner, + RecordScannerJWTData, + RecordScannerOptions, +} from "./record-scanner.js"; import { SealanceMerkleTree } from "./integrations/sealance/merkle-tree.js"; // @TODO: This function is no longer needed, remove it. @@ -99,7 +146,13 @@ async function initializeWasm() { console.warn("initializeWasm is deprecated, you no longer need to use it"); } -export { ProgramManager, ProvingRequestOptions, ExecuteOptions, FeeAuthorizationOptions, AuthorizationOptions } from "./program-manager.js"; +export { + ProgramManager, + ProvingRequestOptions, + ExecuteOptions, + FeeAuthorizationOptions, + AuthorizationOptions, +} from "./program-manager.js"; export { logAndThrow } from "./utils.js"; @@ -288,4 +341,10 @@ export { KeyVerifier as FunctionKeyVerifier, } from "./keys/verifier/interface.js"; -export { encryptAuthorization, encryptProvingRequest, encryptViewKey, encryptRegistrationRequest, zeroizeBytes } from "./security.js"; +export { + encryptAuthorization, + encryptProvingRequest, + encryptViewKey, + encryptRegistrationRequest, + zeroizeBytes, +} from "./security.js"; diff --git a/sdk/src/constants.ts b/sdk/src/constants.ts index b7be652fb..4c0786ea5 100644 --- a/sdk/src/constants.ts +++ b/sdk/src/constants.ts @@ -1,13 +1,13 @@ -import {VerifyingKey, Metadata} from "./wasm.js"; +import { VerifyingKey, Metadata } from "./wasm.js"; export const KEY_STORE = Metadata.baseUrl(); export interface Key { - name: string, - locator: string, - prover: string, - verifier: string, - verifyingKey: () => VerifyingKey, + name: string; + locator: string; + prover: string; + verifier: string; + verifyingKey: () => VerifyingKey; } function convert(metadata: Metadata): Key { @@ -43,13 +43,13 @@ export const CREDITS_PROGRAM_KEYS = { transfer_public_as_signer: convert(Metadata.transfer_public_as_signer()), transfer_public_to_private: convert(Metadata.transfer_public_to_private()), unbond_public: convert(Metadata.unbond_public()), - getKey: function(key: string): Key { + getKey: function (key: string): Key { if (this.hasOwnProperty(key)) { return (this as any)[key] as Key; } else { throw new Error(`Key "${key}" not found.`); } - } + }, }; export const PRIVATE_TRANSFER_TYPES = new Set([ @@ -117,6 +117,6 @@ export const RECORD_DOMAIN = "RecordScannerV0"; /** * Zero address on Aleo blockchain that corresponds to field element 0. Used as padding in Merkle trees and as a sentinel value. */ -export const ZERO_ADDRESS = "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc"; +export const ZERO_ADDRESS = + "aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc"; export const FIVE_MINUTES = 5 * 60 * 1000; // 5 minutes in milliseconds - diff --git a/sdk/src/integrations/sealance/merkle-tree.ts b/sdk/src/integrations/sealance/merkle-tree.ts index 94339968e..d8100d61f 100644 --- a/sdk/src/integrations/sealance/merkle-tree.ts +++ b/sdk/src/integrations/sealance/merkle-tree.ts @@ -26,26 +26,26 @@ import { ZERO_ADDRESS } from "../../constants.js"; */ class SealanceMerkleTree { private static hasher = new Poseidon4(); - + /** - * Converts an Aleo blockchain address to a field element. - * - * This function decodes a bech32m-encoded Aleo address and converts it to a field element - * represented as a BigInt. The address format follows the Aleo protocol specification, - * starting with the prefix "aleo1" followed by encoded data. - * - * @param address - The Aleo blockchain address (e.g., "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px") - * @returns A BigInt representing the field element. - * @throws Error if the address is invalid or cannot be decoded. - * - * @example - * ```typescript - * const sealance = new SealanceMerkleTree(); - * const address = "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"; - * const fieldValue = sealance.convertAddressToField(address); - * console.log(fieldValue); // 123456789...n - * ``` - */ + * Converts an Aleo blockchain address to a field element. + * + * This function decodes a bech32m-encoded Aleo address and converts it to a field element + * represented as a BigInt. The address format follows the Aleo protocol specification, + * starting with the prefix "aleo1" followed by encoded data. + * + * @param address - The Aleo blockchain address (e.g., "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px") + * @returns A BigInt representing the field element. + * @throws Error if the address is invalid or cannot be decoded. + * + * @example + * ```typescript + * const sealance = new SealanceMerkleTree(); + * const address = "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"; + * const fieldValue = sealance.convertAddressToField(address); + * console.log(fieldValue); // 123456789...n + * ``` + */ convertAddressToField(address: string): bigint { const { words } = bech32m.decode(address as `${string}1${string}`); const bytes = bech32m.fromWords(words); @@ -60,38 +60,44 @@ class SealanceMerkleTree { } /** - * Hashes two elements using Poseidon4 hash function - * @param prefix - Prefix for the hash (e.g., "0field" for nodes, "1field" for leaves) - * @param el1 - First element to hash - * @param el2 - Second element to hash - * @returns The hash result as a Field - * @throws {Error} If inputs are empty or invalid - */ + * Hashes two elements using Poseidon4 hash function + * @param prefix - Prefix for the hash (e.g., "0field" for nodes, "1field" for leaves) + * @param el1 - First element to hash + * @param el2 - Second element to hash + * @returns The hash result as a Field + * @throws {Error} If inputs are empty or invalid + */ hashTwoElements(prefix: string, el1: string, el2: string): Field { if (!el1 || !el2) { throw new Error("Invalid inputs: elements cannot be empty"); } - const fields = [Field.fromString(prefix), Field.fromString(el1), Field.fromString(el2)]; - const arrayPlaintext = Plaintext.fromString(`[${fields.map(f => f.toString()).join(",")}]`); + const fields = [ + Field.fromString(prefix), + Field.fromString(el1), + Field.fromString(el2), + ]; + const arrayPlaintext = Plaintext.fromString( + `[${fields.map((f) => f.toString()).join(",")}]`, + ); return SealanceMerkleTree.hasher.hash(arrayPlaintext.toFields()); } /** - * Builds a Merkle tree from given leaves. The tree is built bottom-up, hashing pairs of elements at each level. - * - * @param leaves - Array of leaf elements (must have even number of elements). - * @returns Array representing the complete Merkle tree as BigInts. - * @throws {Error} If leaves array is empty or has odd number of elements. - * - * @example - * ```typescript - * const sealance = new SealanceMerkleTree(); - * const leaves = ["0field", "1field", "2field", "3field"]; - * const tree = sealance.buildTree(leaves); - * const root = tree[tree.length - 1]; // Get the Merkle root - * ``` - */ + * Builds a Merkle tree from given leaves. The tree is built bottom-up, hashing pairs of elements at each level. + * + * @param leaves - Array of leaf elements (must have even number of elements). + * @returns Array representing the complete Merkle tree as BigInts. + * @throws {Error} If leaves array is empty or has odd number of elements. + * + * @example + * ```typescript + * const sealance = new SealanceMerkleTree(); + * const leaves = ["0field", "1field", "2field", "3field"]; + * const tree = sealance.buildTree(leaves); + * const root = tree[tree.length - 1]; // Get the Merkle root + * ``` + */ buildTree(leaves: string[]): bigint[] { if (leaves.length === 0) { throw new Error("Leaves array cannot be empty"); @@ -108,7 +114,8 @@ class SealanceMerkleTree { for (let i = 0; i < levelSize; i += 2) { const left = currentLevel[i]; const right = currentLevel[i + 1]; - const prefix = leaves.length === levelSize ? "1field" : "0field"; + const prefix = + leaves.length === levelSize ? "1field" : "0field"; const hash = this.hashTwoElements(prefix, left, right); nextLevel.push(hash.toString()); } @@ -116,27 +123,29 @@ class SealanceMerkleTree { currentLevel = nextLevel; levelSize = currentLevel.length; } - return tree.map(element => BigInt(element.slice(0, element.length - "field".length))); + return tree.map((element) => + BigInt(element.slice(0, element.length - "field".length)), + ); } - /** - * Converts an array of decimal string representations of U256 numbers to an array of BigInts. - * - * @param tree - Array of decimal string representations of U256 numbers. - * @returns Array of BigInts. - * - * @example - * ```typescript - * const treeStrings = ["0","4328470178059738374782465505490977516512210899136548187530607227309847251692","1741259420362056497457198439964202806733137875365061915996980524089960046336"]; - * const sealance = new SealanceMerkleTree(); - * const treeBigInts = sealance.convertTreeToBigInt(treeStrings); - * console.log(treeBigInts); // [ - * 0, - * 4328470178059738374782465505490977516512210899136548187530607227309847251692, - * 1741259420362056497457198439964202806733137875365061915996980524089960046336 - * ] - * ``` - */ + /** + * Converts an array of decimal string representations of U256 numbers to an array of BigInts. + * + * @param tree - Array of decimal string representations of U256 numbers. + * @returns Array of BigInts. + * + * @example + * ```typescript + * const treeStrings = ["0","4328470178059738374782465505490977516512210899136548187530607227309847251692","1741259420362056497457198439964202806733137875365061915996980524089960046336"]; + * const sealance = new SealanceMerkleTree(); + * const treeBigInts = sealance.convertTreeToBigInt(treeStrings); + * console.log(treeBigInts); // [ + * 0, + * 4328470178059738374782465505490977516512210899136548187530607227309847251692, + * 1741259420362056497457198439964202806733137875365061915996980524089960046336 + * ] + * ``` + */ convertTreeToBigInt(tree: string[]): bigint[] { return tree.map((element) => { try { @@ -149,91 +158,100 @@ class SealanceMerkleTree { } /** - * Converts Aleo addresses to field elements, sorts them, pads with zero fields, and returns an array. This prepares addresses for Merkle tree construction. - * - * @param addresses - Array of Aleo addresses. - * @param maxTreeDepth - Maximum depth of the Merkle tree (default: 15). - * @returns Array of field elements ready for Merkle tree construction. - * @throws {Error} If the number of addresses exceeds the maximum capacity. - * - * @example - * ```typescript - * const addresses = [ - * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * ]; - * const sealance = new SealanceMerkleTree(); - * const leaves = sealance.generateLeaves(addresses, 15); - * console.log(leaves); // [ - * "0field", - * "1295133970529764960316948294624974168921228814652993007266766481909235735940field", - * "1295133970529764960316948294624974168921228814652993007266766481909235735940field", - * "3501665755452795161867664882580888971213780722176652848275908626939553697821field" - * ] - * ``` - */ + * Converts Aleo addresses to field elements, sorts them, pads with zero fields, and returns an array. This prepares addresses for Merkle tree construction. + * + * @param addresses - Array of Aleo addresses. + * @param maxTreeDepth - Maximum depth of the Merkle tree (default: 15). + * @returns Array of field elements ready for Merkle tree construction. + * @throws {Error} If the number of addresses exceeds the maximum capacity. + * + * @example + * ```typescript + * const addresses = [ + * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * ]; + * const sealance = new SealanceMerkleTree(); + * const leaves = sealance.generateLeaves(addresses, 15); + * console.log(leaves); // [ + * "0field", + * "1295133970529764960316948294624974168921228814652993007266766481909235735940field", + * "1295133970529764960316948294624974168921228814652993007266766481909235735940field", + * "3501665755452795161867664882580888971213780722176652848275908626939553697821field" + * ] + * ``` + */ generateLeaves(addresses: string[], maxTreeDepth: number = 15): string[] { const maxNumLeaves = Math.floor(2 ** (maxTreeDepth - 1)); // Filter out zero addresses - addresses = addresses.filter(addr => addr !== ZERO_ADDRESS); + addresses = addresses.filter((addr) => addr !== ZERO_ADDRESS); let numLeaves = 0; - if (addresses.length === 0 || addresses.length === 1) { - numLeaves = 2; - } else { - numLeaves = Math.pow(2, Math.ceil(Math.log2(addresses.length))); - } + if (addresses.length === 0 || addresses.length === 1) { + numLeaves = 2; + } else { + numLeaves = Math.pow(2, Math.ceil(Math.log2(addresses.length))); + } if (addresses.length > maxNumLeaves) { - throw new Error(`Leaves limit exceeded. Max: ${maxNumLeaves}, provided: ${addresses.length}`); + throw new Error( + `Leaves limit exceeded. Max: ${maxNumLeaves}, provided: ${addresses.length}`, + ); } // Convert addresses to fields - const addressFields = addresses.map(addr => ({ + const addressFields = addresses.map((addr) => ({ address: addr, field: this.convertAddressToField(addr), })); // Sort by field value - const sortedFields = addressFields.sort((a, b) => (a.field < b.field ? -1 : 1)).map(item => item.field); + const sortedFields = addressFields + .sort((a, b) => (a.field < b.field ? -1 : 1)) + .map((item) => item.field); // Convert to field strings - const sortedFieldElements = sortedFields.map(field => field.toString() + "field"); + const sortedFieldElements = sortedFields.map( + (field) => field.toString() + "field", + ); // Pad with zeros to reach power of 2 - const fullTree = Array(Math.max(numLeaves - sortedFieldElements.length, 0)).fill("0field"); + const fullTree = Array( + Math.max(numLeaves - sortedFieldElements.length, 0), + ).fill("0field"); return fullTree.concat(sortedFieldElements); } - /** - * Finds the leaf indices for non-inclusion proof of an address and returns the indices of the two adjacent leaves that surround the target address. - * - * @param merkleTree - The complete Merkle tree as array of BigInts. - * @param address - The Aleo address for which to find indices. - * @returns Tuple of [leftLeafIndex, rightLeafIndex]. - * - * @example - * ```typescript - * const addresses = [ - * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * ]; - * const sealance = new SealanceMerkleTree(); - * const leaves = sealance.generateLeaves(addresses); - * const tree = sealance.buildTree(leaves); - * const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, "aleo1..."); - * ``` - */ + * Finds the leaf indices for non-inclusion proof of an address and returns the indices of the two adjacent leaves that surround the target address. + * + * @param merkleTree - The complete Merkle tree as array of BigInts. + * @param address - The Aleo address for which to find indices. + * @returns Tuple of [leftLeafIndex, rightLeafIndex]. + * + * @example + * ```typescript + * const addresses = [ + * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * ]; + * const sealance = new SealanceMerkleTree(); + * const leaves = sealance.generateLeaves(addresses); + * const tree = sealance.buildTree(leaves); + * const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, "aleo1..."); + * ``` + */ getLeafIndices(merkleTree: bigint[], address: string): [number, number] { const num_leaves = Math.floor((merkleTree.length + 1) / 2); const addressBigInt = this.convertAddressToField(address); const leaves = merkleTree.slice(0, num_leaves); - let rightLeafIndex = leaves.findIndex((leaf: bigint) => addressBigInt <= leaf); + let rightLeafIndex = leaves.findIndex( + (leaf: bigint) => addressBigInt <= leaf, + ); let leftLeafIndex = rightLeafIndex - 1; if (rightLeafIndex === -1) { rightLeafIndex = leaves.length - 1; @@ -246,28 +264,28 @@ class SealanceMerkleTree { } /** - * Generates the sibling path (Merkle proof) for a given leaf index - * - * @param tree - The complete Merkle tree. - * @param leafIndex - Index of the leaf for which to generate the proof. - * @param depth - Maximum depth of the tree. - * @returns Object containing siblings array and leaf_index. - * - * @example - * ```typescript - * const addresses = [ - * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * ]; - * const sealance = new SealanceMerkleTree(); - * const leaves = sealance.generateLeaves(addresses); - * const tree = sealance.buildTree(leaves); - * const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, "aleo1..."); - * const proof = sealance.getSiblingPath(tree, leftIdx, 15); - * // proof = { siblings: [0n, 1n, ...], leaf_index: leftIdx } - * ``` - */ + * Generates the sibling path (Merkle proof) for a given leaf index + * + * @param tree - The complete Merkle tree. + * @param leafIndex - Index of the leaf for which to generate the proof. + * @param depth - Maximum depth of the tree. + * @returns Object containing siblings array and leaf_index. + * + * @example + * ```typescript + * const addresses = [ + * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * ]; + * const sealance = new SealanceMerkleTree(); + * const leaves = sealance.generateLeaves(addresses); + * const tree = sealance.buildTree(leaves); + * const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, "aleo1..."); + * const proof = sealance.getSiblingPath(tree, leftIdx, 15); + * // proof = { siblings: [0n, 1n, ...], leaf_index: leftIdx } + * ``` + */ getSiblingPath( tree: bigint[], leafIndex: number, @@ -298,36 +316,42 @@ class SealanceMerkleTree { } /** - * Generates a formatted exclusion proof suitable for Aleo transactions. - * - * @param proof - An array of two {sibling path, leafindex} objects. - * @returns String representation of the exclusion proof. - * - * @example - * ```typescript - * const addresses = [ - * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - * ]; - * const sealance = new SealanceMerkleTree(); - * const leaves = sealance.generateLeaves(addresses); - * const tree = sealance.buildTree(leaves); - * const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, "aleo1..."); - * const proof1 = getSiblingPath(tree, leftIdx, 15); - * const proof2 = getSiblingPath(tree, rightIdx, 15); - * const formattedProof = formatMerkleProof([proof1, proof2]); - * // formattedProof = "[{ siblings: [0field, 1field, ...], leaf_index: 0u32 }, { siblings: [0field, 2field, ...], leaf_index: 1u32 }]" - * ``` - */ - formatMerkleProof(proof: { siblings: bigint[]; leaf_index: number }[]): string { - const formatted = proof.map(item => { - const siblings = item.siblings.map(s => `${s}field`).join(", "); - return `{siblings: [${siblings}], leaf_index: ${item.leaf_index}u32}`; - }).join(", "); - + * Generates a formatted exclusion proof suitable for Aleo transactions. + * + * @param proof - An array of two {sibling path, leafindex} objects. + * @returns String representation of the exclusion proof. + * + * @example + * ```typescript + * const addresses = [ + * "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + * ]; + * const sealance = new SealanceMerkleTree(); + * const leaves = sealance.generateLeaves(addresses); + * const tree = sealance.buildTree(leaves); + * const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, "aleo1..."); + * const proof1 = getSiblingPath(tree, leftIdx, 15); + * const proof2 = getSiblingPath(tree, rightIdx, 15); + * const formattedProof = formatMerkleProof([proof1, proof2]); + * // formattedProof = "[{ siblings: [0field, 1field, ...], leaf_index: 0u32 }, { siblings: [0field, 2field, ...], leaf_index: 1u32 }]" + * ``` + */ + formatMerkleProof( + proof: { siblings: bigint[]; leaf_index: number }[], + ): string { + const formatted = proof + .map((item) => { + const siblings = item.siblings + .map((s) => `${s}field`) + .join(", "); + return `{siblings: [${siblings}], leaf_index: ${item.leaf_index}u32}`; + }) + .join(", "); + return `[${formatted}]`; } } -export { SealanceMerkleTree }; \ No newline at end of file +export { SealanceMerkleTree }; diff --git a/sdk/src/keys/keystore/error.ts b/sdk/src/keys/keystore/error.ts index 4a64a7dc1..79a9233f4 100644 --- a/sdk/src/keys/keystore/error.ts +++ b/sdk/src/keys/keystore/error.ts @@ -24,7 +24,7 @@ export class InvalidLocatorError extends Error { constructor( message: string, public readonly locator: string, - public readonly reason: InvalidLocatorReason + public readonly reason: InvalidLocatorReason, ) { super(message); this.name = "InvalidLocatorError"; diff --git a/sdk/src/keys/keystore/file.ts b/sdk/src/keys/keystore/file.ts index e56f3fb3a..7add22c0c 100644 --- a/sdk/src/keys/keystore/file.ts +++ b/sdk/src/keys/keystore/file.ts @@ -23,7 +23,10 @@ export class LocalFileKeyStore implements KeyStore { */ constructor(directory?: string) { this.directory = directory ?? path.join(process.cwd(), ".aleo"); - if (directory !== undefined && path.basename(this.directory) !== ".aleo") { + if ( + directory !== undefined && + path.basename(this.directory) !== ".aleo" + ) { this.directory = path.join(this.directory, ".aleo"); } fsSync.mkdirSync(this.directory, { recursive: true }); @@ -42,7 +45,7 @@ export class LocalFileKeyStore implements KeyStore { throw new InvalidLocatorError( `Invalid locator: reserved or empty name "${locator}"`, locator, - "reserved_name" + "reserved_name", ); } @@ -51,16 +54,20 @@ export class LocalFileKeyStore implements KeyStore { throw new InvalidLocatorError( "Invalid locator: path traversal detected", locator, - "path_traversal" + "path_traversal", ); } // Block path separators and null byte - if (locator.includes("/") || locator.includes("\\") || locator.includes("\0")) { + if ( + locator.includes("/") || + locator.includes("\\") || + locator.includes("\0") + ) { throw new InvalidLocatorError( "Invalid locator: path separator or null byte not allowed", locator, - "path_separator" + "path_separator", ); } } @@ -136,7 +143,7 @@ export class LocalFileKeyStore implements KeyStore { throw err; } } - + /** * Atomically writes data to a file, ensuring the parent directories exist. * @@ -154,13 +161,16 @@ export class LocalFileKeyStore implements KeyStore { await fs.mkdir(dir, { recursive: true }); const tempPath = path.join( dir, - `.${path.basename(filepath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp` + `.${path.basename(filepath)}.${process.pid}.${Date.now()}.${Math.random().toString(16).slice(2)}.tmp`, ); await fs.writeFile(tempPath, data); try { await fs.rename(tempPath, filepath); } catch (err: unknown) { - const code = err && typeof err === "object" && "code" in err ? (err as NodeJS.ErrnoException).code : undefined; + const code = + err && typeof err === "object" && "code" in err + ? (err as NodeJS.ErrnoException).code + : undefined; // Windows often throws EEXIST when target exists; EPERM/EACCES happen with locks/AV. if (code === "EEXIST" || code === "EPERM" || code === "EACCES") { await fs.unlink(filepath).catch(() => {}); @@ -191,7 +201,10 @@ export class LocalFileKeyStore implements KeyStore { try { await fs.rm(dir, { recursive: true, force: true }); } catch (err: unknown) { - const code = err && typeof err === "object" && "code" in err ? (err as NodeJS.ErrnoException).code : undefined; + const code = + err && typeof err === "object" && "code" in err + ? (err as NodeJS.ErrnoException).code + : undefined; if (code === "ENOENT") { return; } @@ -221,7 +234,9 @@ export class LocalFileKeyStore implements KeyStore { async getKeyBytes(locator: KeyLocator): Promise { this.validateLocator(locator.locator); // Attempt to read key bytes from storage (under this.directory). - const keyBytes = await this.readFileOptional(path.join(this.directory, locator.locator)); + const keyBytes = await this.readFileOptional( + path.join(this.directory, locator.locator), + ); // If no key bytes were found, return null. if (!keyBytes) return null; @@ -351,8 +366,14 @@ export class LocalFileKeyStore implements KeyStore { ]); // Write the proving and verifying key bytes and their metadata to storage (under this.directory). - await this.writeFileAtomic(path.join(this.directory, proverLocator.locator), provingKeyBytes); - await this.writeFileAtomic(path.join(this.directory, verifierLocator.locator), verifyingKeyBytes); + await this.writeFileAtomic( + path.join(this.directory, proverLocator.locator), + provingKeyBytes, + ); + await this.writeFileAtomic( + path.join(this.directory, verifierLocator.locator), + verifyingKeyBytes, + ); await this.writeKeyMetadata(proverLocator.locator, proverFingerPrint); await this.writeKeyMetadata( verifierLocator.locator, @@ -372,7 +393,10 @@ export class LocalFileKeyStore implements KeyStore { * const keys = await generateKeys(); * await setKeyBytes(keys.provingKey.toBytes(), { locator: 'transfer_private.prover' }); */ - async setKeyBytes(keyBytes: Uint8Array, locator: KeyLocator): Promise { + async setKeyBytes( + keyBytes: Uint8Array, + locator: KeyLocator, + ): Promise { this.validateLocator(locator.locator); // Compute the key metadata including fingerprint const computedMetadata = await this.keyVerifier.computeKeyMetadata({ @@ -382,7 +406,10 @@ export class LocalFileKeyStore implements KeyStore { }); // Write the key bytes and metadata atomically (key file under this.directory). - await this.writeFileAtomic(path.join(this.directory, locator.locator), keyBytes); + await this.writeFileAtomic( + path.join(this.directory, locator.locator), + keyBytes, + ); await this.writeKeyMetadata(locator.locator, computedMetadata); } @@ -456,4 +483,4 @@ export class LocalFileKeyStore implements KeyStore { async clear(): Promise { await this.clearDirectory(this.directory); } -} \ No newline at end of file +} diff --git a/sdk/src/keys/keystore/interface.ts b/sdk/src/keys/keystore/interface.ts index 4c175f68f..d47b976be 100644 --- a/sdk/src/keys/keystore/interface.ts +++ b/sdk/src/keys/keystore/interface.ts @@ -49,7 +49,11 @@ export interface KeyStore { * @param {KeyLocator} verifierLocator The unique locator for the desired verifying key. * @param {FunctionKeyPair} keys The proving and verifying keys. */ - setKeys(proverLocator: KeyLocator, verifierLocator: KeyLocator, keys: FunctionKeyPair): Promise; + setKeys( + proverLocator: KeyLocator, + verifierLocator: KeyLocator, + keys: FunctionKeyPair, + ): Promise; /** * Store a raw proving or verifying key in storage along with its fingerprint metadata for future verification. @@ -70,9 +74,7 @@ export interface KeyStore { * @param {string} locator The unique locator for the key. * @returns {Promise} The stored fingerprint for that locator, or null if none exists. */ - getKeyMetadata( - locator: string - ): Promise; + getKeyMetadata(locator: string): Promise; /** * Determines if a given key exists or not. diff --git a/sdk/src/keys/provider/interface.ts b/sdk/src/keys/provider/interface.ts index 1d7b02ef9..853462424 100644 --- a/sdk/src/keys/provider/interface.ts +++ b/sdk/src/keys/provider/interface.ts @@ -182,4 +182,4 @@ interface FunctionKeyProvider { unBondPublicKeys(): Promise; } -export { FunctionKeyProvider, KeySearchParams } +export { FunctionKeyProvider, KeySearchParams }; diff --git a/sdk/src/keys/provider/memory.ts b/sdk/src/keys/provider/memory.ts index ee54378b4..7e84bfd73 100644 --- a/sdk/src/keys/provider/memory.ts +++ b/sdk/src/keys/provider/memory.ts @@ -9,17 +9,11 @@ import { PUBLIC_TRANSFER_AS_SIGNER, } from "../../constants.js"; -import { - CachedKeyPair, - FunctionKeyPair -} from "../../models/keyPair.js"; +import { CachedKeyPair, FunctionKeyPair } from "../../models/keyPair.js"; import { FunctionKeyProvider, KeySearchParams } from "./interface"; -import { - ProvingKey, - VerifyingKey, -} from "../../wasm.js"; +import { ProvingKey, VerifyingKey } from "../../wasm.js"; import { get } from "../../utils.js"; import { KeyStore } from "../keystore/interface.js"; @@ -48,7 +42,12 @@ class AleoKeyProviderParams implements KeySearchParams { * * @param { AleoKeyProviderInitParams } params - Optional search parameters */ - constructor(params: {proverUri?: string, verifierUri?: string, cacheKey?: string, name?: string}) { + constructor(params: { + proverUri?: string; + verifierUri?: string; + cacheKey?: string; + name?: string; + }) { this.proverUri = params.proverUri; this.verifierUri = params.verifierUri; this.cacheKey = params.cacheKey; @@ -56,7 +55,6 @@ class AleoKeyProviderParams implements KeySearchParams { } } - /** * AleoKeyProvider class. Implements the FunctionKeyProvider interface. Enables the retrieval of Aleo program proving and * verifying keys for the credits.aleo program over HTTP from official Aleo sources and storing and retrieving function @@ -546,4 +544,4 @@ class AleoKeyProvider implements FunctionKeyProvider { } } -export { AleoKeyProvider, AleoKeyProviderInitParams, AleoKeyProviderParams } \ No newline at end of file +export { AleoKeyProvider, AleoKeyProviderInitParams, AleoKeyProviderParams }; diff --git a/sdk/src/keys/provider/offline.ts b/sdk/src/keys/provider/offline.ts index 744ee4a63..f71fd4e62 100644 --- a/sdk/src/keys/provider/offline.ts +++ b/sdk/src/keys/provider/offline.ts @@ -1,17 +1,8 @@ -import { - FunctionKeyProvider, - KeySearchParams, -} from "./interface.js"; +import { FunctionKeyProvider, KeySearchParams } from "./interface.js"; -import { - CachedKeyPair, - FunctionKeyPair, -} from "../../models/keyPair.js" +import { CachedKeyPair, FunctionKeyPair } from "../../models/keyPair.js"; -import { - ProvingKey, - VerifyingKey, -} from "../../wasm.js"; +import { ProvingKey, VerifyingKey } from "../../wasm.js"; import { CREDITS_PROGRAM_KEYS, @@ -55,42 +46,60 @@ class OfflineSearchParams implements KeySearchParams { * Create a new OfflineSearchParams instance for the bond_public function of the credits.aleo program. */ static bondPublicKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.bond_public.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.bond_public.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the bond_validator function of the credits.aleo program. */ static bondValidatorKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.bond_validator.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.bond_validator.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the claim_unbond_public function of the credits.aleo program. */ static claimUnbondPublicKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.claim_unbond_public.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.claim_unbond_public.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the fee_private function of the credits.aleo program. */ static feePrivateKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.fee_private.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.fee_private.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the fee_public function of the credits.aleo program. */ static feePublicKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.fee_public.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.fee_public.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the inclusion prover function. */ static inclusionKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.inclusion.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.inclusion.locator, + true, + ); } /** @@ -104,56 +113,80 @@ class OfflineSearchParams implements KeySearchParams { * Create a new OfflineSearchParams instance for the set_validator_state function of the credits.aleo program. */ static setValidatorStateKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.set_validator_state.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.set_validator_state.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the split function of the credits.aleo program. */ static splitKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.split.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.split.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the transfer_private function of the credits.aleo program. */ static transferPrivateKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_private.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.transfer_private.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the transfer_private_to_public function of the credits.aleo program. */ static transferPrivateToPublicKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the transfer_public function of the credits.aleo program. */ static transferPublicKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_public.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.transfer_public.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the transfer_public_as_signer function of the credits.aleo program. */ static transferPublicAsSignerKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_public_as_signer.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.transfer_public_as_signer.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the transfer_public_to_private function of the credits.aleo program. */ static transferPublicToPrivateKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator, + true, + ); } /** * Create a new OfflineSearchParams instance for the unbond_public function of the credits.aleo program. */ static unbondPublicKeyParams(): OfflineSearchParams { - return new OfflineSearchParams(CREDITS_PROGRAM_KEYS.unbond_public.locator, true); + return new OfflineSearchParams( + CREDITS_PROGRAM_KEYS.unbond_public.locator, + true, + ); } } @@ -227,7 +260,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ bondPublicKeys(): Promise { return this.functionKeys(OfflineSearchParams.bondPublicKeyParams()); - }; + } /** * Get bond_validator function keys from the credits.aleo program. The keys must be cached prior to calling this @@ -237,8 +270,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ bondValidatorKeys(): Promise { return this.functionKeys(OfflineSearchParams.bondValidatorKeyParams()); - }; - + } /** * Cache a set of keys. This will overwrite any existing keys with the same keyId. The user can check if a keyId @@ -250,7 +282,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { cacheKeys(keyId: string, keys: FunctionKeyPair): void { const [provingKey, verifyingKey] = keys; this.cache.set(keyId, [provingKey.toBytes(), verifyingKey.toBytes()]); - }; + } /** * Get unbond_public function keys from the credits.aleo program. The keys must be cached prior to calling this @@ -259,8 +291,10 @@ class OfflineKeyProvider implements FunctionKeyProvider { * @returns {Promise} Proving and verifying keys for the unbond_public function */ claimUnbondPublicKeys(): Promise { - return this.functionKeys(OfflineSearchParams.claimUnbondPublicKeyParams()); - }; + return this.functionKeys( + OfflineSearchParams.claimUnbondPublicKeyParams(), + ); + } /** * Get arbitrary function key from the offline key provider cache. @@ -289,18 +323,33 @@ class OfflineKeyProvider implements FunctionKeyProvider { functionKeys(params?: KeySearchParams): Promise { return new Promise((resolve, reject) => { if (params === undefined) { - reject(new Error("No search parameters provided, cannot retrieve keys")); + reject( + new Error( + "No search parameters provided, cannot retrieve keys", + ), + ); } else { const keyId = params.cacheKey; const verifyCreditsKeys = params.verifyCreditsKeys; if (this.cache.has(keyId)) { - const [provingKeyBytes, verifyingKeyBytes] = this.cache.get(keyId) as CachedKeyPair; + const [provingKeyBytes, verifyingKeyBytes] = this.cache.get( + keyId, + ) as CachedKeyPair; const provingKey = ProvingKey.fromBytes(provingKeyBytes); - const verifyingKey = VerifyingKey.fromBytes(verifyingKeyBytes); + const verifyingKey = + VerifyingKey.fromBytes(verifyingKeyBytes); if (verifyCreditsKeys) { - const keysMatchExpected = this.verifyCreditsKeys(keyId, provingKey, verifyingKey) + const keysMatchExpected = this.verifyCreditsKeys( + keyId, + provingKey, + verifyingKey, + ); if (!keysMatchExpected) { - reject (new Error(`Cached keys do not match expected keys for ${keyId}`)); + reject( + new Error( + `Cached keys do not match expected keys for ${keyId}`, + ), + ); } } resolve([provingKey, verifyingKey]); @@ -309,41 +358,82 @@ class OfflineKeyProvider implements FunctionKeyProvider { } } }); - }; + } /** * Determines if the keys for a given credits function match the expected keys. * * @returns {boolean} Whether the keys match the expected keys */ - verifyCreditsKeys(locator: string, provingKey: ProvingKey, verifyingKey: VerifyingKey): boolean { + verifyCreditsKeys( + locator: string, + provingKey: ProvingKey, + verifyingKey: VerifyingKey, + ): boolean { switch (locator) { case CREDITS_PROGRAM_KEYS.bond_public.locator: - return provingKey.isBondPublicProver() && verifyingKey.isBondPublicVerifier(); + return ( + provingKey.isBondPublicProver() && + verifyingKey.isBondPublicVerifier() + ); case CREDITS_PROGRAM_KEYS.claim_unbond_public.locator: - return provingKey.isClaimUnbondPublicProver() && verifyingKey.isClaimUnbondPublicVerifier(); + return ( + provingKey.isClaimUnbondPublicProver() && + verifyingKey.isClaimUnbondPublicVerifier() + ); case CREDITS_PROGRAM_KEYS.fee_private.locator: - return provingKey.isFeePrivateProver() && verifyingKey.isFeePrivateVerifier(); + return ( + provingKey.isFeePrivateProver() && + verifyingKey.isFeePrivateVerifier() + ); case CREDITS_PROGRAM_KEYS.fee_public.locator: - return provingKey.isFeePublicProver() && verifyingKey.isFeePublicVerifier(); + return ( + provingKey.isFeePublicProver() && + verifyingKey.isFeePublicVerifier() + ); case CREDITS_PROGRAM_KEYS.inclusion.locator: - return provingKey.isInclusionProver() && verifyingKey.isInclusionVerifier(); + return ( + provingKey.isInclusionProver() && + verifyingKey.isInclusionVerifier() + ); case CREDITS_PROGRAM_KEYS.join.locator: - return provingKey.isJoinProver() && verifyingKey.isJoinVerifier(); + return ( + provingKey.isJoinProver() && verifyingKey.isJoinVerifier() + ); case CREDITS_PROGRAM_KEYS.set_validator_state.locator: - return provingKey.isSetValidatorStateProver() && verifyingKey.isSetValidatorStateVerifier(); + return ( + provingKey.isSetValidatorStateProver() && + verifyingKey.isSetValidatorStateVerifier() + ); case CREDITS_PROGRAM_KEYS.split.locator: - return provingKey.isSplitProver() && verifyingKey.isSplitVerifier(); + return ( + provingKey.isSplitProver() && verifyingKey.isSplitVerifier() + ); case CREDITS_PROGRAM_KEYS.transfer_private.locator: - return provingKey.isTransferPrivateProver() && verifyingKey.isTransferPrivateVerifier(); + return ( + provingKey.isTransferPrivateProver() && + verifyingKey.isTransferPrivateVerifier() + ); case CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator: - return provingKey.isTransferPrivateToPublicProver() && verifyingKey.isTransferPrivateToPublicVerifier(); + return ( + provingKey.isTransferPrivateToPublicProver() && + verifyingKey.isTransferPrivateToPublicVerifier() + ); case CREDITS_PROGRAM_KEYS.transfer_public.locator: - return provingKey.isTransferPublicProver() && verifyingKey.isTransferPublicVerifier(); + return ( + provingKey.isTransferPublicProver() && + verifyingKey.isTransferPublicVerifier() + ); case CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator: - return provingKey.isTransferPublicToPrivateProver() && verifyingKey.isTransferPublicToPrivateVerifier(); + return ( + provingKey.isTransferPublicToPrivateProver() && + verifyingKey.isTransferPublicToPrivateVerifier() + ); case CREDITS_PROGRAM_KEYS.unbond_public.locator: - return provingKey.isUnbondPublicProver() && verifyingKey.isUnbondPublicVerifier(); + return ( + provingKey.isUnbondPublicProver() && + verifyingKey.isUnbondPublicVerifier() + ); default: return false; } @@ -357,7 +447,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ feePrivateKeys(): Promise { return this.functionKeys(OfflineSearchParams.feePrivateKeyParams()); - }; + } /** * Get fee_public function keys from the credits.aleo program. The keys must be cached prior to calling this @@ -367,7 +457,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ feePublicKeys(): Promise { return this.functionKeys(OfflineSearchParams.feePublicKeyParams()); - }; + } /** * Get the inclusion prover keys from. The keys must be cached prior to calling this method for it to work. @@ -376,7 +466,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ inclusionKeys(): Promise { return this.functionKeys(OfflineSearchParams.inclusionKeyParams()); - }; + } /** * Get join function keys from the credits.aleo program. The keys must be cached prior to calling this @@ -386,7 +476,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ joinKeys(): Promise { return this.functionKeys(OfflineSearchParams.joinKeyParams()); - }; + } /** * Get split function keys from the credits.aleo program. The keys must be cached prior to calling this @@ -396,7 +486,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ splitKeys(): Promise { return this.functionKeys(OfflineSearchParams.splitKeyParams()); - }; + } /** * Get keys for a variant of the transfer function from the credits.aleo program. @@ -422,19 +512,29 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ transferKeys(visibility: string): Promise { if (PRIVATE_TRANSFER.has(visibility)) { - return this.functionKeys(OfflineSearchParams.transferPrivateKeyParams()); + return this.functionKeys( + OfflineSearchParams.transferPrivateKeyParams(), + ); } else if (PRIVATE_TO_PUBLIC_TRANSFER.has(visibility)) { - return this.functionKeys(OfflineSearchParams.transferPrivateToPublicKeyParams()); + return this.functionKeys( + OfflineSearchParams.transferPrivateToPublicKeyParams(), + ); } else if (PUBLIC_TRANSFER.has(visibility)) { - return this.functionKeys(OfflineSearchParams.transferPublicKeyParams()); + return this.functionKeys( + OfflineSearchParams.transferPublicKeyParams(), + ); } else if (PUBLIC_TRANSFER_AS_SIGNER.has(visibility)) { - return this.functionKeys(OfflineSearchParams.transferPublicAsSignerKeyParams()); + return this.functionKeys( + OfflineSearchParams.transferPublicAsSignerKeyParams(), + ); } else if (PUBLIC_TO_PRIVATE_TRANSFER.has(visibility)) { - return this.functionKeys(OfflineSearchParams.transferPublicToPrivateKeyParams()); + return this.functionKeys( + OfflineSearchParams.transferPublicToPrivateKeyParams(), + ); } else { throw new Error("Invalid visibility type"); } - }; + } /** * Get unbond_public function keys from the credits.aleo program @@ -443,7 +543,7 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ async unBondPublicKeys(): Promise { return this.functionKeys(OfflineSearchParams.unbondPublicKeyParams()); - }; + } /** * Insert the proving and verifying keys for the bond_public function into the cache. Only the proving key needs @@ -454,9 +554,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertBondPublicKeys(provingKey: ProvingKey) { if (provingKey.isBondPublicProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.bond_public.locator, [provingKey.toBytes(), VerifyingKey.bondPublicVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.bond_public.locator, [ + provingKey.toBytes(), + VerifyingKey.bondPublicVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for bond_public"); + throw new Error( + "Attempted to insert invalid proving keys for bond_public", + ); } } @@ -469,9 +574,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertClaimUnbondPublicKeys(provingKey: ProvingKey) { if (provingKey.isClaimUnbondPublicProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.claim_unbond_public.locator, [provingKey.toBytes(), VerifyingKey.claimUnbondPublicVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.claim_unbond_public.locator, [ + provingKey.toBytes(), + VerifyingKey.claimUnbondPublicVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for claim_unbond_public"); + throw new Error( + "Attempted to insert invalid proving keys for claim_unbond_public", + ); } } @@ -484,9 +594,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertFeePrivateKeys(provingKey: ProvingKey) { if (provingKey.isFeePrivateProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.fee_private.locator, [provingKey.toBytes(), VerifyingKey.feePrivateVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.fee_private.locator, [ + provingKey.toBytes(), + VerifyingKey.feePrivateVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for fee_private"); + throw new Error( + "Attempted to insert invalid proving keys for fee_private", + ); } } @@ -499,9 +614,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertFeePublicKeys(provingKey: ProvingKey) { if (provingKey.isFeePublicProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.fee_public.locator, [provingKey.toBytes(), VerifyingKey.feePublicVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.fee_public.locator, [ + provingKey.toBytes(), + VerifyingKey.feePublicVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for fee_public"); + throw new Error( + "Attempted to insert invalid proving keys for fee_public", + ); } } @@ -514,9 +634,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertInclusionKeys(provingKey: ProvingKey) { if (provingKey.isInclusionProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.inclusion.locator, [provingKey.toBytes(), VerifyingKey.inclusionVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.inclusion.locator, [ + provingKey.toBytes(), + VerifyingKey.inclusionVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for the inclusion prover"); + throw new Error( + "Attempted to insert invalid proving keys for the inclusion prover", + ); } } @@ -529,9 +654,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertJoinKeys(provingKey: ProvingKey) { if (provingKey.isJoinProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.join.locator, [provingKey.toBytes(), VerifyingKey.joinVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.join.locator, [ + provingKey.toBytes(), + VerifyingKey.joinVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for join"); + throw new Error( + "Attempted to insert invalid proving keys for join", + ); } } @@ -544,9 +674,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertSetValidatorStateKeys(provingKey: ProvingKey) { if (provingKey.isSetValidatorStateProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.set_validator_state.locator, [provingKey.toBytes(), VerifyingKey.setValidatorStateVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.set_validator_state.locator, [ + provingKey.toBytes(), + VerifyingKey.setValidatorStateVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for set_validator_state"); + throw new Error( + "Attempted to insert invalid proving keys for set_validator_state", + ); } } @@ -559,9 +694,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertSplitKeys(provingKey: ProvingKey) { if (provingKey.isSplitProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.split.locator, [provingKey.toBytes(), VerifyingKey.splitVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.split.locator, [ + provingKey.toBytes(), + VerifyingKey.splitVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for split"); + throw new Error( + "Attempted to insert invalid proving keys for split", + ); } } @@ -574,9 +714,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertTransferPrivateKeys(provingKey: ProvingKey) { if (provingKey.isTransferPrivateProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.transfer_private.locator, [provingKey.toBytes(), VerifyingKey.transferPrivateVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.transfer_private.locator, [ + provingKey.toBytes(), + VerifyingKey.transferPrivateVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for transfer_private"); + throw new Error( + "Attempted to insert invalid proving keys for transfer_private", + ); } } @@ -589,9 +734,17 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertTransferPrivateToPublicKeys(provingKey: ProvingKey) { if (provingKey.isTransferPrivateToPublicProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator, [provingKey.toBytes(), VerifyingKey.transferPrivateToPublicVerifier().toBytes()]); + this.cache.set( + CREDITS_PROGRAM_KEYS.transfer_private_to_public.locator, + [ + provingKey.toBytes(), + VerifyingKey.transferPrivateToPublicVerifier().toBytes(), + ], + ); } else { - throw new Error("Attempted to insert invalid proving keys for transfer_private_to_public"); + throw new Error( + "Attempted to insert invalid proving keys for transfer_private_to_public", + ); } } @@ -604,9 +757,14 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertTransferPublicKeys(provingKey: ProvingKey) { if (provingKey.isTransferPublicProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.transfer_public.locator, [provingKey.toBytes(), VerifyingKey.transferPublicVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.transfer_public.locator, [ + provingKey.toBytes(), + VerifyingKey.transferPublicVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for transfer_public"); + throw new Error( + "Attempted to insert invalid proving keys for transfer_public", + ); } } @@ -619,20 +777,32 @@ class OfflineKeyProvider implements FunctionKeyProvider { */ insertTransferPublicToPrivateKeys(provingKey: ProvingKey) { if (provingKey.isTransferPublicToPrivateProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator, [provingKey.toBytes(), VerifyingKey.transferPublicToPrivateVerifier().toBytes()]); + this.cache.set( + CREDITS_PROGRAM_KEYS.transfer_public_to_private.locator, + [ + provingKey.toBytes(), + VerifyingKey.transferPublicToPrivateVerifier().toBytes(), + ], + ); } else { - throw new Error("Attempted to insert invalid proving keys for transfer_public_to_private"); + throw new Error( + "Attempted to insert invalid proving keys for transfer_public_to_private", + ); } } insertUnbondPublicKeys(provingKey: ProvingKey) { if (provingKey.isUnbondPublicProver()) { - this.cache.set(CREDITS_PROGRAM_KEYS.unbond_public.locator, [provingKey.toBytes(), VerifyingKey.unbondPublicVerifier().toBytes()]); + this.cache.set(CREDITS_PROGRAM_KEYS.unbond_public.locator, [ + provingKey.toBytes(), + VerifyingKey.unbondPublicVerifier().toBytes(), + ]); } else { - throw new Error("Attempted to insert invalid proving keys for unbond_public"); + throw new Error( + "Attempted to insert invalid proving keys for unbond_public", + ); } } } - -export {OfflineKeyProvider, OfflineSearchParams} +export { OfflineKeyProvider, OfflineSearchParams }; diff --git a/sdk/src/keys/verifier/interface.ts b/sdk/src/keys/verifier/interface.ts index 994a54839..b2c7b8f27 100644 --- a/sdk/src/keys/verifier/interface.ts +++ b/sdk/src/keys/verifier/interface.ts @@ -9,7 +9,6 @@ export interface KeyFingerprint { size: number; } - /** * Options for verifying the integrity of proving and verifying keys. An identifier to allow the interface to find key metadata and/or the desired metadata to verify must be passed in. * @@ -42,10 +41,10 @@ export class KeyVerificationError extends Error { public readonly locator: string, public readonly field: "checksum" | "size", public readonly expected: string, - public readonly actual: string + public readonly actual: string, ) { super( - `Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}` + `Key verification ${locator} ${field} mismatch: expected ${expected}, got ${actual}`, ); this.name = "ChecksumMismatchError"; Object.setPrototypeOf(this, KeyVerificationError.prototype); @@ -85,4 +84,4 @@ export interface KeyVerifier { * @returns {Promise} Promise that resolves when verification succeeds. */ verifyKeyBytes(keyMetadata: KeyMetadata): Promise; -} \ No newline at end of file +} diff --git a/sdk/src/keys/verifier/memory.ts b/sdk/src/keys/verifier/memory.ts index 2a09e249f..525774870 100644 --- a/sdk/src/keys/verifier/memory.ts +++ b/sdk/src/keys/verifier/memory.ts @@ -1,11 +1,17 @@ -import { KeyVerificationError, KeyVerifier, KeyFingerprint, KeyMetadata, sha256Hex } from "./interface"; +import { + KeyVerificationError, + KeyVerifier, + KeyFingerprint, + KeyMetadata, + sha256Hex, +} from "./interface"; /** * In-memory implementation of KeyVerifier that stores and verifies key fingerprints. * Provides functionality to compute and verify cryptographic checksums of keys, storing them * in memory for subsequent verification. This implementation is primarily used for testing * and development purposes where persistence is not required. - * + * * Key features: * - Computes SHA-256 checksums and sizes for key bytes. * - Stores key fingerprints in memory using string locators. @@ -23,11 +29,13 @@ export class MemKeyVerifier implements KeyVerifier { * @throws {KeyVerificationError} When provided keyFingerprint doesn't match computed values. * @returns {Promise} Computed key metadata. */ - async computeKeyMetadata(keyMetadata: KeyMetadata): Promise { + async computeKeyMetadata( + keyMetadata: KeyMetadata, + ): Promise { // Compute the metadata from the key bytes const computedFingerprint: KeyFingerprint = { checksum: await sha256Hex(keyMetadata.keyBytes), - size: keyMetadata.keyBytes.length + size: keyMetadata.keyBytes.length, }; // If a KeyFingerprint is provided, verify it matches computed values. @@ -37,15 +45,18 @@ export class MemKeyVerifier implements KeyVerifier { keyMetadata.locator ? keyMetadata.locator : "", "size", String(keyMetadata.fingerprint.size), - String(computedFingerprint.size) + String(computedFingerprint.size), ); } - if (keyMetadata.fingerprint.checksum !== computedFingerprint.checksum) { + if ( + keyMetadata.fingerprint.checksum !== + computedFingerprint.checksum + ) { throw new KeyVerificationError( keyMetadata.locator ? keyMetadata.locator : "", "checksum", keyMetadata.fingerprint.checksum, - computedFingerprint.checksum + computedFingerprint.checksum, ); } } @@ -76,7 +87,7 @@ export class MemKeyVerifier implements KeyVerifier { // Compute the fingerprint for the provided bytes. const computedFingerprint = await this.computeKeyMetadata({ - keyBytes: keyMetadata.keyBytes + keyBytes: keyMetadata.keyBytes, }); // Determine which fingerprint to verify against. @@ -91,7 +102,9 @@ export class MemKeyVerifier implements KeyVerifier { } if (!fingerprintToVerify) { - throw new Error("Either fingerprint or a valid locator must be provided for verification."); + throw new Error( + "Either fingerprint or a valid locator must be provided for verification.", + ); } // Verify the key size. @@ -100,7 +113,7 @@ export class MemKeyVerifier implements KeyVerifier { keyMetadata.locator || "", "size", String(fingerprintToVerify.size), - String(computedFingerprint.size) + String(computedFingerprint.size), ); } @@ -110,8 +123,8 @@ export class MemKeyVerifier implements KeyVerifier { keyMetadata.locator || "", "checksum", fingerprintToVerify.checksum, - computedFingerprint.checksum + computedFingerprint.checksum, ); } } -} \ No newline at end of file +} diff --git a/sdk/src/models/authorization.ts b/sdk/src/models/authorization.ts index d9cfedff5..1d7bce350 100644 --- a/sdk/src/models/authorization.ts +++ b/sdk/src/models/authorization.ts @@ -4,4 +4,4 @@ import { TransitionJSON } from "./transition/transitionJSON"; export interface AuthorizationJSON { requests: RequestJSON[]; transitions: TransitionJSON[]; -} \ No newline at end of file +} diff --git a/sdk/src/models/blockJSON.ts b/sdk/src/models/blockJSON.ts index e765ab6c0..eab23322e 100644 --- a/sdk/src/models/blockJSON.ts +++ b/sdk/src/models/blockJSON.ts @@ -1,18 +1,18 @@ import { ConfirmedTransactionJSON } from "./confirmed_transaction.js"; -import { RatificationJSON} from "./ratification.js"; +import { RatificationJSON } from "./ratification.js"; import { SolutionsJSON } from "./solution.js"; export type BlockJSON = { block_hash: string; previous_hash: string; header: Header; - transactions?: (ConfirmedTransactionJSON)[]; + transactions?: ConfirmedTransactionJSON[]; signature: string; - ratifications: (RatificationJSON)[]; + ratifications: RatificationJSON[]; solutions: SolutionsJSON; aborted_solution_ids: string[]; aborted_transaction_ids: string[]; -} +}; export type Header = { previous_state_root: string; @@ -22,7 +22,7 @@ export type Header = { solutions_root: string; subdag_root: string; metadata: Metadata; -} +}; export type Metadata = { network: bigint; @@ -33,4 +33,4 @@ export type Metadata = { timestamp: bigint; cumulative_weight: bigint; cumulative_proof_target: bigint; -} +}; diff --git a/sdk/src/models/confirmed_transaction.ts b/sdk/src/models/confirmed_transaction.ts index 8d20d391c..905dba93a 100644 --- a/sdk/src/models/confirmed_transaction.ts +++ b/sdk/src/models/confirmed_transaction.ts @@ -2,7 +2,7 @@ import { TransactionJSON } from "./transaction/transactionJSON.js"; import { FinalizeJSON } from "./finalizeJSON.js"; export interface ConfirmedTransactionJSON { - status: string + status: string; type: string; index: bigint; transaction: TransactionJSON; diff --git a/sdk/src/models/cryptoBoxPubkey.ts b/sdk/src/models/cryptoBoxPubkey.ts index 56887d831..8b5297eee 100644 --- a/sdk/src/models/cryptoBoxPubkey.ts +++ b/sdk/src/models/cryptoBoxPubkey.ts @@ -1,4 +1,4 @@ export interface CryptoBoxPubKey { - key_id: string, - public_key: string, -} \ No newline at end of file + key_id: string; + public_key: string; +} diff --git a/sdk/src/models/deployment/deploymentJSON.ts b/sdk/src/models/deployment/deploymentJSON.ts index ac7321125..8fec766a1 100644 --- a/sdk/src/models/deployment/deploymentJSON.ts +++ b/sdk/src/models/deployment/deploymentJSON.ts @@ -1,7 +1,7 @@ export type VerifyingKeys = [string, [string, string]]; export interface DeploymentJSON { - "edition" : number, - "program" : string, - "verifying_keys" : VerifyingKeys, -} \ No newline at end of file + edition: number; + program: string; + verifying_keys: VerifyingKeys; +} diff --git a/sdk/src/models/deployment/deploymentObject.ts b/sdk/src/models/deployment/deploymentObject.ts index ca84cc35c..e958aa402 100644 --- a/sdk/src/models/deployment/deploymentObject.ts +++ b/sdk/src/models/deployment/deploymentObject.ts @@ -1,7 +1,7 @@ import { FunctionObject } from "../functionObject.js"; export interface DeploymentObject { - "edition" : number, - "program" : string, - "functions" : FunctionObject[], + edition: number; + program: string; + functions: FunctionObject[]; } diff --git a/sdk/src/models/encryptedProvingRequest.ts b/sdk/src/models/encryptedProvingRequest.ts index 7c1d4d3da..b1650c657 100644 --- a/sdk/src/models/encryptedProvingRequest.ts +++ b/sdk/src/models/encryptedProvingRequest.ts @@ -1,4 +1,4 @@ export interface EncryptedProvingRequest { - key_id: string, - ciphertext: string, -} \ No newline at end of file + key_id: string; + ciphertext: string; +} diff --git a/sdk/src/models/finalizeJSON.ts b/sdk/src/models/finalizeJSON.ts index f81b78bea..1a70c5a7e 100644 --- a/sdk/src/models/finalizeJSON.ts +++ b/sdk/src/models/finalizeJSON.ts @@ -1,6 +1,6 @@ export interface FinalizeJSON { - "type": string; - "mapping_id": string; - "key_id": string; - "value_id": string; + type: string; + mapping_id: string; + key_id: string; + value_id: string; } diff --git a/sdk/src/models/functionInput.ts b/sdk/src/models/functionInput.ts index ceb0737eb..69f9e5b26 100644 --- a/sdk/src/models/functionInput.ts +++ b/sdk/src/models/functionInput.ts @@ -1,7 +1,7 @@ export interface FunctionInput { - type: string, - visibility: string, - record?: string, - register?: string, - members?: FunctionInput[], -} \ No newline at end of file + type: string; + visibility: string; + record?: string; + register?: string; + members?: FunctionInput[]; +} diff --git a/sdk/src/models/functionObject.ts b/sdk/src/models/functionObject.ts index a18a7db45..3ee0bd28b 100644 --- a/sdk/src/models/functionObject.ts +++ b/sdk/src/models/functionObject.ts @@ -1,9 +1,9 @@ import { VerifyingKey } from "../wasm.js"; export interface FunctionObject { - "name" : string, - "constraints" : number, - "variables" : number, - "verifyingKey" : string | VerifyingKey, - "certificate" : string, + name: string; + constraints: number; + variables: number; + verifyingKey: string | VerifyingKey; + certificate: string; } diff --git a/sdk/src/models/imports.ts b/sdk/src/models/imports.ts index 11f2b4d16..37340476a 100644 --- a/sdk/src/models/imports.ts +++ b/sdk/src/models/imports.ts @@ -6,4 +6,4 @@ interface ImportedPrograms { [key: string]: string; // This allows for arbitrary keys with any type values } -export { ImportedVerifyingKeys, ImportedPrograms } \ No newline at end of file +export { ImportedVerifyingKeys, ImportedPrograms }; diff --git a/sdk/src/models/input/inputJSON.ts b/sdk/src/models/input/inputJSON.ts index fbe9cbf2f..70c0710ee 100644 --- a/sdk/src/models/input/inputJSON.ts +++ b/sdk/src/models/input/inputJSON.ts @@ -6,4 +6,4 @@ export interface InputJSON { id: string; tag?: string; value?: string; -} \ No newline at end of file +} diff --git a/sdk/src/models/input/inputObject.ts b/sdk/src/models/input/inputObject.ts index b6a991e4f..7d1c5673f 100644 --- a/sdk/src/models/input/inputObject.ts +++ b/sdk/src/models/input/inputObject.ts @@ -8,8 +8,8 @@ import { PlaintextObject } from "../plaintext/plaintext.js"; * Object representation of an Input as raw JSON returned from a SnarkOS node. */ export interface InputObject { - type: "string", - id: "string" | Field, - tag?: string | Field, - value?: Ciphertext | Plaintext | PlaintextObject, + type: "string"; + id: "string" | Field; + tag?: string | Field; + value?: Ciphertext | Plaintext | PlaintextObject; } diff --git a/sdk/src/models/inputID.ts b/sdk/src/models/inputID.ts index f63bd8ce7..e97ab9c3d 100644 --- a/sdk/src/models/inputID.ts +++ b/sdk/src/models/inputID.ts @@ -1,4 +1,4 @@ export interface InputID { type: string; id: string; -} \ No newline at end of file +} diff --git a/sdk/src/models/keyPair.ts b/sdk/src/models/keyPair.ts index 66a7f58bb..6b236f6a5 100644 --- a/sdk/src/models/keyPair.ts +++ b/sdk/src/models/keyPair.ts @@ -3,4 +3,4 @@ import { ProvingKey, VerifyingKey } from "../wasm.js"; type FunctionKeyPair = [ProvingKey, VerifyingKey]; type CachedKeyPair = [Uint8Array, Uint8Array]; -export {CachedKeyPair, FunctionKeyPair}; \ No newline at end of file +export { CachedKeyPair, FunctionKeyPair }; diff --git a/sdk/src/models/output/outputObject.ts b/sdk/src/models/output/outputObject.ts index c3fc29eb0..ecd0fdee9 100644 --- a/sdk/src/models/output/outputObject.ts +++ b/sdk/src/models/output/outputObject.ts @@ -8,11 +8,11 @@ import { PlaintextObject } from "../plaintext/plaintext.js"; * Object representation of an Input as raw JSON returned from a SnarkOS node. */ export interface OutputObject { - type: string, - id: string | Field, - value?: Ciphertext | Plaintext | PlaintextObject, - checksum?: string | Field, - program?: string, - function?: string, - arguments?: Array | Array<OutputObject> + type: string; + id: string | Field; + value?: Ciphertext | Plaintext | PlaintextObject; + checksum?: string | Field; + program?: string; + function?: string; + arguments?: Array<Plaintext> | Array<OutputObject>; } diff --git a/sdk/src/models/owner/ownerJSON.ts b/sdk/src/models/owner/ownerJSON.ts index b167b6aee..dc0f1217c 100644 --- a/sdk/src/models/owner/ownerJSON.ts +++ b/sdk/src/models/owner/ownerJSON.ts @@ -1,4 +1,4 @@ export interface OwnerJSON { address: string; signature: string; -} \ No newline at end of file +} diff --git a/sdk/src/models/plaintext/array.ts b/sdk/src/models/plaintext/array.ts index 11e9e6ca3..d35a8e44c 100644 --- a/sdk/src/models/plaintext/array.ts +++ b/sdk/src/models/plaintext/array.ts @@ -1,4 +1,7 @@ import { PlaintextLiteral } from "./literal.js"; import { PlaintextStruct } from "./struct.js"; -export type PlaintextArray = PlaintextLiteral[] | PlaintextStruct[] | PlaintextArray[]; +export type PlaintextArray = + | PlaintextLiteral[] + | PlaintextStruct[] + | PlaintextArray[]; diff --git a/sdk/src/models/plaintext/literal.ts b/sdk/src/models/plaintext/literal.ts index d1ebd875f..ff6a92abd 100644 --- a/sdk/src/models/plaintext/literal.ts +++ b/sdk/src/models/plaintext/literal.ts @@ -1 +1 @@ -export type PlaintextLiteral = boolean | bigint | number | string; \ No newline at end of file +export type PlaintextLiteral = boolean | bigint | number | string; diff --git a/sdk/src/models/plaintext/plaintext.ts b/sdk/src/models/plaintext/plaintext.ts index f5226b162..78b2c8684 100644 --- a/sdk/src/models/plaintext/plaintext.ts +++ b/sdk/src/models/plaintext/plaintext.ts @@ -3,4 +3,8 @@ import { PlaintextArray } from "./array.js"; import { PlaintextLiteral } from "./literal.js"; import { PlaintextStruct } from "./struct.js"; -export type PlaintextObject = PlaintextArray| PlaintextLiteral | PlaintextStruct | Plaintext; +export type PlaintextObject = + | PlaintextArray + | PlaintextLiteral + | PlaintextStruct + | Plaintext; diff --git a/sdk/src/models/plaintext/struct.ts b/sdk/src/models/plaintext/struct.ts index 3f9038863..7918c06ed 100644 --- a/sdk/src/models/plaintext/struct.ts +++ b/sdk/src/models/plaintext/struct.ts @@ -3,4 +3,4 @@ import { PlaintextLiteral } from "./literal.js"; export type PlaintextStruct = { [key: string]: PlaintextArray | PlaintextLiteral | PlaintextStruct; -} +}; diff --git a/sdk/src/models/provingRequest.ts b/sdk/src/models/provingRequest.ts index 10f55d2c7..aeabdb331 100644 --- a/sdk/src/models/provingRequest.ts +++ b/sdk/src/models/provingRequest.ts @@ -4,4 +4,4 @@ export interface ProvingRequestJSON { authorization: AuthorizationJSON; fee_authorization?: AuthorizationJSON; broadcast: boolean; -} \ No newline at end of file +} diff --git a/sdk/src/models/provingResponse.ts b/sdk/src/models/provingResponse.ts index 46848716c..9b8003728 100644 --- a/sdk/src/models/provingResponse.ts +++ b/sdk/src/models/provingResponse.ts @@ -57,10 +57,8 @@ export function isProvingResponse(value: unknown): value is ProvingResponse { } /** Type guard: value is a ProveApiErrorBody. */ -export function isProveApiErrorBody(value: unknown): value is ProveApiErrorBody { - return ( - typeof value === "object" && - value !== null && - "message" in value - ); -} \ No newline at end of file +export function isProveApiErrorBody( + value: unknown, +): value is ProveApiErrorBody { + return typeof value === "object" && value !== null && "message" in value; +} diff --git a/sdk/src/models/ratification.ts b/sdk/src/models/ratification.ts index f4b171a0c..82d096569 100644 --- a/sdk/src/models/ratification.ts +++ b/sdk/src/models/ratification.ts @@ -1,4 +1,4 @@ export interface RatificationJSON { type: string; amount: number; -} \ No newline at end of file +} diff --git a/sdk/src/models/record-provider/encryptedRecord.ts b/sdk/src/models/record-provider/encryptedRecord.ts index 59e14fa42..eb665ac1d 100644 --- a/sdk/src/models/record-provider/encryptedRecord.ts +++ b/sdk/src/models/record-provider/encryptedRecord.ts @@ -1,6 +1,6 @@ /** * Encrypted Record found on chain. This type provides the record ciphertext and metadata from the ledger such as the record's name, the program/function that produced it, etc. - * + * * @property {string} commitment - The commitment of the record. * @property {string | undefined} checksum - The checksum of the record. * @property {number | undefined} block_height - The block height of the record. @@ -17,7 +17,7 @@ * @property {string | undefined} transition_id - The ID of the transition that produced the record. * @property {number | undefined} transaction_index - The index of the transaction that produced the record. * @property {number | undefined} transition_index - The index of the transition that produced the record. - * + * * @example * const encryptedRecord: EncryptedRecord = { * commitment: "1754131901135854615627743152473414463769543922079966020586765988138574911385field", @@ -55,4 +55,4 @@ export type EncryptedRecord = { transition_id?: string; transaction_index?: number; transition_index?: number; -} \ No newline at end of file +}; diff --git a/sdk/src/models/record-provider/ownedRecord.ts b/sdk/src/models/record-provider/ownedRecord.ts index d905558e2..d249d4402 100644 --- a/sdk/src/models/record-provider/ownedRecord.ts +++ b/sdk/src/models/record-provider/ownedRecord.ts @@ -57,4 +57,4 @@ export type OwnedRecord = { transition_id?: string; transaction_index?: number; transition_index?: number; -} \ No newline at end of file +}; diff --git a/sdk/src/models/record-provider/recordSearchParams.ts b/sdk/src/models/record-provider/recordSearchParams.ts index 10820e65f..26ded38f3 100644 --- a/sdk/src/models/record-provider/recordSearchParams.ts +++ b/sdk/src/models/record-provider/recordSearchParams.ts @@ -1,7 +1,7 @@ /** * Interface for record search parameters. This allows for arbitrary search parameters to be passed to record provider * implementations. - * + * * @example * const recordSearchParams: RecordSearchParams = { * // Declared fields @@ -16,4 +16,4 @@ export interface RecordSearchParams { unspent?: boolean; nonces?: string[]; [key: string]: any; // This allows for arbitrary keys with any type values -} \ No newline at end of file +} diff --git a/sdk/src/models/record-scanner/encryptedRecordsResult.ts b/sdk/src/models/record-scanner/encryptedRecordsResult.ts index 8592ca50e..22780ea3f 100644 --- a/sdk/src/models/record-scanner/encryptedRecordsResult.ts +++ b/sdk/src/models/record-scanner/encryptedRecordsResult.ts @@ -6,5 +6,6 @@ export interface EncryptedRecordsSuccess { data: EncryptedRecord[]; } -export type EncryptedRecordsResult = EncryptedRecordsSuccess | RecordScannerFailure; - +export type EncryptedRecordsResult = + | EncryptedRecordsSuccess + | RecordScannerFailure; diff --git a/sdk/src/models/record-scanner/error.ts b/sdk/src/models/record-scanner/error.ts index bcf880000..e11db55c5 100644 --- a/sdk/src/models/record-scanner/error.ts +++ b/sdk/src/models/record-scanner/error.ts @@ -84,4 +84,3 @@ export interface RecordScannerFailure { status: number; error: RecordScannerErrorBody; } - diff --git a/sdk/src/models/record-scanner/ownedFilter.ts b/sdk/src/models/record-scanner/ownedFilter.ts index ed2cfe413..6b60fc74e 100644 --- a/sdk/src/models/record-scanner/ownedFilter.ts +++ b/sdk/src/models/record-scanner/ownedFilter.ts @@ -4,7 +4,7 @@ import { OwnedRecordsResponseFilter } from "./ownedRecordsResponseFilter"; /** * OwnedFilter is an extension of RecordSearchParams that represents a filter for scanning owned records. - * + * * @example * const ownedFilter: OwnedFilter = { * unspent: true, @@ -14,10 +14,10 @@ import { OwnedRecordsResponseFilter } from "./ownedRecordsResponseFilter"; * record: "credits", * }, * } - */ + */ export interface OwnedFilter extends RecordSearchParams { filter?: RecordsFilter; responseFilter?: OwnedRecordsResponseFilter; unspent?: boolean; uuid?: string; -} \ No newline at end of file +} diff --git a/sdk/src/models/record-scanner/ownedRecordsResponseFilter.ts b/sdk/src/models/record-scanner/ownedRecordsResponseFilter.ts index 0dc72bf4e..cdfefb224 100644 --- a/sdk/src/models/record-scanner/ownedRecordsResponseFilter.ts +++ b/sdk/src/models/record-scanner/ownedRecordsResponseFilter.ts @@ -1,7 +1,7 @@ /** * OwnedRecordsResponseFilter is a type that represents a filter for the response from a record provider. * A `true` value for a field in the filter will include that field in the response. - * + * * @example * const ownedRecordsResponseFilter: OwnedRecordsResponseFilter = { * commitment: true, @@ -39,4 +39,4 @@ export interface OwnedRecordsResponseFilter { transaction_id?: boolean; transaction_index?: boolean; transition_index?: boolean; -} \ No newline at end of file +} diff --git a/sdk/src/models/record-scanner/ownedRecordsResult.ts b/sdk/src/models/record-scanner/ownedRecordsResult.ts index 244156f4c..1c516e353 100644 --- a/sdk/src/models/record-scanner/ownedRecordsResult.ts +++ b/sdk/src/models/record-scanner/ownedRecordsResult.ts @@ -13,4 +13,3 @@ export interface OwnedRecordsSuccess { } export type OwnedRecordsResult = OwnedRecordsSuccess | RecordScannerFailure; - diff --git a/sdk/src/models/record-scanner/recordsFilter.ts b/sdk/src/models/record-scanner/recordsFilter.ts index bc65024f3..b18d58746 100644 --- a/sdk/src/models/record-scanner/recordsFilter.ts +++ b/sdk/src/models/record-scanner/recordsFilter.ts @@ -3,7 +3,7 @@ import { RecordsResponseFilter } from "./recordsResponseFilter"; /** * RecordsFilter is an extension of RecordSearchParams that represents a filter for scanning encrypted or owned records. - * + * * @example * const recordsFilter: RecordsFilter = { * start: 0, @@ -33,4 +33,4 @@ export interface RecordsFilter extends RecordSearchParams { functions?: string[]; results_per_page?: number; page?: number; -} \ No newline at end of file +} diff --git a/sdk/src/models/record-scanner/recordsResponseFilter.ts b/sdk/src/models/record-scanner/recordsResponseFilter.ts index 172751a6e..0cbe72433 100644 --- a/sdk/src/models/record-scanner/recordsResponseFilter.ts +++ b/sdk/src/models/record-scanner/recordsResponseFilter.ts @@ -1,7 +1,7 @@ /** * RecordsResponseFilter is a type that represents a filter for the response from a record provider. * A `true` value for a field in the filter will include that field in the response. - * + * * @example * const recordsResponseFilter: RecordsResponseFilter = { * block_height: true, @@ -39,4 +39,4 @@ export type RecordsResponseFilter = { transition_id?: boolean; transaction_index?: boolean; transition_index?: boolean; -} \ No newline at end of file +}; diff --git a/sdk/src/models/record-scanner/registrationRequest.ts b/sdk/src/models/record-scanner/registrationRequest.ts index 53cb71dd0..86bce4eb2 100644 --- a/sdk/src/models/record-scanner/registrationRequest.ts +++ b/sdk/src/models/record-scanner/registrationRequest.ts @@ -1,6 +1,6 @@ /** * RegistrationRequest is a type that represents a request to register an account's view key with a record scanning service. - * + * * @example * const registrationRequest: RegistrationRequest = { * view_key: "AViewKey1ccEt8A2Ryva5rxnKcAbn7wgTaTsb79tzkKHFpeKsm9NX", @@ -10,4 +10,4 @@ export type RegistrationRequest = { view_key: string; start: number; -} \ No newline at end of file +}; diff --git a/sdk/src/models/record-scanner/registrationResponse.ts b/sdk/src/models/record-scanner/registrationResponse.ts index 51c54ee76..2164ca771 100644 --- a/sdk/src/models/record-scanner/registrationResponse.ts +++ b/sdk/src/models/record-scanner/registrationResponse.ts @@ -1,6 +1,6 @@ /** * RegistrationResponse is a type that represents a response from a record scanning service's registration endpoint. - * + * * @example * const registrationResponse: RegistrationResponse = { * uuid: "5291249998620209321712738612705518874926462927543783711572375085855029172391field", @@ -8,6 +8,6 @@ * } */ export interface RegistrationResponse { - uuid: string, - status?: string - } \ No newline at end of file + uuid: string; + status?: string; +} diff --git a/sdk/src/models/record-scanner/serialNumbersResult.ts b/sdk/src/models/record-scanner/serialNumbersResult.ts index 5ba61cfaa..bb1068d05 100644 --- a/sdk/src/models/record-scanner/serialNumbersResult.ts +++ b/sdk/src/models/record-scanner/serialNumbersResult.ts @@ -15,4 +15,3 @@ export interface SerialNumbersSuccess { * Success or failure variant of serialNumbers() result. */ export type SerialNumbersResult = SerialNumbersSuccess | RecordScannerFailure; - diff --git a/sdk/src/models/record-scanner/statusResponse.ts b/sdk/src/models/record-scanner/statusResponse.ts index d969393cc..19181f7a7 100644 --- a/sdk/src/models/record-scanner/statusResponse.ts +++ b/sdk/src/models/record-scanner/statusResponse.ts @@ -1,6 +1,6 @@ /** * StatusResponse is a type that represents a response from a record scanning service's status endpoint. - * + * * @example * const statusResponse: StatusResponse = { * synced: true, @@ -10,4 +10,4 @@ export interface StatusResponse { synced: boolean; percentage: number; -} \ No newline at end of file +} diff --git a/sdk/src/models/record-scanner/statusResult.ts b/sdk/src/models/record-scanner/statusResult.ts index 0dcb1d430..891fea855 100644 --- a/sdk/src/models/record-scanner/statusResult.ts +++ b/sdk/src/models/record-scanner/statusResult.ts @@ -13,4 +13,3 @@ export interface StatusSuccess { } export type StatusResult = StatusSuccess | RecordScannerFailure; - diff --git a/sdk/src/models/record-scanner/tagsResult.ts b/sdk/src/models/record-scanner/tagsResult.ts index 6d49d9a7a..02dd2608d 100644 --- a/sdk/src/models/record-scanner/tagsResult.ts +++ b/sdk/src/models/record-scanner/tagsResult.ts @@ -12,4 +12,3 @@ export interface TagsSuccess { } export type TagsResult = TagsSuccess | RecordScannerFailure; - diff --git a/sdk/src/models/request.ts b/sdk/src/models/request.ts index 10a99be6f..942f57fa1 100644 --- a/sdk/src/models/request.ts +++ b/sdk/src/models/request.ts @@ -1,4 +1,4 @@ -import { InputID} from "./inputID"; +import { InputID } from "./inputID"; export interface RequestJSON { signer: string; @@ -12,4 +12,4 @@ export interface RequestJSON { tvk: string; tcm: string; scm: string; -} \ No newline at end of file +} diff --git a/sdk/src/models/solution.ts b/sdk/src/models/solution.ts index 5d5c343de..09efe9184 100644 --- a/sdk/src/models/solution.ts +++ b/sdk/src/models/solution.ts @@ -13,4 +13,4 @@ export interface PartialSolutionJSON { epoch_hash: string; address: string; counter: BigInt; -} \ No newline at end of file +} diff --git a/sdk/src/models/transaction/transactionObject.ts b/sdk/src/models/transaction/transactionObject.ts index a406f05b4..49fd1b830 100644 --- a/sdk/src/models/transaction/transactionObject.ts +++ b/sdk/src/models/transaction/transactionObject.ts @@ -1,15 +1,18 @@ import { DeploymentObject } from "../deployment/deploymentObject.js"; -import { ExecutionObject, FeeExecutionObject } from "../execution/executionObject.js"; +import { + ExecutionObject, + FeeExecutionObject, +} from "../execution/executionObject.js"; import { OwnerObject } from "../owner/ownerObject.js"; export interface TransactionObject { - type : string; - id : string; + type: string; + id: string; execution?: ExecutionObject; - deployment? : DeploymentObject; + deployment?: DeploymentObject; fee: FeeExecutionObject; owner?: OwnerObject; - feeAmount? : bigint; - baseFee? : bigint; - priorityFee? : bigint; + feeAmount?: bigint; + baseFee?: bigint; + priorityFee?: bigint; } diff --git a/sdk/src/models/transition/transitionJSON.ts b/sdk/src/models/transition/transitionJSON.ts index 335939b8f..015c00739 100644 --- a/sdk/src/models/transition/transitionJSON.ts +++ b/sdk/src/models/transition/transitionJSON.ts @@ -5,8 +5,8 @@ export interface TransitionJSON { id: string; program: string; function: string; - inputs?: (InputJSON)[]; - outputs?: (OutputJSON)[]; + inputs?: InputJSON[]; + outputs?: OutputJSON[]; proof: string; tpk: string; tcm: string; diff --git a/sdk/src/models/transition/transitionObject.ts b/sdk/src/models/transition/transitionObject.ts index fd0140e94..6f3886e2f 100644 --- a/sdk/src/models/transition/transitionObject.ts +++ b/sdk/src/models/transition/transitionObject.ts @@ -6,8 +6,8 @@ export interface TransitionObject { id: string; program: string; function: string; - inputs?: (InputObject)[]; - outputs?: (OutputObject)[]; + inputs?: InputObject[]; + outputs?: OutputObject[]; proof: string; tpk: string | Group; tcm: string | Field; diff --git a/sdk/src/network-client.ts b/sdk/src/network-client.ts index c71a427cd..9aefad01b 100644 --- a/sdk/src/network-client.ts +++ b/sdk/src/network-client.ts @@ -1,4 +1,12 @@ -import { get, post, parseJSON, logAndThrow, retryWithBackoff, environment, isNode } from "./utils.js"; +import { + get, + post, + parseJSON, + logAndThrow, + retryWithBackoff, + environment, + isNode, +} from "./utils.js"; import { Account } from "./account.js"; import { BlockJSON } from "./models/blockJSON.js"; import { TransactionJSON } from "./models/transaction/transactionJSON.js"; @@ -23,7 +31,7 @@ import { } from "./models/provingResponse.js"; import { CryptoBoxPubKey } from "./models/cryptoBoxPubkey.js"; import { EncryptedProvingRequest } from "./models/encryptedProvingRequest.js"; -import { encryptProvingRequest } from "./security.js" +import { encryptProvingRequest } from "./security.js"; import { FIVE_MINUTES } from "./constants"; type ProgramImports = { [key: string]: string | Program }; @@ -36,7 +44,7 @@ interface AleoNetworkClientOptions { /** * Interface for the JWT data. - * + * * @property jwt {string} The JWT token string. * @property expiration {number} The expiration time of the JWT token in UNIX timestamp format. */ @@ -55,12 +63,12 @@ interface JWTData { * @property jwt {string} An optional JWT token used for authenticating with the proving service. */ interface DelegatedProvingParams { - provingRequest: ProvingRequest | string; - url?: string; - apiKey?: string; - consumerId?: string; - jwtData?: JWTData; - dpsPrivacy?: boolean; + provingRequest: ProvingRequest | string; + url?: string; + apiKey?: string; + consumerId?: string; + jwtData?: JWTData; + dpsPrivacy?: boolean; } /** @@ -119,7 +127,8 @@ class AleoNetworkClient { // If a record scanner uri was specified, set the record scanner uri. if (options.recordScannerUri) { - this.recordScannerUri = options.recordScannerUri + "/%%NETWORK%%"; + this.recordScannerUri = + options.recordScannerUri + "/%%NETWORK%%"; } } else { this.headers = { @@ -273,7 +282,7 @@ class AleoNetworkClient { */ async fetchRaw(url = "/"): Promise<string> { try { - const ctx = {...this.ctx}; + const ctx = { ...this.ctx }; return await retryWithBackoff(async () => { const response = await get(this.host + url, { headers: { @@ -443,7 +452,7 @@ class AleoNetworkClient { const transition = transaction.execution.transitions[ k - ]; + ]; // Only search for unspent records in the specified programs. if ( !(typeof programs === "undefined") @@ -500,14 +509,19 @@ class AleoNetworkClient { } if (unspent) { - const recordViewKey = recordPlaintext.recordViewKey(viewKey).toString(); + const recordViewKey = + recordPlaintext + .recordViewKey( + viewKey, + ) + .toString(); // Otherwise record the nonce that has been found const serialNumber = recordPlaintext.serialNumberString( resolvedPrivateKey, "credits.aleo", "credits", - recordViewKey + recordViewKey, ); // Attempt to see if the serial number is spent try { @@ -556,14 +570,14 @@ class AleoNetworkClient { "undefined" ) && amounts.length > - 0 + 0 ) { let amounts_found = 0; if ( recordPlaintext.microcredits() > amounts[ amounts_found - ] + ] ) { amounts_found += 1; records.push( @@ -609,9 +623,9 @@ class AleoNetworkClient { // If there is an error fetching blocks, log it and keep searching console.warn( "Error fetching blocks in range: " + - start.toString() + - "-" + - end.toString(), + start.toString() + + "-" + + end.toString(), ); console.warn("Error: ", error); failures += 1; @@ -833,7 +847,9 @@ class AleoNetworkClient { program = program.id(); } try { - this.ctx = { "X-ALEO-METHOD": "getDeploymentTransactionForProgram" }; + this.ctx = { + "X-ALEO-METHOD": "getDeploymentTransactionForProgram", + }; const transaction_id = <string>( await this.getDeploymentTransactionIDForProgram(program) ); @@ -870,7 +886,9 @@ class AleoNetworkClient { program: Program | string, ): Promise<Transaction> { try { - this.ctx = { "X-ALEO-METHOD": "getDeploymentTransactionObjectForProgram" }; + this.ctx = { + "X-ALEO-METHOD": "getDeploymentTransactionObjectForProgram", + }; const transaction_id = <string>( await this.getDeploymentTransactionIDForProgram(program) ); @@ -1075,7 +1093,9 @@ class AleoNetworkClient { async getLatestProgramEdition(programId: string): Promise<number> { try { this.ctx = { "X-ALEO-METHOD": "getLatestProgramEdition" }; - const raw = await this.fetchRaw("/program/" + programId + "/latest_edition"); + const raw = await this.fetchRaw( + "/program/" + programId + "/latest_edition", + ); return JSON.parse(raw); } catch (error) { throw new Error(`Error fetching program ${programId}: ${error}`); @@ -1107,7 +1127,10 @@ class AleoNetworkClient { * // Both program objects should be equal * assert(programObjectFromID.to_string() === programObjectFromSource.to_string()); */ - async getProgramObject(inputProgram: string, edition?: number): Promise<Program> { + async getProgramObject( + inputProgram: string, + edition?: number, + ): Promise<Program> { try { this.ctx = { "X-ALEO-METHOD": "getProgramObject" }; return Program.fromString( @@ -1152,7 +1175,10 @@ class AleoNetworkClient { * programImports = await networkClient.getProgramImports(double_test); * assert.deepStrictEqual(programImports, expectedImports); */ - async getProgramImports(inputProgram: Program | string, imports: ProgramImports = {}): Promise<ProgramImports> { + async getProgramImports( + inputProgram: Program | string, + imports: ProgramImports = {}, + ): Promise<ProgramImports> { try { this.ctx = { "X-ALEO-METHOD": "getProgramImports" }; @@ -1181,8 +1207,12 @@ class AleoNetworkClient { for (let i = 0; i < importList.length; i++) { const import_id = importList[i]; if (!imports.hasOwnProperty(import_id)) { - const programSource = <string>await this.getProgram(import_id); - const nestedImports = <ProgramImports>await this.getProgramImports(programSource, imports); + const programSource = <string>( + await this.getProgram(import_id) + ); + const nestedImports = <ProgramImports>( + await this.getProgramImports(programSource, imports) + ); for (const key in nestedImports) { if (!imports.hasOwnProperty(key)) { @@ -1202,7 +1232,6 @@ class AleoNetworkClient { } } - /** * Get a list of the program names that a program imports. * @@ -1662,13 +1691,22 @@ class AleoNetworkClient { ? transaction.toString() : transaction; try { - const endpoint = this.verboseErrors ? "transaction/broadcast?check_transaction=true" : "transaction/broadcast"; + const endpoint = this.verboseErrors + ? "transaction/broadcast?check_transaction=true" + : "transaction/broadcast"; const response = await retryWithBackoff(() => this._sendPost(`${this.host}/${endpoint}`, { body: transactionString, - headers: Object.assign({}, {...this.headers, "X-ALEO-METHOD" : "submitTransaction"}, { - "Content-Type": "application/json", - }), + headers: Object.assign( + {}, + { + ...this.headers, + "X-ALEO-METHOD": "submitTransaction", + }, + { + "Content-Type": "application/json", + }, + ), }), ); @@ -1681,9 +1719,7 @@ class AleoNetworkClient { ); } } catch (error: any) { - throw new Error( - `Error posting transaction: ${error}`, - ); + throw new Error(`Error posting transaction: ${error}`); } } @@ -1698,9 +1734,13 @@ class AleoNetworkClient { const response = await retryWithBackoff(() => post(this.host + "/solution/broadcast", { body: solution, - headers: Object.assign({}, {...this.headers, "X-ALEO-METHOD": "submitSolution"}, { - "Content-Type": "application/json", - }), + headers: Object.assign( + {}, + { ...this.headers, "X-ALEO-METHOD": "submitSolution" }, + { + "Content-Type": "application/json", + }, + ), }), ); @@ -1721,40 +1761,43 @@ class AleoNetworkClient { /** * Refreshes the JWT by making a POST request to /jwts/{consumer_id} - * + * * @param {string} apiKey - The API key for authentication. * @param {string} consumerId - The consumer ID associated with the API key. * @returns {Promise<JwtData>} The JWT token and expiration time */ - private async refreshJwt(apiKey: string, consumerId: string): Promise<JWTData> { + private async refreshJwt( + apiKey: string, + consumerId: string, + ): Promise<JWTData> { if (!apiKey || !consumerId) { - throw new Error('API key and consumer ID are required to refresh JWT'); + throw new Error( + "API key and consumer ID are required to refresh JWT", + ); } - const response = await post( - `${this.baseUrl}/jwts/${consumerId}`, - { - headers: { - 'X-Provable-API-Key': apiKey - } - } - ); - const authHeader = response.headers.get('authorization'); + const response = await post(`${this.baseUrl}/jwts/${consumerId}`, { + headers: { + "X-Provable-API-Key": apiKey, + }, + }); + const authHeader = response.headers.get("authorization"); if (!authHeader) { - throw new Error('No authorization header in JWT refresh response'); + throw new Error("No authorization header in JWT refresh response"); } const body = await response.json(); - + return { jwt: authHeader, - expiration: body.exp * 1000 // Convert to milliseconds + expiration: body.exp * 1000, // Convert to milliseconds }; } - /** * Parses a /prove or /prove/encrypted response. Returns a result object (never throws for 200/400/500/503). */ - private async handleProvingResponse(response: Response): Promise<ProvingResult> { + private async handleProvingResponse( + response: Response, + ): Promise<ProvingResult> { // Get the proving response text. const text = await response.text(); let body: unknown; @@ -1779,7 +1822,11 @@ class AleoNetworkClient { } // If the response is non 200, return the information back to the caller so it can be handled. - if (response.status === 400 || response.status === 500 || response.status === 503) { + if ( + response.status === 400 || + response.status === 500 || + response.status === 503 + ) { const error: ProveApiErrorBody = isProveApiErrorBody(body) ? body : { message: text || `${response.status} error` }; @@ -1798,7 +1845,9 @@ class AleoNetworkClient { * @param {DelegatedProvingParams} options - The optional parameters required to submit a proving request. * @returns {Promise<ProvingResponse>} The ProvingResponse containing the transaction result and the result of the broadcast if the `broadcast` flag was set to `true`. */ - async submitProvingRequest(options: DelegatedProvingParams): Promise<ProvingResponse> { + async submitProvingRequest( + options: DelegatedProvingParams, + ): Promise<ProvingResponse> { const result = await this.submitProvingRequestSafe(options); if (result.ok) { return result.data; @@ -1814,12 +1863,15 @@ class AleoNetworkClient { * @param {DelegatedProvingParams} options - The optional parameters required to submit a proving request. * @returns {Promise<ProvingResult>} `{ ok: true, data }` on success (200), or `{ ok: false, status, error }` on 400/500/503. Check `result.ok` and then either `result.data` or `result.status` / `result.error.message`. */ - async submitProvingRequestSafe(options: DelegatedProvingParams): Promise<ProvingResult> { + async submitProvingRequestSafe( + options: DelegatedProvingParams, + ): Promise<ProvingResult> { // Attempt to get the Prover URI first from the options, then from any configured globally, or third try the main configured host. - const proverUri = (options.url ?? this.proverUri) ?? this.host; - const provingRequestString = options.provingRequest instanceof ProvingRequest - ? options.provingRequest.toString() - : options.provingRequest; + const proverUri = options.url ?? this.proverUri ?? this.host; + const provingRequestString = + options.provingRequest instanceof ProvingRequest + ? options.provingRequest.toString() + : options.provingRequest; // Try to get JWT data to access the Provable API. const apiKey = options.apiKey ?? this.apiKey; @@ -1827,14 +1879,17 @@ class AleoNetworkClient { let jwtData = options.jwtData ?? this.jwtData; // Check to see if the JWT needs refreshing. - const isExpired = jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES; + const isExpired = + jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES; if (!jwtData || isExpired) { if (apiKey && consumerId) { jwtData = await this.refreshJwt(apiKey!, consumerId!); this.jwtData = jwtData; options.jwtData = jwtData; } else { - console.warn('JWT or both apiKey and consumerId are required when using the Provable API'); + console.warn( + "JWT or both apiKey and consumerId are required when using the Provable API", + ); } } @@ -1874,7 +1929,9 @@ class AleoNetworkClient { }; // We're in node, attempt to set the cookie manually. - const cookie = isNode() ? pubKeyResponse.headers.get("set-cookie"): undefined; + const cookie = isNode() + ? pubKeyResponse.headers.get("set-cookie") + : undefined; // Send the encrypted proving request to the DPS service. const res = await fetch(`${proverUri}/prove/encrypted`, { @@ -1882,7 +1939,7 @@ class AleoNetworkClient { body: JSON.stringify(payload), headers: { ...headers, - ...(cookie ? { Cookie: cookie } : {}) + ...(cookie ? { Cookie: cookie } : {}), }, credentials: "include", }); @@ -1916,7 +1973,9 @@ class AleoNetworkClient { // If 500s are hit responses are returned, attempt retries. if (result.status === 500 || result.status === 503) { - const err = new Error(result.error.message) as ProvingRequestError; + const err = new Error( + result.error.message, + ) as ProvingRequestError; err.status = result.status; throw err; } @@ -1986,7 +2045,8 @@ class AleoNetworkClient { { headers: { ...this.headers, - "X-ALEO-METHOD" : "waitForTransactionConfirmation", + "X-ALEO-METHOD": + "waitForTransactionConfirmation", }, }, ); diff --git a/sdk/src/polyfill/fetch.ts b/sdk/src/polyfill/fetch.ts index 8a26d272a..c1f227670 100644 --- a/sdk/src/polyfill/fetch.ts +++ b/sdk/src/polyfill/fetch.ts @@ -1,17 +1,14 @@ import * as $fs from "node:fs"; import $mime from "mime/lite"; - const oldFetch = globalThis.fetch; - let supports: Promise<boolean> | null = null; async function checkFetch() { try { await oldFetch(new URL("file:")); return true; - } catch (e) { return false; } @@ -25,9 +22,11 @@ async function supportsFetch(): Promise<boolean> { return await supports; } - // We always polyfill fetch because Node's fetch doesn't support file URLs. -(globalThis.fetch as any) = async function (resource: URL | RequestInfo, options: RequestInit | undefined): Promise<Response> { +(globalThis.fetch as any) = async function ( + resource: URL | RequestInfo, + options: RequestInit | undefined, +): Promise<Response> { const request = new Request(resource, options); const url = new URL(request.url); @@ -48,7 +47,6 @@ async function supportsFetch(): Promise<boolean> { statusText: "OK", headers, }); - } else { return await oldFetch(request); } diff --git a/sdk/src/polyfill/worker.ts b/sdk/src/polyfill/worker.ts index 8ef0edba5..483a92479 100644 --- a/sdk/src/polyfill/worker.ts +++ b/sdk/src/polyfill/worker.ts @@ -23,13 +23,14 @@ if (globalThis.Worker == null) { } url = url.href; - } else { - throw new Error("Filepaths are unreliable, use `new URL(\"...\", import.meta.url)` instead."); + throw new Error( + 'Filepaths are unreliable, use `new URL("...", import.meta.url)` instead.', + ); } if (!options || options.type !== "module") { - throw new Error("Workers must use \`type: \"module\"\`"); + throw new Error('Workers must use \`type: "module"\`'); } const code = ` @@ -80,7 +81,10 @@ if (globalThis.Worker == null) { } postMessage(message: any, transfer: Array<Transferable>): void; - postMessage(message: any, options?: StructuredSerializeOptions | undefined): void; + postMessage( + message: any, + options?: StructuredSerializeOptions | undefined, + ): void; postMessage(value: any, transfer: any) { this._worker.postMessage(value, transfer); } @@ -97,7 +101,6 @@ if (globalThis.Worker == null) { }; } - if (!$worker.isMainThread) { const globals = globalThis as unknown as DedicatedWorkerGlobalScope; @@ -135,7 +138,6 @@ if (!$worker.isMainThread) { }; }; - // We only start listening for messages / errors when the worker calls addEventListener const startOnMessage = memoize(() => { $worker.parentPort!.on("message", (data) => { @@ -153,7 +155,6 @@ if (!$worker.isMainThread) { }); }); - // Node workers don't have top-level events, so we have to make our own const workerEvents = new EventTarget(); @@ -161,7 +162,11 @@ if (!$worker.isMainThread) { process.exit(); }; - globals.addEventListener = (type: string, callback: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined) => { + globals.addEventListener = ( + type: string, + callback: EventListenerOrEventListenerObject | null, + options?: boolean | EventListenerOptions | undefined, + ) => { workerEvents.addEventListener(type, callback, options); if (type === "message") { @@ -173,12 +178,19 @@ if (!$worker.isMainThread) { } }; - globals.removeEventListener = (type: string, callback: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined) => { + globals.removeEventListener = ( + type: string, + callback: EventListenerOrEventListenerObject | null, + options?: boolean | EventListenerOptions | undefined, + ) => { workerEvents.removeEventListener(type, callback, options); }; function postMessage(message: any, transfer: Transferable[]): void; - function postMessage(message: any, options?: StructuredSerializeOptions | undefined): void; + function postMessage( + message: any, + options?: StructuredSerializeOptions | undefined, + ): void; function postMessage(value: any, transfer: any) { $worker.parentPort!.postMessage(value, transfer); } diff --git a/sdk/src/polyfill/xmlhttprequest.ts b/sdk/src/polyfill/xmlhttprequest.ts index 4ae903f12..3e725c7da 100644 --- a/sdk/src/polyfill/xmlhttprequest.ts +++ b/sdk/src/polyfill/xmlhttprequest.ts @@ -2,9 +2,11 @@ import $xmlhttprequest from "xmlhttprequest-ssl"; if (globalThis.XMLHttpRequest == null) { - (globalThis as any).XMLHttpRequest = class extends $xmlhttprequest.XMLHttpRequest { + (globalThis as any).XMLHttpRequest = class extends ( + $xmlhttprequest.XMLHttpRequest + ) { constructor(opts?: any) { super({ syncPolicy: "enabled", ...opts }); } }; -} \ No newline at end of file +} diff --git a/sdk/src/program-manager.ts b/sdk/src/program-manager.ts index f10e4cbd3..50ac1390e 100644 --- a/sdk/src/program-manager.ts +++ b/sdk/src/program-manager.ts @@ -1,5 +1,9 @@ import { Account } from "./account.js"; -import { AleoNetworkClient, AleoNetworkClientOptions, ProgramImports } from "./network-client.js"; +import { + AleoNetworkClient, + AleoNetworkClientOptions, + ProgramImports, +} from "./network-client.js"; import { ImportedPrograms, ImportedVerifyingKeys } from "./models/imports.js"; import { RecordProvider } from "./record-provider.js"; import { RecordSearchParams } from "./models/record-provider/recordSearchParams.js"; @@ -13,9 +17,7 @@ import { AleoKeyProviderParams, } from "./keys/provider/memory.js"; -import { - FunctionKeyPair -} from "./models/keyPair.js"; +import { FunctionKeyPair } from "./models/keyPair.js"; import { Address, @@ -97,7 +99,7 @@ interface ExecuteOptions { offlineQuery?: OfflineQuery; program?: string | Program; imports?: ProgramImports; - edition?: number, + edition?: number; } /** @@ -118,7 +120,7 @@ interface AuthorizationOptions { programSource?: string | Program; privateKey?: PrivateKey; programImports?: ProgramImports; - edition?: number, + edition?: number; } /** @@ -131,11 +133,11 @@ interface AuthorizationOptions { * @property {RecordPlaintext} [feeRecord] A record to specify to pay the private fee. If this is specified a `fee_private` authorization will be built. */ interface FeeAuthorizationOptions { - deploymentOrExecutionId: string, - baseFeeCredits: number, - priorityFeeCredits?: number, - privateKey?: PrivateKey, - feeRecord?: RecordPlaintext, + deploymentOrExecutionId: string; + baseFeeCredits: number; + priorityFeeCredits?: number; + privateKey?: PrivateKey; + feeRecord?: RecordPlaintext; } /** @@ -151,8 +153,8 @@ interface FeeAuthorizationOptions { */ interface ExecuteAuthorizationOptions { programName: string; - authorization: Authorization, - feeAuthorization?: Authorization, + authorization: Authorization; + feeAuthorization?: Authorization; keySearchParams?: KeySearchParams; provingKey?: ProvingKey; verifyingKey?: VerifyingKey; @@ -216,7 +218,7 @@ interface FeeEstimateOptions { functionName?: string; program?: string | Program; imports?: ProgramImports; - edition?: number, + edition?: number; authorization?: Authorization; } @@ -244,7 +246,10 @@ class ProgramManager { networkClientOptions?: AleoNetworkClientOptions | undefined, ) { this.host = host ? host : "https://api.provable.com/v2"; - this.networkClient = new AleoNetworkClient(this.host, networkClientOptions); + this.networkClient = new AleoNetworkClient( + this.host, + networkClientOptions, + ); this.keyProvider = keyProvider ? keyProvider : new AleoKeyProvider(); this.recordProvider = recordProvider; @@ -254,8 +259,9 @@ class ProgramManager { * Check if the fee is sufficient to pay for the transaction */ async checkFee(address: string, feeAmount: bigint) { - const balance = - BigInt(await this.networkClient.getPublicBalance(address)); + const balance = BigInt( + await this.networkClient.getPublicBalance(address), + ); if (feeAmount > balance) { throw Error( `The desired execution requires a fee of ${feeAmount} microcredits, but the account paying the fee has ${balance} microcredits available.`, @@ -339,20 +345,22 @@ class ProgramManager { */ async setInclusionProver(provingKey?: ProvingKey) { if (this.inclusionKeysLoaded) { - return + return; } try { if (provingKey) { - WasmProgramManager.loadInclusionProver(provingKey) + WasmProgramManager.loadInclusionProver(provingKey); this.inclusionKeysLoaded = true; } else { const inclusionKeys = await this.keyProvider.inclusionKeys(); - WasmProgramManager.loadInclusionProver(inclusionKeys[0]) + WasmProgramManager.loadInclusionProver(inclusionKeys[0]); this.inclusionKeysLoaded = true; } return; } catch { - console.log("Setting the inclusion prover requires either a key provider to be configured for the ProgramManager OR to pass the inclusion prover directly"); + console.log( + "Setting the inclusion prover requires either a key provider to be configured for the ProgramManager OR to pass the inclusion prover directly", + ); } } @@ -371,7 +379,7 @@ class ProgramManager { * programManager.removeHeader('X-Aleo-SDK-Version'); */ removeHeader(headerName: string) { - delete this.networkClient.headers[headerName] + delete this.networkClient.headers[headerName]; } /** @@ -444,7 +452,9 @@ class ProgramManager { ); } if (typeof programSource === "string") { - throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`); + throw Error( + `Program ${programObject.id()} already exists on the network, please rename your program`, + ); } } catch (e: any) { logAndThrow(`Error validating program: ${e.message}`); @@ -469,10 +479,20 @@ class ProgramManager { let fee = priorityFee; // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. if (!feeRecord) { - console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + console.log( + "Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.", + ); const programString = programObject.toString(); - const imports = await this.networkClient.getProgramImports(programString); - const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + const imports = + await this.networkClient.getProgramImports( + programString, + ); + const baseFee = Number( + WasmProgramManager.estimateDeploymentFee( + programString, + imports, + ), + ); fee = baseFee + priorityFee; } @@ -481,11 +501,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -562,9 +582,10 @@ class ProgramManager { * }, 20000); */ async buildUpgradeTransaction( - options: DeployOptions + options: DeployOptions, ): Promise<Transaction> { - const { program, priorityFee, privateFee, recordSearchParams } = options; + const { program, priorityFee, privateFee, recordSearchParams } = + options; let feeRecord = options.feeRecord; let privateKey = options.privateKey; @@ -614,10 +635,20 @@ class ProgramManager { let fee = priorityFee; // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. if (!feeRecord) { - console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + console.log( + "Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.", + ); const programString = programObject.toString(); - const imports = await this.networkClient.getProgramImports(programString); - const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + const imports = + await this.networkClient.getProgramImports( + programString, + ); + const baseFee = Number( + WasmProgramManager.estimateDeploymentFee( + programString, + imports, + ), + ); fee = baseFee + priorityFee; } @@ -626,11 +657,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -726,7 +757,7 @@ class ProgramManager { recordSearchParams, feeRecord, privateKey, - ) + ) ); let feeAddress; @@ -813,7 +844,8 @@ class ProgramManager { // Ensure the function exists on the network if (program === undefined) { try { - programObject = await this.networkClient.getProgramObject(programName); + programObject = + await this.networkClient.getProgramObject(programName); program = <string>programObject.toString(); } catch (e: any) { logAndThrow( @@ -824,7 +856,9 @@ class ProgramManager { try { programObject = Program.fromString(program); } catch (e: any) { - logAndThrow(`Program sources passed for ${programName} were invalid: ${e}`); + logAndThrow( + `Program sources passed for ${programName} were invalid: ${e}`, + ); } } else if (program instanceof Program) { programObject = program; @@ -842,9 +876,14 @@ class ProgramManager { if (edition == undefined) { try { - edition = await this.networkClient.getLatestProgramEdition(programName); + edition = + await this.networkClient.getLatestProgramEdition( + programName, + ); } catch (e: any) { - console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`); + console.warn( + `Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`, + ); edition = 0; } } @@ -908,7 +947,14 @@ class ProgramManager { let fee = priorityFee; // If a fee record wasn't provided, estimate the fee that needs to be paid. if (!feeRecord) { - const baseFee = Number(await this.estimateExecutionFee({programName, functionName, program, imports})); + const baseFee = Number( + await this.estimateExecutionFee({ + programName, + functionName, + program, + imports, + }), + ); fee = baseFee + priorityFee; } @@ -917,11 +963,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -932,11 +978,13 @@ class ProgramManager { if (offlineQuery && !this.inclusionKeysLoaded) { try { const inclusionKeys = await this.keyProvider.inclusionKeys(); - WasmProgramManager.loadInclusionProver(inclusionKeys[0]) + WasmProgramManager.loadInclusionProver(inclusionKeys[0]); this.inclusionKeysLoaded = true; console.log("Successfully loaded inclusion key"); } catch { - logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`) + logAndThrow( + `Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`, + ); } } @@ -955,7 +1003,7 @@ class ProgramManager { feeProvingKey, feeVerifyingKey, offlineQuery, - edition + edition, ); } @@ -1033,10 +1081,7 @@ class ProgramManager { options: ExecuteAuthorizationOptions, ): Promise<Transaction> { // Destructure the options object to access the parameters. - const { - programName, - authorization, - } = options; + const { programName, authorization } = options; const feeAuthorization = options.feeAuthorization; const keySearchParams = options.keySearchParams; @@ -1063,7 +1108,9 @@ class ProgramManager { // Get the fee proving and verifying keys from the key provider. let feeKeys; - const privateFee = feeAuthorization ? feeAuthorization.isFeePrivate() : false; + const privateFee = feeAuthorization + ? feeAuthorization.isFeePrivate() + : false; try { feeKeys = privateFee ? <FunctionKeyPair>await this.keyProvider.feePrivateKeys() @@ -1108,16 +1155,18 @@ class ProgramManager { console.log("Loading inclusion keys for offline proving."); try { const inclusionKeys = await this.keyProvider.inclusionKeys(); - WasmProgramManager.loadInclusionProver(inclusionKeys[0]) + WasmProgramManager.loadInclusionProver(inclusionKeys[0]); this.inclusionKeysLoaded = true; console.log("Successfully loaded inclusion key"); } catch { - logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`) + logAndThrow( + `Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`, + ); } } // Build an execution transaction from the authorization. - console.log("Executing authorizations") + console.log("Executing authorizations"); return await WasmProgramManager.executeAuthorization( authorization, feeAuthorization, @@ -1128,8 +1177,8 @@ class ProgramManager { feeVerifyingKey, imports, this.host, - offlineQuery - ) + offlineQuery, + ); } /** @@ -1164,10 +1213,7 @@ class ProgramManager { options: AuthorizationOptions, ): Promise<Authorization> { // Destructure the options object to access the parameters. - const { - functionName, - inputs, - } = options; + const { functionName, inputs } = options; const privateKey = options.privateKey; let program = options.programSource; @@ -1210,9 +1256,14 @@ class ProgramManager { if (edition == undefined) { try { - edition = await this.networkClient.getLatestProgramEdition(programName); + edition = + await this.networkClient.getLatestProgramEdition( + programName, + ); } catch (e: any) { - console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`); + console.warn( + `Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`, + ); edition = 0; } } @@ -1238,7 +1289,7 @@ class ProgramManager { functionName, inputs, imports, - edition + edition, ); } @@ -1274,10 +1325,7 @@ class ProgramManager { options: AuthorizationOptions, ): Promise<Authorization> { // Destructure the options object to access the parameters. - const { - functionName, - inputs, - } = options; + const { functionName, inputs } = options; const privateKey = options.privateKey; let program = options.programSource; @@ -1334,9 +1382,14 @@ class ProgramManager { if (edition == undefined) { try { - edition = await this.networkClient.getLatestProgramEdition(programName); + edition = + await this.networkClient.getLatestProgramEdition( + programName, + ); } catch (e: any) { - console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`); + console.warn( + `Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`, + ); edition = 0; } } @@ -1348,7 +1401,7 @@ class ProgramManager { functionName, inputs, imports, - edition + edition, ); } @@ -1399,7 +1452,9 @@ class ProgramManager { const baseFee = options.baseFee ? options.baseFee : 0; const privateKey = options.privateKey; - const useFeeMaster = options.useFeeMaster ? options.useFeeMaster : false; + const useFeeMaster = options.useFeeMaster + ? options.useFeeMaster + : false; let program = options.programSource; let programName = options.programName; let feeRecord = options.feeRecord; @@ -1428,9 +1483,14 @@ class ProgramManager { if (edition == undefined) { try { - edition = await this.networkClient.getLatestProgramEdition(programName); + edition = + await this.networkClient.getLatestProgramEdition( + programName, + ); } catch (e: any) { - console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`); + console.warn( + `Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`, + ); edition = 0; } } @@ -1468,7 +1528,14 @@ class ProgramManager { let fee = priorityFee; // If a fee record wasn't provided, estimate the fee that needs to be paid. if (!feeRecord) { - const baseFee = Number(await this.estimateExecutionFee({programName, functionName, program: program.toString(), imports})); + const baseFee = Number( + await this.estimateExecutionFee({ + programName, + functionName, + program: program.toString(), + imports, + }), + ); fee = baseFee + priorityFee; } @@ -1477,11 +1544,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -1502,7 +1569,7 @@ class ProgramManager { broadcast, unchecked, edition, - useFeeMaster + useFeeMaster, ); } @@ -1673,7 +1740,7 @@ class ProgramManager { verifyingKey?: VerifyingKey, privateKey?: PrivateKey, offlineQuery?: OfflineQuery, - edition?: number + edition?: number, ): Promise<ExecutionResponse> { // Get the private key from the account if it is not provided in the parameters let executionPrivateKey = privateKey; @@ -1717,7 +1784,7 @@ class ProgramManager { verifyingKey, this.host, offlineQuery, - edition + edition, ); } @@ -1774,11 +1841,9 @@ class ProgramManager { ) { executionPrivateKey = this.account.privateKey(); feeAddress = this.account?.address(); - } - else if (typeof executionPrivateKey === "undefined") { + } else if (typeof executionPrivateKey === "undefined") { throw "No private key provided and no private key set in the ProgramManager"; - } - else { + } else { feeAddress = Address.from_private_key(executionPrivateKey); } @@ -1804,7 +1869,12 @@ class ProgramManager { let fee = priorityFee; // If a fee record wasn't provided, estimate the fee that needs to be paid. if (!feeRecord) { - const baseFee = Number(await this.estimateExecutionFee({programName: "credits.aleo", functionName: "join"})); + const baseFee = Number( + await this.estimateExecutionFee({ + programName: "credits.aleo", + functionName: "join", + }), + ); fee = baseFee + priorityFee; } @@ -1813,11 +1883,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -1845,11 +1915,13 @@ class ProgramManager { if (offlineQuery && !this.inclusionKeysLoaded) { try { const inclusionKeys = await this.keyProvider.inclusionKeys(); - WasmProgramManager.loadInclusionProver(inclusionKeys[0]) + WasmProgramManager.loadInclusionProver(inclusionKeys[0]); this.inclusionKeysLoaded = true; console.log("Successfully loaded inclusion key"); } catch { - logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`) + logAndThrow( + `Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`, + ); } } @@ -1951,11 +2023,13 @@ class ProgramManager { if (offlineQuery && !this.inclusionKeysLoaded) { try { const inclusionKeys = await this.keyProvider.inclusionKeys(); - WasmProgramManager.loadInclusionProver(inclusionKeys[0]) + WasmProgramManager.loadInclusionProver(inclusionKeys[0]); this.inclusionKeysLoaded = true; console.log("Successfully loaded inclusion key"); } catch { - logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`) + logAndThrow( + `Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`, + ); } } @@ -2110,11 +2184,11 @@ class ProgramManager { if (requiresAmountRecord(transferType)) { // If the transfer type is private and requires an amount record, get it from the record provider amountRecord = await this.getCreditsRecord( - priorityFee, - [], - amountRecord, - recordSearchParams, - ); + priorityFee, + [], + amountRecord, + recordSearchParams, + ); nonces.push(amountRecord.nonce()); } else { amountRecord = undefined; @@ -2125,11 +2199,11 @@ class ProgramManager { priorityFee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -2140,14 +2214,16 @@ class ProgramManager { // Load the inclusion prover offline. if (offlineQuery && !this.inclusionKeysLoaded) { const inclusionKeys = await this.keyProvider.inclusionKeys(); - WasmProgramManager.loadInclusionProver(inclusionKeys[0]) + WasmProgramManager.loadInclusionProver(inclusionKeys[0]); try { const inclusionKeys = await this.keyProvider.inclusionKeys(); - WasmProgramManager.loadInclusionProver(inclusionKeys[0]) + WasmProgramManager.loadInclusionProver(inclusionKeys[0]); this.inclusionKeysLoaded = true; console.log("Successfully loaded inclusion key"); } catch { - logAndThrow(`Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`) + logAndThrow( + `Inclusion key bytes not loaded, please ensure the program manager is initialized with a KeyProvider that includes the inclusion key.`, + ); } } @@ -3064,7 +3140,12 @@ class ProgramManager { * const isValid = programManager.verifyExecution(executionResponse, blockHeight, imports, importedVerifyingKeys); * assert(isValid); */ - verifyExecution(executionResponse: ExecutionResponse, blockHeight: number, imports?: ImportedPrograms, importedVerifyingKeys?: ImportedVerifyingKeys): boolean { + verifyExecution( + executionResponse: ExecutionResponse, + blockHeight: number, + imports?: ImportedPrograms, + importedVerifyingKeys?: ImportedVerifyingKeys, + ): boolean { try { const execution = <FunctionExecution>( executionResponse.getExecution() @@ -3079,7 +3160,7 @@ class ProgramManager { function_id, imports, importedVerifyingKeys, - blockHeight + blockHeight, ); } catch (e) { console.warn( @@ -3175,25 +3256,36 @@ class ProgramManager { * }); */ async estimateFeeForAuthorization( - options: FeeEstimateOptions + options: FeeEstimateOptions, ): Promise<bigint> { - const { - authorization, - programName, - program, - imports, - edition - } = options; + const { authorization, programName, program, imports, edition } = + options; if (!authorization) { - throw new Error("Authorization must be provided if estimating fee for Authorization.") + throw new Error( + "Authorization must be provided if estimating fee for Authorization.", + ); } - const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition); - const programImports = imports ? imports : await this.networkClient.getProgramImports(programSource); + const programSource = program + ? program.toString() + : await this.networkClient.getProgram(programName, edition); + const programImports = imports + ? imports + : await this.networkClient.getProgramImports(programSource); console.log(JSON.stringify(programImports)); if (Object.keys(programImports)) { - return WasmProgramManager.estimateFeeForAuthorization(authorization, programSource, programImports, edition); + return WasmProgramManager.estimateFeeForAuthorization( + authorization, + programSource, + programImports, + edition, + ); } - return WasmProgramManager.estimateFeeForAuthorization(authorization, programSource, imports, edition); + return WasmProgramManager.estimateFeeForAuthorization( + authorization, + programSource, + imports, + edition, + ); } /** @@ -3222,25 +3314,34 @@ class ProgramManager { * }); * const baseFeeCredits = Number(baseFeeMicrocredits)/1000000; */ - async estimateExecutionFee( - options: FeeEstimateOptions, - ): Promise<bigint> { - const { - functionName, - programName, - program, - imports, - edition - } = options; + async estimateExecutionFee(options: FeeEstimateOptions): Promise<bigint> { + const { functionName, programName, program, imports, edition } = + options; if (!functionName) { - throw new Error("Function name must be specified when estimating fee."); + throw new Error( + "Function name must be specified when estimating fee.", + ); } - const programSource = program ? program.toString() : await this.networkClient.getProgram(programName, edition); - const programImports = imports ? imports : await this.networkClient.getProgramImports(programSource); + const programSource = program + ? program.toString() + : await this.networkClient.getProgram(programName, edition); + const programImports = imports + ? imports + : await this.networkClient.getProgramImports(programSource); if (Object.keys(programImports)) { - return WasmProgramManager.estimateExecutionFee(programSource, functionName, programImports, edition); + return WasmProgramManager.estimateExecutionFee( + programSource, + functionName, + programImports, + edition, + ); } - return WasmProgramManager.estimateExecutionFee(programSource, functionName, imports, edition); + return WasmProgramManager.estimateExecutionFee( + programSource, + functionName, + imports, + edition, + ); } // Internal utility function for getting a credits.aleo record @@ -3253,22 +3354,26 @@ class ProgramManager { if (record) { try { return record instanceof RecordPlaintext - ? record : RecordPlaintext.fromString(<string>record); + ? record + : RecordPlaintext.fromString(<string>record); } catch { logAndThrow(`Record '${record}' could not be parsed, please ensure a valid credits.aleo record - is passed prior to trying again`) + is passed prior to trying again`); } } else { try { const recordProvider = <RecordProvider>this.recordProvider; - const record = await recordProvider.findCreditsRecord( - amount, - { ...params, unspent: true, nonces } - ); + const record = await recordProvider.findCreditsRecord(amount, { + ...params, + unspent: true, + nonces, + }); if (record.record_plaintext) { return RecordPlaintext.fromString(record.record_plaintext); } else { - logAndThrow("Failed to deserialize record returned from record provider"); + logAndThrow( + "Failed to deserialize record returned from record provider", + ); } } catch (e: any) { logAndThrow( @@ -3289,7 +3394,7 @@ class ProgramManager { * @example * /// Import the mainnet version of the sdk. * import { AleoKeyProvider, getOrInitConsensusVersionTestHeights, ProgramManager, NetworkRecordProvider } from "@provablehq/sdk/mainnet.js"; - * + * * // Initialize the development consensus heights in order to work with devnode. * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12"); * @@ -3341,7 +3446,8 @@ class ProgramManager { // Ensure the function exists on the network if (program === undefined) { try { - programObject = await this.networkClient.getProgramObject(programName); + programObject = + await this.networkClient.getProgramObject(programName); program = <string>programObject.toString(); } catch (e: any) { logAndThrow( @@ -3352,7 +3458,9 @@ class ProgramManager { try { programObject = Program.fromString(program); } catch (e: any) { - logAndThrow(`Program sources passed for ${programName} were invalid: ${e}`); + logAndThrow( + `Program sources passed for ${programName} were invalid: ${e}`, + ); } } else if (program instanceof Program) { programObject = program; @@ -3370,9 +3478,14 @@ class ProgramManager { if (edition == undefined) { try { - edition = await this.networkClient.getLatestProgramEdition(programName); + edition = + await this.networkClient.getLatestProgramEdition( + programName, + ); } catch (e: any) { - console.warn(`Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`); + console.warn( + `Error finding edition for ${programName}. Network response: '${e.message}'. Assuming edition 0.`, + ); edition = 0; } } @@ -3396,10 +3509,20 @@ class ProgramManager { let fee = priorityFee; // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. if (!feeRecord) { - console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + console.log( + "Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.", + ); const programString = programObject.toString(); - const imports = await this.networkClient.getProgramImports(programString); - const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + const imports = + await this.networkClient.getProgramImports( + programString, + ); + const baseFee = Number( + WasmProgramManager.estimateDeploymentFee( + programString, + imports, + ), + ); fee = baseFee + priorityFee; } @@ -3408,11 +3531,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -3433,7 +3556,7 @@ class ProgramManager { ); } } - + // Build a transaction without a proof return await WasmProgramManager.buildDevnodeExecutionTransaction( executionPrivateKey, @@ -3444,10 +3567,10 @@ class ProgramManager { feeRecord, this.host, imports, - edition + edition, ); } - + /** * Builds a deployment transaction with placeholder certificates and verifying keys for each function in the program. * Intended for use with a local devnode. @@ -3459,7 +3582,7 @@ class ProgramManager { * @example * /// Import the mainnet version of the sdk. * import { ProgramManager, NetworkRecordProvider, getOrInitConsensusVersionTestHeights } from "@provablehq/sdk/mainnet.js"; - * + * * // Initialize the development consensus heights in order to work with a local devnode. * getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12"); * @@ -3486,9 +3609,10 @@ class ProgramManager { * }, 20000); */ async buildDevnodeDeploymentTransaction( - options: DeployOptions + options: DeployOptions, ): Promise<Transaction> { - const { program, priorityFee, privateFee, recordSearchParams } = options; + const { program, priorityFee, privateFee, recordSearchParams } = + options; let feeRecord = options.feeRecord; let privateKey = options.privateKey; @@ -3516,7 +3640,9 @@ class ProgramManager { ); } if (typeof programSource === "string") { - throw Error(`Program ${programObject.id()} already exists on the network, please rename your program`); + throw Error( + `Program ${programObject.id()} already exists on the network, please rename your program`, + ); } } catch (e: any) { logAndThrow(`Error validating program: ${e.message}`); @@ -3541,10 +3667,20 @@ class ProgramManager { let fee = priorityFee; // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. if (!feeRecord) { - console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + console.log( + "Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.", + ); const programString = programObject.toString(); - const imports = await this.networkClient.getProgramImports(programString); - const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + const imports = + await this.networkClient.getProgramImports( + programString, + ); + const baseFee = Number( + WasmProgramManager.estimateDeploymentFee( + programString, + imports, + ), + ); fee = baseFee + priorityFee; } @@ -3553,11 +3689,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -3574,7 +3710,7 @@ class ProgramManager { `Error finding program imports. Network response: '${e.message}'. Please ensure you're connected to a valid Aleo network and the program is deployed to the network.`, ); } - + return await WasmProgramManager.buildDevnodeDeploymentTransaction( deploymentPrivateKey, program, @@ -3619,9 +3755,10 @@ class ProgramManager { * }, 20000); */ async buildDevnodeUpgradeTransaction( - options: DeployOptions + options: DeployOptions, ): Promise<Transaction> { - const { program, priorityFee, privateFee, recordSearchParams } = options; + const { program, priorityFee, privateFee, recordSearchParams } = + options; let feeRecord = options.feeRecord; let privateKey = options.privateKey; @@ -3671,10 +3808,20 @@ class ProgramManager { let fee = priorityFee; // If a private fee is specified, but no fee record is provided, estimate the fee and find a matching record. if (!feeRecord) { - console.log("Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.") + console.log( + "Private fee specified, but no private fee record provided, estimating fee and finding a matching fee record.", + ); const programString = programObject.toString(); - const imports = await this.networkClient.getProgramImports(programString); - const baseFee = Number(WasmProgramManager.estimateDeploymentFee(programString, imports)); + const imports = + await this.networkClient.getProgramImports( + programString, + ); + const baseFee = Number( + WasmProgramManager.estimateDeploymentFee( + programString, + imports, + ), + ); fee = baseFee + priorityFee; } @@ -3683,11 +3830,11 @@ class ProgramManager { fee, [], feeRecord, - recordSearchParams - ) + recordSearchParams, + ); } else { // If it's specified NOT to use a privateFee, use a public fee. - feeRecord = undefined + feeRecord = undefined; } } catch (e: any) { logAndThrow( @@ -3725,8 +3872,14 @@ function validateTransferType(transferType: string): string { return VALID_TRANSFER_TYPES.has(transferType) ? transferType : logAndThrow( - `Invalid transfer type '${transferType}'. Valid transfer types are 'private', 'privateToPublic', 'public', and 'publicToPrivate'.`, - ); + `Invalid transfer type '${transferType}'. Valid transfer types are 'private', 'privateToPublic', 'public', and 'publicToPrivate'.`, + ); } -export { ProgramManager, AuthorizationOptions, FeeAuthorizationOptions, ExecuteOptions, ProvingRequestOptions }; +export { + ProgramManager, + AuthorizationOptions, + FeeAuthorizationOptions, + ExecuteOptions, + ProvingRequestOptions, +}; diff --git a/sdk/src/record-provider.ts b/sdk/src/record-provider.ts index 1e166eb39..7096f05b6 100644 --- a/sdk/src/record-provider.ts +++ b/sdk/src/record-provider.ts @@ -25,8 +25,11 @@ interface RecordProvider { * @param {RecordSearchParams} recordsFilter The filter used to find the records. * @param {RecordsResponseFilter} responseFilter The filter used to filter the response. * @returns {Promise<EncryptedRecord[]>} The encrypted records. - */ - encryptedRecords(recordsFilter: RecordSearchParams, responseFilter?: RecordsResponseFilter): Promise<EncryptedRecord[]>; + */ + encryptedRecords( + recordsFilter: RecordSearchParams, + responseFilter?: RecordsResponseFilter, + ): Promise<EncryptedRecord[]>; /** * Check if a list of serial numbers exist in the chosen provider. @@ -34,7 +37,9 @@ interface RecordProvider { * @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>>; + checkSerialNumbers( + serialNumbers: string[], + ): Promise<Record<string, boolean>>; /** * Check if a list of tags exist in the chosen provider. @@ -64,7 +69,10 @@ interface RecordProvider { * const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5); */ - findCreditsRecord(microcredits: number, searchParameters: RecordSearchParams): Promise<OwnedRecord>; + findCreditsRecord( + microcredits: number, + searchParameters: RecordSearchParams, + ): Promise<OwnedRecord>; /** * Find a list of credit.aleo records with a given number of microcredits from the chosen provider @@ -88,7 +96,10 @@ interface RecordProvider { * const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5); */ - findCreditsRecords(microcreditAmounts: number[], searchParameters: RecordSearchParams): Promise<OwnedRecord[]>; + findCreditsRecords( + microcreditAmounts: number[], + searchParameters: RecordSearchParams, + ): Promise<OwnedRecord[]>; /** * Find an arbitrary record @@ -226,25 +237,43 @@ class NetworkRecordProvider implements RecordProvider { * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5); * * */ - async findCreditsRecords(microcredits: number[], searchParameters: RecordSearchParams): Promise<OwnedRecord[]> { + async findCreditsRecords( + microcredits: number[], + searchParameters: RecordSearchParams, + ): Promise<OwnedRecord[]> { let startHeight = 0; let endHeight = 0; let maxAmount = undefined; if (searchParameters) { - if ("startHeight" in searchParameters && typeof searchParameters["startHeight"] == "number") { + if ( + "startHeight" in searchParameters && + typeof searchParameters["startHeight"] == "number" + ) { startHeight = searchParameters["startHeight"]; } - if ("endHeight" in searchParameters && typeof searchParameters["endHeight"] == "number") { + if ( + "endHeight" in searchParameters && + typeof searchParameters["endHeight"] == "number" + ) { endHeight = searchParameters["endHeight"]; } - if ("amounts" in searchParameters && Array.isArray(searchParameters["amounts"]) && searchParameters["amount"].every((item: any) => typeof item === 'number')) { + if ( + "amounts" in searchParameters && + Array.isArray(searchParameters["amounts"]) && + searchParameters["amount"].every( + (item: any) => typeof item === "number", + ) + ) { microcredits = searchParameters["amounts"]; } - if ("maxAmount" in searchParameters && typeof searchParameters["maxAmount"] == "number") { + if ( + "maxAmount" in searchParameters && + typeof searchParameters["maxAmount"] == "number" + ) { maxAmount = searchParameters["maxAmount"]; } } @@ -260,12 +289,21 @@ class NetworkRecordProvider implements RecordProvider { logAndThrow("Start height must be less than end height"); } - const recordsPts = await this.networkClient.findRecords(startHeight, endHeight, searchParameters.unspent, ["credits.aleo"], microcredits, maxAmount, searchParameters.nonces, this.account.privateKey()); + const recordsPts = await this.networkClient.findRecords( + startHeight, + endHeight, + searchParameters.unspent, + ["credits.aleo"], + microcredits, + maxAmount, + searchParameters.nonces, + this.account.privateKey(), + ); return recordsPts.map((record) => ({ - owner: record.owner().toString(), - program_name: 'credits.aleo', - record_name: 'credits', - record_plaintext: record.toString(), + owner: record.owner().toString(), + program_name: "credits.aleo", + record_name: "credits", + record_plaintext: record.toString(), })); } @@ -294,11 +332,17 @@ class NetworkRecordProvider implements RecordProvider { * const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider, recordProvider); * programManager.transfer(1, "aleo166q6ww6688cug7qxwe7nhctjpymydwzy2h7rscfmatqmfwnjvggqcad0at", "public", 0.5); */ - async findCreditsRecord(microcredits: number, searchParameters: RecordSearchParams): Promise<OwnedRecord> { + async findCreditsRecord( + microcredits: number, + searchParameters: RecordSearchParams, + ): Promise<OwnedRecord> { let records = null; try { - records = await this.findCreditsRecords([microcredits], searchParameters); + records = await this.findCreditsRecords( + [microcredits], + searchParameters, + ); } catch (e) { console.log("No records found with error:", e); } @@ -314,7 +358,9 @@ class NetworkRecordProvider implements RecordProvider { /** * Find an arbitrary record. WARNING: This function is not implemented yet and will throw an error. */ - async findRecord(searchParameters: RecordSearchParams): Promise<OwnedRecord> { + async findRecord( + searchParameters: RecordSearchParams, + ): Promise<OwnedRecord> { let records; try { @@ -334,7 +380,9 @@ class NetworkRecordProvider implements RecordProvider { /** * Find multiple records from a specified program. */ - async findRecords(searchParameters: RecordSearchParams): Promise<OwnedRecord[]> { + async findRecords( + searchParameters: RecordSearchParams, + ): Promise<OwnedRecord[]> { let startHeight = 0; let endHeight = 0; let amounts = undefined; @@ -342,27 +390,51 @@ class NetworkRecordProvider implements RecordProvider { let programs = undefined; if (searchParameters) { - if ("startHeight" in searchParameters && typeof searchParameters["startHeight"] == "number") { + if ( + "startHeight" in searchParameters && + typeof searchParameters["startHeight"] == "number" + ) { startHeight = searchParameters["startHeight"]; } - if ("endHeight" in searchParameters && typeof searchParameters["endHeight"] == "number") { + if ( + "endHeight" in searchParameters && + typeof searchParameters["endHeight"] == "number" + ) { endHeight = searchParameters["endHeight"]; } - if ("amounts" in searchParameters && Array.isArray(searchParameters["amounts"]) && searchParameters["amounts"].every((item: any) => typeof item === 'number')) { + if ( + "amounts" in searchParameters && + Array.isArray(searchParameters["amounts"]) && + searchParameters["amounts"].every( + (item: any) => typeof item === "number", + ) + ) { amounts = searchParameters["amounts"]; } - if ("maxAmount" in searchParameters && typeof searchParameters["maxAmount"] == "number") { + if ( + "maxAmount" in searchParameters && + typeof searchParameters["maxAmount"] == "number" + ) { maxAmount = searchParameters["maxAmount"]; } - if ("program" in searchParameters && typeof searchParameters["program"] == "string") { + if ( + "program" in searchParameters && + typeof searchParameters["program"] == "string" + ) { programs = [searchParameters["program"]]; } - if ("programs" in searchParameters && Array.isArray(searchParameters["programs"]) && searchParameters["programs"].every((item: any) => typeof item === "string")) { + if ( + "programs" in searchParameters && + Array.isArray(searchParameters["programs"]) && + searchParameters["programs"].every( + (item: any) => typeof item === "string", + ) + ) { programs = searchParameters["programs"]; } } @@ -378,17 +450,31 @@ class NetworkRecordProvider implements RecordProvider { logAndThrow("Start height must be less than end height"); } - const recordPts = await this.networkClient.findRecords(startHeight, endHeight, searchParameters.unspent, programs, amounts, maxAmount, searchParameters.nonces, this.account.privateKey()); + const recordPts = await this.networkClient.findRecords( + startHeight, + endHeight, + searchParameters.unspent, + programs, + amounts, + maxAmount, + searchParameters.nonces, + this.account.privateKey(), + ); return recordPts.map((record) => ({ record_plaintext: record.toString(), })); } - async encryptedRecords(recordsFilter: RecordSearchParams, responseFilter?: RecordsResponseFilter): Promise<EncryptedRecord[]> { + async encryptedRecords( + recordsFilter: RecordSearchParams, + responseFilter?: RecordsResponseFilter, + ): Promise<EncryptedRecord[]> { throw new Error("Not implemented"); } - async checkSerialNumbers(serialNumbers: string[]): Promise<Record<string, boolean>> { + async checkSerialNumbers( + serialNumbers: string[], + ): Promise<Record<string, boolean>> { throw new Error("Not implemented"); } @@ -426,8 +512,4 @@ class BlockHeightSearch implements RecordSearchParams { } } -export { - BlockHeightSearch, - NetworkRecordProvider, - RecordProvider, -}; \ No newline at end of file +export { BlockHeightSearch, NetworkRecordProvider, RecordProvider }; diff --git a/sdk/src/record-scanner.ts b/sdk/src/record-scanner.ts index 73318256a..f9f943c57 100644 --- a/sdk/src/record-scanner.ts +++ b/sdk/src/record-scanner.ts @@ -5,7 +5,13 @@ import { EncryptedRegistrationRequest } from "./models/record-scanner/encryptedR import { OwnedFilter } from "./models/record-scanner/ownedFilter"; import { OwnedRecord } from "./models/record-provider/ownedRecord"; import { RecordProvider } from "./record-provider"; -import { Field, Poseidon4, RecordCiphertext, RecordPlaintext, ViewKey } from "./wasm"; +import { + Field, + Poseidon4, + RecordCiphertext, + RecordPlaintext, + ViewKey, +} from "./wasm"; import { RecordsFilter } from "./models/record-scanner/recordsFilter"; import { RegisterResult } from "./models/record-scanner/registrationResult.js"; import { RevokeResult } from "./models/record-scanner/revokeResult.js"; @@ -55,7 +61,7 @@ export interface RecordScannerJWTData { */ export interface RecordScannerOptions { url: string; - apiKey?: string | { header: string, value: string }; + apiKey?: string | { header: string; value: string }; consumerId?: string; jwtData?: RecordScannerJWTData; viewKeys?: ViewKey[]; @@ -112,7 +118,7 @@ class RecordScanner implements RecordProvider { readonly cacheViewKeysOnRegister?: boolean; readonly url: string; private readonly baseUrl: string; - private apiKey?: { header: string, value: string }; + private apiKey?: { header: string; value: string }; private consumerId?: string; private jwtData?: RecordScannerJWTData; private uuid?: Field; @@ -129,8 +135,13 @@ class RecordScanner implements RecordProvider { const network = "/%%NETWORK%%"; // If the user has configured a network in their uri, throw. - if (options.url.endsWith("/mainnet") || options.url.endsWith("/testnet")) { - throw new Error("The record scanning url should not include the specific network, this is automatically configured by the Provable SDK."); + if ( + options.url.endsWith("/mainnet") || + options.url.endsWith("/testnet") + ) { + throw new Error( + "The record scanning url should not include the specific network, this is automatically configured by the Provable SDK.", + ); } // Configure the url to use the network the SDK is using. @@ -140,8 +151,13 @@ class RecordScanner implements RecordProvider { this.url = options.url + network; // Get any view keys passed in the options. - this.viewKeys = options.viewKeys ? - Object.fromEntries(options.viewKeys.map(viewKey => [this.computeUUID(viewKey), viewKey])) + this.viewKeys = options.viewKeys + ? Object.fromEntries( + options.viewKeys.map((viewKey) => [ + this.computeUUID(viewKey), + viewKey, + ]), + ) : undefined; // Set the view key caching flag if provided (default: true). @@ -157,10 +173,13 @@ class RecordScanner implements RecordProvider { } // Configure authentication options. - this.apiKey = typeof options.apiKey === "string" ? { - header: "X-Provable-API-Key", - value: options.apiKey - } : options.apiKey; + this.apiKey = + typeof options.apiKey === "string" + ? { + header: "X-Provable-API-Key", + value: options.apiKey, + } + : options.apiKey; this.consumerId = options.consumerId; this.jwtData = options.jwtData; this.autoReRegister = options.autoReRegister; @@ -172,8 +191,11 @@ class RecordScanner implements RecordProvider { * * @param {string | { header: string, value: string }} apiKey The API key to use for the record scanner. */ - setApiKey(apiKey: string | { header: string, value: string }) { - this.apiKey = typeof apiKey === "string" ? { header: "X-Provable-API-Key", value: apiKey } : apiKey; + setApiKey(apiKey: string | { header: string; value: string }) { + this.apiKey = + typeof apiKey === "string" + ? { header: "X-Provable-API-Key", value: apiKey } + : apiKey; } /** @@ -219,7 +241,9 @@ class RecordScanner implements RecordProvider { */ addViewKey(viewKey: ViewKey) { const uuid = this.computeUUID(viewKey).toString(); - this.viewKeys = this.viewKeys ? { ...this.viewKeys, [uuid]: viewKey } : { [uuid]: viewKey }; + this.viewKeys = this.viewKeys + ? { ...this.viewKeys, [uuid]: viewKey } + : { [uuid]: viewKey }; } /** @@ -246,7 +270,8 @@ class RecordScanner implements RecordProvider { if (cachedVk) return cachedVk; // Otherwise use the account's view key if it matches this UUID. const accountVk = this.account?.viewKey(); - if (accountVk && this.computeUUID(accountVk).toString() === uuid) return accountVk; + if (accountVk && this.computeUUID(accountVk).toString() === uuid) + return accountVk; return undefined; } @@ -276,15 +301,15 @@ class RecordScanner implements RecordProvider { * @param {string} consumerId The consumer ID for the JWT endpoint. * @returns {Promise<RecordScannerJWTData>} The new JWT data. */ - private async refreshJwt(apiKey: string, consumerId: string): Promise<RecordScannerJWTData> { - const response = await post( - `${this.baseUrl}/jwts/${consumerId}`, - { - headers: { - "X-Provable-API-Key": apiKey, - }, - } - ); + private async refreshJwt( + apiKey: string, + consumerId: string, + ): Promise<RecordScannerJWTData> { + const response = await post(`${this.baseUrl}/jwts/${consumerId}`, { + headers: { + "X-Provable-API-Key": apiKey, + }, + }); const authHeader = response.headers.get("authorization"); if (!authHeader) { throw new Error("No authorization header in JWT refresh response"); @@ -304,7 +329,8 @@ class RecordScanner implements RecordProvider { private async getAuthHeaders(): Promise<Record<string, string>> { let jwtData = this.jwtData; // Consider JWT expired a few minutes early to avoid race at boundary. - const isExpired = jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES; + const isExpired = + jwtData && Date.now() >= jwtData.expiration - FIVE_MINUTES; if (!jwtData || isExpired) { const apiKey = this.apiKey?.value; if (apiKey && this.consumerId) { @@ -322,11 +348,14 @@ class RecordScanner implements RecordProvider { /** * 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) { - this.uuid = keyMaterial instanceof ViewKey ? this.computeUUID(keyMaterial) : keyMaterial; + this.uuid = + keyMaterial instanceof ViewKey + ? this.computeUUID(keyMaterial) + : keyMaterial; } /** @@ -355,7 +384,10 @@ class RecordScanner implements RecordProvider { * @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. */ - async register(viewKey: ViewKey, startBlock: number): Promise<RegisterResult> { + async register( + viewKey: ViewKey, + startBlock: number, + ): Promise<RegisterResult> { try { const request: RegistrationRequest = { view_key: viewKey.to_string(), @@ -394,7 +426,7 @@ class RecordScanner implements RecordProvider { */ async getPubkey(): Promise<CryptoBoxPubKey> { const response = await this.request( - new Request(`${this.url}/pubkey`, { method: "GET" }) + new Request(`${this.url}/pubkey`, { method: "GET" }), ); return parseJSON(await response.text()) as CryptoBoxPubKey; } @@ -414,7 +446,11 @@ class RecordScanner implements RecordProvider { // Get the ephemeral public key from the record scanner. const pubkey = await this.getPubkey(); // Encrypt the registration request using the ephemeral public key. - const ciphertext = encryptRegistrationRequest(pubkey.public_key, viewKey, startBlock); + const ciphertext = encryptRegistrationRequest( + pubkey.public_key, + viewKey, + startBlock, + ); const payload: EncryptedRegistrationRequest = { key_id: pubkey.key_id, ciphertext, @@ -458,7 +494,10 @@ class RecordScanner implements RecordProvider { this.viewKeys = undefined; } } - if (this.account != null && this.computeUUID(this.account.viewKey()).toString() === uuidStr) { + if ( + this.account != null && + this.computeUUID(this.account.viewKey()).toString() === uuidStr + ) { this.account = undefined; } } @@ -475,9 +514,14 @@ class RecordScanner implements RecordProvider { async revoke(uuid?: string | Field): Promise<RevokeResult> { const resolvedUuid = uuid ?? this.uuid; if (!resolvedUuid) { - throw new UUIDError("No UUID configured for the record scanner and no UUID provided."); + throw new UUIDError( + "No UUID configured for the record scanner and no UUID provided.", + ); } - const uuidStr = typeof resolvedUuid === "string" ? resolvedUuid : resolvedUuid.toString(); + const uuidStr = + typeof resolvedUuid === "string" + ? resolvedUuid + : resolvedUuid.toString(); if (!this.uuidIsValid(uuidStr)) { throw new UUIDError(`UUID provided ${uuidStr} is invalid`, uuidStr); } @@ -504,7 +548,9 @@ class RecordScanner implements RecordProvider { * @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. */ - async encrypted(recordsFilter: RecordsFilter): Promise<EncryptedRecordsResult> { + async encrypted( + recordsFilter: RecordsFilter, + ): Promise<EncryptedRecordsResult> { try { const response = await this.request( new Request(`${this.url}/records/encrypted`, { @@ -526,10 +572,15 @@ class RecordScanner implements RecordProvider { * @param {RecordsFilter} recordsFilter The filter to use to find the records and filter the response. * @returns {Promise<EncryptedRecord[]>} The encrypted records. */ - async encryptedRecords(recordsFilter: RecordsFilter): Promise<EncryptedRecord[]> { + async encryptedRecords( + recordsFilter: RecordsFilter, + ): Promise<EncryptedRecord[]> { const result = await this.encrypted(recordsFilter); if (result.ok) return result.data; - throw new RecordScannerRequestError(result.error.message, result.status); + throw new RecordScannerRequestError( + result.error.message, + result.status, + ); } /** @@ -560,10 +611,15 @@ class RecordScanner implements RecordProvider { * @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. */ - async checkSerialNumbers(serialNumbers: string[]): Promise<Record<string, boolean>> { + async checkSerialNumbers( + serialNumbers: string[], + ): Promise<Record<string, boolean>> { const result = await this.serialNumbers(serialNumbers); if (result.ok) return result.data; - throw new RecordScannerRequestError(result.error.message, result.status); + throw new RecordScannerRequestError( + result.error.message, + result.status, + ); } /** @@ -591,14 +647,17 @@ class RecordScanner implements RecordProvider { /** * 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. */ async checkTags(tags: string[]): Promise<Record<string, boolean>> { const result = await this.tags(tags); if (result.ok) return result.data; - throw new RecordScannerRequestError(result.error.message, result.status); + throw new RecordScannerRequestError( + result.error.message, + result.status, + ); } /** @@ -637,7 +696,7 @@ class RecordScanner implements RecordProvider { /** * Find a record in the record scanning service. - * + * * @param {OwnedFilter} searchParameters The filter to use to find the record. * @returns {Promise<OwnedRecord>} The record. */ @@ -688,7 +747,9 @@ class RecordScanner implements RecordProvider { }; // When decryption is enabled and a view key exists for this UUID, decrypt records in place before returning. - const attemptDecrypt = (result: OwnedRecordsResult): OwnedRecordsResult => { + const attemptDecrypt = ( + result: OwnedRecordsResult, + ): OwnedRecordsResult => { if (result.ok && this.decryptEnabled) { const viewKey = this.getViewKeyForUuid(uuid); if (viewKey) this.decrypt(viewKey, result.data); @@ -720,7 +781,7 @@ class RecordScanner implements RecordProvider { /** * Find records using the record scanning service. - * + * * @param {OwnedFilter} searchParameters The filter to use to find the records. * @returns {Promise<OwnedRecord[]>} The records. */ @@ -729,7 +790,10 @@ class RecordScanner implements RecordProvider { const result = await this.owned(searchParameters); // If the request was successful, return the records otherwise throw an error. if (result.ok) return result.data; - throw new RecordScannerRequestError(result.error.message, result.status); + throw new RecordScannerRequestError( + result.error.message, + result.status, + ); } /** @@ -768,15 +832,22 @@ class RecordScanner implements RecordProvider { /** * 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. */ - async findCreditsRecord(microcredits: number, searchParameters: OwnedFilter): Promise<OwnedRecord> { + async findCreditsRecord( + microcredits: number, + searchParameters: OwnedFilter, + ): Promise<OwnedRecord> { const uuid = <string>this.getUUID(searchParameters); if (!uuid) { - throw new UUIDError(`No uuid found in the record scanner filter`, uuid, searchParameters); + throw new UUIDError( + `No uuid found in the record scanner filter`, + uuid, + searchParameters, + ); } if (!this.decryptEnabled) { @@ -807,11 +878,13 @@ class RecordScanner implements RecordProvider { }); // First record whose plaintext microcredits >= requested amount. - const record = records.find(r => { + const record = records.find((r) => { const plaintext = this.getPlaintext(r); if (!plaintext) return false; try { - const amountStr = plaintext.getMember("microcredits").toString(); + const amountStr = plaintext + .getMember("microcredits") + .toString(); const amount = parseInt(amountStr.replace("u64", "")); return amount >= microcredits; } catch { @@ -835,15 +908,22 @@ class RecordScanner implements RecordProvider { /** * 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 */ - async findCreditsRecords(microcreditAmounts: number[], searchParameters: OwnedFilter): Promise<OwnedRecord[]> { + async findCreditsRecords( + microcreditAmounts: number[], + searchParameters: OwnedFilter, + ): Promise<OwnedRecord[]> { const uuid = <string>this.getUUID(searchParameters); if (!uuid) { - throw new UUIDError(`No uuid found in the record scanner filter, and none configured within the record scanner`, uuid, searchParameters); + throw new UUIDError( + `No uuid found in the record scanner filter, and none configured within the record scanner`, + uuid, + searchParameters, + ); } if (!this.decryptEnabled) { @@ -873,12 +953,16 @@ class RecordScanner implements RecordProvider { uuid, }); // Keep only records whose plaintext microcredits match one of the requested amounts. - return records.filter(r => { + return records.filter((r) => { const plaintext = this.getPlaintext(r); if (!plaintext) return false; try { - const amount = plaintext.getMember("microcredits").toString(); - return microcreditAmounts.includes(parseInt(amount.replace("u64", ""))); + const amount = plaintext + .getMember("microcredits") + .toString(); + return microcreditAmounts.includes( + parseInt(amount.replace("u64", "")), + ); } catch { return false; } @@ -913,7 +997,8 @@ class RecordScanner implements RecordProvider { if (!response.ok) { const text = await response.text(); throw new RecordScannerRequestError( - text || `Request to ${req.url} failed with status ${response.status}`, + text || + `Request to ${req.url} failed with status ${response.status}`, response.status, ); } @@ -933,7 +1018,11 @@ class RecordScanner implements RecordProvider { */ computeUUID(viewKey: ViewKey): Field { // Construct the material needed for the Poseidon oracle. - const inputs = [Field.newDomainSeparator(RECORD_DOMAIN), viewKey.toField(), Field.one()] + const inputs = [ + Field.newDomainSeparator(RECORD_DOMAIN), + viewKey.toField(), + Field.one(), + ]; // Calculate the uuid. const hasher = new Poseidon4(); return hasher.hash(inputs); diff --git a/sdk/src/security.ts b/sdk/src/security.ts index e165bbcea..d274481cc 100644 --- a/sdk/src/security.ts +++ b/sdk/src/security.ts @@ -10,7 +10,10 @@ await sodium.ready; * * @returns {string} the encrypted authorization in RFC 4648 standard Base64. */ -export function encryptAuthorization(publicKey: string, authorization: Authorization): string { +export function encryptAuthorization( + publicKey: string, + authorization: Authorization, +): string { // Ready the cryptobox lib. return encryptMessage(publicKey, authorization.toBytesLe()); } @@ -23,7 +26,10 @@ export function encryptAuthorization(publicKey: string, authorization: Authoriza * * @returns {string} the encrypted ProvingRequest in RFC 4648 standard Base64. */ -export function encryptProvingRequest(publicKey: string, provingRequest: ProvingRequest): string { +export function encryptProvingRequest( + publicKey: string, + provingRequest: ProvingRequest, +): string { return encryptMessage(publicKey, provingRequest.toBytesLe()); } @@ -48,7 +54,11 @@ export function encryptViewKey(publicKey: string, viewKey: ViewKey): string { * * @returns {string} the encrypted view key in RFC 4648 standard Base64. */ -export function encryptRegistrationRequest(publicKey: string, viewKey: ViewKey, start: number): string { +export function encryptRegistrationRequest( + publicKey: string, + viewKey: ViewKey, + start: number, +): string { // Turn the view key into a Uint8Array. const vk_bytes: Uint8Array = viewKey.toBytesLe(); // Create a new array to hold the original bytes and the 4-byte start height. @@ -107,6 +117,12 @@ export function zeroizeBytes(bytes: Uint8Array): void { * @returns {string} the encrypted bytes in RFC 4648 standard Base64. */ function encryptMessage(publicKey: string, message: Uint8Array): string { - const publicKeyBytes = sodium.from_base64(publicKey, sodium.base64_variants.ORIGINAL); - return sodium.to_base64(sodium.crypto_box_seal(message, publicKeyBytes), sodium.base64_variants.ORIGINAL); + const publicKeyBytes = sodium.from_base64( + publicKey, + sodium.base64_variants.ORIGINAL, + ); + return sodium.to_base64( + sodium.crypto_box_seal(message, publicKeyBytes), + sodium.base64_variants.ORIGINAL, + ); } diff --git a/sdk/src/utils.ts b/sdk/src/utils.ts index 4d0d53fc4..b813c49cc 100644 --- a/sdk/src/utils.ts +++ b/sdk/src/utils.ts @@ -1,11 +1,17 @@ function detectBrowser() { const userAgent = navigator.userAgent; - if (/chrome|crios|crmo/i.test(userAgent) && !/edge|edg|opr/i.test(userAgent)) { + if ( + /chrome|crios|crmo/i.test(userAgent) && + !/edge|edg|opr/i.test(userAgent) + ) { return "chrome"; } else if (/firefox|fxios/i.test(userAgent)) { return "firefox"; - } else if (/safari/i.test(userAgent) && !/chrome|crios|crmo|android/i.test(userAgent)) { + } else if ( + /safari/i.test(userAgent) && + !/chrome|crios|crmo|android/i.test(userAgent) + ) { return "safari"; } else if (/edg/i.test(userAgent)) { return "edge"; @@ -17,19 +23,20 @@ function detectBrowser() { } export function isNode(): boolean { - return typeof process !== "undefined" && - process.versions != null && - process.versions.node != null; + return ( + typeof process !== "undefined" && + process.versions != null && + process.versions.node != null + ); } export function environment() { - if ((typeof process !== 'undefined') && - (process.release?.name === 'node')) { - return 'node'; - } else if (typeof window !== 'undefined') { + if (typeof process !== "undefined" && process.release?.name === "node") { + return "node"; + } else if (typeof window !== "undefined") { return detectBrowser(); } else { - return 'unknown'; + return "unknown"; } } @@ -69,7 +76,7 @@ export async function post(url: URL | string, options: RequestInit) { const error = await response.text(); let message = `${response.status} error received from ${url}`; if (error) { - message = `${error}` + message = `${error}`; } throw new Error(message); } diff --git a/sdk/src/wasm.ts b/sdk/src/wasm.ts index 2762cf701..4fabe58b8 100644 --- a/sdk/src/wasm.ts +++ b/sdk/src/wasm.ts @@ -15,8 +15,8 @@ export { Field, GraphKey, Group, - I8, - I16, + I8, + I16, I32, I64, I128, diff --git a/sdk/tests/account.test.ts b/sdk/tests/account.test.ts index 2c2527817..f1c113a04 100644 --- a/sdk/tests/account.test.ts +++ b/sdk/tests/account.test.ts @@ -1,15 +1,32 @@ import sinon from "sinon"; import { expect } from "chai"; -import { Account, Address, ComputeKey, PrivateKey, RecordCiphertext, ViewKey, zeroizeBytes } from "../src/node.js"; -import { seed, message, beaconPrivateKeyString, beaconViewKeyString, beaconAddressString, recordCiphertextString, foreignCiphertextString, recordPlaintextString } from "./data/account-data.js"; - -describe('Account', () => { +import { + Account, + Address, + ComputeKey, + PrivateKey, + RecordCiphertext, + ViewKey, + zeroizeBytes, +} from "../src/node.js"; +import { + seed, + message, + beaconPrivateKeyString, + beaconViewKeyString, + beaconAddressString, + recordCiphertextString, + foreignCiphertextString, + recordPlaintextString, +} from "./data/account-data.js"; + +describe("Account", () => { afterEach(() => { sinon.restore(); }); - describe('constructors', () => { - it('creates a new account if no parameters are passed', () => { + describe("constructors", () => { + it("creates a new account if no parameters are passed", () => { // Generate account from rng const account = new Account(); @@ -25,9 +42,9 @@ describe('Account', () => { expect(account.computeKey()).instanceOf(ComputeKey); }); - it('creates a new from seed', () => { + it("creates a new from seed", () => { // Generate account from a seed - const account = new Account({seed: seed}); + const account = new Account({ seed: seed }); // Test object member type consistency expect(account._privateKey).instanceof(PrivateKey); @@ -40,19 +57,23 @@ describe('Account', () => { expect(account.address()).instanceof(Address); expect(account.computeKey()).instanceOf(ComputeKey); // Test that expected output is generated - expect(account.privateKey().to_string()).equal(beaconPrivateKeyString); + expect(account.privateKey().to_string()).equal( + beaconPrivateKeyString, + ); expect(account.viewKey().to_string()).equal(beaconViewKeyString); expect(account.address().to_string()).equal(beaconAddressString); expect(account.toString()).equal(beaconAddressString); }); - it('throws an error if parameters are invalid', () => { - expect(() => new Account({privateKey: 'invalidPrivateKey'})).throw(); + it("throws an error if parameters are invalid", () => { + expect( + () => new Account({ privateKey: "invalidPrivateKey" }), + ).throw(); }); - it('creates an account object from a valid private key string', () => { + it("creates an account object from a valid private key string", () => { // Generate account from valid private key string - const account = new Account( {privateKey: beaconPrivateKeyString}); + const account = new Account({ privateKey: beaconPrivateKeyString }); // Test object member type consistency expect(account._privateKey).instanceof(PrivateKey); @@ -63,37 +84,56 @@ describe('Account', () => { expect(account.viewKey()).instanceof(ViewKey); expect(account.address()).instanceof(Address); // Test that expected output is generated - expect(account.privateKey().to_string()).equal(beaconPrivateKeyString); + expect(account.privateKey().to_string()).equal( + beaconPrivateKeyString, + ); expect(account.viewKey().to_string()).equal(beaconViewKeyString); expect(account.address().to_string()).equal(beaconAddressString); expect(account.toString()).equal(beaconAddressString); }); - it('can encrypt an account and decrypt to the same account', () => { + it("can encrypt an account and decrypt to the same account", () => { const newAccount = new Account(); - const privateKeyCiphertext = newAccount.encryptAccount("mypassword"); + const privateKeyCiphertext = + newAccount.encryptAccount("mypassword"); const privateKeyCiphertextString = privateKeyCiphertext.toString(); // Generate account from valid private key string - const accountFromString = Account.fromCiphertext(privateKeyCiphertextString, "mypassword"); - const accountFromObject = Account.fromCiphertext(privateKeyCiphertext, "mypassword"); + const accountFromString = Account.fromCiphertext( + privateKeyCiphertextString, + "mypassword", + ); + const accountFromObject = Account.fromCiphertext( + privateKeyCiphertext, + "mypassword", + ); for (const account of [accountFromString, accountFromObject]) { // Test that expected output is generated - expect(account.privateKey().to_string()).equal(newAccount.privateKey().to_string()); - expect(account.viewKey().to_string()).equal(newAccount.viewKey().to_string()); - expect(account.address().to_string()).equal(newAccount.toString()); + expect(account.privateKey().to_string()).equal( + newAccount.privateKey().to_string(), + ); + expect(account.viewKey().to_string()).equal( + newAccount.viewKey().to_string(), + ); + expect(account.address().to_string()).equal( + newAccount.toString(), + ); expect(account.toString()).equal(newAccount.toString()); } }); - it('fails to create an account from a bad password', () => { + it("fails to create an account from a bad password", () => { const newAccount = new Account(); - const privateKeyCiphertext = newAccount.encryptAccount("mypassword"); + const privateKeyCiphertext = + newAccount.encryptAccount("mypassword"); const privateKeyCiphertextString = privateKeyCiphertext.toString(); try { - Account.fromCiphertext(privateKeyCiphertextString, "badpassword"); + Account.fromCiphertext( + privateKeyCiphertextString, + "badpassword", + ); Account.fromCiphertext(privateKeyCiphertext, "badpassword"); // Should not get here @@ -105,37 +145,44 @@ describe('Account', () => { }); }); - describe('isValidAddress', () => { - it('returns true for a valid address string', () => { + describe("isValidAddress", () => { + it("returns true for a valid address string", () => { expect(Account.isValidAddress(beaconAddressString)).equal(true); }); - it('returns false for invalid address strings', () => { - expect(Account.isValidAddress('invalid_address')).equal(false); - expect(Account.isValidAddress('aleo1xyz')).equal(false); - expect(Account.isValidAddress('')).equal(false); + it("returns false for invalid address strings", () => { + expect(Account.isValidAddress("invalid_address")).equal(false); + expect(Account.isValidAddress("aleo1xyz")).equal(false); + expect(Account.isValidAddress("")).equal(false); }); - it('returns true for valid address bytes', () => { + it("returns true for valid address bytes", () => { const address = Address.from_string(beaconAddressString); const bytes = address.toBytesLe(); expect(Account.isValidAddress(bytes)).equal(true); }); - it('returns false for invalid address bytes', () => { - expect(Account.isValidAddress(new Uint8Array([1, 2, 3]))).equal(false); + it("returns false for invalid address bytes", () => { + expect(Account.isValidAddress(new Uint8Array([1, 2, 3]))).equal( + false, + ); expect(Account.isValidAddress(new Uint8Array([]))).equal(false); }); }); - describe('View Key Record Decryption', () => { - it('decrypts a record in ciphertext form', () => { - const account = new Account({privateKey: "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls"}); + describe("View Key Record Decryption", () => { + it("decrypts a record in ciphertext form", () => { + const account = new Account({ + privateKey: + "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls", + }); - const decrypt_spy = sinon.spy(account._viewKey, 'decrypt'); + const decrypt_spy = sinon.spy(account._viewKey, "decrypt"); // Decrypt record the private key owns - const decryptedRecord = account.decryptRecord(recordCiphertextString); + const decryptedRecord = account.decryptRecord( + recordCiphertextString, + ); // Ensure the underlying wasm is being called with the right data expect(decrypt_spy.calledWith(recordCiphertextString)).equal(true); @@ -143,32 +190,44 @@ describe('Account', () => { expect(decryptedRecord).equal(recordPlaintextString); }); - it('doesnt decrypt records from other accounts nor identifies them as the record owner', () => { + it("doesnt decrypt records from other accounts nor identifies them as the record owner", () => { function tryDecrypt() { try { return account.decryptRecord(foreignCiphertextString); } catch (err) { - throw("Record didn't decrypt") + throw "Record didn't decrypt"; } } - const account = new Account({privateKey: "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls"}); - const decrypt_spy = sinon.spy(account._viewKey, 'decrypt'); - const recordCiphertext = RecordCiphertext.fromString(foreignCiphertextString); + const account = new Account({ + privateKey: + "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls", + }); + const decrypt_spy = sinon.spy(account._viewKey, "decrypt"); + const recordCiphertext = RecordCiphertext.fromString( + foreignCiphertextString, + ); // Ensure a foreign record decryption attempt throws expect(tryDecrypt).throw(); // Ensure the underlying wasm is being called with the right data expect(decrypt_spy.calledWith(foreignCiphertextString)).equal(true); // Ensure the account doesn't identify the record ciphertext as its own from both string and object forms - expect(account.ownsRecordCiphertext(foreignCiphertextString)).equal(false); + expect(account.ownsRecordCiphertext(foreignCiphertextString)).equal( + false, + ); expect(account.ownsRecordCiphertext(recordCiphertext)).equal(false); - }); - it('decrypts an array of records in ciphertext form', () => { - const account = new Account({privateKey: "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls"}); - const ciphertexts = [recordCiphertextString, recordCiphertextString]; - const decrypt_spy = sinon.spy(account._viewKey, 'decrypt'); + it("decrypts an array of records in ciphertext form", () => { + const account = new Account({ + privateKey: + "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls", + }); + const ciphertexts = [ + recordCiphertextString, + recordCiphertextString, + ]; + const decrypt_spy = sinon.spy(account._viewKey, "decrypt"); const decryptedRecords = account.decryptRecords(ciphertexts); // Ensure the right number of calls were called and right inputs were passed @@ -176,16 +235,20 @@ describe('Account', () => { expect(decrypt_spy.calledWith(ciphertexts[0])).equal(true); expect(decrypt_spy.calledWith(ciphertexts[1])).equal(true); // Ensure the records were decrypted to the correct data - expect(decryptedRecords).deep.equal([recordPlaintextString, recordPlaintextString]); + expect(decryptedRecords).deep.equal([ + recordPlaintextString, + recordPlaintextString, + ]); }); }); - describe('Sign and Verify', () => { - + describe("Sign and Verify", () => { it("verifies the signature on a message", () => { const account = new Account(); - const other_message = Uint8Array.from([104, 101, 108, 108, 111, 32, 120, 121, 114, 108, 99]); - const sign_spy = sinon.spy(account._privateKey, 'sign'); + const other_message = Uint8Array.from([ + 104, 101, 108, 108, 111, 32, 120, 121, 114, 108, 99, + ]); + const sign_spy = sinon.spy(account._privateKey, "sign"); const signature = account.sign(message); // Ensure the signature was called with the right message @@ -195,13 +258,21 @@ describe('Account', () => { expect(sign_spy.calledWith(other_message)).equal(true); expect(sign_spy.callCount).equal(2); - const verify_spy = sinon.spy(account._address, 'verify'); + const verify_spy = sinon.spy(account._address, "verify"); const isValid = account.verify(message, signature); expect(verify_spy.calledWith(message, signature)).equal(true); - const isSigValidForWrongMessage = account.verify(other_message, signature); + const isSigValidForWrongMessage = account.verify( + other_message, + signature, + ); expect(verify_spy.calledWith(other_message, signature)).equal(true); - const isSigValidForMultipleMessages = account.verify(other_message, other_signature); - expect(verify_spy.calledWith(other_message, other_signature)).equal(true); + const isSigValidForMultipleMessages = account.verify( + other_message, + other_signature, + ); + expect(verify_spy.calledWith(other_message, other_signature)).equal( + true, + ); expect(verify_spy.callCount).equal(3); // Ensure the signature was valid expect(isValid).equal(true); @@ -212,8 +283,8 @@ describe('Account', () => { }); }); - describe('Account Lifecycle & Zeroization', () => { - it('destroy prevents further use of the account', () => { + describe("Account Lifecycle & Zeroization", () => { + it("destroy prevents further use of the account", () => { const account = new Account(); account.destroy(); @@ -222,64 +293,91 @@ describe('Account', () => { expect(() => account.computeKey()).to.throw(/destroyed/); expect(() => account.address()).to.throw(/destroyed/); expect(() => account.sign(message)).to.throw(/destroyed/); - expect(() => account.verify(message, new Account().sign(message))).to.throw(/destroyed/); + expect(() => + account.verify(message, new Account().sign(message)), + ).to.throw(/destroyed/); expect(() => account.clone()).to.throw(/destroyed/); expect(() => account.toString()).to.throw(/destroyed/); - expect(() => account.encryptAccount("password")).to.throw(/destroyed/); - expect(() => account.decryptRecord("ciphertext")).to.throw(/destroyed/); - expect(() => account.decryptRecords(["ciphertext"])).to.throw(/destroyed/); + expect(() => account.encryptAccount("password")).to.throw( + /destroyed/, + ); + expect(() => account.decryptRecord("ciphertext")).to.throw( + /destroyed/, + ); + expect(() => account.decryptRecords(["ciphertext"])).to.throw( + /destroyed/, + ); }); - it('destroy is idempotent', () => { + it("destroy is idempotent", () => { const account = new Account(); account.destroy(); expect(() => account.destroy()).to.not.throw(); }); - it('Symbol.dispose triggers destroy', () => { + it("Symbol.dispose triggers destroy", () => { const account = new Account(); account[Symbol.dispose](); expect(() => account.privateKey()).to.throw(/destroyed/); }); - it('clone produces a working independent copy', () => { - const account = new Account({seed: seed}); + it("clone produces a working independent copy", () => { + const account = new Account({ seed: seed }); const cloned = account.clone(); // Verify the clone has the same keys - expect(cloned.privateKey().to_string()).to.equal(account.privateKey().to_string()); - expect(cloned.viewKey().to_string()).to.equal(account.viewKey().to_string()); - expect(cloned.address().to_string()).to.equal(account.address().to_string()); + expect(cloned.privateKey().to_string()).to.equal( + account.privateKey().to_string(), + ); + expect(cloned.viewKey().to_string()).to.equal( + account.viewKey().to_string(), + ); + expect(cloned.address().to_string()).to.equal( + account.address().to_string(), + ); // Destroying the original should not affect the clone account.destroy(); expect(() => cloned.privateKey()).to.not.throw(); - expect(cloned.privateKey().to_string()).to.equal(beaconPrivateKeyString); + expect(cloned.privateKey().to_string()).to.equal( + beaconPrivateKeyString, + ); cloned.destroy(); }); - it('fromCiphertext produces a working account', () => { + it("fromCiphertext produces a working account", () => { const account = new Account(); const ciphertext = account.encryptAccount("testpassword"); - const recovered = Account.fromCiphertext(ciphertext, "testpassword"); - - expect(recovered.privateKey().to_string()).to.equal(account.privateKey().to_string()); - expect(recovered.viewKey().to_string()).to.equal(account.viewKey().to_string()); - expect(recovered.address().to_string()).to.equal(account.address().to_string()); + const recovered = Account.fromCiphertext( + ciphertext, + "testpassword", + ); + + expect(recovered.privateKey().to_string()).to.equal( + account.privateKey().to_string(), + ); + expect(recovered.viewKey().to_string()).to.equal( + account.viewKey().to_string(), + ); + expect(recovered.address().to_string()).to.equal( + account.address().to_string(), + ); account.destroy(); recovered.destroy(); }); - it('accepts a PrivateKey object in AccountParam', () => { - const original = new Account({seed: seed}); + it("accepts a PrivateKey object in AccountParam", () => { + const original = new Account({ seed: seed }); const pk = original.privateKey(); // Create account from PrivateKey object (byte-serialization path, no string leak) - const fromPk = new Account({privateKey: pk}); - expect(fromPk.privateKey().to_string()).to.equal(beaconPrivateKeyString); + const fromPk = new Account({ privateKey: pk }); + expect(fromPk.privateKey().to_string()).to.equal( + beaconPrivateKeyString, + ); expect(fromPk.viewKey().to_string()).to.equal(beaconViewKeyString); expect(fromPk.address().to_string()).to.equal(beaconAddressString); @@ -287,12 +385,12 @@ describe('Account', () => { fromPk.destroy(); }); - it('zeroizeBytes clears a byte array', () => { + it("zeroizeBytes clears a byte array", () => { const bytes = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); - expect(bytes.some(b => b !== 0)).to.equal(true); + expect(bytes.some((b) => b !== 0)).to.equal(true); zeroizeBytes(bytes); - expect(bytes.every(b => b === 0)).to.equal(true); + expect(bytes.every((b) => b === 0)).to.equal(true); }); }); }); diff --git a/sdk/tests/algorithm.test.ts b/sdk/tests/algorithm.test.ts index bac64448a..7ab76bc62 100644 --- a/sdk/tests/algorithm.test.ts +++ b/sdk/tests/algorithm.test.ts @@ -1,6 +1,19 @@ import sinon from "sinon"; import { expect } from "chai"; -import { Field, Group, Scalar, BHP256, BHP512, BHP768, BHP1024, Pedersen64, Pedersen128, Poseidon2, Poseidon4, Poseidon8 } from "@provablehq/sdk/%%NETWORK%%.js"; +import { + Field, + Group, + Scalar, + BHP256, + BHP512, + BHP768, + BHP1024, + Pedersen64, + Pedersen128, + Poseidon2, + Poseidon4, + Poseidon8, +} from "@provablehq/sdk/%%NETWORK%%.js"; import * as algebraicData from "./data/algebra.js"; const Fg = Field.fromString(algebraicData.FieldGenerator); @@ -9,19 +22,19 @@ const F3 = F2.multiply(Fg); const F4 = F3.multiply(Fg); const SFg = Scalar.fromString(algebraicData.ScalarGenerator); const fieldArray = [Fg, F2, F3, F4]; -const finiteFieldBytes = fieldArray.map(field => field.toBitsLe()).flat(); +const finiteFieldBytes = fieldArray.map((field) => field.toBitsLe()).flat(); function deepCopyFieldArray(array: Field[]): Field[] { - return array.map(item => item.clone()); + return array.map((item) => item.clone()); } -describe('Hash Function Export Tests', () => { +describe("Hash Function Export Tests", () => { afterEach(() => { sinon.restore(); }); - describe('BHP Hasher Tests', () => { - it('Check BHP Hashers hash to expected values.', () => { + describe("BHP Hasher Tests", () => { + it("Check BHP Hashers hash to expected values.", () => { // Create all BHP hashers. const BHP256Hasher = new BHP256(); const BHP512Hasher = new BHP512(); @@ -29,72 +42,241 @@ describe('Hash Function Export Tests', () => { const BHP1024Hasher = new BHP1024(); // Run all possible operations for BHP256. - expect(BHP256Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP256Hash); - expect(BHP256Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP256HashToGroup); - expect(BHP256Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP256Commit); - expect(BHP256Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP256CommitToGroup); + expect(BHP256Hasher.hash(finiteFieldBytes).toString()).equals( + algebraicData.expectedBHP256Hash, + ); + expect( + BHP256Hasher.hashToGroup(finiteFieldBytes).toString(), + ).equals(algebraicData.expectedBHP256HashToGroup); + expect( + BHP256Hasher.commit(finiteFieldBytes, SFg.clone()).toString(), + ).equals(algebraicData.expectedBHP256Commit); + expect( + BHP256Hasher.commitToGroup( + finiteFieldBytes, + SFg.clone(), + ).toString(), + ).equals(algebraicData.expectedBHP256CommitToGroup); // Run all possible operations for BHP512. - expect(BHP512Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP512Hash); - expect(BHP512Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP512HashToGroup); - expect(BHP512Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP512Commit); - expect(BHP512Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP512CommitToGroup); + expect(BHP512Hasher.hash(finiteFieldBytes).toString()).equals( + algebraicData.expectedBHP512Hash, + ); + expect( + BHP512Hasher.hashToGroup(finiteFieldBytes).toString(), + ).equals(algebraicData.expectedBHP512HashToGroup); + expect( + BHP512Hasher.commit(finiteFieldBytes, SFg.clone()).toString(), + ).equals(algebraicData.expectedBHP512Commit); + expect( + BHP512Hasher.commitToGroup( + finiteFieldBytes, + SFg.clone(), + ).toString(), + ).equals(algebraicData.expectedBHP512CommitToGroup); // Run all possible operations for BHP768. - expect(BHP768Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP768Hash); - expect(BHP768Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP768HashToGroup); - expect(BHP768Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP768Commit); - expect(BHP768Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP768CommitToGroup); + expect(BHP768Hasher.hash(finiteFieldBytes).toString()).equals( + algebraicData.expectedBHP768Hash, + ); + expect( + BHP768Hasher.hashToGroup(finiteFieldBytes).toString(), + ).equals(algebraicData.expectedBHP768HashToGroup); + expect( + BHP768Hasher.commit(finiteFieldBytes, SFg.clone()).toString(), + ).equals(algebraicData.expectedBHP768Commit); + expect( + BHP768Hasher.commitToGroup( + finiteFieldBytes, + SFg.clone(), + ).toString(), + ).equals(algebraicData.expectedBHP768CommitToGroup); // Run all possible operations for BHP1024. - expect(BHP1024Hasher.hash(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP1024Hash); - expect(BHP1024Hasher.hashToGroup(finiteFieldBytes).toString()).equals(algebraicData.expectedBHP1024HashToGroup); - expect(BHP1024Hasher.commit(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP1024Commit); - expect(BHP1024Hasher.commitToGroup(finiteFieldBytes, SFg.clone()).toString()).equals(algebraicData.expectedBHP1024CommitToGroup); + expect(BHP1024Hasher.hash(finiteFieldBytes).toString()).equals( + algebraicData.expectedBHP1024Hash, + ); + expect( + BHP1024Hasher.hashToGroup(finiteFieldBytes).toString(), + ).equals(algebraicData.expectedBHP1024HashToGroup); + expect( + BHP1024Hasher.commit(finiteFieldBytes, SFg.clone()).toString(), + ).equals(algebraicData.expectedBHP1024Commit); + expect( + BHP1024Hasher.commitToGroup( + finiteFieldBytes, + SFg.clone(), + ).toString(), + ).equals(algebraicData.expectedBHP1024CommitToGroup); }); - it('Check Pedersen hashers hash to expected values', () => { + it("Check Pedersen hashers hash to expected values", () => { // Create all Pedersen hashers. const Pedersen64Hasher = new Pedersen64(); const Pedersen128Hasher = new Pedersen128(); // Create a bit array which is 2 2u32 elements concatenated. - const bitArray = [true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]; + const bitArray = [ + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + ]; // Run all possible operations for Pedersen64. - expect(Pedersen64Hasher.hash(bitArray).toString()).equals(algebraicData.expectedPedersen64Hash); - expect(Pedersen64Hasher.commit(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen64Commit); - expect(Pedersen64Hasher.commitToGroup(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen64CommitToGroup); + expect(Pedersen64Hasher.hash(bitArray).toString()).equals( + algebraicData.expectedPedersen64Hash, + ); + expect( + Pedersen64Hasher.commit(bitArray, SFg.clone()).toString(), + ).equals(algebraicData.expectedPedersen64Commit); + expect( + Pedersen64Hasher.commitToGroup( + bitArray, + SFg.clone(), + ).toString(), + ).equals(algebraicData.expectedPedersen64CommitToGroup); // Run all possible operations for Pedersen128. - expect(Pedersen128Hasher.hash(bitArray).toString()).equals(algebraicData.expectedPedersen128Hash); - expect(Pedersen128Hasher.commit(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen128Commit); - expect(Pedersen128Hasher.commitToGroup(bitArray, SFg.clone()).toString()).equals(algebraicData.expectedPedersen128CommitToGroup); + expect(Pedersen128Hasher.hash(bitArray).toString()).equals( + algebraicData.expectedPedersen128Hash, + ); + expect( + Pedersen128Hasher.commit(bitArray, SFg.clone()).toString(), + ).equals(algebraicData.expectedPedersen128Commit); + expect( + Pedersen128Hasher.commitToGroup( + bitArray, + SFg.clone(), + ).toString(), + ).equals(algebraicData.expectedPedersen128CommitToGroup); }); - it('Check Poseidon hashers hash to expected values', () => { + it("Check Poseidon hashers hash to expected values", () => { // Create all Poseidon hashers. const Poseidon2Hasher = new Poseidon2(); const Poseidon4Hasher = new Poseidon4(); const Poseidon8Hasher = new Poseidon8(); // Run all possible operations for Poseidon2. - expect(Poseidon2Hasher.hash(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon2Hash); - expect(Poseidon2Hasher.hashToScalar(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon2HashToScalar); - expect(Poseidon2Hasher.hashToGroup(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon2HashToGroup); - expect(Poseidon2Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map(field => field.toString())).deep.equals(algebraicData.expectedPoseidon2HashMany); + expect( + Poseidon2Hasher.hash(deepCopyFieldArray(fieldArray)).toString(), + ).equals(algebraicData.expectedPoseidon2Hash); + expect( + Poseidon2Hasher.hashToScalar( + deepCopyFieldArray(fieldArray), + ).toString(), + ).equals(algebraicData.expectedPoseidon2HashToScalar); + expect( + Poseidon2Hasher.hashToGroup( + deepCopyFieldArray(fieldArray), + ).toString(), + ).equals(algebraicData.expectedPoseidon2HashToGroup); + expect( + Poseidon2Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map( + (field) => field.toString(), + ), + ).deep.equals(algebraicData.expectedPoseidon2HashMany); // Run all possible operations for Poseidon4. - expect(Poseidon4Hasher.hash(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon4Hash); - expect(Poseidon4Hasher.hashToScalar(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon4HashToScalar); - expect(Poseidon4Hasher.hashToGroup(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon4HashToGroup); - expect(Poseidon4Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map(field => field.toString())).deep.equals(algebraicData.expectedPoseidon4HashMany); + expect( + Poseidon4Hasher.hash(deepCopyFieldArray(fieldArray)).toString(), + ).equals(algebraicData.expectedPoseidon4Hash); + expect( + Poseidon4Hasher.hashToScalar( + deepCopyFieldArray(fieldArray), + ).toString(), + ).equals(algebraicData.expectedPoseidon4HashToScalar); + expect( + Poseidon4Hasher.hashToGroup( + deepCopyFieldArray(fieldArray), + ).toString(), + ).equals(algebraicData.expectedPoseidon4HashToGroup); + expect( + Poseidon4Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map( + (field) => field.toString(), + ), + ).deep.equals(algebraicData.expectedPoseidon4HashMany); // Run all possible operations for Poseidon8. - expect(Poseidon8Hasher.hash(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon8Hash); - expect(Poseidon8Hasher.hashToScalar(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon8HashToScalar); - expect(Poseidon8Hasher.hashToGroup(deepCopyFieldArray(fieldArray)).toString()).equals(algebraicData.expectedPoseidon8HashToGroup); - expect(Poseidon8Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map(field => field.toString())).deep.equals(algebraicData.expectedPoseidon8HashMany); + expect( + Poseidon8Hasher.hash(deepCopyFieldArray(fieldArray)).toString(), + ).equals(algebraicData.expectedPoseidon8Hash); + expect( + Poseidon8Hasher.hashToScalar( + deepCopyFieldArray(fieldArray), + ).toString(), + ).equals(algebraicData.expectedPoseidon8HashToScalar); + expect( + Poseidon8Hasher.hashToGroup( + deepCopyFieldArray(fieldArray), + ).toString(), + ).equals(algebraicData.expectedPoseidon8HashToGroup); + expect( + Poseidon8Hasher.hashMany(deepCopyFieldArray(fieldArray), 2).map( + (field) => field.toString(), + ), + ).deep.equals(algebraicData.expectedPoseidon8HashMany); }); }); }); diff --git a/sdk/tests/arithmetic.test.ts b/sdk/tests/arithmetic.test.ts index d7c616f50..4f11bc49b 100644 --- a/sdk/tests/arithmetic.test.ts +++ b/sdk/tests/arithmetic.test.ts @@ -1,15 +1,34 @@ import sinon from "sinon"; import { expect } from "chai"; -import { Field, Scalar, Group, Boolean, I8, I16, I32, I64, I128, U8, U16, U32, U64, U128} from "../src/node.js"; -import { FieldGenerator, GroupGenerator, ScalarGenerator } from "./data/algebra.js"; - -describe('Field and Group Arithmetic Tests', () => { +import { + Field, + Scalar, + Group, + Boolean, + I8, + I16, + I32, + I64, + I128, + U8, + U16, + U32, + U64, + U128, +} from "../src/node.js"; +import { + FieldGenerator, + GroupGenerator, + ScalarGenerator, +} from "./data/algebra.js"; + +describe("Field and Group Arithmetic Tests", () => { afterEach(() => { sinon.restore(); }); - describe('Field and Group arithmetic', () => { - it('Check field arithmetic functions work as expected', () => { + describe("Field and Group arithmetic", () => { + it("Check field arithmetic functions work as expected", () => { // Create the 2 field element. const a = Field.fromString("2field"); // Create the inverse of the 2 field element. @@ -44,7 +63,7 @@ describe('Field and Group Arithmetic Tests', () => { expect(two_power_four.equals(two_times_eight)).equal(true); }); - it('Check boolean creation and serialization', () => { + it("Check boolean creation and serialization", () => { const t = Boolean.fromString("true"); const f = Boolean.fromString("false"); @@ -70,7 +89,7 @@ describe('Field and Group Arithmetic Tests', () => { expect(f.equals(fFromBits)).equals(true); }); - it('Check boolean logical operations', () => { + it("Check boolean logical operations", () => { const t = new Boolean(true); const f = new Boolean(false); @@ -96,7 +115,7 @@ describe('Field and Group Arithmetic Tests', () => { expect(t.nor(f).toString()).equals("false"); }); - it('Check I8 serialization and arithmetic', () => { + it("Check I8 serialization and arithmetic", () => { const i8 = I8.fromString("42i8"); const i8Clone = i8.clone(); @@ -146,7 +165,7 @@ describe('Field and Group Arithmetic Tests', () => { expect(base.powU32(U32.fromString("3u32")).toString()).equal("8i8"); }); - it('Check I16 serialization and arithmetic', () => { + it("Check I16 serialization and arithmetic", () => { const i16 = I16.fromString("42i16"); const i16Clone = i16.clone(); @@ -192,11 +211,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = I16.fromString("2i16"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8i16"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8i16"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8i16"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8i16", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8i16", + ); }); - it('Check I32 serialization and arithmetic', () => { + it("Check I32 serialization and arithmetic", () => { const i32 = I32.fromString("42i32"); const i32Clone = i32.clone(); @@ -242,11 +265,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = I32.fromString("2i32"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8i32"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8i32"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8i32"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8i32", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8i32", + ); }); - it('Check I64 serialization and arithmetic', () => { + it("Check I64 serialization and arithmetic", () => { const i64 = I64.fromString("42i64"); const i64Clone = i64.clone(); @@ -292,11 +319,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = I64.fromString("2i64"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8i64"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8i64"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8i64"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8i64", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8i64", + ); }); - it('Check I128 serialization and arithmetic', () => { + it("Check I128 serialization and arithmetic", () => { const i128 = I128.fromString("42i128"); const i128Clone = i128.clone(); @@ -342,11 +373,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = I128.fromString("2i128"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8i128"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8i128"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8i128"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8i128", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8i128", + ); }); - it('Check U8 serialization and arithmetic', () => { + it("Check U8 serialization and arithmetic", () => { const u8 = U8.fromString("42u8"); const u8Clone = u8.clone(); @@ -393,7 +428,7 @@ describe('Field and Group Arithmetic Tests', () => { expect(base.powU32(U32.fromString("3u32")).toString()).equal("8u8"); }); - it('Check U16 serialization and arithmetic', () => { + it("Check U16 serialization and arithmetic", () => { const u16 = U16.fromString("42u16"); const u16Clone = u16.clone(); @@ -436,11 +471,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = U16.fromString("2u16"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8u16"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8u16"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8u16"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8u16", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8u16", + ); }); - it('Check U32 serialization and arithmetic', () => { + it("Check U32 serialization and arithmetic", () => { const u32 = U32.fromString("42u32"); const u32Clone = u32.clone(); @@ -483,12 +522,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = U32.fromString("2u32"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8u32"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8u32"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8u32"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8u32", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8u32", + ); }); - - it('Check U64 serialization and arithmetic', () => { + it("Check U64 serialization and arithmetic", () => { const u64 = U64.fromString("42u64"); const u64Clone = u64.clone(); @@ -531,11 +573,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = U64.fromString("2u64"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8u64"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8u64"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8u64"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8u64", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8u64", + ); }); - it('Check U128 serialization and arithmetic', () => { + it("Check U128 serialization and arithmetic", () => { const u128 = U128.fromString("42u128"); const u128Clone = u128.clone(); @@ -578,11 +624,15 @@ describe('Field and Group Arithmetic Tests', () => { const base = U128.fromString("2u128"); expect(base.powU8(U8.fromString("3u8")).toString()).equal("8u128"); - expect(base.powU16(U16.fromString("3u16")).toString()).equal("8u128"); - expect(base.powU32(U32.fromString("3u32")).toString()).equal("8u128"); + expect(base.powU16(U16.fromString("3u16")).toString()).equal( + "8u128", + ); + expect(base.powU32(U32.fromString("3u32")).toString()).equal( + "8u128", + ); }); - it('Check scalar field arithmetic', () => { + it("Check scalar field arithmetic", () => { // Create the 2 scalar element. const a = Scalar.fromString("2scalar"); // Create the inverse of the 2 scalar element. @@ -617,7 +667,7 @@ describe('Field and Group Arithmetic Tests', () => { expect(two_power_four.equals(two_times_eight)).equal(true); }); - it('Test group operations', () => { + it("Test group operations", () => { // Get the 2 element of the group. const G = Group.fromString("2group"); // Get the point at infinity (i.e. "0"/additive identity in elliptic curves). @@ -631,16 +681,16 @@ describe('Field and Group Arithmetic Tests', () => { // Do a point doubling through scalar multiplication. const d = G.scalarMultiply(a); // Find point (x, -y) - const G_inv= G.inverse(); + const G_inv = G.inverse(); // Ensure addition and doubling landed on the same point. expect(b.equals(c)).equals(true); // Ensure point doubling and scalar multiplication by 2scalar ends up as the same element as doubling. expect(c.equals(d)).equals(true); // Ensure adding the inverse element leads to the point at infinity. - expect((G.add(G_inv)).equals(Ginf)).equals(true); + expect(G.add(G_inv).equals(Ginf)).equals(true); }); - it('Ensure bit and byte serialization is correctly implemented', () => { + it("Ensure bit and byte serialization is correctly implemented", () => { // Create the chosen generators. const G = Group.generator(); const a = Scalar.fromString(ScalarGenerator); diff --git a/sdk/tests/data/account-data.ts b/sdk/tests/data/account-data.ts index 4b39cce31..aa6dcdae1 100644 --- a/sdk/tests/data/account-data.ts +++ b/sdk/tests/data/account-data.ts @@ -1,40 +1,95 @@ // Test data created from a run of SnarkOS local network // A random seed used to generate the private key -const seed = new Uint8Array([94, 91, 52, 251, 240, 230, 226, 35, 117, 253, 224, 210, 175, 13, 205, 120, 155, 214, 7, 169, 66, 62, 206, 50, 188, 40, 29, 122, 40, 250, 54, 18]); +const seed = new Uint8Array([ + 94, 91, 52, 251, 240, 230, 226, 35, 117, 253, 224, 210, 175, 13, 205, 120, + 155, 214, 7, 169, 66, 62, 206, 50, 188, 40, 29, 122, 40, 250, 54, 18, +]); // UTF-8 bytes of the message "hello world" -const message = Uint8Array.from([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]); +const message = Uint8Array.from([ + 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, +]); // Private key string derived from the seed -const beaconPrivateKeyString = "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH" -const fundedPrivateKeyString = "APrivateKey1zkp3dQx4WASWYQVWKkq14v3RoQDfY2kbLssUj7iifi1VUQ6" -const fundedAddressString = "aleo184vuwr5u7u0ha5f5k44067dd2uaqewxx6pe5ltha5pv99wvhfqxqv339h4" +const beaconPrivateKeyString = + "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH"; +const fundedPrivateKeyString = + "APrivateKey1zkp3dQx4WASWYQVWKkq14v3RoQDfY2kbLssUj7iifi1VUQ6"; +const fundedAddressString = + "aleo184vuwr5u7u0ha5f5k44067dd2uaqewxx6pe5ltha5pv99wvhfqxqv339h4"; -const privateKeyString = "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls"; +const privateKeyString = + "APrivateKey1zkpJkyYRGYtkeHDaFfwsKtUJzia7csiWhfBWPXWhXJzy9Ls"; const viewKeyString = "AViewKey1ccEt8A2Ryva5rxnKcAbn7wgTaTsb79tzkKHFpeKsm9NX"; -const addressString = "aleo1j7qxyunfldj2lp8hsvy7mw5k8zaqgjfyr72x2gh3x4ewgae8v5gscf5jh3"; +const addressString = + "aleo1j7qxyunfldj2lp8hsvy7mw5k8zaqgjfyr72x2gh3x4ewgae8v5gscf5jh3"; // View key string derived from the private key -const beaconViewKeyString = "AViewKey1mSnpFFC8Mj4fXbK5YiWgZ3mjiV8CxA79bYNa8ymUpTrw" +const beaconViewKeyString = + "AViewKey1mSnpFFC8Mj4fXbK5YiWgZ3mjiV8CxA79bYNa8ymUpTrw"; // Address string derived from the view key -const beaconAddressString = "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px" +const beaconAddressString = + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"; // Ciphertext of a record generated by the private key above -const recordCiphertextString = "record1qyqsqpe2szk2wwwq56akkwx586hkndl3r8vzdwve32lm7elvphh37rsyqyxx66trwfhkxun9v35hguerqqpqzqrtjzeu6vah9x2me2exkgege824sd8x2379scspmrmtvczs0d93qttl7y92ga0k0rsexu409hu3vlehe3yxjhmey3frh2z5pxm5cmxsv4un97q"; +const recordCiphertextString = + "record1qyqsqpe2szk2wwwq56akkwx586hkndl3r8vzdwve32lm7elvphh37rsyqyxx66trwfhkxun9v35hguerqqpqzqrtjzeu6vah9x2me2exkgege824sd8x2379scspmrmtvczs0d93qttl7y92ga0k0rsexu409hu3vlehe3yxjhmey3frh2z5pxm5cmxsv4un97q"; // Plaintext record corresponding to the ciphertext generated by the private key -const recordPlaintextString = "{\n owner: aleo1j7qxyunfldj2lp8hsvy7mw5k8zaqgjfyr72x2gh3x4ewgae8v5gscf5jh3.private,\n microcredits: 1500000000000000u64.private,\n _nonce: 3077450429259593211617823051143573281856129402760267155982965992208217472983group.public,\n _version: 0u8.public\n}"; +const recordPlaintextString = + "{\n owner: aleo1j7qxyunfldj2lp8hsvy7mw5k8zaqgjfyr72x2gh3x4ewgae8v5gscf5jh3.private,\n microcredits: 1500000000000000u64.private,\n _nonce: 3077450429259593211617823051143573281856129402760267155982965992208217472983group.public,\n _version: 0u8.public\n}"; // Cipher text of a record generated by a different private key -const foreignCiphertextString = "record1qyqsq553yxz8ylwqyqfmcfmwz03x6xsxf2h2kypcwhykzgm50ut4susyqyxx66trwfhkxun9v35hguerqqpqzqyjt8kxnp28v83t460knvp0dq86a3r3dyve945u0xqeksq323paqtegslprdc5zypksrja7rmctx90jnpeq5sqkwlfct7ygy990a5pqs7y5pt0" +const foreignCiphertextString = + "record1qyqsq553yxz8ylwqyqfmcfmwz03x6xsxf2h2kypcwhykzgm50ut4susyqyxx66trwfhkxun9v35hguerqqpqzqyjt8kxnp28v83t460knvp0dq86a3r3dyve945u0xqeksq323paqtegslprdc5zypksrja7rmctx90jnpeq5sqkwlfct7ygy990a5pqs7y5pt0"; // View key string of a different private key -const foreignViewKeyString = "AViewKey1ghtvuJQQzQ31xSiVh6X1PK8biEVhQBygRGV4KdYmq4JT" -const helloProgramId = 'hellothere.aleo'; -const helloProgramMainFunction = 'hello'; -const helloProgram = 'program ' + helloProgramId + ';\n\nfunction ' + helloProgramMainFunction + ':\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n'; -const recordStatePathv0 = "path1qxuhaurx4jsylq34z6vwnufa8m2s3sgpz8w2cp6myj5hps48pd3sp59sqgqqqqqqqzqslytl8jkafzlmzy294psjce649x64scwxdq2kys70y7kh5fxqwgzc6f9lg9ax0mr52p6d42c2lax9m4e6fnxdpde3722sjrlv77ssn7lh9yvtdfwxhpkzhrfscnxw7h36t9j767w34u8rxmdw0y5vyurdtakhtj7tu69nx6yzdv3p0ssr0ursrzgfkqftwjxg6qpj5pg96rkt4vsduph46p9e7nzkmfcn6g2vlc2sz80wr8ct37zdvqmhg4jjpdzfh607804rqksqd40zvn6shrauxv775zgcpd6ecl6gee9kjqfs5hevpjpzkv94md4arwgynpy6wr4k3txhap0n93y3fl3edwgul0gyn268hrlylqmyqvv8rse40tytnk9atrjx9mlnhp6mrqhd45u99s8gsjw899kz6yhktejwj6snlp7r38kwdmlxczr7m0sn46tmjjndqzn7zmw23jd0x2vwf4x3z7dt642zwweq597dlzaxqx2qg6e89jgmq8fq9pwpy6uqt6prke4v66u39aqz7qewnh2a8er0y78m2dyayr8sdsjpdcdvc78f95wdvapn8072h2jqtj6zvkc52tavlkqfmvfxvpq87h39ajdx4c0sfckm05aqg9mfdykse2cf0vsr0regrp9m9dundvgyat0rcg3fsex6c0335hs7tafxjgkw7k9mxm7arus94ytv4g5x7y0d0pjs2gzexf9lxt2mwegumamgvfplafglgkm46h5j26cvcacfqmj39p7x28pfdma2dp0jyet5nz4pr9j0yf6f6l72kzexsd7kp82s6z6rnk6l2yg7xpeyc3dy907wefjn5w35prnacapd4acn20qeldgwwmuev0d8t6tz02x2kv8qg9sxhakx6fmw5rd35fda735pchuct5gcg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyd7cpzdqmmk8kqx9mamt3823llmpxe7ucl5xrh99eey6uttcvcq8jq456p3k5rd8qjdz5kdd0ps8egwka9h6aj3gtg3pzp8xv56tlsq88fastmq94wcfwr90jzg07xyyvade4x90g9zvhyrxhm93vkkagpcqsqqqqqqqqqqz4rs96qtywgyfgwa3uugkzhamnpcdp35u0usvts0n6r9upzg62qxdvc5crng8rccmx8g5mrggcsljgn0mm50uff23ltnvydjfxv9fq8c3nfvfpd5e9zfzseckwstayj9808fxvacfe5wnjfync9gxl3xsrqy63cndhvvngjkg2n5klutrqs04thqmrvt6jmdy49ujdseu4ejwqvqgqqqqqqqqqqrjf3j0jre2q0dfv994v6k9e3apt4nxz5cke8a03y7cvqvk4ay7slq7jqun026crdwt8z20greysgvn3ylv6yj3g9qv3hclavf8gk6sqss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqg7rszp4encvm274xvqyvz3nrceue6lghh5z23yunq6ulqeqghhcgqqqqqqqqqqqqq0rnqyt54zawe3s0z04lj64qv2sctu9xdqm9sgevv2xzajdfpaszss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqgqgqqp6n2y4sgn45qchvjz0y9vt02yqzzhutntc5udmvmfjdvjvnnulqlnzx34dqfdxukrz5td8ewq5tm4dkylpa5syj6c3x7zem2l4dagg9qmhsh27kayvt7vmaht8qpxf0g89p4lh707gzf789gtdkxg8e5qpqyqqqqqqqqqqqwrrsyh67ntxpeax5m7m39lh6783j5m3g8uugflxc7gs39jnety9kje9ndg9yua6vgnysjnnfx4ug38pam80cped3p5al3uehdfsgjruyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyqypq8eq9ac7z7vuxhqwqd35zwln8ql73uwzw8wutp8343gh2n7arc5spz3duaf"; -const statePathRecordv0 = "{ owner: aleo1n43sa2tl5zu28nma3uklpq77gg67rsvg6m460gkzref4ps9wlvgqdvlemw.private, microcredits: 1000000u64.private, _nonce: 5254014383534489106096858537900997326995709579576253332703268634863784343217group.public }"; -const stateRootv0 = "sr1h9l0qe4v5p8cydgknr5lz0f765yvzqg3mjkqwkey49cv9fctvvqqmrrj3m"; -const recordStatePathv1 = "path1qy9gd9an697vtuj0k2rcgne8xv34hculrjz3hll8xz3vsa5hpmhqsrdtn5qqqqqqqzgqmjkc8zzaa7lhmpr9zqzmgparvp7kshm9csxud4a5mcgmtq2p9rmt6jydgt3g7fy7mudea65e6ynzhlsssuemzjnet9a3cryw56cj432zvgr26vhmhhcfrt86r8rdxcjjzp6f598yll562mjt46wtdyycxpv0cx29umt8ysjy7xl3ccvyyyw9w6ajva0w87fqc6vc386zgplmulsc64thu77y9tnn8y78dm5tv7rx5cqsfct6hync5dvhu3paqs55ty4c37268zrpravns4cpf8j5ek8d3d449hllz8ujq3lvdzxshkulv0vfdenjglmx0shw262ul00jwucf4tdgr0l48vx9sjx3kxsjae69r8qvkjmpr8rk7zl9ul3e9nw348wp79dh2a0q880qtjxeac9c5674sqkc3kwf0q8vkd5twqnxkf7y5pd6rr694q7lhrxevu3dxyx2ggwtdfj24f83m3k6ux67v9wyf7v3wm0zve8uwynav2rw7j83qzsy9004gf5ffmx8zpz6rnwm6hd2wqvhejmf85reqc6saledhszpy3rqfyj5k0p7rwhv4wrs5v0mk8jj3fz22gmrev38uk7kt45uaxsxng7wrtdr7c0667uxmzepmxxcw4mwfdvnem4j4ufhkskdgju9qc95lx37n86c4wpjd0y3fmne2qfkszfejxandv3cz8n0kk6sc3vtwq0d0pjs2gzexf9lxt2mwegumamgvfplafglgkm46h5j26cvcacfqepyu479s0vadm5aulzh7hlg0av9ck3837tjep4grr6a5248ckcstunvecs0en96wkn0a5wqak40qd8jqt233hqejnv60vnx5kak5lcrdf9qdg8v42dnheckldhzk2ndrze709ftsd9pgqxxvpjprasx5uzlny5yssqluzmwp3s8e0ac20gjpr2mes5gr300e74yv5keacu9kq98j7aw6pmzg3tgcaa2mxt5rk2uest57r5dhkv3pvadksalpd65p8n5epq4j7dhef4hq9e7y870nzdnlg9vplfkwmgqt780z6rvr4vq8sjfrw6rxkgmgtr8a3gvwc2lse9p7nn0cxnntj7fffufud0k02cgf3gs30qehrwhxfm9xcglsfwr0s74a8sepy4d8ctpgq95vlafmc9lvsaw0kcyua8ujfnqlff48nn0lu25e42pgxzf5jqdx8vswhqczyvyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksy6wz6qstma8g2en5e20t5s7ndaq0mvncwc4hr7kdd7arslflccvr7q9gf9d3547swa5v3l4g09fwn9r53ehn5nefu3mmvuehtjfphgq6lz4sfsfd9tt956qwdj6yxl8fhe4fdm0s3lvg7rhlu0m60fqytqgqsqqqqqqqqqqx56rca5t44ff2p29y4zfa5d827l0lvecru4avuq78p7kuwzjqwp09sd6lwcwj46q5c3xqsxvg3wnd4dunxxgrz9pyef354fyjlcprq6e55eljj8euaqkvy5veynh9vyr9gjdkcg8ssnq9hxw939ex7hgg0q8463npwfy47l8srwvr9ugcewfz00q6jhkyu62fxx74z3umtsp4quqqqqqqqqqqqqzzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qs8333ca20qq98tku6jh9jhcler3wpt42w6favjhsu0hpaxsl4v3qqqqqqqqqqqqqz0lhuapcg0g22xgqn3ngd6u7carphq8hrftgxfewwcd7ke4xevq8pq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qsqsqqxmat4z3xtcds83tukufpk27w2fjvq7yk5mg2mruvdckqpuedpjquck8eqcclherqslh6vmue482z208r9nqkw4cg6tvqutp2snp78q4z69jcdrh6htvxqukzwa0na6ulch7c6wy6z2rt4hl5e3e92anzgwqvqqqqqqqqqqpej2y05yej8wv7jtwztrknw7mt30y68ufcl7ld6778c09093qrcpryuh3k2ed9un0a5ct2dnzmkx20x7fa8vf9lv72l2s9knp0a58ux89g9htx5z3s5wqnt4cr590el4l9k866s4ur6gys6mp3sky349cp5yzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqgqgrqd3n4xly7gdpzlvegkvtdn5d8llxp8ltgpknatmea8zx0kh82ycqy64xqcm"; -const recordCommitmentv1 = "989999648478433123532784640366486241902260408903239982853319188519809923683field"; -const statePathRecordv1 = "{ owner: aleo12a4wll9ax6w5355jph0dr5wt2vla5sss2t4cnch0tc3vzh643v8qcfvc7a.private, microcredits: 1000000u64.private, _nonce: 3634848344765318974603121890869676775499130077229666060613233255327643175219group.public, _version: 1u8.public }"; -const stateRootv1 = "sr1p2rf0v730nzlynajs7zy7fenydd788cus5dllees5ty8d9cwacyqc3nu92"; -const statePathv0RecordOwnerPrivateKey = "APrivateKey1zkpAZAjaJJvPS7EJ7zvk5fb3QcZDCDxMSHSN5ap7ep4FAD7"; +const foreignViewKeyString = + "AViewKey1ghtvuJQQzQ31xSiVh6X1PK8biEVhQBygRGV4KdYmq4JT"; +const helloProgramId = "hellothere.aleo"; +const helloProgramMainFunction = "hello"; +const helloProgram = + "program " + + helloProgramId + + ";\n\nfunction " + + helloProgramMainFunction + + ":\n input r0 as u32.public;\n input r1 as u32.private;\n add r0 r1 into r2;\n output r2 as u32.private;\n"; +const recordStatePathv0 = + "path1qxuhaurx4jsylq34z6vwnufa8m2s3sgpz8w2cp6myj5hps48pd3sp59sqgqqqqqqqzqslytl8jkafzlmzy294psjce649x64scwxdq2kys70y7kh5fxqwgzc6f9lg9ax0mr52p6d42c2lax9m4e6fnxdpde3722sjrlv77ssn7lh9yvtdfwxhpkzhrfscnxw7h36t9j767w34u8rxmdw0y5vyurdtakhtj7tu69nx6yzdv3p0ssr0ursrzgfkqftwjxg6qpj5pg96rkt4vsduph46p9e7nzkmfcn6g2vlc2sz80wr8ct37zdvqmhg4jjpdzfh607804rqksqd40zvn6shrauxv775zgcpd6ecl6gee9kjqfs5hevpjpzkv94md4arwgynpy6wr4k3txhap0n93y3fl3edwgul0gyn268hrlylqmyqvv8rse40tytnk9atrjx9mlnhp6mrqhd45u99s8gsjw899kz6yhktejwj6snlp7r38kwdmlxczr7m0sn46tmjjndqzn7zmw23jd0x2vwf4x3z7dt642zwweq597dlzaxqx2qg6e89jgmq8fq9pwpy6uqt6prke4v66u39aqz7qewnh2a8er0y78m2dyayr8sdsjpdcdvc78f95wdvapn8072h2jqtj6zvkc52tavlkqfmvfxvpq87h39ajdx4c0sfckm05aqg9mfdykse2cf0vsr0regrp9m9dundvgyat0rcg3fsex6c0335hs7tafxjgkw7k9mxm7arus94ytv4g5x7y0d0pjs2gzexf9lxt2mwegumamgvfplafglgkm46h5j26cvcacfqmj39p7x28pfdma2dp0jyet5nz4pr9j0yf6f6l72kzexsd7kp82s6z6rnk6l2yg7xpeyc3dy907wefjn5w35prnacapd4acn20qeldgwwmuev0d8t6tz02x2kv8qg9sxhakx6fmw5rd35fda735pchuct5gcg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyd7cpzdqmmk8kqx9mamt3823llmpxe7ucl5xrh99eey6uttcvcq8jq456p3k5rd8qjdz5kdd0ps8egwka9h6aj3gtg3pzp8xv56tlsq88fastmq94wcfwr90jzg07xyyvade4x90g9zvhyrxhm93vkkagpcqsqqqqqqqqqqz4rs96qtywgyfgwa3uugkzhamnpcdp35u0usvts0n6r9upzg62qxdvc5crng8rccmx8g5mrggcsljgn0mm50uff23ltnvydjfxv9fq8c3nfvfpd5e9zfzseckwstayj9808fxvacfe5wnjfync9gxl3xsrqy63cndhvvngjkg2n5klutrqs04thqmrvt6jmdy49ujdseu4ejwqvqgqqqqqqqqqqrjf3j0jre2q0dfv994v6k9e3apt4nxz5cke8a03y7cvqvk4ay7slq7jqun026crdwt8z20greysgvn3ylv6yj3g9qv3hclavf8gk6sqss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqg7rszp4encvm274xvqyvz3nrceue6lghh5z23yunq6ulqeqghhcgqqqqqqqqqqqqq0rnqyt54zawe3s0z04lj64qv2sctu9xdqm9sgevv2xzajdfpaszss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqgqgqqp6n2y4sgn45qchvjz0y9vt02yqzzhutntc5udmvmfjdvjvnnulqlnzx34dqfdxukrz5td8ewq5tm4dkylpa5syj6c3x7zem2l4dagg9qmhsh27kayvt7vmaht8qpxf0g89p4lh707gzf789gtdkxg8e5qpqyqqqqqqqqqqqwrrsyh67ntxpeax5m7m39lh6783j5m3g8uugflxc7gs39jnety9kje9ndg9yua6vgnysjnnfx4ug38pam80cped3p5al3uehdfsgjruyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyqypq8eq9ac7z7vuxhqwqd35zwln8ql73uwzw8wutp8343gh2n7arc5spz3duaf"; +const statePathRecordv0 = + "{ owner: aleo1n43sa2tl5zu28nma3uklpq77gg67rsvg6m460gkzref4ps9wlvgqdvlemw.private, microcredits: 1000000u64.private, _nonce: 5254014383534489106096858537900997326995709579576253332703268634863784343217group.public }"; +const stateRootv0 = + "sr1h9l0qe4v5p8cydgknr5lz0f765yvzqg3mjkqwkey49cv9fctvvqqmrrj3m"; +const recordStatePathv1 = + "path1qy9gd9an697vtuj0k2rcgne8xv34hculrjz3hll8xz3vsa5hpmhqsrdtn5qqqqqqqzgqmjkc8zzaa7lhmpr9zqzmgparvp7kshm9csxud4a5mcgmtq2p9rmt6jydgt3g7fy7mudea65e6ynzhlsssuemzjnet9a3cryw56cj432zvgr26vhmhhcfrt86r8rdxcjjzp6f598yll562mjt46wtdyycxpv0cx29umt8ysjy7xl3ccvyyyw9w6ajva0w87fqc6vc386zgplmulsc64thu77y9tnn8y78dm5tv7rx5cqsfct6hync5dvhu3paqs55ty4c37268zrpravns4cpf8j5ek8d3d449hllz8ujq3lvdzxshkulv0vfdenjglmx0shw262ul00jwucf4tdgr0l48vx9sjx3kxsjae69r8qvkjmpr8rk7zl9ul3e9nw348wp79dh2a0q880qtjxeac9c5674sqkc3kwf0q8vkd5twqnxkf7y5pd6rr694q7lhrxevu3dxyx2ggwtdfj24f83m3k6ux67v9wyf7v3wm0zve8uwynav2rw7j83qzsy9004gf5ffmx8zpz6rnwm6hd2wqvhejmf85reqc6saledhszpy3rqfyj5k0p7rwhv4wrs5v0mk8jj3fz22gmrev38uk7kt45uaxsxng7wrtdr7c0667uxmzepmxxcw4mwfdvnem4j4ufhkskdgju9qc95lx37n86c4wpjd0y3fmne2qfkszfejxandv3cz8n0kk6sc3vtwq0d0pjs2gzexf9lxt2mwegumamgvfplafglgkm46h5j26cvcacfqepyu479s0vadm5aulzh7hlg0av9ck3837tjep4grr6a5248ckcstunvecs0en96wkn0a5wqak40qd8jqt233hqejnv60vnx5kak5lcrdf9qdg8v42dnheckldhzk2ndrze709ftsd9pgqxxvpjprasx5uzlny5yssqluzmwp3s8e0ac20gjpr2mes5gr300e74yv5keacu9kq98j7aw6pmzg3tgcaa2mxt5rk2uest57r5dhkv3pvadksalpd65p8n5epq4j7dhef4hq9e7y870nzdnlg9vplfkwmgqt780z6rvr4vq8sjfrw6rxkgmgtr8a3gvwc2lse9p7nn0cxnntj7fffufud0k02cgf3gs30qehrwhxfm9xcglsfwr0s74a8sepy4d8ctpgq95vlafmc9lvsaw0kcyua8ujfnqlff48nn0lu25e42pgxzf5jqdx8vswhqczyvyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksy6wz6qstma8g2en5e20t5s7ndaq0mvncwc4hr7kdd7arslflccvr7q9gf9d3547swa5v3l4g09fwn9r53ehn5nefu3mmvuehtjfphgq6lz4sfsfd9tt956qwdj6yxl8fhe4fdm0s3lvg7rhlu0m60fqytqgqsqqqqqqqqqqx56rca5t44ff2p29y4zfa5d827l0lvecru4avuq78p7kuwzjqwp09sd6lwcwj46q5c3xqsxvg3wnd4dunxxgrz9pyef354fyjlcprq6e55eljj8euaqkvy5veynh9vyr9gjdkcg8ssnq9hxw939ex7hgg0q8463npwfy47l8srwvr9ugcewfz00q6jhkyu62fxx74z3umtsp4quqqqqqqqqqqqqzzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqfpq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qs8333ca20qq98tku6jh9jhcler3wpt42w6favjhsu0hpaxsl4v3qqqqqqqqqqqqqz0lhuapcg0g22xgqn3ngd6u7carphq8hrftgxfewwcd7ke4xevq8pq4n6js4r56pexg3w667ruzkkejgla4gxfg5e3sph7z0gty2ksyss2eafg236dqunyghdd0p7pttvey0765ry52vccqmlp859j9tgzgg9v7559gaxswfjytkkhslq44kvj8ld2pj29xvvqdlsn6zez45pyyzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qsqsqqxmat4z3xtcds83tukufpk27w2fjvq7yk5mg2mruvdckqpuedpjquck8eqcclherqslh6vmue482z208r9nqkw4cg6tvqutp2snp78q4z69jcdrh6htvxqukzwa0na6ulch7c6wy6z2rt4hl5e3e92anzgwqvqqqqqqqqqqpej2y05yej8wv7jtwztrknw7mt30y68ufcl7ld6778c09093qrcpryuh3k2ed9un0a5ct2dnzmkx20x7fa8vf9lv72l2s9knp0a58ux89g9htx5z3s5wqnt4cr590el4l9k866s4ur6gys6mp3sky349cp5yzk022z5wng8yez9mttc0s26mxfrlk4qe9znxxqxlcfapv326qjzpt849p28f5rjv3za44u8c9ddny3lm2svj3fnrqr0uy7skg4dqgqgrqd3n4xly7gdpzlvegkvtdn5d8llxp8ltgpknatmea8zx0kh82ycqy64xqcm"; +const recordCommitmentv1 = + "989999648478433123532784640366486241902260408903239982853319188519809923683field"; +const statePathRecordv1 = + "{ owner: aleo12a4wll9ax6w5355jph0dr5wt2vla5sss2t4cnch0tc3vzh643v8qcfvc7a.private, microcredits: 1000000u64.private, _nonce: 3634848344765318974603121890869676775499130077229666060613233255327643175219group.public, _version: 1u8.public }"; +const stateRootv1 = + "sr1p2rf0v730nzlynajs7zy7fenydd788cus5dllees5ty8d9cwacyqc3nu92"; +const statePathv0RecordOwnerPrivateKey = + "APrivateKey1zkpAZAjaJJvPS7EJ7zvk5fb3QcZDCDxMSHSN5ap7ep4FAD7"; -export { addressString, beaconAddressString, beaconPrivateKeyString, beaconViewKeyString, foreignCiphertextString, foreignViewKeyString, fundedAddressString, fundedPrivateKeyString, helloProgram, helloProgramId, helloProgramMainFunction, message, recordCiphertextString, recordPlaintextString, privateKeyString, seed, viewKeyString, recordStatePathv0, statePathRecordv0, stateRootv0, recordCommitmentv1, recordStatePathv1, statePathRecordv1, stateRootv1, statePathv0RecordOwnerPrivateKey }; \ No newline at end of file +export { + addressString, + beaconAddressString, + beaconPrivateKeyString, + beaconViewKeyString, + foreignCiphertextString, + foreignViewKeyString, + fundedAddressString, + fundedPrivateKeyString, + helloProgram, + helloProgramId, + helloProgramMainFunction, + message, + recordCiphertextString, + recordPlaintextString, + privateKeyString, + seed, + viewKeyString, + recordStatePathv0, + statePathRecordv0, + stateRootv0, + recordCommitmentv1, + recordStatePathv1, + statePathRecordv1, + stateRootv1, + statePathv0RecordOwnerPrivateKey, +}; diff --git a/sdk/tests/data/algebra.ts b/sdk/tests/data/algebra.ts index bfebb56fa..d498aa0ab 100644 --- a/sdk/tests/data/algebra.ts +++ b/sdk/tests/data/algebra.ts @@ -1,73 +1,135 @@ /// Chosen field generator for unit tests. -const FieldGenerator = "6901184695964460143517399399785179769303979738604374595034454667750561389951field"; +const FieldGenerator = + "6901184695964460143517399399785179769303979738604374595034454667750561389951field"; -const expectedBHP256Hash = "6598783590400646668520287182111940745128736688662567560889280298782004590744field"; -const expectedBHP256HashToGroup = "6598783590400646668520287182111940745128736688662567560889280298782004590744group"; -const expectedBHP256Commit = "2082593229082262381320813637490421437118190271647234845496709605556536239325field"; -const expectedBHP256CommitToGroup = "2082593229082262381320813637490421437118190271647234845496709605556536239325group"; +const expectedBHP256Hash = + "6598783590400646668520287182111940745128736688662567560889280298782004590744field"; +const expectedBHP256HashToGroup = + "6598783590400646668520287182111940745128736688662567560889280298782004590744group"; +const expectedBHP256Commit = + "2082593229082262381320813637490421437118190271647234845496709605556536239325field"; +const expectedBHP256CommitToGroup = + "2082593229082262381320813637490421437118190271647234845496709605556536239325group"; -const expectedBHP512Hash = "1999822247055455701784132575168585963242270924818325055212777584340934143916field"; -const expectedBHP512HashToGroup = "1999822247055455701784132575168585963242270924818325055212777584340934143916group"; -const expectedBHP512Commit = "5702389816400555524923367597422208802601150072230907483584911172290469121501field"; -const expectedBHP512CommitToGroup = "5702389816400555524923367597422208802601150072230907483584911172290469121501group"; +const expectedBHP512Hash = + "1999822247055455701784132575168585963242270924818325055212777584340934143916field"; +const expectedBHP512HashToGroup = + "1999822247055455701784132575168585963242270924818325055212777584340934143916group"; +const expectedBHP512Commit = + "5702389816400555524923367597422208802601150072230907483584911172290469121501field"; +const expectedBHP512CommitToGroup = + "5702389816400555524923367597422208802601150072230907483584911172290469121501group"; -const expectedBHP768Hash = "2151969465368594987252495420983687091399796648584310972463234119446869285487field"; -const expectedBHP768HashToGroup = "2151969465368594987252495420983687091399796648584310972463234119446869285487group"; -const expectedBHP768Commit = "6864405450086290602821044466228181619883104706596830134315601499052844411477field"; -const expectedBHP768CommitToGroup = "6864405450086290602821044466228181619883104706596830134315601499052844411477group"; +const expectedBHP768Hash = + "2151969465368594987252495420983687091399796648584310972463234119446869285487field"; +const expectedBHP768HashToGroup = + "2151969465368594987252495420983687091399796648584310972463234119446869285487group"; +const expectedBHP768Commit = + "6864405450086290602821044466228181619883104706596830134315601499052844411477field"; +const expectedBHP768CommitToGroup = + "6864405450086290602821044466228181619883104706596830134315601499052844411477group"; -const expectedBHP1024Hash = "5012718612752062270473753102141489376962182387590529397014861985202772382691field"; -const expectedBHP1024HashToGroup = "5012718612752062270473753102141489376962182387590529397014861985202772382691group"; -const expectedBHP1024Commit = "7355825599810198751875041563628058158258329601754296576667945643687737135311field"; -const expectedBHP1024CommitToGroup = "7355825599810198751875041563628058158258329601754296576667945643687737135311group"; +const expectedBHP1024Hash = + "5012718612752062270473753102141489376962182387590529397014861985202772382691field"; +const expectedBHP1024HashToGroup = + "5012718612752062270473753102141489376962182387590529397014861985202772382691group"; +const expectedBHP1024Commit = + "7355825599810198751875041563628058158258329601754296576667945643687737135311field"; +const expectedBHP1024CommitToGroup = + "7355825599810198751875041563628058158258329601754296576667945643687737135311group"; -const expectedPedersen64Hash = "7641648045329738245569851934154954931983769745339084901557923877960530097536field"; -const expectedPedersen64Commit = "4214712642932384902492032091870474319129850239541860097363932997605707681866field"; -const expectedPedersen64CommitToGroup = "4214712642932384902492032091870474319129850239541860097363932997605707681866group"; +const expectedPedersen64Hash = + "7641648045329738245569851934154954931983769745339084901557923877960530097536field"; +const expectedPedersen64Commit = + "4214712642932384902492032091870474319129850239541860097363932997605707681866field"; +const expectedPedersen64CommitToGroup = + "4214712642932384902492032091870474319129850239541860097363932997605707681866group"; -const expectedPedersen128Hash = "2635650346747854690304767890512510318865900869251739129887484152040487384946field"; -const expectedPedersen128Commit = "2510571858482041100602183741907011481539483352792333201599869098117502813920field"; -const expectedPedersen128CommitToGroup = "2510571858482041100602183741907011481539483352792333201599869098117502813920group"; +const expectedPedersen128Hash = + "2635650346747854690304767890512510318865900869251739129887484152040487384946field"; +const expectedPedersen128Commit = + "2510571858482041100602183741907011481539483352792333201599869098117502813920field"; +const expectedPedersen128CommitToGroup = + "2510571858482041100602183741907011481539483352792333201599869098117502813920group"; // Poseidon2 (2-bit) -const expectedPoseidon2Hash = "5077032915756006405056357976663159304886914340125619713231037384461532417432field"; -const expectedPoseidon2HashToScalar = "1458530127089875298069764695141662184472227319324352086997987884214247116184scalar"; -const expectedPoseidon2HashToGroup = "6725009305095024168741689761204333975914699402177819232581771014527031061395group"; +const expectedPoseidon2Hash = + "5077032915756006405056357976663159304886914340125619713231037384461532417432field"; +const expectedPoseidon2HashToScalar = + "1458530127089875298069764695141662184472227319324352086997987884214247116184scalar"; +const expectedPoseidon2HashToGroup = + "6725009305095024168741689761204333975914699402177819232581771014527031061395group"; const expectedPoseidon2HashMany = [ "5077032915756006405056357976663159304886914340125619713231037384461532417432field", - "7556882202382565798668668067123114920984638418911135511310139766017967865048field" + "7556882202382565798668668067123114920984638418911135511310139766017967865048field", ]; // Poseidon4 (4-bit) -const expectedPoseidon4Hash = "7634545961811588936484713301052408690645763622397315677026301158133701966262field"; -const expectedPoseidon4HashToScalar = "397540384479326722511526738009414449816389580794780424560202157639131363766scalar"; -const expectedPoseidon4HashToGroup = "3501730277828187397862093302382961412852320364543135222391952713144715771762group"; +const expectedPoseidon4Hash = + "7634545961811588936484713301052408690645763622397315677026301158133701966262field"; +const expectedPoseidon4HashToScalar = + "397540384479326722511526738009414449816389580794780424560202157639131363766scalar"; +const expectedPoseidon4HashToGroup = + "3501730277828187397862093302382961412852320364543135222391952713144715771762group"; const expectedPoseidon4HashMany = [ "7634545961811588936484713301052408690645763622397315677026301158133701966262field", - "6897041872403455461974187270360400367782809654597486212257448618986966168076field" + "6897041872403455461974187270360400367782809654597486212257448618986966168076field", ]; // Poseidon8 (8-bit) -const expectedPoseidon8Hash = "491310798299229303453029190751493384624125693265313448104421146921465784609field"; -const expectedPoseidon8HashToScalar = "491310798299229303453029190751493384624125693265313448104421146921465784609scalar"; -const expectedPoseidon8HashToGroup = "7639970001763982540831449531477540915761314714085721616177080725803487493174group"; +const expectedPoseidon8Hash = + "491310798299229303453029190751493384624125693265313448104421146921465784609field"; +const expectedPoseidon8HashToScalar = + "491310798299229303453029190751493384624125693265313448104421146921465784609scalar"; +const expectedPoseidon8HashToGroup = + "7639970001763982540831449531477540915761314714085721616177080725803487493174group"; const expectedPoseidon8HashMany = [ "491310798299229303453029190751493384624125693265313448104421146921465784609field", - "476244868544635089532724552855423418881253978175875838088194622836984800804field" + "476244868544635089532724552855423418881253978175875838088194622836984800804field", ]; /// Write the string representation of the group generator. -const GroupGenerator = "1540945439182663264862696551825005342995406165131907382295858612069623286213group"; +const GroupGenerator = + "1540945439182663264862696551825005342995406165131907382295858612069623286213group"; /// Choose a scalar generator for unit tests. -const ScalarGenerator = "1774157567936692047646837016039369013254365378639847034769080448564598011047scalar"; +const ScalarGenerator = + "1774157567936692047646837016039369013254365378639847034769080448564598011047scalar"; -export {expectedBHP512Commit, expectedBHP512CommitToGroup, expectedBHP512Hash, - expectedBHP512HashToGroup, expectedBHP768Commit, expectedBHP768CommitToGroup, expectedBHP768Hash, - expectedBHP768HashToGroup, expectedBHP1024Commit, expectedBHP1024CommitToGroup, expectedBHP1024Hash, - expectedBHP1024HashToGroup, expectedBHP256Commit, expectedBHP256CommitToGroup, expectedBHP256Hash, - expectedBHP256HashToGroup, expectedPedersen64CommitToGroup, expectedPedersen64Commit, expectedPedersen64Hash, - expectedPedersen128CommitToGroup, expectedPedersen128Commit, expectedPedersen128Hash, expectedPoseidon2Hash, - expectedPoseidon2HashToGroup, expectedPoseidon2HashToScalar, expectedPoseidon2HashMany, expectedPoseidon4Hash, - expectedPoseidon4HashToGroup, expectedPoseidon4HashToScalar, expectedPoseidon4HashMany, expectedPoseidon8Hash, - expectedPoseidon8HashToGroup, expectedPoseidon8HashToScalar, expectedPoseidon8HashMany, FieldGenerator, - GroupGenerator, ScalarGenerator -}; \ No newline at end of file +export { + expectedBHP512Commit, + expectedBHP512CommitToGroup, + expectedBHP512Hash, + expectedBHP512HashToGroup, + expectedBHP768Commit, + expectedBHP768CommitToGroup, + expectedBHP768Hash, + expectedBHP768HashToGroup, + expectedBHP1024Commit, + expectedBHP1024CommitToGroup, + expectedBHP1024Hash, + expectedBHP1024HashToGroup, + expectedBHP256Commit, + expectedBHP256CommitToGroup, + expectedBHP256Hash, + expectedBHP256HashToGroup, + expectedPedersen64CommitToGroup, + expectedPedersen64Commit, + expectedPedersen64Hash, + expectedPedersen128CommitToGroup, + expectedPedersen128Commit, + expectedPedersen128Hash, + expectedPoseidon2Hash, + expectedPoseidon2HashToGroup, + expectedPoseidon2HashToScalar, + expectedPoseidon2HashMany, + expectedPoseidon4Hash, + expectedPoseidon4HashToGroup, + expectedPoseidon4HashToScalar, + expectedPoseidon4HashMany, + expectedPoseidon8Hash, + expectedPoseidon8HashToGroup, + expectedPoseidon8HashToScalar, + expectedPoseidon8HashMany, + FieldGenerator, + GroupGenerator, + ScalarGenerator, +}; diff --git a/sdk/tests/data/program.ts b/sdk/tests/data/program.ts index 4c5e06efb..e8df88fdb 100644 --- a/sdk/tests/data/program.ts +++ b/sdk/tests/data/program.ts @@ -1,9 +1,22 @@ // Define an execution with imports, its imported programs, and the verifying keys for the functions called from the imported programs. -const SPIN_VERIFYING_KEY: string = "verifier1qysqqqqqqqqqqqxrjvqqqqqqqqqtaycqqqqqqqqqnpjszqqqqqqqpm9lqyqqqqqqqrddxqqqqqqqqqqvqqqqqqqqqqqt8ksvknxqn3yc8rlnf75w0d6v7h3kad4patlmn4rzq7yny8rawe4c58yq3t64v2p4ts3g506j2juq5mzxwk3z34zrjrvkhl99xju75derjkxv64ktsdzmu3mwkj486tnndw2x8cnwj9q469yf3l7304sqzyufsu8pqdcykenav93ms3uqh9sgahmxrr74f6wr8eqyxkud0xw2jlt9v674gj0ml4zrqzf3mz0pszntjt5mtw79spqcud6ekjpqc9u28snzyg8vdt78up3q6mlq7nh7ms06xyslj7s9jcna0r7s2t9pdqyvae8kcjqcl358u88qrcu83q3zta2rf58yvqrtvqmtux8d0h3sx9r44xw4a069lz989262kja6xuqzttnujfrthkj6fr0vlvz9fdzle8kupdqea0x8pe6xju962lskf7sajl3pddxx4arqd8aafuzxczypyk0cqvjt26th3hg845vrxx9vtzpgn5vpu5j49ednwvyf4yl86ujpknqcqzq2jvn5ux8sh286tkgsqv7mqz20629zxffvee9ncdl8mdw38uhqsfdam3585jdxcuu6wxds7m8mss5yxj2tmcu6cq93hh28s9ws33ey0vz9mv9xcy3pfyekzruxtznw0625ynvc4j2mtd8yl9drcftlkqqvl4em0dxz6umvxua9cqg9sesfw6gs6fwvgtadw79mkh48kwpvevypd0f6da8scpck2xnhxhe6zcxrknntkd92wcqccdfdy2qhwne53e3fhak42p0y73re7njr8xyraq7dy4ncvfcly5j2jler3ln04dgzkrdsm7vufdrarh7xy6vpw634mv2jyv088zj9upl63gntav65gskcyez0cg86wa6hzhjjjygqzh7mndqnk8yhdh9ppud0a7tqruvdqge9a9ltkk4vjua07kq2l7xqd55zjyh7jqq96tyglk55l4a25t2sqqqqqqqqqs6zqrd"; -const SPEND_VERIFYING_KEY: string = "verifier1qygqqqqqqqqqqqz33cqqqqqqqqqxrrsqqqqqqqqql5rqzqqqqqqqptslqyqqqqqqqpedvqqqqqqqqqqvqqqqqqqqqqqyudfp2mjt8zj3lskjzunts69hgx2kpwryn37p62f30mwqtca442epnw2dqstdnh88ljx44p5cmjyqnfx620nl9pwsh6qsaeughffgfrpxc5qhl5wda6ekss7pt3j72ps32wlrv7mlljjq9lq5jn8yn29gztqk8yz8ehq7smprjs3gzsllqpzaq07gk9yw50te0yarl5yd796xldapdfpc4n75ccy7z2jkmfyss8zz89gz7fw7937xjzt3k3cmhrekgjzzvxmqtzd20ffae8eym3lhq2gwejgqjmw9c8edum06s6a24q2zv9w3kcz5uavcgatlskxxy285nq5uhceusavwzu8rqu0e6lj6j5kgva00aw6x0khfyn4gg3ppeqqwmw3crvtgrdc8w45flwm4d9e8r3edct8hsgven70juqd9me3jk27cgr9fz90h57r2eqdctegc3ecqfh3kr9vx8u0tjhmfp4edpd9skke5yqw9u2ep8l5mwcuhdqcthr8u98wqsng6thy7wsfm2hut3musppwexfetnc7p46ckcjx2a3zvcxf8hht9yja03ymx5sy9wuhsmvsag676w6jg5yeqw3qzah5r38zpqxk8wzm0vk52ed4qtypnup37x2uj89lftvk4hfskd9wwumxvgawsm5xfum05p7vapjzpwu79k9hr7qf0wckhmkmmdxka8zz5qkaxrjsz3eny3x0vdsn5z9zcm8n2me44a56ej7893uduwv2l3ls7ua09dgqsvuddsgn4c9gn68hplup49rtfht0we6ruwn2sqe53z9sv4zq8924w7fku98fwpujykme2tumda4sqk54july0cd8gsvdx62t7gqjrfmrt6t7ezc8cvrxw99rq72h3qm3rr87fl47d5uw55pvdpfcj7weqqvks75erlpc73d08e2xsatay4vc2ey69wz8648wzqvagsyx4vs4lmnqqqqqqqqqqqwv0uty"; -const MINT_VERIFYING_KEY: string = "verifier1qygqqqqqqqqqqqyjvyqqqqqqqqqfvcgqqqqqqqqqar9sqqqqqqqqq282qqqqqqqqqpjexqqqqqqqqqqvqqqqqqqqqqqtj8lxtlmlrtwaffkdwfctwu2ujt795jnnwj0jsgyc57n0lcsqkjy5u04k25gf9wzt45xwaxt9vzsqwj2ad42zuukvjklg8r8t5uxw6sslpsjdetjnx0vpqdwp7rddernk8kdr6av3cpx6e5cd7tu579ucr47qrhz003d2xpwc5rejfp60acvra8edv3qmkp29ukhzv0htt8gnnkfk6afgj2vg9ys9hjl4ww3gszkd2zl2yh8ms0tfxn546w8q20dmj5fcxuyj8vevl9r0pk3fm0y27g5r25z97gjyxrgfdperxh490qxjhcffdfxdc65hvtan5q76ghfpqg3mzzs350s0jlu5pc4k3252nq93dctnjscmy3rgxypswpvucjqxreh6x5ahwfpa7yrl24s5pq2naz6fz0u3v2zhqp3fcx0ycq9ptxtqscnf3g6u8llurjer85g5zyqpwfd599vphjna37cjehn88y36u6hl0tp9r206grcdrssggawcjfcf3vuscy90sks8s0klryzmdgxszagsqqxzly4j92q7psv9a36exzuz7ckkrslcxtnv77vq9hcurdcxq98xvdz0a8pnct7lcr9x8dprsxhdsq2j4t3805e3dztss26h8p8e6q3mzhv0549fw8dktm06tjayvnu5xag8hx9k3hsgvjknyaaawqzcj0wm5nzffuq3tr0pfh0arw5qgsc326sccyn7wz3h0nu7vcz9pu4k39syt248t7hexlapdvffpzq0we245z8l70qk8c5kmqkfwmedmf0dje4kxtc0qntwc9yxl09jhcshytl73ddhuem5lyx9ns6qvcgpspr50m76j4c4aa5wrgrqvm40jl00whc5yajujcqlwmlpdsskfw3se2ghy25pg66wm7z8zxatz25sp6e63w6qr378h0ul7v5udurmc26fkkeqynypurffzz50lmcmqv67vjpqqqqqqqqqqa59tnr"; -const PROGRAM: string = "import puzzle_arcade_coin_v002.aleo;\nimport puzzle_arcade_ticket_v002.aleo;\n\nprogram puzzle_spinner_v002.aleo;\n\nstruct Result:\n nonce as field;\n tickets as u64;\n\nmapping used_nonces:\n key as field.public;\n value as boolean.public;\n\nfunction spin:\n input r0 as puzzle_arcade_coin_v002.aleo/PuzzleArcadeCoin.record;\n input r1 as Result.public;\n input r2 as signature.private;\n sign.verify r2 aleo196a39wq9q8ea779cmlmff0c9pj2gl4f5e8fhjpvmufe5utuq7y8snz4h2l r1 into r3;\n assert.eq r3 true ;\n is.eq r1.tickets 1000000u64 into r4;\n is.eq r1.tickets 2000000u64 into r5;\n or r4 r5 into r6;\n is.eq r1.tickets 5000000u64 into r7;\n or r6 r7 into r8;\n is.eq r1.tickets 10000000u64 into r9;\n or r8 r9 into r10;\n assert.eq r10 true ;\n call puzzle_arcade_coin_v002.aleo/spend r0 1000000u64 into r11;\n call puzzle_arcade_ticket_v002.aleo/mint r0.owner r1.tickets into r12 r13;\n async spin r13 r1.nonce into r14;\n output r11 as puzzle_arcade_coin_v002.aleo/PuzzleArcadeCoin.record;\n output r12 as puzzle_arcade_ticket_v002.aleo/PuzzleArcadeTicket.record;\n output r14 as puzzle_spinner_v002.aleo/spin.future;\n\nfinalize spin:\n input r0 as puzzle_arcade_ticket_v002.aleo/mint.future;\n input r1 as field.public;\n get.or_use used_nonces[r1] false into r2;\n assert.eq r2 false ;\n set true into used_nonces[r1];\n await r0;\n"; -const IMPORT_1: string = "program puzzle_arcade_coin_v002.aleo;\n\nrecord PuzzleArcadeCoin:\n owner as address.private;\n amount as u64.private;\n\nfunction mint:\n input r0 as address.public;\n input r1 as u64.public;\n assert.eq self.caller self.signer ;\n assert.eq self.caller aleo196a39wq9q8ea779cmlmff0c9pj2gl4f5e8fhjpvmufe5utuq7y8snz4h2l ;\n cast r0 r1 into r2 as PuzzleArcadeCoin.record;\n output r2 as PuzzleArcadeCoin.record;\n\nfunction spend:\n input r0 as PuzzleArcadeCoin.record;\n input r1 as u64.public;\n gte r0.amount r1 into r2;\n assert.eq r2 true ;\n sub r0.amount r1 into r3;\n cast r0.owner r3 into r4 as PuzzleArcadeCoin.record;\n output r4 as PuzzleArcadeCoin.record;\n"; -const IMPORT_2: string = "program puzzle_arcade_ticket_v002.aleo;\n\nrecord PuzzleArcadeTicket:\n owner as address.private;\n amount as u64.private;\n\nmapping registry:\n key as address.public;\n value as boolean.public;\n\nfunction add_program_to_registry:\n input r0 as address.private;\n assert.eq self.caller self.signer ;\n assert.eq self.caller aleo196a39wq9q8ea779cmlmff0c9pj2gl4f5e8fhjpvmufe5utuq7y8snz4h2l ;\n async add_program_to_registry r0 into r1;\n output r1 as puzzle_arcade_ticket_v002.aleo/add_program_to_registry.future;\n\nfinalize add_program_to_registry:\n input r0 as address.public;\n set true into registry[r0];\n\nfunction mint:\n input r0 as address.public;\n input r1 as u64.public;\n cast r0 r1 into r2 as PuzzleArcadeTicket.record;\n async mint self.caller into r3;\n output r2 as PuzzleArcadeTicket.record;\n output r3 as puzzle_arcade_ticket_v002.aleo/mint.future;\n\nfinalize mint:\n input r0 as address.public;\n get.or_use registry[r0] false into r1;\n assert.eq r1 true ;\n\nfunction spend:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as u64.public;\n gte r0.amount r1 into r2;\n assert.eq r2 true ;\n sub r0.amount r1 into r3;\n cast r0.owner r3 into r4 as PuzzleArcadeTicket.record;\n output r4 as PuzzleArcadeTicket.record;\n\nfunction join:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r2;\n assert.eq r2 true ;\n gt r1.amount 0u64 into r3;\n assert.eq r3 true ;\n add r0.amount r1.amount into r4;\n cast self.signer r4 into r5 as PuzzleArcadeTicket.record;\n output r5 as PuzzleArcadeTicket.record;\n\nfunction join3:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n input r2 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r3;\n assert.eq r3 true ;\n gt r1.amount 0u64 into r4;\n assert.eq r4 true ;\n gt r2.amount 0u64 into r5;\n assert.eq r5 true ;\n add r0.amount r1.amount into r6;\n add r6 r2.amount into r7;\n cast self.signer r7 into r8 as PuzzleArcadeTicket.record;\n output r8 as PuzzleArcadeTicket.record;\n\nfunction join4:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n input r2 as PuzzleArcadeTicket.record;\n input r3 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r4;\n assert.eq r4 true ;\n gt r1.amount 0u64 into r5;\n assert.eq r5 true ;\n gt r2.amount 0u64 into r6;\n assert.eq r6 true ;\n gt r3.amount 0u64 into r7;\n assert.eq r7 true ;\n add r0.amount r1.amount into r8;\n add r8 r2.amount into r9;\n add r9 r3.amount into r10;\n cast self.signer r10 into r11 as PuzzleArcadeTicket.record;\n output r11 as PuzzleArcadeTicket.record;\n\nfunction join5:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n input r2 as PuzzleArcadeTicket.record;\n input r3 as PuzzleArcadeTicket.record;\n input r4 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r5;\n assert.eq r5 true ;\n gt r1.amount 0u64 into r6;\n assert.eq r6 true ;\n gt r2.amount 0u64 into r7;\n assert.eq r7 true ;\n gt r3.amount 0u64 into r8;\n assert.eq r8 true ;\n gt r4.amount 0u64 into r9;\n assert.eq r9 true ;\n add r0.amount r1.amount into r10;\n add r10 r2.amount into r11;\n add r11 r3.amount into r12;\n add r12 r4.amount into r13;\n cast self.signer r13 into r14 as PuzzleArcadeTicket.record;\n output r14 as PuzzleArcadeTicket.record;\n"; +const SPIN_VERIFYING_KEY: string = + "verifier1qysqqqqqqqqqqqxrjvqqqqqqqqqtaycqqqqqqqqqnpjszqqqqqqqpm9lqyqqqqqqqrddxqqqqqqqqqqvqqqqqqqqqqqt8ksvknxqn3yc8rlnf75w0d6v7h3kad4patlmn4rzq7yny8rawe4c58yq3t64v2p4ts3g506j2juq5mzxwk3z34zrjrvkhl99xju75derjkxv64ktsdzmu3mwkj486tnndw2x8cnwj9q469yf3l7304sqzyufsu8pqdcykenav93ms3uqh9sgahmxrr74f6wr8eqyxkud0xw2jlt9v674gj0ml4zrqzf3mz0pszntjt5mtw79spqcud6ekjpqc9u28snzyg8vdt78up3q6mlq7nh7ms06xyslj7s9jcna0r7s2t9pdqyvae8kcjqcl358u88qrcu83q3zta2rf58yvqrtvqmtux8d0h3sx9r44xw4a069lz989262kja6xuqzttnujfrthkj6fr0vlvz9fdzle8kupdqea0x8pe6xju962lskf7sajl3pddxx4arqd8aafuzxczypyk0cqvjt26th3hg845vrxx9vtzpgn5vpu5j49ednwvyf4yl86ujpknqcqzq2jvn5ux8sh286tkgsqv7mqz20629zxffvee9ncdl8mdw38uhqsfdam3585jdxcuu6wxds7m8mss5yxj2tmcu6cq93hh28s9ws33ey0vz9mv9xcy3pfyekzruxtznw0625ynvc4j2mtd8yl9drcftlkqqvl4em0dxz6umvxua9cqg9sesfw6gs6fwvgtadw79mkh48kwpvevypd0f6da8scpck2xnhxhe6zcxrknntkd92wcqccdfdy2qhwne53e3fhak42p0y73re7njr8xyraq7dy4ncvfcly5j2jler3ln04dgzkrdsm7vufdrarh7xy6vpw634mv2jyv088zj9upl63gntav65gskcyez0cg86wa6hzhjjjygqzh7mndqnk8yhdh9ppud0a7tqruvdqge9a9ltkk4vjua07kq2l7xqd55zjyh7jqq96tyglk55l4a25t2sqqqqqqqqqs6zqrd"; +const SPEND_VERIFYING_KEY: string = + "verifier1qygqqqqqqqqqqqz33cqqqqqqqqqxrrsqqqqqqqqql5rqzqqqqqqqptslqyqqqqqqqpedvqqqqqqqqqqvqqqqqqqqqqqyudfp2mjt8zj3lskjzunts69hgx2kpwryn37p62f30mwqtca442epnw2dqstdnh88ljx44p5cmjyqnfx620nl9pwsh6qsaeughffgfrpxc5qhl5wda6ekss7pt3j72ps32wlrv7mlljjq9lq5jn8yn29gztqk8yz8ehq7smprjs3gzsllqpzaq07gk9yw50te0yarl5yd796xldapdfpc4n75ccy7z2jkmfyss8zz89gz7fw7937xjzt3k3cmhrekgjzzvxmqtzd20ffae8eym3lhq2gwejgqjmw9c8edum06s6a24q2zv9w3kcz5uavcgatlskxxy285nq5uhceusavwzu8rqu0e6lj6j5kgva00aw6x0khfyn4gg3ppeqqwmw3crvtgrdc8w45flwm4d9e8r3edct8hsgven70juqd9me3jk27cgr9fz90h57r2eqdctegc3ecqfh3kr9vx8u0tjhmfp4edpd9skke5yqw9u2ep8l5mwcuhdqcthr8u98wqsng6thy7wsfm2hut3musppwexfetnc7p46ckcjx2a3zvcxf8hht9yja03ymx5sy9wuhsmvsag676w6jg5yeqw3qzah5r38zpqxk8wzm0vk52ed4qtypnup37x2uj89lftvk4hfskd9wwumxvgawsm5xfum05p7vapjzpwu79k9hr7qf0wckhmkmmdxka8zz5qkaxrjsz3eny3x0vdsn5z9zcm8n2me44a56ej7893uduwv2l3ls7ua09dgqsvuddsgn4c9gn68hplup49rtfht0we6ruwn2sqe53z9sv4zq8924w7fku98fwpujykme2tumda4sqk54july0cd8gsvdx62t7gqjrfmrt6t7ezc8cvrxw99rq72h3qm3rr87fl47d5uw55pvdpfcj7weqqvks75erlpc73d08e2xsatay4vc2ey69wz8648wzqvagsyx4vs4lmnqqqqqqqqqqqwv0uty"; +const MINT_VERIFYING_KEY: string = + "verifier1qygqqqqqqqqqqqyjvyqqqqqqqqqfvcgqqqqqqqqqar9sqqqqqqqqq282qqqqqqqqqpjexqqqqqqqqqqvqqqqqqqqqqqtj8lxtlmlrtwaffkdwfctwu2ujt795jnnwj0jsgyc57n0lcsqkjy5u04k25gf9wzt45xwaxt9vzsqwj2ad42zuukvjklg8r8t5uxw6sslpsjdetjnx0vpqdwp7rddernk8kdr6av3cpx6e5cd7tu579ucr47qrhz003d2xpwc5rejfp60acvra8edv3qmkp29ukhzv0htt8gnnkfk6afgj2vg9ys9hjl4ww3gszkd2zl2yh8ms0tfxn546w8q20dmj5fcxuyj8vevl9r0pk3fm0y27g5r25z97gjyxrgfdperxh490qxjhcffdfxdc65hvtan5q76ghfpqg3mzzs350s0jlu5pc4k3252nq93dctnjscmy3rgxypswpvucjqxreh6x5ahwfpa7yrl24s5pq2naz6fz0u3v2zhqp3fcx0ycq9ptxtqscnf3g6u8llurjer85g5zyqpwfd599vphjna37cjehn88y36u6hl0tp9r206grcdrssggawcjfcf3vuscy90sks8s0klryzmdgxszagsqqxzly4j92q7psv9a36exzuz7ckkrslcxtnv77vq9hcurdcxq98xvdz0a8pnct7lcr9x8dprsxhdsq2j4t3805e3dztss26h8p8e6q3mzhv0549fw8dktm06tjayvnu5xag8hx9k3hsgvjknyaaawqzcj0wm5nzffuq3tr0pfh0arw5qgsc326sccyn7wz3h0nu7vcz9pu4k39syt248t7hexlapdvffpzq0we245z8l70qk8c5kmqkfwmedmf0dje4kxtc0qntwc9yxl09jhcshytl73ddhuem5lyx9ns6qvcgpspr50m76j4c4aa5wrgrqvm40jl00whc5yajujcqlwmlpdsskfw3se2ghy25pg66wm7z8zxatz25sp6e63w6qr378h0ul7v5udurmc26fkkeqynypurffzz50lmcmqv67vjpqqqqqqqqqqa59tnr"; +const PROGRAM: string = + "import puzzle_arcade_coin_v002.aleo;\nimport puzzle_arcade_ticket_v002.aleo;\n\nprogram puzzle_spinner_v002.aleo;\n\nstruct Result:\n nonce as field;\n tickets as u64;\n\nmapping used_nonces:\n key as field.public;\n value as boolean.public;\n\nfunction spin:\n input r0 as puzzle_arcade_coin_v002.aleo/PuzzleArcadeCoin.record;\n input r1 as Result.public;\n input r2 as signature.private;\n sign.verify r2 aleo196a39wq9q8ea779cmlmff0c9pj2gl4f5e8fhjpvmufe5utuq7y8snz4h2l r1 into r3;\n assert.eq r3 true ;\n is.eq r1.tickets 1000000u64 into r4;\n is.eq r1.tickets 2000000u64 into r5;\n or r4 r5 into r6;\n is.eq r1.tickets 5000000u64 into r7;\n or r6 r7 into r8;\n is.eq r1.tickets 10000000u64 into r9;\n or r8 r9 into r10;\n assert.eq r10 true ;\n call puzzle_arcade_coin_v002.aleo/spend r0 1000000u64 into r11;\n call puzzle_arcade_ticket_v002.aleo/mint r0.owner r1.tickets into r12 r13;\n async spin r13 r1.nonce into r14;\n output r11 as puzzle_arcade_coin_v002.aleo/PuzzleArcadeCoin.record;\n output r12 as puzzle_arcade_ticket_v002.aleo/PuzzleArcadeTicket.record;\n output r14 as puzzle_spinner_v002.aleo/spin.future;\n\nfinalize spin:\n input r0 as puzzle_arcade_ticket_v002.aleo/mint.future;\n input r1 as field.public;\n get.or_use used_nonces[r1] false into r2;\n assert.eq r2 false ;\n set true into used_nonces[r1];\n await r0;\n"; +const IMPORT_1: string = + "program puzzle_arcade_coin_v002.aleo;\n\nrecord PuzzleArcadeCoin:\n owner as address.private;\n amount as u64.private;\n\nfunction mint:\n input r0 as address.public;\n input r1 as u64.public;\n assert.eq self.caller self.signer ;\n assert.eq self.caller aleo196a39wq9q8ea779cmlmff0c9pj2gl4f5e8fhjpvmufe5utuq7y8snz4h2l ;\n cast r0 r1 into r2 as PuzzleArcadeCoin.record;\n output r2 as PuzzleArcadeCoin.record;\n\nfunction spend:\n input r0 as PuzzleArcadeCoin.record;\n input r1 as u64.public;\n gte r0.amount r1 into r2;\n assert.eq r2 true ;\n sub r0.amount r1 into r3;\n cast r0.owner r3 into r4 as PuzzleArcadeCoin.record;\n output r4 as PuzzleArcadeCoin.record;\n"; +const IMPORT_2: string = + "program puzzle_arcade_ticket_v002.aleo;\n\nrecord PuzzleArcadeTicket:\n owner as address.private;\n amount as u64.private;\n\nmapping registry:\n key as address.public;\n value as boolean.public;\n\nfunction add_program_to_registry:\n input r0 as address.private;\n assert.eq self.caller self.signer ;\n assert.eq self.caller aleo196a39wq9q8ea779cmlmff0c9pj2gl4f5e8fhjpvmufe5utuq7y8snz4h2l ;\n async add_program_to_registry r0 into r1;\n output r1 as puzzle_arcade_ticket_v002.aleo/add_program_to_registry.future;\n\nfinalize add_program_to_registry:\n input r0 as address.public;\n set true into registry[r0];\n\nfunction mint:\n input r0 as address.public;\n input r1 as u64.public;\n cast r0 r1 into r2 as PuzzleArcadeTicket.record;\n async mint self.caller into r3;\n output r2 as PuzzleArcadeTicket.record;\n output r3 as puzzle_arcade_ticket_v002.aleo/mint.future;\n\nfinalize mint:\n input r0 as address.public;\n get.or_use registry[r0] false into r1;\n assert.eq r1 true ;\n\nfunction spend:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as u64.public;\n gte r0.amount r1 into r2;\n assert.eq r2 true ;\n sub r0.amount r1 into r3;\n cast r0.owner r3 into r4 as PuzzleArcadeTicket.record;\n output r4 as PuzzleArcadeTicket.record;\n\nfunction join:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r2;\n assert.eq r2 true ;\n gt r1.amount 0u64 into r3;\n assert.eq r3 true ;\n add r0.amount r1.amount into r4;\n cast self.signer r4 into r5 as PuzzleArcadeTicket.record;\n output r5 as PuzzleArcadeTicket.record;\n\nfunction join3:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n input r2 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r3;\n assert.eq r3 true ;\n gt r1.amount 0u64 into r4;\n assert.eq r4 true ;\n gt r2.amount 0u64 into r5;\n assert.eq r5 true ;\n add r0.amount r1.amount into r6;\n add r6 r2.amount into r7;\n cast self.signer r7 into r8 as PuzzleArcadeTicket.record;\n output r8 as PuzzleArcadeTicket.record;\n\nfunction join4:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n input r2 as PuzzleArcadeTicket.record;\n input r3 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r4;\n assert.eq r4 true ;\n gt r1.amount 0u64 into r5;\n assert.eq r5 true ;\n gt r2.amount 0u64 into r6;\n assert.eq r6 true ;\n gt r3.amount 0u64 into r7;\n assert.eq r7 true ;\n add r0.amount r1.amount into r8;\n add r8 r2.amount into r9;\n add r9 r3.amount into r10;\n cast self.signer r10 into r11 as PuzzleArcadeTicket.record;\n output r11 as PuzzleArcadeTicket.record;\n\nfunction join5:\n input r0 as PuzzleArcadeTicket.record;\n input r1 as PuzzleArcadeTicket.record;\n input r2 as PuzzleArcadeTicket.record;\n input r3 as PuzzleArcadeTicket.record;\n input r4 as PuzzleArcadeTicket.record;\n gt r0.amount 0u64 into r5;\n assert.eq r5 true ;\n gt r1.amount 0u64 into r6;\n assert.eq r6 true ;\n gt r2.amount 0u64 into r7;\n assert.eq r7 true ;\n gt r3.amount 0u64 into r8;\n assert.eq r8 true ;\n gt r4.amount 0u64 into r9;\n assert.eq r9 true ;\n add r0.amount r1.amount into r10;\n add r10 r2.amount into r11;\n add r11 r3.amount into r12;\n add r12 r4.amount into r13;\n cast self.signer r13 into r14 as PuzzleArcadeTicket.record;\n output r14 as PuzzleArcadeTicket.record;\n"; -export { IMPORT_1, IMPORT_2, MINT_VERIFYING_KEY, PROGRAM, SPEND_VERIFYING_KEY, SPIN_VERIFYING_KEY }; \ No newline at end of file +export { + IMPORT_1, + IMPORT_2, + MINT_VERIFYING_KEY, + PROGRAM, + SPEND_VERIFYING_KEY, + SPIN_VERIFYING_KEY, +}; diff --git a/sdk/tests/data/proving.ts b/sdk/tests/data/proving.ts index 5655208b2..f1143551c 100644 --- a/sdk/tests/data/proving.ts +++ b/sdk/tests/data/proving.ts @@ -6,9 +6,15 @@ amount: 1000000u64.private, _nonce: 3552842606932684888288059180163265370185468417773274452409126263862773749561group.public }`; -const PUZZLE_SPINNER_V002_INPUT_1 = "{nonce: 1170758118field, tickets: 5000000u64}"; +const PUZZLE_SPINNER_V002_INPUT_1 = + "{nonce: 1170758118field, tickets: 5000000u64}"; -const PUZZLE_SPINNER_V002_INPUT_2 = "sign1qkveh904rhh9q72mvg7r9p20q54w73w55m375dplgqmg4dj07cqgfu6nmyennfeyvczvdlsztndg3vstm5wrdx8gwl7ucjp8mmlwsqxyx67umlz8tz8pw8zk599sj05tsqczr4ufz06e4jl0lve0k6p3pvyztyddnpcpvq3p66k3ryatluay3cndws6fktfvnytg3hcswahqjdc7n92"; +const PUZZLE_SPINNER_V002_INPUT_2 = + "sign1qkveh904rhh9q72mvg7r9p20q54w73w55m375dplgqmg4dj07cqgfu6nmyennfeyvczvdlsztndg3vstm5wrdx8gwl7ucjp8mmlwsqxyx67umlz8tz8pw8zk599sj05tsqczr4ufz06e4jl0lve0k6p3pvyztyddnpcpvq3p66k3ryatluay3cndws6fktfvnytg3hcswahqjdc7n92"; -export { PUZZLE_SPINNER_PROGRAM_ID, PUZZLE_SPINNER_V002_INPUT_0, PUZZLE_SPINNER_V002_INPUT_1, - PUZZLE_SPINNER_V002_INPUT_2} \ No newline at end of file +export { + PUZZLE_SPINNER_PROGRAM_ID, + PUZZLE_SPINNER_V002_INPUT_0, + PUZZLE_SPINNER_V002_INPUT_1, + PUZZLE_SPINNER_V002_INPUT_2, +}; diff --git a/sdk/tests/data/records.ts b/sdk/tests/data/records.ts index 648c0a6ba..f8b22a19a 100644 --- a/sdk/tests/data/records.ts +++ b/sdk/tests/data/records.ts @@ -1,20 +1,28 @@ /// V1 credits.aleo record. -const CREDITS_RECORD_V1 = "{ owner: aleo12a4wll9ax6w5355jph0dr5wt2vla5sss2t4cnch0tc3vzh643v8qcfvc7a.private, microcredits: 1000000u64.private, _nonce: 3634848344765318974603121890869676775499130077229666060613233255327643175219group.public, _version: 1u8.public }"; +const CREDITS_RECORD_V1 = + "{ owner: aleo12a4wll9ax6w5355jph0dr5wt2vla5sss2t4cnch0tc3vzh643v8qcfvc7a.private, microcredits: 1000000u64.private, _nonce: 3634848344765318974603121890869676775499130077229666060613233255327643175219group.public, _version: 1u8.public }"; /// Record view key for the V1 credits.aleo record. -const CREDITS_RECORD_VIEW_KEY = "5237002936265850807349726649400053591020997883662246784632368923777787639801field"; +const CREDITS_RECORD_VIEW_KEY = + "5237002936265850807349726649400053591020997883662246784632368923777787639801field"; /// Sender ciphertext of the credits.aleo record. -const CREDITS_SENDER_CIPHERTEXT = "1182590395568997043375432557467567048762179115999922880321493200728848194550field"; +const CREDITS_SENDER_CIPHERTEXT = + "1182590395568997043375432557467567048762179115999922880321493200728848194550field"; /// Sender plaintext of the credits.aleo record. -const CREDITS_SENDER_PLAINTEXT = "aleo1j92w9mhqznj2hvufad796y8suykjppk7f6n6xmncmktfm95vggzqx4sjlh"; +const CREDITS_SENDER_PLAINTEXT = + "aleo1j92w9mhqznj2hvufad796y8suykjppk7f6n6xmncmktfm95vggzqx4sjlh"; // Ciphertext records -const RECORD_CIPHERTEXT_STRING = "record1qyqsqpe2szk2wwwq56akkwx586hkndl3r8vzdwve32lm7elvphh37rsyqyxx66trwfhkxun9v35hguerqqpqzqrtjzeu6vah9x2me2exkgege824sd8x2379scspmrmtvczs0d93qttl7y92ga0k0rsexu409hu3vlehe3yxjhmey3frh2z5pxm5cmxsv4un97q"; -const RECORD_CIPHERTEXT_STRING_COPY = "record1qyqsqpe2szk2wwwq56akkwx586hkndl3r8vzdwve32lm7elvphh37rsyqyxx66trwfhkxun9v35hguerqqpqzqrtjzeu6vah9x2me2exkgege824sd8x2379scspmrmtvczs0d93qttl7y92ga0k0rsexu409hu3vlehe3yxjhmey3frh2z5pxm5cmxsv4un97q"; -const RECORD_CIPHERTEXT_STRING_NOT_OWNED = "RECORD1QVQSQ5H8YT5682E73ZT7PYNJGPL29MWTSETRVS9VHCKFHJRNX9RX94CFQYXX66TRWFHKXUN9V35HGUERQQPQZQZ6KMY7S5HPKKF02L6R46QM8RQCW9X0K4RQ6GT234AMJ2UG3LMTQT5NY4UG8SXJY3U8D05K4Q3E9F54VX67ZMD3G6JYQQ7KXRWS0R0SWM6P833"; -const RECORD_CIPHERTEXT_STRING_NOT_OWNED2 = "RECORD1QVQSP37HJE4CEU8EFZE8XMAHE5TDTXCZ0K534WQPKVN6C9R629X3C4Q8QYRXZMT0W4H8GGCQQGQSPVUJYCN0K7HYFHENXA40HXTFSX68092WMVJ4E3XSEXR2DY0FMCCXT0DS42W5MAASZFJV930QVQRKATQJ900AKU4K777UMH2K54ZHLUGQC2AFJD"; +const RECORD_CIPHERTEXT_STRING = + "record1qyqsqpe2szk2wwwq56akkwx586hkndl3r8vzdwve32lm7elvphh37rsyqyxx66trwfhkxun9v35hguerqqpqzqrtjzeu6vah9x2me2exkgege824sd8x2379scspmrmtvczs0d93qttl7y92ga0k0rsexu409hu3vlehe3yxjhmey3frh2z5pxm5cmxsv4un97q"; +const RECORD_CIPHERTEXT_STRING_COPY = + "record1qyqsqpe2szk2wwwq56akkwx586hkndl3r8vzdwve32lm7elvphh37rsyqyxx66trwfhkxun9v35hguerqqpqzqrtjzeu6vah9x2me2exkgege824sd8x2379scspmrmtvczs0d93qttl7y92ga0k0rsexu409hu3vlehe3yxjhmey3frh2z5pxm5cmxsv4un97q"; +const RECORD_CIPHERTEXT_STRING_NOT_OWNED = + "RECORD1QVQSQ5H8YT5682E73ZT7PYNJGPL29MWTSETRVS9VHCKFHJRNX9RX94CFQYXX66TRWFHKXUN9V35HGUERQQPQZQZ6KMY7S5HPKKF02L6R46QM8RQCW9X0K4RQ6GT234AMJ2UG3LMTQT5NY4UG8SXJY3U8D05K4Q3E9F54VX67ZMD3G6JYQQ7KXRWS0R0SWM6P833"; +const RECORD_CIPHERTEXT_STRING_NOT_OWNED2 = + "RECORD1QVQSP37HJE4CEU8EFZE8XMAHE5TDTXCZ0K534WQPKVN6C9R629X3C4Q8QYRXZMT0W4H8GGCQQGQSPVUJYCN0K7HYFHENXA40HXTFSX68092WMVJ4E3XSEXR2DY0FMCCXT0DS42W5MAASZFJV930QVQRKATQJ900AKU4K777UMH2K54ZHLUGQC2AFJD"; // Plaintext record const RECORD_PLAINTEXT_V0_STRING = `{ @@ -23,202 +31,250 @@ const RECORD_PLAINTEXT_V0_STRING = `{ _nonce: 3077450429259593211617823051143573281856129402760267155982965992208217472983group.public }`; - -const RECORD_PLAINTEXT_V1_STRING = "{\n owner: aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3.private,\n microcredits: 50000u64.private,\n _nonce: 8327477210335641151082470829879168522735279120730137538049818239556464339772group.public,\n _version: 1u8.public\n}"; +const RECORD_PLAINTEXT_V1_STRING = + "{\n owner: aleo1vskzxa2qqgnhznxsqh6tgq93c30sfkj6xqwe7sr85lgjkexjlcxs3lxhy3.private,\n microcredits: 50000u64.private,\n _nonce: 8327477210335641151082470829879168522735279120730137538049818239556464339772group.public,\n _version: 1u8.public\n}"; // View key and record view key const VIEW_KEY_STRING = "AViewKey1ccEt8A2Ryva5rxnKcAbn7wgTaTsb79tzkKHFpeKsm9NX"; -const RECORD_VIEW_KEY_STRING = "4445718830394614891114647247073357094867447866913203502139893824059966201724field"; +const RECORD_VIEW_KEY_STRING = + "4445718830394614891114647247073357094867447866913203502139893824059966201724field"; const ENCRYPTED_RECORDS = [ - { - "commitment": "6965909872581584914039191373421463306059339471297771776610572511362422620805field", - "checksum": "2207226491095267609257407999738609780640985730602113917526120008525943934340field", - "block_height": 10002012, - "program_name": "credits.aleo", - "function_name": "transfer_public_to_private", - "output_index": 0, - "owner": "ciphertext1qyqzlxhmhtgtjaee6new2hsfls2ygj68va5h009u3h5htdp59gqasqcwfsafx", - "record_ciphertext": "record1qvqsqtu6lwadpwth88209e27p87pg3ztgankjaauhjx7jad5xs4qrkqrqyxx66trwfhkxun9v35hguerqqpqzqxkezwzg7z8lvpj4t2as605kn9ge8k9cvfls3mg0sqult798mfwqcskzfqlyn83lu3e9kexqmklf5ad9d4shvz5ntuenzrdujqpq0esqdddm55", - "record_name": "credits", - "record_nonce": "429364576614161221583920327616773968748250238602322587322300571348491329825group", - "transaction_id": "at1rdlnll8kljpuy506f0tx0r0xd2fck60hw7phkmjnptke77xukg8sekd6c9 ", - "transition_id": "au1f505f9uvu39f2mys9g0gl2qe5t3pr8tsdutzj4pd2apd5l6hzuqqv0t2n3 ", - "transaction_index": 0, - "transition_index": 0 - }, - { - "commitment": "1188397772964800575271786433438577861359270789629265569589281492544120074436field", - "checksum": "5605422683473081183936132265672104254159529769293480273428181950973232566109field", - "block_height": 10003899, - "program_name": "token_registry.aleo", - "function_name": "transfer_public_to_private", - "output_index": 0, - "owner": "ciphertext1qyqvlfsfleandncmy35mrzr2fwt0epgmnmj34dr6nq7zlndg2v3murskqakke", - "record_ciphertext": "record1qvqspnaxp8l8kdk0rvjxnvvgdf9edly9rw0w2x4502vrct7d4pfj80swqsrxzmt0w4h8ggcqqgqsprxt748jj4tl4lm799ys2qjnzz5s6zjrrq6r3ftpn26x4nscncqwpp6x76m9de0kjezrqqpqyq9x2mvm0uzcmt8unclhhze2aa0w2exzx6dwaqrzm879ya65gdytp55qf2ysvks4435vnzkmgwszppeh5vyud97kurp50zn95h7pakj3y8m90p6x2unwv9k97ct4w35x7unf0fshg6t0de0hyet3w45hyetyyvqqyqgqp7tq23f3nz56q6ptwrjyr3xf657pshhrmhvm8yqpuhkfjxjp85gpqct4w35x7unf0fjkghm4de6xjmprqqpqzqyp9aa48avx62cfnce36trczs0fj88ydw8cxg38pv69t983an7gqgaqvv25079m7w7gayzq25ddxadmnnxh5jttm4ra6jrn00vqn7cq7a36w4h", - "record_name": "Token", - "record_nonce": "7096758660619171646018003922303056044411325151108024435925109326208495060538group", - "transaction_id": "at1khgxe7rt7tlcv6kyrytlt57rdu4t67euelyy4fzrysa5spplms8qmyzm0g ", - "transition_id": "au1jxjw3vxpnjl7vrs3ckhkta0yxwq87wxeh7pwcs9q4nfw3knzlgzs95m32s ", - "transaction_index": 0, - "transition_index": 0 - } -] + { + commitment: + "6965909872581584914039191373421463306059339471297771776610572511362422620805field", + checksum: + "2207226491095267609257407999738609780640985730602113917526120008525943934340field", + block_height: 10002012, + program_name: "credits.aleo", + function_name: "transfer_public_to_private", + output_index: 0, + owner: "ciphertext1qyqzlxhmhtgtjaee6new2hsfls2ygj68va5h009u3h5htdp59gqasqcwfsafx", + record_ciphertext: + "record1qvqsqtu6lwadpwth88209e27p87pg3ztgankjaauhjx7jad5xs4qrkqrqyxx66trwfhkxun9v35hguerqqpqzqxkezwzg7z8lvpj4t2as605kn9ge8k9cvfls3mg0sqult798mfwqcskzfqlyn83lu3e9kexqmklf5ad9d4shvz5ntuenzrdujqpq0esqdddm55", + record_name: "credits", + record_nonce: + "429364576614161221583920327616773968748250238602322587322300571348491329825group", + transaction_id: + "at1rdlnll8kljpuy506f0tx0r0xd2fck60hw7phkmjnptke77xukg8sekd6c9 ", + transition_id: + "au1f505f9uvu39f2mys9g0gl2qe5t3pr8tsdutzj4pd2apd5l6hzuqqv0t2n3 ", + transaction_index: 0, + transition_index: 0, + }, + { + commitment: + "1188397772964800575271786433438577861359270789629265569589281492544120074436field", + checksum: + "5605422683473081183936132265672104254159529769293480273428181950973232566109field", + block_height: 10003899, + program_name: "token_registry.aleo", + function_name: "transfer_public_to_private", + output_index: 0, + owner: "ciphertext1qyqvlfsfleandncmy35mrzr2fwt0epgmnmj34dr6nq7zlndg2v3murskqakke", + record_ciphertext: + "record1qvqspnaxp8l8kdk0rvjxnvvgdf9edly9rw0w2x4502vrct7d4pfj80swqsrxzmt0w4h8ggcqqgqsprxt748jj4tl4lm799ys2qjnzz5s6zjrrq6r3ftpn26x4nscncqwpp6x76m9de0kjezrqqpqyq9x2mvm0uzcmt8unclhhze2aa0w2exzx6dwaqrzm879ya65gdytp55qf2ysvks4435vnzkmgwszppeh5vyud97kurp50zn95h7pakj3y8m90p6x2unwv9k97ct4w35x7unf0fshg6t0de0hyet3w45hyetyyvqqyqgqp7tq23f3nz56q6ptwrjyr3xf657pshhrmhvm8yqpuhkfjxjp85gpqct4w35x7unf0fjkghm4de6xjmprqqpqzqyp9aa48avx62cfnce36trczs0fj88ydw8cxg38pv69t983an7gqgaqvv25079m7w7gayzq25ddxadmnnxh5jttm4ra6jrn00vqn7cq7a36w4h", + record_name: "Token", + record_nonce: + "7096758660619171646018003922303056044411325151108024435925109326208495060538group", + transaction_id: + "at1khgxe7rt7tlcv6kyrytlt57rdu4t67euelyy4fzrysa5spplms8qmyzm0g ", + transition_id: + "au1jxjw3vxpnjl7vrs3ckhkta0yxwq87wxeh7pwcs9q4nfw3knzlgzs95m32s ", + transaction_index: 0, + transition_index: 0, + }, +]; const OWNED_RECORDS = [ - { - block_height: 6090931, - commitment: '3190431573263899914391947973857035974322781794111836590387626738139790056909field', - function_name: 'place_bid', - output_index: 0, - owner: '7447609208500997115362207913251419702258326233440670198345216549215225004914field', - program_name: 'private_auction.aleo', - record_ciphertext: 'record1qyqspn9v8rwmmtwfynxl7z98sr3hgevduqk7wl4ufmp8yxanp9x5lhgdqsrxy6tyv3jhyscqqgpqp2k5sw5f37f86hge97ls2avx94pvg7fnaxr7uqwfwkqaljq8jlgyg480mg8fy67fkhqe6ysjxdr47whlt09gwuwmlme57nxvz4kn5qyqy6tygvqqyqsqaljsdk8g0g7f9t4s8z2utywzg6qwpgrl57k8fzvcz5qrthzy5qrk422ak9e9pwylg9k7fh99qa05qu4qj9k5mn7hjf5586fmlrk6qpqxv9kk7atwws3sqqspqryrtmup2kn26693c4f7e79gc0yfdammkxexk84vt0z5wms8dgnq2ztfwd0hw6twdejhygcqqgqsppdjyztk35ekkzq60jx7gg498tccxc9l0ysmk6hal0yglk436ng8pq5wrq99rlhqqglf0gvv70lfavzfu6mgf4sgzmuwej2a2f4umgzssk0fhc', - record_plaintext: '{\n' + - ' owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n' + - ' bidder: aleo1jqnajd8g6ezqjq0eefm4zeqynwx6vzed8flnkmejw0afy09dusrszzk46k.private,\n' + - ' id: 127430097643352927347108200field.private,\n' + - ' amount: 2u64.private,\n' + - ' is_winner: false.private,\n' + - ' _nonce: 2648035478322331583038604395085314793293985787056490619358776499723646871560group.public,\n' + - ' _version: 0u8.public\n' + - '}', - record_name: 'Bid', - spent: false, - tag: '5941252181432651644402279701137165256963073258332916685063623109173576520831field', - transaction_id: 'at1unmfl5gv6d28c9dt405kxxj8nl7ytl5eq0ue9ctpc7lpmds8qgqqqglh2z ', - transition_id: 'au1rc32lk3umuye2ccpqkx8gw5m2cf92a29wzyfj5r7tg9jtnd2duxq6gtuss ', - transaction_index: 2, - transition_index: 0 - }, - { - block_height: 10139458, - commitment: '1402624598475573407524150006714553637670194594508553840106512461758960165497field', - function_name: 'transfer_public_to_private', - output_index: 0, - owner: '7447609208500997115362207913251419702258326233440670198345216549215225004914field', - program_name: 'credits.aleo', - record_ciphertext: 'record1qvqsq9pcyh8s0zlqwydq0uef42kanl88hct6c7f0y7m3gawxv8er20g2qyxx66trwfhkxun9v35hguerqqpqzqzue2ynz2mfnucdf9gft6cuqussdtge8efqwnlkqvq5nr52nhq9pmv47u20cmsk6mnsl70kxhengndepngqja8s5myu0778nncrttls576mt26', - record_plaintext: '{\n' + - ' owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n' + - ' microcredits: 1000000u64.private,\n' + - ' _nonce: 4974295747251926833599773262811444029929021845727007977270637854284605120473group.public,\n' + - ' _version: 1u8.public\n' + - '}', - record_name: 'credits', - spent: false, - tag: '2965517500209150226508265073635793457193572667031485750956287906078711930968field', - transaction_id: 'at10g988ruzr70rv0jrnx67ay0f62gzn0vsmlpgsk9seqzd4k406sys8q9xhk ', - transition_id: 'au1wr7q6y6fw2gyrwem2zjm8kp2lvl04eay8hqmaz6l2gd4p60ddy9s2lj2qr ', - transaction_index: 0, - transition_index: 0 - }, - { - block_height: 10140546, - commitment: '6211743159355787867034474916630197652498323872351981366700814088987411118433field', - function_name: 'transfer_public_to_private', - output_index: 0, - owner: '7447609208500997115362207913251419702258326233440670198345216549215225004914field', - program_name: 'credits.aleo', - record_ciphertext: 'record1qvqspk7dttx96hemjmnzkf2vf7exfxt6uu04xxxhr987nycq5xx4lkc3qyxx66trwfhkxun9v35hguerqqpqzqqjw24ydxw5t5jgjhszz6dmtgwcw2r0ckgkg3kkxkh8y8cy0gump9an579cxeg5rxkawwdvh2wmesusl8tpwldpe60wkmgzdgd97pns2akmwge', - record_plaintext: '{\n' + - ' owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n' + - ' microcredits: 2000000u64.private,\n' + - ' _nonce: 2445210375074181986314301912374182923270809668425048232267618676093400660603group.public,\n' + - ' _version: 1u8.public\n' + - '}', - record_name: 'credits', - spent: false, - tag: '8421937347379608036510120951995833971195343843566214313082589116311107280540field', - transaction_id: 'at1hy45m9w56cr8f5yzqp675heh3rde9996exy9875ylzk2w2nmt5qsfudud5 ', - transition_id: 'au1yp2x7t378w3d74kkkz9vcq7ckeg4h4amc3ltysmylz7a85yuy5pqefcjmh ', - transaction_index: 0, - transition_index: 0 - } -] + { + block_height: 6090931, + commitment: + "3190431573263899914391947973857035974322781794111836590387626738139790056909field", + function_name: "place_bid", + output_index: 0, + owner: "7447609208500997115362207913251419702258326233440670198345216549215225004914field", + program_name: "private_auction.aleo", + record_ciphertext: + "record1qyqspn9v8rwmmtwfynxl7z98sr3hgevduqk7wl4ufmp8yxanp9x5lhgdqsrxy6tyv3jhyscqqgpqp2k5sw5f37f86hge97ls2avx94pvg7fnaxr7uqwfwkqaljq8jlgyg480mg8fy67fkhqe6ysjxdr47whlt09gwuwmlme57nxvz4kn5qyqy6tygvqqyqsqaljsdk8g0g7f9t4s8z2utywzg6qwpgrl57k8fzvcz5qrthzy5qrk422ak9e9pwylg9k7fh99qa05qu4qj9k5mn7hjf5586fmlrk6qpqxv9kk7atwws3sqqspqryrtmup2kn26693c4f7e79gc0yfdammkxexk84vt0z5wms8dgnq2ztfwd0hw6twdejhygcqqgqsppdjyztk35ekkzq60jx7gg498tccxc9l0ysmk6hal0yglk436ng8pq5wrq99rlhqqglf0gvv70lfavzfu6mgf4sgzmuwej2a2f4umgzssk0fhc", + record_plaintext: + "{\n" + + " owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n" + + " bidder: aleo1jqnajd8g6ezqjq0eefm4zeqynwx6vzed8flnkmejw0afy09dusrszzk46k.private,\n" + + " id: 127430097643352927347108200field.private,\n" + + " amount: 2u64.private,\n" + + " is_winner: false.private,\n" + + " _nonce: 2648035478322331583038604395085314793293985787056490619358776499723646871560group.public,\n" + + " _version: 0u8.public\n" + + "}", + record_name: "Bid", + spent: false, + tag: "5941252181432651644402279701137165256963073258332916685063623109173576520831field", + transaction_id: + "at1unmfl5gv6d28c9dt405kxxj8nl7ytl5eq0ue9ctpc7lpmds8qgqqqglh2z ", + transition_id: + "au1rc32lk3umuye2ccpqkx8gw5m2cf92a29wzyfj5r7tg9jtnd2duxq6gtuss ", + transaction_index: 2, + transition_index: 0, + }, + { + block_height: 10139458, + commitment: + "1402624598475573407524150006714553637670194594508553840106512461758960165497field", + function_name: "transfer_public_to_private", + output_index: 0, + owner: "7447609208500997115362207913251419702258326233440670198345216549215225004914field", + program_name: "credits.aleo", + record_ciphertext: + "record1qvqsq9pcyh8s0zlqwydq0uef42kanl88hct6c7f0y7m3gawxv8er20g2qyxx66trwfhkxun9v35hguerqqpqzqzue2ynz2mfnucdf9gft6cuqussdtge8efqwnlkqvq5nr52nhq9pmv47u20cmsk6mnsl70kxhengndepngqja8s5myu0778nncrttls576mt26", + record_plaintext: + "{\n" + + " owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n" + + " microcredits: 1000000u64.private,\n" + + " _nonce: 4974295747251926833599773262811444029929021845727007977270637854284605120473group.public,\n" + + " _version: 1u8.public\n" + + "}", + record_name: "credits", + spent: false, + tag: "2965517500209150226508265073635793457193572667031485750956287906078711930968field", + transaction_id: + "at10g988ruzr70rv0jrnx67ay0f62gzn0vsmlpgsk9seqzd4k406sys8q9xhk ", + transition_id: + "au1wr7q6y6fw2gyrwem2zjm8kp2lvl04eay8hqmaz6l2gd4p60ddy9s2lj2qr ", + transaction_index: 0, + transition_index: 0, + }, + { + block_height: 10140546, + commitment: + "6211743159355787867034474916630197652498323872351981366700814088987411118433field", + function_name: "transfer_public_to_private", + output_index: 0, + owner: "7447609208500997115362207913251419702258326233440670198345216549215225004914field", + program_name: "credits.aleo", + record_ciphertext: + "record1qvqspk7dttx96hemjmnzkf2vf7exfxt6uu04xxxhr987nycq5xx4lkc3qyxx66trwfhkxun9v35hguerqqpqzqqjw24ydxw5t5jgjhszz6dmtgwcw2r0ckgkg3kkxkh8y8cy0gump9an579cxeg5rxkawwdvh2wmesusl8tpwldpe60wkmgzdgd97pns2akmwge", + record_plaintext: + "{\n" + + " owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n" + + " microcredits: 2000000u64.private,\n" + + " _nonce: 2445210375074181986314301912374182923270809668425048232267618676093400660603group.public,\n" + + " _version: 1u8.public\n" + + "}", + record_name: "credits", + spent: false, + tag: "8421937347379608036510120951995833971195343843566214313082589116311107280540field", + transaction_id: + "at1hy45m9w56cr8f5yzqp675heh3rde9996exy9875ylzk2w2nmt5qsfudud5 ", + transition_id: + "au1yp2x7t378w3d74kkkz9vcq7ckeg4h4amc3ltysmylz7a85yuy5pqefcjmh ", + transaction_index: 0, + transition_index: 0, + }, +]; const OWNED_CREDITS_RECORDS = [ - { - block_height: 6090931, - commitment: '3190431573263899914391947973857035974322781794111836590387626738139790056909field', - function_name: 'place_bid', - output_index: 0, - owner: '7447609208500997115362207913251419702258326233440670198345216549215225004914field', - program_name: 'private_auction.aleo', - record_ciphertext: 'record1qyqspn9v8rwmmtwfynxl7z98sr3hgevduqk7wl4ufmp8yxanp9x5lhgdqsrxy6tyv3jhyscqqgpqp2k5sw5f37f86hge97ls2avx94pvg7fnaxr7uqwfwkqaljq8jlgyg480mg8fy67fkhqe6ysjxdr47whlt09gwuwmlme57nxvz4kn5qyqy6tygvqqyqsqaljsdk8g0g7f9t4s8z2utywzg6qwpgrl57k8fzvcz5qrthzy5qrk422ak9e9pwylg9k7fh99qa05qu4qj9k5mn7hjf5586fmlrk6qpqxv9kk7atwws3sqqspqryrtmup2kn26693c4f7e79gc0yfdammkxexk84vt0z5wms8dgnq2ztfwd0hw6twdejhygcqqgqsppdjyztk35ekkzq60jx7gg498tccxc9l0ysmk6hal0yglk436ng8pq5wrq99rlhqqglf0gvv70lfavzfu6mgf4sgzmuwej2a2f4umgzssk0fhc', - record_plaintext: '{\n' + - ' owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n' + - ' bidder: aleo1jqnajd8g6ezqjq0eefm4zeqynwx6vzed8flnkmejw0afy09dusrszzk46k.private,\n' + - ' id: 127430097643352927347108200field.private,\n' + - ' amount: 2u64.private,\n' + - ' is_winner: false.private,\n' + - ' _nonce: 2648035478322331583038604395085314793293985787056490619358776499723646871560group.public,\n' + - ' _version: 0u8.public\n' + - '}', - record_name: 'Bid', - spent: false, - tag: '5941252181432651644402279701137165256963073258332916685063623109173576520831field', - transaction_id: 'at1unmfl5gv6d28c9dt405kxxj8nl7ytl5eq0ue9ctpc7lpmds8qgqqqglh2z ', - transition_id: 'au1rc32lk3umuye2ccpqkx8gw5m2cf92a29wzyfj5r7tg9jtnd2duxq6gtuss ', - transaction_index: 2, - transition_index: 0 - }, - { - block_height: 10139458, - commitment: '1402624598475573407524150006714553637670194594508553840106512461758960165497field', - function_name: 'transfer_public_to_private', - output_index: 0, - owner: '7447609208500997115362207913251419702258326233440670198345216549215225004914field', - program_name: 'credits.aleo', - record_ciphertext: 'record1qvqsq9pcyh8s0zlqwydq0uef42kanl88hct6c7f0y7m3gawxv8er20g2qyxx66trwfhkxun9v35hguerqqpqzqzue2ynz2mfnucdf9gft6cuqussdtge8efqwnlkqvq5nr52nhq9pmv47u20cmsk6mnsl70kxhengndepngqja8s5myu0778nncrttls576mt26', - record_plaintext: '{\n' + - ' owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n' + - ' microcredits: 1000000u64.private,\n' + - ' _nonce: 4974295747251926833599773262811444029929021845727007977270637854284605120473group.public,\n' + - ' _version: 1u8.public\n' + - '}', - record_name: 'credits', - spent: false, - tag: '2965517500209150226508265073635793457193572667031485750956287906078711930968field', - transaction_id: 'at10g988ruzr70rv0jrnx67ay0f62gzn0vsmlpgsk9seqzd4k406sys8q9xhk ', - transition_id: 'au1wr7q6y6fw2gyrwem2zjm8kp2lvl04eay8hqmaz6l2gd4p60ddy9s2lj2qr ', - transaction_index: 0, - transition_index: 0 - }, - { - block_height: 10140546, - commitment: '6211743159355787867034474916630197652498323872351981366700814088987411118433field', - function_name: 'transfer_public_to_private', - output_index: 0, - owner: '7447609208500997115362207913251419702258326233440670198345216549215225004914field', - program_name: 'credits.aleo', - record_ciphertext: 'record1qvqspk7dttx96hemjmnzkf2vf7exfxt6uu04xxxhr987nycq5xx4lkc3qyxx66trwfhkxun9v35hguerqqpqzqqjw24ydxw5t5jgjhszz6dmtgwcw2r0ckgkg3kkxkh8y8cy0gump9an579cxeg5rxkawwdvh2wmesusl8tpwldpe60wkmgzdgd97pns2akmwge', - record_plaintext: '{\n' + - ' owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n' + - ' microcredits: 2000000u64.private,\n' + - ' _nonce: 2445210375074181986314301912374182923270809668425048232267618676093400660603group.public,\n' + - ' _version: 1u8.public\n' + - '}', - record_name: 'credits', - spent: false, - tag: '8421937347379608036510120951995833971195343843566214313082589116311107280540field', - transaction_id: 'at1hy45m9w56cr8f5yzqp675heh3rde9996exy9875ylzk2w2nmt5qsfudud5 ', - transition_id: 'au1yp2x7t378w3d74kkkz9vcq7ckeg4h4amc3ltysmylz7a85yuy5pqefcjmh ', - transaction_index: 0, - transition_index: 0 - } -] + { + block_height: 6090931, + commitment: + "3190431573263899914391947973857035974322781794111836590387626738139790056909field", + function_name: "place_bid", + output_index: 0, + owner: "7447609208500997115362207913251419702258326233440670198345216549215225004914field", + program_name: "private_auction.aleo", + record_ciphertext: + "record1qyqspn9v8rwmmtwfynxl7z98sr3hgevduqk7wl4ufmp8yxanp9x5lhgdqsrxy6tyv3jhyscqqgpqp2k5sw5f37f86hge97ls2avx94pvg7fnaxr7uqwfwkqaljq8jlgyg480mg8fy67fkhqe6ysjxdr47whlt09gwuwmlme57nxvz4kn5qyqy6tygvqqyqsqaljsdk8g0g7f9t4s8z2utywzg6qwpgrl57k8fzvcz5qrthzy5qrk422ak9e9pwylg9k7fh99qa05qu4qj9k5mn7hjf5586fmlrk6qpqxv9kk7atwws3sqqspqryrtmup2kn26693c4f7e79gc0yfdammkxexk84vt0z5wms8dgnq2ztfwd0hw6twdejhygcqqgqsppdjyztk35ekkzq60jx7gg498tccxc9l0ysmk6hal0yglk436ng8pq5wrq99rlhqqglf0gvv70lfavzfu6mgf4sgzmuwej2a2f4umgzssk0fhc", + record_plaintext: + "{\n" + + " owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n" + + " bidder: aleo1jqnajd8g6ezqjq0eefm4zeqynwx6vzed8flnkmejw0afy09dusrszzk46k.private,\n" + + " id: 127430097643352927347108200field.private,\n" + + " amount: 2u64.private,\n" + + " is_winner: false.private,\n" + + " _nonce: 2648035478322331583038604395085314793293985787056490619358776499723646871560group.public,\n" + + " _version: 0u8.public\n" + + "}", + record_name: "Bid", + spent: false, + tag: "5941252181432651644402279701137165256963073258332916685063623109173576520831field", + transaction_id: + "at1unmfl5gv6d28c9dt405kxxj8nl7ytl5eq0ue9ctpc7lpmds8qgqqqglh2z ", + transition_id: + "au1rc32lk3umuye2ccpqkx8gw5m2cf92a29wzyfj5r7tg9jtnd2duxq6gtuss ", + transaction_index: 2, + transition_index: 0, + }, + { + block_height: 10139458, + commitment: + "1402624598475573407524150006714553637670194594508553840106512461758960165497field", + function_name: "transfer_public_to_private", + output_index: 0, + owner: "7447609208500997115362207913251419702258326233440670198345216549215225004914field", + program_name: "credits.aleo", + record_ciphertext: + "record1qvqsq9pcyh8s0zlqwydq0uef42kanl88hct6c7f0y7m3gawxv8er20g2qyxx66trwfhkxun9v35hguerqqpqzqzue2ynz2mfnucdf9gft6cuqussdtge8efqwnlkqvq5nr52nhq9pmv47u20cmsk6mnsl70kxhengndepngqja8s5myu0778nncrttls576mt26", + record_plaintext: + "{\n" + + " owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n" + + " microcredits: 1000000u64.private,\n" + + " _nonce: 4974295747251926833599773262811444029929021845727007977270637854284605120473group.public,\n" + + " _version: 1u8.public\n" + + "}", + record_name: "credits", + spent: false, + tag: "2965517500209150226508265073635793457193572667031485750956287906078711930968field", + transaction_id: + "at10g988ruzr70rv0jrnx67ay0f62gzn0vsmlpgsk9seqzd4k406sys8q9xhk ", + transition_id: + "au1wr7q6y6fw2gyrwem2zjm8kp2lvl04eay8hqmaz6l2gd4p60ddy9s2lj2qr ", + transaction_index: 0, + transition_index: 0, + }, + { + block_height: 10140546, + commitment: + "6211743159355787867034474916630197652498323872351981366700814088987411118433field", + function_name: "transfer_public_to_private", + output_index: 0, + owner: "7447609208500997115362207913251419702258326233440670198345216549215225004914field", + program_name: "credits.aleo", + record_ciphertext: + "record1qvqspk7dttx96hemjmnzkf2vf7exfxt6uu04xxxhr987nycq5xx4lkc3qyxx66trwfhkxun9v35hguerqqpqzqqjw24ydxw5t5jgjhszz6dmtgwcw2r0ckgkg3kkxkh8y8cy0gump9an579cxeg5rxkawwdvh2wmesusl8tpwldpe60wkmgzdgd97pns2akmwge", + record_plaintext: + "{\n" + + " owner: aleo1q8zc0asncaw9d83ft2dynyqz08fcpq3p40depmrj4wjda28rdvrsvegg45.private,\n" + + " microcredits: 2000000u64.private,\n" + + " _nonce: 2445210375074181986314301912374182923270809668425048232267618676093400660603group.public,\n" + + " _version: 1u8.public\n" + + "}", + record_name: "credits", + spent: false, + tag: "8421937347379608036510120951995833971195343843566214313082589116311107280540field", + transaction_id: + "at1hy45m9w56cr8f5yzqp675heh3rde9996exy9875ylzk2w2nmt5qsfudud5 ", + transition_id: + "au1yp2x7t378w3d74kkkz9vcq7ckeg4h4amc3ltysmylz7a85yuy5pqefcjmh ", + transaction_index: 0, + transition_index: 0, + }, +]; const CHECK_SNS_RESPONSE = { - '1621694306596217216370326054181178914897851479837084979111511176605457690717field': true, - '5684626152578699086223993752521225507576791345254401210560771329591763880242field': false, -} + "1621694306596217216370326054181178914897851479837084979111511176605457690717field": + true, + "5684626152578699086223993752521225507576791345254401210560771329591763880242field": + false, +}; const CHECK_TAGS_RESPONSE = { - '2965517500209150226508265073635793457193572667031485750956287906078711930968field': false, - '8421937347379608036510120951995833971195343843566214313082589116311107280540field': false, - '5941252181432651644402279701137165256963073258332916685063623109173576520831field': false -} + "2965517500209150226508265073635793457193572667031485750956287906078711930968field": + false, + "8421937347379608036510120951995833971195343843566214313082589116311107280540field": + false, + "5941252181432651644402279701137165256963073258332916685063623109173576520831field": + false, +}; export { CREDITS_RECORD_V1, @@ -238,4 +294,4 @@ export { RECORD_PLAINTEXT_V1_STRING, RECORD_VIEW_KEY_STRING, VIEW_KEY_STRING, - }; \ No newline at end of file +}; diff --git a/sdk/tests/key-provider.test.ts b/sdk/tests/key-provider.test.ts index 191c8cc60..04322767a 100644 --- a/sdk/tests/key-provider.test.ts +++ b/sdk/tests/key-provider.test.ts @@ -43,25 +43,35 @@ describe("KeyProvider", () => { it("Should use cache when set and not use it when not", async () => { keyProvider.useCache(true); - const [provingKey, verifyingKey] = <FunctionKeyPair>await keyProvider.feePublicKeys(); + const [provingKey, verifyingKey] = <FunctionKeyPair>( + await keyProvider.feePublicKeys() + ); expect(keyProvider.cache.size).equal(1); expect(provingKey).instanceof(ProvingKey); expect(verifyingKey).instanceof(VerifyingKey); const transferCacheKey = keyProvider.cache.keys().next().value; - const [cachedProvingKey, cachedVerifyingKey] = <CachedKeyPair>keyProvider.cache.get(transferCacheKey!); + const [cachedProvingKey, cachedVerifyingKey] = <CachedKeyPair>( + keyProvider.cache.get(transferCacheKey!) + ); expect(cachedProvingKey).instanceof(Uint8Array); expect(cachedVerifyingKey).instanceof(Uint8Array); // Ensure the functionKeys method to get the keys and that the cache is used to do so - const [fetchedProvingKey, fetchedVerifyingKey] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public) + const [fetchedProvingKey, fetchedVerifyingKey] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.fee_public, + ) + ); expect(keyProvider.cache.size).equal(1); expect(fetchedProvingKey).instanceof(ProvingKey); expect(fetchedVerifyingKey).instanceof(VerifyingKey); keyProvider.clearCache(); keyProvider.useCache(false); - const [redownloadedProvingKey, redownloadedVerifyingKey] = <FunctionKeyPair>await keyProvider.feePublicKeys(); + const [redownloadedProvingKey, redownloadedVerifyingKey] = < + FunctionKeyPair + >await keyProvider.feePublicKeys(); expect(keyProvider.cache.size).equal(0); expect(redownloadedProvingKey).instanceof(ProvingKey); expect(redownloadedVerifyingKey).instanceof(VerifyingKey); @@ -69,74 +79,202 @@ describe("KeyProvider", () => { it.skip("Should not fetch offline keys that haven't already been stored", async () => { // Download the credits.aleo function keys - const [bondPublicProver, bondPublicVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.bond_public); - const [claimUnbondPublicProver, claimUnbondVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.claim_unbond_public); - const [feePrivateProver, feePrivateVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_private); - const [feePublicProver, feePublicVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public); - const [joinProver, joinVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.join); - const [setValidatorStateProver, setValidatorStateVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.set_validator_state); - const [splitProver, splitVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.split); - const [transferPrivateProver, transferPrivateVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_private); - const [transferPrivateToPublicProver, transferPrivateToPublicVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_private_to_public); - const [transferPublicProver, transferPublicVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public); - const [transferPublicToPrivateProver, transferPublicToPrivateVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public_to_private); - const [unbondPublicProver, unbondPublicVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.unbond_public); + const [bondPublicProver, bondPublicVerifier] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.bond_public, + ) + ); + const [claimUnbondPublicProver, claimUnbondVerifier] = < + FunctionKeyPair + >await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.claim_unbond_public, + ); + const [feePrivateProver, feePrivateVerifier] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.fee_private, + ) + ); + const [feePublicProver, feePublicVerifier] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.fee_public, + ) + ); + const [joinProver, joinVerifier] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.join) + ); + const [setValidatorStateProver, setValidatorStateVerifier] = < + FunctionKeyPair + >await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.set_validator_state, + ); + const [splitProver, splitVerifier] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.split) + ); + const [transferPrivateProver, transferPrivateVerifier] = < + FunctionKeyPair + >await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.transfer_private, + ); + const [ + transferPrivateToPublicProver, + transferPrivateToPublicVerifier, + ] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.transfer_private_to_public, + ) + ); + const [transferPublicProver, transferPublicVerifier] = < + FunctionKeyPair + >await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.transfer_public, + ); + const [ + transferPublicToPrivateProver, + transferPublicToPrivateVerifier, + ] = <FunctionKeyPair>( + await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.transfer_public_to_private, + ) + ); + const [unbondPublicProver, unbondPublicVerifier] = < + FunctionKeyPair + >await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.unbond_public, + ); // Ensure the insertion methods work as expected without throwing an exception offlineKeyProvider.insertBondPublicKeys(bondPublicProver); - offlineKeyProvider.insertClaimUnbondPublicKeys(claimUnbondPublicProver); + offlineKeyProvider.insertClaimUnbondPublicKeys( + claimUnbondPublicProver, + ); offlineKeyProvider.insertFeePrivateKeys(feePrivateProver); offlineKeyProvider.insertFeePublicKeys(feePublicProver); offlineKeyProvider.insertJoinKeys(joinProver); - offlineKeyProvider.insertSetValidatorStateKeys(setValidatorStateProver); + offlineKeyProvider.insertSetValidatorStateKeys( + setValidatorStateProver, + ); offlineKeyProvider.insertSplitKeys(splitProver); offlineKeyProvider.insertTransferPrivateKeys(transferPrivateProver); - offlineKeyProvider.insertTransferPrivateToPublicKeys(transferPrivateToPublicProver); + offlineKeyProvider.insertTransferPrivateToPublicKeys( + transferPrivateToPublicProver, + ); offlineKeyProvider.insertTransferPublicKeys(transferPublicProver); - offlineKeyProvider.insertTransferPublicToPrivateKeys(transferPublicToPrivateProver); + offlineKeyProvider.insertTransferPublicToPrivateKeys( + transferPublicToPrivateProver, + ); offlineKeyProvider.insertUnbondPublicKeys(unbondPublicProver); - const [bondPublicProverLocal, bondPublicVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.bondPublicKeys(); - const [claimUnbondPublicProverLocal, claimUnbondVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.claimUnbondPublicKeys(); - const [feePrivateProverLocal, feePrivateVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.feePrivateKeys(); - const [feePublicProverLocal, feePublicVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.feePublicKeys(); - const [joinProverLocal, joinVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.joinKeys(); - const [splitProverLocal, splitVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.splitKeys(); - const [transferPrivateProverLocal, transferPrivateVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.transferKeys("private"); - const [transferPrivateToPublicProverLocal, transferPrivateToPublicVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.transferKeys("privateToPublic"); - const [transferPublicProverLocal, transferPublicVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.transferKeys("public"); - const [transferPublicToPrivateProverLocal, transferPublicToPrivateVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.transferKeys("publicToPrivate"); - const [unbondPublicProverLocal, unbondPublicVerifierLocal] = <FunctionKeyPair>await offlineKeyProvider.unBondPublicKeys(); + const [bondPublicProverLocal, bondPublicVerifierLocal] = < + FunctionKeyPair + >await offlineKeyProvider.bondPublicKeys(); + const [claimUnbondPublicProverLocal, claimUnbondVerifierLocal] = < + FunctionKeyPair + >await offlineKeyProvider.claimUnbondPublicKeys(); + const [feePrivateProverLocal, feePrivateVerifierLocal] = < + FunctionKeyPair + >await offlineKeyProvider.feePrivateKeys(); + const [feePublicProverLocal, feePublicVerifierLocal] = < + FunctionKeyPair + >await offlineKeyProvider.feePublicKeys(); + const [joinProverLocal, joinVerifierLocal] = <FunctionKeyPair>( + await offlineKeyProvider.joinKeys() + ); + const [splitProverLocal, splitVerifierLocal] = <FunctionKeyPair>( + await offlineKeyProvider.splitKeys() + ); + const [transferPrivateProverLocal, transferPrivateVerifierLocal] = < + FunctionKeyPair + >await offlineKeyProvider.transferKeys("private"); + const [ + transferPrivateToPublicProverLocal, + transferPrivateToPublicVerifierLocal, + ] = <FunctionKeyPair>( + await offlineKeyProvider.transferKeys("privateToPublic") + ); + const [transferPublicProverLocal, transferPublicVerifierLocal] = < + FunctionKeyPair + >await offlineKeyProvider.transferKeys("public"); + const [ + transferPublicToPrivateProverLocal, + transferPublicToPrivateVerifierLocal, + ] = <FunctionKeyPair>( + await offlineKeyProvider.transferKeys("publicToPrivate") + ); + const [unbondPublicProverLocal, unbondPublicVerifierLocal] = < + FunctionKeyPair + >await offlineKeyProvider.unBondPublicKeys(); // Ensure the checksum of the recovered keys match those of the original keys - expect(bondPublicProver.checksum()).equal(bondPublicProverLocal.checksum()); - expect(bondPublicVerifier.checksum()).equal(bondPublicVerifierLocal.checksum()); - expect(claimUnbondPublicProver.checksum()).equal(claimUnbondPublicProverLocal.checksum()); - expect(claimUnbondVerifier.checksum()).equal(claimUnbondVerifierLocal.checksum()); - expect(feePrivateProver.checksum()).equal(feePrivateProverLocal.checksum()); - expect(feePrivateVerifier.checksum()).equal(feePrivateVerifierLocal.checksum()); - expect(feePublicProver.checksum()).equal(feePublicProverLocal.checksum()); - expect(feePublicVerifier.checksum()).equal(feePublicVerifierLocal.checksum()); + expect(bondPublicProver.checksum()).equal( + bondPublicProverLocal.checksum(), + ); + expect(bondPublicVerifier.checksum()).equal( + bondPublicVerifierLocal.checksum(), + ); + expect(claimUnbondPublicProver.checksum()).equal( + claimUnbondPublicProverLocal.checksum(), + ); + expect(claimUnbondVerifier.checksum()).equal( + claimUnbondVerifierLocal.checksum(), + ); + expect(feePrivateProver.checksum()).equal( + feePrivateProverLocal.checksum(), + ); + expect(feePrivateVerifier.checksum()).equal( + feePrivateVerifierLocal.checksum(), + ); + expect(feePublicProver.checksum()).equal( + feePublicProverLocal.checksum(), + ); + expect(feePublicVerifier.checksum()).equal( + feePublicVerifierLocal.checksum(), + ); expect(joinProver.checksum()).equal(joinProverLocal.checksum()); expect(joinVerifier.checksum()).equal(joinVerifierLocal.checksum()); expect(splitProver.checksum()).equal(splitProverLocal.checksum()); - expect(splitVerifier.checksum()).equal(splitVerifierLocal.checksum()); - expect(transferPrivateProver.checksum()).equal(transferPrivateProverLocal.checksum()); - expect(transferPrivateVerifier.checksum()).equal(transferPrivateVerifierLocal.checksum()); - expect(transferPrivateToPublicProver.checksum()).equal(transferPrivateToPublicProverLocal.checksum()); - expect(transferPrivateToPublicVerifier.checksum()).equal(transferPrivateToPublicVerifierLocal.checksum()); - expect(transferPublicProver.checksum()).equal(transferPublicProverLocal.checksum()); - expect(transferPublicVerifier.checksum()).equal(transferPublicVerifierLocal.checksum()); - expect(transferPublicToPrivateProver.checksum()).equal(transferPublicToPrivateProverLocal.checksum()); - expect(transferPublicToPrivateVerifier.checksum()).equal(transferPublicToPrivateVerifierLocal.checksum()); - expect(unbondPublicProver.checksum()).equal(unbondPublicProverLocal.checksum()); - expect(unbondPublicVerifier.checksum()).equal(unbondPublicVerifierLocal.checksum()); + expect(splitVerifier.checksum()).equal( + splitVerifierLocal.checksum(), + ); + expect(transferPrivateProver.checksum()).equal( + transferPrivateProverLocal.checksum(), + ); + expect(transferPrivateVerifier.checksum()).equal( + transferPrivateVerifierLocal.checksum(), + ); + expect(transferPrivateToPublicProver.checksum()).equal( + transferPrivateToPublicProverLocal.checksum(), + ); + expect(transferPrivateToPublicVerifier.checksum()).equal( + transferPrivateToPublicVerifierLocal.checksum(), + ); + expect(transferPublicProver.checksum()).equal( + transferPublicProverLocal.checksum(), + ); + expect(transferPublicVerifier.checksum()).equal( + transferPublicVerifierLocal.checksum(), + ); + expect(transferPublicToPrivateProver.checksum()).equal( + transferPublicToPrivateProverLocal.checksum(), + ); + expect(transferPublicToPrivateVerifier.checksum()).equal( + transferPublicToPrivateVerifierLocal.checksum(), + ); + expect(unbondPublicProver.checksum()).equal( + unbondPublicProverLocal.checksum(), + ); + expect(unbondPublicVerifier.checksum()).equal( + unbondPublicVerifierLocal.checksum(), + ); // Ensure the recovered keys are of the correct type expect(bondPublicProverLocal.isBondPublicProver()).equal(true); expect(bondPublicVerifierLocal.isBondPublicVerifier()).equal(true); - expect(claimUnbondPublicProverLocal.isClaimUnbondPublicProver()).equal(true); - expect(claimUnbondVerifierLocal.isClaimUnbondPublicVerifier()).equal(true); + expect( + claimUnbondPublicProverLocal.isClaimUnbondPublicProver(), + ).equal(true); + expect( + claimUnbondVerifierLocal.isClaimUnbondPublicVerifier(), + ).equal(true); expect(feePrivateProverLocal.isFeePrivateProver()).equal(true); expect(feePrivateVerifierLocal.isFeePrivateVerifier()).equal(true); expect(feePublicProverLocal.isFeePublicProver()).equal(true); @@ -145,16 +283,34 @@ describe("KeyProvider", () => { expect(joinVerifierLocal.isJoinVerifier()).equal(true); expect(splitProverLocal.isSplitProver()).equal(true); expect(splitVerifierLocal.isSplitVerifier()).equal(true); - expect(transferPrivateProverLocal.isTransferPrivateProver()).equal(true); - expect(transferPrivateVerifierLocal.isTransferPrivateVerifier()).equal(true); - expect(transferPrivateToPublicProverLocal.isTransferPrivateToPublicProver()).equal(true); - expect(transferPrivateToPublicVerifierLocal.isTransferPrivateToPublicVerifier()).equal(true); - expect(transferPublicProverLocal.isTransferPublicProver()).equal(true); - expect(transferPublicVerifierLocal.isTransferPublicVerifier()).equal(true); - expect(transferPublicToPrivateProverLocal.isTransferPublicToPrivateProver()).equal(true); - expect(transferPublicToPrivateVerifierLocal.isTransferPublicToPrivateVerifier()).equal(true); + expect(transferPrivateProverLocal.isTransferPrivateProver()).equal( + true, + ); + expect( + transferPrivateVerifierLocal.isTransferPrivateVerifier(), + ).equal(true); + expect( + transferPrivateToPublicProverLocal.isTransferPrivateToPublicProver(), + ).equal(true); + expect( + transferPrivateToPublicVerifierLocal.isTransferPrivateToPublicVerifier(), + ).equal(true); + expect(transferPublicProverLocal.isTransferPublicProver()).equal( + true, + ); + expect( + transferPublicVerifierLocal.isTransferPublicVerifier(), + ).equal(true); + expect( + transferPublicToPrivateProverLocal.isTransferPublicToPrivateProver(), + ).equal(true); + expect( + transferPublicToPrivateVerifierLocal.isTransferPublicToPrivateVerifier(), + ).equal(true); expect(unbondPublicProverLocal.isUnbondPublicProver()).equal(true); - expect(unbondPublicVerifierLocal.isUnbondPublicVerifier()).equal(true); + expect(unbondPublicVerifierLocal.isUnbondPublicVerifier()).equal( + true, + ); }); }); }); @@ -167,10 +323,16 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { const proverLoc = "program.aleo.missing.prover"; const verifierLoc = "program.aleo.missing.verifier"; expect(await keystore.getKeyBytes(locator(proverLoc))).equal(null); - expect(await keystore.getProvingKey(locator(proverLoc))).equal(null); - expect(await keystore.getVerifyingKey(locator(verifierLoc))).equal(null); + expect(await keystore.getProvingKey(locator(proverLoc))).equal( + null, + ); + expect(await keystore.getVerifyingKey(locator(verifierLoc))).equal( + null, + ); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); @@ -182,7 +344,9 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { keystore = new LocalFileKeyStore(tempDir); }); after(async () => { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); }); it("throws InvalidLocatorError for locator containing '..'", async () => { @@ -191,14 +355,18 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { expect.fail("should throw"); } catch (e) { expect(e).instanceof(InvalidLocatorError); - expect((e as InvalidLocatorError).reason).equal("path_traversal"); + expect((e as InvalidLocatorError).reason).equal( + "path_traversal", + ); } try { await keystore.setKeyBytes(new Uint8Array(10), locator("..")); expect.fail("should throw"); } catch (e) { expect(e).instanceof(InvalidLocatorError); - expect((e as InvalidLocatorError).reason).equal("reserved_name"); + expect((e as InvalidLocatorError).reason).equal( + "reserved_name", + ); } }); @@ -208,14 +376,18 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { expect.fail("should throw"); } catch (e) { expect(e).instanceof(InvalidLocatorError); - expect((e as InvalidLocatorError).reason).equal("path_separator"); + expect((e as InvalidLocatorError).reason).equal( + "path_separator", + ); } try { await keystore.has("dir\\key.prover"); expect.fail("should throw"); } catch (e) { expect(e).instanceof(InvalidLocatorError); - expect((e as InvalidLocatorError).reason).equal("path_separator"); + expect((e as InvalidLocatorError).reason).equal( + "path_separator", + ); } }); @@ -226,7 +398,9 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { expect.fail(`should throw for "${invalid}"`); } catch (e) { expect(e).instanceof(InvalidLocatorError); - expect((e as InvalidLocatorError).reason).equal("reserved_name"); + expect((e as InvalidLocatorError).reason).equal( + "reserved_name", + ); } } }); @@ -243,7 +417,9 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { expect.fail("should throw"); } catch (e) { expect(e).instanceof(InvalidLocatorError); - expect((e as InvalidLocatorError).reason).equal("path_separator"); + expect((e as InvalidLocatorError).reason).equal( + "path_separator", + ); } }); @@ -260,12 +436,20 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { const keystore = new LocalFileKeyStore(); const loc = "default_path_test.prover"; try { - await keystore.setKeyBytes(new Uint8Array([1, 2, 3]), locator(loc)); + await keystore.setKeyBytes( + new Uint8Array([1, 2, 3]), + locator(loc), + ); const expectedPath = path.join(cwd, ".aleo", loc); await $fs.access(expectedPath); expect(await keystore.has(loc)).equal(true); } finally { - await $fs.rm(path.join(cwd, ".aleo"), { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(path.join(cwd, ".aleo"), { + recursive: true, + force: true, + }) + .catch(() => {}); } }); @@ -280,10 +464,12 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { const wrongPath = path.join(base, loc); await $fs.access(wrongPath).then( () => expect.fail("key should not be directly under base"), - () => {} + () => {}, ); } finally { - await $fs.rm(base, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(base, { recursive: true, force: true }) + .catch(() => {}); } }); @@ -299,10 +485,12 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { const doubleAleo = path.join(aleoDir, ".aleo", loc); await $fs.access(doubleAleo).then( () => expect.fail("should not create .aleo/.aleo"), - () => {} + () => {}, ); } finally { - await $fs.rm(base, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(base, { recursive: true, force: true }) + .catch(() => {}); } }); }); @@ -318,15 +506,21 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { await keystore1.setKeyBytes(keyBytes, locator(loc)); const fromFirst = await keystore1.getKeyBytes(locator(loc)); expect(fromFirst).not.equal(null); - expect(Buffer.from(fromFirst!).equals(Buffer.from(keyBytes))).equal(true); + expect( + Buffer.from(fromFirst!).equals(Buffer.from(keyBytes)), + ).equal(true); // Simulate restart: new instance, no in-memory fingerprint; verification must use disk metadata. const keystore2 = new LocalFileKeyStore(tempDir); const fromSecond = await keystore2.getKeyBytes(locator(loc)); expect(fromSecond).not.equal(null); - expect(Buffer.from(fromSecond!).equals(Buffer.from(keyBytes))).equal(true); + expect( + Buffer.from(fromSecond!).equals(Buffer.from(keyBytes)), + ).equal(true); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); @@ -334,19 +528,27 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { this.timeout(20000); const tempDir = `${process.cwd()}/.keystore-test-${Date.now()}-${Math.floor(Math.random() * 1e6)}`; const kp = new AleoKeyProvider(); - const [prov, ver] = <FunctionKeyPair>await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public); + const [prov, ver] = <FunctionKeyPair>( + await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public) + ); const proverLoc = "fee_public.prover"; const verifierLoc = "fee_public.verifier"; const keystore1 = new LocalFileKeyStore(tempDir); try { - await keystore1.setKeys(locator(proverLoc), locator(verifierLoc), [prov, ver]); + await keystore1.setKeys( + locator(proverLoc), + locator(verifierLoc), + [prov, ver], + ); const keystore2 = new LocalFileKeyStore(tempDir); const got = await keystore2.getProvingKey(locator(proverLoc)); expect(got).not.equal(null); expect(got!.checksum()).equal(prov.checksum()); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); }); @@ -355,7 +557,10 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { it("removes the keystore directory so it no longer exists", async () => { const tempDir = `${process.cwd()}/.keystore-test-${Date.now()}-${Math.floor(Math.random() * 1e6)}`; const keystore = new LocalFileKeyStore(tempDir); - await keystore.setKeyBytes(new Uint8Array([1]), locator("x.prover")); + await keystore.setKeyBytes( + new Uint8Array([1]), + locator("x.prover"), + ); await $fs.access(path.join(tempDir, ".aleo", "x.prover")); await keystore.clear(); try { @@ -364,29 +569,45 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { } catch { // expected: ENOENT } - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); }); it("subsequent write recreates directory", async () => { const tempDir = `${process.cwd()}/.keystore-test-${Date.now()}-${Math.floor(Math.random() * 1e6)}`; const keystore = new LocalFileKeyStore(tempDir); try { - await keystore.setKeyBytes(new Uint8Array([1]), locator("a.prover")); + await keystore.setKeyBytes( + new Uint8Array([1]), + locator("a.prover"), + ); await keystore.clear(); - await keystore.setKeyBytes(new Uint8Array([2]), locator("b.prover")); + await keystore.setKeyBytes( + new Uint8Array([2]), + locator("b.prover"), + ); const p = path.join(tempDir, ".aleo", "b.prover"); await $fs.access(p); - expect(await keystore.getKeyBytes(locator("b.prover"))).not.equal(null); + expect( + await keystore.getKeyBytes(locator("b.prover")), + ).not.equal(null); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); }); it("should set, get, has, delete, and clear using raw bytes on disk", async () => { const kp = new AleoKeyProvider(); - const [provA, verA] = <FunctionKeyPair>await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public); - const [provB, verB] = <FunctionKeyPair>await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.join); + const [provA, verA] = <FunctionKeyPair>( + await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public) + ); + const [provB, verB] = <FunctionKeyPair>( + await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.join) + ); const proverBytesA = provA.toBytes(); const verifierBytesA = verA.toBytes(); const proverBytesB = provB.toBytes(); @@ -396,10 +617,18 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { const keystore = new LocalFileKeyStore(tempDir); try { await keystore.clear(); - await keystore.delete("program.aleo.function_a.prover").catch(() => {}); - await keystore.delete("program.aleo.function_a.verifier").catch(() => {}); - await keystore.delete("program.aleo.function_b.prover").catch(() => {}); - await keystore.delete("program.aleo.function_b.verifier").catch(() => {}); + await keystore + .delete("program.aleo.function_a.prover") + .catch(() => {}); + await keystore + .delete("program.aleo.function_a.verifier") + .catch(() => {}); + await keystore + .delete("program.aleo.function_b.prover") + .catch(() => {}); + await keystore + .delete("program.aleo.function_b.verifier") + .catch(() => {}); const locatorAProver = "program.aleo.function_a.prover"; const locatorAVerifier = "program.aleo.function_a.verifier"; @@ -407,24 +636,44 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { const locatorBVerifier = "program.aleo.function_b.verifier"; expect(await keystore.has(locatorAProver)).equal(false); - expect(await keystore.getKeyBytes(locator(locatorAProver))).equal(null); - expect(await keystore.getProvingKey(locator(locatorAProver))).equal(null); - expect(await keystore.getVerifyingKey(locator(locatorAVerifier))).equal(null); + expect(await keystore.getKeyBytes(locator(locatorAProver))).equal( + null, + ); + expect(await keystore.getProvingKey(locator(locatorAProver))).equal( + null, + ); + expect( + await keystore.getVerifyingKey(locator(locatorAVerifier)), + ).equal(null); await keystore.setKeyBytes(proverBytesA, locator(locatorAProver)); - await keystore.setKeyBytes(verifierBytesA, locator(locatorAVerifier)); + await keystore.setKeyBytes( + verifierBytesA, + locator(locatorAVerifier), + ); expect(await keystore.has(locatorAProver)).equal(true); expect(await keystore.has(locatorAVerifier)).equal(true); - const gotProverA = await keystore.getKeyBytes(locator(locatorAProver)); - const gotVerifierA = await keystore.getKeyBytes(locator(locatorAVerifier)); + const gotProverA = await keystore.getKeyBytes( + locator(locatorAProver), + ); + const gotVerifierA = await keystore.getKeyBytes( + locator(locatorAVerifier), + ); expect(gotProverA).not.equal(null); expect(gotVerifierA).not.equal(null); - expect(Buffer.from(gotProverA!).equals(Buffer.from(proverBytesA))).equal(true); - expect(Buffer.from(gotVerifierA!).equals(Buffer.from(verifierBytesA))).equal(true); + expect( + Buffer.from(gotProverA!).equals(Buffer.from(proverBytesA)), + ).equal(true); + expect( + Buffer.from(gotVerifierA!).equals(Buffer.from(verifierBytesA)), + ).equal(true); await keystore.setKeyBytes(proverBytesB, locator(locatorBProver)); - await keystore.setKeyBytes(verifierBytesB, locator(locatorBVerifier)); + await keystore.setKeyBytes( + verifierBytesB, + locator(locatorBVerifier), + ); expect(await keystore.has(locatorBProver)).equal(true); expect(await keystore.has(locatorBVerifier)).equal(true); @@ -432,14 +681,20 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { await keystore.delete(locatorAVerifier); expect(await keystore.has(locatorAProver)).equal(false); expect(await keystore.has(locatorAVerifier)).equal(false); - expect(await keystore.getKeyBytes(locator(locatorAProver))).equal(null); + expect(await keystore.getKeyBytes(locator(locatorAProver))).equal( + null, + ); await keystore.clear(); expect(await keystore.has(locatorBProver)).equal(false); expect(await keystore.has(locatorBVerifier)).equal(false); - expect(await keystore.getKeyBytes(locator(locatorBProver))).equal(null); + expect(await keystore.getKeyBytes(locator(locatorBProver))).equal( + null, + ); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); @@ -456,12 +711,17 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { const kp = new AleoKeyProvider(); const [prov, ver] = <FunctionKeyPair>await kp.feePublicKeys(); - await keystore.setKeys(locator(proverLoc), locator(verifierLoc), [prov, ver]); + await keystore.setKeys(locator(proverLoc), locator(verifierLoc), [ + prov, + ver, + ]); expect(await keystore.has(proverLoc)).equal(true); expect(await keystore.has(verifierLoc)).equal(true); const gotProver = await keystore.getProvingKey(locator(proverLoc)); - const gotVerifier = await keystore.getVerifyingKey(locator(verifierLoc)); + const gotVerifier = await keystore.getVerifyingKey( + locator(verifierLoc), + ); expect(gotProver).not.equal(null); expect(gotVerifier).not.equal(null); expect(gotProver!).instanceof(ProvingKey); @@ -470,24 +730,38 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { expect(gotVerifier!.checksum()).equal(ver.checksum()); const proverBytes = await keystore.getKeyBytes(locator(proverLoc)); - const verifierBytes = await keystore.getKeyBytes(locator(verifierLoc)); + const verifierBytes = await keystore.getKeyBytes( + locator(verifierLoc), + ); expect(proverBytes).not.equal(null); expect(verifierBytes).not.equal(null); - expect(ProvingKey.fromBytes(proverBytes!).checksum()).equal(prov.checksum()); - expect(VerifyingKey.fromBytes(verifierBytes!).checksum()).equal(ver.checksum()); + expect(ProvingKey.fromBytes(proverBytes!).checksum()).equal( + prov.checksum(), + ); + expect(VerifyingKey.fromBytes(verifierBytes!).checksum()).equal( + ver.checksum(), + ); await keystore.delete(proverLoc); await keystore.delete(verifierLoc); - expect(await keystore.getProvingKey(locator(proverLoc))).equal(null); - expect(await keystore.getVerifyingKey(locator(verifierLoc))).equal(null); + expect(await keystore.getProvingKey(locator(proverLoc))).equal( + null, + ); + expect(await keystore.getVerifyingKey(locator(verifierLoc))).equal( + null, + ); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); it("getKeyMetadata returns fingerprint after setKeyBytes and null for missing locator", async () => { const kp = new AleoKeyProvider(); - const [prov] = <FunctionKeyPair>await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public); + const [prov] = <FunctionKeyPair>( + await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public) + ); const keyBytes = prov.toBytes(); const tempDir = `${process.cwd()}/.keystore-test-${Date.now()}-${Math.floor(Math.random() * 1e6)}`; @@ -506,7 +780,9 @@ describe("KeyStore (file) – LocalFileKeyStore", () => { await keystore.delete(loc); expect(await keystore.getKeyMetadata(loc)).equal(null); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); }); @@ -529,7 +805,9 @@ describe("Key verifier (MemKeyVerifier & sha256Hex)", () => { it("returns correct SHA-256 hash for empty input", async () => { const h = await sha256Hex(new Uint8Array(0)); - expect(h).to.equal("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + expect(h).to.equal( + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + ); }); }); @@ -546,7 +824,9 @@ describe("Key verifier (MemKeyVerifier & sha256Hex)", () => { it("succeeds when provided fingerprint matches computed (same checksum and size)", async () => { const verifier = new MemKeyVerifier(); const bytes = new Uint8Array([10, 20, 30]); - const computed = await verifier.computeKeyMetadata({ keyBytes: bytes }); + const computed = await verifier.computeKeyMetadata({ + keyBytes: bytes, + }); const meta = await verifier.computeKeyMetadata({ keyBytes: bytes, fingerprint: computed, @@ -559,7 +839,7 @@ describe("Key verifier (MemKeyVerifier & sha256Hex)", () => { const verifier = new MemKeyVerifier(); const bytes = new Uint8Array([10, 20, 30]); const wrongFingerprint: KeyFingerprint = { - checksum: (await sha256Hex(bytes)), + checksum: await sha256Hex(bytes), size: 99, }; let thrown: KeyVerificationError | undefined; @@ -620,7 +900,9 @@ describe("Key verifier (MemKeyVerifier & sha256Hex)", () => { it("resolves when fingerprint matches key bytes", async () => { const verifier = new MemKeyVerifier(); const bytes = new Uint8Array([5, 6, 7, 8]); - const fingerprint = await verifier.computeKeyMetadata({ keyBytes: bytes }); + const fingerprint = await verifier.computeKeyMetadata({ + keyBytes: bytes, + }); await verifier.verifyKeyBytes({ keyBytes: bytes, fingerprint, @@ -630,7 +912,10 @@ describe("Key verifier (MemKeyVerifier & sha256Hex)", () => { it("throws KeyVerificationError when size does not match stored fingerprint", async () => { const verifier = new MemKeyVerifier(); const bytes = new Uint8Array([1, 2, 3]); - await verifier.computeKeyMetadata({ keyBytes: bytes, locator: "loc" }); + await verifier.computeKeyMetadata({ + keyBytes: bytes, + locator: "loc", + }); const wrongBytes = new Uint8Array([1, 2]); let thrown: KeyVerificationError | undefined; try { @@ -648,7 +933,10 @@ describe("Key verifier (MemKeyVerifier & sha256Hex)", () => { it("throws KeyVerificationError when checksum does not match (corrupted bytes)", async () => { const verifier = new MemKeyVerifier(); const bytes = new Uint8Array([1, 2, 3]); - await verifier.computeKeyMetadata({ keyBytes: bytes, locator: "loc" }); + await verifier.computeKeyMetadata({ + keyBytes: bytes, + locator: "loc", + }); const corruptedBytes = new Uint8Array([1, 2, 99]); let thrown: KeyVerificationError | undefined; try { @@ -697,7 +985,9 @@ describe("Key verifier (MemKeyVerifier & sha256Hex)", () => { describe("Key verifier with LocalFileKeyStore (checksum verification on read)", () => { it("getKeyBytes throws KeyVerificationError when key file is corrupted after being stored", async () => { const kp = new AleoKeyProvider(); - const [prov] = <FunctionKeyPair>await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public); + const [prov] = <FunctionKeyPair>( + await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public) + ); const keyBytes = prov.toBytes(); const tempDir = `${process.cwd()}/.keystore-test-${Date.now()}-${Math.floor(Math.random() * 1e6)}`; @@ -710,7 +1000,9 @@ describe("Key verifier with LocalFileKeyStore (checksum verification on read)", await keystore.setKeyBytes(keyBytes, locator(loc)); const readBefore = await keystore.getKeyBytes(locator(loc)); expect(readBefore).not.to.equal(null); - expect(Buffer.from(readBefore!).equals(Buffer.from(keyBytes))).equal(true); + expect( + Buffer.from(readBefore!).equals(Buffer.from(keyBytes)), + ).equal(true); const keyPath = path.join(tempDir, ".aleo", loc); await $fs.writeFile(keyPath, new Uint8Array([1, 2, 3, 4, 5])); @@ -724,13 +1016,17 @@ describe("Key verifier with LocalFileKeyStore (checksum verification on read)", expect(thrown).to.be.instanceOf(KeyVerificationError); expect(thrown!.field).to.equal("size"); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); it("getKeyBytes succeeds when fingerprint in locator matches key bytes", async () => { const kp = new AleoKeyProvider(); - const [prov] = <FunctionKeyPair>await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public); + const [prov] = <FunctionKeyPair>( + await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public) + ); const keyBytes = prov.toBytes(); const verifier = new MemKeyVerifier(); @@ -748,15 +1044,21 @@ describe("Key verifier with LocalFileKeyStore (checksum verification on read)", fingerprint, }); expect(readWithFingerprint).not.to.equal(null); - expect(Buffer.from(readWithFingerprint!).equals(Buffer.from(keyBytes))).equal(true); + expect( + Buffer.from(readWithFingerprint!).equals(Buffer.from(keyBytes)), + ).equal(true); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); it("getKeyBytes throws KeyVerificationError when locator fingerprint does not match stored bytes", async () => { const kp = new AleoKeyProvider(); - const [prov] = <FunctionKeyPair>await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public); + const [prov] = <FunctionKeyPair>( + await kp.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.fee_public) + ); const keyBytes = prov.toBytes(); const tempDir = `${process.cwd()}/.keystore-test-${Date.now()}-${Math.floor(Math.random() * 1e6)}`; @@ -783,7 +1085,9 @@ describe("Key verifier with LocalFileKeyStore (checksum verification on read)", expect(thrown).to.be.instanceOf(KeyVerificationError); expect(thrown!.field).to.equal("checksum"); } finally { - await $fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); + await $fs + .rm(tempDir, { recursive: true, force: true }) + .catch(() => {}); } }); }); diff --git a/sdk/tests/network-client.integration.ts b/sdk/tests/network-client.integration.ts index 37edba365..3bd61257d 100644 --- a/sdk/tests/network-client.integration.ts +++ b/sdk/tests/network-client.integration.ts @@ -1,61 +1,114 @@ import { expect } from "chai"; -import {beaconAddressString, beaconPrivateKeyString} from "./data/account-data.js"; -import {Account, AleoNetworkClient} from "../src/browser.js"; +import { + beaconAddressString, + beaconPrivateKeyString, +} from "./data/account-data.js"; +import { Account, AleoNetworkClient } from "../src/browser.js"; -describe('NodeConnection', () => { +describe("NodeConnection", () => { let localApiClient: AleoNetworkClient; let remoteApiClientWithPrivateKey: AleoNetworkClient; beforeEach(() => { localApiClient = new AleoNetworkClient("http://0.0.0.0:3030"); - remoteApiClientWithPrivateKey = new AleoNetworkClient("http://0.0.0.0:3030"); - remoteApiClientWithPrivateKey.setAccount(new Account({privateKey: beaconPrivateKeyString})); + remoteApiClientWithPrivateKey = new AleoNetworkClient( + "http://0.0.0.0:3030", + ); + remoteApiClientWithPrivateKey.setAccount( + new Account({ privateKey: beaconPrivateKeyString }), + ); }); - describe('findUnspentRecords', () => { - + describe("findUnspentRecords", () => { // Integration tests to be run with a local node (run with -s flag) - it('should find records', async () => { - const records = await localApiClient.findUnspentRecords(0, undefined, undefined, undefined, undefined, []); + it("should find records", async () => { + const records = await localApiClient.findUnspentRecords( + 0, + undefined, + undefined, + undefined, + undefined, + [], + ); expect(Array.isArray(records)).equal(true); if (!(records instanceof Error)) { expect(records.length).above(0); } }); - it('should find records when a private key is pre-configured', async () => { - const records = await remoteApiClientWithPrivateKey.findUnspentRecords(0, undefined, ["credits.aleo"], undefined, 100, []); + it("should find records when a private key is pre-configured", async () => { + const records = + await remoteApiClientWithPrivateKey.findUnspentRecords( + 0, + undefined, + ["credits.aleo"], + undefined, + 100, + [], + ); expect(Array.isArray(records)).equal(true); if (!(records instanceof Error)) { expect(records.length).above(0); } }); - it('should find records even when block height specified is higher than current block height', async () => { - const records = await localApiClient.findUnspentRecords(0, 50000000000000, ["credits.aleo"], undefined, 100, [], beaconPrivateKeyString); + it("should find records even when block height specified is higher than current block height", async () => { + const records = await localApiClient.findUnspentRecords( + 0, + 50000000000000, + ["credits.aleo"], + undefined, + 100, + [], + beaconPrivateKeyString, + ); expect(Array.isArray(records)).equal(true); if (!(records instanceof Error)) { expect(records.length).above(0); } }); - it('should find records with specified amounts', async () => { - let records = await localApiClient.findUnspentRecords(0, 3, ["credits.aleo"], [100, 200], undefined, [], beaconPrivateKeyString); + it("should find records with specified amounts", async () => { + let records = await localApiClient.findUnspentRecords( + 0, + 3, + ["credits.aleo"], + [100, 200], + undefined, + [], + beaconPrivateKeyString, + ); expect(Array.isArray(records)).equal(true); if (!(records instanceof Error)) { expect(records.length).equal(2); } - records = await localApiClient.findUnspentRecords(0, undefined, ["credits.aleo"], undefined, 1000, [], beaconPrivateKeyString); + records = await localApiClient.findUnspentRecords( + 0, + undefined, + ["credits.aleo"], + undefined, + 1000, + [], + beaconPrivateKeyString, + ); expect(Array.isArray(records)).equal(true); if (!(records instanceof Error)) { expect(records.length).above(0); } }); - it('should not find records with existing nonces', async () => { + it("should not find records with existing nonces", async () => { const nonces: string[] = []; - const records = await localApiClient.findUnspentRecords(0, 3, ["credits.aleo"], [100, 200], undefined, [], beaconPrivateKeyString); + const records = await localApiClient.findUnspentRecords( + 0, + 3, + ["credits.aleo"], + [100, 200], + undefined, + [], + beaconPrivateKeyString, + ); expect(Array.isArray(records)).equal(true); // Find two records and store their nonces @@ -66,7 +119,15 @@ describe('NodeConnection', () => { nonces.push(record.nonce()); }); // Check the next records found do not have the same nonces - const new_records = await localApiClient.findUnspentRecords(0, 3, ["credits.aleo"], [100, 200], undefined, nonces, undefined); + const new_records = await localApiClient.findUnspentRecords( + 0, + 3, + ["credits.aleo"], + [100, 200], + undefined, + nonces, + undefined, + ); expect(Array.isArray(records)).equal(true); if (!(new_records instanceof Error)) { expect(new_records.length).equal(2); diff --git a/sdk/tests/network-client.test.ts b/sdk/tests/network-client.test.ts index a42cc6d33..b75275e15 100644 --- a/sdk/tests/network-client.test.ts +++ b/sdk/tests/network-client.test.ts @@ -41,7 +41,7 @@ describe("NodeConnection", () => { beforeEach(() => { connection = new AleoNetworkClient("https://api.provable.com/v2"); - windowFetchSpy = sinon.spy(globalThis, 'fetch'); + windowFetchSpy = sinon.spy(globalThis, "fetch"); }); afterEach(() => { @@ -97,13 +97,18 @@ describe("NodeConnection", () => { expect(typeof programV0).equal("string"); // Ensure the program returned is of the correct object.. - const programWasm = await connection.getProgramObject("credits.aleo"); - const programWasmV0 = await connection.getProgramObject("credits.aleo", 0); + const programWasm = + await connection.getProgramObject("credits.aleo"); + const programWasmV0 = await connection.getProgramObject( + "credits.aleo", + 0, + ); expect(programWasm.id()).equals("credits.aleo"); expect(programWasmV0.id()).equals("credits.aleo"); // Ensure the edition returned is correct. - const creditsEdition = await connection.getLatestProgramEdition("credits.aleo"); + const creditsEdition = + await connection.getLatestProgramEdition("credits.aleo"); expect(creditsEdition == 0).to.equal(true); }); @@ -337,15 +342,16 @@ describe("NodeConnection", () => { ? testnetAcceptedTx : mainnetAcceptedTx : isTestnet - ? testnetRejectedTx - : mainnetRejectedTx; + ? testnetRejectedTx + : mainnetRejectedTx; } it("should return confirmed transaction data for an accepted tx ID", async () => { if (connection.network === "mainnet") { const connection = new AleoNetworkClient(host); const txId = getTxId(connection, "accepted"); - const data = await connection.waitForTransactionConfirmation(txId); + const data = + await connection.waitForTransactionConfirmation(txId); expect(data.status).to.equal("accepted"); expect(data.type).to.be.a("string"); } @@ -363,14 +369,18 @@ describe("NodeConnection", () => { } catch (err: any) { console.log(err.message); if (connection.network === "mainnet") { - expect(err.message).to.include("was rejected by the network"); + expect(err.message).to.include( + "was rejected by the network", + ); } } }); it("should throw for a malformed tx ID", async () => { const connection = new AleoNetworkClient(host); - expectThrows(() => connection.waitForTransactionConfirmation(invalidTx)); + expectThrows(() => + connection.waitForTransactionConfirmation(invalidTx), + ); }); }); @@ -477,8 +487,8 @@ describe("NodeConnection", () => { }); }); - describe('Test API methods that return wasm objects', () => { - it('Plaintext returned from the API should have expected properties', async () => { + describe("Test API methods that return wasm objects", () => { + it("Plaintext returned from the API should have expected properties", async () => { if (connection.network === "testnet") { // Check a struct variant of a plaintext object. let plaintext = await connection.getProgramMappingPlaintext( @@ -511,7 +521,9 @@ describe("NodeConnection", () => { it("should have correct data within the wasm object and summary object for an execution transaction", async () => { // Get the first transaction at block 24700 on testnet. if (connection.network === "testnet") { - const transaction = await connection.getTransactionObject("at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd"); + const transaction = await connection.getTransactionObject( + "at1fjy6s9md2v4rgcn3j3q4qndtfaa2zvg58a4uha0rujvrn4cumu9qfazxdd", + ); const transition = <Transition>transaction.transitions()[0]; const summary = <TransactionObject>transaction.summary(true); @@ -622,7 +634,9 @@ describe("NodeConnection", () => { it.skip("should have correct data within the wasm object and summary object for a deployment transaction", async () => { // Get the deployment transaction for token_registry.aleo if (connection.network === "mainnet") { - const transaction = await connection.getTransactionObject("at15mwg0jyhvpjjrfxwrlwzn8puusnmy7r3xzvpjht4e5gzgnp68q9qd0qqec"); + const transaction = await connection.getTransactionObject( + "at15mwg0jyhvpjjrfxwrlwzn8puusnmy7r3xzvpjht4e5gzgnp68q9qd0qqec", + ); const summary = <TransactionObject>transaction.summary(true); const deployment = <DeploymentObject>summary.deployment; @@ -650,7 +664,7 @@ describe("NodeConnection", () => { } }); - it('Should give the correct JSON response when requesting multiple transactions', async () => { + it("Should give the correct JSON response when requesting multiple transactions", async () => { if (connection.network === "testnet") { const transactions = await connection.getTransactions(27400); expect(transactions.length).equal(4); @@ -732,18 +746,18 @@ describe("NodeConnection", () => { }); describe("getProgramImports", () => { - it("should not fetch the same program multiple times with overlapping imports", async function() { + it("should not fetch the same program multiple times with overlapping imports", async function () { // Only run on testnet - amm_orcl_intrfc_v.aleo only exists on testnet if (connection.network !== "testnet") { this.skip(); return; } - + // Track all calls to getProgram to detect duplicates const fetchedPrograms = new Map<string, number>(); const originalGetProgram = connection.getProgram.bind(connection); - - connection.getProgram = async function(programId: string) { + + connection.getProgram = async function (programId: string) { const count = (fetchedPrograms.get(programId) || 0) + 1; fetchedPrograms.set(programId, count); return originalGetProgram(programId); @@ -751,22 +765,32 @@ describe("NodeConnection", () => { // Test with amm_orcl_intrfc_v.aleo which has 23 imports with heavy overlap // This program previously caused exponentially recursive API calls resulting in rate limiting. - const imports = await connection.getProgramImports("amm_orcl_intrfc_v.aleo"); - + const imports = await connection.getProgramImports( + "amm_orcl_intrfc_v.aleo", + ); + // Verify we got all the imports expect(Object.keys(imports).length).to.be.greaterThan(0); - + // Verify each program was fetched exactly once (no duplicates) - const duplicates = Array.from(fetchedPrograms.entries()) - .filter(([_, count]) => count > 1); - + const duplicates = Array.from(fetchedPrograms.entries()).filter( + ([_, count]) => count > 1, + ); + if (duplicates.length > 0) { - const duplicateNames = duplicates.map(([name, count]) => `${name} (${count}x)`).join(', '); - throw new Error(`Programs fetched multiple times: ${duplicateNames}`); + const duplicateNames = duplicates + .map(([name, count]) => `${name} (${count}x)`) + .join(", "); + throw new Error( + `Programs fetched multiple times: ${duplicateNames}`, + ); } - + // Verify total API calls equals number of unique imports - const totalCalls = Array.from(fetchedPrograms.values()).reduce((a, b) => a + b, 0); + const totalCalls = Array.from(fetchedPrograms.values()).reduce( + (a, b) => a + b, + 0, + ); expect(totalCalls).to.equal(fetchedPrograms.size); }); }); @@ -776,7 +800,7 @@ describe("AleoNetworkClient JWT refresh URL", () => { let fetchStub: sinon.SinonStub; beforeEach(() => { - fetchStub = sinon.stub(globalThis, 'fetch'); + fetchStub = sinon.stub(globalThis, "fetch"); }); afterEach(() => { @@ -784,12 +808,36 @@ describe("AleoNetworkClient JWT refresh URL", () => { }); const jwtEdgeCases = [ - { label: "standard Provable API", host: "https://api.provable.com/v2", expectedOrigin: "https://api.provable.com" }, - { label: "custom host with path", host: "https://custom-api.example.com/v2", expectedOrigin: "https://custom-api.example.com" }, - { label: "host with deep path", host: "https://api.example.com/v2/extra", expectedOrigin: "https://api.example.com" }, - { label: "host with port", host: "https://custom-api.example.com:8080/v2", expectedOrigin: "https://custom-api.example.com:8080" }, - { label: "host with trailing slash", host: "https://api.provable.com/v2/", expectedOrigin: "https://api.provable.com" }, - { label: "localhost with port", host: "http://localhost:3030", expectedOrigin: "http://localhost:3030" }, + { + label: "standard Provable API", + host: "https://api.provable.com/v2", + expectedOrigin: "https://api.provable.com", + }, + { + label: "custom host with path", + host: "https://custom-api.example.com/v2", + expectedOrigin: "https://custom-api.example.com", + }, + { + label: "host with deep path", + host: "https://api.example.com/v2/extra", + expectedOrigin: "https://api.example.com", + }, + { + label: "host with port", + host: "https://custom-api.example.com:8080/v2", + expectedOrigin: "https://custom-api.example.com:8080", + }, + { + label: "host with trailing slash", + host: "https://api.provable.com/v2/", + expectedOrigin: "https://api.provable.com", + }, + { + label: "localhost with port", + host: "http://localhost:3030", + expectedOrigin: "http://localhost:3030", + }, ]; jwtEdgeCases.forEach(({ label, host, expectedOrigin }) => { @@ -801,17 +849,28 @@ describe("AleoNetworkClient JWT refresh URL", () => { fetchStub.resolves({ ok: true, status: 200, - headers: new Headers({ authorization: "Bearer test-jwt-token" }), - json: () => Promise.resolve({ exp: Math.floor(Date.now() / 1000) + 3600 }), + headers: new Headers({ + authorization: "Bearer test-jwt-token", + }), + json: () => + Promise.resolve({ + exp: Math.floor(Date.now() / 1000) + 3600, + }), text: () => Promise.resolve(JSON.stringify({ status: "ok" })), }); try { - await client.submitProvingRequestSafe({ provingRequest: "test-request" }); - } catch { } + await client.submitProvingRequestSafe({ + provingRequest: "test-request", + }); + } catch {} - const firstCallUrl = fetchStub.firstCall.args[0]?.toString() ?? fetchStub.firstCall.args[0]?.url; - expect(firstCallUrl).to.equal(`${expectedOrigin}/jwts/test-consumer-id`); + const firstCallUrl = + fetchStub.firstCall.args[0]?.toString() ?? + fetchStub.firstCall.args[0]?.url; + expect(firstCallUrl).to.equal( + `${expectedOrigin}/jwts/test-consumer-id`, + ); }); }); }); diff --git a/sdk/tests/program-manager.test.ts b/sdk/tests/program-manager.test.ts index 781b561b2..d1174ed56 100644 --- a/sdk/tests/program-manager.test.ts +++ b/sdk/tests/program-manager.test.ts @@ -15,7 +15,7 @@ import { Transaction, verifyFunctionExecution, VerifyingKey, - ViewKey + ViewKey, } from "@provablehq/sdk/%%NETWORK%%.js"; import { beaconAddressString, @@ -26,145 +26,239 @@ import { statePathv0RecordOwnerPrivateKey, stateRootv0, } from "./data/account-data.js"; -import { IMPORT_1, IMPORT_2, MINT_VERIFYING_KEY, PROGRAM, SPEND_VERIFYING_KEY, SPIN_VERIFYING_KEY } from "./data/program.js"; +import { + IMPORT_1, + IMPORT_2, + MINT_VERIFYING_KEY, + PROGRAM, + SPEND_VERIFYING_KEY, + SPIN_VERIFYING_KEY, +} from "./data/program.js"; import { RECORD_PLAINTEXT_V1_STRING } from "./data/records"; import { expect } from "chai"; import { PUZZLE_SPINNER_PROGRAM_ID, PUZZLE_SPINNER_V002_INPUT_0, PUZZLE_SPINNER_V002_INPUT_1, - PUZZLE_SPINNER_V002_INPUT_2 + PUZZLE_SPINNER_V002_INPUT_2, } from "./data/proving.js"; import * as process from "node:process"; import { ProvingResponse } from "../src/models/provingResponse.js"; import { encryptProvingRequest } from "../src/browser"; import sodium from "libsodium-wrappers"; -describe('Program Manager', async () => { +describe("Program Manager", async () => { const keyProvider = new AleoKeyProvider(); keyProvider.useCache(true); - const programManager = new ProgramManager("https://api.provable.com/v2", keyProvider); - programManager.setAccount(new Account({ privateKey: statePathv0RecordOwnerPrivateKey })); + const programManager = new ProgramManager( + "https://api.provable.com/v2", + keyProvider, + ); + programManager.setAccount( + new Account({ privateKey: statePathv0RecordOwnerPrivateKey }), + ); const network = programManager.networkClient.network; - programManager.networkClient.setProverUri("https://accelerate.provable.com"); - describe('Instantiate with AleoNetworkClientOptions', () => { - it('should have the specified headers when instantiated', async () => { - const newProgramManager = new ProgramManager("https://api.provable.com/v2", undefined, undefined, { headers: { 'X-Test-Header': 'programManager' } }); - expect(Object.keys(newProgramManager.networkClient.headers).length).equal(1); - expect(newProgramManager.networkClient.headers['X-Test-Header']).equal('programManager'); - expect(newProgramManager.networkClient.headers['X-Aleo-SDK-Version']).undefined; - }) + programManager.networkClient.setProverUri( + "https://accelerate.provable.com", + ); + describe("Instantiate with AleoNetworkClientOptions", () => { + it("should have the specified headers when instantiated", async () => { + const newProgramManager = new ProgramManager( + "https://api.provable.com/v2", + undefined, + undefined, + { headers: { "X-Test-Header": "programManager" } }, + ); + expect( + Object.keys(newProgramManager.networkClient.headers).length, + ).equal(1); + expect( + newProgramManager.networkClient.headers["X-Test-Header"], + ).equal("programManager"); + expect( + newProgramManager.networkClient.headers["X-Aleo-SDK-Version"], + ).undefined; + }); }); - describe('networkClient header methods', () => { - it('should correctly udpdate the networkClient headers map', async () => { - programManager.setHeader('X-Added-Header', 'programManager'); - expect(programManager.networkClient.headers['X-Added-Header']).equal('programManager'); - }) - - it('should remove header from the networkClient headers map', async () => { - programManager.removeHeader('X-Added-Header'); - expect(programManager.networkClient.headers['X-Added-Header']).undefined; - }) - }) - - describe('Execute offline', () => { - it.skip('Program manager should execute offline and verify the resulting proof correctly', async () => { - const execution_result = <ExecutionResponse>await programManager.run(helloProgram, "hello", ["5u32", "5u32"], true, undefined, undefined, undefined, undefined, undefined, undefined) + describe("networkClient header methods", () => { + it("should correctly udpdate the networkClient headers map", async () => { + programManager.setHeader("X-Added-Header", "programManager"); + expect( + programManager.networkClient.headers["X-Added-Header"], + ).equal("programManager"); + }); + + it("should remove header from the networkClient headers map", async () => { + programManager.removeHeader("X-Added-Header"); + expect(programManager.networkClient.headers["X-Added-Header"]) + .undefined; + }); + }); + + describe("Execute offline", () => { + it.skip("Program manager should execute offline and verify the resulting proof correctly", async () => { + const execution_result = <ExecutionResponse>( + await programManager.run( + helloProgram, + "hello", + ["5u32", "5u32"], + true, + undefined, + undefined, + undefined, + undefined, + undefined, + undefined, + ) + ); expect(execution_result.getOutputs()[0]).equal("10u32"); programManager.verifyExecution(execution_result, 10_300_000); }); }); - describe('Verify execution with multiple imports', () => { - it('Program manager should verify an execution with multiple imports', async () => { + describe("Verify execution with multiple imports", () => { + it("Program manager should verify an execution with multiple imports", async () => { if (network === "mainnet") { // Get the execution, program, and verifying key from the transaction. - const transaction = <Transaction>await programManager.networkClient.getTransactionObject("at1ve39dz2nlm636ewq6g3wl978kmsfqafcvhaj9px4mk28hk855srq06veqh"); + const transaction = <Transaction>( + await programManager.networkClient.getTransactionObject( + "at1ve39dz2nlm636ewq6g3wl978kmsfqafcvhaj9px4mk28hk855srq06veqh", + ) + ); const execution = transaction.execution(); const program = Program.fromString(PROGRAM); - const verifyingKey = VerifyingKey.fromString(SPIN_VERIFYING_KEY); + const verifyingKey = + VerifyingKey.fromString(SPIN_VERIFYING_KEY); // Create the imports and verifying keys for the imported programs. const imports = <ImportedPrograms>{ "puzzle_arcade_coin_v002.aleo": IMPORT_1, - "puzzle_arcade_ticket_v002.aleo": IMPORT_2 + "puzzle_arcade_ticket_v002.aleo": IMPORT_2, }; const importedVerifyingKeys = <ImportedVerifyingKeys>{ - "puzzle_arcade_coin_v002.aleo": [["spend", SPEND_VERIFYING_KEY]], - "puzzle_arcade_ticket_v002.aleo": [["mint", MINT_VERIFYING_KEY]] + "puzzle_arcade_coin_v002.aleo": [ + ["spend", SPEND_VERIFYING_KEY], + ], + "puzzle_arcade_ticket_v002.aleo": [ + ["mint", MINT_VERIFYING_KEY], + ], }; if (!execution) { throw new Error("Execution is undefined"); } else { - const verified = verifyFunctionExecution(execution, verifyingKey, program, "spin", imports, importedVerifyingKeys, 6291400); + const verified = verifyFunctionExecution( + execution, + verifyingKey, + program, + "spin", + imports, + importedVerifyingKeys, + 6291400, + ); expect(verified).equal(true); } } }); }); - describe('Offline query', () => { - it.skip('The offline query should work as expected', async () => { + describe("Offline query", () => { + it.skip("The offline query should work as expected", async () => { const offlineQuery = new OfflineQuery(10_300_000, stateRootv0); - const record_plaintext = RecordPlaintext.fromString(statePathRecordv0); - const pk = PrivateKey.from_string("APrivateKey1zkpAZAjaJJvPS7EJ7zvk5fb3QcZDCDxMSHSN5ap7ep4FAD7"); + const record_plaintext = + RecordPlaintext.fromString(statePathRecordv0); + const pk = PrivateKey.from_string( + "APrivateKey1zkpAZAjaJJvPS7EJ7zvk5fb3QcZDCDxMSHSN5ap7ep4FAD7", + ); const vk = ViewKey.from_private_key(pk); const record_vk = record_plaintext.recordViewKey(vk); - const commitment = record_plaintext.commitment("credits.aleo", "credits", record_vk.toString()).toString(); + const commitment = record_plaintext + .commitment("credits.aleo", "credits", record_vk.toString()) + .toString(); offlineQuery.addStatePath(commitment, recordStatePathv0); - const credits = <string>await programManager.networkClient.getProgram("credits.aleo"); + const credits = <string>( + await programManager.networkClient.getProgram("credits.aleo") + ); - const execution_result = <ExecutionResponse>await programManager.run(credits, "transfer_private", [statePathRecordv0, beaconAddressString, "5u64"], true, undefined, undefined, undefined, undefined, undefined, offlineQuery); - const verified = programManager.verifyExecution(execution_result, 10_300_000); + const execution_result = <ExecutionResponse>( + await programManager.run( + credits, + "transfer_private", + [statePathRecordv0, beaconAddressString, "5u64"], + true, + undefined, + undefined, + undefined, + undefined, + undefined, + offlineQuery, + ) + ); + const verified = programManager.verifyExecution( + execution_result, + 10_300_000, + ); expect(verified).equal(true); }); }); - describe('ProgramManager Executions', () => { - it.skip('Should create a split transaction without fees', async function() { + describe("ProgramManager Executions", () => { + it.skip("Should create a split transaction without fees", async function () { this.retries(3); if (network === "testnet") { const keyPair = await programManager.keyProvider.splitKeys(); - programManager.keyProvider.cacheKeys("credits.aleo/split", keyPair); - const pk = PrivateKey.from_string(<string>process.env["PUZZLE_PK"]); + programManager.keyProvider.cacheKeys( + "credits.aleo/split", + keyPair, + ); + const pk = PrivateKey.from_string( + <string>process.env["PUZZLE_PK"], + ); - const tx = <Transaction>await programManager.buildExecutionTransaction({ - programName: "credits.aleo", - functionName: "split", - priorityFee: 0.0, - privateFee: false, - inputs: [statePathRecordv1, "10u64"], - keySearchParams: { "cacheKey": CREDITS_PROGRAM_KEYS.split.locator }, - privateKey: pk - }); + const tx = <Transaction>( + await programManager.buildExecutionTransaction({ + programName: "credits.aleo", + functionName: "split", + priorityFee: 0.0, + privateFee: false, + inputs: [statePathRecordv1, "10u64"], + keySearchParams: { + cacheKey: CREDITS_PROGRAM_KEYS.split.locator, + }, + privateKey: pk, + }) + ); expect(tx.feeAmount()).equal(BigInt(0)); } }); - it('Should create a split ProvingRequest without fees', async function() { + it("Should create a split ProvingRequest without fees", async function () { this.retries(3); if (network === "testnet") { - const pk = PrivateKey.from_string(<string>process.env["PUZZLE_PK"]); + const pk = PrivateKey.from_string( + <string>process.env["PUZZLE_PK"], + ); - const request = <ProvingRequest>await programManager.provingRequest({ - programName: "credits.aleo", - functionName: "split", - priorityFee: 0.0, - privateFee: false, - inputs: [statePathRecordv1, "10u64"], - broadcast: false, - privateKey: pk - }); + const request = <ProvingRequest>( + await programManager.provingRequest({ + programName: "credits.aleo", + functionName: "split", + priorityFee: 0.0, + privateFee: false, + inputs: [statePathRecordv1, "10u64"], + broadcast: false, + privateKey: pk, + }) + ); expect(request.feeAuthorization()).equal(undefined); } }); - it('Should execute an encrypted proving request', async function() { + it("Should execute an encrypted proving request", async function () { this.retries(3); if (network === "testnet") { @@ -224,7 +318,9 @@ describe('Program Manager', async () => { if (safeResult.ok) { expect(safeResult.data).to.have.property("transaction"); expect(safeResult.data.transaction).to.have.property("id"); - expect(safeResult.data).to.have.property("broadcast_result"); + expect(safeResult.data).to.have.property( + "broadcast_result", + ); expect(safeResult.data.broadcast_result.status).to.equal( "Skipped", ); @@ -232,12 +328,10 @@ describe('Program Manager', async () => { // Submit the proving request using the regular method. const result = - await programManager.networkClient.submitProvingRequest( - { - provingRequest, - dpsPrivacy: true, - }, - ); + await programManager.networkClient.submitProvingRequest({ + provingRequest, + dpsPrivacy: true, + }); expect(result).to.have.property("transaction"); expect(result).to.have.property("broadcast_result"); @@ -246,8 +340,8 @@ describe('Program Manager', async () => { }); }); - describe('Proving Requests and Authorizations', () => { - it('Should build correct authorizations from Proving Request', async () => { + describe("Proving Requests and Authorizations", () => { + it("Should build correct authorizations from Proving Request", async () => { // Build a proving request for the "spin" function of "puzzle_spinner_v002.aleo". const provingRequest = await programManager.provingRequest({ programName: PUZZLE_SPINNER_PROGRAM_ID, @@ -260,12 +354,18 @@ describe('Program Manager', async () => { PUZZLE_SPINNER_V002_INPUT_2, ], broadcast: false, - privateKey: PrivateKey.from_string(<string>process.env["PUZZLE_PK"]) + privateKey: PrivateKey.from_string( + <string>process.env["PUZZLE_PK"], + ), }); // Ensure serialization methods lead to the expected. - const provingRequestFromString = ProvingRequest.fromString(provingRequest.toString()); - const provingRequestFromBytes = ProvingRequest.fromBytesLe(provingRequest.toBytesLe()); + const provingRequestFromString = ProvingRequest.fromString( + provingRequest.toString(), + ); + const provingRequestFromBytes = ProvingRequest.fromBytesLe( + provingRequest.toBytesLe(), + ); // Ensure all authorizations are equal. expect(provingRequestFromString.equals(provingRequestFromBytes)); @@ -276,14 +376,16 @@ describe('Program Manager', async () => { // Get the authorizations. const authorization = provingRequest.authorization(); - const feeAuthorization = <Authorization>provingRequest.feeAuthorization(); + const feeAuthorization = <Authorization>( + provingRequest.feeAuthorization() + ); // Ensure the authorizations have the correct number of transitions. expect(authorization.transitions().length).equal(3); expect(feeAuthorization.transitions().length).equal(1); }); - it('Should build proving request without fee authorization when useFeeMaster is set', async () => { + it("Should build proving request without fee authorization when useFeeMaster is set", async () => { const provingRequest = await programManager.provingRequest({ programName: PUZZLE_SPINNER_PROGRAM_ID, functionName: "spin", @@ -296,7 +398,9 @@ describe('Program Manager', async () => { ], broadcast: false, useFeeMaster: true, - privateKey: PrivateKey.from_string(<string>process.env["PUZZLE_PK"]) + privateKey: PrivateKey.from_string( + <string>process.env["PUZZLE_PK"], + ), }); expect(provingRequest.feeAuthorization()).equal(undefined); @@ -305,7 +409,7 @@ describe('Program Manager', async () => { expect(authorization.transitions().length).equal(3); }); - it('Should build correct authorizations', async () => { + it("Should build correct authorizations", async () => { // Build an authorization for the spin function of "puzzle_spinner_v002.aleo". const authorization = await programManager.buildAuthorization({ programName: PUZZLE_SPINNER_PROGRAM_ID, @@ -315,119 +419,138 @@ describe('Program Manager', async () => { PUZZLE_SPINNER_V002_INPUT_1, PUZZLE_SPINNER_V002_INPUT_2, ], - privateKey: PrivateKey.from_string(<string>process.env["PUZZLE_PK"]) + privateKey: PrivateKey.from_string( + <string>process.env["PUZZLE_PK"], + ), }); // Ensure serialization methods lead to the expected. - const authorizationFromString = Authorization.fromString(authorization.toString()); - const authorizationFromBytes = Authorization.fromBytesLe(authorization.toBytesLe()); + const authorizationFromString = Authorization.fromString( + authorization.toString(), + ); + const authorizationFromBytes = Authorization.fromBytesLe( + authorization.toBytesLe(), + ); // Ensure all authorizations are equal. expect(authorizationFromString.equals(authorizationFromBytes)); expect(authorizationFromString.equals(authorization)); - const baseFeeMicrocredits = await programManager.estimateFeeForAuthorization({ - authorization, - programName: PUZZLE_SPINNER_PROGRAM_ID, - }); + const baseFeeMicrocredits = + await programManager.estimateFeeForAuthorization({ + authorization, + programName: PUZZLE_SPINNER_PROGRAM_ID, + }); const baseFeeCredits = Number(baseFeeMicrocredits) / 1000000; // Get execution ID from previous authorization. const executionId = authorization.toExecutionId().toString(); - const feeAuthorization = await programManager.buildFeeAuthorization({ - deploymentOrExecutionId: executionId, - baseFeeCredits, - }); + const feeAuthorization = await programManager.buildFeeAuthorization( + { + deploymentOrExecutionId: executionId, + baseFeeCredits, + }, + ); // Ensure the authorizations have the correct number of transitions. expect(authorization.transitions().length).equal(3); expect(feeAuthorization.transitions().length).equal(1); }); - it('Should build correct authorizations when using the unchecked version', async () => { + it("Should build correct authorizations when using the unchecked version", async () => { // Build an authorization for the spin function of "puzzle_spinner_v002.aleo". if (network === "mainnet") { - const authorization = await programManager.buildAuthorizationUnchecked({ - programName: PUZZLE_SPINNER_PROGRAM_ID, - functionName: "spin", - inputs: [ - PUZZLE_SPINNER_V002_INPUT_0, - PUZZLE_SPINNER_V002_INPUT_1, - PUZZLE_SPINNER_V002_INPUT_2, - ], - privateKey: PrivateKey.from_string(<string>process.env["PUZZLE_PK"]) - }); + const authorization = + await programManager.buildAuthorizationUnchecked({ + programName: PUZZLE_SPINNER_PROGRAM_ID, + functionName: "spin", + inputs: [ + PUZZLE_SPINNER_V002_INPUT_0, + PUZZLE_SPINNER_V002_INPUT_1, + PUZZLE_SPINNER_V002_INPUT_2, + ], + privateKey: PrivateKey.from_string( + <string>process.env["PUZZLE_PK"], + ), + }); // Ensure serialization methods lead to the expected. - const authorizationFromString = Authorization.fromString(authorization.toString()); - const authorizationFromBytes = Authorization.fromBytesLe(authorization.toBytesLe()); + const authorizationFromString = Authorization.fromString( + authorization.toString(), + ); + const authorizationFromBytes = Authorization.fromBytesLe( + authorization.toBytesLe(), + ); // Ensure all authorizations are equal. expect(authorizationFromString.equals(authorizationFromBytes)); expect(authorizationFromString.equals(authorization)); - const baseFeeMicrocredits = await programManager.estimateFeeForAuthorization({ - authorization, - programName: PUZZLE_SPINNER_PROGRAM_ID, - }); + const baseFeeMicrocredits = + await programManager.estimateFeeForAuthorization({ + authorization, + programName: PUZZLE_SPINNER_PROGRAM_ID, + }); const baseFeeCredits = Number(baseFeeMicrocredits) / 1000000; // Get execution ID from previous authorization. const executionId = authorization.toExecutionId().toString(); - const feeAuthorization = await programManager.buildFeeAuthorization({ - deploymentOrExecutionId: executionId, - baseFeeCredits, - }); + const feeAuthorization = + await programManager.buildFeeAuthorization({ + deploymentOrExecutionId: executionId, + baseFeeCredits, + }); // Ensure the authorizations have the correct number of transitions. expect(authorization.transitions().length).equal(3); expect(feeAuthorization.transitions().length).equal(1); } - }); - it('Should estimate the fee correctly for credits.aleo functions', async () => { - const transferFeeMicrocredits = await programManager.estimateExecutionFee({ - programName: "credits.aleo", - functionName: "transfer_public" - }); + it("Should estimate the fee correctly for credits.aleo functions", async () => { + const transferFeeMicrocredits = + await programManager.estimateExecutionFee({ + programName: "credits.aleo", + functionName: "transfer_public", + }); - const bondFeeMicrocredits = await programManager.estimateExecutionFee({ - programName: "credits.aleo", - functionName: "transfer_public_to_private" - }); + const bondFeeMicrocredits = + await programManager.estimateExecutionFee({ + programName: "credits.aleo", + functionName: "transfer_public_to_private", + }); expect(transferFeeMicrocredits).equal(BigInt(2725)); expect(bondFeeMicrocredits).equal(BigInt(2304)); - }); }); - describe('Program Manager record retrieval', () => { - it('Should return records without searching for them if records are passed', async () => { + describe("Program Manager record retrieval", () => { + it("Should return records without searching for them if records are passed", async () => { const retrievedRecord1 = await programManager.getCreditsRecord( 50000, [], RECORD_PLAINTEXT_V1_STRING, ); - expect(RECORD_PLAINTEXT_V1_STRING).equal(retrievedRecord1.toString()) + expect(RECORD_PLAINTEXT_V1_STRING).equal( + retrievedRecord1.toString(), + ); const retrievedRecord2 = await programManager.getCreditsRecord( 50000, [], - RecordPlaintext.fromString(RECORD_PLAINTEXT_V1_STRING) + RecordPlaintext.fromString(RECORD_PLAINTEXT_V1_STRING), + ); + expect(RECORD_PLAINTEXT_V1_STRING).equal( + retrievedRecord2.toString(), ); - expect(RECORD_PLAINTEXT_V1_STRING).equal(retrievedRecord2.toString()); }); - it('Should search for a record if none is specified', async () => { + it("Should search for a record if none is specified", async () => { try { - await programManager.getCreditsRecord( - 50000, - [], - ); + await programManager.getCreditsRecord(50000, []); } catch (e) { expect(`${e}`).contain("findCreditsRecord"); } }); }); -}); \ No newline at end of file +}); diff --git a/sdk/tests/record-provider.integration.ts b/sdk/tests/record-provider.integration.ts index 53c864e0f..5f9a1417d 100644 --- a/sdk/tests/record-provider.integration.ts +++ b/sdk/tests/record-provider.integration.ts @@ -1,36 +1,47 @@ import { expect } from "chai"; import { beaconPrivateKeyString } from "./data/account-data.js"; -import { Account, AleoNetworkClient, NetworkRecordProvider, RecordPlaintext } from "../src/node.js"; +import { + Account, + AleoNetworkClient, + NetworkRecordProvider, + RecordPlaintext, +} from "../src/node.js"; -describe('RecordProvider', () => { +describe("RecordProvider", () => { let account: Account; let networkClient: AleoNetworkClient; let recordProvider: NetworkRecordProvider; beforeEach(() => { - account = new Account({privateKey: beaconPrivateKeyString}); + account = new Account({ privateKey: beaconPrivateKeyString }); networkClient = new AleoNetworkClient("http://0.0.0.0:3030"); recordProvider = new NetworkRecordProvider(account, networkClient); }); - describe('Record provider', () => { - it('should find credits records', async () => { + describe("Record provider", () => { + it("should find credits records", async () => { try { // Find two records with findCreditsRecords const nonces: string[] = []; - const records = await recordProvider.findCreditsRecords([100, 200], { unspent: true, nonces }); + const records = await recordProvider.findCreditsRecords( + [100, 200], + { unspent: true, nonces }, + ); if (Array.isArray(records)) { expect(records.length).equal(2); records.forEach((record) => { - let pt = new RecordPlaintext(record.record_plaintext); - nonces.push(pt.nonce()); + let pt = new RecordPlaintext(record.record_plaintext); + nonces.push(pt.nonce()); }); } else { expect(Array.isArray(records)).equal(true); } // Get another two records with findCreditsRecords and ensure they are unique - const records2 = await recordProvider.findCreditsRecords([100, 200], { unspent: true, nonces }); + const records2 = await recordProvider.findCreditsRecords( + [100, 200], + { unspent: true, nonces }, + ); if (Array.isArray(records2)) { expect(records2.length).equal(2); records2.forEach((record) => { diff --git a/sdk/tests/record-provider.test.ts b/sdk/tests/record-provider.test.ts index ef72e4247..a1ec54a84 100644 --- a/sdk/tests/record-provider.test.ts +++ b/sdk/tests/record-provider.test.ts @@ -1,22 +1,30 @@ import { expect } from "chai"; -import {Account, AleoNetworkClient, BlockHeightSearch, NetworkRecordProvider} from "../src/node.js"; -import {beaconPrivateKeyString} from "./data/account-data.js"; +import { + Account, + AleoNetworkClient, + BlockHeightSearch, + NetworkRecordProvider, +} from "../src/node.js"; +import { beaconPrivateKeyString } from "./data/account-data.js"; -describe.skip('RecordProvider', () => { +describe.skip("RecordProvider", () => { let account: Account; let networkClient: AleoNetworkClient; let recordProvider: NetworkRecordProvider; beforeEach(() => { - account = new Account({privateKey: beaconPrivateKeyString}); + account = new Account({ privateKey: beaconPrivateKeyString }); networkClient = new AleoNetworkClient("https://api.provable.com/v2"); recordProvider = new NetworkRecordProvider(account, networkClient); }); - describe('Record provider', () => { - it('should not find records where there are none', async () => { + describe("Record provider", () => { + it("should not find records where there are none", async () => { const params = new BlockHeightSearch(0, 100); - const records = await recordProvider.findCreditsRecords([100, 200], params); + const records = await recordProvider.findCreditsRecords( + [100, 200], + params, + ); expect(<object>records).equal([]); }); }); diff --git a/sdk/tests/record-scanner-integration.spec.ts b/sdk/tests/record-scanner-integration.spec.ts index dff5218d8..564a745df 100644 --- a/sdk/tests/record-scanner-integration.spec.ts +++ b/sdk/tests/record-scanner-integration.spec.ts @@ -13,7 +13,9 @@ describe("RecordScanner", () => { const result = await recordScanner.register(viewKey, 0); expect(result.ok).to.equal(true); if (result.ok) { - expect(result.data.uuid).to.equal(recordScanner.computeUUID(viewKey).toString()); + expect(result.data.uuid).to.equal( + recordScanner.computeUUID(viewKey).toString(), + ); } }); @@ -30,7 +32,7 @@ describe("RecordScanner", () => { program_name: true, record_name: true, commitment: true, - } + }, }); expect(response.length).to.equal(9); for (const record of response) { @@ -104,8 +106,10 @@ describe("RecordScanner", () => { "1050894655374138905808887909092891940183499902306462627909572997011712750387field", ]); expect(response).to.deep.equal({ - "2497968624879919117393326048350070098671407363450098197552864797993755823036field": false, - "1050894655374138905808887909092891940183499902306462627909572997011712750387field": false, + "2497968624879919117393326048350070098671407363450098197552864797993755823036field": + false, + "1050894655374138905808887909092891940183499902306462627909572997011712750387field": + false, }); }); @@ -115,8 +119,10 @@ describe("RecordScanner", () => { "448505083045691117285710413252063292683250969684463991322463606849073525242field", ]); expect(response).to.deep.equal({ - "2726311268578079710210289900019159614843633435399431654197596897028642765098field": false, - "448505083045691117285710413252063292683250969684463991322463606849073525242field": false, + "2726311268578079710210289900019159614843633435399431654197596897028642765098field": + false, + "448505083045691117285710413252063292683250969684463991322463606849073525242field": + false, }); }); @@ -127,5 +133,5 @@ describe("RecordScanner", () => { expect(response.data.synced).to.be.instanceOf(Boolean); expect(response.data.percentage).to.be.instanceOf(Number); } - }) -}); \ No newline at end of file + }); +}); diff --git a/sdk/tests/record-scanner.test.ts b/sdk/tests/record-scanner.test.ts index 8b1823b80..024379020 100644 --- a/sdk/tests/record-scanner.test.ts +++ b/sdk/tests/record-scanner.test.ts @@ -1,5 +1,10 @@ import { Account } from "../src/account"; -import { CHECK_SNS_RESPONSE, CHECK_TAGS_RESPONSE, ENCRYPTED_RECORDS, OWNED_RECORDS } from "./data/records"; +import { + CHECK_SNS_RESPONSE, + CHECK_TAGS_RESPONSE, + ENCRYPTED_RECORDS, + OWNED_RECORDS, +} from "./data/records"; import { expect } from "chai"; import sodium from "libsodium-wrappers"; import { @@ -24,14 +29,17 @@ const apiKey = process.env.KONG_API_KEY as string; const consumerId = process.env.CONSUMER_ID as string | undefined; describe("RecordScanner", () => { - const defaultAccount = new Account({ privateKey: "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH" }); + const defaultAccount = new Account({ + privateKey: + "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH", + }); let recordScanner: RecordScanner; let fetchStub: sinon.SinonStub; const baseUrl = "https://record-scanner.aleo.org"; const network = "/%%NETWORK%%"; beforeEach(() => { - fetchStub = sinon.stub(globalThis, 'fetch'); + fetchStub = sinon.stub(globalThis, "fetch"); }); afterEach(() => { @@ -43,16 +51,21 @@ describe("RecordScanner", () => { expect(recordScanner.url).to.equal(baseUrl + network); }); - const validTestUuid = "7884164224800444110633570141944665301008802280502652120359195870264061098703field"; + const validTestUuid = + "7884164224800444110633570141944665301008802280502652120359195870264061098703field"; it("should intialize with the correct api key as a string", async () => { - recordScanner = new RecordScanner({ url: baseUrl, apiKey: "1234567890" }); - + recordScanner = new RecordScanner({ + url: baseUrl, + apiKey: "1234567890", + }); + const mockResponse = { ok: true, status: 201, - text: () => Promise.resolve(JSON.stringify({ uuid: validTestUuid })), - json: () => Promise.resolve({ uuid: validTestUuid }) + text: () => + Promise.resolve(JSON.stringify({ uuid: validTestUuid })), + json: () => Promise.resolve({ uuid: validTestUuid }), }; const revokeResponse = { ok: true, @@ -63,9 +76,11 @@ describe("RecordScanner", () => { fetchStub.onCall(0).resolves(mockResponse); fetchStub.onCall(1).resolves(revokeResponse); await recordScanner.register(defaultAccount.viewKey(), 0); - + const request = fetchStub.firstCall.args[0] as Request; - expect(request.headers.get("X-Provable-API-Key")).to.equal("1234567890"); + expect(request.headers.get("X-Provable-API-Key")).to.equal( + "1234567890", + ); const revokeResult = await recordScanner.revoke(validTestUuid); expect(revokeResult.ok).to.equal(true); @@ -76,13 +91,17 @@ describe("RecordScanner", () => { }); it("should intialize with the correct api key as an object", async () => { - recordScanner = new RecordScanner({ url: baseUrl, apiKey: { header: "Some-API-Key", value: "1234567890" } }); - + recordScanner = new RecordScanner({ + url: baseUrl, + apiKey: { header: "Some-API-Key", value: "1234567890" }, + }); + const mockResponse = { ok: true, status: 201, - text: () => Promise.resolve(JSON.stringify({ uuid: validTestUuid })), - json: () => Promise.resolve({ uuid: validTestUuid }) + text: () => + Promise.resolve(JSON.stringify({ uuid: validTestUuid })), + json: () => Promise.resolve({ uuid: validTestUuid }), }; const revokeResponse = { ok: true, @@ -93,7 +112,7 @@ describe("RecordScanner", () => { fetchStub.onCall(0).resolves(mockResponse); fetchStub.onCall(1).resolves(revokeResponse); await recordScanner.register(defaultAccount.viewKey(), 0); - + const request = fetchStub.firstCall.args[0] as Request; expect(request.headers.get("Some-API-Key")).to.equal("1234567890"); @@ -103,12 +122,13 @@ describe("RecordScanner", () => { it("should return RegisterResult with data after successfully registering the account", async () => { recordScanner = new RecordScanner({ url: baseUrl }); - + const mockResponse = { ok: true, status: 201, - text: () => Promise.resolve(JSON.stringify({ uuid: validTestUuid })), - json: () => Promise.resolve({ uuid: validTestUuid }) + text: () => + Promise.resolve(JSON.stringify({ uuid: validTestUuid })), + json: () => Promise.resolve({ uuid: validTestUuid }), }; const revokeResponse = { ok: true, @@ -118,22 +138,32 @@ describe("RecordScanner", () => { }; fetchStub.onCall(0).resolves(mockResponse); fetchStub.onCall(1).resolves(revokeResponse); - const result = await recordScanner.register(defaultAccount.viewKey(), 0); - + const result = await recordScanner.register( + defaultAccount.viewKey(), + 0, + ); + expect(fetchStub.called).to.be.true; const request = fetchStub.firstCall.args[0] as Request; expect(request.url).to.equal(recordScanner.url + "/register"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); - + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); + const body = await request.text(); - const expectedBody = JSON.stringify({ view_key: defaultAccount.viewKey().to_string(), start: 0 }); + const expectedBody = JSON.stringify({ + view_key: defaultAccount.viewKey().to_string(), + start: 0, + }); expect(body).to.equal(expectedBody); - + expect(result.ok).to.equal(true); if (result.ok) expect(result.data.uuid).equal(validTestUuid); - const revokeResult = await recordScanner.revoke(result.ok ? result.data.uuid : undefined); + const revokeResult = await recordScanner.revoke( + result.ok ? result.data.uuid : undefined, + ); expect(revokeResult.ok).to.equal(true); const revokeRequest = fetchStub.secondCall.args[0] as Request; expect(revokeRequest.url).to.equal(recordScanner.url + "/revoke"); @@ -142,12 +172,16 @@ describe("RecordScanner", () => { it("should return the optional fields of RegistrationResponse if present after successfully registering the account", async () => { recordScanner = new RecordScanner({ url: baseUrl }); - + const mockResponse = { ok: true, status: 201, - text: () => Promise.resolve(JSON.stringify({ uuid: validTestUuid, status: "pending" })), - json: () => Promise.resolve({ uuid: validTestUuid, status: "pending" }) + text: () => + Promise.resolve( + JSON.stringify({ uuid: validTestUuid, status: "pending" }), + ), + json: () => + Promise.resolve({ uuid: validTestUuid, status: "pending" }), }; const revokeResponse = { ok: true, @@ -157,18 +191,26 @@ describe("RecordScanner", () => { }; fetchStub.onCall(0).resolves(mockResponse); fetchStub.onCall(1).resolves(revokeResponse); - const result = await recordScanner.register(defaultAccount.viewKey(), 0); + const result = await recordScanner.register( + defaultAccount.viewKey(), + 0, + ); expect(fetchStub.called).to.be.true; const request = fetchStub.firstCall.args[0] as Request; expect(request.url).to.equal(recordScanner.url + "/register"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); - + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); + const body = await request.text(); - const expectedBody = JSON.stringify({ view_key: defaultAccount.viewKey().to_string(), start: 0 }); + const expectedBody = JSON.stringify({ + view_key: defaultAccount.viewKey().to_string(), + start: 0, + }); expect(body).to.equal(expectedBody); - + expect(result.ok).to.equal(true); if (result.ok) { expect(result.data.uuid).equal(validTestUuid); @@ -230,7 +272,6 @@ describe("RecordScanner", () => { expect(fetchStub.called).to.be.false; }); - it("should return EncryptedRecord[] after successfully getting encrypted records", async () => { recordScanner = new RecordScanner({ url: baseUrl }); const mockResponse = { @@ -239,7 +280,7 @@ describe("RecordScanner", () => { text: () => Promise.resolve(JSON.stringify(ENCRYPTED_RECORDS)), json: () => Promise.resolve(ENCRYPTED_RECORDS), }; - + fetchStub.resolves(mockResponse); const responseFilter: RecordsResponseFilter = { commitment: true, @@ -265,14 +306,16 @@ describe("RecordScanner", () => { }; const encryptedRecords = await recordScanner.encryptedRecords(filter); expect(encryptedRecords).to.equal(ENCRYPTED_RECORDS); - + const request = fetchStub.firstCall.args[0] as Request; const body = await request.text(); const expectedBody = JSON.stringify(filter); expect(body).to.equal(expectedBody); expect(request.url).to.equal(recordScanner.url + "/records/encrypted"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); }); it("should return OwnedRecord[] after successfully getting owned records", async () => { @@ -320,7 +363,9 @@ describe("RecordScanner", () => { expect(body).to.equal(expectedBody); expect(request.url).to.equal(recordScanner.url + "/records/owned"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); }); it("should return OwnedRecord after successfully getting owned record", async () => { @@ -347,7 +392,9 @@ describe("RecordScanner", () => { expect(body).to.equal(expectedBody); expect(request.url).to.equal(recordScanner.url + "/records/owned"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); }); it("should throw UUIDError if the uuid is not set on scanner and filter uuid is invalid", async () => { @@ -400,7 +447,9 @@ describe("RecordScanner", () => { expect(body).to.equal(expectedBody); expect(request.url).to.equal(recordScanner.url + "/records/sns"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); }); it("should return record of string->boolean after successfully checking tags", async () => { @@ -429,7 +478,9 @@ describe("RecordScanner", () => { expect(body).to.equal(expectedBody); expect(request.url).to.equal(recordScanner.url + "/records/tags"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); }); it("should return StatusResponse after successfully checking status", async () => { @@ -437,7 +488,10 @@ describe("RecordScanner", () => { const mockResponse = { ok: true, status: 200, - text: () => Promise.resolve(JSON.stringify({ synced: true, percentage: 100 })), + text: () => + Promise.resolve( + JSON.stringify({ synced: true, percentage: 100 }), + ), json: () => Promise.resolve({ synced: true, percentage: 100 }), }; recordScanner.setUuid(defaultAccount.viewKey()); @@ -445,16 +499,23 @@ describe("RecordScanner", () => { const statusResponse = await recordScanner.status(); expect(statusResponse.ok).to.equal(true); if (statusResponse.ok) { - expect(statusResponse.data).to.deep.equal({ synced: true, percentage: 100 }); + expect(statusResponse.data).to.deep.equal({ + synced: true, + percentage: 100, + }); } const request = fetchStub.firstCall.args[0] as Request; const body = await request.text(); - const expectedBody = JSON.stringify(recordScanner.computeUUID(defaultAccount.viewKey()).toString()); + const expectedBody = JSON.stringify( + recordScanner.computeUUID(defaultAccount.viewKey()).toString(), + ); expect(body).to.equal(expectedBody); expect(request.url).to.equal(recordScanner.url + "/status"); expect(request.method).to.equal("POST"); - expect(request.headers.get("Content-Type")).to.equal("application/json"); + expect(request.headers.get("Content-Type")).to.equal( + "application/json", + ); }); it("should handle HTTP errors", async () => { @@ -470,7 +531,9 @@ describe("RecordScanner", () => { expect(result.ok).to.equal(false); if (!result.ok) { expect(result.status).to.equal(500); - expect(result.error.message).to.equal('{"error": "Internal server error"}'); + expect(result.error.message).to.equal( + '{"error": "Internal server error"}', + ); } mockResponse = { @@ -484,7 +547,9 @@ describe("RecordScanner", () => { expect(result.ok).to.equal(false); if (!result.ok) { expect(result.status).to.equal(422); - expect(result.error.message).to.equal('{"error": "Invalid view key"}'); + expect(result.error.message).to.equal( + '{"error": "Invalid view key"}', + ); } fetchStub.rejects(new Error("Unknown error")); @@ -503,13 +568,19 @@ describe("RecordScanner", () => { it("registerEncrypted returns ok: false with status 422 when server returns 422", async () => { await sodium.ready; const keyPair = sodium.crypto_box_keypair(); - const pubKeyB64 = sodium.to_base64(keyPair.publicKey, sodium.base64_variants.ORIGINAL); + const pubKeyB64 = sodium.to_base64( + keyPair.publicKey, + sodium.base64_variants.ORIGINAL, + ); recordScanner = new RecordScanner({ url: baseUrl }); fetchStub.onCall(0).resolves({ ok: true, text: () => Promise.resolve( - JSON.stringify({ key_id: "test-key-id", public_key: pubKeyB64 }), + JSON.stringify({ + key_id: "test-key-id", + public_key: pubKeyB64, + }), ), }); fetchStub.onCall(1).resolves({ @@ -525,47 +596,82 @@ describe("RecordScanner", () => { expect(result.ok).to.equal(false); if (!result.ok) { expect(result.status).to.equal(422); - expect(result.error.message).to.include("Encrypted registration failed"); + expect(result.error.message).to.include( + "Encrypted registration failed", + ); } }); it("registerEncrypted returns ok: false with status 500 when server returns 500", async () => { await sodium.ready; const keyPair = sodium.crypto_box_keypair(); - const pubKeyB64 = sodium.to_base64(keyPair.publicKey, sodium.base64_variants.ORIGINAL); + const pubKeyB64 = sodium.to_base64( + keyPair.publicKey, + sodium.base64_variants.ORIGINAL, + ); recordScanner = new RecordScanner({ url: baseUrl }); fetchStub.onCall(0).resolves({ ok: true, text: () => Promise.resolve( - JSON.stringify({ key_id: "test-key-id", public_key: pubKeyB64 }), + JSON.stringify({ + key_id: "test-key-id", + public_key: pubKeyB64, + }), ), }); fetchStub.onCall(1).resolves({ ok: false, status: 500, text: () => - Promise.resolve("Something went wrong: Unable to register view key."), + Promise.resolve( + "Something went wrong: Unable to register view key.", + ), }); const viewKey = defaultAccount.viewKey(); const result = await recordScanner.registerEncrypted(viewKey, 0); expect(result.ok).to.equal(false); if (!result.ok) { expect(result.status).to.equal(500); - expect(result.error.message).to.include("Unable to register view key"); + expect(result.error.message).to.include( + "Unable to register view key", + ); } }); - }); describe("JWT refresh URL", () => { const jwtEdgeCases = [ - { label: "standard scanner URL", url: "https://record-scanner.aleo.org", expectedOrigin: "https://record-scanner.aleo.org" }, - { label: "Provable API with /scanner path", url: "https://api.provable.com/scanner", expectedOrigin: "https://api.provable.com" }, - { label: "URL with deep path", url: "https://api.example.com/v2/extra", expectedOrigin: "https://api.example.com" }, - { label: "URL with port", url: "https://custom-api.example.com:8080/scanner", expectedOrigin: "https://custom-api.example.com:8080" }, - { label: "URL with trailing slash", url: "https://api.provable.com/scanner/", expectedOrigin: "https://api.provable.com" }, - { label: "localhost with port", url: "http://localhost:3030", expectedOrigin: "http://localhost:3030" }, + { + label: "standard scanner URL", + url: "https://record-scanner.aleo.org", + expectedOrigin: "https://record-scanner.aleo.org", + }, + { + label: "Provable API with /scanner path", + url: "https://api.provable.com/scanner", + expectedOrigin: "https://api.provable.com", + }, + { + label: "URL with deep path", + url: "https://api.example.com/v2/extra", + expectedOrigin: "https://api.example.com", + }, + { + label: "URL with port", + url: "https://custom-api.example.com:8080/scanner", + expectedOrigin: "https://custom-api.example.com:8080", + }, + { + label: "URL with trailing slash", + url: "https://api.provable.com/scanner/", + expectedOrigin: "https://api.provable.com", + }, + { + label: "localhost with port", + url: "http://localhost:3030", + expectedOrigin: "http://localhost:3030", + }, ]; jwtEdgeCases.forEach(({ label, url, expectedOrigin }) => { @@ -579,17 +685,33 @@ describe("RecordScanner", () => { fetchStub.resolves({ ok: true, status: 200, - headers: new Headers({ authorization: "Bearer test-jwt-token" }), - json: () => Promise.resolve({ exp: Math.floor(Date.now() / 1000) + 3600 }), - text: () => Promise.resolve(JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 3600 })), + headers: new Headers({ + authorization: "Bearer test-jwt-token", + }), + json: () => + Promise.resolve({ + exp: Math.floor(Date.now() / 1000) + 3600, + }), + text: () => + Promise.resolve( + JSON.stringify({ + exp: Math.floor(Date.now() / 1000) + 3600, + }), + ), }); try { - await recordScanner.status("7884164224800444110633570141944665301008802280502652120359195870264061098703field"); - } catch { } - - const firstCallUrl = fetchStub.firstCall.args[0]?.toString() ?? fetchStub.firstCall.args[0]?.url; - expect(firstCallUrl).to.equal(`${expectedOrigin}/jwts/test-consumer-id`); + await recordScanner.status( + "7884164224800444110633570141944665301008802280502652120359195870264061098703field", + ); + } catch {} + + const firstCallUrl = + fetchStub.firstCall.args[0]?.toString() ?? + fetchStub.firstCall.args[0]?.url; + expect(firstCallUrl).to.equal( + `${expectedOrigin}/jwts/test-consumer-id`, + ); }); }); }); @@ -618,7 +740,10 @@ describe("Record scanner encrypted registration", function () { const keyPair = sodium.crypto_box_keypair(); const viewKey = new Account().viewKey(); const ciphertext = encryptRegistrationRequest( - sodium.to_base64(keyPair.publicKey, sodium.base64_variants.ORIGINAL), + sodium.to_base64( + keyPair.publicKey, + sodium.base64_variants.ORIGINAL, + ), viewKey, startBlock, ); @@ -629,7 +754,10 @@ describe("Record scanner encrypted registration", function () { ); const vkBytes = viewKey.toBytesLe(); expect(plaintextBytes.length).to.equal(vkBytes.length + 4); - const decodedStart = new DataView(plaintextBytes.buffer).getUint32(plaintextBytes.length - 4, true); + const decodedStart = new DataView(plaintextBytes.buffer).getUint32( + plaintextBytes.length - 4, + true, + ); expect(decodedStart).to.equal(startBlock); // Register via encrypted flow against sandbox. @@ -638,11 +766,16 @@ describe("Record scanner encrypted registration", function () { ...(apiKey && { apiKey }), ...(consumerId && { consumerId }), }); - const result = await recordScanner.registerEncrypted(viewKey, startBlock); + const result = await recordScanner.registerEncrypted( + viewKey, + startBlock, + ); expect(result.ok).to.equal(true); if (result.ok) { expect(result.data).to.have.property("uuid"); - expect(result.data.uuid).to.equal(recordScanner.computeUUID(viewKey).toString()); + expect(result.data.uuid).to.equal( + recordScanner.computeUUID(viewKey).toString(), + ); await new Promise((r) => setTimeout(r, 5000)); const revokeResult = await recordScanner.revoke(result.data.uuid); expect(revokeResult.ok).to.equal(true); @@ -736,7 +869,9 @@ describe("RecordScanner (real API)", function () { ...(apiKey && { apiKey }), ...(consumerId && { consumerId }), }); - const uuid = scanner.computeUUID(accountFromEnv.viewKey()).toString(); + const uuid = scanner + .computeUUID(accountFromEnv.viewKey()) + .toString(); const result = await scanner.owned({ uuid, unspent: true, @@ -747,7 +882,10 @@ describe("RecordScanner (real API)", function () { expect(result.ok).to.equal(true); if (result.ok) { expect(result.data).to.be.an("array"); - if (result.data.length > 0 && result.data[0].record_ciphertext) { + if ( + result.data.length > 0 && + result.data[0].record_ciphertext + ) { expect(result.data[0].record_plaintext).to.be.a("string"); } } @@ -845,7 +983,10 @@ describe("RecordScanner (real API)", function () { const first = result.data[0]; if (first.record_ciphertext) { expect(first.record_plaintext).to.satisfy( - (v: unknown) => v === undefined || v === "" || (typeof v === "string" && v.trim() === ""), + (v: unknown) => + v === undefined || + v === "" || + (typeof v === "string" && v.trim() === ""), ); } } @@ -871,10 +1012,14 @@ describe("RecordScanner (real API)", function () { }); expect(result.ok).to.equal(true); if (result.ok && result.data.length > 0) { - const withCipher = result.data.find((r) => r.record_ciphertext?.trim()); + const withCipher = result.data.find((r) => + r.record_ciphertext?.trim(), + ); if (withCipher) { expect(withCipher.record_plaintext).to.be.a("string"); - expect(withCipher.record_plaintext!.trim().length).to.be.greaterThan(0); + expect( + withCipher.record_plaintext!.trim().length, + ).to.be.greaterThan(0); } } }); @@ -890,7 +1035,10 @@ describe("RecordScanner (real API)", function () { }); const uuid = scanner.computeUUID(viewKey).toString(); try { - const record = await scanner.findCreditsRecord(1, { uuid, unspent: true }); + const record = await scanner.findCreditsRecord(1, { + uuid, + unspent: true, + }); expect(record).to.have.property("record_plaintext"); expect(record.record_plaintext).to.be.a("string"); } catch (err) { @@ -911,7 +1059,10 @@ describe("RecordScanner (real API)", function () { ...(consumerId && { consumerId }), }); const uuid = scanner.computeUUID(viewKey).toString(); - const records = await scanner.findCreditsRecords([1, 100], { uuid, unspent: true }); + const records = await scanner.findCreditsRecords([1, 100], { + uuid, + unspent: true, + }); expect(records).to.be.an("array"); for (const record of records) { expect(record).to.have.property("record_plaintext"); @@ -929,7 +1080,9 @@ describe("RecordScanner (real API)", function () { ...(consumerId && { consumerId }), }); const result = await scanner.register(ephemeralViewKey, 0); - const expectedUUID = scanner.computeUUID(ephemeralViewKey).toString(); + const expectedUUID = scanner + .computeUUID(ephemeralViewKey) + .toString(); expect(result.ok).to.equal(true); let actualUUID = expectedUUID; if (result.ok) { @@ -938,7 +1091,6 @@ describe("RecordScanner (real API)", function () { const revokeResult = await scanner.revoke(result.data.uuid); expect(revokeResult.ok).to.equal(true); } - }); it("registerEncrypted returns ok and uuid matches computeUUID", async function () { @@ -952,7 +1104,9 @@ describe("RecordScanner (real API)", function () { const result = await scanner.registerEncrypted(ephemeralViewKey, 0); expect(result.ok).to.equal(true); if (result.ok) { - expect(result.data.uuid).to.equal(scanner.computeUUID(ephemeralViewKey).toString()); + expect(result.data.uuid).to.equal( + scanner.computeUUID(ephemeralViewKey).toString(), + ); const revokeResult = await scanner.revoke(result.data.uuid); expect(revokeResult.ok).to.equal(true); } @@ -976,4 +1130,4 @@ describe("RecordScanner (real API)", function () { } }); }); -}); \ No newline at end of file +}); diff --git a/sdk/tests/sealance-merkle-tree.test.ts b/sdk/tests/sealance-merkle-tree.test.ts index a33e5fee1..129281ab1 100644 --- a/sdk/tests/sealance-merkle-tree.test.ts +++ b/sdk/tests/sealance-merkle-tree.test.ts @@ -1,167 +1,205 @@ import { SealanceMerkleTree } from "../src/integrations/sealance/merkle-tree.js"; -import {ZERO_ADDRESS} from "../src/constants.js"; +import { ZERO_ADDRESS } from "../src/constants.js"; import { expect } from "chai"; const sealance = new SealanceMerkleTree(); describe("merkle_tree lib, buildTree", () => { - it("should build a valid tree with 2 leaves", async () => { - const leaves = ["1field", "2field"]; - const tree = sealance.buildTree(leaves); - expect(tree).to.have.lengthOf(3); - }); - - it("should build a valid tree with 4 leaves", async () => { - const leaves = ["1field", "2field", "3field", "4field"]; - const tree = sealance.buildTree(leaves); - expect(tree).to.have.lengthOf(7); - }); - - it("should throw error for empty leaves", async () => { - expect(() => sealance.buildTree([])).to.throw(`Leaves array cannot be empty`); - }); - - it("should throw error for odd number of leaves", async () => { - expect(() => sealance.buildTree(["1field", "2field", "3field"])).to.throw(`Leaves array must have even number of elements`); - }); + it("should build a valid tree with 2 leaves", async () => { + const leaves = ["1field", "2field"]; + const tree = sealance.buildTree(leaves); + expect(tree).to.have.lengthOf(3); + }); + + it("should build a valid tree with 4 leaves", async () => { + const leaves = ["1field", "2field", "3field", "4field"]; + const tree = sealance.buildTree(leaves); + expect(tree).to.have.lengthOf(7); + }); + + it("should throw error for empty leaves", async () => { + expect(() => sealance.buildTree([])).to.throw( + `Leaves array cannot be empty`, + ); + }); + + it("should throw error for odd number of leaves", async () => { + expect(() => + sealance.buildTree(["1field", "2field", "3field"]), + ).to.throw(`Leaves array must have even number of elements`); + }); }); describe("merkle_tree lib, generateLeaves", () => { - it("should generate correct number of leaves from 1 leaf", () => { - const leaves = ["aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"]; - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(2); - }); - - it("should generate correct number of leaves from 2 leaves", () => { - const leaves = [ - "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - ]; - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(2); - }); - - it("should generate correct number of leaves from 3 leaves", () => { - const leaves = [ - "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - ]; - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(4); - }); - - it("should generate correct number of leaves from 5 leaves", () => { - const leaves = Array(5).fill("aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"); - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(8); - }); - - it("should generate correct number of leaves from 9 leaves", () => { - const leaves = Array(9).fill("aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"); - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(16); - }); - - it("should pad with 0field when needed", () => { - const leaves = ["aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"]; - const result = sealance.generateLeaves(leaves); - - expect(result).to.have.lengthOf(2); - expect(result.filter(x => x === "0field").length).to.equal(1); - }); - - it("should sort leaves correctly", () => { - const leaves = [ - "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", - "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - ]; - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(2); - expect(result[0]).to.not.equal(result[1]); - expect(result[0]).to.equal("1295133970529764960316948294624974168921228814652993007266766481909235735940field"); -}); + it("should generate correct number of leaves from 1 leaf", () => { + const leaves = [ + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ]; + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(2); + }); + + it("should generate correct number of leaves from 2 leaves", () => { + const leaves = [ + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + ]; + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(2); + }); + + it("should generate correct number of leaves from 3 leaves", () => { + const leaves = [ + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + ]; + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(4); + }); + + it("should generate correct number of leaves from 5 leaves", () => { + const leaves = Array(5).fill( + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ); + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(8); + }); - it("should pad with 0field when needed", () => { - const leaves = ["aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"]; - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(2); - expect(result.filter(x => x === "0field").length).to.equal(1); - }); - - it("should filter ZERO_ADDRESS", () => { - const leaves = [ - ZERO_ADDRESS, - ZERO_ADDRESS, - ZERO_ADDRESS, - "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - ]; - const result = sealance.generateLeaves(leaves); - expect(result).to.have.lengthOf(2); - expect(result[0]).to.equal("0field"); - expect(result[1]).to.equal("3501665755452795161867664882580888971213780722176652848275908626939553697821field"); - }); - - it("should throw error when exceeding max tree capacity", () => { - const maxDepth = 15; - const maxLeaves = 2 ** (maxDepth - 1); - const tooManyAddresses = Array(maxLeaves + 1).fill( - "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", - ); - - expect(() => sealance.generateLeaves(tooManyAddresses, maxDepth)).to.throw("Leaves limit exceeded"); + it("should generate correct number of leaves from 9 leaves", () => { + const leaves = Array(9).fill( + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ); + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(16); + }); + + it("should pad with 0field when needed", () => { + const leaves = [ + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ]; + const result = sealance.generateLeaves(leaves); + + expect(result).to.have.lengthOf(2); + expect(result.filter((x) => x === "0field").length).to.equal(1); + }); + + it("should sort leaves correctly", () => { + const leaves = [ + "aleo1s3ws5tra87fjycnjrwsjcrnw2qxr8jfqqdugnf0xzqqw29q9m5pqem2u4t", + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ]; + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(2); + expect(result[0]).to.not.equal(result[1]); + expect(result[0]).to.equal( + "1295133970529764960316948294624974168921228814652993007266766481909235735940field", + ); + }); + + it("should pad with 0field when needed", () => { + const leaves = [ + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ]; + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(2); + expect(result.filter((x) => x === "0field").length).to.equal(1); + }); + + it("should filter ZERO_ADDRESS", () => { + const leaves = [ + ZERO_ADDRESS, + ZERO_ADDRESS, + ZERO_ADDRESS, + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ]; + const result = sealance.generateLeaves(leaves); + expect(result).to.have.lengthOf(2); + expect(result[0]).to.equal("0field"); + expect(result[1]).to.equal( + "3501665755452795161867664882580888971213780722176652848275908626939553697821field", + ); + }); + + it("should throw error when exceeding max tree capacity", () => { + const maxDepth = 15; + const maxLeaves = 2 ** (maxDepth - 1); + const tooManyAddresses = Array(maxLeaves + 1).fill( + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ); + + expect(() => + sealance.generateLeaves(tooManyAddresses, maxDepth), + ).to.throw("Leaves limit exceeded"); }); }); describe("merkle_tree lib, getLeafIndices", () => { - it("should handle address larger than all leaves in tree", () => { - // Create a tree with a single address - const addresses = ["aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"]; - const leaves = sealance.generateLeaves(addresses, 15); - const tree = sealance.buildTree(leaves); - - // Use an address that's larger than all addresses in the tree - // This is generated from (2^253 - 1), the maximum valid field value - const largeAddress = "aleo1lllllllllllllllllllllllllllllllllllllllllllllllllu0snjvma4"; - - const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, largeAddress); - - // When address is larger than all leaves, both indices should point to the last leaf - expect(leftIdx).to.equal(1); - expect(rightIdx).to.equal(1); - }); + it("should handle address larger than all leaves in tree", () => { + // Create a tree with a single address + const addresses = [ + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ]; + const leaves = sealance.generateLeaves(addresses, 15); + const tree = sealance.buildTree(leaves); + + // Use an address that's larger than all addresses in the tree + // This is generated from (2^253 - 1), the maximum valid field value + const largeAddress = + "aleo1lllllllllllllllllllllllllllllllllllllllllllllllllu0snjvma4"; + + const [leftIdx, rightIdx] = sealance.getLeafIndices(tree, largeAddress); + + // When address is larger than all leaves, both indices should point to the last leaf + expect(leftIdx).to.equal(1); + expect(rightIdx).to.equal(1); + }); }); describe("merkle_tree lib, getSiblingPath", () => { - it("should handle odd leaf index (test sibling calculation branch)", () => { - // Create a tree with 4 leaves - const leaves = ["1field", "2field", "3field", "4field"]; - const tree = sealance.buildTree(leaves); - - // Get sibling path for leaf index 1 (odd index) - // This will test the "index - 1" branch of the sibling index calculation - const proof = sealance.getSiblingPath(tree, 1, 15); - const formattedProof = sealance.formatMerkleProof([proof, proof]); - - expect(proof).to.not.be.undefined; - expect(proof.leaf_index).to.equal(1); - expect(proof.siblings).to.not.be.undefined; - expect(proof.siblings).to.have.lengthOf(15); // Depth 15 - expect(proof.siblings[0]).to.equal(2n); // The leaf itself (index 1 = "2field") - expect(proof.siblings[1]).to.equal(1n); // Its sibling (index 0 = "1field") - expect(formattedProof).to.equal("[{siblings: [2field, 1field, 4560646903308595113151029585344265575242682454899063992850623350939538444867field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 1u32}, {siblings: [2field, 1field, 4560646903308595113151029585344265575242682454899063992850623350939538444867field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 1u32}]"); - }); + it("should handle odd leaf index (test sibling calculation branch)", () => { + // Create a tree with 4 leaves + const leaves = ["1field", "2field", "3field", "4field"]; + const tree = sealance.buildTree(leaves); + + // Get sibling path for leaf index 1 (odd index) + // This will test the "index - 1" branch of the sibling index calculation + const proof = sealance.getSiblingPath(tree, 1, 15); + const formattedProof = sealance.formatMerkleProof([proof, proof]); + + expect(proof).to.not.be.undefined; + expect(proof.leaf_index).to.equal(1); + expect(proof.siblings).to.not.be.undefined; + expect(proof.siblings).to.have.lengthOf(15); // Depth 15 + expect(proof.siblings[0]).to.equal(2n); // The leaf itself (index 1 = "2field") + expect(proof.siblings[1]).to.equal(1n); // Its sibling (index 0 = "1field") + expect(formattedProof).to.equal( + "[{siblings: [2field, 1field, 4560646903308595113151029585344265575242682454899063992850623350939538444867field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 1u32}, {siblings: [2field, 1field, 4560646903308595113151029585344265575242682454899063992850623350939538444867field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 1u32}]", + ); + }); }); describe("merkle_tree lib, formatMerkleProof", () => { - it("should format merkle proof correctly", () => { - const tree_string = ["0","3501665755452795161867664882580888971213780722176652848275908626939553697821","4539470720491009302557179633742244202521723448302096289013203539461297095738","7426353931016374702593127301815460123689343416970811802742952620156952318730","7052697765310872540032307615262912043079143561812812402660738198777516992718","7162588494104628985065247846816902886874444240334527780589295712502521092900","7179654795269183001911849426990084952297107287660910339902947270666145459461"]; - const tree = sealance.convertTreeToBigInt(tree_string); - const indices = sealance.getLeafIndices(tree, "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px"); - const proof1 = sealance.getSiblingPath(tree, indices[0], 15); - const proof2 = sealance.getSiblingPath(tree, indices[1], 15); - const formattedProof = sealance.formatMerkleProof([proof1, proof2]); - - expect(formattedProof).to.equal("[{siblings: [0field, 3501665755452795161867664882580888971213780722176652848275908626939553697821field, 7162588494104628985065247846816902886874444240334527780589295712502521092900field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 0u32}, {siblings: [3501665755452795161867664882580888971213780722176652848275908626939553697821field, 0field, 7162588494104628985065247846816902886874444240334527780589295712502521092900field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 1u32}]"); - }); -}); \ No newline at end of file + it("should format merkle proof correctly", () => { + const tree_string = [ + "0", + "3501665755452795161867664882580888971213780722176652848275908626939553697821", + "4539470720491009302557179633742244202521723448302096289013203539461297095738", + "7426353931016374702593127301815460123689343416970811802742952620156952318730", + "7052697765310872540032307615262912043079143561812812402660738198777516992718", + "7162588494104628985065247846816902886874444240334527780589295712502521092900", + "7179654795269183001911849426990084952297107287660910339902947270666145459461", + ]; + const tree = sealance.convertTreeToBigInt(tree_string); + const indices = sealance.getLeafIndices( + tree, + "aleo1rhgdu77hgyqd3xjj8ucu3jj9r2krwz6mnzyd80gncr5fxcwlh5rsvzp9px", + ); + const proof1 = sealance.getSiblingPath(tree, indices[0], 15); + const proof2 = sealance.getSiblingPath(tree, indices[1], 15); + const formattedProof = sealance.formatMerkleProof([proof1, proof2]); + + expect(formattedProof).to.equal( + "[{siblings: [0field, 3501665755452795161867664882580888971213780722176652848275908626939553697821field, 7162588494104628985065247846816902886874444240334527780589295712502521092900field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 0u32}, {siblings: [3501665755452795161867664882580888971213780722176652848275908626939553697821field, 0field, 7162588494104628985065247846816902886874444240334527780589295712502521092900field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field, 0field], leaf_index: 1u32}]", + ); + }); +}); diff --git a/sdk/tests/wasm.test.ts b/sdk/tests/wasm.test.ts index 8d525ddd9..db778dfcc 100644 --- a/sdk/tests/wasm.test.ts +++ b/sdk/tests/wasm.test.ts @@ -1,5 +1,22 @@ import { expect } from "chai"; -import { Address, AleoNetworkClient, CREDITS_PROGRAM_KEYS, Field, FunctionKeyPair, PrivateKey, ViewKey, Signature, RecordCiphertext, RecordPlaintext, PrivateKeyCiphertext, EncryptionToolkit, Transition, VerifyingKey, AleoKeyProvider, getOrInitConsensusVersionTestHeights} from "../src/node.js"; +import { + Address, + AleoNetworkClient, + CREDITS_PROGRAM_KEYS, + Field, + FunctionKeyPair, + PrivateKey, + ViewKey, + Signature, + RecordCiphertext, + RecordPlaintext, + PrivateKeyCiphertext, + EncryptionToolkit, + Transition, + VerifyingKey, + AleoKeyProvider, + getOrInitConsensusVersionTestHeights, +} from "../src/node.js"; import { seed, message, @@ -9,7 +26,7 @@ import { recordCiphertextString, foreignViewKeyString, recordPlaintextString, - beaconPrivateKeyString + beaconPrivateKeyString, } from "./data/account-data.js"; import { CREDITS_RECORD_V1, @@ -26,9 +43,9 @@ import { } from "./data/records.js"; import process from "node:process"; -describe('WASM Objects', () => { - describe('Address', () => { - it('can be constructed from a private key', () => { +describe("WASM Objects", () => { + describe("Address", () => { + it("can be constructed from a private key", () => { const privateKey = PrivateKey.from_string(privateKeyString); const address = Address.from_private_key(privateKey); @@ -38,7 +55,7 @@ describe('WASM Objects', () => { expect(address.to_string()).string(addressString); }); - it('can be constructed from view key', () => { + it("can be constructed from view key", () => { const viewKey = ViewKey.from_string(viewKeyString); const address = Address.from_view_key(viewKey); @@ -48,7 +65,7 @@ describe('WASM Objects', () => { expect(address.to_string()).string(addressString); }); - it('can be constructed from an address string', () => { + it("can be constructed from an address string", () => { const address = Address.from_string(addressString); // Ensure the address is an instance of Address @@ -57,19 +74,19 @@ describe('WASM Objects', () => { expect(address.to_string()).string(addressString); }); - it('can verify a message signed by the correct private key', () => { + it("can verify a message signed by the correct private key", () => { const privateKey = PrivateKey.from_string(privateKeyString); const signature = privateKey.sign(message); const address = Address.from_private_key(privateKey); const result = address.verify(message, signature); // Ensure the result is a boolean - expect(typeof result).equal('boolean'); + expect(typeof result).equal("boolean"); // Ensure the signature verified expect(result).equal(true); }); - it('cannot verify a message signed by the wrong private key', () => { + it("cannot verify a message signed by the wrong private key", () => { const privateKey = new PrivateKey(); const otherPrivateKey = new PrivateKey(); const signature = otherPrivateKey.sign(message); @@ -77,55 +94,61 @@ describe('WASM Objects', () => { const result = address.verify(message, signature); // Ensure the result is a boolean - expect(typeof result).equal('boolean'); + expect(typeof result).equal("boolean"); // Ensure the signature failed to verify expect(result).equal(false); }); - it('validates a correct address string with isValid', () => { + it("validates a correct address string with isValid", () => { const result = Address.isValid(addressString); expect(result).equal(true); }); - it('returns false for an invalid address string with isValid', () => { - expect(Address.isValid('invalid_address')).equal(false); - expect(Address.isValid('aleo1xyz')).equal(false); - expect(Address.isValid('')).equal(false); + it("returns false for an invalid address string with isValid", () => { + expect(Address.isValid("invalid_address")).equal(false); + expect(Address.isValid("aleo1xyz")).equal(false); + expect(Address.isValid("")).equal(false); }); - it('validates correct address bytes with isValid', () => { + it("validates correct address bytes with isValid", () => { const address = Address.from_string(addressString); const bytes = address.toBytesLe(); expect(Address.isValid(bytes)).equal(true); }); - it('returns false for invalid address bytes with isValid', () => { + it("returns false for invalid address bytes with isValid", () => { expect(Address.isValid(new Uint8Array([1, 2, 3]))).equal(false); expect(Address.isValid(new Uint8Array([]))).equal(false); }); - it('accepts uppercase address strings with isValid (auto-lowercased)', () => { + it("accepts uppercase address strings with isValid (auto-lowercased)", () => { const uppercaseAddress = addressString.toUpperCase(); expect(Address.isValid(uppercaseAddress)).equal(true); }); - it('accepts mixed case address strings with isValid (auto-lowercased)', () => { - const mixedCaseAddress = addressString.charAt(0).toUpperCase() + addressString.slice(1); + it("accepts mixed case address strings with isValid (auto-lowercased)", () => { + const mixedCaseAddress = + addressString.charAt(0).toUpperCase() + addressString.slice(1); expect(Address.isValid(mixedCaseAddress)).equal(true); }); - it('auto-lowercases address strings in from_string', () => { + it("auto-lowercases address strings in from_string", () => { const uppercaseAddress = addressString.toUpperCase(); - const mixedCaseAddress = addressString.charAt(0).toUpperCase() + addressString.slice(1); + const mixedCaseAddress = + addressString.charAt(0).toUpperCase() + addressString.slice(1); const expected = Address.from_string(addressString); - expect(Address.from_string(uppercaseAddress).to_string()).equal(expected.to_string()); - expect(Address.from_string(mixedCaseAddress).to_string()).equal(expected.to_string()); + expect(Address.from_string(uppercaseAddress).to_string()).equal( + expected.to_string(), + ); + expect(Address.from_string(mixedCaseAddress).to_string()).equal( + expected.to_string(), + ); }); }); - describe('PrivateKey', () => { - it ('creates new accounts from sampling an rng for the initial seed', () => { + describe("PrivateKey", () => { + it("creates new accounts from sampling an rng for the initial seed", () => { const privateKey = new PrivateKey(); const privateKey2 = new PrivateKey(); @@ -137,7 +160,7 @@ describe('WASM Objects', () => { expect(privateKey.to_string()).not.equal(privateKey2.to_string()); }); - it('constructs properly from a seed', () => { + it("constructs properly from a seed", () => { const privateKey = PrivateKey.from_seed_unchecked(seed); // Ensure the private key is a PrivateKey instance @@ -146,7 +169,7 @@ describe('WASM Objects', () => { expect(privateKey.to_string()).string(beaconPrivateKeyString); }); - it('constructs properly from a private key string', () => { + it("constructs properly from a private key string", () => { const privateKey = PrivateKey.from_string(privateKeyString); // Ensure the private key is a PrivateKey instance @@ -155,7 +178,7 @@ describe('WASM Objects', () => { expect(privateKey.to_string()).string(privateKeyString); }); - it('derives the correct view key and address', () => { + it("derives the correct view key and address", () => { const privateKey = PrivateKey.from_string(privateKeyString); const viewKey = privateKey.to_view_key(); const address = privateKey.to_address(); @@ -168,10 +191,11 @@ describe('WASM Objects', () => { expect(address.to_string()).string(addressString); }); - it('can construct directly to ciphertext and then decrypt to a private key', () => { - const secret = 'mypassword'; + it("can construct directly to ciphertext and then decrypt to a private key", () => { + const secret = "mypassword"; const ciphertext = PrivateKey.newEncrypted(secret); - const privateKeyFromCiphertext = PrivateKey.fromPrivateKeyCiphertext(ciphertext, secret); + const privateKeyFromCiphertext = + PrivateKey.fromPrivateKeyCiphertext(ciphertext, secret); // Ensure the ciphertext is a PrivateKeyCiphertext instance expect(ciphertext).instanceof(PrivateKeyCiphertext); @@ -179,21 +203,24 @@ describe('WASM Objects', () => { expect(privateKeyFromCiphertext).instanceof(PrivateKey); }); - it('encrypts and decrypts to and from ciphertext', () => { - const secret = 'mypassword'; + it("encrypts and decrypts to and from ciphertext", () => { + const secret = "mypassword"; const privateKey = new PrivateKey(); const ciphertext = privateKey.toCiphertext(secret); // Ensure the ciphertext is a PrivateKeyCiphertext instance expect(ciphertext).instanceof(PrivateKeyCiphertext); - const privateKeyFromCiphertext = PrivateKey.fromPrivateKeyCiphertext(ciphertext, secret); + const privateKeyFromCiphertext = + PrivateKey.fromPrivateKeyCiphertext(ciphertext, secret); // Ensure the decrypted private key is a PrivateKey instance expect(privateKeyFromCiphertext).instanceof(PrivateKey); // Ensure the decrypted private key is the same as the original - expect(privateKeyFromCiphertext.to_string()).equal(privateKey.to_string()); + expect(privateKeyFromCiphertext.to_string()).equal( + privateKey.to_string(), + ); }); - it('properly assesses equality and inequality', () => { + it("properly assesses equality and inequality", () => { const privateKey1 = new PrivateKey(); const privateKey2 = PrivateKey.from_string(privateKeyString); const privateKey3 = PrivateKey.from_string(privateKeyString); @@ -205,13 +232,19 @@ describe('WASM Objects', () => { expect(privateKey2.to_string()).equal(privateKey3.to_string()); }); - it('has different ciphertexts for the same password, but decrypts to the same key', () => { - const secret = 'mypassword'; + it("has different ciphertexts for the same password, but decrypts to the same key", () => { + const secret = "mypassword"; const privateKey = PrivateKey.from_string(privateKeyString); const ciphertext = privateKey.toCiphertext(secret); const ciphertext2 = privateKey.toCiphertext(secret); - const decryptedPrivateKey = PrivateKey.fromPrivateKeyCiphertext(ciphertext, secret); - const decryptedPrivateKey2 = PrivateKey.fromPrivateKeyCiphertext(ciphertext2, secret); + const decryptedPrivateKey = PrivateKey.fromPrivateKeyCiphertext( + ciphertext, + secret, + ); + const decryptedPrivateKey2 = PrivateKey.fromPrivateKeyCiphertext( + ciphertext2, + secret, + ); // Ensure the ciphertexts are different expect(ciphertext).not.equal(ciphertext2); @@ -221,21 +254,23 @@ describe('WASM Objects', () => { // Ensure the decrypted private keys are the same as the original expect(decryptedPrivateKey.to_string()).equal(privateKeyString); expect(decryptedPrivateKey2.to_string()).equal(privateKeyString); - expect(decryptedPrivateKey.to_string()).equal(decryptedPrivateKey2.to_string()); + expect(decryptedPrivateKey.to_string()).equal( + decryptedPrivateKey2.to_string(), + ); }); }); - describe('ViewKey', () => { + describe("ViewKey", () => { const viewKey = ViewKey.from_string(viewKeyString); - it('constructs properly from a string', () => { + it("constructs properly from a string", () => { // Ensure the view key is a ViewKey instance expect(viewKey).instanceof(ViewKey); // Ensure the view key is the correct value expect(viewKey.to_string()).string(viewKeyString); }); - it('derives the correct address', () => { + it("derives the correct address", () => { const address = viewKey.to_address(); // Ensure the address is an Address instance @@ -244,7 +279,7 @@ describe('WASM Objects', () => { expect(address.to_string()).string(addressString); }); - it('properly assesses equality and inequality', () => { + it("properly assesses equality and inequality", () => { const viewKey1 = new ViewKey(); const viewKey2 = ViewKey.from_string(viewKeyString); const viewKey3 = ViewKey.from_string(viewKeyString); @@ -256,7 +291,7 @@ describe('WASM Objects', () => { expect(viewKey2.to_string()).equal(viewKey3.to_string()); }); - it('can decrypt a record generated by the account', () => { + it("can decrypt a record generated by the account", () => { const decryptedRecord = viewKey.decrypt(recordCiphertextString); // Ensure it decrypts to the correct data @@ -264,33 +299,33 @@ describe('WASM Objects', () => { }); }); - describe('Signature', () => { + describe("Signature", () => { const privateKey = new PrivateKey(); - it('can verify a message signed by the correct private key', () => { + it("can verify a message signed by the correct private key", () => { const address = Address.from_private_key(privateKey); const signature = Signature.sign(privateKey, message); const result = signature.verify(address, message); // Ensure the result is a boolean - expect(typeof result).equal('boolean'); + expect(typeof result).equal("boolean"); // Ensure the signature verified expect(result).equal(true); }); - it('cannot verify a message signed by the wrong private key', () => { + it("cannot verify a message signed by the wrong private key", () => { const address = Address.from_private_key(privateKey); const otherPrivateKey = PrivateKey.from_string(privateKeyString); const signature = Signature.sign(otherPrivateKey, message); const result = signature.verify(address, message); // Ensure the result is a boolean - expect(typeof result).equal('boolean'); + expect(typeof result).equal("boolean"); // Ensure the signature failed to verify expect(result).equal(false); }); - it('can go to and from string', () => { + it("can go to and from string", () => { const signature = Signature.sign(privateKey, message); const signatureString = signature.to_string(); const signatureFromString = Signature.from_string(signatureString); @@ -305,12 +340,15 @@ describe('WASM Objects', () => { }); }); - describe('PrivateKeyCipherText', () => { + describe("PrivateKeyCipherText", () => { const privateKey = PrivateKey.from_string(privateKeyString); - const secret = 'mypassword'; - const ciphertext = PrivateKeyCiphertext.encryptPrivateKey(privateKey, secret); + const secret = "mypassword"; + const ciphertext = PrivateKeyCiphertext.encryptPrivateKey( + privateKey, + secret, + ); - it('should encrypt and decrypt a private key to and from ciphertext', () => { + it("should encrypt and decrypt a private key to and from ciphertext", () => { const decryptedKey = ciphertext.decryptToPrivateKey(secret); // Ensure the decrypted key is a PrivateKey instance and is the same as the original @@ -318,8 +356,8 @@ describe('WASM Objects', () => { expect(decryptedKey.to_string()).equal(privateKeyString); }); - it('should fail to decrypt with a bad secret', () => { - const badSecret = 'badpassword'; + it("should fail to decrypt with a bad secret", () => { + const badSecret = "badpassword"; try { ciphertext.decryptToPrivateKey(badSecret); @@ -331,8 +369,11 @@ describe('WASM Objects', () => { } }); - it('should not create ciphertexts that match for the same password, but should decrypt to the same key', () => { - const ciphertext2 = PrivateKeyCiphertext.encryptPrivateKey(privateKey, secret); + it("should not create ciphertexts that match for the same password, but should decrypt to the same key", () => { + const ciphertext2 = PrivateKeyCiphertext.encryptPrivateKey( + privateKey, + secret, + ); const decryptedKey = ciphertext.decryptToPrivateKey(secret); const decryptedKey2 = ciphertext2.decryptToPrivateKey(secret); @@ -345,26 +386,40 @@ describe('WASM Objects', () => { expect(decryptedKey2.to_string()).equal(privateKeyString); }); - it('round trip to and from string for PrivateKeyCiphertext', () => { + it("round trip to and from string for PrivateKeyCiphertext", () => { const private_key = new PrivateKey(); const password = "mypassword"; - const privateKeyCiphertext = PrivateKeyCiphertext.encryptPrivateKey(private_key, password); - const privateKeyCipherText2 = PrivateKeyCiphertext.fromString(privateKeyCiphertext.toString()); + const privateKeyCiphertext = PrivateKeyCiphertext.encryptPrivateKey( + private_key, + password, + ); + const privateKeyCipherText2 = PrivateKeyCiphertext.fromString( + privateKeyCiphertext.toString(), + ); // Assert the round trip to and from string journey results in the same key - expect(privateKeyCiphertext.toString()).equal(privateKeyCipherText2.toString()); + expect(privateKeyCiphertext.toString()).equal( + privateKeyCipherText2.toString(), + ); }); - it('decryption of PrivateKeyCiphertext with edge cases', () => { - const privateKeyString = "APrivateKey1zkpAYS46Dq4rnt9wdohyWMwdmjmTeMJKPZdp5AhvjXZDsVG"; + it("decryption of PrivateKeyCiphertext with edge cases", () => { + const privateKeyString = + "APrivateKey1zkpAYS46Dq4rnt9wdohyWMwdmjmTeMJKPZdp5AhvjXZDsVG"; const privateKey = PrivateKey.from_string(privateKeyString); - const ciphertext = "ciphertext1qvqg7rgvam3xdcu55pwu6sl8rxwefxaj5gwthk0yzln6jv5fastzup0qn0qftqlqq7jcckyx03fzv9kke0z9puwd7cl7jzyhxfy2f2juplz39dkqs6p24urhxymhv364qm3z8mvyklv5gr52n4fxr2z59jgqytyddj8"; - const bad_ciphertext = "ciphertext1qvqg7rgvam3xdcu55pwu6sl8rxwefxaj5gwthk0yzln6jv5fastzup0qn0qftqlqq7jcckyx03fzv9kke0z9puwd7cl7jzyhxfy2f2juplz39dkqs6p24urhxymhv364qm3z8mvyklv5er52n4fxr2z59jgqytyddj8"; - const privateKeyCiphertext = PrivateKeyCiphertext.fromString(ciphertext); - const decryptedPrivateKey = privateKeyCiphertext.decryptToPrivateKey("mypassword"); + const ciphertext = + "ciphertext1qvqg7rgvam3xdcu55pwu6sl8rxwefxaj5gwthk0yzln6jv5fastzup0qn0qftqlqq7jcckyx03fzv9kke0z9puwd7cl7jzyhxfy2f2juplz39dkqs6p24urhxymhv364qm3z8mvyklv5gr52n4fxr2z59jgqytyddj8"; + const bad_ciphertext = + "ciphertext1qvqg7rgvam3xdcu55pwu6sl8rxwefxaj5gwthk0yzln6jv5fastzup0qn0qftqlqq7jcckyx03fzv9kke0z9puwd7cl7jzyhxfy2f2juplz39dkqs6p24urhxymhv364qm3z8mvyklv5er52n4fxr2z59jgqytyddj8"; + const privateKeyCiphertext = + PrivateKeyCiphertext.fromString(ciphertext); + const decryptedPrivateKey = + privateKeyCiphertext.decryptToPrivateKey("mypassword"); // Assert that the private key is the same as the original for a valid ciphertext and secret - expect(privateKey.to_string()).equal(decryptedPrivateKey.to_string()); + expect(privateKey.to_string()).equal( + decryptedPrivateKey.to_string(), + ); // Assert the incorrect secret fails expect(() => { privateKeyCiphertext.decryptToPrivateKey("badpassword"); @@ -376,18 +431,20 @@ describe('WASM Objects', () => { }); }); - describe('RecordCiphertext', () => { + describe("RecordCiphertext", () => { const viewKey = ViewKey.from_string(viewKeyString); const ciphertext = RecordCiphertext.fromString(recordCiphertextString); - it('can be created from and output to a string', () => { - const ciphertext = RecordCiphertext.fromString(recordCiphertextString); + it("can be created from and output to a string", () => { + const ciphertext = RecordCiphertext.fromString( + recordCiphertextString, + ); // Ensure the string matches the string the record was created from expect(ciphertext.toString()).equal(recordCiphertextString); }); - it('can be decrypted and identified as owner with a valid view key', () => { + it("can be decrypted and identified as owner with a valid view key", () => { const plaintext = ciphertext.decrypt(viewKey); const isOwner = ciphertext.isOwner(viewKey); @@ -397,7 +454,7 @@ describe('WASM Objects', () => { expect(isOwner).equal(true); }); - it('cant be decrypted nor identified as owner with a foreign view key', () => { + it("cant be decrypted nor identified as owner with a foreign view key", () => { const foreignViewKey = ViewKey.from_string(foreignViewKeyString); // Ensure the record ciphertext cannot be decrypted with a foreign view key @@ -406,25 +463,30 @@ describe('WASM Objects', () => { expect(() => ciphertext.decrypt(foreignViewKey)).throw(); }); - it('can be decrypted with a valid record view key', () => { + it("can be decrypted with a valid record view key", () => { const recordViewKey = ciphertext.recordViewKey(viewKey); - const plaintext = ciphertext.decryptWithRecordViewKey(recordViewKey); + const plaintext = + ciphertext.decryptWithRecordViewKey(recordViewKey); const isOwner = ciphertext.isOwner(viewKey); // Ensure the record ciphertext is decrypted correctly expect(plaintext.toString()).equal(recordPlaintextString); expect(isOwner).equal(true); - }) + }); - it('cannot be decrypted with an invalid record view key', () => { - const badRecordViewKey = ciphertext.recordViewKey(ViewKey.from_string(foreignViewKeyString)); + it("cannot be decrypted with an invalid record view key", () => { + const badRecordViewKey = ciphertext.recordViewKey( + ViewKey.from_string(foreignViewKeyString), + ); // Ensure the record ciphertext cannot be decrypted with an invalid record view key - expect(() => ciphertext.decryptWithRecordViewKey(badRecordViewKey)).throw(); - }) + expect(() => + ciphertext.decryptWithRecordViewKey(badRecordViewKey), + ).throw(); + }); }); - describe('RecordPlaintext', () => { - it('can be created from a string gives the correct number of microcredits, and can export to a string', () => { + describe("RecordPlaintext", () => { + it("can be created from a string gives the correct number of microcredits, and can export to a string", () => { const plaintext = RecordPlaintext.fromString(recordPlaintextString); // Ensure the string matches the string the record was created from @@ -434,155 +496,256 @@ describe('WASM Objects', () => { }); }); - describe('Transition', () => { + describe("Transition", () => { const transitionStringTestnet = `{"id":"au1u62jasyx78x9hktak24awyj38fz73aseq8g9cx98u8egd9pj9uxq3u6s2z","program":"hello_hello.aleo","function":"hello","inputs":[{"type":"public","id":"3748790614260807060977840590007893602934308327222309419419577452790958781330field","value":"1u32"},{"type":"private","id":"5954208307642819953251922459490586292095132973876550778604572231610245257004field","value":"ciphertext1qyq0m5mp0d2gzh2pv9p25z70gz2avhqdt3dp8y8thzwf3aq6g35zcqcuyptz3"}],"outputs":[{"type":"private","id":"1557506318887190915592751299113729867877933642317637206076176689093854281418field","value":"ciphertext1qyqzmhw8ln9r6uuyh0n5jrsqlt25wdggqp3d9yqyttpr3g7g00k2sysdf9rmv"}],"tpk":"7532444547840484531569841377269810017844130178606467837628364672670182422388group","tcm":"7292056195970541935877520517416922164990366931599720071937561392936678536563field","scm":"8283770351301010771186520129040704279224805960417079922462917369178354050332field"}`; - const transitionTestnet = Transition.fromString(transitionStringTestnet); - const transitionDecryptedStringTestnet = `{"id":"au1mhdz6jqm973v5vfkz2pwgv63p340c9tpvydxha2zs8w03746qcpqvx3yye","program":"hello_hello.aleo","function":"hello","inputs":[{"type":"public","id":"3748790614260807060977840590007893602934308327222309419419577452790958781330field","value":"1u32"},{"type":"public","id":"5954208307642819953251922459490586292095132973876550778604572231610245257004field","value":"2u32"}],"outputs":[{"type":"public","id":"1557506318887190915592751299113729867877933642317637206076176689093854281418field","value":"3u32"}],"tpk":"7532444547840484531569841377269810017844130178606467837628364672670182422388group","tcm":"7292056195970541935877520517416922164990366931599720071937561392936678536563field","scm":"8283770351301010771186520129040704279224805960417079922462917369178354050332field"}` - const transitionDecryptedTestnet = Transition.fromString(transitionDecryptedStringTestnet); - const transitionViewKeyStringTestnet = "3975242887442171718863200089461896014344887434842278474302914755871123010247field"; + const transitionTestnet = Transition.fromString( + transitionStringTestnet, + ); + const transitionDecryptedStringTestnet = `{"id":"au1mhdz6jqm973v5vfkz2pwgv63p340c9tpvydxha2zs8w03746qcpqvx3yye","program":"hello_hello.aleo","function":"hello","inputs":[{"type":"public","id":"3748790614260807060977840590007893602934308327222309419419577452790958781330field","value":"1u32"},{"type":"public","id":"5954208307642819953251922459490586292095132973876550778604572231610245257004field","value":"2u32"}],"outputs":[{"type":"public","id":"1557506318887190915592751299113729867877933642317637206076176689093854281418field","value":"3u32"}],"tpk":"7532444547840484531569841377269810017844130178606467837628364672670182422388group","tcm":"7292056195970541935877520517416922164990366931599720071937561392936678536563field","scm":"8283770351301010771186520129040704279224805960417079922462917369178354050332field"}`; + const transitionDecryptedTestnet = Transition.fromString( + transitionDecryptedStringTestnet, + ); + const transitionViewKeyStringTestnet = + "3975242887442171718863200089461896014344887434842278474302914755871123010247field"; const transitionStringMainnet = `{"id":"au1mguuz0dh20f78802m4z0py7n08xhl0pz60llzck63mhl8pc8l5xqxpwgtn","program":"hello_hello.aleo","function":"main","inputs":[{"type":"public","id":"6393584049543470937057043098611271993206122889317039351966319038535020834557field","value": "1u32"},{"type":"private","id":"8207446256045172951742235001162005156507562935942883128759030124682934277495field","value":"ciphertext1qyqqgz9qnupeld9vr4vuwp6yrpmhgtkvmgag5m7mmrruw0r6je666qgqdswk3"}],"outputs":[{"type":"private","id":"127469473292952941321346770257126666363371158501875622169294663492714835110field","value":"ciphertext1qyqyapkjuxm9dcslgyjf7hkr2k3dek500z40gjspnwvll0uawj23vzgggc405"}],"tpk":"7647553513996966044119163122930125808381703910407273818947266861843062002251group","tcm":"4479413938380109857414238205380483440836495997450846894155088299187217672609field","scm":"6461007226176477784737642021400489186736987671609840640950580467598882134642field"}`; - const transitionMainnet = Transition.fromString(transitionStringMainnet); + const transitionMainnet = Transition.fromString( + transitionStringMainnet, + ); const transitionDecryptedStringMainnet = `{"id":"au1jl2ur42sj7hwe4r0alv6gnklqxj0fszrvu3q82gjcls5x6q9pyzqdgmu2k","program":"hello_hello.aleo","function":"main","inputs":[{"type":"public","id":"6393584049543470937057043098611271993206122889317039351966319038535020834557field","value":"1u32"},{"type":"public","id":"8207446256045172951742235001162005156507562935942883128759030124682934277495field","value":"2u32"}],"outputs":[{"type":"public","id":"127469473292952941321346770257126666363371158501875622169294663492714835110field","value":"3u32"}],"tpk":"7647553513996966044119163122930125808381703910407273818947266861843062002251group","tcm":"4479413938380109857414238205380483440836495997450846894155088299187217672609field","scm":"6461007226176477784737642021400489186736987671609840640950580467598882134642field"}`; - const transitionDecryptedMainnet = Transition.fromString(transitionDecryptedStringMainnet); - const transitionViewKeyStringMainnet = "8161419549946991944867064830365679191883723972221767444308198038592561311302field"; - - const invalidTransitionViewKeyString = "5089075468761042335883809641276568724119791331127957254389204093712358605127field" - const invalidTransitionViewKey = Field.fromString(invalidTransitionViewKeyString); - const privateKey = PrivateKey.from_string("APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH"); + const transitionDecryptedMainnet = Transition.fromString( + transitionDecryptedStringMainnet, + ); + const transitionViewKeyStringMainnet = + "8161419549946991944867064830365679191883723972221767444308198038592561311302field"; + + const invalidTransitionViewKeyString = + "5089075468761042335883809641276568724119791331127957254389204093712358605127field"; + const invalidTransitionViewKey = Field.fromString( + invalidTransitionViewKeyString, + ); + const privateKey = PrivateKey.from_string( + "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH", + ); const viewKey = privateKey.to_view_key(); let connection = new AleoNetworkClient("https://api.provable.com/v2"); if (connection.network === "testnet") { - it('can be decrypted with a valid transition view key', () => { - const tvk = transitionTestnet.tvk(viewKey); - const transitionDecryptedWithTVK = transitionTestnet.decryptTransition(tvk); - // Ensure the transition is valid - expect(transitionDecryptedWithTVK.toString()).equal(transitionDecryptedTestnet.toString()); - }); - - it('cannot be decrypted with an invalid transition view key', () => { - expect(() => transitionTestnet.decryptTransition(invalidTransitionViewKey).toThrow()); - }); - - it('can generate a transition view key from a valid view key', () => { - const generatedTransitionViewKey = transitionTestnet.tvk(viewKey); - - // Ensure the generated transition view key is the same as the one used to decrypt - expect(generatedTransitionViewKey.toString()).equal(transitionViewKeyStringTestnet); - }); - } - if (connection.network === "mainnet") { - const tvk = transitionMainnet.tvk(viewKey); - it('can be decrypted with a valid transition view key', () => { - const transitionDecryptedWithTVK = transitionMainnet.decryptTransition(tvk); - // Ensure the transition is valid - expect(transitionDecryptedWithTVK.toString()).equal(transitionDecryptedMainnet.toString()); - }); - - it('cannot be decrypted with an invalid transition view key', () => { - expect(() => transitionMainnet.decryptTransition(invalidTransitionViewKey)).to.throw(); - }); - - it('can generate a transition view key from a valid view key', () => { - const generatedTransitionViewKey = transitionMainnet.tvk(viewKey); - - // Ensure the generated transition view key is the same as the one used to decrypt - expect(generatedTransitionViewKey.toString()).equal(transitionViewKeyStringMainnet); - }); - } -}); + it("can be decrypted with a valid transition view key", () => { + const tvk = transitionTestnet.tvk(viewKey); + const transitionDecryptedWithTVK = + transitionTestnet.decryptTransition(tvk); + // Ensure the transition is valid + expect(transitionDecryptedWithTVK.toString()).equal( + transitionDecryptedTestnet.toString(), + ); + }); + + it("cannot be decrypted with an invalid transition view key", () => { + expect(() => + transitionTestnet + .decryptTransition(invalidTransitionViewKey) + .toThrow(), + ); + }); + + it("can generate a transition view key from a valid view key", () => { + const generatedTransitionViewKey = + transitionTestnet.tvk(viewKey); + + // Ensure the generated transition view key is the same as the one used to decrypt + expect(generatedTransitionViewKey.toString()).equal( + transitionViewKeyStringTestnet, + ); + }); + } + if (connection.network === "mainnet") { + const tvk = transitionMainnet.tvk(viewKey); + it("can be decrypted with a valid transition view key", () => { + const transitionDecryptedWithTVK = + transitionMainnet.decryptTransition(tvk); + // Ensure the transition is valid + expect(transitionDecryptedWithTVK.toString()).equal( + transitionDecryptedMainnet.toString(), + ); + }); + + it("cannot be decrypted with an invalid transition view key", () => { + expect(() => + transitionMainnet.decryptTransition( + invalidTransitionViewKey, + ), + ).to.throw(); + }); + + it("can generate a transition view key from a valid view key", () => { + const generatedTransitionViewKey = + transitionMainnet.tvk(viewKey); + + // Ensure the generated transition view key is the same as the one used to decrypt + expect(generatedTransitionViewKey.toString()).equal( + transitionViewKeyStringMainnet, + ); + }); + } + }); - describe('EncryptionToolkit', () => { - const recordCiphertext = RecordCiphertext.fromString(RECORD_CIPHERTEXT_STRING); - const recordCiphertextNotOwned = RecordCiphertext.fromString(RECORD_CIPHERTEXT_STRING_NOT_OWNED); - const recordCiphertextNotOwned2 = RecordCiphertext.fromString(RECORD_CIPHERTEXT_STRING_NOT_OWNED2); - const recordCiphertextArray = [recordCiphertext, recordCiphertextNotOwned, recordCiphertextNotOwned2]; - const recordCiphertextArrayCopy = recordCiphertextArray.map(record => record.clone()); - const recordPlaintext = RecordPlaintext.fromString(RECORD_PLAINTEXT_V0_STRING); + describe("EncryptionToolkit", () => { + const recordCiphertext = RecordCiphertext.fromString( + RECORD_CIPHERTEXT_STRING, + ); + const recordCiphertextNotOwned = RecordCiphertext.fromString( + RECORD_CIPHERTEXT_STRING_NOT_OWNED, + ); + const recordCiphertextNotOwned2 = RecordCiphertext.fromString( + RECORD_CIPHERTEXT_STRING_NOT_OWNED2, + ); + const recordCiphertextArray = [ + recordCiphertext, + recordCiphertextNotOwned, + recordCiphertextNotOwned2, + ]; + const recordCiphertextArrayCopy = recordCiphertextArray.map((record) => + record.clone(), + ); + const recordPlaintext = RecordPlaintext.fromString( + RECORD_PLAINTEXT_V0_STRING, + ); const recordPlaintextCopy = recordPlaintext.clone(); const viewKey = ViewKey.from_string(VIEW_KEY_STRING); const recordViewKey = Field.fromString(RECORD_VIEW_KEY_STRING); - - it('can generate a record view key from a view key and a record ciphertext', () => { - const generatedRecordViewKey = EncryptionToolkit.generateRecordViewKey(viewKey, recordCiphertext); + + it("can generate a record view key from a view key and a record ciphertext", () => { + const generatedRecordViewKey = + EncryptionToolkit.generateRecordViewKey( + viewKey, + recordCiphertext, + ); // Ensure the generated record view key is the same as the one used to decrypt - expect(generatedRecordViewKey.toString()).equal(recordViewKey.toString()); - }); - it('can decrypt a record ciphertext with the record view key', () => { - const decryptedRecord = EncryptionToolkit.decryptRecordWithRVk(recordViewKey, recordCiphertext); + expect(generatedRecordViewKey.toString()).equal( + recordViewKey.toString(), + ); + }); + it("can decrypt a record ciphertext with the record view key", () => { + const decryptedRecord = EncryptionToolkit.decryptRecordWithRVk( + recordViewKey, + recordCiphertext, + ); // Ensure the decrypted record is the same as the plaintext - expect(decryptedRecord.toString()).equal(recordPlaintext.toString()); - }); - it('cannot decrypt a record ciphertext with an invalid record view key', () => { - const invalidRecordViewKey = Field.fromString("4445718830394614891114647247073357114867447866913203502139893824059966201724field"); - expect(() => EncryptionToolkit.decryptRecordWithRVk(invalidRecordViewKey, recordCiphertext)).to.throw(); - }); - it('can check if a record ciphertext from an array of record ciphertexts is owned by a view key', () => { - const ownedRecords = EncryptionToolkit.checkOwnedRecords(viewKey, recordCiphertextArray); + expect(decryptedRecord.toString()).equal( + recordPlaintext.toString(), + ); + }); + it("cannot decrypt a record ciphertext with an invalid record view key", () => { + const invalidRecordViewKey = Field.fromString( + "4445718830394614891114647247073357114867447866913203502139893824059966201724field", + ); + expect(() => + EncryptionToolkit.decryptRecordWithRVk( + invalidRecordViewKey, + recordCiphertext, + ), + ).to.throw(); + }); + it("can check if a record ciphertext from an array of record ciphertexts is owned by a view key", () => { + const ownedRecords = EncryptionToolkit.checkOwnedRecords( + viewKey, + recordCiphertextArray, + ); // Ensure the record ciphertext is owned by the view key - expect(ownedRecords[0].toString()).equal(RECORD_CIPHERTEXT_STRING_COPY.toString()); - }); - it('can decrypt a record ciphertext from an array of record ciphertexts', () => { - const decryptedRecords = EncryptionToolkit.decryptOwnedRecords(viewKey, recordCiphertextArrayCopy); + expect(ownedRecords[0].toString()).equal( + RECORD_CIPHERTEXT_STRING_COPY.toString(), + ); + }); + it("can decrypt a record ciphertext from an array of record ciphertexts", () => { + const decryptedRecords = EncryptionToolkit.decryptOwnedRecords( + viewKey, + recordCiphertextArrayCopy, + ); // Ensure the decrypted record is the same as the plaintext - expect(decryptedRecords[0].toString()).equal(recordPlaintextCopy.toString()); + expect(decryptedRecords[0].toString()).equal( + recordPlaintextCopy.toString(), + ); }); - it('can decrypt sender ciphertexts', () => { + it("can decrypt sender ciphertexts", () => { // Get the private key corresponding to the record. - const private_key = PrivateKey.from_string(<string>process.env["PUZZLE_PK"]); + const private_key = PrivateKey.from_string( + <string>process.env["PUZZLE_PK"], + ); const view_key = ViewKey.from_private_key(private_key); // Construct the record and the sender ciphertext. const record = RecordPlaintext.fromString(CREDITS_RECORD_V1); const record_view_key = Field.fromString(CREDITS_RECORD_VIEW_KEY); - const sender_ciphertext = Field.fromString(CREDITS_SENDER_CIPHERTEXT); + const sender_ciphertext = Field.fromString( + CREDITS_SENDER_CIPHERTEXT, + ); // Decrypt the sender ciphertext using the record object and ensure it's from the expected address. let sender = record.decryptSender(view_key, sender_ciphertext); expect(sender.to_string()).to.equal(CREDITS_SENDER_PLAINTEXT); // Decrypt the sender ciphertext using the EncryptionToolkit function and ensure it's from the expected address. - sender = EncryptionToolkit.decryptSender(view_key, record, sender_ciphertext); + sender = EncryptionToolkit.decryptSender( + view_key, + record, + sender_ciphertext, + ); expect(sender.to_string()).to.equal(CREDITS_SENDER_PLAINTEXT); // Decrypt the sender ciphertext using only the record view key and ensure it's from the expected address. - sender = EncryptionToolkit.decryptSenderWithRvk(record_view_key, sender_ciphertext); + sender = EncryptionToolkit.decryptSenderWithRvk( + record_view_key, + sender_ciphertext, + ); expect(sender.to_string()).to.equal(CREDITS_SENDER_PLAINTEXT); - }) - it('can decryption') + }); + it("can decryption"); }); - describe('VerifyingKey', () => { - it('can get the number of constraints', async () => { + describe("VerifyingKey", () => { + it("can get the number of constraints", async () => { const keyProvider = new AleoKeyProvider(); - const [transferPublicProver, transferPublicVerifier] = <FunctionKeyPair>await keyProvider.fetchCreditsKeys(CREDITS_PROGRAM_KEYS.transfer_public); + const [transferPublicProver, transferPublicVerifier] = < + FunctionKeyPair + >await keyProvider.fetchCreditsKeys( + CREDITS_PROGRAM_KEYS.transfer_public, + ); const numConstraints = transferPublicVerifier.numConstraints(); expect(numConstraints).to.equal(12326); }); }); - describe('Set development consensus version heights', () => { - it('Consensus version heights can be set externally', async () => { + describe("Set development consensus version heights", () => { + it("Consensus version heights can be set externally", async () => { if (process.env["RUN_SKIPPED"]) { - const heights = getOrInitConsensusVersionTestHeights("0,1,2,3,4,5,6,7,8,9,10,11,12"); + const heights = getOrInitConsensusVersionTestHeights( + "0,1,2,3,4,5,6,7,8,9,10,11,12", + ); console.log(heights); - expect(heights).to.deep.equal([0,1,2,3,4,5,6,7,8,9,10,11,12]); + expect(heights).to.deep.equal([ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + ]); } }); }); describe("ProgramID", () => { - let connection = new AleoNetworkClient("https://api.explorer.provable.com/v2"); + let connection = new AleoNetworkClient( + "https://api.explorer.provable.com/v2", + ); it("Can can successfully get the correct address from a ProgramID string.", () => { const programIDString = "credits.aleo"; if (connection.network === "mainnet") { const programAddress = Address.fromProgramId(programIDString); - expect(programAddress.to_string()).to.equal("aleo1lqmly7ez2k48ajf5hs92ulphaqr05qm4n8qwzj8v0yprmasgpqgsez59gg"); + expect(programAddress.to_string()).to.equal( + "aleo1lqmly7ez2k48ajf5hs92ulphaqr05qm4n8qwzj8v0yprmasgpqgsez59gg", + ); } if (connection.network === "testnet") { const programAddress = Address.fromProgramId(programIDString); - expect(programAddress.to_string()).to.equal("aleo1lqmly7ez2k48ajf5hs92ulphaqr05qm4n8qwzj8v0yprmasgpqgsez59gg"); + expect(programAddress.to_string()).to.equal( + "aleo1lqmly7ez2k48ajf5hs92ulphaqr05qm4n8qwzj8v0yprmasgpqgsez59gg", + ); } - }); - }) + }); + }); }); - diff --git a/sdk/tsconfig.json b/sdk/tsconfig.json index b933e4d3d..13bd40781 100644 --- a/sdk/tsconfig.json +++ b/sdk/tsconfig.json @@ -1,28 +1,28 @@ { - // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs - "include": ["src"], - "exclude": ["node_modules", "tests"], - "compilerOptions": { - "outDir": "./dist", - "target": "es2022", - "module": "esnext", - "lib": ["dom", "esnext", "webworker"], - "importHelpers": true, - // output .d.ts declaration files for consumers - "declaration": true, - // output .js.map sourcemap files for consumers - "sourceMap": true, - // match output dir to input dir. e.g. dist/index instead of dist/src/index - "rootDir": "./src", - // stricter type-checking for stronger correctness. Recommended by TS - "strict": true, - // use Node's module resolution algorithm, instead of the legacy TS one - "moduleResolution": "bundler", - // interop between ESM and CJS modules. Recommended by TS - "esModuleInterop": true, - // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS - "skipLibCheck": true, - // error out if import and file system have a casing mismatch. Recommended by TS - "forceConsistentCasingInFileNames": true - } + // see https://www.typescriptlang.org/tsconfig to better understand tsconfigs + "include": ["src"], + "exclude": ["node_modules", "tests"], + "compilerOptions": { + "outDir": "./dist", + "target": "es2022", + "module": "esnext", + "lib": ["dom", "esnext", "webworker"], + "importHelpers": true, + // output .d.ts declaration files for consumers + "declaration": true, + // output .js.map sourcemap files for consumers + "sourceMap": true, + // match output dir to input dir. e.g. dist/index instead of dist/src/index + "rootDir": "./src", + // stricter type-checking for stronger correctness. Recommended by TS + "strict": true, + // use Node's module resolution algorithm, instead of the legacy TS one + "moduleResolution": "bundler", + // interop between ESM and CJS modules. Recommended by TS + "esModuleInterop": true, + // significant perf increase by skipping checking .d.ts files, particularly those in node_modules. Recommended by TS + "skipLibCheck": true, + // error out if import and file system have a casing mismatch. Recommended by TS + "forceConsistentCasingInFileNames": true + } } diff --git a/sdk/tsconfig.test.json b/sdk/tsconfig.test.json index 45805ff8d..58729417e 100644 --- a/sdk/tsconfig.test.json +++ b/sdk/tsconfig.test.json @@ -1,6 +1,6 @@ { - "extends": "./tsconfig.json", - "compilerOptions": { - "rootDir": "." - } + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "." + } } diff --git a/wasm/LICENSE.md b/wasm/LICENSE.md index b95c626e2..bea2ca68d 100644 --- a/wasm/LICENSE.md +++ b/wasm/LICENSE.md @@ -1,5 +1,4 @@ -GNU General Public License -========================== +# GNU General Public License Version 3, 29 June 2007 @@ -10,59 +9,62 @@ document, but changing it is not allowed. ## Preamble -The GNU General Public License is a free, copyleft license for software and other -kinds of works. - -The licenses for most software and other practical works are designed to take away -your freedom to share and change the works. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change all versions of a -program--to make sure it remains free software for all its users. We, the Free -Software Foundation, use the GNU General Public License for most of our software; it -applies also to any other work released this way by its authors. You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our General -Public Licenses are designed to make sure that you have the freedom to distribute -copies of free software (and charge for them if you wish), that you receive source -code or can get it if you want it, that you can change the software or use pieces of -it in new free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you these rights or -asking you to surrender the rights. Therefore, you have certain responsibilities if -you distribute copies of the software, or if you modify it: responsibilities to -respect the freedom of others. - -For example, if you distribute copies of such a program, whether gratis or for a fee, -you must pass on to the recipients the same freedoms that you received. You must make -sure that they, too, receive or can get the source code. And you must show them these -terms so they know their rights. - -Developers that use the GNU GPL protect your rights with two steps: **(1)** assert -copyright on the software, and **(2)** offer you this License giving you legal permission -to copy, distribute and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains that there is -no warranty for this free software. For both users' and authors' sake, the GPL -requires that modified versions be marked as changed, so that their problems will not -be attributed erroneously to authors of previous versions. - -Some devices are designed to deny users access to install or run modified versions of -the software inside them, although the manufacturer can do so. This is fundamentally -incompatible with the aim of protecting users' freedom to change the software. The -systematic pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we have designed -this version of the GPL to prohibit the practice for those products. If such problems -arise substantially in other domains, we stand ready to extend this provision to -those domains in future versions of the GPL, as needed to protect the freedom of -users. - -Finally, every program is threatened constantly by software patents. States should -not allow patents to restrict development and use of software on general-purpose -computers, but in those that do, we wish to avoid the special danger that patents -applied to a free program could make it effectively proprietary. To prevent this, the -GPL assures that patents cannot be used to render the program non-free. - -The precise terms and conditions for copying, distribution and modification follow. +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most of +our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can do +these things. + +To protect your rights, we need to prevent others from denying you these rights +or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a +fee, you must pass on to the recipients the same freedoms that you received. You +must make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: **(1)** +assert copyright on the software, and **(2)** offer you this License giving you +legal permission to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there +is no warranty for this free software. For both users' and authors' sake, the +GPL requires that modified versions be marked as changed, so that their problems +will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to +change the software. The systematic pattern of such abuse occurs in the area of +products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. ## TERMS AND CONDITIONS @@ -70,70 +72,70 @@ The precise terms and conditions for copying, distribution and modification foll “This License” refers to version 3 of the GNU General Public License. -“Copyright” also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. +“Copyright” also means copyright-like laws that apply to other kinds of works, +such as semiconductor masks. -“The Program” refers to any copyrightable work licensed under this -License. Each licensee is addressed as “you”. “Licensees” and -“recipients” may be individuals or organizations. +“The Program” refers to any copyrightable work licensed under this License. Each +licensee is addressed as “you”. “Licensees” and “recipients” may be individuals +or organizations. -To “modify” a work means to copy from or adapt all or part of the work in -a fashion requiring copyright permission, other than the making of an exact copy. The -resulting work is called a “modified version” of the earlier work or a -work “based on” the earlier work. +To “modify” a work means to copy from or adapt all or part of the work in a +fashion requiring copyright permission, other than the making of an exact copy. +The resulting work is called a “modified version” of the earlier work or a work +“based on” the earlier work. -A “covered work” means either the unmodified Program or a work based on -the Program. +A “covered work” means either the unmodified Program or a work based on the +Program. -To “propagate” a work means to do anything with it that, without -permission, would make you directly or secondarily liable for infringement under -applicable copyright law, except executing it on a computer or modifying a private -copy. Propagation includes copying, distribution (with or without modification), +To “propagate” a work means to do anything with it that, without permission, +would make you directly or secondarily liable for infringement under applicable +copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. -To “convey” a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through a computer -network, with no transfer of a copy, is not conveying. +To “convey” a work means any kind of propagation that enables other parties to +make or receive copies. Mere interaction with a user through a computer network, +with no transfer of a copy, is not conveying. -An interactive user interface displays “Appropriate Legal Notices” to the -extent that it includes a convenient and prominently visible feature that **(1)** -displays an appropriate copyright notice, and **(2)** tells the user that there is no -warranty for the work (except to the extent that warranties are provided), that -licensees may convey the work under this License, and how to view a copy of this -License. If the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. +An interactive user interface displays “Appropriate Legal Notices” to the extent +that it includes a convenient and prominently visible feature that **(1)** +displays an appropriate copyright notice, and **(2)** tells the user that there +is no warranty for the work (except to the extent that warranties are provided), +that licensees may convey the work under this License, and how to view a copy of +this License. If the interface presents a list of user commands or options, such +as a menu, a prominent item in the list meets this criterion. ### 1. Source Code -The “source code” for a work means the preferred form of the work for -making modifications to it. “Object code” means any non-source form of a -work. - -A “Standard Interface” means an interface that either is an official -standard defined by a recognized standards body, or, in the case of interfaces -specified for a particular programming language, one that is widely used among -developers working in that language. - -The “System Libraries” of an executable work include anything, other than -the work as a whole, that **(a)** is included in the normal form of packaging a Major -Component, but which is not part of that Major Component, and **(b)** serves only to -enable use of the work with that Major Component, or to implement a Standard -Interface for which an implementation is available to the public in source code form. -A “Major Component”, in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system (if any) on which -the executable work runs, or a compiler used to produce the work, or an object code -interpreter used to run it. - -The “Corresponding Source” for a work in object code form means all the -source code needed to generate, install, and (for an executable work) run the object -code and to modify the work, including scripts to control those activities. However, -it does not include the work's System Libraries, or general-purpose tools or -generally available free programs which are used unmodified in performing those -activities but which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for the work, and -the source code for shared libraries and dynamically linked subprograms that the work -is specifically designed to require, such as by intimate data communication or -control flow between those subprograms and other parts of the work. +The “source code” for a work means the preferred form of the work for making +modifications to it. “Object code” means any non-source form of a work. + +A “Standard Interface” means an interface that either is an official standard +defined by a recognized standards body, or, in the case of interfaces specified +for a particular programming language, one that is widely used among developers +working in that language. + +The “System Libraries” of an executable work include anything, other than the +work as a whole, that **(a)** is included in the normal form of packaging a +Major Component, but which is not part of that Major Component, and **(b)** +serves only to enable use of the work with that Major Component, or to implement +a Standard Interface for which an implementation is available to the public in +source code form. A “Major Component”, in this context, means a major essential +component (kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to produce the +work, or an object code interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the source +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose +tools or generally available free programs which are used unmodified in +performing those activities but which are not part of the work. For example, +Corresponding Source includes interface definition files associated with source +files for the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, such as by +intimate data communication or control flow between those subprograms and other +parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. @@ -142,328 +144,341 @@ The Corresponding Source for a work in source code form is that same work. ### 2. Basic Permissions -All rights granted under this License are granted for the term of copyright on the -Program, and are irrevocable provided the stated conditions are met. This License -explicitly affirms your unlimited permission to run the unmodified Program. The -output from running a covered work is covered by this License only if the output, -given its content, constitutes a covered work. This License acknowledges your rights -of fair use or other equivalent, as provided by copyright law. +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This +License explicitly affirms your unlimited permission to run the unmodified +Program. The output from running a covered work is covered by this License only +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. You may make, run and propagate covered works that you do not convey, without -conditions so long as your license otherwise remains in force. You may convey covered -works to others for the sole purpose of having them make modifications exclusively -for you, or provide you with facilities for running those works, provided that you -comply with the terms of this License in conveying all material for which you do not -control copyright. Those thus making or running the covered works for you must do so -exclusively on your behalf, under your direction and control, on terms that prohibit -them from making any copies of your copyrighted material outside their relationship -with you. +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, +provided that you comply with the terms of this License in conveying all +material for which you do not control copyright. Those thus making or running +the covered works for you must do so exclusively on your behalf, under your +direction and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law -No covered work shall be deemed part of an effective technological measure under any -applicable law fulfilling obligations under article 11 of the WIPO copyright treaty -adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention -of such measures. +No covered work shall be deemed part of an effective technological measure under +any applicable law fulfilling obligations under article 11 of the WIPO copyright +treaty adopted on 20 December 1996, or similar laws prohibiting or restricting +circumvention of such measures. -When you convey a covered work, you waive any legal power to forbid circumvention of -technological measures to the extent such circumvention is effected by exercising -rights under this License with respect to the covered work, and you disclaim any -intention to limit operation or modification of the work as a means of enforcing, -against the work's users, your or third parties' legal rights to forbid circumvention -of technological measures. +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention is +effected by exercising rights under this License with respect to the covered +work, and you disclaim any intention to limit operation or modification of the +work as a means of enforcing, against the work's users, your or third parties' +legal rights to forbid circumvention of technological measures. ### 4. Conveying Verbatim Copies -You may convey verbatim copies of the Program's source code as you receive it, in any -medium, provided that you conspicuously and appropriately publish on each copy an -appropriate copyright notice; keep intact all notices stating that this License and -any non-permissive terms added in accord with section 7 apply to the code; keep -intact all notices of the absence of any warranty; and give all recipients a copy of -this License along with the Program. +You may convey verbatim copies of the Program's source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on each +copy an appropriate copyright notice; keep intact all notices stating that this +License and any non-permissive terms added in accord with section 7 apply to the +code; keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. -You may charge any price or no price for each copy that you convey, and you may offer -support or warranty protection for a fee. +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. ### 5. Conveying Modified Source Versions -You may convey a work based on the Program, or the modifications to produce it from -the Program, in the form of source code under the terms of section 4, provided that -you also meet all of these conditions: - -* **a)** The work must carry prominent notices stating that you modified it, and giving a -relevant date. -* **b)** The work must carry prominent notices stating that it is released under this -License and any conditions added under section 7. This requirement modifies the -requirement in section 4 to “keep intact all notices”. -* **c)** You must license the entire work, as a whole, under this License to anyone who -comes into possession of a copy. This License will therefore apply, along with any -applicable section 7 additional terms, to the whole of the work, and all its parts, -regardless of how they are packaged. This License gives no permission to license the -work in any other way, but it does not invalidate such permission if you have -separately received it. -* **d)** If the work has interactive user interfaces, each must display Appropriate Legal -Notices; however, if the Program has interactive interfaces that do not display -Appropriate Legal Notices, your work need not make them do so. - -A compilation of a covered work with other separate and independent works, which are -not by their nature extensions of the covered work, and which are not combined with -it such as to form a larger program, in or on a volume of a storage or distribution -medium, is called an “aggregate” if the compilation and its resulting -copyright are not used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work in an aggregate -does not cause this License to apply to the other parts of the aggregate. +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: + +- **a)** The work must carry prominent notices stating that you modified it, and + giving a relevant date. +- **b)** The work must carry prominent notices stating that it is released under + this License and any conditions added under section 7. This requirement + modifies the requirement in section 4 to “keep intact all notices”. +- **c)** You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will therefore apply, + along with any applicable section 7 additional terms, to the whole of the + work, and all its parts, regardless of how they are packaged. This License + gives no permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- **d)** If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive interfaces + that do not display Appropriate Legal Notices, your work need not make them do + so. + +A compilation of a covered work with other separate and independent works, which +are not by their nature extensions of the covered work, and which are not +combined with it such as to form a larger program, in or on a volume of a +storage or distribution medium, is called an “aggregate” if the compilation and +its resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a +covered work in an aggregate does not cause this License to apply to the other +parts of the aggregate. ### 6. Conveying Non-Source Forms -You may convey a covered work in object code form under the terms of sections 4 and -5, provided that you also convey the machine-readable Corresponding Source under the -terms of this License, in one of these ways: - -* **a)** Convey the object code in, or embodied in, a physical product (including a -physical distribution medium), accompanied by the Corresponding Source fixed on a -durable physical medium customarily used for software interchange. -* **b)** Convey the object code in, or embodied in, a physical product (including a -physical distribution medium), accompanied by a written offer, valid for at least -three years and valid for as long as you offer spare parts or customer support for -that product model, to give anyone who possesses the object code either **(1)** a copy of -the Corresponding Source for all the software in the product that is covered by this -License, on a durable physical medium customarily used for software interchange, for -a price no more than your reasonable cost of physically performing this conveying of -source, or **(2)** access to copy the Corresponding Source from a network server at no -charge. -* **c)** Convey individual copies of the object code with a copy of the written offer to -provide the Corresponding Source. This alternative is allowed only occasionally and -noncommercially, and only if you received the object code with such an offer, in -accord with subsection 6b. -* **d)** Convey the object code by offering access from a designated place (gratis or for -a charge), and offer equivalent access to the Corresponding Source in the same way -through the same place at no further charge. You need not require recipients to copy -the Corresponding Source along with the object code. If the place to copy the object -code is a network server, the Corresponding Source may be on a different server -(operated by you or a third party) that supports equivalent copying facilities, -provided you maintain clear directions next to the object code saying where to find -the Corresponding Source. Regardless of what server hosts the Corresponding Source, -you remain obligated to ensure that it is available for as long as needed to satisfy -these requirements. -* **e)** Convey the object code using peer-to-peer transmission, provided you inform -other peers where the object code and Corresponding Source of the work are being -offered to the general public at no charge under subsection 6d. +You may convey a covered work in object code form under the terms of sections 4 +and 5, provided that you also convey the machine-readable Corresponding Source +under the terms of this License, in one of these ways: + +- **a)** Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the Corresponding + Source fixed on a durable physical medium customarily used for software + interchange. +- **b)** Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a written offer, + valid for at least three years and valid for as long as you offer spare parts + or customer support for that product model, to give anyone who possesses the + object code either **(1)** a copy of the Corresponding Source for all the + software in the product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no more than + your reasonable cost of physically performing this conveying of source, or + **(2)** access to copy the Corresponding Source from a network server at no + charge. +- **c)** Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed only + occasionally and noncommercially, and only if you received the object code + with such an offer, in accord with subsection 6b. +- **d)** Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the Corresponding + Source in the same way through the same place at no further charge. You need + not require recipients to copy the Corresponding Source along with the object + code. If the place to copy the object code is a network server, the + Corresponding Source may be on a different server (operated by you or a third + party) that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the Corresponding + Source, you remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. +- **e)** Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the work + are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. -A “User Product” is either **(1)** a “consumer product”, which -means any tangible personal property which is normally used for personal, family, or -household purposes, or **(2)** anything designed or sold for incorporation into a -dwelling. In determining whether a product is a consumer product, doubtful cases -shall be resolved in favor of coverage. For a particular product received by a -particular user, “normally used” refers to a typical or common use of -that class of product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected to use, the -product. A product is a consumer product regardless of whether the product has -substantial commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - -“Installation Information” for a User Product means any methods, -procedures, authorization keys, or other information required to install and execute -modified versions of a covered work in that User Product from a modified version of -its Corresponding Source. The information must suffice to ensure that the continued -functioning of the modified object code is in no case prevented or interfered with -solely because modification has been made. - -If you convey an object code work under this section in, or with, or specifically for -use in, a User Product, and the conveying occurs as part of a transaction in which -the right of possession and use of the User Product is transferred to the recipient -in perpetuity or for a fixed term (regardless of how the transaction is -characterized), the Corresponding Source conveyed under this section must be -accompanied by the Installation Information. But this requirement does not apply if -neither you nor any third party retains the ability to install modified object code -on the User Product (for example, the work has been installed in ROM). - -The requirement to provide Installation Information does not include a requirement to -continue to provide support service, warranty, or updates for a work that has been -modified or installed by the recipient, or for the User Product in which it has been -modified or installed. Access to a network may be denied when the modification itself -materially and adversely affects the operation of the network or violates the rules -and protocols for communication across the network. - -Corresponding Source conveyed, and Installation Information provided, in accord with -this section must be in a format that is publicly documented (and with an +A “User Product” is either **(1)** a “consumer product”, which means any +tangible personal property which is normally used for personal, family, or +household purposes, or **(2)** anything designed or sold for incorporation into +a dwelling. In determining whether a product is a consumer product, doubtful +cases shall be resolved in favor of coverage. For a particular product received +by a particular user, “normally used” refers to a typical or common use of that +class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, +the product. A product is a consumer product regardless of whether the product +has substantial commercial, industrial or non-consumer uses, unless such uses +represent the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified version +of its Corresponding Source. The information must suffice to ensure that the +continued functioning of the modified object code is in no case prevented or +interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as part of a +transaction in which the right of possession and use of the User Product is +transferred to the recipient in perpetuity or for a fixed term (regardless of +how the transaction is characterized), the Corresponding Source conveyed under +this section must be accompanied by the Installation Information. But this +requirement does not apply if neither you nor any third party retains the +ability to install modified object code on the User Product (for example, the +work has been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates for a +work that has been modified or installed by the recipient, or for the User +Product in which it has been modified or installed. Access to a network may be +denied when the modification itself materially and adversely affects the +operation of the network or violates the rules and protocols for communication +across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord +with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. ### 7. Additional Terms -“Additional permissions” are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. Additional -permissions that are applicable to the entire Program shall be treated as though they -were included in this License, to the extent that they are valid under applicable -law. If additional permissions apply only to part of the Program, that part may be -used separately under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. +“Additional permissions” are terms that supplement the terms of this License by +making exceptions from one or more of its conditions. Additional permissions +that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may +be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional -permissions may be written to require their own removal in certain cases when you -modify the work.) You may place additional permissions on material, added by you to a -covered work, for which you have or can give appropriate copyright permission. +permissions may be written to require their own removal in certain cases when +you modify the work.) You may place additional permissions on material, added by +you to a covered work, for which you have or can give appropriate copyright +permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: -* **a)** Disclaiming warranty or limiting liability differently from the terms of -sections 15 and 16 of this License; or -* **b)** Requiring preservation of specified reasonable legal notices or author -attributions in that material or in the Appropriate Legal Notices displayed by works -containing it; or -* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that -modified versions of such material be marked in reasonable ways as different from the -original version; or -* **d)** Limiting the use for publicity purposes of names of licensors or authors of the -material; or -* **e)** Declining to grant rights under trademark law for use of some trade names, -trademarks, or service marks; or -* **f)** Requiring indemnification of licensors and authors of that material by anyone -who conveys the material (or modified versions of it) with contractual assumptions of -liability to the recipient, for any liability that these contractual assumptions -directly impose on those licensors and authors. - -All other non-permissive additional terms are considered “further -restrictions” within the meaning of section 10. If the Program as you received -it, or any part of it, contains a notice stating that it is governed by this License -along with a term that is a further restriction, you may remove that term. If a -license document contains a further restriction but permits relicensing or conveying -under this License, you may add to a covered work material governed by the terms of -that license document, provided that the further restriction does not survive such -relicensing or conveying. - -If you add terms to a covered work in accord with this section, you must place, in -the relevant source files, a statement of the additional terms that apply to those -files, or a notice indicating where to find the applicable terms. +- **a)** Disclaiming warranty or limiting liability differently from the terms + of sections 15 and 16 of this License; or +- **b)** Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices displayed by + works containing it; or +- **c)** Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in reasonable ways + as different from the original version; or +- **d)** Limiting the use for publicity purposes of names of licensors or + authors of the material; or +- **e)** Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or +- **f)** Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with contractual + assumptions of liability to the recipient, for any liability that these + contractual assumptions directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further restrictions” +within the meaning of section 10. If the Program as you received it, or any part +of it, contains a notice stating that it is governed by this License along with +a term that is a further restriction, you may remove that term. If a license +document contains a further restriction but permits relicensing or conveying +under this License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does not survive +such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, +in the relevant source files, a statement of the additional terms that apply to +those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a -separately written license, or stated as exceptions; the above requirements apply -either way. +separately written license, or stated as exceptions; the above requirements +apply either way. ### 8. Termination -You may not propagate or modify a covered work except as expressly provided under -this License. Any attempt otherwise to propagate or modify it is void, and will -automatically terminate your rights under this License (including any patent licenses -granted under the third paragraph of section 11). +You may not propagate or modify a covered work except as expressly provided +under this License. Any attempt otherwise to propagate or modify it is void, and +will automatically terminate your rights under this License (including any +patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a -particular copyright holder is reinstated **(a)** provisionally, unless and until the -copyright holder explicitly and finally terminates your license, and **(b)** permanently, -if the copyright holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. +particular copyright holder is reinstated **(a)** provisionally, unless and +until the copyright holder explicitly and finally terminates your license, and +**(b)** permanently, if the copyright holder fails to notify you of the +violation by some reasonable means prior to 60 days after the cessation. -Moreover, your license from a particular copyright holder is reinstated permanently -if the copyright holder notifies you of the violation by some reasonable means, this -is the first time you have received notice of violation of this License (for any -work) from that copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. +Moreover, your license from a particular copyright holder is reinstated +permanently if the copyright holder notifies you of the violation by some +reasonable means, this is the first time you have received notice of violation +of this License (for any work) from that copyright holder, and you cure the +violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your -rights have been terminated and not permanently reinstated, you do not qualify to -receive new licenses for the same material under section 10. +rights have been terminated and not permanently reinstated, you do not qualify +to receive new licenses for the same material under section 10. ### 9. Acceptance Not Required for Having Copies -You are not required to accept this License in order to receive or run a copy of the -Program. Ancillary propagation of a covered work occurring solely as a consequence of -using peer-to-peer transmission to receive a copy likewise does not require -acceptance. However, nothing other than this License grants you permission to -propagate or modify any covered work. These actions infringe copyright if you do not -accept this License. Therefore, by modifying or propagating a covered work, you -indicate your acceptance of this License to do so. +You are not required to accept this License in order to receive or run a copy of +the Program. Ancillary propagation of a covered work occurring solely as a +consequence of using peer-to-peer transmission to receive a copy likewise does +not require acceptance. However, nothing other than this License grants you +permission to propagate or modify any covered work. These actions infringe +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to do +so. ### 10. Automatic Licensing of Downstream Recipients -Each time you convey a covered work, the recipient automatically receives a license -from the original licensors, to run, modify and propagate that work, subject to this -License. You are not responsible for enforcing compliance by third parties with this -License. +Each time you convey a covered work, the recipient automatically receives a +license from the original licensors, to run, modify and propagate that work, +subject to this License. You are not responsible for enforcing compliance by +third parties with this License. An “entity transaction” is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an organization, or -merging organizations. If propagation of a covered work results from an entity -transaction, each party to that transaction who receives a copy of the work also -receives whatever licenses to the work the party's predecessor in interest had or -could give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if the predecessor -has it or can get it with reasonable efforts. - -You may not impose any further restrictions on the exercise of the rights granted or -affirmed under this License. For example, you may not impose a license fee, royalty, -or other charge for exercise of rights granted under this License, and you may not -initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging -that any patent claim is infringed by making, using, selling, offering for sale, or -importing the Program or any portion of it. +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered work results +from an entity transaction, each party to that transaction who receives a copy +of the work also receives whatever licenses to the work the party's predecessor +in interest had or could give under the previous paragraph, plus a right to +possession of the Corresponding Source of the work from the predecessor in +interest, if the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights +granted or affirmed under this License. For example, you may not impose a +license fee, royalty, or other charge for exercise of rights granted under this +License, and you may not initiate litigation (including a cross-claim or +counterclaim in a lawsuit) alleging that any patent claim is infringed by +making, using, selling, offering for sale, or importing the Program or any +portion of it. ### 11. Patents -A “contributor” is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The work thus -licensed is called the contributor's “contributor version”. +A “contributor” is a copyright holder who authorizes use under this License of +the Program or a work on which the Program is based. The work thus licensed is +called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or -controlled by the contributor, whether already acquired or hereafter acquired, that -would be infringed by some manner, permitted by this License, of making, using, or -selling its contributor version, but do not include claims that would be infringed -only as a consequence of further modification of the contributor version. For -purposes of this definition, “control” includes the right to grant patent -sublicenses in a manner consistent with the requirements of this License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free patent license -under the contributor's essential patent claims, to make, use, sell, offer for sale, -import and otherwise run, modify and propagate the contents of its contributor -version. - -In the following three paragraphs, a “patent license” is any express -agreement or commitment, however denominated, not to enforce a patent (such as an -express permission to practice a patent or covenant not to sue for patent -infringement). To “grant” such a patent license to a party means to make -such an agreement or commitment not to enforce a patent against the party. +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would +be infringed only as a consequence of further modification of the contributor +version. For purposes of this definition, “control” includes the right to grant +patent sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent +license under the contributor's essential patent claims, to make, use, sell, +offer for sale, import and otherwise run, modify and propagate the contents of +its contributor version. + +In the following three paragraphs, a “patent license” is any express agreement +or commitment, however denominated, not to enforce a patent (such as an express +permission to practice a patent or covenant not to sue for patent infringement). +To “grant” such a patent license to a party means to make such an agreement or +commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the -Corresponding Source of the work is not available for anyone to copy, free of charge -and under the terms of this License, through a publicly available network server or -other readily accessible means, then you must either **(1)** cause the Corresponding -Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the -patent license for this particular work, or **(3)** arrange, in a manner consistent with -the requirements of this License, to extend the patent license to downstream -recipients. “Knowingly relying” means you have actual knowledge that, but -for the patent license, your conveying the covered work in a country, or your -recipient's use of the covered work in a country, would infringe one or more -identifiable patents in that country that you have reason to believe are valid. +Corresponding Source of the work is not available for anyone to copy, free of +charge and under the terms of this License, through a publicly available network +server or other readily accessible means, then you must either **(1)** cause the +Corresponding Source to be so available, or **(2)** arrange to deprive yourself +of the benefit of the patent license for this particular work, or **(3)** +arrange, in a manner consistent with the requirements of this License, to extend +the patent license to downstream recipients. “Knowingly relying” means you have +actual knowledge that, but for the patent license, your conveying the covered +work in a country, or your recipient's use of the covered work in a country, +would infringe one or more identifiable patents in that country that you have +reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you -convey, or propagate by procuring conveyance of, a covered work, and grant a patent -license to some of the parties receiving the covered work authorizing them to use, -propagate, modify or convey a specific copy of the covered work, then the patent -license you grant is automatically extended to all recipients of the covered work and -works based on it. - -A patent license is “discriminatory” if it does not include within the -scope of its coverage, prohibits the exercise of, or is conditioned on the -non-exercise of one or more of the rights that are specifically granted under this -License. You may not convey a covered work if you are a party to an arrangement with -a third party that is in the business of distributing software, under which you make -payment to the third party based on the extent of your activity of conveying the -work, and under which the third party grants, to any of the parties who would receive -the covered work from you, a discriminatory patent license **(a)** in connection with -copies of the covered work conveyed by you (or copies made from those copies), or **(b)** -primarily for and in connection with specific products or compilations that contain -the covered work, unless you entered into that arrangement, or that patent license -was granted, prior to 28 March 2007. +convey, or propagate by procuring conveyance of, a covered work, and grant a +patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, +then the patent license you grant is automatically extended to all recipients of +the covered work and works based on it. + +A patent license is “discriminatory” if it does not include within the scope of +its coverage, prohibits the exercise of, or is conditioned on the non-exercise +of one or more of the rights that are specifically granted under this License. +You may not convey a covered work if you are a party to an arrangement with a +third party that is in the business of distributing software, under which you +make payment to the third party based on the extent of your activity of +conveying the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory patent +license **(a)** in connection with copies of the covered work conveyed by you +(or copies made from those copies), or **(b)** primarily for and in connection +with specific products or compilations that contain the covered work, unless you +entered into that arrangement, or that patent license was granted, prior to 28 +March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you @@ -471,51 +486,55 @@ under applicable patent law. ### 12. No Surrender of Others' Freedom -If conditions are imposed on you (whether by court order, agreement or otherwise) -that contradict the conditions of this License, they do not excuse you from the -conditions of this License. If you cannot convey a covered work so as to satisfy -simultaneously your obligations under this License and any other pertinent -obligations, then as a consequence you may not convey it at all. For example, if you -agree to terms that obligate you to collect a royalty for further conveying from -those to whom you convey the Program, the only way you could satisfy both those terms -and this License would be to refrain entirely from conveying the Program. +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not excuse +you from the conditions of this License. If you cannot convey a covered work so +as to satisfy simultaneously your obligations under this License and any other +pertinent obligations, then as a consequence you may not convey it at all. For +example, if you agree to terms that obligate you to collect a royalty for +further conveying from those to whom you convey the Program, the only way you +could satisfy both those terms and this License would be to refrain entirely +from conveying the Program. ### 13. Use with the GNU Affero General Public License -Notwithstanding any other provision of this License, you have permission to link or -combine any covered work with a work licensed under version 3 of the GNU Affero -General Public License into a single combined work, and to convey the resulting work. -The terms of this License will continue to apply to the part which is the covered -work, but the special requirements of the GNU Affero General Public License, section -13, concerning interaction through a network will apply to the combination as such. +Notwithstanding any other provision of this License, you have permission to link +or combine any covered work with a work licensed under version 3 of the GNU +Affero General Public License into a single combined work, and to convey the +resulting work. The terms of this License will continue to apply to the part +which is the covered work, but the special requirements of the GNU Affero +General Public License, section 13, concerning interaction through a network +will apply to the combination as such. ### 14. Revised Versions of this License The Free Software Foundation may publish revised and/or new versions of the GNU -General Public License from time to time. Such new versions will be similar in spirit -to the present version, but may differ in detail to address new problems or concerns. +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. -Each version is given a distinguishing version number. If the Program specifies that -a certain numbered version of the GNU General Public License “or any later +Each version is given a distinguishing version number. If the Program specifies +that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and -conditions either of that numbered version or of any later version published by the -Free Software Foundation. If the Program does not specify a version number of the GNU -General Public License, you may choose any version ever published by the Free -Software Foundation. - -If the Program specifies that a proxy can decide which future versions of the GNU -General Public License can be used, that proxy's public statement of acceptance of a -version permanently authorizes you to choose that version for the Program. +conditions either of that numbered version or of any later version published by +the Free Software Foundation. If the Program does not specify a version number +of the GNU General Public License, you may choose any version ever published by +the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the +GNU General Public License can be used, that proxy's public statement of +acceptance of a version permanently authorizes you to choose that version for +the Program. -Later license versions may give you additional or different permissions. However, no -additional obligations are imposed on any author or copyright holder as a result of -your choosing to follow a later version. +Later license versions may give you additional or different permissions. +However, no additional obligations are imposed on any author or copyright holder +as a result of your choosing to follow a later version. ### 15. Disclaimer of Warranty THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER +PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE @@ -526,32 +545,32 @@ DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, -INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE -OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE -WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### 17. Interpretation of Sections 15 and 16 -If the disclaimer of warranty and limitation of liability provided above cannot be -given local legal effect according to their terms, reviewing courts shall apply local -law that most closely approximates an absolute waiver of all civil liability in -connection with the Program, unless a warranty or assumption of liability accompanies -a copy of the Program in return for a fee. +If the disclaimer of warranty and limitation of liability provided above cannot +be given local legal effect according to their terms, reviewing courts shall +apply local law that most closely approximates an absolute waiver of all civil +liability in connection with the Program, unless a warranty or assumption of +liability accompanies a copy of the Program in return for a fee. _END OF TERMS AND CONDITIONS_ ## How to Apply These Terms to Your New Programs -If you develop a new program, and you want it to be of the greatest possible use to -the public, the best way to achieve this is to make it free software which everyone -can redistribute and change under these terms. +If you develop a new program, and you want it to be of the greatest possible use +to the public, the best way to achieve this is to make it free software which +everyone can redistribute and change under these terms. -To do so, attach the following notices to the program. It is safest to attach them -to the start of each source file to most effectively state the exclusion of warranty; -and each file should have at least the “copyright” line and a pointer to -where the full notice is found. +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion of +warranty; and each file should have at least the “copyright” line and a pointer +to where the full notice is found. <one line to give the program's name and a brief idea of what it does.> Copyright (C) <year> <name of author> @@ -571,26 +590,26 @@ where the full notice is found. Also add information on how to contact you by electronic and paper mail. -If the program does terminal interaction, make it output a short notice like this -when it starts in an interactive mode: +If the program does terminal interaction, make it output a short notice like +this when it starts in an interactive mode: <program> Copyright (C) <year> <name of author> This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. -The hypothetical commands `show w` and `show c` should show the appropriate parts of -the General Public License. Of course, your program's commands might be different; -for a GUI interface, you would use an “about box”. +The hypothetical commands `show w` and `show c` should show the appropriate +parts of the General Public License. Of course, your program's commands might be +different; for a GUI interface, you would use an “about box”. -You should also get your employer (if you work as a programmer) or school, if any, to -sign a “copyright disclaimer” for the program, if necessary. For more +You should also get your employer (if you work as a programmer) or school, if +any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see &lt;<http://www.gnu.org/licenses/>&gt;. The GNU General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may consider it -more useful to permit linking proprietary applications with the library. If this is -what you want to do, use the GNU Lesser General Public License instead of this -License. But first, please read +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. But first, please read &lt;<http://www.gnu.org/philosophy/why-not-lgpl.html>&gt;. diff --git a/wasm/README.md b/wasm/README.md index e062be9ad..f32e6616b 100644 --- a/wasm/README.md +++ b/wasm/README.md @@ -3,35 +3,50 @@ [![github]](https://github.com/ProvableHQ/sdk) -[github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github -[crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust -[docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs +[github]: + https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github +[crates-io]: + https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust +[docs-rs]: + https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs # Aleo Wasm -Aleo JavaScript and WebAssembly bindings for building zero-knowledge web applications. +Aleo JavaScript and WebAssembly bindings for building zero-knowledge web +applications. -`Rust` compiles easily to `WebAssembly`, but creating the glue code necessary to use compiled WebAssembly binaries -from other languages such as JavaScript is a challenging task. `wasm-bindgen` is a tool that simplifies this process by -auto-generating JavaScript bindings to Rust code that has been compiled into WebAssembly. +`Rust` compiles easily to `WebAssembly`, but creating the glue code necessary to +use compiled WebAssembly binaries from other languages such as JavaScript is a +challenging task. `wasm-bindgen` is a tool that simplifies this process by +auto-generating JavaScript bindings to Rust code that has been compiled into +WebAssembly. -This crate uses `wasm-bindgen` to create JavaScript bindings to Aleo source code so that it can be used to create zero-knowledge proofs directly within web browsers and `Node.js`. +This crate uses `wasm-bindgen` to create JavaScript bindings to Aleo source code +so that it can be used to create zero-knowledge proofs directly within web +browsers and `Node.js`. Functionality exposed by this crate includes: -* Aleo account management objects -* Aleo primitives such as `Records`, `Programs`, and `Transactions` and their associated helper methods -* A `ProgramManager` object that contains methods for authoring, deploying, and interacting with Aleo programs -More information on these concepts can be found at the [Aleo Developer Hub](https://docs.leo-lang.org/concepts). +- Aleo account management objects +- Aleo primitives such as `Records`, `Programs`, and `Transactions` and their + associated helper methods +- A `ProgramManager` object that contains methods for authoring, deploying, and + interacting with Aleo programs + +More information on these concepts can be found at the +[Aleo Developer Hub](https://docs.leo-lang.org/concepts). ## Usage -The [rollup-plugin-rust](https://github.com/wasm-tool/rollup-plugin-rust/) tool is used to compile the Rust code in this crate into JavaScript -modules which can be imported into other JavaScript projects. +The [rollup-plugin-rust](https://github.com/wasm-tool/rollup-plugin-rust/) tool +is used to compile the Rust code in this crate into JavaScript modules which can +be imported into other JavaScript projects. #### Installation -Follow the [installation instructions](https://github.com/wasm-tool/rollup-plugin-rust/#installation) on the rollup-plugin-rust README. +Follow the +[installation instructions](https://github.com/wasm-tool/rollup-plugin-rust/#installation) +on the rollup-plugin-rust README. ### Build Instructions @@ -44,18 +59,21 @@ This will produce `.js` and `.wasm` files inside of the `dist` folder. ## Testing Run tests in Node.js + ```bash wasm-pack test --node ``` Run tests in a browser + ```bash wasm-pack test --[firefox/chrome/safari] ``` ## Building Web Apps -Further documentation and tutorials as to how to use the modules built from this crate to build web apps will be built -in the future. However, in the meantime, the [provable.tools](https://provable.tools) website is a good -example of how to use these modules to build a web app. Its source code can be found in the +Further documentation and tutorials as to how to use the modules built from this +crate to build web apps will be built in the future. However, in the meantime, +the [provable.tools](https://provable.tools) website is a good example of how to +use these modules to build a web app. Its source code can be found in the [Provable SDK](https://github.com/ProvableHQ/sdk) repo in the `website` folder. diff --git a/wasm/build.js b/wasm/build.js index b5d3f5245..efcd532f3 100644 --- a/wasm/build.js +++ b/wasm/build.js @@ -4,55 +4,59 @@ import { rollup } from "rollup"; import virtual from "@rollup/plugin-virtual"; import rust from "@wasm-tool/rollup-plugin-rust"; - async function buildRollup(input, output) { const bundle = await rollup(input); try { await bundle.write(output); - } finally { await bundle.close(); } } - async function buildWasm(network) { - await buildRollup({ - input: { - "aleo_wasm": "./Cargo.toml", - "aleo_wasm_custom": "./Cargo.toml?custom", + await buildRollup( + { + input: { + aleo_wasm: "./Cargo.toml", + aleo_wasm_custom: "./Cargo.toml?custom", + }, + plugins: [ + rust({ + extraArgs: { + cargo: [ + "--no-default-features", + "--features", + `browser,${network}`, + ], + rustc: ["-C", "link-arg=--max-memory=4294967296"], + wasmOpt: [ + "-O", + "--enable-threads", + "--enable-bulk-memory", + "--enable-bulk-memory-opt", + "--enable-nontrapping-float-to-int", + ], + }, + + experimental: { + atomics: true, + typescriptDeclarationDir: `dist/${network}`, + }, + }), + ], }, - plugins: [ - rust({ - extraArgs: { - cargo: [ - "--no-default-features", - "--features", `browser,${network}`, - ], - rustc: [ - "-C", "link-arg=--max-memory=4294967296", - ], - wasmOpt: ["-O", "--enable-threads", "--enable-bulk-memory", "--enable-bulk-memory-opt", "--enable-nontrapping-float-to-int"], - }, - - experimental: { - atomics: true, - typescriptDeclarationDir: `dist/${network}`, - }, - }), - ], - }, { - dir: `dist/${network}/tmp`, - format: "es", - sourcemap: true, - assetFileNames: `[name][extname]`, - chunkFileNames: `[name].js`, - entryFileNames: `[name].js`, - }); + { + dir: `dist/${network}/tmp`, + format: "es", + sourcemap: true, + assetFileNames: `[name][extname]`, + chunkFileNames: `[name].js`, + entryFileNames: `[name].js`, + }, + ); } - async function buildJS(network) { const js = `export * from "./dist/${network}/tmp/aleo_wasm.js"; @@ -68,23 +72,25 @@ export async function initThreadPool(threads) { await wasmInitThreadPool(new URL("worker.js", import.meta.url), threads); }`; - await buildRollup({ - input: { - "index": "entry", + await buildRollup( + { + input: { + index: "entry", + }, + plugins: [ + virtual({ + entry: js, + }), + ], + }, + { + dir: `dist/${network}`, + format: "es", + sourcemap: true, }, - plugins: [ - virtual({ - "entry": js, - }), - ], - }, { - dir: `dist/${network}`, - format: "es", - sourcemap: true, - }); + ); } - async function buildWorker(network) { const worker = `import { init } from "./dist/${network}/tmp/aleo_wasm_custom.js"; @@ -118,23 +124,25 @@ async function initializeWorker() { await initializeWorker();`; - await buildRollup({ - input: { - "worker": "entry", + await buildRollup( + { + input: { + worker: "entry", + }, + plugins: [ + virtual({ + entry: worker, + }), + ], }, - plugins: [ - virtual({ - "entry": worker, - }), - ], - }, { - dir: `dist/${network}`, - format: "es", - sourcemap: true, - }); + { + dir: `dist/${network}`, + format: "es", + sourcemap: true, + }, + ); } - async function buildTypes(network) { const js = `/** * Initializes a thread pool of Workers. This enables multi-threading, which significantly improves performance. @@ -147,7 +155,7 @@ export * from "./aleo_wasm.js";`; const worker = `export {};`; - await $fs.mkdir(`dist/${network}`, { recursive: true }) + await $fs.mkdir(`dist/${network}`, { recursive: true }); await Promise.all([ $fs.writeFile(`dist/${network}/index.d.ts`, js), @@ -155,7 +163,6 @@ export * from "./aleo_wasm.js";`; ]); } - // This uses multiple Rollup builds, instead of 1 build. // // The reason is because the `worker.js` file needs to be @@ -169,15 +176,9 @@ export * from "./aleo_wasm.js";`; // and `worker.js` builds, so we build the Wasm, and then // build the `index.js` and `worker.js` separately. async function build(network) { - await Promise.all([ - buildTypes(network), - buildWasm(network), - ]); + await Promise.all([buildTypes(network), buildWasm(network)]); - await Promise.all([ - buildJS(network), - buildWorker(network), - ]); + await Promise.all([buildJS(network), buildWorker(network)]); await $fs.rename( $path.join("dist", network, "tmp", "aleo_wasm.wasm"), @@ -190,13 +191,9 @@ async function build(network) { ]); } - console.time("Building wasm"); -const networks = [ - "testnet", - "mainnet", -]; +const networks = ["testnet", "mainnet"]; await Promise.all(networks.map(build)); diff --git a/wasm/package.json b/wasm/package.json index c3cfd10e3..3ca1b686a 100644 --- a/wasm/package.json +++ b/wasm/package.json @@ -1,51 +1,51 @@ { - "name": "@provablehq/wasm", - "version": "0.9.18", - "type": "module", - "description": "SnarkVM WASM binaries with javascript bindings", - "collaborators": [ - "The Provable Team" - ], - "license": "GPL-3.0", - "main": "./dist/testnet/index.js", - "browser": "./dist/testnet/index.js", - "types": "./dist/testnet/index.d.ts", - "exports": { - ".": "./dist/testnet/index.js", - "./worker.js": "./dist/testnet/worker.js", - "./testnet.js": "./dist/testnet/index.js", - "./testnet/worker.js": "./dist/testnet/worker.js", - "./mainnet.js": "./dist/mainnet/index.js", - "./mainnet/worker.js": "./dist/mainnet/worker.js" - }, - "files": [ - "dist", - "LICENSE.md", - "README.md" - ], - "repository": { - "type": "git", - "url": "git+https://github.com/ProvableHQ/sdk.git" - }, - "keywords": [ - "Aleo", - "Blockchain", - "Zero-Knowledge", - "ZK" - ], - "bugs": { - "url": "https://github.com/ProvableHQ/sdk/issues" - }, - "homepage": "https://github.com/ProvableHQ/sdk#readme", - "scripts": { - "build": "rimraf dist && node build.js", - "test": "node test.js" - }, - "devDependencies": { - "@rollup/plugin-virtual": "^3.0.2", - "@wasm-tool/rollup-plugin-rust": "^3.1.4", - "binaryen": "^120.0.0", - "rimraf": "^6.0.1", - "rollup": "^4.59.0" - } + "name": "@provablehq/wasm", + "version": "0.9.18", + "type": "module", + "description": "SnarkVM WASM binaries with javascript bindings", + "collaborators": [ + "The Provable Team" + ], + "license": "GPL-3.0", + "main": "./dist/testnet/index.js", + "browser": "./dist/testnet/index.js", + "types": "./dist/testnet/index.d.ts", + "exports": { + ".": "./dist/testnet/index.js", + "./worker.js": "./dist/testnet/worker.js", + "./testnet.js": "./dist/testnet/index.js", + "./testnet/worker.js": "./dist/testnet/worker.js", + "./mainnet.js": "./dist/mainnet/index.js", + "./mainnet/worker.js": "./dist/mainnet/worker.js" + }, + "files": [ + "dist", + "LICENSE.md", + "README.md" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/ProvableHQ/sdk.git" + }, + "keywords": [ + "Aleo", + "Blockchain", + "Zero-Knowledge", + "ZK" + ], + "bugs": { + "url": "https://github.com/ProvableHQ/sdk/issues" + }, + "homepage": "https://github.com/ProvableHQ/sdk#readme", + "scripts": { + "build": "rimraf dist && node build.js", + "test": "node test.js" + }, + "devDependencies": { + "@rollup/plugin-virtual": "^3.0.2", + "@wasm-tool/rollup-plugin-rust": "^3.1.4", + "binaryen": "^120.0.0", + "rimraf": "^6.0.1", + "rollup": "^4.59.0" + } } diff --git a/wasm/test.js b/wasm/test.js index e5cf44377..b5c983d4d 100644 --- a/wasm/test.js +++ b/wasm/test.js @@ -7,11 +7,13 @@ $fs.writeFileSync("package.json", file.replace(/"type": "module",/g, "")); try { ["testnet", "mainnet"].forEach((network) => { - $child.execSync(`wasm-pack test --node --features ${network} -- --nocapture`, { - stdio: "inherit", - }); + $child.execSync( + `wasm-pack test --node --features ${network} -- --nocapture`, + { + stdio: "inherit", + }, + ); }); - } finally { $fs.writeFileSync("package.json", file); } diff --git a/website/.eslintrc.cjs b/website/.eslintrc.cjs index e0d8ab3ad..00b0ae2a0 100644 --- a/website/.eslintrc.cjs +++ b/website/.eslintrc.cjs @@ -11,6 +11,6 @@ module.exports = { plugins: ["react-refresh"], rules: { "react-refresh/only-export-components": "warn", - "react/prop-types":"off" + "react/prop-types": "off", }, }; diff --git a/website/README.md b/website/README.md index ea5c07a8f..a3328246e 100644 --- a/website/README.md +++ b/website/README.md @@ -6,9 +6,12 @@ This project was bootstrapped with [Vite](https://vitejs.dev/). ### Prerequisites -- Follow the [SDK Build Guide](https://github.com/ProvableHQ/sdk#2-build-guide) to get Rust installed -- Install Node.js `18` or `20` through the [official website](https://nodejs.org/) or via a node manager like [NVM](https://github.com/creationix/nvm) -- Install [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) +- Follow the [SDK Build Guide](https://github.com/ProvableHQ/sdk#2-build-guide) + to get Rust installed +- Install Node.js `18` or `20` through the + [official website](https://nodejs.org/) or via a node manager like + [NVM](https://github.com/creationix/nvm) +- Install [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) ```bash yarn @@ -24,4 +27,5 @@ In the project directory, you can run: ### `yarn build` Builds the app for production to the `dist` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. +It correctly bundles React in production mode and optimizes the build for the +best performance. diff --git a/website/index.html b/website/index.html index 66149b3d0..bd7e5b1af 100644 --- a/website/index.html +++ b/website/index.html @@ -23,11 +23,12 @@ position: relative; text-align: center; - -webkit-animation: sk-rotate 2.0s infinite linear; - animation: sk-rotate 2.0s infinite linear; + -webkit-animation: sk-rotate 2s infinite linear; + animation: sk-rotate 2s infinite linear; } - .dot1, .dot2 { + .dot1, + .dot2 { width: 60%; height: 60%; display: inline-block; @@ -36,33 +37,49 @@ background-color: #18e48f; border-radius: 100%; - -webkit-animation: sk-bounce 2.0s infinite ease-in-out; - animation: sk-bounce 2.0s infinite ease-in-out; + -webkit-animation: sk-bounce 2s infinite ease-in-out; + animation: sk-bounce 2s infinite ease-in-out; } .dot2 { top: auto; bottom: 0; - -webkit-animation-delay: -1.0s; - animation-delay: -1.0s; + -webkit-animation-delay: -1s; + animation-delay: -1s; } - @-webkit-keyframes sk-rotate { 100% { -webkit-transform: rotate(360deg) }} - @keyframes sk-rotate { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg) }} + @-webkit-keyframes sk-rotate { + 100% { + -webkit-transform: rotate(360deg); + } + } + @keyframes sk-rotate { + 100% { + transform: rotate(360deg); + -webkit-transform: rotate(360deg); + } + } @-webkit-keyframes sk-bounce { - 0%, 100% { -webkit-transform: scale(0.0) } - 50% { -webkit-transform: scale(1.0) } + 0%, + 100% { + -webkit-transform: scale(0); + } + 50% { + -webkit-transform: scale(1); + } } @keyframes sk-bounce { - 0%, 100% { - transform: scale(0.0); - -webkit-transform: scale(0.0); - } 50% { - transform: scale(1.0); - -webkit-transform: scale(1.0); - } + 0%, + 100% { + transform: scale(0); + -webkit-transform: scale(0); + } + 50% { + transform: scale(1); + -webkit-transform: scale(1); + } } </style> </head> diff --git a/website/src/App.css b/website/src/App.css index 26a55253a..b22a650e1 100644 --- a/website/src/App.css +++ b/website/src/App.css @@ -17,8 +17,8 @@ input[type="text"]:disabled { .headerDark { font-family: "AleoSans"; color: white; - font-size:32px; - margin-left:2rem; + font-size: 32px; + margin-left: 2rem; a { color: white; } @@ -27,9 +27,9 @@ input[type="text"]:disabled { .headerLight { font-family: "AleoSans"; color: black; - font-size:32px; - margin-left:2rem; - a{ + font-size: 32px; + margin-left: 2rem; + a { color: black; } -} \ No newline at end of file +} diff --git a/website/src/components/WasmLoadingMessage.jsx b/website/src/components/WasmLoadingMessage.jsx index 3e3f7f8a4..34f7cee23 100644 --- a/website/src/components/WasmLoadingMessage.jsx +++ b/website/src/components/WasmLoadingMessage.jsx @@ -7,18 +7,19 @@ export function WasmLoadingMessage() { const [_, aleoLoading] = useAleoWASM(); useEffect(() => { - if(aleoLoading){ + if (aleoLoading) { message.open({ key: "wasmLoading", - type: 'loading', - content: 'Loading... some functionality may not be available yet...', + type: "loading", + content: + "Loading... some functionality may not be available yet...", duration: 0, }); } else { message.open({ key: "wasmLoading", - type: 'success', - content: 'Provable.tools Loaded!', + type: "success", + content: "Provable.tools Loaded!", duration: 2, }); } diff --git a/website/src/index.jsx b/website/src/index.jsx index 35bef4b3d..f9a001827 100644 --- a/website/src/index.jsx +++ b/website/src/index.jsx @@ -4,8 +4,8 @@ import { createRoot } from "react-dom/client"; import { RouterProvider } from "react-router-dom"; import { router } from "./routing.jsx"; import WorkerProvider from "./workers/WorkerProvider.jsx"; -import { init } from '@amplitude/analytics-browser'; -import { autocapturePlugin } from '@amplitude/plugin-autocapture-browser'; +import { init } from "@amplitude/analytics-browser"; +import { autocapturePlugin } from "@amplitude/plugin-autocapture-browser"; // // Initialize Amplitude with your API key // const AMPLITUDE_API_KEY = process.env.VITE_AMPLITUDE_API_KEY; @@ -52,4 +52,4 @@ const reportWebVitals = (onPerfEntry) => { // If you want to start measuring performance in your app, pass a function // to log results (for example: reportWebVitals(console.log)) // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); \ No newline at end of file +reportWebVitals(); diff --git a/website/src/main.jsx b/website/src/main.jsx index 30f019d4e..82095078d 100644 --- a/website/src/main.jsx +++ b/website/src/main.jsx @@ -86,10 +86,8 @@ function Main() { <WasmLoadingMessage /> <Layout style={{ minHeight: "100vh" }}> <Sider breakpoint="lg" collapsedWidth="0" theme="light"> - <h1 className={darkMode ? "headerDark": "headerLight"}> - <Link to="/"> - Provable SDK - </Link> + <h1 className={darkMode ? "headerDark" : "headerLight"}> + <Link to="/">Provable SDK</Link> </h1> <Menu theme="light" @@ -110,18 +108,45 @@ function Main() { /> </Sider> <Layout> - <Content style={{ padding: "50px 50px", margin: "0 auto", minWidth: "850px" }}> + <Content + style={{ + padding: "50px 50px", + margin: "0 auto", + minWidth: "850px", + }} + > <Outlet /> </Content> - <Footer style={{ textAlign: "center", display:"flex", flexDirection: "column" }}> - + <Footer + style={{ + textAlign: "center", + display: "flex", + flexDirection: "column", + }} + > <a href="https://github.com/ProvableHQ/sdk"> - <img src="../public/github-mark-white.png" style={{height:"24px"}}></img> + <img + src="../public/github-mark-white.png" + style={{ height: "24px" }} + ></img> </a> - <Link to="https://sdk.betteruptime.com/" style={{color: "white"}}> <span>Status</span> </Link> - <Link to="/terms_of_use" style={{color: "white"}}> <span>Terms of Use</span> </Link> - <Link to="/privacy_policy" style={{color:"white"}}><span>Privacy Policy</span></Link> - + <Link + to="https://sdk.betteruptime.com/" + style={{ color: "white" }} + > + {" "} + <span>Status</span>{" "} + </Link> + <Link to="/terms_of_use" style={{ color: "white" }}> + {" "} + <span>Terms of Use</span>{" "} + </Link> + <Link + to="/privacy_policy" + style={{ color: "white" }} + > + <span>Privacy Policy</span> + </Link> © 2025 Provable Inc. </Footer> </Layout> diff --git a/website/src/pages/Homepage.css b/website/src/pages/Homepage.css index 178c8971f..7766aaaf6 100644 --- a/website/src/pages/Homepage.css +++ b/website/src/pages/Homepage.css @@ -4,14 +4,13 @@ font-size: 14px; } -.buttonRow { +.buttonRow { display: flex; flex-direction: column; align-items: center; gap: 20px; } - .button { background-color: #12e172; padding: 16px 32px 16px 32px; @@ -101,7 +100,7 @@ a:nth-child(3) { font-size: 18px; } - .buttonRow { + .buttonRow { display: flex; flex-direction: row; gap: 20px; diff --git a/website/src/pages/Homepage.jsx b/website/src/pages/Homepage.jsx index bab4e1245..bd712d9a7 100644 --- a/website/src/pages/Homepage.jsx +++ b/website/src/pages/Homepage.jsx @@ -19,24 +19,22 @@ const Homepage = () => { fingertips </p>{" "} <div className="buttonRow"> - <Link - to="/account" - > - <button className="button"> - {" "} - Try now <span className="arrow">&rarr;</span>{" "} - </button> - </Link>{" "} - <Link - target="_blank" - rel="noopener noreferrer" - to="https://developer.aleo.org/sdk/overview/" - > - <button className="button"> - {" "} - See Docs <span className="arrow">&rarr;</span>{" "} - </button> - </Link>{" "} + <Link to="/account"> + <button className="button"> + {" "} + Try now <span className="arrow">&rarr;</span>{" "} + </button> + </Link>{" "} + <Link + target="_blank" + rel="noopener noreferrer" + to="https://developer.aleo.org/sdk/overview/" + > + <button className="button"> + {" "} + See Docs <span className="arrow">&rarr;</span>{" "} + </button> + </Link>{" "} </div> <ul className="actionRow"> <Link to="/account" className="actionItem"> @@ -72,12 +70,24 @@ const Homepage = () => { style={{ height: "24px", marginBottom: "1rem" }} ></img> </a> - <Link to="https://sdk.betteruptime.com/" style={{color: "white"}}> <span>Status</span> </Link> - <Link to="/terms_of_use" style={{ color: "white", textDecoration: "none" }}> + <Link + to="https://sdk.betteruptime.com/" + style={{ color: "white" }} + > + {" "} + <span>Status</span>{" "} + </Link> + <Link + to="/terms_of_use" + style={{ color: "white", textDecoration: "none" }} + > {" "} <span>Terms of Use</span>{" "} </Link> - <Link to="/privacy_policy" style={{ color: "white", textDecoration: "none"}}> + <Link + to="/privacy_policy" + style={{ color: "white", textDecoration: "none" }} + > <span>Privacy Policy</span> </Link> © 2025 Provable Inc. diff --git a/website/src/pages/PrivacyPolicy.css b/website/src/pages/PrivacyPolicy.css index d04741ce8..4b917e2ea 100644 --- a/website/src/pages/PrivacyPolicy.css +++ b/website/src/pages/PrivacyPolicy.css @@ -1,5 +1,5 @@ body { - font-family:'AleoSans'; + font-family: "AleoSans"; line-height: 1.6; margin: 20px; } @@ -12,10 +12,13 @@ table { border-collapse: collapse; margin: 20px 0; } -table, th, td { +table, +th, +td { border: 1px solid #ddd; } -th, td { +th, +td { padding: 10px; text-align: left; -} \ No newline at end of file +} diff --git a/website/src/pages/PrivacyPolicy.jsx b/website/src/pages/PrivacyPolicy.jsx index f61980278..853f15947 100644 --- a/website/src/pages/PrivacyPolicy.jsx +++ b/website/src/pages/PrivacyPolicy.jsx @@ -1291,60 +1291,100 @@ const PrivacyPolicy = () => { the EEA, UK, or Switzerland. </p> </div> - <br/> + <br /> <h2> PROVABLE COOKIE NOTICE </h2> <p> Last updated: 8/19/2024 </p> <h2>Scope of Notice</h2> - <p>This Cookie Notice supplements the information contained in our Privacy Notice by providing information in the tables set forth below about the cookies which are most frequently used on our websites. The tables may be updated from time to time, so please check back on a regular basis for any changes. For details about the information collected through the use of cookies and the purposes for which such information is used, please read the <em>Our Collection and Use of Personal Data</em> section of our Privacy Notice.</p> - - <h2>Cookie Choices</h2> - <p>Please note there is a “Cookie Preferences” manager linked in the footer of our websites that allows you to adjust your cookie preferences on the specific website you are visiting for the specific device and browser you are using at that time (which means you will need to change your preferences on each device and browser you use to interact with the specific website you are visiting). For information about other choices you may have in relation to our use of cookies and other automatic data collection technologies, please refer to the <em>Your Privacy Choices</em> section of our Privacy Notice.</p> - - <h2>Strictly Necessary Cookies</h2> - <p>These cookies are essential to making our websites work. They enable core functionality such as security, network management and accessibility. Without these cookies, services that are necessary for you to use our websites, such as accessing secure areas or remembering the information inputted into a webform, are not available. These cookies do not track browsing history on third-party websites. The legal basis for our use of strictly necessary cookies are our legitimate interests, namely being able to provide and maintain our websites.</p> - - <h2>Functional Cookies</h2> - <p>These cookies enable a website to remember information that changes the way the website behaves or looks, such as preferred language or the region that an individual is accessing our website from. If you are accessing our websites with an IP address from the European Economic Area, United Kingdom, or Switzerland, you have been asked to consent to the use of these cookies.</p> - - <table> - <tr> - <th>Cookie Name</th> - <th>Cookie Provider</th> - <th>When is the Cookie Set?</th> - <th>How Long Does the Cookie Last?</th> - <th>Purpose of the Cookie</th> - </tr> - <tr> - <td>None.</td> - <td></td> - <td></td> - <td></td> - <td></td> - </tr> - </table> - - <h2>Analytical or Performance Cookies</h2> - <p>These cookies collect information about the number of visitors to our websites, and information about how visitors move around our websites when they are using them. This helps us to improve the way our websites work, for example, by ensuring that visitors are finding what they are looking for easily. If you are accessing our websites with an IP address from the European Economic Area, United Kingdom, or Switzerland, you have been asked to consent to the use of these cookies.</p> - - <table> - <tr> - <th>Cookie Name</th> - <th>Cookie Provider</th> - <th>When is the Cookie Set?</th> - <th>How Long Does the Cookie Last?</th> - <th>Purpose of the Cookie</th> - </tr> - <tr> - <td>None.</td> - <td></td> - <td></td> - <td></td> - <td></td> - </tr> - </table> - + <p> + This Cookie Notice supplements the information contained in our + Privacy Notice by providing information in the tables set forth + below about the cookies which are most frequently used on our + websites. The tables may be updated from time to time, so please + check back on a regular basis for any changes. For details about + the information collected through the use of cookies and the + purposes for which such information is used, please read the{" "} + <em>Our Collection and Use of Personal Data</em> section of our + Privacy Notice. + </p> + <h2>Cookie Choices</h2> + <p> + Please note there is a “Cookie Preferences” manager linked in + the footer of our websites that allows you to adjust your cookie + preferences on the specific website you are visiting for the + specific device and browser you are using at that time (which + means you will need to change your preferences on each device + and browser you use to interact with the specific website you + are visiting). For information about other choices you may have + in relation to our use of cookies and other automatic data + collection technologies, please refer to the{" "} + <em>Your Privacy Choices</em> section of our Privacy Notice. + </p> + <h2>Strictly Necessary Cookies</h2> + <p> + These cookies are essential to making our websites work. They + enable core functionality such as security, network management + and accessibility. Without these cookies, services that are + necessary for you to use our websites, such as accessing secure + areas or remembering the information inputted into a webform, + are not available. These cookies do not track browsing history + on third-party websites. The legal basis for our use of strictly + necessary cookies are our legitimate interests, namely being + able to provide and maintain our websites. + </p> + <h2>Functional Cookies</h2> + <p> + These cookies enable a website to remember information that + changes the way the website behaves or looks, such as preferred + language or the region that an individual is accessing our + website from. If you are accessing our websites with an IP + address from the European Economic Area, United Kingdom, or + Switzerland, you have been asked to consent to the use of these + cookies. + </p> + <table> + <tr> + <th>Cookie Name</th> + <th>Cookie Provider</th> + <th>When is the Cookie Set?</th> + <th>How Long Does the Cookie Last?</th> + <th>Purpose of the Cookie</th> + </tr> + <tr> + <td>None.</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + </table> + <h2>Analytical or Performance Cookies</h2> + <p> + These cookies collect information about the number of visitors + to our websites, and information about how visitors move around + our websites when they are using them. This helps us to improve + the way our websites work, for example, by ensuring that + visitors are finding what they are looking for easily. If you + are accessing our websites with an IP address from the European + Economic Area, United Kingdom, or Switzerland, you have been + asked to consent to the use of these cookies. + </p> + <table> + <tr> + <th>Cookie Name</th> + <th>Cookie Provider</th> + <th>When is the Cookie Set?</th> + <th>How Long Does the Cookie Last?</th> + <th>Purpose of the Cookie</th> + </tr> + <tr> + <td>None.</td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + </table> </div> - ); }; diff --git a/website/src/pages/TermsOfUse.jsx b/website/src/pages/TermsOfUse.jsx index ecfc004c6..33d87e702 100644 --- a/website/src/pages/TermsOfUse.jsx +++ b/website/src/pages/TermsOfUse.jsx @@ -5,7 +5,7 @@ const TermsOfUse = () => { return ( <div> <h1>PROVABLE </h1> - <h2> Terms of Use </h2> + <h2> Terms of Use </h2> <p> <strong>Last Modified:</strong> 8/19/2024 </p> @@ -859,10 +859,10 @@ const TermsOfUse = () => { the understanding between you and us. In the event of any conflict between the English version of these Terms and any translation, the English version will prevail. - </p> + </p> - <h2>13.2 Arbitration</h2> - <p> + <h2>13.2 Arbitration</h2> + <p> Agreement (a) General. READ THIS SECTION CAREFULLY BECAUSE IT REQUIRES THE PARTIES TO ARBITRATE THEIR DISPUTES AND LIMITS THE MANNER IN WHICH YOU CAN SEEK RELIEF FROM US. This Arbitration @@ -879,18 +879,21 @@ const TermsOfUse = () => { collectively, “Claims”). This Arbitration Agreement will apply, without limitation, to all Claims that arose or were asserted before or after your consent to these Terms. - </p> - <p> (b) Opting Out of - Arbitration Agreement. If you are a new User, you can reject and - opt out of this Arbitration Agreement within thirty (30) days of - accepting these Terms by emailing us at legal@provable.com with - your full, legal name and stating your intent to opt out of this - Arbitration Agreement. Opting out of this Arbitration Agreement - does not affect the binding nature of any other part of these - Terms, including the provisions regarding controlling law or the - courts in which any disputes must be brought. </p> - <p>(c) - Dispute-Resolution Process. For any Claim, you will first + </p> + <p> + {" "} + (b) Opting Out of Arbitration Agreement. If you are a new User, + you can reject and opt out of this Arbitration Agreement within + thirty (30) days of accepting these Terms by emailing us at + legal@provable.com with your full, legal name and stating your + intent to opt out of this Arbitration Agreement. Opting out of + this Arbitration Agreement does not affect the binding nature of + any other part of these Terms, including the provisions + regarding controlling law or the courts in which any disputes + must be brought.{" "} + </p> + <p> + (c) Dispute-Resolution Process. For any Claim, you will first contact us at legal@provable.com attempt to resolve the Claim with us informally. In the unlikely event that we have not been able to resolve a Claim after sixty (60) days, we each agree to @@ -932,21 +935,24 @@ const TermsOfUse = () => { whether these Terms are, or whether any provision of these Terms is, unconscionable or illusory, and any defense to arbitration, including waiver, delay, laches, unconscionability, and/or - estoppel.</p> - <p> (d) Equitable Relief. NOTHING IN THIS ARBITRATION - AGREEMENT WILL BE DEEMED AS: PREVENTING US FROM SEEKING - INJUNCTIVE OR OTHER EQUITABLE RELIEF FROM THE COURTS AS - NECESSARY TO PREVENT THE ACTUAL OR THREATENED INFRINGEMENT, - MISAPPROPRIATION, OR VIOLATION OF OUR DATA SECURITY, - CONFIDENTIAL INFORMATION, OR INTELLECTUAL PROPERTY RIGHTS; OR - PREVENTING YOU FROM ASSERTING CLAIMS IN A SMALL CLAIMS COURT, - PROVIDED THAT YOUR CLAIMS QUALIFY AND SO LONG AS THE MATTER - REMAINS IN SUCH COURT AND ADVANCES ON ONLY AN INDIVIDUAL - (NON-CLASS, NON-COLLECTIVE, AND NON-REPRESENTATIVE) BASIS. - </p> - <p>(E) - Severability. If this Arbitration Agreement is found to be void, - unenforceable, or unlawful, in whole or in part, the void, + estoppel. + </p> + <p> + {" "} + (d) Equitable Relief. NOTHING IN THIS ARBITRATION AGREEMENT WILL + BE DEEMED AS: PREVENTING US FROM SEEKING INJUNCTIVE OR OTHER + EQUITABLE RELIEF FROM THE COURTS AS NECESSARY TO PREVENT THE + ACTUAL OR THREATENED INFRINGEMENT, MISAPPROPRIATION, OR + VIOLATION OF OUR DATA SECURITY, CONFIDENTIAL INFORMATION, OR + INTELLECTUAL PROPERTY RIGHTS; OR PREVENTING YOU FROM ASSERTING + CLAIMS IN A SMALL CLAIMS COURT, PROVIDED THAT YOUR CLAIMS + QUALIFY AND SO LONG AS THE MATTER REMAINS IN SUCH COURT AND + ADVANCES ON ONLY AN INDIVIDUAL (NON-CLASS, NON-COLLECTIVE, AND + NON-REPRESENTATIVE) BASIS. + </p> + <p> + (E) Severability. If this Arbitration Agreement is found to be + void, unenforceable, or unlawful, in whole or in part, the void, unenforceable, or unlawful provision, in whole or in part, will be severed. Severance of the void, unenforceable, or unlawful provision, in whole or in part, will have no impact on the @@ -964,189 +970,197 @@ const TermsOfUse = () => { Arbitration Agreement will be arbitrated under its terms, and the parties agree that litigation of any dispute regarding the entitlement to public injunctive relief will be stayed pending - the outcome of any individual claims in arbitration. </p> + the outcome of any individual claims in arbitration.{" "} + </p> - <p> 13.3 + <p> + {" "} + 13.3 <strong>Class Action/Jury Trial Waiver. </strong> - BY ENTERING INTO THESE TERMS, - YOU AND PROVABLE ARE EACH WAIVING THE RIGHT TO A TRIAL BY JURY - OR TO BRING, JOIN, OR PARTICIPATE IN ANY PURPORTED CLASS ACTION, - COLLECTIVE ACTION, PRIVATE ATTORNEY GENERAL ACTION, OR OTHER - REPRESENTATIVE PROCEEDING OF ANY KIND AS A PLAINTIFF OR CLASS - MEMBER. THE FOREGOING APPLIES TO ALL USERS (BOTH NATUAL PERSONS - AND ENTITIES), REGARDLESS OF WHETHER YOU HAVE OBTAINED OR USED - THE PLATFORM FOR PERSONAL, COMMERCIAL, OR OTHER PURPOSES. THIS - CLASS ACTION/JURY TRIAL WAIVER APPLIES TO CLASS ARBITRATION, - AND, UNLESS WE AGREE OTHERWISE, THE ARBITRATOR MAY NOT - CONSOLIDATE MORE THAN ONE PERSON’S OR ENTITY’S CLAIMS. YOU AND - PROVABLE AGREE THAT THE ARBITRATOR MAY AWARD RELIEF ONLY TO AN - INDIVIDUAL CLAIMANT AND ONLY TO THE EXTENT NECESSARY TO PROVIDE - RELIEF ON YOUR INDIVIDUAL CLAIM(S). ANY RELIEF AWARDED MAY NOT - AFFECT OTHER USERS. - - </p> - <h2> - 14. U.S. Government Restricted Rights - </h2> - <p>To the - extent the Platform is being used by or on behalf of the U.S. - Government, the Platform will be deemed commercial computer + BY ENTERING INTO THESE TERMS, YOU AND PROVABLE ARE EACH WAIVING + THE RIGHT TO A TRIAL BY JURY OR TO BRING, JOIN, OR PARTICIPATE + IN ANY PURPORTED CLASS ACTION, COLLECTIVE ACTION, PRIVATE + ATTORNEY GENERAL ACTION, OR OTHER REPRESENTATIVE PROCEEDING OF + ANY KIND AS A PLAINTIFF OR CLASS MEMBER. THE FOREGOING APPLIES + TO ALL USERS (BOTH NATUAL PERSONS AND ENTITIES), REGARDLESS OF + WHETHER YOU HAVE OBTAINED OR USED THE PLATFORM FOR PERSONAL, + COMMERCIAL, OR OTHER PURPOSES. THIS CLASS ACTION/JURY TRIAL + WAIVER APPLIES TO CLASS ARBITRATION, AND, UNLESS WE AGREE + OTHERWISE, THE ARBITRATOR MAY NOT CONSOLIDATE MORE THAN ONE + PERSON’S OR ENTITY’S CLAIMS. YOU AND PROVABLE AGREE THAT THE + ARBITRATOR MAY AWARD RELIEF ONLY TO AN INDIVIDUAL CLAIMANT AND + ONLY TO THE EXTENT NECESSARY TO PROVIDE RELIEF ON YOUR + INDIVIDUAL CLAIM(S). ANY RELIEF AWARDED MAY NOT AFFECT OTHER + USERS. + </p> + <h2>14. U.S. Government Restricted Rights</h2> + <p> + To the extent the Platform is being used by or on behalf of the + U.S. Government, the Platform will be deemed commercial computer software or commercial computer software documentation (as applicable). Accordingly, if you are an agency of the U.S. Government or any contractor therefor, you receive only those rights with respect to the Platform as are granted to all other Users hereunder, in accordance with 48 C.F.R. §227.7202 and 48 C.F.R. §12.212, as applicable. - + </p> + <h2> 15. Export Controls, Sanctions and Anti-Money Laundering</h2> + <p> + {" "} + You understand and acknowledge that the Platform may be subject + to export control laws and regulations. You will comply with all + applicable import and export and re-export control and trade and + economic sanctions laws and regulations, including the Export + Administration Regulations maintained by the U.S. Department of + Commerce, trade and economic sanctions maintained by the U.S. + Treasury Department’s Office of Foreign Assets Control (“OFAC”), + and the International Traffic in Arms Regulations maintained by + the U.S. State Department. You represent and warrant that: + <p> + {" "} + (a) You will not access the Platform from any country, + region, territory or jurisdiction that is the target of + comprehensive territorial or embargo-like sanctions + administered by OFAC (each such country, region, territory + or jurisdiction a “Sanctioned Country”), which, as of the + time of this Agreement, are Crimea, Cuba, Iran, North Korea, + Syria, the so-called Donetsk People’s Republic, and the + so-called Luhansk People’s Republic; </p> - <h2> 15. Export Controls, Sanctions - and Anti-Money Laundering - </h2> - <p> You understand and acknowledge that - the Platform may be subject to export control laws and - regulations. You will comply with all applicable import and - export and re-export control and trade and economic sanctions - laws and regulations, including the Export Administration - Regulations maintained by the U.S. Department of Commerce, trade - and economic sanctions maintained by the U.S. Treasury - Department’s Office of Foreign Assets Control (“OFAC”), and the - International Traffic in Arms Regulations maintained by the U.S. - State Department. You represent and warrant that: - - <p> (a) You will - not access the Platform from any country, region, territory or - jurisdiction that is the target of comprehensive territorial or - embargo-like sanctions administered by OFAC (each such country, - region, territory or jurisdiction a “Sanctioned Country”), - which, as of the time of this Agreement, are Crimea, Cuba, Iran, - North Korea, Syria, the so-called Donetsk People’s Republic, and - the so-called Luhansk People’s Republic; + <p> + (b) You will not provide access to the Platform to any + person that is, or that is owned or controlled by a person + that is, the target of any sanctions administered or + enforced by OFAC or the U.S. Department of + State,(collectively, “Sanctions”) or that is located, + organized or resident in any Sanctioned Country; </p> - - <p>(b) You will not - provide access to the Platform to any person that is, or that is - owned or controlled by a person that is, the target of any - sanctions administered or enforced by OFAC or the U.S. - Department of State,(collectively, “Sanctions”) or that is - located, organized or resident in any Sanctioned Country; + <p> + (c) None of you, your subsidiaries, directors, officers, + employees, agents or affiliates is a person that is, or is + owned or controlled by persons that are, the target of any + Sanctions; </p> - <p>(c) - None of you, your subsidiaries, directors, officers, employees, - agents or affiliates is a person that is, or is owned or - controlled by persons that are, the target of any Sanctions; + <p> + (d) None of you, your subsidiaries, directors, officers, + employees, agents or affiliates are located, organized, or + resident in, or owned or controlled by persons that are + located, organized or resident in a Sanctioned Country; 14 </p> - <p>(d) - None of you, your subsidiaries, directors, officers, employees, - agents or affiliates are located, organized, or resident in, or - owned or controlled by persons that are located, organized or - resident in a Sanctioned Country; 14 + <p> + (e) You will not utilize the Platform in any manner that, + directly or directly, conducts, facilitates, funds or + otherwise engages in activities with any person that is the + target of Sanctions or is located, organized or resident in + a Sanctioned Country or in any other manner that would + result in a violation of Sanctions by any person, including + Provable; </p> - <p>(e) You will not utilize - the Platform in any manner that, directly or directly, conducts, - facilitates, funds or otherwise engages in activities with any - person that is the target of Sanctions or is located, organized - or resident in a Sanctioned Country or in any other manner that - would result in a violation of Sanctions by any person, - including Provable; + <p> + (f) You will not utilize the Platform in any manner that, + directly or indirectly, conducts, facilitates, funds or + otherwise engages in any money laundering or terrorist + financing activities or business or in any other manner that + would result in a violation by any person, including + Provable, of any applicable law, rule or regulation of any + jurisdiction concerning or relating to anti-money + laundering, including the USA PATRIOT Act of 2001, as + amended (“Anti-Money Laundering Laws”); and </p> - <p>(f) You will not utilize the Platform in any - manner that, directly or indirectly, conducts, facilitates, - funds or otherwise engages in any money laundering or terrorist - financing activities or business or in any other manner that - would result in a violation by any person, including Provable, - of any applicable law, rule or regulation of any jurisdiction - concerning or relating to anti-money laundering, including the - USA PATRIOT Act of 2001, as amended (“Anti-Money Laundering - Laws”); and - </p><p>(g) You have conducted and will conduct all - activities generating funds, assets or other items of value - introduced to the Platform in compliance with applicable - Anti-Money Laundering Laws and Sanctions. + <p> + (g) You have conducted and will conduct all activities + generating funds, assets or other items of value introduced + to the Platform in compliance with applicable Anti-Money + Laundering Laws and Sanctions. </p> - <h2>16. General Provisions</h2> - <p>16.1 Assignment. These Terms, and any rights and licenses - granted hereunder, may not be transferred or assigned by you - without our prior express written consent, but may be assigned - by us without restriction. Any attempted transfer or assignment - in violation hereof will be null and void. + <p> + 16.1 Assignment. These Terms, and any rights and licenses + granted hereunder, may not be transferred or assigned by you + without our prior express written consent, but may be + assigned by us without restriction. Any attempted transfer + or assignment in violation hereof will be null and void. </p> - - <p>16.2 Notification - Procedures and Changes to these Terms. We may provide - notifications, whether such notifications are required by - Applicable Law or are for marketing or other business-related - purposes, to you via email notice or written or hard copy - notice, or through posting of such notice on the Platform, as we - determine, in our sole discretion. We reserve the right to - determine the form and means of providing notifications to - Users, provided that you may opt out of certain means of - notification, as required under Applicable Law or as described - in these Terms. We are not responsible for any automatic - filtering you or your network provider may apply to email - notifications we send to the email address you provide us. We - may, in our sole discretion, modify or update these Terms from - time to time, and so you should review this page periodically. - When we change these Terms in a material manner, we will update - the ‘last modified’ date at the top of this page and notify you - that material changes have been made to these Terms. These Terms - apply to and govern your access to and use of the Platform - effective as of the start of your access to the Platform, even - if such access began before publication of these Terms. Your - continued use of the Platform after any change to these Terms - constitutes your acceptance of the new Terms of Use. If you do - not agree to any part of these Terms or to any future Terms of - Use, do not access or use (or continue to access or use) the - Platform. + <p> + 16.2 Notification Procedures and Changes to these Terms. We + may provide notifications, whether such notifications are + required by Applicable Law or are for marketing or other + business-related purposes, to you via email notice or + written or hard copy notice, or through posting of such + notice on the Platform, as we determine, in our sole + discretion. We reserve the right to determine the form and + means of providing notifications to Users, provided that you + may opt out of certain means of notification, as required + under Applicable Law or as described in these Terms. We are + not responsible for any automatic filtering you or your + network provider may apply to email notifications we send to + the email address you provide us. We may, in our sole + discretion, modify or update these Terms from time to time, + and so you should review this page periodically. When we + change these Terms in a material manner, we will update the + ‘last modified’ date at the top of this page and notify you + that material changes have been made to these Terms. These + Terms apply to and govern your access to and use of the + Platform effective as of the start of your access to the + Platform, even if such access began before publication of + these Terms. Your continued use of the Platform after any + change to these Terms constitutes your acceptance of the new + Terms of Use. If you do not agree to any part of these Terms + or to any future Terms of Use, do not access or use (or + continue to access or use) the Platform. + </p> + <p> + 16.3 Entire Agreement; Severability. These Terms, together + with any amendments and any additional agreements you may + enter into with us in connection with the Platform, will + constitute the entire agreement between you and us + concerning the Platform. Any statements or comments made + between you and any of our employees or representatives are + expressly excluded from these Terms and will not apply to + you or us, or to your access to or use of the Platform. + Except as otherwise stated in the Arbitration Agreement, if + any provision of these Terms is deemed invalid by a court of + competent jurisdiction, the invalidity of such provision + will not affect the validity of the remaining provisions of + these Terms, which will remain in full force and effect. + </p> + <p> + 16.4 Interpretation. For purposes of these Terms, (a) the + words “include,” “includes” and “including” are deemed to be + followed by the words “without limitation”; (b) the word + “or” is not exclusive; 15 and (c) the words “herein,” + “hereof,” “hereto” and “hereunder” refer to these Terms as a + whole. These Terms shall be construed without regard to any + presumption or rule requiring construction or interpretation + against the party drafting an instrument or causing any + instrument to be drafted. Whenever the masculine is used in + this Agreement, the same shall include the feminine and + whenever the feminine is used herein, the same shall include + the masculine, where appropriate. Whenever the singular is + used in this Agreement, the same shall include the plural, + and whenever the plural is used herein, the same shall + include the singular, where appropriate. </p> - <p>16.3 Entire Agreement; Severability. These Terms, - together with any amendments and any additional agreements you - may enter into with us in connection with the Platform, will - constitute the entire agreement between you and us concerning - the Platform. Any statements or comments made between you and - any of our employees or representatives are expressly excluded - from these Terms and will not apply to you or us, or to your - access to or use of the Platform. Except as otherwise stated in - the Arbitration Agreement, if any provision of these Terms is - deemed invalid by a court of competent jurisdiction, the - invalidity of such provision will not affect the validity of the - remaining provisions of these Terms, which will remain in full - force and effect. + <p> + 16.5 No Waiver. No waiver of any term of these Terms will be + deemed a further or continuing waiver of such term or of any + other term, and our failure to assert any right or provision + under these Terms will not constitute a waiver of such right + or provision. </p> - <p>16.4 Interpretation. For purposes of these - Terms, (a) the words “include,” “includes” and “including” are - deemed to be followed by the words “without limitation”; (b) the - word “or” is not exclusive; 15 and (c) the words “herein,” - “hereof,” “hereto” and “hereunder” refer to these Terms as a - whole. These Terms shall be construed without regard to any - presumption or rule requiring construction or interpretation - against the party drafting an instrument or causing any - instrument to be drafted. Whenever the masculine is used in this - Agreement, the same shall include the feminine and whenever the - feminine is used herein, the same shall include the masculine, - where appropriate. Whenever the singular is used in this - Agreement, the same shall include the plural, and whenever the - plural is used herein, the same shall include the singular, - where appropriate. + 16.6 California Residents. The provider of the Platform is: + Provable Inc., 5470 Kietzke Lane, STE 300, Reno, NV 89511. If + you are a California resident, in accordance with Cal. Civ. Code + §1789.3, you may report complaints to the Complaint Assistance + Unit of the Division of Consumer Services of the California + Department of Consumer Affairs by contacting it in writing at + 1625 North Market Blvd., Suite N 112 Sacramento, CA 95834, or by + telephone at (800) 952-5210 or (916) 445-1254. + <p> + 16.7 Contact. If you have any questions about these Terms + and/or the Platform, please contact us at + termsofuse@provable.com. </p> - <p>16.5 No Waiver. No waiver of any term of - these Terms will be deemed a further or continuing waiver of - such term or of any other term, and our failure to assert any - right or provision under these Terms will not constitute a - waiver of such right or provision. - </p>16.6 California Residents. - The provider of the Platform is: Provable Inc., 5470 Kietzke - Lane, STE 300, Reno, NV 89511. If you are a California resident, - in accordance with Cal. Civ. Code §1789.3, you may report - complaints to the Complaint Assistance Unit of the Division of - Consumer Services of the California Department of Consumer - Affairs by contacting it in writing at 1625 North Market Blvd., - Suite N 112 Sacramento, CA 95834, or by telephone at (800) - 952-5210 or (916) 445-1254. - <p>16.7 Contact. If you have any - questions about these Terms and/or the Platform, please contact - us at termsofuse@provable.com. - </p> </p> </div> ); diff --git a/website/src/routing.jsx b/website/src/routing.jsx index 0f365a815..f584a091f 100644 --- a/website/src/routing.jsx +++ b/website/src/routing.jsx @@ -25,9 +25,9 @@ import { GetMappingValue } from "./tabs/rest/GetMappingValue.jsx"; import { FieldArithmetic } from "./tabs/algebra/FieldArithmetic.jsx"; import { GroupArithmetic } from "./tabs/algebra/GroupArithmetic.jsx"; import { HashFunctions } from "./tabs/algebra/HashFunctions.jsx"; -import Homepage from "./pages/Homepage"; +import Homepage from "./pages/Homepage"; import TermsOfUse from "./pages/TermsOfUse"; -import PrivacyPolicy from "./pages/PrivacyPolicy" +import PrivacyPolicy from "./pages/PrivacyPolicy"; import { TransactionInfo } from "./tabs/protocol/TransactionInfo.jsx"; export const router = createBrowserRouter([ @@ -154,7 +154,6 @@ export const router = createBrowserRouter([ </> ), }, - ], }, ]); diff --git a/website/src/tabs/account/AccountFromPrivateKey.jsx b/website/src/tabs/account/AccountFromPrivateKey.jsx index f21b72b4e..16067d99d 100644 --- a/website/src/tabs/account/AccountFromPrivateKey.jsx +++ b/website/src/tabs/account/AccountFromPrivateKey.jsx @@ -54,11 +54,7 @@ export const AccountFromPrivateKey = () => { size="large" placeholder="View Key" value={viewKey()} - addonAfter={ - <CopyButton - data={viewKey()} - /> - } + addonAfter={<CopyButton data={viewKey()} />} disabled /> </Form.Item> @@ -67,11 +63,7 @@ export const AccountFromPrivateKey = () => { size="large" placeholder="Address" value={address()} - addonAfter={ - <CopyButton - data={address()} - /> - } + addonAfter={<CopyButton data={address()} />} disabled /> </Form.Item> diff --git a/website/src/tabs/account/AddressFromViewKey.jsx b/website/src/tabs/account/AddressFromViewKey.jsx index 49ae57ea2..10e412c30 100644 --- a/website/src/tabs/account/AddressFromViewKey.jsx +++ b/website/src/tabs/account/AddressFromViewKey.jsx @@ -25,10 +25,7 @@ export const AddressFromViewKey = () => { : ""; return ( - <Card - title="Load Address from View Key" - style={{ width: "100%" }} - > + <Card title="Load Address from View Key" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="View Key" colon={false}> <Input @@ -48,11 +45,7 @@ export const AddressFromViewKey = () => { size="large" placeholder="Address" value={address()} - addonAfter={ - <CopyButton - data={address()} - /> - } + addonAfter={<CopyButton data={address()} />} disabled /> </Form.Item> diff --git a/website/src/tabs/account/NewAccount.jsx b/website/src/tabs/account/NewAccount.jsx index ed18f9570..6bbf7beeb 100644 --- a/website/src/tabs/account/NewAccount.jsx +++ b/website/src/tabs/account/NewAccount.jsx @@ -30,10 +30,7 @@ export const NewAccount = () => { if (aleo !== null) { return ( - <Card - title="Create a New Account" - style={{ width: "100%" }} - > + <Card title="Create a New Account" style={{ width: "100%" }}> <Row justify="center"> <Col> <Button @@ -46,7 +43,7 @@ export const NewAccount = () => { </Button> </Col> <Col offset="1"> - <Button size="large" onClick={clear}> + <Button size="large" onClick={clear}> Clear </Button> </Col> diff --git a/website/src/tabs/account/SignMessage.jsx b/website/src/tabs/account/SignMessage.jsx index 0a487853d..0ddde4409 100644 --- a/website/src/tabs/account/SignMessage.jsx +++ b/website/src/tabs/account/SignMessage.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Card, Divider, Form, Input } from "antd"; import { CopyButton } from "../../components/CopyButton"; import { useAleoWASM } from "../../aleo-wasm-hook"; @@ -38,22 +38,16 @@ export const SignMessage = () => { const layout = { labelCol: { span: 3 }, wrapperCol: { span: 21 } }; const signatureString = useMemo(() => { - return signingKey !== null ? signingKey : "" + return signingKey !== null ? signingKey : ""; }, [signingKey]); const messageString = useMemo(() => { - return message !== null ? message : "" + return message !== null ? message : ""; }, [signingKey]); - if (aleo !== null) { - - return ( - <Card - title="Sign a Message" - style={{ width: "100%" }} - > + <Card title="Sign a Message" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="Private Key" colon={false}> <Input diff --git a/website/src/tabs/account/VerifyMessage.jsx b/website/src/tabs/account/VerifyMessage.jsx index 365d1b676..1b7846e75 100644 --- a/website/src/tabs/account/VerifyMessage.jsx +++ b/website/src/tabs/account/VerifyMessage.jsx @@ -65,10 +65,7 @@ export const VerifyMessage = () => { const signatureString = () => signatureInput !== null ? signatureInput.toString() : ""; return ( - <Card - title="Verify a Message" - style={{ width: "100%"}} - > + <Card title="Verify a Message" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="Address" colon={false}> <Input diff --git a/website/src/tabs/advanced/DecryptAccount.jsx b/website/src/tabs/advanced/DecryptAccount.jsx index f02516217..74a0db2ff 100644 --- a/website/src/tabs/advanced/DecryptAccount.jsx +++ b/website/src/tabs/advanced/DecryptAccount.jsx @@ -96,11 +96,7 @@ export const DecryptAccount = () => { size="large" placeholder="Private Key" value={privateKey()} - addonAfter={ - <CopyButton - data={privateKey()} - /> - } + addonAfter={<CopyButton data={privateKey()} />} disabled /> </Form.Item> @@ -109,11 +105,7 @@ export const DecryptAccount = () => { size="large" placeholder="View Key" value={viewKey()} - addonAfter={ - <CopyButton - data={viewKey()} - /> - } + addonAfter={<CopyButton data={viewKey()} />} disabled /> </Form.Item> @@ -122,11 +114,7 @@ export const DecryptAccount = () => { size="large" placeholder="Address" value={address()} - addonAfter={ - <CopyButton - data={address()} - /> - } + addonAfter={<CopyButton data={address()} />} disabled /> </Form.Item> diff --git a/website/src/tabs/advanced/EncryptAccount.jsx b/website/src/tabs/advanced/EncryptAccount.jsx index ec19940de..3c1f455be 100644 --- a/website/src/tabs/advanced/EncryptAccount.jsx +++ b/website/src/tabs/advanced/EncryptAccount.jsx @@ -55,10 +55,7 @@ export const EncryptAccount = () => { if (aleo !== null) { return ( - <Card - title="Create a New Account" - style={{ width: "100%"}} - > + <Card title="Create a New Account" style={{ width: "100%" }}> <Row justify="center"> <Col> <Button @@ -71,7 +68,7 @@ export const EncryptAccount = () => { </Button> </Col> <Col offset="1"> - <Button size="large" onClick={clear}> + <Button size="large" onClick={clear}> Clear </Button> </Col> diff --git a/website/src/tabs/algebra/FieldArithmetic.jsx b/website/src/tabs/algebra/FieldArithmetic.jsx index 0e4b43309..905fb3cc7 100644 --- a/website/src/tabs/algebra/FieldArithmetic.jsx +++ b/website/src/tabs/algebra/FieldArithmetic.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Card, Divider, Form, Input, Select, Radio, Button } from "antd"; import { CopyButton } from "../../components/CopyButton"; import { useAleoWASM } from "../../aleo-wasm-hook"; @@ -27,7 +27,7 @@ export const FieldArithmetic = () => { { value: "div", label: "Divide (÷)" }, { value: "pow", label: "Power (x^y)" }, { value: "inv", label: "Additive Inverse (-x)" }, - { value: "mulinv", label: "Multiplicative Inverse (1/x)"}, + { value: "mulinv", label: "Multiplicative Inverse (1/x)" }, { value: "equals", label: "Equals (==)" }, ]; @@ -47,8 +47,10 @@ export const FieldArithmetic = () => { }; const calculateResult = (num1, num2, op) => { - if (((op === "inv" || op === "mulinv") && num1 === "") || - ((op !== "inv" && op !== "mulinv") && (num1 === "" || num2 === ""))) { + if ( + ((op === "inv" || op === "mulinv") && num1 === "") || + (op !== "inv" && op !== "mulinv" && (num1 === "" || num2 === "")) + ) { setResult(""); return; } @@ -64,11 +66,11 @@ export const FieldArithmetic = () => { if (op === "inv") { resultField = field1.inverse(); } else if (op === "mulinv") { - resultField = field1.divide(field1).divide(field1) + resultField = field1.divide(field1).divide(field1); } else { let field2Text = num2; if (!field2Text.includes("field")) { - field2Text = field2Text + "field" + field2Text = field2Text + "field"; } const field2 = wasm.Field.fromString(field2Text); @@ -107,22 +109,23 @@ export const FieldArithmetic = () => { } }; - const layout = { - labelCol: { span: 6 }, + const layout = { + labelCol: { span: 6 }, wrapperCol: { span: 18 }, - style: { marginBottom: '24px' } + style: { marginBottom: "24px" }, }; return ( - <Card - title="Finite Field Arithmetic" - style={{ width: "100%" }} - > + <Card title="Finite Field Arithmetic" style={{ width: "100%" }}> <Form {...layout}> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Field Element 1</span>} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Field Element 1 + </span> + } colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Input.Group compact> <Input @@ -132,29 +135,35 @@ export const FieldArithmetic = () => { allowClear value={fieldValueOne} onChange={onFirstNumberChange} - style={{ width: 'calc(100% - 110px)' }} + style={{ width: "calc(100% - 110px)" }} /> - <Button + <Button size="large" - onClick={() => onFirstNumberChange({target:{value:generateRandomField()}})} - style={{ width: '110px' }} + onClick={() => + onFirstNumberChange({ + target: { value: generateRandomField() }, + }) + } + style={{ width: "110px" }} > Random </Button> </Input.Group> </Form.Item> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Operation</span>} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Operation</span> + } colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Radio.Group value={operation} onChange={(e) => onOperationChange(e.target.value)} size="large" > - {operations.map(op => ( + {operations.map((op) => ( <Radio.Button key={op.value} value={op.value}> {op.label} </Radio.Button> @@ -163,10 +172,14 @@ export const FieldArithmetic = () => { </Form.Item> {operation !== "inv" && operation !== "mulinv" && ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Field Element 2</span>} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Field Element 2 + </span> + } colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Input.Group compact> <Input @@ -176,12 +189,18 @@ export const FieldArithmetic = () => { value={fieldValueTwo} allowClear={true} onChange={onSecondNumberChange} - style={{ width: 'calc(100% - 110px)' }} + style={{ width: "calc(100% - 110px)" }} /> - <Button + <Button size="large" - onClick={() => onSecondNumberChange({target:{value:generateRandomField()}})} - style={{ width: '110px' }} + onClick={() => + onSecondNumberChange({ + target: { + value: generateRandomField(), + }, + }) + } + style={{ width: "110px" }} > Random </Button> @@ -190,18 +209,16 @@ export const FieldArithmetic = () => { )} <Divider /> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Result</span>} + <Form.Item + label={<span style={{ whiteSpace: "nowrap" }}>Result</span>} colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Input size="large" placeholder="Result will appear here" value={result} - addonAfter={ - <CopyButton data={result} /> - } + addonAfter={<CopyButton data={result} />} disabled /> </Form.Item> diff --git a/website/src/tabs/algebra/GroupArithmetic.jsx b/website/src/tabs/algebra/GroupArithmetic.jsx index e79cfcab4..7484c80fd 100644 --- a/website/src/tabs/algebra/GroupArithmetic.jsx +++ b/website/src/tabs/algebra/GroupArithmetic.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Card, Divider, Form, Input, Select, Radio, Button } from "antd"; import { CopyButton } from "../../components/CopyButton"; import { useAleoWASM } from "../../aleo-wasm-hook"; @@ -67,13 +67,28 @@ export const GroupArithmetic = () => { const onScalarChange = (event) => { setScalarValue(event.target.value); - calculateResult(groupValueOne, groupValueTwo, operation, event.target.value); + calculateResult( + groupValueOne, + groupValueTwo, + operation, + event.target.value, + ); }; - const calculateResult = (group1Str, group2Str, op, scalar = scalarValue) => { - if ((op === "scalar mul" && (group1Str === "" || scalar === "")) || - (op !== "scalar mul" && op !== "neg" && op !== "double" && (group1Str === "" || group2Str === "")) || - ((op === "neg" || op === "double") && group1Str === "")) { + const calculateResult = ( + group1Str, + group2Str, + op, + scalar = scalarValue, + ) => { + if ( + (op === "scalar mul" && (group1Str === "" || scalar === "")) || + (op !== "scalar mul" && + op !== "neg" && + op !== "double" && + (group1Str === "" || group2Str === "")) || + ((op === "neg" || op === "double") && group1Str === "") + ) { setResult(""); return; } @@ -82,10 +97,10 @@ export const GroupArithmetic = () => { let group1String = group1Str; let group2String = group2Str; if (!group1String.includes("group")) { - group1String = group1Str + "group" + group1String = group1Str + "group"; } if (!group2String.includes("group")) { - group2String = group1Str + "group" + group2String = group1Str + "group"; } const group1 = wasm.Group.fromString(group1String); let resultGroup; @@ -156,24 +171,25 @@ export const GroupArithmetic = () => { const newValue = generateRandomScalar(); setScalarValue(newValue); calculateResult(groupValueOne, groupValueTwo, operation, newValue); - } + }; - const layout = { - labelCol: { span: 6 }, + const layout = { + labelCol: { span: 6 }, wrapperCol: { span: 18 }, - style: { marginBottom: '24px' } + style: { marginBottom: "24px" }, }; return ( - <Card - title="Eliptic Curve Group Arithmetic" - style={{ width: "100%" }} - > + <Card title="Eliptic Curve Group Arithmetic" style={{ width: "100%" }}> <Form {...layout}> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Group Element 1</span>} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Group Element 1 + </span> + } colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Input.Group compact> <Input @@ -183,36 +199,38 @@ export const GroupArithmetic = () => { allowClear value={groupValueOne} onChange={onFirstGroupChange} - style={{ width: 'calc(100% - 220px)' }} + style={{ width: "calc(100% - 220px)" }} /> - <Button + <Button size="large" onClick={onRandomGroupOne} - style={{ width: '110px' }} + style={{ width: "110px" }} > Random </Button> - <Button + <Button size="large" onClick={onGeneratorOne} - style={{ width: '110px' }} + style={{ width: "110px" }} > Generator </Button> </Input.Group> </Form.Item> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Operation</span>} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Operation</span> + } colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Radio.Group value={operation} onChange={(e) => onOperationChange(e.target.value)} size="large" > - {operations.map(op => ( + {operations.map((op) => ( <Radio.Button key={op.value} value={op.value}> {op.label} </Radio.Button> @@ -220,11 +238,17 @@ export const GroupArithmetic = () => { </Radio.Group> </Form.Item> - {operation !== "scalar mul" && operation !== "neg" && operation !== "double" ? ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Group Element 2</span>} + {operation !== "scalar mul" && + operation !== "neg" && + operation !== "double" ? ( + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Group Element 2 + </span> + } colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Input.Group compact> <Input @@ -234,29 +258,31 @@ export const GroupArithmetic = () => { value={groupValueTwo} allowClear={true} onChange={onSecondGroupChange} - style={{ width: 'calc(100% - 220px)' }} + style={{ width: "calc(100% - 220px)" }} /> - <Button + <Button size="large" onClick={onRandomGroupTwo} - style={{ width: '110px' }} + style={{ width: "110px" }} > Random </Button> - <Button + <Button size="large" onClick={onGeneratorTwo} - style={{ width: '110px' }} + style={{ width: "110px" }} > Generator </Button> </Input.Group> </Form.Item> ) : operation === "scalar mul" ? ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Scalar</span>} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Scalar</span> + } colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Input.Group compact> <Input @@ -266,12 +292,12 @@ export const GroupArithmetic = () => { allowClear value={scalarValue} onChange={onScalarChange} - style={{ width: 'calc(100% - 110px)' }} + style={{ width: "calc(100% - 110px)" }} /> - <Button + <Button size="large" onClick={onScalarRandom} - style={{ width: '110px' }} + style={{ width: "110px" }} > Random </Button> @@ -280,18 +306,16 @@ export const GroupArithmetic = () => { ) : null} <Divider /> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Result</span>} + <Form.Item + label={<span style={{ whiteSpace: "nowrap" }}>Result</span>} colon={false} - style={{ marginBottom: '24px' }} + style={{ marginBottom: "24px" }} > <Input size="large" placeholder="Result will appear here" value={result} - addonAfter={ - <CopyButton data={result} /> - } + addonAfter={<CopyButton data={result} />} disabled /> </Form.Item> diff --git a/website/src/tabs/algebra/HashFunctions.jsx b/website/src/tabs/algebra/HashFunctions.jsx index d97fd2537..c75424591 100644 --- a/website/src/tabs/algebra/HashFunctions.jsx +++ b/website/src/tabs/algebra/HashFunctions.jsx @@ -1,5 +1,16 @@ import { useEffect, useState } from "react"; -import { Card, Divider, Form, Input, Radio, Select, Button, Typography, Space, Collapse } from "antd"; +import { + Card, + Divider, + Form, + Input, + Radio, + Select, + Button, + Typography, + Space, + Collapse, +} from "antd"; import { CopyButton } from "../../components/CopyButton"; import { useAleoWASM } from "../../aleo-wasm-hook"; @@ -64,7 +75,7 @@ export const HashFunctions = () => { const layout = { labelCol: { span: 6 }, wrapperCol: { span: 18 }, - style: { marginBottom: '24px' } + style: { marginBottom: "24px" }, }; const parseBits = (text) => { @@ -74,8 +85,10 @@ export const HashFunctions = () => { .filter(Boolean) .map((t) => { const lower = t.toLowerCase(); - if (lower === "true" || lower === "t" || lower === "1") return true; - if (lower === "false" || lower === "f" || lower === "0") return false; + if (lower === "true" || lower === "t" || lower === "1") + return true; + if (lower === "false" || lower === "f" || lower === "0") + return false; throw new Error(`Invalid bit '${t}'. Use true/false or 1/0`); }); }; @@ -112,29 +125,50 @@ export const HashFunctions = () => { let hasher; switch (bhp) { - case "BHP256": hasher = new wasm.BHP256(); break; - case "BHP512": hasher = new wasm.BHP512(); break; - case "BHP768": hasher = new wasm.BHP768(); break; - case "BHP1024": hasher = new wasm.BHP1024(); break; - default: hasher = new wasm.BHP256(); + case "BHP256": + hasher = new wasm.BHP256(); + break; + case "BHP512": + hasher = new wasm.BHP512(); + break; + case "BHP768": + hasher = new wasm.BHP768(); + break; + case "BHP1024": + hasher = new wasm.BHP1024(); + break; + default: + hasher = new wasm.BHP256(); } let out; switch (bhpOp) { case "hash": - out = hasher.hash(bytesFromFieldsLe(parseFields(finiteFieldCsv))); + out = hasher.hash( + bytesFromFieldsLe(parseFields(finiteFieldCsv)), + ); break; case "hashToGroup": - out = hasher.hashToGroup(bytesFromFieldsLe(parseFields(finiteFieldCsv))); + out = hasher.hashToGroup( + bytesFromFieldsLe(parseFields(finiteFieldCsv)), + ); break; case "commit": - out = hasher.commit(bytesFromFieldsLe(parseFields(finiteFieldCsv)), scalar.clone()); + out = hasher.commit( + bytesFromFieldsLe(parseFields(finiteFieldCsv)), + scalar.clone(), + ); break; case "commitToGroup": - out = hasher.commitToGroup(bytesFromFieldsLe(parseFields(finiteFieldCsv)), scalar.clone()); + out = hasher.commitToGroup( + bytesFromFieldsLe(parseFields(finiteFieldCsv)), + scalar.clone(), + ); break; default: - out = hasher.hash(bytesFromFieldsLe(parseFields(finiteFieldCsv))); + out = hasher.hash( + bytesFromFieldsLe(parseFields(finiteFieldCsv)), + ); } setResult(out.toString()); } catch (e) { @@ -152,9 +186,14 @@ export const HashFunctions = () => { let hasher; switch (pedersen) { - case "Pedersen64": hasher = new wasm.Pedersen64(); break; - case "Pedersen128": hasher = new wasm.Pedersen128(); break; - default: hasher = new wasm.Pedersen64(); + case "Pedersen64": + hasher = new wasm.Pedersen64(); + break; + case "Pedersen128": + hasher = new wasm.Pedersen128(); + break; + default: + hasher = new wasm.Pedersen64(); } let out; @@ -185,10 +224,17 @@ export const HashFunctions = () => { const fields = parseFields(finiteFieldCsv); let hasher; switch (poseidon) { - case "Poseidon2": hasher = new wasm.Poseidon2(); break; - case "Poseidon4": hasher = new wasm.Poseidon4(); break; - case "Poseidon8": hasher = new wasm.Poseidon8(); break; - default: hasher = new wasm.Poseidon2(); + case "Poseidon2": + hasher = new wasm.Poseidon2(); + break; + case "Poseidon4": + hasher = new wasm.Poseidon4(); + break; + case "Poseidon8": + hasher = new wasm.Poseidon8(); + break; + default: + hasher = new wasm.Poseidon2(); } let out; @@ -204,8 +250,14 @@ export const HashFunctions = () => { break; case "hashMany": { - const n = Math.max(1, parseInt(hashManyChunkSize || "2", 10)); - const arr = hasher.hashMany(fields.map((f) => f.clone()), n); + const n = Math.max( + 1, + parseInt(hashManyChunkSize || "2", 10), + ); + const arr = hasher.hashMany( + fields.map((f) => f.clone()), + n, + ); out = `[${arr.map((f) => f.toString()).join(", ")}]`; } break; @@ -245,7 +297,7 @@ export const HashFunctions = () => { } else { computePoseidon(); } - // eslint-disable-next-line react-hooks/exhaustive-deps + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ wasm, family, @@ -273,7 +325,11 @@ export const HashFunctions = () => { const encoder = new TextEncoder(); const utf8 = encoder.encode(text || ""); const fields = []; - for (let i = 0; i < utf8.length || (utf8.length === 0 && i === 0); i += 31) { + for ( + let i = 0; + i < utf8.length || (utf8.length === 0 && i === 0); + i += 31 + ) { const chunk = utf8.subarray(i, Math.min(i + 31, utf8.length)); const padded = new Uint8Array(32); padded.set(chunk); @@ -299,9 +355,11 @@ export const HashFunctions = () => { for (let i = 0; i < bytes.length; i++) { const b = bytes[i]; if (order === "le") { - for (let bit = 0; bit < 8; bit++) out.push(((b >> bit) & 1) === 1); + for (let bit = 0; bit < 8; bit++) + out.push(((b >> bit) & 1) === 1); } else { - for (let bit = 7; bit >= 0; bit--) out.push(((b >> bit) & 1) === 1); + for (let bit = 7; bit >= 0; bit--) + out.push(((b >> bit) & 1) === 1); } } return out; @@ -327,26 +385,64 @@ export const HashFunctions = () => { if (family === "BHP") { return ( <> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Hasher</span>} colon={false} style={{ marginBottom: '24px' }}> - <Select value={bhp} onChange={setBhp} options={BHP_OPTIONS} size="large" /> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Hasher</span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Select + value={bhp} + onChange={setBhp} + options={BHP_OPTIONS} + size="large" + /> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>String → Fields</span>} colon={false} style={{ marginBottom: '24px' }}> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + String → Fields + </span> + } + colon={false} + style={{ marginBottom: "24px" }} + > <Collapse> - <Collapse.Panel header="String → Fields (optional)" key="strFieldsBHP"> - <Space direction="vertical" style={{ width: '100%' }}> - <Text>Enter a string to convert into 31-byte field limbs. This will populate the Fields input.</Text> + <Collapse.Panel + header="String → Fields (optional)" + key="strFieldsBHP" + > + <Space + direction="vertical" + style={{ width: "100%" }} + > + <Text> + Enter a string to convert into 31-byte + field limbs. This will populate the + Fields input. + </Text> <Input size="large" placeholder="Enter any UTF-8 string" value={stringInput} - onChange={(e) => { setStringInput(e.target.value); encodeStringToFieldsFrom(e.target.value); }} + onChange={(e) => { + setStringInput(e.target.value); + encodeStringToFieldsFrom( + e.target.value, + ); + }} allowClear /> {stringFieldsPreview ? ( <Input size="large" value={stringFieldsPreview} - addonAfter={<CopyButton data={stringFieldsPreview} />} + addonAfter={ + <CopyButton + data={stringFieldsPreview} + /> + } disabled /> ) : null} @@ -354,30 +450,60 @@ export const HashFunctions = () => { </Collapse.Panel> </Collapse> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Operation</span>} colon={false} style={{ marginBottom: '24px' }}> - <Radio.Group value={bhpOp} onChange={(e) => setBhpOp(e.target.value)} size="large"> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Operation + </span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Radio.Group + value={bhpOp} + onChange={(e) => setBhpOp(e.target.value)} + size="large" + > <Radio.Button value="hash">Hash</Radio.Button> - <Radio.Button value="hashToGroup">Hash → Group</Radio.Button> + <Radio.Button value="hashToGroup"> + Hash → Group + </Radio.Button> <Radio.Button value="commit">Commit</Radio.Button> - <Radio.Button value="commitToGroup">Commit → Group</Radio.Button> + <Radio.Button value="commitToGroup"> + Commit → Group + </Radio.Button> </Radio.Group> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Inputs</span>} colon={false} style={{ marginBottom: '24px' }}> - <Space direction="vertical" style={{ width: '100%' }}> - <Text>Enter Field elements (comma-separated). Example: 1, 2, 3</Text> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Inputs</span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Space direction="vertical" style={{ width: "100%" }}> + <Text> + Enter Field elements (comma-separated). Example: + 1, 2, 3 + </Text> <Input size="large" placeholder="e.g. 1, 2, 3, 4" value={finiteFieldCsv} - onChange={(e) => setFiniteFieldCsv(e.target.value)} + onChange={(e) => + setFiniteFieldCsv(e.target.value) + } allowClear /> - {(bhpOp === "commit" || bhpOp === "commitToGroup") && ( + {(bhpOp === "commit" || + bhpOp === "commitToGroup") && ( <Input size="large" placeholder="Scalar (e.g. 5)" value={scalarInput} - onChange={(e) => setScalarInput(e.target.value)} + onChange={(e) => + setScalarInput(e.target.value) + } allowClear /> )} @@ -390,46 +516,111 @@ export const HashFunctions = () => { if (family === "Pedersen") { return ( <> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Hasher</span>} colon={false} style={{ marginBottom: '24px' }}> - <Select value={pedersen} onChange={setPedersen} options={PEDERSEN_OPTIONS} size="large" /> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Hasher</span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Select + value={pedersen} + onChange={setPedersen} + options={PEDERSEN_OPTIONS} + size="large" + /> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>String → Bits</span>} colon={false} style={{ marginBottom: '24px' }}> - <Space direction="vertical" style={{ width: '100%' }}> - <Text>Enter a string to convert to UTF-8 bytes and then to a bit array for Pedersen.</Text> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + String → Bits + </span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Space direction="vertical" style={{ width: "100%" }}> + <Text> + Enter a string to convert to UTF-8 bytes and + then to a bit array for Pedersen. + </Text> <Input size="large" placeholder="Enter any UTF-8 string" value={bitsStringInput} - onChange={(e) => setBitsStringInput(e.target.value)} + onChange={(e) => + setBitsStringInput(e.target.value) + } allowClear /> <Space> - <Radio.Group value={bitOrder} onChange={(e) => setBitOrder(e.target.value)} size="large"> - <Radio.Button value="le">Per-byte: Little Endian</Radio.Button> - <Radio.Button value="be">Per-byte: Big Endian</Radio.Button> + <Radio.Group + value={bitOrder} + onChange={(e) => + setBitOrder(e.target.value) + } + size="large" + > + <Radio.Button value="le"> + Per-byte: Little Endian + </Radio.Button> + <Radio.Button value="be"> + Per-byte: Big Endian + </Radio.Button> </Radio.Group> - <Button size="large" onClick={encodeStringToBits} disabled={!wasm}>Convert & Fill</Button> + <Button + size="large" + onClick={encodeStringToBits} + disabled={!wasm} + > + Convert & Fill + </Button> </Space> {bitsPreview ? ( <Input size="large" value={bitsPreview} - addonAfter={<CopyButton data={bitsPreview} />} + addonAfter={ + <CopyButton data={bitsPreview} /> + } disabled /> ) : null} </Space> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Operation</span>} colon={false} style={{ marginBottom: '24px' }}> - <Radio.Group value={pedersenOp} onChange={(e) => setPedersenOp(e.target.value)} size="large"> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Operation + </span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Radio.Group + value={pedersenOp} + onChange={(e) => setPedersenOp(e.target.value)} + size="large" + > <Radio.Button value="hash">Hash</Radio.Button> <Radio.Button value="commit">Commit</Radio.Button> - <Radio.Button value="commitToGroup">Commit → Group</Radio.Button> + <Radio.Button value="commitToGroup"> + Commit → Group + </Radio.Button> </Radio.Group> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Inputs</span>} colon={false} style={{ marginBottom: '24px' }}> - <Space direction="vertical" style={{ width: '100%' }}> - <Text>Enter bits as comma-separated values. Example: 1,0,0,1,1</Text> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Inputs</span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Space direction="vertical" style={{ width: "100%" }}> + <Text> + Enter bits as comma-separated values. Example: + 1,0,0,1,1 + </Text> <Input size="large" placeholder="e.g. true, false, 1, 0, 1, 0" @@ -437,12 +628,15 @@ export const HashFunctions = () => { onChange={(e) => setBitsInput(e.target.value)} allowClear /> - {(pedersenOp === "commit" || pedersenOp === "commitToGroup") && ( + {(pedersenOp === "commit" || + pedersenOp === "commitToGroup") && ( <Input size="large" placeholder="Scalar (e.g. 5)" value={scalarInput} - onChange={(e) => setScalarInput(e.target.value)} + onChange={(e) => + setScalarInput(e.target.value) + } allowClear /> )} @@ -455,26 +649,61 @@ export const HashFunctions = () => { // Poseidon return ( <> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Hasher</span>} colon={false} style={{ marginBottom: '24px' }}> - <Select value={poseidon} onChange={setPoseidon} options={POSEIDON_OPTIONS} size="large" /> + <Form.Item + label={<span style={{ whiteSpace: "nowrap" }}>Hasher</span>} + colon={false} + style={{ marginBottom: "24px" }} + > + <Select + value={poseidon} + onChange={setPoseidon} + options={POSEIDON_OPTIONS} + size="large" + /> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>String → Fields</span>} colon={false} style={{ marginBottom: '24px' }}> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + String → Fields + </span> + } + colon={false} + style={{ marginBottom: "24px" }} + > <Collapse> - <Collapse.Panel header="String → Fields (optional)" key="strFieldsPoseidon"> - <Space direction="vertical" style={{ width: '100%' }}> - <Text>Enter a string to convert into 31-byte field limbs. This will populate the Fields input.</Text> + <Collapse.Panel + header="String → Fields (optional)" + key="strFieldsPoseidon" + > + <Space + direction="vertical" + style={{ width: "100%" }} + > + <Text> + Enter a string to convert into 31-byte field + limbs. This will populate the Fields input. + </Text> <Input size="large" placeholder="Enter any UTF-8 string" value={stringInput} - onChange={(e) => { setStringInput(e.target.value); encodeStringToFieldsFrom(e.target.value); }} + onChange={(e) => { + setStringInput(e.target.value); + encodeStringToFieldsFrom( + e.target.value, + ); + }} allowClear /> {stringFieldsPreview ? ( <Input size="large" value={stringFieldsPreview} - addonAfter={<CopyButton data={stringFieldsPreview} />} + addonAfter={ + <CopyButton + data={stringFieldsPreview} + /> + } disabled /> ) : null} @@ -482,17 +711,38 @@ export const HashFunctions = () => { </Collapse.Panel> </Collapse> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Operation</span>} colon={false} style={{ marginBottom: '24px' }}> - <Radio.Group value={poseidonOp} onChange={(e) => setPoseidonOp(e.target.value)} size="large"> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Operation</span> + } + colon={false} + style={{ marginBottom: "24px" }} + > + <Radio.Group + value={poseidonOp} + onChange={(e) => setPoseidonOp(e.target.value)} + size="large" + > <Radio.Button value="hash">Hash</Radio.Button> - <Radio.Button value="hashToScalar">Hash → Scalar</Radio.Button> - <Radio.Button value="hashToGroup">Hash → Group</Radio.Button> + <Radio.Button value="hashToScalar"> + Hash → Scalar + </Radio.Button> + <Radio.Button value="hashToGroup"> + Hash → Group + </Radio.Button> <Radio.Button value="hashMany">Hash Many</Radio.Button> </Radio.Group> </Form.Item> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Inputs</span>} colon={false} style={{ marginBottom: '24px' }}> - <Space direction="vertical" style={{ width: '100%' }}> - <Text>Enter Field elements (comma-separated). Example: 1, 2, 3</Text> + <Form.Item + label={<span style={{ whiteSpace: "nowrap" }}>Inputs</span>} + colon={false} + style={{ marginBottom: "24px" }} + > + <Space direction="vertical" style={{ width: "100%" }}> + <Text> + Enter Field elements (comma-separated). Example: 1, + 2, 3 + </Text> <Input size="large" placeholder="e.g. 1, 2, 3, 4" @@ -505,7 +755,9 @@ export const HashFunctions = () => { size="large" placeholder="Chunk size (e.g. 2)" value={hashManyChunkSize} - onChange={(e) => setHashManyChunkSize(e.target.value)} + onChange={(e) => + setHashManyChunkSize(e.target.value) + } /> )} </Space> @@ -517,10 +769,20 @@ export const HashFunctions = () => { return ( <Card title="Hash Functions" style={{ width: "100%" }}> <Form {...layout}> - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Family</span>} colon={false} style={{ marginBottom: '24px' }}> - <Radio.Group value={family} onChange={(e) => setFamily(e.target.value)} size="large"> + <Form.Item + label={<span style={{ whiteSpace: "nowrap" }}>Family</span>} + colon={false} + style={{ marginBottom: "24px" }} + > + <Radio.Group + value={family} + onChange={(e) => setFamily(e.target.value)} + size="large" + > {HASHER_FAMILIES.map((f) => ( - <Radio.Button key={f.value} value={f.value}>{f.label}</Radio.Button> + <Radio.Button key={f.value} value={f.value}> + {f.label} + </Radio.Button> ))} </Radio.Group> </Form.Item> @@ -529,12 +791,22 @@ export const HashFunctions = () => { <Divider /> {error ? ( - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Error</span>} colon={false} style={{ marginBottom: '24px' }}> + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}>Error</span> + } + colon={false} + style={{ marginBottom: "24px" }} + > <Input size="large" value={error} disabled /> </Form.Item> ) : null} - <Form.Item label={<span style={{ whiteSpace: 'nowrap' }}>Result</span>} colon={false} style={{ marginBottom: '24px' }}> + <Form.Item + label={<span style={{ whiteSpace: "nowrap" }}>Result</span>} + colon={false} + style={{ marginBottom: "24px" }} + > <Input size="large" placeholder="Result will appear here" @@ -551,4 +823,3 @@ export const HashFunctions = () => { }; export default HashFunctions; - diff --git a/website/src/tabs/develop/Deploy.jsx b/website/src/tabs/develop/Deploy.jsx index 58546ac79..18cd5665f 100644 --- a/website/src/tabs/develop/Deploy.jsx +++ b/website/src/tabs/develop/Deploy.jsx @@ -10,14 +10,13 @@ import { Row, Result, Spin, - Space, Switch, + Space, + Switch, } from "antd"; import { CodeEditor } from "./execute/CodeEditor.jsx"; import { useAleoWASM } from "../../aleo-wasm-hook.js"; - export const Deploy = () => { - const [form] = Form.useForm(); const [aleoWASM] = useAleoWASM(); const [deploymentFeeRecord, setDeploymentFeeRecord] = useState(null); @@ -33,7 +32,7 @@ export const Deploy = () => { const [transactionID, setTransactionID] = useState(null); const [worker, setWorker] = useState(null); const [messageApi, contextHolder] = message.useMessage(); - + function spawnWorker() { let worker = new Worker( new URL("../../workers/worker.js", import.meta.url), @@ -204,14 +203,12 @@ export const Deploy = () => { transactionID !== null ? transactionID : ""; const deploymentErrorString = () => deploymentError !== null ? deploymentError : ""; - const feeString = () => deploymentFeeEstimate ? deploymentFeeEstimate : ""; + const feeString = () => + deploymentFeeEstimate ? deploymentFeeEstimate : ""; const peerUrl = () => (deployUrl !== null ? deployUrl : ""); const generateKey = () => { - const newKey = new aleoWASM.PrivateKey().to_string() - form.setFieldValue( - "private_key", - newKey - ); + const newKey = new aleoWASM.PrivateKey().to_string(); + form.setFieldValue("private_key", newKey); setPrivateKey(newKey); form.validateFields(["private_key"]); @@ -219,34 +216,28 @@ export const Deploy = () => { return ( <Card title="Deploy Program" - style={{ width: "100%"}} + style={{ width: "100%" }} extra={ - <Button - type="primary" - size="middle" - onClick={demo} - > + <Button type="primary" size="middle" onClick={demo}> Insert Demo Program </Button> } > - <Form - form={form} - {...layout}> + <Form form={form} {...layout}> <Divider /> - <Form.Item - label="Program" - name="program" - tooltip={"This must be an Aleo Instructions program."} - rules={[ - { - required: true, - message: "Please input or load an Aleo program", - }, - ]} - > - <CodeEditor onChange={onProgramChange} /> - </Form.Item> + <Form.Item + label="Program" + name="program" + tooltip={"This must be an Aleo Instructions program."} + rules={[ + { + required: true, + message: "Please input or load an Aleo program", + }, + ]} + > + <CodeEditor onChange={onProgramChange} /> + </Form.Item> <Divider /> <Form.Item label="Private Key" @@ -255,10 +246,10 @@ export const Deploy = () => { validateStatus={status} > <Input.Search - enterButton="Generate Random Key" - onSearch={generateKey} - onChange={onPrivateKeyChange} - /> + enterButton="Generate Random Key" + onSearch={generateKey} + onChange={onPrivateKeyChange} + /> </Form.Item> <Form.Item label="Peer Url" @@ -305,7 +296,6 @@ export const Deploy = () => { <Space> <Button type="primary" - size="middle" onClick={deploy} > @@ -314,7 +304,6 @@ export const Deploy = () => { {contextHolder} <Button type="primary" - size="middle" onClick={estimate} > @@ -354,7 +343,9 @@ export const Deploy = () => { status="success" title="Estimated Deployment Fee" subTitle={ - "Estimated Deployment Fee: " + feeString() + " credits" + "Estimated Deployment Fee: " + + feeString() + + " credits" } /> )} diff --git a/website/src/tabs/develop/ExecuteLegacy.jsx b/website/src/tabs/develop/ExecuteLegacy.jsx index 5a0ca12fc..4b66df562 100644 --- a/website/src/tabs/develop/ExecuteLegacy.jsx +++ b/website/src/tabs/develop/ExecuteLegacy.jsx @@ -390,12 +390,7 @@ export const ExecuteLegacy = () => { title="Execute Program" style={{ width: "100%" }} extra={ - <Button - type="primary" - - size="middle" - onClick={demo} - > + <Button type="primary" size="middle" onClick={demo}> Demo </Button> } @@ -557,7 +552,6 @@ export const ExecuteLegacy = () => { <Space> <Button type="primary" - size="middle" onClick={execute} > diff --git a/website/src/tabs/develop/Join.jsx b/website/src/tabs/develop/Join.jsx index 06dba09fe..cf9d3c00e 100644 --- a/website/src/tabs/develop/Join.jsx +++ b/website/src/tabs/develop/Join.jsx @@ -1,5 +1,15 @@ import { useState, useEffect } from "react"; -import {Button, Card, Col, Form, Input, Row, Result, Spin, Switch} from "antd"; +import { + Button, + Card, + Col, + Form, + Input, + Row, + Result, + Spin, + Switch, +} from "antd"; import axios from "axios"; export const Join = () => { @@ -131,10 +141,7 @@ export const Join = () => { const peerUrl = () => (joinUrl !== null ? joinUrl : ""); return ( - <Card - title="Join Records" - style={{ width: "100%" }} - > + <Card title="Join Records" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="Record One" @@ -217,12 +224,7 @@ export const Join = () => { </Form.Item> <Row justify="center"> <Col justify="center"> - <Button - type="primary" - - size="middle" - onClick={join} - > + <Button type="primary" size="middle" onClick={join}> Join </Button> </Col> diff --git a/website/src/tabs/develop/Split.jsx b/website/src/tabs/develop/Split.jsx index 74c02133b..7321d9aad 100644 --- a/website/src/tabs/develop/Split.jsx +++ b/website/src/tabs/develop/Split.jsx @@ -128,10 +128,7 @@ export const Split = () => { const peerUrl = () => (splitUrl !== null ? splitUrl : ""); return ( - <Card - title="Split Record" - style={{ width: "100%"}} - > + <Card title="Split Record" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="Split Amount" @@ -187,12 +184,7 @@ export const Split = () => { </Form.Item> <Row justify="center"> <Col justify="center"> - <Button - type="primary" - - size="middle" - onClick={split} - > + <Button type="primary" size="middle" onClick={split}> Split </Button> </Col> diff --git a/website/src/tabs/develop/Transfer.jsx b/website/src/tabs/develop/Transfer.jsx index fbd04a2f2..533cf1a93 100644 --- a/website/src/tabs/develop/Transfer.jsx +++ b/website/src/tabs/develop/Transfer.jsx @@ -19,7 +19,9 @@ import axios from "axios"; export const Transfer = () => { const [transferFeeRecord, setTransferFeeRecord] = useState(null); const [amountRecord, setAmountRecord] = useState(null); - const [transferUrl, setTransferUrl] = useState("https://api.provable.com/v2"); + const [transferUrl, setTransferUrl] = useState( + "https://api.provable.com/v2", + ); const [transferAmount, setTransferAmount] = useState("1.0"); const [privateFee, setPrivateFee] = useState(false); const [recipient, setRecipient] = useState(null); @@ -227,7 +229,9 @@ export const Transfer = () => { style={{ width: "9rem" }} placeholder={visibilityString()} options={items} - onChange={(item) => {setVisibility(item)}} + onChange={(item) => { + setVisibility(item); + }} ></Select> </div> <Form.Item diff --git a/website/src/tabs/develop/execute/index.jsx b/website/src/tabs/develop/execute/index.jsx index 32c7d2af7..d1fa60d41 100644 --- a/website/src/tabs/develop/execute/index.jsx +++ b/website/src/tabs/develop/execute/index.jsx @@ -34,7 +34,7 @@ export const Execute = () => { " input r0 as u32.public;\n" + " input r1 as u32.private;\n" + " add r0 r1 into r2;\n" + - " output r2 as u32.private;\n" + " output r2 as u32.private;\n", ); form.setFieldValue("manual_input", true); form.setFieldValue("functionName", "hello"); @@ -116,7 +116,7 @@ export const Execute = () => { function spawnWorker() { let worker = new Worker( new URL("../../../workers/worker.js", import.meta.url), - { type: "module" } + { type: "module" }, ); worker.addEventListener("message", (ev) => { if (ev.data.type == "OFFLINE_EXECUTION_COMPLETED") { @@ -194,7 +194,7 @@ export const Execute = () => { const generateKey = () => { form.setFieldValue( "private_key", - new aleoWASM.PrivateKey().to_string() + new aleoWASM.PrivateKey().to_string(), ); form.validateFields(["private_key"]); }; @@ -239,225 +239,244 @@ export const Execute = () => { return ( <> + <NewAccount /> - <NewAccount /> + <br /> - <br/> - - <Card - title="Execute Program" - extra={ - <Select - placeholder="Select a demo" - onChange={demoSelect} - options={[ - { - value: "hello", - label: "hello_hello.aleo", - }, - ]} - /> - } - > - <Modal - title="Executing program..." - open={modalOpen} - onOk={handleOk} - confirmLoading={loading} - cancelButtonProps={{ style: { display: "none" } }} - closeIcon={false} - maskClosable={false} - > - {loading ? <Skeleton active /> : <Result {...modalResult} />} - </Modal> - <Form.Provider - onFormFinish={(name, info) => { - if (name !== "execute") { - form.setFieldValue("functionName", name); - let translatedArray = info.values.inputs.map((item) => { - return JSON.stringify(item).replaceAll('"', ""); - }); - form.setFieldValue( - "inputs", - JSON.stringify(translatedArray) - ); - form.submit(); - } - }} - > - <Form - form={form} - name="execute" - {...layout} - onFinish={execute} - autoComplete="off" - scrollToFirstError="true" - > - <LoadProgram onResponse={onLoadProgram} /> - <Form.Item - label="Program" - name="program" - rules={[ - { - required: true, - message: "Please input or load an Aleo program", - }, - ]} - > - <CodeEditor onChange={onProgramEdit} /> - </Form.Item> - <Divider dashed /> - <Form.Item - label="Private Key" - name="private_key" - rules={[ + <Card + title="Execute Program" + extra={ + <Select + placeholder="Select a demo" + onChange={demoSelect} + options={[ { - required: true, - message: "Private key required", + value: "hello", + label: "hello_hello.aleo", }, ]} - > - <Input.Search - enterButton="Generate Random Key" - onSearch={generateKey} - /> - </Form.Item> - <Divider dashed /> - <Form.Item - label="Execute On-Chain" - name="execute_onchain" - valuePropName="checked" - initialValue={false} - > - <Switch /> - </Form.Item> - <Form.Item - noStyle - shouldUpdate={(prevValues, currentValues) => - prevValues.execute_onchain !== - currentValues.execute_onchain - } - > - {({ getFieldValue }) => ( - <> - <Form.Item - label="Peer URL" - name="peer_url" - initialValue="https://api.provable.com/v2" - hidden={!getFieldValue("execute_onchain")} - > - <Input /> - </Form.Item> - <Form.Item - label="Private Fee" - name="private_fee" - valuePropName="checked" - initialValue={false} - hidden={!getFieldValue("execute_onchain")} - > - <Switch defaultChecked /> - </Form.Item> - <Form.Item - noStyle - shouldUpdate={(prevValues, currentValues) => - prevValues.private_fee !== - currentValues.private_fee - } - > - {({ getFieldValue }) => ( - <> - <Form.Item - label="Fee Record" - name="fee_record" - hidden={ - !getFieldValue( - "private_fee" - ) || - !getFieldValue( - "execute_onchain" - ) - } - rules={[ - { - required: - getFieldValue( - "private_fee" - ) && - getFieldValue( - "execute_onchain" - ), - message: - "Fee record needed for private fee", - }, - ]} - > - <Input.TextArea /> - </Form.Item> - </> - )} - </Form.Item> - </> - )} - </Form.Item> - <Divider dashed /> - <Form.Item - label="Manual Input" - name="manual_input" - valuePropName="checked" - initialValue={false} - > - <Switch /> - </Form.Item> - <Form.Item - noStyle - shouldUpdate={(prevValues, currentValues) => - prevValues.manual_input !== - currentValues.manual_input + /> + } + > + <Modal + title="Executing program..." + open={modalOpen} + onOk={handleOk} + confirmLoading={loading} + cancelButtonProps={{ style: { display: "none" } }} + closeIcon={false} + maskClosable={false} + > + {loading ? ( + <Skeleton active /> + ) : ( + <Result {...modalResult} /> + )} + </Modal> + <Form.Provider + onFormFinish={(name, info) => { + if (name !== "execute") { + form.setFieldValue("functionName", name); + let translatedArray = info.values.inputs.map( + (item) => { + return JSON.stringify(item).replaceAll( + '"', + "", + ); + }, + ); + form.setFieldValue( + "inputs", + JSON.stringify(translatedArray), + ); + form.submit(); } + }} + > + <Form + form={form} + name="execute" + {...layout} + onFinish={execute} + autoComplete="off" + scrollToFirstError="true" > - {({ getFieldValue }) => ( - <> - <Form.Item - label="Function" - name="functionName" - hidden={!getFieldValue("manual_input")} - > - <Input /> - </Form.Item> - <Form.Item - label="Inputs" - name="inputs" - hidden={!getFieldValue("manual_input")} - > - <Input.TextArea /> - </Form.Item> - <Form.Item - wrapperCol={{ - xs: { - offset: 0, - }, - sm: { - offset: 4, - }, - }} - hidden={!getFieldValue("manual_input")} - > - <Button type="primary" htmlType="submit"> - Run - </Button> - </Form.Item> - </> - )} - </Form.Item> - </Form> - <Divider dashed>Program Functions</Divider> - {functions.length > 0 ? ( - <Collapse bordered={false} items={functions} /> - ) : ( - <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> - )} - </Form.Provider> - </Card> + <LoadProgram onResponse={onLoadProgram} /> + <Form.Item + label="Program" + name="program" + rules={[ + { + required: true, + message: + "Please input or load an Aleo program", + }, + ]} + > + <CodeEditor onChange={onProgramEdit} /> + </Form.Item> + <Divider dashed /> + <Form.Item + label="Private Key" + name="private_key" + rules={[ + { + required: true, + message: "Private key required", + }, + ]} + > + <Input.Search + enterButton="Generate Random Key" + onSearch={generateKey} + /> + </Form.Item> + <Divider dashed /> + <Form.Item + label="Execute On-Chain" + name="execute_onchain" + valuePropName="checked" + initialValue={false} + > + <Switch /> + </Form.Item> + <Form.Item + noStyle + shouldUpdate={(prevValues, currentValues) => + prevValues.execute_onchain !== + currentValues.execute_onchain + } + > + {({ getFieldValue }) => ( + <> + <Form.Item + label="Peer URL" + name="peer_url" + initialValue="https://api.provable.com/v2" + hidden={ + !getFieldValue("execute_onchain") + } + > + <Input /> + </Form.Item> + <Form.Item + label="Private Fee" + name="private_fee" + valuePropName="checked" + initialValue={false} + hidden={ + !getFieldValue("execute_onchain") + } + > + <Switch defaultChecked /> + </Form.Item> + <Form.Item + noStyle + shouldUpdate={( + prevValues, + currentValues, + ) => + prevValues.private_fee !== + currentValues.private_fee + } + > + {({ getFieldValue }) => ( + <> + <Form.Item + label="Fee Record" + name="fee_record" + hidden={ + !getFieldValue( + "private_fee", + ) || + !getFieldValue( + "execute_onchain", + ) + } + rules={[ + { + required: + getFieldValue( + "private_fee", + ) && + getFieldValue( + "execute_onchain", + ), + message: + "Fee record needed for private fee", + }, + ]} + > + <Input.TextArea /> + </Form.Item> + </> + )} + </Form.Item> + </> + )} + </Form.Item> + <Divider dashed /> + <Form.Item + label="Manual Input" + name="manual_input" + valuePropName="checked" + initialValue={false} + > + <Switch /> + </Form.Item> + <Form.Item + noStyle + shouldUpdate={(prevValues, currentValues) => + prevValues.manual_input !== + currentValues.manual_input + } + > + {({ getFieldValue }) => ( + <> + <Form.Item + label="Function" + name="functionName" + hidden={!getFieldValue("manual_input")} + > + <Input /> + </Form.Item> + <Form.Item + label="Inputs" + name="inputs" + hidden={!getFieldValue("manual_input")} + > + <Input.TextArea /> + </Form.Item> + <Form.Item + wrapperCol={{ + xs: { + offset: 0, + }, + sm: { + offset: 4, + }, + }} + hidden={!getFieldValue("manual_input")} + > + <Button + type="primary" + htmlType="submit" + > + Run + </Button> + </Form.Item> + </> + )} + </Form.Item> + </Form> + <Divider dashed>Program Functions</Divider> + {functions.length > 0 ? ( + <Collapse bordered={false} items={functions} /> + ) : ( + <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> + )} + </Form.Provider> + </Card> </> ); }; @@ -474,8 +493,8 @@ const renderInput = (input, inputIndex, nameArray = []) => { renderInput( member, memberIndex, - [].concat(nameArray).concat(input.name || inputIndex) - ) + [].concat(nameArray).concat(input.name || inputIndex), + ), )} </div> ); @@ -503,7 +522,7 @@ const functionForm = (func, funcInputs) => { > {funcInputs.length > 0 ? ( funcInputs.map((input, inputIndex) => - renderInput(input, inputIndex, ["inputs"]) + renderInput(input, inputIndex, ["inputs"]), ) ) : ( <Form.Item diff --git a/website/src/tabs/protocol/DecryptRecord.css b/website/src/tabs/protocol/DecryptRecord.css index c5b60a947..4589ea8b5 100644 --- a/website/src/tabs/protocol/DecryptRecord.css +++ b/website/src/tabs/protocol/DecryptRecord.css @@ -1,3 +1,3 @@ .container { max-width: 850px; -} \ No newline at end of file +} diff --git a/website/src/tabs/protocol/DecryptRecord.jsx b/website/src/tabs/protocol/DecryptRecord.jsx index 44fa9058e..96b0d32d6 100644 --- a/website/src/tabs/protocol/DecryptRecord.jsx +++ b/website/src/tabs/protocol/DecryptRecord.jsx @@ -71,7 +71,7 @@ export const DecryptRecord = () => { setIsOwner(null); }; - const layout = { labelCol: { span: 4 }, wrapperCol: { span: 21 }}; + const layout = { labelCol: { span: 4 }, wrapperCol: { span: 21 } }; if (aleo !== null) { const recordPlaintext = () => @@ -93,7 +93,7 @@ export const DecryptRecord = () => { record is used, it’s consumed and can’t be reused. </li> <li> - Functions can consume records as input and/or generate + Functions can consume records as input and/or generate new records as output. </li> <li> @@ -109,10 +109,10 @@ export const DecryptRecord = () => { <br /> <p> - Try the demo below! Enter a record and - decrypt it using your View Key to experience how the process - works. You can also click the "Show Demo" button on the - right to generate an example. + Try the demo below! Enter a record and decrypt it using your + View Key to experience how the process works. You can also + click the "Show Demo" button on the right to generate an + example. </p> <br /> diff --git a/website/src/tabs/protocol/TransactionInfo.jsx b/website/src/tabs/protocol/TransactionInfo.jsx index 409a913ce..747e78a59 100644 --- a/website/src/tabs/protocol/TransactionInfo.jsx +++ b/website/src/tabs/protocol/TransactionInfo.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState, useEffect} from "react"; +import { useMemo, useState, useEffect } from "react"; import { Card, Divider, Form, Input, Row, Col, Typography, Radio } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton.jsx"; @@ -24,24 +24,29 @@ export const TransactionInfo = () => { // Initial load of default transaction useEffect(() => { if (wasm) { - const defaultTx = selectedDefault === "execute" ? executeTransaction : deployTransaction; + const defaultTx = + selectedDefault === "execute" + ? executeTransaction + : deployTransaction; tryParseTransaction(defaultTx, false); } }, [selectedDefault, wasm]); // Add parameter to indicate if input needs JSON stringifying const tryParseTransaction = (txData, needsStringify = true) => { - const displayData = needsStringify ? JSON.stringify(txData, null, 2) : txData; + const displayData = needsStringify + ? JSON.stringify(txData, null, 2) + : txData; setTransaction(displayData); try { - const parsed = needsStringify ? - wasm.Transaction.fromString(JSON.stringify(txData)) : - wasm.Transaction.fromString(txData); + const parsed = needsStringify + ? wasm.Transaction.fromString(JSON.stringify(txData)) + : wasm.Transaction.fromString(txData); setParsedTransaction(parsed); - + // Set transaction type setTransactionType(parsed.transactionType()); - + // Try to get deployed program try { const program = parsed.deployedProgram(); @@ -53,13 +58,13 @@ export const TransactionInfo = () => { } catch (error) { setDeployedProgram(""); } - + // Format records const records = parsed.records(); if (Array.isArray(records)) { const formattedRecords = records - .map(record => record.record.toString()) - .join('\n'); + .map((record) => record.record.toString()) + .join("\n"); setRecordsString(formattedRecords); } else { setRecordsString(records.toString()); @@ -68,12 +73,14 @@ export const TransactionInfo = () => { // Format verifying keys const vKeys = parsed.verifyingKeys(); if (Array.isArray(vKeys)) { - setVerifyingKeys(vKeys.map(key => ({ - program: key.program, - function: key.function, - verifyingKey: key.verifyingKey, - certificate: key.certificate - }))); + setVerifyingKeys( + vKeys.map((key) => ({ + program: key.program, + function: key.function, + verifyingKey: key.verifyingKey, + certificate: key.certificate, + })), + ); } else { setVerifyingKeys([]); } @@ -92,23 +99,23 @@ export const TransactionInfo = () => { transitionIndex: index, programId: transition.programId(), functionName: transition.functionName(), - inputs: transitionInputs.map(input => ({ + inputs: transitionInputs.map((input) => ({ type: input.type, - id: input.id?.toString() || '', - tag: input.tag?.toString() || '', - value: input.value?.toString() || '' + id: input.id?.toString() || "", + tag: input.tag?.toString() || "", + value: input.value?.toString() || "", })), - outputs: transitionOutputs.map(output => ({ + outputs: transitionOutputs.map((output) => ({ type: output.type, - id: output.id?.toString() || '', - value: output.value?.toString() || '', - checksum: output.checksum?.toString() || '', - program: output.program || '', - function: output.function || '', - arguments: output.arguments?.map(arg => - arg.toString() - ) || [] - })) + id: output.id?.toString() || "", + value: output.value?.toString() || "", + checksum: output.checksum?.toString() || "", + program: output.program || "", + function: output.function || "", + arguments: + output.arguments?.map((arg) => arg.toString()) || + [], + })), }; }); setTransitionOutputs(outputs); @@ -132,7 +139,9 @@ export const TransactionInfo = () => { try { if (id) { axios - .get(`https://api.provable.com/v2/testnet/transaction/${id}`) + .get( + `https://api.provable.com/v2/testnet/transaction/${id}`, + ) .then((response) => { tryParseTransaction(response.data, true); }) @@ -149,322 +158,399 @@ export const TransactionInfo = () => { }; // Update layout to give more space to labels - const layout = { - labelCol: { span: 4 }, + const layout = { + labelCol: { span: 4 }, wrapperCol: { span: 21 }, - style: { marginBottom: '24px' } + style: { marginBottom: "24px" }, }; const transactionString = useMemo(() => { - return transaction !== null ? transaction.toString() : "" + return transaction !== null ? transaction.toString() : ""; }, [transaction]); return ( <div className="container"> - <h1>Transactions</h1> - <h2>Description</h2> - <ul> - <li> - {" "} - Transactions are an Aleo protocol object that carry - information about either a program deployment or function - execution and the fee paid to the network for these actions. - </li> - <li> - When the transaction communicates a function execution it - contains the execution proof, individual chain state transitions - (+ their inputs and outputs) and fee information. - </li> - <li> - When the transaction communicates a program deployment it contains - the program, verifying keys & certificates for each function and - the fee information. - </li> - </ul> - - <br /> - <p> - The Provable SDK provides methods for accessing the information within - transactions and transitions allowing app developers to develop front or - backend logic based on actions taken on the network. - </p> + <h1>Transactions</h1> + <h2>Description</h2> + <ul> + <li> + {" "} + Transactions are an Aleo protocol object that carry + information about either a program deployment or function + execution and the fee paid to the network for these actions. + </li> + <li> + When the transaction communicates a function execution it + contains the execution proof, individual chain state + transitions (+ their inputs and outputs) and fee + information. + </li> + <li> + When the transaction communicates a program deployment it + contains the program, verifying keys & certificates for each + function and the fee information. + </li> + </ul> - <br /> - <Card - title="Transaction" - style={{ width: "100%" }} - > - <Form {...layout}> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Example</span>} - colon={false} - > - <Radio.Group - value={selectedDefault} - onChange={(e) => setSelectedDefault(e.target.value)} - style={{ marginBottom: '16px' }} - > - <Radio.Button value="deploy">Deploy Transaction</Radio.Button> - <Radio.Button value="execute">Execute Transaction</Radio.Button> - </Radio.Group> - </Form.Item> + <br /> + <p> + The Provable SDK provides methods for accessing the information + within transactions and transitions allowing app developers to + develop front or backend logic based on actions taken on the + network. + </p> - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Transaction ID</span>} - colon={false} - validateStatus={status} - > - <Input.Search - name="id" - size="large" - placeholder="Enter a testnet transaction ID to override" - allowClear - onSearch={tryRequest} - /> - </Form.Item> - </Form> - {transaction !== null && ( + <br /> + <Card title="Transaction" style={{ width: "100%" }}> <Form {...layout}> - <Divider /> - {transactionType && ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Type</span>} - colon={false} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Example + </span> + } + colon={false} + > + <Radio.Group + value={selectedDefault} + onChange={(e) => setSelectedDefault(e.target.value)} + style={{ marginBottom: "16px" }} > - <Input - size="large" - value={transactionType} - disabled - addonAfter={<CopyButton data={transactionType} />} - /> - </Form.Item> - )} - - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Transaction</span>} + <Radio.Button value="deploy"> + Deploy Transaction + </Radio.Button> + <Radio.Button value="execute"> + Execute Transaction + </Radio.Button> + </Radio.Group> + </Form.Item> + + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Transaction ID + </span> + } colon={false} + validateStatus={status} > - <Input.TextArea + <Input.Search + name="id" size="large" - rows={6} - placeholder="Transaction" - value={transactionString} - disabled - addonAfter={<CopyButton data={transactionString} />} + placeholder="Enter a testnet transaction ID to override" + allowClear + onSearch={tryRequest} /> </Form.Item> - - {parsedTransaction && ( - <> - {recordsString && ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Records</span>} - colon={false} - > - <Input.TextArea - size="large" - rows={4} - value={recordsString} - disabled - addonAfter={<CopyButton data={recordsString} />} - /> - </Form.Item> - )} - - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Fees</span>} + </Form> + {transaction !== null && ( + <Form {...layout}> + <Divider /> + {transactionType && ( + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Type + </span> + } colon={false} - style={{ marginBottom: '24px' }} > - <Row gutter={[16, 16]} align="middle"> - <Col span={8}> - <Card - size="small" - style={{ textAlign: 'center' }} - > - <Typography.Text type="secondary"> - Base Fee - </Typography.Text> - <div style={{ margin: '8px 0' }}> - {baseFee || '0'} - </div> - <CopyButton data={baseFee} /> - </Card> - </Col> - <Col span={8}> - <Card - size="small" - style={{ textAlign: 'center' }} - > - <Typography.Text type="secondary"> - Priority Fee - </Typography.Text> - <div style={{ margin: '8px 0' }}> - {priorityFee || '0'} - </div> - <CopyButton data={priorityFee} /> - </Card> - </Col> - <Col span={8}> - <Card - size="small" - style={{ textAlign: 'center' }} - > - <Typography.Text type="secondary"> - Total Fee - </Typography.Text> - <div style={{ margin: '8px 0' }}> - {totalFee || '0'} - </div> - <CopyButton data={totalFee} /> - </Card> - </Col> - </Row> + <Input + size="large" + value={transactionType} + disabled + addonAfter={ + <CopyButton data={transactionType} /> + } + /> </Form.Item> + )} + + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Transaction + </span> + } + colon={false} + > + <Input.TextArea + size="large" + rows={6} + placeholder="Transaction" + value={transactionString} + disabled + addonAfter={ + <CopyButton data={transactionString} /> + } + /> + </Form.Item> + + {parsedTransaction && ( + <> + {recordsString && ( + <Form.Item + label={ + <span + style={{ whiteSpace: "nowrap" }} + > + Records + </span> + } + colon={false} + > + <Input.TextArea + size="large" + rows={4} + value={recordsString} + disabled + addonAfter={ + <CopyButton + data={recordsString} + /> + } + /> + </Form.Item> + )} - {deployedProgram && ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Program</span>} + <Form.Item + label={ + <span style={{ whiteSpace: "nowrap" }}> + Fees + </span> + } colon={false} + style={{ marginBottom: "24px" }} > - <CodeEditor - value={deployedProgram} - language="leo" - readOnly={true} - height="300px" - style={{ width: '100%' }} - /> + <Row gutter={[16, 16]} align="middle"> + <Col span={8}> + <Card + size="small" + style={{ textAlign: "center" }} + > + <Typography.Text type="secondary"> + Base Fee + </Typography.Text> + <div + style={{ margin: "8px 0" }} + > + {baseFee || "0"} + </div> + <CopyButton data={baseFee} /> + </Card> + </Col> + <Col span={8}> + <Card + size="small" + style={{ textAlign: "center" }} + > + <Typography.Text type="secondary"> + Priority Fee + </Typography.Text> + <div + style={{ margin: "8px 0" }} + > + {priorityFee || "0"} + </div> + <CopyButton + data={priorityFee} + /> + </Card> + </Col> + <Col span={8}> + <Card + size="small" + style={{ textAlign: "center" }} + > + <Typography.Text type="secondary"> + Total Fee + </Typography.Text> + <div + style={{ margin: "8px 0" }} + > + {totalFee || "0"} + </div> + <CopyButton data={totalFee} /> + </Card> + </Col> + </Row> </Form.Item> - )} - {verifyingKeys.length > 0 && ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Verifying Keys</span>} - colon={false} - > - {verifyingKeys.map((key, index) => ( - <Card - key={index} - size="small" - style={{ marginBottom: '12px' }} - title={ - <Row justify="space-between" align="middle"> - <Col> - <Typography.Text strong> - Verifying Key {index + 1} + {deployedProgram && ( + <Form.Item + label={ + <span + style={{ whiteSpace: "nowrap" }} + > + Program + </span> + } + colon={false} + > + <CodeEditor + value={deployedProgram} + language="leo" + readOnly={true} + height="300px" + style={{ width: "100%" }} + /> + </Form.Item> + )} + + {verifyingKeys.length > 0 && ( + <Form.Item + label={ + <span + style={{ whiteSpace: "nowrap" }} + > + Verifying Keys + </span> + } + colon={false} + > + {verifyingKeys.map((key, index) => ( + <Card + key={index} + size="small" + style={{ marginBottom: "12px" }} + title={ + <Row + justify="space-between" + align="middle" + > + <Col> + <Typography.Text + strong + > + Verifying Key{" "} + {index + 1} + </Typography.Text> + </Col> + <Col> + <CopyButton + data={JSON.stringify( + key, + null, + 2, + )} + style={{ + marginLeft: + "8px", + }} + /> + </Col> + </Row> + } + > + <Row gutter={[16, 16]}> + <Col span={12}> + <Typography.Text type="secondary"> + Program: </Typography.Text> + <Input + value={key.program} + disabled + style={{ + marginTop: + "4px", + }} + /> </Col> - <Col> - <CopyButton - data={JSON.stringify(key, null, 2)} - style={{ marginLeft: '8px' }} + <Col span={12}> + <Typography.Text type="secondary"> + Function: + </Typography.Text> + <Input + value={key.function} + disabled + style={{ + marginTop: + "4px", + }} /> </Col> - </Row> - } - > - <Row gutter={[16, 16]}> - <Col span={12}> - <Typography.Text type="secondary">Program:</Typography.Text> - <Input - value={key.program} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - <Col span={12}> - <Typography.Text type="secondary">Function:</Typography.Text> - <Input - value={key.function} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - <Col span={24}> - <Typography.Text type="secondary">Verifying Key:</Typography.Text> - <Input.TextArea - value={key.verifyingKey} - disabled - rows={2} - style={{ marginTop: '4px' }} - /> - </Col> - <Col span={24}> - <Typography.Text type="secondary">Certificate:</Typography.Text> - <Input.TextArea - value={key.certificate} - disabled - rows={2} - style={{ marginTop: '4px' }} - /> - </Col> - </Row> - </Card> - ))} - </Form.Item> - )} - - {transitionOutputs.length > 0 && ( - <Form.Item - label={<span style={{ whiteSpace: 'nowrap' }}>Transitions</span>} - colon={false} - > - {transitionOutputs.map((transition, transitionIndex) => ( - <Card - key={transitionIndex} - size="small" - style={{ marginBottom: '12px' }} - title={ - <Row justify="space-between" align="middle"> - <Col> - <Typography.Text strong> - Transition {transition.transitionIndex + 1} + <Col span={24}> + <Typography.Text type="secondary"> + Verifying Key: </Typography.Text> + <Input.TextArea + value={ + key.verifyingKey + } + disabled + rows={2} + style={{ + marginTop: + "4px", + }} + /> </Col> - <Col> - <CopyButton - data={JSON.stringify(transition, null, 2)} - style={{ marginLeft: '8px' }} + <Col span={24}> + <Typography.Text type="secondary"> + Certificate: + </Typography.Text> + <Input.TextArea + value={ + key.certificate + } + disabled + rows={2} + style={{ + marginTop: + "4px", + }} /> </Col> </Row> - } - > - <Row gutter={[16, 16]}> - <Col span={12}> - <Typography.Text type="secondary">Program ID:</Typography.Text> - <Input - value={transition.programId} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - <Col span={12}> - <Typography.Text type="secondary">Function Name:</Typography.Text> - <Input - value={transition.functionName} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - </Row> + </Card> + ))} + </Form.Item> + )} - {transition.inputs.map((input, inputIndex) => ( + {transitionOutputs.length > 0 && ( + <Form.Item + label={ + <span + style={{ whiteSpace: "nowrap" }} + > + Transitions + </span> + } + colon={false} + > + {transitionOutputs.map( + (transition, transitionIndex) => ( <Card - key={inputIndex} + key={transitionIndex} size="small" - style={{ marginTop: '16px', marginBottom: '8px' }} + style={{ + marginBottom: "12px", + }} title={ - <Row justify="space-between" align="middle"> + <Row + justify="space-between" + align="middle" + > <Col> - <Typography.Text> - Input {inputIndex + 1} + <Typography.Text + strong + > + Transition{" "} + {transition.transitionIndex + + 1} </Typography.Text> </Col> <Col> - <CopyButton - data={JSON.stringify(input, null, 2)} - style={{ marginLeft: '8px' }} + <CopyButton + data={JSON.stringify( + transition, + null, + 2, + )} + style={{ + marginLeft: + "8px", + }} /> </Col> </Row> @@ -472,166 +558,421 @@ export const TransactionInfo = () => { > <Row gutter={[16, 16]}> <Col span={12}> - <Typography.Text type="secondary">Type:</Typography.Text> + <Typography.Text type="secondary"> + Program ID: + </Typography.Text> <Input - value={input.type} + value={ + transition.programId + } disabled - style={{ marginTop: '4px' }} + style={{ + marginTop: + "4px", + }} /> </Col> <Col span={12}> - <Typography.Text type="secondary">ID:</Typography.Text> + <Typography.Text type="secondary"> + Function Name: + </Typography.Text> <Input - value={input.id} + value={ + transition.functionName + } disabled - style={{ marginTop: '4px' }} + style={{ + marginTop: + "4px", + }} /> </Col> - {input.tag && ( - <Col span={24}> - <Typography.Text type="secondary">Tag:</Typography.Text> - <Input - value={input.tag} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - )} - {input.value && ( - <Col span={24}> - <Typography.Text type="secondary">Value:</Typography.Text> - <Input.TextArea - value={input.value} - disabled - rows={2} - style={{ marginTop: '4px' }} - /> - </Col> - )} </Row> - </Card> - ))} - {transition.outputs.map((output, outputIndex) => ( - <Card - key={outputIndex} - size="small" - style={{ marginTop: '16px', marginBottom: '8px' }} - title={ - <Row justify="space-between" align="middle"> - <Col> - <Typography.Text> - Output {outputIndex + 1} - </Typography.Text> - </Col> - <Col> - <CopyButton - data={JSON.stringify(output, null, 2)} - style={{ marginLeft: '8px' }} - /> - </Col> - </Row> - } - > - <Row gutter={[16, 16]}> - <Col span={12}> - <Typography.Text type="secondary">Type:</Typography.Text> - <Input - value={output.type} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - <Col span={12}> - <Typography.Text type="secondary">ID:</Typography.Text> - <Input - value={output.id} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - {output.value && ( - <Col span={24}> - <Typography.Text type="secondary">Value:</Typography.Text> - <Input.TextArea - value={output.value} - disabled - rows={2} - style={{ marginTop: '4px' }} - /> - </Col> - )} - {output.checksum && ( - <Col span={24}> - <Typography.Text type="secondary">Checksum:</Typography.Text> - <Input - value={output.checksum} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - )} - {output.program && ( - <Col span={12}> - <Typography.Text type="secondary">Program:</Typography.Text> - <Input - value={output.program} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - )} - {output.function && ( - <Col span={12}> - <Typography.Text type="secondary">Function:</Typography.Text> - <Input - value={output.function} - disabled - style={{ marginTop: '4px' }} - /> - </Col> - )} - {output.arguments && output.arguments.length > 0 && ( - <Col span={24}> - <Typography.Text type="secondary"> - Arguments: - </Typography.Text> - <div style={{ marginTop: '4px' }}> - {output.arguments.map((arg, argIndex) => ( - <div - key={argIndex} - style={{ - marginBottom: argIndex < output.arguments.length - 1 ? '8px' : 0 + {transition.inputs.map( + (input, inputIndex) => ( + <Card + key={inputIndex} + size="small" + style={{ + marginTop: + "16px", + marginBottom: + "8px", + }} + title={ + <Row + justify="space-between" + align="middle" + > + <Col> + <Typography.Text> + Input{" "} + {inputIndex + + 1} + </Typography.Text> + </Col> + <Col> + <CopyButton + data={JSON.stringify( + input, + null, + 2, + )} + style={{ + marginLeft: + "8px", + }} + /> + </Col> + </Row> + } + > + <Row + gutter={[ + 16, 16, + ]} + > + <Col + span={ + 12 + } + > + <Typography.Text type="secondary"> + Type: + </Typography.Text> + <Input + value={ + input.type + } + disabled + style={{ + marginTop: + "4px", + }} + /> + </Col> + <Col + span={ + 12 + } + > + <Typography.Text type="secondary"> + ID: + </Typography.Text> + <Input + value={ + input.id + } + disabled + style={{ + marginTop: + "4px", + }} + /> + </Col> + {input.tag && ( + <Col + span={ + 24 + } + > + <Typography.Text type="secondary"> + Tag: + </Typography.Text> + <Input + value={ + input.tag + } + disabled + style={{ + marginTop: + "4px", + }} + /> + </Col> + )} + {input.value && ( + <Col + span={ + 24 + } + > + <Typography.Text type="secondary"> + Value: + </Typography.Text> + <Input.TextArea + value={ + input.value + } + disabled + rows={ + 2 + } + style={{ + marginTop: + "4px", + }} + /> + </Col> + )} + </Row> + </Card> + ), + )} + + {transition.outputs.map( + ( + output, + outputIndex, + ) => ( + <Card + key={ + outputIndex + } + size="small" + style={{ + marginTop: + "16px", + marginBottom: + "8px", + }} + title={ + <Row + justify="space-between" + align="middle" + > + <Col> + <Typography.Text> + Output{" "} + {outputIndex + + 1} + </Typography.Text> + </Col> + <Col> + <CopyButton + data={JSON.stringify( + output, + null, + 2, + )} + style={{ + marginLeft: + "8px", + }} + /> + </Col> + </Row> + } + > + <Row + gutter={[ + 16, 16, + ]} + > + <Col + span={ + 12 + } + > + <Typography.Text type="secondary"> + Type: + </Typography.Text> + <Input + value={ + output.type + } + disabled + style={{ + marginTop: + "4px", }} + /> + </Col> + <Col + span={ + 12 + } + > + <Typography.Text type="secondary"> + ID: + </Typography.Text> + <Input + value={ + output.id + } + disabled + style={{ + marginTop: + "4px", + }} + /> + </Col> + {output.value && ( + <Col + span={ + 24 + } + > + <Typography.Text type="secondary"> + Value: + </Typography.Text> + <Input.TextArea + value={ + output.value + } + disabled + rows={ + 2 + } + style={{ + marginTop: + "4px", + }} + /> + </Col> + )} + {output.checksum && ( + <Col + span={ + 24 + } > + <Typography.Text type="secondary"> + Checksum: + </Typography.Text> <Input - addonBefore={`Arg ${argIndex + 1}`} - value={arg} + value={ + output.checksum + } disabled - addonAfter={ - <CopyButton - data={arg} - style={{ border: 'none', background: 'none' }} - /> + style={{ + marginTop: + "4px", + }} + /> + </Col> + )} + {output.program && ( + <Col + span={ + 12 + } + > + <Typography.Text type="secondary"> + Program: + </Typography.Text> + <Input + value={ + output.program } + disabled + style={{ + marginTop: + "4px", + }} /> - </div> - ))} - </div> - </Col> - )} - </Row> + </Col> + )} + {output.function && ( + <Col + span={ + 12 + } + > + <Typography.Text type="secondary"> + Function: + </Typography.Text> + <Input + value={ + output.function + } + disabled + style={{ + marginTop: + "4px", + }} + /> + </Col> + )} + {output.arguments && + output + .arguments + .length > + 0 && ( + <Col + span={ + 24 + } + > + <Typography.Text type="secondary"> + Arguments: + </Typography.Text> + <div + style={{ + marginTop: + "4px", + }} + > + {output.arguments.map( + ( + arg, + argIndex, + ) => ( + <div + key={ + argIndex + } + style={{ + marginBottom: + argIndex < + output + .arguments + .length - + 1 + ? "8px" + : 0, + }} + > + <Input + addonBefore={`Arg ${argIndex + 1}`} + value={ + arg + } + disabled + addonAfter={ + <CopyButton + data={ + arg + } + style={{ + border: "none", + background: + "none", + }} + /> + } + /> + </div> + ), + )} + </div> + </Col> + )} + </Row> + </Card> + ), + )} </Card> - ))} - </Card> - ))} - </Form.Item> - )} - </> - )} - </Form> - )} - </Card> + ), + )} + </Form.Item> + )} + </> + )} + </Form> + )} + </Card> </div> ); }; diff --git a/website/src/tabs/protocol/transactions.js b/website/src/tabs/protocol/transactions.js index c3dd1ea10..550850c58 100644 --- a/website/src/tabs/protocol/transactions.js +++ b/website/src/tabs/protocol/transactions.js @@ -1,2 +1,4 @@ -export const executeTransaction = '{"type":"execute","id":"at1659war3z5t4wppr9h5rck3kpf5gmzf80xpud2hz8yuv3ds286u8s5lxh7c","execution":{"transitions":[{"id":"au17tn7sj8ywgg02melfh8mtsdrvr63z5k9gexagc478rejyp3w7q9qxfx7z6","program":"puzzle_arcade_coin_v001.aleo","function":"mint","inputs":[{"type":"public","id":"8270810521484911625112554982337338864935357860651826051684964871745393294042field","value":"aleo15sq744cm7dktgs73nn76e9dzj0pecyepe6qpjmeh05kylva4jg8qf9n9ge"},{"type":"public","id":"4575027690961186223652944521623745902600814120833621438807414620127639448668field","value":"1000000u64"}],"outputs":[{"type":"record","id":"6601842756832595700550356294123653730742948877912987042229716808083817596459field","checksum":"5062430203063356807847438506000770884656113117950337142360103911384470732056field","value":"record1qyqsq098e30xzmk3r99pzpmrcmrfh5tncewwzdgyx93yxsnq69fxz9sgqyrxzmt0w4h8ggcqqgqsq8ntqf37luu2nhaeas7qlq7gthug5kcmk2fcf3pm8uf875lz6ccfdywwf8g55p2lssye8ehwxtl8d76gynqaq7ml5s4dguxg3rs5wsyq52hq72"}],"tpk":"1607964622748471919440330478795163782437732099532604541664497619753994045319group","tcm":"847503920995797121862828111592398987072218963085962261207266349613895092180field","scm":"5713435921917019040572885748455116720616001991905041025594018041827076402171field"}],"global_state_root":"sr1uwx36xp95j7p2w7yadnj5ups6n8ktf0uwnvq0yauk2fefa2lsqysj4ydym","proof":"proof1qyqsqqqqqqqqqqqpqqqqqqqqqqqqwl66tmy9nzuz3ykg9xcpngmgfkxs6kf0zd2njt3vzdncge3uc5gqhz5aq92dwnr8ry9lu7fc8l5qqym0md7xkvk5qvltg2cmgc8lesfavxjyynp8m5sdwp9apq75vluvxxhqgnltywh43zp2r2anq5l5jqxenxqzp68crzjkfkllqy3pseguf7e4rtva4dccaacfgphq0x5ukk4lgzpdrhffh56sjjhrn7tfdkq42jwjrqv7pkectpnzc60u4ar0v2205m5hmcl24gcmr6q0we8fjupvs0xag4jt3wq2c26vpd7capspq3jvdylspluz6eufpx5q6gmnddx6uqm5g2f775r6p2uqe5t74s9tyqap7926nemq9s6qllm5waeszwush664wttpkc3r2xmflfy5r3d357nz0490nvmfn2pqacllmqnju9fd0m97jlld5smyvkyfwhtnsz62q7gtlx3ak257pep9jnyk5lugl5k7wt4mx6utxlp3l93vttfrjmc44sju32pfqnsthcnwp264dq8f0es2pz5yul64666kcue24ufeh7nxeyvjpulvta89jz6dkfzmrgnfgfgw65dft6seunhfhskr2gqyxlgc2jamcdsx4wnxggyn2vqlsezxac3rx56h9vw8mjrrpult5mmzacc26lv7zdpe3xg2fg22spgqsj5j66lzf3rkfhkz7ql0qfqhmw3dausfu0y677hpxthawy57ry89hag8yhdt8f8my7y0tlmyejvprgq69ana5eljw94lkksdud6scr0tr940urqftqywy2cym6yc6hff3aqlejz5ghtfdawetdcc84djqekg9400fz6h3l5l746a794dvtp8e6j8zl5z3xdggtrsgjwtg09sggz50h29qhwl56vdxv94j8gjewe8w5ctx2jmw5llnuttet4q0qqpqen24g39k7ln2d6w03zjk9zx566hn0mt3khskp7qk3g5lq5at5gnycuvgll02ak2fy0strcaurpwwplenjst3a25vleqlzdxxcyuyyxe5vl0lckd95dy6rss2sk0f7fw8hzvyefwg583veztqa2d9sg6p6sp8ra44f9rsc39z9zpztn0sqe5hkwtn2e0qdye2jzhchnwlm6svr2phqg3whpt3ej66nfzlj436f3suramv2sykw44gjh7q892qtcvqvqqqqqqqqqqpfcge0yyu6rvmktfslcvcmfm3mk4y3ngw03zxuam6qvclc09w87ufy6lym9wsu09734kx0d5f5tfqqqqucqz5ax7x8ddysek2kmu8cn3w6dqu8xf9tp2g9sew2vst530d705wlvv32pdqtnc8zjvtfmf725qqychc8kasqeft3575qq3v977syaak5j7qwkqux36umjt9xpgceuqalkhfgnnxd3sm4tflld9ef5hjcy9y0u7xy8xfm4hnctzvf23yedmu93ew9pcxrak8ssx40pc45zxqqqq94maf4"},"fee":{"transition":{"id":"au17ruxv9lgyc9r8ry72feq2dvukpyt0qmhp648qtkvxplnex7mmvzqvs7jf0","program":"credits.aleo","function":"fee_public","inputs":[{"type":"public","id":"2857075909811966273248114664812182277685354141121132361741553916429123320689field","value":"1449u64"},{"type":"public","id":"4095481960635608001176558539568862060430570697615096310772303148074416971125field","value":"0u64"},{"type":"public","id":"4331277641684651355445846833955413094644827521582315388405139541197216789244field","value":"1471442956713731556455575787580046168427032545671114373095796924821174474332field"}],"outputs":[{"type":"future","id":"1284488568170329868180693924120309631178056866257455108841198811483973130242field","value":"{\\n program_id: credits.aleo,\\n function_name: fee_public,\\n arguments: [\\n aleo193cgzzpr5lcwq6rmzq4l2ctg5f4mznead080mclfgrc0e5k0w5pstfdfps,\\n 1449u64\\n ]\\n}"}],"tpk":"1459551963926237749695253042528222267316827324542606050781185281459449486502group","tcm":"1960482517436921007100929744436748815928399310579329137811605688908348968736field","scm":"1613191919762957281003855895868519342839714495622392528901736670716120128919field"},"global_state_root":"sr1uwx36xp95j7p2w7yadnj5ups6n8ktf0uwnvq0yauk2fefa2lsqysj4ydym","proof":"proof1qyqsqqqqqqqqqqqpqqqqqqqqqqqws834tlcqydg43qy2qzfww25ppxvdzcuqmem54k6yhrd8lc492l6z50dl6a6s66pp7kscn8cazkvqq9yr9jymtyk9ucejv04myqhmczwy6feg4hyelzhqyehzpajvjmuvq7cn8v85shmy3mehmze4e9s6jqqhaxawal2cwt9talqkkkvp9w3dg80m8ddsq2w36z76jqas58j8ve6s4ghw3jxgxw77yjg3agft4cq8npaxa5pkkaurn9f4zdeswxhzxcr52qmusdv0p5jyht40e7ru3ljetuq2tdcv4dsp7gja0hm05k5pze5f75qhw26p3t0vmpcw8h6637qcxsfenenzjryunet5aprx8rtckdjapvt68amlwfskmtu9rzfqppfr0l08v2wqv5g23tz4rw4l64z85daym6mccws9qy8yf9tav24p3x02sn3dlcj83rgft0hnfs3fqr3kq7y4lecdfeqqfawq3xph3w3t3c8ghzskgrd5x98wj0dw4m55quj04s3a88tq5q2zykfws90xeq0xw3k0evz22qqaa706e9umam999fuqeks6zwstug5qqslh6j6hhmxzjypjxlv2wh5k60s9mxujnvqgx9hja9quft93x8qn93ju8008elj5rgdnrhefyrw5ytc2u3v4unrm2qphnn86vyq4wjjgvvq0ggcp0hss9msjs6fd9r2hq2gs0yy04fzse37cm8d470vv0gyhl5guvcr4z5lhjsm709twrmm3r7gw3zj88hyl4eqxzc8rllpcp2dymr92xz4y8yzjzwzdfu8cexmr4nvfs7ktupt2el92ht5w0a0als4da54ypexuvxz6egcvd0jafx9059khmml47lnq9qmjas9ya3m55eqndcgsmwa38kku806t97dx4hffeexg26eukn3ly7w90tff8za7kenpg7gr4ht2txppsa0ee9ceca3y6c9ze64gk56ry4gct70rkq57dyt9zgxa2urlz9tl4mzftaav0dsszqyn6xkymvqjvpzz6e6u8yt8tr89xrhnqlc0skvvyt34dvr9g2laav7d09szn0u9mzwf5f3mgul5zk5npdkj8rr35png2ju0lsz2jxuc07w9yawwnvr0hr27l8kmwzee6rqqdmuc5wam8xcmlw4gr8qjd753932vh4dcemh97v2lr63kjm48gmsqqvqqqqqqqqqqqy55a0dnshhzavcyuf5qpjd9v3wg32ygk6cd5vttk3pcg0capxcg725h3hmp6wl3nl8fma45aeu0qqqvldz4rwfpjvfp9g7wyzuwa6kvnsh5qptzht87q2ypmrzu0wa5wvxwasqvag00laxjnfwmnehykrcpqysuspnsh2t5qencjpqd0kyfvrvmw2z6unam0cvrrr0kzxcyvlypr8lqlt8des67rec0v4w2sur5gajd0n003qupz8sgqla6juv8rs0jlmsrhprypn2dwydp37wty8ycsqqq6rp8sd"}}'; -export const deployTransaction = '{"type":"deploy","id":"at1v2n8krgmlmax4n0695s997uul6nd2f5l2shvmemukq067kacrg8qzzf5kt","owner":{"address":"aleo1mgjcqhnar38zvschjwxr830pwk57ywnce8rp7smf8c3yp2xexvgq40d4fx","signature":"sign1kxwcg0kdxeduag2wwaxzqv5s3kavm0sry88h4wlxaa9g52f0ggqj0gk7uwxkjpjyawkknfp97sjfa8kgzdjwh6ns3ju7t5w8xzc9wqa6xq3s6sjztquzwulp903h8ez9wgystuw4ex0fp7dhjahh6l7vzqp5t5sent5yawr9056h0su4gperdp9nljk0vhsq43jsu7rh2a3qc4vwp3r"},"deployment":{"edition":0,"program":"import credits.aleo;\\n\\nprogram token_registry.aleo;\\n\\nrecord Token:\\n owner as address.private;\\n amount as u128.private;\\n token_id as field.private;\\n external_authorization_required as boolean.private;\\n authorized_until as u32.private;\\n\\nstruct TokenMetadata:\\n token_id as field;\\n name as u128;\\n symbol as u128;\\n decimals as u8;\\n supply as u128;\\n max_supply as u128;\\n admin as address;\\n external_authorization_required as boolean;\\n external_authorization_party as address;\\n\\nstruct TokenOwner:\\n account as address;\\n token_id as field;\\n\\nstruct Balance:\\n token_id as field;\\n account as address;\\n balance as u128;\\n authorized_until as u32;\\n\\nstruct Allowance:\\n account as address;\\n spender as address;\\n token_id as field;\\n\\nmapping registered_tokens:\\n key as field.public;\\n value as TokenMetadata.public;\\n\\nmapping balances:\\n key as field.public;\\n value as Balance.public;\\n\\nmapping authorized_balances:\\n key as field.public;\\n value as Balance.public;\\n\\nmapping allowances:\\n key as field.public;\\n value as u128.public;\\n\\nmapping roles:\\n key as field.public;\\n value as u8.public;\\n\\nfunction transfer_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.caller r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n cast r1 r0 into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async transfer_public r0 r1 r2 self.caller r4 r6 into r7;\\n output r7 as token_registry.aleo/transfer_public.future;\\n\\nfinalize transfer_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as address.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n get authorized_balances[r4] into r6;\\n get registered_tokens[r0] into r7;\\n lte block.height r6.authorized_until into r8;\\n not r7.external_authorization_required into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n sub r6.balance r2 into r11;\\n cast r0 r3 r11 r6.authorized_until into r12 as Balance;\\n set r12 into authorized_balances[r4];\\n get registered_tokens[r0] into r13;\\n ternary r13.external_authorization_required 0u32 4294967295u32 into r14;\\n cast r0 r1 0u128 r14 into r15 as Balance;\\n get.or_use balances[r5] r15 into r16;\\n get.or_use authorized_balances[r5] r15 into r17;\\n ternary r13.external_authorization_required r16.token_id r17.token_id into r18;\\n ternary r13.external_authorization_required r16.account r17.account into r19;\\n ternary r13.external_authorization_required r16.balance r17.balance into r20;\\n ternary r13.external_authorization_required r16.authorized_until r17.authorized_until into r21;\\n cast r18 r19 r20 r21 into r22 as Balance;\\n add r22.balance r2 into r23;\\n cast r0 r1 r23 r22.authorized_until into r24 as Balance;\\n branch.eq r13.external_authorization_required false to end_then_00;\\n set r24 into balances[r5];\\n branch.eq true true to end_otherwise_01;\\n position end_then_00;\\n set r24 into authorized_balances[r5];\\n position end_otherwise_01;\\n\\nfunction transfer_public_as_signer:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.signer r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n cast r1 r0 into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async transfer_public_as_signer r0 r1 r2 self.signer r4 r6 into r7;\\n output r7 as token_registry.aleo/transfer_public_as_signer.future;\\n\\nfinalize transfer_public_as_signer:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as address.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n get authorized_balances[r4] into r6;\\n get registered_tokens[r0] into r7;\\n lte block.height r6.authorized_until into r8;\\n not r7.external_authorization_required into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n sub r6.balance r2 into r11;\\n cast r0 r3 r11 r6.authorized_until into r12 as Balance;\\n set r12 into authorized_balances[r4];\\n get registered_tokens[r0] into r13;\\n ternary r13.external_authorization_required 0u32 4294967295u32 into r14;\\n cast r0 r1 0u128 r14 into r15 as Balance;\\n get.or_use balances[r5] r15 into r16;\\n get.or_use authorized_balances[r5] r15 into r17;\\n ternary r13.external_authorization_required r16.token_id r17.token_id into r18;\\n ternary r13.external_authorization_required r16.account r17.account into r19;\\n ternary r13.external_authorization_required r16.balance r17.balance into r20;\\n ternary r13.external_authorization_required r16.authorized_until r17.authorized_until into r21;\\n cast r18 r19 r20 r21 into r22 as Balance;\\n add r22.balance r2 into r23;\\n cast r0 r1 r23 r22.authorized_until into r24 as Balance;\\n branch.eq r13.external_authorization_required false to end_then_02;\\n set r24 into balances[r5];\\n branch.eq true true to end_otherwise_03;\\n position end_then_02;\\n set r24 into authorized_balances[r5];\\n position end_otherwise_03;\\n\\nfunction transfer_private:\\n input r0 as address.private;\\n input r1 as u128.private;\\n input r2 as Token.record;\\n sub r2.amount r1 into r3;\\n cast r2.owner r3 r2.token_id r2.external_authorization_required r2.authorized_until into r4 as Token.record;\\n ternary r2.external_authorization_required 0u32 4294967295u32 into r5;\\n cast r0 r1 r2.token_id r2.external_authorization_required r5 into r6 as Token.record;\\n async transfer_private r2.external_authorization_required r2.authorized_until into r7;\\n output r4 as Token.record;\\n output r6 as Token.record;\\n output r7 as token_registry.aleo/transfer_private.future;\\n\\nfinalize transfer_private:\\n input r0 as boolean.public;\\n input r1 as u32.public;\\n lte block.height r1 into r2;\\n not r0 into r3;\\n or r2 r3 into r4;\\n assert.eq r4 true ;\\n\\nfunction transfer_private_to_public:\\n input r0 as address.public;\\n input r1 as u128.public;\\n input r2 as Token.record;\\n sub r2.amount r1 into r3;\\n cast r2.owner r3 r2.token_id r2.external_authorization_required r2.authorized_until into r4 as Token.record;\\n cast r0 r2.token_id into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async transfer_private_to_public r2.token_id r0 r1 r2.authorized_until r2.external_authorization_required r6 into r7;\\n output r4 as Token.record;\\n output r7 as token_registry.aleo/transfer_private_to_public.future;\\n\\nfinalize transfer_private_to_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as u32.public;\\n input r4 as boolean.public;\\n input r5 as field.public;\\n lte block.height r3 into r6;\\n not r4 into r7;\\n or r6 r7 into r8;\\n assert.eq r8 true ;\\n get registered_tokens[r0] into r9;\\n ternary r9.external_authorization_required 0u32 4294967295u32 into r10;\\n cast r0 r1 0u128 r10 into r11 as Balance;\\n get.or_use balances[r5] r11 into r12;\\n get.or_use authorized_balances[r5] r11 into r13;\\n ternary r9.external_authorization_required r12.token_id r13.token_id into r14;\\n ternary r9.external_authorization_required r12.account r13.account into r15;\\n ternary r9.external_authorization_required r12.balance r13.balance into r16;\\n ternary r9.external_authorization_required r12.authorized_until r13.authorized_until into r17;\\n cast r14 r15 r16 r17 into r18 as Balance;\\n add r18.balance r2 into r19;\\n cast r0 r1 r19 r18.authorized_until into r20 as Balance;\\n branch.eq r9.external_authorization_required false to end_then_04;\\n set r20 into balances[r5];\\n branch.eq true true to end_otherwise_05;\\n position end_then_04;\\n set r20 into authorized_balances[r5];\\n position end_otherwise_05;\\n\\nfunction transfer_public_to_private:\\n input r0 as field.public;\\n input r1 as address.private;\\n input r2 as u128.public;\\n input r3 as boolean.public;\\n ternary r3 0u32 4294967295u32 into r4;\\n cast r1 r2 r0 r3 r4 into r5 as Token.record;\\n cast self.caller r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n async transfer_public_to_private r0 r2 self.caller r3 r7 into r8;\\n output r5 as Token.record;\\n output r8 as token_registry.aleo/transfer_public_to_private.future;\\n\\nfinalize transfer_public_to_private:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as address.public;\\n input r3 as boolean.public;\\n input r4 as field.public;\\n get registered_tokens[r0] into r5;\\n assert.eq r5.external_authorization_required r3 ;\\n get authorized_balances[r4] into r6;\\n get registered_tokens[r0] into r7;\\n lte block.height r6.authorized_until into r8;\\n not r7.external_authorization_required into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n sub r6.balance r1 into r11;\\n cast r0 r2 r11 r6.authorized_until into r12 as Balance;\\n set r12 into authorized_balances[r4];\\n\\nfunction join:\\n input r0 as Token.record;\\n input r1 as Token.record;\\n is.eq r0.token_id r1.token_id into r2;\\n assert.eq r2 true ;\\n add r0.amount r1.amount into r3;\\n lt r0.authorized_until r1.authorized_until into r4;\\n ternary r4 r0.authorized_until r1.authorized_until into r5;\\n cast r0.owner r3 r0.token_id r0.external_authorization_required r5 into r6 as Token.record;\\n output r6 as Token.record;\\n\\nfunction split:\\n input r0 as Token.record;\\n input r1 as u128.private;\\n gte r0.amount r1 into r2;\\n assert.eq r2 true ;\\n cast r0.owner r1 r0.token_id r0.external_authorization_required r0.authorized_until into r3 as Token.record;\\n sub r0.amount r1 into r4;\\n cast r0.owner r4 r0.token_id r0.external_authorization_required r0.authorized_until into r5 as Token.record;\\n output r3 as Token.record;\\n output r5 as Token.record;\\n\\nfunction initialize:\\n async initialize into r0;\\n output r0 as token_registry.aleo/initialize.future;\\n\\nfinalize initialize:\\n contains registered_tokens[3443843282313283355522573239085696902919850365217539366784739393210722344986field] into r0;\\n assert.eq r0 false ;\\n cast 3443843282313283355522573239085696902919850365217539366784739393210722344986field 1095517519u128 1095517519u128 6u8 0u128 10000000000000000u128 wrapped_credits.aleo false token_registry.aleo into r1 as TokenMetadata;\\n set r1 into registered_tokens[3443843282313283355522573239085696902919850365217539366784739393210722344986field];\\n\\nfunction register_token:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as u128.public;\\n input r3 as u8.public;\\n input r4 as u128.public;\\n input r5 as boolean.public;\\n input r6 as address.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r7;\\n assert.eq r7 true ;\\n cast r0 r1 r2 r3 0u128 r4 self.caller r5 r6 into r8 as TokenMetadata;\\n async register_token r8 into r9;\\n output r9 as token_registry.aleo/register_token.future;\\n\\nfinalize register_token:\\n input r0 as TokenMetadata.public;\\n contains registered_tokens[r0.token_id] into r1;\\n assert.eq r1 false ;\\n set r0 into registered_tokens[r0.token_id];\\n\\nfunction update_token_management:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r3;\\n assert.eq r3 true ;\\n async update_token_management r0 r1 r2 self.caller into r4;\\n output r4 as token_registry.aleo/update_token_management.future;\\n\\nfinalize update_token_management:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n input r3 as address.public;\\n get registered_tokens[r0] into r4;\\n assert.eq r3 r4.admin ;\\n cast r0 r4.name r4.symbol r4.decimals r4.supply r4.max_supply r1 r4.external_authorization_required r2 into r5 as TokenMetadata;\\n set r5 into registered_tokens[r0];\\n\\nfunction set_role:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u8.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r3;\\n assert.eq r3 true ;\\n is.eq r2 1u8 into r4;\\n is.eq r2 2u8 into r5;\\n or r4 r5 into r6;\\n is.eq r2 3u8 into r7;\\n or r6 r7 into r8;\\n assert.eq r8 true ;\\n cast r1 r0 into r9 as TokenOwner;\\n hash.bhp256 r9 into r10 as field;\\n async set_role r0 r2 self.caller r10 into r11;\\n output r11 as token_registry.aleo/set_role.future;\\n\\nfinalize set_role:\\n input r0 as field.public;\\n input r1 as u8.public;\\n input r2 as address.public;\\n input r3 as field.public;\\n get registered_tokens[r0] into r4;\\n assert.eq r2 r4.admin ;\\n set r1 into roles[r3];\\n\\nfunction remove_role:\\n input r0 as field.public;\\n input r1 as address.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r2;\\n assert.eq r2 true ;\\n cast r1 r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n async remove_role r0 self.caller r4 into r5;\\n output r5 as token_registry.aleo/remove_role.future;\\n\\nfinalize remove_role:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as field.public;\\n get registered_tokens[r0] into r3;\\n assert.eq r1 r3.admin ;\\n remove roles[r2];\\n\\nfunction mint_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as u32.public;\\n cast self.caller r0 into r4 as TokenOwner;\\n hash.bhp256 r4 into r5 as field;\\n cast r1 r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n async mint_public r0 r1 r2 r3 self.caller r5 r7 into r8;\\n output r8 as token_registry.aleo/mint_public.future;\\n\\nfinalize mint_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as u32.public;\\n input r4 as address.public;\\n input r5 as field.public;\\n input r6 as field.public;\\n get registered_tokens[r0] into r7;\\n is.eq r4 r7.admin into r8;\\n not r8 into r9;\\n branch.eq r9 false to end_then_06;\\n get roles[r5] into r10;\\n is.eq r10 1u8 into r11;\\n is.eq r10 3u8 into r12;\\n or r11 r12 into r13;\\n assert.eq r13 true ;\\n branch.eq true true to end_otherwise_07;\\n position end_then_06;\\n position end_otherwise_07;\\n add r7.supply r2 into r14;\\n lte r14 r7.max_supply into r15;\\n assert.eq r15 true ;\\n cast r0 r1 0u128 r3 into r16 as Balance;\\n get.or_use balances[r6] r16 into r17;\\n get.or_use authorized_balances[r6] r16 into r18;\\n ternary r7.external_authorization_required r17.token_id r18.token_id into r19;\\n ternary r7.external_authorization_required r17.account r18.account into r20;\\n ternary r7.external_authorization_required r17.balance r18.balance into r21;\\n ternary r7.external_authorization_required r17.authorized_until r18.authorized_until into r22;\\n cast r19 r20 r21 r22 into r23 as Balance;\\n add r23.balance r2 into r24;\\n cast r0 r1 r24 r23.authorized_until into r25 as Balance;\\n branch.eq r7.external_authorization_required false to end_then_08;\\n set r25 into balances[r6];\\n branch.eq true true to end_otherwise_09;\\n position end_then_08;\\n set r25 into authorized_balances[r6];\\n position end_otherwise_09;\\n cast r0 r7.name r7.symbol r7.decimals r14 r7.max_supply r7.admin r7.external_authorization_required r7.external_authorization_party into r26 as TokenMetadata;\\n set r26 into registered_tokens[r0];\\n\\nfunction mint_private:\\n input r0 as field.public;\\n input r1 as address.private;\\n input r2 as u128.public;\\n input r3 as boolean.public;\\n input r4 as u32.public;\\n cast r1 r2 r0 r3 r4 into r5 as Token.record;\\n cast self.caller r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n async mint_private r0 r2 r3 r4 self.caller r7 into r8;\\n output r5 as Token.record;\\n output r8 as token_registry.aleo/mint_private.future;\\n\\nfinalize mint_private:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as boolean.public;\\n input r3 as u32.public;\\n input r4 as address.public;\\n input r5 as field.public;\\n get registered_tokens[r0] into r6;\\n is.eq r4 r6.admin into r7;\\n not r7 into r8;\\n branch.eq r8 false to end_then_010;\\n get roles[r5] into r9;\\n is.eq r9 1u8 into r10;\\n is.eq r9 3u8 into r11;\\n or r10 r11 into r12;\\n assert.eq r12 true ;\\n branch.eq true true to end_otherwise_011;\\n position end_then_010;\\n position end_otherwise_011;\\n add r6.supply r1 into r13;\\n lte r13 r6.max_supply into r14;\\n assert.eq r14 true ;\\n assert.eq r6.external_authorization_required r2 ;\\n is.eq r3 0u32 into r15;\\n not r6.external_authorization_required into r16;\\n or r15 r16 into r17;\\n assert.eq r17 true ;\\n cast r0 r6.name r6.symbol r6.decimals r13 r6.max_supply r6.admin r6.external_authorization_required r6.external_authorization_party into r18 as TokenMetadata;\\n set r18 into registered_tokens[r0];\\n\\nfunction burn_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast r1 r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n cast self.caller r0 into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async burn_public r3 r2 self.caller r6 r4 into r7;\\n output r7 as token_registry.aleo/burn_public.future;\\n\\nfinalize burn_public:\\n input r0 as TokenOwner.public;\\n input r1 as u128.public;\\n input r2 as address.public;\\n input r3 as field.public;\\n input r4 as field.public;\\n get registered_tokens[r0.token_id] into r5;\\n is.neq r2 r5.admin into r6;\\n branch.eq r6 false to end_then_012;\\n get roles[r3] into r7;\\n is.eq r7 2u8 into r8;\\n is.eq r7 3u8 into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n branch.eq true true to end_otherwise_013;\\n position end_then_012;\\n position end_otherwise_013;\\n sub r5.supply r1 into r11;\\n cast r5.token_id r5.name r5.symbol r5.decimals r11 r5.max_supply r5.admin r5.external_authorization_required r5.external_authorization_party into r12 as TokenMetadata;\\n set r12 into registered_tokens[r0.token_id];\\n cast r0.token_id r0.account 0u128 0u32 into r13 as Balance;\\n get.or_use authorized_balances[r4] r13 into r14;\\n gte r14.balance 0u128 into r15;\\n branch.eq r15 false to end_then_014;\\n gt r14.balance r1 into r16;\\n branch.eq r16 false to end_then_116;\\n sub r14.balance r1 into r17;\\n cast r0.token_id r0.account r17 r14.authorized_until into r18 as Balance;\\n set r18 into authorized_balances[r4];\\n branch.eq true true to end_otherwise_117;\\n position end_then_116;\\n cast r0.token_id r0.account 0u128 r14.authorized_until into r19 as Balance;\\n set r19 into authorized_balances[r4];\\n sub r1 r14.balance into r20;\\n is.eq r20 0u128 into r21;\\n branch.eq r21 false to end_then_218;\\n branch.eq true true to end_otherwise_219;\\n position end_then_218;\\n get balances[r4] into r22;\\n sub r22.balance r20 into r23;\\n cast r0.token_id r0.account r23 r22.authorized_until into r24 as Balance;\\n set r24 into balances[r4];\\n position end_otherwise_219;\\n position end_otherwise_117;\\n branch.eq true true to end_otherwise_015;\\n position end_then_014;\\n get balances[r4] into r25;\\n sub r25.balance r1 into r26;\\n cast r0.token_id r0.account r26 r25.authorized_until into r27 as Balance;\\n set r27 into balances[r4];\\n position end_otherwise_015;\\n\\nfunction burn_private:\\n input r0 as Token.record;\\n input r1 as u128.public;\\n sub r0.amount r1 into r2;\\n cast r0.owner r2 r0.token_id r0.external_authorization_required r0.authorized_until into r3 as Token.record;\\n cast self.caller r0.token_id into r4 as TokenOwner;\\n hash.bhp256 r4 into r5 as field;\\n async burn_private r0.token_id r1 self.caller r5 into r6;\\n output r3 as Token.record;\\n output r6 as token_registry.aleo/burn_private.future;\\n\\nfinalize burn_private:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as address.public;\\n input r3 as field.public;\\n get registered_tokens[r0] into r4;\\n is.eq r2 r4.admin into r5;\\n not r5 into r6;\\n branch.eq r6 false to end_then_020;\\n get roles[r3] into r7;\\n is.eq r7 2u8 into r8;\\n is.eq r7 3u8 into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n branch.eq true true to end_otherwise_021;\\n position end_then_020;\\n position end_otherwise_021;\\n sub r4.supply r1 into r11;\\n cast r0 r4.name r4.symbol r4.decimals r11 r4.max_supply r4.admin r4.external_authorization_required r4.external_authorization_party into r12 as TokenMetadata;\\n set r12 into registered_tokens[r0];\\n\\nfunction prehook_public:\\n input r0 as TokenOwner.public;\\n input r1 as u128.public;\\n input r2 as u32.public;\\n hash.bhp256 r0 into r3 as field;\\n async prehook_public r0 r1 r2 self.caller r3 into r4;\\n output r4 as token_registry.aleo/prehook_public.future;\\n\\nfinalize prehook_public:\\n input r0 as TokenOwner.public;\\n input r1 as u128.public;\\n input r2 as u32.public;\\n input r3 as address.public;\\n input r4 as field.public;\\n get registered_tokens[r0.token_id] into r5;\\n assert.eq r5.external_authorization_required true ;\\n is.eq r3 r5.external_authorization_party into r6;\\n assert.eq r6 true ;\\n cast r0.token_id r0.account 0u128 0u32 into r7 as Balance;\\n get.or_use balances[r4] r7 into r8;\\n get.or_use authorized_balances[r4] r7 into r9;\\n lt r9.authorized_until block.height into r10;\\n add r8.balance r9.balance into r11;\\n ternary r10 r11 r8.balance into r12;\\n ternary r10 0u128 r9.balance into r13;\\n sub r12 r1 into r14;\\n add r13 r1 into r15;\\n cast r0.token_id r0.account r15 r2 into r16 as Balance;\\n set r16 into authorized_balances[r4];\\n cast r0.token_id r0.account r14 r8.authorized_until into r17 as Balance;\\n set r17 into balances[r4];\\n\\nfunction prehook_private:\\n input r0 as Token.record;\\n input r1 as u128.private;\\n input r2 as u32.private;\\n sub r0.amount r1 into r3;\\n cast r0.owner r3 r0.token_id r0.external_authorization_required r0.authorized_until into r4 as Token.record;\\n cast r0.owner r1 r0.token_id r0.external_authorization_required r2 into r5 as Token.record;\\n async prehook_private r0.token_id self.caller into r6;\\n output r4 as Token.record;\\n output r5 as Token.record;\\n output r6 as token_registry.aleo/prehook_private.future;\\n\\nfinalize prehook_private:\\n input r0 as field.public;\\n input r1 as address.public;\\n get registered_tokens[r0] into r2;\\n is.eq r1 r2.external_authorization_party into r3;\\n assert.eq r3 true ;\\n\\nfunction approve_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.caller r1 r0 into r3 as Allowance;\\n hash.bhp256 r3 into r4 as field;\\n async approve_public r2 r4 into r5;\\n output r5 as token_registry.aleo/approve_public.future;\\n\\nfinalize approve_public:\\n input r0 as u128.public;\\n input r1 as field.public;\\n get.or_use allowances[r1] 0u128 into r2;\\n add r2 r0 into r3;\\n set r3 into allowances[r1];\\n\\nfunction unapprove_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.caller r1 r0 into r3 as Allowance;\\n hash.bhp256 r3 into r4 as field;\\n async unapprove_public r2 r4 into r5;\\n output r5 as token_registry.aleo/unapprove_public.future;\\n\\nfinalize unapprove_public:\\n input r0 as u128.public;\\n input r1 as field.public;\\n get allowances[r1] into r2;\\n sub r2 r0 into r3;\\n set r3 into allowances[r1];\\n\\nfunction transfer_from_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n input r3 as u128.public;\\n cast r1 self.caller r0 into r4 as Allowance;\\n hash.bhp256 r4 into r5 as field;\\n cast r1 r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n cast r2 r0 into r8 as TokenOwner;\\n hash.bhp256 r8 into r9 as field;\\n async transfer_from_public r0 r1 r2 r3 r5 r7 r9 into r10;\\n output r10 as token_registry.aleo/transfer_from_public.future;\\n\\nfinalize transfer_from_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n input r3 as u128.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n input r6 as field.public;\\n get allowances[r4] into r7;\\n sub r7 r3 into r8;\\n set r8 into allowances[r4];\\n get authorized_balances[r5] into r9;\\n get registered_tokens[r0] into r10;\\n lte block.height r9.authorized_until into r11;\\n not r10.external_authorization_required into r12;\\n or r11 r12 into r13;\\n assert.eq r13 true ;\\n sub r9.balance r3 into r14;\\n cast r0 r1 r14 r9.authorized_until into r15 as Balance;\\n set r15 into authorized_balances[r5];\\n get registered_tokens[r0] into r16;\\n ternary r16.external_authorization_required 0u32 4294967295u32 into r17;\\n cast r0 r2 0u128 r17 into r18 as Balance;\\n get.or_use balances[r6] r18 into r19;\\n get.or_use authorized_balances[r6] r18 into r20;\\n ternary r16.external_authorization_required r19.token_id r20.token_id into r21;\\n ternary r16.external_authorization_required r19.account r20.account into r22;\\n ternary r16.external_authorization_required r19.balance r20.balance into r23;\\n ternary r16.external_authorization_required r19.authorized_until r20.authorized_until into r24;\\n cast r21 r22 r23 r24 into r25 as Balance;\\n add r25.balance r3 into r26;\\n cast r0 r2 r26 r25.authorized_until into r27 as Balance;\\n branch.eq r16.external_authorization_required false to end_then_022;\\n set r27 into balances[r6];\\n branch.eq true true to end_otherwise_023;\\n position end_then_022;\\n set r27 into authorized_balances[r6];\\n position end_otherwise_023;\\n\\nfunction transfer_from_public_to_private:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.private;\\n input r3 as u128.public;\\n input r4 as boolean.public;\\n ternary r4 0u32 4294967295u32 into r5;\\n cast r2 r3 r0 r4 r5 into r6 as Token.record;\\n cast r1 self.caller r0 into r7 as Allowance;\\n hash.bhp256 r7 into r8 as field;\\n cast r1 r0 into r9 as TokenOwner;\\n hash.bhp256 r9 into r10 as field;\\n async transfer_from_public_to_private r0 r1 r3 r4 r8 r10 into r11;\\n output r6 as Token.record;\\n output r11 as token_registry.aleo/transfer_from_public_to_private.future;\\n\\nfinalize transfer_from_public_to_private:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as boolean.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n get registered_tokens[r0] into r6;\\n assert.eq r6.external_authorization_required r3 ;\\n get allowances[r4] into r7;\\n sub r7 r2 into r8;\\n set r8 into allowances[r4];\\n get authorized_balances[r5] into r9;\\n get registered_tokens[r0] into r10;\\n lte block.height r9.authorized_until into r11;\\n not r10.external_authorization_required into r12;\\n or r11 r12 into r13;\\n assert.eq r13 true ;\\n sub r9.balance r2 into r14;\\n cast r0 r1 r14 r9.authorized_until into r15 as Balance;\\n set r15 into authorized_balances[r5];\\n","verifying_keys":[["transfer_public",["verifier1qygqqqqqqqqqqqr32gqqqqqqqqq825sqqqqqqqqqcmzqqqqqqqqqpkhqqqqqqqqqqrj8kqqqqqqqqqqvqqqqqqqqqqqpv0cxuwgvvr8zu0qvq5t6vj7vssv3aaj2r7sfehtvwe33nddup7jmaeu2pkv6zll656y579tv5wupknlzdum3pp7zjqd45wprdtswvcs3q06dngn4l4u253c3zwpn5y8rjt0yt5xk2m49c2f9we3zzwssq5676m26nnajgntrlejh5a5yme2l4rq870ltqyysh6ks3tjqxqgfy953rvxck3unay2yqxekgwt0qxmfrmnuc6c7850hfrhecl63z5d7r6c8f9fn5xzny0natgq0rtmgyhlysx78aq6ggdpp3ej9v0e2qqf8w4yf4smjrpdcnd5wjhmv94gzl0t3g88wsannkw0jk3drj24df8msdls0frj0ujygqsq9k55j7qqwla30x9rglr3lkyfrn26qu6q9x2ms0jp4anuacx8uyx35pam6f53tcru86h6vv58rhrxft69ugrvqx5pghc6l6sn9eypx69gerwtk6ljxe835kfrl7x9qqwwxnvxqg8lksm82ahfph86xc3z5n230d7scqjw5vqcn6s3zzy6ac865vm2sg2m9dyafrhtf0ujpsm0dcur953g2ux8rhvsthe5k2rnysf2d54l4qqc3lm2nfxy2zlwaqy447fnexle4jdq35096chtjy3lkwshmqx37mwn7dxl8ljm88xns6te4k9nxmqygaeyadav74q5xg8vrcq36dpqssnxu3h6lfh8hslxhnxkcwmjxt9mygu58mrj7p7ttzuuj5gh02sqr0m3ynltleefn2z77588akxm9gew5tcpmvqcnu464g0p2007d9cae262wdsyha0anlnr7vzx5p6gqwsavgmvkf0jh70qsnpyqasgzfjw3rax2z630zp7ez6qmrhxeemdjvwrekaa02uwj6xapz28reqjcr844ctmayahcs4r4h4af6eevv5pxj2jqete3gnxlmkvv3mu4q6fhhdkqqqqqqqqqqwmvgg5","certificate1qyqsqqqqqqqqqqqhucsq3ysfuyptwl5zkh5j65z5cu05pskmrm452vzwl505xndn4m6gz3hn3m5utfxyhsprrqkqlwqqqvn7tlv"]],["transfer_public_as_signer",["verifier1qygqqqqqqqqqqqr32gqqqqqqqqq825sqqqqqqqqqcmzqqqqqqqqqpkhqqqqqqqqqqrj8kqqqqqqqqqqvqqqqqqqqqqqt8c0jfjvcgv3lw2xfud6ms9q8d6jvcgw5p0mkx9qhfwe7rmc5tsgrgl5987zryrtr0sx4azedj6gpr6jrdk64u7pwjr4myfg0ruj8c8h8n85cfkuegwunx9h9jf5mntj73z5v83m8t2ercewgj2ec696gq5676m26nnajgntrlejh5a5yme2l4rq870ltqyysh6ks3tjqxqgfy953rvxck3unay2yqxekgwt0q86gq60gha7f55j4s5czl7fjv5dqdckuzn4659xq3jacyugv2cf0l9vsksxzv52gqe87wcev6dh44qq9xtmjr3jde9h7nxu8jn4xzavrsvgpn7s5x23uh2xm854xp54ptyrg6438wykpsu4eplep5xqrz7qwla30x9rglr3lkyfrn26qu6q9x2ms0jp4anuacx8uyx35pam6f53tcru86h6vv58rhrxft69ugrvq3awjwmjuxy9px3tamr78dvlwckw8edy8rf2fj7kpp7wffcjnzcwvk3ur2swkg99r2k7huy9slvxgp3qqmkqqu0g39c95dn44fxhn8jn7p5whsumcxvg0g8dvucx2v3vvjqf4lgtlvv8rdqq6dkqanzt4syc3lm2nfxy2zlwaqy447fnexle4jdq35096chtjy3lkwshmqx37mwn7dxl8ljm88xns6te4k9nxmqxmunw3kz9py8x27v4hthwlkgplwk5yauqnlnypexpj0xdkjpy0qwxfznsvmrxm54l6czvzpy74xkqvgemrmuahfrq9aqtdmvytac93hh9kfeu9asm3k5qzeyqc3755ckez9ukuccwkp0zw82lsa4qqfj5qwsavgmvkf0jh70qsnpyqasgzfjw3rax2z630zp7ez6qmrhxeemdjvwrekaa02uwj6xapz28reqjczdu8kgsxyf30huwr68y2l5xhaxh96xrueuvf0dh4wm8ug58r7vyujdhqqqqqqqqqql4fskz","certificate1qyqsqqqqqqqqqqxwrxuq4t6m0z49pvkyx00ly5yw8jnwzt7q3cvsz6et42snm6uy4p6ncfvz3egcmvdcwnsdnev4hxqqq7xertv"]],["transfer_private",["verifier1qysqqqqqqqqqqqrupcqsqqqqqqq2srspqqqqqqqqnqnqyqqqqqqqpjqcqgqqqqqqqzztyqgqqqqqqqqvqqqqqqqqqqqz6rmtnyay2pspz49msr634pu9pa6s9vr9zd87gd88228nrpx2cu7qhcpyh08ruwa4tc9qj8agm6vqzcevsp9yqqffzdc6sfenfuy6u3cdx2kvhw0673w33ut9pv62k0zewymds7zhzw839lpr87k06x2gzhwdq9ve3swq2qg5k62znqnapwyelr4lm5mwn28u0qy8xzau4s6cs8nqnnhhy7u9d6tzdzzglmvwszm7sk7hvdwqjf4yy9l8y9p4ulc9vm74stln8r2xna48wyw5xve2wmktqyxweka97apg8gnjhe6kxq2vpmvw4gakzcqarr9dh609ky3q9h9mvmdz5pd6yzu2yx63r4ssplh5lwk26sdluhcwvqgud5jad2q462vdgfxtasu79ccs0unkw9msck7vtnqnckl9l06tz56get9r7swalxstzm222yvcxuw8v8gs0yuplu0q72thvkcg5dspq520922vlxfnfddvdjhdxnsgwq3avmx0es9tdhlmsgq0smhwgedvjyne6gzgqaa3ds8y79unxytlf8ddu9p8p5n6tffgyufu3xezq457ph4zrkxtplx95wrv90kk5gwut3me3e65qz0cvcds0w256ny46j88pz664lyt0mgd555zgks2q39nhtv78gsdvsux36mva74s8ffrynuar4uxhqy06pnp5m9c96svxwd72zfhczsuusr779equhz8xdcvjpj7yqzp75cd00d45kn8e8cferam6f2cgkquvcysk6443qftkp8qqegytn8ydrlw8yl006u3sv2ah02x32hvatl2phxhk5m9a7k7fh7metxd3fqqn0gxc4j06s20tt46qq2jrqhs0499zqxsl0zetqnft7rd2zqkvu9lyd4ejp8lucpud40weuqkyavgztf0y82wr5g80uj3jah435uw49ktnv3ca3kwvmusuw2phqh6tqv9de0szqqqqqqqqqvrtll","certificate1qyqsqqqqqqqqqqzlvnda2my8rmz5fren33ldcfmaml0584etsyywt8ne66lcwggyf7myezkwjmqumcz5xuw4tdp8yqqqquykepd"]],["transfer_private_to_public",["verifier1qygqqqqqqqqqqqzqevqqqqqqqqqx0jcqqqqqqqqqwzwqzqqqqqqqqk5eqyqqqqqqqpxyyqgqqqqqqqqvqqqqqqqqqqq84l423cf8a8yeujypax58e0eugaf2qvxcap0xarlfmz9yvvxya46a4sc6dluv0en36zf7hs7v7xvqccdrw5lp53hf3jq96wg4gpdyev5yse6643dlf653n26489v0ssczl5f3q2dps8w2u43s3rhcwj4qzltf6575k7gxvkv8hva6yl7x0nw3jd4kfa8w9gmd94uwmphl2dgx3rf88ngyaxkfsccmy4n4jeqzsymknlqra0ptxgu0h8uue0plm7l3tf8hs7l3rf634lqd3kzjm0ykkcqhf8j29p4za6f8fnep2c8zcq03v58rmdku6xs2fes4ahmdjtfn5jj8kvzjy4x0lkyn95dym2n3y2naesy3jcg2u2srw6hfecq7pyqr0tg356uu7gsdd8cshfawurg8akha0kmq3hgq96ycc4j2alhzpd7x4fz88fsgn5lzjv4hlal5jegpyyc3hf9wwcf84yjrjcjh8304q7e9cqld0qvuvyteqr9r7s0n7h5eq296mj6unykd98zqps0dmmjcpqn9lumy4w9s9kgck3nwys9s7nc5ku449x0gvzw0hnz27gttcvkxnkc74u6z904g6w65n6x39wuvqypc76rz2jn7knqn630p8jtlrgjk7vazxhqcg7gc9sekc9a3frzg5aeu5lsulssdkxcn0ruj9lfltqxl92qau7y9u6rawxlh33apapvrg7fq30jwqxgyz9ng0snqrkwuvvllyvpf67e4xyk8s46xjjdf05q9jv25aarwpzs2dnaa2qjxvsnsnunvd04zmhnpaedtn7rpuy77sa94usm603u0539utf0wl0pc6cvqdu993g9m5y378mnef5uyg79w5fc950dlpal4jag66dwe4eln75skhyrql0q5yr7a6y46qwjly9kgp6se266s4nz200ngk8d8jemnrdd3t734fhmph658xqahgwhuz654uy8qzqqqqqqqqgxvrqa","certificate1qyqsqqqqqqqqqq9s7qku3j723ugjtgtad99vyj2wlf48m8accnf93d455ndmyrhgx3j567qpkklhfn572ddu392yzjqsqcqxn6p"]],["transfer_public_to_private",["verifier1qygqqqqqqqqqqqrnjcqqqqqqqqqge9sqqqqqqqqqv42qzqqqqqqqq0r2qyqqqqqqqrcwcqqqqqqqqqqvqqqqqqqqqqqpx3awtk2vtqng3l3sztryttkewhv5vjgvy2kr842u0ermkuxnxenws0yd8hdhjsnrmkm5pgj2ckgpwer8sfcs4d380ryn6vve3mxgtfd3z7m7n9e06nxt5ahcs34p69qwyv53avg0724xyrh63t0qfwcsp4accpdzp4pggwj4vwl0grq8lnv7dnkjn700snvzusg2u7qhrfrwhkuslqh4fr8snqf0mwtg2gr2syeynv6wqcjwhppdqmjlrw4rj2fc0zggqu3zndf3esg0p4e903ezp380nytlpxy8wwlehytr7xqmxqyf0gp57kwt7p48q6aet9l9akvhk35rgrf4n8e24dmlmc4hympmevkpwt4564k35msat5y8m0vmt5q5zm8pjn9ks6cvgrdr6jkmmamd9t24lwnvdvekkzhx7fxs44yeapvr9fxtrvaue7jdsurnfg6zvxcpjwqmynsgn3m335zdj7ly6sa8vl4tvghg89c6vjnm9rjr35nsxl7vukumfuawa09ykagpuq4e9qqqr89swwwsntqdg50rufu457fluqpzqq49hxlzyfxj4qfpd75624egnrlhsgag3dayzx0h5uv26smuqqmzztsvueqvlfwxsnyefv7a0kg0qgnk4k8lutvpulw5cdsnct5flwzm2qunhhzdwn6sp7fhs9m6hq98506xchxlsnxrvfxjsuq5jc4pkxl3pkppjnf639nul6eudzrae5tg4ngeurglyjs9vljq9xgewsqdfpmx84gf26lgp7pyzxal2hkv3tdv8mdhv89czuz3l7pfl56796wkz70tazx6p8whvajl80sds7qqgnhrpgg3lqwnqxncqmz4qaskyt9wz0zumng4pzwpnlx62svrn3c3a2lxu0zddv7228fpe8se5ezcztnupqzhg3c3lwalrw8e6q92heg6hnge9p3afgcfqen9jn0nqtw90lqqqqqqqqqqq23eq44","certificate1qyqsqqqqqqqqqqym2ncr0tr4mvymqj36p6tqp5e7ntp6y3pat9nr3xrsxrexgt7m7zsgjqm20lhguv69vl0wf8w5tjqqq5z8exw"]],["join",["verifier1qygqqqqqqqqqqqzk75qqqqqqqqqg0agqqqqqqqqqv0zqzqqqqqqqqc9dqyqqqqqqqq9gxqgqqqqqqqqvqqqqqqqqqqqdjr35uzk85eq74g2r3uw69pncj52mm6kcrl93yn73sfs7yfxd68lra4nvnlsexfjgumetgk276v5p3x8yvj790t5v6nrusnuqv5j866y3jr857wt5a0l359xxug9mys9qcww3pnhtm794zjrms8tqv6fcpc8c9nrh7vejechj2uv7jnlz9257a6z5ecl8yucpu4c5cq6tr2hgu30p3nhmerw2wq5rm9my3lg5qqzerauy9rwxwkxxmrgplkdx2gfseplg9hxfc35ky2wacgvf6a540cdugu5rphhwmm34ke4u8n406qymrygknwpqvm7mhfkwjkrc58y5u97s5aw74smzt4ukxhpuad7hgdhd7et8epa2rdzf596jjq6wnyqnv3lfyucvcdhhx357j2v4ye4g04ttvc42zlmugqy30x5fjmnnewh3dc6tjtnvxu76thkah2lg4yuqp50zjje872w34gnwt5fl4gjryy36f5lkmrw7rll9v48r8k2dtukky0qp7840l44k7tj7yn4k2kxcq9a7mesnmkz2q4a9a4k04pcl9p4a5pwu74909cjefm53vfy258v8rx52ecrpvfy9tg096798lpsmqrhnwgp4xvetatcz3mhd7xj95qe5swc07d7yjhv7zld8l0fedkklrp8w7fumv392dcuhpf2a4qk2uq0nff4hh2fmpqeltf4v2ffjykxds6g8ytaauzch3c46pndpltx83swpwwwyqpu4f5pf0zqa3dfnggqucuyslfhm2p2kadp34qpnyna0arcnkq9h7g36skj5y3xlkz20pkupfkxk64752puwszr75d2kx6yq3nhzvla2f0y606wvv69z04z2ej3jlmqpx8k45xfs8k6ye3c0wygvdtm8whd4tan53lznt2pykdusqqjn5lg9tfnlp8cvvj9n4gtz4ucuzw4450puqevcaszhcwpm8rst4dzszqqqqqqqqz0ls3g","certificate1qyqsqqqqqqqqqqxa46ys625t2hv3h3daz6r5t8rzgrwmxdv5supt7wqjtgs853jkrx5rxgksqz3vemuxkwq0kkrmcqqqqpjtuya"]],["split",["verifier1qygqqqqqqqqqqq8eq5qsqqqqqqqr2pspqqqqqqqqvsrsyqqqqqqqqv0zqyqqqqqqqpk6jqgqqqqqqqqvqqqqqqqqqqq9p5raynwuxsu2kha5kk8qjlt6vejc069e3kac7ne6vpaqfpnuw5e2jhluu6qvnx7408rhvjnpj9gqn5yumvyvm3x25dx8tgddcgj7t70eaay9aemm03lgpxp772q4u6cv6l0ts76ks5ra366cwjra7fagz4z837rmjaxvjxsc94sx9gt480vahnqm0ntwu8n26mgf0a5ejthscpgfwvunflpq9cyjkfk4dxvesrsrqzquggpan9x7y4wnp9x02f5p4fle5h7qnuzvx8wyq56qpky7pdsagdvps8rqj37qpex75wy5uqq270r0c27zp5438k849y5fgm03xxzv8fdkjetfu8pkqzy970g4fdpgdu5hj6fsc7dvjdxnkq8mhsq09lukdmkt3e3recv8wjnjnvcfg7kzqhxnew2zzm0cnzv4h98z5lasnjr7699jm2x7hgxyjwh6pqqqr89ptxc7mr24sc7drp0ps2qz0gh0qjutunvkaswe0jsn0244v3jg3uysfglenzjn4ajurru80arsztqpdgr98gu4gpvy3dkfvnhscw955jkvazzp7vr93pgr28yg57tghswgfala782f58nx0s34fmy6sq4hyulmmfa0fh5fv240jlgkx079gn0fzrpcmnvqmmnladywt8jlqrrzx6jwhz0r0wscfn9y5ah3fqym93yx4n93l57qwpvqkn48ld0ulh35y4rn7p4cjv4at0ppgz34lj3ppxq6jxc8frhw4cclencl9sqcg03ygr9z5aygaakcx77tacvcv5fcywytx5tp3amp82njh3n6w5w8eddhvxrgag26ce6zjqdyltvpq946dx339kda0m7kx7kd5kvljvw8lpznxduqfuflmpe8jx5ta7vhmzkuv7kdfd2yfzy3uetn8e4grwrg30wa20jzd865m9m9pl9y5af5quzke7uzazvv5q3j4n60lmwy2dfqzqqqqqqqqc74m8f","certificate1qyqsqqqqqqqqqqxpglmsj6yycw8e34w6dxmkrfusggu853pek8u5axdynmvhu89wfqyl2fftt0rchyapaywn8ud3djqsqprzsv7"]],["initialize",["verifier1qygqqqqqqqqqqqq5ycqqqqqqqqqqvfsqqqqqqqqqw49qqqqqqqqqpk6uqqqqqqqqqrln2qqqqqqqqqqvqqqqqqqqqqq28c5qkxsz699ds72k2mx68w7rya7us8lspk8wxvalynxzlw58xmrft8rqz6emp2rmzzktarnm9kuq9hq27mlrk3cy6ht7cg98rnq0hhqscf4w9ksjaahmq7zmmq0j48jrupr2rnak3x3dt2l9jyl4qxxgpl39sylwl86srm2u3s3ksddgp5rrta7llmw0ttd5wvuvkrzxvw06e4v9z9xwfm6kyn89kls0yvnhszl3t9wn264achfvg6k2w64nrn06dq8sd0z0gvest2s4jncrzw7m8vjfj7tk9u9huqpydvrzl25f9q8gyll5gps94eq2pnqg2pg7v7v3u5cvlzsh9yg7xj06yglpyjf62hkz8w863yaxg8nwhq74kehmy6qxztxkg32u6cqcc76hwlg0xlju7dhmszmks8g5gc7jmgauaz75fr8dhqeppq6r80t3e9ldqevs2e5qlvl2cuz5d9gehdrhg5u5v7v2pjrf7ma88myprrvps7shqkz6423ssa8hjddpa34cjuzf63nrd7egpm479ul7lnk3q0lus75qx9lnjj776fwv2wk36mk6tx8yqy9n29wz5pnl8k0ptdzztm8ggxryz3nrq9ft2slsgkgzv8qwgcvrvg2g37fgmmg09lgvclfj5fw4whtcmt7e7wplhuyx8s5wg4g88q3rc74efqxwqnkypyghrv3jn6hngdsrkxyy6ur5drwaf3d4lkufwr0g558706ezw02chwtcadz9neyj64lfdvql55ljzpwfyyl3mzq2yx03g0p3d904ges6adtp7eun043plxqrw03r9m9tmr0j5h6rsjvc22jh6csqa4sjdnfaxez33vhj3g24yhwhx4gx2pvadcrvmwatt8kfpvaz0l8jeaywkvnz9jlw5g0a4jw93xnszj2dckxdhzq6mkw3vqqdh70qzerp7qz6fyj4mqj7e7x79sd3zj0kxyuqqqqqqqqqq5cf4st","certificate1qyqsqqqqqqqqqq9cyh2mjemc0sp2g9k6ng5w6cu4f93fqvmzv5g4s4cjsaerp40exh6ms2zntjaqexa58whwd04da6qqqg304nw"]],["register_token",["verifier1qygqqqqqqqqqqq8j8cqqqqqqqqqwj0sqqqqqqqqqvx5sqqqqqqqqpmh4qqqqqqqqqpqy7qqqqqqqqqqvqqqqqqqqqqqzfmsu2e6427j04cl7clcpsx60tv5dna6w45pzx0h62mr6aevrgglk28ln7gvs2qur2g4jncse83yqy4j07fqxefacd0v62q7htggyvy4smjdeglf9fw49lp09atvn0dpa0rzftcysyqs0wdftjwv0kgysrnlkndmf3mfzr5a43rm00vkfq78ftzvmv46vjjl26e64sx0rlvff2vuwnhdvq62lnay8t6mc9gats8ek0hasruckm757mc672kse3ztda7l652u5g34uqu72m6lwzv3krpevnpcrt7jhdvt4jxdr840zgq9jcter7ae8vl3fnxm53r2ys4pf5723y7h9xptvjchva7p40zye9mpmtg3l5d039xm2wfuvrs8h9vqxssgkdcrxds9lkpk3gszqr6zae8608mq6ucdpfwyhu8tuzknf0jkl0mjwkddesdqdypnt9xgtwwvp0g6vhrsgzwzr3ddq6upwleygng8h4nwzavusky092fdgj832t7lqj2lffh4qelswvwah87x2523qpj43aqxp0792en68sjyr9hvyn9uk6tya7pld78jntrc05ya6madlqqfgn36zasyx8tfecwm5k4e0q9pdhquphnu5k2jydd8m4qt0m4pm6km0dt9gr20qk5t5p7ppqphupfkxfsf347t9u8v8f26x84yr3qwucy2fdx5azw6d88ztwze0526zt7mggp6ehdkesl0x6l5quw3l7degxqp85stjk5u4j5hpc862jgqd8dcur7yc0xmk50m3ff2422ls4c59zaj62h0ha4udgt8cs7gu45yjtalcujhj9reqcf92zglddgyppn7sl9z2c36urk8s68a8z57aafgfu76xdkhf4c3m9p0vllskxha4puws6dyvceq5g8qc6r60jadsr4u6x09cacehsvtywx4nr3lk8huhycfp3gkq6wfjv6d6qjpv5f02setsqqqqqqqqqf6a96w","certificate1qyqsqqqqqqqqqqz9trae2j33d7z4uk32234saw89n8t5zha4f9tg5vgd93ga34ggepkwden6t097gq3vjxumffnkmzqqqy04z5n"]],["update_token_management",["verifier1qygqqqqqqqqqqq8wxuqqqqqqqqqwvdcqqqqqqqqqg6ysqqqqqqqqpwaaqqqqqqqqqpc55qqqqqqqqqqvqqqqqqqqqqqpnhta6enxfrcpcjyam2trhmezqc6ywpplmldy6vuslsaeh0lntnm7artp6fjvn8pk8mvuq27qvacppexjr65nt8w6qhx7e6rnj970s4fwzg96th5fjk635ddr9kwug0jmtrq23m248x62etsmm6pl3atcqyn72cz8gzjp5cwm9485zlsfkt8awae064y89d83m89mqekqukl4gehprfpm2av5ae647yxwn0jfqye7wpfg3dk92rgl0wz3yfw46z3kchsrzld65dpu29rt9c293zmy42uh7ga4kca29uq0mg38t4n4gq8wc8cnk9ny3ggcjl2gljrjr70jrygyrkqt8cuv48pnn0plkup3y0hjq2vgtyf6ch3mfxcmpm2t8vqscmsqwwwf64y55pquyqs4gewdka9ruk7p68fg9fw755tj3nvcvph0cpv06ju9ru0mzk5dquref5uqxx0a7t0az4256cps7a4n7jectxtrhaxmtk0vnwd6kav8whzes7av2lj32ct8ef0gqstk8m9jvncgpj09efw2xs5u02u49xq3sgssuwd5yw43raurq2gtd0f7mwe70l7wzvj5ke5n8jm8zwdqfgqy4shyszh8jp92mk0mnptmeayrzdthaj0u987hh55dpwchqenllaaq55vyvyx50f7hdt6gfdvsfdsl96sreqy3wsjaucyzzvanvt7aeehhan538t2qulldru94vlvqtm655y7mfdjqd45tm8k47swmpaqtta7x8sqd0hfeztt9ukkvx4ww0rjtszrexzfzhl84ncrgu4muyed2cxfvlgwq9y03r8ksjh2fmexy6edlmfvpz7dx4fkaag58ffamy3dlgccvnnl9zflw2y9w5u6vtwkj2cehepxvhpl8z9z76pfj40g07wx2ty0sp4ful7ndfp37vdd88a7fmprg97p52fx56e90kzul5t9e5yqr605qxfxsqqqqqqqqqeux90u","certificate1qyqsqqqqqqqqqqynrl9hv3uhtdyyne9fe9486vqcdnhg7064uf5vu8c38fq77x7pgxh86ewa0ns6x35733rpghzaawqqqrmsjr8"]],["set_role",["verifier1qygqqqqqqqqqqq8kgqqqqqqqqqq0gsqqqqqqqqqqykvqqqqqqqqqqmacqqqqqqqqqzp9uqqqqqqqqqqvqqqqqqqqqqqf7d6q5590advpkce5cf6zqleg27ugd84qjl3686909dlnckuc6m8838gt7kcvqejdnmeydkt8g9qqdl7amxnfsh0e9rdsyw9prz0gnt7dx9tw7x67lp85gymq7se5sgghde439g8kqx0hlvvd0hswsg0cpz6n6ga8xpdu0c9hrl8vj9setdk9jtj66j6mcj53ft3zdcshcaug9ygdmskueujs79qwefquzps0s8ydgj2r6ct9dec25x6svl904fpes0axls2zpkep95lmd2exlqed86dg3km0002mrhcpc77uvjzwyqyjyt0ldvqy06d44r5pa50768uth5wt4cydgye29m3chflqqgmedgaxw8ns38tqqka7rqjggakn0jqs4pvdat3mvvjs6dx86n9t44u40gy5jp3yenk88avxfnzw39fs7vwkugxfw4fph8u29670rrdwvguqreslx4ly8a4xn0wc9mj3dszadnpg4dcfmjq7sc8sp632lnxrk9dge0qclh2x3vygulrj7m2vj5ngr2jzq5vfqec7clqxvnfdr8vdce47flrtvrz8evdkwrn69uncf9z2ldug0h7skr4xshlxfp7cjzpqq8qgttzprs42yp3l35rqv7lnscwf32s8qksp9yayjlv4xuqc6ju4vf8lhma2gyzmun390uz45the0qg3cwtyhylt5c609fnugaeskt5azkn64vmkuvfdsatvnrntvl3zktfwuja3x0xzmnfp6l45tm0ayuq757kjuukj896hqrxwlg4f0pnv8mpqkdp928nfp9p6eczqsfahcz2rzvvcu46myuuzunkhj4sezduqsfqah5mymgy33ha0eal5tuut2lm7la303dk7v7n0hez80vlwux3u5mk3kcrqczsmtc733xmfl0aqqnm48g5me38uujsa8q29dn9qv90hkt0ym4ncw6xp4m8aly9me9ts5atqqqqqqqqqqtug8ye","certificate1qyqsqqqqqqqqqq99m2xzyak37q6eqdqzm49pvhna5x9cayqcw6yxak7xwld4t9y7p26rrq7k0kfkh7en24vhq45dmgqqq3y0h8d"]],["remove_role",["verifier1qygqqqqqqqqqqqqr8uqqqqqqqqqqq0cqqqqqqqqqmk8sqqqqqqqqqe92qqqqqqqqqzv9cqqqqqqqqqqvqqqqqqqqqqqxj46pqn2tecwd8nvcze2rc6hmvvdgfujmw9lkdcwalw27degleusyh249ueh63muwsev4p20r2wsqtlj7gf4rzuwnjpvjglm89m6qt3t0caesdermmz2tm77l7eargyrm0wwl6dm6ntw9nrutk494ls2qzacpwyyvf52pm9545s6uzfr2y5x8ve4ark5t52q4waf67ktzxqaa6545jp795y6nga3z26jla3vfqych0jjj5hxjqf044l4vxzyp2wpu2mr9fs3un7fm2n294afmecqh84xtdj4qcfg28a0y38nrzmxrtqvj6wx7lva7zmm8vhn5lx3kftcwgcsp8em8h3732zzu9tglcw5ccms2gu8fdksyyqef7hl3hqaq8gqxynw0gmsvcv70jjmdkv3ewyfvrw95sqhfwc5lrzja28g6rlywvgmjd6h93wt0hxxykz4cqjxptfgpcmj8khlp3wfxvz4wcxvwatwu0433n9rpf8wrnxnf4mt5pascfc0082ur458jxj6kfpc3c24stu6sry7ykxvmej8a2gd965y58ld2enx5m94yncktpgwr9qz6xtpz58kxqxrzp598eljmafekge7v4ykgqqmuwxsnm78x5hzqd8vuj275xjdlfsu543f8dnskxjtklutxpy82h7a8wg0ejfn6x6cfj7c2y0c2aq8vgug5rqv5lckm0vd2hhue3e9hul8mzsgkk0fapkefdy8u63yz357hf6qkanjryy94dyus4n5qcyqtt80kggytld3g7gy96ae2k3wchnu3w5szlhncex2fz4v6etrwc76n8979y8wtxnmaml2c20u0yscp293e5mvsl2tuwcrlezmlpf06es80zk29hwqcf6hr8gw30yzkuh9mn9zlxkegy7wm4egndughgg4qqysfdxhjpkq2ufeew88hyrsfht2yhljr2w0wgjss5q0zn6j599khqp2sqqqqqqqqqflxwmw","certificate1qyqsqqqqqqqqqqrlhd2ghgx6d3nt2s8tk9z60p0895d9nl9c9n2fza4zzqwwzr362kp3nzr3xgs0zzclkz20cg4uwgqsqmdtk72"]],["mint_public",["verifier1qygqqqqqqqqqqqr52sqqqqqqqqq8s4qqqqqqqqqqxnxsqqqqqqqqq500qqqqqqqqqrr86qqqqqqqqqqvqqqqqqqqqqqz4pndn6d7p3e5c08caj09kprycj9uegq5ek5xtqr8jdku854xvxx3lkzlk8me72d47ea6k25cs3yqwfu9w3ag5uetjcprekv0wntsgvyqdqpv4l49svgeyh0ktwgy2rt8c5jyxzhqefrwuncjhdj6yvgsp8athz2xw9c8rl8tuak8wj23f2pugp2gu4lvxph9ht98gh8z93zdsmzk0fnu288c3zu6sckrp9vssxs4h8nwjejses8e0eqvgygf6dxp96w70r9fclmgjtt42mddvnze0fn65s0z3peqg9amnx4t7sa6sq2h088hpnndp9ex84q6w9267e6t2arm958cc3fap9008ganjcfy5du6nay620q7ykg926kj33vhq2qtyqqn5f8slyyspetwqr0vg23rxvyp36lk5uec0hm9zh4r5k0hzrsgmrfk6ymwuvqzpuyd0hdj94cqrensm33uf4xt3r73zv9ut76zrdq42jwh6c4x8ytqsj08858vrzgan6grsyqxawas8j85kay3z8ecqefjt2dr2rs8uvnrk94qhxdwhnfxr7fyzj07hsxnk0a739hquvl5glu5pl4u0aw9xemxfwg7nl52qxu8zlllzv02f0w9yz53strszlggqdry7eaqwv0ecmjzwfhwe2ymppxwmsqf9rex4neqkwhmg4z8hq06upxp32jk6k7ehvnznrp3hcq97ysg66h50pv7uynf3s87xk2qxsy8tsjvwcyng752tdx5j66s9gqawmyzejz4uf5a74ltggxkex7jyk0kzp8qv4l7p60aa4lvyja3yrpskes0a2qg76g40r5dn5zg7huqyvz5tzml2e64sewhaaaur9n8r00v9qvnv9fp8a5vswuy36a3f3t7wgvljvr3d7pdg5aklh52la0sreccx9sptayhyevafavcd0crj6hfuwktjwqjt6jn2z56x8ujrnhuy4hqqqqqqqqqq43hk9s","certificate1qyqsqqqqqqqqqqxyr2udfznsr05x5qzv7sr3ctvdqmyw3r654v0e8tsuzs89ju2952vv476jt4wtlcq3k4l095n3dsqqqtw8w60"]],["mint_private",["verifier1qygqqqqqqqqqqqrknqqqqqqqqqqglxqqqqqqqqqql4wqzqqqqqqqp2rcqyqqqqqqqrw7uqqqqqqqqqqvqqqqqqqqqqqfdzqrcd0n2esskpw2r7p0n6akvs26m679qk404j5078wvjjqwf4fe58egdqlqjkju7ts89eth99vpes3gnu3slyfwzpqenafad9mn4rqa4n92lhjhpxekkz2rl54842x4uk9jtsksrhz84srj60nxnrsqqlqv3t4dxwcyhm23rgc2kf9c53pdymy2j5lqtll0vnz9z8f84heg8m83lclxtewfmfg8xm6wxyyhq8r09fz2scex9rc6ek8j6cm9jy3pc048t78w5qnd456zsnw2pcxmpjry94r7cmch9c653t42nyqcqqqyd0ccx6m6x07s6725l66aqsq79lmge4ll7uh804hvhq4a5yugwhm34w7ckwgetd5387v46784tkqljuh49auq3sgd430wnggjm759jyvcq8s5dzveprzs0298sh7hqxqk8xjw8a2ykpcn6djlcrrmx9spn6t9aghrp6srgxw68f94lf7fy47h9tmv5rdhfxy3gtww4jcaw9gkxkj05wkufcs94spx24k92u5gr82zj0fj50g920excrhsxamxch52t363uzng48hs8vykdl6654t3vhwmlnqj8dezz50wq09cu629qrzqxexrsdhxfx8ym9kkzg799qwuxay4s4kw0y26ch326kkz6qy25uy4hhwwwwuw4fjmkq7av53kdqgg2uz82e09s3nltyudlla33vkkty24wjdyqe3va5qjrp940d2dq42hfa0jgfdzatdekyu7ucuanzqjqm97nt2qvvswr7xlwpcn655p8xgv0f2vd27e5vkhc725jewxvz8lnuz7y0hjjx660l7ej9tcydgqqdffwttcxeusxvuv9edz7apu8aum7ux4njg0u74dlqsfmfj44kjs4dnjwlxmw4xjtyggl7cftyyspvcz8grkxux95np9e8xhramrkdalmtc56uhx673h27zru92hdlsxhxlsqqqqqqqqqjewvsm","certificate1qyqsqqqqqqqqqq945hhccj8ns9exu2j8kaegumjza5pcw6a4snq0jtvx29zyxwzwjdlqagvufvt5ks832dplkgfnguqqqlfmnew"]],["burn_public",["verifier1qygqqqqqqqqqqqr32gqqqqqqqqq825sqqqqqqqqqcmzqqqqqqqqqpkhqqqqqqqqqqrj8kqqqqqqqqqqvqqqqqqqqqqqx8kxpqx55v6kds2kff94dxfp0gdcu5d4fwsz8txt4y9jlwcfedlr2zjcemaculjfk0d0rw2h9dgsqvlham5v4q5zqppqczh4dsh6nepauwvpwsg5q9grrrg8unwzwz23vh8c4tgcwq4wvlmdracx9t94cqduu0v62kpfmz0etusqsh46kchzekrn8k3szpqq3n60f7uu7wh0dnkge9cwpsxar3d9tqvspw76mq9n6waws0djg5szzmnle0aash48da0xpp84en2jk2u02tj4wrras5j9u7593fr9lszw7g5vs50ufrqpukfe8z5gfezphsra8meff2w0ry0aezz5m8gdzw8lw58rewz7fghnk56wpl3zxf3tr4wavl4yhxxqz5anw4usgaegmtag8ctpdec5k4penkn9shmvzlutl693hmgnk4yp2wmr38qvem0uzptvt7740dkuqf7mec8egwm03yw06cjf94jym5z5snq8wsqc49swaruhm2w74sahhlf0epgx3xk7l0dptvqkvnu9gpns3g4wk654j3tullt7pmsnju3qw8m9tkdw6795eyyd0ykr3jn0cz2gh686a323pspm82su9v4tpqzn35kdhesumzyafx2r8c6y3ske5gjn79g9cx95jucss2976uyu56a75eaz9rlsmdcj5xrvpakj69q9jeq967juvnzxyeu9qx9v0aejgz25h7ssqwkycaxqlhn6k25vlq3p26cl4skent6d3fhefckmw9vqy3qec90rdmp6rncdqjlp3ssn29f69sk6c86xfhhy9unfyz7n442ue9ljsga9q8nhwnla90mvusdyq2e8z0lc7jlsrytn8h3sqypxe5lqj2tpshtnasdq2pp60eedz4uswq9ctwn5s6lyvs3hgrj4cccacp4j4qyp09uq9s5r8cmvw7hhqmdln0t2n2zl96grsf2rmzwtq4zjrrakqqqqqqqqqqfn6zc0","certificate1qyqsqqqqqqqqqqqcjhw3w5rd54nyf9v9fnh0tpshnrue8ehwykpp9gcsd8spcm4m3zcw2l5stmnvkaa6hwzmdjernuqqq32hju7"]],["burn_private",["verifier1qygqqqqqqqqqqqzrevqqqqqqqqqxmjcqqqqqqqqqu6tqzqqqqqqqp0vtqyqqqqqqqr6yyqgqqqqqqqqvqqqqqqqqqqq9fy36tgc46lp00s9ncz0mf9m494snmce92huqqey2dwhnezuven2ws0hh2s2xmjpu52lvnnsgrryqav3w5e0465a8vygrkmzmasr9v3ymcqy4rcdacfs908s279309nqga5e2wlw44etqd66zz03mj7fqp23a9z6yg5x9esrwmhqgat5ct9fuphxp60hnvxs3sxm55nhqr5qenr7ghq3ecumxyyt9qtvm24fdsr40wjclwcph8z2le0qhf5anekx4kv7furckr5hex0fegvjyhcdzp00frmg3z43csjdfmmmjap0qmq0r6xmxzrz3rtqrh3djqnx5ftydcvpp6rc8gtlunmamh5ar7vpxpmldumzlyf5j9k6gaeqemame37qhvtew4ynp385kdxql6fd838pw45zp3zum36m9y4xtfmx4xhwwsakqe6vutqcehlxclrw4kfah9uyq9uuewky3ttv83knd5sjyajj98dm2pl37ppktguhd00zd7hjnt9chzf5y9z2vxqsjtdskweanfzjqqkyawzht56jcdtuslkycdkap7tmj905mnlvw2pumxstf4hd2c32rfxv2jjg04n3c5jh94phff9szspq279a7q5dq6wm36xs5wq67u4dvgk3qhjkcre28eetthufaks4h2jf6f4qe7drk3ursg80s2ml72qpcfm0s62awfsad3j7x2qgvhxk0wdtu7kl7rrv09elmhavtur7xelz4yf273dy79yr0xnsr3hg07qqprdc0dm6ra85na9v8tqcq48qqc2gl7xhc7yr5vhmhhyfptfslhut6ad4769rza2wenx4yfv45r8uqxenrry9z2vus0m2h48jkwdqelzqpy2d6w6l3lq44zujldrquyn0jw8plmfyej5gcywj0w8g8qygcra4cs6g2nzzzwyx8n8ue07n3vtzpujn9n75kascpglqymgfr60t3wy9qzqqqqqqqqfy0tau","certificate1qyqsqqqqqqqqqqrhdwvwlagrdxzqgwpp4u8pyvx65jmw7ejvqaw9ceu377w6l9zullvavxndevn4pk7jmjdqf9ynp5qsqm97qxz"]],["prehook_public",["verifier1qygqqqqqqqqqqqyvgvqqqqqqqqqgsscqqqqqqqqqmw3sqqqqqqqqq7wvqqqqqqqqqzqxqqqqqqqqqqqvqqqqqqqqqqq2jwwylmryjhz0ptfqlry7d57xvkkdv4veg6sp7nqchl38v7uhe98r9zwpm73h3h95xa0es7xjhxsqfx87dnyns7gutur5cvjzhlg60tqxxg8lk0y98jl35qr8evctm00wv0lzdxy5hrp4ekz5cxcn5vcqz90muz4el37t37g4vx54sahv6m27f3yus094jqa9j7r5lr4w3gxsthqmhumzgvvfapux00gmr7jtsxk58x292gxs422wec0v0vqhvkaq0ev8jlvp47z9552k4utdrdqw2cge5c8hs7dy3nrrw3df33894qvm22newmnx6twfe5ghnfwg4jft9fhkmp5vvwa5hrvvzn0d52lvajt2k7tsrknn7rryhhz7kq99s5qp7ptp72qa6gq3q9mphx4nq2knvcv0jzur8py7d7haslwtzsxwy4uru6w25q96x6vq42832gqy32gpzqu6pls0nyny2jprgltlwpwsktvn6p8vur9xnnfcy6sx8jrcwuy3et7f3e4lwpvc7q3u6zqypjgsrsevylcd3zkup5gehrtp5g64ra90a5sy5y5ekf3j5hsx9hzqjlvhmxkx8lvjvqc9nhzt0c05ytu3qqatrcrfrm0t445whvdajuad9jx6f8htt4c7dq0ssch6d0w68n3emy0cq8aruxfhqcgp8hgjpt59hqrhlwpz55qdmyfawmt038ymxd3y29ydsn76uls2fzp3k0nrsp9jnws46mwklw5vy29f3svq0excsgqtgxumd7ssu760f25shycs75g5l9k8kegmms24uqz9qlt54v9xkjv387n5prkz54cmuw8kfeamu7cpzvm6lxht0rt6mjqgrxp6p6e4djspyl7w4t7nsydn76rknalftx3987vc4txkcf8nae8qusy9fwnqpd9s6mmeldrzl4g2zdyh66rc3qg8nuhjktvtrssy4qu39tp6jnvpdfdqqqqqqqqqqjdea44","certificate1qyqsqqqqqqqqqqxywjqwvqfgjcddcfy0evrdmfspx0z49t5ggd5cfa7n88t3mnjt8tyje7lfyz86zy4m86ycdx9f4uqqqq4m7zt"]],["prehook_private",["verifier1qysqqqqqqqqqqqynpcqsqqqqqqqvqrspqqqqqqqq75nsyqqqqqqqp3c6qgqqqqqqqzztyqgqqqqqqqqvqqqqqqqqqqqz6vrjdzwutcupdqfkcry6g5x485s46nq48uhj87u9q2r4dgkfc8xz3hhtmwsgnypuxww75mssx45p2klvzps69nxeg66l6ptd8nl028x2lu940lhnsvhgd3wurkk2ld9gtp38u976gnjx4jarqwkg53yqqd3284p8ra0qz6wkam3zucdh4fyh6cx7zgawny9gxrm5uv66jtrcmjwqaxgn6vvxfj7309j2nhr3sqd7v35lxvrufsaa86wperu3gpqpnyh2p25drt6fe2a4ffclzzdqyf32ldcuvm335hcl7tx6sst3cq0pu4p5cly2m7rdus6zuv22ntwjpezfskjrrktnw3rjhx3jgzynnr5ach0942q0cv5xpnepz66fwsqhxysxxw5q4nhqq7u8azv5pxqdhmpypw3yh7qy4atr0plwgrsq6s4e74np8avkwnucpjapnh3t5k5q0vsm3m6xhz403pkc57gkdg8e0y5mvhd8c0spl4hgwsg25glddkzqknlfgakj84t2z3qenleu4emqp52vxsx9pdcmmxjs3zptpfk724r4dh7403z9qenxqdrkeun3650jkex3jf7w0clx7cpn9nmcmwmlqruj4qyndqzq8h66kzfu2zkmujg6h6nznuqnj4j4ukknfz28y44rekkg5xh8sw3gqj534znn2hvg0qya45hmgxamajwm5gunu9rdts9wtrdpdd5un98achkrat23m3qxlvc2tfmakemwqhap86pelm3pn7qye30wzmmpvjjj7tgq0ruzzhy6qesqaze6t89mm3nyq9st4cs6v9dkgtuuzyzphw7vtwdyuf87xusp7c09zppmewdlpxnn4lpwgugn0c428xr3667m80tuj367c77g3m07lwaqj85ktk75ekquc7je946qr3nkcumfhw5cyuvh99j33slwn0etzlkwf59u0vpr59cmfstzzer5a9wqzqqqqqqqqdklfq7","certificate1qyqsqqqqqqqqqq8h8za2rp7acnwx3k7qr7fh7dkpup6mpkg304sc0kccurn3xwyk0h9asgc73t4q5y9pfngeg3z5yzqqqpjaynl"]],["approve_public",["verifier1qygqqqqqqqqqqq95fgqqqqqqqqqtgjsqqqqqqqqqs6kqqqqqqqqqqkkzqqqqqqqqqpuhqqqqqqqqqqqvqqqqqqqqqqqzj394vazm38xp56z3l7xr96e0avyjajgjxk9xrlwxj3gjleakemc3p3nx90fct4tyz6n9uv644qgqvcax2xjt7ae6jg6zwdg7d9we9efk7rdw32zwhw3v2v9x44r02hwnj6ehqt6cxjrnhv4gsucgekqqp7dr3j2ev8qqp49um47966ehnfmyetdwwxwl859awju7avard4xx902vxrag6fkxhknzdruu7va9q80pk57xcttgw7uqkz75uyxwqs7hwrvf63qa9rw2lrh8nehheh5mpuxqvuw5hm70j8yxhjf52syqdqryum0x3gne3l6ugyxrpjnkaaejhr2n6sfz3ly32h32tk6ejf3fhgnstk5za629y78gk68vcepmqjqjm40p9kljcf22hd79uhlp4sdfsu5njj8xp5f5a2c5n3uamvrd4afc5xqfj945z6snsv7k44d4unypx20y982djdc0a46g2vvuq4dezm2gl34mk5t27u92w8epxv4c8t2dmsqjnezp34ppk6zk3tlccdycz5vmm0v65fp95en6v22mdl7xw24762ulzrxwac4sf7r0hsf43p793dartlr5ckplx400tpkg63v5s8exu3c259fkug8zlqkkul2ag9k94tnxtxvm6wfg4z69ymzddw3jswkdnj66dsjae6rj6cz4c925qqz8z5yekprd740u2xenvdyxdj9vnnkc3lx2ds3ymeeyrc5jw8fr37r43d8fly4c72gun5rus43ytuqjfu43dl9u2luqk4vgclaxhvmrvddpjpm7mljumqjdz02ucan0czmsgfa42ux47nc032ns408s6pypmtqez5fq7lzvw5hvxs6y9h43nckz949y6ca3z9lruvvppnmypwuch7us4qga7p8ha6ty5pxkxqncpwwjs5p663lqfa9jcjg4jzuwgtph0r4pv8n67yt543vyzdq6te6lepssqqqqqqqqqz3l5zn","certificate1qyqsqqqqqqqqqqyzj8tz39t4k3vx9jjjtn09uc9pf6y4dlk4gaplzgqdvcjs4j4254p6j5ad2mds8l2n50wx7qmwnvqqqmqyz33"]],["unapprove_public",["verifier1qygqqqqqqqqqqq95fgqqqqqqqqqtgjsqqqqqqqqqs6kqqqqqqqqqqkkzqqqqqqqqqpuhqqqqqqqqqqqvqqqqqqqqqqqdfnldv73j83wvjc9n6az6jdu2n002ymqaqrhfmmq3tz669nd66zrksm2lgf3xrja8u70z79d4ysgqjk2v4h7ljyjgx8ahq0fl8wn0myje7peqh4ftfdqz4yq69edz3ggrqgdr5nlr60j4dg4fy99q5wnsp7dr3j2ev8qqp49um47966ehnfmyetdwwxwl859awju7avard4xx902vxrag6fkxhknzdruu7va9qyff847f6qwl80uhw0k9rnx0qj429res34r074jdl84xq68ernxsfzhkplslhek2sff8dsh3huk80qg8p6t330q5mu92678ktx6jchp2gxh89g68wn6wd5v27vx3e5m2pqanecr7fv3u65j9de7pyg8jvxqzm40p9kljcf22hd79uhlp4sdfsu5njj8xp5f5a2c5n3uamvrd4afc5xqfj945z6snsv7k44d4unyppyh5ye33jn56sl8usmr2m8as8upnsk9pauy9wjygw40wp8n0snk4y8vt4q3tgqm2nylqz89nvq0sqhqat6ttxll5p5qg5nvrvawazuytytuevwgydhpwpugp38esls29544fy0l787f839s0edmgaqahsrexu3c259fkug8zlqkkul2ag9k94tnxtxvm6wfg4z69ymzddw3jswkdnj66dsjae6rj6cz4c925qq9vklzyur22dyt5vqqvncha7w7cjznsd4pcpe9sfrv5dcdu5tx5ky6jn0078t8jklvvgewz46nl8jqyk7yevwhykz4xpmzvrlu7e2rwmgvukj2tf28zq9hucxdtv4mgnyxfmjhf2ap8l5uaphn8yfd7tecqmtqez5fq7lzvw5hvxs6y9h43nckz949y6ca3z9lruvvppnmypwuch7us4qga7p8ha6ty5pxkxqncqtgthwqwx4y9666rx2tmqw7nzglg4ahnkfaw20dtaz9702m0xh0vy33qqqqqqqqqq9zjdxw","certificate1qyqsqqqqqqqqqqphugs89v7djwls7ctxj4hvel5k85pggd5jdapvr2zg06fy995wvgha6as4f29tzxek5l06zl2yksqqq9ldkjq"]],["transfer_from_public",["verifier1qygqqqqqqqqqqqxgdgqqqqqqqqqd26sqqqqqqqqqvyqqzqqqqqqqpegwqyqqqqqqqzn2sqqqqqqqqqqvqqqqqqqqqqqrgjf7k9g6rr5d4pdg4t0qnwyxwpxlnkr5pk2y27p42aqruz7dztfzd2wcnmnsfm5d64fk09e0yr5qyclsgkgw83f8lrvtsl0e3xzk0ffccqpdr64k0x8q3r7utfhdqmgaaqth5v8uj930rm8euwflvhqqp2pg2na879rp3qudglp03naw9x4fa6dzw4p9t5jlvq4wkel2gu8k9p8alct0v3dk67ldsvvj2j9qs8vn7hvdncw6575hhzlat2thkr0kvxnkxnwc9kzpgxydxdgfgpslthslepk0qk8ulh5qr2cxvv93yq8n2z2vaf74f9wx2lhs2gmk2qsrrpdmjr45p9hs3ay4228shg2pjnyagl5nq0q6zmhupdcac3gwxwqfflrd3cqxwhyaymjyq6lkqjfnlslp4edfafy3583lc0ncwnlclk77fvv4m4jpzjkkpgwsk9sjdecqqdfukvw09szh7rw6l47yuy9tp92u0p3f94pu7v49eq90sela4amgcyrhavglut0spfxk83m53kpspet4he2kur4900nya0dak5l2hcsgva2eu3s22gfl2xeg36zvztn3fet7uzl7qqfta3qmxds6d2zzq8fkqzvm28vtqy3kcmvv4qvuq54kacgnc95ssfcja8r53maxf5twlpg9kvmx4cncjgnc00ggrsk3vqdfaf0ulw6kc55dgydmsq5tteqyvqkf2jxkz2rr8hhvlmmzf3ygxdsnr3deyencrkus566nyus5dkqrnsgna0nqhmuyg0mflh2dhl49jhyw0xqutt7tt6h7gewll42gnwyn26cezmhu8g2kwftyc59juk5pvct8qxkcxn95gj9kltxmmdadfp68wcccy6tpjr5cp794auu9nc0xh9tzm7esjjjxpmh6fxwc7qgcqz42jj3m2c7gqcglukxrzs8r6x30vr3e4qx39ldt96ej5xjz9zwxhwysqqqqqqqqq69v486","certificate1qyqsqqqqqqqqqqywt3kl073jwa6phg44nn5r5ysv7zru6syckl9xy9mcv23c5rzv5c2n3rfu4ct274juyl7z3hzkhkqqqeqvdss"]],["transfer_from_public_to_private",["verifier1qygqqqqqqqqqqqx24cqqqqqqqqqwetsqqqqqqqqqqzgqzqqqqqqqq3ucqyqqqqqqqzepjqgqqqqqqqqvqqqqqqqqqqqvj9mzuwdc2vsy7y674l855pmhrlsfjf2yewd3vvxl9akh3fxqtrnr2sx466gws92hq0atwldlffvqfm08ql7sqxh8dwkzp6ts6vtphv6hmj89tq5hnmansyjhlq55xah5z93fp3n9ns3azj8dz2axpvgqp6l4k3mez28qexdwxg0slhtgzx57hk9zh7psqxnu54w37gvjx3nrel4vm526r6hf7knve9wv9qarqxdppejj8h4v8r5m76hyad7s0kral3t5rpezhytucgvzwqr0r0r275ntpuk3fpw7eaetwxupdz6gqqxt2ujkqkfhdl3yg4sun6uf2zvx9vkmh9k2y5gxwqfdcdj6qk3vjjn44rg30nj26h9mhfz3dj2uzyqs8alxz5e7ywsag0490qxfy5c9sdlvyxvyn0hald56tz02x85skd3gjvvfhewymh2jp3tdlysfuzuq53hfcm66uhmqz68mm432l3kmcznc3lxk7f6tlsrwgv8p8we67v7v7xy37wd8z6n8wkgu77afxf6czul4ctvfwzrlzg6pavy7s9ltts0a9ft3f6guvuscc5chws3fe2xxcszcfv0cgkpzuhg8f3pzpesfs86c053u8e7tr6jhf9g0pxe7vwm5fsqrkn6ytkmh88wet4z8peqywn5hgl7ryup6hn7hxtzr59ejwqtzstwzr0yrzdn9s8mycw92guhmatgzg070u4mdmvuunluav7srhas3w5vrethyus3mge6tap3kd6qnva5a3zlf8tyrgl83eh5jy59dq6wwq46usrl3p995eljsdjd4f7jpel4syultaz9ks3v7rynv6supgv4lrgecm5srglmffenk0vcw2aunsks38cw9p50454r4d0krx42jmwllykpd90df7s3pe9jzgpqgpwpxd6jwr4c2drtz9wdt8lrkhc6nnwlgg7za7gw2qeadexgfv50x3rwsqqqqqqqqqvqgyy2","certificate1qyqsqqqqqqqqqqz03aym43ld30ryp9x0zq59y2kvw65ycp5ttl0ll72j0q905juexzc0g7yjvv9w3z7qkjh7efzz2jqqqthftqn"]]]},"fee":{"transition":{"id":"au15su0w2ntw0m3m6esrpsn2qr8gd7k8agyjhw7e4sl30safu992v8ss2xvuy","program":"credits.aleo","function":"fee_public","inputs":[{"type":"public","id":"4204621360079531345924164626937619166045633684764009904150285352606157294663field","value":"75798350u64"},{"type":"public","id":"4735952515899317750664065480769415405047337445011285358998869614339923087468field","value":"0u64"},{"type":"public","id":"5334314194126344724134730353523431566409568078871284993701941218344284892420field","value":"7219828396534389733733442787671942045312203808091523682877515319252858785481field"}],"outputs":[{"type":"future","id":"6174508564661104955080204863722384082623898596601834669252840681217993530846field","value":"{\\n program_id: credits.aleo,\\n function_name: fee_public,\\n arguments: [\\n aleo1mgjcqhnar38zvschjwxr830pwk57ywnce8rp7smf8c3yp2xexvgq40d4fx,\\n 75798350u64\\n ]\\n}"}],"tpk":"2027543674195242663337742133501773784411198927637377797704373036741782384715group","tcm":"997104720953711142107147047071806792302946972417712248013297291566700066824field","scm":"8400595953876995264554804889824421621333978905052559491209753497117901891982field"},"global_state_root":"sr1a0uv94c0224sgy56r7fxwalh77dy67w327ahcdduqxglx3j0supqaykkvn","proof":"proof1qyqsqqqqqqqqqqqpqqqqqqqqqqq9z8dp4t232hgt85c5dsxsv8vd3r7sz26su9ec48g979djme82mssyj825938a835c330mqgdwk0ypqyjhwpgw0sjkww227dygndxsf23tnl6wgnx5pp48jh9rfv4kewcr8wptakq9wvyshdc9qexlt6m5jqtcenv29u4xvgj2c0hmew32nt24uvgnvdjnqf2cu7l0m7cy4537u7nqdheksf5dx38qtgaljs3j7gqrx8uj3dxyu94pr789p44d44757a30ac6tv6gq3ygmsvz82dsjv2f8h5lj7y6c827e2n9dqndg94cqh7nvx7kc94haqjv8uyhzp52hcxwhgax07gj4d649taaumzvny66ql3s3a739gqy9wfmfd5mc7l0gq3gyks8rm78tud0d5pvc0kmq42aremt7yjp9vy9rrx69y76vm5fxa29pqtczlsthxx8a0v67w762sqnfutzqg4akywa42dehjlc7s43dx6s6yry4qyhpzxkemmja3q6u37wg4dc5z662n8cthv9czmj3jqftppk0hx3a3d5k8ukl4huwgqma4wr89d4nxz8pcqpxpzx8ztnmhc7w5m0ph96dweewqv4fyu3xm7qfj274yg7kfnxm3wnwpjc0pjeh0cc3gafx7p4trrw496egkt2k7lr6tjgdtee5vz97pc2a9r9lmfvpuyfq7dva95qrdvkrq5apep883g0uxj3vuz8qsykfe3uaz7gtjyph9ygycry8tu7nnuysmxqczm5mz753d79ysecgqnvjaeek5q02uz2egw7tgnt3kd6mpcc57yjyd8mtlhhp489pxspz2nr8rhadeuy9p5jvx0t2mkqkr7a7w75z6gt2j5ajcxuurhrns76lrrawttvz29xqya8gx33fq5gce3vpkmfvt0dpzj6f9ykvts7rlcxvesqkrkz435sdwauj69dxcp9fg2x3fgen2wstlcc7t6p7mp07d6j2tvetvgzfqqx6ldxucxq0qn33rtsheh7umme6zhzmdpklg0dyhh49g9n28kpkup8zgc7fgj7n5fllufz9c3f3gh49qy2aqeqr9fg99hgyqvrrhqm7pw0e9mklv7v254r84424phxucl2gpx3hjgy5q8am38gwug025pxp95jyw99gcjt8yzqcyjc25mwf2ml9av39fa4998ktglduh30pvlcgqvqqqqqqqqqqpeq5luz4wutddrrc2whkaze0kkv757lqjlw7nly3ezl97dxufq9m5zpyfafhfhat3vv3k67z2gn9qyq2jlusmp92fyshvpff9v2p4ym2x90zy0v57jtc29edhh8ejf3depnrcqde3kdwew8sjxuvhj4hl65qq9xe5efg20gf5q2l0vc8yaaerzflelxa6j3sn2tmfszve8zh7rjq27xqn4g3lt93rt8pf9282z8eu49ekyukal64v8rtyd7y9gapktz42f5rf4agqcn0ym34mf3twh8eqqqqa8fq22"}}'; \ No newline at end of file +export const executeTransaction = + '{"type":"execute","id":"at1659war3z5t4wppr9h5rck3kpf5gmzf80xpud2hz8yuv3ds286u8s5lxh7c","execution":{"transitions":[{"id":"au17tn7sj8ywgg02melfh8mtsdrvr63z5k9gexagc478rejyp3w7q9qxfx7z6","program":"puzzle_arcade_coin_v001.aleo","function":"mint","inputs":[{"type":"public","id":"8270810521484911625112554982337338864935357860651826051684964871745393294042field","value":"aleo15sq744cm7dktgs73nn76e9dzj0pecyepe6qpjmeh05kylva4jg8qf9n9ge"},{"type":"public","id":"4575027690961186223652944521623745902600814120833621438807414620127639448668field","value":"1000000u64"}],"outputs":[{"type":"record","id":"6601842756832595700550356294123653730742948877912987042229716808083817596459field","checksum":"5062430203063356807847438506000770884656113117950337142360103911384470732056field","value":"record1qyqsq098e30xzmk3r99pzpmrcmrfh5tncewwzdgyx93yxsnq69fxz9sgqyrxzmt0w4h8ggcqqgqsq8ntqf37luu2nhaeas7qlq7gthug5kcmk2fcf3pm8uf875lz6ccfdywwf8g55p2lssye8ehwxtl8d76gynqaq7ml5s4dguxg3rs5wsyq52hq72"}],"tpk":"1607964622748471919440330478795163782437732099532604541664497619753994045319group","tcm":"847503920995797121862828111592398987072218963085962261207266349613895092180field","scm":"5713435921917019040572885748455116720616001991905041025594018041827076402171field"}],"global_state_root":"sr1uwx36xp95j7p2w7yadnj5ups6n8ktf0uwnvq0yauk2fefa2lsqysj4ydym","proof":"proof1qyqsqqqqqqqqqqqpqqqqqqqqqqqqwl66tmy9nzuz3ykg9xcpngmgfkxs6kf0zd2njt3vzdncge3uc5gqhz5aq92dwnr8ry9lu7fc8l5qqym0md7xkvk5qvltg2cmgc8lesfavxjyynp8m5sdwp9apq75vluvxxhqgnltywh43zp2r2anq5l5jqxenxqzp68crzjkfkllqy3pseguf7e4rtva4dccaacfgphq0x5ukk4lgzpdrhffh56sjjhrn7tfdkq42jwjrqv7pkectpnzc60u4ar0v2205m5hmcl24gcmr6q0we8fjupvs0xag4jt3wq2c26vpd7capspq3jvdylspluz6eufpx5q6gmnddx6uqm5g2f775r6p2uqe5t74s9tyqap7926nemq9s6qllm5waeszwush664wttpkc3r2xmflfy5r3d357nz0490nvmfn2pqacllmqnju9fd0m97jlld5smyvkyfwhtnsz62q7gtlx3ak257pep9jnyk5lugl5k7wt4mx6utxlp3l93vttfrjmc44sju32pfqnsthcnwp264dq8f0es2pz5yul64666kcue24ufeh7nxeyvjpulvta89jz6dkfzmrgnfgfgw65dft6seunhfhskr2gqyxlgc2jamcdsx4wnxggyn2vqlsezxac3rx56h9vw8mjrrpult5mmzacc26lv7zdpe3xg2fg22spgqsj5j66lzf3rkfhkz7ql0qfqhmw3dausfu0y677hpxthawy57ry89hag8yhdt8f8my7y0tlmyejvprgq69ana5eljw94lkksdud6scr0tr940urqftqywy2cym6yc6hff3aqlejz5ghtfdawetdcc84djqekg9400fz6h3l5l746a794dvtp8e6j8zl5z3xdggtrsgjwtg09sggz50h29qhwl56vdxv94j8gjewe8w5ctx2jmw5llnuttet4q0qqpqen24g39k7ln2d6w03zjk9zx566hn0mt3khskp7qk3g5lq5at5gnycuvgll02ak2fy0strcaurpwwplenjst3a25vleqlzdxxcyuyyxe5vl0lckd95dy6rss2sk0f7fw8hzvyefwg583veztqa2d9sg6p6sp8ra44f9rsc39z9zpztn0sqe5hkwtn2e0qdye2jzhchnwlm6svr2phqg3whpt3ej66nfzlj436f3suramv2sykw44gjh7q892qtcvqvqqqqqqqqqqpfcge0yyu6rvmktfslcvcmfm3mk4y3ngw03zxuam6qvclc09w87ufy6lym9wsu09734kx0d5f5tfqqqqucqz5ax7x8ddysek2kmu8cn3w6dqu8xf9tp2g9sew2vst530d705wlvv32pdqtnc8zjvtfmf725qqychc8kasqeft3575qq3v977syaak5j7qwkqux36umjt9xpgceuqalkhfgnnxd3sm4tflld9ef5hjcy9y0u7xy8xfm4hnctzvf23yedmu93ew9pcxrak8ssx40pc45zxqqqq94maf4"},"fee":{"transition":{"id":"au17ruxv9lgyc9r8ry72feq2dvukpyt0qmhp648qtkvxplnex7mmvzqvs7jf0","program":"credits.aleo","function":"fee_public","inputs":[{"type":"public","id":"2857075909811966273248114664812182277685354141121132361741553916429123320689field","value":"1449u64"},{"type":"public","id":"4095481960635608001176558539568862060430570697615096310772303148074416971125field","value":"0u64"},{"type":"public","id":"4331277641684651355445846833955413094644827521582315388405139541197216789244field","value":"1471442956713731556455575787580046168427032545671114373095796924821174474332field"}],"outputs":[{"type":"future","id":"1284488568170329868180693924120309631178056866257455108841198811483973130242field","value":"{\\n program_id: credits.aleo,\\n function_name: fee_public,\\n arguments: [\\n aleo193cgzzpr5lcwq6rmzq4l2ctg5f4mznead080mclfgrc0e5k0w5pstfdfps,\\n 1449u64\\n ]\\n}"}],"tpk":"1459551963926237749695253042528222267316827324542606050781185281459449486502group","tcm":"1960482517436921007100929744436748815928399310579329137811605688908348968736field","scm":"1613191919762957281003855895868519342839714495622392528901736670716120128919field"},"global_state_root":"sr1uwx36xp95j7p2w7yadnj5ups6n8ktf0uwnvq0yauk2fefa2lsqysj4ydym","proof":"proof1qyqsqqqqqqqqqqqpqqqqqqqqqqqws834tlcqydg43qy2qzfww25ppxvdzcuqmem54k6yhrd8lc492l6z50dl6a6s66pp7kscn8cazkvqq9yr9jymtyk9ucejv04myqhmczwy6feg4hyelzhqyehzpajvjmuvq7cn8v85shmy3mehmze4e9s6jqqhaxawal2cwt9talqkkkvp9w3dg80m8ddsq2w36z76jqas58j8ve6s4ghw3jxgxw77yjg3agft4cq8npaxa5pkkaurn9f4zdeswxhzxcr52qmusdv0p5jyht40e7ru3ljetuq2tdcv4dsp7gja0hm05k5pze5f75qhw26p3t0vmpcw8h6637qcxsfenenzjryunet5aprx8rtckdjapvt68amlwfskmtu9rzfqppfr0l08v2wqv5g23tz4rw4l64z85daym6mccws9qy8yf9tav24p3x02sn3dlcj83rgft0hnfs3fqr3kq7y4lecdfeqqfawq3xph3w3t3c8ghzskgrd5x98wj0dw4m55quj04s3a88tq5q2zykfws90xeq0xw3k0evz22qqaa706e9umam999fuqeks6zwstug5qqslh6j6hhmxzjypjxlv2wh5k60s9mxujnvqgx9hja9quft93x8qn93ju8008elj5rgdnrhefyrw5ytc2u3v4unrm2qphnn86vyq4wjjgvvq0ggcp0hss9msjs6fd9r2hq2gs0yy04fzse37cm8d470vv0gyhl5guvcr4z5lhjsm709twrmm3r7gw3zj88hyl4eqxzc8rllpcp2dymr92xz4y8yzjzwzdfu8cexmr4nvfs7ktupt2el92ht5w0a0als4da54ypexuvxz6egcvd0jafx9059khmml47lnq9qmjas9ya3m55eqndcgsmwa38kku806t97dx4hffeexg26eukn3ly7w90tff8za7kenpg7gr4ht2txppsa0ee9ceca3y6c9ze64gk56ry4gct70rkq57dyt9zgxa2urlz9tl4mzftaav0dsszqyn6xkymvqjvpzz6e6u8yt8tr89xrhnqlc0skvvyt34dvr9g2laav7d09szn0u9mzwf5f3mgul5zk5npdkj8rr35png2ju0lsz2jxuc07w9yawwnvr0hr27l8kmwzee6rqqdmuc5wam8xcmlw4gr8qjd753932vh4dcemh97v2lr63kjm48gmsqqvqqqqqqqqqqqy55a0dnshhzavcyuf5qpjd9v3wg32ygk6cd5vttk3pcg0capxcg725h3hmp6wl3nl8fma45aeu0qqqvldz4rwfpjvfp9g7wyzuwa6kvnsh5qptzht87q2ypmrzu0wa5wvxwasqvag00laxjnfwmnehykrcpqysuspnsh2t5qencjpqd0kyfvrvmw2z6unam0cvrrr0kzxcyvlypr8lqlt8des67rec0v4w2sur5gajd0n003qupz8sgqla6juv8rs0jlmsrhprypn2dwydp37wty8ycsqqq6rp8sd"}}'; +export const deployTransaction = + '{"type":"deploy","id":"at1v2n8krgmlmax4n0695s997uul6nd2f5l2shvmemukq067kacrg8qzzf5kt","owner":{"address":"aleo1mgjcqhnar38zvschjwxr830pwk57ywnce8rp7smf8c3yp2xexvgq40d4fx","signature":"sign1kxwcg0kdxeduag2wwaxzqv5s3kavm0sry88h4wlxaa9g52f0ggqj0gk7uwxkjpjyawkknfp97sjfa8kgzdjwh6ns3ju7t5w8xzc9wqa6xq3s6sjztquzwulp903h8ez9wgystuw4ex0fp7dhjahh6l7vzqp5t5sent5yawr9056h0su4gperdp9nljk0vhsq43jsu7rh2a3qc4vwp3r"},"deployment":{"edition":0,"program":"import credits.aleo;\\n\\nprogram token_registry.aleo;\\n\\nrecord Token:\\n owner as address.private;\\n amount as u128.private;\\n token_id as field.private;\\n external_authorization_required as boolean.private;\\n authorized_until as u32.private;\\n\\nstruct TokenMetadata:\\n token_id as field;\\n name as u128;\\n symbol as u128;\\n decimals as u8;\\n supply as u128;\\n max_supply as u128;\\n admin as address;\\n external_authorization_required as boolean;\\n external_authorization_party as address;\\n\\nstruct TokenOwner:\\n account as address;\\n token_id as field;\\n\\nstruct Balance:\\n token_id as field;\\n account as address;\\n balance as u128;\\n authorized_until as u32;\\n\\nstruct Allowance:\\n account as address;\\n spender as address;\\n token_id as field;\\n\\nmapping registered_tokens:\\n key as field.public;\\n value as TokenMetadata.public;\\n\\nmapping balances:\\n key as field.public;\\n value as Balance.public;\\n\\nmapping authorized_balances:\\n key as field.public;\\n value as Balance.public;\\n\\nmapping allowances:\\n key as field.public;\\n value as u128.public;\\n\\nmapping roles:\\n key as field.public;\\n value as u8.public;\\n\\nfunction transfer_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.caller r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n cast r1 r0 into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async transfer_public r0 r1 r2 self.caller r4 r6 into r7;\\n output r7 as token_registry.aleo/transfer_public.future;\\n\\nfinalize transfer_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as address.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n get authorized_balances[r4] into r6;\\n get registered_tokens[r0] into r7;\\n lte block.height r6.authorized_until into r8;\\n not r7.external_authorization_required into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n sub r6.balance r2 into r11;\\n cast r0 r3 r11 r6.authorized_until into r12 as Balance;\\n set r12 into authorized_balances[r4];\\n get registered_tokens[r0] into r13;\\n ternary r13.external_authorization_required 0u32 4294967295u32 into r14;\\n cast r0 r1 0u128 r14 into r15 as Balance;\\n get.or_use balances[r5] r15 into r16;\\n get.or_use authorized_balances[r5] r15 into r17;\\n ternary r13.external_authorization_required r16.token_id r17.token_id into r18;\\n ternary r13.external_authorization_required r16.account r17.account into r19;\\n ternary r13.external_authorization_required r16.balance r17.balance into r20;\\n ternary r13.external_authorization_required r16.authorized_until r17.authorized_until into r21;\\n cast r18 r19 r20 r21 into r22 as Balance;\\n add r22.balance r2 into r23;\\n cast r0 r1 r23 r22.authorized_until into r24 as Balance;\\n branch.eq r13.external_authorization_required false to end_then_00;\\n set r24 into balances[r5];\\n branch.eq true true to end_otherwise_01;\\n position end_then_00;\\n set r24 into authorized_balances[r5];\\n position end_otherwise_01;\\n\\nfunction transfer_public_as_signer:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.signer r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n cast r1 r0 into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async transfer_public_as_signer r0 r1 r2 self.signer r4 r6 into r7;\\n output r7 as token_registry.aleo/transfer_public_as_signer.future;\\n\\nfinalize transfer_public_as_signer:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as address.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n get authorized_balances[r4] into r6;\\n get registered_tokens[r0] into r7;\\n lte block.height r6.authorized_until into r8;\\n not r7.external_authorization_required into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n sub r6.balance r2 into r11;\\n cast r0 r3 r11 r6.authorized_until into r12 as Balance;\\n set r12 into authorized_balances[r4];\\n get registered_tokens[r0] into r13;\\n ternary r13.external_authorization_required 0u32 4294967295u32 into r14;\\n cast r0 r1 0u128 r14 into r15 as Balance;\\n get.or_use balances[r5] r15 into r16;\\n get.or_use authorized_balances[r5] r15 into r17;\\n ternary r13.external_authorization_required r16.token_id r17.token_id into r18;\\n ternary r13.external_authorization_required r16.account r17.account into r19;\\n ternary r13.external_authorization_required r16.balance r17.balance into r20;\\n ternary r13.external_authorization_required r16.authorized_until r17.authorized_until into r21;\\n cast r18 r19 r20 r21 into r22 as Balance;\\n add r22.balance r2 into r23;\\n cast r0 r1 r23 r22.authorized_until into r24 as Balance;\\n branch.eq r13.external_authorization_required false to end_then_02;\\n set r24 into balances[r5];\\n branch.eq true true to end_otherwise_03;\\n position end_then_02;\\n set r24 into authorized_balances[r5];\\n position end_otherwise_03;\\n\\nfunction transfer_private:\\n input r0 as address.private;\\n input r1 as u128.private;\\n input r2 as Token.record;\\n sub r2.amount r1 into r3;\\n cast r2.owner r3 r2.token_id r2.external_authorization_required r2.authorized_until into r4 as Token.record;\\n ternary r2.external_authorization_required 0u32 4294967295u32 into r5;\\n cast r0 r1 r2.token_id r2.external_authorization_required r5 into r6 as Token.record;\\n async transfer_private r2.external_authorization_required r2.authorized_until into r7;\\n output r4 as Token.record;\\n output r6 as Token.record;\\n output r7 as token_registry.aleo/transfer_private.future;\\n\\nfinalize transfer_private:\\n input r0 as boolean.public;\\n input r1 as u32.public;\\n lte block.height r1 into r2;\\n not r0 into r3;\\n or r2 r3 into r4;\\n assert.eq r4 true ;\\n\\nfunction transfer_private_to_public:\\n input r0 as address.public;\\n input r1 as u128.public;\\n input r2 as Token.record;\\n sub r2.amount r1 into r3;\\n cast r2.owner r3 r2.token_id r2.external_authorization_required r2.authorized_until into r4 as Token.record;\\n cast r0 r2.token_id into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async transfer_private_to_public r2.token_id r0 r1 r2.authorized_until r2.external_authorization_required r6 into r7;\\n output r4 as Token.record;\\n output r7 as token_registry.aleo/transfer_private_to_public.future;\\n\\nfinalize transfer_private_to_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as u32.public;\\n input r4 as boolean.public;\\n input r5 as field.public;\\n lte block.height r3 into r6;\\n not r4 into r7;\\n or r6 r7 into r8;\\n assert.eq r8 true ;\\n get registered_tokens[r0] into r9;\\n ternary r9.external_authorization_required 0u32 4294967295u32 into r10;\\n cast r0 r1 0u128 r10 into r11 as Balance;\\n get.or_use balances[r5] r11 into r12;\\n get.or_use authorized_balances[r5] r11 into r13;\\n ternary r9.external_authorization_required r12.token_id r13.token_id into r14;\\n ternary r9.external_authorization_required r12.account r13.account into r15;\\n ternary r9.external_authorization_required r12.balance r13.balance into r16;\\n ternary r9.external_authorization_required r12.authorized_until r13.authorized_until into r17;\\n cast r14 r15 r16 r17 into r18 as Balance;\\n add r18.balance r2 into r19;\\n cast r0 r1 r19 r18.authorized_until into r20 as Balance;\\n branch.eq r9.external_authorization_required false to end_then_04;\\n set r20 into balances[r5];\\n branch.eq true true to end_otherwise_05;\\n position end_then_04;\\n set r20 into authorized_balances[r5];\\n position end_otherwise_05;\\n\\nfunction transfer_public_to_private:\\n input r0 as field.public;\\n input r1 as address.private;\\n input r2 as u128.public;\\n input r3 as boolean.public;\\n ternary r3 0u32 4294967295u32 into r4;\\n cast r1 r2 r0 r3 r4 into r5 as Token.record;\\n cast self.caller r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n async transfer_public_to_private r0 r2 self.caller r3 r7 into r8;\\n output r5 as Token.record;\\n output r8 as token_registry.aleo/transfer_public_to_private.future;\\n\\nfinalize transfer_public_to_private:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as address.public;\\n input r3 as boolean.public;\\n input r4 as field.public;\\n get registered_tokens[r0] into r5;\\n assert.eq r5.external_authorization_required r3 ;\\n get authorized_balances[r4] into r6;\\n get registered_tokens[r0] into r7;\\n lte block.height r6.authorized_until into r8;\\n not r7.external_authorization_required into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n sub r6.balance r1 into r11;\\n cast r0 r2 r11 r6.authorized_until into r12 as Balance;\\n set r12 into authorized_balances[r4];\\n\\nfunction join:\\n input r0 as Token.record;\\n input r1 as Token.record;\\n is.eq r0.token_id r1.token_id into r2;\\n assert.eq r2 true ;\\n add r0.amount r1.amount into r3;\\n lt r0.authorized_until r1.authorized_until into r4;\\n ternary r4 r0.authorized_until r1.authorized_until into r5;\\n cast r0.owner r3 r0.token_id r0.external_authorization_required r5 into r6 as Token.record;\\n output r6 as Token.record;\\n\\nfunction split:\\n input r0 as Token.record;\\n input r1 as u128.private;\\n gte r0.amount r1 into r2;\\n assert.eq r2 true ;\\n cast r0.owner r1 r0.token_id r0.external_authorization_required r0.authorized_until into r3 as Token.record;\\n sub r0.amount r1 into r4;\\n cast r0.owner r4 r0.token_id r0.external_authorization_required r0.authorized_until into r5 as Token.record;\\n output r3 as Token.record;\\n output r5 as Token.record;\\n\\nfunction initialize:\\n async initialize into r0;\\n output r0 as token_registry.aleo/initialize.future;\\n\\nfinalize initialize:\\n contains registered_tokens[3443843282313283355522573239085696902919850365217539366784739393210722344986field] into r0;\\n assert.eq r0 false ;\\n cast 3443843282313283355522573239085696902919850365217539366784739393210722344986field 1095517519u128 1095517519u128 6u8 0u128 10000000000000000u128 wrapped_credits.aleo false token_registry.aleo into r1 as TokenMetadata;\\n set r1 into registered_tokens[3443843282313283355522573239085696902919850365217539366784739393210722344986field];\\n\\nfunction register_token:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as u128.public;\\n input r3 as u8.public;\\n input r4 as u128.public;\\n input r5 as boolean.public;\\n input r6 as address.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r7;\\n assert.eq r7 true ;\\n cast r0 r1 r2 r3 0u128 r4 self.caller r5 r6 into r8 as TokenMetadata;\\n async register_token r8 into r9;\\n output r9 as token_registry.aleo/register_token.future;\\n\\nfinalize register_token:\\n input r0 as TokenMetadata.public;\\n contains registered_tokens[r0.token_id] into r1;\\n assert.eq r1 false ;\\n set r0 into registered_tokens[r0.token_id];\\n\\nfunction update_token_management:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r3;\\n assert.eq r3 true ;\\n async update_token_management r0 r1 r2 self.caller into r4;\\n output r4 as token_registry.aleo/update_token_management.future;\\n\\nfinalize update_token_management:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n input r3 as address.public;\\n get registered_tokens[r0] into r4;\\n assert.eq r3 r4.admin ;\\n cast r0 r4.name r4.symbol r4.decimals r4.supply r4.max_supply r1 r4.external_authorization_required r2 into r5 as TokenMetadata;\\n set r5 into registered_tokens[r0];\\n\\nfunction set_role:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u8.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r3;\\n assert.eq r3 true ;\\n is.eq r2 1u8 into r4;\\n is.eq r2 2u8 into r5;\\n or r4 r5 into r6;\\n is.eq r2 3u8 into r7;\\n or r6 r7 into r8;\\n assert.eq r8 true ;\\n cast r1 r0 into r9 as TokenOwner;\\n hash.bhp256 r9 into r10 as field;\\n async set_role r0 r2 self.caller r10 into r11;\\n output r11 as token_registry.aleo/set_role.future;\\n\\nfinalize set_role:\\n input r0 as field.public;\\n input r1 as u8.public;\\n input r2 as address.public;\\n input r3 as field.public;\\n get registered_tokens[r0] into r4;\\n assert.eq r2 r4.admin ;\\n set r1 into roles[r3];\\n\\nfunction remove_role:\\n input r0 as field.public;\\n input r1 as address.public;\\n is.neq r0 3443843282313283355522573239085696902919850365217539366784739393210722344986field into r2;\\n assert.eq r2 true ;\\n cast r1 r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n async remove_role r0 self.caller r4 into r5;\\n output r5 as token_registry.aleo/remove_role.future;\\n\\nfinalize remove_role:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as field.public;\\n get registered_tokens[r0] into r3;\\n assert.eq r1 r3.admin ;\\n remove roles[r2];\\n\\nfunction mint_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as u32.public;\\n cast self.caller r0 into r4 as TokenOwner;\\n hash.bhp256 r4 into r5 as field;\\n cast r1 r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n async mint_public r0 r1 r2 r3 self.caller r5 r7 into r8;\\n output r8 as token_registry.aleo/mint_public.future;\\n\\nfinalize mint_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as u32.public;\\n input r4 as address.public;\\n input r5 as field.public;\\n input r6 as field.public;\\n get registered_tokens[r0] into r7;\\n is.eq r4 r7.admin into r8;\\n not r8 into r9;\\n branch.eq r9 false to end_then_06;\\n get roles[r5] into r10;\\n is.eq r10 1u8 into r11;\\n is.eq r10 3u8 into r12;\\n or r11 r12 into r13;\\n assert.eq r13 true ;\\n branch.eq true true to end_otherwise_07;\\n position end_then_06;\\n position end_otherwise_07;\\n add r7.supply r2 into r14;\\n lte r14 r7.max_supply into r15;\\n assert.eq r15 true ;\\n cast r0 r1 0u128 r3 into r16 as Balance;\\n get.or_use balances[r6] r16 into r17;\\n get.or_use authorized_balances[r6] r16 into r18;\\n ternary r7.external_authorization_required r17.token_id r18.token_id into r19;\\n ternary r7.external_authorization_required r17.account r18.account into r20;\\n ternary r7.external_authorization_required r17.balance r18.balance into r21;\\n ternary r7.external_authorization_required r17.authorized_until r18.authorized_until into r22;\\n cast r19 r20 r21 r22 into r23 as Balance;\\n add r23.balance r2 into r24;\\n cast r0 r1 r24 r23.authorized_until into r25 as Balance;\\n branch.eq r7.external_authorization_required false to end_then_08;\\n set r25 into balances[r6];\\n branch.eq true true to end_otherwise_09;\\n position end_then_08;\\n set r25 into authorized_balances[r6];\\n position end_otherwise_09;\\n cast r0 r7.name r7.symbol r7.decimals r14 r7.max_supply r7.admin r7.external_authorization_required r7.external_authorization_party into r26 as TokenMetadata;\\n set r26 into registered_tokens[r0];\\n\\nfunction mint_private:\\n input r0 as field.public;\\n input r1 as address.private;\\n input r2 as u128.public;\\n input r3 as boolean.public;\\n input r4 as u32.public;\\n cast r1 r2 r0 r3 r4 into r5 as Token.record;\\n cast self.caller r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n async mint_private r0 r2 r3 r4 self.caller r7 into r8;\\n output r5 as Token.record;\\n output r8 as token_registry.aleo/mint_private.future;\\n\\nfinalize mint_private:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as boolean.public;\\n input r3 as u32.public;\\n input r4 as address.public;\\n input r5 as field.public;\\n get registered_tokens[r0] into r6;\\n is.eq r4 r6.admin into r7;\\n not r7 into r8;\\n branch.eq r8 false to end_then_010;\\n get roles[r5] into r9;\\n is.eq r9 1u8 into r10;\\n is.eq r9 3u8 into r11;\\n or r10 r11 into r12;\\n assert.eq r12 true ;\\n branch.eq true true to end_otherwise_011;\\n position end_then_010;\\n position end_otherwise_011;\\n add r6.supply r1 into r13;\\n lte r13 r6.max_supply into r14;\\n assert.eq r14 true ;\\n assert.eq r6.external_authorization_required r2 ;\\n is.eq r3 0u32 into r15;\\n not r6.external_authorization_required into r16;\\n or r15 r16 into r17;\\n assert.eq r17 true ;\\n cast r0 r6.name r6.symbol r6.decimals r13 r6.max_supply r6.admin r6.external_authorization_required r6.external_authorization_party into r18 as TokenMetadata;\\n set r18 into registered_tokens[r0];\\n\\nfunction burn_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast r1 r0 into r3 as TokenOwner;\\n hash.bhp256 r3 into r4 as field;\\n cast self.caller r0 into r5 as TokenOwner;\\n hash.bhp256 r5 into r6 as field;\\n async burn_public r3 r2 self.caller r6 r4 into r7;\\n output r7 as token_registry.aleo/burn_public.future;\\n\\nfinalize burn_public:\\n input r0 as TokenOwner.public;\\n input r1 as u128.public;\\n input r2 as address.public;\\n input r3 as field.public;\\n input r4 as field.public;\\n get registered_tokens[r0.token_id] into r5;\\n is.neq r2 r5.admin into r6;\\n branch.eq r6 false to end_then_012;\\n get roles[r3] into r7;\\n is.eq r7 2u8 into r8;\\n is.eq r7 3u8 into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n branch.eq true true to end_otherwise_013;\\n position end_then_012;\\n position end_otherwise_013;\\n sub r5.supply r1 into r11;\\n cast r5.token_id r5.name r5.symbol r5.decimals r11 r5.max_supply r5.admin r5.external_authorization_required r5.external_authorization_party into r12 as TokenMetadata;\\n set r12 into registered_tokens[r0.token_id];\\n cast r0.token_id r0.account 0u128 0u32 into r13 as Balance;\\n get.or_use authorized_balances[r4] r13 into r14;\\n gte r14.balance 0u128 into r15;\\n branch.eq r15 false to end_then_014;\\n gt r14.balance r1 into r16;\\n branch.eq r16 false to end_then_116;\\n sub r14.balance r1 into r17;\\n cast r0.token_id r0.account r17 r14.authorized_until into r18 as Balance;\\n set r18 into authorized_balances[r4];\\n branch.eq true true to end_otherwise_117;\\n position end_then_116;\\n cast r0.token_id r0.account 0u128 r14.authorized_until into r19 as Balance;\\n set r19 into authorized_balances[r4];\\n sub r1 r14.balance into r20;\\n is.eq r20 0u128 into r21;\\n branch.eq r21 false to end_then_218;\\n branch.eq true true to end_otherwise_219;\\n position end_then_218;\\n get balances[r4] into r22;\\n sub r22.balance r20 into r23;\\n cast r0.token_id r0.account r23 r22.authorized_until into r24 as Balance;\\n set r24 into balances[r4];\\n position end_otherwise_219;\\n position end_otherwise_117;\\n branch.eq true true to end_otherwise_015;\\n position end_then_014;\\n get balances[r4] into r25;\\n sub r25.balance r1 into r26;\\n cast r0.token_id r0.account r26 r25.authorized_until into r27 as Balance;\\n set r27 into balances[r4];\\n position end_otherwise_015;\\n\\nfunction burn_private:\\n input r0 as Token.record;\\n input r1 as u128.public;\\n sub r0.amount r1 into r2;\\n cast r0.owner r2 r0.token_id r0.external_authorization_required r0.authorized_until into r3 as Token.record;\\n cast self.caller r0.token_id into r4 as TokenOwner;\\n hash.bhp256 r4 into r5 as field;\\n async burn_private r0.token_id r1 self.caller r5 into r6;\\n output r3 as Token.record;\\n output r6 as token_registry.aleo/burn_private.future;\\n\\nfinalize burn_private:\\n input r0 as field.public;\\n input r1 as u128.public;\\n input r2 as address.public;\\n input r3 as field.public;\\n get registered_tokens[r0] into r4;\\n is.eq r2 r4.admin into r5;\\n not r5 into r6;\\n branch.eq r6 false to end_then_020;\\n get roles[r3] into r7;\\n is.eq r7 2u8 into r8;\\n is.eq r7 3u8 into r9;\\n or r8 r9 into r10;\\n assert.eq r10 true ;\\n branch.eq true true to end_otherwise_021;\\n position end_then_020;\\n position end_otherwise_021;\\n sub r4.supply r1 into r11;\\n cast r0 r4.name r4.symbol r4.decimals r11 r4.max_supply r4.admin r4.external_authorization_required r4.external_authorization_party into r12 as TokenMetadata;\\n set r12 into registered_tokens[r0];\\n\\nfunction prehook_public:\\n input r0 as TokenOwner.public;\\n input r1 as u128.public;\\n input r2 as u32.public;\\n hash.bhp256 r0 into r3 as field;\\n async prehook_public r0 r1 r2 self.caller r3 into r4;\\n output r4 as token_registry.aleo/prehook_public.future;\\n\\nfinalize prehook_public:\\n input r0 as TokenOwner.public;\\n input r1 as u128.public;\\n input r2 as u32.public;\\n input r3 as address.public;\\n input r4 as field.public;\\n get registered_tokens[r0.token_id] into r5;\\n assert.eq r5.external_authorization_required true ;\\n is.eq r3 r5.external_authorization_party into r6;\\n assert.eq r6 true ;\\n cast r0.token_id r0.account 0u128 0u32 into r7 as Balance;\\n get.or_use balances[r4] r7 into r8;\\n get.or_use authorized_balances[r4] r7 into r9;\\n lt r9.authorized_until block.height into r10;\\n add r8.balance r9.balance into r11;\\n ternary r10 r11 r8.balance into r12;\\n ternary r10 0u128 r9.balance into r13;\\n sub r12 r1 into r14;\\n add r13 r1 into r15;\\n cast r0.token_id r0.account r15 r2 into r16 as Balance;\\n set r16 into authorized_balances[r4];\\n cast r0.token_id r0.account r14 r8.authorized_until into r17 as Balance;\\n set r17 into balances[r4];\\n\\nfunction prehook_private:\\n input r0 as Token.record;\\n input r1 as u128.private;\\n input r2 as u32.private;\\n sub r0.amount r1 into r3;\\n cast r0.owner r3 r0.token_id r0.external_authorization_required r0.authorized_until into r4 as Token.record;\\n cast r0.owner r1 r0.token_id r0.external_authorization_required r2 into r5 as Token.record;\\n async prehook_private r0.token_id self.caller into r6;\\n output r4 as Token.record;\\n output r5 as Token.record;\\n output r6 as token_registry.aleo/prehook_private.future;\\n\\nfinalize prehook_private:\\n input r0 as field.public;\\n input r1 as address.public;\\n get registered_tokens[r0] into r2;\\n is.eq r1 r2.external_authorization_party into r3;\\n assert.eq r3 true ;\\n\\nfunction approve_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.caller r1 r0 into r3 as Allowance;\\n hash.bhp256 r3 into r4 as field;\\n async approve_public r2 r4 into r5;\\n output r5 as token_registry.aleo/approve_public.future;\\n\\nfinalize approve_public:\\n input r0 as u128.public;\\n input r1 as field.public;\\n get.or_use allowances[r1] 0u128 into r2;\\n add r2 r0 into r3;\\n set r3 into allowances[r1];\\n\\nfunction unapprove_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n cast self.caller r1 r0 into r3 as Allowance;\\n hash.bhp256 r3 into r4 as field;\\n async unapprove_public r2 r4 into r5;\\n output r5 as token_registry.aleo/unapprove_public.future;\\n\\nfinalize unapprove_public:\\n input r0 as u128.public;\\n input r1 as field.public;\\n get allowances[r1] into r2;\\n sub r2 r0 into r3;\\n set r3 into allowances[r1];\\n\\nfunction transfer_from_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n input r3 as u128.public;\\n cast r1 self.caller r0 into r4 as Allowance;\\n hash.bhp256 r4 into r5 as field;\\n cast r1 r0 into r6 as TokenOwner;\\n hash.bhp256 r6 into r7 as field;\\n cast r2 r0 into r8 as TokenOwner;\\n hash.bhp256 r8 into r9 as field;\\n async transfer_from_public r0 r1 r2 r3 r5 r7 r9 into r10;\\n output r10 as token_registry.aleo/transfer_from_public.future;\\n\\nfinalize transfer_from_public:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.public;\\n input r3 as u128.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n input r6 as field.public;\\n get allowances[r4] into r7;\\n sub r7 r3 into r8;\\n set r8 into allowances[r4];\\n get authorized_balances[r5] into r9;\\n get registered_tokens[r0] into r10;\\n lte block.height r9.authorized_until into r11;\\n not r10.external_authorization_required into r12;\\n or r11 r12 into r13;\\n assert.eq r13 true ;\\n sub r9.balance r3 into r14;\\n cast r0 r1 r14 r9.authorized_until into r15 as Balance;\\n set r15 into authorized_balances[r5];\\n get registered_tokens[r0] into r16;\\n ternary r16.external_authorization_required 0u32 4294967295u32 into r17;\\n cast r0 r2 0u128 r17 into r18 as Balance;\\n get.or_use balances[r6] r18 into r19;\\n get.or_use authorized_balances[r6] r18 into r20;\\n ternary r16.external_authorization_required r19.token_id r20.token_id into r21;\\n ternary r16.external_authorization_required r19.account r20.account into r22;\\n ternary r16.external_authorization_required r19.balance r20.balance into r23;\\n ternary r16.external_authorization_required r19.authorized_until r20.authorized_until into r24;\\n cast r21 r22 r23 r24 into r25 as Balance;\\n add r25.balance r3 into r26;\\n cast r0 r2 r26 r25.authorized_until into r27 as Balance;\\n branch.eq r16.external_authorization_required false to end_then_022;\\n set r27 into balances[r6];\\n branch.eq true true to end_otherwise_023;\\n position end_then_022;\\n set r27 into authorized_balances[r6];\\n position end_otherwise_023;\\n\\nfunction transfer_from_public_to_private:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as address.private;\\n input r3 as u128.public;\\n input r4 as boolean.public;\\n ternary r4 0u32 4294967295u32 into r5;\\n cast r2 r3 r0 r4 r5 into r6 as Token.record;\\n cast r1 self.caller r0 into r7 as Allowance;\\n hash.bhp256 r7 into r8 as field;\\n cast r1 r0 into r9 as TokenOwner;\\n hash.bhp256 r9 into r10 as field;\\n async transfer_from_public_to_private r0 r1 r3 r4 r8 r10 into r11;\\n output r6 as Token.record;\\n output r11 as token_registry.aleo/transfer_from_public_to_private.future;\\n\\nfinalize transfer_from_public_to_private:\\n input r0 as field.public;\\n input r1 as address.public;\\n input r2 as u128.public;\\n input r3 as boolean.public;\\n input r4 as field.public;\\n input r5 as field.public;\\n get registered_tokens[r0] into r6;\\n assert.eq r6.external_authorization_required r3 ;\\n get allowances[r4] into r7;\\n sub r7 r2 into r8;\\n set r8 into allowances[r4];\\n get authorized_balances[r5] into r9;\\n get registered_tokens[r0] into r10;\\n lte block.height r9.authorized_until into r11;\\n not r10.external_authorization_required into r12;\\n or r11 r12 into r13;\\n assert.eq r13 true ;\\n sub r9.balance r2 into r14;\\n cast r0 r1 r14 r9.authorized_until into r15 as Balance;\\n set r15 into authorized_balances[r5];\\n","verifying_keys":[["transfer_public",["verifier1qygqqqqqqqqqqqr32gqqqqqqqqq825sqqqqqqqqqcmzqqqqqqqqqpkhqqqqqqqqqqrj8kqqqqqqqqqqvqqqqqqqqqqqpv0cxuwgvvr8zu0qvq5t6vj7vssv3aaj2r7sfehtvwe33nddup7jmaeu2pkv6zll656y579tv5wupknlzdum3pp7zjqd45wprdtswvcs3q06dngn4l4u253c3zwpn5y8rjt0yt5xk2m49c2f9we3zzwssq5676m26nnajgntrlejh5a5yme2l4rq870ltqyysh6ks3tjqxqgfy953rvxck3unay2yqxekgwt0qxmfrmnuc6c7850hfrhecl63z5d7r6c8f9fn5xzny0natgq0rtmgyhlysx78aq6ggdpp3ej9v0e2qqf8w4yf4smjrpdcnd5wjhmv94gzl0t3g88wsannkw0jk3drj24df8msdls0frj0ujygqsq9k55j7qqwla30x9rglr3lkyfrn26qu6q9x2ms0jp4anuacx8uyx35pam6f53tcru86h6vv58rhrxft69ugrvqx5pghc6l6sn9eypx69gerwtk6ljxe835kfrl7x9qqwwxnvxqg8lksm82ahfph86xc3z5n230d7scqjw5vqcn6s3zzy6ac865vm2sg2m9dyafrhtf0ujpsm0dcur953g2ux8rhvsthe5k2rnysf2d54l4qqc3lm2nfxy2zlwaqy447fnexle4jdq35096chtjy3lkwshmqx37mwn7dxl8ljm88xns6te4k9nxmqygaeyadav74q5xg8vrcq36dpqssnxu3h6lfh8hslxhnxkcwmjxt9mygu58mrj7p7ttzuuj5gh02sqr0m3ynltleefn2z77588akxm9gew5tcpmvqcnu464g0p2007d9cae262wdsyha0anlnr7vzx5p6gqwsavgmvkf0jh70qsnpyqasgzfjw3rax2z630zp7ez6qmrhxeemdjvwrekaa02uwj6xapz28reqjcr844ctmayahcs4r4h4af6eevv5pxj2jqete3gnxlmkvv3mu4q6fhhdkqqqqqqqqqqwmvgg5","certificate1qyqsqqqqqqqqqqqhucsq3ysfuyptwl5zkh5j65z5cu05pskmrm452vzwl505xndn4m6gz3hn3m5utfxyhsprrqkqlwqqqvn7tlv"]],["transfer_public_as_signer",["verifier1qygqqqqqqqqqqqr32gqqqqqqqqq825sqqqqqqqqqcmzqqqqqqqqqpkhqqqqqqqqqqrj8kqqqqqqqqqqvqqqqqqqqqqqt8c0jfjvcgv3lw2xfud6ms9q8d6jvcgw5p0mkx9qhfwe7rmc5tsgrgl5987zryrtr0sx4azedj6gpr6jrdk64u7pwjr4myfg0ruj8c8h8n85cfkuegwunx9h9jf5mntj73z5v83m8t2ercewgj2ec696gq5676m26nnajgntrlejh5a5yme2l4rq870ltqyysh6ks3tjqxqgfy953rvxck3unay2yqxekgwt0q86gq60gha7f55j4s5czl7fjv5dqdckuzn4659xq3jacyugv2cf0l9vsksxzv52gqe87wcev6dh44qq9xtmjr3jde9h7nxu8jn4xzavrsvgpn7s5x23uh2xm854xp54ptyrg6438wykpsu4eplep5xqrz7qwla30x9rglr3lkyfrn26qu6q9x2ms0jp4anuacx8uyx35pam6f53tcru86h6vv58rhrxft69ugrvq3awjwmjuxy9px3tamr78dvlwckw8edy8rf2fj7kpp7wffcjnzcwvk3ur2swkg99r2k7huy9slvxgp3qqmkqqu0g39c95dn44fxhn8jn7p5whsumcxvg0g8dvucx2v3vvjqf4lgtlvv8rdqq6dkqanzt4syc3lm2nfxy2zlwaqy447fnexle4jdq35096chtjy3lkwshmqx37mwn7dxl8ljm88xns6te4k9nxmqxmunw3kz9py8x27v4hthwlkgplwk5yauqnlnypexpj0xdkjpy0qwxfznsvmrxm54l6czvzpy74xkqvgemrmuahfrq9aqtdmvytac93hh9kfeu9asm3k5qzeyqc3755ckez9ukuccwkp0zw82lsa4qqfj5qwsavgmvkf0jh70qsnpyqasgzfjw3rax2z630zp7ez6qmrhxeemdjvwrekaa02uwj6xapz28reqjczdu8kgsxyf30huwr68y2l5xhaxh96xrueuvf0dh4wm8ug58r7vyujdhqqqqqqqqqql4fskz","certificate1qyqsqqqqqqqqqqxwrxuq4t6m0z49pvkyx00ly5yw8jnwzt7q3cvsz6et42snm6uy4p6ncfvz3egcmvdcwnsdnev4hxqqq7xertv"]],["transfer_private",["verifier1qysqqqqqqqqqqqrupcqsqqqqqqq2srspqqqqqqqqnqnqyqqqqqqqpjqcqgqqqqqqqzztyqgqqqqqqqqvqqqqqqqqqqqz6rmtnyay2pspz49msr634pu9pa6s9vr9zd87gd88228nrpx2cu7qhcpyh08ruwa4tc9qj8agm6vqzcevsp9yqqffzdc6sfenfuy6u3cdx2kvhw0673w33ut9pv62k0zewymds7zhzw839lpr87k06x2gzhwdq9ve3swq2qg5k62znqnapwyelr4lm5mwn28u0qy8xzau4s6cs8nqnnhhy7u9d6tzdzzglmvwszm7sk7hvdwqjf4yy9l8y9p4ulc9vm74stln8r2xna48wyw5xve2wmktqyxweka97apg8gnjhe6kxq2vpmvw4gakzcqarr9dh609ky3q9h9mvmdz5pd6yzu2yx63r4ssplh5lwk26sdluhcwvqgud5jad2q462vdgfxtasu79ccs0unkw9msck7vtnqnckl9l06tz56get9r7swalxstzm222yvcxuw8v8gs0yuplu0q72thvkcg5dspq520922vlxfnfddvdjhdxnsgwq3avmx0es9tdhlmsgq0smhwgedvjyne6gzgqaa3ds8y79unxytlf8ddu9p8p5n6tffgyufu3xezq457ph4zrkxtplx95wrv90kk5gwut3me3e65qz0cvcds0w256ny46j88pz664lyt0mgd555zgks2q39nhtv78gsdvsux36mva74s8ffrynuar4uxhqy06pnp5m9c96svxwd72zfhczsuusr779equhz8xdcvjpj7yqzp75cd00d45kn8e8cferam6f2cgkquvcysk6443qftkp8qqegytn8ydrlw8yl006u3sv2ah02x32hvatl2phxhk5m9a7k7fh7metxd3fqqn0gxc4j06s20tt46qq2jrqhs0499zqxsl0zetqnft7rd2zqkvu9lyd4ejp8lucpud40weuqkyavgztf0y82wr5g80uj3jah435uw49ktnv3ca3kwvmusuw2phqh6tqv9de0szqqqqqqqqqvrtll","certificate1qyqsqqqqqqqqqqzlvnda2my8rmz5fren33ldcfmaml0584etsyywt8ne66lcwggyf7myezkwjmqumcz5xuw4tdp8yqqqquykepd"]],["transfer_private_to_public",["verifier1qygqqqqqqqqqqqzqevqqqqqqqqqx0jcqqqqqqqqqwzwqzqqqqqqqqk5eqyqqqqqqqpxyyqgqqqqqqqqvqqqqqqqqqqq84l423cf8a8yeujypax58e0eugaf2qvxcap0xarlfmz9yvvxya46a4sc6dluv0en36zf7hs7v7xvqccdrw5lp53hf3jq96wg4gpdyev5yse6643dlf653n26489v0ssczl5f3q2dps8w2u43s3rhcwj4qzltf6575k7gxvkv8hva6yl7x0nw3jd4kfa8w9gmd94uwmphl2dgx3rf88ngyaxkfsccmy4n4jeqzsymknlqra0ptxgu0h8uue0plm7l3tf8hs7l3rf634lqd3kzjm0ykkcqhf8j29p4za6f8fnep2c8zcq03v58rmdku6xs2fes4ahmdjtfn5jj8kvzjy4x0lkyn95dym2n3y2naesy3jcg2u2srw6hfecq7pyqr0tg356uu7gsdd8cshfawurg8akha0kmq3hgq96ycc4j2alhzpd7x4fz88fsgn5lzjv4hlal5jegpyyc3hf9wwcf84yjrjcjh8304q7e9cqld0qvuvyteqr9r7s0n7h5eq296mj6unykd98zqps0dmmjcpqn9lumy4w9s9kgck3nwys9s7nc5ku449x0gvzw0hnz27gttcvkxnkc74u6z904g6w65n6x39wuvqypc76rz2jn7knqn630p8jtlrgjk7vazxhqcg7gc9sekc9a3frzg5aeu5lsulssdkxcn0ruj9lfltqxl92qau7y9u6rawxlh33apapvrg7fq30jwqxgyz9ng0snqrkwuvvllyvpf67e4xyk8s46xjjdf05q9jv25aarwpzs2dnaa2qjxvsnsnunvd04zmhnpaedtn7rpuy77sa94usm603u0539utf0wl0pc6cvqdu993g9m5y378mnef5uyg79w5fc950dlpal4jag66dwe4eln75skhyrql0q5yr7a6y46qwjly9kgp6se266s4nz200ngk8d8jemnrdd3t734fhmph658xqahgwhuz654uy8qzqqqqqqqqgxvrqa","certificate1qyqsqqqqqqqqqq9s7qku3j723ugjtgtad99vyj2wlf48m8accnf93d455ndmyrhgx3j567qpkklhfn572ddu392yzjqsqcqxn6p"]],["transfer_public_to_private",["verifier1qygqqqqqqqqqqqrnjcqqqqqqqqqge9sqqqqqqqqqv42qzqqqqqqqq0r2qyqqqqqqqrcwcqqqqqqqqqqvqqqqqqqqqqqpx3awtk2vtqng3l3sztryttkewhv5vjgvy2kr842u0ermkuxnxenws0yd8hdhjsnrmkm5pgj2ckgpwer8sfcs4d380ryn6vve3mxgtfd3z7m7n9e06nxt5ahcs34p69qwyv53avg0724xyrh63t0qfwcsp4accpdzp4pggwj4vwl0grq8lnv7dnkjn700snvzusg2u7qhrfrwhkuslqh4fr8snqf0mwtg2gr2syeynv6wqcjwhppdqmjlrw4rj2fc0zggqu3zndf3esg0p4e903ezp380nytlpxy8wwlehytr7xqmxqyf0gp57kwt7p48q6aet9l9akvhk35rgrf4n8e24dmlmc4hympmevkpwt4564k35msat5y8m0vmt5q5zm8pjn9ks6cvgrdr6jkmmamd9t24lwnvdvekkzhx7fxs44yeapvr9fxtrvaue7jdsurnfg6zvxcpjwqmynsgn3m335zdj7ly6sa8vl4tvghg89c6vjnm9rjr35nsxl7vukumfuawa09ykagpuq4e9qqqr89swwwsntqdg50rufu457fluqpzqq49hxlzyfxj4qfpd75624egnrlhsgag3dayzx0h5uv26smuqqmzztsvueqvlfwxsnyefv7a0kg0qgnk4k8lutvpulw5cdsnct5flwzm2qunhhzdwn6sp7fhs9m6hq98506xchxlsnxrvfxjsuq5jc4pkxl3pkppjnf639nul6eudzrae5tg4ngeurglyjs9vljq9xgewsqdfpmx84gf26lgp7pyzxal2hkv3tdv8mdhv89czuz3l7pfl56796wkz70tazx6p8whvajl80sds7qqgnhrpgg3lqwnqxncqmz4qaskyt9wz0zumng4pzwpnlx62svrn3c3a2lxu0zddv7228fpe8se5ezcztnupqzhg3c3lwalrw8e6q92heg6hnge9p3afgcfqen9jn0nqtw90lqqqqqqqqqqq23eq44","certificate1qyqsqqqqqqqqqqym2ncr0tr4mvymqj36p6tqp5e7ntp6y3pat9nr3xrsxrexgt7m7zsgjqm20lhguv69vl0wf8w5tjqqq5z8exw"]],["join",["verifier1qygqqqqqqqqqqqzk75qqqqqqqqqg0agqqqqqqqqqv0zqzqqqqqqqqc9dqyqqqqqqqq9gxqgqqqqqqqqvqqqqqqqqqqqdjr35uzk85eq74g2r3uw69pncj52mm6kcrl93yn73sfs7yfxd68lra4nvnlsexfjgumetgk276v5p3x8yvj790t5v6nrusnuqv5j866y3jr857wt5a0l359xxug9mys9qcww3pnhtm794zjrms8tqv6fcpc8c9nrh7vejechj2uv7jnlz9257a6z5ecl8yucpu4c5cq6tr2hgu30p3nhmerw2wq5rm9my3lg5qqzerauy9rwxwkxxmrgplkdx2gfseplg9hxfc35ky2wacgvf6a540cdugu5rphhwmm34ke4u8n406qymrygknwpqvm7mhfkwjkrc58y5u97s5aw74smzt4ukxhpuad7hgdhd7et8epa2rdzf596jjq6wnyqnv3lfyucvcdhhx357j2v4ye4g04ttvc42zlmugqy30x5fjmnnewh3dc6tjtnvxu76thkah2lg4yuqp50zjje872w34gnwt5fl4gjryy36f5lkmrw7rll9v48r8k2dtukky0qp7840l44k7tj7yn4k2kxcq9a7mesnmkz2q4a9a4k04pcl9p4a5pwu74909cjefm53vfy258v8rx52ecrpvfy9tg096798lpsmqrhnwgp4xvetatcz3mhd7xj95qe5swc07d7yjhv7zld8l0fedkklrp8w7fumv392dcuhpf2a4qk2uq0nff4hh2fmpqeltf4v2ffjykxds6g8ytaauzch3c46pndpltx83swpwwwyqpu4f5pf0zqa3dfnggqucuyslfhm2p2kadp34qpnyna0arcnkq9h7g36skj5y3xlkz20pkupfkxk64752puwszr75d2kx6yq3nhzvla2f0y606wvv69z04z2ej3jlmqpx8k45xfs8k6ye3c0wygvdtm8whd4tan53lznt2pykdusqqjn5lg9tfnlp8cvvj9n4gtz4ucuzw4450puqevcaszhcwpm8rst4dzszqqqqqqqqz0ls3g","certificate1qyqsqqqqqqqqqqxa46ys625t2hv3h3daz6r5t8rzgrwmxdv5supt7wqjtgs853jkrx5rxgksqz3vemuxkwq0kkrmcqqqqpjtuya"]],["split",["verifier1qygqqqqqqqqqqq8eq5qsqqqqqqqr2pspqqqqqqqqvsrsyqqqqqqqqv0zqyqqqqqqqpk6jqgqqqqqqqqvqqqqqqqqqqq9p5raynwuxsu2kha5kk8qjlt6vejc069e3kac7ne6vpaqfpnuw5e2jhluu6qvnx7408rhvjnpj9gqn5yumvyvm3x25dx8tgddcgj7t70eaay9aemm03lgpxp772q4u6cv6l0ts76ks5ra366cwjra7fagz4z837rmjaxvjxsc94sx9gt480vahnqm0ntwu8n26mgf0a5ejthscpgfwvunflpq9cyjkfk4dxvesrsrqzquggpan9x7y4wnp9x02f5p4fle5h7qnuzvx8wyq56qpky7pdsagdvps8rqj37qpex75wy5uqq270r0c27zp5438k849y5fgm03xxzv8fdkjetfu8pkqzy970g4fdpgdu5hj6fsc7dvjdxnkq8mhsq09lukdmkt3e3recv8wjnjnvcfg7kzqhxnew2zzm0cnzv4h98z5lasnjr7699jm2x7hgxyjwh6pqqqr89ptxc7mr24sc7drp0ps2qz0gh0qjutunvkaswe0jsn0244v3jg3uysfglenzjn4ajurru80arsztqpdgr98gu4gpvy3dkfvnhscw955jkvazzp7vr93pgr28yg57tghswgfala782f58nx0s34fmy6sq4hyulmmfa0fh5fv240jlgkx079gn0fzrpcmnvqmmnladywt8jlqrrzx6jwhz0r0wscfn9y5ah3fqym93yx4n93l57qwpvqkn48ld0ulh35y4rn7p4cjv4at0ppgz34lj3ppxq6jxc8frhw4cclencl9sqcg03ygr9z5aygaakcx77tacvcv5fcywytx5tp3amp82njh3n6w5w8eddhvxrgag26ce6zjqdyltvpq946dx339kda0m7kx7kd5kvljvw8lpznxduqfuflmpe8jx5ta7vhmzkuv7kdfd2yfzy3uetn8e4grwrg30wa20jzd865m9m9pl9y5af5quzke7uzazvv5q3j4n60lmwy2dfqzqqqqqqqqc74m8f","certificate1qyqsqqqqqqqqqqxpglmsj6yycw8e34w6dxmkrfusggu853pek8u5axdynmvhu89wfqyl2fftt0rchyapaywn8ud3djqsqprzsv7"]],["initialize",["verifier1qygqqqqqqqqqqqq5ycqqqqqqqqqqvfsqqqqqqqqqw49qqqqqqqqqpk6uqqqqqqqqqrln2qqqqqqqqqqvqqqqqqqqqqq28c5qkxsz699ds72k2mx68w7rya7us8lspk8wxvalynxzlw58xmrft8rqz6emp2rmzzktarnm9kuq9hq27mlrk3cy6ht7cg98rnq0hhqscf4w9ksjaahmq7zmmq0j48jrupr2rnak3x3dt2l9jyl4qxxgpl39sylwl86srm2u3s3ksddgp5rrta7llmw0ttd5wvuvkrzxvw06e4v9z9xwfm6kyn89kls0yvnhszl3t9wn264achfvg6k2w64nrn06dq8sd0z0gvest2s4jncrzw7m8vjfj7tk9u9huqpydvrzl25f9q8gyll5gps94eq2pnqg2pg7v7v3u5cvlzsh9yg7xj06yglpyjf62hkz8w863yaxg8nwhq74kehmy6qxztxkg32u6cqcc76hwlg0xlju7dhmszmks8g5gc7jmgauaz75fr8dhqeppq6r80t3e9ldqevs2e5qlvl2cuz5d9gehdrhg5u5v7v2pjrf7ma88myprrvps7shqkz6423ssa8hjddpa34cjuzf63nrd7egpm479ul7lnk3q0lus75qx9lnjj776fwv2wk36mk6tx8yqy9n29wz5pnl8k0ptdzztm8ggxryz3nrq9ft2slsgkgzv8qwgcvrvg2g37fgmmg09lgvclfj5fw4whtcmt7e7wplhuyx8s5wg4g88q3rc74efqxwqnkypyghrv3jn6hngdsrkxyy6ur5drwaf3d4lkufwr0g558706ezw02chwtcadz9neyj64lfdvql55ljzpwfyyl3mzq2yx03g0p3d904ges6adtp7eun043plxqrw03r9m9tmr0j5h6rsjvc22jh6csqa4sjdnfaxez33vhj3g24yhwhx4gx2pvadcrvmwatt8kfpvaz0l8jeaywkvnz9jlw5g0a4jw93xnszj2dckxdhzq6mkw3vqqdh70qzerp7qz6fyj4mqj7e7x79sd3zj0kxyuqqqqqqqqqq5cf4st","certificate1qyqsqqqqqqqqqq9cyh2mjemc0sp2g9k6ng5w6cu4f93fqvmzv5g4s4cjsaerp40exh6ms2zntjaqexa58whwd04da6qqqg304nw"]],["register_token",["verifier1qygqqqqqqqqqqq8j8cqqqqqqqqqwj0sqqqqqqqqqvx5sqqqqqqqqpmh4qqqqqqqqqpqy7qqqqqqqqqqvqqqqqqqqqqqzfmsu2e6427j04cl7clcpsx60tv5dna6w45pzx0h62mr6aevrgglk28ln7gvs2qur2g4jncse83yqy4j07fqxefacd0v62q7htggyvy4smjdeglf9fw49lp09atvn0dpa0rzftcysyqs0wdftjwv0kgysrnlkndmf3mfzr5a43rm00vkfq78ftzvmv46vjjl26e64sx0rlvff2vuwnhdvq62lnay8t6mc9gats8ek0hasruckm757mc672kse3ztda7l652u5g34uqu72m6lwzv3krpevnpcrt7jhdvt4jxdr840zgq9jcter7ae8vl3fnxm53r2ys4pf5723y7h9xptvjchva7p40zye9mpmtg3l5d039xm2wfuvrs8h9vqxssgkdcrxds9lkpk3gszqr6zae8608mq6ucdpfwyhu8tuzknf0jkl0mjwkddesdqdypnt9xgtwwvp0g6vhrsgzwzr3ddq6upwleygng8h4nwzavusky092fdgj832t7lqj2lffh4qelswvwah87x2523qpj43aqxp0792en68sjyr9hvyn9uk6tya7pld78jntrc05ya6madlqqfgn36zasyx8tfecwm5k4e0q9pdhquphnu5k2jydd8m4qt0m4pm6km0dt9gr20qk5t5p7ppqphupfkxfsf347t9u8v8f26x84yr3qwucy2fdx5azw6d88ztwze0526zt7mggp6ehdkesl0x6l5quw3l7degxqp85stjk5u4j5hpc862jgqd8dcur7yc0xmk50m3ff2422ls4c59zaj62h0ha4udgt8cs7gu45yjtalcujhj9reqcf92zglddgyppn7sl9z2c36urk8s68a8z57aafgfu76xdkhf4c3m9p0vllskxha4puws6dyvceq5g8qc6r60jadsr4u6x09cacehsvtywx4nr3lk8huhycfp3gkq6wfjv6d6qjpv5f02setsqqqqqqqqqf6a96w","certificate1qyqsqqqqqqqqqqz9trae2j33d7z4uk32234saw89n8t5zha4f9tg5vgd93ga34ggepkwden6t097gq3vjxumffnkmzqqqy04z5n"]],["update_token_management",["verifier1qygqqqqqqqqqqq8wxuqqqqqqqqqwvdcqqqqqqqqqg6ysqqqqqqqqpwaaqqqqqqqqqpc55qqqqqqqqqqvqqqqqqqqqqqpnhta6enxfrcpcjyam2trhmezqc6ywpplmldy6vuslsaeh0lntnm7artp6fjvn8pk8mvuq27qvacppexjr65nt8w6qhx7e6rnj970s4fwzg96th5fjk635ddr9kwug0jmtrq23m248x62etsmm6pl3atcqyn72cz8gzjp5cwm9485zlsfkt8awae064y89d83m89mqekqukl4gehprfpm2av5ae647yxwn0jfqye7wpfg3dk92rgl0wz3yfw46z3kchsrzld65dpu29rt9c293zmy42uh7ga4kca29uq0mg38t4n4gq8wc8cnk9ny3ggcjl2gljrjr70jrygyrkqt8cuv48pnn0plkup3y0hjq2vgtyf6ch3mfxcmpm2t8vqscmsqwwwf64y55pquyqs4gewdka9ruk7p68fg9fw755tj3nvcvph0cpv06ju9ru0mzk5dquref5uqxx0a7t0az4256cps7a4n7jectxtrhaxmtk0vnwd6kav8whzes7av2lj32ct8ef0gqstk8m9jvncgpj09efw2xs5u02u49xq3sgssuwd5yw43raurq2gtd0f7mwe70l7wzvj5ke5n8jm8zwdqfgqy4shyszh8jp92mk0mnptmeayrzdthaj0u987hh55dpwchqenllaaq55vyvyx50f7hdt6gfdvsfdsl96sreqy3wsjaucyzzvanvt7aeehhan538t2qulldru94vlvqtm655y7mfdjqd45tm8k47swmpaqtta7x8sqd0hfeztt9ukkvx4ww0rjtszrexzfzhl84ncrgu4muyed2cxfvlgwq9y03r8ksjh2fmexy6edlmfvpz7dx4fkaag58ffamy3dlgccvnnl9zflw2y9w5u6vtwkj2cehepxvhpl8z9z76pfj40g07wx2ty0sp4ful7ndfp37vdd88a7fmprg97p52fx56e90kzul5t9e5yqr605qxfxsqqqqqqqqqeux90u","certificate1qyqsqqqqqqqqqqynrl9hv3uhtdyyne9fe9486vqcdnhg7064uf5vu8c38fq77x7pgxh86ewa0ns6x35733rpghzaawqqqrmsjr8"]],["set_role",["verifier1qygqqqqqqqqqqq8kgqqqqqqqqqq0gsqqqqqqqqqqykvqqqqqqqqqqmacqqqqqqqqqzp9uqqqqqqqqqqvqqqqqqqqqqqf7d6q5590advpkce5cf6zqleg27ugd84qjl3686909dlnckuc6m8838gt7kcvqejdnmeydkt8g9qqdl7amxnfsh0e9rdsyw9prz0gnt7dx9tw7x67lp85gymq7se5sgghde439g8kqx0hlvvd0hswsg0cpz6n6ga8xpdu0c9hrl8vj9setdk9jtj66j6mcj53ft3zdcshcaug9ygdmskueujs79qwefquzps0s8ydgj2r6ct9dec25x6svl904fpes0axls2zpkep95lmd2exlqed86dg3km0002mrhcpc77uvjzwyqyjyt0ldvqy06d44r5pa50768uth5wt4cydgye29m3chflqqgmedgaxw8ns38tqqka7rqjggakn0jqs4pvdat3mvvjs6dx86n9t44u40gy5jp3yenk88avxfnzw39fs7vwkugxfw4fph8u29670rrdwvguqreslx4ly8a4xn0wc9mj3dszadnpg4dcfmjq7sc8sp632lnxrk9dge0qclh2x3vygulrj7m2vj5ngr2jzq5vfqec7clqxvnfdr8vdce47flrtvrz8evdkwrn69uncf9z2ldug0h7skr4xshlxfp7cjzpqq8qgttzprs42yp3l35rqv7lnscwf32s8qksp9yayjlv4xuqc6ju4vf8lhma2gyzmun390uz45the0qg3cwtyhylt5c609fnugaeskt5azkn64vmkuvfdsatvnrntvl3zktfwuja3x0xzmnfp6l45tm0ayuq757kjuukj896hqrxwlg4f0pnv8mpqkdp928nfp9p6eczqsfahcz2rzvvcu46myuuzunkhj4sezduqsfqah5mymgy33ha0eal5tuut2lm7la303dk7v7n0hez80vlwux3u5mk3kcrqczsmtc733xmfl0aqqnm48g5me38uujsa8q29dn9qv90hkt0ym4ncw6xp4m8aly9me9ts5atqqqqqqqqqqtug8ye","certificate1qyqsqqqqqqqqqq99m2xzyak37q6eqdqzm49pvhna5x9cayqcw6yxak7xwld4t9y7p26rrq7k0kfkh7en24vhq45dmgqqq3y0h8d"]],["remove_role",["verifier1qygqqqqqqqqqqqqr8uqqqqqqqqqqq0cqqqqqqqqqmk8sqqqqqqqqqe92qqqqqqqqqzv9cqqqqqqqqqqvqqqqqqqqqqqxj46pqn2tecwd8nvcze2rc6hmvvdgfujmw9lkdcwalw27degleusyh249ueh63muwsev4p20r2wsqtlj7gf4rzuwnjpvjglm89m6qt3t0caesdermmz2tm77l7eargyrm0wwl6dm6ntw9nrutk494ls2qzacpwyyvf52pm9545s6uzfr2y5x8ve4ark5t52q4waf67ktzxqaa6545jp795y6nga3z26jla3vfqych0jjj5hxjqf044l4vxzyp2wpu2mr9fs3un7fm2n294afmecqh84xtdj4qcfg28a0y38nrzmxrtqvj6wx7lva7zmm8vhn5lx3kftcwgcsp8em8h3732zzu9tglcw5ccms2gu8fdksyyqef7hl3hqaq8gqxynw0gmsvcv70jjmdkv3ewyfvrw95sqhfwc5lrzja28g6rlywvgmjd6h93wt0hxxykz4cqjxptfgpcmj8khlp3wfxvz4wcxvwatwu0433n9rpf8wrnxnf4mt5pascfc0082ur458jxj6kfpc3c24stu6sry7ykxvmej8a2gd965y58ld2enx5m94yncktpgwr9qz6xtpz58kxqxrzp598eljmafekge7v4ykgqqmuwxsnm78x5hzqd8vuj275xjdlfsu543f8dnskxjtklutxpy82h7a8wg0ejfn6x6cfj7c2y0c2aq8vgug5rqv5lckm0vd2hhue3e9hul8mzsgkk0fapkefdy8u63yz357hf6qkanjryy94dyus4n5qcyqtt80kggytld3g7gy96ae2k3wchnu3w5szlhncex2fz4v6etrwc76n8979y8wtxnmaml2c20u0yscp293e5mvsl2tuwcrlezmlpf06es80zk29hwqcf6hr8gw30yzkuh9mn9zlxkegy7wm4egndughgg4qqysfdxhjpkq2ufeew88hyrsfht2yhljr2w0wgjss5q0zn6j599khqp2sqqqqqqqqqflxwmw","certificate1qyqsqqqqqqqqqqrlhd2ghgx6d3nt2s8tk9z60p0895d9nl9c9n2fza4zzqwwzr362kp3nzr3xgs0zzclkz20cg4uwgqsqmdtk72"]],["mint_public",["verifier1qygqqqqqqqqqqqr52sqqqqqqqqq8s4qqqqqqqqqqxnxsqqqqqqqqq500qqqqqqqqqrr86qqqqqqqqqqvqqqqqqqqqqqz4pndn6d7p3e5c08caj09kprycj9uegq5ek5xtqr8jdku854xvxx3lkzlk8me72d47ea6k25cs3yqwfu9w3ag5uetjcprekv0wntsgvyqdqpv4l49svgeyh0ktwgy2rt8c5jyxzhqefrwuncjhdj6yvgsp8athz2xw9c8rl8tuak8wj23f2pugp2gu4lvxph9ht98gh8z93zdsmzk0fnu288c3zu6sckrp9vssxs4h8nwjejses8e0eqvgygf6dxp96w70r9fclmgjtt42mddvnze0fn65s0z3peqg9amnx4t7sa6sq2h088hpnndp9ex84q6w9267e6t2arm958cc3fap9008ganjcfy5du6nay620q7ykg926kj33vhq2qtyqqn5f8slyyspetwqr0vg23rxvyp36lk5uec0hm9zh4r5k0hzrsgmrfk6ymwuvqzpuyd0hdj94cqrensm33uf4xt3r73zv9ut76zrdq42jwh6c4x8ytqsj08858vrzgan6grsyqxawas8j85kay3z8ecqefjt2dr2rs8uvnrk94qhxdwhnfxr7fyzj07hsxnk0a739hquvl5glu5pl4u0aw9xemxfwg7nl52qxu8zlllzv02f0w9yz53strszlggqdry7eaqwv0ecmjzwfhwe2ymppxwmsqf9rex4neqkwhmg4z8hq06upxp32jk6k7ehvnznrp3hcq97ysg66h50pv7uynf3s87xk2qxsy8tsjvwcyng752tdx5j66s9gqawmyzejz4uf5a74ltggxkex7jyk0kzp8qv4l7p60aa4lvyja3yrpskes0a2qg76g40r5dn5zg7huqyvz5tzml2e64sewhaaaur9n8r00v9qvnv9fp8a5vswuy36a3f3t7wgvljvr3d7pdg5aklh52la0sreccx9sptayhyevafavcd0crj6hfuwktjwqjt6jn2z56x8ujrnhuy4hqqqqqqqqqq43hk9s","certificate1qyqsqqqqqqqqqqxyr2udfznsr05x5qzv7sr3ctvdqmyw3r654v0e8tsuzs89ju2952vv476jt4wtlcq3k4l095n3dsqqqtw8w60"]],["mint_private",["verifier1qygqqqqqqqqqqqrknqqqqqqqqqqglxqqqqqqqqqql4wqzqqqqqqqp2rcqyqqqqqqqrw7uqqqqqqqqqqvqqqqqqqqqqqfdzqrcd0n2esskpw2r7p0n6akvs26m679qk404j5078wvjjqwf4fe58egdqlqjkju7ts89eth99vpes3gnu3slyfwzpqenafad9mn4rqa4n92lhjhpxekkz2rl54842x4uk9jtsksrhz84srj60nxnrsqqlqv3t4dxwcyhm23rgc2kf9c53pdymy2j5lqtll0vnz9z8f84heg8m83lclxtewfmfg8xm6wxyyhq8r09fz2scex9rc6ek8j6cm9jy3pc048t78w5qnd456zsnw2pcxmpjry94r7cmch9c653t42nyqcqqqyd0ccx6m6x07s6725l66aqsq79lmge4ll7uh804hvhq4a5yugwhm34w7ckwgetd5387v46784tkqljuh49auq3sgd430wnggjm759jyvcq8s5dzveprzs0298sh7hqxqk8xjw8a2ykpcn6djlcrrmx9spn6t9aghrp6srgxw68f94lf7fy47h9tmv5rdhfxy3gtww4jcaw9gkxkj05wkufcs94spx24k92u5gr82zj0fj50g920excrhsxamxch52t363uzng48hs8vykdl6654t3vhwmlnqj8dezz50wq09cu629qrzqxexrsdhxfx8ym9kkzg799qwuxay4s4kw0y26ch326kkz6qy25uy4hhwwwwuw4fjmkq7av53kdqgg2uz82e09s3nltyudlla33vkkty24wjdyqe3va5qjrp940d2dq42hfa0jgfdzatdekyu7ucuanzqjqm97nt2qvvswr7xlwpcn655p8xgv0f2vd27e5vkhc725jewxvz8lnuz7y0hjjx660l7ej9tcydgqqdffwttcxeusxvuv9edz7apu8aum7ux4njg0u74dlqsfmfj44kjs4dnjwlxmw4xjtyggl7cftyyspvcz8grkxux95np9e8xhramrkdalmtc56uhx673h27zru92hdlsxhxlsqqqqqqqqqjewvsm","certificate1qyqsqqqqqqqqqq945hhccj8ns9exu2j8kaegumjza5pcw6a4snq0jtvx29zyxwzwjdlqagvufvt5ks832dplkgfnguqqqlfmnew"]],["burn_public",["verifier1qygqqqqqqqqqqqr32gqqqqqqqqq825sqqqqqqqqqcmzqqqqqqqqqpkhqqqqqqqqqqrj8kqqqqqqqqqqvqqqqqqqqqqqx8kxpqx55v6kds2kff94dxfp0gdcu5d4fwsz8txt4y9jlwcfedlr2zjcemaculjfk0d0rw2h9dgsqvlham5v4q5zqppqczh4dsh6nepauwvpwsg5q9grrrg8unwzwz23vh8c4tgcwq4wvlmdracx9t94cqduu0v62kpfmz0etusqsh46kchzekrn8k3szpqq3n60f7uu7wh0dnkge9cwpsxar3d9tqvspw76mq9n6waws0djg5szzmnle0aash48da0xpp84en2jk2u02tj4wrras5j9u7593fr9lszw7g5vs50ufrqpukfe8z5gfezphsra8meff2w0ry0aezz5m8gdzw8lw58rewz7fghnk56wpl3zxf3tr4wavl4yhxxqz5anw4usgaegmtag8ctpdec5k4penkn9shmvzlutl693hmgnk4yp2wmr38qvem0uzptvt7740dkuqf7mec8egwm03yw06cjf94jym5z5snq8wsqc49swaruhm2w74sahhlf0epgx3xk7l0dptvqkvnu9gpns3g4wk654j3tullt7pmsnju3qw8m9tkdw6795eyyd0ykr3jn0cz2gh686a323pspm82su9v4tpqzn35kdhesumzyafx2r8c6y3ske5gjn79g9cx95jucss2976uyu56a75eaz9rlsmdcj5xrvpakj69q9jeq967juvnzxyeu9qx9v0aejgz25h7ssqwkycaxqlhn6k25vlq3p26cl4skent6d3fhefckmw9vqy3qec90rdmp6rncdqjlp3ssn29f69sk6c86xfhhy9unfyz7n442ue9ljsga9q8nhwnla90mvusdyq2e8z0lc7jlsrytn8h3sqypxe5lqj2tpshtnasdq2pp60eedz4uswq9ctwn5s6lyvs3hgrj4cccacp4j4qyp09uq9s5r8cmvw7hhqmdln0t2n2zl96grsf2rmzwtq4zjrrakqqqqqqqqqqfn6zc0","certificate1qyqsqqqqqqqqqqqcjhw3w5rd54nyf9v9fnh0tpshnrue8ehwykpp9gcsd8spcm4m3zcw2l5stmnvkaa6hwzmdjernuqqq32hju7"]],["burn_private",["verifier1qygqqqqqqqqqqqzrevqqqqqqqqqxmjcqqqqqqqqqu6tqzqqqqqqqp0vtqyqqqqqqqr6yyqgqqqqqqqqvqqqqqqqqqqq9fy36tgc46lp00s9ncz0mf9m494snmce92huqqey2dwhnezuven2ws0hh2s2xmjpu52lvnnsgrryqav3w5e0465a8vygrkmzmasr9v3ymcqy4rcdacfs908s279309nqga5e2wlw44etqd66zz03mj7fqp23a9z6yg5x9esrwmhqgat5ct9fuphxp60hnvxs3sxm55nhqr5qenr7ghq3ecumxyyt9qtvm24fdsr40wjclwcph8z2le0qhf5anekx4kv7furckr5hex0fegvjyhcdzp00frmg3z43csjdfmmmjap0qmq0r6xmxzrz3rtqrh3djqnx5ftydcvpp6rc8gtlunmamh5ar7vpxpmldumzlyf5j9k6gaeqemame37qhvtew4ynp385kdxql6fd838pw45zp3zum36m9y4xtfmx4xhwwsakqe6vutqcehlxclrw4kfah9uyq9uuewky3ttv83knd5sjyajj98dm2pl37ppktguhd00zd7hjnt9chzf5y9z2vxqsjtdskweanfzjqqkyawzht56jcdtuslkycdkap7tmj905mnlvw2pumxstf4hd2c32rfxv2jjg04n3c5jh94phff9szspq279a7q5dq6wm36xs5wq67u4dvgk3qhjkcre28eetthufaks4h2jf6f4qe7drk3ursg80s2ml72qpcfm0s62awfsad3j7x2qgvhxk0wdtu7kl7rrv09elmhavtur7xelz4yf273dy79yr0xnsr3hg07qqprdc0dm6ra85na9v8tqcq48qqc2gl7xhc7yr5vhmhhyfptfslhut6ad4769rza2wenx4yfv45r8uqxenrry9z2vus0m2h48jkwdqelzqpy2d6w6l3lq44zujldrquyn0jw8plmfyej5gcywj0w8g8qygcra4cs6g2nzzzwyx8n8ue07n3vtzpujn9n75kascpglqymgfr60t3wy9qzqqqqqqqqfy0tau","certificate1qyqsqqqqqqqqqqrhdwvwlagrdxzqgwpp4u8pyvx65jmw7ejvqaw9ceu377w6l9zullvavxndevn4pk7jmjdqf9ynp5qsqm97qxz"]],["prehook_public",["verifier1qygqqqqqqqqqqqyvgvqqqqqqqqqgsscqqqqqqqqqmw3sqqqqqqqqq7wvqqqqqqqqqzqxqqqqqqqqqqqvqqqqqqqqqqq2jwwylmryjhz0ptfqlry7d57xvkkdv4veg6sp7nqchl38v7uhe98r9zwpm73h3h95xa0es7xjhxsqfx87dnyns7gutur5cvjzhlg60tqxxg8lk0y98jl35qr8evctm00wv0lzdxy5hrp4ekz5cxcn5vcqz90muz4el37t37g4vx54sahv6m27f3yus094jqa9j7r5lr4w3gxsthqmhumzgvvfapux00gmr7jtsxk58x292gxs422wec0v0vqhvkaq0ev8jlvp47z9552k4utdrdqw2cge5c8hs7dy3nrrw3df33894qvm22newmnx6twfe5ghnfwg4jft9fhkmp5vvwa5hrvvzn0d52lvajt2k7tsrknn7rryhhz7kq99s5qp7ptp72qa6gq3q9mphx4nq2knvcv0jzur8py7d7haslwtzsxwy4uru6w25q96x6vq42832gqy32gpzqu6pls0nyny2jprgltlwpwsktvn6p8vur9xnnfcy6sx8jrcwuy3et7f3e4lwpvc7q3u6zqypjgsrsevylcd3zkup5gehrtp5g64ra90a5sy5y5ekf3j5hsx9hzqjlvhmxkx8lvjvqc9nhzt0c05ytu3qqatrcrfrm0t445whvdajuad9jx6f8htt4c7dq0ssch6d0w68n3emy0cq8aruxfhqcgp8hgjpt59hqrhlwpz55qdmyfawmt038ymxd3y29ydsn76uls2fzp3k0nrsp9jnws46mwklw5vy29f3svq0excsgqtgxumd7ssu760f25shycs75g5l9k8kegmms24uqz9qlt54v9xkjv387n5prkz54cmuw8kfeamu7cpzvm6lxht0rt6mjqgrxp6p6e4djspyl7w4t7nsydn76rknalftx3987vc4txkcf8nae8qusy9fwnqpd9s6mmeldrzl4g2zdyh66rc3qg8nuhjktvtrssy4qu39tp6jnvpdfdqqqqqqqqqqjdea44","certificate1qyqsqqqqqqqqqqxywjqwvqfgjcddcfy0evrdmfspx0z49t5ggd5cfa7n88t3mnjt8tyje7lfyz86zy4m86ycdx9f4uqqqq4m7zt"]],["prehook_private",["verifier1qysqqqqqqqqqqqynpcqsqqqqqqqvqrspqqqqqqqq75nsyqqqqqqqp3c6qgqqqqqqqzztyqgqqqqqqqqvqqqqqqqqqqqz6vrjdzwutcupdqfkcry6g5x485s46nq48uhj87u9q2r4dgkfc8xz3hhtmwsgnypuxww75mssx45p2klvzps69nxeg66l6ptd8nl028x2lu940lhnsvhgd3wurkk2ld9gtp38u976gnjx4jarqwkg53yqqd3284p8ra0qz6wkam3zucdh4fyh6cx7zgawny9gxrm5uv66jtrcmjwqaxgn6vvxfj7309j2nhr3sqd7v35lxvrufsaa86wperu3gpqpnyh2p25drt6fe2a4ffclzzdqyf32ldcuvm335hcl7tx6sst3cq0pu4p5cly2m7rdus6zuv22ntwjpezfskjrrktnw3rjhx3jgzynnr5ach0942q0cv5xpnepz66fwsqhxysxxw5q4nhqq7u8azv5pxqdhmpypw3yh7qy4atr0plwgrsq6s4e74np8avkwnucpjapnh3t5k5q0vsm3m6xhz403pkc57gkdg8e0y5mvhd8c0spl4hgwsg25glddkzqknlfgakj84t2z3qenleu4emqp52vxsx9pdcmmxjs3zptpfk724r4dh7403z9qenxqdrkeun3650jkex3jf7w0clx7cpn9nmcmwmlqruj4qyndqzq8h66kzfu2zkmujg6h6nznuqnj4j4ukknfz28y44rekkg5xh8sw3gqj534znn2hvg0qya45hmgxamajwm5gunu9rdts9wtrdpdd5un98achkrat23m3qxlvc2tfmakemwqhap86pelm3pn7qye30wzmmpvjjj7tgq0ruzzhy6qesqaze6t89mm3nyq9st4cs6v9dkgtuuzyzphw7vtwdyuf87xusp7c09zppmewdlpxnn4lpwgugn0c428xr3667m80tuj367c77g3m07lwaqj85ktk75ekquc7je946qr3nkcumfhw5cyuvh99j33slwn0etzlkwf59u0vpr59cmfstzzer5a9wqzqqqqqqqqdklfq7","certificate1qyqsqqqqqqqqqq8h8za2rp7acnwx3k7qr7fh7dkpup6mpkg304sc0kccurn3xwyk0h9asgc73t4q5y9pfngeg3z5yzqqqpjaynl"]],["approve_public",["verifier1qygqqqqqqqqqqq95fgqqqqqqqqqtgjsqqqqqqqqqs6kqqqqqqqqqqkkzqqqqqqqqqpuhqqqqqqqqqqqvqqqqqqqqqqqzj394vazm38xp56z3l7xr96e0avyjajgjxk9xrlwxj3gjleakemc3p3nx90fct4tyz6n9uv644qgqvcax2xjt7ae6jg6zwdg7d9we9efk7rdw32zwhw3v2v9x44r02hwnj6ehqt6cxjrnhv4gsucgekqqp7dr3j2ev8qqp49um47966ehnfmyetdwwxwl859awju7avard4xx902vxrag6fkxhknzdruu7va9q80pk57xcttgw7uqkz75uyxwqs7hwrvf63qa9rw2lrh8nehheh5mpuxqvuw5hm70j8yxhjf52syqdqryum0x3gne3l6ugyxrpjnkaaejhr2n6sfz3ly32h32tk6ejf3fhgnstk5za629y78gk68vcepmqjqjm40p9kljcf22hd79uhlp4sdfsu5njj8xp5f5a2c5n3uamvrd4afc5xqfj945z6snsv7k44d4unypx20y982djdc0a46g2vvuq4dezm2gl34mk5t27u92w8epxv4c8t2dmsqjnezp34ppk6zk3tlccdycz5vmm0v65fp95en6v22mdl7xw24762ulzrxwac4sf7r0hsf43p793dartlr5ckplx400tpkg63v5s8exu3c259fkug8zlqkkul2ag9k94tnxtxvm6wfg4z69ymzddw3jswkdnj66dsjae6rj6cz4c925qqz8z5yekprd740u2xenvdyxdj9vnnkc3lx2ds3ymeeyrc5jw8fr37r43d8fly4c72gun5rus43ytuqjfu43dl9u2luqk4vgclaxhvmrvddpjpm7mljumqjdz02ucan0czmsgfa42ux47nc032ns408s6pypmtqez5fq7lzvw5hvxs6y9h43nckz949y6ca3z9lruvvppnmypwuch7us4qga7p8ha6ty5pxkxqncpwwjs5p663lqfa9jcjg4jzuwgtph0r4pv8n67yt543vyzdq6te6lepssqqqqqqqqqz3l5zn","certificate1qyqsqqqqqqqqqqyzj8tz39t4k3vx9jjjtn09uc9pf6y4dlk4gaplzgqdvcjs4j4254p6j5ad2mds8l2n50wx7qmwnvqqqmqyz33"]],["unapprove_public",["verifier1qygqqqqqqqqqqq95fgqqqqqqqqqtgjsqqqqqqqqqs6kqqqqqqqqqqkkzqqqqqqqqqpuhqqqqqqqqqqqvqqqqqqqqqqqdfnldv73j83wvjc9n6az6jdu2n002ymqaqrhfmmq3tz669nd66zrksm2lgf3xrja8u70z79d4ysgqjk2v4h7ljyjgx8ahq0fl8wn0myje7peqh4ftfdqz4yq69edz3ggrqgdr5nlr60j4dg4fy99q5wnsp7dr3j2ev8qqp49um47966ehnfmyetdwwxwl859awju7avard4xx902vxrag6fkxhknzdruu7va9qyff847f6qwl80uhw0k9rnx0qj429res34r074jdl84xq68ernxsfzhkplslhek2sff8dsh3huk80qg8p6t330q5mu92678ktx6jchp2gxh89g68wn6wd5v27vx3e5m2pqanecr7fv3u65j9de7pyg8jvxqzm40p9kljcf22hd79uhlp4sdfsu5njj8xp5f5a2c5n3uamvrd4afc5xqfj945z6snsv7k44d4unyppyh5ye33jn56sl8usmr2m8as8upnsk9pauy9wjygw40wp8n0snk4y8vt4q3tgqm2nylqz89nvq0sqhqat6ttxll5p5qg5nvrvawazuytytuevwgydhpwpugp38esls29544fy0l787f839s0edmgaqahsrexu3c259fkug8zlqkkul2ag9k94tnxtxvm6wfg4z69ymzddw3jswkdnj66dsjae6rj6cz4c925qq9vklzyur22dyt5vqqvncha7w7cjznsd4pcpe9sfrv5dcdu5tx5ky6jn0078t8jklvvgewz46nl8jqyk7yevwhykz4xpmzvrlu7e2rwmgvukj2tf28zq9hucxdtv4mgnyxfmjhf2ap8l5uaphn8yfd7tecqmtqez5fq7lzvw5hvxs6y9h43nckz949y6ca3z9lruvvppnmypwuch7us4qga7p8ha6ty5pxkxqncqtgthwqwx4y9666rx2tmqw7nzglg4ahnkfaw20dtaz9702m0xh0vy33qqqqqqqqqq9zjdxw","certificate1qyqsqqqqqqqqqqphugs89v7djwls7ctxj4hvel5k85pggd5jdapvr2zg06fy995wvgha6as4f29tzxek5l06zl2yksqqq9ldkjq"]],["transfer_from_public",["verifier1qygqqqqqqqqqqqxgdgqqqqqqqqqd26sqqqqqqqqqvyqqzqqqqqqqpegwqyqqqqqqqzn2sqqqqqqqqqqvqqqqqqqqqqqrgjf7k9g6rr5d4pdg4t0qnwyxwpxlnkr5pk2y27p42aqruz7dztfzd2wcnmnsfm5d64fk09e0yr5qyclsgkgw83f8lrvtsl0e3xzk0ffccqpdr64k0x8q3r7utfhdqmgaaqth5v8uj930rm8euwflvhqqp2pg2na879rp3qudglp03naw9x4fa6dzw4p9t5jlvq4wkel2gu8k9p8alct0v3dk67ldsvvj2j9qs8vn7hvdncw6575hhzlat2thkr0kvxnkxnwc9kzpgxydxdgfgpslthslepk0qk8ulh5qr2cxvv93yq8n2z2vaf74f9wx2lhs2gmk2qsrrpdmjr45p9hs3ay4228shg2pjnyagl5nq0q6zmhupdcac3gwxwqfflrd3cqxwhyaymjyq6lkqjfnlslp4edfafy3583lc0ncwnlclk77fvv4m4jpzjkkpgwsk9sjdecqqdfukvw09szh7rw6l47yuy9tp92u0p3f94pu7v49eq90sela4amgcyrhavglut0spfxk83m53kpspet4he2kur4900nya0dak5l2hcsgva2eu3s22gfl2xeg36zvztn3fet7uzl7qqfta3qmxds6d2zzq8fkqzvm28vtqy3kcmvv4qvuq54kacgnc95ssfcja8r53maxf5twlpg9kvmx4cncjgnc00ggrsk3vqdfaf0ulw6kc55dgydmsq5tteqyvqkf2jxkz2rr8hhvlmmzf3ygxdsnr3deyencrkus566nyus5dkqrnsgna0nqhmuyg0mflh2dhl49jhyw0xqutt7tt6h7gewll42gnwyn26cezmhu8g2kwftyc59juk5pvct8qxkcxn95gj9kltxmmdadfp68wcccy6tpjr5cp794auu9nc0xh9tzm7esjjjxpmh6fxwc7qgcqz42jj3m2c7gqcglukxrzs8r6x30vr3e4qx39ldt96ej5xjz9zwxhwysqqqqqqqqq69v486","certificate1qyqsqqqqqqqqqqywt3kl073jwa6phg44nn5r5ysv7zru6syckl9xy9mcv23c5rzv5c2n3rfu4ct274juyl7z3hzkhkqqqeqvdss"]],["transfer_from_public_to_private",["verifier1qygqqqqqqqqqqqx24cqqqqqqqqqwetsqqqqqqqqqqzgqzqqqqqqqq3ucqyqqqqqqqzepjqgqqqqqqqqvqqqqqqqqqqqvj9mzuwdc2vsy7y674l855pmhrlsfjf2yewd3vvxl9akh3fxqtrnr2sx466gws92hq0atwldlffvqfm08ql7sqxh8dwkzp6ts6vtphv6hmj89tq5hnmansyjhlq55xah5z93fp3n9ns3azj8dz2axpvgqp6l4k3mez28qexdwxg0slhtgzx57hk9zh7psqxnu54w37gvjx3nrel4vm526r6hf7knve9wv9qarqxdppejj8h4v8r5m76hyad7s0kral3t5rpezhytucgvzwqr0r0r275ntpuk3fpw7eaetwxupdz6gqqxt2ujkqkfhdl3yg4sun6uf2zvx9vkmh9k2y5gxwqfdcdj6qk3vjjn44rg30nj26h9mhfz3dj2uzyqs8alxz5e7ywsag0490qxfy5c9sdlvyxvyn0hald56tz02x85skd3gjvvfhewymh2jp3tdlysfuzuq53hfcm66uhmqz68mm432l3kmcznc3lxk7f6tlsrwgv8p8we67v7v7xy37wd8z6n8wkgu77afxf6czul4ctvfwzrlzg6pavy7s9ltts0a9ft3f6guvuscc5chws3fe2xxcszcfv0cgkpzuhg8f3pzpesfs86c053u8e7tr6jhf9g0pxe7vwm5fsqrkn6ytkmh88wet4z8peqywn5hgl7ryup6hn7hxtzr59ejwqtzstwzr0yrzdn9s8mycw92guhmatgzg070u4mdmvuunluav7srhas3w5vrethyus3mge6tap3kd6qnva5a3zlf8tyrgl83eh5jy59dq6wwq46usrl3p995eljsdjd4f7jpel4syultaz9ks3v7rynv6supgv4lrgecm5srglmffenk0vcw2aunsks38cw9p50454r4d0krx42jmwllykpd90df7s3pe9jzgpqgpwpxd6jwr4c2drtz9wdt8lrkhc6nnwlgg7za7gw2qeadexgfv50x3rwsqqqqqqqqqvqgyy2","certificate1qyqsqqqqqqqqqqz03aym43ld30ryp9x0zq59y2kvw65ycp5ttl0ll72j0q905juexzc0g7yjvv9w3z7qkjh7efzz2jqqqthftqn"]]]},"fee":{"transition":{"id":"au15su0w2ntw0m3m6esrpsn2qr8gd7k8agyjhw7e4sl30safu992v8ss2xvuy","program":"credits.aleo","function":"fee_public","inputs":[{"type":"public","id":"4204621360079531345924164626937619166045633684764009904150285352606157294663field","value":"75798350u64"},{"type":"public","id":"4735952515899317750664065480769415405047337445011285358998869614339923087468field","value":"0u64"},{"type":"public","id":"5334314194126344724134730353523431566409568078871284993701941218344284892420field","value":"7219828396534389733733442787671942045312203808091523682877515319252858785481field"}],"outputs":[{"type":"future","id":"6174508564661104955080204863722384082623898596601834669252840681217993530846field","value":"{\\n program_id: credits.aleo,\\n function_name: fee_public,\\n arguments: [\\n aleo1mgjcqhnar38zvschjwxr830pwk57ywnce8rp7smf8c3yp2xexvgq40d4fx,\\n 75798350u64\\n ]\\n}"}],"tpk":"2027543674195242663337742133501773784411198927637377797704373036741782384715group","tcm":"997104720953711142107147047071806792302946972417712248013297291566700066824field","scm":"8400595953876995264554804889824421621333978905052559491209753497117901891982field"},"global_state_root":"sr1a0uv94c0224sgy56r7fxwalh77dy67w327ahcdduqxglx3j0supqaykkvn","proof":"proof1qyqsqqqqqqqqqqqpqqqqqqqqqqq9z8dp4t232hgt85c5dsxsv8vd3r7sz26su9ec48g979djme82mssyj825938a835c330mqgdwk0ypqyjhwpgw0sjkww227dygndxsf23tnl6wgnx5pp48jh9rfv4kewcr8wptakq9wvyshdc9qexlt6m5jqtcenv29u4xvgj2c0hmew32nt24uvgnvdjnqf2cu7l0m7cy4537u7nqdheksf5dx38qtgaljs3j7gqrx8uj3dxyu94pr789p44d44757a30ac6tv6gq3ygmsvz82dsjv2f8h5lj7y6c827e2n9dqndg94cqh7nvx7kc94haqjv8uyhzp52hcxwhgax07gj4d649taaumzvny66ql3s3a739gqy9wfmfd5mc7l0gq3gyks8rm78tud0d5pvc0kmq42aremt7yjp9vy9rrx69y76vm5fxa29pqtczlsthxx8a0v67w762sqnfutzqg4akywa42dehjlc7s43dx6s6yry4qyhpzxkemmja3q6u37wg4dc5z662n8cthv9czmj3jqftppk0hx3a3d5k8ukl4huwgqma4wr89d4nxz8pcqpxpzx8ztnmhc7w5m0ph96dweewqv4fyu3xm7qfj274yg7kfnxm3wnwpjc0pjeh0cc3gafx7p4trrw496egkt2k7lr6tjgdtee5vz97pc2a9r9lmfvpuyfq7dva95qrdvkrq5apep883g0uxj3vuz8qsykfe3uaz7gtjyph9ygycry8tu7nnuysmxqczm5mz753d79ysecgqnvjaeek5q02uz2egw7tgnt3kd6mpcc57yjyd8mtlhhp489pxspz2nr8rhadeuy9p5jvx0t2mkqkr7a7w75z6gt2j5ajcxuurhrns76lrrawttvz29xqya8gx33fq5gce3vpkmfvt0dpzj6f9ykvts7rlcxvesqkrkz435sdwauj69dxcp9fg2x3fgen2wstlcc7t6p7mp07d6j2tvetvgzfqqx6ldxucxq0qn33rtsheh7umme6zhzmdpklg0dyhh49g9n28kpkup8zgc7fgj7n5fllufz9c3f3gh49qy2aqeqr9fg99hgyqvrrhqm7pw0e9mklv7v254r84424phxucl2gpx3hjgy5q8am38gwug025pxp95jyw99gcjt8yzqcyjc25mwf2ml9av39fa4998ktglduh30pvlcgqvqqqqqqqqqqpeq5luz4wutddrrc2whkaze0kkv757lqjlw7nly3ezl97dxufq9m5zpyfafhfhat3vv3k67z2gn9qyq2jlusmp92fyshvpff9v2p4ym2x90zy0v57jtc29edhh8ejf3depnrcqde3kdwew8sjxuvhj4hl65qq9xe5efg20gf5q2l0vc8yaaerzflelxa6j3sn2tmfszve8zh7rjq27xqn4g3lt93rt8pf9282z8eu49ekyukal64v8rtyd7y9gapktz42f5rf4agqcn0ym34mf3twh8eqqqqa8fq22"}}'; diff --git a/website/src/tabs/rest/GetBlockByHash.jsx b/website/src/tabs/rest/GetBlockByHash.jsx index df75c94f5..d99ba4c47 100644 --- a/website/src/tabs/rest/GetBlockByHash.jsx +++ b/website/src/tabs/rest/GetBlockByHash.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Card, Divider, Form, Input, Row, Col } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -42,14 +42,11 @@ export const GetBlockByHash = () => { const layout = { labelCol: { span: 4 }, wrapperCol: { span: 21 } }; const blockString = useMemo(() => { - return blockByHash !== null ? blockByHash.toString() : "" + return blockByHash !== null ? blockByHash.toString() : ""; }, [blockByHash]); return ( - <Card - title="Get Block By Hash" - style={{ width: "100%"}} - > + <Card title="Get Block By Hash" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="Block Hash" diff --git a/website/src/tabs/rest/GetBlockByHeight.jsx b/website/src/tabs/rest/GetBlockByHeight.jsx index a47edeaad..4182bd8e9 100644 --- a/website/src/tabs/rest/GetBlockByHeight.jsx +++ b/website/src/tabs/rest/GetBlockByHeight.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Card, Divider, Form, Input, Row, Col } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -44,14 +44,11 @@ export const GetBlockByHeight = () => { const layout = { labelCol: { span: 4 }, wrapperCol: { span: 21 } }; const blockString = useMemo(() => { - return blockByHeight !== null ? blockByHeight.toString() : "" + return blockByHeight !== null ? blockByHeight.toString() : ""; }, [blockByHeight]); return ( - <Card - title="Get Block By Height" - style={{ width: "100%" }} - > + <Card title="Get Block By Height" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="Block Height" diff --git a/website/src/tabs/rest/GetLatestBlock.jsx b/website/src/tabs/rest/GetLatestBlock.jsx index ff7d64289..439df1ea6 100644 --- a/website/src/tabs/rest/GetLatestBlock.jsx +++ b/website/src/tabs/rest/GetLatestBlock.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Button, Card, Col, Divider, Form, Input, Row } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -22,22 +22,14 @@ export const GetLatestBlock = () => { const layout = { labelCol: { span: 3 }, wrapperCol: { span: 21 } }; const latestBlockString = useMemo(() => { - return latestBlock !== null ? latestBlock.toString() : "" + return latestBlock !== null ? latestBlock.toString() : ""; }, [latestBlock]); return ( - <Card - title="Get Latest Block" - style={{ width: "100%" }} - > + <Card title="Get Latest Block" style={{ width: "100%" }}> <Row justify="center"> <Col> - <Button - type="primary" - - size="middle" - onClick={tryRequest} - > + <Button type="primary" size="middle" onClick={tryRequest}> Get Latest Block </Button> </Col> diff --git a/website/src/tabs/rest/GetLatestBlockHeight.jsx b/website/src/tabs/rest/GetLatestBlockHeight.jsx index c29f52bbf..03fef35c0 100644 --- a/website/src/tabs/rest/GetLatestBlockHeight.jsx +++ b/website/src/tabs/rest/GetLatestBlockHeight.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Button, Card, Col, Divider, Form, Input, Row } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -22,22 +22,14 @@ export const GetLatestBlockHeight = () => { const layout = { labelCol: { span: 3 }, wrapperCol: { span: 21 } }; const latestHeightString = useMemo(() => { - return latestHeight !== null ? latestHeight.toString() : "" + return latestHeight !== null ? latestHeight.toString() : ""; }, [latestHeight]); return ( - <Card - title="Get Latest Block Height" - style={{ width: "100%" }} - > + <Card title="Get Latest Block Height" style={{ width: "100%" }}> <Row justify="center"> <Col> - <Button - type="primary" - - size="middle" - onClick={tryRequest} - > + <Button type="primary" size="middle" onClick={tryRequest}> Get Latest Block Height </Button> </Col> @@ -52,9 +44,7 @@ export const GetLatestBlockHeight = () => { placeholder="Block" value={latestHeightString} addonAfter={ - <CopyButton - data={latestHeightString} - /> + <CopyButton data={latestHeightString} /> } disabled /> diff --git a/website/src/tabs/rest/GetMappingNames.jsx b/website/src/tabs/rest/GetMappingNames.jsx index 47dd924ee..09e7a5eb4 100644 --- a/website/src/tabs/rest/GetMappingNames.jsx +++ b/website/src/tabs/rest/GetMappingNames.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Button, Card, Col, Divider, Form, Input, Row } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -58,11 +58,11 @@ export const GetMappingNames = () => { const layout = { labelCol: { span: 4 }, wrapperCol: { span: 21 } }; const mappingString = useMemo(() => { - return mapping !== null ? mapping : "" + return mapping !== null ? mapping : ""; }, [mapping]); const programIDString = useMemo(() => { - return programID !== null ? programID : "" + return programID !== null ? programID : ""; }, [programID]); return ( @@ -72,7 +72,6 @@ export const GetMappingNames = () => { extra={ <Button type="primary" - size="middle" onClick={() => { tryRequest("credits.aleo"); diff --git a/website/src/tabs/rest/GetMappingValue.jsx b/website/src/tabs/rest/GetMappingValue.jsx index 98ec9e2bb..ea9334442 100644 --- a/website/src/tabs/rest/GetMappingValue.jsx +++ b/website/src/tabs/rest/GetMappingValue.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Button, Card, Col, Divider, Form, Input, Result, Row } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -44,7 +44,7 @@ export const GetMappingValue = () => { }; const mappingErrorString = useMemo(() => { - return mappingError !== null ? mappingError : "" + return mappingError !== null ? mappingError : ""; }, [mappingError]); // Attempts to request the program bytecode with the given program id. @@ -93,7 +93,6 @@ export const GetMappingValue = () => { extra={ <Button type="primary" - size="middle" onClick={() => { setDefaultRequest( @@ -141,12 +140,7 @@ export const GetMappingValue = () => { </Form> <Row justify="center"> <Col> - <Button - type="primary" - - size="middle" - onClick={tryRequest} - > + <Button type="primary" size="middle" onClick={tryRequest}> Get Mapping Value </Button> </Col> diff --git a/website/src/tabs/rest/GetProgram.jsx b/website/src/tabs/rest/GetProgram.jsx index 439696b13..023802b75 100644 --- a/website/src/tabs/rest/GetProgram.jsx +++ b/website/src/tabs/rest/GetProgram.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Button, Card, Col, Divider, Form, Input, Row } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -56,12 +56,12 @@ export const GetProgram = () => { const layout = { labelCol: { span: 4 }, wrapperCol: { span: 21 } }; const programString = useMemo(() => { - return program !== null ? program : "" + return program !== null ? program : ""; }, [program]); const programIDString = useMemo(() => { - return programID !== null ? programID : "" - }, [programID]) + return programID !== null ? programID : ""; + }, [programID]); return ( <Card @@ -70,7 +70,6 @@ export const GetProgram = () => { extra={ <Button type="primary" - size="middle" onClick={() => { tryRequest("credits.aleo"); diff --git a/website/src/tabs/rest/GetTransaction.jsx b/website/src/tabs/rest/GetTransaction.jsx index 944a97eff..630e51d7e 100644 --- a/website/src/tabs/rest/GetTransaction.jsx +++ b/website/src/tabs/rest/GetTransaction.jsx @@ -1,4 +1,4 @@ -import {useMemo, useState} from "react"; +import { useMemo, useState } from "react"; import { Card, Divider, Form, Input, Row, Col } from "antd"; import axios from "axios"; import { CopyButton } from "../../components/CopyButton"; @@ -21,7 +21,9 @@ export const GetTransaction = () => { try { if (id) { axios - .get(`https://api.provable.com/v2/testnet/transaction/${id}`) + .get( + `https://api.provable.com/v2/testnet/transaction/${id}`, + ) .then((response) => { setTransaction(JSON.stringify(response.data, null, 2)); setStatus("success"); @@ -42,14 +44,11 @@ export const GetTransaction = () => { const layout = { labelCol: { span: 4 }, wrapperCol: { span: 21 } }; const transactionString = useMemo(() => { - return transaction !== null ? transaction.toString() : "" + return transaction !== null ? transaction.toString() : ""; }, [transaction]); return ( - <Card - title="Get Transaction" - style={{ width: "100%" }} - > + <Card title="Get Transaction" style={{ width: "100%" }}> <Form {...layout}> <Form.Item label="Transaction ID" diff --git a/website/src/workers/worker.js b/website/src/workers/worker.js index 797f4c4d0..3a742d3de 100644 --- a/website/src/workers/worker.js +++ b/website/src/workers/worker.js @@ -4,7 +4,11 @@ await aleo.initThreadPool(); const defaultHost = "https://api.provable.com/v2"; const keyProvider = new aleo.AleoKeyProvider(); -const programManager = new aleo.ProgramManager(defaultHost, keyProvider, undefined); +const programManager = new aleo.ProgramManager( + defaultHost, + keyProvider, + undefined, +); keyProvider.useCache(true); self.postMessage({ @@ -21,27 +25,41 @@ self.addEventListener("message", (ev) => { (async function () { try { - const privateKeyObject = aleo.PrivateKey.from_string(privateKey); + const privateKeyObject = + aleo.PrivateKey.from_string(privateKey); // Ensure the program is valid and that it contains the function specified - const program = programManager.createProgramFromSource(localProgram); + const program = + programManager.createProgramFromSource(localProgram); const program_id = program.id(); if (!program.hasFunction(aleoFunction)) { - throw new Error(`Program ${program_id} does not contain function ${aleoFunction}`); + throw new Error( + `Program ${program_id} does not contain function ${aleoFunction}`, + ); } const cacheKey = `${program_id}:${aleoFunction}`; // Get the program imports - const imports = programManager.networkClient.getProgramImports(localProgram); + const imports = + programManager.networkClient.getProgramImports( + localProgram, + ); // Get the proving and verifying keys for the function if (lastLocalProgram !== localProgram) { - const keys = await programManager.synthesizeKeys(localProgram, aleoFunction, inputs, privateKeyObject); + const keys = await programManager.synthesizeKeys( + localProgram, + aleoFunction, + inputs, + privateKeyObject, + ); programManager.keyProvider.cacheKeys(cacheKey, keys); lastLocalProgram = localProgram; } // Pass the cache key to the execute function - const keyParams = new aleo.AleoKeyProviderParams({"cacheKey": cacheKey}); + const keyParams = new aleo.AleoKeyProviderParams({ + cacheKey: cacheKey, + }); // Execute the function locally let response = await programManager.run( @@ -54,17 +72,26 @@ self.addEventListener("message", (ev) => { undefined, undefined, privateKeyObject, - undefined + undefined, ); // Return the outputs to the main thread - console.log(`Web worker: Local execution completed in ${performance.now() - startTime} ms`); + console.log( + `Web worker: Local execution completed in ${performance.now() - startTime} ms`, + ); const outputs = response.getOutputs(); let execution = response.getExecution(); if (execution) { - aleo.verifyFunctionExecution(execution, keyProvider.getKeys(cacheKey)[1], program, "hello"); + aleo.verifyFunctionExecution( + execution, + keyProvider.getKeys(cacheKey)[1], + program, + "hello", + ); execution = execution.toString(); - console.log("Execution verified successfully: " + execution); + console.log( + "Execution verified successfully: " + execution, + ); } else { execution = ""; } @@ -72,7 +99,7 @@ self.addEventListener("message", (ev) => { console.log(`Function execution response: ${outputs}`); self.postMessage({ type: "OFFLINE_EXECUTION_COMPLETED", - outputs: {outputs: outputs, execution: execution} + outputs: { outputs: outputs, execution: execution }, }); } catch (error) { console.error(error); @@ -99,27 +126,42 @@ self.addEventListener("message", (ev) => { (async function () { try { - const privateKeyObject = aleo.PrivateKey.from_string(privateKey) + const privateKeyObject = + aleo.PrivateKey.from_string(privateKey); // Ensure the program is valid and that it contains the function specified - const program = await programManager.createProgramFromSource(remoteProgram); + const program = + await programManager.createProgramFromSource(remoteProgram); const program_id = program.id(); if (!program.hasFunction(aleoFunction)) { - throw new Error(`Program ${program_id} does not contain function ${aleoFunction}`); + throw new Error( + `Program ${program_id} does not contain function ${aleoFunction}`, + ); } // Get the proving and verifying keys for the function const cacheKey = `${program_id}:${aleoFunction}`; if (!programManager.keyProvider.containsKeys(cacheKey)) { - console.log(`Web worker: Synthesizing proving & verifying keys for: '${program_id}:${aleoFunction}'`); - const keys = await programManager.synthesizeKeys(remoteProgram, aleoFunction, inputs, privateKeyObject); + console.log( + `Web worker: Synthesizing proving & verifying keys for: '${program_id}:${aleoFunction}'`, + ); + const keys = await programManager.synthesizeKeys( + remoteProgram, + aleoFunction, + inputs, + privateKeyObject, + ); programManager.keyProvider.cacheKeys(cacheKey, keys); } // Pass the cache key to the execute function - const keyParams = new aleo.AleoKeyProviderParams({"cacheKey": cacheKey}) + const keyParams = new aleo.AleoKeyProviderParams({ + cacheKey: cacheKey, + }); // Set the host to the provided URL if provided - if (typeof url === "string") { programManager.setHost(url); } + if (typeof url === "string") { + programManager.setHost(url); + } const transaction = await programManager.execute({ programName: program_id, functionName: aleoFunction, @@ -128,11 +170,13 @@ self.addEventListener("message", (ev) => { inputs: inputs, keySearchParams: keyParams, feeRecord: feeRecord, - privateKey: privateKeyObject + privateKey: privateKeyObject, }); // Return the transaction id to the main thread - console.log(`Web worker: On-chain execution transaction created in ${performance.now() - startTime} ms`); + console.log( + `Web worker: On-chain execution transaction created in ${performance.now() - startTime} ms`, + ); self.postMessage({ type: "EXECUTION_TRANSACTION_COMPLETED", executeTransaction: transaction, @@ -156,41 +200,67 @@ self.addEventListener("message", (ev) => { (async function () { try { - const privateKeyObject = aleo.PrivateKey.from_string(privateKey); + const privateKeyObject = + aleo.PrivateKey.from_string(privateKey); // Ensure the program is valid and that it contains the function specified - const program = await programManager.networkClient.getProgramObject(remoteProgram); + const program = + await programManager.networkClient.getProgramObject( + remoteProgram, + ); const program_id = program.id(); if (!program.getFunctions().includes(aleoFunction)) { - throw new Error(`Program ${program_id} does not contain function ${aleoFunction}`); + throw new Error( + `Program ${program_id} does not contain function ${aleoFunction}`, + ); } const cacheKey = `${program_id}:${aleoFunction}`; - const imports = await programManager.networkClient.getProgramImports(remoteProgram); + const imports = + await programManager.networkClient.getProgramImports( + remoteProgram, + ); // Get the proving and verifying keys for the function if (!programManager.keyProvider.containsKeys(cacheKey)) { - console.log(`Web worker: Synthesizing proving & verifying keys for: '${program_id}:${aleoFunction}'`); - const keys = await programManager.synthesizeKeys(program.toString(), aleoFunction, inputs, privateKeyObject); + console.log( + `Web worker: Synthesizing proving & verifying keys for: '${program_id}:${aleoFunction}'`, + ); + const keys = await programManager.synthesizeKeys( + program.toString(), + aleoFunction, + inputs, + privateKeyObject, + ); programManager.keyProvider.cacheKeys(cacheKey, keys); } let edition = 1; try { - edition = programManager.networkClient.getLatestProgramEdition(program_id); + edition = + programManager.networkClient.getLatestProgramEdition( + program_id, + ); } catch { - console.warn(`Error finding edition for ${program_id}. Assuming edition 1.`); + console.warn( + `Error finding edition for ${program_id}. Assuming edition 1.`, + ); } // Estimate the execution fee - let executeFee = await aleo.ProgramManagerBase.estimateExecutionFee( - remoteProgram, - aleoFunction, - imports, - edition - ); + let executeFee = + await aleo.ProgramManagerBase.estimateExecutionFee( + remoteProgram, + aleoFunction, + imports, + edition, + ); // Return the execution fee estimate to the main thread - console.log(`Web worker: Execution fee estimated in ${performance.now() - startTime} ms`); - console.log(`Execution Fee Estimation: ${executeFee} microcredits`); + console.log( + `Web worker: Execution fee estimated in ${performance.now() - startTime} ms`, + ); + console.log( + `Execution Fee Estimation: ${executeFee} microcredits`, + ); self.postMessage({ type: "EXECUTION_FEE_ESTIMATION_COMPLETED", executionFee: Number(executeFee) / 1000000 + 0.01, @@ -211,7 +281,10 @@ self.addEventListener("message", (ev) => { let startTime = performance.now(); (async function () { try { - const imports = await programManager.networkClient.getProgramImports(program); + const imports = + await programManager.networkClient.getProgramImports( + program, + ); console.log("Estimating deployment fee.."); let deploymentFee = await aleo.ProgramManagerBase.estimateDeploymentFee( @@ -220,8 +293,12 @@ self.addEventListener("message", (ev) => { ); // Return the deployment fee estimate to the main thread - console.log(`Web worker: Deployment fee estimation completed in ${performance.now() - startTime} ms`); - console.log(`Deployment Fee Estimation: ${deploymentFee} microcredits`); + console.log( + `Web worker: Deployment fee estimation completed in ${performance.now() - startTime} ms`, + ); + console.log( + `Deployment Fee Estimation: ${deploymentFee} microcredits`, + ); self.postMessage({ type: "DEPLOYMENT_FEE_ESTIMATION_COMPLETED", deploymentFee: Number(deploymentFee) / 1000000 + 0.01, @@ -255,7 +332,9 @@ self.addEventListener("message", (ev) => { (async function () { try { // Set the host to the provided URL if provided - if (typeof url === "string") { programManager.setHost(url); } + if (typeof url === "string") { + programManager.setHost(url); + } // Create the transfer transaction and submit it to the network const transaction = await programManager.transfer( @@ -268,11 +347,13 @@ self.addEventListener("message", (ev) => { amountRecord, feeRecord, aleo.PrivateKey.from_string(privateKey), - undefined + undefined, ); // Return the transaction id to the main thread - console.log(`Web worker: Transfer transaction ${transaction} created in ${performance.now() - startTime} ms`); + console.log( + `Web worker: Transfer transaction ${transaction} created in ${performance.now() - startTime} ms`, + ); self.postMessage({ type: "TRANSFER_TRANSACTION_COMPLETED", transferTransaction: transaction, @@ -288,7 +369,8 @@ self.addEventListener("message", (ev) => { } })(); } else if (ev.data.type === "ALEO_DEPLOY") { - const { program, privateKey, fee, privateFee, feeRecord, url } = ev.data; + const { program, privateKey, fee, privateFee, feeRecord, url } = + ev.data; console.log("Web worker: Creating deployment..."); @@ -296,19 +378,26 @@ self.addEventListener("message", (ev) => { (async function () { try { // Set the network client host if specified - if (typeof url === "string") { programManager.setHost(url); } + if (typeof url === "string") { + programManager.setHost(url); + } // Check if the program is valid try { - const programObject = programManager.createProgramFromSource(program); + const programObject = + programManager.createProgramFromSource(program); } catch (error) { - throw new Error(`Invalid program, ensure the program is valid and try again.`); + throw new Error( + `Invalid program, ensure the program is valid and try again.`, + ); } // Check if the program already exists on the network. If so, throw an error let programExists = false; try { - await programManager.networkClient.getProgram(programObject.id()); + await programManager.networkClient.getProgram( + programObject.id(), + ); programExists = true; } catch (e) { console.log( @@ -317,23 +406,30 @@ self.addEventListener("message", (ev) => { } if (programExists) { - throw new Error(`Program ${programObject.id()} already exists on the network`); + throw new Error( + `Program ${programObject.id()} already exists on the network`, + ); } // Create the deployment transaction and submit it to the network - let transaction = await programManager.buildDeploymentTransaction( - program, - fee, - privateFee, - undefined, - feeRecord, - aleo.PrivateKey.from_string(privateKey), - ) - await programManager.networkClient.submitTransaction(transaction); + let transaction = + await programManager.buildDeploymentTransaction( + program, + fee, + privateFee, + undefined, + feeRecord, + aleo.PrivateKey.from_string(privateKey), + ); + await programManager.networkClient.submitTransaction( + transaction, + ); const transaction_id = transaction.id(); // Return the transaction id to the main thread - console.log(`Web worker: Deployment transaction ${transaction_id} created in ${performance.now() - startTime} ms`); + console.log( + `Web worker: Deployment transaction ${transaction_id} created in ${performance.now() - startTime} ms`, + ); self.postMessage({ type: "DEPLOY_TRANSACTION_COMPLETED", deployTransaction: transaction, @@ -357,18 +453,22 @@ self.addEventListener("message", (ev) => { (async function () { try { // Set the network client host if specified - if (typeof url === "string") { programManager.setHost(url); } + if (typeof url === "string") { + programManager.setHost(url); + } // Create the split transaction and submit to the network const transaction = await programManager.split( splitAmount, record, aleo.PrivateKey.from_string(privateKey), - undefined + undefined, ); // Return the transaction id to the main thread - console.log(`Web worker: Split transaction ${transaction} created in ${performance.now() - startTime} ms`); + console.log( + `Web worker: Split transaction ${transaction} created in ${performance.now() - startTime} ms`, + ); self.postMessage({ type: "SPLIT_TRANSACTION_COMPLETED", splitTransaction: transaction, @@ -384,17 +484,25 @@ self.addEventListener("message", (ev) => { } })(); } else if (ev.data.type === "ALEO_JOIN") { - const { recordOne, recordTwo, fee, privateFee, feeRecord, privateKey, url } = - ev.data; + const { + recordOne, + recordTwo, + fee, + privateFee, + feeRecord, + privateKey, + url, + } = ev.data; console.log("Web worker: Creating join..."); let startTime = performance.now(); (async function () { - try { // Set the network client host if specified - if (typeof url === "string") { programManager.setHost(url); } + if (typeof url === "string") { + programManager.setHost(url); + } // Create the join transaction and submit it to the network const transaction = await programManager.join( @@ -405,11 +513,13 @@ self.addEventListener("message", (ev) => { undefined, feeRecord, aleo.PrivateKey.from_string(privateKey), - undefined + undefined, ); // Return the transaction id to the main thread - console.log(`Web worker: Join transaction ${transaction} created in ${performance.now() - startTime} ms`); + console.log( + `Web worker: Join transaction ${transaction} created in ${performance.now() - startTime} ms`, + ); self.postMessage({ type: "JOIN_TRANSACTION_COMPLETED", joinTransaction: transaction, diff --git a/website/vercel.json b/website/vercel.json index c02783adf..6b263c3ac 100644 --- a/website/vercel.json +++ b/website/vercel.json @@ -1,27 +1,27 @@ { - "rewrites": [ - { - "source": "/(.*)", - "destination": "/index.html" - } - ], - "headers": [ - { - "source": "/(.*)", - "headers": [ + "rewrites": [ { - "key": "Cross-Origin-Opener-Policy", - "value": "same-origin" - }, - { - "key": "Cross-Origin-Resource-Policy", - "value": "same-site" - }, + "source": "/(.*)", + "destination": "/index.html" + } + ], + "headers": [ { - "key": "Cross-Origin-Embedder-Policy", - "value": "require-corp" + "source": "/(.*)", + "headers": [ + { + "key": "Cross-Origin-Opener-Policy", + "value": "same-origin" + }, + { + "key": "Cross-Origin-Resource-Policy", + "value": "same-site" + }, + { + "key": "Cross-Origin-Embedder-Policy", + "value": "require-corp" + } + ] } - ] - } - ] + ] } diff --git a/website/vite.config.js b/website/vite.config.js index 8a5d8caac..26d972571 100644 --- a/website/vite.config.js +++ b/website/vite.config.js @@ -3,7 +3,7 @@ import react from "@vitejs/plugin-react-swc"; // https://vitejs.dev/config/ export default defineConfig({ -assetsInclude: ['**/*.wasm'], + assetsInclude: ["**/*.wasm"], worker: { format: "es", }, diff --git a/website/webpack.config.js b/website/webpack.config.js index f2eec9a26..00bb56357 100644 --- a/website/webpack.config.js +++ b/website/webpack.config.js @@ -7,11 +7,10 @@ import path from "path"; const env = dotenv.config().parsed || {}; const envKeys = Object.keys(env).reduce((acc, key) => { - acc[`process.env.${key}`] = JSON.stringify(env[key]); - return acc; + acc[`process.env.${key}`] = JSON.stringify(env[key]); + return acc; }, {}); - const appConfig = { mode: "production", entry: { @@ -55,7 +54,8 @@ const appConfig = { { from: "public", to: "public" }, { from: "_headers", to: "." }, { from: "_redirects", to: "." }, - { from: "vercel.json", to: "." }], + { from: "vercel.json", to: "." }, + ], }), new HtmlWebpackPlugin({ template: "./index.html", @@ -64,12 +64,14 @@ const appConfig = { ], optimization: { minimize: true, - minimizer: [new TerserPlugin({ - terserOptions: { - module: true, - } - })], - }, + minimizer: [ + new TerserPlugin({ + terserOptions: { + module: true, + }, + }), + ], + }, performance: { hints: false, maxAssetSize: 13 * 1024 * 1024, // 12 MiB