diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 000000000..4e8422ad1 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1,88 @@ +module.exports = { + env: { + browser: true, + es2020: true, + }, + extends: [ + 'plugin:prettier/recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + ], + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + tsconfigRootDir: __dirname, + project: [ + './packages/**/tsconfig.json', + './docs/**/tsconfig.json', + '**/tsconfig.eslint.json', + ], + }, + plugins: ['@typescript-eslint', 'import'], + rules: { + 'prettier/prettier': 'warn', + quotes: [ + 2, + 'single', + { + avoidEscape: true, + }, + ], + 'import/no-unresolved': [ + 2, + { + ignore: [ + '@concordium/rust-bindings', + 'grpc-api', + '^#.+$', // ESLint resolver does not support subpath imports: https://github.com/import-js/eslint-plugin-import/issues/1868. + ], + }, + ], + 'import/no-extraneous-dependencies': [ + 'error', + { + devDependencies: ['**/*/test/*', '**/*.config.js'], + }, + ], + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + ignoreRestSiblings: true, + }, + ], + }, + overrides: [ + { + files: ['*.config.js'], + rules: { + '@typescript-eslint/no-var-requires': 'off', + 'import/namespace': 'off', + }, + }, + ], + ignorePatterns: [ + '**/pkg/**/', + '**/dist/**/', + '**/lib/**/', + 'deps/**/*', + '**/src/grpc-api/*', + 'typedoc/**', + ], + settings: { + 'import/ignore': ['bs58check'], + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + 'import/resolver': { + exports: true, + typescript: { + project: ['packages/*/tsconfig.json'], + }, + node: { + project: ['packages/*/tsconfig.json'], + }, + }, + }, +}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index e9cd6365b..000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "env": { - "browser": true, - "es2020": true - }, - "extends": [ - "plugin:prettier/recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/typescript" - ], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module", - "project": [ "./packages/**/tsconfig.eslint.json", "./tsconfig.eslint.json", "./examples/tsconfig.eslint.json"] - }, - "plugins": [ - "@typescript-eslint", - "import" - ], - "rules": { - "quotes": [2, "single", { "avoidEscape": true }], - "import/no-unresolved": [2, { "ignore": ["@concordium/rust-bindings", "@concordium/common-sdk", "@concordium/node-sdk", "grpc"]}], - "import/no-extraneous-dependencies": ["error", {"devDependencies": ["**/*.test.ts", "**/*.test.tsx", "**/*.config.js"]}], - "@typescript-eslint/no-unused-vars": ["warn", { "ignoreRestSiblings": true }] - }, - "overrides": [ - { - "files": ["*.config.js"], - "rules": { - "@typescript-eslint/no-var-requires": "off", - "import/namespace": "off" - } - } - ], - "ignorePatterns": ["**/pkg/**/", "**/lib/**/", "deps/**/*", "**/nodejs/grpc/*", "**/common/grpc/*", "typedoc/**"], - "settings": { - "import/ignore": [ - "bs58check" - ], - "import/parsers": { - "@typescript-eslint/parser": [".ts", ".tsx"] - }, - "import/resolver": { - "typescript": { - "project": ["packages/*/tsconfig.json"] - }, - "node": { - "project": ["packages/*/tsconfig.json"] - } - } - } -} diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 5fb4c4d02..8d3187ba1 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -4,7 +4,7 @@ on: # Allows you to run this workflow manually from the Actions tab workflow_dispatch: -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages + # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read pages: write @@ -38,7 +38,8 @@ jobs: with: workspaces: | packages/rust-bindings - deps/concordium-deps + deps/concordium-base/rust-src + deps/concordium-base/concordium-contracts-common - name: Get wasm-pack uses: jetli/wasm-pack-action@v0.4.0 @@ -49,11 +50,22 @@ jobs: id: yarn-cache uses: actions/cache@v3 with: - path: node_modules + path: | + ./node_modules + ./docs/node_modules key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} restore-keys: | ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} + - name: Cache GRPC + id: cache-grpc + uses: actions/cache@v3 + with: + path: | + ./packages/sdk/src/grpc-api + key: ${{ runner.os }}-grpc-${{ hashFiles('deps/concordium-base/concordium-grpc-api') }} + restore-keys: ${{ runner.os }}-grpc + - name: Get dependencies if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn install --immutable @@ -66,19 +78,17 @@ jobs: with: name: build-release path: | - packages/*/lib - packages/*/grpc - packages/*/package.json - packages/*/README.md - packages/rust-bindings/pkg/*/concordium_rust_bindings* + ./packages/sdk/lib + ./packages/sdk/src + ./packages/rust-bindings/lib + ./packages/*/package.json + ./packages/*/README.md build-typedoc: runs-on: ubuntu-22.04 needs: build steps: - uses: actions/checkout@v3 - with: - submodules: "recursive" - uses: actions/setup-node@v3 with: @@ -93,11 +103,13 @@ jobs: - name: Restore cached dependencies uses: actions/cache/restore@v3 with: - path: node_modules + path: | + ./node_modules + ./docs/node_modules key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} - name: Generate typedoc documentation - run: yarn typedoc + run: yarn build:docs - name: Store typedoc uses: ./.github/actions/upload-artifact diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index d2d429624..6064a49e1 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -7,254 +7,315 @@ on: pull_request: branches: [main, release**, feature**] # Don't run on draft PR's, see: https://github.com/orgs/community/discussions/25722#discussioncomment-3248917 - types: [opened, synchronize, reopened, ready_for_review] + types: [ opened, synchronize, reopened, ready_for_review ] # Allows us to run the workflow manually from the Actions tab workflow_dispatch: env: - NODE_VERSION: 18.16.0 - RUST_VERSION: 1.65 - RUST_FMT: nightly-2023-04-01-x86_64-unknown-linux-gnu + DUMMY: 2 # For cache busting. + NODE_VERSION: 18.16.0 + RUST_VERSION: 1.65 + RUST_FMT: nightly-2023-04-01-x86_64-unknown-linux-gnu jobs: - build: - if: github.event.pull_request.draft == false - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - with: - submodules: "recursive" - - - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: yarn - - - name: Cache GRPC - id: cache-grpc - uses: actions/cache@v3 - with: - path: | - ./packages/common/grpc - ./packages/nodejs/grpc - key: ${{ runner.os }}-grpc-${{ hashFiles('deps/concordium-base/concordium-grpc-api') }} - - - name: Cache WebPack - uses: actions/cache@v3 - with: - path: ./packages/web/.webpack-cache - # These two lines ensure that a fresh cache is generated after each run - key: ${{ runner.os }}-webpack-${{ github.run_id }} - restore-keys: ${{ runner.os }}-webpack - - - name: Get dependencies - run: yarn install --immutable - - - name: Install rust - run: rustup default ${{ env.RUST_VERSION }} - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: | - packages/rust-bindings - deps/concordium-deps - - - name: Get wasm-pack - uses: jetli/wasm-pack-action@v0.4.0 - with: - version: 'latest' - - - name: Build GRPC bindings - if: steps.cache-grpc.outputs.cache-hit != 'true' - run: | - mkdir -p packages/common/grpc - mkdir -p packages/nodejs/grpc - yarn workspace @concordium/common-sdk generate - yarn workspace @concordium/node-sdk generate - - - name: Build - run: yarn build:dev - - - name: Store build-debug - uses: ./.github/actions/upload-artifact - with: - name: build-debug - path: | - ./node_modules/@concordium - ./packages/rust-bindings/pkg - ./packages/common/lib - ./packages/nodejs/lib - ./packages/common/grpc - ./packages/nodejs/grpc - ./packages/ccd-js-gen/lib - - typecheck-examples: - runs-on: ubuntu-22.04 - needs: build - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: yarn - - - name: Get build-debug - uses: ./.github/actions/download-artifact - with: - name: build-debug - - - name: Get dependencies - run: yarn install --immutable - - - name: Typecheck examples - run: yarn workspace @concordium/examples typecheck - - common-tests: - runs-on: ubuntu-22.04 - needs: build - defaults: - run: - working-directory: packages/common - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: yarn - - - name: Get build-debug - uses: ./.github/actions/download-artifact - with: - name: build-debug - - - name: Get dependencies - run: yarn install --immutable - - - name: Run Tests - run: yarn test - - typedoc: - runs-on: ubuntu-22.04 - needs: build - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: yarn - - - name: Get build-debug - uses: ./.github/actions/download-artifact - with: - name: build-debug - - - name: Get dependencies - run: yarn install --immutable - - - name: Build typedoc - run: yarn typedoc - - lint: - runs-on: ubuntu-22.04 - needs: build - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: yarn - - - name: Get build-debug - uses: ./.github/actions/download-artifact - with: - name: build-debug - - - name: Install dependencies - run: yarn install --immutable - - - name: Lint - run: yarn lint - - markdown-lint: - runs-on: ubuntu-22.04 - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: yarn - - - name: Install dependencies - run: yarn install --immutable - - - name: Lint markdown - run: yarn markdown:lint - - markdown-linkcheck: - runs-on: ubuntu-22.04 - if: github.event.pull_request.draft == false - steps: - - uses: actions/checkout@v3 - - - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_VERSION }} - cache: yarn - - - name: Install dependencies - run: yarn install --immutable - - - name: Lint markdown - run: yarn markdown:linkcheck - - rust_lint_fmt: - runs-on: ubuntu-22.04 - if: github.event.pull_request.draft == false - defaults: - run: - working-directory: packages/rust-bindings - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Install formatter - run: | - rustup default ${{ env.RUST_FMT }} - rustup component add rustfmt - - - name: Format - run: cargo fmt -- --color=always --check - - rust_lint_clippy: - runs-on: ubuntu-22.04 - needs: rust_lint_fmt - if: github.event.pull_request.draft == false - defaults: - run: - working-directory: packages/rust-bindings - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: "recursive" - - - name: Install clippy - run: | - rustup default ${{ env.RUST_VERSION }} - rustup component add clippy - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: | - packages/rust-bindings - deps/concordium-deps - - - name: Run clippy - run: cargo clippy --color=always --tests --benches -- -Dclippy::all + deps: + if: github.event.pull_request.draft == false + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Cache dependencies + id: yarn-cache + uses: actions/cache@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ env.DUMMY }} + restore-keys: | + ${{ runner.os }}-yarn + + - name: Get dependencies + run: yarn install --immutable + + build: + if: github.event.pull_request.draft == false + runs-on: ubuntu-22.04 + needs: deps + steps: + - uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Cache GRPC + id: cache-grpc + uses: actions/cache@v3 + with: + path: | + ./packages/sdk/src/grpc-api + key: ${{ runner.os }}-grpc-${{ hashFiles('deps/concordium-base/concordium-grpc-api') }}-${{ env.DUMMY }} + restore-keys: ${{ runner.os }}-grpc + + - name: Cache WebPack + uses: actions/cache@v3 + with: + path: ./packages/web/.webpack-cache + # These two lines ensure that a fresh cache is generated after each run + key: ${{ runner.os }}-webpack-${{ github.run_id }} + restore-keys: ${{ runner.os }}-webpack + + - name: Restore dependencies + uses: actions/cache/restore@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn + + - name: Install rust + run: rustup default ${{ env.RUST_VERSION }} + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + packages/rust-bindings + deps/concordium-base/rust-src + deps/concordium-base/concordium-contracts-common + + - name: Build + run: yarn build + + - name: Store build-debug + uses: ./.github/actions/upload-artifact + with: + name: build-debug + # sdk/src is needed here because of sdk/src/grpc-api + path: | + ./packages/rust-bindings/lib + ./packages/sdk/lib + ./packages/sdk/src + ./packages/ccd-js-gen/lib + + typecheck-examples: + runs-on: ubuntu-22.04 + needs: build + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Restore dependencies + uses: actions/cache/restore@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn + + - name: Get build-debug + uses: ./.github/actions/download-artifact + with: + name: build-debug + + - name: Typecheck examples + run: yarn workspace @concordium/examples typecheck + + tests: + runs-on: ubuntu-22.04 + needs: build + strategy: + matrix: + package: ['sdk', 'ccd-js-gen'] + defaults: + run: + working-directory: packages/${{matrix.package}} + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Restore dependencies + uses: actions/cache/restore@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn + + - name: Get build-debug + uses: ./.github/actions/download-artifact + with: + name: build-debug + + - name: Run Tests + run: yarn test-ci + + typedoc: + runs-on: ubuntu-22.04 + needs: build + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Restore dependencies + uses: actions/cache/restore@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn + + - name: Get build-debug + uses: ./.github/actions/download-artifact + with: + name: build-debug + + - name: Build typedoc + run: yarn build:docs + + lint: + runs-on: ubuntu-22.04 + needs: build + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Restore dependencies + uses: actions/cache/restore@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn + + - name: Get build-debug + uses: ./.github/actions/download-artifact + with: + name: build-debug + + - name: Lint + run: yarn lint + + markdown-lint: + runs-on: ubuntu-22.04 + if: github.event.pull_request.draft == false + needs: deps + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Restore dependencies + uses: actions/cache/restore@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn + + - name: Lint markdown + run: yarn markdown:lint + + markdown-linkcheck: + runs-on: ubuntu-22.04 + needs: deps + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_VERSION }} + cache: yarn + + - name: Restore dependencies + uses: actions/cache/restore@v3 + with: + path: | + ./node_modules + ./docs/node_modules + key: ${{ runner.os }}-yarn + + - name: Lint markdown + run: yarn markdown:linkcheck + + rust_lint_fmt: + runs-on: ubuntu-22.04 + if: github.event.pull_request.draft == false + defaults: + run: + working-directory: packages/rust-bindings + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Install formatter + run: | + rustup default ${{ env.RUST_FMT }} + rustup component add rustfmt + + - name: Format + run: cargo fmt -- --color=always --check + + rust_lint_clippy: + runs-on: ubuntu-22.04 + needs: rust_lint_fmt + if: github.event.pull_request.draft == false + defaults: + run: + working-directory: packages/rust-bindings + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Install clippy + run: | + rustup default ${{ env.RUST_VERSION }} + rustup component add clippy + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + packages/rust-bindings + deps/concordium-base/rust-src + deps/concordium-base/concordium-contracts-common + + - name: Run clippy + run: cargo clippy --color=always --tests --benches -- -Dclippy::all diff --git a/.gitignore b/.gitignore index b69a29000..15646c335 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ dist doc # Auto generate files from the gRPC proto file -grpc +grpc-api # Webpack cache .webpack-cache diff --git a/.markdown-linkcheck.json b/.markdown-linkcheck.json index 84e981113..72dc9011a 100644 --- a/.markdown-linkcheck.json +++ b/.markdown-linkcheck.json @@ -1,7 +1,7 @@ { - "ignorePatterns": [ - { - "pattern": "classes/Common_GRPC_Client.ConcordiumGRPCClient.html" - } - ] + "ignorePatterns": [ + { + "pattern": "classes/grpc.ConcordiumGRPCClient.html" + } + ] } diff --git a/.markdownlint.yaml b/.markdownlint.yaml index d93ada531..f3f457cbc 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -59,25 +59,26 @@ MD012: maximum: 1 # MD013/line-length - Line length -MD013: - # Number of characters - line_length: 80 - # Number of characters for headings - heading_line_length: 80 - # Number of characters for code blocks - code_block_line_length: 80 - # Include code blocks - code_blocks: false - # Include tables - tables: false - # Include headings - headings: false - # Include headings - headers: false - # Strict length checking - strict: false - # Stern length checking - stern: false +MD013: false +# MD013: +# # Number of characters +# line_length: 120 +# # Number of characters for headings +# heading_line_length: 120 +# # Number of characters for code blocks +# code_block_line_length: 120 +# # Include code blocks +# code_blocks: false +# # Include tables +# tables: false +# # Include headings +# headings: false +# # Include headings +# headers: false +# # Strict length checking +# strict: false +# # Stern length checking +# stern: false # MD014/commands-show-output - Dollar signs used before commands without showing output MD014: true @@ -167,9 +168,9 @@ MD035: style: "consistent" # MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading -MD036: +MD036: false # Punctuation characters - punctuation: ".,;:!?。,;:!?" + # punctuation: ".,;:!?。,;:!?" # MD037/no-space-in-emphasis - Spaces inside emphasis markers MD037: true diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..8db60caac --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/README.md b/README.md index 83fb004e4..ca9bd7ec0 100644 --- a/README.md +++ b/README.md @@ -10,19 +10,16 @@ Wrappers for interacting with the Concordium node. - [Concordium SDK for Javascript](#concordium-sdk-for-javascript) - [Documentation](#documentation) - [Packages](#packages) - - [Nodejs package](#nodejs-package) - - [Web package](#web-package) - - [Common package](#common-package) + - [SDK package](#sdk-package) - [Rust-bindings package](#rust-bindings-package) + - [Ccd-js-gen package](#ccd-js-gen-package) - [Install/updating dependencies](#installupdating-dependencies) - [MacOS arm64](#macos-arm64) - [Build](#build) - [Building for a release](#building-for-a-release) - [Building for development](#building-for-development) - [Making a new release](#making-a-new-release) - - [common](#common) - - [nodejs](#nodejs) - - [web](#web) + - [SDK](#sdk) - [rust-bindings](#rust-bindings) - [Test](#test) @@ -37,27 +34,21 @@ for more information Contains the different packages for the JS-SDKs. -### Nodejs package +### SDK package -The [Nodejs package](./packages/nodejs) contains the Nodejs SDK, particularly -the client that wraps the GRPC calls to the node, is located here. +The [SDK](./packages/sdk) contains the actual SDK, which can used in +both a web and NodeJS environment. -### Web package - -The [Web package](./packages/web) contains the Web SDK, which can used in -a web environment. - -### Common package +### Rust-bindings package -The [common package](./packages/common) contains the shared library for the -Nodejs and Web SDK's. The GRPC-client, all serialization and most utility -functions are located in this package. +The [rust-bindings](./packages/rust-bindings) contains bindings for Rust code, +which is used by the SDK through WASM. This package is a utility package that +should not be used directly, only through the usage of the SDK. -### Rust-bindings package +### Ccd-js-gen package -The [common package](./packages/common) contains bindings for Rust code, -which is used by the common package. This package is a utility package that -should not be used directly, only through the usage of the common package. +The [ccd-js-gen](./packages/ccd-js-gen) contains a library and CLI for +generating smart contract clients for TypeScript and JavaScript. ## Install/updating dependencies @@ -96,9 +87,6 @@ yarn build This will build all the subprojects. -Note that you must have [wasm-pack](https://rustwasm.github.io/wasm-pack/) -installed to build the project. - ### Building for development To build the project quickly during development run @@ -115,60 +103,26 @@ Note that this skips generating the grpc API and optimizing the wasm modules. The following describes the requirements for creating a new release for each of the packages contained in this repository. -### common +### SDK -- Bump the version in [package.json](./packages/common/package.json). -- Update the [CHANGELOG](./packages/common/CHANGELOG.md) describing the +- Bump the version in [package.json](./packages/sdk/package.json). +- Update the [CHANGELOG](./packages/sdk/CHANGELOG.md) describing the changes made. -- Update the dependency to common in the [web](./packages/web/package.json) - and [nodejs](./packages/nodejs/package.json) packages. -- Update the CHANGELOG in the [web](./packages/web/CHANGELOG.md) and - [nodejs](./packages/nodejs/CHANGELOG.md) packages. - - Add a change entry: Bumped @concordium/common-sdk to x.y.z. - Commit and tag the release. - - Tag should be `common/x.y.z`. + - Tag should be `sdk/x.y.z`. - Run the deploy workflow. - Under github actions, run the "deploy" workflow and download the `build-release` artifact. Unpack this file and use it for the release. - Publish the release to NPM. - - From the common package directory (packages/common) run `yarn npm publish` - -### nodejs - -- Bump the version in [package.json](./packages/nodejs/package.json). -- Update the [CHANGELOG](./packages/nodejs/CHANGELOG.md) describing the - changes made. -- Commit and tag the release. - - Tag should be `nodejs/x.y.z`. -- Build the release. - - Under github actions, run the "deploy" workflow and download the - `build-release` artifact. Unpack this file and use it for the release. -- Publish the release to NPM. - - From the nodejs package directory (packages/nodejs) run `yarn npm publish` - -### web - -- Bump the version in [package.json](./packages/web/package.json). -- Update the [CHANGELOG](./packages/web/CHANGELOG.md) describing the - changes made. -- Commit and tag the release. - - Tag should be `web/x.y.z`. -- Build the release. - - Under github actions, run the "deploy" workflow and download the - `build-release` artifact. Unpack this file and use it for the release. -- Publish the release to NPM. - - From the web package directory (packages/web) run `yarn npm publish` + - From the sdk package directory (packages/sdk) run `yarn npm publish` ### rust-bindings - Bump the version in [package.json](./packages/rust-bindings/package.json). - Update the [CHANGELOG](./packages/rust-bindings/CHANGELOG.md) describing the changes made. -- Update the dependency to rust-bindings in the - [common](./packages/common/package.json) and - [web](./packages/web/package.json) packages. -- Update the CHANGELOG in the [common](./packages/common/CHANGELOG.md) and - [web](./packages/web/CHANGELOG.md) packages. +- Update the dependency to rust-bindings in the [sdk](./packages/sdk/package.json) +- Update the CHANGELOG in the [sdk](./packages/sdk/CHANGELOG.md) - Add a change entry: Bumped @concordium/rust-bindings to x.y.z. - Commit and tag the release. - Tag should be `rust-bindings/x.y.z`. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..042c9bed1 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,10 @@ +# docs + +Holds documentation to be built into the documentation generated by typedoc. +Currently, this needs to use its own version of typescript due to incompatibility +with the version used in the rest of the workspace. + +## Project structure + +Pages reside in the `./pages` folder, and should be included by adding +a corresponding entry in `typedoc.json`. diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 000000000..d905e50bd --- /dev/null +++ b/docs/package.json @@ -0,0 +1,16 @@ +{ + "name": "docs", + "packageManager": "yarn@3.2.1", + "private": true, + "scripts": { + "build:docs": "typedoc" + }, + "devDependencies": { + "@knodes/typedoc-plugin-code-blocks": "^0.23.0", + "@knodes/typedoc-plugin-pages": "^0.23.0", + "typedoc": "^0.23", + "typedoc-plugin-merge-modules": "^4.0.1", + "typedoc-plugin-missing-exports": "^0.23", + "typescript": "5.0" + } +} diff --git a/pages/cis2-contracts.md b/docs/pages/cis2-contracts.md similarity index 99% rename from pages/cis2-contracts.md rename to docs/pages/cis2-contracts.md index b05a0e713..06d830406 100644 --- a/pages/cis2-contracts.md +++ b/docs/pages/cis2-contracts.md @@ -21,7 +21,7 @@ This document describes the helper class for working with CIS-2 contracts ## CIS2Contract The CIS2Contract class wraps the -[ConcordiumGRPCClient](../classes/Common_GRPC_Client.ConcordiumGRPCClient.html), +[ConcordiumGRPCClient](../classes/grpc.ConcordiumGRPCClient.html), defining an interface matching the [CIS-2 standard](https://proposals.concordium.software/CIS/cis-2.html). diff --git a/docs/pages/documentation.md b/docs/pages/documentation.md new file mode 100644 index 000000000..cbc916063 --- /dev/null +++ b/docs/pages/documentation.md @@ -0,0 +1,75 @@ +This is the documentation for the Concordium Javascript SDK. Here we cover +the JS wrappers for interacting with the Concordium nodes. + +Most functionality is provideded by the +[GRPC-Client](../classes/grpc.ConcordiumGRPCClient.html) +however there exists additional helper functions, for example to help with +creating {@page transactions.md transactions}, or {@page identity-proofs.md +creating identity proof statements}, or {@page utility-functions.md general +utility functions}. + +A good way to get started is to check out the {@page runnable-examples.md +runnable examples}. + +To create a GRPC-Client for use with nodeJS: + +```ts + import { ConcordiumGRPCNodeClient, credentials } from '@concordium/web-sdk/nodejs'; + ... + return new ConcordiumGRPCNodeClient( + address, + port, + credentials.createSsl(), + { timeout: 15000 } + ); +``` + +To create a GRPC-Client for use in a browser (requires GRPC-web enabled on the node): + +```ts + import { ConcordiumGRPCWebClient } from '@concordium/web-sdk'; + ... + return new ConcordiumGRPCWebClient( + address, + port, + { timeout: 15000 } + ); +``` + +## Compatibility + +As of version 7, the SDK's are compatible only with environments +respecting the `exports` field of a modules `package.json`, i.e. taking this into +account when resolving modules. This means + +- NodeJS versions **16** and later. +- Bundlers resolving modules by `package.json` `exports`. + +### Typescript + +Typescript has support for resolving modules through `exports` of `package.json` +from versions + +- NodeJS -> Typescript version **4.7** and later with +`compilerOptions.moduleResolution: "node16" // Or "nodenext"`. +- Bundlers -> Typescript version **5.0** and later with +`compilerOptions.moduleResolution: "bundler"`. + +### NodeJS + +The web-sdk is published as an ES module, and as such packages using it must +also be ES modules. + +The easiest way to run your node application as an ES module, is by setting +the `type` field of `package.json` to be set to `"module"`: + +```json +{ + ... + "type": "module", + ... +} +``` + +Alternatively, files names with the extension `mjs` (or `mts` for TypeScript) +are always handled as ES modules. diff --git a/pages/identity-proofs.md b/docs/pages/identity-proofs.md similarity index 100% rename from pages/identity-proofs.md rename to docs/pages/identity-proofs.md diff --git a/pages/misc-pages/account-creation.md b/docs/pages/misc-pages/account-creation.md similarity index 97% rename from pages/misc-pages/account-creation.md rename to docs/pages/misc-pages/account-creation.md index f4aa390cb..99c4800ef 100644 --- a/pages/misc-pages/account-creation.md +++ b/docs/pages/misc-pages/account-creation.md @@ -98,11 +98,12 @@ using a seed: // The address, that the account created by the transaction will get, can // be derived ahead of time. const accountAddress: AccountAddress = getAccountAddress(credentialDeploymentTransaction.unsignedCdi.credId); + const payload = serializeCredentialDeploymentPayload(signatures, credentialDeploymentTransaction); // Send the transaction to the node const success = await client.sendCredentialDeploymentTransaction( - credentialDeploymentTransaction, - signatures + payload, + expiry ); if (success) { // The node accepted the transaction. This does not ensure that the transaction diff --git a/docs/pages/misc-pages/bundler-optimizations.md b/docs/pages/misc-pages/bundler-optimizations.md new file mode 100644 index 000000000..4bffeee80 --- /dev/null +++ b/docs/pages/misc-pages/bundler-optimizations.md @@ -0,0 +1,61 @@ +## Optimizing bundled applications with async WASM + +As part of the logic of the SDK's is implemented in rust, some functionality +requires WASM modules to be loaded. By default, this is embedded in a UMD +to make it easy to use, however the recommended way to load WASM into the browser +is to asynchronously load and initiate the module. To facilitate this, each +WASM module exposes by `@concordium/rust-bindings` have a corresponding +`bundler` entrypoint, which can be used by aliasing `@concordium/rust-bindings` +as `@concordium/rust-bindings/bundler`. This does require some special handling +by the bundler, which is why it's not the default. + +### Webpack 5 + +The following example shows how to optimize the produced bundle of your +application with **Webpack 5**. + +```ts +// webpack.config.ts +import { Configuration } from 'webpack'; + +const config: Configuration = { + ..., // Other webpack configuration options. + resolve: { + ..., + alias: { + // Resolve bundler-specific wasm entrypoints. + '@concordium/rust-bindings': '@concordium/rust-bindings/bundler', + } + }, + experiments: { + // Needed to handle bundler-specific wasm entrypoint + asyncWebAssembly: true + } +}; + +export default config; +``` + +### Vite + +The following example shows how to optimize the produced bundle of your +application with **Vite**. + +```ts +// vite.config.ts +import { defineConfig } from 'vite'; +import wasm from "vite-plugin-wasm"; +import topLevelAwait from "vite-plugin-top-level-await"; + +export default defineConfig({ + plugins: [ + wasm(), + topLevelAwait() // For compatibility with older browsers. + ], + resolve: { + alias: { + '@concordium/rust-bindings': '@concordium/rust-bindings/bundler', // Resolve bundler-specific wasm entrypoints. + } + } +}); +``` diff --git a/pages/misc-pages/grpc-migration.md b/docs/pages/misc-pages/grpc-migration.md similarity index 100% rename from pages/misc-pages/grpc-migration.md rename to docs/pages/misc-pages/grpc-migration.md diff --git a/pages/misc-pages/grpc-v1.md b/docs/pages/misc-pages/grpc-v1.md similarity index 99% rename from pages/misc-pages/grpc-v1.md rename to docs/pages/misc-pages/grpc-v1.md index fde400114..1650f82af 100644 --- a/pages/misc-pages/grpc-v1.md +++ b/docs/pages/misc-pages/grpc-v1.md @@ -1,5 +1,5 @@ > :warning: **This explains behaviour of the deprecated v1 concordium client**: -check out [the documentation the v2 client](../../classes/Common_GRPC_Client.ConcordiumGRPCClient.html) +check out [the documentation the v2 client](../../classes/grpc.ConcordiumGRPCClient.html) This describes the JSON-RPC client, which can interact with the [Concordium JSON-RPC server](https://github.com/Concordium/concordium-json-rpc) @@ -49,6 +49,8 @@ interface as the grpc v1 node client: ## GRPCv1-Client +_Only accessible prior to version 7 of the web SDK (and in all versions of the deprecated nodejs SDK)_ + The ConcordiumNodeClient defines the interface to be used to send and receive data from a concordium-node. diff --git a/docs/pages/misc-pages/upgrade-guide.md b/docs/pages/misc-pages/upgrade-guide.md new file mode 100644 index 000000000..8a24613ef --- /dev/null +++ b/docs/pages/misc-pages/upgrade-guide.md @@ -0,0 +1,157 @@ +## web SDK version 7 + +Several types have been replaced with a module containing the type itself together with functions for constructing and +converting the type: + +- `AccountAddress` is now a module with functions related to account addresses: + - To refer to `AccountAddress` as a type use `AccountAddress.Type`. + - Constructing `new AccountAddress("
")` is now `AccountAddress.fromBase58("
")`. + - `isAlias` and `getAlias` are now accessable from `AccountAddress.isAlias` and `AccountAddress.getAlias`. +- `ContractAddresss` is now a module with functions related to contract addresses: + - To refer to `ContractAddress` as a type use `ContractAddress.Type`. + - To construct the type use `ContractAddress.create(index, subindex)`. +- `CredentialRegistrationId` is now a module with functions related to credential registration IDs: + - To refer to `CredentialRegistrationId` as a type use `CredentialRegistrationId.Type`. + - Constructing `new CredentialRegistrationId("")` is now + `CredentialRegistrationId.fromHexString("")`. +- `Duration` is now a module with functions related to durations of time. + - To refer to `Duration` as a type use `Duration.Type`. +- `Timestamp` is now a module with functions related to timestamps. + - To refer to `Timestamp` as a type use `Timestamp.Type`. + +The API now uses dedicated types instead of language primitives: + +- Uses `AccountAddress` instead of a string with base58 encoding. + Can be constructed using `AccountAddress.fromBase58('')`. +- Uses `BlockHash` instead of a string with hex encoding. + Can be constructed using `BlockHash.fromHexString('')`. +- Uses `TranactionHash` instead of a string with hex encoding. + Can be constructed using `TransactionHash.fromHexString('')`. +- Uses `Energy` instead of a bigint. + Can be constructed using `Energy.create()`. +- Uses `ReceiveName` instead of a string. + Can be constructed using `ReceiveName.fromString('.')`. +- Uses `InitName` instead of a string. + Can be constructed using `Init.fromString('init_')`. +- Uses `ContractName` instead of a string. + Can be constructed using `ContractName.fromString('')`. +- Uses `EntrypointName` instead of a string. + Can be constructed using `EntrypointName.fromString('')`. +- Uses `Parameter` instead of a string with hex encoding. + Can be constructed using `Parameter.fromHexString('')`. +- Uses `SequenceNumber` (formerly called nonce) instead of a bigint. + Can be constructed using `SequenceNumber.create()`. +- Uses `Timestamp` instead of a bigint. + Can be constructed using `Timestamp.fromMillis()`. +- Uses `Duration` instead of a bigint. + Can be constructed using `Duration.fromMillis()`. + +The `@concordium/web-sdk` now requires bundlers to respect `exports` field of +`package.json` of a module. This is due to relying on entrypoints declared in +the `exports` field of `@concordium/rust-bindings` to make it possible to select +only parts of the SDK to include in your application. +Furthermore, the SDK is now published as an ES module, making it possible to +eliminate dead code. + +For **TypeScript** users, at least typescript version 5 is required along with +the setting `compilerOptions.moduleResultion` to `"bundler"` to match the +resolution strategy of modern bundlers. + +The following entrypoints are made available for consumers of +`@concordium/web-sdk`: + +- `@concordium/web-sdk` exposes the full API of the SDK. +This can safely be used by projects built with ES modules. +- `@concordium/web-sdk/cis0` entrypoint exposes functionality for working with +contracts adhering to the +[CIS-0](https://proposals.concordium.software/CIS/cis-0.html) standard. +- `@concordium/web-sdk/cis2` entrypoint exposes functionality for working with +contracts adhering to the +[CIS-2](https://proposals.concordium.software/CIS/cis-2.html) standard. +- `@concordium/web-sdk/cis4` entrypoint exposes functionality for working with +contracts adhering to the +[CIS-4](https://proposals.concordium.software/CIS/cis-4.html) standard. +- `@concordium/web-sdk/grpc` entrypoint exposes the grpc client for interacting +with a nodes GRPCv2 interface. +- `@concordium/web-sdk/id` entrypoint exposes functionality for working with +ID proofs. +- `@concordium/web-sdk/json-rpc` entrypoint exposes the **(deprecated)** +json-rpc client for interacting with a nodes GPRCv1 interface. +- `@concordium/web-sdk/schema` entrypoint exposes functionality for working +with smart contract schemas, i.e.(de)serializing types using a smart +contract schema. + - This uses the wasm entrypoint at `@concordium/rust-bindings/dapp`. +- `@concordium/web-sdk/types` entrypoint exposes functionality for working +with concordium domain types. +- `@concordium/web-sdk/wasm` entrypoint exposes a variety of functionality for +working with concordium domain types, which requires WASM. + - This uses the wasm entrypoint at `@concorodium/rust-bindings/wallet`. +- `@concordium/web-sdk/web3-id` entrypoint exposes functionality for working +with web3-id proofs. + +### NodeJS + +_The `@concordium/node-sdk` has been removed in favor of unifying both web and +nodejs SDK's in a single platform agnostic SDK. This means users of +`@concordium/node-sdk` looking to upgrade, will need to install +`@concordium/web-sdk` instead._ + +The `@concordium/web-sdk` module is not compatible with +node versions <16 and is published only as an ES module. +This, in turn, requires users to also run applications as ES modules. + +The easiest way to run your node application as an ES module, is by setting +the `type` field of `package.json` to be set to `"module"`: + +```json +{ + ... + "type": "module", + ... +} +``` + +Alternatively, files names with the extension `mjs` (or `mts` for TypeScript) +are always handled as ES modules. + +For **TypeScript** users, at least typescript version 4.7 is required along +with the setting `compilerOptions.moduleResultion` to `"node16"` or +`"nodenext"` to match the resolution strategy of node version 16 and later. + +## Common SDK version 5 to 6 (Web 2->3) (Node 5->6) + +Some classes and types have been renamed or changed, and should be update if used. + +### AccountTransactionType + +Some types in the AccountTransactionType enum have been changed to align +with other languages. + +- InitializeSmartContractInstance -> InitContract +- UpdateSmartContractInstance -> Update +- SimpleTransfer -> Transfer +- EncryptedTransfer -> EncryptedAmountTransfer +- SimpleTransferWithMemo -> TransferWithMemo +- EncryptedTransferWithMemo -> EncryptedAmountTransferWithMemo + +### GtuAmount + +The `GtuAmount` class has been renamed to `CcdAmount` to reflect the current +name of token. If an object was used in place of a class instance, the field +`microGtuAmount` should be renamed to `microCcdAmount`. + +### DeployModule + +The field `content` has been renamed to `source` to align with other languages. + +### UpdateConcractPayload + +Field names have been renamed to align with other languages. `contractAddress` +field has been renamed to `address`. `parameter` field has been renamed to +`message`. + +### InitContractPayload + +Field names have been renamed to align with other languages. `contractName` +field has been renamed to `initName`. `parameter` field has been renamed to +`params`. diff --git a/pages/runnable-examples.md b/docs/pages/runnable-examples.md similarity index 100% rename from pages/runnable-examples.md rename to docs/pages/runnable-examples.md diff --git a/pages/transactions.md b/docs/pages/transactions.md similarity index 100% rename from pages/transactions.md rename to docs/pages/transactions.md diff --git a/pages/utility-functions.md b/docs/pages/utility-functions.md similarity index 98% rename from pages/utility-functions.md rename to docs/pages/utility-functions.md index a85303da6..3939d190f 100644 --- a/pages/utility-functions.md +++ b/docs/pages/utility-functions.md @@ -233,7 +233,7 @@ and does not give any guarantees for whether the contract adheres to the standard it claims to implement. The function returns `undefined` if the contract does not support CIS-0. -This requires a [`ConcordiumGRPCClient`](../classes/Common_GRPC_Client.ConcordiumGRPCClient.html). +This requires a [`ConcordiumGRPCClient`](../classes/grpc.ConcordiumGRPCClient.html). ```ts const client = ...; // `ConcordiumGRPCClient` diff --git a/docs/tsconfig.json b/docs/tsconfig.json new file mode 100644 index 000000000..d92fe122c --- /dev/null +++ b/docs/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext" + }, + "include": [ + "../packages/**/*", + "./typedoc.config.cjs" + ], + "exclude": [ + "../packages/ccd-js-gen/**/*" + ] +} diff --git a/docs/typedoc.config.cjs b/docs/typedoc.config.cjs new file mode 100644 index 000000000..a7fd163ad --- /dev/null +++ b/docs/typedoc.config.cjs @@ -0,0 +1,76 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const path = require('node:path'); + +module.exports = { + name: 'Concordium JS-SDK', + out: path.resolve(__dirname, '../typedoc'), + entryPointStrategy: 'expand', + entryPoints: [path.resolve(__dirname, '../packages/sdk/src/pub')], + tsconfig: path.resolve(__dirname, '../tsconfig.json'), + readme: path.resolve(__dirname, './pages/documentation.md'), + plugin: [ + '@knodes/typedoc-plugin-code-blocks', + '@knodes/typedoc-plugin-pages', + 'typedoc-plugin-merge-modules', + ], + mergeModulesMergeMode: 'module', + pluginCodeBlocks: { + source: path.resolve(__dirname, '../examples'), + }, + pluginPages: { + source: path.resolve(__dirname, './pages'), + pages: [ + { + name: 'Concordium JS-SDK', + children: [ + { + name: 'CIS2-Contracts', + source: 'cis2-contracts.md', + }, + { + name: 'Identity Proofs', + source: 'identity-proofs.md', + }, + { + name: 'Runnable Examples', + source: 'runnable-examples.md', + }, + { + name: 'Transactions', + source: 'transactions.md', + }, + { + name: 'Utility Functions', + source: 'utility-functions.md', + }, + { + name: 'Miscellaneous Pages', + childrenDir: 'misc-pages', + children: [ + { + name: 'Account Creation', + source: 'account-creation.md', + }, + { + name: 'Optimizing bundled applications', + source: 'bundler-optimizations.md', + }, + { + name: 'GRPCv1 to GRPCv2 Migration Guide', + source: 'grpc-migration.md', + }, + { + name: 'Old GRPC-Client', + source: 'grpc-v1.md', + }, + { + name: 'Upgrade Guide', + source: 'upgrade-guide.md', + }, + ], + }, + ], + }, + ], + }, +}; diff --git a/examples/ccd-js-gen/wCCD/client-error-message.ts b/examples/ccd-js-gen/wCCD/client-error-message.ts new file mode 100644 index 000000000..aeabebed7 --- /dev/null +++ b/examples/ccd-js-gen/wCCD/client-error-message.ts @@ -0,0 +1,107 @@ +import { credentials } from '@grpc/grpc-js'; +import * as SDK from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import meow from 'meow'; +import { parseEndpoint } from '../../shared/util.js'; + +// The generated module could be imported directly like below, +// but for this example it is imported dynamicly to improve +// the error message when not generated. +// import * as wCCDContractClient from './lib/cis2_wCCD'; + +const cli = meow( + ` + This example uses a generated smart contract client for the wCCD smart contract. + + Usage + $ yarn run-example [options] + + Required + --index, -i The index of the smart contract. Defaults to 2059, which is wCCD on testnet. + + Options + --help, -h Displays this message + --endpoint, -e Specify endpoint of a grpc2 interface of a Concordium node in the format "://
:". Defaults to 'http://localhost:20000' + --subindex, The subindex of the smart contract. Defaults to 0 +`, + { + importMeta: import.meta, + flags: { + endpoint: { + type: 'string', + alias: 'e', + default: 'http://localhost:20000', + }, + index: { + type: 'number', + alias: 'i', + default: 2059, + }, + subindex: { + type: 'number', + default: 0, + }, + }, + } +); + +const [address, port, scheme] = parseEndpoint(cli.flags.endpoint); +const grpcClient = new ConcordiumGRPCNodeClient( + address, + Number(port), + scheme === 'https' ? credentials.createSsl() : credentials.createInsecure() +); + +const contractAddress = SDK.ContractAddress.create( + cli.flags.index, + cli.flags.subindex +); + +(async () => { + // Importing the generated smart contract module client. + /* eslint-disable import/no-unresolved */ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const wCCDContractClient = await import('./lib/wCCD_cis2_wCCD.js').catch( + (e) => { + /* eslint-enable import/no-unresolved */ + console.error( + '\nFailed to load the generated wCCD module, did you run the `generate` script?\n' + ); + throw e; + } + ); + + const wCCDTokenId = ''; + const fromAddress = SDK.AccountAddress.fromBuffer( + new Uint8Array(32).fill(0) + ); + const toAddress = SDK.AccountAddress.fromBuffer(new Uint8Array(32).fill(1)); + const parameter = [ + { + token_id: wCCDTokenId, + amount: 1000, + from: { type: 'Account', content: fromAddress }, + to: { type: 'Account', content: toAddress }, + data: '', + } as const, + ]; + const contract = await wCCDContractClient.create( + grpcClient, + contractAddress + ); + + const unauthorizedInvoker = SDK.AccountAddress.fromBase58( + '357EYHqrmMiJBmUZTVG5FuaMq4soAhgtgz6XNEAJaXHW3NHaUf' + ); + + const result = await wCCDContractClient.dryRunTransfer( + contract, + parameter, + { + invoker: unauthorizedInvoker, + } + ); + const errorMessage = wCCDContractClient.parseErrorMessageTransfer(result); + console.log('Transfer failed with error: ', errorMessage); +})(); diff --git a/examples/ccd-js-gen/wCCD/client-events.ts b/examples/ccd-js-gen/wCCD/client-events.ts new file mode 100644 index 000000000..9f156f42a --- /dev/null +++ b/examples/ccd-js-gen/wCCD/client-events.ts @@ -0,0 +1,113 @@ +import { credentials } from '@grpc/grpc-js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import * as SDK from '@concordium/web-sdk'; +import meow from 'meow'; +import { parseEndpoint } from '../../shared/util.js'; + +// The generated module could be imported directly like below, +// but for this example it is imported dynamicly to improve +// the error message when not generated. +// import * as wCCDContractClient from './lib/cis2_wCCD.js'; + +const cli = meow( + ` + This example uses a generated smart contract client for the wCCD smart contract to display events. + + Usage + $ yarn run-example [options] + + Required + --index, -i The index of the smart contract. Defaults to 2059, which is wCCD on testnet. + + Options + --help, -h Displays this message + --endpoint, -e Specify endpoint of a grpc2 interface of a Concordium node in the format "://
:". Defaults to 'http://localhost:20000' + --subindex, The subindex of the smart contract. Defaults to 0 +`, + { + importMeta: import.meta, + flags: { + endpoint: { + type: 'string', + alias: 'e', + default: 'http://localhost:20000', + }, + index: { + type: 'number', + alias: 'i', + default: 2059, + }, + subindex: { + type: 'number', + default: 0, + }, + }, + } +); + +const [address, port, scheme] = parseEndpoint(cli.flags.endpoint); +const grpcClient = new ConcordiumGRPCNodeClient( + address, + Number(port), + scheme === 'https' ? credentials.createSsl() : credentials.createInsecure() +); + +const contractAddress = SDK.ContractAddress.create( + cli.flags.index, + cli.flags.subindex +); + +(async () => { + // Importing the generated smart contract module client. + /* eslint-disable import/no-unresolved */ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const wCCDContractClient = await import('./lib/wCCD_cis2_wCCD.js').catch( + (e) => { + /* eslint-enable import/no-unresolved */ + console.error( + '\nFailed to load the generated wCCD module, did you run the `generate` script?\n' + ); + throw e; + } + ); + + // The sender of the transaction, i.e the one updating an operator. + const senderAccount = SDK.AccountAddress.fromBase58( + '357EYHqrmMiJBmUZTVG5FuaMq4soAhgtgz6XNEAJaXHW3NHaUf' + ); + // The parameter adding the wCCD contract as an operator of sender. + const parameter = [ + { + update: { type: 'Add' }, + operator: { type: 'Contract', content: contractAddress }, + } as const, + ]; + + // The client for the wCCD contract + const contract = await wCCDContractClient.create( + grpcClient, + contractAddress + ); + + // Dry run the update of operator. + const result = await wCCDContractClient.dryRunUpdateOperator( + contract, + parameter, + { invoker: senderAccount } + ); + if (result.tag !== 'success') { + throw new Error('Unexpected failure'); + } + for (const traceEvent of result.events) { + if ( + traceEvent.tag === SDK.TransactionEventTag.Updated || + traceEvent.tag === SDK.TransactionEventTag.Interrupted + ) { + for (const contractEvent of traceEvent.events) { + const parsed = wCCDContractClient.parseEvent(contractEvent); + console.log(parsed); + } + } + } +})(); diff --git a/examples/ccd-js-gen/wCCD/client-tokenMetadata.ts b/examples/ccd-js-gen/wCCD/client-tokenMetadata.ts index 6692991b3..262321ff4 100644 --- a/examples/ccd-js-gen/wCCD/client-tokenMetadata.ts +++ b/examples/ccd-js-gen/wCCD/client-tokenMetadata.ts @@ -1,12 +1,13 @@ import { credentials } from '@grpc/grpc-js'; -import * as SDK from '@concordium/node-sdk'; +import * as SDK from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import meow from 'meow'; -import { parseEndpoint } from '../../shared/util'; +import { parseEndpoint } from '../../shared/util.js'; // The generated module could be imported directly like below, // but for this example it is imported dynamicly to improve // the error message when not generated. -// import * as wCCDModule from './lib/wCCD'; +// import * as wCCDContractClient from './lib/cis2_wCCD'; const cli = meow( ` @@ -45,33 +46,47 @@ const cli = meow( ); const [address, port, scheme] = parseEndpoint(cli.flags.endpoint); -const grpcClient = SDK.createConcordiumClient( +const grpcClient = new ConcordiumGRPCNodeClient( address, Number(port), scheme === 'https' ? credentials.createSsl() : credentials.createInsecure() ); -const contractAddress: SDK.ContractAddress = { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), -}; +const contractAddress = SDK.ContractAddress.create( + cli.flags.index, + cli.flags.subindex +); (async () => { // Importing the generated smart contract module client. /* eslint-disable import/no-unresolved */ // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - const wCCDModule = await import('./lib/wCCD').catch((e) => { - /* eslint-enable import/no-unresolved */ - console.error( - '\nFailed to load the generated wCCD module, did you run the `generate` script?\n' - ); - throw e; - }); + const wCCDContractClient = await import('./lib/wCCD_cis2_wCCD.js').catch( + (e) => { + /* eslint-enable import/no-unresolved */ + console.error( + '\nFailed to load the generated wCCD module, did you run the `generate` script?\n' + ); + throw e; + } + ); - const parameter = '010000'; // First 2 bytes for number of tokens to query, 1 byte for the token ID. - const contract = new wCCDModule.Cis2WCCD(grpcClient, contractAddress); + const wCCDTokenId = ''; + const parameter = [wCCDTokenId]; + const contract = await wCCDContractClient.create( + grpcClient, + contractAddress + ); - const responseHex = await contract.dryRun.tokenMetadata(parameter); - console.log({ responseHex }); + const result = await wCCDContractClient.dryRunTokenMetadata( + contract, + parameter + ); + const returnValue = + wCCDContractClient.parseReturnValueTokenMetadata(result); + console.log( + 'The token metadata for wCCD can be found at: ', + returnValue?.[0].url + ); })(); diff --git a/examples/ccd-js-gen/wCCD/generate.ts b/examples/ccd-js-gen/wCCD/generate.ts index b7c621192..41ff7529a 100644 --- a/examples/ccd-js-gen/wCCD/generate.ts +++ b/examples/ccd-js-gen/wCCD/generate.ts @@ -1,10 +1,11 @@ import { credentials } from '@grpc/grpc-js'; -import * as SDK from '@concordium/node-sdk'; +import * as SDK from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import * as Gen from '@concordium/ccd-js-gen'; import * as Path from 'node:path'; import * as Url from 'node:url'; import meow from 'meow'; -import { parseEndpoint } from '../../shared/util'; +import { parseEndpoint } from '../../shared/util.js'; const cli = meow( ` @@ -13,13 +14,9 @@ const cli = meow( Usage $ yarn run-example [options] - Required - --index, -i The index of the smart contract. Defaults to 2059, which is wCCD on Testnet. - Options --help, -h Displays this message --endpoint, -e Specify endpoint of a grpc2 interface of a Concordium node in the format "address:port". Defaults to 'localhost:20000' - --subindex, The subindex of the smart contract. Defaults to 0 `, { importMeta: import.meta, @@ -29,39 +26,26 @@ const cli = meow( alias: 'e', default: 'localhost:20000', }, - index: { - type: 'number', - alias: 'i', - isRequired: true, - default: 2059, - }, - subindex: { - type: 'number', - default: 0, - }, }, } ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const grpcClient = SDK.createConcordiumClient( +const grpcClient = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); -const contractAddress: SDK.ContractAddress = { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), -}; +const wCCDModuleRef = SDK.ModuleReference.fromHexString( + 'cc285180b45d7695db75c29dee004d2e81a1383880c9b122399bea809196c98f' +); (async () => { - console.info(`Fetching instance information for ${contractAddress.index}.`); - const info = await grpcClient.getInstanceInfo(contractAddress); console.info( - `Fetching smart contract module source with reference '${info.sourceModule.moduleRef}'.` + `Fetching smart contract module source with reference '${wCCDModuleRef.moduleRef}'.` ); - const moduleSource = await grpcClient.getModuleSource(info.sourceModule); + const moduleSource = await grpcClient.getModuleSource(wCCDModuleRef); const filePath = Url.fileURLToPath(import.meta.url); const outDir = Path.join(Path.dirname(filePath), 'lib'); console.info(`Generating smart contract module client at '${outDir}'.`); diff --git a/examples/cis2/balanceOf.ts b/examples/cis2/balanceOf.ts index 85a5ee13d..ab41b19db 100644 --- a/examples/cis2/balanceOf.ts +++ b/examples/cis2/balanceOf.ts @@ -1,7 +1,8 @@ -import { createConcordiumClient, CIS2Contract } from '@concordium/node-sdk'; +import { CIS2Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; -import { parseAddress, parseEndpoint } from '../shared/util'; +import { parseAddress, parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -50,17 +51,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS2Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS2Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const accountBalance = await contract.balanceOf({ address: parseAddress(cli.flags.address), diff --git a/examples/cis2/dryRun.transfer.ts b/examples/cis2/dryRun.transfer.ts index 36abb7ce5..258993f14 100644 --- a/examples/cis2/dryRun.transfer.ts +++ b/examples/cis2/dryRun.transfer.ts @@ -1,11 +1,14 @@ import { - createConcordiumClient, CIS2, CIS2Contract, -} from '@concordium/node-sdk'; + ContractAddress, + AccountAddress, + EntrypointName, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; -import { parseAddress, parseEndpoint } from '../shared/util'; +import { parseAddress, parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -67,28 +70,29 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS2Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS2Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const tokenId = cli.flags.tokenId; - const from = cli.flags.from; + const from = AccountAddress.fromBase58(cli.flags.from); const toAddress = parseAddress(cli.flags.to); - const to: CIS2.Receiver = - typeof toAddress === 'string' - ? toAddress - : { - address: toAddress, - hookName: cli.flags.receiveHookName ?? '', - }; + const to: CIS2.Receiver = AccountAddress.instanceOf(toAddress) + ? toAddress + : { + address: toAddress, + hookName: EntrypointName.fromString( + cli.flags.receiveHookName ?? '' + ), + }; const result = await contract.dryRun.transfer(from, { from, diff --git a/examples/cis2/dryRun.updateOperator.ts b/examples/cis2/dryRun.updateOperator.ts index 17e216d63..54412ae06 100644 --- a/examples/cis2/dryRun.updateOperator.ts +++ b/examples/cis2/dryRun.updateOperator.ts @@ -1,7 +1,8 @@ -import { createConcordiumClient, CIS2Contract } from '@concordium/node-sdk'; +import { CIS2Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; -import { parseAddress, parseEndpoint } from '../shared/util'; +import { parseAddress, parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -50,17 +51,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS2Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS2Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const owner = parseAddress(cli.flags.owner); const address = parseAddress(cli.flags.address); diff --git a/examples/cis2/operatorOf.ts b/examples/cis2/operatorOf.ts index 023dd8a11..186f1fa6d 100644 --- a/examples/cis2/operatorOf.ts +++ b/examples/cis2/operatorOf.ts @@ -1,7 +1,8 @@ -import { createConcordiumClient, CIS2Contract } from '@concordium/node-sdk'; +import { CIS2Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; -import { parseAddress, parseEndpoint } from '../shared/util'; +import { parseAddress, parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -50,17 +51,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS2Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS2Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const owner = parseAddress(cli.flags.owner); const address = parseAddress(cli.flags.address); diff --git a/examples/cis2/tokenMetadata.ts b/examples/cis2/tokenMetadata.ts index 8d6220603..dbfad7221 100644 --- a/examples/cis2/tokenMetadata.ts +++ b/examples/cis2/tokenMetadata.ts @@ -1,6 +1,8 @@ -import { createConcordiumClient, CIS2Contract } from '@concordium/node-sdk'; +import { CIS2Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; +import fetch from 'node-fetch'; const cli = meow( ` @@ -43,7 +45,7 @@ const cli = meow( ); const [address, port] = cli.flags.endpoint.split(':'); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -54,10 +56,10 @@ if (cli.flags.h) { } (async () => { - const contract = await CIS2Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS2Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const url = await contract.tokenMetadata(cli.flags.tokenId); console.log('url object:', url); diff --git a/examples/cis2/transfer.ts b/examples/cis2/transfer.ts index 43c591fe6..3a6ced7c9 100644 --- a/examples/cis2/transfer.ts +++ b/examples/cis2/transfer.ts @@ -1,12 +1,16 @@ import { - createConcordiumClient, CIS2, CIS2Contract, buildBasicAccountSigner, -} from '@concordium/node-sdk'; + ContractAddress, + AccountAddress, + EntrypointName, + Energy, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; -import { parseAddress, parseEndpoint } from '../shared/util'; +import { parseAddress, parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -74,34 +78,35 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS2Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS2Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const signer = buildBasicAccountSigner(cli.flags.privateKey); const tokenId = cli.flags.tokenId; - const from = cli.flags.from; + const from = AccountAddress.fromBase58(cli.flags.from); const toAddress = parseAddress(cli.flags.to); - const to: CIS2.Receiver = - typeof toAddress === 'string' - ? toAddress - : { - address: toAddress, - hookName: cli.flags.receiveHookName ?? '', - }; + const to: CIS2.Receiver = AccountAddress.instanceOf(toAddress) + ? toAddress + : { + address: toAddress, + hookName: EntrypointName.fromString( + cli.flags.receiveHookName ?? '' + ), + }; const txHash = await contract.transfer( { senderAddress: from, - energy: 10000n, + energy: Energy.create(10000), }, { from, diff --git a/examples/cis2/updateOperator.ts b/examples/cis2/updateOperator.ts index dd762e6ec..e599b5b31 100644 --- a/examples/cis2/updateOperator.ts +++ b/examples/cis2/updateOperator.ts @@ -1,11 +1,14 @@ import { - createConcordiumClient, CIS2Contract, buildBasicAccountSigner, -} from '@concordium/node-sdk'; + ContractAddress, + AccountAddress, + Energy, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; -import { parseAddress, parseEndpoint } from '../shared/util'; +import { parseAddress, parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -64,26 +67,26 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS2Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS2Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const signer = buildBasicAccountSigner(cli.flags.privateKey); - const owner = cli.flags.owner; + const owner = AccountAddress.fromBase58(cli.flags.owner); const address = parseAddress(cli.flags.address); const txHash = await contract.updateOperator( { senderAddress: owner, - energy: 10000n, + energy: Energy.create(10000), }, { type: cli.flags.updateType as 'add' | 'remove', diff --git a/examples/cis4/credentialEntry.ts b/examples/cis4/credentialEntry.ts index 5fc208235..8378ccea4 100644 --- a/examples/cis4/credentialEntry.ts +++ b/examples/cis4/credentialEntry.ts @@ -1,8 +1,9 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; -import { CIS4Contract, createConcordiumClient } from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +import { CIS4Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -45,17 +46,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const credentialEntry = await contract.credentialEntry(cli.flags.credId); console.log('Credential entry:', credentialEntry); diff --git a/examples/cis4/credentialStatus.ts b/examples/cis4/credentialStatus.ts index a681a7d1d..c159dbef5 100644 --- a/examples/cis4/credentialStatus.ts +++ b/examples/cis4/credentialStatus.ts @@ -1,12 +1,9 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; -import { - CIS4, - CIS4Contract, - createConcordiumClient, -} from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +import { CIS4, CIS4Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -49,17 +46,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const credentialStatus = await contract.credentialStatus(cli.flags.credId); diff --git a/examples/cis4/issuer.ts b/examples/cis4/issuer.ts index e17719a88..097faa4b5 100644 --- a/examples/cis4/issuer.ts +++ b/examples/cis4/issuer.ts @@ -1,8 +1,9 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; -import { CIS4Contract, createConcordiumClient } from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +import { CIS4Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -39,17 +40,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const issuer = await contract.issuer(); console.log('Issuer public key:', issuer); diff --git a/examples/cis4/registerCredential.ts b/examples/cis4/registerCredential.ts index 1978ff54b..0d1a64811 100644 --- a/examples/cis4/registerCredential.ts +++ b/examples/cis4/registerCredential.ts @@ -2,17 +2,21 @@ import fs from 'fs'; import path from 'path'; import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; -import * as ed25519 from '@noble/ed25519'; +import * as ed25519 from '#ed25519'; import { + AccountAddress, buildAccountSigner, CIS4, CIS4Contract, - createConcordiumClient, + ContractAddress, + Energy, HexString, parseWallet, -} from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; + Timestamp, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -78,7 +82,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -92,15 +96,15 @@ const wallet = parseWallet(walletFile); const signer = buildAccountSigner(wallet); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); let holderPubKey: HexString; if (!cli.flags.holderPubKey) { const prv = ed25519.utils.randomPrivateKey(); - const pub = Buffer.from(await ed25519.getPublicKey(prv)).toString( + const pub = Buffer.from(await ed25519.getPublicKeyAsync(prv)).toString( 'hex' ); @@ -121,16 +125,19 @@ const signer = buildAccountSigner(wallet); const credential: CIS4.CredentialInfo = { holderPubKey, holderRevocable: cli.flags.holderRevoke, - validFrom, - validUntil, + validFrom: Timestamp.fromDate(validFrom), + validUntil: + validUntil === undefined + ? undefined + : Timestamp.fromDate(validUntil), metadataUrl: { url: cli.flags.metadataUrl }, }; const txHash = await contract.registerCredential( signer, { - senderAddress: wallet.value.address, - energy: 10000n, + senderAddress: AccountAddress.fromBase58(wallet.value.address), + energy: Energy.create(10000), }, credential, cli.flags.data diff --git a/examples/cis4/registerRevocationKeys.ts b/examples/cis4/registerRevocationKeys.ts index 24432eb43..c7c780cb2 100644 --- a/examples/cis4/registerRevocationKeys.ts +++ b/examples/cis4/registerRevocationKeys.ts @@ -1,17 +1,20 @@ import fs from 'fs'; import path from 'path'; import meow from 'meow'; -import * as ed25519 from '@noble/ed25519'; +import * as ed25519 from '#ed25519'; import { credentials } from '@grpc/grpc-js'; import { + AccountAddress, buildAccountSigner, CIS4Contract, - createConcordiumClient, + ContractAddress, + Energy, HexString, parseWallet, -} from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -63,7 +66,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -77,15 +80,15 @@ const wallet = parseWallet(walletFile); const signer = buildAccountSigner(wallet); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); let keys: HexString[] = cli.flags.keys ?? []; if (!cli.flags.keys?.length) { const prv = ed25519.utils.randomPrivateKey(); - const pub = Buffer.from(await ed25519.getPublicKey(prv)).toString( + const pub = Buffer.from(await ed25519.getPublicKeyAsync(prv)).toString( 'hex' ); @@ -99,8 +102,8 @@ const signer = buildAccountSigner(wallet); const txHash = await contract.registerRevocationKeys( signer, { - senderAddress: wallet.value.address, - energy: 10000n, + senderAddress: AccountAddress.fromBase58(wallet.value.address), + energy: Energy.create(10000), }, keys, cli.flags.data diff --git a/examples/cis4/registryMetadata.ts b/examples/cis4/registryMetadata.ts index 1fbb735f5..0bec48350 100644 --- a/examples/cis4/registryMetadata.ts +++ b/examples/cis4/registryMetadata.ts @@ -1,8 +1,9 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; -import { CIS4Contract, createConcordiumClient } from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +import { CIS4Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -39,17 +40,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const metadata = await contract.registryMetadata(); console.log('Metadata:', metadata); diff --git a/examples/cis4/removeRevocationKeys.ts b/examples/cis4/removeRevocationKeys.ts index a6b433506..c23e66030 100644 --- a/examples/cis4/removeRevocationKeys.ts +++ b/examples/cis4/removeRevocationKeys.ts @@ -4,12 +4,15 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; import { + AccountAddress, buildAccountSigner, CIS4Contract, - createConcordiumClient, + ContractAddress, + Energy, parseWallet, -} from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -62,7 +65,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -76,16 +79,16 @@ const wallet = parseWallet(walletFile); const signer = buildAccountSigner(wallet); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const txHash = await contract.removeRevocationKeys( signer, { - senderAddress: wallet.value.address, - energy: 10000n, + senderAddress: AccountAddress.fromBase58(wallet.value.address), + energy: Energy.create(10000), }, cli.flags.keys, cli.flags.data diff --git a/examples/cis4/revocationKeys.ts b/examples/cis4/revocationKeys.ts index 57437b05f..6c3971157 100644 --- a/examples/cis4/revocationKeys.ts +++ b/examples/cis4/revocationKeys.ts @@ -1,8 +1,9 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; -import { CIS4Contract, createConcordiumClient } from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +import { CIS4Contract, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -39,17 +40,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const keys = await contract.revocationKeys(); console.log('Revocation keys:', keys); diff --git a/examples/cis4/revokeCredentialAsHolder.ts b/examples/cis4/revokeCredentialAsHolder.ts index c28090cf0..aa2577588 100644 --- a/examples/cis4/revokeCredentialAsHolder.ts +++ b/examples/cis4/revokeCredentialAsHolder.ts @@ -4,13 +4,16 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; import { + AccountAddress, buildAccountSigner, CIS4Contract, - createConcordiumClient, + ContractAddress, + Energy, parseWallet, Web3IdSigner, -} from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -71,7 +74,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -85,18 +88,18 @@ const wallet = parseWallet(walletFile); const signer = buildAccountSigner(wallet); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const hSigner = await Web3IdSigner.from(cli.flags.holderPrivateKey); const nonce = BigInt(cli.flags.nonce); const txHash = await contract.revokeCredentialAsHolder( signer, { - senderAddress: wallet.value.address, - energy: 10000n, + senderAddress: AccountAddress.fromBase58(wallet.value.address), + energy: Energy.create(10000), }, hSigner, nonce, diff --git a/examples/cis4/revokeCredentialAsIssuer.ts b/examples/cis4/revokeCredentialAsIssuer.ts index f5c1c9939..fc56bdbdc 100644 --- a/examples/cis4/revokeCredentialAsIssuer.ts +++ b/examples/cis4/revokeCredentialAsIssuer.ts @@ -4,12 +4,15 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; import { + AccountAddress, buildAccountSigner, CIS4Contract, - createConcordiumClient, + ContractAddress, + Energy, parseWallet, -} from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -66,7 +69,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -80,16 +83,16 @@ const wallet = parseWallet(walletFile); const signer = buildAccountSigner(wallet); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const txHash = await contract.revokeCredentialAsIssuer( signer, { - senderAddress: wallet.value.address, - energy: 10000n, + senderAddress: AccountAddress.fromBase58(wallet.value.address), + energy: Energy.create(10000), }, cli.flags.credId, cli.flags.reason, diff --git a/examples/cis4/revokeCredentialAsOther.ts b/examples/cis4/revokeCredentialAsOther.ts index ac4e7d351..07769b379 100644 --- a/examples/cis4/revokeCredentialAsOther.ts +++ b/examples/cis4/revokeCredentialAsOther.ts @@ -4,13 +4,16 @@ import meow from 'meow'; import { credentials } from '@grpc/grpc-js'; import { + AccountAddress, buildAccountSigner, CIS4Contract, - createConcordiumClient, + ContractAddress, + Energy, parseWallet, Web3IdSigner, -} from '@concordium/node-sdk'; -import { parseEndpoint } from '../shared/util'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -77,7 +80,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -91,18 +94,18 @@ const wallet = parseWallet(walletFile); const signer = buildAccountSigner(wallet); (async () => { - const contract = await CIS4Contract.create(client, { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contract = await CIS4Contract.create( + client, + ContractAddress.create(cli.flags.index, cli.flags.subindex) + ); const rSigner = await Web3IdSigner.from(cli.flags.revokerPrivateKey); const nonce = BigInt(cli.flags.nonce); const txHash = await contract.revokeCredentialAsOther( signer, { - senderAddress: wallet.value.address, - energy: 10000n, + senderAddress: AccountAddress.fromBase58(wallet.value.address), + energy: Energy.create(10000), }, rSigner, cli.flags.credId, diff --git a/examples/client/banPeer.ts b/examples/client/banPeer.ts index 73bacd71b..27cb9dca1 100644 --- a/examples/client/banPeer.ts +++ b/examples/client/banPeer.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +35,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/dumpStart.ts b/examples/client/dumpStart.ts index 41db83030..0b3ebe173 100644 --- a/examples/client/dumpStart.ts +++ b/examples/client/dumpStart.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -41,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/dumpStop.ts b/examples/client/dumpStop.ts index 5c2e97c54..0dffb33e4 100644 --- a/examples/client/dumpStop.ts +++ b/examples/client/dumpStop.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +27,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/findFirstFinalizedBlockNoLaterThan.ts b/examples/client/findFirstFinalizedBlockNoLaterThan.ts index b28111268..b9b9035b3 100644 --- a/examples/client/findFirstFinalizedBlockNoLaterThan.ts +++ b/examples/client/findFirstFinalizedBlockNoLaterThan.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -42,7 +42,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/findFirstNonGenesisAccount.ts b/examples/client/findFirstNonGenesisAccount.ts index 80695f553..b4149fe6d 100644 --- a/examples/client/findFirstNonGenesisAccount.ts +++ b/examples/client/findFirstNonGenesisAccount.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, streamToList } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { streamToList } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -34,7 +35,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/findInstanceCreation.ts b/examples/client/findInstanceCreation.ts index 13dd290b8..06449ed0b 100644 --- a/examples/client/findInstanceCreation.ts +++ b/examples/client/findInstanceCreation.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { ContractAddress, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -47,7 +48,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -63,10 +64,7 @@ const client = createConcordiumClient( cli.flags.from !== undefined ? BigInt(cli.flags.from) : undefined; const to = cli.flags.from !== undefined ? BigInt(cli.flags.from) : undefined; - const address: ContractAddress = { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }; + const address = ContractAddress.create(cli.flags.index, cli.flags.subindex); const instanceCreation = await client.findInstanceCreation( address, from, diff --git a/examples/client/getAccountInfo.ts b/examples/client/getAccountInfo.ts index 93bced81b..575371382 100644 --- a/examples/client/getAccountInfo.ts +++ b/examples/client/getAccountInfo.ts @@ -1,10 +1,11 @@ -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import { AccountAddress, AccountInfo, - createConcordiumClient, - isDelegatorAccount, -} from '@concordium/node-sdk'; + AccountInfoType, + BlockHash, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -45,7 +46,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -61,17 +62,21 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const accountAddress = new AccountAddress(cli.flags.account); + const accountAddress = AccountAddress.fromBase58(cli.flags.account); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const accountInfo: AccountInfo = await client.getAccountInfo( accountAddress, - cli.flags.block + blockHash ); console.log('Account balance:', accountInfo.accountAmount); console.log('Account address:', accountInfo.accountAddress); // If the account is a delegator print delegator information - if (isDelegatorAccount(accountInfo)) { + if (accountInfo.type === AccountInfoType.Delegator) { console.log( 'Delegated stake amount:', accountInfo.accountDelegation.stakedAmount diff --git a/examples/client/getAccountList.ts b/examples/client/getAccountList.ts index a013bfb1f..a0cf49390 100644 --- a/examples/client/getAccountList.ts +++ b/examples/client/getAccountList.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, Base58String } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, AccountAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -48,13 +49,16 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const accounts: AsyncIterable = client.getAccountList( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const accounts: AsyncIterable = + client.getAccountList(blockHash); // #endregion documentation-snippet console.log('Accounts that exists at the end of the given block:'); for await (const account of accounts) { - console.log(account); + console.log(AccountAddress.toBase58(account)); } })(); diff --git a/examples/client/getAccountNonFinalizedTransactions.ts b/examples/client/getAccountNonFinalizedTransactions.ts index 65c430504..6d17c3f3a 100644 --- a/examples/client/getAccountNonFinalizedTransactions.ts +++ b/examples/client/getAccountNonFinalizedTransactions.ts @@ -1,9 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - AccountAddress, - createConcordiumClient, - HexString, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { AccountAddress, TransactionHash } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -39,7 +36,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -57,8 +54,8 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const accountAddress = new AccountAddress(cli.flags.account); - const transactions: AsyncIterable = + const accountAddress = AccountAddress.fromBase58(cli.flags.account); + const transactions: AsyncIterable = client.getAccountNonFinalizedTransactions(accountAddress); // #endregion documentation-snippet diff --git a/examples/client/getAncestors.ts b/examples/client/getAncestors.ts index 1b5849ccd..02efc5b2e 100644 --- a/examples/client/getAncestors.ts +++ b/examples/client/getAncestors.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, HexString } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -40,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -58,9 +59,13 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const ancestors: AsyncIterable = client.getAncestors( + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const ancestors: AsyncIterable = client.getAncestors( BigInt(cli.flags.maxAncestors), - cli.flags.block + blockHash ); // #endregion documentation-snippet diff --git a/examples/client/getAnonymityRevokers.ts b/examples/client/getAnonymityRevokers.ts index c67f0ab3b..e3adcb6df 100644 --- a/examples/client/getAnonymityRevokers.ts +++ b/examples/client/getAnonymityRevokers.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { ArInfo, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ArInfo, BlockHash } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -48,9 +49,11 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const ars: AsyncIterable = client.getAnonymityRevokers( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const ars: AsyncIterable = client.getAnonymityRevokers(blockHash); for await (const ar of ars) { console.log('Anonymity Revoker ID:', ar.arIdentity); diff --git a/examples/client/getBakerList.ts b/examples/client/getBakerList.ts index 2aaaeba47..c7f0f0e87 100644 --- a/examples/client/getBakerList.ts +++ b/examples/client/getBakerList.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { BakerId, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BakerId, BlockHash } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -48,9 +49,11 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const bakerIds: AsyncIterable = client.getBakerList( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const bakerIds: AsyncIterable = client.getBakerList(blockHash); // #endregion documentation-snippet console.log('List of BakerID at the specified block:'); diff --git a/examples/client/getBannedPeers.ts b/examples/client/getBannedPeers.ts index d7b44927f..5e8d465f3 100644 --- a/examples/client/getBannedPeers.ts +++ b/examples/client/getBannedPeers.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, IpAddressString } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { IpAddressString } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +28,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getBlockChainParameters.ts b/examples/client/getBlockChainParameters.ts index 8eb666eab..54b3de953 100644 --- a/examples/client/getBlockChainParameters.ts +++ b/examples/client/getBlockChainParameters.ts @@ -1,10 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - ChainParameters, - createConcordiumClient, - isChainParametersV1, - isChainParametersV2, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, ChainParameters } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -37,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -49,9 +45,11 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const cp: ChainParameters = await client.getBlockChainParameters( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const cp: ChainParameters = await client.getBlockChainParameters(blockHash); const euroPerEnergy = cp.euroPerEnergy.numerator + '/' + cp.euroPerEnergy.denominator; @@ -59,9 +57,9 @@ const client = createConcordiumClient( console.log('Euro per Energy:', euroPerEnergy); // Check version of chain parameters - if (isChainParametersV2(cp)) { + if (cp.version === 2) { console.log('Minimum block time', cp.minBlockTime); - } else if (isChainParametersV1(cp)) { + } else if (cp.version === 1) { console.log('Minimum equity capital:', cp.minimumEquityCapital); } else { console.log( diff --git a/examples/client/getBlockFinalizationSummary.ts b/examples/client/getBlockFinalizationSummary.ts index fe8d66262..b61d2d273 100644 --- a/examples/client/getBlockFinalizationSummary.ts +++ b/examples/client/getBlockFinalizationSummary.ts @@ -1,8 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - BlockFinalizationSummary, - createConcordiumClient, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockFinalizationSummary, BlockHash } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -51,8 +49,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const summary: BlockFinalizationSummary = - await client.getBlockFinalizationSummary(cli.flags.block); + await client.getBlockFinalizationSummary(blockHash); if (summary.tag === 'record') { // Response contains finalization summary for the given block: diff --git a/examples/client/getBlockInfo.ts b/examples/client/getBlockInfo.ts index c7f1f4702..bf9b99d09 100644 --- a/examples/client/getBlockInfo.ts +++ b/examples/client/getBlockInfo.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { BlockInfo, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, BlockInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, port, credentials.createInsecure() @@ -45,7 +46,11 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const blockInfo: BlockInfo = await client.getBlockInfo(cli.flags.block); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const blockInfo: BlockInfo = await client.getBlockInfo(blockHash); // #endregion documentation-snippet console.dir(blockInfo, { depth: null, colors: true }); diff --git a/examples/client/getBlockItemStatus.ts b/examples/client/getBlockItemStatus.ts index 5843a6afd..173625bbb 100644 --- a/examples/client/getBlockItemStatus.ts +++ b/examples/client/getBlockItemStatus.ts @@ -1,5 +1,10 @@ -import { parseEndpoint } from '../shared/util'; -import { BlockItemStatus, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { + BlockItemStatus, + CcdAmount, + TransactionHash, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +40,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -52,8 +57,9 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockItemStatus: BlockItemStatus = await client.getBlockItemStatus( - cli.flags.transaction + TransactionHash.fromHexString(cli.flags.transaction) ); console.log('Status of the transaction:', cli.flags.transaction, '\n'); @@ -84,7 +90,7 @@ const client = createConcordiumClient( case 'transfer': // The transaction is a simple transfer const { amount, to } = summary.transfer; - const ccdAmount = Number(amount / 1000000n); + const ccdAmount = CcdAmount.toCcd(amount); console.log(ccdAmount, 'CCD sent to', to); break; case 'failed': diff --git a/examples/client/getBlockPendingUpdates.ts b/examples/client/getBlockPendingUpdates.ts index 69a7af3b7..bcdeb447f 100644 --- a/examples/client/getBlockPendingUpdates.ts +++ b/examples/client/getBlockPendingUpdates.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, PendingUpdate } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, PendingUpdate } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -51,8 +52,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const pendingUpdates: AsyncIterable = - client.getBlockPendingUpdates(cli.flags.block); + client.getBlockPendingUpdates(blockHash); // #endregion documentation-snippet for await (const pendingUpdate of pendingUpdates) { diff --git a/examples/client/getBlockSpecialEvents.ts b/examples/client/getBlockSpecialEvents.ts index 639a71412..50a9b8213 100644 --- a/examples/client/getBlockSpecialEvents.ts +++ b/examples/client/getBlockSpecialEvents.ts @@ -1,8 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - BlockSpecialEvent, - createConcordiumClient, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, BlockSpecialEvent } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -55,8 +53,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const events: AsyncIterable = - client.getBlockSpecialEvents(cli.flags.block); + client.getBlockSpecialEvents(blockHash); // #endregion documentation-snippet for await (const event of events) { diff --git a/examples/client/getBlockTransactionEvents.ts b/examples/client/getBlockTransactionEvents.ts index b2dd89837..d312fca62 100644 --- a/examples/client/getBlockTransactionEvents.ts +++ b/examples/client/getBlockTransactionEvents.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { BlockItemSummary, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, BlockItemSummary } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -50,8 +51,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const events: AsyncIterable = - client.getBlockTransactionEvents(cli.flags.block); + client.getBlockTransactionEvents(blockHash); // #endregion documentation-snippet for await (const event of events) { diff --git a/examples/client/getBlocks.ts b/examples/client/getBlocks.ts index 8f13b2b57..a4e0204ac 100644 --- a/examples/client/getBlocks.ts +++ b/examples/client/getBlocks.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { ArrivedBlockInfo, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ArrivedBlockInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +28,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getBlocksAbort.ts b/examples/client/getBlocksAbort.ts index 646761fc8..c82f6b2c7 100644 --- a/examples/client/getBlocksAbort.ts +++ b/examples/client/getBlocksAbort.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, ArrivedBlockInfo } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ArrivedBlockInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +28,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getBlocksAtHeightAbsolute.ts b/examples/client/getBlocksAtHeightAbsolute.ts index 2a1fa8649..219cf36bc 100644 --- a/examples/client/getBlocksAtHeightAbsolute.ts +++ b/examples/client/getBlocksAtHeightAbsolute.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, HexString } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +36,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -47,7 +48,7 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const blocks: HexString[] = await client.getBlocksAtHeight( + const blocks: BlockHash.Type[] = await client.getBlocksAtHeight( BigInt(cli.flags.height) ); // #endregion documentation-snippet diff --git a/examples/client/getBlocksAtHeightRelative.ts b/examples/client/getBlocksAtHeightRelative.ts index f7c6364c0..23423635e 100644 --- a/examples/client/getBlocksAtHeightRelative.ts +++ b/examples/client/getBlocksAtHeightRelative.ts @@ -1,9 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - BlocksAtHeightRequest, - createConcordiumClient, - HexString, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, BlocksAtHeightRequest } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -53,7 +50,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -70,7 +67,7 @@ const client = createConcordiumClient( height: BigInt(cli.flags.height), restrict: cli.flags.restrict, }; - const blocks: HexString[] = await client.getBlocksAtHeight(request); + const blocks: BlockHash.Type[] = await client.getBlocksAtHeight(request); // #endregion documentation-snippet for (const block of blocks) { diff --git a/examples/client/getBranches.ts b/examples/client/getBranches.ts index 4ce830789..c64a84223 100644 --- a/examples/client/getBranches.ts +++ b/examples/client/getBranches.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { Branch, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { Branch } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +28,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getConsensusStatus.ts b/examples/client/getConsensusStatus.ts index e2065a364..e3192a547 100644 --- a/examples/client/getConsensusStatus.ts +++ b/examples/client/getConsensusStatus.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { ConsensusStatus, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConsensusStatus } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +28,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getCryptographicParameters.ts b/examples/client/getCryptographicParameters.ts index 72ceaf046..618da9363 100644 --- a/examples/client/getCryptographicParameters.ts +++ b/examples/client/getCryptographicParameters.ts @@ -1,8 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - createConcordiumClient, - CryptographicParameters, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, CryptographicParameters } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -48,8 +46,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const parameters: CryptographicParameters = - await client.getCryptographicParameters(cli.flags.block); + await client.getCryptographicParameters(blockHash); // #endregion documentation-snippet console.log('Genesis string:', parameters.genesisString); diff --git a/examples/client/getElectionInfo.ts b/examples/client/getElectionInfo.ts index 43ae5aaf9..09097b5c1 100644 --- a/examples/client/getElectionInfo.ts +++ b/examples/client/getElectionInfo.ts @@ -1,9 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - createConcordiumClient, - ElectionInfo, - isElectionInfoV0, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, ElectionInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -36,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -49,9 +46,11 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const electionInfo: ElectionInfo = await client.getElectionInfo( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const electionInfo: ElectionInfo = await client.getElectionInfo(blockHash); // Discard address, convert to tuple: const bakers: [bigint, number][] = electionInfo.bakerElectionInfo.map( @@ -63,7 +62,7 @@ const client = createConcordiumClient( console.log('Bakers sorted by lottery power:', sortedBakers); console.log('Election nonce:', electionInfo.electionNonce); - if (isElectionInfoV0(electionInfo)) { + if (electionInfo.version === 0) { console.log('Election difficulty:', electionInfo.electionDifficulty); } // #endregion documentation-snippet diff --git a/examples/client/getEmbeddedSchema.ts b/examples/client/getEmbeddedSchema.ts index 87cddbeab..dcb81bb27 100644 --- a/examples/client/getEmbeddedSchema.ts +++ b/examples/client/getEmbeddedSchema.ts @@ -1,4 +1,5 @@ -import { createConcordiumClient, ModuleReference } from '@concordium/node-sdk'; +import { ModuleReference } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -40,7 +41,7 @@ const cli = meow( ); const [address, port] = cli.flags.endpoint.split(':'); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -52,7 +53,7 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const moduleRef = new ModuleReference(cli.flags.module); + const moduleRef = ModuleReference.fromHexString(cli.flags.module); const schema = await client.getEmbeddedSchema(moduleRef); // #endregion documentation-snippet diff --git a/examples/client/getFinalizedBlocks.ts b/examples/client/getFinalizedBlocks.ts index 1220ee3bc..1336f77fd 100644 --- a/examples/client/getFinalizedBlocks.ts +++ b/examples/client/getFinalizedBlocks.ts @@ -1,8 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - createConcordiumClient, - FinalizedBlockInfo, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { FinalizedBlockInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -30,7 +28,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getFinalizedBlocksFrom.ts b/examples/client/getFinalizedBlocksFrom.ts index 406b43bce..ec0a636ac 100644 --- a/examples/client/getFinalizedBlocksFrom.ts +++ b/examples/client/getFinalizedBlocksFrom.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -39,7 +39,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getIdentityProviders.ts b/examples/client/getIdentityProviders.ts index 1a7b3939d..9bf97fe9d 100644 --- a/examples/client/getIdentityProviders.ts +++ b/examples/client/getIdentityProviders.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, IpInfo } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, IpInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -48,9 +49,11 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const ips: AsyncIterable = client.getIdentityProviders( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const ips: AsyncIterable = client.getIdentityProviders(blockHash); for await (const ip of ips) { console.log('Identity Provider ID:', ip.ipIdentity); diff --git a/examples/client/getInstanceInfo.ts b/examples/client/getInstanceInfo.ts index 3c5ad4ce6..99a361eee 100644 --- a/examples/client/getInstanceInfo.ts +++ b/examples/client/getInstanceInfo.ts @@ -1,9 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - ContractAddress, - createConcordiumClient, - InstanceInfo, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, ContractAddress, InstanceInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -44,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -57,14 +54,15 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const contractAddress: ContractAddress = { - index: BigInt(cli.flags.contract), - subindex: 0n, - }; + const contractAddress = ContractAddress.create(cli.flags.contract); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const instanceInfo: InstanceInfo = await client.getInstanceInfo( contractAddress, - cli.flags.block + blockHash ); console.log('Name:', instanceInfo.name); diff --git a/examples/client/getInstanceState.ts b/examples/client/getInstanceState.ts index 7c2fc74a2..be557aa81 100644 --- a/examples/client/getInstanceState.ts +++ b/examples/client/getInstanceState.ts @@ -1,8 +1,10 @@ -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import { - createConcordiumClient, + BlockHash, + ContractAddress, InstanceStateKVPair, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -43,7 +45,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -60,13 +62,14 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const contractAddress = { - index: BigInt(cli.flags.contract), - subindex: 0n, - }; + const contractAddress = ContractAddress.create(cli.flags.contract); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const states: AsyncIterable = client.getInstanceState( contractAddress, - cli.flags.block + blockHash ); // #endregion documentation-snippet diff --git a/examples/client/getModuleList.ts b/examples/client/getModuleList.ts index 59ec10c31..0d678ac32 100644 --- a/examples/client/getModuleList.ts +++ b/examples/client/getModuleList.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, HexString } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, ModuleReference } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -49,13 +50,16 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const moduleRefs: AsyncIterable = client.getModuleList( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const moduleRefs: AsyncIterable = + client.getModuleList(blockHash); // #endregion documentation-snippet // Prints module references for await (const moduleRef of moduleRefs) { - console.log(moduleRef); + console.log(ModuleReference.toHexString(moduleRef)); } })(); diff --git a/examples/client/getModuleSource.ts b/examples/client/getModuleSource.ts index b2da89935..6d53dd54c 100644 --- a/examples/client/getModuleSource.ts +++ b/examples/client/getModuleSource.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, ModuleReference } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, ModuleReference } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import fs from 'fs'; @@ -47,7 +48,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -60,8 +61,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const ref = new ModuleReference(cli.flags.module); - const versionedSource = await client.getModuleSource(ref, cli.flags.block); + const ref = ModuleReference.fromHexString(cli.flags.module); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const versionedSource = await client.getModuleSource(ref, blockHash); // #endregion documentation-snippet fs.writeFileSync(cli.flags.outPath, versionedSource.source); diff --git a/examples/client/getNextAccountSequenceNumber.ts b/examples/client/getNextAccountSequenceNumber.ts index 9d9068f39..84b9216ba 100644 --- a/examples/client/getNextAccountSequenceNumber.ts +++ b/examples/client/getNextAccountSequenceNumber.ts @@ -1,9 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - AccountAddress, - createConcordiumClient, - NextAccountNonce, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { AccountAddress, NextAccountNonce } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -39,7 +36,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -55,7 +52,7 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const account = new AccountAddress(cli.flags.account); + const account = AccountAddress.fromBase58(cli.flags.account); const nextNonce: NextAccountNonce = await client.getNextAccountNonce( account ); diff --git a/examples/client/getNextUpdateSequenceNumbers.ts b/examples/client/getNextUpdateSequenceNumbers.ts index 2a7365f8c..7a2fd324a 100644 --- a/examples/client/getNextUpdateSequenceNumbers.ts +++ b/examples/client/getNextUpdateSequenceNumbers.ts @@ -1,8 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - createConcordiumClient, - NextUpdateSequenceNumbers, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, NextUpdateSequenceNumbers } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -48,8 +46,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const updateSeqNums: NextUpdateSequenceNumbers = - await client.getNextUpdateSequenceNumbers(cli.flags.block); + await client.getNextUpdateSequenceNumbers(blockHash); // #endregion documentation-snippet console.dir(updateSeqNums, { depth: null, colors: true }); diff --git a/examples/client/getNodeInfo.ts b/examples/client/getNodeInfo.ts index 477ce7d74..80423fad8 100644 --- a/examples/client/getNodeInfo.ts +++ b/examples/client/getNodeInfo.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, NodeInfo } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { NodeInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getPassiveDelegationInfo.ts b/examples/client/getPassiveDelegationInfo.ts index 8855ce952..175947b59 100644 --- a/examples/client/getPassiveDelegationInfo.ts +++ b/examples/client/getPassiveDelegationInfo.ts @@ -1,8 +1,10 @@ -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import { - createConcordiumClient, + BlockHash, + CcdAmount, PassiveDelegationStatus, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +37,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -49,16 +51,20 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const passiveDelegationInfo: PassiveDelegationStatus = - await client.getPassiveDelegationInfo(cli.flags.block); + await client.getPassiveDelegationInfo(blockHash); console.log( 'CCD provided by the delegators to the pool:', - passiveDelegationInfo.delegatedCapital / 1000000n + CcdAmount.toCcd(passiveDelegationInfo.delegatedCapital) ); console.log( 'Total capital in CCD of ALL pools:', - passiveDelegationInfo.allPoolTotalCapital / 1000000n + CcdAmount.toCcd(passiveDelegationInfo.allPoolTotalCapital) ); console.log('Pool commision rates:', passiveDelegationInfo.commissionRates); // #endregion documentation-snippet diff --git a/examples/client/getPassiveDelegators.ts b/examples/client/getPassiveDelegators.ts index da7815d51..68d27d338 100644 --- a/examples/client/getPassiveDelegators.ts +++ b/examples/client/getPassiveDelegators.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, DelegatorInfo } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, DelegatorInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -54,8 +55,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const delegators: AsyncIterable = - client.getPassiveDelegators(cli.flags.block); + client.getPassiveDelegators(blockHash); console.log('Each staking account and the amount of stake they have:\n'); for await (const delegatorInfo of delegators) { diff --git a/examples/client/getPassiveDelegatorsPendingChanges.ts b/examples/client/getPassiveDelegatorsPendingChanges.ts index 90a48ce44..8880b2f0c 100644 --- a/examples/client/getPassiveDelegatorsPendingChanges.ts +++ b/examples/client/getPassiveDelegatorsPendingChanges.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, DelegatorInfo } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, DelegatorInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -32,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -54,8 +55,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const delegators: AsyncIterable = - client.getPassiveDelegators(cli.flags.block); + client.getPassiveDelegators(blockHash); console.log('Each staking account and the amount of stake they have:\n'); for await (const delegatorInfo of delegators) { diff --git a/examples/client/getPassiveDelegatorsRewardPeriod.ts b/examples/client/getPassiveDelegatorsRewardPeriod.ts index 380f9a34c..c64499e88 100644 --- a/examples/client/getPassiveDelegatorsRewardPeriod.ts +++ b/examples/client/getPassiveDelegatorsRewardPeriod.ts @@ -1,8 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - createConcordiumClient, - DelegatorRewardPeriodInfo, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, DelegatorRewardPeriodInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +33,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -56,8 +54,12 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const delegators: AsyncIterable = - client.getPassiveDelegatorsRewardPeriod(cli.flags.block); + client.getPassiveDelegatorsRewardPeriod(blockHash); console.log('Each staking account and the amount of stake they have:\n'); for await (const delegatorInfo of delegators) { diff --git a/examples/client/getPeersInfo.ts b/examples/client/getPeersInfo.ts index b6f402b15..ad736fad5 100644 --- a/examples/client/getPeersInfo.ts +++ b/examples/client/getPeersInfo.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, PeerInfo } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { PeerInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +28,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/getPoolDelegators.ts b/examples/client/getPoolDelegators.ts index db7bba9a5..e4d4817f0 100644 --- a/examples/client/getPoolDelegators.ts +++ b/examples/client/getPoolDelegators.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient, DelegatorInfo } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, DelegatorInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -10,7 +11,7 @@ const cli = meow( $ yarn run-example [options] Required: - --pool-owner, -p The BakerId of the pool owner + --pool-owner, -p The BakerId of the pool owner Options --help, -h Displays this message @@ -40,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -62,9 +63,13 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const delegators: AsyncIterable = client.getPoolDelegators( BigInt(cli.flags.poolOwner), - cli.flags.block + blockHash ); console.log('Each staking account and the amount of stake they have:\n'); diff --git a/examples/client/getPoolDelegatorsRewardPeriod.ts b/examples/client/getPoolDelegatorsRewardPeriod.ts index 016fa2230..d535bcfb9 100644 --- a/examples/client/getPoolDelegatorsRewardPeriod.ts +++ b/examples/client/getPoolDelegatorsRewardPeriod.ts @@ -1,8 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - createConcordiumClient, - DelegatorRewardPeriodInfo, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, DelegatorRewardPeriodInfo } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -13,7 +11,7 @@ const cli = meow( $ yarn run-example [options] Required: - --pool-owner, -p The BakerId of the pool owner + --pool-owner, -p The BakerId of the pool owner Options --help, -h Displays this message @@ -43,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -64,10 +62,14 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const delegators: AsyncIterable = client.getPoolDelegatorsRewardPeriod( BigInt(cli.flags.poolOwner), - cli.flags.block + blockHash ); console.log('Each staking account and the amount of stake they have:\n'); diff --git a/examples/client/getPoolInfo.ts b/examples/client/getPoolInfo.ts index c33e7e2bc..70c1636ae 100644 --- a/examples/client/getPoolInfo.ts +++ b/examples/client/getPoolInfo.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { BakerPoolStatus, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BakerPoolStatus, BlockHash, CcdAmount } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -10,7 +11,7 @@ const cli = meow( $ yarn run-example [options] Required: - --pool-owner, -p The BakerId of the pool owner + --pool-owner, -p The BakerId of the pool owner Options --help, -h Displays this message @@ -40,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -53,24 +54,28 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const bakerPool: BakerPoolStatus = await client.getPoolInfo( BigInt(cli.flags.poolOwner), - cli.flags.block + blockHash ); console.log('Open status:', bakerPool.poolInfo.openStatus); console.log('Baker address:', bakerPool.bakerAddress); console.log( 'CCD provided by the baker to the pool:', - bakerPool.bakerEquityCapital / 1000000n + CcdAmount.toCcd(bakerPool.bakerEquityCapital) ); console.log( 'CCD provided by the delegators to the pool:', - bakerPool.delegatedCapital / 1000000n + CcdAmount.toCcd(bakerPool.delegatedCapital) ); console.log( 'Total capital in CCD of ALL pools:', - bakerPool.allPoolTotalCapital / 1000000n + CcdAmount.toCcd(bakerPool.allPoolTotalCapital) ); console.log('Pool commision rates:', bakerPool.poolInfo.commissionRates); // #endregion documentation-snippet diff --git a/examples/client/getTokenomicsInfo.ts b/examples/client/getTokenomicsInfo.ts index a4d81be24..a7d0a0f74 100644 --- a/examples/client/getTokenomicsInfo.ts +++ b/examples/client/getTokenomicsInfo.ts @@ -1,5 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { isRewardStatusV1, createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -10,7 +11,7 @@ const cli = meow( $ yarn run-example [options] Required: - --pool-owner, -p The BakerId of the pool owner + --pool-owner, -p The BakerId of the pool owner Options --help, Displays this message @@ -40,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -53,11 +54,15 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const tokenomics = await client.getTokenomicsInfo(cli.flags.block); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + const tokenomics = await client.getTokenomicsInfo(blockHash); // Protocol version 4 expanded the amount of information in the response, so one should check the type to access that. // This information includes information about the payday and total amount of funds staked. - if (isRewardStatusV1(tokenomics)) { + if (tokenomics.version === 1) { console.log('Next payday time:', tokenomics.nextPaydayTime); console.log( 'Total staked amount by bakers and delegators', diff --git a/examples/client/healthCheck.ts b/examples/client/healthCheck.ts index bca2d96da..c49b22a25 100644 --- a/examples/client/healthCheck.ts +++ b/examples/client/healthCheck.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +27,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/instanceStateLookup.ts b/examples/client/instanceStateLookup.ts index cbbbdcddc..0d4a038e7 100644 --- a/examples/client/instanceStateLookup.ts +++ b/examples/client/instanceStateLookup.ts @@ -1,9 +1,6 @@ -import { parseEndpoint } from '../shared/util'; -import { - ContractAddress, - HexString, - createConcordiumClient, -} from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { BlockHash, ContractAddress, HexString } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -50,7 +47,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -65,15 +62,16 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const contract: ContractAddress = { - index: BigInt(cli.flags.contract), - subindex: 0n, - }; + const contract = ContractAddress.create(cli.flags.contract); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); const state: HexString = await client.instanceStateLookup( contract, cli.flags.key, - cli.flags.block + blockHash ); // #endregion documentation-snippet diff --git a/examples/client/invokeContract.ts b/examples/client/invokeContract.ts index e6b4d863c..b7b9936c8 100644 --- a/examples/client/invokeContract.ts +++ b/examples/client/invokeContract.ts @@ -1,13 +1,18 @@ -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import { AccountAddress, + BlockHash, CcdAmount, + ContractAddress, ContractContext, ContractTraceEvent, - createConcordiumClient, -} from '@concordium/node-sdk'; + Energy, + Parameter, + ReceiveName, + ReturnValue, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; -import { Buffer } from 'buffer/index.js'; import meow from 'meow'; @@ -27,7 +32,7 @@ const cli = meow( --energy, -n The maximum amount of energy to allow for execution, defaults to 1000000 --invoker, -i The address of the invoker, defaults to the zero account address --endpoint, -e Specify endpoint of the form "address:port", defaults to localhost:20000 - --parameter, -p The serialized parameters that the contract will be invoked with, as a + --parameter, -p The serialized parameters that the contract will be invoked with, as a hex string. Will default to an empty hex string meaning no parameters `, { @@ -74,7 +79,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -92,23 +97,22 @@ const client = createConcordiumClient( // #region documentation-snippet // Handle the optional arguments const invoker = cli.flags.invoker - ? new AccountAddress(cli.flags.invoker) + ? AccountAddress.fromBase58(cli.flags.invoker) : undefined; const amount = cli.flags.amount - ? new CcdAmount(BigInt(cli.flags.amount)) + ? CcdAmount.fromMicroCcd(cli.flags.amount) : undefined; const parameter = cli.flags.parameter - ? Buffer.from(cli.flags.parameter, 'hex') + ? Parameter.fromHexString(cli.flags.parameter) + : undefined; + const energy = cli.flags.energy + ? Energy.create(cli.flags.energy) : undefined; - const energy = cli.flags.energy ? BigInt(cli.flags.energy) : undefined; - const contract = { - index: BigInt(cli.flags.contract), - subindex: 0n, - }; + const contract = ContractAddress.create(cli.flags.contract); const context: ContractContext = { // Required - method: cli.flags.receive, + method: ReceiveName.fromString(cli.flags.receive), contract, // Optional invoker, @@ -116,8 +120,12 @@ const client = createConcordiumClient( parameter, energy, }; + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); - const result = await client.invokeContract(context, cli.flags.block); + const result = await client.invokeContract(context, blockHash); // We can inspect the result if (result.tag === 'failure') { @@ -126,9 +134,12 @@ const client = createConcordiumClient( } else if (result.tag === 'success') { console.log('Invoke was succesful'); - const returnValue: string | undefined = result.returnValue; // If the invoked method has return value + const returnValue = result.returnValue; // If the invoked method has return value if (returnValue) { - console.log('The return value of the invoked method:', returnValue); + console.log( + 'The return value of the invoked method:', + ReturnValue.toHexString(returnValue) + ); } const events: ContractTraceEvent[] = result.events; diff --git a/examples/client/peerConnect.ts b/examples/client/peerConnect.ts index 17f516fcc..461e14654 100644 --- a/examples/client/peerConnect.ts +++ b/examples/client/peerConnect.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -41,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/peerDisconnect.ts b/examples/client/peerDisconnect.ts index c3005644c..eddbf1190 100644 --- a/examples/client/peerDisconnect.ts +++ b/examples/client/peerDisconnect.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -41,7 +41,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/shutdown.ts b/examples/client/shutdown.ts index 5fc872e7c..ac326179a 100644 --- a/examples/client/shutdown.ts +++ b/examples/client/shutdown.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -27,7 +27,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/client/unbanPeer.ts b/examples/client/unbanPeer.ts index dc99232f9..1d54805f1 100644 --- a/examples/client/unbanPeer.ts +++ b/examples/client/unbanPeer.ts @@ -1,5 +1,5 @@ -import { parseEndpoint } from '../shared/util'; -import { createConcordiumClient } from '@concordium/node-sdk'; +import { parseEndpoint } from '../shared/util.js'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -35,7 +35,7 @@ const cli = meow( const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/common/aliases.ts b/examples/common/aliases.ts index 71aff6a98..c20a4fa5b 100644 --- a/examples/common/aliases.ts +++ b/examples/common/aliases.ts @@ -1,4 +1,4 @@ -import { AccountAddress, getAlias, isAlias } from '@concordium/node-sdk'; +import { AccountAddress } from '@concordium/web-sdk'; /** * The following shows how to generate an account alias, and how to test @@ -8,26 +8,29 @@ import { AccountAddress, getAlias, isAlias } from '@concordium/node-sdk'; */ // #region documentation-snippet -const accountAddress = new AccountAddress( +const accountAddress = AccountAddress.fromBase58( '3sAHwfehRNEnXk28W7A3XB3GzyBiuQkXLNRmDwDGPUe8JsoAcU' ); -const seperateAccount = new AccountAddress( +const seperateAccount = AccountAddress.fromBase58( '4ZJBYQbVp3zVZyjCXfZAAYBVkJMyVj8UKUNj9ox5YqTCBdBq2M' ); const aliasCounter = 0; -const alias: AccountAddress = getAlias(accountAddress, aliasCounter); +const alias: AccountAddress.Type = AccountAddress.getAlias( + accountAddress, + aliasCounter +); console.log('Original address:', accountAddress.address); console.log('Alias address:', alias.address); // The function `isAlias` can be used to check if two acounts are aliases -if (!isAlias(alias, accountAddress)) { +if (!AccountAddress.isAlias(alias, accountAddress)) { throw Error('Expected accounts to be aliases!'); } // Of course, using `isAlias` on a completely seperate account returns false -if (isAlias(accountAddress, seperateAccount)) { +if (AccountAddress.isAlias(accountAddress, seperateAccount)) { throw Error('Two seperate accounts are claimed to be aliases!'); } // #endregion documentation-snippet diff --git a/examples/common/bakerAdd.ts b/examples/common/bakerAdd.ts index 99c9bc13d..e37f41dbe 100644 --- a/examples/common/bakerAdd.ts +++ b/examples/common/bakerAdd.ts @@ -5,17 +5,17 @@ import { AccountTransactionType, signTransaction, TransactionExpiry, - createConcordiumClient, CcdAmount, generateBakerKeys, ConfigureBakerPayload, OpenStatus, parseWallet, buildAccountSigner, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import { readFileSync } from 'node:fs'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -55,7 +55,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -71,11 +71,11 @@ const client = createConcordiumClient( // Read wallet-file const walletFile = readFileSync(cli.flags.walletFile, 'utf8'); const wallet = parseWallet(walletFile); - const sender = new AccountAddress(wallet.value.address); + const sender = AccountAddress.fromBase58(wallet.value.address); const signer = buildAccountSigner(wallet); const header: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: (await client.getNextAccountNonce(sender)).nonce, sender, }; @@ -83,7 +83,7 @@ const client = createConcordiumClient( const bakerKeys = generateBakerKeys(sender); const configureBakerPayload: ConfigureBakerPayload = { - stake: new CcdAmount(BigInt(cli.flags.stake)), + stake: CcdAmount.fromMicroCcd(cli.flags.stake), restakeEarnings: true, openForDelegation: OpenStatus.OpenForAll, keys: bakerKeys, diff --git a/examples/common/bakerRemove.ts b/examples/common/bakerRemove.ts index 9ffd40ca8..8c109ca28 100644 --- a/examples/common/bakerRemove.ts +++ b/examples/common/bakerRemove.ts @@ -5,15 +5,15 @@ import { AccountTransactionType, signTransaction, TransactionExpiry, - createConcordiumClient, CcdAmount, ConfigureBakerPayload, parseWallet, buildAccountSigner, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import { readFileSync } from 'node:fs'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -47,7 +47,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -62,17 +62,17 @@ const client = createConcordiumClient( // Read wallet-file const walletFile = readFileSync(cli.flags.walletFile, 'utf8'); const wallet = parseWallet(walletFile); - const sender = new AccountAddress(wallet.value.address); + const sender = AccountAddress.fromBase58(wallet.value.address); const signer = buildAccountSigner(wallet); const header: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: (await client.getNextAccountNonce(sender)).nonce, sender, }; const configureBakerPayload: ConfigureBakerPayload = { - stake: new CcdAmount(0n), + stake: CcdAmount.zero(), }; const configureBakerAccountTransaction: AccountTransaction = { diff --git a/examples/common/buildAccountSigner.ts b/examples/common/buildAccountSigner.ts index 912c0ac34..0655ae9a4 100644 --- a/examples/common/buildAccountSigner.ts +++ b/examples/common/buildAccountSigner.ts @@ -6,7 +6,7 @@ import { signMessage, buildAccountSigner, parseWallet, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; const cli = meow( ` @@ -45,9 +45,11 @@ const wallet = parseWallet(walletFile); try { const signer = buildAccountSigner(wallet); - signMessage(new AccountAddress(wallet.value.address), 'test', signer).then( - console.log - ); + signMessage( + AccountAddress.fromBase58(wallet.value.address), + 'test', + signer + ).then(console.log); } catch { console.error('File passed does not conform to a supported JSON format'); } diff --git a/examples/common/cis0Supports.ts b/examples/common/cis0Supports.ts index caa6d4450..20461ccdf 100644 --- a/examples/common/cis0Supports.ts +++ b/examples/common/cis0Supports.ts @@ -1,11 +1,8 @@ -import { - createConcordiumClient, - cis0Supports, - ContractAddress, -} from '@concordium/node-sdk'; +import { cis0Supports, ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -50,17 +47,17 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() ); (async () => { - const contractAddress: ContractAddress = { - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }; + const contractAddress = ContractAddress.create( + cli.flags.index, + cli.flags.subindex + ); const response = await cis0Supports( client, contractAddress, diff --git a/examples/common/delegationAdd.ts b/examples/common/delegationAdd.ts index 75cff2311..480f34749 100644 --- a/examples/common/delegationAdd.ts +++ b/examples/common/delegationAdd.ts @@ -5,18 +5,18 @@ import { AccountTransactionType, signTransaction, TransactionExpiry, - createConcordiumClient, ConfigureDelegationPayload, CcdAmount, DelegationTargetType, parseWallet, buildAccountSigner, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import { readFileSync } from 'node:fs'; import meow from 'meow'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; const cli = meow( ` @@ -60,7 +60,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -76,17 +76,17 @@ const client = createConcordiumClient( // Read wallet-file const walletFile = readFileSync(cli.flags.walletFile, 'utf8'); const wallet = parseWallet(walletFile); - const sender = new AccountAddress(wallet.value.address); + const sender = AccountAddress.fromBase58(wallet.value.address); const signer = buildAccountSigner(wallet); const header: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: (await client.getNextAccountNonce(sender)).nonce, sender: sender, }; const configureDelegationPayload: ConfigureDelegationPayload = { - stake: new CcdAmount(BigInt(cli.flags.stake)), + stake: CcdAmount.fromMicroCcd(cli.flags.stake), delegationTarget: { delegateType: DelegationTargetType.PassiveDelegation, }, diff --git a/examples/common/delegationRemove.ts b/examples/common/delegationRemove.ts index 30229177f..9b8a495ce 100644 --- a/examples/common/delegationRemove.ts +++ b/examples/common/delegationRemove.ts @@ -5,15 +5,15 @@ import { AccountTransactionType, signTransaction, TransactionExpiry, - createConcordiumClient, ConfigureDelegationPayload, CcdAmount, parseWallet, buildAccountSigner, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import { readFileSync } from 'node:fs'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -47,7 +47,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -62,16 +62,16 @@ const client = createConcordiumClient( // Read wallet-file const walletFile = readFileSync(cli.flags.walletFile, 'utf8'); const wallet = parseWallet(walletFile); - const sender = new AccountAddress(wallet.value.address); + const sender = AccountAddress.fromBase58(wallet.value.address); const header: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: (await client.getNextAccountNonce(sender)).nonce, sender: sender, }; const configureDelegationPayload: ConfigureDelegationPayload = { - stake: new CcdAmount(0n), + stake: CcdAmount.zero(), }; const configureDelegationTransaction: AccountTransaction = { diff --git a/examples/common/deployModule.ts b/examples/common/deployModule.ts index baf53243e..d740412cd 100644 --- a/examples/common/deployModule.ts +++ b/examples/common/deployModule.ts @@ -7,14 +7,13 @@ import { DeployModulePayload, signTransaction, TransactionExpiry, - createConcordiumClient, parseWallet, buildAccountSigner, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import { readFileSync } from 'node:fs'; -import { Buffer } from 'buffer/index.js'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -54,7 +53,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -68,7 +67,7 @@ const client = createConcordiumClient( // #region documentation-snippet const walletFile = readFileSync(cli.flags.walletFile, 'utf8'); const wallet = parseWallet(walletFile); - const sender = new AccountAddress(wallet.value.address); + const sender = AccountAddress.fromBase58(wallet.value.address); // Get the wasm file as a buffer. const wasmModule = Buffer.from(readFileSync(cli.flags.moduleFile)); @@ -82,7 +81,7 @@ const client = createConcordiumClient( }; const header: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: (await client.getNextAccountNonce(sender)).nonce, sender, }; diff --git a/examples/common/simpleTransfer.ts b/examples/common/simpleTransfer.ts index 1bae14443..1b3417046 100644 --- a/examples/common/simpleTransfer.ts +++ b/examples/common/simpleTransfer.ts @@ -9,14 +9,13 @@ import { NextAccountNonce, signTransaction, TransactionExpiry, - createConcordiumClient, parseWallet, buildAccountSigner, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import { readFileSync } from 'node:fs'; -import { Buffer } from 'buffer/index.js'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -68,7 +67,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -82,15 +81,15 @@ const client = createConcordiumClient( // #region documentation-snippet const walletFile = readFileSync(cli.flags.walletFile, 'utf8'); const walletExport = parseWallet(walletFile); - const sender = new AccountAddress(walletExport.value.address); + const sender = AccountAddress.fromBase58(walletExport.value.address); - const toAddress = new AccountAddress(cli.flags.receiver); + const toAddress = AccountAddress.fromBase58(cli.flags.receiver); const nextNonce: NextAccountNonce = await client.getNextAccountNonce( sender ); const header: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: nextNonce.nonce, sender, }; @@ -99,13 +98,13 @@ const client = createConcordiumClient( let simpleTransfer = undefined; if (cli.flags.memo) { simpleTransfer = { - amount: new CcdAmount(BigInt(cli.flags.amount)), + amount: CcdAmount.fromMicroCcd(cli.flags.amount), toAddress, memo: new DataBlob(Buffer.from(cli.flags.memo, 'hex')), }; } else { simpleTransfer = { - amount: new CcdAmount(BigInt(cli.flags.amount)), + amount: CcdAmount.fromMicroCcd(cli.flags.amount), toAddress, }; } diff --git a/examples/common/statements.ts b/examples/common/statements.ts index 2a33d6035..c79447832 100644 --- a/examples/common/statements.ts +++ b/examples/common/statements.ts @@ -2,7 +2,7 @@ import { IdStatementBuilder, verifyIdstatement, AttributesKeys, -} from '@concordium/node-sdk'; +} from '@concordium/web-sdk'; /** * The following example shows how a proof statement can be built up. diff --git a/examples/common/streamToList.ts b/examples/common/streamToList.ts index dd3353594..66d8c4988 100644 --- a/examples/common/streamToList.ts +++ b/examples/common/streamToList.ts @@ -1,10 +1,7 @@ -import { - BakerId, - createConcordiumClient, - streamToList, -} from '@concordium/node-sdk'; +import { BakerId, BlockHash, streamToList } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -36,7 +33,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -48,9 +45,12 @@ const client = createConcordiumClient( */ async () => { - const bakerIds: AsyncIterable = client.getBakerList( - cli.flags.block - ); + const blockHash = + cli.flags.block === undefined + ? undefined + : BlockHash.fromHexString(cli.flags.block); + + const bakerIds: AsyncIterable = client.getBakerList(blockHash); const bakerList = await streamToList(bakerIds); diff --git a/examples/composed-examples/findAccountCreationBlock.ts b/examples/composed-examples/findAccountCreationBlock.ts index 8f6115c9e..0f2af7991 100644 --- a/examples/composed-examples/findAccountCreationBlock.ts +++ b/examples/composed-examples/findAccountCreationBlock.ts @@ -1,10 +1,7 @@ -import { - AccountAddress, - createConcordiumClient, - isRpcError, -} from '@concordium/node-sdk'; +import { AccountAddress, isRpcError } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -43,7 +40,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -54,7 +51,7 @@ const client = createConcordiumClient( */ (async () => { - const account = new AccountAddress(cli.flags.account); + const account = AccountAddress.fromBase58(cli.flags.account); const accBlock = await client.findEarliestFinalized(async (bi) => { try { @@ -84,7 +81,7 @@ const client = createConcordiumClient( for await (const summary of summaries) { if ( summary.type === 'accountCreation' && - summary.address === account.address + AccountAddress.equals(summary.address, account) ) { console.log( 'Hash of transaction that created the account:', diff --git a/examples/composed-examples/getEmbeddedSchemaFromInstance.ts b/examples/composed-examples/getEmbeddedSchemaFromInstance.ts index cf8e96e1b..c1dea6d12 100644 --- a/examples/composed-examples/getEmbeddedSchemaFromInstance.ts +++ b/examples/composed-examples/getEmbeddedSchemaFromInstance.ts @@ -1,4 +1,5 @@ -import { createConcordiumClient } from '@concordium/node-sdk'; +import { ContractAddress } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import meow from 'meow'; @@ -45,7 +46,7 @@ const cli = meow( ); const [address, port] = cli.flags.endpoint.split(':'); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -57,10 +58,11 @@ const client = createConcordiumClient( (async () => { // #region documentation-snippet - const info = await client.getInstanceInfo({ - index: BigInt(cli.flags.index), - subindex: BigInt(cli.flags.subindex), - }); + const contractAddress = ContractAddress.create( + cli.flags.index, + cli.flags.subindex + ); + const info = await client.getInstanceInfo(contractAddress); const moduleRef = info.sourceModule; const schema = await client.getEmbeddedSchema(moduleRef); // #endregion documentation-snippet diff --git a/examples/composed-examples/initAndUpdateContract.ts b/examples/composed-examples/initAndUpdateContract.ts index b2390ab82..c61a63a8f 100644 --- a/examples/composed-examples/initAndUpdateContract.ts +++ b/examples/composed-examples/initAndUpdateContract.ts @@ -5,7 +5,6 @@ import { AccountTransactionType, CcdAmount, ContractContext, - createConcordiumClient, deserializeReceiveReturnValue, InitContractPayload, ModuleReference, @@ -18,11 +17,16 @@ import { parseWallet, buildAccountSigner, affectedContracts, -} from '@concordium/node-sdk'; + ContractName, + ReceiveName, + Energy, + EntrypointName, + ReturnValue, +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; import { readFileSync } from 'node:fs'; -import { Buffer } from 'buffer/index.js'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -56,7 +60,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -73,15 +77,15 @@ const client = createConcordiumClient( const walletFile = readFileSync(cli.flags.walletFile, 'utf8'); const wallet = parseWallet(walletFile); - const sender = new AccountAddress(wallet.value.address); + const sender = AccountAddress.fromBase58(wallet.value.address); const signer = buildAccountSigner(wallet); - const moduleRef = new ModuleReference( + const moduleRef = ModuleReference.fromHexString( '44434352ddba724930d6b1b09cd58bd1fba6ad9714cf519566d5fe72d80da0d1' ); - const maxCost = 30000n; - const contractName = 'weather'; - const receiveName = 'weather.set'; + const maxCost = Energy.create(30000); + const contractName = ContractName.fromStringUnchecked('weather'); + const receiveName = ReceiveName.fromStringUnchecked('weather.set'); const schema = await client.getEmbeddedSchema(moduleRef); // --- Initialize Contract --- // @@ -91,7 +95,7 @@ const client = createConcordiumClient( // #region documentation-snippet-init-contract const initHeader: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: (await client.getNextAccountNonce(sender)).nonce, sender, }; @@ -103,7 +107,7 @@ const client = createConcordiumClient( ); const initPayload: InitContractPayload = { - amount: new CcdAmount(0n), + amount: CcdAmount.zero(), moduleRef: moduleRef, initName: contractName, param: initParams, @@ -142,20 +146,20 @@ const client = createConcordiumClient( // #region documentation-snippet-update-contract const updateHeader: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: TransactionExpiry.futureMinutes(60), nonce: (await client.getNextAccountNonce(sender)).nonce, sender, }; const updateParams = serializeUpdateContractParameters( contractName, - 'set', + EntrypointName.fromString('set'), rainyWeather, schema ); const updatePayload: UpdateContractPayload = { - amount: new CcdAmount(0n), + amount: CcdAmount.zero(), address: unwrap(contractAddress), receiveName, message: updateParams, @@ -192,21 +196,18 @@ const client = createConcordiumClient( const contextPostInit: ContractContext = { contract: unwrap(contractAddress), invoker: sender, - method: 'weather.get', + method: ReceiveName.fromString('weather.get'), }; const invokedPostInit = await client.invokeContract(contextPostInit); if (invokedPostInit.tag === 'success') { - const rawReturnValue = Buffer.from( - unwrap(invokedPostInit.returnValue), - 'hex' - ); + const rawReturnValue = unwrap(invokedPostInit.returnValue); const returnValue = deserializeReceiveReturnValue( - rawReturnValue, + ReturnValue.toBuffer(rawReturnValue), schema, - 'weather', - 'get' + contractName, + EntrypointName.fromString('get') ); console.log('\nThe weather is now:'); console.dir(returnValue, { depth: null, colors: true }); diff --git a/examples/composed-examples/listAccountCreation.ts b/examples/composed-examples/listAccountCreation.ts index 801fa1c5e..af6e65ade 100644 --- a/examples/composed-examples/listAccountCreation.ts +++ b/examples/composed-examples/listAccountCreation.ts @@ -1,10 +1,7 @@ -import { - createConcordiumClient, - streamToList, - unwrap, -} from '@concordium/node-sdk'; +import { streamToList, unwrap } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -42,7 +39,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/composed-examples/listInitialAccounts.ts b/examples/composed-examples/listInitialAccounts.ts index 9e0a2d2b5..73fc8df95 100644 --- a/examples/composed-examples/listInitialAccounts.ts +++ b/examples/composed-examples/listInitialAccounts.ts @@ -1,6 +1,7 @@ -import { createConcordiumClient, unwrap } from '@concordium/node-sdk'; +import { unwrap } from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import { credentials } from '@grpc/grpc-js'; -import { parseEndpoint } from '../shared/util'; +import { parseEndpoint } from '../shared/util.js'; import meow from 'meow'; @@ -38,7 +39,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() diff --git a/examples/composed-examples/listNumberAccountTransactions.ts b/examples/composed-examples/listNumberAccountTransactions.ts index 6837d2c25..c9b21bc99 100644 --- a/examples/composed-examples/listNumberAccountTransactions.ts +++ b/examples/composed-examples/listNumberAccountTransactions.ts @@ -1,15 +1,14 @@ import { AccountAddress, - createConcordiumClient, - isAlias, isTransferLikeSummary, unwrap, -} from '@concordium/node-sdk'; -import { credentials } from '@grpc/grpc-js'; -import { parseEndpoint } from '../shared/util'; - +} from '@concordium/web-sdk'; +import { ConcordiumGRPCNodeClient } from '@concordium/web-sdk/nodejs'; import meow from 'meow'; +import { credentials } from '@grpc/grpc-js'; +import { parseEndpoint } from '../shared/util.js'; + const cli = meow( ` Usage @@ -44,7 +43,7 @@ const cli = meow( ); const [address, port] = parseEndpoint(cli.flags.endpoint); -const client = createConcordiumClient( +const client = new ConcordiumGRPCNodeClient( address, Number(port), credentials.createInsecure() @@ -90,20 +89,20 @@ const client = createConcordiumClient( // For each transaction in the block: trxLoop: for await (const trx of trxStream) { if (isTransferLikeSummary(trx)) { - const trxAcc = new AccountAddress(trx.sender); + const trxAcc = trx.sender; // Loop over account dictionary entries to check if account // is already in dictionary: for (const [addr, trxSent] of Object.entries(dict)) { - const acc = new AccountAddress(addr); - if (isAlias(acc, trxAcc)) { + const acc = AccountAddress.fromBase58(addr); + if (AccountAddress.isAlias(acc, trxAcc)) { dict[addr] = trxSent + 1; break trxLoop; } } // If account is not in dictionary, then add it: - dict[trx.sender] = 1; + dict[AccountAddress.toBase58(trx.sender)] = 1; } } } diff --git a/examples/package.json b/examples/package.json index 68896a721..b4b66b555 100644 --- a/examples/package.json +++ b/examples/package.json @@ -1,29 +1,31 @@ { "name": "@concordium/examples", + "private": true, "type": "module", + "imports": { + "#ed25519": { + "node": "./shims/ed25519.node.ts", + "default": "@noble/ed25519" + } + }, "dependencies": { "@concordium/ccd-js-gen": "workspace:^", - "@concordium/common-sdk": "workspace:^", - "@concordium/node-sdk": "workspace:^", + "@concordium/web-sdk": "workspace:^", "@grpc/grpc-js": "^1.3.4", - "@noble/ed25519": "^1.7.1", - "@typescript-eslint/eslint-plugin": "^4.28.1", - "@typescript-eslint/parser": "^4.28.1", + "@noble/ed25519": "^2.0.0", "buffer": "^6.0.3", - "eslint": "^7.29.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-import": "^2.23.4", - "eslint-plugin-prettier": "^3.4.0", - "lint-staged": "^12.0.2", "meow": "11.0", - "prettier": "^2.3.2", + "node-fetch": "^3.3.2" + }, + "devDependencies": { + "eslint": "^8.50.0", "ts-node": "10.9", - "typescript": "^4.3.5" + "typescript": "^5.2.2" }, "scripts": { "lint": "eslint . --cache --ext .ts,.tsx --max-warnings 0", "lint-fix": "yarn lint --fix; exit 0", "typecheck": "tsc --noEmit", - "run-example": "node --experimental-specifier-resolution=node --loader ts-node/esm" + "run-example": "ts-node" } } diff --git a/examples/readme.md b/examples/readme.md index 0d5606cd0..c06c8bb33 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -2,8 +2,12 @@ This is a collection of scripts/examples that utilizes the SDK. There are three directories with examples: -- `client` containing examples that utilize the client to interact with a Concordium node. + +- `ccd-js-gen` containing examples with generate and using smart contract clients. +- `client` containing examples that utilize the client to interact with +a Concordium node. - `cis2` containing examples that helps interact with CIS-2 compliant smart contracts. +- `cis4` containing examples that helps interact with CIS-4 compliant smart contracts. - `common` that use various general functions from the library. To run an example call: diff --git a/examples/shared/util.ts b/examples/shared/util.ts index 658665c15..35e45c5dc 100644 --- a/examples/shared/util.ts +++ b/examples/shared/util.ts @@ -1,12 +1,19 @@ -import { Base58String, ContractAddress } from '@concordium/node-sdk'; +import { ContractAddress, AccountAddress } from '@concordium/web-sdk'; -export const parseAddress = (input: string): Base58String | ContractAddress => { +export const parseAddress = ( + input: string +): AccountAddress.Type | ContractAddress.Type => { if (!input.includes(',')) { - return input; + return AccountAddress.fromBase58(input); } const [i, si] = input.split(','); - return { index: BigInt(i), subindex: BigInt(si) }; + const index = parseInt(i); + const subindex = parseInt(si); + if (isNaN(index) || isNaN(subindex)) { + throw new Error('Invalid address'); + } + return ContractAddress.create(index, subindex); }; // Regular expression for matching the scheme prefix of a URL. diff --git a/examples/shims/ed25519.node.ts b/examples/shims/ed25519.node.ts new file mode 100644 index 000000000..2ebf319b4 --- /dev/null +++ b/examples/shims/ed25519.node.ts @@ -0,0 +1,9 @@ +// To add support for node versions <19. +// From https://www.npmjs.com/package/@noble/ed25519#usage +import { webcrypto } from 'node:crypto'; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +if (!globalThis.crypto) globalThis.crypto = webcrypto; + +export * from '@noble/ed25519'; diff --git a/examples/tsconfig.json b/examples/tsconfig.json index e702c49b7..5e3e50075 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "ts-node/node12/tsconfig.json", + "extends": "ts-node/node16/tsconfig.json", "ts-node": { "files": true, "esm": true @@ -10,7 +10,8 @@ "esnext" ], "baseUrl": ".", - "module": "es2020", + "module": "NodeNext", + "moduleResolution": "NodeNext", "target": "es2020" } } diff --git a/package.json b/package.json index 33d900797..11ab2f2a9 100644 --- a/package.json +++ b/package.json @@ -2,12 +2,9 @@ "private": true, "workspaces": { "packages": [ - "./packages/rust-bindings", - "./packages/common", - "./packages/nodejs", - "./packages/web", - "./packages/ccd-js-gen", - "./examples" + "./packages/*", + "./examples", + "./docs" ] }, "husky": { @@ -32,45 +29,31 @@ "singleQuote": true, "tabWidth": 4 }, + "type": "module", "devDependencies": { - "@babel/core": "^7.17.10", - "@babel/plugin-transform-modules-commonjs": "^7.12.1", - "@babel/plugin-transform-runtime": "^7.12.1", - "@babel/preset-env": "^7.12.1", - "@knodes/typedoc-plugin-code-blocks": "^0.23.0", - "@knodes/typedoc-plugin-pages": "^0.23.0", - "@typescript-eslint/eslint-plugin": "^4.28.1", - "@typescript-eslint/parser": "^4.28.1", - "babel-jest": "^27.0.6", - "babel-loader": "^8.1.0", - "eslint": "^7.29.0", + "@typescript-eslint/eslint-plugin": "^6.7.0", + "@typescript-eslint/parser": "^6.7.0", + "eslint": "^8.51.0", "eslint-config-prettier": "^8.3.0", + "eslint-import-resolver-exports": "^1.0.0-beta.5", "eslint-import-resolver-typescript": "^2.7.1", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^3.4.0", "husky": "^4.2.5", - "jest": "^27.0.6", "lint-staged": "^12.0.2", "markdown-link-check": "^3.1.4", "markdownlint-cli": "^0.34.0", - "prettier": "^2.3.2", - "stream-browserify": "^3.0.0", - "ts-jest": "^27.0.3", - "typedoc": "^0.23", - "typedoc-plugin-merge-modules": "^4.0.1", - "typedoc-plugin-missing-exports": "^0.23", - "typescript": "^4.3.5", - "webpack": "^5.72.0", - "webpack-cli": "^4.9.2" + "prettier": "^2.3.2" }, "scripts": { "test": "FORCE_COLOR=true yarn workspaces foreach --no-private run test --passWithNoTests", "lint": "FORCE_COLOR=true yarn workspaces foreach --no-private run lint", "lint-fix": "eslint --cache . --fix", - "markdown:lint": "yarn markdownlint pages/**/*.md packages/*/README.md README.md", - "markdown:lint-fix": "yarn markdownlint --fix pages/**/*.md packages/*/README.md README.md", - "markdown:linkcheck": "yarn markdown-link-check --config .markdown-linkcheck.json pages/**/*.md packages/*/README.md README.md", + "markdown:lint": "yarn markdownlint docs/README.md docs/pages/**/*.md packages/*/README.md examples/readme.md README.md", + "markdown:lint-fix": "yarn markdownlint --fix docs/README.md docs/pages/**/*.md packages/*/README.md examples/readme.md README.md", + "markdown:linkcheck": "yarn markdown-link-check --config .markdown-linkcheck.json docs/README.md docs/pages/**/*.md packages/*/README.md examples/readme.md README.md", "build": "FORCE_COLOR=true yarn workspaces foreach -v -t --no-private run build", - "build:dev": "FORCE_COLOR=true yarn workspaces foreach -v -t --no-private run build-dev" + "build:dev": "FORCE_COLOR=true yarn workspaces foreach -v -t --no-private run build-dev", + "clean": "FORCE_COLOR=true yarn workspaces foreach -p --no-private run clean" } } diff --git a/packages/ccd-js-gen/README.md b/packages/ccd-js-gen/README.md new file mode 100644 index 000000000..27021edba --- /dev/null +++ b/packages/ccd-js-gen/README.md @@ -0,0 +1,561 @@ + +# Smart Contract Client Generator + +Generate TypeScript/JavaScript code for interating with smart contracts and modules on the Concordium blockchain. + +- Functions for instantiating new smart contract instances from a module. +- Functions for dry-running and calling entrypoints of the smart contract. +- Functions for constructing parameters. +- Parsing logged events, return values and error messages. +- Structured types for parameter, logged events, return values and error messages. + +The code is generated from deployable smart contract modules, meaning it can be done with any smart contract available +locally and any smart contract deployed on chain. + +## Example usage of a generated client + +An example of using a generated client for a token smart contract implementing the +[CIS-2 standard](https://proposals.concordium.software/CIS/cis-2.html). +In the example, a contract client is constructed and a transaction calling the +[`transfer` entrypoint](https://proposals.concordium.software/CIS/cis-2.html#transfer) +of the smart contract. The parameter includes a transfer of 10 tokens from `sender` address to `receiver` address. + +```typescript +import * as SDK from "@concordium/web-sdk"; +import * as MyContract from "./generated/my-contract.js"; // Code generated from a smart contract module. + +const grpcClient = // Setup gRPC client code here ... +const contractAddress = // Address of the smart contract instance. +const signer = // Keys for signing an account transaction. +const sender = // The AccountAddress sending the tokens and signing the transaction. +const receiver = // The AccountAddress receiving the tokens. + +// Create a client for 'my-contract' smart contract. +const contractClient = await MyContract.create( + grpcClient, + contractAddress +); + +// Construct the parameter for the 'transfer' entrypoint. The structure of the parameter depends on the contract. +const parameter: MyContract.TransferParameter = [{ + tokenId: "", + amount: 10, + from: { type: 'Account', content: sender }, + to: { type: 'Account', content: receiver }, + data: "", +}]; + +// Send transaction for invoking the 'transfer' entrypoint of the smart contract. +const transactionHash = await MyContract.sendTransfer(contractClient, { + senderAddress: sender, + energy: SDK.Energy.create(12000) // The amount of energy needed will depend on the contract. +}, parameter, signer); +``` + + +- [Example usage of a generated client](#example-usage-of-a-generated-client) +- [Install the package](#install-the-package) +- [Using the CLI](#using-the-cli) + - [Example](#example) +- [Using the library](#using-the-library) + - [Generate from smart contract module file](#generate-from-smart-contract-module-file) + - [Generate from smart contract module on chain](#generate-from-smart-contract-module-on-chain) +- [Using the generated client](#using-the-generated-client) + - [Generated module client](#generated-module-client) + - [The client type](#the-client-type) + - [function `create`](#function-create) + - [function `createUnchecked`](#function-createunchecked) + - [const `moduleReference`](#const-modulereference) + - [function `getModuleSource`](#function-getmodulesource) + - [type `Parameter`](#type-contractnameparameter) + - [function `instantiate`](#function-instantiatecontractname) + - [Generated contract client](#generated-contract-client) + - [The contract client type](#the-contract-client-type) + - [function `create`](#function-create-1) + - [function `createUnchecked`](#function-createunchecked-1) + - [const `moduleReference`](#const-modulereference-1) + - [type `Event`](#type-event) + - [function `parseEvent`](#function-parseevent) + - [type `Parameter`](#type-entrypointnameparameter) + - [function `send`](#function-sendentrypointname) + - [function `dryRun`](#function-dryrunentrypointname) + - [type `ReturnValue`](#type-returnvalueentrypointname) + - [function `parseReturnValue`](#function-parsereturnvalueentrypointname) + - [type `ErrorMessage`](#type-errormessageentrypointname) + - [function `parseErrorMessage`](#function-parseerrormessageentrypointname) + + +## Install the package + +Install the package, saving it to `devDependencies`: + +**npm** + +```bash +npm install --save-dev @concordium/ccd-js-gen +``` + +**yarn** + +```bash +yarn add --dev @concordium/ccd-js-gen +``` + +**pnpm** + +```bash +pnpm install --save-dev @concordium/ccd-js-gen +``` + +> The package can also be used directly using `npx`, without first adding it as a dependency, +however it is recommended to add it in `package.json` to keep track the exact version used when +generating the code, as different version might produce different code. + +## Using the CLI + +This package provides the `ccd-js-gen` command, which can be used from the commandline. + +**Options**: + +- `-m, --module ` Path to the smart contract module. +- `-o, --out-dir ` Directory to use for the generated code. + +### Example + +To generate smart contract clients into a directory `generated` from the smart contract +module `./my-contract.wasm.v1`: + +```bash +ccd-js-gen --module ./my-contract.wasm.v1 --out-dir ./generated +``` + +> For a dapp project, it is recommended to have this as part of a script in `package.json`. + +## Using the library + +This package can be used programmatically as well. + +```typescript +import * as ccdJsGen from "@concordium/ccd-js-gen" +``` + +### Generate from smart contract module file + +To generate smart contract clients for a smart contract module file, +either downloaded from the blockchain or build from the smart contract source code: + +```typescript +import * as ccdJsGen from "@concordium/ccd-js-gen" + +const moduleFilePath = "./my-contract.wasm.v1"; // Path to smart contract module. +const outDirPath = "./generated"; // The directory to use for the generated files. + +// Read the module and generate the smart contract clients. +console.log('Generating smart contract module clients.') +await ccdJsGen.generateContractClientsFromFile(moduleFilePath, outDirPath); +console.log('Code generation was successful.') +``` + +### Generate from smart contract module on chain + +To generate smart contract clients for a smart contract module on chain, +you need access to the gRPC of a Concordium node. +Use `@concordium/web-sdk` to download the smart contract module and use it to generate the clients. + +```typescript +import * as ccdJsGen from "@concordium/ccd-js-gen" +import * as SDK from "@concordium/web-sdk" + +const outDirPath = "./generated"; // The directory to use for the generated files. +const outputModuleName = "wCCD-module"; // The name to give the output smart contract module. + +const grpcClient = ...; // A Concordium gRPC client from @concordium/web-sdk. +const moduleRef = SDK.ModuleReference.fromHexString(''); + +// Fetch the smart contract module source from chain. +const moduleSource = await grpcClient.getModuleSource(moduleRef); + +// Generate the smart contract clients from module source. +console.info('Generating smart contract module clients.'); +await ccdJsGen.generateContractClients(moduleSource, outputModuleName, outDirPath); +console.info('Code generation was successful.'); +``` + +## Using the generated client + +The generator produces a file with functions for interacting with the smart contract module and files +for each smart contract in the smart contract module used. + +For example: generating clients for a smart contract module `my-module` containing +the smart contracts `my-contract-a` and `my-contract-b`. +into the directory `./generated` produces the following structure: + +```bash +generated/ +├─ my-module.js // Functions for interacting with the 'my-module' smart contract module on chain. +├─ my-module_my-contract-a.js // Functions for interacting with the 'my-contract-a' smart contract. +└─ my-module_my-contract-b.js // Functions for interacting with the 'my-contract-b' smart contract. +``` + +> There might also be type declarations (`.d.ts`) for TypeScript, depending on the provided options. + +### Generated module client + +A file is produced with a client for interacting with the smart contract module. +This provides functions for instantiating smart contract instances. + +An example of importing a smart contract module generated from a `my-module.wasm.v1` file: + +```typescript +import * as MyModule from "./generated/my-module.js"; +``` + +#### The client type + +The type representing the client for the smart contract module is accessable using `MyModule.Type`. + +#### function `create` + +Construct a module client for interacting with a smart contract module on chain. +This function ensures the smart contract module is deployed on chain and throws an error otherwise. + +**Parameter:** The function takes a gRPC client from '@concordium/web-sdk'. + +```typescript +const grpcClient = ...; // Concordium gRPC client from '@concordium/web-sdk'. +const myModule: MyModule.Type = await MyModule.create(grpcClient); +``` + +#### function `createUnchecked` + +Construct a module client for interacting with a smart contract module on chain. +This function _skips_ the check of the smart contract module being deployed on chain and leaves it +up to the caller to ensure this. + +**Parameter:** The function takes a gRPC client from '@concordium/web-sdk'. + +```typescript +const grpcClient = ...; // Concordium gRPC client from '@concordium/web-sdk'. +const myModule: MyModule.Type = MyModule.createUnchecked(grpcClient); +``` + +> To run the checks manually use `await MyModule.checkOnChain(myModule);`. + +#### const `moduleReference` + +Variable with the reference of the smart contract module. + +```typescript +const ref = MyModule.moduleReference; +``` + +#### function `getModuleSource` + +Get the module source of the deployed smart contract module from chain. + +```typescript +const myModule: MyModule.Type = ...; // Generated module client +const moduleSource = await MyModule.getModuleSource(myModule); +``` + +#### type `Parameter` + +Type representing the parameter for when instantiating a smart contract. +The type is named `Parameter` where `` is the smart contract name in Pascal case. + +_This is only generated when the schema contains init-function parameter type._ + +#### function `instantiate` + +For each smart contract in module a function for instantiating new instance is produced. +These are named `instantiate` where `` is the smart contract name in Pascal case. + +An example for a smart contract module containing a smart contract named `my-contract`, +the function becomes `instantiateMyContract`. + +**Parameters** + +The function parameters are: + +- `moduleClient` The client of the on-chain smart contract module. +- `transactionMetadata` Metadata related to constructing the transaction, such as energy and CCD amount to include. + - `senderAddress` The address invoking this call. + - `energy` The energy reserved for executing this transaction. + - `amount` The amount of CCD included in the transaction. Defaults to 0. + - `expiry` Expiry date of the transaction. Defaults to 5 minutes from when constructing the transaction. +- `parameter` Parameter to provide the smart contract module for the instantiation. + + _With schema type:_ + If the schema contains type information for the parameter, + a type for the parameter is generated and used for this function. + The type is named `Parameter` where `` is the smart contract name in Pascal case. + + _Without schema type:_ + If no schema information is present, the function uses the generic `Parameter` from `@concordium/web-sdk`. + +- `signer` The keys to use for signing the transaction. + +**Returns**: Promise with the hash of the transaction. + +```typescript +// Generated module client. +const myModule: MyModule.Type = await MyModule.create(grpcClient); +// The keys to use for signing the transaction. +const signer = ...; +// Transaction meta information. +const transactionMeta = { + // The account address signing the transaction. + senderAddress: SDK.AccountAddress.fromBase58("357EYHqrmMiJBmUZTVG5FuaMq4soAhgtgz6XNEAJaXHW3NHaUf"), + // The amount of energy needed will depend on the contract. + energy: SDK.Energy.create(12000), +}; +// Parameter to pass the smart contract init-function. The structure depends on the contract. +const parameter = ...; +const transactionHash = await MyModule.instantiateMyContract(myModule, transactionMeta, parameter, signer); +``` + +### Generated contract client + +For each of the smart contracts in the module a file is produced named after the smart contract. +Each file contains functions for interacting with an instance of this smart contract. + +An example of importing a smart contract contract client generated from a module containing a +smart contract named `my-contract`: + +```typescript +import * as MyContract from "./generated/my-module_my-contract.js"; +``` + +#### The contract client type + +The type representing the client for the smart contract instance is accessable using `MyContract.Type`. + +#### function `create` + +Construct a client for interacting with a smart contract instance on chain. +This function ensures the smart contract instance exists on chain, and that it is using a +smart contract module with a matching reference. + +**Parameters:** + +- `grpcClient` The function takes a gRPC client from `@concordium/web-sdk`. +- `contractAddress` The contract address of the smart contract instance. + +```typescript +const grpcClient = ...; // Concordium gRPC client from '@concordium/web-sdk'. +const contractAddress = SDK.ContractAddress.create(...); // The address of the contract instance. +const myContract: MyContract.Type = await MyContract.create(grpcClient, contractAddress); +``` + +#### function `createUnchecked` + +Construct a client for interacting with a smart contract instance on chain. +This function _skips_ the check ensuring the smart contract instance exists on chain, +and that it is using a smart contract module with a matching reference, leaving it up to the caller to ensure this. + +**Parameters:** + +- `grpcClient` The function takes a gRPC client from `@concordium/web-sdk`. +- `contractAddress` The contract address of the smart contract instance. + +```typescript +const grpcClient = ...; // Concordium gRPC client from '@concordium/web-sdk'. +const contractAddress = SDK.ContractAddress.create(...); // The address of the contract instance. +const myContract: MyContract.Type = MyContract.createUnchecked(grpcClient, contractAddress); +``` + +> To run the checks manually use `await MyContract.checkOnChain(myContract);`. + +#### const `moduleReference` + +Variable with the reference of the smart contract module used by this contract. + +```typescript +const ref = MyContract.moduleReference; +``` + +#### type `Event` + +Type representing the structured event logged by this smart contract. + +_This is only generated when the schema contains contract event type._ + +#### function `parseEvent` + +Parse a raw contract event logged by this contract into a structured representation. + +_This is only generated when the schema contains contract event type._ + +**Parameter:** `event` The contract event to parse. + +**Returns:** The structured event of the `Event` type (see type above). + +```typescript +const rawContractEvent = ...; // The unparsed contract event from some transaction. +const event: MyContract.Event = MyContract.parseEvent(rawContractEvent); +``` + +#### type `Parameter` + +Type representing the parameter of for an entrypoint. +The type is named `Parameter` where `` is the name of the entrypoint in Pascal case. + +_This is only generated when the schema contains contract entrypoint parameter type._ + +#### function `send` + +For each entrypoint of the smart contract a function for sending a transaction calling this entrypoint is produced. +These are named `send` where `` is the name of the entrypoint in Pascal case. + +An example for a smart contract with an entrypoint named `launch-rocket`, the function becomes `sendLaunchRocket`. + +**Parameters** + +The function parameters are: + +- `contractClient` The client of the smart contract instance. +- `transactionMetadata` Metadata related to constructing the transaction, such as energy and CCD amount to include. + - `senderAddress` The address invoking this call. + - `energy` The energy reserved for executing this transaction. + - `amount` The amount of CCD included in the transaction. Defaults to 0. + - `expiry` Expiry date of the transaction. Defaults to 5 minutes from when constructing the transaction. +- `parameter` Parameter to provide to the smart contract entrypoint. + + _With schema type:_ + If the schema contains type information for the parameter, + a type for the parameter is generated and used for this function (see type above). + + _Without schema type:_ + If no schema information is present, the function uses the generic `Parameter` from `@concordium/web-sdk`. + +- `signer` The keys of to use for signing the transaction. + +**Returns**: Promise with the hash of the transaction. + +```typescript +// Generated contract client. +const myContract: MyContract.Type = await MyContract.create(grpcClient, contractAddress); +// The keys to use for signing the transaction. +const signer = ...; +// Transaction meta information. +const transactionMeta = { + // The account address signing the transaction. + senderAddress: SDK.AccountAddress.fromBase58("357EYHqrmMiJBmUZTVG5FuaMq4soAhgtgz6XNEAJaXHW3NHaUf"), + // The amount of energy needed will depend on the contract. + energy: SDK.Energy.create(12000), +}; +// Parameter to pass the smart contract entrypoint. The structure depends on the contract. +const parameter = ...; +// Send the transaction calling the `launch-rockets` entrypoint. +const transactionHash = await MyContract.sendLaunchRocket(myContract, transactionMeta, parameter, signer); +``` + +#### function `dryRun` + +For each entrypoint of the smart contract a function for dry-running a transaction calling this entrypoint is produced. +These are named `dryRun` where `` is the name of the entrypoint in Pascal case. + +An example for a smart contract with an entrypoint named `launch-rocket`, the function becomes `dryRunLaunchRocket`. + +**Parameters** + +The function parameters are: + +- `contractClient` The client of the smart contract instance. +- `parameter` Parameter to provide to the smart contract entrypoint. + + _With schema type:_ + If the schema contains type information for the parameter, + a type for the parameter is generated and used for this function (see type above). + + _Without schema type:_ + If no schema information is present, the function uses the generic `Parameter` from `@concordium/web-sdk`. + +- `invokeMetadata` Optional transaction metadata object with the following optional properties: + - `invoker` The address invoking this call, can be either an `AccountAddress` or `ContractAddress`. + Defaults to an `AccountAddress` (Base58check encoding of 32 bytes with value zero). + - `amount` The amount of CCD included in the transaction. Defaults to 0. + - `energy` The energy reserved for executing this transaction. Defaults to max energy possible. +- `blockHash` (optional) Provide to specify the block hash, for which the state will be used for dry-running. + When not provided, the last finalized block is used. + +**Returns**: Promise with the invoke result. + +```typescript +const myContract: MyContract.Type = ...; // Generated contract client. +const parameter = ...; // Parameter to pass the smart contract entrypoint. +// Transaction metadata for invoking. +const metadata = { + // Amount of CCD to include in the transaction. + amount: SDK.CcdAmount.fromCcd(10), + // Invoker of the transaction + invoker: SDK.AccountAddress.fromBase58("357EYHqrmMiJBmUZTVG5FuaMq4soAhgtgz6XNEAJaXHW3NHaUf") +}; +const invokeResult = await MyContract.dryRunLaunchRocket(myContract, parameter, metadata); +``` + +#### type `ReturnValue` + +Type representing the return value from a successful dry-run/invocation of an entrypoint. +The type is named `ReturnValue` where `` is the name of +the relevant entrypoint in Pascal case. + +_This is only generated when the schema contains entrypoint return value type._ + +#### function `parseReturnValue` + +For each entrypoint of the smart contract a function for parsing the return value in a +successful invocation/dry-running. +These are named `parseReturnValue` where `` is the name +of the entrypoint in Pascal case. + +_This is only generated when the schema contains entrypoint return value type._ + +An example for a smart contract with an entrypoint named `launch-rocket`, the function +becomes `parseReturnValueLaunchRocket`. + +**Parameter:** `invokeResult` The result from dry-running a transactions calling the entrypoint. + +**Returns:** Undefined if the invocation was not successful otherwise the parsed return +value of the type `ReturnValue`. + +```typescript +// Dry run the entrypoint +const invokeResult = await MyContract.dryRunLaunchRocket(myContract, invoker, parameter); + +// Parse the return value +const returnValue: MyContract.ReturnValueLaunchRocket | undefined = parseReturnValueLaunchRocket(invokeResult); +``` + +#### type `ErrorMessage` + +Type representing the error message from a rejected dry-run/invocation of an entrypoint. +The type is named `ErrorMessage` where `` is the name of +the relevant entrypoint in Pascal case. + +_This is only generated when the schema contains entrypoint error message type._ + +#### function `parseErrorMessage` + +For each entrypoint of the smart contract a function for parsing the error message in a +rejected invocation/dry-running. +These are named `parseErrorMessage` where `` is the name +of the entrypoint in Pascal case. + +_This is only generated when the schema contains entrypoint error message type._ + +An example for a smart contract with an entrypoint named `launch-rocket`, the function +becomes `parseErrorMessageLaunchRocket`. + +**Parameter:** `invokeResult` The result from dry-running a transactions calling some entrypoint. + +**Returns:** Undefined if the invocation was not rejected, otherwise the parsed error +message of the type `ReturnValue`. + +```typescript +// Dry run the entrypoint +const invokeResult = await MyContract.dryRunLaunchRocket(myContract, invoker, parameter); + +// Parse the error message +const message: MyContract.ErrorMessageLaunchRocket | undefined = MyContract.parseErrorMessageLaunchRocket(invokeResult); +``` diff --git a/packages/ccd-js-gen/bin/ccd-js-gen.js b/packages/ccd-js-gen/bin/ccd-js-gen.js index 76a857b7c..295dba800 100755 --- a/packages/ccd-js-gen/bin/ccd-js-gen.js +++ b/packages/ccd-js-gen/bin/ccd-js-gen.js @@ -1,4 +1,3 @@ -#!/usr/bin/env node -// eslint-disable-next-line @typescript-eslint/no-var-requires -const cli = require('../lib/src/cli.js'); -cli.main(); +#!/usr/bin/env -S node --no-warnings +import { main } from '../lib/src/cli.js'; +main(); diff --git a/packages/ccd-js-gen/jest.config.ts b/packages/ccd-js-gen/jest.config.ts new file mode 100644 index 000000000..f9cd4484d --- /dev/null +++ b/packages/ccd-js-gen/jest.config.ts @@ -0,0 +1,14 @@ +import type { Config } from 'jest'; +import type {} from 'ts-jest'; + +const config: Config = { + preset: 'ts-jest/presets/js-with-ts-esm', + moduleNameMapper: { + '^(\\.\\.?\\/.+)\\.js$': '$1', // Remap esmodule file extensions + }, + transformIgnorePatterns: [ + 'node_modules/(?!@noble/ed25519)', // @noble/ed25519 is an ES module only + ], +}; + +export default config; diff --git a/packages/ccd-js-gen/package.json b/packages/ccd-js-gen/package.json index 79336522c..f277741c8 100644 --- a/packages/ccd-js-gen/package.json +++ b/packages/ccd-js-gen/package.json @@ -2,16 +2,19 @@ "name": "@concordium/ccd-js-gen", "version": "1.0.0", "description": "Generate JS clients for the Concordium Blockchain", + "type": "module", "bin": "bin/ccd-js-gen.js", "main": "lib/src/lib.js", + "module": "lib/src/lib.js", "typings": "lib/src/lib.d.ts", "scripts": { - "build": "tsc", + "build": "tsc -p tsconfig.build.json", "build-dev": "yarn build", "clean": "rm -r lib", "lint": "eslint src/** bin/** --cache --ext .ts,.js --max-warnings 0", "lint-fix": "yarn --silent lint --fix; exit 0", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "jest", + "test-ci": "yarn test" }, "keywords": [ "concordium", @@ -28,22 +31,23 @@ "url": "https://concordium.com" }, "license": "Apache-2.0", + "peerDependencies": { + "@concordium/web-sdk": "7.0.0" + }, "dependencies": { - "@concordium/common-sdk": "9.4.0", + "@concordium/web-sdk": "7.0.0", "buffer": "^6.0.3", "commander": "^11.0.0", + "sanitize-filename": "^1.6.3", "ts-morph": "^19.0.0" }, "devDependencies": { + "@types/jest": "^26.0.23", "@types/node": "^20.5.0", - "@typescript-eslint/eslint-plugin": "^4.28.1", - "@typescript-eslint/parser": "^4.28.1", - "eslint": "^7.29.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-import": "^2.23.4", - "eslint-plugin-prettier": "^3.4.0", - "prettier": "^2.3.2", - "typescript": "^4.3.5" + "eslint": "^8.50.0", + "jest": "^29.6.2", + "ts-jest": "^29.1.1", + "typescript": "^5.2.2" }, "prettier": { "singleQuote": true, diff --git a/packages/ccd-js-gen/src/cli.ts b/packages/ccd-js-gen/src/cli.ts index fe66d0c6a..a86a82a98 100644 --- a/packages/ccd-js-gen/src/cli.ts +++ b/packages/ccd-js-gen/src/cli.ts @@ -2,8 +2,8 @@ This file contains code for building the command line inferface to the ccd-js-gen library. */ import { Command } from 'commander'; -import packageJson from '../package.json'; -import * as lib from './lib'; +import packageJson from '../package.json' assert { type: 'json' }; +import * as lib from './lib.js'; /** Type representing the CLI options/arguments and needs to match the options set with commander.js */ type Options = { @@ -13,11 +13,11 @@ type Options = { outDir: string; }; -// Main function, which is called be the executable script in `bin`. +// Main function, which is called in the executable script in `bin`. export async function main(): Promise { const program = new Command(); program - .name(packageJson.name) + .name('ccd-js-gen') .description(packageJson.description) .version(packageJson.version) .requiredOption( @@ -30,5 +30,18 @@ export async function main(): Promise { ) .parse(process.argv); const options = program.opts(); - await lib.generateContractClientsFromFile(options.module, options.outDir); + console.log('Generating smart contract clients...'); + + const startTime = Date.now(); + await lib.generateContractClientsFromFile(options.module, options.outDir, { + onProgress(update) { + if (update.type === 'Progress') { + console.log( + `[${update.doneItems}/${update.totalItems}] ${update.spentTime}ms` + ); + } + }, + }); + + console.log(`Done in ${Date.now() - startTime}ms`); } diff --git a/packages/ccd-js-gen/src/lib.ts b/packages/ccd-js-gen/src/lib.ts index dcab02646..1798c9a83 100644 --- a/packages/ccd-js-gen/src/lib.ts +++ b/packages/ccd-js-gen/src/lib.ts @@ -1,7 +1,8 @@ import * as fs from 'node:fs/promises'; import * as path from 'node:path'; import * as tsm from 'ts-morph'; -import * as SDK from '@concordium/common-sdk'; +import * as SDK from '@concordium/web-sdk'; +import sanitize from 'sanitize-filename'; /** * Output options for the generated code. @@ -16,17 +17,35 @@ export type OutputOptions = | 'TypedJavaScript' | 'Everything'; -/** Options for generating clients */ +/** Options for generating clients. */ export type GenerateContractClientsOptions = { - /** Options for the output */ + /** Options for the output. */ output?: OutputOptions; + /** Callback for getting notified on progress. */ + onProgress?: NotifyProgress; +}; + +/** Callback for getting notified on progress. */ +export type NotifyProgress = (progress: Progress) => void; + +/** + * Progress notification + */ +export type Progress = { + type: 'Progress'; + /** Total number of 'items' to be generated. */ + totalItems: number; + /** Number of 'items' generated at the time of this notification. */ + doneItems: number; + /** Number of milliseconds spent on the previous item. */ + spentTime: number; }; /** * Generate smart contract client code for a given smart contract module file. - * @param modulePath Path to the smart contract module. - * @param outDirPath Path to the directory to use for the output. - * @param options Options for generating the clients. + * @param {string} modulePath Path to the smart contract module. + * @param {string} outDirPath Path to the directory to use for the output. + * @param {GenerateContractClientsOptions} [options] Options for generating the clients. * @throws If unable to: read provided file at `modulePath`, parse the provided smart contract module or write to provided directory `outDirPath`. */ export async function generateContractClientsFromFile( @@ -52,10 +71,10 @@ export async function generateContractClientsFromFile( /** * Generate smart contract client code for a given smart contract module. - * @param moduleSource Buffer with bytes for the smart contract module. - * @param outName Name for the output file. - * @param outDirPath Path to the directory to use for the output. - * @param options Options for generating the clients. + * @param {SDK.VersionedModuleSource} moduleSource Buffer with bytes for the smart contract module. + * @param {string} outName Name for the output file. + * @param {string} outDirPath Path to the directory to use for the output. + * @param {GenerateContractClientsOptions} [options] Options for generating the clients. * @throws If unable to write to provided directory `outDirPath`. */ export async function generateContractClients( @@ -65,12 +84,6 @@ export async function generateContractClients( options: GenerateContractClientsOptions = {} ): Promise { const outputOption = options.output ?? 'Everything'; - const moduleInterface = await SDK.parseModuleInterface(moduleSource); - const outputFilePath = path.format({ - dir: outDirPath, - name: outName, - ext: '.ts', - }); const compilerOptions: tsm.CompilerOptions = { outDir: outDirPath, @@ -78,10 +91,15 @@ export async function generateContractClients( outputOption === 'Everything' || outputOption === 'TypedJavaScript', }; const project = new tsm.Project({ compilerOptions }); - const sourceFile = project.createSourceFile(outputFilePath, '', { - overwrite: true, - }); - addModuleClients(sourceFile, moduleInterface); + + await generateCode( + project, + outName, + outDirPath, + moduleSource, + options.onProgress + ); + if (outputOption === 'Everything' || outputOption === 'TypeScript') { await project.save(); } @@ -94,179 +112,997 @@ export async function generateContractClients( } } -/** Iterates a module interface adding code to the provided source file. */ -function addModuleClients( - sourceFile: tsm.SourceFile, - moduleInterface: SDK.ModuleInterface +/** + * Iterates a module interface building source files in the project. + * @param {tsm.Project} project The project to use for creating sourcefiles. + * @param {string} outModuleName The name for outputting the module client file. + * @param {string} outDirPath The directory to use for outputting files. + * @param {SDK.VersionedModuleSource} moduleSource The source of the smart contract module. + * @param {NotifyProgress} [notifyProgress] Callback to report progress. + */ +async function generateCode( + project: tsm.Project, + outModuleName: string, + outDirPath: string, + moduleSource: SDK.VersionedModuleSource, + notifyProgress?: NotifyProgress ) { - sourceFile.addImportDeclaration({ - namespaceImport: 'SDK', - moduleSpecifier: '@concordium/common-sdk', + const [moduleInterface, moduleRef, rawModuleSchema] = await Promise.all([ + SDK.parseModuleInterface(moduleSource), + SDK.calculateModuleReference(moduleSource), + SDK.getEmbeddedModuleSchema(moduleSource), + ]); + + let totalItems = 0; + for (const contracts of moduleInterface.values()) { + totalItems += contracts.entrypointNames.size; + } + let doneItems = 0; + notifyProgress?.({ type: 'Progress', totalItems, doneItems, spentTime: 0 }); + + const moduleSchema = + rawModuleSchema === null + ? null + : SDK.parseRawModuleSchema(rawModuleSchema); + + const outputFilePath = path.format({ + dir: outDirPath, + name: outModuleName, + ext: '.ts', + }); + const moduleSourceFile = project.createSourceFile(outputFilePath, '', { + overwrite: true, }); + const moduleClientId = 'moduleClient'; + const moduleClientType = `${toPascalCase(outModuleName)}Module`; + const internalModuleClientId = 'internalModuleClient'; + + generateModuleBaseCode( + moduleSourceFile, + moduleRef, + moduleClientId, + moduleClientType, + internalModuleClientId + ); for (const contract of moduleInterface.values()) { - const contractNameId = 'contractName'; - const genericContractId = 'genericContract'; - const grpcClientId = 'grpcClient'; - const contractAddressId = 'contractAddress'; - const dryRunId = 'dryRun'; - const contractClassId = toPascalCase(contract.contractName); - const contractDryRunClassId = `${contractClassId}DryRun`; - - const classDecl = sourceFile.addClass({ - docs: ['Smart contract client for a contract instance on chain.'], + const contractSchema: SDK.SchemaContractV3 | undefined = + moduleSchema?.module.contracts.get(contract.contractName); + + generactionModuleContractCode( + moduleSourceFile, + contract.contractName, + moduleClientId, + moduleClientType, + internalModuleClientId, + moduleRef, + contractSchema + ); + + const contractOutputFilePath = path.format({ + dir: outDirPath, + name: `${outModuleName}_${sanitize(contract.contractName, { + replacement: '-', + })}`, + ext: '.ts', + }); + const contractSourceFile = project.createSourceFile( + contractOutputFilePath, + '', + { + overwrite: true, + } + ); + + const contractClientType = `${toPascalCase( + contract.contractName + )}Contract`; + const contractClientId = 'contractClient'; + + generateContractBaseCode( + contractSourceFile, + contract.contractName, + contractClientId, + contractClientType, + moduleRef, + contractSchema + ); + + for (const entrypointName of contract.entrypointNames) { + const startTime = Date.now(); + const entrypointSchema = + contractSchema?.receive.get(entrypointName); + generateContractEntrypointCode( + contractSourceFile, + contract.contractName, + contractClientId, + contractClientType, + entrypointName, + entrypointSchema + ); + const spentTime = Date.now() - startTime; + doneItems++; + notifyProgress?.({ + type: 'Progress', + totalItems, + doneItems, + spentTime, + }); + } + } +} + +/** + * Generate code for a smart contract module client. + * @param moduleSourceFile The sourcefile of the module. + * @param moduleRef The module reference. + * @param moduleClientId The identifier to use for the module client. + * @param moduleClientType The identifier to use for the type of the module client. + * @param internalModuleClientId The identifier to use for referencing the internal module client. + */ +function generateModuleBaseCode( + moduleSourceFile: tsm.SourceFile, + moduleRef: SDK.ModuleReference.Type, + moduleClientId: string, + moduleClientType: string, + internalModuleClientId: string +) { + const moduleRefId = 'moduleReference'; + + moduleSourceFile.addImportDeclaration({ + namespaceImport: 'SDK', + moduleSpecifier: '@concordium/web-sdk', + }); + + moduleSourceFile.addVariableStatement({ + isExported: true, + declarationKind: tsm.VariableDeclarationKind.Const, + docs: [ + 'The reference of the smart contract module supported by the provided client.', + ], + declarations: [ + { + name: moduleRefId, + type: 'SDK.ModuleReference.Type', + initializer: `/*#__PURE__*/ SDK.ModuleReference.fromHexString('${moduleRef.moduleRef}')`, + }, + ], + }); + + const moduleClassDecl = moduleSourceFile.addClass({ + docs: [ + `Client for an on-chain smart contract module with module reference '${moduleRef.moduleRef}', can be used for instantiating new smart contract instances.`, + ], + name: moduleClientType, + properties: [ + { + docs: [ + 'Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing).', + ], + scope: tsm.Scope.Private, + name: '__nominal', + initializer: 'true', + }, + { + docs: ['Generic module client used internally.'], + scope: tsm.Scope.Public, + isReadonly: true, + name: internalModuleClientId, + type: 'SDK.ModuleClient.Type', + }, + ], + }); + + moduleClassDecl + .addConstructor({ + docs: [ + 'Constructor is only ment to be used internally in this module. Use functions such as `create` or `createUnchecked` for construction.', + ], + parameters: [ + { + name: internalModuleClientId, + type: 'SDK.ModuleClient.Type', + }, + ], + }) + .setBodyText( + `this.${internalModuleClientId} = ${internalModuleClientId};` + ); + + moduleSourceFile.addTypeAlias({ + docs: [ + `Client for an on-chain smart contract module with module reference '${moduleRef.moduleRef}', can be used for instantiating new smart contract instances.`, + ], + name: 'Type', + isExported: true, + type: moduleClientType, + }); + + const grpcClientId = 'grpcClient'; + moduleSourceFile + .addFunction({ + docs: [ + [ + `Construct a ${moduleClientType} client for interacting with a smart contract module on chain.`, + 'This function ensures the smart contract module is deployed on chain.', + `@param {SDK.ConcordiumGRPCClient} ${grpcClientId} - The concordium node client to use.`, + '@throws If failing to communicate with the concordium node or if the module reference is not present on chain.', + `@returns {${moduleClientType}} A module client ensured to be deployed on chain.`, + ].join('\n'), + ], + isExported: true, + isAsync: true, + name: 'create', + parameters: [ + { + name: grpcClientId, + type: 'SDK.ConcordiumGRPCClient', + }, + ], + returnType: `Promise<${moduleClientType}>`, + }) + .setBodyText( + [ + `const moduleClient = await SDK.ModuleClient.create(${grpcClientId}, ${moduleRefId});`, + `return new ${moduleClientType}(moduleClient);`, + ].join('\n') + ); + moduleSourceFile + .addFunction({ + docs: [ + [ + `Construct a ${moduleClientType} client for interacting with a smart contract module on chain.`, + 'It is up to the caller to ensure the module is deployed on chain.', + `@param {SDK.ConcordiumGRPCClient} ${grpcClientId} - The concordium node client to use.`, + `@returns {${moduleClientType}}`, + ].join('\n'), + ], isExported: true, - name: contractClassId, - properties: [ + name: 'createUnchecked', + parameters: [ { - docs: [ - 'Name of the smart contract supported by this client.', - ], - scope: tsm.Scope.Public, - isReadonly: true, - name: contractNameId, - type: 'string', - initializer: `'${contract.contractName}'`, + name: grpcClientId, + type: 'SDK.ConcordiumGRPCClient', }, + ], + returnType: `${moduleClientType}`, + }) + .setBodyText( + [ + `const moduleClient = SDK.ModuleClient.createUnchecked(${grpcClientId}, ${moduleRefId});`, + `return new ${moduleClientType}(moduleClient);`, + ].join('\n') + ); + + moduleSourceFile + .addFunction({ + docs: [ + [ + `Construct a ${moduleClientType} client for interacting with a smart contract module on chain.`, + 'This function ensures the smart contract module is deployed on chain.', + `@param {${moduleClientType}} ${moduleClientId} - The client of the on-chain smart contract module with referecence '${moduleRef.moduleRef}'.`, + '@throws If failing to communicate with the concordium node or if the module reference is not present on chain.', + `@returns {${moduleClientType}} A module client ensured to be deployed on chain.`, + ].join('\n'), + ], + isExported: true, + name: 'checkOnChain', + parameters: [ { - docs: ['Generic contract client used internally.'], - scope: tsm.Scope.Private, - name: genericContractId, - type: 'SDK.Contract', + name: moduleClientId, + type: moduleClientType, }, + ], + returnType: 'Promise', + }) + .setBodyText( + `return SDK.ModuleClient.checkOnChain(${moduleClientId}.${internalModuleClientId});` + ); + + moduleSourceFile + .addFunction({ + docs: [ + [ + 'Get the module source of the deployed smart contract module.', + `@param {${moduleClientType}} ${moduleClientId} - The client of the on-chain smart contract module with referecence '${moduleRef.moduleRef}'.`, + '@throws {SDK.RpcError} If failing to communicate with the concordium node or module not found.', + '@returns {SDK.VersionedModuleSource} Module source of the deployed smart contract module.', + ].join('\n'), + ], + isExported: true, + name: 'getModuleSource', + parameters: [ { - docs: ['Dry run entrypoints of the smart contract.'], - scope: tsm.Scope.Public, - isReadonly: true, - name: dryRunId, - type: contractDryRunClassId, + name: moduleClientId, + type: moduleClientType, }, ], + returnType: 'Promise', + }) + .setBodyText( + `return SDK.ModuleClient.getModuleSource(${moduleClientId}.${internalModuleClientId});` + ); +} + +/** + * Generate code in the module client specific to each contract in the module. + * @param {tsm.SourceFile} moduleSourceFile The sourcefile of the module client. + * @param {string} contractName The name of the contract. + * @param {string} moduleClientId The identifier for the module client. + * @param {string} moduleClientType The identifier for the type of the module client. + * @param {string} internalModuleClientId The identifier for the internal module client. + * @param {SDK.ModuleReference.Type} moduleRef The reference of the module. + * @param {SDK.SchemaContractV3} [contractSchema] The contract schema. + */ +function generactionModuleContractCode( + moduleSourceFile: tsm.SourceFile, + contractName: string, + moduleClientId: string, + moduleClientType: string, + internalModuleClientId: string, + moduleRef: SDK.ModuleReference.Type, + contractSchema?: SDK.SchemaContractV3 +) { + const transactionMetadataId = 'transactionMetadata'; + const parameterId = 'parameter'; + const signerId = 'signer'; + + const initParameter = createParameterCode( + parameterId, + contractSchema?.init?.parameter + ); + + const initParameterTypeId = `${toPascalCase(contractName)}Parameter`; + + const createInitParameterFnId = `create${toPascalCase( + contractName + )}Parameter`; + + if (initParameter !== undefined) { + moduleSourceFile.addTypeAlias({ + docs: [ + `Parameter type transaction for instantiating a new '${contractName}' smart contract instance`, + ], + isExported: true, + name: initParameterTypeId, + type: initParameter.type, }); - const dryRunClassDecl = sourceFile.addClass({ + moduleSourceFile + .addFunction({ + docs: [ + [ + `Construct Parameter type transaction for instantiating a new '${contractName}' smart contract instance.`, + `@param {${initParameterTypeId}} ${parameterId} The structured parameter to construct from.`, + '@returns {SDK.Parameter.Type} The smart contract parameter.', + ].join('\n'), + ], + isExported: true, + name: createInitParameterFnId, + parameters: [ + { + type: initParameterTypeId, + name: parameterId, + }, + ], + returnType: 'SDK.Parameter.Type', + }) + .setBodyText( + [...initParameter.code, `return ${initParameter.id}`].join('\n') + ); + } + + moduleSourceFile + .addFunction({ docs: [ - `Smart contract client for dry running messages to a contract instance of '${contract.contractName}' on chain.`, + [ + `Send transaction for instantiating a new '${contractName}' smart contract instance.`, + `@param {${moduleClientType}} ${moduleClientId} - The client of the on-chain smart contract module with referecence '${moduleRef.moduleRef}'.`, + `@param {SDK.ContractTransactionMetadata} ${transactionMetadataId} - Metadata related to constructing a transaction for a smart contract module.`, + ...(initParameter === undefined + ? [] + : [ + `@param {${initParameterTypeId}} ${parameterId} - Parameter to provide as part of the transaction for the instantiation of a new smart contract contract.`, + ]), + `@param {SDK.AccountSigner} ${signerId} - The signer of the update contract transaction.`, + '@throws If failing to communicate with the concordium node.', + '@returns {SDK.TransactionHash.Type}', + ].join('\n'), ], isExported: true, - name: contractDryRunClassId, + name: `instantiate${toPascalCase(contractName)}`, + parameters: [ + { + name: moduleClientId, + type: moduleClientType, + }, + { + name: transactionMetadataId, + type: 'SDK.ContractTransactionMetadata', + }, + ...(initParameter === undefined + ? [] + : [ + { + name: parameterId, + type: initParameterTypeId, + }, + ]), + { + name: signerId, + type: 'SDK.AccountSigner', + }, + ], + returnType: 'Promise', + }) + .setBodyText( + [ + 'return SDK.ModuleClient.createAndSendInitTransaction(', + ` ${moduleClientId}.${internalModuleClientId},`, + ` SDK.ContractName.fromStringUnchecked('${contractName}'),`, + ` ${transactionMetadataId},`, + ...(initParameter === undefined + ? [] + : [` ${createInitParameterFnId}(${parameterId}),`]), + ` ${signerId}`, + ');', + ].join('\n') + ); +} + +/** + * Generate code for a smart contract instance client. + * @param {tsm.SourceFile} contractSourceFile The sourcefile of the contract client. + * @param {string} contractName The name of the smart contract. + * @param {string} contractClientId The identifier to use for the contract client. + * @param {string} contractClientType The identifier to use for the type of the contract client. + * @param {SDK.ModuleReference.Type} moduleRef The module reference. + * @param {SDK.SchemaContractV3} [contractSchema] The contract schema to use in the client. + */ +function generateContractBaseCode( + contractSourceFile: tsm.SourceFile, + contractName: string, + contractClientId: string, + contractClientType: string, + moduleRef: SDK.ModuleReference.Type, + contractSchema?: SDK.SchemaContractV3 +) { + const moduleRefId = 'moduleReference'; + const grpcClientId = 'grpcClient'; + const contractNameId = 'contractName'; + const genericContractId = 'genericContract'; + const contractAddressId = 'contractAddress'; + const blockHashId = 'blockHash'; + + contractSourceFile.addImportDeclaration({ + namespaceImport: 'SDK', + moduleSpecifier: '@concordium/web-sdk', + }); + + contractSourceFile.addVariableStatement({ + docs: [ + 'The reference of the smart contract module supported by the provided client.', + ], + isExported: true, + declarationKind: tsm.VariableDeclarationKind.Const, + declarations: [ + { + name: moduleRefId, + type: 'SDK.ModuleReference.Type', + initializer: `/*#__PURE__*/ SDK.ModuleReference.fromHexString('${moduleRef.moduleRef}')`, + }, + ], + }); + + contractSourceFile.addVariableStatement({ + docs: ['Name of the smart contract supported by this client.'], + isExported: true, + declarationKind: tsm.VariableDeclarationKind.Const, + declarations: [ + { + name: contractNameId, + type: 'SDK.ContractName.Type', + initializer: `/*#__PURE__*/ SDK.ContractName.fromStringUnchecked('${contractName}')`, + }, + ], + }); + + const contractClassDecl = contractSourceFile.addClass({ + docs: ['Smart contract client for a contract instance on chain.'], + name: contractClientType, + properties: [ + { + docs: [ + 'Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing).', + ], + scope: tsm.Scope.Private, + name: '__nominal', + initializer: 'true', + }, + { + docs: ['The gRPC connection used by this client.'], + scope: tsm.Scope.Public, + isReadonly: true, + name: grpcClientId, + type: 'SDK.ConcordiumGRPCClient', + }, + { + docs: ['The contract address used by this client.'], + scope: tsm.Scope.Public, + isReadonly: true, + name: contractAddressId, + type: 'SDK.ContractAddress.Type', + }, + + { + docs: ['Generic contract client used internally.'], + scope: tsm.Scope.Public, + isReadonly: true, + name: genericContractId, + type: 'SDK.Contract', + }, + ], + }); + + contractClassDecl + .addConstructor({ + parameters: [ + { name: grpcClientId, type: 'SDK.ConcordiumGRPCClient' }, + { + name: contractAddressId, + type: 'SDK.ContractAddress.Type', + }, + { name: genericContractId, type: 'SDK.Contract' }, + ], + }) + .setBodyText( + [grpcClientId, contractAddressId, genericContractId] + .map((name) => `this.${name} = ${name};`) + .join('\n') + ); + + contractSourceFile.addTypeAlias({ + docs: ['Smart contract client for a contract instance on chain.'], + name: 'Type', + isExported: true, + type: contractClientType, + }); + + contractSourceFile + .addFunction({ + docs: [ + [ + `Construct an instance of \`${contractClientType}\` for interacting with a '${contractName}' contract on chain.`, + 'Checking the information instance on chain.', + `@param {SDK.ConcordiumGRPCClient} ${grpcClientId} - The client used for contract invocations and updates.`, + `@param {SDK.ContractAddress.Type} ${contractAddressId} - Address of the contract instance.`, + `@param {SDK.BlockHash.Type} [${blockHashId}] - Hash of the block to check the information at. When not provided the last finalized block is used.`, + '@throws If failing to communicate with the concordium node or if any of the checks fails.', + `@returns {${contractClientType}}`, + ].join('\n'), + ], + isExported: true, + isAsync: true, + name: 'create', + parameters: [ + { + name: grpcClientId, + type: 'SDK.ConcordiumGRPCClient', + }, + { + name: contractAddressId, + type: 'SDK.ContractAddress.Type', + }, + { + name: blockHashId, + hasQuestionToken: true, + type: 'SDK.BlockHash.Type', + }, + ], + returnType: `Promise<${contractClientType}>`, + }) + .setBodyText( + [ + `const ${genericContractId} = new SDK.Contract(${grpcClientId}, ${contractAddressId}, ${contractNameId});`, + `await ${genericContractId}.checkOnChain({ moduleReference: ${moduleRefId}, blockHash: ${blockHashId} });`, + `return new ${contractClientType}(`, + ` ${grpcClientId},`, + ` ${contractAddressId},`, + ` ${genericContractId}`, + ');', + ].join('\n') + ); + + contractSourceFile + .addFunction({ + docs: [ + [ + `Construct the \`${contractClientType}\` for interacting with a '${contractName}' contract on chain.`, + 'Without checking the instance information on chain.', + `@param {SDK.ConcordiumGRPCClient} ${grpcClientId} - The client used for contract invocations and updates.`, + `@param {SDK.ContractAddress.Type} ${contractAddressId} - Address of the contract instance.`, + `@returns {${contractClientType}}`, + ].join('\n'), + ], + isExported: true, + name: 'createUnchecked', + parameters: [ + { + name: grpcClientId, + type: 'SDK.ConcordiumGRPCClient', + }, + { + name: contractAddressId, + type: 'SDK.ContractAddress.Type', + }, + ], + returnType: contractClientType, + }) + .setBodyText( + [ + `const ${genericContractId} = new SDK.Contract(${grpcClientId}, ${contractAddressId}, ${contractNameId});`, + `return new ${contractClientType}(`, + ` ${grpcClientId},`, + ` ${contractAddressId},`, + ` ${genericContractId},`, + ');', + ].join('\n') + ); + + contractSourceFile + .addFunction({ + docs: [ + [ + 'Check if the smart contract instance exists on the blockchain and whether it uses a matching contract name and module reference.', + `@param {${contractClientType}} ${contractClientId} The client for a '${contractName}' smart contract instance on chain.`, + `@param {SDK.BlockHash.Type} [${blockHashId}] A optional block hash to use for checking information on chain, if not provided the last finalized will be used.`, + '@throws {SDK.RpcError} If failing to communicate with the concordium node or if any of the checks fails.', + ].join('\n'), + ], + isExported: true, + name: 'checkOnChain', + parameters: [ + { + name: contractClientId, + type: contractClientType, + }, + { + name: blockHashId, + hasQuestionToken: true, + type: 'SDK.BlockHash.Type', + }, + ], + returnType: 'Promise', + }) + .setBodyText( + `return ${contractClientId}.${genericContractId}.checkOnChain({moduleReference: ${moduleRefId}, blockHash: ${blockHashId} });` + ); + + const eventParameterId = 'event'; + const eventParameterTypeId = 'Event'; + const eventParser = parseEventCode(eventParameterId, contractSchema?.event); + if (eventParser !== undefined) { + contractSourceFile.addTypeAlias({ + docs: [`Contract event type for the '${contractName}' contract.`], + isExported: true, + name: eventParameterTypeId, + type: eventParser.type, }); - classDecl - .addConstructor({ - docs: ['Contruct a client for a contract instance on chain'], + contractSourceFile + .addFunction({ + docs: [ + [ + `Parse the contract events logged by the '${contractName}' contract.`, + `@param {SDK.ContractEvent.Type} ${eventParameterId} The unparsed contract event.`, + `@returns {${eventParameterTypeId}} The structured contract event.`, + ].join('\n'), + ], + isExported: true, + name: 'parseEvent', parameters: [ { - name: grpcClientId, - type: 'SDK.ConcordiumGRPCClient', - scope: tsm.Scope.Public, + name: eventParameterId, + type: 'SDK.ContractEvent.Type', }, + ], + returnType: eventParameterTypeId, + }) + .setBodyText( + [...eventParser.code, `return ${eventParser.id};`].join('\n') + ); + } +} + +/** + * Generate contract client code for each entrypoint. + * @param {tsm.SourceFile} contractSourceFile The sourcefile of the contract. + * @param {string} contractName The name of the contract. + * @param {string} contractClientId The identifier to use for the contract client. + * @param {string} contractClientType The identifier to use for the type of the contract client. + * @param {string} entrypointName The name of the entrypoint. + * @param {SDK.SchemaFunctionV2} [entrypointSchema] The schema to use for the entrypoint. + */ +function generateContractEntrypointCode( + contractSourceFile: tsm.SourceFile, + contractName: string, + contractClientId: string, + contractClientType: string, + entrypointName: string, + entrypointSchema?: SDK.SchemaFunctionV2 +) { + const invokeMetadataId = 'invokeMetadata'; + const parameterId = 'parameter'; + const transactionMetadataId = 'transactionMetadata'; + const signerId = 'signer'; + const genericContractId = 'genericContract'; + const blockHashId = 'blockHash'; + + const receiveParameter = createParameterCode( + parameterId, + entrypointSchema?.parameter + ); + + const receiveParameterTypeId = `${toPascalCase(entrypointName)}Parameter`; + + const createReceiveParameterFnId = `create${toPascalCase( + entrypointName + )}Parameter`; + + if (receiveParameter !== undefined) { + contractSourceFile.addTypeAlias({ + docs: [ + `Parameter type for update transaction for '${entrypointName}' entrypoint of the '${contractName}' contract.`, + ], + isExported: true, + name: receiveParameterTypeId, + type: receiveParameter.type, + }); + + contractSourceFile + .addFunction({ + docs: [ + [ + `Construct Parameter for update transactions for '${entrypointName}' entrypoint of the '${contractName}' contract.`, + `@param {${receiveParameterTypeId}} ${parameterId} The structured parameter to construct from.`, + '@returns {SDK.Parameter.Type} The smart contract parameter.', + ].join('\n'), + ], + isExported: true, + name: createReceiveParameterFnId, + parameters: [ { - name: contractAddressId, - type: 'SDK.ContractAddress', - isReadonly: true, - scope: tsm.Scope.Public, + type: receiveParameterTypeId, + name: parameterId, }, ], + returnType: 'SDK.Parameter.Type', }) .setBodyText( - `this.${genericContractId} = new SDK.Contract(${grpcClientId}, ${contractAddressId}, '${contract.contractName}'); -this.${dryRunId} = new ${contractDryRunClassId}(this.${genericContractId});` + [ + ...receiveParameter.code, + `return ${receiveParameter.id};`, + ].join('\n') ); + } + + contractSourceFile + .addFunction({ + docs: [ + [ + `Send an update-contract transaction to the '${entrypointName}' entrypoint of the '${contractName}' contract.`, + `@param {${contractClientType}} ${contractClientId} The client for a '${contractName}' smart contract instance on chain.`, + `@param {SDK.ContractTransactionMetadata} ${transactionMetadataId} - Metadata related to constructing a transaction for a smart contract.`, + ...(receiveParameter === undefined + ? [] + : [ + `@param {${receiveParameterTypeId}} ${parameterId} - Parameter to provide the smart contract entrypoint as part of the transaction.`, + ]), + `@param {SDK.AccountSigner} ${signerId} - The signer of the update contract transaction.`, + '@throws If the entrypoint is not successfully invoked.', + '@returns {SDK.TransactionHash.Type} Hash of the transaction.', + ].join('\n'), + ], + isExported: true, + name: `send${toPascalCase(entrypointName)}`, + parameters: [ + { + name: contractClientId, + type: contractClientType, + }, + { + name: transactionMetadataId, + type: 'SDK.ContractTransactionMetadata', + }, + ...(receiveParameter === undefined + ? [] + : [ + { + name: parameterId, + type: receiveParameterTypeId, + }, + ]), + { + name: signerId, + type: 'SDK.AccountSigner', + }, + ], + returnType: 'Promise', + }) + .setBodyText( + [ + `return ${contractClientId}.${genericContractId}.createAndSendUpdateTransaction(`, + ` SDK.EntrypointName.fromStringUnchecked('${entrypointName}'),`, + ' SDK.Parameter.toBuffer,', + ` ${transactionMetadataId},`, + ...(receiveParameter === undefined + ? [] + : [` ${createReceiveParameterFnId}(${parameterId}),`]), + ` ${signerId}`, + ');', + ].join('\n') + ); - dryRunClassDecl.addConstructor({ - docs: ['Contruct a client for a contract instance on chain'], + contractSourceFile + .addFunction({ + docs: [ + [ + `Dry-run an update-contract transaction to the '${entrypointName}' entrypoint of the '${contractName}' contract.`, + `@param {${contractClientType}} ${contractClientId} The client for a '${contractName}' smart contract instance on chain.`, + `@param {SDK.ContractAddress.Type | SDK.AccountAddress.Type} ${invokeMetadataId} - The address of the account or contract which is invoking this transaction.`, + ...(receiveParameter === undefined + ? [] + : [ + `@param {${receiveParameterTypeId}} ${parameterId} - Parameter to provide the smart contract entrypoint as part of the transaction.`, + ]), + `@param {SDK.BlockHash.Type} [${blockHashId}] - Optional block hash allowing for dry-running the transaction at the end of a specific block.`, + '@throws {SDK.RpcError} If failing to communicate with the concordium node or if any of the checks fails.', + '@returns {SDK.InvokeContractResult} The result of invoking the smart contract instance.', + ].join('\n'), + ], + isExported: true, + name: `dryRun${toPascalCase(entrypointName)}`, parameters: [ { - name: genericContractId, - type: 'SDK.Contract', - scope: tsm.Scope.Private, + name: contractClientId, + type: contractClientType, + }, + ...(receiveParameter === undefined + ? [] + : [ + { + name: parameterId, + type: receiveParameterTypeId, + }, + ]), + { + name: invokeMetadataId, + type: 'SDK.ContractInvokeMetadata', + initializer: '{}', + }, + { + name: blockHashId, + hasQuestionToken: true, + type: 'SDK.BlockHash.Type', }, ], + returnType: 'Promise', + }) + .setBodyText( + [ + `return ${contractClientId}.${genericContractId}.dryRun.invokeMethod(`, + ` SDK.EntrypointName.fromStringUnchecked('${entrypointName}'),`, + ` ${invokeMetadataId},`, + ' SDK.Parameter.toBuffer,', + ...(receiveParameter === undefined + ? [] + : [` ${createReceiveParameterFnId}(${parameterId}),`]), + ` ${blockHashId}`, + ');', + ].join('\n') + ); + + const invokeResultId = 'invokeResult'; + const returnValueTokens = parseReturnValueCode( + `${invokeResultId}.returnValue`, + entrypointSchema?.returnValue + ); + if (returnValueTokens !== undefined) { + const returnValueTypeId = `ReturnValue${toPascalCase(entrypointName)}`; + + contractSourceFile.addTypeAlias({ + docs: [ + `Return value for dry-running update transaction for '${entrypointName}' entrypoint of the '${contractName}' contract.`, + ], + isExported: true, + name: returnValueTypeId, + type: returnValueTokens.type, }); - for (const entrypointName of contract.entrypointNames) { - const transactionMetadataId = 'transactionMetadata'; - const parameterId = 'parameter'; - const signerId = 'signer'; - classDecl - .addMethod({ - docs: [ - `Send an update-contract transaction to the '${entrypointName}' entrypoint of the '${contract.contractName}' contract. - -@param {SDK.ContractTransactionMetadata} ${transactionMetadataId} - Hex encoded parameter for entrypoint -@param {SDK.HexString} ${parameterId} - Hex encoded parameter for entrypoint -@param {SDK.AccountSigner} ${signerId} - The signer of the update contract transaction. - -@throws If the entrypoint is not successfully invoked. - -@returns {SDK.HexString} Transaction hash`, - ], - scope: tsm.Scope.Public, - name: toCamelCase(entrypointName), - parameters: [ - { - name: transactionMetadataId, - type: 'SDK.ContractTransactionMetadata', - }, - { - name: parameterId, - type: 'SDK.HexString', - }, - { - name: signerId, - type: 'SDK.AccountSigner', - }, - ], - returnType: 'Promise', - }) - .setBodyText( - `return this.${genericContractId}.createAndSendUpdateTransaction( - '${entrypointName}', - SDK.encodeHexString, - ${transactionMetadataId}, - ${parameterId}, - ${signerId} -);` - ); - const blockHashId = 'blockHash'; - dryRunClassDecl - .addMethod({ - docs: [ - `Dry run an update-contract transaction to the '${entrypointName}' entrypoint of the '${contract.contractName}' contract. - -@param {SDK.HexString} ${parameterId} - Hex encoded parameter for entrypoint -@param {SDK.HexString} [${blockHashId}] - Block hash of the block to invoke entrypoint at - -@throws If the entrypoint is not successfully invoked. - -returns {SDK.HexString} Hex encoded response`, - ], - scope: tsm.Scope.Public, - name: toCamelCase(entrypointName), - parameters: [ - { - name: parameterId, - type: 'SDK.HexString', - }, - { - name: blockHashId, - type: 'SDK.HexString', - hasQuestionToken: true, - }, - ], - returnType: 'Promise', - }) - .setBodyText( - `return this.${genericContractId}.invokeView( - '${entrypointName}', - SDK.encodeHexString, - (hex: SDK.HexString) => hex, - ${parameterId}, - ${blockHashId} -);` - ); - } + contractSourceFile + .addFunction({ + docs: [ + [ + `Get and parse the return value from dry-running update transaction for '${entrypointName}' entrypoint of the '${contractName}' contract.`, + 'Returns undefined if the result is not successful.', + '@param {SDK.InvokeContractResult} invokeResult The result from dry-running the transaction.', + `@returns {${returnValueTypeId} | undefined} The structured return value or undefined if result was not a success.`, + ].join('\n'), + ], + isExported: true, + name: `parseReturnValue${toPascalCase(entrypointName)}`, + parameters: [ + { + name: invokeResultId, + type: 'SDK.InvokeContractResult', + }, + ], + returnType: `${returnValueTypeId} | undefined`, + }) + .setBodyText( + [ + `if (${invokeResultId}.tag !== 'success') {`, + ' return undefined;', + '}', + `if (${invokeResultId}.returnValue === undefined) {`, + " throw new Error('Unexpected missing \\'returnValue\\' in result of invocation. Client expected a V1 smart contract.');", + '}', + ...returnValueTokens.code, + `return ${returnValueTokens.id};`, + ].join('\n') + ); + } + + const errorMessageTokens = parseReturnValueCode( + `${invokeResultId}.returnValue`, + entrypointSchema?.error + ); + if (errorMessageTokens !== undefined) { + const errorMessageTypeId = `ErrorMessage${toPascalCase( + entrypointName + )}`; + + contractSourceFile.addTypeAlias({ + docs: [ + `Error message for dry-running update transaction for '${entrypointName}' entrypoint of the '${contractName}' contract.`, + ], + isExported: true, + name: errorMessageTypeId, + type: errorMessageTokens.type, + }); + + contractSourceFile + .addFunction({ + docs: [ + [ + `Get and parse the error message from dry-running update transaction for '${entrypointName}' entrypoint of the '${contractName}' contract.`, + 'Returns undefined if the result is not a failure.', + '@param {SDK.InvokeContractResult} invokeResult The result from dry-running the transaction.', + `@returns {${errorMessageTypeId} | undefined} The structured error message or undefined if result was not a failure or failed for other reason than contract rejectedReceive.`, + ].join('\n'), + ], + isExported: true, + name: `parseErrorMessage${toPascalCase(entrypointName)}`, + parameters: [ + { + name: invokeResultId, + type: 'SDK.InvokeContractResult', + }, + ], + returnType: `${errorMessageTypeId} | undefined`, + }) + .setBodyText( + [ + `if (${invokeResultId}.tag !== 'failure' || ${invokeResultId}.reason.tag !== 'RejectedReceive') {`, + ' return undefined;', + '}', + `if (${invokeResultId}.returnValue === undefined) {`, + " throw new Error('Unexpected missing \\'returnValue\\' in result of invocation. Client expected a V1 smart contract.');", + '}', + ...errorMessageTokens.code, + `return ${errorMessageTokens.id}`, + ].join('\n') + ); } } @@ -275,17 +1111,6 @@ function capitalize(str: string): string { return str.charAt(0).toUpperCase() + str.substring(1); } -/** - * Convert a string in snake_case or kebab-case into camelCase. - * This is used to transform entrypoint names in the smart contract to follow formatting javascript convention. - */ -function toCamelCase(str: string): string { - return str - .split(/[-_]/g) - .map((word, index) => (index === 0 ? word : capitalize(word))) - .join(''); -} - /** * Convert a string in snake_case or kebab-case into PascalCase. * This is used to transform contract names in the smart contract to follow formatting javascript convention. @@ -293,3 +1118,948 @@ function toCamelCase(str: string): string { function toPascalCase(str: string): string { return str.split(/[-_]/g).map(capitalize).join(''); } + +/** Type information from the schema. */ +type SchemaNativeType = { + /** The type to provide for a given schema type. */ + nativeType: string; + /** The type in the Schema JSON format. */ + jsonType: string; + /** + * Provided the identifier for the input (of the above type), this generates + * tokens for converting it into Schema JSON format. + */ + nativeToJson: (nativeId: string) => { code: string[]; id: string }; + /** + * Provided the identifier for the input (Schema JSON format), this generates + * tokens for converting it into a native type (the above type). + */ + jsonToNative: (jsonId: string) => { code: string[]; id: string }; +}; + +/** + * From a schema type construct a 'native' type, a type of the expected JSON format and converter functions + * between this native type and the JSON format expected when serializing using a schema. + * + * @param {SDK.SchemaType} schemaType The schema type + * @returns {SchemaNativeType} native type, JSON type and converters. + */ +function schemaAsNativeType(schemaType: SDK.SchemaType): SchemaNativeType { + switch (schemaType.type) { + case 'Unit': + return { + nativeType: '"Unit"', + jsonType: '[]', + jsonToNative() { + return { code: [], id: '[]' }; + }, + nativeToJson() { + return { code: [], id: '"Unit"' }; + }, + }; + case 'Bool': + return { + nativeType: 'boolean', + jsonType: 'boolean', + nativeToJson(nativeId) { + return { code: [], id: nativeId }; + }, + jsonToNative(jsonId) { + return { code: [], id: jsonId }; + }, + }; + case 'U8': + case 'U16': + case 'U32': + case 'I8': + case 'I16': + case 'I32': + return { + nativeType: 'number', + jsonType: 'number', + nativeToJson(id) { + return { code: [], id }; + }, + jsonToNative(id) { + return { code: [], id }; + }, + }; + case 'U64': + case 'I64': + return { + nativeType: 'number | bigint', + jsonType: 'bigint', + nativeToJson(id) { + const resultId = idGenerator('number'); + return { + code: [`const ${resultId} = BigInt(${id});`], + id: resultId, + }; + }, + jsonToNative(id) { + return { code: [], id }; + }, + }; + case 'U128': + case 'I128': + return { + nativeType: 'number | bigint', + jsonType: 'string', + nativeToJson(id) { + const resultId = idGenerator('number'); + return { + code: [`const ${resultId} = BigInt(${id}).toString();`], + id: resultId, + }; + }, + jsonToNative(id) { + return { code: [], id: `BigInt(${id})` }; + }, + }; + case 'Amount': + return { + nativeType: 'SDK.CcdAmount.Type', + jsonType: 'SDK.CcdAmount.SchemaValue', + nativeToJson(id) { + const resultId = idGenerator('amount'); + return { + code: [ + `const ${resultId} = SDK.CcdAmount.toSchemaValue(${id});`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('amount'); + return { + code: [ + `const ${resultId} = SDK.CcdAmount.fromSchemaValue(${id});`, + ], + id: resultId, + }; + }, + }; + case 'AccountAddress': + return { + nativeType: 'SDK.AccountAddress.Type', + jsonType: 'SDK.AccountAddress.SchemaValue', + nativeToJson(id) { + const resultId = idGenerator('accountAddress'); + return { + code: [ + `const ${resultId} = SDK.AccountAddress.toSchemaValue(${id});`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('accountAddress'); + return { + code: [ + `const ${resultId} = SDK.AccountAddress.fromSchemaValue(${id});`, + ], + id: resultId, + }; + }, + }; + case 'ContractAddress': + return { + nativeType: 'SDK.ContractAddress.Type', + jsonType: 'SDK.ContractAddress.SchemaValue', + nativeToJson(id) { + const resultId = idGenerator('contractAddress'); + return { + code: [ + `const ${resultId} = SDK.ContractAddress.toSchemaValue(${id});`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('contractAddress'); + return { + code: [ + `const ${resultId} = SDK.ContractAddress.fromSchemaValue(${id});`, + ], + id: resultId, + }; + }, + }; + case 'Timestamp': + return { + nativeType: 'SDK.Timestamp.Type', + jsonType: 'SDK.Timestamp.SchemaValue', + nativeToJson(id) { + const resultId = idGenerator('timestamp'); + return { + code: [ + `const ${resultId} = SDK.Timestamp.toSchemaValue(${id});`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('timestamp'); + return { + code: [ + `const ${resultId} = SDK.Timestamp.fromSchemaValue(${id});`, + ], + id: resultId, + }; + }, + }; + case 'Duration': + return { + nativeType: 'SDK.Duration.Type', + jsonType: 'SDK.Duration.SchemaValue', + nativeToJson(id) { + const resultId = idGenerator('duration'); + return { + code: [ + `const ${resultId} = SDK.Duration.toSchemaValue(${id});`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('duration'); + return { + code: [ + `const ${resultId} = SDK.Duration.fromSchemaValue(${id});`, + ], + id: resultId, + }; + }, + }; + case 'Pair': + const first = schemaAsNativeType(schemaType.first); + const second = schemaAsNativeType(schemaType.second); + + return { + nativeType: `[${first.nativeType}, ${second.nativeType}]`, + jsonType: `[${first.jsonType}, ${second.jsonType}]`, + nativeToJson(id) { + const resultId = idGenerator('pair'); + const firstTokens = first.nativeToJson(`${id}[0]`); + const secondTokens = second.nativeToJson(`${id}[1]`); + return { + code: [ + ...firstTokens.code, + ...secondTokens.code, + `const ${resultId}: ${this.jsonType} = [${firstTokens.id}, ${secondTokens.id}];`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('pair'); + const firstTokens = first.jsonToNative(`${id}[0]`); + const secondTokens = second.jsonToNative(`${id}[1]`); + return { + code: [ + ...firstTokens.code, + ...secondTokens.code, + `const ${resultId}: ${this.nativeType} = [${firstTokens.id}, ${secondTokens.id}];`, + ], + id: resultId, + }; + }, + }; + case 'List': { + const item = schemaAsNativeType(schemaType.item); + return { + nativeType: `Array<${item.nativeType}>`, + jsonType: `Array<${item.jsonType}>`, + nativeToJson(id) { + const resultId = idGenerator('list'); + const itemId = idGenerator('item'); + const tokens = item.nativeToJson(itemId); + // Check if any mapping is needed. + if (tokens.id === itemId && tokens.code.length === 0) { + return { + code: [], + id, + }; + } + return { + code: [ + `const ${resultId} = ${id}.map((${itemId}) => {`, + ...tokens.code, + `return ${tokens.id};`, + '});', + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('list'); + const itemId = idGenerator('item'); + const tokens = item.jsonToNative(itemId); + // Check if any mapping is needed. + if (tokens.id === itemId && tokens.code.length === 0) { + return { + code: [], + id, + }; + } + return { + code: [ + `const ${resultId} = ${id}.map((${itemId}) => {`, + ...tokens.code, + `return ${tokens.id};`, + '});', + ], + id: resultId, + }; + }, + }; + } + case 'Set': { + const item = schemaAsNativeType(schemaType.item); + return { + nativeType: `Set<${item.nativeType}>`, + jsonType: `Array<${item.jsonType}>`, + nativeToJson(id) { + const resultId = idGenerator('set'); + const valueId = idGenerator('value'); + const valuesId = idGenerator('values'); + const valueTokens = item.nativeToJson(valueId); + return { + code: [ + `const ${valuesId} = [...${id}.values()]..map((${valueId}) => {`, + ...valueTokens.code, + `return ${valueTokens.id};`, + '});', + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('set'); + const valueId = idGenerator('value'); + const valuesId = idGenerator('values'); + const valueTokens = item.jsonToNative(valueId); + return { + code: [ + `const ${valuesId} = ${id}.map((${valueId}) => {`, + ...valueTokens.code, + `return ${valueTokens.id}; `, + '});', + `const ${resultId} = new Set(${valuesId});`, + ], + id: resultId, + }; + }, + }; + } + case 'Map': { + const key = schemaAsNativeType(schemaType.key); + const value = schemaAsNativeType(schemaType.value); + return { + nativeType: `Map<${key.nativeType}, ${value.nativeType}>`, + jsonType: `[${key.jsonType}, ${value.jsonType}][]`, + nativeToJson(id) { + const resultId = idGenerator('map'); + const keyId = idGenerator('key'); + const valueId = idGenerator('value'); + const keyTokens = key.nativeToJson(keyId); + const valueTokens = value.nativeToJson(valueId); + + return { + code: [ + `const ${resultId}: ${this.jsonType} = [...${id}.entries()].map(([${keyId}, ${valueId}]) => {`, + ...keyTokens.code, + ...valueTokens.code, + ` return [${keyTokens.id}, ${valueTokens.id}];`, + '});', + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('map'); + const entriesId = idGenerator('entries'); + const keyId = idGenerator('key'); + const valueId = idGenerator('value'); + const keyTokens = key.jsonToNative(keyId); + const valueTokens = value.jsonToNative(valueId); + return { + code: [ + `const ${entriesId} = ${id}.map(([${keyId}, ${valueId}]) => {`, + ...keyTokens.code, + ...valueTokens.code, + `return [${keyTokens.id}, ${valueTokens.id}];`, + '});', + `const ${resultId}: ${this.nativeType} = Map.fromEntries(${entriesId});`, + ], + id: resultId, + }; + }, + }; + } + case 'Array': { + const item = schemaAsNativeType(schemaType.item); + return { + nativeType: `[${new Array(schemaType.size) + .fill(item.nativeType) + .join(', ')}]`, + jsonType: `[${new Array(schemaType.size) + .fill(item.jsonType) + .join(', ')}]`, + nativeToJson(id) { + const resultId = idGenerator('array'); + const itemId = idGenerator('item'); + const tokens = item.nativeToJson(itemId); + // Check if any mapping is needed. + if (tokens.id === itemId && tokens.code.length === 0) { + return { + code: [], + id, + }; + } + + return { + code: [ + `const ${resultId} = ${id}.map((${itemId}) => {`, + ...tokens.code, + ` return ${tokens.id};`, + '});', + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('array'); + const itemId = idGenerator('item'); + const tokens = item.jsonToNative(itemId); + // Check if any mapping is needed. + if (tokens.id === itemId && tokens.code.length === 0) { + return { + code: [], + id, + }; + } + return { + code: [ + `const ${resultId} = ${id}.map((${itemId}: any) => {`, + ...tokens.code, + ` return ${tokens.id};`, + '});', + ], + id: resultId, + }; + }, + }; + } + case 'Struct': + return fieldToTypeAndMapper(schemaType.fields); + + case 'Enum': + case 'TaggedEnum': { + const variants = + schemaType.type === 'Enum' + ? schemaType.variants + : [...schemaType.variants.values()]; + + const variantFieldSchemas = variants.map((variant) => ({ + name: variant.name, + ...fieldToTypeAndMapper(variant.fields), + })); + + const variantTypes = variantFieldSchemas.map((variantSchema) => + variantSchema.nativeType === '"no-fields"' + ? `{ type: '${variantSchema.name}'}` + : `{ type: '${variantSchema.name}', content: ${variantSchema.nativeType} }` + ); + + const variantJsonTypes = variantFieldSchemas.map( + (variantSchema) => + `{'${variantSchema.name}' : ${variantSchema.jsonType} }` + ); + + return { + nativeType: variantTypes.join(' | '), + jsonType: variantJsonTypes.join(' | '), + nativeToJson(id) { + const resultId = idGenerator('match'); + + const variantCases = variantFieldSchemas.flatMap( + (variantSchema) => { + const tokens = variantSchema.nativeToJson( + `${id}.content` + ); + return [ + ` case '${variantSchema.name}':`, + ...tokens.code, + ` ${resultId} = { ${defineProp( + variantSchema.name, + tokens.id + )} };`, + ' break;', + ]; + } + ); + return { + code: [ + `let ${resultId}: ${this.jsonType};`, + `switch (${id}.type) {`, + ...variantCases, + '}', + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('match'); + //const variantKeyId = idGenerator('variantKey'); + + const variantIfStatements = variantFieldSchemas.map( + (variantFieldSchema) => { + const variantId = idGenerator('variant'); + const variantTokens = + variantFieldSchema.jsonToNative(variantId); + return [ + `if ('${variantFieldSchema.name}' in ${id}) {`, + ...(variantTokens.id === '"no-fields"' + ? [ + ` ${resultId} = {`, + ` type: '${variantFieldSchema.name}',`, + ' };', + ] + : [ + ` const ${variantId} = ${accessProp( + id, + variantFieldSchema.name + )};`, + ...variantTokens.code, + ` ${resultId} = {`, + ` type: '${variantFieldSchema.name}',`, + ` content: ${variantTokens.id},`, + ' };', + ]), + '}', + ].join('\n'); + } + ); + return { + code: [ + `let ${resultId}: ${this.nativeType};`, + variantIfStatements.join(' else '), + ' else {', + ' throw new Error("Unexpected enum variant");', + '}', + ], + id: resultId, + }; + }, + }; + } + case 'String': + return { + nativeType: 'string', + jsonType: 'string', + nativeToJson(id) { + return { code: [], id }; + }, + jsonToNative(id) { + return { + code: [], + id, + }; + }, + }; + case 'ContractName': + return { + nativeType: 'SDK.ContractName.Type', + jsonType: 'SDK.ContractName.SchemaType', + nativeToJson(id) { + const resultId = idGenerator('contractName'); + return { + code: [ + `const ${resultId} = SDK.ContractName.toSchemaValue(${id});`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('contractName'); + return { + code: [ + `const ${resultId} = SDK.ContractName.fromSchemaValue(${id});`, + ], + id: resultId, + }; + }, + }; + case 'ReceiveName': + return { + nativeType: 'SDK.ReceiveName.Type', + jsonType: 'SDK.ReceiveName.SchemaType', + nativeToJson(id) { + const resultId = idGenerator('receiveName'); + return { + code: [ + `const ${resultId} = SDK.ReceiveName.toSchemaValue(${id});`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('receiveName'); + return { + code: [ + `const ${resultId} = SDK.ReceiveName.fromSchemaValue(${id});`, + ], + id: resultId, + }; + }, + }; + case 'ULeb128': + case 'ILeb128': + return { + nativeType: 'number | bigint', + jsonType: 'bigint', + nativeToJson(id) { + const resultId = idGenerator('number'); + return { + code: [`const ${resultId} = BigInt(${id}).toString();`], + id: resultId, + }; + }, + jsonToNative(id) { + return { + code: [], + id: `BigInt(${id})`, + }; + }, + }; + case 'ByteList': + case 'ByteArray': + return { + nativeType: 'SDK.HexString', + jsonType: 'string', + nativeToJson(id) { + return { code: [], id }; + }, + jsonToNative(id) { + return { + code: [], + id, + }; + }, + }; + } +} + +function fieldToTypeAndMapper(fields: SDK.SchemaFields): SchemaNativeType { + switch (fields.type) { + case 'Named': { + const schemas = fields.fields.map((named) => ({ + name: named.name, + ...schemaAsNativeType(named.field), + })); + + const objectFieldTypes = schemas.map((s) => + defineProp(s.name, s.nativeType) + ); + const objectFieldJsonTypes = schemas.map((s) => + defineProp(s.name, s.jsonType) + ); + + return { + nativeType: `{\n${objectFieldTypes.join('\n')}\n}`, + jsonType: `{\n${objectFieldJsonTypes.join('\n')}\n}`, + nativeToJson(id) { + const resultId = idGenerator('named'); + const fields = schemas.map((s) => { + const fieldId = idGenerator('field'); + const field = s.nativeToJson(fieldId); + return { + name: s.name, + constructTokens: [ + `const ${fieldId} = ${accessProp(id, s.name)};`, + ...field.code, + ], + id: field.id, + }; + }); + const constructTokens = fields.flatMap( + (tokens) => tokens.constructTokens + ); + + return { + code: [ + ...constructTokens, + `const ${resultId} = {`, + ...fields.map((tokens) => + defineProp(tokens.name, tokens.id) + ), + '};', + ], + id: resultId, + }; + }, + jsonToNative(id) { + const fields = schemas.map((s) => { + const fieldId = idGenerator('field'); + const field = s.jsonToNative(fieldId); + return { + name: s.name, + constructTokens: [ + `const ${fieldId} = ${accessProp(id, s.name)};`, + ...field.code, + ], + id: field.id, + }; + }); + const constructTokens = fields.flatMap( + (tokens) => tokens.constructTokens + ); + const resultId = idGenerator('named'); + return { + code: [ + ...constructTokens, + `const ${resultId} = {`, + ...fields.map((tokens) => + defineProp(tokens.name, tokens.id) + ), + '};', + ], + id: resultId, + }; + }, + }; + } + case 'Unnamed': { + const schemas = fields.fields.flatMap((f) => { + const schema = schemaAsNativeType(f); + return schema === undefined ? [] : [schema]; + }); + if (schemas.length === 1) { + const schema = schemas[0]; + return { + nativeType: schema.nativeType, + jsonType: `[${schema.jsonType}]`, + nativeToJson(id) { + const tokens = schema.nativeToJson(id); + return { code: tokens.code, id: `[${tokens.id}]` }; + }, + jsonToNative(id) { + return schema.jsonToNative(`${id}[0]`); + }, + }; + } else { + return { + nativeType: `[${schemas + .map((s) => s.nativeType) + .join(', ')}]`, + jsonType: `[${schemas.map((s) => s.jsonType).join(', ')}]`, + nativeToJson(id) { + const resultId = idGenerator('unnamed'); + const mapped = schemas.map((schema, index) => + schema.nativeToJson(`${id}[${index}]`) + ); + const constructFields = mapped.flatMap( + (tokens) => tokens.code + ); + const fieldIds = mapped.map((s) => s.id); + return { + code: [ + ...constructFields, + `const ${resultId}: ${ + this.jsonType + } = [${fieldIds.join(', ')}];`, + ], + id: resultId, + }; + }, + jsonToNative(id) { + const resultId = idGenerator('unnamed'); + const mapped = schemas.map((schema, index) => + schema.jsonToNative(`${id}[${index}]`) + ); + const constructFields = mapped.flatMap( + (tokens) => tokens.code + ); + const fieldIds = mapped.map((s) => s.id); + return { + code: [ + ...constructFields, + `const ${resultId} = [${fieldIds.join(', ')}];`, + ], + id: resultId, + }; + }, + }; + } + } + case 'None': + return { + nativeType: '"no-fields"', + jsonType: '[]', + nativeToJson() { + return { code: [], id: '[]' }; + }, + jsonToNative() { + return { code: [], id: '"no-fields"' }; + }, + }; + } +} + +/** + * Information related to conversion between JSON and native type. + */ +type TypeConversionCode = { + /** The native type. */ + type: string; + /** Code to convert JSON either to or from native type. */ + code: string[]; + /** Identifier for the result of the code. */ + id: string; +}; + +/** + * Generate tokens for creating the parameter from input. + * @param {string} parameterId Identifier of the input. + * @param {SDK.SchemaType} [schemaType] The schema type to use for the parameter. + * @returns Undefined if no parameter is expected. + */ +function createParameterCode( + parameterId: string, + schemaType?: SDK.SchemaType +): TypeConversionCode | undefined { + // No schema type is present so fallback to plain parameter. + if (schemaType === undefined) { + return { + type: 'SDK.Parameter.Type', + code: [], + id: parameterId, + }; + } + + if (schemaType.type === 'Unit') { + // No parameter is needed according to the schema. + return undefined; + } + const typeAndMapper = schemaAsNativeType(schemaType); + const buffer = SDK.serializeSchemaType(schemaType); + const base64Schema = Buffer.from(buffer).toString('base64'); + + const mappedParameter = typeAndMapper.nativeToJson(parameterId); + const resultId = 'out'; + return { + type: typeAndMapper.nativeType, + code: [ + ...mappedParameter.code, + `const ${resultId} = SDK.Parameter.fromBase64SchemaType('${base64Schema}', ${mappedParameter.id});`, + ], + id: resultId, + }; +} + +/** + * Generate tokens for parsing a contract event. + * @param {string} eventId Identifier of the event to parse. + * @param {SDK.SchemaType} [schemaType] The schema to take into account when parsing. + * @returns Undefined if no code should be produced. + */ +function parseEventCode( + eventId: string, + schemaType?: SDK.SchemaType +): TypeConversionCode | undefined { + // No schema type is present so generate any code. + if (schemaType === undefined) { + return undefined; + } + const typeAndMapper = schemaAsNativeType(schemaType); + const base64Schema = Buffer.from( + SDK.serializeSchemaType(schemaType) + ).toString('base64'); + + const schemaJsonId = 'schemaJson'; + const tokens = typeAndMapper.jsonToNative(schemaJsonId); + return { + type: typeAndMapper.nativeType, + code: [ + `const ${schemaJsonId} = <${typeAndMapper.jsonType}>SDK.ContractEvent.parseWithSchemaTypeBase64(${eventId}, '${base64Schema}');`, + ...tokens.code, + ], + id: tokens.id, + }; +} + +/** + * Generate tokens for parsing a return type. + * @param {string} returnTypeId Identifier of the return type to parse. + * @param {SDK.SchemaType} [schemaType] The schema to take into account when parsing return type. + * @returns Undefined if no code should be produced. + */ +function parseReturnValueCode( + returnTypeId: string, + schemaType?: SDK.SchemaType +): TypeConversionCode | undefined { + // No schema type is present so don't generate any code. + if (schemaType === undefined) { + return undefined; + } + const typeAndMapper = schemaAsNativeType(schemaType); + const base64Schema = Buffer.from( + SDK.serializeSchemaType(schemaType) + ).toString('base64'); + + const schemaJsonId = 'schemaJson'; + const tokens = typeAndMapper.jsonToNative(schemaJsonId); + return { + type: typeAndMapper.nativeType, + + code: [ + `const ${schemaJsonId} = <${typeAndMapper.jsonType}>SDK.ReturnValue.parseWithSchemaTypeBase64(${returnTypeId}, '${base64Schema}');`, + ...tokens.code, + ], + id: tokens.id, + }; +} + +/** + * Stateful function which suffixes a provided string with a number, which increments everytime this is called. + * Used to ensure identifiers are unique. + * @param {string} name Name of the identifier. + * @returns {string} The name of the identifier suffixed with a number. + */ +const idGenerator = (() => { + let counter = 0; + return (name: string) => `${name}${counter++}`; +})(); + +/** + * Create tokens for accessing a property on an object. + * @param {string} objectId Identifier for the object. + * @param {string} propId Identifier for the property. + * @returns {string} Tokens for accessing the prop. + */ +function accessProp(objectId: string, propId: string): string { + return identifierRegex.test(propId) + ? `${objectId}.${propId}` + : `${objectId}['${propId}']`; +} + +/** + * Create tokens for defining a property in an object + * @param {string} propId Identifier for the property. + * @param {string} valueId Identifier for the value. + * @returns {string} Tokens for defining a property initialized to the value. + */ +function defineProp(propId: string, valueId: string): string { + return identifierRegex.test(propId) + ? `${propId}: ${valueId},` + : `'${propId}': ${valueId},`; +} + +/** + * Regular expression matching the format of valid identifiers in javascript. + * + * > Note: this does not check for collision with keywords, which is not a problem + * when accessing props or defining fields in an object. + */ +const identifierRegex = /^[$A-Z_][0-9A-Z_$]*$/i; diff --git a/packages/ccd-js-gen/test/generate-test-bench.test.ts b/packages/ccd-js-gen/test/generate-test-bench.test.ts new file mode 100644 index 000000000..faa4fc3b1 --- /dev/null +++ b/packages/ccd-js-gen/test/generate-test-bench.test.ts @@ -0,0 +1,93 @@ +/* + * This file contains tests for generating clients for the smart contract module 'test-bench' + * located at https://github.com/Concordium/concordium-misc-tools/tree/main/wallet-connect-test-bench/smart-contract + * + * The tests will run the client generator, typecheck the generated files and ensure the files + * exposes relevant functions. + * + * These tests depends on the smart contract module being a file with path: + * + * test/resources/test-bench.wasm.v1 + * + * Which can be built from the above linked project using `cargo-concordium`: + * + * ``` + * cargo concordium build --schema-embed --out ./path/to/test/resources/test-bench.wasm.v1 + * ``` + */ +import * as Gen from '../src/lib.js'; +import { assertTypeChecks } from './testHelpers.js'; + +const outDir = __dirname + '/lib'; +const outModuleFile = `${outDir}/test-bench`; +const outContractFile = `${outDir}/test-bench_smart_contract_test_bench`; + +beforeAll(() => { + return Gen.generateContractClientsFromFile( + __dirname + '/resources/test-bench.wasm.v1', + outDir, + { output: 'Everything' } + ); +}, 60_000); + +describe('Generated module client file', () => { + test('Type checks', () => { + assertTypeChecks([`${outModuleFile}.ts`]); + }); + + test('Exports basic functions based on wasm', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outModuleFile}.js`); + + expect(module.create).toBeDefined(); + expect(module.createUnchecked).toBeDefined(); + expect(module.checkOnChain).toBeDefined(); + expect(module.getModuleSource).toBeDefined(); + }); + + test('Exports module specific functions based on wasm', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outModuleFile}.js`); + expect(module.instantiateSmartContractTestBench).toBeDefined(); + }); + + test('Exports functions based on schema', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outModuleFile}.js`); + expect(module.createSmartContractTestBenchParameter).toBeDefined(); + }); +}); + +describe('Generated contract client file', () => { + test('Type checks', () => { + assertTypeChecks([`${outContractFile}.ts`]); + }); + + test('Exports basic functions based on wasm module', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outContractFile}.js`); + + expect(module.create).toBeDefined(); + expect(module.createUnchecked).toBeDefined(); + expect(module.checkOnChain).toBeDefined(); + }); + + test('Exports contract specific functions based on wasm module', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outContractFile}.js`); + + expect(module.sendSetU8).toBeDefined(); + expect(module.dryRunSetU8).toBeDefined(); + expect(module.sendSetU8Payable).toBeDefined(); + expect(module.dryRunSetU8Payable).toBeDefined(); + }); + + test('Exports functions based on schema', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outContractFile}.js`); + expect(module.parseEvent).not.toBeDefined(); + expect(module.createSetU8Parameter).toBeDefined(); + expect(module.createSetU8PayableParameter).toBeDefined(); + expect(module.parseErrorMessageSetU8).toBeDefined(); + }); +}); diff --git a/packages/ccd-js-gen/test/generate-wCCD.test.ts b/packages/ccd-js-gen/test/generate-wCCD.test.ts new file mode 100644 index 000000000..6c8f27baa --- /dev/null +++ b/packages/ccd-js-gen/test/generate-wCCD.test.ts @@ -0,0 +1,98 @@ +/* + * This file contains tests for generating clients for the smart contract module of wCCD + * (module reference "cc285180b45d7695db75c29dee004d2e81a1383880c9b122399bea809196c98f"). + * + * The tests will run the client generator, typecheck the generated files and ensure the files + * exposes relevant functions. + * + * These tests depends on the smart contract module being a file with path: + * + * test/resources/wCCD.wasm.v1 + * + * Which can be generated using the SDK: + * + * ``` + * import * as SDK from "@concordium/web-sdk"; + * import * as fs from "node:fs/promises"; + * const client = ...// Grpc client for Testnet. + * const source = await client.getModuleSource("cc285180b45d7695db75c29dee004d2e81a1383880c9b122399bea809196c98f"); + * await fs.writeFile("test/resources/wCCD.wasm.v1", versionedModuleSourceToBuffer(source)); + * ``` + */ +import * as Gen from '../src/lib.js'; +import { assertTypeChecks } from './testHelpers.js'; + +const moduleFileName = 'wCCD'; +const moduleFilePath = `${__dirname}/resources/${moduleFileName}.wasm.v1`; +const outDir = `${__dirname}/lib`; +const outModuleFile = `${outDir}/${moduleFileName}`; +const outContractFile = `${outDir}/${moduleFileName}_cis2_wCCD`; + +beforeAll(() => { + return Gen.generateContractClientsFromFile(moduleFilePath, outDir, { + output: 'Everything', + }); +}, 60_000); + +describe('Generated module client file', () => { + test('Type checks', () => { + assertTypeChecks([`${outModuleFile}.ts`]); + }); + + test('Exports basic functions based on wasm', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outModuleFile}.js`); + + expect(module.create).toBeDefined(); + expect(module.createUnchecked).toBeDefined(); + expect(module.checkOnChain).toBeDefined(); + expect(module.getModuleSource).toBeDefined(); + }); + + test('Exports module specific functions based on wasm', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outModuleFile}.js`); + expect(module.instantiateCis2WCCD).toBeDefined(); + }); + + test('Exports functions based on schema', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outModuleFile}.js`); + expect(module.createCis2WCCDParameter).toBeDefined(); + }); +}); + +describe('Generated contract client file', () => { + test('Type checks', () => { + assertTypeChecks([`${outContractFile}.ts`]); + }); + + test('Exports basic functions based on wasm module', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outContractFile}.js`); + + expect(module.create).toBeDefined(); + expect(module.createUnchecked).toBeDefined(); + expect(module.checkOnChain).toBeDefined(); + }); + + test('Exports contract specific functions based on wasm module', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outContractFile}.js`); + + expect(module.sendWrap).toBeDefined(); + expect(module.dryRunWrap).toBeDefined(); + expect(module.sendTransfer).toBeDefined(); + expect(module.dryRunTransfer).toBeDefined(); + }); + + test('Exports functions based on schema', async () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const module = require(`${outContractFile}.js`); + expect(module.parseEvent).toBeDefined(); + expect(module.createUnwrapParameter).toBeDefined(); + expect(module.parseErrorMessageUnwrap).toBeDefined(); + expect(module.createTransferParameter).toBeDefined(); + expect(module.parseErrorMessageTransfer).toBeDefined(); + }); +}); diff --git a/packages/ccd-js-gen/test/resources/test-bench.wasm.v1 b/packages/ccd-js-gen/test/resources/test-bench.wasm.v1 new file mode 100644 index 000000000..4e6fcbca8 Binary files /dev/null and b/packages/ccd-js-gen/test/resources/test-bench.wasm.v1 differ diff --git a/packages/ccd-js-gen/test/resources/wCCD.wasm.v1 b/packages/ccd-js-gen/test/resources/wCCD.wasm.v1 new file mode 100644 index 000000000..a0097861d Binary files /dev/null and b/packages/ccd-js-gen/test/resources/wCCD.wasm.v1 differ diff --git a/packages/ccd-js-gen/test/testHelpers.ts b/packages/ccd-js-gen/test/testHelpers.ts new file mode 100644 index 000000000..7711214ce --- /dev/null +++ b/packages/ccd-js-gen/test/testHelpers.ts @@ -0,0 +1,42 @@ +import * as ts from 'typescript'; + +/** + * Use typescript compiler to typecheck the provided files, fails the test otherwise. + * @param {string[]} files Typescript files to type check. + */ +export function assertTypeChecks(files: string[]) { + const program = ts.createProgram(files, { + noEmit: true, + target: ts.ScriptTarget.ES2022, + moduleResolution: ts.ModuleResolutionKind.NodeNext, + module: ts.ModuleKind.NodeNext, + }); + const emitResult = program.emit(); + + const allDiagnostics = ts + .getPreEmitDiagnostics(program) + .concat(emitResult.diagnostics); + + const errors = allDiagnostics.map((diagnostic) => { + if (diagnostic.file) { + const { line, character } = ts.getLineAndCharacterOfPosition( + diagnostic.file, + diagnostic.start! + ); + const message = ts.flattenDiagnosticMessageText( + diagnostic.messageText, + '\n' + ); + return `${diagnostic.file.fileName} (${line + 1},${ + character + 1 + }): ${message}`; + } else { + return ts.flattenDiagnosticMessageText( + diagnostic.messageText, + '\n' + ); + } + }); + expect(errors).toEqual([]); + expect(emitResult.emitSkipped).toBeFalsy(); +} diff --git a/packages/ccd-js-gen/tsconfig.build.json b/packages/ccd-js-gen/tsconfig.build.json new file mode 100644 index 000000000..edc2723b8 --- /dev/null +++ b/packages/ccd-js-gen/tsconfig.build.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*", "package.json"] +} diff --git a/packages/ccd-js-gen/tsconfig.json b/packages/ccd-js-gen/tsconfig.json index 93480aa50..4f493d24a 100644 --- a/packages/ccd-js-gen/tsconfig.json +++ b/packages/ccd-js-gen/tsconfig.json @@ -1,10 +1,11 @@ { "extends": "../../tsconfig-base.json", - "include": ["src/**/*", "package.json"], + "include": ["src/**/*", "test/**/*", "package.json", "jest.config.ts"], "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", "outDir": "./lib", "lib": ["ES2020", "dom"], // "dom" is only added to get the typings for the global variable `WebAssembly`. - "target": "ES2020", "resolveJsonModule": true } } diff --git a/packages/common/README.md b/packages/common/README.md deleted file mode 100644 index f6f699409..000000000 --- a/packages/common/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Concordium JS SDKs Common Functionality - -This package is the shared library for the NodeJS and Web SDK's. - -Please see the -[documentation](https://developer.concordium.software/concordium-node-sdk-js/index.html) -for more information diff --git a/packages/common/jest.config.js b/packages/common/jest.config.js deleted file mode 100644 index 40810b105..000000000 --- a/packages/common/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - moduleFileExtensions: ['js', 'ts', 'json'], - moduleDirectories: ['node_modules'], - globals: { - 'ts-jest': { - tsconfig: 'tsconfig.json', - }, - }, -}; diff --git a/packages/common/package.json b/packages/common/package.json deleted file mode 100644 index 8a820a38b..000000000 --- a/packages/common/package.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "@concordium/common-sdk", - "version": "9.5.1", - "license": "Apache-2.0", - "engines": { - "node": ">=14.16.0" - }, - "repository": { - "type": "git", - "url": "https://github.com/Concordium/concordium-node-sdk-js", - "directory": "packages/common" - }, - "main": "lib/index.js", - "types": "lib/index.d.ts", - "files": [ - "/grpc/**/*", - "/lib/**/*" - ], - "devDependencies": { - "@protobuf-ts/plugin": "2.8.1", - "@types/big.js": "^6.2.0", - "@types/bs58check": "^2.1.0", - "@types/jest": "^26.0.23", - "@types/json-bigint": "^1.0.1", - "@types/uuid": "^8.3.4", - "@typescript-eslint/eslint-plugin": "^4.28.1", - "@typescript-eslint/parser": "^4.28.1", - "babel-jest": "^27.0.6", - "eslint": "^7.29.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-prettier": "^3.4.0", - "grpc-tools": "^1.11.2", - "grpc_tools_node_protoc_ts": "5.3.0", - "husky": "^4.2.5", - "jest": "^27.0.6", - "lint-staged": "^12.0.2", - "prettier": "^2.3.2", - "ts-jest": "^27.0.3", - "typescript": "^4.3.5" - }, - "prettier": { - "singleQuote": true, - "tabWidth": 4 - }, - "scripts": { - "generate-ts-v2": "yarn run grpc_tools_node_protoc --plugin=protoc-gen-ts=../../node_modules/@protobuf-ts/plugin/bin/protoc-gen-ts --ts_opt 'optimize_code_size,output_legacy_commonjs,output_javascript' --ts_out=grpc -I ../../deps/concordium-base/concordium-grpc-api ../../deps/concordium-base/concordium-grpc-api/v2/concordium/*.proto", - "generate": "([ -e \"../../deps/concordium-base/concordium-grpc-api\" ] && yarn generate-ts-v2) || echo 'Please checkout submodules before building'", - "lint": "eslint . --cache --ext .ts,.tsx --max-warnings 0", - "lint-fix": "yarn --silent lint --fix; exit 0", - "test": "jest", - "build": "rm -rf grpc; mkdir -p grpc; yarn generate && tsc", - "build-dev": "tsc" - }, - "dependencies": { - "@concordium/rust-bindings": "1.2.0", - "@grpc/grpc-js": "^1.3.4", - "@noble/ed25519": "^1.7.1", - "@protobuf-ts/runtime-rpc": "^2.8.2", - "@scure/bip39": "^1.1.0", - "big.js": "^6.2.0", - "bs58check": "^2.1.2", - "buffer": "^6.0.3", - "cross-fetch": "3.1.5", - "hash.js": "^1.1.7", - "iso-3166-1": "^2.1.1", - "json-bigint": "^1.0.0", - "uuid": "^8.3.2" - } -} diff --git a/packages/common/src/JsonRpcClient.ts b/packages/common/src/JsonRpcClient.ts deleted file mode 100644 index 692f43d2f..000000000 --- a/packages/common/src/JsonRpcClient.ts +++ /dev/null @@ -1,406 +0,0 @@ -import { Buffer } from 'buffer/'; -import { - AccountInfo, - AccountTransaction, - AccountTransactionSignature, - buildInvoker, - ConcordiumBftStatus, - ConsensusStatus, - ConsensusStatusV0, - ConsensusStatusV1, - ContractAddress, - ContractContext, - CryptographicParameters, - InstanceInfo, - InvokeContractResultV1, - NextAccountNonce, - SignedCredentialDeploymentDetails, - TransactionStatus, - TransactionSummary, - Versioned, -} from './types'; -import { AccountAddress } from './types/accountAddress'; -import Provider, { JsonRpcResponse } from './providers/provider'; -import { - serializeAccountTransactionForSubmission, - serializeSignedCredentialDeploymentDetailsForSubmission, -} from './serialization'; -import { CcdAmount } from './types/ccdAmount'; -import { ModuleReference } from './types/moduleReference'; -import { - buildJsonResponseReviver, - intToStringTransformer, - isValidHash, -} from './util'; -import { CredentialRegistrationId } from './types/CredentialRegistrationId'; - -function transformJsonResponse( - jsonString: string, - reviver?: (this: unknown, key: string, value: unknown) => unknown, - transformer?: (json: string) => string -): JsonRpcResponse { - if (transformer) { - const transformedJson = transformer(jsonString); - return JSON.parse(transformedJson, reviver); - } - - return JSON.parse(jsonString, reviver); -} - -/** - * @deprecated This has been deprecated in favor of the {@link ConcordiumGRPCClient} that uses version 2 of the concordium gRPC API - */ -export class JsonRpcClient { - provider: Provider; - - constructor(provider: Provider) { - this.provider = provider; - } - - async getNextAccountNonce( - accountAddress: AccountAddress - ): Promise { - const response = await this.provider.request('getNextAccountNonce', { - address: accountAddress.address, - }); - - const bigIntPropertyKeys: (keyof NextAccountNonce)[] = ['nonce']; - - const res = transformJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - return res.result; - } - - async getTransactionStatus( - transactionHash: string - ): Promise { - const response = await this.provider.request('getTransactionStatus', { - transactionHash: transactionHash, - }); - - // TODO avoid code duplication with nodejs client - const bigIntPropertyKeys: (keyof TransactionSummary)[] = [ - 'cost', - 'energyCost', - 'index', - ]; - - const res = transformJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - return res.result; - } - - /** - * @param serializedTransaction the transaction serialized as a base64-encoded string. - */ - private async sendRawTransaction( - serializedTransaction: string - ): Promise { - const res = await this.provider.request('sendTransaction', { - transaction: serializedTransaction, - }); - return JSON.parse(res).result || false; - } - - async sendAccountTransaction( - accountTransaction: AccountTransaction, - signatures: AccountTransactionSignature - ): Promise { - const serializedAccountTransaction: Buffer = Buffer.from( - serializeAccountTransactionForSubmission( - accountTransaction, - signatures - ) - ); - return this.sendRawTransaction( - serializedAccountTransaction.toString('base64') - ); - } - - async sendCredentialDeployment( - credentialDetails: SignedCredentialDeploymentDetails - ): Promise { - const serializedDetails = - serializeSignedCredentialDeploymentDetailsForSubmission( - credentialDetails - ); - return this.sendRawTransaction(serializedDetails.toString('base64')); - } - - async getConsensusStatus(): Promise { - const response = await this.provider.request('getConsensusStatus'); - type CS = ConsensusStatusV0 & ConsensusStatusV1; - - // TODO Avoid code duplication with nodejs client - const datePropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'blockLastReceivedTime', - 'blockLastArrivedTime', - 'genesisTime', - 'currentEraGenesisTime', - 'lastFinalizedTime', - - // v1 - 'triggerBlockTime', - ]; - const bigIntPropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'epochDuration', - 'slotDuration', - 'bestBlockHeight', - 'lastFinalizedBlockHeight', - 'finalizationCount', - 'blocksVerifiedCount', - 'blocksReceivedCount', - 'protocolVersion', - - // v1 - 'currentTimeoutDuration', - 'currentRound', - 'currentEpoch', - ]; - - const res = transformJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - if (!res.result) { - throw new Error( - 'Nothing was returned when trying to get the consensus status.' - ); - } - - return res.result; - } - - /** - * Retrieve information about a given smart contract instance. - * @param blockHash the block hash to get the smart contact instances at - * @param address the address of the smart contract - * @returns A JSON object with information about the contract instance - */ - async getInstanceInfo( - address: ContractAddress, - blockHash?: string - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } else if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const response = await this.provider.request('getInstanceInfo', { - index: address.index, - subindex: address.subindex, - blockHash, - }); - - const result = JSON.parse(response).result; - - if (!result) { - return undefined; - } - - // TODO: Avoid code duplication with nodejs client - const common = { - amount: new CcdAmount(BigInt(result.amount)), - sourceModule: new ModuleReference(result.sourceModule), - owner: new AccountAddress(result.owner), - methods: result.methods, - name: result.name, - }; - - switch (result.version) { - case 1: - return { - version: 1, - ...common, - }; - case undefined: - case 0: - return { - version: 0, - ...common, - model: Buffer.from(result.model, 'hex'), - }; - default: - throw new Error( - 'InstanceInfo had unsupported version: ' + - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (result as any).version - ); - } - } - - /** - * Retrieves the account info for the given account. If the provided block - * hash is in a block prior to the finalization of the account, then the account - * information will not be available. - * A credential registration id can also be provided, instead of an address. In this case - * the node will return the account info of the account, which the corresponding credential - * is (or was) deployed to. - * @param accountAddress base58 account address (or a credential registration id) to get the account info for - * @param blockHash the block hash to get the account info at - * @returns the account info for the provided account address, undefined is the account does not exist - */ - async getAccountInfo( - accountAddress: string | AccountAddress | CredentialRegistrationId, - blockHash?: string - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } else if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - let address: string; - if (typeof accountAddress === 'string') { - address = accountAddress; - } else if ('address' in accountAddress) { - address = accountAddress.address; - } else if ('credId' in accountAddress) { - address = accountAddress.credId; - } else { - throw new Error('Invalid accountAddress input'); - } - - const response = await this.provider.request('getAccountInfo', { - blockHash, - address, - }); - - const datePropertyKeys = ['timestamp', 'effectiveTime']; - const bigIntPropertyKeys = [ - 'accountAmount', - 'accountNonce', - 'accountIndex', - 'startIndex', - 'total', - 'amount', - 'stakedAmount', - 'bakerId', - 'newStake', - 'epoch', - ]; - const res = transformJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - return res.result; - } - - /** - * Retrieves the global cryptographic parameters on the blockchain at - * the provided block. - * @param blockHash the block to get the cryptographic parameters at - * @returns the global cryptographic parameters at the given block, or undefined it the block does not exist. - */ - async getCryptographicParameters( - blockHash?: string - ): Promise | undefined> { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } else if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const response = await this.provider.request( - 'getCryptographicParameters', - { - blockHash, - } - ); - - const res = - transformJsonResponse>(response); - - return res.result; - } - - /** - * Retrieves the source of the given module at - * the provided block. - * @param moduleReference the module's reference, which is the hex encoded hash of the source. - * @param blockHash the block to get the cryptographic parameters at - * @returns the source of the module as raw bytes. - */ - async getModuleSource( - moduleReference: ModuleReference, - blockHash?: string - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } else if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const response = await this.provider.request('getModuleSource', { - moduleReference: moduleReference.moduleRef, - blockHash, - }); - - return Buffer.from(JSON.parse(response).result, 'base64'); - } - - /** - * Invokes a smart contract. - * @param context the collection of details used to invoke the contract. Must include the address of the contract and the method invoked. - * @param blockHash the block hash at which the contract should be invoked at. The contract is invoked in the state at the end of this block. - * @returns If the node was able to invoke, then a object describing the outcome is returned. - * The outcome is determined by the `tag` field, which is either `success` or `failure`. - * The `usedEnergy` field will always be present, and is the amount of NRG was used during the execution. - * If the tag is `success`, then an `events` field is present, and it contains the events that would have been generated. - * If invoking a V1 contract and it produces a return value, it will be present in the `returnValue` field. - * If the tag is `failure`, then a `reason` field is present, and it contains the reason the update would have been rejected. - * If either the block does not exist, or then node fails to parse of any of the inputs, then undefined is returned. - */ - async invokeContract( - contractContext: ContractContext, - blockHash?: string - ): Promise { - if (!blockHash) { - const consensusStatus = await this.getConsensusStatus(); - blockHash = consensusStatus.lastFinalizedBlock; - } else if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const invoker = buildInvoker(contractContext.invoker); - - const context = { - ...contractContext, - invoker, - amount: - contractContext.amount && contractContext.amount.microCcdAmount, - parameter: - contractContext.parameter && - contractContext.parameter.toString('hex'), - }; - - const response = await this.provider.request('invokeContract', { - blockHash, - context, - }); - - const bigIntPropertyKeys = ['usedEnergy', 'index', 'subindex']; - const res = transformJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - return res.result; - } -} diff --git a/packages/common/src/accountHelpers.ts b/packages/common/src/accountHelpers.ts deleted file mode 100644 index 6ca4f3461..000000000 --- a/packages/common/src/accountHelpers.ts +++ /dev/null @@ -1,47 +0,0 @@ -import * as wasm from '@concordium/rust-bindings'; -import { AccountAddress } from './types/accountAddress'; -import { - ReduceStakePendingChange, - RemovalPendingChange, - AccountInfo, - AccountInfoBaker, - AccountInfoDelegator, - GenerateBakerKeysOutput, -} from './types'; - -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoDelegator}, i.e. the account is a delegator */ -export const isDelegatorAccount = ( - ai: AccountInfo -): ai is AccountInfoDelegator => - (ai as AccountInfoDelegator).accountDelegation !== undefined; - -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBaker}, i.e. the account is a baker. */ -export const isBakerAccount = (ai: AccountInfo): ai is AccountInfoBaker => - (ai as AccountInfoBaker).accountBaker !== undefined; - -/** Whether the pending change given is of type {@link ReduceStakePendingChange} */ -export const isReduceStakePendingChange = ( - spc: ReduceStakePendingChange | RemovalPendingChange -): spc is ReduceStakePendingChange => - (spc as ReduceStakePendingChange).newStake !== undefined; - -/** Whether the pending change given is of type {@link RemovalPendingChange} */ -export const isRemovalPendingChange = ( - spc: ReduceStakePendingChange | RemovalPendingChange -): spc is RemovalPendingChange => !isReduceStakePendingChange(spc); - -/** - * Generates random baker keys for the specified account, that can be used with the configureBaker transaction - * @param account the address of the account that the keys should be added to. - * @returns an object containing the public baker keys, their associated proofs and their associated private keys. - */ -export function generateBakerKeys( - account: AccountAddress -): GenerateBakerKeysOutput { - const rawKeys = wasm.generateBakerKeys(account.address); - try { - return JSON.parse(rawKeys); - } catch (e) { - throw new Error(rawKeys); - } -} diff --git a/packages/common/src/alias.ts b/packages/common/src/alias.ts deleted file mode 100644 index d504d90d0..000000000 --- a/packages/common/src/alias.ts +++ /dev/null @@ -1,57 +0,0 @@ -import * as bs58check from 'bs58check'; -import { Buffer } from 'buffer/'; -import { AccountAddress } from './types/accountAddress'; - -const addressByteLength = 32; -const aliasBytesLength = 3; -const commonBytesLength = addressByteLength - aliasBytesLength; -const maxCount = 16777215; // 2^(8 * 3) - 1 - -/** - * Given two accountAddresses, return whether they are aliases. - * @param address an AccountAddress - * @param alias another AccountAddress - * @returns boolean that indicates whether address and alias are aliases - */ -export function isAlias( - address: AccountAddress, - alias: AccountAddress -): boolean { - return ( - 0 === - address.decodedAddress.compare( - alias.decodedAddress, - 0, - commonBytesLength, - 0, - commonBytesLength - ) - ); -} - -/** - * Given an AccountAddress and a counter, returns an alias for the address. - * @param address the account address for which the function should get an alias for - * @param counter number s.t. 0 <= counter < 2^24, decides which alias is returned. - * If a counter outside this scope is given, then the function will throw an exception - * @returns an AccountAddress, which is an alias to the given address - */ -export function getAlias( - address: AccountAddress, - counter: number -): AccountAddress { - if (counter < 0 || counter > maxCount) { - throw new Error( - `An invalid counter value was given: ${counter}. The value has to satisfy that 0 <= counter < 2^24` - ); - } - const commonBytes = address.decodedAddress.slice(0, commonBytesLength); - const aliasBytes = Buffer.alloc(aliasBytesLength); - aliasBytes.writeUIntBE(counter, 0, aliasBytesLength); - const prefixedWithVersion = Buffer.concat([ - Buffer.of(1), - commonBytes, - aliasBytes, - ]); - return new AccountAddress(bs58check.encode(prefixedWithVersion)); -} diff --git a/packages/common/src/blockSummaryHelpers.ts b/packages/common/src/blockSummaryHelpers.ts deleted file mode 100644 index ebebe3e3c..000000000 --- a/packages/common/src/blockSummaryHelpers.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { - BlockSummary, - BlockSummaryV0, - BlockSummaryV1, - BlockSummaryV2, - UpdateQueues, - UpdateQueuesV0, - UpdateQueuesV1, - UpdateQueuesV2, - Updates, - UpdatesV0, - UpdatesV1, - UpdatesV2, -} from './types'; - -/** Whether {@link UpdateQueues} parameter given is of type {@link UpdateQueuesV0} */ -export const isUpdateQueuesV0 = (uq: UpdateQueues): uq is UpdateQueuesV0 => - (uq as UpdateQueuesV0).bakerStakeThreshold !== undefined; - -/** Whether {@link UpdateQueues} parameter given is of type {@link UpdateQueuesV1} */ -export const isUpdateQueuesV1 = (uq: UpdateQueues): uq is UpdateQueuesV1 => - (uq as UpdateQueuesV1).timeParameters !== undefined; - -/** Whether {@link UpdateQueues} parameter given is of type {@link UpdateQueuesV2} */ -export const isUpdateQueuesV2 = (uq: UpdateQueues): uq is UpdateQueuesV2 => - (uq as UpdateQueuesV2).consensus2TimingParameters !== undefined; - -export const isUpdatesV0 = (u: Updates): u is UpdatesV0 => - isUpdateQueuesV0(u.updateQueues); - -export const isUpdatesV1 = (u: Updates): u is UpdatesV1 => - isUpdateQueuesV1(u.updateQueues); - -export const isUpdatesV2 = (u: Updates): u is UpdatesV2 => - isUpdateQueuesV2(u.updateQueues); - -export const isBlockSummaryV0 = (bs: BlockSummary): bs is BlockSummaryV0 => - bs.protocolVersion === undefined || bs.protocolVersion <= 3n; - -export const isBlockSummaryV1 = (bs: BlockSummary): bs is BlockSummaryV1 => - bs.protocolVersion !== undefined && - bs.protocolVersion > 3n && - bs.protocolVersion <= 5n; - -export const isBlockSummaryV2 = (bs: BlockSummary): bs is BlockSummaryV2 => - bs.protocolVersion !== undefined && bs.protocolVersion > 5n; diff --git a/packages/common/src/cis2/index.ts b/packages/common/src/cis2/index.ts deleted file mode 100644 index d9d69d176..000000000 --- a/packages/common/src/cis2/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type { CIS2 } from './util'; -export { tokenAddressFromBase58, tokenAddressToBase58 } from './util'; -export * from './CIS2Contract'; diff --git a/packages/common/src/cis4/index.ts b/packages/common/src/cis4/index.ts deleted file mode 100644 index 9a37bc789..000000000 --- a/packages/common/src/cis4/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { CIS4, Web3IdSigner } from './util'; -export * from './CIS4Contract'; diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts deleted file mode 100644 index a57ac1bca..000000000 --- a/packages/common/src/constants.ts +++ /dev/null @@ -1 +0,0 @@ -export const DEFAULT_INVOKE_ENERGY = 1000000n; diff --git a/packages/common/src/deserialization.ts b/packages/common/src/deserialization.ts deleted file mode 100644 index 369fe8e4b..000000000 --- a/packages/common/src/deserialization.ts +++ /dev/null @@ -1,335 +0,0 @@ -import * as wasm from '@concordium/rust-bindings'; -import { Buffer } from 'buffer/'; -import { getAccountTransactionHandler } from './accountTransactions'; -import { - AccountTransaction, - AccountTransactionHeader, - AccountTransactionSignature, - BlockItemKind, - isAccountTransactionType, - SmartContractTypeValues, - TypedCredentialDeployment, -} from './types'; -import { AccountAddress } from './types/accountAddress'; -import { TransactionExpiry } from './types/transactionExpiry'; -import { PassThrough, Readable } from 'stream'; - -/** - * Reads an unsigned 8-bit integer from the given {@link Readable}. - * - * @param source input stream - * @returns number from 0 to 255 - */ -export function deserializeUint8(source: Readable): number { - return source.read(1).readUInt8(0); -} - -/** - * Given a contract's raw state, its name and its schema, return the state as a JSON object. - * The return type is any, and the actual type should be determined by using the schema. - */ -export function deserializeContractState( - contractName: string, - schema: Buffer, - state: Buffer, - verboseErrorMessage = false - // eslint-disable-next-line @typescript-eslint/no-explicit-any -): any { - const serializedState = wasm.deserializeState( - contractName, - state.toString('hex'), - schema.toString('hex'), - verboseErrorMessage - ); - try { - return JSON.parse(serializedState); - } catch (e) { - throw new Error( - 'unable to deserialize state, due to: ' + serializedState - ); // In this case serializedState is the error message from the rust module - } -} - -function deserializeMap( - serialized: Readable, - decodeSize: (size: Readable) => number, - decodeKey: (k: Readable) => K, - decodeValue: (t: Readable) => T -): Record { - const size = decodeSize(serialized); - const result = {} as Record; - for (let i = 0; i < size; i += 1) { - const key = decodeKey(serialized); - const value = decodeValue(serialized); - result[key] = value; - } - return result; -} - -function deserializeAccountTransactionSignature( - signatures: Readable -): AccountTransactionSignature { - const decodeSignature = (serialized: Readable) => { - const length = serialized.read(2).readUInt16BE(0); - return serialized.read(length).toString('hex'); - }; - const decodeCredentialSignatures = (serialized: Readable) => - deserializeMap( - serialized, - deserializeUint8, - deserializeUint8, - decodeSignature - ); - return deserializeMap( - signatures, - deserializeUint8, - deserializeUint8, - decodeCredentialSignatures - ); -} - -function deserializeTransactionHeader( - serializedHeader: Readable -): AccountTransactionHeader { - const sender = AccountAddress.fromBytes( - Buffer.from(serializedHeader.read(32)) - ); - const nonce = serializedHeader.read(8).readBigUInt64BE(0); - // TODO: extract payloadSize and energyAmount? - // energyAmount - serializedHeader.read(8).readBigUInt64BE(0); - // payloadSize - serializedHeader.read(4).readUInt32BE(0); - const expiry = TransactionExpiry.fromEpochSeconds( - serializedHeader.read(8).readBigUInt64BE(0), - true - ); - return { - sender, - nonce, - expiry, - }; -} - -function deserializeAccountTransaction(serializedTransaction: Readable): { - accountTransaction: AccountTransaction; - signatures: AccountTransactionSignature; -} { - const signatures = deserializeAccountTransactionSignature( - serializedTransaction - ); - - const header = deserializeTransactionHeader(serializedTransaction); - - const transactionType = deserializeUint8(serializedTransaction); - if (!isAccountTransactionType(transactionType)) { - throw new Error( - 'TransactionType is not a valid value: ' + transactionType - ); - } - const accountTransactionHandler = - getAccountTransactionHandler(transactionType); - const payload = accountTransactionHandler.deserialize( - serializedTransaction - ); - - return { - accountTransaction: { - type: transactionType, - payload, - header, - }, - signatures, - }; -} - -function deserializeCredentialDeployment(serializedDeployment: Readable) { - const raw = wasm.deserializeCredentialDeployment( - serializedDeployment.read().toString('hex') - ); - try { - const parsed = JSON.parse(raw); - return { - credential: parsed.credential, - expiry: parsed.messageExpiry, - }; - } catch { - // If the return value is not a proper JSON, it should be an error message. - throw new Error(raw); - } -} - -export type BlockItem = - | { - kind: BlockItemKind.AccountTransactionKind; - transaction: { - accountTransaction: AccountTransaction; - signatures: AccountTransactionSignature; - }; - } - | { - kind: BlockItemKind.CredentialDeploymentKind; - transaction: { - credential: TypedCredentialDeployment; - expiry: number; - }; - }; - -/** - * Deserializes a transaction, from the binary format used to send it to the node, back into an js object. - * @param serializedTransaction A buffer containing the binary transaction. It is expected to start with the version and blockItemKind. - * @returns An object specifiying the blockItemKind that the transaction has. The object also contains the actual transaction under the transaction field. - **/ -export function deserializeTransaction( - serializedTransaction: Buffer -): BlockItem { - const bufferStream = new PassThrough(); - bufferStream.end(serializedTransaction); - - const version = deserializeUint8(bufferStream); - if (version !== 0) { - throw new Error( - 'Supplied version ' + - version + - ' is not valid. Only transactions with version 0 format are supported' - ); - } - const blockItemKind = deserializeUint8(bufferStream); - switch (blockItemKind) { - case BlockItemKind.AccountTransactionKind: - return { - kind: BlockItemKind.AccountTransactionKind, - transaction: deserializeAccountTransaction(bufferStream), - }; - case BlockItemKind.CredentialDeploymentKind: - return { - kind: BlockItemKind.CredentialDeploymentKind, - transaction: deserializeCredentialDeployment(bufferStream), - }; - case BlockItemKind.UpdateInstructionKind: - throw new Error( - 'deserialization of UpdateInstructions is not supported' - ); - default: - throw new Error('Invalid blockItemKind'); - } -} - -/** - * Deserializes a receive functions's return value from a sequence of bytes into a json object. - * @param returnValueBytes A buffer containing the return value as raw bytes. - * @param moduleSchema The raw module schema as a buffer. - * @param contractName The name of the contract where the receive function is located. - * @param functionName The name of the receive function which return value you want to deserialize. - * @param schemaVersion The schema version as a number. This parameter is optional, if you provide a serialized versioned schema this argument won't be needed. - * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. - */ -export function deserializeReceiveReturnValue( - returnValueBytes: Buffer, - moduleSchema: Buffer, - contractName: string, - functionName: string, - schemaVersion?: number, - verboseErrorMessage = false - // eslint-disable-next-line @typescript-eslint/no-explicit-any -): any { - const deserializedReturnValue = wasm.deserializeReceiveReturnValue( - returnValueBytes.toString('hex'), - moduleSchema.toString('hex'), - contractName, - functionName, - schemaVersion, - verboseErrorMessage - ); - try { - return JSON.parse(deserializedReturnValue); - } catch (e) { - throw new Error( - 'unable to deserialize the return value, due to: ' + - deserializedReturnValue - ); // In this case deserializedReturnValue is the error message from the rust module - } -} - -/** - * Deserializes a receive function's error from a sequence of bytes into a json object. - * @param errorBytes A buffer containing the error as raw bytes. - * @param moduleSchema The raw module schema as a buffer. - * @param contractName The name of the contract where the receive function is located. - * @param functionName The name of the receive function which error you want to deserialize. - * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. - */ -export function deserializeReceiveError( - errorBytes: Buffer, - moduleSchema: Buffer, - contractName: string, - functionName: string, - verboseErrorMessage = false - // eslint-disable-next-line @typescript-eslint/no-explicit-any -): any { - const deserializedError = wasm.deserializeReceiveError( - errorBytes.toString('hex'), - moduleSchema.toString('hex'), - contractName, - functionName, - verboseErrorMessage - ); - try { - return JSON.parse(deserializedError); - } catch (e) { - throw new Error( - 'unable to deserialize the error value, due to: ' + - deserializedError - ); // In this case deserializedError is the error message from the rust module - } -} - -/** - * Deserializes an init function's error from a sequence of bytes into a json object. - * @param errorBytes A buffer containing the error as raw bytes. - * @param moduleSchema The raw module schema as a buffer. - * @param contractName The name of the init function which error you want to deserialize. - * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. - */ -export function deserializeInitError( - errorBytes: Buffer, - moduleSchema: Buffer, - contractName: string, - verboseErrorMessage = false - // eslint-disable-next-line @typescript-eslint/no-explicit-any -): any { - const deserializedError = wasm.deserializeInitError( - errorBytes.toString('hex'), - moduleSchema.toString('hex'), - contractName, - verboseErrorMessage - ); - try { - return JSON.parse(deserializedError); - } catch (e) { - throw new Error( - 'unable to deserialize the error value, due to: ' + - deserializedError - ); // In this case deserializedError is the error message from the rust module - } -} - -/** - * Given a binary value for a smart contract type, and the raw schema for that type, deserialize the value into the JSON representation. - * @param value the value that should be deserialized. - * @param rawSchema the schema for the type that the given value should be deserialized as - * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. - * @returns the deserialized value - */ -export function deserializeTypeValue( - value: Buffer, - rawSchema: Buffer, - verboseErrorMessage = false -): SmartContractTypeValues { - const deserializedValue = wasm.deserializeTypeValue( - value.toString('hex'), - rawSchema.toString('hex'), - verboseErrorMessage - ); - return JSON.parse(deserializedValue); -} diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts deleted file mode 100644 index 502901489..000000000 --- a/packages/common/src/index.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { sha256 } from './hash'; -export * from './types'; -export { - getAccountTransactionHash, - getAccountTransactionSignDigest, - getCredentialDeploymentSignDigest, - getCredentialDeploymentTransactionHash, - getCredentialForExistingAccountSignDigest, - serializeInitContractParameters, - serializeUpdateContractParameters, - serializeAccountTransactionForSubmission, - serializeCredentialDeploymentTransactionForSubmission, - getSignedCredentialDeploymentTransactionHash, - serializeTypeValue, - serializeAccountTransactionPayload, - serializeCredentialDeploymentPayload, -} from './serialization'; -export { encodeHexString } from './serializationHelpers'; -export { sha256 }; -export { CredentialRegistrationId } from './types/CredentialRegistrationId'; -export { AccountAddress } from './types/accountAddress'; -export { CcdAmount } from './types/ccdAmount'; -export { TransactionExpiry } from './types/transactionExpiry'; -export { DataBlob } from './types/DataBlob'; -export { ModuleReference } from './types/moduleReference'; -export * from './types/VersionedModuleSource'; -export { - VerifiablePresentation, - reviveDateFromTimeStampAttribute, - replaceDateWithTimeStampAttribute, -} from './types/VerifiablePresentation'; -export * from './credentialDeploymentTransactions'; -export { isAlias, getAlias } from './alias'; -export { - deserializeContractState, - deserializeTransaction, - deserializeReceiveReturnValue, - deserializeReceiveError, - deserializeInitError, - deserializeTypeValue, -} from './deserialization'; - -export { - StatementTypes, - StatementBuilder, - MIN_DATE, - MAX_DATE, - EU_MEMBERS, -} from './commonProofTypes'; -export * from './idProofTypes'; -export * from './idProofs'; -export * from './web3ProofTypes'; -export * from './web3Proofs'; - -export * from './signHelpers'; -export * from './versionedTypeHelpers'; -export * from './accountHelpers'; -export * from './blockSummaryHelpers'; -export * from './rewardStatusHelpers'; -export * from './HdWallet'; -export * from './schemaHelpers'; -export * from './web3IdHelpers'; - -export { getContractName } from './contractHelpers'; -export { isHex, streamToList, wasmToSchema, unwrap } from './util'; - -export { HttpProvider } from './providers/httpProvider'; -export { JsonRpcClient } from './JsonRpcClient'; -export * from './identity'; -export { ConcordiumGRPCClient } from './GRPCClient'; - -export { getAccountTransactionHandler } from './accountTransactions'; -export * from './energyCost'; - -export * from './uleb128'; -export * from './cis2'; -export * from './cis0'; -export * from './cis4'; -export * from './GenericContract'; diff --git a/packages/common/src/providers/httpProvider.ts b/packages/common/src/providers/httpProvider.ts deleted file mode 100644 index 7abb88b5c..000000000 --- a/packages/common/src/providers/httpProvider.ts +++ /dev/null @@ -1,71 +0,0 @@ -import Provider, { JsonRpcRequest } from './provider'; -import fetch from 'cross-fetch'; -import JSONBig from 'json-bigint'; -import { v4 as uuidv4 } from 'uuid'; - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export class HttpProvider implements Provider { - request: JsonRpcRequest; - cookie?: string; - - /** - * @param internalFetch Fetch function that performs the request. Defaults to using the cross-fetch package. - */ - constructor( - url: string, - internalFetch: typeof fetch = fetch, - onSetCookie?: (cookie: string) => void, - initialCookie?: string, - autoUpdateCookie = true - ) { - this.cookie = initialCookie; - this.request = async function (method, params?) { - const request = { - method: method, - params: params, - id: uuidv4(), - jsonrpc: '2.0', - }; - - const options = { - method: 'POST', - // Use JSONBig in order ensure bigints are automatically parsed (as numbers) - body: JSONBig.stringify(request), - headers: { - 'Content-Type': 'application/json', - ...(this.cookie && { cookie: this.cookie }), - }, - }; - - const res = await internalFetch(url, options); - if (res.status >= 400) { - const json = await res.json(); - if (json.error) { - throw new Error( - `${json.error.code}: ${json.error.message} (id: ${json.id})` - ); - } else { - throw new Error( - `${res.status}: ${res.statusText} (id: ${json.id})` - ); - } - } - - const setCookieValue = res.headers.get('set-cookie'); - if (setCookieValue) { - onSetCookie?.(setCookieValue); - if (autoUpdateCookie) { - this.updateCookie(setCookieValue); - } - } - - return res.text(); - }; - } - - updateCookie(newCookie: string): void { - this.cookie = newCookie; - } -} diff --git a/packages/common/src/providers/provider.ts b/packages/common/src/providers/provider.ts deleted file mode 100644 index 67cb1e2b4..000000000 --- a/packages/common/src/providers/provider.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { Invoker } from '../types'; - -/* eslint-disable @typescript-eslint/no-explicit-any */ -interface JsonRpcResponseBase { - jsonrpc: '2.0'; - id: string | null; -} - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export interface JsonRpcResponseError extends JsonRpcResponseBase { - error: { - code: number; - message: string; - data?: any; - }; - result?: never; -} - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export interface JsonRpcResponseSuccess extends JsonRpcResponseBase { - error?: never; - result: Result; -} - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export type JsonRpcResponse = - | JsonRpcResponseError - | JsonRpcResponseSuccess; - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export type JsonRpcRequest = ( - ...args: - | ['getNextAccountNonce', { address: string }] - | ['getTransactionStatus', { transactionHash: string }] - | ['getConsensusStatus'] - | [ - 'getInstanceInfo', - { blockHash: string; index: bigint; subindex: bigint } - ] - | ['sendTransaction', { transaction: string }] - | ['getAccountInfo', { address: string; blockHash: string }] - | ['getCryptographicParameters', { blockHash: string }] - | ['getModuleSource', { blockHash: string; moduleReference: string }] - | [ - 'invokeContract', - { - blockHash: string; - context: { - contract: { index: bigint; subindex: bigint }; - method: string; - amount?: bigint; - invoker: Invoker; - energy?: bigint; - parameter?: string; - }; - } - ] -) => Promise; - -/** - * @deprecated This is only used by the JSON-RPC client, which has been deprecated - */ -export default interface Provider { - request: JsonRpcRequest; -} diff --git a/packages/common/src/rewardStatusHelpers.ts b/packages/common/src/rewardStatusHelpers.ts deleted file mode 100644 index 30697c623..000000000 --- a/packages/common/src/rewardStatusHelpers.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { RewardStatus, RewardStatusV1 } from './types'; - -export function isRewardStatusV1(rs: RewardStatus): rs is RewardStatusV1 { - return rs.protocolVersion !== undefined && rs.protocolVersion > 3n; -} diff --git a/packages/common/src/schemaHelpers.ts b/packages/common/src/schemaHelpers.ts deleted file mode 100644 index 27394aaa8..000000000 --- a/packages/common/src/schemaHelpers.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Buffer } from 'buffer/'; -import { SchemaVersion } from './types'; -import * as wasm from '@concordium/rust-bindings'; - -/** - * @param moduleSchema buffer for the schema of a module that contains the contract - * @param contractName name of the contract that the init contract transaction will initialize - * @param schemaVersion the version of the schema provided - * @returns buffer containing the schema for of init contract parameters - */ -export function getInitContractParameterSchema( - moduleSchema: Buffer, - contractName: string, - schemaVersion?: SchemaVersion -): Buffer { - const parameterSchema = wasm.getInitContractParameterSchema( - moduleSchema.toString('hex'), - contractName, - schemaVersion - ); - return Buffer.from(parameterSchema, 'hex'); -} - -/** - * @param moduleSchema buffer for the schema of a module that contains the contract - * @param contractName name of the contract that the update contract transaction will update - * @param receiveFunctionName name of function that the update contract transaction will invoke - * @param schemaVersion the version of the schema provided - * @returns buffer containing the schema for of update contract parameters - */ -export function getUpdateContractParameterSchema( - moduleSchema: Buffer, - contractName: string, - receiveFunctionName: string, - schemaVersion?: SchemaVersion -): Buffer { - const parameterSchema = wasm.getReceiveContractParameterSchema( - moduleSchema.toString('hex'), - contractName, - receiveFunctionName, - schemaVersion - ); - return Buffer.from(parameterSchema, 'hex'); -} - -/** - * @param rawSchema the schema for the type - * @returns JSON template of the schema - */ -export function displayTypeSchemaTemplate(rawSchema: Buffer): string { - return wasm.displayTypeSchemaTemplate(rawSchema.toString('hex')); -} diff --git a/packages/common/src/schemaTypes.ts b/packages/common/src/schemaTypes.ts deleted file mode 100644 index a936258f5..000000000 --- a/packages/common/src/schemaTypes.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * The JSON schema representation of a rust Option - * - * @template T - The type to represent as optional - */ -export type OptionJson = { None: [] } | { Some: [T] }; - -/** - * Takes a value and wraps it in a {@link OptionJson}. - * - * @template T - The type to represent as optional - * - * @param {T} value - The value to wrap. - * - * @returns {OptionJson} the wrapped value - */ -export function toOptionJson(value: T | undefined): OptionJson { - if (value === undefined) { - return { None: [] }; - } - - return { Some: [value] }; -} diff --git a/packages/common/src/types/CredentialRegistrationId.ts b/packages/common/src/types/CredentialRegistrationId.ts deleted file mode 100644 index 8daf30d1e..000000000 --- a/packages/common/src/types/CredentialRegistrationId.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { isHex } from '../util'; - -/** - * Representation of a credential registration id, which enforces that it: - * - Is a valid Hex string - * - Has length exactly 96, because a credId is 48 bytes. - * - Checks the first bit is 1, which indicates that the value represents a compressed BLS12-381 curve point. - */ -export class CredentialRegistrationId { - credId: string; - - constructor(credId: string) { - if (credId.length !== 96) { - throw new Error( - 'The provided credId ' + - credId + - ' is invalid as its length was not 96' - ); - } - if (!isHex(credId)) { - throw new Error( - 'The provided credId ' + - credId + - ' does not represent a hexidecimal value' - ); - } - // Check that the first bit is 1 - if ((parseInt(credId.substring(0, 2), 16) & 0b10000000) === 0) { - throw new Error( - 'The provided credId ' + - credId + - 'does not represent a compressed BLS12-381 point' - ); - } - - this.credId = credId; - } - - toJSON(): string { - return this.credId; - } -} diff --git a/packages/common/src/types/DataBlob.ts b/packages/common/src/types/DataBlob.ts deleted file mode 100644 index 888db2070..000000000 --- a/packages/common/src/types/DataBlob.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Buffer } from 'buffer/'; -import { packBufferWithWord16Length } from '../serializationHelpers'; - -/** - * Representation of a transfer's memo or a registerData transaction's data, which enforces that: - * - the byte length is <= 256 - */ -export class DataBlob { - data: Buffer; - - constructor(data: Buffer) { - if (data.length > 256) { - throw new Error("A data blob's size cannot exceed 256 bytes"); - } - this.data = data; - } - - toJSON(): string { - return packBufferWithWord16Length(this.data).toString('hex'); - } -} diff --git a/packages/common/src/types/accountAddress.ts b/packages/common/src/types/accountAddress.ts deleted file mode 100644 index e03bffcae..000000000 --- a/packages/common/src/types/accountAddress.ts +++ /dev/null @@ -1,41 +0,0 @@ -import bs58check from 'bs58check'; -import { Buffer } from 'buffer/'; - -/** - * Representation of an account address, which enforces that it: - * - Hash length exactly 50 - * - Is a valid base58 string - */ -export class AccountAddress { - address: string; - - decodedAddress: Buffer; - - constructor(address: string) { - if (address.length !== 50) { - throw new Error( - 'The provided address ' + - address + - ' is invalid as its length was not 50' - ); - } - try { - this.decodedAddress = Buffer.from( - bs58check.decode(address).subarray(1) - ); - this.address = address; - } catch (error) { - throw error; - } - } - - static fromBytes(bytes: Buffer): AccountAddress { - return new AccountAddress( - bs58check.encode(Buffer.concat([Buffer.of(1), bytes])) - ); - } - - toJSON(): string { - return this.address; - } -} diff --git a/packages/common/src/types/ccdAmount.ts b/packages/common/src/types/ccdAmount.ts deleted file mode 100644 index e92772e74..000000000 --- a/packages/common/src/types/ccdAmount.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Big, BigSource } from 'big.js'; - -const MICRO_CCD_PER_CCD = 1000000; - -/** - * Representation of a CCD amount. - * The base unit of CCD is micro CCD, which is the representation - * used on chain. - */ -export class CcdAmount { - microCcdAmount: bigint; - - /** - * Constructs a CcdAmount and checks that it is valid. It accepts a number, string, big, or bigint as parameter. - * It can accept a string as parameter with either a comma or a dot as the decimal separator. - * - * @param microCcdAmount The amount of micro CCD as a number, string, big, or bigint. - * @throws If an invalid micro CCD amount is passed, i.e. any value which is not an unsigned 64-bit integer - */ - constructor(microCcdAmount: bigint | BigSource) { - // If the input is a "BigSource" assert that the number is whole - if (typeof microCcdAmount !== 'bigint') { - microCcdAmount = newBig(microCcdAmount); - - if (!microCcdAmount.mod(Big(1)).eq(Big(0))) { - throw Error( - 'Can not create CcdAmount from a non-whole number!' - ); - } - - microCcdAmount = BigInt(microCcdAmount.toFixed()); - } - - if (microCcdAmount < 0n) { - throw new Error( - 'A micro CCD amount must be a non-negative integer but was: ' + - microCcdAmount - ); - } else if (microCcdAmount > 18446744073709551615n) { - throw new Error( - 'A micro CCD amount must be representable as an unsigned 64 bit integer but was: ' + - microCcdAmount - ); - } - - this.microCcdAmount = microCcdAmount; - } - - /** - * Returns the amount of micro CCD as a Big. - * - * @returns The amount of micro CCD as a Big - */ - toMicroCcd(): Big { - return Big(this.microCcdAmount.toString()); - } - - /** - * Returns the amount of CCD as a Big. - * - * @returns The amount of CCD as a Big - */ - toCcd(): Big { - return this.toMicroCcd().div(Big(MICRO_CCD_PER_CCD)); - } - - /** - * Creates a CcdAmount from a number, string, big, or bigint. - * - * @param ccd The amount of micro CCD as a number, string, big or bigint. - * @returns The CcdAmount object derived from the ccd input parameter - * @throws If a number is passed with several decimal seperators - * @throws If a negative amount of micro CCD is passed - * @throws If the micro CCD passed is greater than what can be contained in a 64-bit integer - */ - static fromCcd(ccd: BigSource | bigint): CcdAmount { - if (typeof ccd === 'bigint') { - ccd = ccd.toString(); - } - - const microCcd = newBig(ccd).mul(Big(MICRO_CCD_PER_CCD)); - return new CcdAmount(microCcd); - } - - /** - * Converts an amount of CCD to micro CCD and asserts that the amount is a valid amount of CCD. - * - * @param ccd The amount of CCD as a number, string, big or bigint. - * @returns The amount of micro CCD as a Big - * @throws If a number is passed with several decimal seperators - * @throws If a negative amount of micro CCD is passed - * @throws If the micro CCD passed is greater than what can be contained in a 64-bit integer - */ - static ccdToMicroCcd(ccd: BigSource | bigint): Big { - return CcdAmount.fromCcd(ccd).toMicroCcd(); - } - - /** - * Converts an amount of micro CCD to CCD and asserts that the amount is a valid amount of CCD. - * - * @param microCcd The amount of micro CCD as a number, string, big or bigint. - * @returns The amount of CCD as a Big - * @throws If a number is passed with several decimal seperators - * @throws If a negative amount of micro CCD is passed - * @throws If the micro CCD passed is greater than what can be contained in a 64-bit integer - */ - static microCcdToCcd(microCcd: BigSource | bigint): Big { - return new CcdAmount(microCcd).toCcd(); - } - - toJSON(): string { - return this.microCcdAmount.toString(); - } -} - -function newBig(bigSource: BigSource): Big { - if (typeof bigSource === 'string') { - return Big(bigSource.replace(',', '.')); - } - return Big(bigSource); -} diff --git a/packages/common/src/types/moduleReference.ts b/packages/common/src/types/moduleReference.ts deleted file mode 100644 index 35137bd15..000000000 --- a/packages/common/src/types/moduleReference.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Buffer } from 'buffer/'; -import { packBufferWithWord32Length } from '../serializationHelpers'; - -/** - * Representation of a module reference, which enforces that it: - * - Hash length exactly 64 - * - Is a valid 64 length hex string - */ -export class ModuleReference { - moduleRef: string; - - decodedModuleRef: Uint8Array; - - constructor(moduleRef: string) { - if (moduleRef.length !== 64) { - throw new Error( - 'The provided moduleRef ' + - moduleRef + - ' is invalid as its length was not 64' - ); - } - try { - this.decodedModuleRef = Buffer.from(moduleRef, 'hex'); - this.moduleRef = moduleRef; - } catch (error) { - throw error; - } - } - - static fromBytes(bytes: Buffer): ModuleReference { - return new ModuleReference(bytes.toString('hex')); - } - - toJSON(): string { - return packBufferWithWord32Length( - Buffer.from(this.decodedModuleRef) - ).toString('hex'); - } -} diff --git a/packages/common/src/types/transactionExpiry.ts b/packages/common/src/types/transactionExpiry.ts deleted file mode 100644 index ebcb164fd..000000000 --- a/packages/common/src/types/transactionExpiry.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { secondsSinceEpoch } from '../util'; - -/** - * Representation of a transaction expiry date. - * - * A transaction expiry has to be in the future. Note that the concordium-node - * will reject transactions that are too far into the future, currently the default - * value for this rejection is 2 hours. - */ -export class TransactionExpiry { - /** expiry is measured as seconds since epoch */ - expiryEpochSeconds: bigint; - - constructor(expiry: Date, allowExpired = false) { - if (!allowExpired && expiry < new Date()) { - throw new Error( - 'A transaction expiry is not allowed to be in the past: ' + - expiry - ); - } - this.expiryEpochSeconds = secondsSinceEpoch(expiry); - } - - static fromEpochSeconds( - seconds: bigint, - allowExpired = false - ): TransactionExpiry { - return new TransactionExpiry( - new Date(Number(seconds) * 1000), - allowExpired - ); - } - - toJSON(): number { - return Number(this.expiryEpochSeconds); - } -} diff --git a/packages/common/src/versionedTypeHelpers.ts b/packages/common/src/versionedTypeHelpers.ts deleted file mode 100644 index e05a3b868..000000000 --- a/packages/common/src/versionedTypeHelpers.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { - AccountInfo, - AccountInfoBakerV0, - AccountInfoBakerV1, - Authorizations, - AuthorizationsV1, - BlockInfo, - BlockInfoV0, - BlockInfoV1, - ChainParameters, - ChainParametersV0, - ChainParametersV1, - ChainParametersV2, - ConsensusStatus, - ConsensusStatusV0, - ConsensusStatusV1, - ElectionInfo, - ElectionInfoV0, - ElectionInfoV1, - Keys, - KeysV0, - KeysV1, - StakePendingChange, - StakePendingChangeV0, - StakePendingChangeV1, -} from './types'; - -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBakerV0} */ -export const isBakerAccountV0 = (ai: AccountInfo): ai is AccountInfoBakerV0 => - (ai as AccountInfoBakerV1).accountBaker?.bakerPoolInfo === undefined; - -/** Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBakerV1} */ -export const isBakerAccountV1 = (ai: AccountInfo): ai is AccountInfoBakerV1 => - (ai as AccountInfoBakerV1).accountBaker?.bakerPoolInfo !== undefined; - -/** Whether {@link StakePendingChange} parameter given is of type {@link StakePendingChangeV0} */ -export const isStakePendingChangeV0 = ( - spc: StakePendingChange -): spc is StakePendingChangeV0 => - (spc as StakePendingChangeV0).epoch !== undefined; - -/** Whether {@link StakePendingChange} parameter given is of type {@link StakePendingChangeV1} */ -export const isStakePendingChangeV1 = ( - spc: StakePendingChange -): spc is StakePendingChangeV1 => - (spc as StakePendingChangeV1).effectiveTime !== undefined; - -/** Whether {@link Authorizations} parameter given is of type {@link AuthorizationsV1} */ -export const isAuthorizationsV1 = ( - as: Authorizations -): as is AuthorizationsV1 => - (as as AuthorizationsV1).timeParameters !== undefined; - -/** Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV0} */ -export const isChainParametersV0 = ( - cp: ChainParameters -): cp is ChainParametersV0 => - (cp as ChainParametersV0).minimumThresholdForBaking !== undefined; - -/** Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV1} */ -export const isChainParametersV1 = ( - cp: ChainParameters -): cp is ChainParametersV1 => - (cp as ChainParametersV1).mintPerPayday !== undefined && - !isChainParametersV2(cp); - -/** Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV2} */ -export const isChainParametersV2 = ( - cp: ChainParameters -): cp is ChainParametersV2 => - (cp as ChainParametersV2).maximumFinalizers !== undefined; - -/** Whether {@link Keys} parameter given is of type {@link KeysV0} */ -export const isKeysV0 = (ks: Keys): ks is KeysV0 => - !isAuthorizationsV1(ks.level2Keys); - -/** Whether {@link Keys} parameter given is of type {@link KeysV1} */ -export const isKeysV1 = (ks: Keys): ks is KeysV1 => - isAuthorizationsV1(ks.level2Keys); - -/** Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV0} */ -export const isBlockInfoV0 = (bi: BlockInfo): bi is BlockInfoV0 => - (bi as BlockInfoV0).blockSlot !== undefined; - -/** Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV1} */ -export const isBlockInfoV1 = (bi: BlockInfo): bi is BlockInfoV1 => - (bi as BlockInfoV1).round !== undefined; - -/** Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV0} */ -export const isConsensusStatusV0 = ( - cs: ConsensusStatus -): cs is ConsensusStatusV0 => (cs as ConsensusStatusV0).slotDuration != null; - -/** Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV1} */ -export const isConsensusStatusV1 = ( - cs: ConsensusStatus -): cs is ConsensusStatusV1 => - (cs as ConsensusStatusV1).concordiumBFTStatus !== undefined; - -/** Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV0} */ -export const isElectionInfoV0 = (ei: ElectionInfo): ei is ElectionInfoV0 => - (ei as ElectionInfoV0).electionDifficulty !== undefined; - -/** Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV1} */ -export const isElectionInfoV1 = (ei: ElectionInfo): ei is ElectionInfoV1 => - !isElectionInfoV0(ei); diff --git a/packages/common/test/alias.test.ts b/packages/common/test/alias.test.ts deleted file mode 100644 index c87d9ed1b..000000000 --- a/packages/common/test/alias.test.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { AccountAddress } from '../src/types/accountAddress'; -import * as bs58check from 'bs58check'; -import { getAlias, isAlias } from '../src/alias'; -import { Buffer } from 'buffer/'; - -test('isAlias is reflexive', () => { - const address = new AccountAddress( - bs58check.encode( - Buffer.from( - '01e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', - 'hex' - ) - ) - ); - expect(isAlias(address, address)).toBeTruthy(); -}); - -test('isAlias: Addresses with first 29 bytes in common are aliases', () => { - const address = new AccountAddress( - bs58check.encode( - Buffer.from( - '01e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', - 'hex' - ) - ) - ); - const alias = new AccountAddress( - bs58check.encode( - Buffer.from( - '01e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ececb467', - 'hex' - ) - ) - ); - expect(isAlias(address, alias)).toBeTruthy(); -}); - -test('isAlias: Addresses with differences in the 5th byte are not aliases', () => { - const address = new AccountAddress( - bs58check.encode( - Buffer.from( - '01e718721412249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', - 'hex' - ) - ) - ); - const alias = new AccountAddress( - bs58check.encode( - Buffer.from( - '01e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', - 'hex' - ) - ) - ); - expect(isAlias(address, alias)).toBeFalsy(); -}); - -test('isAlias: Addresses with differences in the 29th byte are not aliases', () => { - const address = new AccountAddress( - bs58check.encode( - Buffer.from( - '01e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ececb467', - 'hex' - ) - ) - ); - const alias = new AccountAddress( - bs58check.encode( - Buffer.from( - '01e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83e1ecb467', - 'hex' - ) - ) - ); - expect(isAlias(address, alias)).toBeFalsy(); -}); - -test('getAlias: getAlias returns an alias', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - const alias = getAlias(address, 1); - expect(isAlias(address, alias)).toBeTruthy(); -}); - -test('getAlias: changing counter makes getAlias return different aliases', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - const alias = getAlias(address, 1); - const otherAlias = getAlias(address, 100); - expect(otherAlias.address).not.toBe(alias.address); - expect(isAlias(otherAlias, alias)).toBeTruthy(); -}); - -test('getAlias: last 3 bytes of alias matches counter', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - let alias = getAlias(address, 0xaaaaaa); - expect(alias.decodedAddress.slice(29, 32).toString('hex')).toBe('aaaaaa'); - alias = getAlias(address, 0x152637); - expect(alias.decodedAddress.slice(29, 32).toString('hex')).toBe('152637'); - alias = getAlias(address, 0x000000); - expect(alias.decodedAddress.slice(29, 32).toString('hex')).toBe('000000'); - alias = getAlias(address, 0xffffff); - expect(alias.decodedAddress.slice(29, 32).toString('hex')).toBe('ffffff'); -}); - -test('getAlias: using counter = "last 3 bytes of address" returns the address', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - const alsoAddress = getAlias(address, 0xecb198); - expect(alsoAddress.address).toBe(address.address); -}); - -test('getAlias: accepts counter = 0', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - const alias = getAlias(address, 0); - expect(isAlias(address, alias)).toBeTruthy(); -}); - -test('getAlias: does not accept counter = -1', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - expect(() => getAlias(address, -1)).toThrowError(); -}); - -test('getAlias: accepts counter === 2^24 - 1', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - const alias = getAlias(address, 0xffffff); - expect(isAlias(address, alias)).toBeTruthy(); -}); - -test('getAlias: does not accept counter === 2^24', () => { - const address = new AccountAddress( - '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' - ); - expect(() => getAlias(address, 0x01000000)).toThrowError(); -}); diff --git a/packages/common/test/deserialization.test.ts b/packages/common/test/deserialization.test.ts deleted file mode 100644 index cf42fd25a..000000000 --- a/packages/common/test/deserialization.test.ts +++ /dev/null @@ -1,307 +0,0 @@ -import { - deserializeContractState, - deserializeTransaction, - deserializeReceiveReturnValue, - deserializeReceiveError, - deserializeInitError, -} from '../src/deserialization'; -import { Buffer } from 'buffer/'; -import { serializeAccountTransactionForSubmission } from '../src/serialization'; -import { - AccountAddress, - AccountTransaction, - AccountTransactionHeader, - AccountTransactionPayload, - AccountTransactionSignature, - AccountTransactionType, - BlockItemKind, - DataBlob, - CcdAmount, - RegisterDataPayload, - SimpleTransferPayload, - SimpleTransferWithMemoPayload, - TransactionExpiry, - deserializeTypeValue, - tokenAddressFromBase58, - tokenAddressToBase58, -} from '../src'; -import * as fs from 'fs'; -import { - CIS2_WCCD_STATE_SCHEMA, - V0_PIGGYBANK_SCHEMA, - CIS2_WCCD_STATE_GET_BALANCE_RETURN_VALUE_SCHEMA, - TEST_CONTRACT_INIT_ERROR_SCHEMA, - TEST_CONTRACT_SCHEMA, - TEST_CONTRACT_RECEIVE_ERROR_SCHEMA, - AUCTION_WITH_ERRORS_VIEW_RETURN_VALUE_SCHEMA, -} from './resources/schema'; - -test('test that deserializeContractState works', () => { - const state = deserializeContractState( - 'PiggyBank', - Buffer.from(V0_PIGGYBANK_SCHEMA, 'base64'), - Buffer.from('00', 'hex') - ); - - expect(state.Intact).toBeDefined(); -}); - -function deserializeAccountTransactionBase( - type: AccountTransactionType, - payload: AccountTransactionPayload, - expiry = new TransactionExpiry(new Date(Date.now() + 1200000)) -) { - const header: AccountTransactionHeader = { - expiry, - nonce: 0n, - sender: new AccountAddress( - '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' - ), - }; - - const transaction: AccountTransaction = { - header, - payload, - type, - }; - - const signatures: AccountTransactionSignature = { - 0: { - 0: '780e4f5e00554fb4e235c67795fbd6d4ad638f3778199713f03634c846e4dbec496f0b13c4454e1a760c3efffec7cc8c11c6053a632dd32c9714cd26952cda08', - }, - }; - - const deserialized = deserializeTransaction( - serializeAccountTransactionForSubmission(transaction, signatures) - ); - - if (deserialized.kind !== BlockItemKind.AccountTransactionKind) { - throw new Error('Incorrect BlockItemKind'); - } - - expect(deserialized.transaction).toEqual({ - accountTransaction: transaction, - signatures, - }); -} - -test('test deserialize simpleTransfer ', () => { - const payload: SimpleTransferPayload = { - amount: new CcdAmount(5100000n), - toAddress: new AccountAddress( - '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' - ), - }; - deserializeAccountTransactionBase(AccountTransactionType.Transfer, payload); -}); - -test('test deserialize simpleTransfer with memo ', () => { - const payload: SimpleTransferWithMemoPayload = { - amount: new CcdAmount(5100000n), - toAddress: new AccountAddress( - '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' - ), - memo: new DataBlob(Buffer.from('00', 'hex')), - }; - deserializeAccountTransactionBase( - AccountTransactionType.TransferWithMemo, - payload - ); -}); - -test('test deserialize registerData ', () => { - const payload: RegisterDataPayload = { - data: new DataBlob(Buffer.from('00AB5303926810EE', 'hex')), - }; - deserializeAccountTransactionBase( - AccountTransactionType.RegisterData, - payload - ); -}); - -test('Expired transactions can be deserialized', () => { - const payload: SimpleTransferPayload = { - amount: new CcdAmount(5100000n), - toAddress: new AccountAddress( - '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' - ), - }; - deserializeAccountTransactionBase( - AccountTransactionType.Transfer, - payload, - new TransactionExpiry(new Date(2000, 1), true) - ); -}); - -test('Receive return value can be deserialized', () => { - const returnValue = deserializeReceiveReturnValue( - Buffer.from('80f18c27', 'hex'), - Buffer.from(CIS2_WCCD_STATE_SCHEMA, 'base64'), - 'CIS2-wCCD-State', - 'getBalance' - ); - - expect(returnValue).toEqual('82000000'); -}); - -/** - * Repeats the "Receive return value can be deserialized" test, using deserializeTypeValue and a type specific schema instead. - */ -test('Receive return value can be deserialized using deserializeTypeValue', () => { - const returnValue = deserializeTypeValue( - Buffer.from('80f18c27', 'hex'), - Buffer.from(CIS2_WCCD_STATE_GET_BALANCE_RETURN_VALUE_SCHEMA, 'base64') - ); - expect(returnValue).toEqual('82000000'); -}); - -const auctionRawReturnValue = Buffer.from( - '00000b0000004120676f6f64206974656d00a4fbca84010000', - 'hex' -); - -/** - * Small helper for expected deserialized value of rawAuctionReturnValue - */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const expectAuctionReturnValue = (returnValue: any) => { - expect(returnValue.item).toEqual('A good item'); - expect(returnValue.end).toEqual('2022-12-01T00:00:00+00:00'); - expect(returnValue.auction_state).toHaveProperty('NotSoldYet'); - expect(returnValue.highest_bidder).toHaveProperty('None'); -}; - -test('Return value can be deserialized - auction', () => { - const returnValue = deserializeReceiveReturnValue( - auctionRawReturnValue, - Buffer.from( - fs.readFileSync('./test/resources/auction-with-errors-schema.bin') - ), - 'auction', - 'view' - ); - - expectAuctionReturnValue(returnValue); -}); - -/** - * Repeats the "Return value can be deserialized - auction" test, using deserializeTypeValue and a type specific schema instead. - */ -test('Return value can be deserialized - auction using deserializeTypeValue', () => { - const returnValue = deserializeTypeValue( - auctionRawReturnValue, - Buffer.from(AUCTION_WITH_ERRORS_VIEW_RETURN_VALUE_SCHEMA, 'base64') - ); - - expectAuctionReturnValue(returnValue); -}); - -test('Receive error can be deserialized', () => { - const error = deserializeReceiveError( - Buffer.from('ffff', 'hex'), - Buffer.from(TEST_CONTRACT_SCHEMA, 'base64'), - 'TestContract', - 'receive_function' - ); - - expect(error).toEqual(-1); -}); - -/** - * Repeats the "Receive error can be deserialized" test, using deserializeTypeValue and a type specific schema instead. - */ -test('Receive error can be deserialized using deserializeTypeValue', () => { - const error = deserializeTypeValue( - Buffer.from('ffff', 'hex'), - Buffer.from(TEST_CONTRACT_RECEIVE_ERROR_SCHEMA, 'base64') - ); - expect(error).toEqual(-1); -}); - -test('Init error can be deserialized', () => { - const error = deserializeInitError( - Buffer.from('0100', 'hex'), - Buffer.from(TEST_CONTRACT_SCHEMA, 'base64'), - 'TestContract' - ); - - expect(error).toEqual(1); -}); - -/** - * Repeats the "Init error can be deserialized" test, using deserializeTypeValue and a type specific schema instead. - */ -test('Init error can be deserialized using deserializeTypeValue', () => { - const error = deserializeTypeValue( - Buffer.from('0100', 'hex'), - Buffer.from(TEST_CONTRACT_INIT_ERROR_SCHEMA, 'base64') - ); - expect(error).toEqual(1); -}); - -test('Test parsing of Token Addresses', () => { - let base58 = '5Pxr5EUtU'; - let address = tokenAddressFromBase58(base58); - let rebase58 = tokenAddressToBase58(address); - let expectedAddress = { - contract: { - index: 0n, - subindex: 0n, - }, - id: '', - }; - expect(address).toEqual(expectedAddress); - expect(rebase58).toEqual(base58); - - base58 = 'LQMMu3bAg7'; - address = tokenAddressFromBase58(base58); - rebase58 = tokenAddressToBase58(address); - expectedAddress = { - contract: { - index: 0n, - subindex: 0n, - }, - id: 'aa', - }; - expect(address).toEqual(expectedAddress); - expect(rebase58).toEqual(base58); - - base58 = '5QTdu98KF'; - address = tokenAddressFromBase58(base58); - rebase58 = tokenAddressToBase58(address); - const expectedAddress2 = { - contract: { - index: 1n, - subindex: 0n, - }, - id: '', - }; - expect(address).toEqual(expectedAddress2); - expect(rebase58).toEqual(base58); - - base58 = 'LSYqgoQcb6'; - address = tokenAddressFromBase58(base58); - rebase58 = tokenAddressToBase58(address); - expectedAddress = { - contract: { - index: 1n, - subindex: 0n, - }, - id: 'aa', - }; - expect(address).toEqual(expectedAddress); - expect(rebase58).toEqual(base58); - - base58 = 'LSYXivPSWP'; - address = tokenAddressFromBase58(base58); - rebase58 = tokenAddressToBase58(address); - expectedAddress = { - contract: { - index: 1n, - subindex: 0n, - }, - id: '0a', - }; - expect(address).toEqual(expectedAddress); - expect(rebase58).toEqual(base58); -}); diff --git a/packages/common/test/schemaHelpers.test.ts b/packages/common/test/schemaHelpers.test.ts deleted file mode 100644 index 52aaf3a88..000000000 --- a/packages/common/test/schemaHelpers.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import fs from 'fs'; -import { Buffer } from 'buffer/'; -import { - displayTypeSchemaTemplate, - getUpdateContractParameterSchema, -} from '../src/schemaHelpers'; - -test('schema template display', () => { - const fullSchema = Buffer.from( - fs.readFileSync('./test/resources/cis2-nft-schema.bin') - ); - const schemaVersion = 1; - const contractName = 'CIS2-NFT'; - const functionName = 'transfer'; - const template = displayTypeSchemaTemplate( - getUpdateContractParameterSchema( - fullSchema, - contractName, - functionName, - schemaVersion - ) - ); - expect(template).toBe( - '[{"amount":["",""],"data":[""],"from":{"Enum":[{"Account":[""]},{"Contract":[{"index":"","subindex":""}]}]},"to":{"Enum":[{"Account":[""]},{"Contract":[{"index":"","subindex":""},{"contract":"","func":""}]}]},"token_id":[""]}]' - ); -}); diff --git a/packages/common/test/serialization.test.ts b/packages/common/test/serialization.test.ts deleted file mode 100644 index 5d4813448..000000000 --- a/packages/common/test/serialization.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -import fs from 'fs'; -import { Buffer } from 'buffer/'; -import { AccountAddress } from '../src/types/accountAddress'; -import { CcdAmount } from '../src/types/ccdAmount'; -import { - serializeAccountTransactionForSubmission, - serializeAccountTransactionSignature, - serializeUpdateContractParameters, - serializeTypeValue, -} from '../src/serialization'; -import { - AccountTransaction, - AccountTransactionHeader, - AccountTransactionSignature, - AccountTransactionType, - SimpleTransferPayload, -} from '../src/types'; -import { TransactionExpiry } from '../src/types/transactionExpiry'; -import { getUpdateContractParameterSchema } from '../src'; - -test('fail account transaction serialization if no signatures', () => { - const simpleTransferPayload: SimpleTransferPayload = { - amount: new CcdAmount(5100000n), - toAddress: new AccountAddress( - '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' - ), - }; - - const header: AccountTransactionHeader = { - expiry: new TransactionExpiry(new Date(Date.now() + 1200000)), - nonce: 0n, - sender: new AccountAddress( - '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' - ), - }; - - const simpleTransferAccountTransaction: AccountTransaction = { - header: header, - payload: simpleTransferPayload, - type: AccountTransactionType.Transfer, - }; - - expect(() => - serializeAccountTransactionForSubmission( - simpleTransferAccountTransaction, - {} - ) - ).toThrow(); -}); - -test('serialization of an account signature with two credentials', () => { - const signature: AccountTransactionSignature = { - 0: { - 0: '893f2e4a230bcbeee24675454c4ca95a2f55fd33f328958b626c6fa368341e07902c9ffe7864c3bee23b2b2300ed0922eb814ea41fdee25035be8cddc5c3980f', - }, - 1: { - 0: '620d859224c40160c2bb03dbe84e9f57b8ed17f1a5df28b4e21f10658992531ef27655e6b74b8e47923e1ccb0413d563205e8b6c0cd22b3adce5dc7dc1daf603', - }, - }; - - const serializedSignature = serializeAccountTransactionSignature(signature); - expect(serializedSignature.toString('hex')).toBe( - '020001000040893f2e4a230bcbeee24675454c4ca95a2f55fd33f328958b626c6fa368341e07902c9ffe7864c3bee23b2b2300ed0922eb814ea41fdee25035be8cddc5c3980f0101000040620d859224c40160c2bb03dbe84e9f57b8ed17f1a5df28b4e21f10658992531ef27655e6b74b8e47923e1ccb0413d563205e8b6c0cd22b3adce5dc7dc1daf603' - ); -}); - -test('serialize UpdateContractParameters using CIS2 contract', () => { - const parameter = serializeUpdateContractParameters( - 'CIS2-NFT', - 'transfer', - [ - { - token_id: [], - amount: [200, 0], - from: { - Account: [ - '4RgTGQhg1Y8DAUkC2TpZsKmXdicArDqY9gcgJmBDECg4kkYNg4', - ], - }, - to: { - Account: [ - '3UiNwnmZ64YR423uamgZyY8RnRkD88tfn6SYtKzvWZCkyFdN94', - ], - }, - data: [], - }, - ], - Buffer.from(fs.readFileSync('./test/resources/cis2-nft-schema.bin')), - 1 - ); - - expect(parameter.toString('hex')).toBe( - '010000c80000c320b41f1997accd5d21c6bf4992370948ed711435e0e2c9302def62afd1295f004651a37c65c8461540decd511e7440d1ff6d4191b7e2133b7239b2485be1a4860000' - ); -}); - -test('serialize UpdateContractParameters using CIS2 contract and incorrect name', () => { - const parameter = function () { - serializeUpdateContractParameters( - 'CIS2-NFT', - 'non-existent', - [ - { - token_id: [], - amount: [200, 0], - from: { - Account: [ - '4RgTGQhg1Y8DAUkC2TpZsKmXdicArDqY9gcgJmBDECg4kkYNg4', - ], - }, - to: { - Account: [ - '3UiNwnmZ64YR423uamgZyY8RnRkD88tfn6SYtKzvWZCkyFdN94', - ], - }, - data: [], - }, - ], - Buffer.from( - fs.readFileSync('./test/resources/cis2-nft-schema.bin') - ), - 1 - ); - }; - - expect(parameter).toThrow(); -}); - -test('serialize type value and serializeUpdateContractParameters give same result', () => { - const parameters = [ - { - token_id: [], - amount: [200, 0], - from: { - Account: ['4RgTGQhg1Y8DAUkC2TpZsKmXdicArDqY9gcgJmBDECg4kkYNg4'], - }, - to: { - Account: ['3UiNwnmZ64YR423uamgZyY8RnRkD88tfn6SYtKzvWZCkyFdN94'], - }, - data: [], - }, - ]; - const fullSchema = Buffer.from( - fs.readFileSync('./test/resources/cis2-nft-schema.bin') - ); - const schemaVersion = 1; - const contractName = 'CIS2-NFT'; - const functionName = 'transfer'; - - const serializedParameter = serializeUpdateContractParameters( - contractName, - functionName, - parameters, - fullSchema, - schemaVersion - ); - - const serializedType = serializeTypeValue( - parameters, - getUpdateContractParameterSchema( - fullSchema, - contractName, - functionName, - schemaVersion - ) - ); - - expect(serializedParameter.toString('hex')).toEqual( - serializedType.toString('hex') - ); -}); - -test('serializeTypeValue throws an error if unable to serialize', () => { - expect(() => serializeTypeValue('test', Buffer.alloc(0))).toThrowError( - Error - ); -}); diff --git a/packages/common/test/types/blockSpecialEvents.test.ts b/packages/common/test/types/blockSpecialEvents.test.ts deleted file mode 100644 index 3b08be905..000000000 --- a/packages/common/test/types/blockSpecialEvents.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { - BlockSpecialEventBakingRewards, - BlockSpecialEventPaydayPoolReward, - BlockSpecialEventBlockAccrueReward, - BlockSpecialEventPaydayFoundationReward, - BlockSpecialEventFinalizationRewards, - BlockSpecialEventMint, - BlockSpecialEventPaydayAccountReward, - BlockSpecialEventBlockReward, - specialEventAffectedAccounts, -} from '../../src'; - -const bakingRewards: BlockSpecialEventBakingRewards = { - tag: 'bakingRewards', - remainder: 0n, - bakingRewards: [ - { - account: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - amount: 400000n, - }, - { - account: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - amount: 400000n, - }, - ], -}; - -const finalizationRewards: BlockSpecialEventFinalizationRewards = { - tag: 'finalizationRewards', - remainder: 0n, - finalizationRewards: [ - { - account: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - amount: 400000n, - }, - { - account: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - amount: 400000n, - }, - ], -}; - -const foundationReward: BlockSpecialEventPaydayFoundationReward = { - tag: 'paydayFoundationReward', - developmentCharge: 123n, - foundationAccount: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', -}; - -const mint: BlockSpecialEventMint = { - tag: 'mint', - mintBakingReward: 0n, - mintFinalizationReward: 0n, - mintPlatformDevelopmentCharge: 0n, - foundationAccount: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', -}; - -const paydayAccountReward: BlockSpecialEventPaydayAccountReward = { - tag: 'paydayAccountReward', - account: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - transactionFees: 123n, - bakerReward: 123n, - finalizationReward: 123n, -}; - -const foundationBlockReward: BlockSpecialEventBlockReward = { - tag: 'blockReward', - transactionFees: 1231241n, - bakerReward: 12314n, - foundationCharge: 12n, - newGasAccount: 1n, - oldGasAccount: 0n, - baker: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - foundationAccount: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', -}; - -const blockReward: BlockSpecialEventBlockReward = { - tag: 'blockReward', - transactionFees: 1231241n, - bakerReward: 12314n, - foundationCharge: 12n, - newGasAccount: 1n, - oldGasAccount: 0n, - baker: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - foundationAccount: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', -}; - -const paydayPoolReward: BlockSpecialEventPaydayPoolReward = { - tag: 'paydayPoolReward', - poolOwner: 123n, - finalizationReward: 123n, - bakerReward: 12314n, - transactionFees: 1231241n, -}; - -const accrueReward: BlockSpecialEventBlockAccrueReward = { - tag: 'blockAccrueReward', - transactionFees: 1231241n, - bakerReward: 12314n, - baker: 0n, - foundationCharge: 12n, - newGasAccount: 1n, - oldGasAccount: 0n, - passiveReward: 123n, -}; - -describe('specialEventAffectedAccounts', () => { - test('Returns empty list of accounts for events with no account payouts', () => { - let accounts = specialEventAffectedAccounts(paydayPoolReward); - expect(accounts).toEqual([]); - - accounts = specialEventAffectedAccounts(accrueReward); - expect(accounts).toEqual([]); - }); - - test('Returns correct list of accounts for events with account payouts', () => { - let accounts = specialEventAffectedAccounts(bakingRewards); - expect(accounts).toEqual([ - '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - ]); - - accounts = specialEventAffectedAccounts(finalizationRewards); - expect(accounts).toEqual([ - '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - ]); - - accounts = specialEventAffectedAccounts(foundationReward); - expect(accounts).toEqual([ - '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - ]); - - accounts = specialEventAffectedAccounts(mint); - expect(accounts).toEqual([ - '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - ]); - - accounts = specialEventAffectedAccounts(paydayAccountReward); - expect(accounts).toEqual([ - '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - ]); - - accounts = specialEventAffectedAccounts(foundationBlockReward); - expect(accounts).toEqual([ - '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - ]); - - accounts = specialEventAffectedAccounts(blockReward); - expect(accounts).toEqual([ - '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - ]); - }); -}); diff --git a/packages/common/test/util.test.ts b/packages/common/test/util.test.ts deleted file mode 100644 index a87ed2286..000000000 --- a/packages/common/test/util.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { intToStringTransformer, stringToInt, wasmToSchema } from '../src/util'; -import { readFileSync } from 'fs'; -import { Buffer } from 'buffer/'; - -test('intToStringTransformer transform chosen field, but not others', () => { - const keysToTransform = ['a']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":90071992547409912}'; - const transformed = intToStringTransformer(keysToTransform)(input); - expect(transformed).toEqual( - '{ "a":"90071992547409910", "b":90071992547409911, "aa":90071992547409912}' - ); -}); - -test('intToStringTransformer transforms multiple fields', () => { - const keysToTransform = ['a', 'b']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":1}}'; - const transformed = intToStringTransformer(keysToTransform)(input); - expect(transformed).toEqual( - '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":1}}' - ); -}); - -test('intToStringTransformer will not change the string if no keys match', () => { - const keysToTransform = ['d', 'aaa']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":1}}'; - const transformed = intToStringTransformer(keysToTransform)(input); - expect(transformed).toEqual(input); -}); - -test('stringToInt transforms chosen field, but not others', () => { - const keysToTransform = ['a']; - const input = - '{ "a":"90071992547409910", "b":"90071992547409911", "aa":"90071992547409912"}'; - const transformed = stringToInt(input, keysToTransform); - expect(transformed).toEqual( - '{ "a":90071992547409910, "b":"90071992547409911", "aa":"90071992547409912"}' - ); -}); - -test('stringToInt transforms multiple fields', () => { - const keysToTransform = ['a', 'b']; - const input = - '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":"1"}}'; - const transformed = stringToInt(input, keysToTransform); - expect(transformed).toEqual( - '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":"1"}}' - ); -}); - -test('stringToInt will not change the string if no keys match', () => { - const keysToTransform = ['d', 'aaa']; - const input = - '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":"1"}}'; - const transformed = stringToInt(input, keysToTransform); - expect(transformed).toEqual(input); -}); - -test('stringToInt can inverse intToStringTransformer (with same chosen keys, and no matching number fields)', () => { - const keysToTransform = ['a', 'b']; - const input = - '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":"1"}, "d": true}'; - const transformed = stringToInt( - intToStringTransformer(keysToTransform)(input), - keysToTransform - ); - expect(transformed).toEqual(input); -}); - -test('intToStringTransformer is inverse of stringToInt (with same chosen keys, and no matching string fields)', () => { - const keysToTransform = ['a', 'b']; - const input = - '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":"1"}, "d": true}'; - const transformed = intToStringTransformer(keysToTransform)( - stringToInt(input, keysToTransform) - ); - expect(transformed).toEqual(input); -}); - -test('Embedded schema is the same as a seperate schema file', () => { - const versionedWasmModule = new Buffer( - readFileSync('test/resources/icecream-with-schema.wasm') - ); - // Strip module version information - const wasmModule = versionedWasmModule.slice(8); - - const seperateSchema = new Buffer( - readFileSync('test/resources/icecream-schema.bin') - ); - const embeddedSchema = wasmToSchema(wasmModule); - - expect(seperateSchema).toEqual(embeddedSchema); -}); diff --git a/packages/common/tsconfig.eslint.json b/packages/common/tsconfig.eslint.json deleted file mode 100644 index d4da0a919..000000000 --- a/packages/common/tsconfig.eslint.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*", "test/**/*", "types/**/*"] -} diff --git a/packages/common/tsconfig.json b/packages/common/tsconfig.json deleted file mode 100644 index d560719fc..000000000 --- a/packages/common/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig-base.json", - "include": [ - "src/**/*", - "types/**/*" - ], - "compilerOptions": { - "rootDir": "./src", - "outDir": "./lib" - } -} diff --git a/packages/nodejs/CHANGELOG.md b/packages/nodejs/CHANGELOG.md deleted file mode 100644 index c5b146845..000000000 --- a/packages/nodejs/CHANGELOG.md +++ /dev/null @@ -1,258 +0,0 @@ -# Changelog - -## 9.5.1 - -- Bumped @concordium/common-sdk to 9.5.1. - -## 9.5.0 - -- Bumped @concordium/common-sdk to 9.5.0. - -## 9.4.0 - -- Bumped @concordium/common-sdk to 9.4.0. - -## 9.3.0 - -- Bumped @concordium/common-sdk to 9.3.0. - -## 9.2.0 - -- Bumped @concordium/common-sdk to 9.2.0. - -## 9.1.1 - -### Changed - -- Bumped @concordium/common-sdk to 9.1.1. (includes fixes for `verifyWeb3IdCredentialSignature` and `canProveAtomicStatement`) - -## 9.1.0 - -### Changed - -- Bumped @concordium/common-sdk to 9.1.0. (adds methods for creating verifiable presentation (proving statements about Web3Id Credentials)) - -## 9.0.0 - -### Breaking changes - -- Bumped @concordium/common-sdk to 9.0.0. (adds `displayTypeSchemaTemplate/getTransactionKindString` and renames `AccountTransactionType.TransferWithScheduleWithMemo`) - -## 8.0.0 - -### Breaking changes - -- Bumped @concordium/common-sdk to 8.0.0: - - Properly formed errors thrown by functions wrapping WASM execution (from @concordium/rust-bindings) with more helpful error messages. - - Types adapted to changes in protocol version 6. - - and [more](../common/CHANGELOG.md) - -## 7.0.0 2023-05-15 - -### Breaking Changes - -- Bumped @concordium/common-sdk to 7.0.0: - - Updated `blockInfo` so that the `bakerId` field is optional, since it will be undefined for genesis blocks. - - `waitForTransactionFinalization` now returns a `BlockItemSummaryInBlock` - - Added missing version return type in `getModuleSchema`. It now returns an object containing the schema source and version. - -## 6.4.0 2023-5-03 - -### Changed - -- Bumped @concordium/common-sdk to 6.5.0. (Adds `CIS2Contract`) - -## 6.3.0 2023-3-22 - -### Changed - -- Bumped @concordium/common-sdk to 6.4.0. (Adds `deserializeTypeValue`) - -## 6.2.0 2023-2-27 - -### Added - -- Added a `createConcordiumClient` function to create the gRPC v2 client. - -### Changed - -- Bumped @concordium/common-sdk to 6.3.0. (Adds the gRPC v2 client) - -### Fixed - -- The value of amount fields in the GRPCv1 client's invokeContract's events has been changed to bigint (instead of string) as the type specifies. - -### Deprecated - -- The old gRPC client has been deprecated in favor of the new gRPC v2 client. - -## 6.1.0 2022-11-30 - -### Changed - -- Bumped @concordium/common-sdk to 6.1.0. (adds support for id statements and proofs) - -## 6.0.0 2022-11-15 - -### Breaking Changes - -- Bumped @concordium/common-sdk to 6.0.0. (Which changes transaction type names and field names to be aligned with other implementations) - -## 5.0.0 2022-11-8 - -Breaking Changes - -- Bumped @concordium/common-sdk to 5.2.0. (Which changes the function signature of ConcordiumHdWallet and sign helpers functions) - -## 4.0.0 2022-8-26 - -### Breaking Changes - -- Bumped @concordium/common-sdk to 3.0.0. (Which includes breaking changes to schema versioning) - -## 3.0.2 2022-7-26 - -### Fixed - -- `deserializeTransaction` no longer throws an error on expired transactions. - -## 3.0.1 2022-7-26 - -### Fixed - -- `deserializeTransaction` is now exported from index. - -## 3.0.0 - 2022-7-25 - -### Added - -- `deserializeTransaction` function to deserialize transaction created by `serializeAccountTransactionForSubmission` and `serializeCredentialDeploymentTransactionForSubmission`. (Currently SimpleTransfer, SimpleTransferWithMemo and RegisterData are the only supported account transactions kinds) - -### Breaking changes - -- getInstanceInfo, getModuleSource and invokeContract's parameters have changed order. Now the blockHash is the 2nd parameter instead of the 1st. - -## 2.1.1 2022-7-8 - -### Fixed - -- Fixed contract schema serialization for ByteList. - -## 2.1.0 2022-7-5 - -### Added - -- Support deserializing new schema types: ULeb128, ILeb128, ByteArray and ByteList. -- Support deserializing schemas with versioning information. - -### Changed - -- The function for deserializing a module schema `deserialModuleFromBuffer` now have the schema version as an optional argument. The function will try to extract the version from the buffer. When a version is provided it falls back to this, otherwise it throws an error. - -## 2.0.2 2022-6-27 - -### Fixed - -- `getModuleBuffer` returns correct type of `Buffer`. - -## 2.0.1 2022-6-27 - -### Added - -- `getModuleBuffer`, which is `getModuleFromBuffer` renamed (which was removed in 2.0.0). - -### Fixed - -- Error in build, which caused imports to fail. -- Added missing dependency google-protobuf. -- @noble/ed25519 and cross-fetch moved from devDependencies to dependencies. (In common-sdk) - -## 2.0.0 2022-6-24 - -### Added - -- Using `@concordium/common-sdk` as a dependency, and most features have been removed from this package. (But are re-exported instead) -- Support deserializing version 2 schemas. -- Support serializing parameters for contracts using version 2 schemas. -- Support for deploying versioned smart contract modules, which is the format used in cargo-concordium v2+. (This is done by not supplying the version field in the payload) - -### Breaking changes - -- `serializeInitContractParameters` and `serializeUpdateContractParameters` each have an additional parameter, which denotes the version of the schema provided. For existing users that are using V0 contracts, that parameter should be `SchemaVersion.V1`. -- Deserialization of schemas have been changed: types and functions have been renamed and `deserialModuleFromBuffer` have an additional parameter, and now returns a versioned module schema. - -## 1.1.0 2022-06-14 - -### Added - -- Support for the Invoke contract node entrypoint. - -### Fixed - -- Lossy parsing of uint64's from the node, if their value was above MAX_SAFE_INTEGER. - -## 1.0.0 2022-05-11 - -### Added - -- Support for getting baker list from node. -- Support for getting status of a Baker Pool/Passive delegation (required node to have protocol version 4 or later). -- Support for getting reward status of chain at specific block. -- Helper functions for determining the version of `BlockSummary` and nested types. -- Helper functions for determining the version of `AccountInfo` variants. -- Support for the new "configure delegation" transaction type. - -### Changed - -- Updated `BlockSummary` type to include new version, effective from protocol version 4. -- Updated `AccountInfo` type to include new fields related to delegation introduced with protocol version 4. - -## 0.7.3 2022-05-05 - -### Added - -- Export of serializeCredentialDeploymentTransactionForSubmission. - -### Fixed - -- Added missing dependency "google-protobuf" - -## 0.7.2 2022-05-05 - -### Added - -- Export of serializeAccountTransactionForSubmission. - -## 0.7.1 2022-03-09 - -### Added - -- Support for initiating and updating contracts with parameters. - -## 0.6.0 2022-02-02 - -### Added - -- Function to deserialize contract state. -- Support for register data transaction. - -## 0.5.1 2021-11-19 - -### Added - -- Functions to generate account aliases, and check if addresses are aliases. - -## 0.4.0 2021-11-17 - -### Added - -- Support for getting account info for a credential registration id. -- Support for the update credentials account transaction. -- Support for deploy module, initiate contract and update contract (without parameters). - -## 0.3.0 2021-10-28 - -### Added - -- Support for the credential deployment transaction. -- Helpers to decrypt mobile wallet exports, in particular to extract identity information from the export. diff --git a/packages/nodejs/README.md b/packages/nodejs/README.md deleted file mode 100644 index 903c9b681..000000000 --- a/packages/nodejs/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Concordium NodeJS SDK - -[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](https://github.com/Concordium/.github/blob/main/.github/CODE_OF_CONDUCT.md) - -Wrappers for interacting with the Concordium node, using nodejs. - -Please see the -[documentation](https://developer.concordium.software/concordium-node-sdk-js/index.html) -for more information - -## ConcordiumGRPCClient - -The SDK provides a gRPC client, which can interact with the [Concordium -Node](https://github.com/Concordium/concordium-node) - -For an overview of the endpoints, [click -here](https://developer.concordium.software/concordium-node-sdk-js/modules/Common_GRPC_Client.html). - -To create a client, the function `createConcordiumClient` can be used. It -requires the address and port of the node. It also requires credentials to -be specified. These can be used for create either an insecure connection or -a TLS connection. In the following example the credentials are created for -a TLS connection: - -```ts -import { credentials } from '@grpc/grpc-js/'; -import { createConcordiumClient } from '@concordium/node-sdk'; -... -return createConcordiumClient( - address, - port, - credentials.createSsl(), - { timeout: 15000 } -); -``` - -The fourth argument is additional options. In the example -above we sat the timeout for a call to the node to 15 -seconds. The options allowed here are those allowed by the -[grpc-transport](https://www.npmjs.com/package/@protobuf-ts/grpc-transport). - -The connection to a node can be either an insecure connection or a TLS -connection. Note that the node that you are trying to connect to must support -TLS, for a TLS connection to work. Otherwise an insecure connection can be -created by using `credentials.createInsecure()` instead. - -To see the documentation for the deprecated v1 client, [click -here](https://developer.concordium.software/concordium-node-sdk-js/pages/misc-pages/grpc-v1.html). -For an overview of how to migrate from the v1 client to the v2 client, [click -here](https://developer.concordium.software/concordium-node-sdk-js/pages/misc-pages/grpc-migration.html). diff --git a/packages/nodejs/jest.config.js b/packages/nodejs/jest.config.js deleted file mode 100644 index efc8bc73b..000000000 --- a/packages/nodejs/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - moduleFileExtensions: ['js', 'ts', 'json'], - moduleDirectories: ['node_modules'], - globals: { - 'ts-jest': { - tsconfig: 'tsconfig.jest.json', - }, - }, -}; diff --git a/packages/nodejs/package.json b/packages/nodejs/package.json deleted file mode 100644 index 9fc9972b0..000000000 --- a/packages/nodejs/package.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "name": "@concordium/node-sdk", - "version": "9.5.1", - "description": "Helpers for interacting with the Concordium node", - "repository": { - "type": "git", - "url": "https://github.com/Concordium/concordium-node-sdk-js", - "directory": "packages/nodejs" - }, - "author": { - "name": "Concordium Software", - "email": "support@concordium.software", - "url": "https://concordium.com" - }, - "license": "Apache-2.0", - "engines": { - "node": ">=14.16.0" - }, - "main": "lib/index.js", - "types": "lib/index.d.ts", - "files": [ - "/grpc/*", - "/lib/**/*" - ], - "devDependencies": { - "@noble/ed25519": "^1.7.1", - "@protobuf-ts/grpcweb-transport": "^2.8.2", - "@types/bs58check": "^2.1.0", - "@types/google-protobuf": "^3.15.3", - "@types/jest": "^26.0.23", - "@typescript-eslint/eslint-plugin": "^4.28.1", - "@typescript-eslint/parser": "^4.28.1", - "babel-jest": "^27.0.6", - "cross-env": "5.0.5", - "eslint": "^7.29.0", - "eslint-config-prettier": "^8.3.0", - "eslint-plugin-import": "^2.23.4", - "eslint-plugin-prettier": "^3.4.0", - "grpc-tools": "^1.11.2", - "grpc_tools_node_protoc_ts": "5.3.0", - "isomorphic-fetch": "^3.0.0", - "jest": "^27.0.6", - "lint-staged": "^12.0.2", - "prettier": "^2.3.2", - "ts-jest": "^27.0.3", - "typescript": "^4.3.5" - }, - "prettier": { - "singleQuote": true, - "tabWidth": 4 - }, - "scripts": { - "generate-js": "yarn run grpc_tools_node_protoc --js_out=import_style=commonjs,binary:grpc --grpc_out=grpc_js:grpc --plugin=protoc-gen-grpc=../../node_modules/.bin/grpc_tools_node_protoc_plugin -I ../../deps/concordium-base/concordium-grpc-api ../../deps/concordium-base/concordium-grpc-api/**/*.proto", - "generate-ts": "yarn run grpc_tools_node_protoc --plugin=protoc-gen-ts=../../node_modules/grpc_tools_node_protoc_ts/bin/protoc-gen-ts --ts_out=grpc_js:grpc -I ../../deps/concordium-base/concordium-grpc-api ../../deps/concordium-base/concordium-grpc-api/concordium_p2p_rpc.proto", - "generate": "([ -e \"../../deps/concordium-base/concordium-grpc-api\" ] && yarn generate-js && yarn generate-ts) || echo 'Please checkout submodules before building'", - "lint": "eslint . --cache --ext .ts,.tsx --max-warnings 0", - "lint-fix": "yarn --silent lint --fix; exit 0", - "test": "jest", - "build": "rm -rf grpc; mkdir -p grpc; yarn generate && tsc", - "build-dev": "tsc" - }, - "dependencies": { - "@concordium/common-sdk": "9.5.1", - "@grpc/grpc-js": "^1.3.4", - "@protobuf-ts/grpc-transport": "^2.8.2", - "buffer": "^6.0.3", - "google-protobuf": "^3.20.1" - } -} diff --git a/packages/nodejs/src/client.ts b/packages/nodejs/src/client.ts deleted file mode 100644 index a814c5c5c..000000000 --- a/packages/nodejs/src/client.ts +++ /dev/null @@ -1,951 +0,0 @@ -import { ChannelCredentials, Metadata, ServiceError } from '@grpc/grpc-js'; -import { Buffer as BufferFormater } from 'buffer/'; -import { P2PClient } from '../grpc/concordium_p2p_rpc_grpc_pb'; -import { - AccountAddress, - BlockHash, - BlockHeight, - Empty, - GetAddressInfoRequest, - GetPoolStatusRequest, - GetModuleSourceRequest, - PeerListResponse, - PeersRequest, - SendTransactionRequest, - TransactionHash, - InvokeContractRequest, -} from '../grpc/concordium_p2p_rpc_pb'; -import { - serializeAccountTransactionForSubmission, - serializeCredentialDeploymentTransactionForSubmission, - AccountAddress as Address, - CredentialRegistrationId, - AccountBakerDetails, - AccountEncryptedAmount, - AccountInfo, - AccountReleaseSchedule, - AccountTransaction, - AccountTransactionSignature, - ArInfo, - BlockInfo, - BlockSummary, - ConsensusStatus, - ContractAddress, - CredentialDeploymentTransaction, - CryptographicParameters, - ExchangeRate, - FinalizationData, - IpInfo, - KeysWithThreshold, - NextAccountNonce, - PartyInfo, - ReleaseSchedule, - TransactionStatus, - TransactionSummary, - TransferredEvent, - UpdateQueue, - Versioned, - InstanceInfo, - InstanceInfoSerialized, - BakerId, - ChainParametersV0, - ChainParametersV1, - PoolStatus, - BakerPoolStatusDetails, - CurrentPaydayBakerPoolStatus, - KeysMatching, - BakerPoolPendingChangeReduceBakerCapitalDetails, - BakerPoolStatus, - RewardStatusV0, - RewardStatus, - RewardStatusV1, - ReduceStakePendingChangeV0, - PassiveDelegationStatus, - PassiveDelegationStatusDetails, - ContractContext, - InvokeContractResultV1, - CcdAmount, - ModuleReference, - ReduceStakePendingChangeV1, - buildInvoker, - DelegationStakeChangedEvent, - CooldownParametersV1, - TimeParametersV1, - PoolParametersV1, - BlockInfoV0, - BlockInfoV1, - ConsensusStatusV0, - ConsensusStatusV1, - ChainParametersV2, - ConsensusParameters, - TimeoutParameters, - Ratio, - ConcordiumBftStatus, -} from '@concordium/common-sdk'; -import { - buildJsonResponseReviver, - intToStringTransformer, - isValidHash, - stringToInt, -} from '@concordium/common-sdk/lib/util'; -import { - intListToStringList, - unwrapBoolResponse, - unwrapJsonResponse, -} from './util'; - -/** - * A concordium-node specific gRPC client wrapper. - * - * @example - * import ConcordiumNodeClient from "..." - * const client = new ConcordiumNodeClient('127.0.0.1', 10000, credentials, metadata, 15000); - * @deprecated This has been succeeded by the new V2 client, check {@link createConcordiumClient} - */ -export default class ConcordiumNodeClient { - client: P2PClient; - - metadata: Metadata; - - address: string; - - port: number; - - timeout: number; - - /** - * Initialize a gRPC client for a specific concordium node. - * @param address the ip address of the node, e.g. 127.0.0.1 - * @param port the port to use when econnecting to the node - * @param credentials credentials to use to connect to the node - * @param timeout milliseconds to wait before timing out - * @param options optional options for the P2PClient - */ - constructor( - address: string, - port: number, - credentials: ChannelCredentials, - metadata: Metadata, - timeout: number, - options?: Record - ) { - if (timeout < 0 || !Number.isSafeInteger(timeout)) { - throw new Error( - 'The timeout must be a positive integer, but was: ' + timeout - ); - } - - this.address = address; - this.port = port; - this.timeout = timeout; - this.metadata = metadata; - this.client = new P2PClient(`${address}:${port}`, credentials, options); - } - - /** - * Sends a credential deployment transaction, for creating a new account, - * to the node to be put in a block on the chain. - * - * Note that a transaction can still fail even if it was accepted by the node. - * To keep track of the transaction use getTransactionStatus. - * @param credentialDeploymentTransaction the credential deployment transaction to send to the node - * @param signatures the signatures on the hash of the serialized unsigned credential deployment information, in order - * @returns true if the transaction was accepted, otherwise false - */ - async sendCredentialDeploymentTransaction( - credentialDeploymentTransaction: CredentialDeploymentTransaction, - signatures: string[] - ): Promise { - const serializedCredentialDeploymentTransaction: Buffer = Buffer.from( - serializeCredentialDeploymentTransactionForSubmission( - credentialDeploymentTransaction, - signatures - ) - ); - - const sendTransactionRequest = new SendTransactionRequest(); - sendTransactionRequest.setNetworkId(100); - sendTransactionRequest.setPayload( - serializedCredentialDeploymentTransaction - ); - - const response = await this.sendRequest( - this.client.sendTransaction, - sendTransactionRequest - ); - return unwrapBoolResponse(response); - } - - /** - * Serializes and sends an account transaction to the node to be - * put in a block on the chain. - * - * Note that a transaction can still fail even if it was accepted by the node. - * To keep track of the transaction use getTransactionStatus. - * @param accountTransaction the transaction to send to the node - * @param signatures the signatures on the signing digest of the transaction - * @returns true if the transaction was accepted, otherwise false - */ - async sendAccountTransaction( - accountTransaction: AccountTransaction, - signatures: AccountTransactionSignature - ): Promise { - const serializedAccountTransaction: Buffer = Buffer.from( - serializeAccountTransactionForSubmission( - accountTransaction, - signatures - ) - ); - - const sendTransactionRequest = new SendTransactionRequest(); - sendTransactionRequest.setNetworkId(100); - sendTransactionRequest.setPayload(serializedAccountTransaction); - - const response = await this.sendRequest( - this.client.sendTransaction, - sendTransactionRequest - ); - return unwrapBoolResponse(response); - } - - /** - * Retrieves the account info for the given account. If the provided block - * hash is in a block prior to the finalization of the account, then the account - * information will not be available. - * A credential registration id can also be provided, instead of an address. In this case - * the node will return the account info of the account, which the corresponding credential - * is (or was) deployed to. - * @param accountAddress base58 account address (or a credential registration id) to get the account info for - * @param blockHash the block hash to get the account info at - * @returns the account info for the provided account address, undefined is the account does not exist - */ - async getAccountInfo( - accountAddress: string | Address | CredentialRegistrationId, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const getAddressInfoRequest = new GetAddressInfoRequest(); - - if (typeof accountAddress === 'string') { - getAddressInfoRequest.setAddress(accountAddress); - } else if ('address' in accountAddress) { - getAddressInfoRequest.setAddress(accountAddress.address); - } else if ('credId' in accountAddress) { - getAddressInfoRequest.setAddress(accountAddress.credId); - } else { - throw new Error('Invalid accountAddress input'); - } - - getAddressInfoRequest.setBlockHash(blockHash); - - const response = await this.sendRequest( - this.client.getAccountInfo, - getAddressInfoRequest - ); - const datePropertyKeys: ( - | keyof ReleaseSchedule - | keyof ReduceStakePendingChangeV1 - )[] = ['timestamp', 'effectiveTime']; - const bigIntPropertyKeys: ( - | keyof AccountInfo - | keyof AccountEncryptedAmount - | keyof AccountReleaseSchedule - | keyof ReleaseSchedule - | keyof AccountBakerDetails - | keyof ReduceStakePendingChangeV0 - )[] = [ - 'accountAmount', - 'accountNonce', - 'accountIndex', - 'startIndex', - 'total', - 'amount', - 'stakedAmount', - 'bakerId', - 'newStake', - 'epoch', - ]; - return unwrapJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves the next account nonce for the given account. The account nonce is - * used in all account transactions as part of their header. - * @param accountAddress base58 account address to get the next account nonce for - * @returns the next account nonce, and a boolean indicating if the nonce is reliable - */ - async getNextAccountNonce( - accountAddress: Address - ): Promise { - const accountAddressObject = new AccountAddress(); - accountAddressObject.setAccountAddress(accountAddress.address); - - const response = await this.sendRequest( - this.client.getNextAccountNonce, - accountAddressObject - ); - - const bigIntPropertyKeys: (keyof NextAccountNonce)[] = ['nonce']; - - return unwrapJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves a status for the given transaction. - * @param transactionHash the transaction to get a status for - * @returns the transaction status for the given transaction, or undefined if the transaction does not exist - */ - async getTransactionStatus( - transactionHash: string - ): Promise { - if (!isValidHash(transactionHash)) { - throw new Error( - 'The input was not a valid hash: ' + transactionHash - ); - } - - const bigIntPropertyKeys: (keyof TransactionSummary)[] = [ - 'cost', - 'energyCost', - 'index', - ]; - - const transactionHashObject = new TransactionHash(); - transactionHashObject.setTransactionHash(transactionHash); - const response = await this.sendRequest( - this.client.getTransactionStatus, - transactionHashObject - ); - return unwrapJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves the block summary for a specific block. This contains information - * about finalization, update sequence numbers (their nonce), update queues, - * updateable chain parameters and transaction summaries for any transaction - * in the block. - * @param blockHash the block to get the summary for - * @returns the block summary for the given block, or undefined if the block does not exist - */ - async getBlockSummary( - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const blockHashObject = new BlockHash(); - blockHashObject.setBlockHash(blockHash); - - const response = await this.sendRequest( - this.client.getBlockSummary, - blockHashObject - ); - - const bigIntPropertyKeys: ( - | keyof PartyInfo - | keyof FinalizationData - | keyof TransactionSummary - | keyof (ChainParametersV0 & ChainParametersV1 & ChainParametersV2) - | keyof BlockSummary - | keyof PoolParametersV1 - | keyof CooldownParametersV1 - | keyof TimeParametersV1 - | keyof ExchangeRate - | keyof UpdateQueue - | keyof KeysWithThreshold - | keyof TransferredEvent - | keyof ContractAddress - | keyof DelegationStakeChangedEvent - | keyof ConsensusParameters - | keyof TimeoutParameters - | keyof Ratio - )[] = [ - 'bakerId', - 'newStake', - 'weight', - 'finalizationIndex', - 'finalizationDelay', - 'cost', - 'energyCost', - 'index', - 'numerator', - 'denominator', - 'nextSequenceNumber', - 'amount', - 'index', - 'subindex', - 'protocolVersion', - 'foundationAccountIndex', - - // v0 keys - 'bakerCooldownEpochs', - 'minimumThresholdForBaking', - - // v1 keys - 'rewardPeriodLength', - 'minimumEquityCapital', - 'poolOwnerCooldown', - 'delegatorCooldown', - - // v2 keys - 'timeoutBase', - 'denominator', - 'numerator', - 'minBlockTime', - 'blockEnergyLimit', - ]; - - return unwrapJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys) - ); - } - - /** - * Retrieves information about a specific block. - * @param blockHash the block to get information about - * @returns the block information for the given block, or undefined if the block does not exist - */ - async getBlockInfo(blockHash: string): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const blockHashObject = new BlockHash(); - blockHashObject.setBlockHash(blockHash); - const response = await this.sendRequest( - this.client.getBlockInfo, - blockHashObject - ); - - const datePropertyKeys: (keyof BlockInfo)[] = [ - 'blockArriveTime', - 'blockReceiveTime', - 'blockSlotTime', - ]; - const bigIntPropertyKeys: (keyof (BlockInfoV0 & BlockInfoV1))[] = [ - 'blockHeight', - 'blockBaker', - 'blockSlot', - 'transactionEnergyCost', - 'transactionCount', - 'transactionsSize', - ]; - - return unwrapJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieves the blocks are the given height. - * @param height the block height as a positive integer - * @returns a string array containing the blocks at the given height, i.e. ['blockHash1', 'blockHash2', ...] - */ - async getBlocksAtHeight(height: bigint): Promise { - if (height <= 0n) { - throw new Error( - 'The block height has to be a positive integer, but it was: ' + - height - ); - } - const blockHeight = new BlockHeight(); - blockHeight.setBlockHeight(height.toString()); - const response = await this.sendRequest( - this.client.getBlocksAtHeight, - blockHeight - ); - - const blocksAtHeight = unwrapJsonResponse(response); - if (!blocksAtHeight) { - return []; - } - return blocksAtHeight; - } - - /** - * Retrieves the consensus status information from the node. Note that the optional - * fields will only be unavailable for a newly started node that has not processed - * enough data yet. - */ - async getConsensusStatus(): Promise { - type CS = ConsensusStatusV0 & ConsensusStatusV1; - const response = await this.sendRequest( - this.client.getConsensusStatus, - new Empty() - ); - - const datePropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'blockLastReceivedTime', - 'blockLastArrivedTime', - 'genesisTime', - 'currentEraGenesisTime', - 'lastFinalizedTime', - - //v1 - 'triggerBlockTime', - ]; - const bigIntPropertyKeys: (keyof CS | keyof ConcordiumBftStatus)[] = [ - 'epochDuration', - 'slotDuration', - 'bestBlockHeight', - 'lastFinalizedBlockHeight', - 'finalizationCount', - 'blocksVerifiedCount', - 'blocksReceivedCount', - 'protocolVersion', - - // v1 - 'currentTimeoutDuration', - 'currentRound', - 'currentEpoch', - ]; - - const consensusStatus = unwrapJsonResponse( - response, - buildJsonResponseReviver(datePropertyKeys, bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - - if (!consensusStatus) { - throw new Error( - 'Nothing was returned when trying to get the consensus status.' - ); - } - - return consensusStatus; - } - - /** - * Retrieves the global cryptographic parameters on the blockchain at - * the provided block. - * @param blockHash the block to get the cryptographic parameters at - * @returns the global cryptographic parameters at the given block, or undefined it the block does not exist. - */ - async getCryptographicParameters( - blockHash: string - ): Promise | undefined> { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const blockHashObject = new BlockHash(); - blockHashObject.setBlockHash(blockHash); - const response = await this.sendRequest( - this.client.getCryptographicParameters, - blockHashObject - ); - - return unwrapJsonResponse< - Versioned | undefined - >(response); - } - - /** - * Retrieves a list of the node's peers and connection information related to them. - * @param includeBootstrappers whether or not any bootstrapper nodes should be included in the list - * @returns a list of the node's peers and connection information related to them - */ - async getPeerList( - includeBootstrappers: boolean - ): Promise { - const peersRequest = new PeersRequest(); - peersRequest.setIncludeBootstrappers(includeBootstrappers); - const response = await this.sendRequest( - this.client.peerList, - peersRequest - ); - return PeerListResponse.deserializeBinary(response); - } - - /** - * Retrieves the list of identity providers at the provided blockhash. - * @param blockHash the block to get the identity providers at - * @returns the list of identity providers at the given block - */ - async getIdentityProviders( - blockHash: string - ): Promise { - const blockHashObject = new BlockHash(); - blockHashObject.setBlockHash(blockHash); - const response = await this.sendRequest( - this.client.getIdentityProviders, - blockHashObject - ); - return unwrapJsonResponse(response); - } - - /** - * Retrieves the list of anonymity revokers at the provided blockhash. - * @param blockHash the block to get the anonymity revokers at - * @returns the list of anonymity revokers at the given block - */ - async getAnonymityRevokers( - blockHash: string - ): Promise { - const blockHashObject = new BlockHash(); - blockHashObject.setBlockHash(blockHash); - const response = await this.sendRequest( - this.client.getAnonymityRevokers, - blockHashObject - ); - return unwrapJsonResponse(response); - } - - /** - * Retrieves the addresses of all smart contract instances. - * @param blockHash the block hash to get the smart contact instances at - * @returns a list of contract addresses on the chain, i.e. [{"subindex":0,"index":0},{"subindex":0,"index":1}, ....] - */ - async getInstances( - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - const blockHashObject = new BlockHash(); - blockHashObject.setBlockHash(blockHash); - - const response = await this.sendRequest( - this.client.getInstances, - blockHashObject - ); - const bigIntPropertyKeys: (keyof ContractAddress)[] = [ - 'index', - 'subindex', - ]; - - return unwrapJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - /** - * Retrieve information about a given smart contract instance. - * @param blockHash the block hash to get the smart contact instances at - * @param address the address of the smart contract - * @returns A JSON object with information about the contract instance - */ - async getInstanceInfo( - address: ContractAddress, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - const getAddressInfoRequest = new GetAddressInfoRequest(); - getAddressInfoRequest.setAddress( - `{"subindex":${address.subindex},"index":${address.index}}` - ); - getAddressInfoRequest.setBlockHash(blockHash); - - const response = await this.sendRequest( - this.client.getInstanceInfo, - getAddressInfoRequest - ); - - const result = unwrapJsonResponse(response); - if (result !== undefined) { - const common = { - amount: new CcdAmount(BigInt(result.amount)), - sourceModule: new ModuleReference(result.sourceModule), - owner: new Address(result.owner), - methods: result.methods, - name: result.name, - }; - - switch (result.version) { - case 1: - return { - version: 1, - ...common, - }; - case undefined: - case 0: - return { - version: 0, - ...common, - model: BufferFormater.from(result.model, 'hex'), - }; - default: - throw new Error( - 'InstanceInfo had unsupported version: ' + - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (result as any).version - ); - } - } - } - - async getRewardStatus( - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - type DateKey = KeysMatching; - type BigIntKey = KeysMatching; - - const dates: DateKey[] = ['nextPaydayTime']; - const bigInts: BigIntKey[] = [ - 'protocolVersion', - 'gasAccount', - 'totalAmount', - 'totalStakedCapital', - 'bakingRewardAccount', - 'totalEncryptedAmount', - 'finalizationRewardAccount', - 'foundationTransactionRewards', - ]; - - const bh = new BlockHash(); - bh.setBlockHash(blockHash); - - const response = await this.sendRequest( - this.client.getRewardStatus, - bh - ); - - return unwrapJsonResponse( - response, - buildJsonResponseReviver(dates, bigInts), - intToStringTransformer(bigInts) - ); - } - - /** - * Retrieve list of bakers on the network. - * @param blockHash the block hash to get the smart contact instances at - * @returns A JSON list of baker IDs - */ - async getBakerList(blockHash: string): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const bh = new BlockHash(); - bh.setBlockHash(blockHash); - - const response = await this.sendRequest(this.client.getBakerList, bh); - - return unwrapJsonResponse( - response, - undefined, - intListToStringList - )?.map((v) => BigInt(v)); - } - - /** - * Gets the status of passive delegation. - * @param blockHash the block hash the status at - * @returns The status of passive delegation. - */ - async getPoolStatus( - blockHash: string - ): Promise; - /** - * Gets the status a baker. - * @param blockHash the block hash the status at - * @param bakerId the ID of the baker to get the status for. - * @returns The status of the corresponding baker pool. - */ - async getPoolStatus( - blockHash: string, - bakerId: BakerId - ): Promise; - /** - * Gets the status of either a baker, if a baker ID is supplied, or passive delegation if left undefined. - * @param blockHash the block hash the status at - * @param [bakerId] the ID of the baker to get the status for. If left undefined, the status of passive delegation is returned. - * @returns The status of the corresponding pool. - */ - async getPoolStatus( - blockHash: string, - bakerId?: BakerId - ): Promise; - async getPoolStatus( - blockHash: string, - bakerId?: BakerId - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const req = new GetPoolStatusRequest(); - req.setBlockHash(blockHash); - req.setPassiveDelegation(bakerId === undefined); - - if (bakerId !== undefined) { - req.setBakerId(bakerId.toString()); - } - - type DateKey = KeysMatching< - BakerPoolPendingChangeReduceBakerCapitalDetails, - Date - >; - type BigIntKey = KeysMatching< - BakerPoolStatusDetails & - PassiveDelegationStatusDetails & - CurrentPaydayBakerPoolStatus, - bigint - >; - - const dates: DateKey[] = ['effectiveTime']; - const bigInts: BigIntKey[] = [ - 'bakerId', - 'bakerEquityCapital', - 'delegatedCapital', - 'delegatedCapitalCap', - 'currentPaydayTransactionFeesEarned', - 'currentPaydayDelegatedCapital', - 'blocksBaked', - 'transactionFeesEarned', - 'effectiveStake', - 'allPoolTotalCapital', - ]; - - const response = await this.sendRequest(this.client.getPoolStatus, req); - - return unwrapJsonResponse( - response, - buildJsonResponseReviver(dates, bigInts), - intToStringTransformer(bigInts) - ); - } - - /** - * Retrieves the source of the given module at - * the provided block. - * @param moduleReference the module's reference, which is the hex encoded hash of the source. - * @param blockHash the block to get the cryptographic parameters at - * @returns the source of the module as raw bytes. - */ - async getModuleSource( - moduleReference: ModuleReference, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - const requestObject = new GetModuleSourceRequest(); - requestObject.setBlockHash(blockHash); - requestObject.setModuleRef(moduleReference.moduleRef); - - const response = await this.sendRequest( - this.client.getModuleSource, - requestObject - ); - - if (response.length === 0) { - return undefined; - } - return Buffer.from(response); - } - - /** - * Invokes a smart contract. - * @param context the collection of details used to invoke the contract. Must include the address of the contract and the method invoked. - * @param blockHash the block hash at which the contract should be invoked at. The contract is invoked in the state at the end of this block. - * @returns If the node was able to invoke, then a object describing the outcome is returned. - * The outcome is determined by the `tag` field, which is either `success` or `failure`. - * The `usedEnergy` field will always be present, and is the amount of NRG was used during the execution. - * If the tag is `success`, then an `events` field is present, and it contains the events that would have been generated. - * If invoking a V1 contract and it produces a return value, it will be present in the `returnValue` field. - * If the tag is `failure`, then a `reason` field is present, and it contains the reason the update would have been rejected. - * If either the block does not exist, or then node fails to parse of any of the inputs, then undefined is returned. - */ - async invokeContract( - contractContext: ContractContext, - blockHash: string - ): Promise { - if (!isValidHash(blockHash)) { - throw new Error('The input was not a valid hash: ' + blockHash); - } - - const requestObject = new InvokeContractRequest(); - requestObject.setBlockHash(blockHash); - requestObject.setContext( - stringToInt( - JSON.stringify({ - invoker: buildInvoker(contractContext.invoker), - contract: { - subindex: contractContext.contract.subindex.toString(), - index: contractContext.contract.index.toString(), - }, - amount: - contractContext.amount && - contractContext.amount.microCcdAmount.toString(), - method: contractContext.method, - parameter: - contractContext.parameter && - contractContext.parameter.toString('hex'), - energy: - contractContext.energy && - Number(contractContext.energy.toString()), - }), - ['index', 'subindex'] - ) - ); - - const response = await this.sendRequest( - this.client.invokeContract, - requestObject - ); - const bigIntPropertyKeys = [ - 'usedEnergy', - 'index', - 'subindex', - 'amount', - ]; - return unwrapJsonResponse( - response, - buildJsonResponseReviver([], bigIntPropertyKeys), - intToStringTransformer(bigIntPropertyKeys) - ); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - sendRequest(command: any, input: T): Promise { - const deadline = new Date(Date.now() + this.timeout); - return new Promise((resolve, reject) => { - this.client.waitForReady(deadline, (error) => { - if (error) { - return reject(error); - } - - return command.bind(this.client)( - input, - this.metadata, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (err: ServiceError | null, response: any) => { - if (err) { - return reject(err); - } - return resolve(response.serializeBinary()); - } - ); - }); - }); - } -} diff --git a/packages/nodejs/src/clientV2.ts b/packages/nodejs/src/clientV2.ts deleted file mode 100644 index cb2ac0303..000000000 --- a/packages/nodejs/src/clientV2.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ChannelCredentials } from '@grpc/grpc-js'; -import { GrpcOptions, GrpcTransport } from '@protobuf-ts/grpc-transport'; -import { ConcordiumGRPCClient } from '@concordium/common-sdk/lib/GRPCClient'; - -/** - * Initialize a gRPC client for a specific concordium node. - * @param address the ip address of the node, e.g. http://127.0.0.1 - * @param port the port to use when econnecting to the node - * @param credentials channel credentials for communicating with the node - * @param options optional options for the grpc transport - */ -export function createConcordiumClient( - address: string, - port: number, - credentials: ChannelCredentials, - options?: Partial -): ConcordiumGRPCClient { - const grpcTransport = new GrpcTransport({ - host: `${address}:${port}`, - channelCredentials: credentials, - ...options, - }); - return new ConcordiumGRPCClient(grpcTransport); -} diff --git a/packages/nodejs/src/index.ts b/packages/nodejs/src/index.ts deleted file mode 100644 index a7f69149c..000000000 --- a/packages/nodejs/src/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * A concordium-node specific gRPC client wrapper. - * - * @module NodeJS-SDK - * - */ - -import ConcordiumNodeClient from './client'; -export * from './clientV2'; - -export { ConcordiumNodeClient }; -export { decryptMobileWalletExport, EncryptedData } from './wallet/crypto'; -export { MobileWalletExport } from './wallet/types'; -export * from '@concordium/common-sdk'; -export { getModuleBuffer } from './util'; diff --git a/packages/nodejs/src/util.ts b/packages/nodejs/src/util.ts deleted file mode 100644 index 562471977..000000000 --- a/packages/nodejs/src/util.ts +++ /dev/null @@ -1,55 +0,0 @@ -import * as fs from 'fs'; -import { Buffer } from 'buffer/'; -import { BoolResponse, JsonResponse } from '../grpc/concordium_p2p_rpc_pb'; - -/** - * @deprecated This is a helper function for the v1 gRPC client, which has been deprecated - */ -export function intListToStringList(jsonStruct: string): string { - return jsonStruct.replace(/(\-?[0-9]+)/g, '"$1"'); -} - -/** - * Unwraps a serialized bool response to the corresponding boolean. - * @deprecated This is a helper function for the v1 gRPC client, which has been deprecated - */ -export function unwrapBoolResponse(serializedResponse: Uint8Array): boolean { - return BoolResponse.deserializeBinary(serializedResponse).getValue(); -} - -/** - * Unwraps a serialized JSON response. - * @param serializedResponse the JSON response in bytes as received from the gRPC call - * @param reviver JSON reviver function to change types while parsing - * @param transformer a function to transform the JSON string prior to parsing the JSON - * @returns the unwrapped, transformed and parsed JSON object - * @deprecated This is a helper function for the v1 gRPC client, which has been deprecated - */ -export function unwrapJsonResponse( - serializedResponse: Uint8Array, - reviver?: (this: unknown, key: string, value: unknown) => unknown, - transformer?: (json: string) => string -): T | undefined { - const jsonString = - JsonResponse.deserializeBinary(serializedResponse).getValue(); - - if (jsonString === 'null') { - return undefined; - } - - if (transformer) { - const transformedJson = transformer(jsonString); - return JSON.parse(transformedJson, reviver); - } - - return JSON.parse(jsonString, reviver); -} - -/** - * Loads the module as a buffer, given the given filePath. - * @param filePath the location of the module - * @returns the module as a buffer - */ -export function getModuleBuffer(filePath: string): Buffer { - return Buffer.from(fs.readFileSync(filePath)); -} diff --git a/packages/nodejs/test/client.test.ts b/packages/nodejs/test/client.test.ts deleted file mode 100644 index 81653e540..000000000 --- a/packages/nodejs/test/client.test.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { - isBlockSummaryV0, - isBlockSummaryV1, - isBlockSummaryV2, - isConsensusStatusV0, - isConsensusStatusV1, -} from '@concordium/common-sdk'; -import { getNodeClient } from './testHelpers'; - -/** - * These tests mostly serve the purpose of making sure that the types exposed follow the format returned by the API, - * i.e. we don't change the types to conform to the v2 API, but rather translate the v2 types to the v1 types until we can - * remove the v1 API entirely. - */ - -const client = getNodeClient(); - -// eslint-disable-next-line prefer-const -let CHAIN_GENESIS_BLOCK: string | undefined = undefined; -// eslint-disable-next-line prefer-const -let PV5_BLOCK: string | undefined = undefined; -// eslint-disable-next-line prefer-const -let PV6_BLOCK: string | undefined = undefined; - -// Testnet blocks. -CHAIN_GENESIS_BLOCK = - '4221332d34e1694168c2a0c0b3fd0f273809612cb13d000d5c2e00e85f50f796'; -PV5_BLOCK = '58daebb41ca195442593e10c1a67279bb839a8195c8ea7442ea7116d87114fbb'; - -test.each([CHAIN_GENESIS_BLOCK, PV5_BLOCK, PV6_BLOCK])( - 'blockSummary format as expected', - async (block) => { - if (block === undefined) { - return; - } - - const bs = await client.getBlockSummary(block); - - if (!bs) { - throw new Error('could not find block'); - } - - // BlockSummary - expect(typeof bs.protocolVersion).toEqual('bigint'); - - // UpdateQueues - expect( - typeof bs.updates.updateQueues.foundationAccount.nextSequenceNumber - ).toEqual('bigint'); - expect( - Array.isArray(bs.updates.updateQueues.foundationAccount.queue) - ).toBeTruthy(); - expect( - typeof bs.updates.updateQueues.euroPerEnergy.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.mintDistribution.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.microGTUPerEuro.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.protocol.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.rootKeys.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.gasRewards.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.level1Keys.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.level2Keys.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.addAnonymityRevoker - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.addIdentityProvider - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.transactionFeeDistribution - .nextSequenceNumber - ).toEqual('bigint'); - - // Keys - expect(bs.updates.keys.rootKeys.keys).toBeDefined(); - expect(bs.updates.keys.rootKeys.threshold).toBeDefined(); - expect(bs.updates.keys.level1Keys.keys).toBeDefined(); - expect(bs.updates.keys.level2Keys.keys).toBeDefined(); - expect( - bs.updates.keys.level2Keys.addAnonymityRevoker.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.poolParameters.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.transactionFeeDistribution.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.addIdentityProvider.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.protocol.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.microGTUPerEuro.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.mintDistribution.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.euroPerEnergy.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.foundationAccount.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.electionDifficulty.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.emergency.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.paramGASRewards.authorizedKeys - ).toBeDefined(); - - // Test format of chain parameters holds - expect( - typeof bs.updates.chainParameters.microGTUPerEuro.numerator - ).toEqual('bigint'); - expect( - typeof bs.updates.chainParameters.microGTUPerEuro.denominator - ).toEqual('bigint'); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards.baker - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards.chainUpdate - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards - .accountCreation - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.mintDistribution - .bakingReward - ).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.mintDistribution - .finalizationReward - ).toBeDefined(); - expect(bs.updates.chainParameters.euroPerEnergy).toBeDefined(); - expect(bs.updates.chainParameters.foundationAccountIndex).toBeDefined(); - expect(bs.updates.chainParameters.accountCreationLimit).toBeDefined(); - - if (isBlockSummaryV0(bs)) { - expect( - typeof bs.updates.updateQueues.electionDifficulty - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.bakerStakeThreshold - .nextSequenceNumber - ).toEqual('bigint'); - - expect( - typeof bs.updates.chainParameters.bakerCooldownEpochs - ).toEqual('bigint'); - } else if (isBlockSummaryV1(bs)) { - expect( - typeof bs.updates.updateQueues.electionDifficulty - .nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.poolParameters.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.timeParameters.nextSequenceNumber - ).toEqual('bigint'); - expect( - typeof bs.updates.updateQueues.cooldownParameters - .nextSequenceNumber - ).toEqual('bigint'); - - expect( - bs.updates.keys.level2Keys.cooldownParameters.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.timeParameters.authorizedKeys - ).toBeDefined(); - - expect(bs.updates.chainParameters.electionDifficulty).toBeDefined(); - expect( - bs.updates.chainParameters.rewardParameters.gASRewards - .finalizationProof - ); - expect(bs.updates.chainParameters.mintPerPayday).toBeDefined(); - expect(bs.updates.chainParameters.capitalBound).toBeDefined(); - expect(bs.updates.chainParameters.leverageBound).toBeDefined(); - expect( - bs.updates.chainParameters.finalizationCommissionRange.min - ).toBeDefined(); - } else if (isBlockSummaryV2(bs)) { - expect( - typeof bs.updates.updateQueues.consensus2TimingParameters - .nextSequenceNumber - ).toEqual('bigint'); - - expect( - bs.updates.keys.level2Keys.cooldownParameters.authorizedKeys - ).toBeDefined(); - expect( - bs.updates.keys.level2Keys.timeParameters.authorizedKeys - ).toBeDefined(); - - expect(bs.updates.chainParameters.minimumFinalizers).toBeDefined(); - expect(bs.updates.chainParameters.maximumFinalizers).toBeDefined(); - expect(typeof bs.updates.chainParameters.blockEnergyLimit).toEqual( - 'bigint' - ); - expect(typeof bs.updates.chainParameters.minBlockTime).toEqual( - 'bigint' - ); - expect(typeof bs.updates.chainParameters.timeoutBase).toEqual( - 'bigint' - ); - expect( - typeof bs.updates.chainParameters.timeoutDecrease.numerator - ).toEqual('bigint'); - expect( - typeof bs.updates.chainParameters.timeoutIncrease.denominator - ).toEqual('bigint'); - expect( - bs.updates.chainParameters.finalizerRelativeStakeThreshold - ).toBeDefined(); - } - } -); - -test('consensusStatus format as expected', async () => { - const cs = await client.getConsensusStatus(); - - expect(typeof cs.protocolVersion).toEqual('bigint'); - expect(cs.bestBlock).toBeDefined(); - expect(cs.genesisTime instanceof Date).toBeTruthy(); - expect(cs.genesisBlock).toBeDefined(); - expect(cs.genesisIndex).toBeDefined(); - expect(typeof cs.epochDuration).toEqual('bigint'); - expect(typeof cs.bestBlockHeight).toEqual('bigint'); - expect(typeof cs.finalizationCount).toEqual('bigint'); - expect(cs.lastFinalizedBlock).toBeDefined(); - expect(typeof cs.blocksReceivedCount).toEqual('bigint'); - expect(typeof cs.blocksVerifiedCount).toEqual('bigint'); - expect(cs.blockArriveLatencyEMA).toBeDefined(); - expect(cs.blockLastArrivedTime instanceof Date).toBeTruthy(); - expect(cs.currentEraGenesisTime instanceof Date).toBeTruthy(); - expect(cs.blockArriveLatencyEMSD).toBeDefined(); - expect(cs.currentEraGenesisBlock).toBeDefined(); - expect(cs.transactionsPerBlockEMA).toBeDefined(); - expect(typeof cs.lastFinalizedBlockHeight).toEqual('bigint'); - expect(cs.transactionsPerBlockEMSD).toBeDefined(); - - if (isConsensusStatusV0(cs)) { - expect(typeof cs.slotDuration).toEqual('bigint'); - } else if (isConsensusStatusV1(cs)) { - expect(typeof cs.concordiumBFTStatus.currentTimeoutDuration).toEqual( - 'bigint' - ); - expect(typeof cs.concordiumBFTStatus.currentEpoch).toEqual('bigint'); - expect(typeof cs.concordiumBFTStatus.currentRound).toEqual('bigint'); - expect( - cs.concordiumBFTStatus.triggerBlockTime instanceof Date - ).toBeTruthy(); - } -}); diff --git a/packages/nodejs/test/util.test.ts b/packages/nodejs/test/util.test.ts deleted file mode 100644 index 94998daad..000000000 --- a/packages/nodejs/test/util.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { intListToStringList } from '../src/util'; - -test('Correctly converts stringified list of numbers to stringified list of corresponding strings', () => { - // List of ints - let numbers = '[1, 22, 332]'; - let strings = intListToStringList(numbers); - - expect(strings).toEqual('["1", "22", "332"]'); - - // Empty list - numbers = '[]'; - strings = intListToStringList(numbers); - - expect(strings).toEqual('[]'); - - // Single int list - numbers = '[1]'; - strings = intListToStringList(numbers); - - expect(strings).toEqual('["1"]'); - - // negative int list - numbers = '[-1, 21, -32]'; - strings = intListToStringList(numbers); - - expect(strings).toEqual('["-1", "21", "-32"]'); -}); diff --git a/packages/nodejs/tsconfig.eslint.json b/packages/nodejs/tsconfig.eslint.json deleted file mode 100644 index 911b9eefa..000000000 --- a/packages/nodejs/tsconfig.eslint.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "include": ["src/**/*", "test/**/*"] -} \ No newline at end of file diff --git a/packages/nodejs/tsconfig.jest.json b/packages/nodejs/tsconfig.jest.json deleted file mode 100644 index d3ce9180d..000000000 --- a/packages/nodejs/tsconfig.jest.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "./tsconfig", - "compilerOptions": { - "module": "CommonJS" - }, - "exclude": ["lib/pkg/**/*"] -} diff --git a/packages/nodejs/tsconfig.json b/packages/nodejs/tsconfig.json deleted file mode 100644 index b62f8da08..000000000 --- a/packages/nodejs/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig-base.json", - "include": ["src/**/*"], - "references": [ - { "path": "../common"} - ], - "compilerOptions": { - "rootDir": "src", - "outDir": "./lib" - } -} diff --git a/packages/rust-bindings/CHANGELOG.md b/packages/rust-bindings/CHANGELOG.md index e90c74dae..22e033740 100644 --- a/packages/rust-bindings/CHANGELOG.md +++ b/packages/rust-bindings/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 2.0.0 + +### Breaking changes + +- The package has been split into two entrypoints to decrease the size of bundles produced for applications using only part of the functionality provided. + - `@concordium/rust-bindings` (and its alias `@concordium/rust-bindings/dapp`) entrypoints expose functionality commonly used by dApps. + - `@concordium/rust-bindings/wallet` entrypoint exposes functionality commonly used by wallets and other applications requiring functionality used in wallets. + - If using a bundler, it might be preferable to load the WASM module asynchronously instead of the version which has it inlined. This can be done + by adding an alias to your bundler resolve configuration from `@concordium/rust-bindings` to `@concordium/rust-bindings/bundler`. + - This change makes the library **incompatible** with node versions <16 and requires bundlers to respect the `exports` field of `package.json`. + - For TypeScript projects the minimum required version of typescript is: + - NodeJS: 4.7, `"moduleResolution": "node16" // or "nodenext"` + - Bundled applications (webpack, esbuild, rollup, etc...): 5.0, `"moduleResolution": "bundler"` + ## 1.2.0 ### Added @@ -102,7 +116,7 @@ ### Changes -- Bindings for the HdWallet methods: `getAccountSigningKey`, `getAccountPublicKey`, `getPrfKey`, `getSignatureBlindingRandomness` and `getAttributeCommitmentRandomness` now takes the identity provider index as parameter. +- Bindings for the HdWallet methods: `getAccountSigningKey`, `getAccountPublicKey`, `getPrfKey`, `getSignatureBlindingRandomness` and `getAttributeCommitmentRandomness` now takes the identity provider index as parameter. ## 0.3.0 2022-8-15 @@ -111,4 +125,4 @@ - `createCredentialV1` - `createIdRequestV1` - `createIdentityRecoveryRequest` -- Bindings for the HdWallet methods: `getAccountSigningKey`, `getAccountPublicKey`, `getPrfKey`, `getSignatureBlindingRandomness` and `getAttributeCommitmentRandomness`. +- Bindings for the HdWallet methods: `getAccountSigningKey`, `getAccountPublicKey`, `getPrfKey`, `getSignatureBlindingRandomness` and `getAttributeCommitmentRandomness`. diff --git a/packages/rust-bindings/Cargo.lock b/packages/rust-bindings/Cargo.lock index 670324ebe..abcb9c1d5 100644 --- a/packages/rust-bindings/Cargo.lock +++ b/packages/rust-bindings/Cargo.lock @@ -26,9 +26,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" dependencies = [ "memchr", ] @@ -50,9 +50,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.71" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayvec" @@ -68,9 +68,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "base64ct" @@ -198,15 +198,18 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -216,18 +219,17 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.26" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" +checksum = "defd4e7873dbddba6c7c91e199c7fcb946abc4a6a4ac3195400bcfb01b5de877" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "serde", - "time 0.1.45", "wasm-bindgen", - "winapi", + "windows-targets", ] [[package]] @@ -241,7 +243,7 @@ dependencies = [ "fnv", "hashbrown 0.11.2", "hex", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-integer", "num-traits", "rust_decimal", @@ -256,28 +258,7 @@ version = "4.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", -] - -[[package]] -name = "concordium-rust-bindings" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "concordium_base", - "ed25519-dalek", - "ed25519_hd_key_derivation", - "either", - "hex", - "key_derivation", - "rand 0.7.3", - "serde", - "serde-wasm-bindgen", - "serde_json", - "serde_with", - "thiserror", - "wasm-bindgen", + "syn 2.0.32", ] [[package]] @@ -302,7 +283,7 @@ dependencies = [ "libc", "nom", "num", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-traits", "pairing", "rand 0.7.3", @@ -325,7 +306,49 @@ version = "1.0.0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", +] + +[[package]] +name = "concordium_rust_bindings_common" +version = "0.1.0" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "concordium_rust_bindings_dapp" +version = "0.1.0" +dependencies = [ + "anyhow", + "concordium_base", + "concordium_rust_bindings_common", + "hex", + "serde-wasm-bindgen", + "serde_json", + "wasm-bindgen", +] + +[[package]] +name = "concordium_rust_bindings_wallet" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "concordium_base", + "concordium_rust_bindings_common", + "ed25519-dalek", + "ed25519_hd_key_derivation", + "either", + "hex", + "key_derivation", + "rand 0.7.3", + "serde", + "serde-wasm-bindgen", + "serde_json", + "serde_with", + "thiserror", + "wasm-bindgen", ] [[package]] @@ -417,9 +440,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0558d22a7b463ed0241e993f76f09f30b126687447751a8638587b864e4b3944" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" dependencies = [ "darling_core", "darling_macro", @@ -427,27 +450,36 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8bfa2e259f8ee1ce5e97824a3c55ec4404a0d772ca7fa96bf19f0752a046eb" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] name = "darling_macro" -version = "0.20.1" +version = "0.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.23", + "syn 2.0.32", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +dependencies = [ + "serde", ] [[package]] @@ -520,9 +552,15 @@ dependencies = [ [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "ff" @@ -630,6 +668,12 @@ dependencies = [ "ahash 0.8.3", ] +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + [[package]] name = "hermit-abi" version = "0.3.2" @@ -700,6 +744,17 @@ dependencies = [ "serde", ] +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", + "serde", +] + [[package]] name = "itertools" version = "0.10.5" @@ -711,9 +766,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" @@ -773,15 +828,15 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memoffset" @@ -810,11 +865,11 @@ dependencies = [ [[package]] name = "num" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" dependencies = [ - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-complex", "num-integer", "num-iter", @@ -835,9 +890,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -846,9 +901,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" dependencies = [ "num-traits", ] @@ -881,16 +936,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", - "num-bigint 0.4.3", + "num-bigint 0.4.4", "num-integer", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -969,9 +1024,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -998,9 +1053,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.29" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -1115,9 +1170,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", @@ -1127,9 +1182,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.0" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", @@ -1138,9 +1193,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rend" @@ -1181,14 +1236,12 @@ dependencies = [ [[package]] name = "rust_decimal" -version = "1.30.0" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0446843641c69436765a35a5a77088e28c2e6a12da93e84aa3ab1cd4aa5a042" +checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" dependencies = [ "arrayvec", "borsh", - "bytecheck", - "byteorder", "bytes", "num-traits", "rand 0.8.5", @@ -1208,15 +1261,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "seahash" @@ -1226,15 +1279,15 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.166" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] @@ -1252,20 +1305,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.166" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] name = "serde_json" -version = "1.0.100" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +checksum = "2cc66a619ed80bf7a0f6b17dd063a84b88f6dea1813737cf469aef1d081142c2" dependencies = [ "itoa", "ryu", @@ -1274,30 +1327,31 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.0.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" dependencies = [ "base64", "chrono", "hex", - "indexmap", + "indexmap 1.9.3", + "indexmap 2.0.0", "serde", "serde_json", "serde_with_macros", - "time 0.3.22", + "time", ] [[package]] name = "serde_with_macros" -version = "3.0.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] @@ -1371,9 +1425,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.23" +version = "2.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" dependencies = [ "proc-macro2", "quote", @@ -1388,41 +1442,31 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "1.0.41" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.41" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] [[package]] name = "time" -version = "0.1.45" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" dependencies = [ + "deranged", "itoa", "serde", "time-core", @@ -1437,9 +1481,9 @@ checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" dependencies = [ "time-core", ] @@ -1476,15 +1520,15 @@ checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-ident" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "uuid" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" [[package]] name = "version_check" @@ -1498,12 +1542,6 @@ version = "0.9.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1533,7 +1571,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", "wasm-bindgen-shared", ] @@ -1555,7 +1593,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1566,28 +1604,6 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows" version = "0.48.0" @@ -1599,9 +1615,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -1614,45 +1630,45 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "wyz" @@ -1680,5 +1696,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.32", ] diff --git a/packages/rust-bindings/Cargo.toml b/packages/rust-bindings/Cargo.toml index 37fcbd182..4fa3c8774 100644 --- a/packages/rust-bindings/Cargo.toml +++ b/packages/rust-bindings/Cargo.toml @@ -1,36 +1,30 @@ -[package] -name = "concordium-rust-bindings" -version = "0.1.0" +[workspace] +members = ["packages/*"] + +[workspace.package] authors = ["Concordium AG "] -edition = "2018" +edition = "2021" [profile.dev] opt-level = 3 -[dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -wasm-bindgen = { version = "0.2.80", features = ["serde-serialize"] } +[workspace.dependencies] anyhow = "1.0" -hex = "0.4" +concordium_rust_bindings_common = { path = "./packages/common" } either = "1.6" -thiserror = "1.0" -rand = { version = "=0.7", features = [ "wasm-bindgen" ] } -ed25519-dalek = { version = "=1.0" } -chrono = "0.4.24" -serde_with = "3.0.0" +hex = "0.4" +rand = { version = "=0.7", features = ["wasm-bindgen"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" serde-wasm-bindgen = "0.5" +thiserror = "1.0" +wasm-bindgen = { version = "0.2.80", features = ["serde-serialize"] } -[dependencies.concordium_base] +[workspace.dependencies.concordium_base] path = "../../deps/concordium-base/rust-src/concordium_base" -[dependencies.ed25519_hd_key_derivation] +[workspace.dependencies.ed25519_hd_key_derivation] path = "../../deps/concordium-base/rust-src/ed25519_hd_key_derivation" -[dependencies.key_derivation] +[workspace.dependencies.key_derivation] path = "../../deps/concordium-base/rust-src/key_derivation" - -[lib] -name = "concordium_rust_bindings" -crate-type = ["cdylib"] -path = "src/lib.rs" diff --git a/packages/rust-bindings/package.json b/packages/rust-bindings/package.json index 4cfed00cf..f0c883a42 100644 --- a/packages/rust-bindings/package.json +++ b/packages/rust-bindings/package.json @@ -1,26 +1,104 @@ { "name": "@concordium/rust-bindings", - "version": "1.2.0", + "version": "2.0.0", "license": "Apache-2.0", "engines": { - "node": ">=14.16.0" + "node": ">=16" }, "repository": { "type": "git", "url": "https://github.com/Concordium/concordium-node-sdk-js", "directory": "packages/rust-bindings" }, - "main": "pkg/node/concordium_rust_bindings.js", - "browser": "pkg/bundler/concordium_rust_bindings.js", - "types": "pkg/bundler/concordium_rust_bindings.d.ts", + "sideEffects": [ + "./lib/*/web/index.min.js", + "./lib/*/bundler/index.js" + ], + "main": "lib/dapp/node/cjs/index.js", + "browser": "lib/dapp/web/umd/index.min.js", + "types": "lib/dapp/node/cjs/index.d.ts", + "exports": { + ".": { + "types": "./lib/dapp/node/cjs/index.d.ts", + "node": { + "module": "./lib/dapp/node/umd/index.min.js", + "default": "./lib/dapp/node/cjs/index.js" + }, + "browser": { + "module": "./lib/dapp/web/umd/index.min.js", + "types": "./lib/dapp/web/esm/index.d.ts", + "import": "./lib/dapp/web/esm/index.js", + "default": "./lib/dapp/web/umd/index.min.js" + }, + "default": "./lib/dapp/web/umd/index.min.js" + }, + "./dapp": { + "types": "./lib/dapp/node/cjs/index.d.ts", + "node": { + "module": "./lib/dapp/node/umd/index.min.js", + "default": "./lib/dapp/node/cjs/index.js" + }, + "browser": { + "module": "./lib/dapp/web/umd/index.min.js", + "types": "./lib/dapp/web/esm/index.d.ts", + "import": "./lib/dapp/web/esm/index.js", + "default": "./lib/dapp/web/umd/index.min.js" + }, + "default": "./lib/dapp/web/umd/index.min.js" + }, + "./wallet": { + "types": "./lib/wallet/node/cjs/index.d.ts", + "node": { + "module": "./lib/wallet/node/umd/index.min.js", + "default": "./lib/wallet/node/cjs/index.js" + }, + "browser": { + "module": "./lib/wallet/web/umd/index.min.js", + "types": "./lib/wallet/web/esm/index.d.ts", + "import": "./lib/wallet/web/esm/index.js", + "default": "./lib/wallet/web/umd/index.min.js" + }, + "default": "./lib/wallet/web/umd/index.min.js" + }, + "./bundler": { + "types": "./lib/dapp/bundler/index.d.ts", + "default": "./lib/dapp/bundler/index.js" + }, + "./bundler/dapp": { + "types": "./lib/dapp/bundler/index.d.ts", + "default": "./lib/dapp/bundler/index.js" + }, + "./bundler/wallet": { + "types": "./lib/wallet/bundler/index.d.ts", + "default": "./lib/wallet/bundler/index.js" + } + }, "files": [ - "/pkg/**/concordium_rust_bindings*" + "./lib/**/index*" ], "scripts": { "fmt": "cargo +nightly-2023-04-01-x86_64-unknown-linux-gnu fmt -- --color=always --check", "clippy": "cargo +1.62 clippy --color=always --tests --benches -- -Dclippy::all", - "build": "wasm-pack build --target web --out-dir pkg/bundler \"$@\" && wasm-pack build --target nodejs --out-dir pkg/node \"$@\"", - "build-dev": "yarn build --dev", - "build:rust-bindings": "yarn build" + "build-web": "wasm-pack build ./packages/dapp --target web --out-dir $INIT_CWD/lib/dapp/web/esm --out-name index \"$@\" && wasm-pack build ./packages/wallet --target web --out-dir $INIT_CWD/lib/wallet/web/esm --out-name index \"$@\" && webpack", + "build-node": "wasm-pack build ./packages/dapp --target nodejs --out-dir $INIT_CWD/lib/dapp/node/cjs --out-name index \"$@\" && wasm-pack build ./packages/wallet --target nodejs --out-dir $INIT_CWD/lib/wallet/node/cjs --out-name index \"$@\"", + "build-bundler": "wasm-pack build ./packages/dapp --target bundler --out-dir $INIT_CWD/lib/dapp/bundler --out-name index \"$@\" && wasm-pack build ./packages/wallet --target bundler --out-dir $INIT_CWD/lib/wallet/bundler --out-name index \"$@\"", + "build": "yarn build-node \"$@\" && yarn build-bundler \"$@\" && yarn build-web \"$@\" && yarn sanitize", + "build:rust-bindings": "yarn build", + "clean": "rimraf -- target lib .webpack-cache", + "sanitize": "find . -name \"*.gitignore\" -delete", + "lint": "eslint . --cache --ext .ts,.tsx --max-warnings 0", + "lint-fix": "yarn --silent lint --fix; exit 0" + }, + "devDependencies": { + "eslint": "^8.50.0", + "rimraf": "^5.0.1", + "ts-loader": "^9.4.4", + "typescript": "^5.2.2", + "wasm-pack": "^0.12.1", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" + }, + "dependencies": { + "buffer": "^6.0.3" } } diff --git a/packages/rust-bindings/packages/common/Cargo.toml b/packages/rust-bindings/packages/common/Cargo.toml new file mode 100644 index 000000000..79ba561c0 --- /dev/null +++ b/packages/rust-bindings/packages/common/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "concordium_rust_bindings_common" +authors.workspace = true +version = "0.1.0" +edition.workspace = true + +[dependencies] +wasm-bindgen.workspace = true + +[lib] +name = "concordium_rust_bindings_common" +# crate-type = ["lib"] +path = "src/lib.rs" diff --git a/packages/rust-bindings/packages/common/src/helpers.rs b/packages/rust-bindings/packages/common/src/helpers.rs new file mode 100644 index 000000000..72d3eea8b --- /dev/null +++ b/packages/rust-bindings/packages/common/src/helpers.rs @@ -0,0 +1,8 @@ +use std::fmt::Display; +use wasm_bindgen::prelude::*; + +use crate::types::JsonString; + +pub type JsResult = Result; + +pub fn to_js_error(error: impl Display) -> JsError { JsError::new(&format!("{}", error)) } diff --git a/packages/rust-bindings/packages/common/src/lib.rs b/packages/rust-bindings/packages/common/src/lib.rs new file mode 100644 index 000000000..52af0f9cd --- /dev/null +++ b/packages/rust-bindings/packages/common/src/lib.rs @@ -0,0 +1,2 @@ +pub mod helpers; +pub mod types; diff --git a/packages/rust-bindings/packages/common/src/types.rs b/packages/rust-bindings/packages/common/src/types.rs new file mode 100644 index 000000000..bea6624ca --- /dev/null +++ b/packages/rust-bindings/packages/common/src/types.rs @@ -0,0 +1,3 @@ +pub type JsonString = String; +pub type HexString = String; +pub type Base58String = String; diff --git a/packages/rust-bindings/packages/dapp/Cargo.toml b/packages/rust-bindings/packages/dapp/Cargo.toml new file mode 100644 index 000000000..2db6c918d --- /dev/null +++ b/packages/rust-bindings/packages/dapp/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "concordium_rust_bindings_dapp" +authors.workspace = true +version = "0.1.0" +edition.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde_json.workspace = true +wasm-bindgen.workspace = true +anyhow.workspace = true +hex.workspace = true +serde-wasm-bindgen.workspace = true +concordium_base.workspace = true +concordium_rust_bindings_common.workspace = true + +[lib] +name = "concordium_rust_bindings_dapp" +crate-type = ["cdylib"] +path = "src/lib.rs" diff --git a/packages/rust-bindings/packages/dapp/src/aux_functions.rs b/packages/rust-bindings/packages/dapp/src/aux_functions.rs new file mode 100644 index 000000000..257906e90 --- /dev/null +++ b/packages/rust-bindings/packages/dapp/src/aux_functions.rs @@ -0,0 +1,198 @@ +use anyhow::{anyhow, Result}; +use concordium_base::contracts_common::{ + from_bytes, + schema::{ModuleV0, Type, VersionedModuleSchema}, + Cursor, +}; +use concordium_rust_bindings_common::types::{HexString, JsonString}; +use serde_json::{to_string, Value as SerdeValue}; + +/// Given the bytes of a contract's state, deserialize them to a json object, +/// using the provided schema. Both the state bytes and the schema are given as +/// hex-encoded strings. +pub fn deserialize_state_aux( + contract_name: &str, + state_bytes: HexString, + schema: HexString, + verbose_error_message: bool, +) -> Result { + let module_schema: ModuleV0 = match from_bytes(&hex::decode(schema)?) { + Ok(o) => o, + Err(e) => return Err(anyhow!("unable to parse schema: {:#?}", e)), + }; + let contract_schema = module_schema + .contracts + .get(contract_name) + .ok_or_else(|| anyhow!("Unable to get contract schema: not included in module schema"))?; + let state_schema = contract_schema + .state + .as_ref() + .ok_or_else(|| anyhow!("Unable to get state schema: not included in contract schema"))?; + + deserialize_type_value(state_bytes, state_schema, verbose_error_message) +} + +/// Given the bytes of a receive function's return value, deserialize them to a +/// json object, using the provided schema. +pub fn deserialize_receive_return_value_aux( + return_value_bytes: HexString, + schema: HexString, + contract_name: &str, + function_name: &str, + schema_version: Option, + verbose_error_message: bool, +) -> Result { + let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; + let return_value_schema = + module_schema.get_receive_return_value_schema(contract_name, function_name)?; + + deserialize_type_value( + return_value_bytes, + &return_value_schema, + verbose_error_message, + ) +} + +/// Given the bytes of a receive function's error, deserialize them to a json +/// object, using the provided schema. +pub fn deserialize_receive_error_aux( + error_bytes: HexString, + schema: HexString, + contract_name: &str, + function_name: &str, + verbose_error_message: bool, +) -> Result { + let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &None)?; + let error_schema = module_schema.get_receive_error_schema(contract_name, function_name)?; + + deserialize_type_value(error_bytes, &error_schema, verbose_error_message) +} + +/// Given the bytes of an init function's error, deserialize them to a json +/// object, using the provided schema. +pub fn deserialize_init_error_aux( + error_bytes: HexString, + schema: HexString, + contract_name: &str, + verbose_error_message: bool, +) -> Result { + let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &None)?; + let error_schema = module_schema.get_init_error_schema(contract_name)?; + + deserialize_type_value(error_bytes, &error_schema, verbose_error_message) +} + +/// Given parameters to a receive function as a stringified json, serialize them +/// using the provided schema. +pub fn serialize_receive_contract_parameters_aux( + parameters: JsonString, + schema: HexString, + contract_name: &str, + function_name: &str, + schema_version: Option, + verbose_error_message: bool, +) -> Result { + let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; + let parameter_type = module_schema.get_receive_param_schema(contract_name, function_name)?; + let value: SerdeValue = serde_json::from_str(¶meters)?; + + let buf = parameter_type + .serial_value(&value) + .map_err(|e| anyhow!("{}", e.display(verbose_error_message)))?; + + Ok(hex::encode(buf)) +} + +/// Given parameters to an init function as a stringified json, serialize them +/// using the provided schema. +pub fn serialize_init_contract_parameters_aux( + parameters: JsonString, + schema: HexString, + contract_name: &str, + schema_version: Option, + verbose_error_message: bool, +) -> Result { + let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; + let parameter_type = module_schema.get_init_param_schema(contract_name)?; + let value: SerdeValue = serde_json::from_str(¶meters)?; + + let buf = parameter_type + .serial_value(&value) + .map_err(|e| anyhow!("{}", e.display(verbose_error_message)))?; + + Ok(hex::encode(buf)) +} + +pub fn get_receive_contract_parameter_schema_aux( + schema: HexString, + contract_name: &str, + function_name: &str, + schema_version: Option, +) -> Result { + let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; + let parameter_type = module_schema.get_receive_param_schema(contract_name, function_name)?; + Ok(hex::encode(concordium_base::contracts_common::to_bytes( + ¶meter_type, + ))) +} + +pub fn get_init_contract_parameter_schema_aux( + schema: HexString, + contract_name: &str, + schema_version: Option, +) -> Result { + let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; + let parameter_type = module_schema.get_init_param_schema(contract_name)?; + Ok(hex::encode(concordium_base::contracts_common::to_bytes( + ¶meter_type, + ))) +} + +pub fn serialize_type_value_aux( + parameters: JsonString, + schema: HexString, + verbose_error_message: bool, +) -> Result { + let parameter_type: Type = from_bytes(&hex::decode(schema)?)?; + serialize_type_value(parameters, parameter_type, verbose_error_message) +} + +fn serialize_type_value( + raw_value: JsonString, + value_type: Type, + verbose_error_message: bool, +) -> Result { + let value: SerdeValue = serde_json::from_str(&raw_value)?; + + let buf = value_type + .serial_value(&value) + .map_err(|e| anyhow!("{}", e.display(verbose_error_message)))?; + Ok(hex::encode(buf)) +} + +pub fn deserialize_type_value_aux( + serialized_value: HexString, + schema: HexString, + verbose_error_message: bool, +) -> Result { + let value_type: Type = from_bytes(&hex::decode(schema)?)?; + deserialize_type_value(serialized_value, &value_type, verbose_error_message) +} + +fn deserialize_type_value( + serialized_value: HexString, + value_type: &Type, + verbose_error_message: bool, +) -> Result { + let mut cursor = Cursor::new(hex::decode(serialized_value)?); + match value_type.to_json(&mut cursor) { + Ok(v) => Ok(to_string(&v)?), + Err(e) => Err(anyhow!("{}", e.display(verbose_error_message))), + } +} + +pub fn display_type_schema_template_aux(schema: HexString) -> Result { + let value_type: Type = from_bytes(&hex::decode(schema)?)?; + let v = value_type.to_json_template(); + Ok(to_string(&v)?) +} diff --git a/packages/rust-bindings/packages/dapp/src/external_functions.rs b/packages/rust-bindings/packages/dapp/src/external_functions.rs new file mode 100644 index 000000000..7809d834c --- /dev/null +++ b/packages/rust-bindings/packages/dapp/src/external_functions.rs @@ -0,0 +1,165 @@ +use crate::aux_functions::*; +use concordium_rust_bindings_common::{ + helpers::{to_js_error, JsResult}, + types::{HexString, JsonString}, +}; +use wasm_bindgen::prelude::*; + +#[wasm_bindgen(js_name = deserializeState)] +pub fn deserialize_state( + contract_name: &str, + state_bytes: HexString, + schema: String, + verbose_error_message: Option, +) -> JsResult { + deserialize_state_aux( + contract_name, + state_bytes, + schema, + verbose_error_message.unwrap_or(false), + ) + .map_err(to_js_error) +} + +#[wasm_bindgen(js_name = deserializeReceiveReturnValue)] +pub fn deserialize_receive_return_value( + return_value_bytes: HexString, + module_schema: HexString, + contract_name: &str, + function_name: &str, + schema_version: Option, + verbose_error_message: Option, +) -> JsResult { + deserialize_receive_return_value_aux( + return_value_bytes, + module_schema, + contract_name, + function_name, + schema_version, + verbose_error_message.unwrap_or(false), + ) + .map_err(to_js_error) +} + +#[wasm_bindgen(js_name = deserializeReceiveError)] +pub fn deserialize_receive_error( + error_bytes: HexString, + schema: HexString, + contract_name: &str, + function_name: &str, + verbose_error_message: Option, +) -> JsResult { + deserialize_receive_error_aux( + error_bytes, + schema, + contract_name, + function_name, + verbose_error_message.unwrap_or(false), + ) + .map_err(to_js_error) +} + +#[wasm_bindgen(js_name = deserializeInitError)] +pub fn deserialize_init_error( + error_bytes: HexString, + schema: HexString, + contract_name: &str, + verbose_error_message: Option, +) -> JsResult { + deserialize_init_error_aux( + error_bytes, + schema, + contract_name, + verbose_error_message.unwrap_or(false), + ) + .map_err(to_js_error) +} + +#[wasm_bindgen(js_name = serializeReceiveContractParameters)] +pub fn serialize_receive_contract_parameters( + parameters: JsonString, + schema: HexString, + contract_name: &str, + function_name: &str, + schema_version: Option, + verbose_error_message: Option, +) -> JsResult { + serialize_receive_contract_parameters_aux( + parameters, + schema, + contract_name, + function_name, + schema_version, + verbose_error_message.unwrap_or(false), + ) + .map_err(|e| JsError::new(&format!("Unable to serialize parameters, due to: {}", e))) +} + +#[wasm_bindgen(js_name = serializeInitContractParameters)] +pub fn serialize_init_contract_parameters( + parameters: JsonString, + schema: HexString, + contract_name: &str, + schema_version: Option, + verbose_error_message: Option, +) -> JsResult { + serialize_init_contract_parameters_aux( + parameters, + schema, + contract_name, + schema_version, + verbose_error_message.unwrap_or(false), + ) + .map_err(|e| JsError::new(&format!("Unable to serialize parameters, due to: {}", e))) +} + +#[wasm_bindgen(js_name = getReceiveContractParameterSchema)] +pub fn get_receive_contract_parameter_schema_ext( + schema: HexString, + contract_name: &str, + function_name: &str, + schema_version: Option, +) -> JsResult { + get_receive_contract_parameter_schema_aux(schema, contract_name, function_name, schema_version) + .map_err(|e| JsError::new(&format!("Unable to get parameter schema, due to: {}", e))) +} + +#[wasm_bindgen(js_name = getInitContractParameterSchema)] +pub fn get_init_contract_parameter_schema_ext( + schema: HexString, + contract_name: &str, + schema_version: Option, +) -> JsResult { + get_init_contract_parameter_schema_aux(schema, contract_name, schema_version) + .map_err(|e| JsError::new(&format!("unable to get parameter schema, due to: {}", e))) +} + +#[wasm_bindgen(js_name = serializeTypeValue)] +pub fn serialize_type_value_ext( + value: JsonString, + schema: HexString, + verbose_error_message: Option, +) -> JsResult { + serialize_type_value_aux(value, schema, verbose_error_message.unwrap_or(false)) + .map_err(|e| JsError::new(&format!("Unable to serialize value due to: {}", e))) +} + +#[wasm_bindgen(js_name = deserializeTypeValue)] +pub fn deserialize_type_value_ext( + serialized_value: HexString, + schema: HexString, + verbose_error_message: Option, +) -> JsResult { + deserialize_type_value_aux( + serialized_value, + schema, + verbose_error_message.unwrap_or(false), + ) + .map_err(|e| JsError::new(&format!("Unable to deserialize value due to: {}", e))) +} + +#[wasm_bindgen(js_name = displayTypeSchemaTemplate)] +pub fn display_type_schema_template(schema: HexString) -> JsResult { + display_type_schema_template_aux(schema) + .map_err(|e| JsError::new(&format!("Unable to get template of schema: {}", e))) +} diff --git a/packages/rust-bindings/packages/dapp/src/lib.rs b/packages/rust-bindings/packages/dapp/src/lib.rs new file mode 100644 index 000000000..7b7333f1d --- /dev/null +++ b/packages/rust-bindings/packages/dapp/src/lib.rs @@ -0,0 +1,2 @@ +mod aux_functions; +pub mod external_functions; diff --git a/packages/rust-bindings/packages/wallet/Cargo.toml b/packages/rust-bindings/packages/wallet/Cargo.toml new file mode 100644 index 000000000..763e885f6 --- /dev/null +++ b/packages/rust-bindings/packages/wallet/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "concordium_rust_bindings_wallet" +authors.workspace = true +version = "0.1.0" +edition.workspace = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde.workspace = true +serde_json.workspace = true +wasm-bindgen.workspace = true +anyhow.workspace = true +hex.workspace = true +either.workspace = true +thiserror.workspace = true +rand.workspace = true +serde-wasm-bindgen.workspace = true +concordium_base.workspace = true +ed25519_hd_key_derivation.workspace = true +key_derivation.workspace = true +concordium_rust_bindings_common.workspace = true +ed25519-dalek = { version = "=1.0" } +chrono = "0.4.24" +serde_with = "3.0.0" + +[lib] +name = "concordium_rust_bindings_wallet" +crate-type = ["cdylib"] +path = "src/lib.rs" diff --git a/packages/rust-bindings/src/aux_functions.rs b/packages/rust-bindings/packages/wallet/src/aux_functions.rs similarity index 78% rename from packages/rust-bindings/src/aux_functions.rs rename to packages/rust-bindings/packages/wallet/src/aux_functions.rs index bf660f2aa..af61026d4 100644 --- a/packages/rust-bindings/src/aux_functions.rs +++ b/packages/rust-bindings/packages/wallet/src/aux_functions.rs @@ -1,4 +1,3 @@ -use crate::{helpers::*, types::*}; use anyhow::{anyhow, bail, ensure, Context, Result}; use concordium_base::{ base::{BakerAggregationSignKey, BakerElectionSignKey, BakerKeyPairs, BakerSignatureSignKey}, @@ -7,11 +6,8 @@ use concordium_base::{ types::{KeyIndex, KeyPair, TransactionTime}, *, }, - contracts_common::{ - from_bytes, - schema::{ModuleV0, Type, VersionedModuleSchema}, - ContractAddress, Cursor, - }, + contracts_common::ContractAddress, + curve_arithmetic::Pairing, id::{ account_holder::{ create_credential, create_unsigned_credential, generate_id_recovery_request, @@ -28,21 +24,83 @@ use concordium_base::{ secret_sharing::Threshold, types::*, }, + ps_sig::SigRetrievalRandomness, transactions::{ConfigureBakerKeysPayload, Payload}, web3id::{ CredentialHolderId, OwnedCommitmentInputs, Request, SignedCommitments, Web3IdAttribute, Web3IdSigner, }, }; +use concordium_rust_bindings_common::types::{HexString, JsonString}; use either::Either::Left; -use hex; use key_derivation::{ConcordiumHdWallet, CredentialContext, Net}; use rand::thread_rng; use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; -use serde_json::{from_str, to_string, Value as SerdeValue}; +use serde_json::{from_str, from_value, to_string, Value as SerdeValue}; use std::{collections::BTreeMap, convert::TryInto}; use thiserror::Error; +#[derive(SerdeSerialize, SerdeDeserialize)] +#[serde(bound(serialize = "P: Pairing", deserialize = "P: Pairing"))] +pub struct RandomnessWrapper { + #[serde( + rename = "randomness", + serialize_with = "base16_encode", + deserialize_with = "base16_decode" + )] + pub randomness: SigRetrievalRandomness

, +} + +fn build_key_map(keys: &[VerifyKey]) -> BTreeMap { + keys.iter() + .enumerate() + .map(|(index, key)| (KeyIndex(index.try_into().unwrap()), key.clone())) + .collect() +} + +fn build_signature_map(signatures: &[String]) -> BTreeMap { + signatures + .iter() + .enumerate() + .map(|(index, key)| { + ( + KeyIndex(index.try_into().unwrap()), + base16_decode_string(key).unwrap(), + ) + }) + .collect() +} + +fn build_policy( + attributes: &AttributeList, + revealed_attributes: Vec, +) -> Result> { + let mut policy_vec = std::collections::BTreeMap::new(); + for tag in revealed_attributes { + if let Some(att) = attributes.alist.get(&tag) { + if policy_vec.insert(tag, att.clone()).is_some() { + bail!("Cannot reveal an attribute more than once.") + } + } else { + bail!("Cannot reveal an attribute which is not part of the attribute list.") + } + } + Ok(Policy { + valid_to: attributes.valid_to, + created_at: attributes.created_at, + policy_vec, + _phantom: Default::default(), + }) +} + +/// Try to extract a field with a given name from the JSON value. +fn try_get(v: &SerdeValue, fname: &str) -> Result { + match v.get(fname) { + Some(v) => Ok(from_value(v.clone())?), + None => Err(anyhow!("Field {} not present, but should be.", fname)), + } +} + #[derive(SerdeSerialize, SerdeDeserialize)] pub struct CredId { #[serde( @@ -65,13 +123,6 @@ pub struct IdRequestInput { ar_threshold: u8, } -pub fn error_to_string(result: Result) -> String { - match result { - Ok(s) => s, - Err(e) => format!("{}", e), - } -} - fn get_net(net: &str) -> Result { Ok(match net { "Mainnet" => Net::Mainnet, @@ -572,196 +623,6 @@ pub fn get_credential_deployment_info_aux( Ok(cdi_json.to_string()) } -/// Given the bytes of a contract's state, deserialize them to a json object, -/// using the provided schema. Both the state bytes and the schema are given as -/// hex-encoded strings. -pub fn deserialize_state_aux( - contract_name: &str, - state_bytes: HexString, - schema: HexString, - verbose_error_message: bool, -) -> Result { - let module_schema: ModuleV0 = match from_bytes(&hex::decode(schema)?) { - Ok(o) => o, - Err(e) => return Err(anyhow!("unable to parse schema: {:#?}", e)), - }; - let contract_schema = module_schema - .contracts - .get(contract_name) - .ok_or_else(|| anyhow!("Unable to get contract schema: not included in module schema"))?; - let state_schema = contract_schema - .state - .as_ref() - .ok_or_else(|| anyhow!("Unable to get state schema: not included in contract schema"))?; - - deserialize_type_value(state_bytes, state_schema, verbose_error_message) -} - -/// Given the bytes of a receive function's return value, deserialize them to a -/// json object, using the provided schema. -pub fn deserialize_receive_return_value_aux( - return_value_bytes: HexString, - schema: HexString, - contract_name: &str, - function_name: &str, - schema_version: Option, - verbose_error_message: bool, -) -> Result { - let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; - let return_value_schema = - module_schema.get_receive_return_value_schema(contract_name, function_name)?; - - deserialize_type_value( - return_value_bytes, - &return_value_schema, - verbose_error_message, - ) -} - -/// Given the bytes of a receive function's error, deserialize them to a json -/// object, using the provided schema. -pub fn deserialize_receive_error_aux( - error_bytes: HexString, - schema: HexString, - contract_name: &str, - function_name: &str, - verbose_error_message: bool, -) -> Result { - let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &None)?; - let error_schema = module_schema.get_receive_error_schema(contract_name, function_name)?; - - deserialize_type_value(error_bytes, &error_schema, verbose_error_message) -} - -/// Given the bytes of an init function's error, deserialize them to a json -/// object, using the provided schema. -pub fn deserialize_init_error_aux( - error_bytes: HexString, - schema: HexString, - contract_name: &str, - verbose_error_message: bool, -) -> Result { - let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &None)?; - let error_schema = module_schema.get_init_error_schema(contract_name)?; - - deserialize_type_value(error_bytes, &error_schema, verbose_error_message) -} - -/// Given parameters to a receive function as a stringified json, serialize them -/// using the provided schema. -pub fn serialize_receive_contract_parameters_aux( - parameters: JsonString, - schema: HexString, - contract_name: &str, - function_name: &str, - schema_version: Option, - verbose_error_message: bool, -) -> Result { - let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; - let parameter_type = module_schema.get_receive_param_schema(contract_name, function_name)?; - let value: SerdeValue = serde_json::from_str(¶meters)?; - - let buf = parameter_type - .serial_value(&value) - .map_err(|e| anyhow!("{}", e.display(verbose_error_message)))?; - - Ok(hex::encode(buf)) -} - -/// Given parameters to an init function as a stringified json, serialize them -/// using the provided schema. -pub fn serialize_init_contract_parameters_aux( - parameters: JsonString, - schema: HexString, - contract_name: &str, - schema_version: Option, - verbose_error_message: bool, -) -> Result { - let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; - let parameter_type = module_schema.get_init_param_schema(contract_name)?; - let value: SerdeValue = serde_json::from_str(¶meters)?; - - let buf = parameter_type - .serial_value(&value) - .map_err(|e| anyhow!("{}", e.display(verbose_error_message)))?; - - Ok(hex::encode(buf)) -} - -pub fn get_receive_contract_parameter_schema_aux( - schema: HexString, - contract_name: &str, - function_name: &str, - schema_version: Option, -) -> Result { - let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; - let parameter_type = module_schema.get_receive_param_schema(contract_name, function_name)?; - Ok(hex::encode(concordium_base::contracts_common::to_bytes( - ¶meter_type, - ))) -} - -pub fn get_init_contract_parameter_schema_aux( - schema: HexString, - contract_name: &str, - schema_version: Option, -) -> Result { - let module_schema = VersionedModuleSchema::new(&hex::decode(schema)?, &schema_version)?; - let parameter_type = module_schema.get_init_param_schema(contract_name)?; - Ok(hex::encode(concordium_base::contracts_common::to_bytes( - ¶meter_type, - ))) -} - -pub fn serialize_type_value_aux( - parameters: JsonString, - schema: HexString, - verbose_error_message: bool, -) -> Result { - let parameter_type: Type = from_bytes(&hex::decode(schema)?)?; - serialize_type_value(parameters, parameter_type, verbose_error_message) -} - -fn serialize_type_value( - raw_value: JsonString, - value_type: Type, - verbose_error_message: bool, -) -> Result { - let value: SerdeValue = serde_json::from_str(&raw_value)?; - - let buf = value_type - .serial_value(&value) - .map_err(|e| anyhow!("{}", e.display(verbose_error_message)))?; - Ok(hex::encode(buf)) -} - -pub fn deserialize_type_value_aux( - serialized_value: HexString, - schema: HexString, - verbose_error_message: bool, -) -> Result { - let value_type: Type = from_bytes(&hex::decode(schema)?)?; - deserialize_type_value(serialized_value, &value_type, verbose_error_message) -} - -fn deserialize_type_value( - serialized_value: HexString, - value_type: &Type, - verbose_error_message: bool, -) -> Result { - let mut cursor = Cursor::new(hex::decode(serialized_value)?); - match value_type.to_json(&mut cursor) { - Ok(v) => Ok(to_string(&v)?), - Err(e) => Err(anyhow!("{}", e.display(verbose_error_message))), - } -} - -pub fn display_type_schema_template_aux(schema: HexString) -> Result { - let value_type: Type = from_bytes(&hex::decode(schema)?)?; - let v = value_type.to_json_template(); - Ok(to_string(&v)?) -} - #[derive(SerdeSerialize, SerdeDeserialize)] #[serde(rename_all = "camelCase")] pub struct IdProofInput { @@ -1025,12 +886,3 @@ pub fn verify_web3_id_credential_signature_aux( input.issuer_contract, )) } - -pub fn compare_string_attributes_aux( - attribute1: String, - attribute2: String, -) -> core::cmp::Ordering { - let e1 = Web3IdAttribute::String(AttributeKind(attribute1)).to_field_element(); - let e2 = Web3IdAttribute::String(AttributeKind(attribute2)).to_field_element(); - e1.cmp(&e2) -} diff --git a/packages/rust-bindings/src/external_functions.rs b/packages/rust-bindings/packages/wallet/src/external_functions.rs similarity index 60% rename from packages/rust-bindings/src/external_functions.rs rename to packages/rust-bindings/packages/wallet/src/external_functions.rs index fdb0daee6..16ed41c36 100644 --- a/packages/rust-bindings/src/external_functions.rs +++ b/packages/rust-bindings/packages/wallet/src/external_functions.rs @@ -1,12 +1,10 @@ -use std::{cmp::Ordering, fmt::Display}; - -use crate::{aux_functions::*, types::*}; +use crate::aux_functions::*; +use concordium_rust_bindings_common::{ + helpers::{to_js_error, JsResult}, + types::{Base58String, HexString, JsonString}, +}; use wasm_bindgen::prelude::*; -type JsResult = Result; - -fn to_js_error(error: impl Display) -> JsError { JsError::new(&format!("{}", error)) } - #[wasm_bindgen(js_name = generateUnsignedCredential)] pub fn generate_unsigned_credential_ext(input: &str) -> JsResult { generate_unsigned_credential_aux(input).map_err(|e| { @@ -42,150 +40,6 @@ pub fn get_credential_deployment_info_ext(signatures: &JsValue, unsigned_info: & .map_err(|e| JsError::new(&format!("Unable to get credential due to: {}", e))) } -#[wasm_bindgen(js_name = deserializeState)] -pub fn deserialize_state( - contract_name: &str, - state_bytes: HexString, - schema: String, - verbose_error_message: Option, -) -> JsResult { - deserialize_state_aux( - contract_name, - state_bytes, - schema, - verbose_error_message.unwrap_or(false), - ) - .map_err(to_js_error) -} - -#[wasm_bindgen(js_name = deserializeCredentialDeployment)] -pub fn deserialize_credential_deployment_ext(serialized: JsonString) -> JsResult { - deserialize_credential_deployment_aux(&serialized).map_err(to_js_error) -} - -#[wasm_bindgen(js_name = deserializeReceiveReturnValue)] -pub fn deserialize_receive_return_value( - return_value_bytes: HexString, - module_schema: HexString, - contract_name: &str, - function_name: &str, - schema_version: Option, - verbose_error_message: Option, -) -> JsResult { - deserialize_receive_return_value_aux( - return_value_bytes, - module_schema, - contract_name, - function_name, - schema_version, - verbose_error_message.unwrap_or(false), - ) - .map_err(to_js_error) -} - -#[wasm_bindgen(js_name = deserializeReceiveError)] -pub fn deserialize_receive_error( - error_bytes: HexString, - schema: HexString, - contract_name: &str, - function_name: &str, - verbose_error_message: Option, -) -> JsResult { - deserialize_receive_error_aux( - error_bytes, - schema, - contract_name, - function_name, - verbose_error_message.unwrap_or(false), - ) - .map_err(to_js_error) -} - -#[wasm_bindgen(js_name = deserializeInitError)] -pub fn deserialize_init_error( - error_bytes: HexString, - schema: HexString, - contract_name: &str, - verbose_error_message: Option, -) -> JsResult { - deserialize_init_error_aux( - error_bytes, - schema, - contract_name, - verbose_error_message.unwrap_or(false), - ) - .map_err(to_js_error) -} - -#[wasm_bindgen(js_name = serializeReceiveContractParameters)] -pub fn serialize_receive_contract_parameters( - parameters: JsonString, - schema: HexString, - contract_name: &str, - function_name: &str, - schema_version: Option, - verbose_error_message: Option, -) -> JsResult { - serialize_receive_contract_parameters_aux( - parameters, - schema, - contract_name, - function_name, - schema_version, - verbose_error_message.unwrap_or(false), - ) - .map_err(|e| JsError::new(&format!("Unable to serialize parameters, due to: {}", e))) -} - -#[wasm_bindgen(js_name = serializeInitContractParameters)] -pub fn serialize_init_contract_parameters( - parameters: JsonString, - schema: HexString, - contract_name: &str, - schema_version: Option, - verbose_error_message: Option, -) -> JsResult { - serialize_init_contract_parameters_aux( - parameters, - schema, - contract_name, - schema_version, - verbose_error_message.unwrap_or(false), - ) - .map_err(|e| JsError::new(&format!("Unable to serialize parameters, due to: {}", e))) -} - -#[wasm_bindgen(js_name = getReceiveContractParameterSchema)] -pub fn get_receive_contract_parameter_schema_ext( - schema: HexString, - contract_name: &str, - function_name: &str, - schema_version: Option, -) -> JsResult { - get_receive_contract_parameter_schema_aux(schema, contract_name, function_name, schema_version) - .map_err(|e| JsError::new(&format!("Unable to get parameter schema, due to: {}", e))) -} - -#[wasm_bindgen(js_name = getInitContractParameterSchema)] -pub fn get_init_contract_parameter_schema_ext( - schema: HexString, - contract_name: &str, - schema_version: Option, -) -> JsResult { - get_init_contract_parameter_schema_aux(schema, contract_name, schema_version) - .map_err(|e| JsError::new(&format!("unable to get parameter schema, due to: {}", e))) -} - -#[wasm_bindgen(js_name = serializeTypeValue)] -pub fn serialize_type_value_ext( - value: JsonString, - schema: HexString, - verbose_error_message: Option, -) -> JsResult { - serialize_type_value_aux(value, schema, verbose_error_message.unwrap_or(false)) - .map_err(|e| JsError::new(&format!("Unable to serialize value due to: {}", e))) -} - #[wasm_bindgen(js_name = createIdRequestV1)] pub fn create_id_request_v1_ext(input: JsonString) -> JsResult { create_id_request_v1_aux(serde_json::from_str(&input).unwrap()).map_err(to_js_error) @@ -249,6 +103,11 @@ pub fn get_account_public_key_ext( .map_err(to_js_error) } +#[wasm_bindgen(js_name = deserializeCredentialDeployment)] +pub fn deserialize_credential_deployment_ext(serialized: JsonString) -> JsResult { + deserialize_credential_deployment_aux(&serialized).map_err(to_js_error) +} + #[wasm_bindgen(js_name = getCredentialId)] pub fn get_credential_id_ext( seed_as_hex: HexString, @@ -404,26 +263,6 @@ pub fn generate_baker_keys_ext(sender: Base58String) -> JsResult { generate_baker_keys(sender).map_err(to_js_error) } -#[wasm_bindgen(js_name = deserializeTypeValue)] -pub fn deserialize_type_value_ext( - serialized_value: HexString, - schema: HexString, - verbose_error_message: Option, -) -> JsResult { - deserialize_type_value_aux( - serialized_value, - schema, - verbose_error_message.unwrap_or(false), - ) - .map_err(|e| JsError::new(&format!("Unable to deserialize value due to: {}", e))) -} - -#[wasm_bindgen(js_name = displayTypeSchemaTemplate)] -pub fn display_type_schema_template(schema: HexString) -> JsResult { - display_type_schema_template_aux(schema) - .map_err(|e| JsError::new(&format!("Unable to get template of schema: {}", e))) -} - #[wasm_bindgen(js_name = createWeb3IdProof)] pub fn create_web3_id_proof_ext(raw_input: JsonString) -> JsResult { let input = serde_json::from_str(&raw_input)?; @@ -435,12 +274,3 @@ pub fn verify_web3_id_credential_signature_ext(raw_input: JsonString) -> JsResul let input = serde_json::from_str(&raw_input)?; verify_web3_id_credential_signature_aux(input).map_err(to_js_error) } - -#[wasm_bindgen(js_name = compareStringAttributes)] -pub fn compare_string_attributes_ext(attribute1: String, attribute2: String) -> i8 { - match compare_string_attributes_aux(attribute1, attribute2) { - Ordering::Less => -1, - Ordering::Equal => 0, - Ordering::Greater => 1, - } -} diff --git a/packages/rust-bindings/src/lib.rs b/packages/rust-bindings/packages/wallet/src/lib.rs similarity index 62% rename from packages/rust-bindings/src/lib.rs rename to packages/rust-bindings/packages/wallet/src/lib.rs index 3b48304ff..e4e1d20da 100644 --- a/packages/rust-bindings/src/lib.rs +++ b/packages/rust-bindings/packages/wallet/src/lib.rs @@ -1,7 +1,5 @@ -pub mod aux_functions; +mod aux_functions; pub mod external_functions; -mod helpers; -pub mod types; #[macro_use] extern crate serde_json; diff --git a/packages/rust-bindings/src/helpers.rs b/packages/rust-bindings/src/helpers.rs deleted file mode 100644 index f43f83218..000000000 --- a/packages/rust-bindings/src/helpers.rs +++ /dev/null @@ -1,57 +0,0 @@ -use anyhow::{anyhow, bail, Result}; -use concordium_base::{ - common::{base16_decode_string, types::KeyIndex}, - id::{constants, types::*}, -}; -use serde_json::{from_value, Value as SerdeValue}; -use std::{collections::BTreeMap, convert::TryInto}; - -pub fn build_key_map(keys: &[VerifyKey]) -> BTreeMap { - keys.iter() - .enumerate() - .map(|(index, key)| (KeyIndex(index.try_into().unwrap()), key.clone())) - .collect() -} - -pub fn build_signature_map(signatures: &[String]) -> BTreeMap { - signatures - .iter() - .enumerate() - .map(|(index, key)| { - ( - KeyIndex(index.try_into().unwrap()), - base16_decode_string(key).unwrap(), - ) - }) - .collect() -} - -pub fn build_policy( - attributes: &AttributeList, - revealed_attributes: Vec, -) -> Result> { - let mut policy_vec = std::collections::BTreeMap::new(); - for tag in revealed_attributes { - if let Some(att) = attributes.alist.get(&tag) { - if policy_vec.insert(tag, att.clone()).is_some() { - bail!("Cannot reveal an attribute more than once.") - } - } else { - bail!("Cannot reveal an attribute which is not part of the attribute list.") - } - } - Ok(Policy { - valid_to: attributes.valid_to, - created_at: attributes.created_at, - policy_vec, - _phantom: Default::default(), - }) -} - -/// Try to extract a field with a given name from the JSON value. -pub fn try_get(v: &SerdeValue, fname: &str) -> Result { - match v.get(fname) { - Some(v) => Ok(from_value(v.clone())?), - None => Err(anyhow!("Field {} not present, but should be.", fname)), - } -} diff --git a/packages/rust-bindings/src/types.rs b/packages/rust-bindings/src/types.rs deleted file mode 100644 index 48f3a8cc4..000000000 --- a/packages/rust-bindings/src/types.rs +++ /dev/null @@ -1,19 +0,0 @@ -use concordium_base::{ - common::*, - id::{curve_arithmetic::Pairing, ps_sig::SigRetrievalRandomness}, -}; - -#[derive(SerdeSerialize, SerdeDeserialize)] -#[serde(bound(serialize = "P: Pairing", deserialize = "P: Pairing"))] -pub struct RandomnessWrapper { - #[serde( - rename = "randomness", - serialize_with = "base16_encode", - deserialize_with = "base16_decode" - )] - pub randomness: SigRetrievalRandomness

, -} - -pub type JsonString = String; -pub type HexString = String; -pub type Base58String = String; diff --git a/packages/rust-bindings/ts-src/dapp.ts b/packages/rust-bindings/ts-src/dapp.ts new file mode 100644 index 000000000..20183aae0 --- /dev/null +++ b/packages/rust-bindings/ts-src/dapp.ts @@ -0,0 +1,9 @@ +import { Buffer } from 'buffer/index.js'; + +import { initSync } from '../lib/dapp/web/esm'; +import wasmBase64 from '../lib/dapp/web/esm/index_bg.wasm'; // Expected to resolve to base64 encoded bytes of wasm module + +const bytes = Buffer.from(wasmBase64 as unknown as string, 'base64'); +initSync(bytes); + +export * from '../lib/dapp/web/esm'; diff --git a/packages/rust-bindings/ts-src/wallet.ts b/packages/rust-bindings/ts-src/wallet.ts new file mode 100644 index 000000000..cd14edbde --- /dev/null +++ b/packages/rust-bindings/ts-src/wallet.ts @@ -0,0 +1,9 @@ +import { Buffer } from 'buffer/index.js'; + +import { initSync } from '../lib/wallet/web/esm'; +import wasmBase64 from '../lib/wallet/web/esm/index_bg.wasm'; // Expected to resolve to base64 encoded bytes of wasm module + +const bytes = Buffer.from(wasmBase64 as unknown as string, 'base64'); +initSync(bytes); + +export * from '../lib/wallet/web/esm'; diff --git a/packages/rust-bindings/tsconfig.build.json b/packages/rust-bindings/tsconfig.build.json new file mode 100644 index 000000000..427d7f1e9 --- /dev/null +++ b/packages/rust-bindings/tsconfig.build.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "./ts-src/**/*" + ], + "compilerOptions": { + "rootDir": "./ts-src", + "declaration": false, + "outDir": "./lib/web" + } +} diff --git a/packages/rust-bindings/tsconfig.json b/packages/rust-bindings/tsconfig.json new file mode 100644 index 000000000..567b726b7 --- /dev/null +++ b/packages/rust-bindings/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig-base.json", + "include": [ + "./ts-src/**/*", + "./build/**/*", + "./webpack.config.ts" + ], + "compilerOptions": { + "composite": false, + "lib": [ + "ES2020", + "dom" + ] + }, + "ts-node": { + "compilerOptions": { + "module": "CommonJS" + } + } +} diff --git a/packages/rust-bindings/webpack.config.ts b/packages/rust-bindings/webpack.config.ts new file mode 100644 index 000000000..3805e887f --- /dev/null +++ b/packages/rust-bindings/webpack.config.ts @@ -0,0 +1,56 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { Configuration } from 'webpack'; +import { resolve } from 'path'; + +function configForTarget(target: 'web' | 'node'): Configuration { + return { + mode: 'production', + cache: { + type: 'filesystem', + cacheDirectory: resolve(__dirname, '.webpack-cache'), + }, + target, + entry: { + dapp: resolve(__dirname, './ts-src/dapp.ts'), + wallet: resolve(__dirname, './ts-src/wallet.ts'), + }, + resolve: { + extensionAlias: { + '.js': ['.ts', '.js'], + }, + extensions: ['.tsx', '.ts', '.js'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: { + loader: 'ts-loader', + options: { + configFile: resolve( + __dirname, + './tsconfig.build.json' + ), + }, + }, + exclude: /node_modules/, + }, + { + test: /\.wasm$/, + type: 'asset/inline', + generator: { + dataUrl: (data: Buffer) => data.toString('base64'), + }, + }, + ], + }, + output: { + filename: `[name]/${target}/umd/index.min.js`, + path: resolve(__dirname, 'lib'), + libraryTarget: 'umd', + publicPath: '', + }, + }; +} + +export default [configForTarget('web'), configForTarget('node')]; diff --git a/packages/common/CHANGELOG.md b/packages/sdk/CHANGELOG.md similarity index 59% rename from packages/common/CHANGELOG.md rename to packages/sdk/CHANGELOG.md index f5ca9a44d..0527dfbd1 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/sdk/CHANGELOG.md @@ -1,12 +1,117 @@ # Changelog -## 9.5.1 +## 7.0.0 + +### Breaking changes + +- The package is published as an ES module, instead of commonJS. Migration steps can be seen in [the upgrade guide](../../docs/pages/misc-pages/upgrade-guide.md) +- The package has been split into several entrypoints that can be used to limit the scope of what is included from the SDK. + - `@concordium/common-sdk` exposes the full API of the SDK. + - `@concordium/common-sdk/cis0` entrypoint exposes functionality for working with contracts adhering to the [CIS-0](https://proposals.concordium.software/CIS/cis-0.html) standard. + - `@concordium/common-sdk/cis2` entrypoint exposes functionality for working with contracts adhering to the [CIS-2](https://proposals.concordium.software/CIS/cis-2.html) standard. + - `@concordium/common-sdk/cis4` entrypoint exposes functionality for working with contracts adhering to the [CIS-4](https://proposals.concordium.software/CIS/cis-4.html) standard. + - `@concordium/common-sdk/grpc` entrypoint exposes the grpc client for interacting with a nodes GRPCv2 interface. + - `@concordium/common-sdk/id` entrypoint exposes functionality for working with ID proofs. + - `@concordium/common-sdk/schema` entrypoint exposes functionality for working with smart contract schemas, i.e.(de)serializing types using a smart contract schema. + - This uses the wasm entrypoint at `@concordium/rust-bindings/dapp`. + - `@concordium/common-sdk/types` entrypoint exposes functionality for working with concordium domain types. + - `@concordium/common-sdk/wasm` entrypoint exposes a variety of functionality for working with concordium domain types, which requires WASM. + - This uses the wasm entrypoint at `@concorodium/rust-bindings/wallet`. + - `@concordium/common-sdk/web3-id` entrypoint exposes functionality for working with web3-id proofs. + - This change makes the library **incompatible** with node versions <16 and requires bundlers to respect the `exports` field of `package.json`. + - For TypeScript projects the minimum required version of typescript is: + - NodeJS: 4.7, `"moduleResolution": "node16" // or "nodenext"` + - Bundled applications (webpack, esbuild, rollup, etc...): 5.0, `"moduleResolution": "bundler"` +- The following functions now parse using `json-bigint` meaning that they return bigints instead of numbers _for all numbers no matter size_ + - `deserializeContractState` + - `deserializeReceiveReturnValue` + - `deserializeReceiveError` + - `deserializeInitError` + - `deserializeTypeValue` + +The API now uses dedicated types instead of language primitives: + +- Use `AccountAddress` instead of a string with base58 encoding. Use `AccountAddress.fromBase58('')` to construct it. +- Use `BlockHash` instead of a string with hex encoding. Use `BlockHash.fromHexString('')` to construct it. +- Use `TranactionHash` instead of a string with hex encoding. Use `TransactionHash.fromHexString('')` to construct it. +- Use `Energy` instead of a bigint. Use `Energy.create()` to construct it. +- Use `ReceiveName` instead of a string. Use `ReceiveName.fromString('.')` to construct it. +- Use `InitName` instead of a string. Use `Init.fromString('init_')` to construct it. +- Use `ContractName` instead of a string. Use `ContractName.fromString('')` to construct it. +- Use `EntrypointName` instead of a string. Use `EntrypointName.fromString('')` to construct it. +- Use `Parameter` instead of a string with hex encoding. Use `Parameter.fromHexString('')`. +- Use `SequenceNumber` (formerly called nonce) instead of a bigint. Use `SequenceNumber.create()` to construct it. +- Use `Timestamp` instead of a bigint. Can be constructed using `Timestamp.fromMillis()`. +- Use `Duration` instead of a bigint. Can be constructed using `Duration.fromMillis()`. +- Use `ContractEvent` instead of a string with hex encoding. Can be constructed using `ContractEvent.fromHexString('')`. +- Use `CcdAmount` instead of a bigint. Can be constructed using `CcdAmount.fromMicroCcd()`. +- Use `TransactionExpiry` instead of a Date object. Can be constructed using `TransactionExpiry.fromDate()`. +- Use `ModuleReference` instead of a string with hex encoding. Can be constructed using `ModuleReference.fromHexString('')`. + +Several types have been replaced with a module containing the type itself together with functions for constructing and converting the type: + +- `AccountAddress` is now a module with functions related to account addresses: + - To refer to `AccountAddress` as a type use `AccountAddress.Type`. + - Constructing `new AccountAddress("

")` is now `AccountAddress.fromBase58("
")`. + - `isAlias` and `getAlias` are now accessable from `AccountAddress.isAlias` and `AccountAddress.getAlias`. +- `ContractAddresss` is now a module with functions related to contract addresses: + - To refer to `ContractAddress` as a type use `ContractAddress.Type`. + - To construct the type use `ContractAddress.create(index, subindex)`. +- `CredentialRegistrationId` is now a module with functions related to credential registration IDs: + - To refer to `CredentialRegistrationId` as a type use `CredentialRegistrationId.Type`. + - Constructing `new CredentialRegistrationId("")` is now `CredentialRegistrationId.fromHexString("")`. +- `CcdAmount` is now a module with functions related to amounts of CCDs: + - To refer to `CcdAmount` as a type use `CcdAmount.Type`. + - Constructing `new CcdAmount()` is now `CcdAmount.fromMicroCcd()`. + - The methods `toMicroCcd` and `toCcd` are now functions refered to as `CcdAmount.toMicroCcd` and `CcdAmount.toCcd` respectively. +- `TransactionExpiry` is now a module with functions related to amounts of expiry time of transactions: + - To refer to `TransactionExpiry` as a type use `TransactionExpiry.Type`. + - Constructing `new TransactionExpiry(, )` is now `TransactionExpiry.fromDate()`, and the check of being a time in the future is removed and done when sending the transaction instead. +- `ModuleReference` is now a module with functions related to references to smart contract modules: + - To refer to `ModuleReference` as a type use `ModuleReference.Type`. + - Constructing `new ModuleReference("")` is now `ModuleReference.fromHexString("")`. + - The static method `ModuleReference.fromBytes` is now `ModuleReference.fromBuffer`. +- Removed `JsonRpcClient` and types and functionality associated solely with this class. +- Renamed `AccountSequenceNumber` module to `SequenceNumber`. +- Fix type for `TranferredEvent` from `ContractTraceEvent` to only be from contract addresses to account addresses. +- Added `effectiveTime` field to `PendingUpdate`. + +### Added + +- All JSON serialization in `serialization.ts` is now handled by `json-bigint` meaning that all functions now correctly handles bigint inputs +- `Timestamp` is now a module with functions related to time. + - To refer to `Timestamp` as a type use `Timestamp.Type`. +- `Duration` is now a module with functions related to durations of time. +- `EntrypointName` is now a module with functions related to entrypoint names of a smart contract. +- `ReceiveName` is now a module with functions related to receive-function names of a smart contract. +- `ReturnValue` is now a module with functions related to return values from invoking a smart contract. +- Functions `jsonStringify` and `jsonParse`, which acts as a regular `JSON.stringify` and `JSON.parse` correspondingly, + with the addition of stringifying concordium domain types in a wrapper object that can be parsed into the respective types. +- Introduce function `versionedModuleSourceToBuffer` for serializing a versioned module to a buffer, which can be stored in a file. + + +### Changes + +- Added version discriminators to types versioned by the protocol version of Concordium nodes: + - `MintDistribution` + - `GasRewards` + - `RewardParameters` + - `ChainParameters` + - `Authorizations` + - `RewardStatus` + - `BlockInfo` + - `ConsensusStatus` + - `AccountBakerDetails` + - `ElectionInfo` +- Added type discriminator to different forms of `AccountInfo`. + +## 6.5.1 ### Fixed - An issue where `BakerRewardPeriodInfo` incorrectly mapped `delegatedCapital` field -## 9.5.0 +## 6.5.0 ### Added @@ -19,21 +124,33 @@ New consensus endpoints: - `getFirstBlockEpoch` - `commissionRates` is now added to the `getPoolInfo` under `bakerPoolStatus.PoolInfo` -## 9.4.0 + +## 6.4.0 ### Added - `sendUpdateInstruction` to the gRPC Client. - `healthCheck` to the gRPC Client. -- Functions `calculateModuleReference` for getting the module reference and `parseModuleInterface` for getting the interface from the source of a smart contract module. +- Function `calculateModuleReference` for getting the module reference. +- Function `parseModuleInterface` for getting the interface from the source of a smart contract module. +- Function `getEmbeddedModuleSchema` for getting the module schema embedded into a smart contract module source. - Smart contract related types `ContractName`, `EntrypointName` and helper functions `isInitName`, `isReceiveName`, `getContractNameFromInit` and `getNamesFromReceive`. +- Add `ModuleClient` module and type for interaction with a smart contract module deployed on chain. +- Add `Energy` module with helpers for transaction energy. +- Add `BlockHash` module with helpers for block hashes. +- Add `TransactionHash` module with helpers for transaction hashes. +- Add `InitName` module with helpers for smart contract init-function names. +- Add `ContractName` module with helpers for smart contract names. +- Add `Parameter` module with helpers for smart contract parameters. +- Add `AccountSequenceNumber` module with helpers for account sequence numbers (formerly referred to as nonce). +- Add methods `getInstanceInfo` and `checkOnChain` on the generic contract client `Contract`. ### Fixed - Added missing fields to `getBlockChainParameters` response. (rootKeys, level1Keys, level2Keys) - Use of bigint exponentiation causing issues in web. -## 9.3.0 +## 6.3.0 ### Added @@ -44,13 +161,17 @@ New consensus endpoints: - Stopped using `replaceDateWithTimeStampAttribute` and `reviveDateFromTimeStampAttribute` for serializing and parsing verifiable presentation. - AttributeType no longer contains `Date`, but now instead has `TimestampAttribute`. The statement builders have their types extended to keep allowing for both `Date` and `TimestampAttribute`. -## 9.2.1 +## 6.2.1 ### Fixed - Missing buffer import causing issues in web. -## 9.2.0 +## 6.2.0 + +### Breaking changes + +- `sendCredentialDeploymentTransaction` method of `ConcordiumGRPCClient` signature changed to take an already serialized payload. ### Added @@ -63,14 +184,14 @@ New consensus endpoints: - Aligned credential schema types with the tested types in the browser wallet. - `addMinimumAge` now creates the precise age statement instead of one day off. -## 9.1.1 +## 6.1.1 ### Fixes - `verifyWeb3IdCredentialSignature` now supports dates/timestamp attributes. - `canProveAtomicStatement` now supports timestamp attributes, handles undefined attribute value correctly and handles strings correctly for range statements. -## 9.1.0 +## 6.1.0 ### Added @@ -94,7 +215,7 @@ All function parameters now also accepts strings, these strings can use comma as - The max smart contract parameter length was changed to 65535 bytes in protocol version 5 and onwards. Functions which checks the parameter length will now reflect that. -## 9.0.0 +## 6.0.0 ### Breaking changes @@ -109,7 +230,7 @@ All function parameters now also accepts strings, these strings can use comma as - `getTransactionKindString` function. - `displayTypeSchemaTemplate` function. -## 8.0.0 +## 5.0.0 ### Breaking changes @@ -149,13 +270,13 @@ All function parameters now also accepts strings, these strings can use comma as - `serializeUpdateContractParameters` - `serializeTypeValue` -## 7.0.1 2023-05-25 +## 4.0.1 2023-05-25 ### Fixed - Cost calculation for `deployModule` transaction. -## 7.0.0 2023-05-15 +## 4.0.0 2023-05-15 ### Breaking changes @@ -192,7 +313,7 @@ All function parameters now also accepts strings, these strings can use comma as - Bumped @concordium/rust-bindings to 0.12.0. (Adds key derivation for verifiable credentials) -## 6.5.0 2023-5-03 +## 3.5.0 2023-5-03 ### Added @@ -210,20 +331,20 @@ All function parameters now also accepts strings, these strings can use comma as - Fixed bug where `AccountCreationSummary` type did not have fields: `index`, `energyCost`, `hash` -## 6.4.2 2023-04-21 +## 3.4.2 2023-04-21 ### Changed - `generateBakerKeys` now also returns the private baker keys. -## 6.4.1 2023-03-31 +## 3.4.1 2023-03-31 ### Changed - Replace use of `setImmediate` with `setTimeout` since the former is not supported in browsers. -## 6.4.0 2023-03-22 +## 3.4.0 2023-03-22 ### Added @@ -238,7 +359,7 @@ All function parameters now also accepts strings, these strings can use comma as - `serializeTypeValue` now reports an error when called with invalid data, such as a receive function with missing schema, or a schema that cannot be parsed. -## 6.3.0 2023-02-27 +## 3.3.0 2023-02-27 ### Added @@ -271,7 +392,7 @@ All function parameters now also accepts strings, these strings can use comma as - The JSON-RPC client has been deprecated in favor of the new gRPC v2 client. - Various types and helper functions used by the JSON-RPC client (and the v1 gRPC client) have also been deprecated. -## 6.2.0 2023-01-04 +## 3.2.0 2023-01-04 ### Added @@ -279,7 +400,7 @@ All function parameters now also accepts strings, these strings can use comma as - `getInitContractParameterSchema` Given a buffer containing the schema for a module, extract the schema for a given contract's init function's parameters. - `getReceiveContractParameterSchema` Given a buffer containing the schema for a module, extract the schema for a given contract's receive methods' parameters. -## 6.1.0 2022-11-30 +## 3.1.0 2022-11-30 ### Added @@ -288,7 +409,7 @@ All function parameters now also accepts strings, these strings can use comma as - `getIdProof` function to prove a statement holds for the given identity/account. - Enums for sex and idDocType values. -## 6.0.0 2022-11-15 +## 3.0.0 2022-11-15 ### Breaking changes @@ -302,13 +423,13 @@ All function parameters now also accepts strings, these strings can use comma as - The ability to deserialize error values of receive and init functions using `deserializeReceiveError()` and `deserializeInitError()` respectfully. - Refactored the `upserializeUpdateContractParameters()` and `serializeInitContractParameters()` to call into rust functions. -## 5.2.0 2022-11-8 +## 2.2.0 2022-11-8 ### Added - The ability to deserialize the return values of receive functions using `deserializeReceiveReturnValue()`. -## 5.1.0 2022-9-29 +## 2.1.0 2022-9-29 ### Added @@ -318,7 +439,7 @@ All function parameters now also accepts strings, these strings can use comma as - getAccountInfo no longer relies on instanceof to determine the type of input. -## 5.0.0 2022-9-29 +## 2.0.0 2022-9-29 ### Added @@ -328,7 +449,7 @@ All function parameters now also accepts strings, these strings can use comma as - Updated the signature of helper functions for accounts to sign messages. (and changed the prepend) -## 4.0.0 2022-8-26 +## 1.0.0 2022-8-26 ### Breaking changes @@ -339,7 +460,7 @@ All function parameters now also accepts strings, these strings can use comma as - Added missing `accountAddress` field to `AccountInfo` types. -## 3.0.0 2022-8-26 +## 0.5.0 2022-8-26 ### Added @@ -349,10 +470,11 @@ All function parameters now also accepts strings, these strings can use comma as - SchemaVersion, Module, and schema types are now 0-indexed instead of 1-indexed. This means that the schemas used for V0 contracts are now version 0, and so is the associated types. And the first schema version for V1 contracts are now version 1. -## 2.4.0 2022-8-15 +## 0.4.0 2022-8-15 ### Added +- `deserializeTransaction` function to deserialize transaction created by `serializeAccountTransactionForSubmission` and `serializeCredentialDeploymentTransactionForSubmission`. (Currently SimpleTransfer, SimpleTransferWithMemo and RegisterData are the only supported account transactions kinds) - `createIdentityRequest`, to create identity requests. - `createCredentialV1`, to create credentials using a seedPhrase. - `createIdentityRecoveryRequest`, to create identity recovery requests. @@ -361,54 +483,34 @@ All function parameters now also accepts strings, these strings can use comma as - Added `ConfigureBaker` to `AccountTransactionType` enum. - Added `ConcordiumHdWallet` with functions to get keys and randomness from a seed phrase. -## 2.3.2 2022-7-26 - ### Fixed - `deserializeTransaction` no longer throws an error on expired transactions. - -## 2.3.1 2022-7-26 - -### Fixed - - `deserializeTransaction` is now exported from index. -## 2.3.0 2022-7-25 - -### Added - -- `deserializeTransaction` function to deserialize transaction created by `serializeAccountTransactionForSubmission` and `serializeCredentialDeploymentTransactionForSubmission`. (Currently SimpleTransfer, SimpleTransferWithMemo and RegisterData are the only supported account transactions kinds) - -## 2.2.0 2022-7-21 +## 0.3.0 2022-7-21 ### Added +- Support deserializing new schema types: ULeb128, ILeb128, ByteArray and ByteList. +- Support deserializing schemas with versioning information. - Add support for getAccountInfo, InvokeContract, getCryptographicParameters and getModuleSource with JSON-RPC -## 2.1.1 2022-7-8 - ### Fixed - Fixed contract schema serialization for ByteList -## 2.1.0 2022-7-5 - -### Added - -- Support deserializing new schema types: ULeb128, ILeb128, ByteArray and ByteList. -- Support deserializing schemas with versioning information. - ### Changes - The function for deserializing a module schema `deserialModuleFromBuffer` now have the schema version as an optional argument. The function will try to extract the version from the buffer. When a version is provided it falls back to this, otherwise it throws an error. -## 2.0.1 2022-6-27 +## 0.2.1 2022-6-27 ### Fixed - @noble/ed25519 and cross-fetch moved from devDependencies to dependencies. -## 2.0.0 2022-6-24 +## 0.2.0 2022-6-24 ### Added @@ -421,7 +523,7 @@ All function parameters now also accepts strings, these strings can use comma as - `serializeInitContractParameters` and `serializeUpdateContractParameters` each have an additional parameter, which denotes the version of the schema provided. For existing users that are using V0 contracts, that parameter should be `SchemaVersion.V1`. - Deserialization of schemas have been changed: types and functions have been renamed and `deserialModuleFromBuffer` have an additional parameter. -## 1.0.1 2022-6-2 +## 0.1.1 2022-6-2 ### Fixed @@ -429,6 +531,6 @@ All function parameters now also accepts strings, these strings can use comma as (And update the format of the arguments for the server) - Fixed issue by bumping rust bindings version -## 1.0.0 2022-5-25 +## 0.1.0 2022-5-25 - Initial release diff --git a/packages/sdk/README.md b/packages/sdk/README.md new file mode 100644 index 000000000..860215d58 --- /dev/null +++ b/packages/sdk/README.md @@ -0,0 +1,70 @@ +# concordium-web-sdk + +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](https://github.com/Concordium/.github/blob/main/.github/CODE_OF_CONDUCT.md) + +Wrappers for interacting with the Concordium node, for the web environment. + +Please see the +[documentation](https://developer.concordium.software/concordium-node-sdk-js/index.html) +for more information + +**Table of Contents:** + + +- [concordium-web-sdk](#concordium-web-sdk) + - [ConcordiumGRPCWebClient](#concordiumgrpcwebclient) + - [ConcordiumGRPCNodeClient](#concordiumgrpcnodeclient) + + +## ConcordiumGRPCWebClient + +The SDK provides a gRPC client, which can interact with the [Concordium +Node](https://github.com/Concordium/concordium-node) using gRPC-web. + +For an overview of the endpoints, [click +here](https://developer.concordium.software/concordium-node-sdk-js/classes/grpc.ConcordiumGRPCClient.html). + +To create a client, the `ConcordiumGRPCWebClient` class can be used. It +requires the address and port of the concordium node. + +```ts +import { ConcordiumGRPCWebClient } from '@concordium/web-sdk'; +... +return new ConcordiumGRPCWebClient( + address, + port, + { timeout: 15000 } +); +``` + +The fourth argument is additional options. In the example +above we sat the timeout for a call to the node to 15 +seconds. The options allowed here are those allowed by the +[grpcweb-transport](https://www.npmjs.com/package/@protobuf-ts/grpcweb-transport). + +## ConcordiumGRPCNodeClient + +_Note that this can ONLY be used in a nodeJS environment_ + +The SDK provides a gRPC client, which can interact with the [Concordium +Node](https://github.com/Concordium/concordium-node) using gRPC. + +For an overview of the endpoints, [click +here](https://developer.concordium.software/concordium-node-sdk-js/classes/grpc.ConcordiumGRPCClient.html). + +To create a client, the `ConcordiumGRPCNodeClient` class can be used. It +requires the address and port of the concordium node. + +```ts +import { ConcordiumGRPCNodeClient, credentials } from '@concordium/web-sdk/nodejs'; +... +const creds = ...; // e.g. credentias.createInsecure(); +return new ConcordiumGRPCWebClient( + address, + port, + creds, + { timeout: 15000 } +); +``` + +Like with the grpc web client, the fourth argument is additional options. diff --git a/packages/sdk/jest.config.ts b/packages/sdk/jest.config.ts new file mode 100644 index 000000000..5405a74bd --- /dev/null +++ b/packages/sdk/jest.config.ts @@ -0,0 +1,13 @@ +import type { Config } from 'jest'; + +const esModules = ['@noble/ed25519'].join('|'); + +const config: Config = { + preset: 'ts-jest/presets/js-with-ts-esm', + moduleNameMapper: { + '^(\\.\\.?\\/.+)\\.js$': '$1', // Remap esmodule file extensions + }, + transformIgnorePatterns: [`node_modules/(?!${esModules})`], +}; + +export default config; diff --git a/packages/sdk/package.json b/packages/sdk/package.json new file mode 100644 index 000000000..508c8a387 --- /dev/null +++ b/packages/sdk/package.json @@ -0,0 +1,93 @@ +{ + "name": "@concordium/web-sdk", + "version": "7.0.0", + "license": "Apache-2.0", + "engines": { + "node": ">=16" + }, + "repository": { + "type": "git", + "url": "https://github.com/Concordium/concordium-node-sdk-js", + "directory": "packages/common" + }, + "sideEffects": false, + "type": "module", + "main": "./lib/umd/concordium.min.js", + "cdn": "./lib/umd/concordium.min.js", + "types": "./lib/esm/index.d.ts", + "exports": { + ".": { + "types": "./lib/esm/index.d.ts", + "bun": "./src/index.ts", + "default": "./lib/esm/index.js" + }, + "./nodejs": { + "types": "./lib/esm/pub/nodejs.d.ts", + "bun": "./src/pub/nodejs.ts", + "node": "./lib/esm/pub/nodejs.js", + "browser": null + }, + "./*": { + "types": "./lib/esm/pub/*.d.ts", + "bun": "./src/pub/*.ts", + "default": "./lib/esm/pub/*.js" + } + }, + "imports": { + "#ed25519": { + "node": "./lib/esm/shims/ed25519.node.js", + "default": "@noble/ed25519" + } + }, + "files": [ + "/src/**/*", + "/lib/esm/**/*", + "/lib/umd/**/*" + ], + "devDependencies": { + "@protobuf-ts/plugin": "^2.9.1", + "@types/bs58check": "^2.1.0", + "@types/jest": "^26.0.23", + "@types/json-bigint": "^1.0.1", + "@types/uuid": "^8.3.4", + "eslint": "^8.51.0", + "glob": "^10.3.10", + "grpc-tools": "^1.11.2", + "grpc_tools_node_protoc_ts": "5.3.0", + "jest": "^29.6.2", + "rimraf": "^5.0.1", + "ts-jest": "^29.1.1", + "typescript": "^5.2.2", + "webpack": "^5.88.2", + "webpack-cli": "^5.1.4" + }, + "scripts": { + "generate-ts-v2": "yarn run grpc_tools_node_protoc --plugin=protoc-gen-ts=../../node_modules/@protobuf-ts/plugin/bin/protoc-gen-ts --ts_opt 'optimize_code_size' --ts_out=src/grpc-api -I ../../deps/concordium-base/concordium-grpc-api ../../deps/concordium-base/concordium-grpc-api/v2/concordium/*.proto", + "generate": "([ -e \"../../deps/concordium-base/concordium-grpc-api\" ] && yarn generate-ts-v2 && node scripts/proto-node-esm-compat.js src/grpc-api) || echo 'Please checkout submodules before building'", + "lint": "eslint . --cache --ext .ts,.tsx --max-warnings 0", + "lint-fix": "yarn --silent lint --fix; exit 0", + "test": "jest", + "test-ci": "yarn test ./test/ci", + "build": "yarn clean; mkdir -p src/grpc-api; yarn generate && yarn build-dev", + "build-dev": "tsc -p tsconfig.build.json && yarn webpack", + "webpack": "node --loader ts-node/esm ../../node_modules/.bin/webpack --config webpack.config.ts", + "clean": "rimraf -- lib src/grpc-api" + }, + "dependencies": { + "@concordium/rust-bindings": "2.0.0", + "@grpc/grpc-js": "^1.9.4", + "@noble/ed25519": "^2.0.0", + "@protobuf-ts/grpc-transport": "^2.9.1", + "@protobuf-ts/grpcweb-transport": "^2.9.1", + "@protobuf-ts/runtime-rpc": "^2.8.2", + "@scure/bip39": "^1.2.1", + "@types/big.js": "^6.2.0", + "big.js": "^6.2.0", + "bs58check": "^3.0.1", + "buffer": "^6.0.3", + "hash.js": "^1.1.7", + "iso-3166-1": "^2.1.1", + "json-bigint": "^1.0.0", + "uuid": "^8.3.2" + } +} diff --git a/packages/sdk/scripts/proto-node-esm-compat.js b/packages/sdk/scripts/proto-node-esm-compat.js new file mode 100644 index 000000000..c91e3fd77 --- /dev/null +++ b/packages/sdk/scripts/proto-node-esm-compat.js @@ -0,0 +1,23 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import { glob } from 'glob'; +import fs from 'fs'; + +const protoRoot = process.argv[2]; // First 2 arguments are node and path to this script. 3rd is expected to be location of generated proto type files. + +if (!protoRoot) { + throw new Error('Please supply a dir for the proto types to convert'); +} + +const files = await glob(protoRoot + '/**/*.ts'); +files.forEach((file) => { + let content = fs.readFileSync(file, 'utf-8'); + + content = content + .split('\n') + .map((s) => + s.replace(/^(import .+? from ["']\..+?)(["'];)$/, '$1.js$2') + ) + .join('\n'); + + fs.writeFileSync(file, content, 'utf-8'); +}); diff --git a/packages/common/src/GenericContract.ts b/packages/sdk/src/GenericContract.ts similarity index 67% rename from packages/common/src/GenericContract.ts rename to packages/sdk/src/GenericContract.ts index 49bb7a5f4..0e3dc5ee4 100644 --- a/packages/common/src/GenericContract.ts +++ b/packages/sdk/src/GenericContract.ts @@ -1,38 +1,61 @@ -import { Buffer } from 'buffer/'; +import { Buffer } from 'buffer/index.js'; import { stringify } from 'json-bigint'; -import { checkParameterLength, getContractName } from './contractHelpers'; -import { ConcordiumGRPCClient } from './GRPCClient'; -import { AccountSigner, signTransaction } from './signHelpers'; +import { ConcordiumGRPCClient } from './grpc/GRPCClient.js'; +import { AccountSigner, signTransaction } from './signHelpers.js'; import { AccountTransactionType, - Base58String, Base64String, - ContractAddress, - Energy, HexString, InstanceInfo, InvokeContractResult, MakeOptional, SmartContractTypeValues, UpdateContractPayload, -} from './types'; -import { AccountAddress } from './types/accountAddress'; -import { CcdAmount } from './types/ccdAmount'; -import { TransactionExpiry } from './types/transactionExpiry'; +} from './types.js'; +import * as AccountAddress from './types/AccountAddress.js'; +import * as ContractAddress from './types/ContractAddress.js'; +import * as ContractName from './types/ContractName.js'; +import * as EntrypointName from './types/EntrypointName.js'; +import * as ReceiveName from './types/ReceiveName.js'; +import * as Parameter from './types/Parameter.js'; +import * as Energy from './types/Energy.js'; +import * as TransactionHash from './types/TransactionHash.js'; +import * as CcdAmount from './types/CcdAmount.js'; +import * as TransactionExpiry from './types/TransactionExpiry.js'; +import * as ModuleReference from './types/ModuleReference.js'; +import * as BlockHash from './types/BlockHash.js'; +import * as ReturnValue from './types/ReturnValue.js'; /** * Metadata necessary for smart contract transactions */ export type ContractTransactionMetadata = { - /** Amount (in microCCD) to include in the transaction. Defaults to 0n */ - amount?: bigint; + /** Amount to include in the transaction. Defaults to 0 */ + amount?: CcdAmount.Type; /** The sender address of the transaction */ - senderAddress: HexString; + senderAddress: AccountAddress.Type; /** Expiry date of the transaction. Defaults to 5 minutes in the future */ - expiry?: Date; + expiry?: TransactionExpiry.Type; /** Max energy to be used for the transaction */ - energy: Energy; + energy: Energy.Type; +}; + +/** + * Metadata necessary for invocating a smart contract. + */ +export type ContractInvokeMetadata = { + /** Amount to include in the transaction. Defaults to 0 */ + amount?: CcdAmount.Type; + /** + * Invoker of the contract. + * If this is not supplied then the contract will be invoked by an account with address 0, + * no credentials and sufficient amount of CCD to cover the transfer amount. + * If given, the relevant address (either account or contract) must exist in the blockstate. + */ + invoker?: ContractAddress.Type | AccountAddress.Type; + /** Max energy to be used for the transaction, if not provided the max energy is used. */ + energy?: Energy.Type; }; /** @@ -86,19 +109,10 @@ export type ContractUpdateTransactionWithSchema< /** * Default expiry date used for contract update transactions. */ -export function getContractUpdateDefaultExpiryDate(): Date { - const future5Minutes = Date.now() + 5 * 60 * 1000; - return new Date(future5Minutes); +export function getContractUpdateDefaultExpiryDate(): TransactionExpiry.Type { + return TransactionExpiry.futureMinutes(5); } -/** - * Converts an address (either contract address or account address in its base58 form) to a contract update "invoker" - */ -export const getInvoker = ( - address: Base58String | ContractAddress -): ContractAddress | AccountAddress => - typeof address !== 'string' ? address : new AccountAddress(address); - /** * Defines methods for performing dry-run invocations of updates on a Contract with entrypoints `E` * @@ -107,8 +121,8 @@ export const getInvoker = ( export class ContractDryRun { constructor( protected grpcClient: ConcordiumGRPCClient, - protected contractAddress: ContractAddress, - protected contractName: string + protected contractAddress: ContractAddress.Type, + protected contractName: ContractName.Type ) {} /** @@ -117,36 +131,56 @@ export class ContractDryRun { * * @template T - The type of the input given * - * @param {string} entrypoint - The name of the receive function to invoke. - * @param {ContractAddress | AccountAddress} invoker - The address of the invoker. + * @param {EntrypointName.Type} entrypoint - The name of the receive function to invoke. + * @param {ContractInvokeMetadata | ContractAddress | AccountAddress.Type} metaOrInvoker - Metadata for contract invocation of the address of the invoker. * @param {Function} serializer - A function for serializing the input to bytes. * @param {T} input - Input for for contract function. - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public invokeMethod( - entrypoint: E, - invoker: ContractAddress | AccountAddress, - serializer: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + metaOrInvoker: + | ContractInvokeMetadata + | ContractAddress.Type + | AccountAddress.Type, + serializer: (input: T) => ArrayBuffer, input: T, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { - const parameter = serializer(input); - checkParameterLength(parameter); - + const parameter = Parameter.fromBuffer(serializer(input)); + const meta = + AccountAddress.instanceOf(metaOrInvoker) || + ContractAddress.instanceOf(metaOrInvoker) + ? { invoker: metaOrInvoker } + : metaOrInvoker; return this.grpcClient.invokeContract( { + ...meta, contract: this.contractAddress, parameter, - invoker, - method: `${this.contractName}.${entrypoint}`, + method: ReceiveName.create(this.contractName, entrypoint), }, blockHash ); } } +/** Options for checking contract instance information on chain. */ +export type ContractCheckOnChainOptions = { + /** + * Hash of the block to check the information at. + * When not provided the last finalized block is used. + */ + blockHash?: BlockHash.Type; + /** + * The expected module reference to be used by the contract instance. + * When not provided no check is done against the module reference. + */ + moduleReference?: ModuleReference.Type; +}; + /** * Either a module schema, or a `Record` of parameter schemas per entrypoint `E` * @@ -168,8 +202,8 @@ class ContractBase { constructor( protected grpcClient: ConcordiumGRPCClient, - protected contractAddress: ContractAddress, - protected contractName: string, + protected contractAddress: ContractAddress.Type, + protected contractName: ContractName.Type, protected schema?: Schema ) { this.dryRunInstance = new ContractDryRun( @@ -183,7 +217,7 @@ class ContractBase { * Helper function for getting the {@link InstanceInfo} of a contract * * @param {ConcordiumGRPCClient} grpcClient - The GRPC client for accessing a node. - * @param {ContractAddress} contractAddress - The address of the contract. + * @param {ContractAddress.Type} contractAddress - The address of the contract. * * @throws if the {@link InstanceInfo} of the contract could not be found. * @@ -191,7 +225,7 @@ class ContractBase { */ protected static async getInstanceInfo( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress + contractAddress: ContractAddress.Type ): Promise { try { return await grpcClient.getInstanceInfo(contractAddress); @@ -208,21 +242,66 @@ class ContractBase { * Helper function for getting the name of a contract * * @param {ConcordiumGRPCClient} grpcClient - The GRPC client for accessing a node. - * @param {ContractAddress} contractAddress - The address of the contract. + * @param {ContractAddress.Type} contractAddress - The address of the contract. * * @throws if the {@link InstanceInfo} of the contract could not be found. * - * @returns {string} the name of the contract. + * @returns {ContractName.Type} the name of the contract. */ protected static async getContractName( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress - ): Promise { + contractAddress: ContractAddress.Type + ): Promise { const instanceInfo = await this.getInstanceInfo( grpcClient, contractAddress ); - return getContractName(instanceInfo); + return ContractName.fromInitName(instanceInfo.name); + } + + /** + * Get information on this smart contract instance. + * + * @param {BlockHash.Type} [blockHash] Hash of the block to check information at. When not provided the last finalized block is used. + + * @throws if the {@link InstanceInfo} of the contract could not be found. + + * @returns {InstanceInfo} The instance info. + */ + public async getInstanceInfo( + blockHash?: BlockHash.Type + ): Promise { + return this.grpcClient.getInstanceInfo(this.contractAddress, blockHash); + } + + /** + * Check if the smart contract instance exists on the blockchain and whether it uses a matching contract name. + * Optionally a module reference can be provided to check if the contract instance uses this module. + * + * @param {ContractCheckOnChainOptions} [options] Options for checking information on chain. + * + * @throws {RpcError} If failing to communicate with the concordium node or if the instance does not exist on chain or fails the checks. + */ + public async checkOnChain( + options: ContractCheckOnChainOptions = {} + ): Promise { + const info = await this.getInstanceInfo(options.blockHash); + const contractNameOnChain = ContractName.fromInitName(info.name); + + if (!ContractName.equals(contractNameOnChain, this.contractName)) { + throw new Error( + `Instance ${this.contractAddress} have contract name '${contractNameOnChain}' on chain. The client expected: '${this.contractName}'.` + ); + } + + if ( + options.moduleReference !== undefined && + info.sourceModule.moduleRef !== options.moduleReference.moduleRef + ) { + throw new Error( + `Instance ${this.contractAddress} uses module with reference '${info.sourceModule.moduleRef}' expected '${options.moduleReference.moduleRef}'` + ); + } } /** @@ -237,7 +316,7 @@ class ContractBase { * * @template T - The type of the input * - * @param {string} entrypoint - The name of the receive function to invoke. + * @param {EntrypointName.Type} entrypoint - The name of the receive function to invoke. * @param {Function} serializeInput - A function to serialize the `input` to bytes. * @param {ContractTransactionMetadata} metadata - Metadata to be used for the transaction creation (with defaults). * @param {T} input - Input for for contract function. @@ -247,8 +326,8 @@ class ContractBase { * @returns {ContractUpdateTransaction} Details necesary for submitting the contract update transaction. */ public createUpdateTransaction( - entrypoint: E, - serializeInput: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, metadata: CreateContractTransactionMetadata, input: T ): ContractUpdateTransaction; @@ -259,7 +338,7 @@ class ContractBase { * @template T - The type of the input * @template J - The type of the input formatted as JSON compatible with the corresponding contract schema * - * @param {string} entrypoint - The name of the receive function to invoke. + * @param {EntrypointName.Type} entrypoint - The name of the receive function to invoke. * @param {Function} serializeInput - A function to serialize the `input` to bytes. * @param {ContractTransactionMetadata} metadata - Metadata to be used for the transaction creation (with defaults). * @param {T} input - Input for for contract function. @@ -270,28 +349,30 @@ class ContractBase { * @returns {ContractUpdateTransactionWithSchema} Details necessary for submitting the contract update transaction (with JSON to be serialized with corresponding schema) */ public createUpdateTransaction( - entrypoint: E, - serializeInput: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, metadata: CreateContractTransactionMetadata, input: T, inputJsonFormatter: (input: T) => J ): MakeOptional, 'schema'>; public createUpdateTransaction( - entrypoint: E, - serializeInput: (input: T) => Buffer, - { amount = 0n, energy }: CreateContractTransactionMetadata, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, + { + amount = CcdAmount.zero(), + energy, + }: CreateContractTransactionMetadata, input: T, inputJsonFormatter?: (input: T) => J ): | ContractUpdateTransaction | MakeOptional, 'schema'> { - const parameter = serializeInput(input); - checkParameterLength(parameter); + const parameter = Parameter.fromBuffer(serializeInput(input)); const payload: UpdateContractPayload = { - amount: new CcdAmount(amount), + amount, address: this.contractAddress, - receiveName: `${this.contractName}.${entrypoint}`, + receiveName: ReceiveName.create(this.contractName, entrypoint), maxContractExecutionEnergy: energy, message: parameter, }; @@ -312,9 +393,11 @@ class ContractBase { value: this.schema, type: 'module', }; - } else if (this.schema?.[entrypoint] !== undefined) { + } else if ( + this.schema?.[EntrypointName.toString(entrypoint)] !== undefined + ) { schema = { - value: this.schema[entrypoint], + value: this.schema[EntrypointName.toString(entrypoint)], type: 'parameter', }; } @@ -322,7 +405,7 @@ class ContractBase { return { ...transaction, parameter: { - hex: parameter.toString('hex'), + hex: Parameter.toHexString(parameter), json: jsonParameter, }, schema, @@ -338,7 +421,7 @@ class ContractBase { * * @throws If the query could not be invoked successfully. * - * @returns {HexString} The transaction hash of the update transaction + * @returns {TransactionHash.Type} The transaction hash of the update transaction */ protected async sendUpdateTransaction( transactionBase: ContractUpdateTransaction, @@ -347,13 +430,14 @@ class ContractBase { expiry = getContractUpdateDefaultExpiryDate(), }: ContractTransactionMetadata, signer: AccountSigner - ): Promise { - const sender = new AccountAddress(senderAddress); - const { nonce } = await this.grpcClient.getNextAccountNonce(sender); + ): Promise { + const { nonce } = await this.grpcClient.getNextAccountNonce( + senderAddress + ); const header = { - expiry: new TransactionExpiry(expiry), + expiry, nonce: nonce, - sender, + sender: senderAddress, }; const transaction = { ...transactionBase, @@ -368,7 +452,7 @@ class ContractBase { * * @template T - The type of the input * - * @param {string} entrypoint - The name of the receive function to invoke. + * @param {EntrypointName.Type} entrypoint - The name of the receive function to invoke. * @param {Function} serializeInput - A function to serialize the `input` to bytes. * @param {CIS2.TransactionMetadata} metadata - Metadata to be used for the transaction (with defaults). * @param {T} input - Input for for contract function. @@ -376,15 +460,15 @@ class ContractBase { * * @throws If the query could not be invoked successfully. * - * @returns {HexString} The transaction hash of the update transaction + * @returns {TransactionHash.Type} The transaction hash of the update transaction */ public async createAndSendUpdateTransaction( - entrypoint: E, - serializeInput: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, metadata: ContractTransactionMetadata, input: T, signer: AccountSigner - ): Promise { + ): Promise { const transactionBase = this.createUpdateTransaction( entrypoint, serializeInput, @@ -400,31 +484,30 @@ class ContractBase { * @template T - The type of the input * @template R - The type the invocation response should be deserialized into. * - * @param {string} entrypoint - The name of the view function to invoke. + * @param {EntrypointName.Type} entrypoint - The name of the view function to invoke. * @param {Function} serializeInput - A function to serialize the `input` to bytes. * @param {Function} deserializeResponse - A function to deserialize the value returned from the view invocation. * @param {T | T[]} input - Input for for contract function. - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @throws If the query could not be invoked successfully. * * @returns {R} The transaction hash of the update transaction */ public async invokeView( - entrypoint: V, - serializeInput: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, deserializeResponse: (value: HexString) => R, input: T, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { - const parameter = serializeInput(input); - checkParameterLength(parameter); + const parameter = Parameter.fromBuffer(serializeInput(input)); const response = await this.grpcClient.invokeContract( { contract: this.contractAddress, parameter, - method: `${this.contractName}.${entrypoint}`, + method: ReceiveName.create(this.contractName, entrypoint), }, blockHash ); @@ -443,7 +526,9 @@ class ContractBase { ); } - return deserializeResponse(response.returnValue); + return deserializeResponse( + ReturnValue.toHexString(response.returnValue) + ); } } @@ -474,14 +559,15 @@ export class Contract< V extends string = string >( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress, + contractAddress: ContractAddress.Type, schema?: Schema ): Promise> { const instanceInfo = await super.getInstanceInfo( grpcClient, contractAddress ); - const contractName = getContractName(instanceInfo); + // No reason to run checks, since this is from chain. + const contractName = ContractName.fromInitName(instanceInfo.name); let mSchema: string | undefined; if (!schema) { @@ -489,7 +575,7 @@ export class Contract< const raw = await grpcClient.getEmbeddedSchema( instanceInfo.sourceModule ); - const encoded = raw.toString('base64'); + const encoded = Buffer.from(raw).toString('base64'); if (encoded) { mSchema = encoded; @@ -528,8 +614,8 @@ export abstract class CISContract< constructor( protected grpcClient: ConcordiumGRPCClient, - protected contractAddress: ContractAddress, - protected contractName: string + protected contractAddress: ContractAddress.Type, + protected contractName: ContractName.Type ) { super(grpcClient, contractAddress, contractName); @@ -545,8 +631,8 @@ export abstract class CISContract< */ protected abstract makeDryRunInstance( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress, - contractName: string + contractAddress: ContractAddress.Type, + contractName: ContractName.Type ): D; /** @@ -559,7 +645,7 @@ export abstract class CISContract< /** * Creates a {@link ContractUpdateTransactionWithSchema} contract update transaction, holding the necessary parts to sign/submit to the chain. * - * @param {string} entrypoint - The name of the receive function to invoke. + * @param {EntrypointName.Type} entrypoint - The name of the receive function to invoke. * @param {Function} serializeInput - A function to serialize the `input` to bytes. * @param {ContractTransactionMetadata} metadata - Metadata to be used for the transaction creation (with defaults). * @param {T} input - Input for for contract function. @@ -569,8 +655,8 @@ export abstract class CISContract< * @returns {ContractUpdateTransaction} The transaction hash of the update transaction */ public createUpdateTransaction( - entrypoint: E, - serializeInput: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, metadata: CreateContractTransactionMetadata, input: T ): ContractUpdateTransaction; @@ -578,7 +664,7 @@ export abstract class CISContract< /** * Creates a {@link ContractUpdateTransactionWithSchema} contract update transaction, holding the necessary parts to sign/submit to the chain. * - * @param {string} entrypoint - The name of the receive function to invoke. + * @param {EntrypointName.Type} entrypoint - The name of the receive function to invoke. * @param {Function} serializeInput - A function to serialize the `input` to bytes. * @param {ContractTransactionMetadata} metadata - Metadata to be used for the transaction creation (with defaults). * @param {T} input - Input for for contract function. @@ -589,8 +675,8 @@ export abstract class CISContract< * @returns {ContractUpdateTransactionWithSchema} The transaction hash of the update transaction */ public createUpdateTransaction( - entrypoint: E, - serializeInput: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, metadata: CreateContractTransactionMetadata, input: T, inputJsonFormatter: (input: T) => J @@ -599,8 +685,8 @@ export abstract class CISContract< T, J extends SmartContractTypeValues >( - entrypoint: E, - serializeInput: (input: T) => Buffer, + entrypoint: EntrypointName.Type, + serializeInput: (input: T) => ArrayBuffer, metadata: CreateContractTransactionMetadata, input: T, inputJsonFormatter?: (input: T) => J diff --git a/packages/sdk/src/accountHelpers.ts b/packages/sdk/src/accountHelpers.ts new file mode 100644 index 000000000..074aea5fe --- /dev/null +++ b/packages/sdk/src/accountHelpers.ts @@ -0,0 +1,47 @@ +import { + ReduceStakePendingChange, + RemovalPendingChange, + AccountInfo, + AccountInfoBaker, + AccountInfoDelegator, + StakePendingChange, + AccountInfoType, + StakePendingChangeType, +} from './types.js'; + +/** + * Whether {@link AccountInfo} parameter given is of type {@link AccountInfoDelegator}, i.e. the account is a delegator + * + * @deprecated check `type` member instead. + */ +export const isDelegatorAccount = ( + ai: AccountInfo +): ai is AccountInfoDelegator => ai.type === AccountInfoType.Delegator; + +/** + * Whether {@link AccountInfo} parameter given is of type {@link AccountInfoBaker}, i.e. the account is a baker. + * + * @deprecated check `type` member instead. + */ +export const isBakerAccount = (ai: AccountInfo): ai is AccountInfoBaker => + ai.type === AccountInfoType.Baker; + +/** + * Whether the pending change given is of type {@link ReduceStakePendingChange} + * + * @deprecated check `change` member instead. + */ +export const isReduceStakePendingChange = ( + spc: StakePendingChange +): spc is ReduceStakePendingChange => + spc.change === StakePendingChangeType.ReduceStake; + +/** + * Whether the pending change given is of type {@link RemovalPendingChange} + * + * @deprecated check `change` member instead. + */ +export const isRemovalPendingChange = ( + spc: StakePendingChange +): spc is RemovalPendingChange => + spc.change === StakePendingChangeType.RemoveStake; diff --git a/packages/common/src/accountTransactions.ts b/packages/sdk/src/accountTransactions.ts similarity index 86% rename from packages/common/src/accountTransactions.ts rename to packages/sdk/src/accountTransactions.ts index 640d52db8..4381e54b0 100644 --- a/packages/common/src/accountTransactions.ts +++ b/packages/sdk/src/accountTransactions.ts @@ -1,5 +1,5 @@ -import { Buffer } from 'buffer/'; -import { serializeCredentialDeploymentInfo } from './serialization'; +import { Buffer } from 'buffer/index.js'; +import { serializeCredentialDeploymentInfo } from './serialization.js'; import { encodeWord64, encodeDataBlob, @@ -10,7 +10,7 @@ import { encodeWord8, serializeConfigureDelegationPayload, serializeConfigureBakerPayload, -} from './serializationHelpers'; +} from './serializationHelpers.js'; import { AccountTransactionType, InitContractPayload, @@ -23,17 +23,19 @@ import { RegisterDataPayload, ConfigureDelegationPayload, ConfigureBakerPayload, -} from './types'; -import { AccountAddress } from './types/accountAddress'; -import { DataBlob } from './types/DataBlob'; -import { CcdAmount } from './types/ccdAmount'; -import { Readable } from 'stream'; +} from './types.js'; +import * as AccountAddress from './types/AccountAddress.js'; +import { DataBlob } from './types/DataBlob.js'; +import * as CcdAmount from './types/CcdAmount.js'; +import { Cursor } from './deserializationHelpers.js'; +import * as ReceiveName from './types/ReceiveName.js'; +import * as Parameter from './types/Parameter.js'; interface AccountTransactionHandler< PayloadType extends AccountTransactionPayload = AccountTransactionPayload > { serialize: (payload: PayloadType) => Buffer; - deserialize: (serializedPayload: Readable) => PayloadType; + deserialize: (serializedPayload: Cursor) => PayloadType; getBaseEnergyCost: (payload: PayloadType) => bigint; } @@ -45,16 +47,16 @@ export class SimpleTransferHandler } serialize(transfer: SimpleTransferPayload): Buffer { - const serializedToAddress = transfer.toAddress.decodedAddress; + const serializedToAddress = AccountAddress.toBuffer(transfer.toAddress); const serializedAmount = encodeWord64(transfer.amount.microCcdAmount); return Buffer.concat([serializedToAddress, serializedAmount]); } - deserialize(serializedPayload: Readable): SimpleTransferPayload { - const toAddress = AccountAddress.fromBytes( + deserialize(serializedPayload: Cursor): SimpleTransferPayload { + const toAddress = AccountAddress.fromBuffer( Buffer.from(serializedPayload.read(32)) ); - const amount = new CcdAmount( + const amount = CcdAmount.fromMicroCcd( serializedPayload.read(8).readBigUInt64BE(0) ); return { @@ -69,7 +71,7 @@ export class SimpleTransferWithMemoHandler implements AccountTransactionHandler { serialize(transfer: SimpleTransferWithMemoPayload): Buffer { - const serializedToAddress = transfer.toAddress.decodedAddress; + const serializedToAddress = AccountAddress.toBuffer(transfer.toAddress); const serializedMemo = encodeDataBlob(transfer.memo); const serializedAmount = encodeWord64(transfer.amount.microCcdAmount); return Buffer.concat([ @@ -79,15 +81,15 @@ export class SimpleTransferWithMemoHandler ]); } - deserialize(serializedPayload: Readable): SimpleTransferWithMemoPayload { - const toAddress = AccountAddress.fromBytes( + deserialize(serializedPayload: Cursor): SimpleTransferWithMemoPayload { + const toAddress = AccountAddress.fromBuffer( Buffer.from(serializedPayload.read(32)) ); const memoLength = serializedPayload.read(2).readUInt16BE(0); const memo = new DataBlob( Buffer.from(serializedPayload.read(memoLength)) ); - const amount = new CcdAmount( + const amount = CcdAmount.fromMicroCcd( serializedPayload.read(8).readBigUInt64BE(0) ); return { @@ -102,7 +104,7 @@ export class DeployModuleHandler implements AccountTransactionHandler { getBaseEnergyCost(payload: DeployModulePayload): bigint { - let length = payload.source.length; + let length = payload.source.byteLength; if (payload.version === undefined) { // Remove the 8 bytes from the embedded version and length. length -= 8; @@ -114,7 +116,7 @@ export class DeployModuleHandler serialize(payload: DeployModulePayload): Buffer { if (payload.version === undefined) { // Assume the module has version and length embedded - return payload.source; + return Buffer.from(payload.source); } else { // Assume the module is legacy build, which doesn't contain version and length const serializedWasm = packBufferWithWord32Length(payload.source); @@ -132,7 +134,7 @@ export class InitContractHandler implements AccountTransactionHandler { getBaseEnergyCost(payload: InitContractPayload): bigint { - return payload.maxContractExecutionEnergy; + return payload.maxContractExecutionEnergy.value; } serialize(payload: InitContractPayload): Buffer { @@ -140,7 +142,7 @@ export class InitContractHandler const initNameBuffer = Buffer.from('init_' + payload.initName, 'utf8'); const serializedInitName = packBufferWithWord16Length(initNameBuffer); const serializedModuleRef = payload.moduleRef.decodedModuleRef; - const parameterBuffer = payload.param; + const parameterBuffer = Parameter.toBuffer(payload.param); const serializedParameters = packBufferWithWord16Length(parameterBuffer); return Buffer.concat([ @@ -160,7 +162,7 @@ export class UpdateContractHandler implements AccountTransactionHandler { getBaseEnergyCost(payload: UpdateContractPayload): bigint { - return payload.maxContractExecutionEnergy; + return payload.maxContractExecutionEnergy.value; } serialize(payload: UpdateContractPayload): Buffer { @@ -171,13 +173,15 @@ export class UpdateContractHandler serializeIndex, serializeSubindex, ]); - const receiveNameBuffer = Buffer.from(payload.receiveName, 'utf8'); + const receiveNameBuffer = Buffer.from( + ReceiveName.toString(payload.receiveName), + 'utf8' + ); const serializedReceiveName = packBufferWithWord16Length(receiveNameBuffer); - const parameterBuffer = payload.message; - const serializedParameters = packBufferWithWord16Length( - Buffer.from(parameterBuffer) - ); + const parameterBuffer = Parameter.toBuffer(payload.message); + const serializedParameters = + packBufferWithWord16Length(parameterBuffer); return Buffer.concat([ serializedAmount, serializedContractAddress, @@ -250,7 +254,7 @@ export class RegisterDataHandler return encodeDataBlob(payload.data); } - deserialize(serializedPayload: Readable): RegisterDataPayload { + deserialize(serializedPayload: Cursor): RegisterDataPayload { const memoLength = serializedPayload.read(2).readUInt16BE(0); return { data: new DataBlob(Buffer.from(serializedPayload.read(memoLength))), diff --git a/packages/common/src/cis0.ts b/packages/sdk/src/cis0.ts similarity index 71% rename from packages/common/src/cis0.ts rename to packages/sdk/src/cis0.ts index c331e0375..eb0916b60 100644 --- a/packages/common/src/cis0.ts +++ b/packages/sdk/src/cis0.ts @@ -1,15 +1,20 @@ -import { Buffer } from 'buffer/'; +import { Buffer } from 'buffer/index.js'; import { stringify } from 'json-bigint'; -import { ContractAddress, HexString } from './types'; -import { ConcordiumGRPCClient } from './GRPCClient'; +import { ConcordiumGRPCClient } from './grpc/GRPCClient.js'; import { encodeWord16, packBufferWithWord8Length, -} from './serializationHelpers'; -import { getContractName } from './contractHelpers'; -import { makeDynamicFunction } from './util'; -import { makeDeserializeListResponse } from './deserializationHelpers'; +} from './serializationHelpers.js'; +import { makeDynamicFunction } from './util.js'; +import { makeDeserializeListResponse } from './deserializationHelpers.js'; +import * as ContractAddress from './types/ContractAddress.js'; +import * as BlockHash from './types/BlockHash.js'; +import * as Parameter from './types/Parameter.js'; +import * as ContractName from './types/ContractName.js'; +import * as ReceiveName from './types/ReceiveName.js'; +import * as ReturnValue from './types/ReturnValue.js'; +import { EntrypointName } from './index.js'; /** * Namespace with types for CIS-0 standard contracts @@ -43,7 +48,7 @@ export namespace CIS0 { /** The standard is supported by another contract located at `address` */ export type SupportBy = SupportResponse & { /** The address supporting the standard queried */ - addresses: ContractAddress[]; + addresses: ContractAddress.Type[]; }; /** Union of the different possible support query results. */ export type SupportResult = NoSupport | Support | SupportBy; @@ -72,13 +77,12 @@ const deserializeSupportResult = } const numAddresses = cursor.read(1).readUInt8(0); - const addresses: ContractAddress[] = []; + const addresses: ContractAddress.Type[] = []; for (let i = 0; i < numAddresses; i++) { - const index = cursor.read(8).readBigUInt64LE(0) as bigint; - const subindex = cursor.read(8).readBigUInt64LE(0) as bigint; - - addresses.push({ index, subindex }); + const index = cursor.read(8).readBigUInt64LE(0).valueOf(); + const subindex = cursor.read(8).readBigUInt64LE(0).valueOf(); + addresses.push(ContractAddress.create(index, subindex)); } return { @@ -91,9 +95,9 @@ const deserializeSupportResult = * Queries a CIS-0 contract for support for a {@link CIS0.StandardIdentifier}. * * @param {ConcordiumGRPCClient} grpcClient - The client to be used for the query. - * @param {ContractAddress} contractAddress - The address of the contract to query. + * @param {ContractAddress.Type} contractAddress - The address of the contract to query. * @param {CIS0.StandardIdentifier} standardId - The standard identifier to query for support in contract. - * @param {HexString} [blockHash] - The hash of the block to query at. + * @param {BlockHash.Type} [blockHash] - The hash of the block to query at. * * @throws If the query could not be invoked successfully. * @@ -101,17 +105,17 @@ const deserializeSupportResult = */ export function cis0Supports( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress, + contractAddress: ContractAddress.Type, standardId: CIS0.StandardIdentifier, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; /** * Queries a CIS-0 contract for support for a {@link CIS0.StandardIdentifier}. * * @param {ConcordiumGRPCClient} grpcClient - The client to be used for the query. - * @param {ContractAddress} contractAddress - The address of the contract to query. + * @param {ContractAddress.Type} contractAddress - The address of the contract to query. * @param {CIS0.StandardIdentifier[]} standardIds - The standard identifiers to query for support in contract. - * @param {HexString} [blockHash] - The hash of the block to query at. + * @param {BlockHash.Type} [blockHash] - The hash of the block to query at. * * @throws If the query could not be invoked successfully. * @@ -119,15 +123,15 @@ export function cis0Supports( */ export function cis0Supports( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress, + contractAddress: ContractAddress.Type, standardIds: CIS0.StandardIdentifier[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; export async function cis0Supports( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress, + contractAddress: ContractAddress.Type, standardIds: CIS0.StandardIdentifier | CIS0.StandardIdentifier[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const instanceInfo = await grpcClient .getInstanceInfo(contractAddress) @@ -139,21 +143,29 @@ export async function cis0Supports( ); }); - const contractName = getContractName(instanceInfo); + const contractName = ContractName.fromInitName(instanceInfo.name); + const supportReceiveName = ReceiveName.create( + contractName, + EntrypointName.fromStringUnchecked('supports') + ); - if (!instanceInfo.methods.includes(`${contractName}.supports`)) { + if ( + !instanceInfo.methods.some((methods) => + ReceiveName.equals(methods, supportReceiveName) + ) + ) { return undefined; } - const parameter = makeDynamicFunction(serializeSupportIdentifiers)( - standardIds + const parameter = Parameter.fromBuffer( + makeDynamicFunction(serializeSupportIdentifiers)(standardIds) ); const response = await grpcClient.invokeContract( { contract: contractAddress, parameter, - method: `${contractName}.supports`, + method: supportReceiveName, }, blockHash ); @@ -172,7 +184,9 @@ export async function cis0Supports( }` ); } - const results = deserializeSupportResult(response.returnValue); + const results = deserializeSupportResult( + ReturnValue.toHexString(response.returnValue) + ); const isListInput = Array.isArray(standardIds); const expectedValuesLength = isListInput ? standardIds.length : 1; diff --git a/packages/common/src/cis2/CIS2Contract.ts b/packages/sdk/src/cis2/CIS2Contract.ts similarity index 83% rename from packages/common/src/cis2/CIS2Contract.ts rename to packages/sdk/src/cis2/CIS2Contract.ts index db4f7a507..55c0c074e 100644 --- a/packages/common/src/cis2/CIS2Contract.ts +++ b/packages/sdk/src/cis2/CIS2Contract.ts @@ -1,8 +1,13 @@ import { stringify } from 'json-bigint'; -import { ContractAddress, HexString, InvokeContractResult } from '../types'; -import { ConcordiumGRPCClient } from '../GRPCClient'; -import { AccountSigner } from '../signHelpers'; +import type { HexString, InvokeContractResult } from '../types.js'; +import * as ContractAddress from '../types/ContractAddress.js'; +import * as BlockHash from '../types/BlockHash.js'; +import * as TransactionHash from '../types/TransactionHash.js'; +import * as ContractName from '../types/ContractName.js'; +import * as EntrypointName from '../types/EntrypointName.js'; +import { ConcordiumGRPCClient } from '../grpc/GRPCClient.js'; +import { AccountSigner } from '../signHelpers.js'; import { serializeCIS2Transfers, serializeCIS2BalanceOfQueries, @@ -14,11 +19,11 @@ import { formatCIS2UpdateOperator, formatCIS2Transfer, serializeCIS2UpdateOperators, -} from './util'; -import type { CIS2 } from './util'; -import { CIS0, cis0Supports } from '../cis0'; -import { CISContract, ContractDryRun, getInvoker } from '../GenericContract'; -import { makeDynamicFunction } from '../util'; + CIS2, +} from './util.js'; +import { CIS0, cis0Supports } from '../cis0.js'; +import { CISContract, ContractDryRun } from '../GenericContract.js'; +import { makeDynamicFunction } from '../util.js'; type Views = 'balanceOf' | 'operatorOf' | 'tokenMetadata'; type Updates = 'transfer' | 'updateOperator'; @@ -52,29 +57,29 @@ class CIS2DryRun extends ContractDryRun { * * @param {CIS2.Address} sender - Address of the sender of the transfer. * @param {CIS2.Transfer | CIS2.Transfer[]} transfer(s) - The transfer object(s). - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public transfer( sender: CIS2.Address, transfer: CIS2.Transfer, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; public transfer( sender: CIS2.Address, transfers: CIS2.Transfer[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; public transfer( sender: CIS2.Address, transfers: CIS2.Transfer | CIS2.Transfer[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const serialize = makeDynamicFunction(serializeCIS2Transfers); return this.invokeMethod( - 'transfer', - getInvoker(sender), + EntrypointName.fromStringUnchecked('transfer'), + sender, serialize, transfers, blockHash @@ -86,29 +91,29 @@ class CIS2DryRun extends ContractDryRun { * * @param {CIS2.Address} owner - Address of the owner of the address to perform the update on. * @param {CIS2.UpdateOperator | CIS2.UpdateOperator[]} update(s) - The update object(s). - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public updateOperator( owner: CIS2.Address, update: CIS2.UpdateOperator, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; public updateOperator( owner: CIS2.Address, updates: CIS2.UpdateOperator[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; public updateOperator( owner: CIS2.Address, updates: CIS2.UpdateOperator | CIS2.UpdateOperator[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const serialize = makeDynamicFunction(serializeCIS2UpdateOperators); return this.invokeMethod( - 'updateOperator', - getInvoker(owner), + EntrypointName.fromStringUnchecked('updateOperator'), + owner, serialize, updates, blockHash @@ -130,8 +135,8 @@ export class CIS2Contract extends CISContract { }; protected makeDryRunInstance( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress, - contractName: string + contractAddress: ContractAddress.Type, + contractName: ContractName.Type ): CIS2DryRun { return new CIS2DryRun(grpcClient, contractAddress, contractName); } @@ -140,14 +145,14 @@ export class CIS2Contract extends CISContract { * Creates a new `CIS2Contract` instance by querying the node for the necessary information through the supplied `grpcClient`. * * @param {ConcordiumGRPCClient} grpcClient - The client used for contract invocations and updates. - * @param {ContractAddress} contractAddress - Address of the contract instance. + * @param {ContractAddress.Type} contractAddress - Address of the contract instance. * * @throws If `InstanceInfo` could not be received for the contract, if the contract does not support the CIS-2 standard, * or if the contract name could not be parsed from the information received from the node. */ public static async create( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress + contractAddress: ContractAddress.Type ): Promise { const contractName = await super.getContractName( grpcClient, @@ -210,7 +215,7 @@ export class CIS2Contract extends CISContract { ); return this.createUpdateTransaction( - 'transfer', + EntrypointName.fromStringUnchecked('transfer'), serialize, metadata, transfers, @@ -226,13 +231,13 @@ export class CIS2Contract extends CISContract { * * @throws If the update could not be invoked successfully. * - * @returns {Promise} The transaction hash of the update transaction + * @returns {Promise} The transaction hash of the update transaction */ public transfer( metadata: CIS2.TransactionMetadata, transfer: CIS2.Transfer, signer: AccountSigner - ): Promise; + ): Promise; /** * Sends a CIS-2 "transfer" update transaction containing a list transfers. * @@ -242,18 +247,18 @@ export class CIS2Contract extends CISContract { * * @throws If the update could not be invoked successfully. * - * @returns {Promise} The transaction hash of the update transaction + * @returns {Promise} The transaction hash of the update transaction */ public transfer( metadata: CIS2.TransactionMetadata, transfers: CIS2.Transfer[], signer: AccountSigner - ): Promise; + ): Promise; public transfer( metadata: CIS2.TransactionMetadata, transfers: CIS2.Transfer | CIS2.Transfer[], signer: AccountSigner - ): Promise { + ): Promise { const transaction = this.createTransfer(metadata, transfers); return this.sendUpdateTransaction(transaction, metadata, signer); } @@ -302,7 +307,7 @@ export class CIS2Contract extends CISContract { ); return this.createUpdateTransaction( - 'updateOperator', + EntrypointName.fromStringUnchecked('updateOperator'), serialize, metadata, updates, @@ -319,13 +324,13 @@ export class CIS2Contract extends CISContract { * * @throws If the update could not be invoked successfully. * - * @returns {Promise} The transaction hash of the update transaction + * @returns {Promise} The transaction hash of the update transaction */ public updateOperator( metadata: CIS2.TransactionMetadata, update: CIS2.UpdateOperator, signer: AccountSigner - ): Promise; + ): Promise; /** * Sends a CIS-2 "operatorOf" update transaction containing a list of operator update instructions. * @@ -335,18 +340,18 @@ export class CIS2Contract extends CISContract { * * @throws If the update could not be invoked successfully. * - * @returns {Promise} The transaction hash of the update transaction + * @returns {Promise} The transaction hash of the update transaction */ public updateOperator( metadata: CIS2.TransactionMetadata, updates: CIS2.UpdateOperator[], signer: AccountSigner - ): Promise; + ): Promise; public updateOperator( metadata: CIS2.TransactionMetadata, updates: CIS2.UpdateOperator | CIS2.UpdateOperator[], signer: AccountSigner - ): Promise { + ): Promise { const transaction = this.createUpdateOperator(metadata, updates); return this.sendUpdateTransaction(transaction, metadata, signer); } @@ -355,7 +360,7 @@ export class CIS2Contract extends CISContract { * Invokes CIS-2 "balanceOf" with a single query. * * @param {CIS2.BalanceOfQuery} query - The query object specifying the details of the query. - * @param {HexString} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. * * @throws If the query could not be invoked successfully. * @@ -363,13 +368,13 @@ export class CIS2Contract extends CISContract { */ public balanceOf( query: CIS2.BalanceOfQuery, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; /** * Invokes CIS-2 "balanceOf" with a list of queries. * * @param {CIS2.BalanceOfQuery[]} queries - A list of query objects, each specifying the details of a query. - * @param {HexString} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. * * @throws If the query could not be invoked successfully. * @@ -377,11 +382,11 @@ export class CIS2Contract extends CISContract { */ public balanceOf( queries: CIS2.BalanceOfQuery[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; public async balanceOf( queries: CIS2.BalanceOfQuery | CIS2.BalanceOfQuery[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const serialize = makeDynamicFunction(serializeCIS2BalanceOfQueries); const deserialize = ensureMatchesInput( @@ -389,7 +394,7 @@ export class CIS2Contract extends CISContract { deserializeCIS2BalanceOfResponse ); return this.invokeView( - 'balanceOf', + EntrypointName.fromStringUnchecked('balanceOf'), serialize, deserialize, queries, @@ -401,7 +406,7 @@ export class CIS2Contract extends CISContract { * Invokes CIS-2 "operatorOf" with a single query. * * @param {CIS2.OperatorOfQuery} query - The query object specifying the details of the query. - * @param {HexString} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. * * @throws If the query could not be invoked successfully. * @@ -409,13 +414,13 @@ export class CIS2Contract extends CISContract { */ public operatorOf( query: CIS2.OperatorOfQuery, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; /** * Invokes CIS-2 "operatorOf" with a list of queries. * * @param {CIS2.OperatorOfQuery[]} queries - A list of query objects, each specifying the details of a query. - * @param {HexString} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. * * @throws If the query could not be invoked successfully. * @@ -424,11 +429,11 @@ export class CIS2Contract extends CISContract { */ public operatorOf( queries: CIS2.OperatorOfQuery[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; public operatorOf( queries: CIS2.OperatorOfQuery | CIS2.OperatorOfQuery[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const serialize = makeDynamicFunction(serializeCIS2OperatorOfQueries); const deserialize = ensureMatchesInput( @@ -436,7 +441,7 @@ export class CIS2Contract extends CISContract { deserializeCIS2OperatorOfResponse ); return this.invokeView( - 'operatorOf', + EntrypointName.fromStringUnchecked('operatorOf'), serialize, deserialize, queries, @@ -448,7 +453,7 @@ export class CIS2Contract extends CISContract { * Invokes CIS-2 "tokenMetadata" with a single token ID. * * @param {HexString} tokenId - The ID of the token to get the metadata URL for. - * @param {HexString} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. * * @throws If the query could not be invoked successfully. * @@ -456,13 +461,13 @@ export class CIS2Contract extends CISContract { */ public tokenMetadata( tokenId: HexString, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; /** * Invokes CIS-2 "tokenMetadata" with a list of token ID's. * * @param {HexString[]} tokenIds - A list of ID's of the tokens to get metadata URL's for. - * @param {HexString} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the query at. Defaults to the latest finalized block. * * @throws If the query could not be invoked successfully. * @@ -471,11 +476,11 @@ export class CIS2Contract extends CISContract { */ public tokenMetadata( tokenIds: HexString[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise; public tokenMetadata( tokenIds: HexString | HexString[], - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const serialize = makeDynamicFunction(serializeCIS2TokenIds); const deserialize = ensureMatchesInput( @@ -483,7 +488,7 @@ export class CIS2Contract extends CISContract { deserializeCIS2TokenMetadataResponse ); return this.invokeView( - 'tokenMetadata', + EntrypointName.fromStringUnchecked('tokenMetadata'), serialize, deserialize, tokenIds, diff --git a/packages/sdk/src/cis2/index.ts b/packages/sdk/src/cis2/index.ts new file mode 100644 index 000000000..bf362b2ad --- /dev/null +++ b/packages/sdk/src/cis2/index.ts @@ -0,0 +1,3 @@ +export type { CIS2 } from './util.js'; +export { tokenAddressFromBase58, tokenAddressToBase58 } from './util.js'; +export * from './CIS2Contract.js'; diff --git a/packages/common/src/cis2/util.ts b/packages/sdk/src/cis2/util.ts similarity index 86% rename from packages/common/src/cis2/util.ts rename to packages/sdk/src/cis2/util.ts index ba2d0e2b3..bcc137f2c 100644 --- a/packages/common/src/cis2/util.ts +++ b/packages/sdk/src/cis2/util.ts @@ -6,26 +6,30 @@ import { makeSerializeOptional, packBufferWithWord16Length, packBufferWithWord8Length, -} from '../serializationHelpers'; +} from '../serializationHelpers.js'; import type { Base58String, - ContractAddress, HexString, SmartContractTypeValues, -} from '../types'; -import { Buffer } from 'buffer/'; -import { AccountAddress } from '../types/accountAddress'; +} from '../types.js'; +import * as ContractAddress from '../types/ContractAddress.js'; +import * as AccountAddress from '../types/AccountAddress.js'; +import * as EntrypointName from '../types/EntrypointName.js'; +import { Buffer } from 'buffer/index.js'; import { uleb128Decode, uleb128DecodeWithIndex, uleb128Encode, -} from '../uleb128'; +} from '../uleb128.js'; import { ContractTransactionMetadata, ContractUpdateTransactionWithSchema, CreateContractTransactionMetadata, -} from '../GenericContract'; -import { Cursor, makeDeserializeListResponse } from '../deserializationHelpers'; +} from '../GenericContract.js'; +import { + Cursor, + makeDeserializeListResponse, +} from '../deserializationHelpers.js'; const TOKEN_ID_MAX_LENGTH = 255; const TOKEN_AMOUNT_MAX_LENGTH = 37; @@ -34,9 +38,9 @@ const TOKEN_RECEIVE_HOOK_MAX_LENGTH = 100; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace CIS2 { /** - * Union between `ContractAddress` and an account address represented by a `Base58String`. + * Union between `ContractAddress` and an account address `AccountAddress`. */ - export type Address = ContractAddress | Base58String; + export type Address = ContractAddress.Type | AccountAddress.Type; /** * A Token ID that uniquely identifies the CIS-2 Token @@ -48,7 +52,7 @@ export namespace CIS2 { * CIS-2 Token ID. */ export type TokenAddress = { - contract: ContractAddress; + contract: ContractAddress.Type; id: TokenId; }; @@ -57,15 +61,15 @@ export namespace CIS2 { */ export type ContractReceiver = { /** Contract address to receive tokens */ - address: ContractAddress; + address: ContractAddress.Type; /** Name of the entrypoint to be called on receiver contract. This is only the name of the function, NOT including the contract name */ - hookName: string; + hookName: EntrypointName.Type; }; /** - * Union between an account address represented by a `Base58String` and a `ContractReceiver`. + * Union between an account address represented by an `AccountAddress` and a `ContractReceiver`. */ - export type Receiver = Base58String | ContractReceiver; + export type Receiver = AccountAddress.Type | ContractReceiver; /** * Data needed to perform a "transfer" invocation according to the CIS-2 standard. @@ -73,7 +77,7 @@ export namespace CIS2 { export type Transfer = { /** The ID of the token to transfer */ tokenId: HexString; - /** The amount of tokens to transfer */ + /** The amount of tokens to transfer, cannot be negative. */ tokenAmount: bigint; /** The address to transfer from */ from: Address; @@ -175,13 +179,6 @@ export namespace CIS2 { }; } -/** - * Checks whether an `Address` is a `ContractAddress` - */ -export const isContractAddress = ( - address: CIS2.Address -): address is ContractAddress => typeof address !== 'string'; - function serializeCIS2TokenId(tokenId: CIS2.TokenId): Buffer { const serialized = Buffer.from(tokenId, 'hex'); @@ -194,13 +191,13 @@ function serializeCIS2TokenId(tokenId: CIS2.TokenId): Buffer { return packBufferWithWord8Length(serialized); } -function deserializeCIS2TokenId(buffer: Buffer): CIS2.TokenId { +function deserializeCIS2TokenId(buffer: Uint8Array): CIS2.TokenId { if (buffer.length > TOKEN_ID_MAX_LENGTH) { throw Error( `Token ID exceeds maximum size of ${TOKEN_ID_MAX_LENGTH} bytes` ); } - return buffer.toString('hex'); + return Buffer.from(buffer).toString('hex'); } function serializeTokenAmount(amount: bigint): Buffer { @@ -219,8 +216,8 @@ function serializeTokenAmount(amount: bigint): Buffer { return serialized; } -function serializeAccountAddress(address: HexString): Buffer { - return new AccountAddress(address).decodedAddress; +function serializeAccountAddress(address: AccountAddress.Type): Uint8Array { + return AccountAddress.toBuffer(address); } /** @@ -230,31 +227,33 @@ function serializeAccountAddress(address: HexString): Buffer { * * @returns {Buffer} the address serialized to bytes */ -export function serializeContractAddress(address: ContractAddress): Buffer { +export function serializeContractAddress( + address: ContractAddress.Type +): Uint8Array { const index = encodeWord64(address.index, true); const subindex = encodeWord64(address.subindex, true); return Buffer.concat([index, subindex]); } function serializeAddress(address: CIS2.Address): Buffer { - const isContract = isContractAddress(address); - const type = encodeWord8(isContract ? 1 : 0); - const serializedAddress = !isContract - ? serializeAccountAddress(address) - : serializeContractAddress(address); - - return Buffer.concat([type, serializedAddress]); + return Buffer.concat( + ContractAddress.instanceOf(address) + ? [encodeWord8(1), serializeContractAddress(address)] + : [encodeWord8(0), serializeAccountAddress(address)] + ); } /** - * Serializes {@link string} contract entrypoint into bytes, prefixed by a 2-byte length + * Serializes {@link EntrypointName.Type} contract entrypoint into bytes, prefixed by a 2-byte length * - * @param {string} hook - the entrypoint to serialize + * @param {EntrypointName.Type} hook - the entrypoint to serialize * - * @returns {Buffer} the entrypoint serialized to bytes + * @returns {Uint8Array} the entrypoint serialized to bytes */ -export function serializeReceiveHookName(hook: string): Buffer { - const serialized = Buffer.from(hook, 'ascii'); +export function serializeReceiveHookName( + hook: EntrypointName.Type +): Uint8Array { + const serialized = Buffer.from(EntrypointName.toString(hook), 'ascii'); if (serialized.length > TOKEN_RECEIVE_HOOK_MAX_LENGTH) { throw new Error( @@ -272,13 +271,11 @@ function serializeContractReceiver(receiver: CIS2.ContractReceiver): Buffer { } function serializeReceiver(receiver: CIS2.Receiver): Buffer { - const isAccount = typeof receiver === 'string'; - const type = encodeWord8(isAccount ? 0 : 1); - const serializedAddress = isAccount - ? serializeAccountAddress(receiver) - : serializeContractReceiver(receiver); - - return Buffer.concat([type, serializedAddress]); + return Buffer.concat( + AccountAddress.instanceOf(receiver) + ? [encodeWord8(0), AccountAddress.toBuffer(receiver)] + : [encodeWord8(1), serializeContractReceiver(receiver)] + ); } function serializeAdditionalData(data: HexString): Buffer { @@ -287,7 +284,7 @@ function serializeAdditionalData(data: HexString): Buffer { } const makeSerializeList = - (serialize: (input: T) => Buffer) => + (serialize: (input: T) => Uint8Array) => (input: T[]): Buffer => { const n = encodeWord16(input.length, true); return Buffer.concat([n, ...input.map(serialize)]); @@ -491,10 +488,7 @@ export function tokenAddressFromBase58(str: Base58String): CIS2.TokenAddress { ); } - const contract = { - index, - subindex, - }; + const contract = ContractAddress.create(index, subindex); const id = deserializeCIS2TokenId(tokenIdBytes); @@ -562,7 +556,7 @@ export function formatCIS2UpdateOperator( ): CIS2.UpdateOperatorParamJson { return { update: input.type === 'add' ? { Add: {} } : { Remove: {} }, - operator: isContractAddress(input.address) + operator: ContractAddress.instanceOf(input.address) ? { Contract: [ { @@ -571,7 +565,7 @@ export function formatCIS2UpdateOperator( }, ], } - : { Account: [input.address] }, + : { Account: [AccountAddress.toBase58(input.address)] }, }; } @@ -581,7 +575,7 @@ export function formatCIS2UpdateOperator( export function formatCIS2Transfer( input: CIS2.Transfer ): CIS2.TransferParamJson { - const from: CIS2.AddressParamJson = isContractAddress(input.from) + const from: CIS2.AddressParamJson = ContractAddress.instanceOf(input.from) ? { Contract: [ { @@ -590,10 +584,10 @@ export function formatCIS2Transfer( }, ], } - : { Account: [input.from] }; + : { Account: [AccountAddress.toBase58(input.from)] }; let to: CIS2.ReceiverParamJson; - if (typeof input.to === 'string') { - to = { Account: [input.to] }; + if (AccountAddress.instanceOf(input.to)) { + to = { Account: [AccountAddress.toBase58(input.to)] }; } else { to = { Contract: [ @@ -601,7 +595,7 @@ export function formatCIS2Transfer( index: Number(input.to.address.index), subindex: Number(input.to.address.subindex), }, - input.to.hookName, + EntrypointName.toString(input.to.hookName), ], }; } diff --git a/packages/common/src/cis4/CIS4Contract.ts b/packages/sdk/src/cis4/CIS4Contract.ts similarity index 81% rename from packages/common/src/cis4/CIS4Contract.ts rename to packages/sdk/src/cis4/CIS4Contract.ts index 7947e1a64..a74bd2596 100644 --- a/packages/common/src/cis4/CIS4Contract.ts +++ b/packages/sdk/src/cis4/CIS4Contract.ts @@ -1,6 +1,5 @@ -import { Buffer } from 'buffer/'; +import { Buffer } from 'buffer/index.js'; -import { AccountSigner, ConcordiumGRPCClient } from '..'; import { ContractTransactionMetadata, ContractUpdateTransactionWithSchema, @@ -8,14 +7,15 @@ import { CISContract, ContractDryRun, getContractUpdateDefaultExpiryDate, - getInvoker, -} from '../GenericContract'; -import type { - ContractAddress, - Base58String, - HexString, - InvokeContractResult, -} from '../types'; +} from '../GenericContract.js'; +import { ConcordiumGRPCClient } from '../grpc/GRPCClient.js'; +import { AccountSigner } from '../signHelpers.js'; +import type { HexString, InvokeContractResult } from '../types.js'; +import * as ContractAddress from '../types/ContractAddress.js'; +import * as AccountAddress from '../types/AccountAddress.js'; +import * as ContractName from '../types/ContractName.js'; +import * as EntrypointName from '../types/EntrypointName.js'; +import * as Timestamp from '../types/Timestamp.js'; import { CIS4, deserializeCIS4CredentialEntry, @@ -34,7 +34,10 @@ import { serializeCIS4RevokeCredentialIssuerParam, serializeCIS4UpdateRevocationKeysParam, Web3IdSigner, -} from './util'; +} from './util.js'; +import * as BlockHash from '../types/BlockHash.js'; +import * as TransactionHash from '../types/TransactionHash.js'; +import * as TransactionExpiry from '../types/TransactionExpiry.js'; type Views = | 'credentialEntry' @@ -59,22 +62,22 @@ class CIS4DryRun extends ContractDryRun { /** * Performs a dry-run invocation of "CIS4.registerCredential" * - * @param {Base58String | ContractAddress} sender - Address of the sender of the transfer. + * @param {AccountAddress.Type | ContractAddress.Type} sender - Address of the sender of the transfer. * @param {CIS4.CredentialInfo} credInfo - the credential info to register * @param {HexString} [additionalData] - any additional data to include - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public registerCredential( - sender: Base58String | ContractAddress, + sender: AccountAddress.Type | ContractAddress.Type, credInfo: CIS4.CredentialInfo, additionalData: HexString = '', - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { return this.invokeMethod( - 'registerCredential', - getInvoker(sender), + EntrypointName.fromStringUnchecked('registerCredential'), + sender, serializeCIS4RegisterCredentialParam, { credInfo, additionalData }, blockHash @@ -84,24 +87,24 @@ class CIS4DryRun extends ContractDryRun { /** * Performs a dry-run invocation of "CIS4.revokeCredentialIssuer" * - * @param {Base58String | ContractAddress} sender - Address of the sender of the transfer. + * @param {AccountAddress.Type | ContractAddress.Type} sender - Address of the sender of the transfer. * @param {HexString} credHolderPubKey - the public key of the credential holder (hex encoded) * @param {string} [reason] - the reason for the revocation * @param {HexString} [additionalData] - any additional data to include - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public revokeCredentialAsIssuer( - sender: Base58String | ContractAddress, + sender: AccountAddress.Type | ContractAddress.Type, credHolderPubKey: HexString, reason?: string, additionalData: HexString = '', - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { return this.invokeMethod( - 'revokeCredentialIssuer', - getInvoker(sender), + EntrypointName.fromStringUnchecked('revokeCredentialIssuer'), + sender, serializeCIS4RevokeCredentialIssuerParam, { credHolderPubKey, reason, additionalData }, blockHash @@ -111,30 +114,32 @@ class CIS4DryRun extends ContractDryRun { /** * Performs a dry-run invocation of "CIS4.revokeCredentialHolder" * - * @param {Base58String | ContractAddress} sender - Address of the sender of the transfer. + * @param {AccountAddress.Type | ContractAddress.Type} sender - Address of the sender of the transfer. * @param {Web3IdSigner} credHolderSigner - A signer structure for the credential holder * @param {bigint} nonce - the nonce of the owner inside the contract * @param {Date} expiry - Expiry time of the revocation message * @param {string} [reason] - the reason for the revocation - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public async revokeCredentialAsHolder( - sender: Base58String | ContractAddress, + sender: AccountAddress.Type | ContractAddress.Type, credHolderSigner: Web3IdSigner, nonce: bigint, expiry: Date, reason?: string, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const credentialPubKey = credHolderSigner.pubKey; - const entrypoint = 'revokeCredentialHolder'; + const entrypoint = EntrypointName.fromStringUnchecked( + 'revokeCredentialHolder' + ); const signingData: CIS4.SigningData = { contractAddress: this.contractAddress, entrypoint, nonce, - timestamp: expiry, + timestamp: Timestamp.fromDate(expiry), }; const serializedData = serializeCIS4RevocationDataHolder({ credentialPubKey, @@ -146,7 +151,7 @@ class CIS4DryRun extends ContractDryRun { return this.invokeMethod( entrypoint, - getInvoker(sender), + sender, () => Buffer.concat([signature, serializedData]), // Reuse existing serialization undefined, blockHash @@ -156,32 +161,34 @@ class CIS4DryRun extends ContractDryRun { /** * Performs a dry-run invocation of "CIS4.revokeCredentialOther" * - * @param {Base58String | ContractAddress} sender - Address of the sender of the transfer. + * @param {AccountAddress.Type | ContractAddress.Type} sender - Address of the sender of the transfer. * @param {Web3IdSigner} revokerSigner - A signer structure for the credential holder * @param {HexString} credentialPubKey - the public key (hex encoded) for the credential to revoke * @param {bigint} nonce - the nonce of the owner inside the contract * @param {Date} expiry - Expiry time of the revocation message * @param {string} [reason] - the reason for the revocation - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public async revokeCredentialAsOther( - sender: Base58String | ContractAddress, + sender: AccountAddress.Type | ContractAddress.Type, revokerSigner: Web3IdSigner, credentialPubKey: HexString, nonce: bigint, expiry: Date, reason?: string, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const revocationPubKey = revokerSigner.pubKey; - const entrypoint = 'revokeCredentialOther'; + const entrypoint = EntrypointName.fromStringUnchecked( + 'revokeCredentialOther' + ); const signingData: CIS4.SigningData = { contractAddress: this.contractAddress, entrypoint, nonce, - timestamp: expiry, + timestamp: Timestamp.fromDate(expiry), }; const serializedData = serializeCIS4RevocationDataOther({ credentialPubKey, @@ -194,7 +201,7 @@ class CIS4DryRun extends ContractDryRun { return this.invokeMethod( entrypoint, - getInvoker(sender), + sender, () => Buffer.concat([signature, serializedData]), // Reuse existing serialization undefined, blockHash @@ -204,23 +211,23 @@ class CIS4DryRun extends ContractDryRun { /** * Performs a dry-run invocation of "CIS4.registerRevocationKeys" * - * @param {Base58String | ContractAddress} sender - Address of the sender of the transfer. + * @param {AccountAddress.Type | ContractAddress.Type} sender - Address of the sender of the transfer. * @param {HexString | HexString[]} keys - a single or list of hex encoded public keys to be used for revocation * @param {HexString} [additionalData] - any additional data to include - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public registerRevocationKeys( - sender: Base58String | ContractAddress, + sender: AccountAddress.Type | ContractAddress.Type, keys: HexString | HexString[], additionalData: HexString = '', - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const ks = Array.isArray(keys) ? keys : [keys]; return this.invokeMethod( - 'registerRevocationKeys', - getInvoker(sender), + EntrypointName.fromStringUnchecked('registerRevocationKeys'), + sender, serializeCIS4UpdateRevocationKeysParam, { additionalData, keys: ks }, blockHash @@ -230,23 +237,23 @@ class CIS4DryRun extends ContractDryRun { /** * Performs a dry-run invocation of "CIS4.removeRevocationKeys" * - * @param {Base58String | ContractAddress} sender - Address of the sender of the transfer. + * @param {AccountAddress.Type | ContractAddress.Type} sender - Address of the sender of the transfer. * @param {HexString | HexString[]} keys - a single or list of hex encoded public keys to be removed * @param {HexString} [additionalData] - any additional data to include - * @param {HexString} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. + * @param {BlockHash.Type} [blockHash] - The hash of the block to perform the invocation of. Defaults to the latest finalized block on chain. * * @returns {InvokeContractResult} the contract invocation result, which includes whether or not the invocation succeeded along with the energy spent. */ public removeRevocationKeys( - sender: Base58String | ContractAddress, + sender: AccountAddress.Type | ContractAddress.Type, keys: HexString | HexString[], additionalData: HexString = '', - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const ks = Array.isArray(keys) ? keys : [keys]; return this.invokeMethod( - 'removeRevocationKeys', - getInvoker(sender), + EntrypointName.fromStringUnchecked('removeRevocationKeys'), + sender, serializeCIS4UpdateRevocationKeysParam, { additionalData, keys: ks }, blockHash @@ -295,7 +302,7 @@ export class CIS4Contract extends CISContract { */ public static async create( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress + contractAddress: ContractAddress.Type ): Promise { const contractName = await super.getContractName( grpcClient, @@ -306,8 +313,8 @@ export class CIS4Contract extends CISContract { protected makeDryRunInstance( grpcClient: ConcordiumGRPCClient, - contractAddress: ContractAddress, - contractName: string + contractAddress: ContractAddress.Type, + contractName: ContractName.Type ): CIS4DryRun { return new CIS4DryRun(grpcClient, contractAddress, contractName); } @@ -316,16 +323,16 @@ export class CIS4Contract extends CISContract { * Look up an entry in the registry by the public key of its holder. * * @param {HexString} credHolderPubKey - public key identifying the credential holder - * @param {HexString} [blockHash] - block to perform query at. + * @param {BlockHash.Type} [blockHash] - block to perform query at. * * @returns {CIS4.CredentialEntry} a corresponding credential entry. */ public credentialEntry( credHolderPubKey: HexString, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { return this.invokeView( - 'credentialEntry', + EntrypointName.fromStringUnchecked('credentialEntry'), (k) => Buffer.from(k, 'hex'), deserializeCIS4CredentialEntry, credHolderPubKey, @@ -337,16 +344,16 @@ export class CIS4Contract extends CISContract { * Look up the status of a credential by the public key of its holder. * * @param {HexString} credHolderPubKey - public key identifying the credential holder - * @param {HexString} [blockHash] - block to perform query at. + * @param {BlockHash.Type} [blockHash] - block to perform query at. * * @returns {CIS4.CredentialStatus} a corresponding credential status. */ public credentialStatus( credHolderPubKey: HexString, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { return this.invokeView( - 'credentialStatus', + EntrypointName.fromStringUnchecked('credentialStatus'), (k) => Buffer.from(k, 'hex'), deserializeCIS4CredentialStatus, credHolderPubKey, @@ -357,15 +364,15 @@ export class CIS4Contract extends CISContract { /** * Get list of all revocation keys and their corresponding nonces. * - * @param {HexString} [blockHash] - block to perform query at. + * @param {BlockHash.Type} [blockHash] - block to perform query at. * * @returns {CIS4.RevocationKeyWithNonce[]} the revocation keys wityh corresponding nonces. */ public revocationKeys( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { return this.invokeView( - 'revocationKeys', + EntrypointName.fromStringUnchecked('revocationKeys'), () => Buffer.alloc(0), deserializeCIS4RevocationKeys, undefined, @@ -376,15 +383,15 @@ export class CIS4Contract extends CISContract { /** * Get the registry metadata. * - * @param {HexString} [blockHash] - block to perform query at. + * @param {BlockHash.Type} [blockHash] - block to perform query at. * * @returns {CIS4.MetadataUrl} a metadata URL. */ public registryMetadata( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { return this.invokeView( - 'registryMetadata', + EntrypointName.fromStringUnchecked('registryMetadata'), () => Buffer.alloc(0), deserializeCIS4MetadataResponse, undefined, @@ -395,13 +402,13 @@ export class CIS4Contract extends CISContract { /** * Get the {@link AccountAddress} public key of the issuer. * - * @param {HexString} [blockHash] - block to perform query at. + * @param {BlockHash.Type} [blockHash] - block to perform query at. * * @returns {HexString} a hex encoded public key. */ - public issuer(blockHash?: HexString): Promise { + public issuer(blockHash?: BlockHash.Type): Promise { return this.invokeView( - 'issuer', + EntrypointName.fromStringUnchecked('issuer'), () => Buffer.alloc(0), (value) => value, undefined, @@ -424,7 +431,7 @@ export class CIS4Contract extends CISContract { additionalData: HexString = '' ): ContractUpdateTransactionWithSchema { return this.createUpdateTransaction( - 'registerCredential', + EntrypointName.fromStringUnchecked('registerCredential'), serializeCIS4RegisterCredentialParam, metadata, { credInfo, additionalData }, @@ -440,14 +447,14 @@ export class CIS4Contract extends CISContract { * @param {CIS4.CredentialInfo} credInfo - the credential info to register * @param {HexString} [additionalData] - any additional data to include * - * @returns {HexString} The hash of the submitted transaction + * @returns {TransactionHash.Type} The hash of the submitted transaction */ public registerCredential( signer: AccountSigner, metadata: ContractTransactionMetadata, credInfo: CIS4.CredentialInfo, additionalData: HexString = '' - ): Promise { + ): Promise { const transaction = this.createRegisterCredential( metadata, credInfo, @@ -473,7 +480,7 @@ export class CIS4Contract extends CISContract { additionalData: HexString = '' ): ContractUpdateTransactionWithSchema { return this.createUpdateTransaction( - 'revokeCredentialIssuer', + EntrypointName.fromStringUnchecked('revokeCredentialIssuer'), serializeCIS4RevokeCredentialIssuerParam, metadata, { credHolderPubKey, reason, additionalData }, @@ -490,7 +497,7 @@ export class CIS4Contract extends CISContract { * @param {string} [reason] - the reason for the revocation * @param {HexString} [additionalData] - any additional data to include * - * @returns {HexString} The hash of the submitted transaction + * @returns {TransactionHash.Type} The hash of the submitted transaction */ public revokeCredentialAsIssuer( signer: AccountSigner, @@ -498,7 +505,7 @@ export class CIS4Contract extends CISContract { credHolderPubKey: HexString, reason?: string, additionalData: HexString = '' - ): Promise { + ): Promise { const transaction = this.createRevokeCredentialAsIssuer( metadata, credHolderPubKey, @@ -527,12 +534,14 @@ export class CIS4Contract extends CISContract { reason?: string ): Promise { const credentialPubKey = credHolderSigner.pubKey; - const entrypoint = 'revokeCredentialHolder'; + const entrypoint = EntrypointName.fromStringUnchecked( + 'revokeCredentialHolder' + ); const signingData: CIS4.SigningData = { contractAddress: this.contractAddress, entrypoint, nonce, - timestamp: expiry, + timestamp: Timestamp.fromDate(expiry), }; const serializedData = serializeCIS4RevocationDataHolder({ credentialPubKey, @@ -567,7 +576,7 @@ export class CIS4Contract extends CISContract { * @param {bigint} nonce - the nonce of the owner inside the contract * @param {string} [reason] - the reason for the revocation * - * @returns {HexString} The hash of the submitted transaction + * @returns {TransactionHash.Type} The hash of the submitted transaction */ public async revokeCredentialAsHolder( signer: AccountSigner, @@ -575,12 +584,14 @@ export class CIS4Contract extends CISContract { credHolderSigner: Web3IdSigner, nonce: bigint, reason?: string - ): Promise { + ): Promise { const transaction = await this.createRevokeCredentialAsHolder( metadata, credHolderSigner, nonce, - metadata.expiry ?? getContractUpdateDefaultExpiryDate(), + TransactionExpiry.toDate( + metadata.expiry ?? getContractUpdateDefaultExpiryDate() + ), reason ); return this.sendUpdateTransaction(transaction, metadata, signer); @@ -607,12 +618,14 @@ export class CIS4Contract extends CISContract { reason?: string ): Promise { const revocationPubKey = revokerSigner.pubKey; - const entrypoint = 'revokeCredentialOther'; + const entrypoint = EntrypointName.fromStringUnchecked( + 'revokeCredentialOther' + ); const signingData: CIS4.SigningData = { contractAddress: this.contractAddress, entrypoint, nonce, - timestamp: expiry, + timestamp: Timestamp.fromDate(expiry), }; const serializedData = serializeCIS4RevocationDataOther({ credentialPubKey, @@ -654,7 +667,7 @@ export class CIS4Contract extends CISContract { * @param {bigint} nonce - the nonce of the owner inside the contract * @param {string} [reason] - the reason for the revocation * - * @returns {HexString} The hash of the submitted transaction + * @returns {TransactionHash.Type} The hash of the submitted transaction */ public async revokeCredentialAsOther( signer: AccountSigner, @@ -663,13 +676,15 @@ export class CIS4Contract extends CISContract { credentialPubKey: HexString, nonce: bigint, reason?: string - ): Promise { + ): Promise { const transaction = await this.createRevokeCredentialAsOther( metadata, revokerSigner, credentialPubKey, nonce, - metadata.expiry ?? getContractUpdateDefaultExpiryDate(), + TransactionExpiry.toDate( + metadata.expiry ?? getContractUpdateDefaultExpiryDate() + ), reason ); return this.sendUpdateTransaction(transaction, metadata, signer); @@ -691,7 +706,7 @@ export class CIS4Contract extends CISContract { ): ContractUpdateTransactionWithSchema { const ks = Array.isArray(keys) ? keys : [keys]; return this.createUpdateTransaction( - 'registerRevocationKeys', + EntrypointName.fromStringUnchecked('registerRevocationKeys'), serializeCIS4UpdateRevocationKeysParam, metadata, { additionalData, keys: ks }, @@ -707,14 +722,14 @@ export class CIS4Contract extends CISContract { * @param {HexString | HexString[]} keys - a single or list of hex encoded public keys to be used for revocation * @param {HexString} [additionalData] - any additional data to include * - * @returns {HexString} The hash of the submitted transaction + * @returns {TransactionHash.Type} The hash of the submitted transaction */ public registerRevocationKeys( signer: AccountSigner, metadata: ContractTransactionMetadata, keys: HexString | HexString[], additionalData: HexString = '' - ): Promise { + ): Promise { const transaction = this.createRegisterRevocationKeys( metadata, keys, @@ -739,7 +754,7 @@ export class CIS4Contract extends CISContract { ): ContractUpdateTransactionWithSchema { const ks = Array.isArray(keys) ? keys : [keys]; return this.createUpdateTransaction( - 'removeRevocationKeys', + EntrypointName.fromStringUnchecked('removeRevocationKeys'), serializeCIS4UpdateRevocationKeysParam, metadata, { additionalData, keys: ks }, @@ -755,14 +770,14 @@ export class CIS4Contract extends CISContract { * @param {HexString | HexString[]} keys - a single or list of hex encoded public keys to be removed * @param {HexString} [additionalData] - any additional data to include * - * @returns {HexString} The hash of the submitted transaction + * @returns {TransactionHash.Type} The hash of the submitted transaction */ public removeRevocationKeys( signer: AccountSigner, metadata: ContractTransactionMetadata, keys: HexString | HexString[], additionalData: HexString = '' - ): Promise { + ): Promise { const transaction = this.createRemoveRevocationKeys( metadata, keys, diff --git a/packages/sdk/src/cis4/index.ts b/packages/sdk/src/cis4/index.ts new file mode 100644 index 000000000..3aa9163d8 --- /dev/null +++ b/packages/sdk/src/cis4/index.ts @@ -0,0 +1,2 @@ +export { CIS4, Web3IdSigner } from './util.js'; +export * from './CIS4Contract.js'; diff --git a/packages/common/src/cis4/util.ts b/packages/sdk/src/cis4/util.ts similarity index 91% rename from packages/common/src/cis4/util.ts rename to packages/sdk/src/cis4/util.ts index a8bc93179..6b43dc0b1 100644 --- a/packages/common/src/cis4/util.ts +++ b/packages/sdk/src/cis4/util.ts @@ -1,15 +1,18 @@ -import { Buffer } from 'buffer/'; -import * as ed25519 from '@noble/ed25519'; +import { Buffer } from 'buffer/index.js'; +import * as ed from '#ed25519'; -import type { ContractAddress, HexString } from '../types'; -import type { CIS2 } from '../cis2'; +import type { HexString } from '../types.js'; +import type { CIS2 } from '../cis2/util.js'; import { deserializeCIS2MetadataUrl, serializeCIS2MetadataUrl, serializeContractAddress, serializeReceiveHookName, -} from '../cis2/util'; -import { Cursor, makeDeserializeListResponse } from '../deserializationHelpers'; +} from '../cis2/util.js'; +import { + Cursor, + makeDeserializeListResponse, +} from '../deserializationHelpers.js'; import { encodeBool, encodeWord16, @@ -17,9 +20,12 @@ import { makeSerializeOptional, packBufferWithWord16Length, packBufferWithWord8Length, -} from '../serializationHelpers'; -import { OptionJson, toOptionJson } from '../schemaTypes'; -import { getSignature } from '../signHelpers'; +} from '../serializationHelpers.js'; +import { OptionJson, toOptionJson } from '../schemaTypes.js'; +import { getSignature } from '../signHelpers.js'; +import * as ContractAddress from '../types/ContractAddress.js'; +import * as EntrypointName from '../types/EntrypointName.js'; +import * as Timestamp from '../types/Timestamp.js'; /** Holds all types related to CIS4 */ // eslint-disable-next-line @typescript-eslint/no-namespace @@ -46,9 +52,9 @@ export namespace CIS4 { /** Whether holder can revoke or not */ holderRevocable: boolean; /** Time the credential is valid from */ - validFrom: Date; + validFrom: Timestamp.Type; /** (Optional) time the credential is valid until */ - validUntil?: Date; + validUntil?: Timestamp.Type; /** Metadata url of the credential */ metadataUrl: MetadataUrl; }; @@ -104,9 +110,9 @@ export namespace CIS4 { /** Whether holder can revoke or not */ holder_revocable: boolean; /** Time (as ISO string) the credential is valid from */ - valid_from: string; + valid_from: Timestamp.SchemaValue; /** (Optional) Time (as ISO string) the credential is valid until */ - valid_until: OptionJson; + valid_until: OptionJson; /** Metadata url of the credential */ metadata_url: { /** The url */ @@ -148,13 +154,13 @@ export namespace CIS4 { /** Signing metadata for credential revocation */ export type SigningData = { /** The contract address of the CIS4 contract */ - contractAddress: ContractAddress; + contractAddress: ContractAddress.Type; /** The CIS4 entrypoint from which the revocation is done */ - entrypoint: string; + entrypoint: EntrypointName.Type; /** The credential nonce */ nonce: bigint; /** Timestamp at which the revocation should be invalidated */ - timestamp: Date; + timestamp: Timestamp.Type; }; export type SigningDataJson = { @@ -281,7 +287,7 @@ export class Web3IdSigner { */ public static async from(privateKey: HexString): Promise { const publicKey = Buffer.from( - await ed25519.getPublicKey(Buffer.from(privateKey, 'hex')) + await ed.getPublicKeyAsync(Buffer.from(privateKey, 'hex')) ).toString('hex'); return new Web3IdSigner(privateKey, publicKey); } @@ -294,11 +300,11 @@ export class Web3IdSigner { /** * Signs the message given * - * @param {Buffer} message - the message to sign + * @param {ArrayBuffer} message - the message to sign * * @returns {Buffer} the signature on `message` */ - public async sign(message: Buffer): Promise { + public async sign(message: ArrayBuffer): Promise { return getSignature(message, this.privateKey); } } @@ -320,13 +326,13 @@ const deserializeOptional = ( return fun(cursor); }; -function serializeDate(date: Date): Buffer { - return encodeWord64(BigInt(date.getTime()), true); +function serializeDate(date: Timestamp.Type): Buffer { + return encodeWord64(BigInt(date.value), true); } -function deserializeDate(cursor: Cursor): Date { +function deserializeDate(cursor: Cursor): Timestamp.Type { const value = cursor.read(8).readBigInt64LE(0); - return new Date(Number(value)); + return Timestamp.fromMillis(Number(value)); } function deserializeEd25519PublicKey(cursor: Cursor): HexString { @@ -468,8 +474,12 @@ export function formatCIS4RegisterCredential({ credential_info: { holder_id: credInfo.holderPubKey, holder_revocable: credInfo.holderRevocable, - valid_from: credInfo.validFrom.toISOString(), - valid_until: toOptionJson(credInfo.validUntil?.toISOString()), + valid_from: Timestamp.toSchemaValue(credInfo.validFrom), + valid_until: toOptionJson( + credInfo.validUntil === undefined + ? undefined + : Timestamp.toSchemaValue(credInfo.validUntil) + ), metadata_url: { url: credInfo.metadataUrl.url, hash: toOptionJson(credInfo.metadataUrl.hash), @@ -565,9 +575,11 @@ export function formatCIS4RevokeCredentialHolder({ index: Number(data.signingData.contractAddress.index), subindex: Number(data.signingData.contractAddress.subindex), }, - entry_point: data.signingData.entrypoint, + entry_point: EntrypointName.toString( + data.signingData.entrypoint + ), nonce: Number(data.signingData.nonce), - timestamp: data.signingData.timestamp.toISOString(), + timestamp: Timestamp.toSchemaValue(data.signingData.timestamp), }, reason: toOptionJson(reason ? { reason } : undefined), }, @@ -624,9 +636,11 @@ export function formatCIS4RevokeCredentialOther({ index: Number(data.signingData.contractAddress.index), subindex: Number(data.signingData.contractAddress.subindex), }, - entry_point: data.signingData.entrypoint, + entry_point: EntrypointName.toString( + data.signingData.entrypoint + ), nonce: Number(data.signingData.nonce), - timestamp: data.signingData.timestamp.toISOString(), + timestamp: Timestamp.toSchemaValue(data.signingData.timestamp), }, revocation_key: data.revocationPubKey, reason: toOptionJson(reason ? { reason } : undefined), diff --git a/packages/common/src/commonProofTypes.ts b/packages/sdk/src/commonProofTypes.ts similarity index 100% rename from packages/common/src/commonProofTypes.ts rename to packages/sdk/src/commonProofTypes.ts diff --git a/packages/sdk/src/constants.ts b/packages/sdk/src/constants.ts new file mode 100644 index 000000000..13f3c6b6f --- /dev/null +++ b/packages/sdk/src/constants.ts @@ -0,0 +1,3 @@ +import * as Energy from './types/Energy.js'; + +export const DEFAULT_INVOKE_ENERGY: Energy.Type = Energy.create(1000000n); diff --git a/packages/common/src/contractHelpers.ts b/packages/sdk/src/contractHelpers.ts similarity index 81% rename from packages/common/src/contractHelpers.ts rename to packages/sdk/src/contractHelpers.ts index 399815063..ad311ec7c 100644 --- a/packages/common/src/contractHelpers.ts +++ b/packages/sdk/src/contractHelpers.ts @@ -1,21 +1,7 @@ -import { Buffer } from 'buffer/'; -import { ContractAddress, InstanceInfo } from './types'; +import type * as ContractAddress from './types/ContractAddress.js'; const CONTRACT_PARAM_MAX_LENGTH = 65535; -/** - * Gets the contract name from an {@link InstanceInfo} object. - * - * @throws If name is not structured as expected - */ -export const getContractName = ({ name }: InstanceInfo): string => { - if (!name.startsWith('init_')) { - throw new Error('Could not get name from contract instance info.'); - } - - return name.substring(5); -}; - /** * Checks if a buffer is larger than what is accepted for smart contract parameters * @@ -25,8 +11,8 @@ export const getContractName = ({ name }: InstanceInfo): string => { * * @throws If buffer exceeds max length allowed for smart contract parameters */ -export const checkParameterLength = (buffer: Buffer): void => { - if (buffer.length > CONTRACT_PARAM_MAX_LENGTH) { +export const checkParameterLength = (buffer: ArrayBuffer): void => { + if (buffer.byteLength > CONTRACT_PARAM_MAX_LENGTH) { throw new Error( `Serialized parameter exceeds max length of smart contract parameter (${CONTRACT_PARAM_MAX_LENGTH} bytes)` ); @@ -37,18 +23,17 @@ export const checkParameterLength = (buffer: Buffer): void => { * Whether two {@link ContractAddress} contract addresses are equal. */ export const isEqualContractAddress = - (a: ContractAddress) => - (b: ContractAddress): boolean => + (a: ContractAddress.Type) => + (b: ContractAddress.Type): boolean => a.index === b.index && a.subindex === b.subindex; /** The name of a smart contract. Note: This does _not_ including the 'init_' prefix. */ export type ContractName = string; - /** The name of an entrypoint exposed by a smart contract. Note: This does _not_ include the '.' prefix. */ export type EntrypointName = string; /** Check that every character is an Ascii alpha, numeric or punctuation. */ -function isAsciiAlphaNumericPunctuation(string: string) { +export function isAsciiAlphaNumericPunctuation(string: string): boolean { for (let i = 0; i < string.length; i++) { const charCode = string.charCodeAt(i); if ( diff --git a/packages/sdk/src/deserialization.ts b/packages/sdk/src/deserialization.ts new file mode 100644 index 000000000..fd5072683 --- /dev/null +++ b/packages/sdk/src/deserialization.ts @@ -0,0 +1,113 @@ +import { getAccountTransactionHandler } from './accountTransactions.js'; +import { Cursor } from './deserializationHelpers.js'; +import { + AccountTransaction, + AccountTransactionHeader, + AccountTransactionSignature, + isAccountTransactionType, +} from './types.js'; +import * as AccountAddress from './types/AccountAddress.js'; +import * as AccountSequenceNumber from './types/SequenceNumber.js'; +import * as TransactionExpiry from './types/TransactionExpiry.js'; + +/** + * Reads an unsigned 8-bit integer from the given {@link Cursor}. + * + * @param source input stream + * @returns number from 0 to 255 + */ +export function deserializeUint8(source: Cursor): number { + return source.read(1).readUInt8(0); +} + +function deserializeMap( + serialized: Cursor, + decodeSize: (size: Cursor) => number, + decodeKey: (k: Cursor) => K, + decodeValue: (t: Cursor) => T +): Record { + const size = decodeSize(serialized); + const result = {} as Record; + for (let i = 0; i < size; i += 1) { + const key = decodeKey(serialized); + const value = decodeValue(serialized); + result[key] = value; + } + return result; +} + +function deserializeAccountTransactionSignature( + signatures: Cursor +): AccountTransactionSignature { + const decodeSignature = (serialized: Cursor) => { + const length = serialized.read(2).readUInt16BE(0); + return serialized.read(length).toString('hex'); + }; + const decodeCredentialSignatures = (serialized: Cursor) => + deserializeMap( + serialized, + deserializeUint8, + deserializeUint8, + decodeSignature + ); + return deserializeMap( + signatures, + deserializeUint8, + deserializeUint8, + decodeCredentialSignatures + ); +} + +function deserializeTransactionHeader( + serializedHeader: Cursor +): AccountTransactionHeader { + const sender = AccountAddress.fromBuffer(serializedHeader.read(32)); + const nonce = AccountSequenceNumber.create( + serializedHeader.read(8).readBigUInt64BE(0) + ); + // TODO: extract payloadSize and energyAmount? + // energyAmount + serializedHeader.read(8).readBigUInt64BE(0); + // payloadSize + serializedHeader.read(4).readUInt32BE(0); + const expiry = TransactionExpiry.fromEpochSeconds( + serializedHeader.read(8).readBigUInt64BE(0) + ); + return { + sender, + nonce, + expiry, + }; +} + +export function deserializeAccountTransaction(serializedTransaction: Cursor): { + accountTransaction: AccountTransaction; + signatures: AccountTransactionSignature; +} { + const signatures = deserializeAccountTransactionSignature( + serializedTransaction + ); + + const header = deserializeTransactionHeader(serializedTransaction); + + const transactionType = deserializeUint8(serializedTransaction); + if (!isAccountTransactionType(transactionType)) { + throw new Error( + 'TransactionType is not a valid value: ' + transactionType + ); + } + const accountTransactionHandler = + getAccountTransactionHandler(transactionType); + const payload = accountTransactionHandler.deserialize( + serializedTransaction + ); + + return { + accountTransaction: { + type: transactionType, + payload, + header, + }, + signatures, + }; +} diff --git a/packages/common/src/deserializationHelpers.ts b/packages/sdk/src/deserializationHelpers.ts similarity index 92% rename from packages/common/src/deserializationHelpers.ts rename to packages/sdk/src/deserializationHelpers.ts index c0c7c0153..515ef837a 100644 --- a/packages/common/src/deserializationHelpers.ts +++ b/packages/sdk/src/deserializationHelpers.ts @@ -1,5 +1,5 @@ -import { Buffer } from 'buffer/'; -import { HexString } from './types'; +import { Buffer } from 'buffer/index.js'; +import { HexString } from './types.js'; /** * A wrapper around some data, which enables reading from the data without @@ -39,9 +39,14 @@ export class Cursor { /** * Read a number of bytes from the cursor. + * + * @param {number} [numBytes=this.remainingBytes.length] - The number of bytes to read. Defaults to the remaining bytes from the cursor position. + * * @throws If the buffer contains fewer bytes than being read. + * + * @returns {Buffer} A buffer containing the number of bytes specified from the cursor position */ - public read(numBytes: number): Buffer { + public read(numBytes: number = this.remainingBytes.length): Buffer { const end = this.cursor + numBytes; if (this.data.length < end) { throw new Error( diff --git a/packages/common/src/energyCost.ts b/packages/sdk/src/energyCost.ts similarity index 84% rename from packages/common/src/energyCost.ts rename to packages/sdk/src/energyCost.ts index 11b168678..d08c03b98 100644 --- a/packages/common/src/energyCost.ts +++ b/packages/sdk/src/energyCost.ts @@ -1,11 +1,13 @@ -import { getAccountTransactionHandler } from './accountTransactions'; -import { collapseRatio, multiplyRatio } from './ratioHelpers'; +import { getAccountTransactionHandler } from './accountTransactions.js'; +import { collapseRatio, multiplyRatio } from './ratioHelpers.js'; import { AccountTransactionPayload, AccountTransactionType, ChainParameters, Ratio, -} from './types'; +} from './types.js'; +import * as Energy from './types/Energy.js'; +import * as CcdAmount from './types/CcdAmount.js'; /** * These constants must be consistent with constA and constB in: @@ -31,11 +33,11 @@ export function calculateEnergyCost( signatureCount: bigint, payloadSize: bigint, transactionSpecificCost: bigint -): bigint { - return ( +): Energy.Type { + return Energy.create( constantA * signatureCount + - constantB * (accountTransactionHeaderSize + payloadSize) + - transactionSpecificCost + constantB * (accountTransactionHeaderSize + payloadSize) + + transactionSpecificCost ); } @@ -48,7 +50,7 @@ export function getEnergyCost( transactionType: AccountTransactionType, payload: AccountTransactionPayload, signatureCount = 1n -): bigint { +): Energy.Type { const handler = getAccountTransactionHandler(transactionType); const size = handler.serialize(payload).length; return calculateEnergyCost( @@ -79,9 +81,11 @@ export function getExchangeRate({ * Given an NRG amount and the current blockchain parameters, this returns the corresponding amount in microCcd. */ export function convertEnergyToMicroCcd( - cost: bigint, + cost: Energy.Type, chainParameters: ChainParameters -): bigint { +): CcdAmount.Type { const rate = getExchangeRate(chainParameters); - return collapseRatio(multiplyRatio(rate, cost)); + return CcdAmount.fromMicroCcd( + collapseRatio(multiplyRatio(rate, cost.value)) + ); } diff --git a/packages/common/src/GRPCClient.ts b/packages/sdk/src/grpc/GRPCClient.ts similarity index 86% rename from packages/common/src/GRPCClient.ts rename to packages/sdk/src/grpc/GRPCClient.ts index d8631d373..eac51d4c9 100644 --- a/packages/common/src/GRPCClient.ts +++ b/packages/sdk/src/grpc/GRPCClient.ts @@ -4,51 +4,62 @@ * * @module Common GRPC-Client */ -import { Buffer } from 'buffer/'; -import * as v1 from './types'; -import * as v2 from '../grpc/v2/concordium/types'; -import { Base58String, HexString, isRpcError } from './types'; -import { QueriesClient } from '../grpc/v2/concordium/service.client'; -import { HealthClient } from '../grpc/v2/concordium/health.client'; +import { Buffer } from 'buffer/index.js'; import type { RpcError, RpcTransport } from '@protobuf-ts/runtime-rpc'; -import { CredentialRegistrationId } from './types/CredentialRegistrationId'; -import * as translate from './GRPCTypeTranslation'; -import { AccountAddress } from './types/accountAddress'; -import { getAccountTransactionHandler } from './accountTransactions'; -import { calculateEnergyCost } from './energyCost'; +import { + GrpcWebFetchTransport, + GrpcWebOptions, +} from '@protobuf-ts/grpcweb-transport'; + +import * as v1 from '../types.js'; +import * as v2 from '../grpc-api/v2/concordium/types.js'; +import { HexString, isRpcError } from '../types.js'; +import { QueriesClient } from '../grpc-api/v2/concordium/service.client.js'; +import { HealthClient } from '../grpc-api/v2/concordium/health.client.js'; +import * as CredentialRegistrationId from '../types/CredentialRegistrationId.js'; +import * as translate from './translation.js'; +import * as AccountAddress from '../types/AccountAddress.js'; +import { getAccountTransactionHandler } from '../accountTransactions.js'; +import { calculateEnergyCost } from '../energyCost.js'; import { countSignatures, isHex, - isValidHash, isValidIp, mapRecord, mapStream, unwrap, wasmToSchema, -} from './util'; -import { - serializeAccountTransactionPayload, - serializeCredentialDeploymentPayload, -} from './serialization'; -import { BlockItemStatus, BlockItemSummary } from './types/blockItemSummary'; -import { ModuleReference } from './types/moduleReference'; -import { DEFAULT_INVOKE_ENERGY } from './constants'; +} from '../util.js'; +import { serializeAccountTransactionPayload } from '../serialization.js'; +import type { + BlockItemStatus, + BlockItemSummary, +} from '../types/blockItemSummary.js'; +import * as ModuleReference from '../types/ModuleReference.js'; +import { DEFAULT_INVOKE_ENERGY } from '../constants.js'; +import * as TransactionExpiry from '../types/TransactionExpiry.js'; +import * as BlockHash from '../types/BlockHash.js'; +import * as TransactionHash from '../types/TransactionHash.js'; +import * as ContractAddress from '../types/ContractAddress.js'; +import * as Parameter from '../types/Parameter.js'; +import * as Energy from '../types/Energy.js'; +import * as SequenceNumber from '../types/SequenceNumber.js'; +import * as ReceiveName from '../types/ReceiveName.js'; +import * as Timestamp from '../types/Timestamp.js'; /** * @hidden */ export type FindInstanceCreationReponse = { - hash: HexString; + hash: BlockHash.Type; height: bigint; instanceInfo: v1.InstanceInfo; }; /** - * A concordium-node specific gRPC client wrapper. - * - * @example - * import { ConcordiumGRPCClient } from "..." - * const client = new ConcordiumGRPCClient('127.0.0.1', 20000, credentials, metadata, 15000); + * A concordium-node specific gRPC client wrapper. Only use this if you intend to supply a custom + * transport layer. Otherwise more user-friendly options {@linkcode ConcordiumGRPCWebClient} and + * `ConcordiumGRPCNodeClient` exist for web/nodejs use respectively. */ export class ConcordiumGRPCClient { client: QueriesClient; @@ -74,10 +85,10 @@ export class ConcordiumGRPCClient { * @returns the next account nonce, and a boolean indicating if the nonce is reliable. */ async getNextAccountNonce( - accountAddress: AccountAddress + accountAddress: AccountAddress.Type ): Promise { const address: v2.AccountAddress = { - value: new Uint8Array(accountAddress.decodedAddress), + value: AccountAddress.toBuffer(accountAddress), }; const response = await this.client.getNextAccountSequenceNumber(address) @@ -96,7 +107,7 @@ export class ConcordiumGRPCClient { * @returns the global cryptographic parameters at the given block, or undefined it the block does not exist. */ async getCryptographicParameters( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const blockHashInput = getBlockHashInput(blockHash); @@ -124,7 +135,7 @@ export class ConcordiumGRPCClient { */ async getAccountInfo( accountIdentifier: v1.AccountIdentifierInput, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const accountInfoRequest: v2.AccountInfoRequest = { blockHash: getBlockHashInput(blockHash), @@ -146,11 +157,10 @@ export class ConcordiumGRPCClient { * @returns the status for the given transaction/block item, or undefined if it does not exist. */ async getBlockItemStatus( - transactionHash: HexString + transactionHash: TransactionHash.Type ): Promise { - assertValidHash(transactionHash); const transactionHashV2: v2.TransactionHash = { - value: Buffer.from(transactionHash, 'hex'), + value: TransactionHash.toBuffer(transactionHash), }; const response = await this.client.getBlockItemStatus(transactionHashV2) @@ -182,8 +192,8 @@ export class ConcordiumGRPCClient { * @throws An error of type `RpcError` if not found in the block. */ async getModuleSource( - moduleRef: ModuleReference, - blockHash?: HexString + moduleRef: ModuleReference.Type, + blockHash?: BlockHash.Type ): Promise { const moduleSourceRequest: v2.ModuleSourceRequest = { blockHash: getBlockHashInput(blockHash), @@ -220,9 +230,9 @@ export class ConcordiumGRPCClient { * @throws If the module or schema cannot be parsed */ async getEmbeddedSchema( - moduleRef: ModuleReference, - blockHash?: HexString - ): Promise { + moduleRef: ModuleReference.Type, + blockHash?: BlockHash.Type + ): Promise { const versionedSource = await this.getModuleSource( moduleRef, blockHash @@ -242,12 +252,12 @@ export class ConcordiumGRPCClient { * @throws An error of type `RpcError` if not found in the block. */ async getInstanceInfo( - contractAddress: v1.ContractAddress, - blockHash?: HexString + contractAddress: ContractAddress.Type, + blockHash?: BlockHash.Type ): Promise { const instanceInfoRequest: v2.InstanceInfoRequest = { blockHash: getBlockHashInput(blockHash), - address: contractAddress, + address: ContractAddress.toProto(contractAddress), }; const response = await this.client.getInstanceInfo(instanceInfoRequest) @@ -278,7 +288,7 @@ export class ConcordiumGRPCClient { */ async invokeContract( context: v1.ContractContext, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const blockHashInput = getBlockHashInput(blockHash); @@ -287,9 +297,11 @@ export class ConcordiumGRPCClient { invoker: getInvokerInput(context.invoker), instance: context.contract, amount: { value: context.amount?.microCcdAmount || 0n }, - entrypoint: { value: context.method }, - parameter: { value: context.parameter || Buffer.alloc(0) }, - energy: { value: context.energy || DEFAULT_INVOKE_ENERGY }, + entrypoint: ReceiveName.toProto(context.method), + parameter: Parameter.toProto( + context.parameter ?? Parameter.empty() + ), + energy: Energy.toProto(context.energy ?? DEFAULT_INVOKE_ENERGY), }; const response = await this.client.invokeInstance(invokeInstanceRequest) @@ -313,7 +325,7 @@ export class ConcordiumGRPCClient { async sendAccountTransaction( transaction: v1.AccountTransaction, signature: v1.AccountTransactionSignature - ): Promise { + ): Promise { const accountTransactionHandler = getAccountTransactionHandler( transaction.type ); @@ -357,19 +369,26 @@ export class ConcordiumGRPCClient { */ async sendRawAccountTransaction( header: v1.AccountTransactionHeader, - energyAmount: bigint, - payload: Buffer, + energyAmount: Energy.Type, + payload: Uint8Array, signature: v1.AccountTransactionSignature - ): Promise { + ): Promise { const transactionSignature: v2.AccountTransactionSignature = translate.accountTransactionSignatureToV2(signature); + if (TransactionExpiry.toDate(header.expiry) < new Date()) { + throw new Error( + 'A transaction expiry is not allowed to be in the past: ' + + TransactionExpiry.toDate(header.expiry) + ); + } + // Put together sendBlockItemRequest const convertedHeader: v2.AccountTransactionHeader = { - sender: { value: header.sender.decodedAddress }, - sequenceNumber: { value: header.nonce }, - energyAmount: { value: energyAmount }, - expiry: { value: header.expiry.expiryEpochSeconds }, + sender: AccountAddress.toProto(header.sender), + sequenceNumber: SequenceNumber.toProto(header.nonce), + energyAmount: Energy.toProto(energyAmount), + expiry: TransactionExpiry.toProto(header.expiry), }; const accountTransaction: v2.AccountTransaction = { signature: transactionSignature, @@ -387,7 +406,7 @@ export class ConcordiumGRPCClient { const response = await this.client.sendBlockItem(sendBlockItemRequest) .response; - return Buffer.from(response.value).toString('hex'); + return TransactionHash.fromProto(response); } /** @@ -399,27 +418,20 @@ export class ConcordiumGRPCClient { * * See [this](git:docs/account-creation.md) document for how this function can be used. * - * @param credentialDeploymentTransaction the credential deployment transaction to send to the node - * @param signatures the signatures on the hash of the serialized unsigned credential deployment information, in order - * @returns The transaction hash as a hex string + * @param rawPayload the serialized payload, consisting of the {@link v1.CredentialDeploymentTransaction} + * along with corresponding signatures. This can be serialized by utilizing the `serializeCredentialDeploymentPayload` function. + * @param expiry the expiry of the transaction + * @returns The transaction hash */ async sendCredentialDeploymentTransaction( - credentialDeploymentTransaction: v1.CredentialDeploymentTransaction, - signatures: string[] - ): Promise { - const payloadHex = serializeCredentialDeploymentPayload( - signatures, - credentialDeploymentTransaction - ); - + rawPayload: Uint8Array, + expiry: TransactionExpiry.Type + ): Promise { const credentialDeployment: v2.CredentialDeployment = { - messageExpiry: { - value: credentialDeploymentTransaction.expiry - .expiryEpochSeconds, - }, + messageExpiry: TransactionExpiry.toProto(expiry), payload: { oneofKind: 'rawPayload', - rawPayload: payloadHex, + rawPayload, }, }; const sendBlockItemRequest: v2.SendBlockItemRequest = { @@ -431,7 +443,7 @@ export class ConcordiumGRPCClient { const response = await this.client.sendBlockItem(sendBlockItemRequest) .response; - return Buffer.from(response.value).toString('hex'); + return TransactionHash.fromProto(response); } /** @@ -440,12 +452,12 @@ export class ConcordiumGRPCClient { * * @param updateInstructionTransaction the update instruction transaction to send to the node * @param signatures map of the signatures on the hash of the serialized unsigned update instruction, with the key index as map key - * @returns The transaction hash as a hex string + * @returns The transaction hash */ async sendUpdateInstruction( updateInstructionTransaction: v1.UpdateInstruction, signatures: Record - ): Promise { + ): Promise { const header = updateInstructionTransaction.header; const updateInstruction: v2.UpdateInstruction = { header: { @@ -484,7 +496,7 @@ export class ConcordiumGRPCClient { const response = await this.client.sendBlockItem(sendBlockItemRequest) .response; - return Buffer.from(response.value).toString('hex'); + return TransactionHash.fromProto(response); } /** @@ -496,7 +508,7 @@ export class ConcordiumGRPCClient { * @returns Info on all of the block chain parameters. */ async getBlockChainParameters( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const blockHashInput = getBlockHashInput(blockHash); const response = await this.client.getBlockChainParameters( @@ -516,7 +528,7 @@ export class ConcordiumGRPCClient { */ async getPoolInfo( bakerId: v1.BakerId, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const input: v2.PoolInfoRequest = { blockHash: getBlockHashInput(blockHash), @@ -537,7 +549,7 @@ export class ConcordiumGRPCClient { * @returns The status of the passive delegators. */ async getPassiveDelegationInfo( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const input = getBlockHashInput(blockHash); const response = await this.client.getPassiveDelegationInfo(input) @@ -553,7 +565,9 @@ export class ConcordiumGRPCClient { * @param blockHash optional block hash to get the reward status at, otherwise retrieves from last finalized block * @returns the reward status at the given block, or undefined it the block does not exist. */ - async getTokenomicsInfo(blockHash?: HexString): Promise { + async getTokenomicsInfo( + blockHash?: BlockHash.Type + ): Promise { const blockHashInput = getBlockHashInput(blockHash); const response = await this.client.getTokenomicsInfo(blockHashInput) @@ -606,10 +620,9 @@ export class ConcordiumGRPCClient { * @returns BlockItemSummary of the transaction. */ async waitForTransactionFinalization( - transactionHash: HexString, + transactionHash: TransactionHash.Type, timeoutTime?: number ): Promise { - assertValidHash(transactionHash); return new Promise(async (resolve, reject) => { const abortController = new AbortController(); if (timeoutTime) { @@ -659,16 +672,16 @@ export class ConcordiumGRPCClient { * * @param blockHash an optional block hash to get the accounts at, otherwise retrieves from last finalized block. * @param abortSignal an optional AbortSignal to close the stream. - * @returns an async iterable of account addresses represented as Base58 encoded strings. + * @returns an async iterable of account addresses. */ getAccountList( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal - ): AsyncIterable { + ): AsyncIterable { const opts = { abort: abortSignal }; const hash = getBlockHashInput(blockHash); const asyncIter = this.client.getAccountList(hash, opts).responses; - return mapStream(asyncIter, translate.unwrapToBase58); + return mapStream(asyncIter, AccountAddress.fromProto); } /** @@ -680,16 +693,16 @@ export class ConcordiumGRPCClient { * * @param blockHash an optional block hash to get the contract modules at, otherwise retrieves from last finalized block. * @param abortSignal an optional AbortSignal to close the stream. - * @returns an async iterable of contract module references, represented as hex strings. + * @returns an async iterable of contract module references. */ getModuleList( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal - ): AsyncIterable { + ): AsyncIterable { const opts = { abort: abortSignal }; const hash = getBlockHashInput(blockHash); const asyncIter = this.client.getModuleList(hash, opts).responses; - return mapStream(asyncIter, translate.unwrapValToHex); + return mapStream(asyncIter, ModuleReference.fromProto); } /** @@ -702,20 +715,20 @@ export class ConcordiumGRPCClient { * @param maxAmountOfAncestors the maximum amount of ancestors as a bigint. * @param blockHash a optional block hash to get the ancestors at, otherwise retrieves from last finalized block. * @param abortSignal an optional AbortSignal to close the stream. - * @returns an async iterable of ancestors' block hashes as hex strings. + * @returns an async iterable of ancestors' block hashes. */ getAncestors( maxAmountOfAncestors: bigint, - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal - ): AsyncIterable { + ): AsyncIterable { const opts = { abort: abortSignal }; const request: v2.AncestorsRequest = { blockHash: getBlockHashInput(blockHash), amount: maxAmountOfAncestors, }; const asyncIter = this.client.getAncestors(request, opts).responses; - return mapStream(asyncIter, translate.unwrapValToHex); + return mapStream(asyncIter, BlockHash.fromProto); } /** @@ -730,14 +743,14 @@ export class ConcordiumGRPCClient { * @returns an async iterable of instance states as key-value pairs of hex strings. */ getInstanceState( - contractAddress: v1.ContractAddress, - blockHash?: HexString, + contractAddress: ContractAddress.Type, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const opts = { abort: abortSignal }; const request: v2.InstanceInfoRequest = { blockHash: getBlockHashInput(blockHash), - address: contractAddress, + address: ContractAddress.toProto(contractAddress), }; const asyncIter = this.client.getInstanceState(request, opts).responses; return mapStream(asyncIter, translate.instanceStateKVPair); @@ -756,13 +769,13 @@ export class ConcordiumGRPCClient { * @returns the state of the contract at the given key as a hex string. */ async instanceStateLookup( - contractAddress: v1.ContractAddress, + contractAddress: ContractAddress.Type, key: HexString, - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { assertValidHex(key); const request: v2.InstanceStateLookupRequest = { - address: contractAddress, + address: ContractAddress.toProto(contractAddress), key: Buffer.from(key, 'hex'), blockHash: getBlockHashInput(blockHash), }; @@ -783,7 +796,7 @@ export class ConcordiumGRPCClient { * @returns an async iterable of identity provider info objects. */ getIdentityProviders( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const opts = { abort: abortSignal }; @@ -804,7 +817,7 @@ export class ConcordiumGRPCClient { * @returns an async iterable of identity provider info objects. */ getAnonymityRevokers( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const opts = { abort: abortSignal }; @@ -823,11 +836,11 @@ export class ConcordiumGRPCClient { */ async getBlocksAtHeight( blockHeightRequest: v1.BlocksAtHeightRequest - ): Promise { + ): Promise { const requestV2 = translate.BlocksAtHeightRequestToV2(blockHeightRequest); const blocks = await this.client.getBlocksAtHeight(requestV2).response; - return translate.blocksAtHeightResponse(blocks); + return blocks.blocks.map(BlockHash.fromProto); } /** @@ -838,7 +851,7 @@ export class ConcordiumGRPCClient { * @param blockHash an optional block hash to get the info from, otherwise retrieves from last finalized block. * @returns information on a block. */ - async getBlockInfo(blockHash?: HexString): Promise { + async getBlockInfo(blockHash?: BlockHash.Type): Promise { const block = getBlockHashInput(blockHash); const blockInfo = await this.client.getBlockInfo(block).response; return translate.blockInfo(blockInfo); @@ -854,7 +867,7 @@ export class ConcordiumGRPCClient { * @returns an async iterable of BakerIds. */ getBakerList( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const opts = { abort: abortSignal }; @@ -880,7 +893,7 @@ export class ConcordiumGRPCClient { */ getPoolDelegators( baker: v1.BakerId, - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const request: v2.GetPoolDelegatorsRequest = { @@ -909,7 +922,7 @@ export class ConcordiumGRPCClient { */ getPoolDelegatorsRewardPeriod( baker: v1.BakerId, - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const request: v2.GetPoolDelegatorsRequest = { @@ -939,7 +952,7 @@ export class ConcordiumGRPCClient { * @returns a stream of DelegatorInfo */ getPassiveDelegators( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const delegatorInfo = this.client.getPassiveDelegators( @@ -964,7 +977,7 @@ export class ConcordiumGRPCClient { * @returns a stream of DelegatorRewardPeriodInfo */ getPassiveDelegatorsRewardPeriod( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const delegatorInfo = this.client.getPassiveDelegatorsRewardPeriod( @@ -993,7 +1006,9 @@ export class ConcordiumGRPCClient { * @param blockHash an optional block hash to get the election info at, otherwise retrieves from last finalized block. * @returns election info for the given block */ - async getElectionInfo(blockHash?: HexString): Promise { + async getElectionInfo( + blockHash?: BlockHash.Type + ): Promise { const blockHashInput = getBlockHashInput(blockHash); const electionInfo = await this.client.getElectionInfo(blockHashInput) .response; @@ -1009,18 +1024,18 @@ export class ConcordiumGRPCClient { * {@codeblock ~~:client/getAccountNonFinalizedTransactions.ts#documentation-snippet} * * @param accountAddress The address of the account that you wish to query. - * @returns a stream of transaction hashes as hex strings. + * @returns a stream of transaction hashes. */ getAccountNonFinalizedTransactions( - accountAddress: AccountAddress, + accountAddress: AccountAddress.Type, abortSignal?: AbortSignal - ): AsyncIterable { + ): AsyncIterable { const transactions = this.client.getAccountNonFinalizedTransactions( - { value: accountAddress.decodedAddress }, + { value: AccountAddress.toBuffer(accountAddress) }, { abort: abortSignal } ).responses; - return mapStream(transactions, translate.unwrapValToHex); + return mapStream(transactions, TransactionHash.fromProto); } /** @@ -1034,7 +1049,7 @@ export class ConcordiumGRPCClient { * @returns a stream of block item summaries */ getBlockTransactionEvents( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const blockItemSummaries = this.client.getBlockTransactionEvents( @@ -1054,7 +1069,7 @@ export class ConcordiumGRPCClient { * @return a NextUpdateSequenceNumbers object */ async getNextUpdateSequenceNumbers( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const sequenceNumbers = await this.client.getNextUpdateSequenceNumbers( getBlockHashInput(blockHash) @@ -1236,7 +1251,7 @@ export class ConcordiumGRPCClient { * @returns a stream of block item summaries */ getBlockSpecialEvents( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const blockSpecialEvents = this.client.getBlockSpecialEvents( @@ -1258,7 +1273,7 @@ export class ConcordiumGRPCClient { * @returns a stream of pending updates */ getBlockPendingUpdates( - blockHash?: HexString, + blockHash?: BlockHash.Type, abortSignal?: AbortSignal ): AsyncIterable { const pendingUpdates = this.client.getBlockPendingUpdates( @@ -1278,7 +1293,7 @@ export class ConcordiumGRPCClient { * @returns a finalization summary */ async getBlockFinalizationSummary( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const finalizationSummary = await this.client.getBlockFinalizationSummary( @@ -1454,7 +1469,7 @@ export class ConcordiumGRPCClient { * @returns {FindInstanceCreationReponse} Information about the block and the contract instance, or undefined if not found. */ async findInstanceCreation( - address: v1.ContractAddress, + address: ContractAddress.Type, from?: v1.AbsoluteBlocksAtHeightRequest, to?: v1.AbsoluteBlocksAtHeightRequest ): Promise { @@ -1521,15 +1536,15 @@ export class ConcordiumGRPCClient { * * @param {v1.BakerId} baker - The baker that should be queried for. * - * @returns {v1.Timestamp} The projected earliest time at which a particular baker will be required to bake a block, as a unix timestamp in milliseconds. + * @returns {Timestamp.Type} The projected earliest time at which a particular baker will be required to bake a block, as a unix timestamp in milliseconds. */ - async getBakerEarliestWinTime(baker: v1.BakerId): Promise { + async getBakerEarliestWinTime(baker: v1.BakerId): Promise { const bakerId = { value: baker, }; const winTime = await this.client.getBakerEarliestWinTime(bakerId) .response; - return winTime.value; + return Timestamp.fromMillis(winTime.value); } /** @@ -1544,7 +1559,7 @@ export class ConcordiumGRPCClient { * @returns the requested block certificates. */ async getBlockCertificates( - blockHash?: HexString + blockHash?: BlockHash.Type ): Promise { const blockHashInput = getBlockHashInput(blockHash); const blockCertificates = await this.client.getBlockCertificates( @@ -1564,7 +1579,7 @@ export class ConcordiumGRPCClient { * @returns All bakers in the reward period of a block */ getBakersRewardPeriod( - blockHash?: HexString + blockHash?: BlockHash.Type ): AsyncIterable { const blockHashInput = getBlockHashInput(blockHash); const bakersRewardPeriod = @@ -1587,12 +1602,12 @@ export class ConcordiumGRPCClient { * @throws an `INVALID_ARGUMENT` RPC error if the input `EpochRequest` is malformed. * @throws an `UNAVAILABLE` RPC error if the endpoint is disabled on the node. * - * @param {HexString | v1.RelativeEpochRequest } epochRequest - Consists of either a hex-encoded block hash or a relative epoch request consisting of a genesis index and an epoch. If none is passed, it queries the last finalized block. + * @param {BlockHash.Type | v1.RelativeEpochRequest } epochRequest - Consists of either a block hash or a relative epoch request consisting of a genesis index and an epoch. If none is passed, it queries the last finalized block. * * @returns {v1.WinningBaker} A stream of winning bakers for a given epoch. */ getWinningBakersEpoch( - epochRequest?: HexString | v1.RelativeEpochRequest + epochRequest?: BlockHash.Type | v1.RelativeEpochRequest ): AsyncIterable { const req = getEpochRequest(epochRequest); const winningBakers = this.client.getWinningBakersEpoch(req).responses; @@ -1610,17 +1625,17 @@ export class ConcordiumGRPCClient { * @throws - an `INVALID_ARGUMENT` RPC error if the input `EpochRequest` is malformed. * @throws - an `UNAVAILABLE` RPC error if the endpoint is disabled on the node. * - * @param {HexString | v1.RelativeEpochRequest } epochRequest - Consists of either a hex-encoded block hash or a relative epoch request consisting of a genesis index and an epoch. If none is passed, it queries the last finalized block. + * @param {BlockHash.Type | v1.RelativeEpochRequest } epochRequest - Consists of either a block hash or a relative epoch request consisting of a genesis index and an epoch. If none is passed, it queries the last finalized block. * * @returns {HexString} The block hash as a hex encoded string. */ async getFirstBlockEpoch( - epochRequest?: HexString | v1.RelativeEpochRequest - ): Promise { + epochRequest?: BlockHash.Type | v1.RelativeEpochRequest + ): Promise { const req = getEpochRequest(epochRequest); const blockHash = await this.client.getFirstBlockEpoch(req).response; - return translate.unwrapValToHex(blockHash); + return BlockHash.fromProto(blockHash); } private async getConsensusHeight() { @@ -1647,24 +1662,24 @@ export class ConcordiumGRPCClient { /** * @hidden */ -export function getBlockHashInput(blockHash?: HexString): v2.BlockHashInput { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let blockHashInput: any = {}; - +export function getBlockHashInput( + blockHash?: BlockHash.Type +): v2.BlockHashInput { if (blockHash) { - assertValidHash(blockHash); - blockHashInput = { - oneofKind: 'given', - given: { value: Buffer.from(blockHash, 'hex') }, + return { + blockHashInput: { + oneofKind: 'given', + given: BlockHash.toProto(blockHash), + }, }; } else { - blockHashInput = { - oneofKind: 'lastFinal', - lastFinal: v2.Empty, + return { + blockHashInput: { + oneofKind: 'lastFinal', + lastFinal: v2.Empty, + }, }; } - - return { blockHashInput: blockHashInput }; } /** @@ -1673,48 +1688,76 @@ export function getBlockHashInput(blockHash?: HexString): v2.BlockHashInput { export function getAccountIdentifierInput( accountIdentifier: v1.AccountIdentifierInput ): v2.AccountIdentifierInput { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const returnIdentifier: any = {}; - - if ((accountIdentifier).decodedAddress !== undefined) { - const address = (accountIdentifier).decodedAddress; - returnIdentifier.oneofKind = 'address'; - returnIdentifier.address = { value: address }; - } else if ( - (accountIdentifier).credId !== undefined - ) { - const credId = (accountIdentifier).credId; - const credIdBytes = Buffer.from(credId, 'hex'); - returnIdentifier.oneofKind = 'credId'; - returnIdentifier.credId = { value: credIdBytes }; + let returnIdentifier: v2.AccountIdentifierInput['accountIdentifierInput']; + + if (AccountAddress.instanceOf(accountIdentifier)) { + returnIdentifier = { + oneofKind: 'address', + address: AccountAddress.toProto(accountIdentifier), + }; + } else if (CredentialRegistrationId.instanceOf(accountIdentifier)) { + returnIdentifier = { + oneofKind: 'credId', + credId: { + value: CredentialRegistrationId.toBuffer(accountIdentifier), + }, + }; + } else if (typeof accountIdentifier === 'bigint') { + returnIdentifier = { + oneofKind: 'accountIndex', + accountIndex: { value: accountIdentifier }, + }; } else { - returnIdentifier.oneofKind = 'accountIndex'; - returnIdentifier.accountIndex = { value: accountIdentifier }; + throw new Error( + `Unsupported account identifier: ${accountIdentifier}.` + ); } return { accountIdentifierInput: returnIdentifier }; } +/** + * A concordium-node specific gRPC client wrapper, using a grpc-web transport layer. + * This requires that the node at the address supplied has grpc-web enabled. + * + * @example + * import { ConcordiumGRPCWebClient } from "..." + * const client = new ConcordiumGRPCWebClient('127.0.0.1', 20000); + */ +export class ConcordiumGRPCWebClient extends ConcordiumGRPCClient { + constructor( + address: string, + port: number, + options?: Partial + ) { + const transport = new GrpcWebFetchTransport({ + baseUrl: `${address}:${port}`, + ...options, + }); + super(transport); + } +} + /** * @hidden */ export function getInvokerInput( - invoker?: AccountAddress | v1.ContractAddress + invoker?: AccountAddress.Type | ContractAddress.Type ): v2.Address | undefined { if (!invoker) { return undefined; - } else if ((invoker).decodedAddress) { + } else if (AccountAddress.instanceOf(invoker)) { return { type: { oneofKind: 'account', - account: { value: (invoker).decodedAddress }, + account: AccountAddress.toProto(invoker), }, }; - } else if ((invoker).index) { + } else if (ContractAddress.instanceOf(invoker)) { return { type: { oneofKind: 'contract', - contract: invoker, + contract: ContractAddress.toProto(invoker), }, }; } else { @@ -1723,10 +1766,10 @@ export function getInvokerInput( } function getEpochRequest( - epochRequest?: HexString | v1.RelativeEpochRequest + epochRequest?: BlockHash.Type | v1.RelativeEpochRequest ): v2.EpochRequest { if ( - typeof epochRequest === 'string' || + BlockHash.instanceOf(epochRequest) || typeof epochRequest === 'undefined' ) { return { @@ -1768,11 +1811,3 @@ function assertValidHex(hex: HexString): void { throw new Error('The input was not a valid hex: ' + hex); } } - -function assertValidHash(hash: HexString): void { - if (!isValidHash(hash)) { - throw new Error( - 'The input was not a valid hash, must be 32 bytes: ' + hash - ); - } -} diff --git a/packages/sdk/src/grpc/index.ts b/packages/sdk/src/grpc/index.ts new file mode 100644 index 000000000..f4788b031 --- /dev/null +++ b/packages/sdk/src/grpc/index.ts @@ -0,0 +1,6 @@ +export { + ConcordiumGRPCClient, + ConcordiumGRPCWebClient, + getAccountIdentifierInput, + getBlockHashInput, +} from './GRPCClient.js'; diff --git a/packages/common/src/GRPCTypeTranslation.ts b/packages/sdk/src/grpc/translation.ts similarity index 83% rename from packages/common/src/GRPCTypeTranslation.ts rename to packages/sdk/src/grpc/translation.ts index 345d427d6..5233c6c43 100644 --- a/packages/common/src/GRPCTypeTranslation.ts +++ b/packages/sdk/src/grpc/translation.ts @@ -1,12 +1,24 @@ -import * as v1 from './types'; -import * as v2 from '../grpc/v2/concordium/types'; -import { mapRecord, unwrap } from './util'; -import { Buffer } from 'buffer/'; +import { Buffer } from 'buffer/index.js'; import bs58check from 'bs58check'; -import { AccountAddress } from './types/accountAddress'; -import { ModuleReference } from './types/moduleReference'; -import { CcdAmount } from './types/ccdAmount'; -import { Base58String } from './types'; + +import * as v1 from '../types.js'; +import * as v2 from '../grpc-api/v2/concordium/types.js'; +import { mapRecord, unwrap } from '../util.js'; +import * as ModuleReference from '../types/ModuleReference.js'; +import * as CcdAmount from '../types/CcdAmount.js'; +import * as AccountAddress from '../types/AccountAddress.js'; +import * as BlockHash from '../types/BlockHash.js'; +import * as ReceiveName from '../types/ReceiveName.js'; +import * as InitName from '../types/InitName.js'; +import * as ContractAddress from '../types/ContractAddress.js'; +import * as Energy from '../types/Energy.js'; +import * as Duration from '../types/Duration.js'; +import * as Timestamp from '../types/Timestamp.js'; +import * as SequenceNumber from '../types/SequenceNumber.js'; +import * as TransactionHash from '../types/TransactionHash.js'; +import * as Parameter from '../types/Parameter.js'; +import * as ReturnValue from '../types/ReturnValue.js'; +import * as ContractEvent from '../types/ContractEvent.js'; function unwrapToHex(bytes: Uint8Array | undefined): v1.HexString { return Buffer.from(unwrap(bytes)).toString('hex'); @@ -27,7 +39,7 @@ export function unwrapToBase58( function trRelease(release: v2.Release): v1.ReleaseScheduleWithTransactions { return { timestamp: trTimestamp(release.timestamp), - amount: unwrap(release.amount?.value), + amount: CcdAmount.fromProto(unwrap(release.amount)), transactions: release.transactions.map(unwrapValToHex), }; } @@ -35,7 +47,7 @@ function trRelease(release: v2.Release): v1.ReleaseScheduleWithTransactions { function trNewRelease(release: v2.NewRelease): v1.ReleaseSchedule { return { timestamp: trTimestamp(release.timestamp), - amount: unwrap(release.amount?.value), + amount: CcdAmount.fromProto(unwrap(release.amount)), }; } @@ -176,7 +188,7 @@ function trTimestamp(timestamp: v2.Timestamp | undefined): Date { function trPendingChange( pendingChange: v2.StakePendingChange | undefined -): v1.StakePendingChangeV1 { +): v1.StakePendingChange { const change = unwrap(pendingChange?.change); if (change.oneofKind === 'reduce') { return { @@ -187,7 +199,7 @@ function trPendingChange( } else if (change.oneofKind === 'remove') { return { effectiveTime: trTimestamp(change.remove), - change: v1.StakePendingChangeType.RemoveStakeV1, + change: v1.StakePendingChangeType.RemoveStake, }; } else { throw Error( @@ -202,7 +214,7 @@ function trDelegator( ): v1.AccountDelegationDetails { return { restakeEarnings: deleg.restakeEarnings, - stakedAmount: unwrap(deleg.stakedAmount?.value), + stakedAmount: CcdAmount.fromProto(unwrap(deleg.stakedAmount)), delegationTarget: trDelegatorTarget(unwrap(deleg.target)), // Set the following value if deleg.pendingChange is set to true ...(deleg.pendingChange && { @@ -230,24 +242,35 @@ function trOpenStatus( function trBaker(baker: v2.AccountStakingInfo_Baker): v1.AccountBakerDetails { const bakerInfo = baker.bakerInfo; - const bakerPoolInfo: v1.BakerPoolInfo = { - openStatus: trOpenStatus(baker.poolInfo?.openStatus), - metadataUrl: unwrap(baker.poolInfo?.url), - commissionRates: trCommissionRates(baker.poolInfo?.commissionRates), - }; - return { + + const v0: v1.AccountBakerDetails = { + version: 0, restakeEarnings: baker.restakeEarnings, - bakerId: unwrap(baker.bakerInfo?.bakerId?.value), + bakerId: unwrap(bakerInfo?.bakerId?.value), bakerAggregationVerifyKey: unwrapValToHex(bakerInfo?.aggregationKey), bakerElectionVerifyKey: unwrapValToHex(baker.bakerInfo?.electionKey), bakerSignatureVerifyKey: unwrapValToHex(bakerInfo?.signatureKey), - bakerPoolInfo: bakerPoolInfo, - stakedAmount: unwrap(baker.stakedAmount?.value), + stakedAmount: CcdAmount.fromProto(unwrap(baker.stakedAmount)), // Set the following value if baker.pendingChange is set to true ...(baker.pendingChange && { pendingChange: trPendingChange(baker.pendingChange), }), }; + + if (baker.poolInfo === undefined) { + return v0; + } + + const bakerPoolInfo: v1.BakerPoolInfo = { + openStatus: trOpenStatus(baker.poolInfo?.openStatus), + metadataUrl: unwrap(baker.poolInfo?.url), + commissionRates: trCommissionRates(baker.poolInfo?.commissionRates), + }; + return { + ...v0, + version: 1, + bakerPoolInfo: bakerPoolInfo, + }; } function trHigherLevelKeysUpdate( @@ -266,7 +289,9 @@ function translateChainParametersCommon( euroPerEnergy: unwrap(params.euroPerEnergy?.value), microGTUPerEuro: unwrap(params.microCcdPerEuro?.value), accountCreationLimit: unwrap(params.accountCreationLimit?.value), - foundationAccount: unwrapToBase58(params.foundationAccount), + foundationAccount: AccountAddress.fromProto( + unwrap(params.foundationAccount) + ), level1Keys: trHigherLevelKeysUpdate(unwrap(params.level1Keys)), rootKeys: trHigherLevelKeysUpdate(unwrap(params.rootKeys)), }; @@ -303,8 +328,8 @@ function transPoolPendingChange( v1.BakerPoolPendingChangeType.ReduceBakerCapital, // TODO ensure units are aligned effectiveTime: trTimestamp(change.change.reduce.effectiveTime), - bakerEquityCapital: unwrap( - change.change.reduce.reducedEquityCapital?.value + bakerEquityCapital: CcdAmount.fromProto( + unwrap(change.change.reduce.reducedEquityCapital) ), }; } @@ -338,11 +363,15 @@ function transPaydayStatus( return { blocksBaked: status.blocksBaked, finalizationLive: status.finalizationLive, - transactionFeesEarned: unwrap(status.transactionFeesEarned?.value), - effectiveStake: unwrap(status.effectiveStake?.value), + transactionFeesEarned: CcdAmount.fromProto( + unwrap(status.transactionFeesEarned) + ), + effectiveStake: CcdAmount.fromProto(unwrap(status.effectiveStake)), lotteryPower: status.lotteryPower, - bakerEquityCapital: unwrap(status.bakerEquityCapital?.value), - delegatedCapital: unwrap(status.delegatedCapital?.value), + bakerEquityCapital: CcdAmount.fromProto( + unwrap(status.bakerEquityCapital) + ), + delegatedCapital: CcdAmount.fromProto(unwrap(status.delegatedCapital)), commissionRates: trCommissionRates(status.commissionRates), }; } @@ -362,13 +391,14 @@ export function accountInfo(acc: v2.AccountInfo): v1.AccountInfo { ...(aggAmount && { aggregatedAmount: unwrapToHex(aggAmount) }), }; const releaseSchedule = { - total: unwrap(acc.schedule?.total?.value), + total: CcdAmount.fromProto(unwrap(acc.schedule?.total)), schedule: unwrap(acc.schedule?.schedules).map(trRelease), }; const accInfoCommon: v1.AccountInfoSimple = { - accountAddress: unwrapToBase58(acc.address), - accountNonce: unwrap(acc.sequenceNumber?.value), - accountAmount: unwrap(acc.amount?.value), + type: v1.AccountInfoType.Simple, + accountAddress: AccountAddress.fromProto(unwrap(acc.address)), + accountNonce: SequenceNumber.fromProto(unwrap(acc.sequenceNumber)), + accountAmount: CcdAmount.fromProto(unwrap(acc.amount)), accountIndex: unwrap(acc.index?.value), accountThreshold: unwrap(acc.threshold?.value), accountEncryptionKey: unwrapValToHex(acc.encryptionKey), @@ -380,11 +410,13 @@ export function accountInfo(acc: v2.AccountInfo): v1.AccountInfo { if (acc.stake?.stakingInfo.oneofKind === 'delegator') { return { ...accInfoCommon, + type: v1.AccountInfoType.Delegator, accountDelegation: trDelegator(acc.stake.stakingInfo.delegator), }; } else if (acc.stake?.stakingInfo.oneofKind === 'baker') { return { ...accInfoCommon, + type: v1.AccountInfoType.Baker, accountBaker: trBaker(acc.stake.stakingInfo.baker), }; } else { @@ -396,7 +428,7 @@ export function nextAccountSequenceNumber( nasn: v2.NextAccountSequenceNumber ): v1.NextAccountNonce { return { - nonce: unwrap(nasn.sequenceNumber?.value), + nonce: SequenceNumber.fromProto(unwrap(nasn.sequenceNumber)), allFinal: nasn.allFinal, }; } @@ -416,13 +448,18 @@ function trChainParametersV0(v0: v2.ChainParametersV0): v1.ChainParametersV0 { const commonRewardParameters = translateRewardParametersCommon(v0); return { ...common, + version: 0, level2Keys: trAuthorizationsV0(unwrap(v0.level2Keys)), electionDifficulty: trAmountFraction(v0.electionDifficulty?.value), bakerCooldownEpochs: unwrap(v0.bakerCooldownEpochs?.value), - minimumThresholdForBaking: unwrap(v0.minimumThresholdForBaking?.value), + minimumThresholdForBaking: CcdAmount.fromProto( + unwrap(v0.minimumThresholdForBaking) + ), rewardParameters: { + version: 0, ...commonRewardParameters, gASRewards: { + version: 0, baker: trAmountFraction(v0.gasRewards?.baker), finalizationProof: trAmountFraction( v0.gasRewards?.finalizationProof @@ -433,6 +470,7 @@ function trChainParametersV0(v0: v2.ChainParametersV0): v1.ChainParametersV0 { chainUpdate: trAmountFraction(v0.gasRewards?.chainUpdate), }, mintDistribution: { + version: 0, bakingReward: trAmountFraction( v0.mintDistribution?.bakingReward ), @@ -452,6 +490,7 @@ function trChainParametersV1( const commonRewardParameters = translateRewardParametersCommon(params); return { ...common, + version: 1, level2Keys: trAuthorizationsV1(unwrap(params.level2Keys)), electionDifficulty: trAmountFraction(params.electionDifficulty?.value), rewardPeriodLength: unwrap( @@ -482,8 +521,8 @@ function trChainParametersV1( transactionCommissionRange: translateCommissionRange( params.poolParameters?.commissionBounds?.transaction ), - minimumEquityCapital: unwrap( - params.poolParameters?.minimumEquityCapital?.value + minimumEquityCapital: CcdAmount.fromProto( + unwrap(params.poolParameters?.minimumEquityCapital) ), capitalBound: trAmountFraction( params.poolParameters?.capitalBound?.value @@ -491,7 +530,9 @@ function trChainParametersV1( leverageBound: unwrap(params.poolParameters?.leverageBound?.value), rewardParameters: { ...commonRewardParameters, + version: 1, gASRewards: { + version: 0, baker: trAmountFraction(params.gasRewards?.baker), finalizationProof: trAmountFraction( params.gasRewards?.finalizationProof @@ -502,6 +543,7 @@ function trChainParametersV1( chainUpdate: trAmountFraction(params.gasRewards?.chainUpdate), }, mintDistribution: { + version: 1, bakingReward: trAmountFraction( params.mintDistribution?.bakingReward ), @@ -521,6 +563,7 @@ function trChainParametersV2( return { ...common, + version: 2, level2Keys: trAuthorizationsV1(unwrap(params.level2Keys)), rewardPeriodLength: unwrap( params.timeParameters?.rewardPeriodLength?.value?.value @@ -550,8 +593,8 @@ function trChainParametersV2( transactionCommissionRange: translateCommissionRange( params.poolParameters?.commissionBounds?.transaction ), - minimumEquityCapital: unwrap( - params.poolParameters?.minimumEquityCapital?.value + minimumEquityCapital: CcdAmount.fromProto( + unwrap(params.poolParameters?.minimumEquityCapital) ), capitalBound: trAmountFraction( params.poolParameters?.capitalBound?.value @@ -559,7 +602,9 @@ function trChainParametersV2( leverageBound: unwrap(params.poolParameters?.leverageBound?.value), rewardParameters: { ...commonRewardParameters, + version: 2, gASRewards: { + version: 1, baker: trAmountFraction(params.gasRewards?.baker), accountCreation: trAmountFraction( params.gasRewards?.accountCreation @@ -567,6 +612,7 @@ function trChainParametersV2( chainUpdate: trAmountFraction(params.gasRewards?.chainUpdate), }, mintDistribution: { + version: 1, bakingReward: trAmountFraction( params.mintDistribution?.bakingReward ), @@ -575,8 +621,8 @@ function trChainParametersV2( ), }, }, - timeoutBase: unwrap( - params.consensusParameters?.timeoutParameters?.timeoutBase?.value + timeoutBase: Duration.fromProto( + unwrap(params.consensusParameters?.timeoutParameters?.timeoutBase) ), timeoutDecrease: unwrap( params.consensusParameters?.timeoutParameters?.timeoutDecrease @@ -584,9 +630,11 @@ function trChainParametersV2( timeoutIncrease: unwrap( params.consensusParameters?.timeoutParameters?.timeoutIncrease ), - minBlockTime: unwrap(params.consensusParameters?.minBlockTime?.value), - blockEnergyLimit: unwrap( - params.consensusParameters?.blockEnergyLimit?.value + minBlockTime: Duration.fromProto( + unwrap(params.consensusParameters?.minBlockTime) + ), + blockEnergyLimit: Energy.fromProto( + unwrap(params.consensusParameters?.blockEnergyLimit) ), finalizerRelativeStakeThreshold: trAmountFraction( params.finalizationCommitteeParameters @@ -623,16 +671,20 @@ export function bakerPoolInfo(info: v2.PoolInfoResponse): v1.BakerPoolStatus { return { poolType: v1.PoolStatusType.BakerPool, bakerId: unwrap(info.baker?.value), - bakerAddress: unwrapToBase58(info.address), - bakerEquityCapital: unwrap(info.equityCapital?.value), - delegatedCapital: unwrap(info.delegatedCapital?.value), - delegatedCapitalCap: unwrap(info.delegatedCapitalCap?.value), + bakerAddress: AccountAddress.fromProto(unwrap(info.address)), + bakerEquityCapital: CcdAmount.fromProto(unwrap(info.equityCapital)), + delegatedCapital: CcdAmount.fromProto(unwrap(info.delegatedCapital)), + delegatedCapitalCap: CcdAmount.fromProto( + unwrap(info.delegatedCapitalCap) + ), poolInfo: transPoolInfo(unwrap(info?.poolInfo)), bakerStakePendingChange: transPoolPendingChange( info.equityPendingChange ), currentPaydayStatus: transPaydayStatus(info.currentPaydayInfo), - allPoolTotalCapital: unwrap(info.allPoolTotalCapital?.value), + allPoolTotalCapital: CcdAmount.fromProto( + unwrap(info.allPoolTotalCapital) + ), }; } @@ -641,15 +693,17 @@ export function passiveDelegationInfo( ): v1.PassiveDelegationStatus { return { poolType: v1.PoolStatusType.PassiveDelegation, - delegatedCapital: unwrap(info.delegatedCapital?.value), + delegatedCapital: CcdAmount.fromProto(unwrap(info.delegatedCapital)), commissionRates: trCommissionRates(info.commissionRates), - currentPaydayTransactionFeesEarned: unwrap( - info.currentPaydayTransactionFeesEarned?.value + currentPaydayTransactionFeesEarned: CcdAmount.fromProto( + unwrap(info.currentPaydayTransactionFeesEarned) ), - currentPaydayDelegatedCapital: unwrap( - info.currentPaydayDelegatedCapital?.value + currentPaydayDelegatedCapital: CcdAmount.fromProto( + unwrap(info.currentPaydayDelegatedCapital) + ), + allPoolTotalCapital: CcdAmount.fromProto( + unwrap(info.allPoolTotalCapital) ), - allPoolTotalCapital: unwrap(info.allPoolTotalCapital?.value), }; } @@ -662,33 +716,45 @@ export function tokenomicsInfo(info: v2.TokenomicsInfo): v1.RewardStatus { case 'v0': { const v0 = info.tokenomics.v0; return { + version: 0, protocolVersion: translateProtocolVersion(v0.protocolVersion), - totalAmount: unwrap(v0.totalAmount?.value), - totalEncryptedAmount: unwrap(v0.totalEncryptedAmount?.value), - bakingRewardAccount: unwrap(v0.bakingRewardAccount?.value), - finalizationRewardAccount: unwrap( - v0.finalizationRewardAccount?.value + totalAmount: CcdAmount.fromProto(unwrap(v0.totalAmount)), + totalEncryptedAmount: CcdAmount.fromProto( + unwrap(v0.totalEncryptedAmount) + ), + bakingRewardAccount: CcdAmount.fromProto( + unwrap(v0.bakingRewardAccount) ), - gasAccount: unwrap(v0.gasAccount?.value), + finalizationRewardAccount: CcdAmount.fromProto( + unwrap(v0.finalizationRewardAccount) + ), + gasAccount: CcdAmount.fromProto(unwrap(v0.gasAccount)), }; } case 'v1': { const v1 = info.tokenomics.v1; return { + version: 1, protocolVersion: translateProtocolVersion(v1.protocolVersion), - totalAmount: unwrap(v1.totalAmount?.value), - totalEncryptedAmount: unwrap(v1.totalEncryptedAmount?.value), - bakingRewardAccount: unwrap(v1.bakingRewardAccount?.value), - finalizationRewardAccount: unwrap( - v1.finalizationRewardAccount?.value + totalAmount: CcdAmount.fromProto(unwrap(v1.totalAmount)), + totalEncryptedAmount: CcdAmount.fromProto( + unwrap(v1.totalEncryptedAmount) + ), + bakingRewardAccount: CcdAmount.fromProto( + unwrap(v1.bakingRewardAccount) ), - gasAccount: unwrap(v1.gasAccount?.value), - foundationTransactionRewards: unwrap( - v1.foundationTransactionRewards?.value + finalizationRewardAccount: CcdAmount.fromProto( + unwrap(v1.finalizationRewardAccount) + ), + gasAccount: CcdAmount.fromProto(unwrap(v1.gasAccount)), + foundationTransactionRewards: CcdAmount.fromProto( + unwrap(v1.foundationTransactionRewards) ), nextPaydayTime: trTimestamp(v1.nextPaydayTime), nextPaydayMintRate: unwrap(v1.nextPaydayMintRate), - totalStakedCapital: unwrap(v1.totalStakedCapital?.value), + totalStakedCapital: CcdAmount.fromProto( + unwrap(v1.totalStakedCapital) + ), }; } case undefined: @@ -698,11 +764,13 @@ export function tokenomicsInfo(info: v2.TokenomicsInfo): v1.RewardStatus { export function consensusInfo(ci: v2.ConsensusInfo): v1.ConsensusStatus { const common: v1.ConsensusStatusCommon = { - bestBlock: unwrapValToHex(ci.bestBlock), - genesisBlock: unwrapValToHex(ci.genesisBlock), - currentEraGenesisBlock: unwrapValToHex(ci.currentEraGenesisBlock), - lastFinalizedBlock: unwrapValToHex(ci.lastFinalizedBlock), - epochDuration: unwrap(ci.epochDuration?.value), + bestBlock: BlockHash.fromProto(unwrap(ci.bestBlock)), + genesisBlock: BlockHash.fromProto(unwrap(ci.genesisBlock)), + currentEraGenesisBlock: BlockHash.fromProto( + unwrap(ci.currentEraGenesisBlock) + ), + lastFinalizedBlock: BlockHash.fromProto(unwrap(ci.lastFinalizedBlock)), + epochDuration: Duration.fromProto(unwrap(ci.epochDuration)), bestBlockHeight: unwrap(ci.bestBlockHeight?.value), lastFinalizedBlockHeight: unwrap(ci.lastFinalizedBlockHeight?.value), finalizationCount: BigInt(unwrap(ci.finalizationCount)), @@ -751,7 +819,8 @@ export function consensusInfo(ci: v2.ConsensusInfo): v1.ConsensusStatus { if (ci.protocolVersion < v2.ProtocolVersion.PROTOCOL_VERSION_6) { const ci0: v1.ConsensusStatusV0 = { ...common, - slotDuration: unwrap(ci.slotDuration?.value), + version: 0, + slotDuration: Duration.fromProto(unwrap(ci.slotDuration)), }; return ci0; @@ -759,8 +828,11 @@ export function consensusInfo(ci: v2.ConsensusInfo): v1.ConsensusStatus { const ci1: v1.ConsensusStatusV1 = { ...common, + version: 1, concordiumBFTStatus: { - currentTimeoutDuration: unwrap(ci.currentTimeoutDuration?.value), + currentTimeoutDuration: Duration.fromProto( + unwrap(ci.currentTimeoutDuration) + ), currentRound: unwrap(ci.currentRound?.value), currentEpoch: unwrap(ci.currentEpoch?.value), triggerBlockTime: trTimestamp(ci.triggerBlockTime), @@ -770,35 +842,16 @@ export function consensusInfo(ci: v2.ConsensusInfo): v1.ConsensusStatus { return ci1; } -function trAccountAddress( - accountAddress: v2.AccountAddress | undefined -): v1.AddressAccount { - return { - type: 'AddressAccount', - address: unwrapToBase58(accountAddress), - }; -} - -function trAddress( - addr: v2.Address | v2.ContractAddress | v2.AccountAddress | undefined -): v1.Address { - const accountAddress = unwrap(addr); - const contractAddress = unwrap(addr); - const address = unwrap(addr); - - if (accountAddress.value) { - return trAccountAddress(accountAddress); - } else if (contractAddress.index) { +function trAddress(address: v2.Address): v1.Address { + if (address.type.oneofKind === 'account') { return { - type: 'AddressContract', - address: contractAddress, + type: 'AddressAccount', + address: AccountAddress.fromProto(unwrap(address.type.account)), }; - } else if (address.type.oneofKind === 'account') { - return trAccountAddress(address.type.account); } else if (address.type.oneofKind === 'contract') { return { type: 'AddressContract', - address: address.type.contract, + address: ContractAddress.fromProto(address.type.contract), }; } else { throw Error('Invalid address encountered!'); @@ -814,36 +867,50 @@ function trContractTraceElement( return { tag: v1.TransactionEventTag.Updated, contractVersion: element.updated.contractVersion, - address: unwrap(element.updated.address), - instigator: trAddress(element.updated.instigator), - amount: unwrap(element.updated.amount?.value), - message: unwrapValToHex(element.updated.parameter), - receiveName: unwrap(element.updated.receiveName?.value), - events: element.updated.events.map(unwrapValToHex), + address: ContractAddress.fromProto( + unwrap(element.updated.address) + ), + instigator: trAddress(unwrap(element.updated.instigator)), + amount: CcdAmount.fromProto(unwrap(element.updated.amount)), + message: Parameter.fromProto(unwrap(element.updated.parameter)), + receiveName: ReceiveName.fromProto( + unwrap(element.updated.receiveName) + ), + events: element.updated.events.map(ContractEvent.fromProto), }; case 'transferred': return { tag: v1.TransactionEventTag.Transferred, - from: trAddress(element.transferred.sender), - amount: unwrap(element.transferred.amount?.value), - to: trAddress(element.transferred.receiver), + from: ContractAddress.fromProto( + unwrap(element.transferred.sender) + ), + amount: CcdAmount.fromProto(unwrap(element.transferred.amount)), + to: AccountAddress.fromProto( + unwrap(element.transferred.receiver) + ), }; case 'interrupted': return { tag: v1.TransactionEventTag.Interrupted, - address: unwrap(element.interrupted.address), - events: element.interrupted.events.map(unwrapValToHex), + address: ContractAddress.fromProto( + unwrap(element.interrupted.address) + ), + events: element.interrupted.events.map(ContractEvent.fromProto), }; case 'resumed': return { tag: v1.TransactionEventTag.Resumed, - address: unwrap(element.resumed.address), + address: ContractAddress.fromProto( + unwrap(element.resumed.address) + ), success: unwrap(element.resumed.success), }; case 'upgraded': return { tag: v1.TransactionEventTag.Upgraded, - address: unwrap(element.upgraded.address), + address: ContractAddress.fromProto( + unwrap(element.upgraded.address) + ), from: unwrapValToHex(element.upgraded.from), to: unwrapValToHex(element.upgraded.to), }; @@ -856,7 +923,7 @@ function trContractTraceElement( function trBakerEvent( bakerEvent: v2.BakerEvent, - account: Base58String + account: AccountAddress.Type ): v1.BakerEvent { const event = bakerEvent.event; switch (event.oneofKind) { @@ -865,11 +932,11 @@ function trBakerEvent( return { tag: v1.TransactionEventTag.BakerAdded, bakerId: unwrap(keysEvent?.bakerId?.value), - account: unwrapToBase58(keysEvent?.account), + account: AccountAddress.fromProto(unwrap(keysEvent?.account)), signKey: unwrapValToHex(keysEvent?.signKey), electionKey: unwrapValToHex(keysEvent?.electionKey), aggregationKey: unwrapValToHex(keysEvent?.aggregationKey), - stake: unwrap(event.bakerAdded.stake?.value), + stake: CcdAmount.fromProto(unwrap(event.bakerAdded.stake)), restakeEarnings: unwrap(event.bakerAdded.restakeEarnings), }; } @@ -883,14 +950,18 @@ function trBakerEvent( return { tag: v1.TransactionEventTag.BakerStakeIncreased, bakerId: unwrap(event.bakerStakeIncreased.bakerId?.value), - newStake: unwrap(event.bakerStakeIncreased.newStake?.value), + newStake: CcdAmount.fromProto( + unwrap(event.bakerStakeIncreased.newStake) + ), account, }; case 'bakerStakeDecreased': return { tag: v1.TransactionEventTag.BakerStakeDecreased, bakerId: unwrap(event.bakerStakeDecreased.bakerId?.value), - newStake: unwrap(event.bakerStakeDecreased.newStake?.value), + newStake: CcdAmount.fromProto( + unwrap(event.bakerStakeDecreased.newStake) + ), account, }; case 'bakerRestakeEarningsUpdated': { @@ -906,7 +977,9 @@ function trBakerEvent( return { tag: v1.TransactionEventTag.BakerKeysUpdated, bakerId: unwrap(event.bakerKeysUpdated.bakerId?.value), - account: unwrapToBase58(event.bakerKeysUpdated.account), + account: AccountAddress.fromProto( + unwrap(event.bakerKeysUpdated.account) + ), signKey: unwrapValToHex(event.bakerKeysUpdated.signKey), electionKey: unwrapValToHex(event.bakerKeysUpdated.electionKey), aggregationKey: unwrapValToHex( @@ -987,7 +1060,7 @@ function trDelegTarget( function trDelegationEvent( delegationEvent: v2.DelegationEvent, - account: Base58String + account: AccountAddress.Type ): v1.DelegationEvent { const event = delegationEvent.event; switch (event.oneofKind) { @@ -995,8 +1068,8 @@ function trDelegationEvent( const stakeIncr = event.delegationStakeIncreased; return { tag: v1.TransactionEventTag.DelegationStakeIncreased, - delegatorId: Number(unwrap(stakeIncr.delegatorId?.id?.value)), - newStake: unwrap(stakeIncr.newStake?.value), + delegatorId: unwrap(stakeIncr.delegatorId?.id?.value), + newStake: CcdAmount.fromProto(unwrap(stakeIncr.newStake)), account, }; } @@ -1004,8 +1077,8 @@ function trDelegationEvent( const stakeDecr = event.delegationStakeDecreased; return { tag: v1.TransactionEventTag.DelegationStakeDecreased, - delegatorId: Number(unwrap(stakeDecr.delegatorId?.id?.value)), - newStake: unwrap(stakeDecr.newStake?.value), + delegatorId: unwrap(stakeDecr.delegatorId?.id?.value), + newStake: CcdAmount.fromProto(unwrap(stakeDecr.newStake)), account, }; } @@ -1013,7 +1086,7 @@ function trDelegationEvent( const restake = event.delegationSetRestakeEarnings; return { tag: v1.TransactionEventTag.DelegationSetRestakeEarnings, - delegatorId: Number(unwrap(restake.delegatorId?.id?.value)), + delegatorId: unwrap(restake.delegatorId?.id?.value), restakeEarnings: unwrap(restake.restakeEarnings), account, }; @@ -1022,7 +1095,7 @@ function trDelegationEvent( const target = event.delegationSetDelegationTarget; return { tag: v1.TransactionEventTag.DelegationSetDelegationTarget, - delegatorId: Number(unwrap(target.delegatorId?.id?.value)), + delegatorId: unwrap(target.delegatorId?.id?.value), delegationTarget: trDelegTarget(target.delegationTarget), account, }; @@ -1030,13 +1103,13 @@ function trDelegationEvent( case 'delegationAdded': return { tag: v1.TransactionEventTag.DelegationAdded, - delegatorId: Number(unwrap(event.delegationAdded.id?.value)), + delegatorId: unwrap(event.delegationAdded.id?.value), account, }; case 'delegationRemoved': return { tag: v1.TransactionEventTag.DelegationRemoved, - delegatorId: Number(unwrap(event.delegationRemoved.id?.value)), + delegatorId: unwrap(event.delegationRemoved.id?.value), account, }; default: @@ -1142,21 +1215,23 @@ function trRejectReason( return { tag: Tag.InvalidInitMethod, contents: { - moduleRef: unwrapValToHex( - reason.invalidInitMethod.moduleRef + moduleRef: ModuleReference.fromProto( + unwrap(reason.invalidInitMethod.moduleRef) + ), + initName: InitName.fromProto( + unwrap(reason.invalidInitMethod.initName) ), - initName: unwrap(reason.invalidInitMethod.initName?.value), }, }; case 'invalidReceiveMethod': return { tag: Tag.InvalidReceiveMethod, contents: { - moduleRef: unwrapValToHex( - reason.invalidReceiveMethod.moduleRef + moduleRef: ModuleReference.fromProto( + unwrap(reason.invalidReceiveMethod.moduleRef) ), - receiveName: unwrap( - reason.invalidReceiveMethod.receiveName?.value + receiveName: ReceiveName.fromProto( + unwrap(reason.invalidReceiveMethod.receiveName) ), }, }; @@ -1168,14 +1243,18 @@ function trRejectReason( case 'invalidContractAddress': return { tag: Tag.InvalidContractAddress, - contents: reason.invalidContractAddress, + contents: ContractAddress.fromProto( + reason.invalidContractAddress + ), }; case 'amountTooLarge': return { tag: Tag.AmountTooLarge, contents: { - address: trAddress(reason.amountTooLarge.address), - amount: unwrap(reason.amountTooLarge.amount?.value), + address: trAddress(unwrap(reason.amountTooLarge.address)), + amount: CcdAmount.fromProto( + unwrap(reason.amountTooLarge.amount) + ), }, }; case 'rejectedInit': @@ -1186,10 +1265,16 @@ function trRejectReason( case 'rejectedReceive': return { tag: Tag.RejectedReceive, - contractAddress: unwrap(reason.rejectedReceive.contractAddress), - receiveName: unwrap(reason.rejectedReceive.receiveName?.value), + contractAddress: ContractAddress.fromProto( + unwrap(reason.rejectedReceive.contractAddress) + ), + receiveName: ReceiveName.fromProto( + unwrap(reason.rejectedReceive.receiveName) + ), rejectReason: unwrap(reason.rejectedReceive.rejectReason), - parameter: unwrapValToHex(reason.rejectedReceive.parameter), + parameter: Parameter.fromProto( + unwrap(reason.rejectedReceive.parameter) + ), }; case 'alreadyABaker': return { @@ -1313,6 +1398,7 @@ function trGasRewardsUpdate(gasRewards: v2.GasRewards): v1.GasRewardsV0Update { return { updateType: v1.UpdateType.GasRewards, update: { + version: 0, baker: trAmountFraction(gasRewards.baker), accountCreation: trAmountFraction(gasRewards.accountCreation), chainUpdate: trAmountFraction(gasRewards.accountCreation), @@ -1327,6 +1413,7 @@ function trGasRewardsCpv2Update( return { updateType: v1.UpdateType.GasRewardsCpv2, update: { + version: 1, baker: trAmountFraction(gasRewards.baker), accountCreation: trAmountFraction(gasRewards.accountCreation), chainUpdate: trAmountFraction(gasRewards.accountCreation), @@ -1373,8 +1460,8 @@ function trPoolParametersCpv1Update( poolParams.commissionBounds?.finalization ), }, - minimumEquityCapital: unwrap( - poolParams.minimumEquityCapital?.value + minimumEquityCapital: CcdAmount.fromProto( + unwrap(poolParams.minimumEquityCapital) ), capitalBound: trAmountFraction(poolParams.capitalBound?.value), leverageBound: unwrap(poolParams.leverageBound?.value), @@ -1431,7 +1518,7 @@ function trTimeoutParameteresUpdate( return { updateType: v1.UpdateType.TimeoutParameters, update: { - timeoutBase: unwrap(timeout.timeoutBase?.value), + timeoutBase: Duration.fromProto(unwrap(timeout.timeoutBase)), timeoutDecrease: unwrap(timeout.timeoutDecrease), timeoutIncrease: unwrap(timeout.timeoutIncrease), }, @@ -1441,7 +1528,7 @@ function trTimeoutParameteresUpdate( function trMinBlockTimeUpdate(duration: v2.Duration): v1.MinBlockTimeUpdate { return { updateType: v1.UpdateType.MinBlockTime, - update: unwrap(duration.value), + update: Duration.fromProto(duration), }; } @@ -1450,7 +1537,7 @@ function trBlockEnergyLimitUpdate( ): v1.BlockEnergyLimitUpdate { return { updateType: v1.UpdateType.BlockEnergyLimit, - update: unwrap(energy.value), + update: Energy.fromProto(energy), }; } @@ -1475,6 +1562,7 @@ function trMintDistributionCpv0Update( return { updateType: v1.UpdateType.MintDistribution, update: { + version: 0, bakingReward: trAmountFraction(mintDist.bakingReward), finalizationReward: trAmountFraction(mintDist.finalizationReward), mintPerSlot: trMintRate(mintDist.mintPerSlot), @@ -1488,6 +1576,7 @@ function trMintDistributionCpv1Update( return { updateType: v1.UpdateType.MintDistribution, update: { + version: 1, bakingReward: trAmountFraction(mintDist.bakingReward), finalizationReward: trAmountFraction(mintDist.finalizationReward), }, @@ -1497,6 +1586,15 @@ function trMintDistributionCpv1Update( export function pendingUpdate( pendingUpdate: v2.PendingUpdate ): v1.PendingUpdate { + return { + effectiveTime: Timestamp.fromProto(unwrap(pendingUpdate.effectiveTime)), + effect: trPendingUpdateEffect(pendingUpdate), + }; +} + +export function trPendingUpdateEffect( + pendingUpdate: v2.PendingUpdate +): v1.PendingUpdateEffect { const effect = pendingUpdate.effect; switch (effect.oneofKind) { case 'protocol': @@ -1724,6 +1822,7 @@ function trKeyUpdate(keyUpdate: v2.RootUpdate | v2.Level1Update): v1.KeyUpdate { typeOfUpdate: v1.AuthorizationKeysUpdateType.Level2KeysUpdateV1, updatePayload: { ...trAuthorizationsV0(v0), + version: 1, cooldownParameters: trAccessStructure( update.parameterCooldown ), @@ -1738,6 +1837,7 @@ function trKeyUpdate(keyUpdate: v2.RootUpdate | v2.Level1Update): v1.KeyUpdate { function trAuthorizationsV0(auths: v2.AuthorizationsV0): v1.AuthorizationsV0 { return { + version: 0, keys: auths.keys.map(trUpdatePublicKey), addIdentityProvider: trAccessStructure(auths.addIdentityProvider), addAnonymityRevoker: trAccessStructure(auths.addAnonymityRevoker), @@ -1759,6 +1859,7 @@ function trAuthorizationsV0(auths: v2.AuthorizationsV0): v1.AuthorizationsV0 { function trAuthorizationsV1(auths: v2.AuthorizationsV1): v1.AuthorizationsV1 { return { ...trAuthorizationsV0(unwrap(auths.v0)), + version: 1, cooldownParameters: trAccessStructure(auths.parameterCooldown), timeParameters: trAccessStructure(auths.parameterTime), }; @@ -1830,7 +1931,7 @@ function trAccountTransactionSummary( ...baseBlockItemSummary, type: v1.TransactionSummaryType.AccountTransaction, cost: unwrap(details.cost?.value), - sender: unwrapToBase58(details.sender), + sender: AccountAddress.fromProto(unwrap(details.sender)), }; const effect = unwrap(details.effects?.effect); @@ -1859,9 +1960,11 @@ function trAccountTransactionSummary( const contractInit = effect.contractInitialized; const event: v1.ContractInitializedEvent = { tag: v1.TransactionEventTag.ContractInitialized, - address: unwrap(contractInit.address), - amount: unwrap(contractInit.amount?.value), - initName: unwrap(contractInit.initName?.value), + address: ContractAddress.fromProto( + unwrap(contractInit.address) + ), + amount: CcdAmount.fromProto(unwrap(contractInit.amount)), + initName: InitName.fromProto(unwrap(contractInit.initName)), events: unwrap(contractInit.events.map(unwrapValToHex)), contractVersion: unwrap(contractInit.contractVersion), ref: unwrapValToHex(contractInit.originRef), @@ -1883,8 +1986,12 @@ function trAccountTransactionSummary( case 'accountTransfer': { const transfer: v1.AccountTransferredEvent = { tag: v1.TransactionEventTag.Transferred, - amount: unwrap(effect.accountTransfer.amount?.value), - to: trAccountAddress(effect.accountTransfer.receiver).address, + amount: CcdAmount.fromProto( + unwrap(effect.accountTransfer.amount) + ), + to: AccountAddress.fromProto( + unwrap(effect.accountTransfer.receiver) + ), }; if (effect.accountTransfer.memo) { return { @@ -1954,7 +2061,7 @@ function trAccountTransactionSummary( ? v1.TransactionEventTag.BakerStakeIncreased : v1.TransactionEventTag.BakerStakeDecreased, bakerId: unwrap(update?.bakerId?.value), - newStake: unwrap(update?.newStake?.value), + newStake: CcdAmount.fromProto(unwrap(update?.newStake)), account: base.sender, }; return { @@ -1974,7 +2081,9 @@ function trAccountTransactionSummary( }; const added: v1.NewEncryptedAmountEvent = { tag: v1.TransactionEventTag.NewEncryptedAmount, - account: unwrapToBase58(transfer.added?.receiver), + account: AccountAddress.fromProto( + unwrap(transfer.added?.receiver) + ), newIndex: Number(unwrap(transfer.added?.newIndex)), encryptedAmount: unwrapValToHex( transfer.added?.encryptedAmount @@ -2004,8 +2113,8 @@ function trAccountTransactionSummary( const transfer = effect.transferredToEncrypted; const added: v1.EncryptedSelfAmountAddedEvent = { tag: v1.TransactionEventTag.EncryptedSelfAmountAdded, - account: unwrapToBase58(transfer.account), - amount: unwrap(transfer.amount?.value), + account: AccountAddress.fromProto(unwrap(transfer.account)), + amount: CcdAmount.fromProto(unwrap(transfer.amount)), newAmount: unwrapValToHex(transfer.newAmount), }; return { @@ -2026,7 +2135,7 @@ function trAccountTransactionSummary( const added: v1.AmountAddedByDecryptionEvent = { tag: v1.TransactionEventTag.AmountAddedByDecryption, account: base.sender, - amount: unwrap(transfer.amount?.value), + amount: CcdAmount.fromProto(unwrap(transfer.amount)), }; return { ...base, @@ -2039,7 +2148,7 @@ function trAccountTransactionSummary( const transfer = effect.transferredWithSchedule; const event: v1.TransferredWithScheduleEvent = { tag: v1.TransactionEventTag.TransferredWithSchedule, - to: unwrapToBase58(transfer.receiver), + to: AccountAddress.fromProto(unwrap(transfer.receiver)), amount: transfer.amount.map(trNewRelease), }; if (transfer.memo) { @@ -2124,8 +2233,8 @@ export function blockItemSummary( ): v1.BlockItemSummary { const base = { index: unwrap(summary.index?.value), - energyCost: unwrap(summary.energyCost?.value), - hash: unwrapValToHex(summary.hash), + energyCost: Energy.fromProto(unwrap(summary.energyCost)), + hash: TransactionHash.fromProto(unwrap(summary.hash)), }; if (summary.details.oneofKind === 'accountTransaction') { return trAccountTransactionSummary( @@ -2141,7 +2250,9 @@ export function blockItemSummary( v2.CredentialType.INITIAL ? 'initial' : 'normal', - address: unwrapToBase58(summary.details.accountCreation.address), + address: AccountAddress.fromProto( + unwrap(summary.details.accountCreation.address) + ), regId: unwrapValToHex(summary.details.accountCreation.regId), }; } else if (summary.details.oneofKind === 'update') { @@ -2160,7 +2271,7 @@ function trBlockItemSummaryInBlock( summary: v2.BlockItemSummaryInBlock ): v1.BlockItemSummaryInBlock { return { - blockHash: unwrapValToHex(summary.blockHash), + blockHash: BlockHash.fromProto(unwrap(summary.blockHash)), summary: blockItemSummary(unwrap(summary.outcome)), }; } @@ -2199,19 +2310,26 @@ export function invokeInstanceResponse( case 'failure': return { tag: 'failure', - usedEnergy: unwrap( - invokeResponse.result.failure.usedEnergy?.value + usedEnergy: Energy.fromProto( + unwrap(invokeResponse.result.failure.usedEnergy) ), reason: trRejectReason(invokeResponse.result.failure.reason), + returnValue: + invokeResponse.result.failure.returnValue === undefined + ? undefined + : ReturnValue.fromBuffer( + invokeResponse.result.failure.returnValue + ), }; case 'success': { const result = invokeResponse.result.success; return { tag: 'success', - usedEnergy: unwrap(result.usedEnergy?.value), - returnValue: result.returnValue - ? Buffer.from(unwrap(result.returnValue)).toString('hex') - : undefined, + usedEnergy: Energy.fromProto(unwrap(result.usedEnergy)), + returnValue: + result.returnValue === undefined + ? undefined + : ReturnValue.fromBuffer(result.returnValue), events: result.effects.map(trContractTraceElement), }; } @@ -2224,13 +2342,11 @@ function trInstanceInfoCommon( info: v2.InstanceInfo_V0 | v2.InstanceInfo_V1 ): Omit { return { - amount: new CcdAmount(unwrap(info.amount?.value)), - sourceModule: ModuleReference.fromBytes( - Buffer.from(unwrap(info.sourceModule?.value)) - ), - owner: AccountAddress.fromBytes(Buffer.from(unwrap(info.owner?.value))), - methods: info.methods.map((name) => name.value), - name: unwrap(info.name?.value), + amount: CcdAmount.fromProto(unwrap(info.amount)), + sourceModule: ModuleReference.fromProto(unwrap(info.sourceModule)), + owner: AccountAddress.fromBuffer(unwrap(info.owner?.value)), + methods: info.methods.map(ReceiveName.fromProto), + name: InitName.fromProto(unwrap(info.name)), }; } @@ -2259,7 +2375,7 @@ export function commonBlockInfo( blockInfo: v2.ArrivedBlockInfo | v2.FinalizedBlockInfo ): v1.CommonBlockInfo { return { - hash: unwrapValToHex(blockInfo.hash), + hash: BlockHash.fromProto(unwrap(blockInfo.hash)), height: unwrap(blockInfo.height?.value), }; } @@ -2292,16 +2408,18 @@ export function arInfo(ar: v2.ArInfo): v1.ArInfo { export function blocksAtHeightResponse( blocks: v2.BlocksAtHeightResponse -): v1.HexString[] { - return blocks.blocks.map(unwrapValToHex); +): BlockHash.Type[] { + return blocks.blocks.map(BlockHash.fromProto); } export function blockInfo(blockInfo: v2.BlockInfo): v1.BlockInfo { const common: v1.BlockInfoCommon = { - blockParent: unwrapValToHex(blockInfo.parentBlock), - blockHash: unwrapValToHex(blockInfo.hash), + blockParent: BlockHash.fromProto(unwrap(blockInfo.parentBlock)), + blockHash: BlockHash.fromProto(unwrap(blockInfo.hash)), blockStateHash: unwrapValToHex(blockInfo.stateHash), - blockLastFinalized: unwrapValToHex(blockInfo.lastFinalizedBlock), + blockLastFinalized: BlockHash.fromProto( + unwrap(blockInfo.lastFinalizedBlock) + ), blockHeight: unwrap(blockInfo.height?.value), blockBaker: blockInfo.baker?.value, blockArriveTime: trTimestamp(blockInfo.arriveTime), @@ -2310,7 +2428,9 @@ export function blockInfo(blockInfo: v2.BlockInfo): v1.BlockInfo { finalized: blockInfo.finalized, transactionCount: BigInt(blockInfo.transactionCount), transactionsSize: BigInt(blockInfo.transactionsSize), - transactionEnergyCost: unwrap(blockInfo.transactionsEnergyCost?.value), + transactionEnergyCost: Energy.fromProto( + unwrap(blockInfo.transactionsEnergyCost) + ), genesisIndex: unwrap(blockInfo.genesisIndex?.value), eraBlockHeight: Number(unwrap(blockInfo.eraBlockHeight?.value)), protocolVersion: translateProtocolVersion(blockInfo.protocolVersion), @@ -2319,6 +2439,7 @@ export function blockInfo(blockInfo: v2.BlockInfo): v1.BlockInfo { if (blockInfo.protocolVersion < v2.ProtocolVersion.PROTOCOL_VERSION_6) { const bi0: v1.BlockInfoV0 = { ...common, + version: 0, blockSlot: unwrap(blockInfo.slotNumber?.value), }; @@ -2327,6 +2448,7 @@ export function blockInfo(blockInfo: v2.BlockInfo): v1.BlockInfo { const bi1: v1.BlockInfoV1 = { ...common, + version: 1, round: unwrap(blockInfo.round?.value), epoch: unwrap(blockInfo.epoch?.value), }; @@ -2338,8 +2460,8 @@ export function delegatorInfo( delegatorInfo: v2.DelegatorInfo ): v1.DelegatorInfo { return { - account: unwrapToBase58(delegatorInfo.account), - stake: unwrap(delegatorInfo.stake?.value), + account: AccountAddress.fromProto(unwrap(delegatorInfo.account)), + stake: CcdAmount.fromProto(unwrap(delegatorInfo.stake)), ...(delegatorInfo.pendingChange && { pendingChange: trPendingChange(delegatorInfo.pendingChange), }), @@ -2348,7 +2470,7 @@ export function delegatorInfo( export function branch(branchV2: v2.Branch): v1.Branch { return { - blockHash: unwrapValToHex(branchV2.blockHash), + blockHash: BlockHash.fromProto(unwrap(branchV2.blockHash)), children: branchV2.children.map(branch), }; } @@ -2358,7 +2480,7 @@ function trBakerElectionInfo( ): v1.BakerElectionInfo { return { baker: unwrap(bakerElectionInfo.baker?.value), - account: unwrapToBase58(bakerElectionInfo.account), + account: AccountAddress.fromProto(unwrap(bakerElectionInfo.account)), lotteryPower: bakerElectionInfo.lotteryPower, }; } @@ -2372,11 +2494,15 @@ export function electionInfo(electionInfo: v2.ElectionInfo): v1.ElectionInfo { if (electionInfo.electionDifficulty === undefined) { // election difficulty removed in protocol version 6. - return common; + return { + ...common, + version: 1, + }; } return { ...common, + version: 0, electionDifficulty: trAmountFraction( electionInfo.electionDifficulty?.value ), @@ -2509,8 +2635,8 @@ export function nodeInfo(nodeInfo: v2.NodeInfo): v1.NodeInfo { return { peerVersion: nodeInfo.peerVersion, - localTime: unwrap(nodeInfo.localTime?.value), - peerUptime: unwrap(nodeInfo.peerUptime?.value), + localTime: Timestamp.fromProto(unwrap(nodeInfo.localTime)), + peerUptime: Duration.fromProto(unwrap(nodeInfo.peerUptime)), networkInfo: trNetworkInfo(nodeInfo.networkInfo), details, }; @@ -2569,8 +2695,8 @@ function trAccountAmount( accountAmount: v2.BlockSpecialEvent_AccountAmounts_Entry ): v1.BlockSpecialEventAccountAmount { return { - account: unwrapToBase58(accountAmount.account), - amount: unwrap(accountAmount.amount?.value), + account: AccountAddress.fromProto(unwrap(accountAmount.account)), + amount: CcdAmount.fromProto(unwrap(accountAmount.amount)), }; } @@ -2585,20 +2711,26 @@ export function blockSpecialEvent( bakingRewards: unwrap( event.bakingRewards.bakerRewards ).entries.map(trAccountAmount), - remainder: unwrap(event.bakingRewards.remainder?.value), + remainder: CcdAmount.fromProto( + unwrap(event.bakingRewards.remainder) + ), }; } case 'mint': { return { tag: 'mint', - mintBakingReward: unwrap(event.mint.mintBakingReward?.value), - mintFinalizationReward: unwrap( - event.mint.mintFinalizationReward?.value + mintBakingReward: CcdAmount.fromProto( + unwrap(event.mint.mintBakingReward) + ), + mintFinalizationReward: CcdAmount.fromProto( + unwrap(event.mint.mintFinalizationReward) + ), + mintPlatformDevelopmentCharge: CcdAmount.fromProto( + unwrap(event.mint.mintPlatformDevelopmentCharge) ), - mintPlatformDevelopmentCharge: unwrap( - event.mint.mintPlatformDevelopmentCharge?.value + foundationAccount: AccountAddress.fromProto( + unwrap(event.mint.foundationAccount) ), - foundationAccount: unwrapToBase58(event.mint.foundationAccount), }; } case 'finalizationRewards': { @@ -2608,69 +2740,85 @@ export function blockSpecialEvent( event.finalizationRewards.finalizationRewards?.entries.map( trAccountAmount ), - remainder: unwrap(event.finalizationRewards.remainder?.value), + remainder: CcdAmount.fromProto( + unwrap(event.finalizationRewards.remainder) + ), }; } case 'blockReward': { return { tag: 'blockReward', - transactionFees: unwrap( - event.blockReward.transactionFees?.value + transactionFees: CcdAmount.fromProto( + unwrap(event.blockReward.transactionFees) + ), + oldGasAccount: CcdAmount.fromProto( + unwrap(event.blockReward.oldGasAccount) + ), + newGasAccount: CcdAmount.fromProto( + unwrap(event.blockReward.newGasAccount) + ), + bakerReward: CcdAmount.fromProto( + unwrap(event.blockReward.bakerReward) + ), + foundationCharge: CcdAmount.fromProto( + unwrap(event.blockReward.foundationCharge) + ), + baker: AccountAddress.fromProto( + unwrap(event.blockReward.baker) ), - oldGasAccount: unwrap(event.blockReward.oldGasAccount?.value), - newGasAccount: unwrap(event.blockReward.newGasAccount?.value), - bakerReward: unwrap(event.blockReward.bakerReward?.value), - foundationCharge: unwrap( - event.blockReward.foundationCharge?.value + foundationAccount: AccountAddress.fromProto( + unwrap(event.blockReward.baker) ), - baker: unwrapToBase58(event.blockReward.baker), - foundationAccount: unwrapToBase58(event.blockReward.baker), }; } case 'paydayFoundationReward': { return { tag: 'paydayFoundationReward', - foundationAccount: unwrapToBase58( - event.paydayFoundationReward.foundationAccount + foundationAccount: AccountAddress.fromProto( + unwrap(event.paydayFoundationReward.foundationAccount) ), - developmentCharge: unwrap( - event.paydayFoundationReward.developmentCharge?.value + developmentCharge: CcdAmount.fromProto( + unwrap(event.paydayFoundationReward.developmentCharge) ), }; } case 'paydayAccountReward': { return { tag: 'paydayAccountReward', - account: unwrapToBase58(event.paydayAccountReward.account), - transactionFees: unwrap( - event.paydayAccountReward.transactionFees?.value + account: AccountAddress.fromProto( + unwrap(event.paydayAccountReward.account) ), - bakerReward: unwrap( - event.paydayAccountReward.bakerReward?.value + transactionFees: CcdAmount.fromProto( + unwrap(event.paydayAccountReward.transactionFees) ), - finalizationReward: unwrap( - event.paydayAccountReward.finalizationReward?.value + bakerReward: CcdAmount.fromProto( + unwrap(event.paydayAccountReward.bakerReward) + ), + finalizationReward: CcdAmount.fromProto( + unwrap(event.paydayAccountReward.finalizationReward) ), }; } case 'blockAccrueReward': { return { tag: 'blockAccrueReward', - transactionFees: unwrap( - event.blockAccrueReward.transactionFees?.value + transactionFees: CcdAmount.fromProto( + unwrap(event.blockAccrueReward.transactionFees) + ), + oldGasAccount: CcdAmount.fromProto( + unwrap(event.blockAccrueReward.oldGasAccount) ), - oldGasAccount: unwrap( - event.blockAccrueReward.oldGasAccount?.value + newGasAccount: CcdAmount.fromProto( + unwrap(event.blockAccrueReward.newGasAccount) ), - newGasAccount: unwrap( - event.blockAccrueReward.newGasAccount?.value + bakerReward: CcdAmount.fromProto( + unwrap(event.blockAccrueReward.bakerReward) ), - bakerReward: unwrap(event.blockAccrueReward.bakerReward?.value), - passiveReward: unwrap( - event.blockAccrueReward.passiveReward?.value + passiveReward: CcdAmount.fromProto( + unwrap(event.blockAccrueReward.passiveReward) ), - foundationCharge: unwrap( - event.blockAccrueReward.foundationCharge?.value + foundationCharge: CcdAmount.fromProto( + unwrap(event.blockAccrueReward.foundationCharge) ), baker: unwrap(event.blockAccrueReward.baker?.value), }; @@ -2679,12 +2827,14 @@ export function blockSpecialEvent( const poolOwner = event.paydayPoolReward.poolOwner?.value; return { tag: 'paydayPoolReward', - transactionFees: unwrap( - event.paydayPoolReward.transactionFees?.value + transactionFees: CcdAmount.fromProto( + unwrap(event.paydayPoolReward.transactionFees) + ), + bakerReward: CcdAmount.fromProto( + unwrap(event.paydayPoolReward.bakerReward) ), - bakerReward: unwrap(event.paydayPoolReward.bakerReward?.value), - finalizationReward: unwrap( - event.paydayPoolReward.finalizationReward?.value + finalizationReward: CcdAmount.fromProto( + unwrap(event.paydayPoolReward.finalizationReward) ), ...(poolOwner !== undefined && { poolOwner }), }; @@ -2711,7 +2861,7 @@ function trFinalizationSummary( summary: v2.FinalizationSummary ): v1.FinalizationSummary { return { - block: unwrapValToHex(summary.block), + block: BlockHash.fromProto(unwrap(summary.block)), index: unwrap(summary.index?.value), delay: unwrap(summary.delay?.value), finalizers: summary.finalizers.map(trFinalizationSummaryParty), @@ -2802,10 +2952,16 @@ export function bakerRewardPeriodInfo( ): v1.BakerRewardPeriodInfo { return { baker: bakerInfo(unwrap(bakerRewardPeriod.baker)), - effectiveStake: unwrap(bakerRewardPeriod.effectiveStake?.value), + effectiveStake: CcdAmount.fromMicroCcd( + unwrap(bakerRewardPeriod.effectiveStake?.value) + ), commissionRates: trCommissionRates(bakerRewardPeriod.commissionRates), - equityCapital: unwrap(bakerRewardPeriod.equityCapital?.value), - delegatedCapital: unwrap(bakerRewardPeriod.delegatedCapital?.value), + equityCapital: CcdAmount.fromMicroCcd( + unwrap(bakerRewardPeriod.equityCapital?.value) + ), + delegatedCapital: CcdAmount.fromMicroCcd( + unwrap(bakerRewardPeriod.delegatedCapital?.value) + ), isFinalizer: bakerRewardPeriod.isFinalizer, }; } diff --git a/packages/common/src/hash.ts b/packages/sdk/src/hash.ts similarity index 75% rename from packages/common/src/hash.ts rename to packages/sdk/src/hash.ts index 042243770..3ab945acc 100644 --- a/packages/common/src/hash.ts +++ b/packages/sdk/src/hash.ts @@ -1,5 +1,5 @@ -import { Buffer } from 'buffer/'; -import * as hash from 'hash.js'; +import { Buffer } from 'buffer/index.js'; +import hash from 'hash.js'; export function sha256(data: (Buffer | Uint8Array)[]): Buffer { const sha256Hash = hash.sha256(); diff --git a/packages/common/src/idProofTypes.ts b/packages/sdk/src/id/idProofTypes.ts similarity index 94% rename from packages/common/src/idProofTypes.ts rename to packages/sdk/src/id/idProofTypes.ts index 63f7d8cbb..4c5d07121 100644 --- a/packages/common/src/idProofTypes.ts +++ b/packages/sdk/src/id/idProofTypes.ts @@ -1,18 +1,18 @@ -import { +import type { AttributeKey, CryptographicParameters, IdentityObjectV1, Network, Versioned, -} from '.'; -import { +} from '../types.js'; +import type { AtomicProof, GenericAtomicStatement, GenericMembershipStatement, GenericNonMembershipStatement, GenericRangeStatement, GenericRevealStatement, -} from './commonProofTypes'; +} from '../commonProofTypes.js'; export type RangeStatement = GenericRangeStatement; export type NonMembershipStatement = GenericNonMembershipStatement< diff --git a/packages/common/src/idProofs.ts b/packages/sdk/src/id/idProofs.ts similarity index 95% rename from packages/common/src/idProofs.ts rename to packages/sdk/src/id/idProofs.ts index 60678ea09..ad3a85c7d 100644 --- a/packages/common/src/idProofs.ts +++ b/packages/sdk/src/id/idProofs.ts @@ -1,25 +1,22 @@ -import * as wasm from '@concordium/rust-bindings'; import { AttributeKey, AttributeKeyString, AttributesKeys, IdDocType, -} from './types'; +} from '../types.js'; import { AtomicStatement, - IdProofInput, - IdProofOutput, IdStatement, MembershipStatement, NonMembershipStatement, RangeStatement, -} from './idProofTypes'; +} from './idProofTypes.js'; import { EU_MEMBERS, MAX_DATE, MIN_DATE, StatementTypes, -} from './commonProofTypes'; +} from '../commonProofTypes.js'; import { whereAlpha2 } from 'iso-3166-1'; /** @@ -417,17 +414,3 @@ export class IdStatementBuilder implements StatementBuilder { return this.addMembership(AttributesKeys.nationality, EU_MEMBERS); } } - -/** - * Given a statement about an identity and the inputs necessary to prove the statement, produces a proof that the associated identity fulfills the statement. - */ -export function getIdProof(input: IdProofInput): IdProofOutput { - const rawRequest = wasm.createIdProof(JSON.stringify(input)); - let out: IdProofOutput; - try { - out = JSON.parse(rawRequest); - } catch (e) { - throw new Error(rawRequest); - } - return out; -} diff --git a/packages/sdk/src/id/index.ts b/packages/sdk/src/id/index.ts new file mode 100644 index 000000000..480dbf2e8 --- /dev/null +++ b/packages/sdk/src/id/index.ts @@ -0,0 +1,2 @@ +export * from './idProofs.js'; +export * from './idProofTypes.js'; diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts new file mode 100644 index 000000000..608f3742e --- /dev/null +++ b/packages/sdk/src/index.ts @@ -0,0 +1,10 @@ +export * from './pub/types.js'; +export * from './pub/util.js'; +export * from './pub/wasm.js'; +export * from './pub/id.js'; +export * from './pub/grpc.js'; +export * from './pub/cis0.js'; +export * from './pub/cis2.js'; +export * from './pub/cis4.js'; +export * from './pub/schema.js'; +export * from './pub/web3-id.js'; diff --git a/packages/sdk/src/nodejs/grpc.ts b/packages/sdk/src/nodejs/grpc.ts new file mode 100644 index 000000000..22ea58e34 --- /dev/null +++ b/packages/sdk/src/nodejs/grpc.ts @@ -0,0 +1,38 @@ +import { ChannelCredentials } from '@grpc/grpc-js'; +import { GrpcOptions, GrpcTransport } from '@protobuf-ts/grpc-transport'; + +import { ConcordiumGRPCClient } from '../grpc/GRPCClient.js'; + +/** + * A concordium-node specific gRPC client wrapper. + * This will not work in a browser environment, in which case {@link ConcordiumGRPCWebClient} + * should be used instead. + * + * @example + * import { ConcordiumGRPCNodeClient } from "..." + * import { credentials } from '@grpc/grpc-js'; + * + * const creds = ...; // e.g. credentials.createInsecure(); + * const client = new ConcordiumGRPCClient('127.0.0.1', 20000, creds); + */ +export class ConcordiumGRPCNodeClient extends ConcordiumGRPCClient { + constructor( + address: string, + port: number, + credentials: ChannelCredentials, + options?: Partial + ) { + const transport = new GrpcTransport({ + host: `${address}:${port}`, + channelCredentials: credentials, + ...options, + }); + super(transport); + } +} + +export { + credentials, + ChannelCredentials, + CallCredentials, +} from '@grpc/grpc-js'; diff --git a/packages/sdk/src/nodejs/index.ts b/packages/sdk/src/nodejs/index.ts new file mode 100644 index 000000000..a59516ac9 --- /dev/null +++ b/packages/sdk/src/nodejs/index.ts @@ -0,0 +1,4 @@ +export * from './grpc.js'; +export * from './util.js'; +export { decryptMobileWalletExport, EncryptedData } from './wallet/crypto.js'; +export { MobileWalletExport } from './wallet/types.js'; diff --git a/packages/sdk/src/nodejs/util.ts b/packages/sdk/src/nodejs/util.ts new file mode 100644 index 000000000..324028e53 --- /dev/null +++ b/packages/sdk/src/nodejs/util.ts @@ -0,0 +1,10 @@ +import fs from 'node:fs'; + +/** + * Loads the module as a buffer, given the given filePath. + * @param filePath the location of the module + * @returns the module as a buffer + */ +export function getModuleBuffer(filePath: string): Buffer { + return Buffer.from(fs.readFileSync(filePath)); +} diff --git a/packages/nodejs/src/wallet/crypto.ts b/packages/sdk/src/nodejs/wallet/crypto.ts similarity index 98% rename from packages/nodejs/src/wallet/crypto.ts rename to packages/sdk/src/nodejs/wallet/crypto.ts index a8f3f49e4..cfe178c77 100644 --- a/packages/nodejs/src/wallet/crypto.ts +++ b/packages/sdk/src/nodejs/wallet/crypto.ts @@ -1,5 +1,5 @@ import * as crypto from 'crypto'; -import { MobileWalletExport } from './types'; +import { MobileWalletExport } from './types.js'; interface EncryptionMetaData { keyLen: number; diff --git a/packages/nodejs/src/wallet/types.ts b/packages/sdk/src/nodejs/wallet/types.ts similarity index 92% rename from packages/nodejs/src/wallet/types.ts rename to packages/sdk/src/nodejs/wallet/types.ts index fdec29701..deb44d050 100644 --- a/packages/nodejs/src/wallet/types.ts +++ b/packages/sdk/src/nodejs/wallet/types.ts @@ -1,4 +1,4 @@ -import { IdentityProvider, Versioned } from '@concordium/common-sdk'; +import { IdentityProvider, Versioned } from '../../types.js'; interface CredentialHolderInformation { idCredSecret: string; diff --git a/packages/common/src/overwrites.d.ts b/packages/sdk/src/overwrites.d.ts similarity index 82% rename from packages/common/src/overwrites.d.ts rename to packages/sdk/src/overwrites.d.ts index c30d7ee54..d0ddf81ef 100644 --- a/packages/common/src/overwrites.d.ts +++ b/packages/sdk/src/overwrites.d.ts @@ -1,7 +1,7 @@ // eslint-disable-next-line @typescript-eslint/no-unused-vars -import { Buffer } from 'buffer/'; +import { Buffer } from 'buffer/index.js'; -declare module 'buffer/' { +declare module 'buffer/index.js' { export interface Buffer { readBigUInt64BE(offset: number): bigint; } diff --git a/packages/sdk/src/pub/cis0.ts b/packages/sdk/src/pub/cis0.ts new file mode 100644 index 000000000..abd4bcd10 --- /dev/null +++ b/packages/sdk/src/pub/cis0.ts @@ -0,0 +1,2 @@ +// Functionality for working with the CIS0 standard +export * from '../cis0.js'; diff --git a/packages/sdk/src/pub/cis2.ts b/packages/sdk/src/pub/cis2.ts new file mode 100644 index 000000000..825dac13d --- /dev/null +++ b/packages/sdk/src/pub/cis2.ts @@ -0,0 +1,2 @@ +// Functionality for working with the CIS2 standard +export * from '../cis2/index.js'; diff --git a/packages/sdk/src/pub/cis4.ts b/packages/sdk/src/pub/cis4.ts new file mode 100644 index 000000000..347af45e5 --- /dev/null +++ b/packages/sdk/src/pub/cis4.ts @@ -0,0 +1,2 @@ +// Functionality for working with the CIS4 standard +export * from '../cis4/index.js'; diff --git a/packages/sdk/src/pub/grpc.ts b/packages/sdk/src/pub/grpc.ts new file mode 100644 index 000000000..5fe8a4edc --- /dev/null +++ b/packages/sdk/src/pub/grpc.ts @@ -0,0 +1,2 @@ +// The GRPC client. +export * from '../grpc/index.js'; diff --git a/packages/sdk/src/pub/id.ts b/packages/sdk/src/pub/id.ts new file mode 100644 index 000000000..3afa6bd2b --- /dev/null +++ b/packages/sdk/src/pub/id.ts @@ -0,0 +1,2 @@ +// Functionality revolving around identities. +export * from '../id/index.js'; diff --git a/packages/sdk/src/pub/nodejs.ts b/packages/sdk/src/pub/nodejs.ts new file mode 100644 index 000000000..a60d4c108 --- /dev/null +++ b/packages/sdk/src/pub/nodejs.ts @@ -0,0 +1 @@ +export * from '../nodejs/index.js'; diff --git a/packages/sdk/src/pub/schema.ts b/packages/sdk/src/pub/schema.ts new file mode 100644 index 000000000..fd01b57e4 --- /dev/null +++ b/packages/sdk/src/pub/schema.ts @@ -0,0 +1,3 @@ +// Functionality for working with smart contract schemas. This uses the WASM at entrypoint `@concordium/rust-bindings/dapp`. +export * from '../schema.js'; +export * from '../schemaTypes.js'; diff --git a/packages/sdk/src/pub/types.ts b/packages/sdk/src/pub/types.ts new file mode 100644 index 000000000..eeb66cc7d --- /dev/null +++ b/packages/sdk/src/pub/types.ts @@ -0,0 +1,97 @@ +// Functionality revolving concordium domain types and utitlity for working with these types. +export * from '../types.js'; +export { + getAccountTransactionHash, + getAccountTransactionSignDigest, + getCredentialDeploymentSignDigest, + getCredentialForExistingAccountSignDigest, + serializeAccountTransactionForSubmission, + serializeAccountTransactionPayload, + serializeAccountTransaction, +} from '../serialization.js'; +export { encodeHexString } from '../serializationHelpers.js'; +export { sha256 } from '../hash.js'; + +export { DataBlob } from '../types/DataBlob.js'; +export * from '../types/VersionedModuleSource.js'; +export { + VerifiablePresentation, + reviveDateFromTimeStampAttribute, + replaceDateWithTimeStampAttribute, +} from '../types/VerifiablePresentation.js'; + +export { deserializeAccountTransaction } from '../deserialization.js'; +export * from '../signHelpers.js'; +export * from '../versionedTypeHelpers.js'; +export * from '../accountHelpers.js'; + +export { isHex, streamToList, wasmToSchema, unwrap } from '../util.js'; + +export { getAccountTransactionHandler } from '../accountTransactions.js'; +export * from '../energyCost.js'; +export * from '../commonProofTypes.js'; + +export * from '../uleb128.js'; +export { + Schema, + Contract, + ContractDryRun, + ContractSchema, + ContractUpdateTransaction, + ContractTransactionMetadata, + ContractInvokeMetadata, + CreateContractTransactionMetadata, + ContractUpdateTransactionWithSchema, +} from '../GenericContract.js'; + +import * as ModuleClient from '../types/ModuleClient.js'; +import * as Parameter from '../types/Parameter.js'; +import * as ReturnValue from '../types/ReturnValue.js'; +import * as SequenceNumber from '../types/SequenceNumber.js'; +import * as Energy from '../types/Energy.js'; +import * as TransactionHash from '../types/TransactionHash.js'; +import * as BlockHash from '../types/BlockHash.js'; +import * as ContractName from '../types/ContractName.js'; +import * as InitName from '../types/InitName.js'; +import * as ReceiveName from '../types/ReceiveName.js'; +import * as CredentialRegistrationId from '../types/CredentialRegistrationId.js'; +import * as AccountAddress from '../types/AccountAddress.js'; +import * as ContractAddress from '../types/ContractAddress.js'; +import * as EntrypointName from '../types/EntrypointName.js'; +import * as Timestamp from '../types/Timestamp.js'; +import * as Duration from '../types/Duration.js'; +import * as ContractEvent from '../types/ContractEvent.js'; +import * as CcdAmount from '../types/CcdAmount.js'; +import * as TransactionExpiry from '../types/TransactionExpiry.js'; +import * as ModuleReference from '../types/ModuleReference.js'; +export { + TypedJsonParseError, + TypedJsonParseErrorCode, + TypedJson, +} from '../types/util.js'; +export { jsonParse, jsonStringify } from '../types/json.js'; + +// These cannot be exported directly as modules because of a bug in an eslint plugin. +// https://github.com/import-js/eslint-plugin-import/issues/2289. +export { + ModuleClient, + Parameter, + ReturnValue, + SequenceNumber, + Energy, + TransactionHash, + BlockHash, + ContractName, + InitName, + ReceiveName, + CredentialRegistrationId, + AccountAddress, + ContractAddress, + EntrypointName, + Timestamp, + Duration, + ContractEvent, + CcdAmount, + TransactionExpiry, + ModuleReference, +}; diff --git a/packages/sdk/src/pub/util.ts b/packages/sdk/src/pub/util.ts new file mode 100644 index 000000000..c5e58535f --- /dev/null +++ b/packages/sdk/src/pub/util.ts @@ -0,0 +1 @@ +export { toBuffer } from '../util.js'; diff --git a/packages/sdk/src/pub/wasm.ts b/packages/sdk/src/pub/wasm.ts new file mode 100644 index 000000000..e0c784834 --- /dev/null +++ b/packages/sdk/src/pub/wasm.ts @@ -0,0 +1,2 @@ +// Functionality revolving around concordium domain types, which require the WASM at entrypoint `@concordium/rust-bindings/wallet`. +export * from '../wasm/index.js'; diff --git a/packages/sdk/src/pub/web3-id.ts b/packages/sdk/src/pub/web3-id.ts new file mode 100644 index 000000000..ca099e1fc --- /dev/null +++ b/packages/sdk/src/pub/web3-id.ts @@ -0,0 +1,2 @@ +// Functionality revolving around Web3 ID credentials. +export * from '../web3-id/index.js'; diff --git a/packages/common/src/ratioHelpers.ts b/packages/sdk/src/ratioHelpers.ts similarity index 95% rename from packages/common/src/ratioHelpers.ts rename to packages/sdk/src/ratioHelpers.ts index eb223747a..a3c015681 100644 --- a/packages/common/src/ratioHelpers.ts +++ b/packages/sdk/src/ratioHelpers.ts @@ -1,4 +1,4 @@ -import { Ratio } from './types'; +import { Ratio } from './types.js'; /** * Collapses the Fraction into a single number. diff --git a/packages/sdk/src/schema.ts b/packages/sdk/src/schema.ts new file mode 100644 index 000000000..775fe4ee3 --- /dev/null +++ b/packages/sdk/src/schema.ts @@ -0,0 +1,294 @@ +import * as wasm from '@concordium/rust-bindings'; +import { Buffer } from 'buffer/index.js'; +import JSONbig from 'json-bigint'; +import * as ContractName from './types/ContractName.js'; +import * as EntrypointName from './types/EntrypointName.js'; +import * as Parameter from './types/Parameter.js'; +import { SchemaVersion, SmartContractTypeValues } from './types.js'; + +/** + * @param moduleSchema buffer for the schema of a module that contains the contract + * @param contractName name of the contract that the init contract transaction will initialize + * @param schemaVersion the version of the schema provided + * @returns buffer containing the schema for of init contract parameters + */ +export function getInitContractParameterSchema( + moduleSchema: ArrayBuffer, + contractName: ContractName.Type, + schemaVersion?: SchemaVersion +): Uint8Array { + const parameterSchema = wasm.getInitContractParameterSchema( + Buffer.from(moduleSchema).toString('hex'), + ContractName.toString(contractName), + schemaVersion + ); + return Buffer.from(parameterSchema, 'hex'); +} + +/** + * @param moduleSchema buffer for the schema of a module that contains the contract + * @param contractName name of the contract that the update contract transaction will update + * @param receiveFunctionName name of function that the update contract transaction will invoke + * @param schemaVersion the version of the schema provided + * @returns buffer containing the schema for of update contract parameters + */ +export function getUpdateContractParameterSchema( + moduleSchema: ArrayBuffer, + contractName: ContractName.Type, + receiveFunctionName: EntrypointName.Type, + schemaVersion?: SchemaVersion +): Uint8Array { + const parameterSchema = wasm.getReceiveContractParameterSchema( + Buffer.from(moduleSchema).toString('hex'), + ContractName.toString(contractName), + EntrypointName.toString(receiveFunctionName), + schemaVersion + ); + return Buffer.from(parameterSchema, 'hex'); +} + +/** + * @param rawSchema the schema for the type + * @returns JSON template of the schema + */ +export function displayTypeSchemaTemplate(rawSchema: ArrayBuffer): string { + return wasm.displayTypeSchemaTemplate( + Buffer.from(rawSchema).toString('hex') + ); +} + +/** + * @param contractName name of the contract that the init contract transaction will initialize + * @param parameters the parameters to be serialized. Should correspond to the JSON representation. + * @param rawSchema buffer for the schema of a module that contains the contract + * @param schemaVersion the version of the schema provided + * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. + * @returns serialized buffer of init contract parameters + */ +export function serializeInitContractParameters( + contractName: ContractName.Type, + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + parameters: any, + rawSchema: ArrayBuffer, + schemaVersion?: SchemaVersion, + verboseErrorMessage = false +): Parameter.Type { + const serializedParameters = wasm.serializeInitContractParameters( + JSONbig.stringify(parameters), + Buffer.from(rawSchema).toString('hex'), + ContractName.toString(contractName), + schemaVersion, + verboseErrorMessage + ); + return Parameter.fromBuffer(Buffer.from(serializedParameters, 'hex')); +} + +/** + * @param contractName name of the contract that the update contract transaction will update + * @param receiveFunctionName name of function that the update contract transaction will invoke + * @param parameters the parameters to be serialized. Should correspond to the JSON representation. + * @param rawSchema buffer for the schema of a module that contains the contract + * @param schemaVersion the version of the schema provided + * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. + * @returns serialized buffer of update contract parameters + */ +export function serializeUpdateContractParameters( + contractName: ContractName.Type, + receiveFunctionName: EntrypointName.Type, + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + parameters: any, + rawSchema: ArrayBuffer, + schemaVersion?: SchemaVersion, + verboseErrorMessage = false +): Parameter.Type { + const serializedParameters = wasm.serializeReceiveContractParameters( + JSONbig.stringify(parameters), + Buffer.from(rawSchema).toString('hex'), + ContractName.toString(contractName), + EntrypointName.toString(receiveFunctionName), + schemaVersion, + verboseErrorMessage + ); + return Parameter.fromBuffer(Buffer.from(serializedParameters, 'hex')); +} + +/** + * Given a value for a smart contract type, and the raw schema for that type, serialize the value into binary format. + * @param value the value that should be serialized. Should correspond to the JSON representation + * @param rawSchema the schema for the type that the given value should be serialized as + * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. + * @returns serialized buffer of the value + */ +export function serializeTypeValue( + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types + value: any, + rawSchema: ArrayBuffer, + verboseErrorMessage = false +): Parameter.Type { + const serializedValue = wasm.serializeTypeValue( + JSONbig.stringify(value), + Buffer.from(rawSchema).toString('hex'), + verboseErrorMessage + ); + return Parameter.fromBuffer(Buffer.from(serializedValue, 'hex')); +} + +/** + * Given a contract's raw state, its name and its schema, return the state as a JSON object. + * The return type is any, and the actual type should be determined by using the schema. + */ +export function deserializeContractState( + contractName: ContractName.Type, + schema: ArrayBuffer, + state: ArrayBuffer, + verboseErrorMessage = false + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + const serializedState = wasm.deserializeState( + ContractName.toString(contractName), + Buffer.from(state).toString('hex'), + Buffer.from(schema).toString('hex'), + verboseErrorMessage + ); + try { + return JSONbig({ + alwaysParseAsBig: true, + useNativeBigInt: true, + }).parse(serializedState); + } catch (e) { + throw new Error( + 'unable to deserialize state, due to: ' + serializedState + ); // In this case serializedState is the error message from the rust module + } +} + +/** + * Deserializes a receive functions's return value from a sequence of bytes into a json object. + * @param returnValueBytes A buffer containing the return value as raw bytes. + * @param moduleSchema The raw module schema as a buffer. + * @param contractName The name of the contract where the receive function is located. + * @param functionName The name of the receive function which return value you want to deserialize. + * @param schemaVersion The schema version as a number. This parameter is optional, if you provide a serialized versioned schema this argument won't be needed. + * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. + */ +export function deserializeReceiveReturnValue( + returnValueBytes: ArrayBuffer, + moduleSchema: ArrayBuffer, + contractName: ContractName.Type, + functionName: EntrypointName.Type, + schemaVersion?: number, + verboseErrorMessage = false + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + const deserializedReturnValue = wasm.deserializeReceiveReturnValue( + Buffer.from(returnValueBytes).toString('hex'), + Buffer.from(moduleSchema).toString('hex'), + ContractName.toString(contractName), + EntrypointName.toString(functionName), + schemaVersion, + verboseErrorMessage + ); + try { + return JSONbig({ + alwaysParseAsBig: true, + useNativeBigInt: true, + }).parse(deserializedReturnValue); + } catch (e) { + throw new Error( + 'unable to deserialize the return value, due to: ' + + deserializedReturnValue + ); // In this case deserializedReturnValue is the error message from the rust module + } +} + +/** + * Deserializes a receive function's error from a sequence of bytes into a json object. + * @param errorBytes A buffer containing the error as raw bytes. + * @param moduleSchema The raw module schema as a buffer. + * @param contractName The name of the contract where the receive function is located. + * @param functionName The name of the receive function which error you want to deserialize. + * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. + */ +export function deserializeReceiveError( + errorBytes: ArrayBuffer, + moduleSchema: ArrayBuffer, + contractName: ContractName.Type, + functionName: EntrypointName.Type, + verboseErrorMessage = false + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + const deserializedError = wasm.deserializeReceiveError( + Buffer.from(errorBytes).toString('hex'), + Buffer.from(moduleSchema).toString('hex'), + ContractName.toString(contractName), + EntrypointName.toString(functionName), + verboseErrorMessage + ); + try { + return JSONbig({ + alwaysParseAsBig: true, + useNativeBigInt: true, + }).parse(deserializedError); + } catch (e) { + throw new Error( + 'unable to deserialize the error value, due to: ' + + deserializedError + ); // In this case deserializedError is the error message from the rust module + } +} + +/** + * Deserializes an init function's error from a sequence of bytes into a json object. + * @param errorBytes A buffer containing the error as raw bytes. + * @param moduleSchema The raw module schema as a buffer. + * @param contractName The name of the init function which error you want to deserialize. + * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. + */ +export function deserializeInitError( + errorBytes: ArrayBuffer, + moduleSchema: ArrayBuffer, + contractName: ContractName.Type, + verboseErrorMessage = false + // eslint-disable-next-line @typescript-eslint/no-explicit-any +): any { + const deserializedError = wasm.deserializeInitError( + Buffer.from(errorBytes).toString('hex'), + Buffer.from(moduleSchema).toString('hex'), + ContractName.toString(contractName), + verboseErrorMessage + ); + try { + return JSONbig({ + alwaysParseAsBig: true, + useNativeBigInt: true, + }).parse(deserializedError); + } catch (e) { + throw new Error( + 'unable to deserialize the error value, due to: ' + + deserializedError + ); // In this case deserializedError is the error message from the rust module + } +} + +/** + * Given a binary value for a smart contract type, and the raw schema for that type, deserialize the value into the JSON representation. + * @param value the value that should be deserialized. + * @param rawSchema the schema for the type that the given value should be deserialized as + * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. + * @returns the deserialized value + */ +export function deserializeTypeValue( + value: ArrayBuffer, + rawSchema: ArrayBuffer, + verboseErrorMessage = false +): SmartContractTypeValues { + const deserializedValue = wasm.deserializeTypeValue( + Buffer.from(value).toString('hex'), + Buffer.from(rawSchema).toString('hex'), + verboseErrorMessage + ); + return JSONbig({ + alwaysParseAsBig: true, + useNativeBigInt: true, + }).parse(deserializedValue); +} diff --git a/packages/sdk/src/schemaTypes.ts b/packages/sdk/src/schemaTypes.ts new file mode 100644 index 000000000..0230eeea7 --- /dev/null +++ b/packages/sdk/src/schemaTypes.ts @@ -0,0 +1,1119 @@ +import { ContractName, EntrypointName } from './contractHelpers.js'; +import { + Cursor, + Deserializer, + deserializeBigUInt64LE, + deserializeUInt16LE, + deserializeUInt32LE, + deserializeUInt8, +} from './deserializationHelpers.js'; +import { Buffer } from 'buffer/index.js'; +import { + encodeWord16, + encodeWord32, + encodeWord64, + encodeWord8, +} from './serializationHelpers.js'; +/** + * The JSON schema representation of a rust Option + * + * @template T - The type to represent as optional + */ +export type OptionJson = { None: [] } | { Some: [T] }; + +/** + * Takes a value and wraps it in a {@link OptionJson}. + * + * @template T - The type to represent as optional + * + * @param {T} value - The value to wrap. + * + * @returns {OptionJson} the wrapped value + */ +export function toOptionJson(value: T | undefined): OptionJson { + if (value === undefined) { + return { None: [] }; + } + + return { Some: [value] }; +} + +/** Schema version from before the schema bytes contained version information. */ +type UnversionedSchemaVersion = 0 | 1; + +/** + * Represents unparsed bytes for a smart contract module schema. + */ +export type RawModuleSchema = + | { + /** The bytes does contain the version information. */ + readonly type: 'versioned'; + /** Buffer containing the schema module bytes, assumed to contain the version information. */ + readonly buffer: ArrayBuffer; + } + | { + /** The bytes does not contain the version information. */ + readonly type: 'unversioned'; + /** Buffer containing the schema module bytes. Assumed to be without the version information */ + readonly buffer: ArrayBuffer; + /** Smart contract module schema version. */ + readonly version: UnversionedSchemaVersion; + }; + +/** + * Parse a raw smart contract module schema into a structured type. + * + * @param {RawModuleSchema} rawModuleSchema The raw smart contract module schema. + * @returns {VersionedSchemaModule} A structured representation of the smart contract module schema. + * @throws If unable to deserialize the module schema from provided bytes. + */ +export function parseRawModuleSchema( + rawModuleSchema: RawModuleSchema +): VersionedSchemaModule { + const cursor = Cursor.fromBuffer(rawModuleSchema.buffer); + if (rawModuleSchema.type === 'versioned') { + return deserializeVersionedSchemaModule(cursor); + } else { + return deserializeUnversionedSchemaModule( + rawModuleSchema.version, + cursor + ); + } +} + +/** + * Represents the different schema versions. + * + * The serialization of this type includes the versioning information. + * The serialization of this is always prefixed with two 255u8 in order to distinguish this versioned schema from the unversioned. + * When embedded into a smart contract module, name the custom section `concordium-schema`. + */ +export type VersionedSchemaModule = + | { + version: 0; + module: SchemaModuleV0; + } + | { + version: 1; + module: SchemaModuleV1; + } + | { + version: 2; + module: SchemaModuleV2; + } + | { + version: 3; + module: SchemaModuleV3; + }; + +/** + * Contains all schemas for a smart contract module V0. + * Older versions of smart contracts might have this embedded in the custom section labelled `concordium-schema-v1`. + */ +export type SchemaModuleV0 = { + /** Map from contract name to a collection of schemas for that contract. */ + contracts: Map; +}; + +/** + * Contains all schemas for a smart contract module V1. + * Older versions of smart contracts might have this embedded in the custom section labelled `concordium-schema-v2`. + */ +export type SchemaModuleV1 = { + /** Map from contract name to a collection of schemas for that contract. */ + contracts: Map; +}; + +/** Contains all the contract schemas for a smart contract module V1. */ +export type SchemaModuleV2 = { + /** Map from contract name to a collection of schemas for that contract. */ + contracts: Map; +}; + +/** Contains all the contract schemas for a smart contract module V1. */ +export type SchemaModuleV3 = { + /** Map from contract name to a collection of schemas for that contract. */ + contracts: Map; +}; + +/** Describes all the schemas of a V0 smart contract. */ +export type SchemaContractV0 = { + /** Schema for the smart contract state. */ + state?: SchemaType; + /** Schemas for the init-function. */ + init?: SchemaFunctionV1; + /** Map of schemas for the receive-functions. */ + receive: Map; +}; + +/** Describes schemas of a smart contract in a V1 smart contract module. */ +export type SchemaContractV1 = { + /** Schemas for the init-function. */ + init?: SchemaFunctionV1; + /** Map of schemas for the receive-functions. */ + receive: Map; +}; + +/** Describes schemas of a smart contract in a V1 smart contract module. */ +export type SchemaContractV2 = { + /** Schemas for the init-function. */ + init?: SchemaFunctionV2; + /** Map of schemas for the receive-functions. */ + receive: Map; +}; + +/** Describes schemas of a smart contract in a V1 smart contract module. */ +export type SchemaContractV3 = { + /** Schemas for the init-function. */ + init?: SchemaFunctionV2; + /** Map of schemas for the receive-functions. */ + receive: Map; + /** Schema for events logged by this contract. */ + event?: SchemaType; +}; + +/** Describes schemas of a init or receive function in a smart contract in a V1 smart contract module. */ +export type SchemaFunctionV1 = { + /** Schema for the parameter of this function. */ + parameter?: SchemaType; + /** Schema for the return value of this function. */ + returnValue?: SchemaType; +}; + +/** Describes schemas of a init or receive function in a smart contract in a V1 smart contract module. */ +export type SchemaFunctionV2 = { + /** Schema for the parameter of this function. */ + parameter?: SchemaType; + /** Schema for the return value of this function. */ + returnValue?: SchemaType; + /** Schema for error message of this function. */ + error?: SchemaType; +}; + +/** Type of the variable used to encode the length collections such as Sets, List, Maps and more. */ +export type SchemaSizeLength = 'U8' | 'U16' | 'U32' | 'U64'; + +/** Schema information for some variant of an enum (here it is an enum in Rust terms). */ +export type SchemaEnumVariant = { + /** Name of the variant. */ + name: string; + /** Fields of this variant */ + fields: SchemaFields; +}; + +/** + * Schema information of fields in either a struct or a variant of some enum (here it is an enum in Rust terms). + * The fields are either named, unnamed or none. + */ +export type SchemaFields = + | { type: 'Named'; fields: SchemaNamedField[] } + | { type: 'Unnamed'; fields: SchemaType[] } + | { type: 'None' }; + +/** + * Schema information of a single named field in either a struct or a variant of some enum (here it is an enum in Rust terms). + */ +export type SchemaNamedField = { name: string; field: SchemaType }; + +/** The schema type information. Provides information of how to serialize or deserialzie some binary information into a structure. */ +export type SchemaType = + | { + type: + | 'Unit' + | 'Bool' + | 'U8' + | 'U16' + | 'U32' + | 'U64' + | 'U128' + | 'I8' + | 'I16' + | 'I32' + | 'I64' + | 'I128' + | 'Amount' + | 'AccountAddress' + | 'ContractAddress' + | 'Timestamp' + | 'Duration'; + } + | { type: 'Pair'; first: SchemaType; second: SchemaType } + | { type: 'List'; sizeLength: SchemaSizeLength; item: SchemaType } + | { type: 'Set'; sizeLength: SchemaSizeLength; item: SchemaType } + | { + type: 'Map'; + sizeLength: SchemaSizeLength; + key: SchemaType; + value: SchemaType; + } + | { type: 'Array'; size: number; item: SchemaType } + | { type: 'Struct'; fields: SchemaFields } + | { type: 'Enum'; variants: SchemaEnumVariant[] } + | { type: 'String'; sizeLength: SchemaSizeLength } + | { type: 'ContractName'; sizeLength: SchemaSizeLength } + | { type: 'ReceiveName'; sizeLength: SchemaSizeLength } + | { type: 'ULeb128'; maxByteSize: number } + | { type: 'ILeb128'; maxByteSize: number } + | { type: 'ByteList'; sizeLength: SchemaSizeLength } + | { type: 'ByteArray'; size: number } + | { type: 'TaggedEnum'; variants: Map }; + +/** + * Prefix of versioned smart contract module schemas. + * This allows tooling to distinguish a module schema with version information from a schema without, since the versioned must have this exact prefix. + */ +const magicPrefixVersionedSchema = Buffer.alloc(2, 255); + +/** + * Deserialize a versioned smart contract module schema. This checks for the prefix of two max-value u8 bytes and fails otherwise. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @throws If provided smart contract module schema is not prefixed with two max-value u8 bytes, or if the deserialization fails. + * @returns {VersionedSchemaModule} The structured representation of a smart contract schema module. + */ +export function deserializeVersionedSchemaModule( + cursor: Cursor +): VersionedSchemaModule { + const prefix = cursor.read(2); + if (!prefix.equals(magicPrefixVersionedSchema)) { + throw new Error( + 'Deserialization failed: Unable to find prefix for versioned module.' + ); + } + const version = deserializeUInt8(cursor); + switch (version) { + case 0: + return { version, module: deserializeSchemaModuleV0(cursor) }; + case 1: + return { version, module: deserializeSchemaModuleV1(cursor) }; + case 2: + return { version, module: deserializeSchemaModuleV2(cursor) }; + case 3: + return { version, module: deserializeSchemaModuleV3(cursor) }; + default: + throw new Error( + 'Deserialization failed: Unsupported version for schema module.' + ); + } +} + +/** + * Deserialize a smart contract module schema which does not contain version information. + * This is only relevant for old versions of the smart contract module schema. + * @param {UnversionedSchemaVersion} version The version of the smart contract schema module. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @throws If the deserialization fails. + * @returns {VersionedSchemaModule} The structured representation of a smart contract schema module. + */ +export function deserializeUnversionedSchemaModule( + version: UnversionedSchemaVersion, + cursor: Cursor +): VersionedSchemaModule { + switch (version) { + case 0: + return { version, module: deserializeSchemaModuleV0(cursor) }; + case 1: + return { version, module: deserializeSchemaModuleV1(cursor) }; + default: + throw new Error( + 'Deserialization failed: Unsupported version provided for unversioned schema module.' + ); + } +} + +/** + * Deserialize maps provided the size length and functions for deserializing keys and values. + * It will first deserialize the size of the map, then deserialize this number of key-value pairs building the map. + * + * @template K Type representing the key in the map. + * @template V Type representing the value in the map. + * @param {SchemaSizeLength} sizeLength Size of the encoding of the collection lenght. + * @param {Deserializer} deserialKey Function for deserializing a key. + * @param {Deserializer} deserialValue Function for deserializing a value. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {Map} + */ +function deserializeMap( + sizeLength: SchemaSizeLength, + deserialKey: Deserializer, + deserialValue: Deserializer, + cursor: Cursor +): Map { + const itemLen = deserializeSize(sizeLength, cursor); + const map = new Map(); + for (let i = 0; i < itemLen; i++) { + const key = deserialKey(cursor); + const value = deserialValue(cursor); + map.set(key, value); + } + return map; +} + +/** + * Deserialize a schema size length. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaSizeLength} + */ +function deserializeSizeLength(cursor: Cursor): SchemaSizeLength { + const sizeLength = deserializeUInt8(cursor); + switch (sizeLength) { + case 0: + return 'U8'; + case 1: + return 'U16'; + case 2: + return 'U32'; + case 3: + return 'U64'; + default: + throw new Error( + 'Deserialization failed: Unknown size length tag: ' + sizeLength + ); + } +} + +/** + * Deserialize a size provided some size length. + * @param {SchemaSizeLength} sizeLength The size length to use for deserializing. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {bigint} The deserialized size. + */ +function deserializeSize(sizeLength: SchemaSizeLength, cursor: Cursor): bigint { + switch (sizeLength) { + case 'U8': + return BigInt(deserializeUInt8(cursor)); + case 'U16': + return BigInt(deserializeUInt16LE(cursor)); + case 'U32': + return BigInt(deserializeUInt32LE(cursor)); + case 'U64': + return deserializeBigUInt64LE(cursor); + } +} + +/** + * Deserialize a string provided the length of the size encoding. + * The function will first deserialize size of the string and then this number of bytes for the content encoded as uft8. + * + * @param {SchemaSizeLength} sizeLength The size length to use for deserializing the string length. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {string} The deserialized string. + */ +function deserializeString( + sizeLength: SchemaSizeLength, + cursor: Cursor +): string { + const byteLen = deserializeSize(sizeLength, cursor); + if (byteLen > BigInt(Number.MAX_SAFE_INTEGER)) { + throw new Error( + 'Deserialization failed: Unsupported string length: ' + byteLen + ); + } + const bytes = cursor.read(Number(byteLen)); // Converting bigint to number here is safe becuase of the check above. + return bytes.toString('utf8'); +} + +/** + * Deserialize a list of items provided the length of the size encoding. + * The function will first deserialize the size of the list and then this number of items. + * + * @template A Type representing an item in the list. + * @param {SchemaSizeLength} sizeLength The size length to use for deserializing the list size. + * @param {Deserializer} deserializeItem Function for deserializing an item in this list. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {A[]} The deserialized list of items. + */ +function deserializeList( + sizeLength: SchemaSizeLength, + deserializeItem: Deserializer, + cursor: Cursor +): A[] { + const len = deserializeSize(sizeLength, cursor); + const out = []; + for (let i = 0n; i < len; i++) { + out.push(deserializeItem(cursor)); + } + return out; +} + +/** + * Deserialize an optional value. + * The function will first deserialize a byte indicating whether a value is present or not, if present it will deserialize the value. + * + * @template A Type representing the optional value. + * @param {Deserializer} deserializeValue Function for deserializing the value. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {A | undefined} The deserialized optional item. + */ +function deserializeOption( + deserializeValue: Deserializer, + cursor: Cursor +): A | undefined { + const byte = deserializeUInt8(cursor); + if (byte === 0) { + return undefined; + } else if (byte === 1) { + return deserializeValue(cursor); + } else { + throw new Error( + 'Deserialization failed: Unexpected tag for optional value: ' + byte + ); + } +} + +/** + * Deserialize a schema type. + * @param {ArrayBuffer} buffer The buffer to deserialize. + * @returns {SchemaType} The deserialized schema type. + */ +export function deserializeSchemaType(buffer: ArrayBuffer): SchemaType { + const cursor = Cursor.fromBuffer(buffer); + return deserialSchemaType(cursor); +} + +/** + * Deserialize a schema type. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaType} The deserialized schema type. + */ +function deserialSchemaType(cursor: Cursor): SchemaType { + const tag = deserializeUInt8(cursor); + switch (tag) { + case 0: + return { type: 'Unit' }; + case 1: + return { type: 'Bool' }; + case 2: + return { type: 'U8' }; + case 3: + return { type: 'U16' }; + case 4: + return { type: 'U32' }; + case 5: + return { type: 'U64' }; + case 6: + return { type: 'I8' }; + case 7: + return { type: 'I16' }; + case 8: + return { type: 'I32' }; + case 9: + return { type: 'I64' }; + case 10: + return { type: 'Amount' }; + case 11: + return { type: 'AccountAddress' }; + case 12: + return { type: 'ContractAddress' }; + case 13: + return { type: 'Timestamp' }; + case 14: + return { type: 'Duration' }; + case 15: + return { + type: 'Pair', + first: deserialSchemaType(cursor), + second: deserialSchemaType(cursor), + }; + case 16: + return { + type: 'List', + sizeLength: deserializeSizeLength(cursor), + item: deserialSchemaType(cursor), + }; + case 17: + return { + type: 'Set', + sizeLength: deserializeSizeLength(cursor), + item: deserialSchemaType(cursor), + }; + case 18: + return { + type: 'Map', + sizeLength: deserializeSizeLength(cursor), + key: deserialSchemaType(cursor), + value: deserialSchemaType(cursor), + }; + case 19: + return { + type: 'Array', + size: deserializeUInt32LE(cursor), + item: deserialSchemaType(cursor), + }; + case 20: + return { + type: 'Struct', + fields: deserializeFields(cursor), + }; + case 21: + return { + type: 'Enum', + variants: deserializeList( + 'U32', + deserializeEnumVariant, + cursor + ), + }; + case 22: + return { + type: 'String', + sizeLength: deserializeSizeLength(cursor), + }; + case 23: + return { type: 'U128' }; + case 24: + return { type: 'I128' }; + case 25: + return { + type: 'ContractName', + sizeLength: deserializeSizeLength(cursor), + }; + case 26: + return { + type: 'ReceiveName', + sizeLength: deserializeSizeLength(cursor), + }; + case 27: + return { + type: 'ULeb128', + maxByteSize: deserializeUInt32LE(cursor), + }; + case 28: + return { + type: 'ILeb128', + maxByteSize: deserializeUInt32LE(cursor), + }; + case 29: + return { + type: 'ByteList', + sizeLength: deserializeSizeLength(cursor), + }; + case 30: + return { + type: 'ByteArray', + size: deserializeUInt32LE(cursor), + }; + case 31: + return { + type: 'TaggedEnum', + variants: deserializeMap( + 'U32', + deserializeUInt8, + deserializeEnumVariant, + cursor + ), + }; + + default: + throw new Error( + 'Deserialization failed: Unexpected tag for SchemaType: ' + tag + ); + } +} + +/** + * Deserialize fields for schema type struct or enum variant. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaFields} The deserialized contract schemas. + */ +function deserializeFields(cursor: Cursor): SchemaFields { + const tag = deserializeUInt8(cursor); + switch (tag) { + case 0: + return { + type: 'Named', + fields: deserializeList('U32', deserializeNamedField, cursor), + }; + case 1: + return { + type: 'Unnamed', + fields: deserializeList('U32', deserialSchemaType, cursor), + }; + case 2: + return { type: 'None' }; + default: + throw new Error( + 'Deserialization failed: Unexpected tag for Fields: ' + tag + ); + } +} + +/** + * Deserialize a named field for schema type struct or enum variant. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaNamedField} The deserialized contract schemas. + */ +function deserializeNamedField(cursor: Cursor): SchemaNamedField { + return { + name: deserializeString('U32', cursor), + field: deserialSchemaType(cursor), + }; +} + +/** + * Deserialize an enum variant. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaEnumVariant} The deserialized contract schemas. + */ +function deserializeEnumVariant(cursor: Cursor): SchemaEnumVariant { + return { + name: deserializeString('U32', cursor), + fields: deserializeFields(cursor), + }; +} + +/** + * Deserialize schemas for a smart contract init- or receive function. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaFunctionV1} The deserialized function schemas. + */ +function deserializeSchemaFunctionV1(cursor: Cursor): SchemaFunctionV1 { + const idx = deserializeUInt8(cursor); + const out: SchemaFunctionV1 = {}; + if ([0, 2].includes(idx)) { + out.parameter = deserialSchemaType(cursor); + } + if ([1, 2].includes(idx)) { + out.returnValue = deserialSchemaType(cursor); + } + return out; +} + +/** + * Deserialize schemas for a smart contract init- or receive function. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaFunctionV2} The deserialized function schemas. + */ +function deserializeSchemaFunctionV2(cursor: Cursor): SchemaFunctionV2 { + const idx = deserializeUInt8(cursor); + if (idx > 7) { + throw new Error('Deserialization failed: Unexpected '); + } + const out: SchemaFunctionV2 = {}; + if ([0, 2, 4, 6].includes(idx)) { + out.parameter = deserialSchemaType(cursor); + } + if ([1, 2, 5, 6].includes(idx)) { + out.returnValue = deserialSchemaType(cursor); + } + if ([3, 4, 5, 6].includes(idx)) { + out.error = deserialSchemaType(cursor); + } + return out; +} + +/** + * Deserialize schemas for a smart contract. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaContractV0} The deserialized contract schemas. + */ +function deserializeContractV0(cursor: Cursor): SchemaContractV0 { + return { + state: deserializeOption(deserialSchemaType, cursor), + init: deserializeOption(deserializeSchemaFunctionV1, cursor), + receive: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeSchemaFunctionV1, + cursor + ), + }; +} + +/** + * Deserialize schemas for a smart contract. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaContractV1} The deserialized contract schemas. + */ +function deserializeContractV1(cursor: Cursor): SchemaContractV1 { + return { + init: deserializeOption(deserializeSchemaFunctionV1, cursor), + receive: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeSchemaFunctionV1, + cursor + ), + }; +} + +/** + * Deserialize schemas for a smart contract. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaContractV2} The deserialized contract schemas. + */ +function deserializeContractV2(cursor: Cursor): SchemaContractV2 { + return { + init: deserializeOption(deserializeSchemaFunctionV2, cursor), + receive: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeSchemaFunctionV2, + cursor + ), + }; +} + +/** + * Deserialize schemas for a smart contract. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaContractV3} The deserialized contract schemas. + */ +function deserializeContractV3(cursor: Cursor): SchemaContractV3 { + return { + init: deserializeOption(deserializeSchemaFunctionV2, cursor), + receive: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeSchemaFunctionV2, + cursor + ), + event: deserializeOption(deserialSchemaType, cursor), + }; +} + +/** + * Deserialize schemas for a smart contract module. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaModuleV0} The deserialized module schemas. + */ +function deserializeSchemaModuleV0(cursor: Cursor): SchemaModuleV0 { + return { + contracts: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeContractV0, + cursor + ), + }; +} + +/** + * Deserialize schemas for a smart contract module. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaModuleV1} The deserialized module schemas. + */ +function deserializeSchemaModuleV1(cursor: Cursor): SchemaModuleV1 { + return { + contracts: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeContractV1, + cursor + ), + }; +} + +/** + * Deserialize schemas for a smart contract module. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaModuleV2} The deserialized module schemas. + */ +function deserializeSchemaModuleV2(cursor: Cursor): SchemaModuleV2 { + return { + contracts: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeContractV2, + cursor + ), + }; +} + +/** + * Deserialize schemas for a smart contract module. + * @param {Cursor} cursor A cursor over the buffer to deserialize. + * @returns {SchemaModuleV3} The deserialized module schemas. + */ +function deserializeSchemaModuleV3(cursor: Cursor): SchemaModuleV3 { + return { + contracts: deserializeMap( + 'U32', + deserializeString.bind(undefined, 'U32'), + deserializeContractV3, + cursor + ), + }; +} + +/** + * Serialize a schema type. + * @param {SchemaType} schemaType The schema type to serialize + * @returns {Uint8Array} The buffer containing the serialized schema type. + */ +export function serializeSchemaType(schemaType: SchemaType): Uint8Array { + switch (schemaType.type) { + case 'Unit': + return Uint8Array.of(0); + case 'Bool': + return Uint8Array.of(1); + case 'U8': + return Uint8Array.of(2); + case 'U16': + return Uint8Array.of(3); + case 'U32': + return Uint8Array.of(4); + case 'U64': + return Uint8Array.of(5); + case 'I8': + return Uint8Array.of(6); + case 'I16': + return Uint8Array.of(7); + case 'I32': + return Uint8Array.of(8); + case 'I64': + return Uint8Array.of(9); + case 'Amount': + return Uint8Array.of(10); + case 'AccountAddress': + return Uint8Array.of(11); + case 'ContractAddress': + return Uint8Array.of(12); + case 'Timestamp': + return Uint8Array.of(13); + case 'Duration': + return Uint8Array.of(14); + case 'Pair': + return Buffer.concat([ + Uint8Array.of(15), + serializeSchemaType(schemaType.first), + serializeSchemaType(schemaType.second), + ]); + case 'List': + return Buffer.concat([ + Uint8Array.of(16), + serialSizeLength(schemaType.sizeLength), + serializeSchemaType(schemaType.item), + ]); + case 'Set': + return Buffer.concat([ + Uint8Array.of(17), + serialSizeLength(schemaType.sizeLength), + serializeSchemaType(schemaType.item), + ]); + case 'Map': + return Buffer.concat([ + Uint8Array.of(18), + serialSizeLength(schemaType.sizeLength), + serializeSchemaType(schemaType.key), + serializeSchemaType(schemaType.value), + ]); + case 'Array': + return Buffer.concat([ + Uint8Array.of(19), + encodeWord32(schemaType.size, true), + serializeSchemaType(schemaType.item), + ]); + case 'Struct': + return Buffer.concat([ + Uint8Array.of(20), + serialFields(schemaType.fields), + ]); + case 'Enum': + return Buffer.concat([ + Uint8Array.of(21), + serializeList('U32', serializeEnumVariant, schemaType.variants), + ]); + case 'String': + return Buffer.concat([ + Uint8Array.of(22), + serialSizeLength(schemaType.sizeLength), + ]); + case 'U128': + return Uint8Array.of(23); + case 'I128': + return Uint8Array.of(24); + case 'ContractName': + return Buffer.concat([ + Uint8Array.of(25), + serialSizeLength(schemaType.sizeLength), + ]); + case 'ReceiveName': + return Buffer.concat([ + Uint8Array.of(26), + serialSizeLength(schemaType.sizeLength), + ]); + case 'ULeb128': + return Buffer.concat([ + Uint8Array.of(27), + encodeWord32(schemaType.maxByteSize, true), + ]); + case 'ILeb128': + return Buffer.concat([ + Uint8Array.of(28), + encodeWord32(schemaType.maxByteSize, true), + ]); + case 'ByteList': + return Buffer.concat([ + Uint8Array.of(29), + serialSizeLength(schemaType.sizeLength), + ]); + case 'ByteArray': + return Buffer.concat([ + Uint8Array.of(30), + encodeWord32(schemaType.size, true), + ]); + case 'TaggedEnum': + return Buffer.concat([ + Uint8Array.of(31), + serializeMap( + 'U32', + encodeWord8, + serializeEnumVariant, + schemaType.variants + ), + ]); + default: + throw new Error( + 'Deserialization failed: Unexpected tag for SchemaType: ' + ); + } +} + +/** + * Serialize the size length itself. + * @param {SchemaSizeLength} sizeLength Size length to serialize. + * @returns {Uint8Array} Buffer with serialization. + */ +function serialSizeLength(sizeLength: SchemaSizeLength): Uint8Array { + switch (sizeLength) { + case 'U8': + return new Uint8Array([0]); + case 'U16': + return new Uint8Array([1]); + case 'U32': + return new Uint8Array([2]); + case 'U64': + return new Uint8Array([3]); + } +} + +/** + * Serialize the size of some collection using the size length. + * @param {SchemaSizeLength} sizeLength Size length to use when serializing the size. + * @param {bigint | number} size Size to serialize. + * @returns {Uint8Array} Buffer with serialization. + */ +function serialSize( + sizeLength: SchemaSizeLength, + size: bigint | number +): Uint8Array { + switch (sizeLength) { + case 'U8': + return encodeWord8(Number(size)); + case 'U16': + return encodeWord16(Number(size), true); + case 'U32': + return encodeWord32(Number(size), true); + case 'U64': + return encodeWord64(BigInt(size), true); + } +} + +/** + * Serialize fields for schema type struct or enum variant. + * @param {SchemaFields} fields Fields for a struct or enum variant. + * @returns {Uint8Array} Buffer with serialization. + */ +function serialFields(fields: SchemaFields): Uint8Array { + switch (fields.type) { + case 'Named': + return Buffer.concat([ + Uint8Array.of(0), + serializeList('U32', serialNamedField, fields.fields), + ]); + case 'Unnamed': + return Buffer.concat([ + Uint8Array.of(1), + serializeList('U32', serializeSchemaType, fields.fields), + ]); + case 'None': + return Uint8Array.of(2); + } +} + +/** + * Serialize named field for schema fields. + * @param {SchemaNamedField} named The named field to serialize. + * @returns {Uint8Array} Buffer with serialization. + */ +function serialNamedField(named: SchemaNamedField): Uint8Array { + return Buffer.concat([ + serializeString('U32', named.name), + serializeSchemaType(named.field), + ]); +} + +/** + * Serialize an enum variant. + * @param {SchemaEnumVariant} variant The enum variant to serialize. + * @returns {Uint8Array} Buffer with serialization. + */ +function serializeEnumVariant(variant: SchemaEnumVariant): Uint8Array { + return Buffer.concat([ + serializeString('U32', variant.name), + serialFields(variant.fields), + ]); +} + +/** + * Represent a function for serializing a type A. + * @template A Item to be serialized. + */ +type Serializer = (a: A) => Uint8Array; + +/** + * Serialize a list of items. + * @template A Type of a list item. + * @param {SchemaSizeLength} sizeLength The size length to use for serializing the size. + * @param {Serializer} serialItem Function for serializing each item. + * @param {A[]} list List of items to serialize. + * @returns {Uint8Array} Buffer with serialization. + */ +function serializeList( + sizeLength: SchemaSizeLength, + serialItem: Serializer, + list: A[] +): Uint8Array { + return Buffer.concat([ + serialSize(sizeLength, list.length), + ...list.map(serialItem), + ]); +} + +/** + * Serialize a string. + * @param {SchemaSizeLength} sizeLength The size length to use for serializing the size. + * @returns {Uint8Array} Buffer with serialization. + */ +function serializeString( + sizeLength: SchemaSizeLength, + value: string +): Uint8Array { + return Buffer.concat([ + serialSize(sizeLength, value.length), + Buffer.from(value, 'utf8'), + ]); +} + +/** + * Serialize a map. + * @template K Type for keys used in the map. + * @template V Type for values used in the map. + * @param {SchemaSizeLength} sizeLength The size length to use for serializing the size. + * @param {Serializer} serialKey Function for serializing a key. + * @param {Serializer} serialValue Function for serializing a value. + * @param {Map} map The map to serialize. + * @returns {Uint8Array} Buffer with serialization. + */ +function serializeMap( + sizeLength: SchemaSizeLength, + serialKey: Serializer, + serialValue: Serializer, + map: Map +): Uint8Array { + const buffers: Uint8Array[] = [serialSize(sizeLength, map.size)]; + for (const [k, v] of map.entries()) { + buffers.push(serialKey(k), serialValue(v)); + } + return Buffer.concat(buffers); +} diff --git a/packages/common/src/serialization.ts b/packages/sdk/src/serialization.ts similarity index 63% rename from packages/common/src/serialization.ts rename to packages/sdk/src/serialization.ts index def4867f4..c922cf655 100644 --- a/packages/common/src/serialization.ts +++ b/packages/sdk/src/serialization.ts @@ -1,5 +1,5 @@ -import { Buffer } from 'buffer/'; -import { getAccountTransactionHandler } from './accountTransactions'; +import { Buffer } from 'buffer/index.js'; +import { getAccountTransactionHandler } from './accountTransactions.js'; import { encodeWord8FromString, encodeWord8, @@ -9,7 +9,7 @@ import { serializeMap, serializeVerifyKey, serializeYearMonth, -} from './serializationHelpers'; +} from './serializationHelpers.js'; import { AccountTransactionHeader, AccountTransactionType, @@ -22,16 +22,13 @@ import { IdOwnershipProofs, UnsignedCredentialDeploymentInformation, CredentialDeploymentInfo, - SchemaVersion, CredentialDeploymentDetails, - SignedCredentialDeploymentDetails, - CredentialDeploymentTransaction, -} from './types'; -import { calculateEnergyCost } from './energyCost'; -import { countSignatures } from './util'; -import { AccountAddress } from './types/accountAddress'; -import { sha256 } from './hash'; -import * as wasm from '@concordium/rust-bindings'; +} from './types.js'; +import { calculateEnergyCost } from './energyCost.js'; +import { countSignatures } from './util.js'; +import * as AccountAddress from './types/AccountAddress.js'; +import * as Energy from './types/Energy.js'; +import { sha256 } from './hash.js'; function serializeAccountTransactionType(type: AccountTransactionType): Buffer { return Buffer.from(Uint8Array.of(type)); @@ -49,11 +46,11 @@ function serializeAccountTransactionType(type: AccountTransactionType): Buffer { function serializeAccountTransactionHeader( header: AccountTransactionHeader, payloadSize: number, - energyAmount: bigint + energyAmount: Energy.Type ) { - const serializedSender = header.sender.decodedAddress; - const serializedNonce = encodeWord64(header.nonce); - const serializedEnergyAmount = encodeWord64(energyAmount); + const serializedSender = AccountAddress.toBuffer(header.sender); + const serializedNonce = encodeWord64(header.nonce.value); + const serializedEnergyAmount = encodeWord64(energyAmount.value); const serializedPayloadSize = encodeWord32(payloadSize); const serializedExpiry = encodeWord64(header.expiry.expiryEpochSeconds); return Buffer.concat([ @@ -366,7 +363,7 @@ export function serializeCredentialDeploymentInfo( */ export function getCredentialForExistingAccountSignDigest( unsignedCredentialDeploymentInfo: UnsignedCredentialDeploymentInformation, - address: AccountAddress + address: AccountAddress.Type ): Buffer { const serializedCredentialValues = serializeCredentialDeploymentValues( unsignedCredentialDeploymentInfo @@ -379,7 +376,7 @@ export function getCredentialForExistingAccountSignDigest( serializedCredentialValues, serializedIdOwnershipProofs, existingAccountByte, - address.decodedAddress, + AccountAddress.toBuffer(address), ]); } @@ -405,185 +402,3 @@ export function getCredentialDeploymentSignDigest( encodeWord64(credentialDeployment.expiry.expiryEpochSeconds), ]); } - -interface DeploymentDetailsResult { - credInfo: string; - serializedTransaction: string; - transactionHash: string; -} - -/** - * Gets the transaction hash that is used to look up the status of a credential - * deployment transaction. - * @param credentialDeployment the transaction to hash - * @param signatures the signatures that will also be part of the hash - * @returns the sha256 hash of the serialized block item kind, signatures, and credential deployment transaction - */ -export function getCredentialDeploymentTransactionHash( - credentialDeployment: CredentialDeploymentDetails, - signatures: string[] -): string { - const credentialDeploymentInfo: DeploymentDetailsResult = JSON.parse( - wasm.getDeploymentDetails( - signatures, - JSON.stringify(credentialDeployment.unsignedCdi), - credentialDeployment.expiry.expiryEpochSeconds - ) - ); - return credentialDeploymentInfo.transactionHash; -} - -/** - * Serializes a credential deployment transaction of a new account, so that it is ready for being - * submitted to the node. - * @param credentialDeployment the credenetial deployment transaction - * @param signatures the signatures on the hash of unsigned credential deployment information - * @returns the serialization of the credential deployment transaction ready for being submitted to a node - */ -export function serializeCredentialDeploymentTransactionForSubmission( - credentialDeployment: CredentialDeploymentDetails, - signatures: string[] -): Buffer { - const credentialDeploymentInfo: DeploymentDetailsResult = JSON.parse( - wasm.getDeploymentDetails( - signatures, - JSON.stringify(credentialDeployment.unsignedCdi), - credentialDeployment.expiry.expiryEpochSeconds - ) - ); - return Buffer.from(credentialDeploymentInfo.serializedTransaction, 'hex'); -} - -/** - * @param contractName name of the contract that the init contract transaction will initialize - * @param parameters the parameters to be serialized. Should correspond to the JSON representation. - * @param rawSchema buffer for the schema of a module that contains the contract - * @param schemaVersion the version of the schema provided - * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. - * @returns serialized buffer of init contract parameters - */ -export function serializeInitContractParameters( - contractName: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - parameters: any, - rawSchema: Buffer, - schemaVersion?: SchemaVersion, - verboseErrorMessage = false -): Buffer { - const serializedParameters = wasm.serializeInitContractParameters( - JSON.stringify(parameters), - rawSchema.toString('hex'), - contractName, - schemaVersion, - verboseErrorMessage - ); - return Buffer.from(serializedParameters, 'hex'); -} - -/** - * @param contractName name of the contract that the update contract transaction will update - * @param receiveFunctionName name of function that the update contract transaction will invoke - * @param parameters the parameters to be serialized. Should correspond to the JSON representation. - * @param rawSchema buffer for the schema of a module that contains the contract - * @param schemaVersion the version of the schema provided - * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. - * @returns serialized buffer of update contract parameters - */ -export function serializeUpdateContractParameters( - contractName: string, - receiveFunctionName: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - parameters: any, - rawSchema: Buffer, - schemaVersion?: SchemaVersion, - verboseErrorMessage = false -): Buffer { - const serializedParameters = wasm.serializeReceiveContractParameters( - JSON.stringify(parameters), - rawSchema.toString('hex'), - contractName, - receiveFunctionName, - schemaVersion, - verboseErrorMessage - ); - return Buffer.from(serializedParameters, 'hex'); -} - -/** - * Given a value for a smart contract type, and the raw schema for that type, serialize the value into binary format. - * @param value the value that should be serialized. Should correspond to the JSON representation - * @param rawSchema the schema for the type that the given value should be serialized as - * @param verboseErrorMessage Whether errors are in a verbose format or not. Defaults to `false`. - * @returns serialized buffer of the value - */ -export function serializeTypeValue( - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types - value: any, - rawSchema: Buffer, - verboseErrorMessage = false -): Buffer { - const serializedValue = wasm.serializeTypeValue( - JSON.stringify(value), - rawSchema.toString('hex'), - verboseErrorMessage - ); - return Buffer.from(serializedValue, 'hex'); -} - -/** - * @deprecated the SignedCredentialDeploymentDetails is only used with JSON-RPC - */ -function serializeSignedCredentialDeploymentDetails( - credentialDetails: SignedCredentialDeploymentDetails -): Buffer { - const serializedBlockItemKind = encodeWord8( - BlockItemKind.CredentialDeploymentKind - ); - const serializedExpiry = encodeWord64( - credentialDetails.expiry.expiryEpochSeconds - ); - const serializedCredentialKind = encodeWord8(1); - const serializedInfo: Buffer = Buffer.from( - serializeCredentialDeploymentInfo(credentialDetails.cdi) - ); - return Buffer.concat([ - serializedBlockItemKind, - serializedExpiry, - serializedCredentialKind, - serializedInfo, - ]); -} - -/** - * @deprecated the SignedCredentialDeploymentDetails is only used with JSON-RPC - */ -export function serializeSignedCredentialDeploymentDetailsForSubmission( - credentialDetails: SignedCredentialDeploymentDetails -): Buffer { - const serializedVersion = encodeWord8(0); - const serializedDetails = - serializeSignedCredentialDeploymentDetails(credentialDetails); - return Buffer.concat([serializedVersion, serializedDetails]); -} - -/** - * @deprecated the SignedCredentialDeploymentDetails is only used with JSON-RPC - */ -export function getSignedCredentialDeploymentTransactionHash( - credentialDetails: SignedCredentialDeploymentDetails -): string { - const serializedDetails = - serializeSignedCredentialDeploymentDetails(credentialDetails); - return sha256([serializedDetails]).toString('hex'); -} - -export function serializeCredentialDeploymentPayload( - signatures: string[], - credentialDeploymentTransaction: CredentialDeploymentTransaction -): Buffer { - const payloadByteArray = wasm.serializeCredentialDeploymentPayload( - signatures, - JSON.stringify(credentialDeploymentTransaction.unsignedCdi) - ); - return Buffer.from(payloadByteArray); -} diff --git a/packages/common/src/serializationHelpers.ts b/packages/sdk/src/serializationHelpers.ts similarity index 95% rename from packages/common/src/serializationHelpers.ts rename to packages/sdk/src/serializationHelpers.ts index 7edfd92cc..25b5ed2bf 100644 --- a/packages/common/src/serializationHelpers.ts +++ b/packages/sdk/src/serializationHelpers.ts @@ -1,4 +1,4 @@ -import { Buffer } from 'buffer/'; +import { Buffer } from 'buffer/index.js'; import { BakerKeysWithProofs, ConfigureBakerPayload, @@ -7,15 +7,15 @@ import { ConfigureDelegationPayload, DelegationTarget, DelegationTargetType, -} from './types'; -import { DataBlob } from './types/DataBlob'; -import { isDefined } from './util'; +} from './types.js'; +import { DataBlob } from './types/DataBlob.js'; +import { isDefined } from './util.js'; export function serializeMap( map: Record, - encodeSize: (size: number) => Buffer, - encodeKey: (k: string) => Buffer, - encodeValue: (t: T) => Buffer + encodeSize: (size: number) => Uint8Array, + encodeKey: (k: string) => Uint8Array, + encodeValue: (t: T) => Uint8Array ): Buffer { const keys = Object.keys(map); const buffers = [encodeSize(keys.length)]; @@ -28,8 +28,8 @@ export function serializeMap( export function serializeList( list: T[], - putSize: (size: number) => Buffer, - putMember: (t: T) => Buffer + putSize: (size: number) => Uint8Array, + putMember: (t: T) => Uint8Array ): Buffer { const buffers = [putSize(list.length)]; list.forEach((member: T) => { @@ -198,7 +198,7 @@ export function encodeDataBlob(blob: DataBlob): Buffer { * @returns Buffer containing the 32 bit length of buffer and buffer. */ export function packBufferWithWord32Length( - buffer: Buffer, + buffer: Uint8Array, useLittleEndian = false ): Buffer { const length = encodeWord32(buffer.length, useLittleEndian); @@ -211,7 +211,7 @@ export function packBufferWithWord32Length( * @returns Buffer containing the length of the buffer of 16 bit and buffer. */ export function packBufferWithWord16Length( - buffer: Buffer, + buffer: Uint8Array, useLittleEndian = false ): Buffer { const length = encodeWord16(buffer.length, useLittleEndian); @@ -223,7 +223,7 @@ export function packBufferWithWord16Length( * @param buffer containing the buffer * @returns Buffer containing the length of the buffer of 8 bit and buffer. */ -export function packBufferWithWord8Length(buffer: Buffer): Buffer { +export function packBufferWithWord8Length(buffer: Uint8Array): Buffer { const length = encodeWord8(buffer.length); return Buffer.concat([length, buffer]); } @@ -297,7 +297,7 @@ function getPayloadBitmap(payload: T, fieldOrder: Array) { * Makes a type with keys from Object and values being functions that take values with types of respective original values, returning a Buffer or undefined. */ type SerializationSpec = Required<{ - [P in keyof T]: (v: T[P]) => Buffer | undefined; + [P in keyof T]: (v: T[P]) => Uint8Array | undefined; }>; /** @@ -312,7 +312,7 @@ const serializeFromSpec = const v = payload[k as keyof T]; const f = spec[k as keyof typeof spec] as ( x: typeof v - ) => Buffer | undefined; + ) => Uint8Array | undefined; return f(v); }) .filter(isDefined); @@ -421,7 +421,9 @@ export function serializeConfigureBakerPayload( * Prefixed with a byte indicating if a value follows or not. */ export const makeSerializeOptional = - (fun: (value: T) => Buffer): ((value: T | undefined) => Buffer) => + ( + fun: (value: T) => Uint8Array + ): ((value: T | undefined) => Uint8Array) => (value) => { if (value === undefined) { return encodeBool(false); diff --git a/packages/sdk/src/shims/ed25519.node.ts b/packages/sdk/src/shims/ed25519.node.ts new file mode 100644 index 000000000..2ebf319b4 --- /dev/null +++ b/packages/sdk/src/shims/ed25519.node.ts @@ -0,0 +1,9 @@ +// To add support for node versions <19. +// From https://www.npmjs.com/package/@noble/ed25519#usage +import { webcrypto } from 'node:crypto'; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore +if (!globalThis.crypto) globalThis.crypto = webcrypto; + +export * from '@noble/ed25519'; diff --git a/packages/common/src/signHelpers.ts b/packages/sdk/src/signHelpers.ts similarity index 74% rename from packages/common/src/signHelpers.ts rename to packages/sdk/src/signHelpers.ts index bb20faa0f..9b28044a1 100644 --- a/packages/common/src/signHelpers.ts +++ b/packages/sdk/src/signHelpers.ts @@ -1,19 +1,98 @@ -import { getAccountTransactionSignDigest } from './serialization'; +import { getAccountTransactionSignDigest } from './serialization.js'; import { AccountInfo, AccountTransaction, AccountTransactionSignature, + Base58String, CredentialSignature, HexString, - SimpleAccountKeys, - WalletExportFormat, - WithAccountKeys, -} from './types'; -import * as ed from '@noble/ed25519'; -import { Buffer } from 'buffer/'; -import { AccountAddress } from './types/accountAddress'; -import { sha256 } from './hash'; -import { mapRecord } from './util'; + JsonString, +} from './types.js'; +import * as ed from '#ed25519'; +import { Buffer } from 'buffer/index.js'; +import * as AccountAddress from './types/AccountAddress.js'; +import { sha256 } from './hash.js'; +import { mapRecord } from './util.js'; + +export interface KeyPair { + signKey: HexString; + verifyKey: HexString; +} + +export interface CredentialKeys { + keys: Record; + threshold: number; +} + +export interface AccountKeys { + keys: Record; + threshold: number; +} + +export type SimpleAccountKeys = Record>; + +export interface WithAccountKeys { + accountKeys: AccountKeys; +} + +export interface WalletExportFormat { + type: string; + v: number; + environment: string; + value: { + accountKeys: AccountKeys; + address: Base58String; + credentials: Record; + }; +} + +/** + * Parses a wallet export file into a WalletExportFormat. The wallet export + * file is exported from a concordium wallet. + */ +export function parseWallet(walletString: JsonString): WalletExportFormat { + const wallet = JSON.parse(walletString); + console.log(typeof wallet.type); + if (typeof wallet.type !== 'string') { + throw Error( + 'Expected field "type" to be of type "string" but was of type "' + + typeof wallet.type + + '"' + ); + } + if (typeof wallet.v !== 'number') { + throw Error( + 'Expected field "v" to be of type "number" but was of type "' + + typeof wallet.v + + '"' + ); + } + if (typeof wallet.environment !== 'string') { + throw Error( + 'Expected field "environment" to be of type "string" but was of type "' + + typeof wallet.environment + + '"' + ); + } + if (typeof wallet.value.address !== 'string') { + throw Error( + 'Expected field "value.address" to be of type "string" but was of type "' + + typeof wallet.value.address + + '"' + ); + } + if (wallet.value.accountKeys === undefined) { + throw Error( + 'Expected field "value.accountKeys" to be defined, but was not' + ); + } + if (wallet.value.credentials === undefined) { + throw Error( + 'Expected field "value.credentials" to be defined, but was not' + ); + } + return wallet; +} /** * A structure to use for creating signatures on a given digest. @@ -22,11 +101,11 @@ export interface AccountSigner { /** * Creates a signature of the provided digest * - * @param {Buffer} digest - The digest to create signatures on. + * @param {ArrayBuffer} digest - The digest to create signatures on. * * @returns {Promise} A promise resolving with a set of signatures for a set of credentials corresponding to some account */ - sign(digest: Buffer): Promise; + sign(digest: ArrayBuffer): Promise; /** * Returns the amount of signatures that the signer produces */ getSignatureCount(): bigint; @@ -35,15 +114,16 @@ export interface AccountSigner { /** * Gets Ed25519 signature for `digest`. * - * @param {Buffer} digest - the message to sign. + * @param {ArrayBuffer} digest - the message to sign. * @param {HexString} privateKey - the ed25519 private key in HEX format. * * @returns {Buffer} the signature. */ export const getSignature = async ( - digest: Buffer, + digest: ArrayBuffer, privateKey: HexString -): Promise => Buffer.from(await ed.sign(digest, privateKey)); +): Promise => + Buffer.from(await ed.signAsync(new Uint8Array(digest), privateKey)); /** * Creates an `AccountSigner` for an account which uses the first credential's first keypair. @@ -58,7 +138,7 @@ export function buildBasicAccountSigner(privateKey: HexString): AccountSigner { getSignatureCount() { return 1n; }, - async sign(digest: Buffer) { + async sign(digest: ArrayBuffer) { const sig = await getSignature(digest, privateKey); return { 0: { @@ -96,7 +176,7 @@ const getKeys = ( }; const getCredentialSignature = async ( - digest: Buffer, + digest: ArrayBuffer, keys: Record ): Promise => { const sig: CredentialSignature = {}; @@ -164,7 +244,7 @@ export function buildAccountSigner( getSignatureCount() { return numKeys; }, - async sign(digest: Buffer) { + async sign(digest: ArrayBuffer) { const sig: AccountTransactionSignature = {}; for (const key in keys) { sig[key] = await getCredentialSignature(digest, keys[key]); @@ -195,13 +275,13 @@ export function signTransaction( * @param message the message to sign, assumed to be utf8 encoded string or a Uint8Array/buffer. */ function getMessageDigest( - account: AccountAddress, + account: AccountAddress.Type, message: string | Uint8Array ): Buffer { const prepend = Buffer.alloc(8, 0); const rawMessage = typeof message === 'string' ? Buffer.from(message, 'utf8') : message; - return sha256([account.decodedAddress, prepend, rawMessage]); + return sha256([AccountAddress.toBuffer(account), prepend, rawMessage]); } /** @@ -212,7 +292,7 @@ function getMessageDigest( * @param signer An object that handles the keys of the account, and performs the actual signing. */ export function signMessage( - account: AccountAddress, + account: AccountAddress.Type, message: string | Uint8Array, signer: AccountSigner ): Promise { @@ -238,10 +318,7 @@ export async function verifyMessageSignature( return false; } - const digest = getMessageDigest( - new AccountAddress(accountInfo.accountAddress), - message - ); + const digest = getMessageDigest(accountInfo.accountAddress, message); for (const credentialIndex of Object.keys(signature)) { const credential = @@ -268,7 +345,7 @@ export async function verifyMessageSignature( ); } if ( - !(await ed.verify( + !(await ed.verifyAsync( credentialSignature[Number(keyIndex)], digest, credentialKeys.keys[Number(keyIndex)].verifyKey diff --git a/packages/common/src/types.ts b/packages/sdk/src/types.ts similarity index 69% rename from packages/common/src/types.ts rename to packages/sdk/src/types.ts index 5baa8ffc2..c751d5cfa 100644 --- a/packages/common/src/types.ts +++ b/packages/sdk/src/types.ts @@ -2,29 +2,35 @@ * @module Common GRPC-Client */ -import { AccountAddress } from './types/accountAddress'; -import { CredentialRegistrationId } from './types/CredentialRegistrationId'; -import { CcdAmount } from './types/ccdAmount'; -import { DataBlob } from './types/DataBlob'; -import { TransactionExpiry } from './types/transactionExpiry'; -import { Buffer } from 'buffer/'; -import { ModuleReference } from './types/moduleReference'; -import { RejectReason, RejectReasonV1 } from './types/rejectReason'; -import { - ContractTraceEvent, - MemoEvent, - TransactionEvent, - TransferredEvent, -} from './types/transactionEvent'; - -export * from './types/NodeInfo'; -export * from './types/PeerInfo'; -export * from './types/blockItemSummary'; -export * from './types/chainUpdate'; -export * from './types/rejectReason'; -export * from './types/transactionEvent'; -export * from './types/BlockSpecialEvents'; -export * from './types/errors'; +import * as AccountAddress from './types/AccountAddress.js'; +import * as ContractAddress from './types/ContractAddress.js'; +import * as Duration from './types/Duration.js'; +import * as Timestamp from './types/Timestamp.js'; +import * as Energy from './types/Energy.js'; +import type * as BlockHash from './types/BlockHash.js'; +import * as CredentialRegistrationId from './types/CredentialRegistrationId.js'; +import * as Parameter from './types/Parameter.js'; +import type * as InitName from './types/InitName.js'; +import type * as ContractName from './types/ContractName.js'; +import type * as ReceiveName from './types/ReceiveName.js'; +import type * as SequenceNumber from './types/SequenceNumber.js'; +import type * as ReturnValue from './types/ReturnValue.js'; +import type * as CcdAmount from './types/CcdAmount.js'; +import type * as TransactionHash from './types/TransactionHash.js'; +import type * as TransactionExpiry from './types/TransactionExpiry.js'; +import { DataBlob } from './types/DataBlob.js'; +import type * as ModuleReference from './types/ModuleReference.js'; +import { RejectReason } from './types/rejectReason.js'; +import { ContractTraceEvent } from './types/transactionEvent.js'; + +export * from './types/NodeInfo.js'; +export * from './types/PeerInfo.js'; +export * from './types/blockItemSummary.js'; +export * from './types/chainUpdate.js'; +export * from './types/rejectReason.js'; +export * from './types/transactionEvent.js'; +export * from './types/BlockSpecialEvents.js'; +export * from './types/errors.js'; export type HexString = string; export type Base58String = string; @@ -59,14 +65,19 @@ export type BakerSignatureVerifyKey = HexString; */ export type BakerAggregationVerifyKey = HexString; -/** A number of milliseconds */ -export type Duration = bigint; -/** Unix timestamp in milliseconds */ -export type Timestamp = bigint; /** A consensus round */ export type Round = bigint; -/** An amount of energy */ -export type Energy = bigint; + +/** + * Utility type that takes an object type and makes the hover overlay more readable. + * + * @example + * type ComplexType = {test: string;} & {another: number;}; // Hovering this type shows: {test: string;} & {another: number;} + * type Test = Compute; // Now it shows: {test: string; another: number;} + */ +type Compute = { + [K in keyof T]: T[K]; +} & unknown; /** * The number of chain restarts via a protocol update. An effected @@ -82,13 +93,14 @@ export type GenesisIndex = number; * @example * type PartiallyOptionalProps = MakeOptional<{test: string; another: number;}, 'another'>; // {test: string; another?: number;} */ -export type MakeOptional = Omit & - Partial>; +export type MakeOptional = Compute< + Omit & Partial> +>; /** Makes keys of type required (i.e. non-optional) */ -export type MakeRequired = Required> & - Omit; - +export type MakeRequired = Compute< + Required> & Omit +>; /** * Returns a union of all keys of type T with values matching type V. */ @@ -162,41 +174,21 @@ export enum TransactionStatusEnum { export interface AddressAccount { type: 'AddressAccount'; - address: Base58String; -} - -export interface ContractAddress { - index: bigint; - subindex: bigint; + address: AccountAddress.Type; } export type AccountIdentifierInput = - | AccountAddress - | CredentialRegistrationId + | AccountAddress.Type + | CredentialRegistrationId.Type | bigint; export type Address = | { type: 'AddressContract'; - address: ContractAddress; + address: ContractAddress.Type; } | AddressAccount; -interface RejectedEventResult { - outcome: 'reject'; - rejectReason: RejectReasonV1; -} - -interface SuccessfulEventResult { - outcome: 'success'; - events: TransactionEvent[]; -} - -export type EventResult = - | SuccessfulEventResult - | TransferWithMemoEventResult - | RejectedEventResult; - export enum TransactionSummaryType { AccountTransaction = 'accountTransaction', CredentialDeploymentTransaction = 'credentialDeploymentTransaction', @@ -219,84 +211,14 @@ export interface GenericTransactionSummaryType } export interface BaseTransactionSummary { - sender?: string; - hash: string; + sender?: AccountAddress.Type; + hash: TransactionHash.Type; - cost: bigint; - energyCost: bigint; + cost: CcdAmount.Type; + energyCost: Energy.Type; index: bigint; } -/** - * @deprecated This is type for describing return types from the V1 gRPC client, which has been deprecated - */ -interface GenericTransactionSummary extends BaseTransactionSummary { - type: GenericTransactionSummaryType; - result: EventResult; -} - -/** - * @deprecated This is type for describing return types from the V1 gRPC client, which has been deprecated - */ -interface TransferWithMemoEventResult { - outcome: 'success'; - events: [TransferredEvent, MemoEvent]; -} - -/** - * @deprecated This is type for describing return types from the V1 gRPC client, which has been deprecated - */ -export interface TransferWithMemoTransactionSummary - extends BaseTransactionSummary { - type: TransferWithMemoSummaryType; - result: TransferWithMemoEventResult; -} - -/** - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type TransactionSummary = - | GenericTransactionSummary - | TransferWithMemoTransactionSummary; - -/** - * @deprecated This is helper for type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function instanceOfTransferWithMemoTransactionSummary( - object: TransactionSummary -): object is TransferWithMemoTransactionSummary { - return ( - object.type !== undefined && object.type.contents === 'transferWithMemo' - ); -} - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface TransactionStatus { - status: TransactionStatusEnum; - outcomes?: Record; -} - -/** - * @deprecated This is type describing return types from the V1 gRPC client, which has been deprecated - */ -export interface PartyInfo { - bakerId: bigint; - weight: bigint; - signed: boolean; -} - -/** - * @deprecated This is type describing return types from the V1 gRPC client, which has been deprecated - */ -export interface FinalizationData { - finalizationIndex: bigint; - finalizationDelay: bigint; - finalizationBlockPointer: string; - finalizers: PartyInfo[]; -} - export interface Ratio { numerator: bigint; denominator: bigint; @@ -329,10 +251,13 @@ interface MintDistributionCommon { } export interface MintDistributionV0 extends MintDistributionCommon { + version: 0; mintPerSlot: number; } -export type MintDistributionV1 = MintDistributionCommon; +export interface MintDistributionV1 extends MintDistributionCommon { + version: 1; +} export type MintDistribution = MintDistributionV0 | MintDistributionV1; @@ -348,12 +273,15 @@ export interface GasRewardsCommon { /** Gas rewards properties for protocol version 1-5 ({@link ChainParametersV0} and {@link ChainParametersV1}). */ export interface GasRewardsV0 extends GasRewardsCommon { + version: 0; /** The fractional amount paid for including a finalization proof */ finalizationProof: number; } /** Gas rewards properties from protocol version 6 ({@link ChainParametersV2}). */ -export type GasRewardsV1 = GasRewardsCommon; +export interface GasRewardsV1 extends GasRewardsCommon { + version: 1; +} /** Common reward parameters used across all protocol versions */ export interface RewardParametersCommon { @@ -363,6 +291,7 @@ export interface RewardParametersCommon { /** Reward parameters used from protocol version 1-3 ({@link ChainParametersV0}). */ export interface RewardParametersV0 extends RewardParametersCommon { + version: 0; /** The current mint distribution */ mintDistribution: MintDistributionV0; /** The current gas rewards parameters */ @@ -371,6 +300,7 @@ export interface RewardParametersV0 extends RewardParametersCommon { /** Reward parameters used in protocol versions 4 and 5 ({@link ChainParametersV1}). */ export interface RewardParametersV1 extends RewardParametersCommon { + version: 1; /** The current mint distribution */ mintDistribution: MintDistributionV1; /** The current gas rewards parameters */ @@ -379,6 +309,7 @@ export interface RewardParametersV1 extends RewardParametersCommon { /** Reward parameters used from protocol version 6 ({@link ChainParametersV2}). */ export interface RewardParametersV2 extends RewardParametersCommon { + version: 2; /** The current mint distribution */ mintDistribution: MintDistributionV1; /** The current gas rewards parameters */ @@ -402,7 +333,7 @@ export interface CooldownParametersV1 { /** Pool parameters used from protocol version 1-3 */ export interface PoolParametersV0 { /** The minimum threshold to stake to become a baker. */ - minimumThresholdForBaking: Amount; + minimumThresholdForBaking: CcdAmount.Type; } /** Pool parameters used from protocol version 4 */ @@ -420,7 +351,7 @@ export interface PoolParametersV1 { /** Fraction of transaction rewards charged by the pool owner. */ transactionCommissionRange: InclusiveRange; /** Minimum equity capital required for a new baker.*/ - minimumEquityCapital: Amount; + minimumEquityCapital: CcdAmount.Type; /** * Maximum fraction of the total staked capital of that a new baker can * have. @@ -448,7 +379,7 @@ export interface TimeParametersV1 { /** Parameters that determine timeouts in the consensus protocol used from protocol version 6. */ export interface TimeoutParameters { /** The base value for triggering a timeout, in milliseconds. */ - timeoutBase: Duration; + timeoutBase: Duration.Type; /** Factor for increasing the timeout. Must be greater than 1. */ timeoutIncrease: Ratio; /** Factor for decreasing the timeout. Must be between 0 and 1. */ @@ -458,9 +389,9 @@ export interface TimeoutParameters { /** Consensus parameters, used from protocol version 6 */ export interface ConsensusParameters { /** Minimum time interval between blocks. */ - minBlockTime: Duration; + minBlockTime: Duration.Type; /** Maximum energy allowed per block. */ - blockEnergyLimit: Energy; + blockEnergyLimit: Energy.Type; } /** @@ -488,9 +419,7 @@ export interface ChainParametersCommon { /** Limit for the number of account creations in a block */ accountCreationLimit: number; /** The chain foundation account */ - foundationAccount: Base58String; - /** The chain foundation account index */ - foundationAccountIndex?: bigint; + foundationAccount: AccountAddress.Type; /** Keys allowed to do level1 updates */ level1Keys: KeysWithThreshold; /** Keys allowed to do root updates */ @@ -501,6 +430,7 @@ export interface ChainParametersCommon { export type ChainParametersV0 = ChainParametersCommon & CooldownParametersV0 & PoolParametersV0 & { + version: 0; /** The election difficulty for consensus lottery */ electionDifficulty: number; /** The election difficulty for consensus lottery */ @@ -514,6 +444,7 @@ export type ChainParametersV1 = ChainParametersCommon & CooldownParametersV1 & TimeParametersV1 & PoolParametersV1 & { + version: 1; /** The election difficulty for consensus lottery */ electionDifficulty: number; /** The election difficulty for consensus lottery */ @@ -530,6 +461,7 @@ export type ChainParametersV2 = ChainParametersCommon & FinalizationCommitteeParameters & TimeoutParameters & ConsensusParameters & { + version: 2; /** The election difficulty for consensus lottery */ rewardParameters: RewardParametersV2; /** Keys allowed to do parameter updates */ @@ -572,12 +504,15 @@ interface AuthorizationsCommon { /** * Used from protocol version 1-3 */ -export type AuthorizationsV0 = AuthorizationsCommon; +export interface AuthorizationsV0 extends AuthorizationsCommon { + version: 0; +} /** * Used from protocol version 4 */ export interface AuthorizationsV1 extends AuthorizationsCommon { + version: 1; cooldownParameters: Authorization; timeParameters: Authorization; } @@ -589,193 +524,25 @@ export interface KeysWithThreshold { threshold: number; } -interface KeysCommon { - rootKeys: KeysWithThreshold; - level1Keys: KeysWithThreshold; -} - -/** - * Used from protocol version 1-3 - */ -export interface KeysV0 extends KeysCommon { - level2Keys: AuthorizationsV0; -} - -/** - * Used from protocol version 4 - */ -export interface KeysV1 extends KeysCommon { - level2Keys: AuthorizationsV1; -} - -export type Keys = KeysV0 | KeysV1; - -export interface UpdateQueueQueue { - effectiveTime: Date; - // TODO Update the type of update to a generic update transaction when - // update types have been added. - /** Information about the actual update. */ - update: unknown; -} - -export interface UpdateQueue { - nextSequenceNumber: bigint; - queue: UpdateQueueQueue[]; -} - -interface UpdateQueuesCommon { - microGTUPerEuro: UpdateQueue; - euroPerEnergy: UpdateQueue; - transactionFeeDistribution: UpdateQueue; - foundationAccount: UpdateQueue; - mintDistribution: UpdateQueue; - protocol: UpdateQueue; - gasRewards: UpdateQueue; - addAnonymityRevoker: UpdateQueue; - addIdentityProvider: UpdateQueue; - rootKeys: UpdateQueue; - level1Keys: UpdateQueue; - level2Keys: UpdateQueue; -} - -/** - * Used from protocol version 1-3 - */ -export interface UpdateQueuesV0 extends UpdateQueuesCommon { - electionDifficulty: UpdateQueue; - bakerStakeThreshold: UpdateQueue; -} - -/** - * Used in protocol version 4 and 5 - */ -export interface UpdateQueuesV1 extends UpdateQueuesCommon { - electionDifficulty: UpdateQueue; - cooldownParameters: UpdateQueue; - timeParameters: UpdateQueue; - poolParameters: UpdateQueue; -} - -/** - * Used from protocol version 6 - */ -export interface UpdateQueuesV2 extends UpdateQueuesV1 { - consensus2TimingParameters: UpdateQueue; -} - -export type UpdateQueues = UpdateQueuesV0 | UpdateQueuesV1 | UpdateQueuesV2; - -interface ProtocolUpdate { - message: string; - specificationUrl: string; - specificationHash: string; - specificationAuxiliaryData: string; -} - -interface UpdatesCommon { - protocolUpdate: ProtocolUpdate | undefined; -} - -/** - * Used from protocol version 1-3 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface UpdatesV0 extends UpdatesCommon { - chainParameters: Omit< - ChainParametersV0, - 'level1Keys' | 'level2Keys' | 'rootKeys' - >; - updateQueues: UpdateQueuesV0; - keys: KeysV0; -} - -/** - * Used in protocol version 4 and 5 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface UpdatesV1 extends UpdatesCommon { - chainParameters: Omit< - ChainParametersV1, - 'level1Keys' | 'level2Keys' | 'rootKeys' - >; - updateQueues: UpdateQueuesV1; - keys: KeysV1; -} - -/** - * Used from protocol version 6 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface UpdatesV2 extends UpdatesCommon { - chainParameters: Omit< - ChainParametersV2, - 'level1Keys' | 'level2Keys' | 'rootKeys' - >; - updateQueues: UpdateQueuesV2; - keys: KeysV1; -} - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type Updates = UpdatesV0 | UpdatesV1 | UpdatesV2; - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -interface BlockSummaryCommon { - protocolVersion?: bigint; - finalizationData: FinalizationData; - transactionSummaries: TransactionSummary[]; -} - -/** - * Used from protocol version 1-3 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface BlockSummaryV0 extends BlockSummaryCommon { - updates: UpdatesV0; -} - -/** - * Used in protocol version 4 and 5 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface BlockSummaryV1 extends BlockSummaryCommon { - updates: UpdatesV1; - protocolVersion: bigint; -} - -/** - * Used from protocol version 6 - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface BlockSummaryV2 extends BlockSummaryCommon { - updates: UpdatesV2; - protocolVersion: bigint; -} - -/** - * @deprecated This is type describing return types from the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type BlockSummary = BlockSummaryV0 | BlockSummaryV1 | BlockSummaryV2; - interface RewardStatusCommon { protocolVersion?: bigint; - totalAmount: Amount; - totalEncryptedAmount: Amount; - bakingRewardAccount: Amount; - finalizationRewardAccount: Amount; - gasAccount: Amount; + totalAmount: CcdAmount.Type; + totalEncryptedAmount: CcdAmount.Type; + bakingRewardAccount: CcdAmount.Type; + finalizationRewardAccount: CcdAmount.Type; + gasAccount: CcdAmount.Type; } -export type RewardStatusV0 = RewardStatusCommon; +export interface RewardStatusV0 extends RewardStatusCommon { + version: 0; +} export interface RewardStatusV1 extends RewardStatusCommon { - foundationTransactionRewards: Amount; + version: 1; + foundationTransactionRewards: CcdAmount.Type; nextPaydayTime: Date; nextPaydayMintRate: MintRate; - totalStakedCapital: Amount; + totalStakedCapital: CcdAmount.Type; protocolVersion: bigint; } @@ -788,13 +555,13 @@ export interface BlockInfoCommon { * Hash of parent block. For the initial genesis block (i.e. not re-genesis) * this will be the hash of the block itself */ - blockParent: HexString; + blockParent: BlockHash.Type; /** Hash of block */ - blockHash: HexString; + blockHash: BlockHash.Type; /** Hash of block state */ blockStateHash: HexString; /** Hash of last finalized block when this block was baked */ - blockLastFinalized: HexString; + blockLastFinalized: BlockHash.Type; /** The absolute height of this (i.e. relative to the initial genesis block) */ blockHeight: bigint; @@ -816,7 +583,7 @@ export interface BlockInfoCommon { /** The total byte size of all transactions in the block */ transactionsSize: bigint; /** The energy cost of the transactions in the block */ - transactionEnergyCost: Energy; + transactionEnergyCost: Energy.Type; /** * The genesis index for the block. This counst the number of protocol updates that have @@ -831,12 +598,14 @@ export interface BlockInfoCommon { /** Block info used for protocol version 1-5 */ export interface BlockInfoV0 extends BlockInfoCommon { + version: 0; /** The slot number in which the block was baked. */ blockSlot: bigint; } /** Block info used from protocol version 6 */ export interface BlockInfoV1 extends BlockInfoCommon { + version: 1; /** The block round */ round: Round; /** The block epoch */ @@ -847,7 +616,7 @@ export interface BlockInfoV1 extends BlockInfoCommon { export type BlockInfo = BlockInfoV0 | BlockInfoV1; export interface CommonBlockInfo { - hash: HexString; + hash: BlockHash.Type; height: bigint; } @@ -868,16 +637,16 @@ export type BlocksAtHeightRequest = /** Common properties for consensus status types used across all protocol versions */ export interface ConsensusStatusCommon { /** Hash of the current best block */ - bestBlock: HexString; + bestBlock: BlockHash.Type; /** Hash of the initial genesis block */ - genesisBlock: HexString; + genesisBlock: BlockHash.Type; /** Hash of the genesis block of the current era, i.e. since the last protocol update. */ - currentEraGenesisBlock: HexString; + currentEraGenesisBlock: BlockHash.Type; /** Hash of the last finalized block */ - lastFinalizedBlock: HexString; + lastFinalizedBlock: BlockHash.Type; /** Current epoch duration, in milliseconds */ - epochDuration: Duration; + epochDuration: Duration.Type; /** Absolute height of the best block */ bestBlockHeight: bigint; /** Absolute height of the last finalized block */ @@ -945,13 +714,14 @@ export interface ConsensusStatusCommon { /** Consensus status used for protocol version 1-5 */ export interface ConsensusStatusV0 extends ConsensusStatusCommon { + version: 0; /** (Current) slot duration in milliseconds */ - slotDuration: Duration; + slotDuration: Duration.Type; } export interface ConcordiumBftStatus { /** Current duration before a round times out, in milliseconds */ - currentTimeoutDuration: Duration; + currentTimeoutDuration: Duration.Type; /** Current round */ currentRound: Round; /** Current epoch */ @@ -965,6 +735,7 @@ export interface ConcordiumBftStatus { /** Consensus status used from protocol version 6 */ export type ConsensusStatusV1 = ConsensusStatusCommon & { + version: 1; concordiumBFTStatus: ConcordiumBftStatus; }; @@ -978,13 +749,13 @@ export interface CryptographicParameters { } export interface NextAccountNonce { - nonce: bigint; + nonce: SequenceNumber.Type; allFinal: boolean; } export interface ReleaseSchedule { timestamp: Date; - amount: Amount; + amount: CcdAmount.Type; } export interface ReleaseScheduleWithTransactions extends ReleaseSchedule { @@ -992,16 +763,16 @@ export interface ReleaseScheduleWithTransactions extends ReleaseSchedule { } export interface AccountReleaseSchedule { - total: Amount; + total: CcdAmount.Type; schedule: ReleaseScheduleWithTransactions[]; } export interface AccountEncryptedAmount { - selfAmount: string; + selfAmount: HexString; startIndex: bigint; - incomingAmounts: string[]; + incomingAmounts: HexString[]; numAggregated?: number; - aggregatedAmount?: string; + aggregatedAmount?: HexString; } export interface VerifyKey { @@ -1009,91 +780,11 @@ export interface VerifyKey { verifyKey: HexString; } -export interface KeyPair { - signKey: HexString; - verifyKey: HexString; -} - export interface CredentialPublicKeys { keys: Record; threshold: number; } -export interface CredentialKeys { - keys: Record; - threshold: number; -} - -export interface AccountKeys { - keys: Record; - threshold: number; -} - -export type SimpleAccountKeys = Record>; - -export interface WithAccountKeys { - accountKeys: AccountKeys; -} - -export interface WalletExportFormat { - type: string; - v: number; - environment: string; - value: { - accountKeys: AccountKeys; - address: Base58String; - credentials: Record; - }; -} - -/** - * Parses a wallet export file into a WalletExportFormat. The wallet export - * file is exported from a concordium wallet. - */ -export function parseWallet(walletString: JsonString): WalletExportFormat { - const wallet = JSON.parse(walletString); - console.log(typeof wallet.type); - if (typeof wallet.type !== 'string') { - throw Error( - 'Expected field "type" to be of type "string" but was of type "' + - typeof wallet.type + - '"' - ); - } - if (typeof wallet.v !== 'number') { - throw Error( - 'Expected field "v" to be of type "number" but was of type "' + - typeof wallet.v + - '"' - ); - } - if (typeof wallet.environment !== 'string') { - throw Error( - 'Expected field "environment" to be of type "string" but was of type "' + - typeof wallet.environment + - '"' - ); - } - if (typeof wallet.value.address !== 'string') { - throw Error( - 'Expected field "value.address" to be of type "string" but was of type "' + - typeof wallet.value.address + - '"' - ); - } - if (wallet.value.accountKeys === undefined) { - throw Error( - 'Expected field "value.accountKeys" to be defined, but was not' - ); - } - if (wallet.value.credentials === undefined) { - throw Error( - 'Expected field "value.credentials" to be defined, but was not' - ); - } - return wallet; -} - export interface ChainArData { encIdCredPubShare: string; } @@ -1143,59 +834,25 @@ export interface InitialAccountCredential { export enum StakePendingChangeType { ReduceStake = 'ReduceStake', - RemoveStakeV0 = 'RemoveBaker', - RemoveStakeV1 = 'RemoveStake', -} - -interface StakePendingChangeV0Common { - epoch: bigint; + RemoveStake = 'RemoveStake', } -interface StakePendingChangeV1Common { +interface StakePendingChangeCommon { effectiveTime: Date; } -interface ReduceStakePendingChangeCommon { - newStake: bigint; -} - -export interface ReduceStakePendingChangeV0 - extends ReduceStakePendingChangeCommon, - StakePendingChangeV0Common { +export interface ReduceStakePendingChange extends StakePendingChangeCommon { change: StakePendingChangeType.ReduceStake; + newStake: bigint; } -export interface ReduceStakePendingChangeV1 - extends ReduceStakePendingChangeCommon, - StakePendingChangeV1Common { - change: StakePendingChangeType.ReduceStake; -} - -export type ReduceStakePendingChange = - | ReduceStakePendingChangeV0 - | ReduceStakePendingChangeV1; - -export interface RemovalPendingChangeV0 extends StakePendingChangeV0Common { - change: StakePendingChangeType.RemoveStakeV0; -} - -export interface RemovalPendingChangeV1 extends StakePendingChangeV1Common { - change: StakePendingChangeType.RemoveStakeV1; +export interface RemovalPendingChange extends StakePendingChangeCommon { + change: StakePendingChangeType.RemoveStake; } -export type RemovalPendingChange = - | RemovalPendingChangeV0 - | RemovalPendingChangeV1; - -export type StakePendingChangeV0 = - | ReduceStakePendingChangeV0 - | RemovalPendingChangeV0; - -export type StakePendingChangeV1 = - | ReduceStakePendingChangeV1 - | RemovalPendingChangeV1; - -export type StakePendingChange = StakePendingChangeV0 | StakePendingChangeV1; +export type StakePendingChange = + | ReduceStakePendingChange + | RemovalPendingChange; export enum OpenStatus { OpenForAll = 0, @@ -1212,10 +869,8 @@ export enum OpenStatusText { ClosedForAll = 'closedForAll', } -export type Amount = bigint; export type BakerId = bigint; -// TODO: Change this to bigint when GrpcV1 is removed. -export type DelegatorId = number; +export type DelegatorId = bigint; export interface BakerPoolInfo { openStatus: OpenStatusText; @@ -1232,11 +887,11 @@ export interface CommissionRates { export interface CurrentPaydayBakerPoolStatus { blocksBaked: bigint; finalizationLive: boolean; - transactionFeesEarned: Amount; - effectiveStake: Amount; + transactionFeesEarned: CcdAmount.Type; + effectiveStake: CcdAmount.Type; lotteryPower: number; - bakerEquityCapital: Amount; - delegatedCapital: Amount; + bakerEquityCapital: CcdAmount.Type; + delegatedCapital: CcdAmount.Type; commissionRates: CommissionRates; } @@ -1254,7 +909,7 @@ type BakerPoolPendingChangeWrapper< }; export interface BakerPoolPendingChangeReduceBakerCapitalDetails { - bakerEquityCapital: Amount; + bakerEquityCapital: CcdAmount.Type; effectiveTime: Date; } @@ -1295,14 +950,14 @@ type PoolStatusWrapper = S & { export interface BakerPoolStatusDetails { bakerId: BakerId; - bakerAddress: Base58String; - bakerEquityCapital: Amount; - delegatedCapital: Amount; - delegatedCapitalCap: Amount; + bakerAddress: AccountAddress.Type; + bakerEquityCapital: CcdAmount.Type; + delegatedCapital: CcdAmount.Type; + delegatedCapitalCap: CcdAmount.Type; poolInfo: BakerPoolInfo; bakerStakePendingChange: BakerPoolPendingChange; currentPaydayStatus: CurrentPaydayBakerPoolStatus | null; - allPoolTotalCapital: Amount; + allPoolTotalCapital: CcdAmount.Type; } export type BakerPoolStatus = PoolStatusWrapper< @@ -1311,11 +966,11 @@ export type BakerPoolStatus = PoolStatusWrapper< >; export interface PassiveDelegationStatusDetails { - delegatedCapital: Amount; + delegatedCapital: CcdAmount.Type; commissionRates: CommissionRates; - currentPaydayTransactionFeesEarned: Amount; - currentPaydayDelegatedCapital: Amount; - allPoolTotalCapital: Amount; + currentPaydayTransactionFeesEarned: CcdAmount.Type; + currentPaydayDelegatedCapital: CcdAmount.Type; + allPoolTotalCapital: CcdAmount.Type; } export type PassiveDelegationStatus = PoolStatusWrapper< @@ -1355,13 +1010,18 @@ interface AccountBakerDetailsCommon { bakerAggregationVerifyKey: string; bakerElectionVerifyKey: string; bakerSignatureVerifyKey: string; - stakedAmount: bigint; + stakedAmount: CcdAmount.Type; pendingChange?: StakePendingChange; } -export type AccountBakerDetailsV0 = AccountBakerDetailsCommon; +/** Protocol version 1-3. */ +export interface AccountBakerDetailsV0 extends AccountBakerDetailsCommon { + version: 0; +} +/** Protocol version 4 and later. */ export interface AccountBakerDetailsV1 extends AccountBakerDetailsCommon { + version: 1; bakerPoolInfo: BakerPoolInfo; } @@ -1369,46 +1029,45 @@ export type AccountBakerDetails = AccountBakerDetailsV0 | AccountBakerDetailsV1; export interface AccountDelegationDetails { restakeEarnings: boolean; - stakedAmount: bigint; + stakedAmount: CcdAmount.Type; delegationTarget: DelegationTarget; - pendingChange?: StakePendingChangeV1; + pendingChange?: StakePendingChange; } export type AccountCredential = Versioned< InitialAccountCredential | NormalAccountCredential >; +export enum AccountInfoType { + Simple = 'simple', + Baker = 'baker', + Delegator = 'delegator', +} + interface AccountInfoCommon { - accountAddress: Base58String; - accountNonce: bigint; - accountAmount: bigint; + accountAddress: AccountAddress.Type; + accountNonce: SequenceNumber.Type; + accountAmount: CcdAmount.Type; accountIndex: bigint; - accountThreshold: number; - accountEncryptionKey: string; accountEncryptedAmount: AccountEncryptedAmount; - accountReleaseSchedule: AccountReleaseSchedule; - accountCredentials: Record; } -export type AccountInfoSimple = AccountInfoCommon; - -export interface AccountInfoBakerV0 extends AccountInfoCommon { - accountBaker: AccountBakerDetailsV0; +export interface AccountInfoSimple extends AccountInfoCommon { + type: AccountInfoType.Simple; } -/** Protocol version 4 and later. */ -export interface AccountInfoBakerV1 extends AccountInfoCommon { - accountBaker: AccountBakerDetailsV1; +export interface AccountInfoBaker extends AccountInfoCommon { + type: AccountInfoType.Baker; + accountBaker: AccountBakerDetails; } -export type AccountInfoBaker = AccountInfoBakerV0 | AccountInfoBakerV1; - /** Protocol version 4 and later. */ export interface AccountInfoDelegator extends AccountInfoCommon { + type: AccountInfoType.Delegator; accountDelegation: AccountDelegationDetails; } @@ -1437,8 +1096,8 @@ export interface ArInfo { } interface DelegatorInfoCommon { - account: Base58String; - stake: Amount; + account: AccountAddress.Type; + stake: CcdAmount.Type; } export interface DelegatorInfo extends DelegatorInfoCommon { pendingChange?: StakePendingChange; @@ -1447,13 +1106,13 @@ export interface DelegatorInfo extends DelegatorInfoCommon { export type DelegatorRewardPeriodInfo = DelegatorInfoCommon; export interface Branch { - blockHash: HexString; + blockHash: BlockHash.Type; children: Branch[]; } export interface BakerElectionInfo { baker: BakerId; - account: Base58String; + account: AccountAddress.Type; lotteryPower: number; } @@ -1465,11 +1124,14 @@ export interface ElectionInfoCommon { /** Election info used for protocol version 1-5 */ export interface ElectionInfoV0 extends ElectionInfoCommon { + version: 0; electionDifficulty: number; } /** Election info used from protocol version 6 */ -export type ElectionInfoV1 = ElectionInfoCommon; +export interface ElectionInfoV1 extends ElectionInfoCommon { + version: 1; +} /** * Union of different versions of election info across all protocol versions. @@ -1514,7 +1176,7 @@ export interface BlockFinalizationSummary_Record { } export interface FinalizationSummary { - block: HexString; + block: BlockHash.Type; index: bigint; delay: bigint; finalizers: FinalizationSummaryParty[]; @@ -1572,70 +1234,70 @@ export interface DeployModulePayload { version?: number; /** Wasm module to be deployed */ - source: Buffer; + source: Uint8Array; } export interface VersionedModuleSource { version: 0 | 1; - source: Buffer; + source: Uint8Array; } export interface InitContractPayload { - /** µCCD amount to transfer */ - amount: CcdAmount; + /** CCD amount to transfer */ + amount: CcdAmount.Type; /** Hash of the module on chain */ - moduleRef: ModuleReference; + moduleRef: ModuleReference.Type; /** Name of the contract */ - initName: string; + initName: ContractName.Type; /** Parameters for the init function */ - param: Buffer; + param: Parameter.Type; /** The amount of energy that can be used for contract execution. The base energy amount for transaction verification will be added to this cost.*/ - maxContractExecutionEnergy: bigint; + maxContractExecutionEnergy: Energy.Type; } export interface UpdateContractPayload { - /** µCCD amount to transfer */ - amount: CcdAmount; + /** CCD amount to transfer */ + amount: CcdAmount.Type; /** Address of contract instance consisting of an index and a subindex */ - address: ContractAddress; + address: ContractAddress.Type; /** Name of receive function including . prefix */ - receiveName: string; + receiveName: ReceiveName.Type; /** Parameters for the update function */ - message: Buffer; + message: Parameter.Type; /** The amount of energy that can be used for contract execution. The base energy amount for transaction verification will be added to this cost.*/ - maxContractExecutionEnergy: bigint; + maxContractExecutionEnergy: Energy.Type; } export interface AccountTransactionHeader { /** account address that is source of this transaction */ - sender: AccountAddress; + sender: AccountAddress.Type; /** * the nonce for the transaction, usually acquired by * getting the next account nonce from the node */ - nonce: bigint; + nonce: SequenceNumber.Type; /** expiration of the transaction */ - expiry: TransactionExpiry; + expiry: TransactionExpiry.Type; } export interface SimpleTransferPayload { - /** µCCD amount to transfer */ - amount: CcdAmount; + /** CCD amount to transfer */ + amount: CcdAmount.Type; /** the recipient of the transfer */ - toAddress: AccountAddress; + toAddress: AccountAddress.Type; } export interface SimpleTransferWithMemoPayload extends SimpleTransferPayload { @@ -1700,7 +1362,7 @@ export type GenerateBakerKeysOutput = PublicBakerKeys & export interface ConfigureBakerPayload { /* stake to bake. if set to 0, this removes the account as a baker */ - stake?: CcdAmount; + stake?: CcdAmount.Type; /* should earnings from baking be added to staked amount */ restakeEarnings?: boolean; openForDelegation?: OpenStatus; @@ -1713,7 +1375,7 @@ export interface ConfigureBakerPayload { export interface ConfigureDelegationPayload { /* stake to delegate. if set to 0, this removes the account as a delegator */ - stake?: CcdAmount; + stake?: CcdAmount.Type; /* should earnings from delegation be added to staked amount */ restakeEarnings?: boolean; /* determines if the account should use passive delegation, or which specific baker to delegate to */ @@ -1737,86 +1399,24 @@ export interface AccountTransaction { payload: AccountTransactionPayload; } -/** - * @deprecated This type was for serialization code, which has been moved to rust-bindings - */ -export enum ParameterType { - /** Nothing. */ - Unit = 0, - /** Boolean (`true` or `false`). */ - Bool, - /** Unsigned 8-bit integer. */ - U8, - /** Unsigned 16-bit integer. */ - U16, - /** Unsigned 32-bit integer. */ - U32, - /** Unsigned 64-bit integer. */ - U64, - /** Signed 8-bit integer. */ - I8, - /** Signed 16-bit integer. */ - I16, - /** Signed 32-bit integer. */ - I32, - /** Signed 64-bit integer. */ - I64, - /** Token amount in microCCD (10^-6 CCD). */ - Amount, - /** Sender account address. */ - AccountAddress, - /** Address of the contract instance consisting of an index and a subindex. */ - ContractAddress, - /** Unsigned 64-bit integer storing milliseconds since UNIX epoch and representing a timestamp. */ - Timestamp, - /** Unsigned 64-bit integer storing milliseconds and representing a duration. */ - Duration, - /** Tuple. */ - Pair, - /** Variable size list. */ - List, - /** Unordered collection of unique elements. */ - Set, - /** Unordered map from keys to values. */ - Map, - /** Fixed size array. */ - Array, - /** Struct. */ - Struct, - /** Enum. */ - Enum, - /** List of bytes representing a string. */ - String, - /** Unsigned 128-bit integer. */ - U128, - /** Signed 128-bit integer. */ - I128, - /** Name of the contract. */ - ContractName, - /** Receive function name. */ - ReceiveName, - /** LEB128 encoding of an unsigned integer */ - ULeb128, - /** LEB128 encoding of a signed integer */ - ILeb128, - /** Variable size list of bytes */ - ByteList, - /** Fixed size list of bytes */ - ByteArray, -} - export interface InstanceInfoCommon { + /** Version of the smart contract module. */ version: number; - amount: CcdAmount; - sourceModule: ModuleReference; - owner: AccountAddress; - methods: string[]; - name: string; + /** Total balance of CCD hold by this instance. */ + amount: CcdAmount.Type; + /** Module reference of the current module being used by this instance. */ + sourceModule: ModuleReference.Type; + /** Account used to instantiate this smart contract instance. */ + owner: AccountAddress.Type; + /** List of receive functions currently exposed by this smart contract. These are of the form '.'. */ + methods: ReceiveName.Type[]; + /** Name of the smart contract. This is of the form 'init_'. */ + name: InitName.Type; } export interface InstanceInfoV0 extends InstanceInfoCommon { version: 0; - model: Buffer; + model: ArrayBuffer; } export interface InstanceInfoV1 extends InstanceInfoCommon { @@ -1825,12 +1425,6 @@ export interface InstanceInfoV1 extends InstanceInfoCommon { export type InstanceInfo = InstanceInfoV0 | InstanceInfoV1; -export const isInstanceInfoV1 = (info: InstanceInfo): info is InstanceInfoV1 => - info.version === 1; - -export const isInstanceInfoV0 = (info: InstanceInfo): info is InstanceInfoV0 => - info.version === undefined || info.version === 0; - export type CredentialSignature = Record; export type AccountTransactionSignature = Record; @@ -1862,96 +1456,39 @@ export interface InstanceStateKVPair { } export interface ContractContext { - invoker?: ContractAddress | AccountAddress; - contract: ContractAddress; - amount?: CcdAmount; - method: string; - parameter?: Buffer; - energy?: bigint; -} - -/** - * Format of invoker expected by the node for the invokeContract entrypoint. - * @deprecated This is type used by the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type Invoker = - | { - type: 'AddressContract'; - address: { - index: DigitString; - subindex: DigitString; - }; - } - | { - type: 'AddressAccount'; - address: Base58String; - } - | null; - -/** - * Takes an accountAddress or ContractAddress and transforms it into the specific format used for - * InvokeContract's invoker parameter. - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function buildInvoker( - invoker?: AccountAddress | ContractAddress -): Invoker { - if (!invoker) { - return null; - } else if ((invoker as AccountAddress).address) { - return { - type: 'AddressAccount', - address: (invoker as AccountAddress).address, - }; - } else if ((invoker as ContractAddress).index !== undefined) { - const invokerContract = invoker as ContractAddress; - return { - type: 'AddressContract', - address: { - subindex: invokerContract.subindex.toString(), - index: invokerContract.index.toString(), - }, - }; - } else { - throw new Error('Unexpected input to build invoker'); - } + invoker?: ContractAddress.Type | AccountAddress.Type; + contract: ContractAddress.Type; + amount?: CcdAmount.Type; + method: ReceiveName.Type; + parameter?: Parameter.Type; + energy?: Energy.Type; } export interface InvokeContractSuccessResult { tag: 'success'; - usedEnergy: bigint; + usedEnergy: Energy.Type; events: ContractTraceEvent[]; - returnValue?: string; + returnValue?: ReturnValue.Type; } export interface InvokeContractFailedResult { tag: 'failure'; - usedEnergy: bigint; + usedEnergy: Energy.Type; reason: RejectReason; -} - -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface InvokeContractFailedResultV1 { - tag: 'failure'; - usedEnergy: bigint; - reason: RejectReasonV1; + /** + * Return value from smart contract call, used to provide error messages. + * Is only defined when smart contract instance is a V1 smart contract and + * the transaction was rejected by the smart contract logic i.e. `reason.tag === "RejectedReceive"`. + */ + returnValue?: ReturnValue.Type; } export type InvokeContractResult = | InvokeContractSuccessResult | InvokeContractFailedResult; -/** - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type InvokeContractResultV1 = - | InvokeContractSuccessResult - | InvokeContractFailedResultV1; - export interface CredentialDeploymentDetails { - expiry: TransactionExpiry; + expiry: TransactionExpiry.Type; unsignedCdi: UnsignedCredentialDeploymentInformation; } @@ -1997,7 +1534,7 @@ export interface CredentialDeploymentInfo extends CredentialDeploymentValues { } export interface SignedCredentialDeploymentDetails { - expiry: TransactionExpiry; + expiry: TransactionExpiry.Type; cdi: CredentialDeploymentInfo; } @@ -2055,7 +1592,7 @@ export interface IdObjectRequestV1 { export interface IdRecoveryRequest { idCredPub: string; - timestamp: Timestamp; + timestamp: Timestamp.Type; proof: string; } @@ -2078,6 +1615,7 @@ export type SmartContractTypeValues = | { [key: string]: SmartContractTypeValues } | SmartContractTypeValues[] | number + | bigint | string | boolean; @@ -2221,7 +1759,7 @@ export interface BakerRewardPeriodInfo { * The effective stake of the baker for the consensus protocol. * The returned amount accounts for delegation, capital bounds and leverage bounds. */ - effectiveStake: Amount; + effectiveStake: CcdAmount.Type; /** * The effective commission rate for the baker that applies for the reward period. */ @@ -2229,11 +1767,11 @@ export interface BakerRewardPeriodInfo { /** * The amount staked by the baker itself. */ - equityCapital: Amount; + equityCapital: CcdAmount.Type; /** * The total amount of capital delegated to this baker pool. */ - delegatedCapital: Amount; + delegatedCapital: CcdAmount.Type; /** * Whether the baker is a finalizer or not. */ diff --git a/packages/sdk/src/types/AccountAddress.ts b/packages/sdk/src/types/AccountAddress.ts new file mode 100644 index 000000000..3824d2fd7 --- /dev/null +++ b/packages/sdk/src/types/AccountAddress.ts @@ -0,0 +1,232 @@ +import bs58check from 'bs58check'; +import { Buffer } from 'buffer/index.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; +import { Base58String } from '../types.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.AccountAddress; +export type Serializable = Base58String; + +/** + * Representation of an account address, which enforces that it: + * - Is a valid base58 string with version byte of 1. + * - The base58 string is a length of 50 (encoding exactly 32 bytes). + */ +class AccountAddress { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The account address represented in base58check. */ + public readonly address: string, + /** The account address represented in bytes. */ + public readonly decodedAddress: Uint8Array + ) {} +} + +/** + * Representation of an account address, which enforces that it: + * - Is a valid base58 string with version byte of 1. + * - The base58 string is a length of 50 (encoding exactly 32 bytes). + */ +export type Type = AccountAddress; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is AccountAddress { + return value instanceof AccountAddress; +} + +/** + * Construct an AccountAddress from a buffer of bytes. + * @param {ArrayBuffer} buffer Buffer containing exactly 32 bytes representing the address of the account. + * @throws If the provided buffer does not contain exactly 32 bytes. + * @returns {AccountAddress} The AccountAddress. + */ +export function fromBuffer(buffer: ArrayBuffer): AccountAddress { + if (buffer.byteLength !== 32) { + throw new Error( + `The provided buffer '${buffer}' is invalid as its length was not 32` + ); + } + + const address = bs58check.encode( + Buffer.concat([Uint8Array.of(1), new Uint8Array(buffer)]) + ); + return new AccountAddress(address, new Uint8Array(buffer)); +} + +/** + * Construct an AccountAddress from a base58check string. + * @param {string} address String of base58check encoded account address, must use a byte version of 1. + * @throws If the provided string is not: exactly 50 characters, a valid base58check encoding using version byte 1. + * @returns {AccountAddress} The AccountAddress. + */ +export function fromBase58(address: string): AccountAddress { + if (address.length !== 50) { + throw new Error( + `The provided address '${address}' is invalid as its length was not 50` + ); + } + const buffer = bs58check.decode(address); + const versionByte = buffer.at(0); + if (versionByte !== 1) { + throw new Error( + `The provided address '${address}' does not use version byte with value of 1` + ); + } + const decodedAddress = buffer.subarray(1, 33); // Ensure only the 32 bytes for the address is kept. + return new AccountAddress(address, new Uint8Array(decodedAddress)); +} + +/** + * Get the bytes corresponding to the account address. + * @param {AccountAddress} accountAddress The account address. + */ +export function toBuffer(accountAddress: AccountAddress): Uint8Array { + return accountAddress.decodedAddress; +} +/** + * Get a base58check string of the account address. + * @param {AccountAddress} accountAddress The account address. + */ +export function toBase58(accountAddress: AccountAddress): string { + return accountAddress.address; +} + +/** Type used when encoding an account address in the JSON format used when serializing using a smart contract schema type. */ +export type SchemaValue = string; + +/** + * Get account address in the JSON format used when serializing using a smart contract schema type. + * @param {AccountAddress} accountAddress The account address. + * @returns {SchemaValue} The schema JSON representation. + */ +export function toSchemaValue(accountAddress: AccountAddress): SchemaValue { + return accountAddress.address; +} + +/** + * Convert to account address from JSON format used when serializing using a smart contract schema type. + * @param {SchemaValue} accountAddress The account address in schema JSON format. + * @returns {AccountAddress} The account address. + */ +export function fromSchemaValue(accountAddress: SchemaValue): AccountAddress { + return fromBase58(accountAddress); +} + +const addressByteLength = 32; +const aliasBytesLength = 3; +const commonBytesLength = addressByteLength - aliasBytesLength; +const maxCount = 16777215; // 2^(8 * 3) - 1 + +/** + * Given two accountAddresses, return whether they are aliases. + * @param address an AccountAddress + * @param alias another AccountAddress + * @returns boolean that indicates whether address and alias are aliases + */ +export function isAlias( + address: AccountAddress, + alias: AccountAddress +): boolean { + return ( + 0 === + Buffer.from(address.decodedAddress).compare( + alias.decodedAddress, + 0, + commonBytesLength, + 0, + commonBytesLength + ) + ); +} + +/** + * Given an AccountAddress and a counter, returns an alias for the address. + * @param address the account address for which the function should get an alias for + * @param counter number s.t. 0 <= counter < 2^24, decides which alias is returned. + * If a counter outside this scope is given, then the function will throw an exception + * @returns an AccountAddress, which is an alias to the given address + */ +export function getAlias( + address: AccountAddress, + counter: number +): AccountAddress { + if (counter < 0 || counter > maxCount) { + throw new Error( + `An invalid counter value was given: ${counter}. The value has to satisfy that 0 <= counter < 2^24` + ); + } + const commonBytes = address.decodedAddress.slice(0, commonBytesLength); + const aliasBytes = Buffer.alloc(aliasBytesLength); + aliasBytes.writeUIntBE(counter, 0, aliasBytesLength); + return fromBuffer(Buffer.concat([commonBytes, aliasBytes])); +} + +/** + * Convert an account address from its protobuf encoding. + * @param {Proto.AccountAddress} accountAddress The account address in protobuf. + * @returns {AccountAddress} The account address + */ +export function fromProto( + accountAddress: Proto.AccountAddress +): AccountAddress { + return fromBuffer(accountAddress.value); +} + +/** + * Convert an account address into its protobuf encoding. + * @param {AccountAddress} accountAddress The account address. + * @returns {Proto.AccountAddress} The protobuf encoding. + */ +export function toProto(accountAddress: AccountAddress): Proto.AccountAddress { + return { + value: accountAddress.decodedAddress, + }; +} + +/** + * Check if two account addresses are the exact same. This will not consider different aliases for the same account as equal. + * @param {AccountAddress} left + * @param {AccountAddress} right + * @returns {boolean} True if they are equal. + */ +export function equals(left: AccountAddress, right: AccountAddress): boolean { + return left.address === right.address; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: AccountAddress): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toBase58(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromBase58 +); diff --git a/packages/sdk/src/types/BlockHash.ts b/packages/sdk/src/types/BlockHash.ts new file mode 100644 index 000000000..83f47c3cd --- /dev/null +++ b/packages/sdk/src/types/BlockHash.ts @@ -0,0 +1,163 @@ +import { Buffer } from 'buffer/index.js'; +import type { HexString } from '../types.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The number of bytes used to represent a block hash. + */ +const BLOCK_HASH_BYTE_LENGTH = 32; +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.BlockHash; +export type Serializable = HexString; + +/** + * Represents a hash of a block in the chain. + */ +class BlockHash { + private typedJsonType = JSON_DISCRIMINATOR; + + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal buffer of bytes representing the hash. */ + public readonly buffer: Uint8Array + ) {} +} + +/** + * Represents a hash of a block in the chain. + */ +export type Type = BlockHash; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is BlockHash { + return value instanceof BlockHash; +} + +/** + * Create a BlockHash from a buffer of 32 bytes. + * @param {ArrayBuffer} buffer Buffer containing 32 bytes for the hash. + * @throws If the provided buffer does not contain exactly 32 bytes. + * @returns {BlockHash} + */ +export function fromBuffer(buffer: ArrayBuffer): BlockHash { + if (buffer.byteLength !== BLOCK_HASH_BYTE_LENGTH) { + throw new Error( + `Invalid transaction hash provided: Expected a buffer containing 32 bytes, instead got '${Buffer.from( + buffer + ).toString('hex')}'.` + ); + } + return new BlockHash(new Uint8Array(buffer)); +} + +/** + * Create a BlockHash from a hex string. + * @param {HexString} hex Hex encoding of block hash. + * @throws If the provided hex encoding does not correspond to a buffer of exactly 32 bytes. + * @returns {BlockHash} + */ +export function fromHexString(hex: HexString): BlockHash { + return fromBuffer(Buffer.from(hex, 'hex')); +} + +/** + * Hex encode a BlockHash. + * @param {BlockHash} hash The block hash to encode. + * @returns {HexString} String containing the hex encoding. + */ +export function toHexString(hash: BlockHash): HexString { + return Buffer.from(hash.buffer).toString('hex'); +} + +/** + * Get byte representation of a BlockHash. + * @param {BlockHash} hash The block hash. + * @returns {ArrayBuffer} Hash represented as bytes. + */ +export function toBuffer(hash: BlockHash): Uint8Array { + return hash.buffer; +} + +/** + * Convert a block hash from its protobuf encoding. + * @param {Proto.BlockHash} hash The protobuf encoding. + * @returns {BlockHash} + */ +export function fromProto(hash: Proto.BlockHash): BlockHash { + return fromBuffer(hash.value); +} + +/** + * Convert a block hash into its protobuf encoding. + * @param {BlockHash} hash The block hash. + * @returns {Proto.BlockHash} The protobuf encoding. + */ +export function toProto(hash: BlockHash): Proto.BlockHash { + return { + value: hash.buffer, + }; +} + +/** + * Construct a 'given' block hash input from a block hash. + * @param {BlockHash} blockHash The given block hash. + * @returns {Proto.BlockHashInput} The given block hash input. + */ +export function toBlockHashInput(blockHash: BlockHash): Proto.BlockHashInput { + return { + blockHashInput: { oneofKind: 'given', given: toProto(blockHash) }, + }; +} + +/** + * Check if two transaction hashes are the same. + * @param {BlockHash} left + * @param {BlockHash} right + * @returns {boolean} True if they are equal. + */ +export function equals(left: BlockHash, right: BlockHash): boolean { + for (let i = 0; i < BLOCK_HASH_BYTE_LENGTH; i++) { + if (left.buffer.at(i) !== right.buffer.at(i)) { + return false; + } + } + return true; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: BlockHash): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/common/src/types/BlockSpecialEvents.ts b/packages/sdk/src/types/BlockSpecialEvents.ts similarity index 71% rename from packages/common/src/types/BlockSpecialEvents.ts rename to packages/sdk/src/types/BlockSpecialEvents.ts index 8d62007c3..6b259da8b 100644 --- a/packages/common/src/types/BlockSpecialEvents.ts +++ b/packages/sdk/src/types/BlockSpecialEvents.ts @@ -1,4 +1,6 @@ -import { Amount, BakerId, Base58String } from '../types'; +import type { BakerId } from '../types.js'; +import * as CcdAmount from './CcdAmount.js'; +import * as AccountAddress from './AccountAddress.js'; export type BlockSpecialEvent = | BlockSpecialEventBakingRewards @@ -15,19 +17,19 @@ export interface BlockSpecialEventBakingRewards { // The amount awarded to each baker. bakingRewards: BlockSpecialEventAccountAmount[]; // The remaining balance of the baker reward account. - remainder: Amount; + remainder: CcdAmount.Type; } export interface BlockSpecialEventMint { tag: 'mint'; // The amount allocated to the banking reward account. - mintBakingReward: Amount; + mintBakingReward: CcdAmount.Type; // The amount allocated to the finalization reward account. - mintFinalizationReward: Amount; + mintFinalizationReward: CcdAmount.Type; // The amount allocated as the platform development charge. - mintPlatformDevelopmentCharge: Amount; + mintPlatformDevelopmentCharge: CcdAmount.Type; // The account to which the platform development charge is paid. - foundationAccount: Base58String; + foundationAccount: AccountAddress.Type; } export interface BlockSpecialEventFinalizationRewards { @@ -35,61 +37,61 @@ export interface BlockSpecialEventFinalizationRewards { // The amount awarded to each finalizer. finalizationRewards?: BlockSpecialEventAccountAmount[]; // The remaining balance of the finalization reward account. - remainder?: Amount; + remainder?: CcdAmount.Type; } export interface BlockSpecialEventBlockReward { tag: 'blockReward'; // The total fees paid for transactions in the block. - transactionFees: Amount; + transactionFees: CcdAmount.Type; // The old balance of the GAS account. - oldGasAccount: Amount; + oldGasAccount: CcdAmount.Type; // The new balance of the GAS account. - newGasAccount: Amount; + newGasAccount: CcdAmount.Type; // The amount awarded to the baker. - bakerReward: Amount; + bakerReward: CcdAmount.Type; // The amount awarded to the foundation. - foundationCharge: Amount; + foundationCharge: CcdAmount.Type; // The baker of the block, who receives the award. - baker: Base58String; + baker: AccountAddress.Type; // The foundation account. - foundationAccount: Base58String; + foundationAccount: AccountAddress.Type; } export interface BlockSpecialEventPaydayFoundationReward { tag: 'paydayFoundationReward'; // The account that got rewarded. - foundationAccount: Base58String; + foundationAccount: AccountAddress.Type; // The transaction fee reward at payday to the account. - developmentCharge: Amount; + developmentCharge: CcdAmount.Type; } export interface BlockSpecialEventPaydayAccountReward { tag: 'paydayAccountReward'; // The account that got rewarded. - account: Base58String; + account: AccountAddress.Type; // The transaction fee reward at payday to the account. - transactionFees: Amount; + transactionFees: CcdAmount.Type; // The baking reward at payday to the account. - bakerReward: Amount; + bakerReward: CcdAmount.Type; // The finalization reward at payday to the account. - finalizationReward: Amount; + finalizationReward: CcdAmount.Type; } export interface BlockSpecialEventBlockAccrueReward { tag: 'blockAccrueReward'; // The total fees paid for transactions in the block. - transactionFees: Amount; + transactionFees: CcdAmount.Type; // The old balance of the GAS account. - oldGasAccount: Amount; + oldGasAccount: CcdAmount.Type; // The new balance of the GAS account. - newGasAccount: Amount; + newGasAccount: CcdAmount.Type; // The amount awarded to the baker. - bakerReward: Amount; + bakerReward: CcdAmount.Type; // The amount awarded to the passive delegators. - passiveReward: Amount; + passiveReward: CcdAmount.Type; // The amount awarded to the foundation. - foundationCharge: Amount; + foundationCharge: CcdAmount.Type; // The baker of the block, who will receive the award. baker: BakerId; } @@ -99,33 +101,33 @@ export interface BlockSpecialEventPaydayPoolReward { // The pool owner (passive delegators when not present). poolOwner?: BakerId; // Accrued transaction fees for pool. - transactionFees: Amount; + transactionFees: CcdAmount.Type; // Accrued baking rewards for pool. - bakerReward: Amount; + bakerReward: CcdAmount.Type; // Accrued finalization rewards for pool. - finalizationReward: Amount; + finalizationReward: CcdAmount.Type; } export interface BlockSpecialEventAccountAmount { // The key type - account: Base58String; + account: AccountAddress.Type; // The value type - amount: Amount; + amount: CcdAmount.Type; } /** - * Gets a list of {@link Base58String} account addresses affected the {@link BlockSpecialEvent}. + * Gets a list of {@link AccountAddress.Type} account addresses affected the {@link BlockSpecialEvent}. * * @param {BlockSpecialEvent} event - The block special event to check. * - * @returns {Base58String[]} List of account addresses affected by the event. + * @returns {AccountAddress.Type[]} List of account addresses affected by the event. */ export function specialEventAffectedAccounts( event: Exclude< BlockSpecialEvent, BlockSpecialEventBlockAccrueReward | BlockSpecialEventPaydayPoolReward > -): Base58String[]; +): AccountAddress.Type[]; export function specialEventAffectedAccounts( event: | BlockSpecialEventBlockAccrueReward @@ -133,10 +135,10 @@ export function specialEventAffectedAccounts( ): never[]; export function specialEventAffectedAccounts( event: BlockSpecialEvent -): Base58String[]; +): AccountAddress.Type[]; export function specialEventAffectedAccounts( event: BlockSpecialEvent -): Base58String[] { +): AccountAddress.Type[] { switch (event.tag) { case 'bakingRewards': return event.bakingRewards.map((br) => br.account); @@ -148,7 +150,7 @@ export function specialEventAffectedAccounts( case 'paydayAccountReward': return [event.account]; case 'blockReward': { - if (event.baker === event.foundationAccount) { + if (AccountAddress.equals(event.baker, event.foundationAccount)) { return [event.baker]; } return [event.baker, event.foundationAccount]; diff --git a/packages/sdk/src/types/CcdAmount.ts b/packages/sdk/src/types/CcdAmount.ts new file mode 100644 index 000000000..5c6373077 --- /dev/null +++ b/packages/sdk/src/types/CcdAmount.ts @@ -0,0 +1,247 @@ +import { Big, BigSource } from 'big.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +const MICRO_CCD_PER_CCD = 1_000_000; +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.CcdAmount; +export type Serializable = string; + +/** + * Representation of a CCD amount. + * The base unit of CCD is micro CCD, which is the representation + * used on chain. + */ +class CcdAmount { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Internal representation of Ccd amound in micro Ccd. */ + public readonly microCcdAmount: bigint + ) {} + + public toJSON(): string { + return this.microCcdAmount.toString(); + } +} + +/** + * Representation of a CCD amount. + * The base unit of CCD is micro CCD, which is the representation + * used on chain. + */ +export type Type = CcdAmount; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is CcdAmount { + return value instanceof CcdAmount; +} + +/** + * Constructs a CcdAmount and checks that it is valid. It accepts a number, string, big, or bigint as parameter. + * It can accept a string as parameter with either a comma or a dot as the decimal separator. + * + * @param microCcdAmount The amount of micro CCD as a number, string, big, or bigint. + * @throws If an invalid micro CCD amount is passed, i.e. any value which is not an unsigned 64-bit integer + */ +export function fromMicroCcd(microCcdAmount: BigSource | bigint): CcdAmount { + // If the input is a "BigSource" assert that the number is whole + if (typeof microCcdAmount !== 'bigint') { + microCcdAmount = newBig(microCcdAmount); + + if (!microCcdAmount.mod(Big(1)).eq(Big(0))) { + throw Error('Can not create CcdAmount from a non-whole number!'); + } + + microCcdAmount = BigInt(microCcdAmount.toFixed()); + } + + if (microCcdAmount < 0n) { + throw new Error( + 'A micro CCD amount must be a non-negative integer but was: ' + + microCcdAmount + ); + } else if (microCcdAmount > 18446744073709551615n) { + throw new Error( + 'A micro CCD amount must be representable as an unsigned 64 bit integer but was: ' + + microCcdAmount + ); + } + + return new CcdAmount(microCcdAmount); +} + +/** + * Create a value representing zero CCD. + * @returns {CcdAmount} A zero amount of CCD + */ +export function zero(): CcdAmount { + return new CcdAmount(0n); +} + +/** + * Creates a CcdAmount from a number, string, big, or bigint. + * + * @param ccd The amount of CCD as a number, string, big or bigint. + * @returns The CcdAmount object derived from the ccd input parameter + * @throws If a number is passed with several decimal seperators + * @throws If a negative amount of micro CCD is passed + * @throws If the micro CCD passed is greater than what can be contained in a 64-bit integer + */ +export function fromCcd(ccd: BigSource | bigint): CcdAmount { + if (typeof ccd === 'bigint') { + ccd = ccd.toString(); + } + + const microCcd = newBig(ccd).mul(Big(MICRO_CCD_PER_CCD)); + return fromMicroCcd(microCcd); +} + +function newBig(bigSource: BigSource): Big { + if (typeof bigSource === 'string') { + return Big(bigSource.replace(',', '.')); + } + return Big(bigSource); +} + +/** + * Returns the amount of micro CCD as a Big. + * @param {CcdAmount} amount The CCD amount. + * @returns {Big} The amount in micro CCD. + */ +export function toMicroCcd(amount: CcdAmount): Big { + return Big(amount.microCcdAmount.toString()); +} + +/** + * Returns the amount of CCD as a Big. + * @param {CcdAmount} amount The CCD amount. + * @returns The amount of CCD as a Big + */ +export function toCcd(amount: CcdAmount): Big { + return Big(amount.microCcdAmount.toString()).div(Big(MICRO_CCD_PER_CCD)); +} + +/** + * Converts an amount of CCD to micro CCD and asserts that the amount is a valid amount of CCD. + * + * @param ccd The amount of CCD as a number, string, big or bigint. + * @returns The amount of micro CCD as a Big + * @throws If a number is passed with several decimal seperators + * @throws If a negative amount of micro CCD is passed + * @throws If the micro CCD passed is greater than what can be contained in a 64-bit integer + */ +export function ccdToMicroCcd(ccd: BigSource | bigint): Big { + return toMicroCcd(fromCcd(ccd)); +} + +/** + * Converts an amount of micro CCD to CCD and asserts that the amount is a valid amount of CCD. + * + * @param microCcd The amount of micro CCD as a number, string, big or bigint. + * @returns The amount of CCD as a Big + * @throws If a number is passed with several decimal seperators + * @throws If a negative amount of micro CCD is passed + * @throws If the micro CCD passed is greater than what can be contained in a 64-bit integer + */ +export function microCcdToCcd(microCcd: BigSource | bigint): Big { + return toCcd(fromMicroCcd(microCcd)); +} + +/** + * Type used when encoding a CCD amount in the JSON format used when serializing using a smart contract schema type. + * String representation of the amount of micro CCD. + */ +export type SchemaValue = string; + +/** + * Get CCD amount in the JSON format used when serializing using a smart contract schema type. + * @param {CcdAmount} amount The amount. + * @returns {SchemaValue} The schema value representation. + */ +export function toSchemaValue(amount: CcdAmount): SchemaValue { + return amount.microCcdAmount.toString(); +} + +/** + * Convert to CCD amount from JSON format used when serializing using a smart contract schema type. + * @param {SchemaValue} microCcdString The amount in schema format. + * @returns {CcdAmount} The amount. + */ +export function fromSchemaValue(microCcdString: SchemaValue): CcdAmount { + return new CcdAmount(BigInt(microCcdString)); +} + +/** + * Convert an amount of CCD from its protobuf encoding. + * @param {Proto.Amount} amount The energy in protobuf. + * @returns {CcdAmount} The energy. + */ +export function fromProto(amount: Proto.Amount): CcdAmount { + return new CcdAmount(amount.value); +} + +/** + * Convert an amount of CCD into its protobuf encoding. + * @param {CcdAmount} amount The CCD amount. + * @returns {Proto.Amount} The protobuf encoding. + */ +export function toProto(amount: CcdAmount): Proto.Amount { + return { + value: amount.microCcdAmount, + }; +} + +/** + * Constructs a {@linkcode Type} from {@linkcode Serializable}. + * @param {Serializable} value + * @returns {Type} The duration. + */ +export function fromSerializable(value: Serializable): Type { + return fromMicroCcd(value); +} + +/** + * Converts {@linkcode Type} into {@linkcode Serializable} + * @param {Type} value + * @returns {Serializable} The serializable value + */ +export function toSerializable(value: Type): Serializable { + return value.microCcdAmount.toString(); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: CcdAmount): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toSerializable(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/ContractAddress.ts b/packages/sdk/src/types/ContractAddress.ts new file mode 100644 index 000000000..650617500 --- /dev/null +++ b/packages/sdk/src/types/ContractAddress.ts @@ -0,0 +1,166 @@ +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ContractAddress; +export type Serializable = { index: string; subindex: string }; + +/** Address of a smart contract instance. */ +class ContractAddress { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The index of the smart contract address. */ + public readonly index: bigint, + /** The subindex of the smart contract address. */ + public readonly subindex: bigint + ) {} +} + +/** Address of a smart contract instance. */ +export type Type = ContractAddress; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ContractAddress { + return value instanceof ContractAddress; +} + +/** + * Construct a ContractAddress type. + * @param {number | bigint} index The index of the smart contract instance. + * @param {number | bigint} [subindex] The subindex of the smart contract instance. Defaults to 0. + * @throws If provided index or subindex is sub-zero. + * @returns {ContractAddress} + */ +export function create( + index: number | bigint, + subindex: number | bigint = 0n +): ContractAddress { + if (index < 0) { + throw new Error( + 'Invalid contract address: The index cannot be a negative number.' + ); + } + if (subindex < 0) { + throw new Error( + 'Invalid contract address: The subindex cannot be a negative number.' + ); + } + return new ContractAddress(BigInt(index), BigInt(subindex)); +} + +/** Type used when encoding a contract address in the JSON format used when serializing using a smart contract schema type. */ +export type SchemaValue = { + index: bigint; + subindex: bigint; +}; + +/** + * Get contract address in the JSON format used when serializing using a smart contract schema type. + * @param {ContractAddress} contractAddress The contract address. + * @returns {SchemaValue} The schema JSON representation. + */ +export function toSchemaValue(contractAddress: ContractAddress): SchemaValue { + return { index: contractAddress.index, subindex: contractAddress.subindex }; +} + +/** + * Convert to contract address from JSON format used when serializing using a smart contract schema type. + * @param {SchemaValue} contractAddress The contract address in schema JSON format. + * @returns {ContractAddress} The contract address. + */ +export function fromSchemaValue(contractAddress: SchemaValue): ContractAddress { + return create(contractAddress.index, contractAddress.subindex); +} + +/** + * Convert a smart contract address from its protobuf encoding. + * @param {Proto.ContractAddress} contractAddress The contract address in protobuf. + * @returns {ContractAddress} The contract address. + */ +export function fromProto( + contractAddress: Proto.ContractAddress +): ContractAddress { + return create(contractAddress.index, contractAddress.subindex); +} + +/** + * Convert a smart contract address into its protobuf encoding. + * @param {ContractAddress} contractAddress The contract address. + * @returns {Proto.ContractAddress} The protobuf encoding. + */ +export function toProto( + contractAddress: ContractAddress +): Proto.ContractAddress { + return { + index: contractAddress.index, + subindex: contractAddress.subindex, + }; +} + +/** + * Check if two contract addresses are the same. + * @param {ContractAddress} left + * @param {ContractAddress} right + * @returns {boolean} True if they are equal. + */ +export function equals(left: ContractAddress, right: ContractAddress): boolean { + return left.index === right.index && left.subindex === right.subindex; +} + +/** + * Constructs a {@linkcode ContractAddress} from {@linkcode Serializable}. + * @param {Serializable} value + * @returns {ContractAddress} The contract address. + */ +export function fromSerializable(value: Serializable): ContractAddress { + return new ContractAddress(BigInt(value.index), BigInt(value.subindex)); +} + +/** + * Converts {@linkcode ContractAddress} into {@linkcode Serializable} + * @param {ContractAddress} contractAddress + * @returns {Serializable} The serializable contract address + */ +export function toSerializable(contractAddress: ContractAddress): Serializable { + return { + index: contractAddress.index.toString(), + subindex: contractAddress.subindex.toString(), + }; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: ContractAddress): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toSerializable(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/ContractEvent.ts b/packages/sdk/src/types/ContractEvent.ts new file mode 100644 index 000000000..a691ee756 --- /dev/null +++ b/packages/sdk/src/types/ContractEvent.ts @@ -0,0 +1,105 @@ +import { Buffer } from 'buffer/index.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { deserializeTypeValue } from '../schema.js'; +import { SchemaType, serializeSchemaType } from '../schemaTypes.js'; +import type { + Base64String, + HexString, + SmartContractTypeValues, +} from '../types.js'; + +/** + * An event logged by a smart contract instance. + */ +class ContractEvent { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __nominal = true; + constructor( + /** The internal buffer of bytes representing the event. */ + public readonly buffer: Uint8Array + ) {} +} + +/** + * An event logged by a smart contract instance. + */ +export type Type = ContractEvent; + +export function fromBuffer(buffer: ArrayBuffer): ContractEvent { + return new ContractEvent(new Uint8Array(buffer)); +} + +/** + * Create a ContractEvent from a hex string. + * @param {HexString} hex Hex encoding of the event. + * @returns {ContractEvent} + */ +export function fromHexString(hex: HexString): ContractEvent { + return fromBuffer(Buffer.from(hex, 'hex')); +} + +/** + * Hex encode a ContractEvent. + * @param {ContractEvent} event The event to encode. + * @returns {HexString} String containing the hex encoding. + */ +export function toHexString(event: ContractEvent): HexString { + return Buffer.from(event.buffer).toString('hex'); +} + +/** + * Get byte representation of a ContractEvent. + * @param {ContractEvent} event The event. + * @returns {ArrayBuffer} Hash represented as bytes. + */ +export function toBuffer(event: ContractEvent): Uint8Array { + return event.buffer; +} + +/** + * Convert a contract event from its protobuf encoding. + * @param {Proto.ContractEvent} event The protobuf encoding. + * @returns {ContractEvent} + */ +export function fromProto(event: Proto.ContractEvent): ContractEvent { + return fromBuffer(event.value); +} + +/** + * Convert a contract event into its protobuf encoding. + * @param {ContractEvent} event The block hash. + * @returns {Proto.ContractEvent} The protobuf encoding. + */ +export function toProto(event: ContractEvent): Proto.ContractEvent { + return { + value: event.buffer, + }; +} + +/** + * Parse a contract event using a schema type. + * @param {ContractEvent} value The event. + * @param {SchemaType} schemaType The schema type for the event. + * @returns {SmartContractTypeValues} + */ +export function parseWithSchemaType( + event: ContractEvent, + schemaType: SchemaType +): SmartContractTypeValues { + const schemaBytes = serializeSchemaType(schemaType); + return deserializeTypeValue(toBuffer(event), schemaBytes); +} + +/** + * Parse a contract event using a schema type. + * @param {ContractEvent} value The event. + * @param {Base64String} schemaBase64 The schema type for the event encoded as Base64. + * @returns {SmartContractTypeValues} + */ +export function parseWithSchemaTypeBase64( + event: ContractEvent, + schemaBase64: Base64String +): SmartContractTypeValues { + const schemaBytes = Buffer.from(schemaBase64, 'base64'); + return deserializeTypeValue(toBuffer(event), schemaBytes); +} diff --git a/packages/sdk/src/types/ContractName.ts b/packages/sdk/src/types/ContractName.ts new file mode 100644 index 000000000..5c2812c12 --- /dev/null +++ b/packages/sdk/src/types/ContractName.ts @@ -0,0 +1,147 @@ +import * as InitName from './InitName.js'; +import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ContractName; +export type Serializable = string; + +/** The name of a smart contract. Note: This does _not_ including the 'init_' prefix. */ +class ContractName { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal string value of the contract name. */ + public readonly value: string + ) {} +} + +/** The name of a smart contract. Note: This does _not_ including the 'init_' prefix. */ +export type Type = ContractName; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ContractName { + return value instanceof ContractName; +} + +/** + * Create a contract name from a string, ensuring it follows the format of a contract name. + * @param {string} value The string of the contract name. + * @throws If the provided value is not a valid contract name. + * @returns {ContractName} + */ +export function fromString(value: string): ContractName { + if (value.length > 95) { + throw new Error( + 'Invalid ContractName: Can be atmost 95 characters long.' + ); + } + if (value.includes('.')) { + throw new Error( + "Invalid ContractName: Must not contain a '.' character." + ); + } + if (!isAsciiAlphaNumericPunctuation(value)) { + throw new Error( + 'Invalid ContractName: Must only contain ASCII alpha, numeric and punctuation characters.' + ); + } + return new ContractName(value); +} + +/** + * Create a contract name from a string, but _without_ ensuring it follows the format of a contract name. + * It is up to the caller to validate the string is a contract name. + * @param {string} value The string of the contract name. + * @returns {ContractName} + */ +export function fromStringUnchecked(value: string): ContractName { + return new ContractName(value); +} + +/** + * Extract the contract name from an {@link InitName.Type}. + * @param {InitName.Type} initName The init-function name of a smart contract. + * @returns {ContractName} + */ +export function fromInitName(initName: InitName.Type): ContractName { + return fromStringUnchecked(initName.value.substring(5)); +} + +/** + * Convert a contract name to a string + * @param {ContractName} contractName The contract name to stringify. + * @returns {string} + */ +export function toString(contractName: ContractName): string { + return contractName.value; +} + +/** Type used when encoding a contract name in the JSON format used when serializing using a smart contract schema type. */ +export type SchemaValue = { + contract: string; +}; + +/** + * Get contract name in the JSON format used when serializing using a smart contract schema type. + * @param {ContractName} contractName The contract name. + * @returns {SchemaValue} The schema JSON representation. + */ +export function toSchemaValue(contractName: ContractName): SchemaValue { + return { contract: contractName.value }; +} + +/** + * Convert to contract name from JSON format used when serializing using a smart contract schema type. + * @param {SchemaValue} contractName The contract name in schema JSON format. + * @returns {ContractName} The contract name. + */ +export function fromSchemaValue(contractName: SchemaValue): ContractName { + return fromString(contractName.contract); +} + +/** + * Check if two contract names represent the same name of a contract. + * @param {ContractName} left + * @param {ContractName} right + * @returns {boolean} True if they are equal. + */ +export function equals(left: ContractName, right: ContractName): boolean { + return left.value === right.value; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: ContractName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/CredentialRegistrationId.ts b/packages/sdk/src/types/CredentialRegistrationId.ts new file mode 100644 index 000000000..681085ac8 --- /dev/null +++ b/packages/sdk/src/types/CredentialRegistrationId.ts @@ -0,0 +1,129 @@ +import { HexString } from '../types.js'; +import { isHex } from '../util.js'; +import { Buffer } from 'buffer/index.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = + TypedJsonDiscriminator.CredentialRegistrationId; +export type Serializable = string; + +/** + * Representation of a credential registration id, which enforces that it: + * - Is a valid Hex string + * - Has length exactly 96, because a credId is 48 bytes. + * - Checks the first bit is 1, which indicates that the value represents a compressed BLS12-381 curve point. + */ +class CredentialRegistrationId { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Representation of a credential registration id */ + public readonly credId: string + ) {} + + public toJSON(): string { + return this.credId; + } +} + +/** + * Representation of a credential registration id, which enforces that it: + * - Is a valid Hex string + * - Has length exactly 96, because a credId is 48 bytes. + * - Checks the first bit is 1, which indicates that the value represents a compressed BLS12-381 curve point. + */ +export type Type = CredentialRegistrationId; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is CredentialRegistrationId { + return value instanceof CredentialRegistrationId; +} + +/** + * Construct a CredentialRegistrationId from a hex string. + * @param {HexString} credId The hex encoding of the credential registration id. + * @throws If the provided input is: not a valid hex string, not of exactly 96 characters, the first bit is not 1. + * @returns {CredentialRegistrationId} + */ +export function fromHexString(credId: HexString): CredentialRegistrationId { + if (credId.length !== 96) { + throw new Error( + 'The provided credId ' + + credId + + ' is invalid as its length was not 96' + ); + } + if (!isHex(credId)) { + throw new Error( + 'The provided credId ' + + credId + + ' does not represent a hexidecimal value' + ); + } + // Check that the first bit is 1 + if ((parseInt(credId.substring(0, 2), 16) & 0b10000000) === 0) { + throw new Error( + 'The provided credId ' + + credId + + 'does not represent a compressed BLS12-381 point' + ); + } + return new CredentialRegistrationId(credId); +} + +/** + * Get the hex string representation of the credential registatration ID. + * @param {CredentialRegistrationId} cred The credential registration ID. + * @returns {HexString} The hex encoding. + */ +export function toHexString(cred: CredentialRegistrationId): HexString { + return cred.credId; +} + +/** + * Get the byte representation of the credential registatration ID. + * @param {CredentialRegistrationId} cred The credential registration ID. + * @returns {Uint8Array} Buffer with byte representation. + */ +export function toBuffer(cred: CredentialRegistrationId): Uint8Array { + return Buffer.from(cred.credId, 'hex'); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON( + value: CredentialRegistrationId +): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/sdk/src/types/DataBlob.ts b/packages/sdk/src/types/DataBlob.ts new file mode 100644 index 000000000..2cae7e46b --- /dev/null +++ b/packages/sdk/src/types/DataBlob.ts @@ -0,0 +1,63 @@ +import { Buffer } from 'buffer/index.js'; +import { packBufferWithWord16Length } from '../serializationHelpers.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; +import type { HexString } from '../types.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.DataBlob; +export type Serializable = HexString; + +/** + * Representation of a transfer's memo or a registerData transaction's data, which enforces that: + * - the byte length is <= 256 + */ +export class DataBlob { + public readonly data: Buffer; + + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor(data: ArrayBuffer) { + if (data.byteLength > 256) { + throw new Error("A data blob's size cannot exceed 256 bytes"); + } + + this.data = Buffer.from(data); + } + + public toJSON(): string { + return packBufferWithWord16Length(this.data).toString('hex'); + } + + /** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @returns {TypedJson} The transformed object. + */ + public toTypedJSON(): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: this.data.toString('hex'), + }; + } + + /** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode DataBlob}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {DataBlob} The parsed instance. + */ + public static fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + (v: Serializable) => { + const data = Buffer.from(v, 'hex'); + return new DataBlob(data); + } + ); +} diff --git a/packages/sdk/src/types/Duration.ts b/packages/sdk/src/types/Duration.ts new file mode 100644 index 000000000..289d28d5e --- /dev/null +++ b/packages/sdk/src/types/Duration.ts @@ -0,0 +1,213 @@ +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Duration; +export type Serializable = string; + +/** + * Type representing a duration of time down to milliseconds. + * Can not be negative. + */ +class Duration { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal value for representing a duration in milliseconds. */ + public readonly value: bigint + ) {} +} + +/** + * Type representing a duration of time down to milliseconds. + * Can not be negative. + */ +export type Type = Duration; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Duration { + return value instanceof Duration; +} + +/** + * Construct a Duration from a given number of milliseconds. + * @param {number} value Number of milliseconds + * @throws If a negative value is provided. + * @returns {Duration} Duration corresponding to the provided value. + */ +export function fromMillis(value: number | bigint): Duration { + if (value < 0) { + throw new Error( + 'Invalid duration: The value cannot be a negative number.' + ); + } + return new Duration(BigInt(value)); +} + +/** + * Regular expression to match a single measure in the duration string format. + * Matches the digits and the unit in separate groups. + */ +const stringMeasureRegexp = /^(\d+)(ms|s|m|h|d)$/; + +/** + * Parse a string containing a list of duration measures separated by whitespaces. + * + * A measure is a number followed by the unit (no whitespace + * between is allowed). Every measure is accumulated into a duration. The + * string is allowed to contain any number of measures with the same unit in no + * particular order. + * + * The supported units are: + * - `ms` for milliseconds + * - `s` for seconds + * - `m` for minutes + * - `h` for hours + * - `d` for days + * + * # Example + * The duration of 10 days, 1 hour, 2 minutes and 7 seconds is: + * ```text + * "10d 1h 2m 7s" + * ``` + * @param {string} durationString string representing a duration. + * @throws The format of the string is not matching the format. + * @returns {Duration} + */ +export function fromString(durationString: string): Duration { + let durationInMillis = 0; + for (const measure of durationString.split(' ')) { + const result = measure.match(stringMeasureRegexp); + if (result === null) { + throw new Error('Invalid duration format'); + } + const [, valueString, unit] = result; + const value = parseInt(valueString, 10); + switch (unit) { + case 'ms': + durationInMillis += value; + break; + case 's': + durationInMillis += value * 1000; + break; + case 'm': + durationInMillis += value * 1000 * 60; + break; + case 'h': + durationInMillis += value * 1000 * 60 * 60; + break; + case 'd': + durationInMillis += value * 1000 * 60 * 60 * 24; + break; + default: + throw new Error( + `Invalid duration format: Unknown unit '${unit}'.` + ); + } + } + return fromMillis(durationInMillis); +} + +/** + * Get the duration in milliseconds. + * @param {Duration} duration The duration. + * @returns {bigint} The duration represented in milliseconds. + */ +export function toMillis(duration: Duration): bigint { + return duration.value; +} + +/** Type used when encoding a duration in the JSON format used when serializing using a smart contract schema type. */ +export type SchemaValue = string; + +/** + * Get duration in the JSON format used when serializing using a smart contract schema type. + * @param {Duration} duration The duration. + * @returns {SchemaValue} The schema JSON representation. + */ +export function toSchemaValue(duration: Duration): SchemaValue { + return `${duration.value} ms`; +} + +/** + * Convert to duration from JSON format used when serializing using a smart contract schema type. + * @param {SchemaValue} duration The duration in schema JSON format. + * @returns {Duration} The duration. + */ +export function fromSchemaValue(duration: SchemaValue): Duration { + return fromString(duration); +} + +/** + * Convert a duration from its protobuf encoding. + * @param {Proto.Duration} duration The duration in protobuf. + * @returns {Duration} The duration. + */ +export function fromProto(duration: Proto.Duration): Duration { + return fromMillis(duration.value); +} + +/** + * Convert a duration into its protobuf encoding. + * @param {Duration} duration The duration. + * @returns {Proto.Duration} The protobuf encoding. + */ +export function toProto(duration: Duration): Proto.Duration { + return { + value: duration.value, + }; +} + +/** + * Constructs a {@linkcode Duration} from {@linkcode Serializable}. + * @param {Serializable} value + * @returns {Duration} The duration. + */ +export function fromSerializable(value: Serializable): Duration { + return fromMillis(BigInt(value)); +} + +/** + * Converts {@linkcode Duration} into {@linkcode Serializable} + * @param {Duration} duration + * @returns {Serializable} The serializable value + */ +export function toSerializable(duration: Duration): Serializable { + return duration.value.toString(); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: Duration): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toSerializable(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/Energy.ts b/packages/sdk/src/types/Energy.ts new file mode 100644 index 000000000..06e9a6d10 --- /dev/null +++ b/packages/sdk/src/types/Energy.ts @@ -0,0 +1,117 @@ +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Energy; +export type Serializable = string; + +/** Energy measure. Used as part of cost calculations for transactions. */ +class Energy { + protected get serializable(): Serializable { + return this.value.toString(); + } + + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal value for representing the energy. */ + public readonly value: bigint + ) {} +} + +/** Energy measure. Used as part of cost calculations for transactions. */ +export type Type = Energy; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Energy { + return value instanceof Energy; +} + +/** + * Construct an Energy type. + * @param {bigint | number} value The measure of energy. + * @throws If the provided value is a negative number. + * @returns {Energy} + */ +export function create(value: bigint | number): Energy { + if (value < 0) { + throw new Error( + 'Invalid energy: The value cannot be a negative number.' + ); + } + return new Energy(BigInt(value)); +} + +/** + * Convert energy from its protobuf encoding. + * @param {Proto.Energy} energy The energy in protobuf. + * @returns {Energy} The energy. + */ +export function fromProto(energy: Proto.Energy): Energy { + return new Energy(energy.value); +} + +/** + * Convert energy into its protobuf encoding. + * @param {Energy} energy The energy. + * @returns {Proto.Energy} The protobuf encoding. + */ +export function toProto(energy: Energy): Proto.Energy { + return { + value: energy.value, + }; +} + +/** + * Constructs a {@linkcode Type} from {@linkcode Serializable}. + * @param {Serializable} value + * @returns {Type} The duration. + */ +export function fromSerializable(value: Serializable): Type { + return create(BigInt(value)); +} + +/** + * Converts {@linkcode Type} into {@linkcode Serializable} + * @param {Type} energy + * @returns {Serializable} The serializable value + */ +export function toSerializable(energy: Type): Serializable { + return energy.value.toString(); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: Type): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toSerializable(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/EntrypointName.ts b/packages/sdk/src/types/EntrypointName.ts new file mode 100644 index 000000000..95a121373 --- /dev/null +++ b/packages/sdk/src/types/EntrypointName.ts @@ -0,0 +1,112 @@ +import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.EntrypointName; +export type Serializable = string; + +/** + * Type representing an entrypoint of a smart contract. + * @template S Use for using string literals for the type. + */ +class EntrypointName { + protected get serializable(): Serializable { + return this.value; + } + + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal string value of the receive name. */ + public readonly value: S + ) {} +} + +/** + * Type representing an entrypoint of a smart contract. + */ +export type Type = EntrypointName; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is EntrypointName { + return value instanceof EntrypointName; +} + +/** + * Create a smart contract entrypoint name from a string, ensuring it follows the required format. + * @param {string} value The string with the entrypoint name. + * @throws If the provided value is not a valid entrypoint name. + * @returns {EntrypointName} + */ +export function fromString(value: S): EntrypointName { + if (value.length > 99) { + throw new Error( + 'Invalid EntrypointName: Can be atmost 99 characters long.' + ); + } + if (!isAsciiAlphaNumericPunctuation(value)) { + throw new Error( + 'Invalid EntrypointName: Must only contain ASCII alpha, numeric and punctuation characters.' + ); + } + return new EntrypointName(value); +} + +/** + * Create a smart contract entrypoint name from a string, but _without_ ensuring it follows the required format. + * It is up to the caller to ensure the string is a valid entrypoint name. + * @param {string} value The string with the entrypoint name. + * @returns {EntrypointName} + */ +export function fromStringUnchecked( + value: S +): EntrypointName { + return new EntrypointName(value); +} + +/** + * Convert a entrypoint name to a string + * @param {EntrypointName} entrypointName The entrypoint name to stringify. + * @returns {string} + */ +export function toString( + entrypointName: EntrypointName +): S { + return entrypointName.value; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: EntrypointName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/InitName.ts b/packages/sdk/src/types/InitName.ts new file mode 100644 index 000000000..a50e46fea --- /dev/null +++ b/packages/sdk/src/types/InitName.ts @@ -0,0 +1,138 @@ +import * as ContractName from './ContractName.js'; +import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.InitName; +export type Serializable = string; + +/** The name of an init-function for a smart contract. Note: This is of the form 'init_'. */ +class InitName { + protected get serializable(): Serializable { + return this.value; + } + + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal string corresponding to the init-function. */ + public readonly value: string + ) {} +} + +/** The name of an init-function for a smart contract. Note: This is of the form 'init_'. */ +export type Type = InitName; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is InitName { + return value instanceof InitName; +} + +/** + * Create an InitName directly from a string, ensuring it follows the format of an init-function name. + * @param {string} value String with the init-function name. + * @throws If the string is not a valid init-function name. + * @returns {InitName} + */ +export function fromString(value: string): InitName { + if (value.length > 100) { + throw new Error('Invalid InitName: Can be atmost 100 characters long.'); + } + if (!value.startsWith('init_')) { + throw new Error("Invalid InitName: Must be prefixed with 'init_'."); + } + if (value.includes('.')) { + throw new Error("Invalid InitName: Must not contain a '.' character."); + } + if (!isAsciiAlphaNumericPunctuation(value)) { + throw new Error( + 'Invalid InitName: Must only contain ASCII alpha, numeric and punctuation characters.' + ); + } + return new InitName(value); +} + +/** + * Create an InitName directly from a string. + * It is up to the caller to ensure the provided string follows the format of an init-function name. + * @param {string} value String with the init-function name. + * @returns {InitName} + */ +export function fromStringUnchecked(value: string): InitName { + return new InitName(value); +} + +/** + * Create an InitName from a contract name. + * @param {ContractName.Type} contractName The contract name to convert into an init-function name. + * @returns {InitName} + */ +export function fromContractName(contractName: ContractName.Type): InitName { + return fromStringUnchecked('init_' + contractName.value); +} + +/** + * Get the string representation of the smart contract init-function name. + * @param {InitName} initName The init-function name of the smart contract. + * @returns {string} a string. + */ +export function toString(initName: InitName): string { + return initName.value; +} + +/** + * Convert a smart contract init name from its protobuf encoding. + * @param {Proto.InitName} initName The protobuf encoding. + * @returns {InitName} + */ +export function fromProto(initName: Proto.InitName): InitName { + return fromStringUnchecked(initName.value); +} + +/** + * Convert a smart contract init name into its protobuf encoding. + * @param {InitName} initName The init name. + * @returns {Proto.InitName} The protobuf encoding. + */ +export function toProto(initName: InitName): Proto.InitName { + return { + value: initName.value, + }; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: InitName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/ModuleClient.ts b/packages/sdk/src/types/ModuleClient.ts new file mode 100644 index 000000000..d167d9a0d --- /dev/null +++ b/packages/sdk/src/types/ModuleClient.ts @@ -0,0 +1,168 @@ +import { ContractTransactionMetadata } from '../GenericContract.js'; +import * as ModuleReference from './ModuleReference.js'; +import * as BlockHash from './BlockHash.js'; +import * as Parameter from './Parameter.js'; +import * as TransactionHash from './TransactionHash.js'; +import * as ContractName from './ContractName.js'; +import { + AccountTransactionType, + InitContractPayload, + VersionedModuleSource, +} from '../types.js'; +import { ConcordiumGRPCClient } from '../grpc/index.js'; +import { AccountSigner, signTransaction } from '../signHelpers.js'; +import * as CcdAmount from './CcdAmount.js'; +import * as TransactionExpiry from './TransactionExpiry.js'; + +/** + * An update transaction without header. + */ +export type ContractInitTransaction = { + /** The type of the transaction, which will always be of type {@link AccountTransactionType.InitContract} */ + type: AccountTransactionType.InitContract; + /** The payload of the transaction, which will always be of type {@link InitContractPayload} */ + payload: InitContractPayload; +}; + +/** + * Internal class representing a smart contract module deployed on chain. + * + * The public type for this {@link ModuleClient} is exported separately to ensure + * the constructor is only available from within this module. + */ +class ModuleClient { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __nominal = true; + constructor( + /** The gRPC connection used by this object */ + public readonly grpcClient: ConcordiumGRPCClient, + /** The reference for this module */ + public readonly moduleReference: ModuleReference.Type + ) {} +} + +/** + * Type representing a smart contract module deployed on chain. + */ +export type Type = ModuleClient; +export function instanceOf(value: unknown): value is ModuleClient { + return value instanceof ModuleClient; +} + +/** + * Create a new `GenericModule` instance for interacting with a smart contract module on chain. + * The caller must ensure that the smart contract module is already deployed on chain. + * + * @param {ConcordiumGRPCClient} grpcClient - The GRPC client for accessing a node. + * @param {ModuleReference} moduleReference - The reference of the deployed smart contract module. + * + * @returns {ModuleClient} + */ +export function createUnchecked( + grpcClient: ConcordiumGRPCClient, + moduleReference: ModuleReference.Type +): ModuleClient { + return new ModuleClient(grpcClient, moduleReference); +} + +/** + * Create a new `GenericModule` instance for interacting with a smart contract module on chain. + * This function ensures the module is already deployed on chain otherwise produces an error. + * + * @param {ConcordiumGRPCClient} grpcClient - The GRPC client for accessing a node. + * @param {ModuleReference} moduleReference - The reference of the deployed smart contract module. + * + * @throws If failing to communicate with the concordium node or module reference does not correspond to a module on chain. + * + * @returns {ModuleClient} + */ +export async function create( + grpcClient: ConcordiumGRPCClient, + moduleReference: ModuleReference.Type +): Promise { + const mod = new ModuleClient(grpcClient, moduleReference); + await checkOnChain(mod); + return mod; +} + +/** + * Check if this module is deployed to the chain. + * + * @param {ModuleClient} moduleClient The client for a smart contract module on chain. + * @param {BlockHash.Type} [blockHash] Hash of the block to check information at. When not provided the last finalized block is used. + * + * @throws {RpcError} If failing to communicate with the concordium node or module is not deployed on chain. + * @returns {boolean} Indicating whether the module is deployed on chain. + */ +export async function checkOnChain( + moduleClient: ModuleClient, + blockHash?: BlockHash.Type +): Promise { + await getModuleSource(moduleClient, blockHash); +} + +/** + * Get the module source of the deployed smart contract module. + * + * @param {ModuleClient} moduleClient The client for a smart contract module on chain. + * @param {BlockHash.Type} [blockHash] Hash of the block to check information at. When not provided the last finalized block is used. + * + * @throws {RpcError} If failing to communicate with the concordium node or module not found. + * @returns {VersionedModuleSource} Module source of the deployed smart contract module. + */ +export function getModuleSource( + moduleClient: ModuleClient, + blockHash?: BlockHash.Type +): Promise { + return moduleClient.grpcClient.getModuleSource( + moduleClient.moduleReference, + blockHash + ); +} + +/** + * Creates and sends transaction for initializing a smart contract `contractName` with parameter `input`. + * + * @param {ModuleClient} moduleClient The client for a smart contract module on chain. + * @param {ContractName.Type} contractName - The name of the smart contract to instantiate (this is without the `init_` prefix). + * @param {ContractTransactionMetadata} metadata - Metadata to be used for the transaction (with defaults). + * @param {Parameter.Type} parameter - Input for for contract function. + * @param {AccountSigner} signer - An object to use for signing the transaction. + * + * @throws If the query could not be invoked successfully. + * + * @returns {TransactionHash.Type} The transaction hash of the update transaction. + */ +export async function createAndSendInitTransaction( + moduleClient: ModuleClient, + contractName: ContractName.Type, + metadata: ContractTransactionMetadata, + parameter: Parameter.Type, + signer: AccountSigner +): Promise { + const payload: InitContractPayload = { + moduleRef: moduleClient.moduleReference, + amount: metadata.amount ?? CcdAmount.zero(), + initName: contractName, + maxContractExecutionEnergy: metadata.energy, + param: parameter, + }; + const { nonce } = await moduleClient.grpcClient.getNextAccountNonce( + metadata.senderAddress + ); + const header = { + expiry: metadata.expiry ?? TransactionExpiry.futureMinutes(5), + nonce: nonce, + sender: metadata.senderAddress, + }; + const transaction = { + type: AccountTransactionType.InitContract, + header, + payload, + }; + const signature = await signTransaction(transaction, signer); + return moduleClient.grpcClient.sendAccountTransaction( + transaction, + signature + ); +} diff --git a/packages/sdk/src/types/ModuleReference.ts b/packages/sdk/src/types/ModuleReference.ts new file mode 100644 index 000000000..6d4d058ca --- /dev/null +++ b/packages/sdk/src/types/ModuleReference.ts @@ -0,0 +1,156 @@ +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { Buffer } from 'buffer/index.js'; +import { packBufferWithWord32Length } from '../serializationHelpers.js'; +import type { HexString } from '../types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The number of bytes used to represent a block hash. + */ +const MODULE_REF_BYTE_LENGTH = 32; +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ModuleReference; +export type Serializable = HexString; + +/** + * Reference to a smart contract module. + */ +class ModuleReference { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Internal field, the module reference represented as a hex string. */ + public readonly moduleRef: HexString, + /** Internal field, buffer containing the 32 bytes for the module reference. */ + public readonly decodedModuleRef: Uint8Array + ) {} + + public toJSON(): string { + return packBufferWithWord32Length(this.decodedModuleRef).toString( + 'hex' + ); + } +} + +/** + * Reference to a smart contract module. + */ +export type Type = ModuleReference; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ModuleReference { + return value instanceof ModuleReference; +} + +/** + * Create a ModuleReference from a buffer of 32 bytes. + * @param {ArrayBuffer} buffer Buffer containing 32 bytes for the hash. + * @throws If the provided buffer does not contain exactly 32 bytes. + * @returns {ModuleReference} A module reference. + */ +export function fromBuffer(buffer: ArrayBuffer): ModuleReference { + const hex = Buffer.from(buffer).toString('hex'); + if (buffer.byteLength !== MODULE_REF_BYTE_LENGTH) { + throw new Error( + 'The provided moduleRef ' + + hex + + ' is invalid as module reference as it does not contain 32 bytes' + ); + } + return new ModuleReference(hex, new Uint8Array(buffer)); +} + +/** + * Create a ModuleReference from a hex string. + * @param {HexString} moduleRef Hex encoding of the module reference. + * @throws If the provided hex encoding does not correspond to a buffer of exactly 32 bytes. + * @returns {ModuleReference} A module reference. + */ +export function fromHexString(moduleRef: HexString): ModuleReference { + if (moduleRef.length !== MODULE_REF_BYTE_LENGTH * 2) { + throw new Error( + 'The provided moduleRef ' + + moduleRef + + ' is invalid as its length was not 64' + ); + } + return new ModuleReference( + moduleRef, + new Uint8Array(Buffer.from(moduleRef, 'hex')) + ); +} + +/** + * Get the module reference bytes encoded as hex. + * @param {ModuleReference} moduleReference The module reference. + * @returns {HexString} String with hex encoding. + */ +export function toHexString(moduleReference: ModuleReference): HexString { + return moduleReference.moduleRef; +} + +/** + * Convert module reference from its protobuf encoding. + * @param {Proto.ModuleRef} moduleReference The module reference in protobuf. + * @returns {ModuleReference} The module reference. + */ +export function fromProto(moduleReference: Proto.ModuleRef): ModuleReference { + return fromBuffer(moduleReference.value); +} + +/** + * Convert module reference into its protobuf encoding. + * @param {ModuleReference} moduleReference The module reference. + * @returns {Proto.ModuleRef} The protobuf encoding. + */ +export function toProto(moduleReference: ModuleReference): Proto.ModuleRef { + return { + value: moduleReference.decodedModuleRef, + }; +} + +/** + * Check if two module references are the same. + * @param {ModuleReference} left + * @param {ModuleReference} right + * @returns {boolean} True if they are equal. + */ +export function equals(left: ModuleReference, right: ModuleReference): boolean { + return left.moduleRef === right.moduleRef; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: ModuleReference): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/common/src/types/NodeInfo.ts b/packages/sdk/src/types/NodeInfo.ts similarity index 86% rename from packages/common/src/types/NodeInfo.ts rename to packages/sdk/src/types/NodeInfo.ts index 1353c5cf2..b80d801af 100644 --- a/packages/common/src/types/NodeInfo.ts +++ b/packages/sdk/src/types/NodeInfo.ts @@ -1,9 +1,11 @@ -import { BakerId, Duration, HexString, Timestamp } from '../types'; +import type { BakerId, HexString } from '../types.js'; +import type * as Duration from '../types/Duration.js'; +import type * as Timestamp from '../types/Timestamp.js'; export interface NodeInfo { peerVersion: string; - localTime: Timestamp; - peerUptime: Duration; + localTime: Timestamp.Type; + peerUptime: Duration.Type; networkInfo: NodeNetworkInfo; details: NodeInfoDetails; } diff --git a/packages/sdk/src/types/Parameter.ts b/packages/sdk/src/types/Parameter.ts new file mode 100644 index 000000000..097253051 --- /dev/null +++ b/packages/sdk/src/types/Parameter.ts @@ -0,0 +1,172 @@ +import { Buffer } from 'buffer/index.js'; +import { checkParameterLength } from '../contractHelpers.js'; +import { SchemaType, serializeSchemaType } from '../schemaTypes.js'; +import { serializeTypeValue } from '../schema.js'; +import type { Base64String, HexString } from '../types.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Parameter; +export type Serializable = HexString; + +/** Parameter for a smart contract entrypoint. */ +class Parameter { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Internal buffer of bytes representing the parameter. */ + public readonly buffer: Uint8Array + ) {} +} + +/** Parameter for a smart contract entrypoint. */ +export type Type = Parameter; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Parameter { + return value instanceof Parameter; +} + +/** + * Create an empty parameter. + * @returns {Parameter} An empty parameter. + */ +export function empty(): Parameter { + return fromBufferUnchecked(new ArrayBuffer(0)); +} + +/** + * Create a parameter for a smart contract entrypoint. + * Ensuring the buffer does not exceed the maximum number of bytes supported for a smart contract parameter. + * @param {ArrayBuffer} buffer The buffer of bytes representing the parameter. + * @throws If the provided buffer exceed the supported number of bytes for a smart contract. + * @returns {Parameter} + */ +export function fromBuffer(buffer: ArrayBuffer): Parameter { + checkParameterLength(buffer); + return fromBufferUnchecked(buffer); +} + +/** + * Create an unchecked parameter for a smart contract entrypoint. + * It is up to the caller to ensure the buffer does not exceed the maximum number of bytes supported for a smart contract parameter. + * @param {ArrayBuffer} buffer The buffer of bytes representing the parameter. + * @returns {Parameter} + */ +export function fromBufferUnchecked(buffer: ArrayBuffer): Parameter { + return new Parameter(new Uint8Array(buffer)); +} + +/** + * Create a parameter for a smart contract entrypoint from a hex string. + * Ensuring the parameter does not exceed the maximum number of bytes supported for a smart contract parameter. + * @param {HexString} hex String with hex encoding of the parameter. + * @throws If the provided parameter exceed the supported number of bytes for a smart contract. + * @returns {Parameter} + */ +export function fromHexString(hex: HexString): Parameter { + return fromBuffer(Buffer.from(hex, 'hex')); +} + +/** + * Convert a parameter into a hex string. + * @param {Parameter} parameter The parameter to encode in a hex string. + * @returns {HexString} + */ +export function toHexString(parameter: Parameter): HexString { + return Buffer.from(parameter.buffer).toString('hex'); +} + +/** + * Convert a parameter into a buffer. + * @param {Parameter} parameter The parameter to get the buffer from. + * @returns {Uint8Array} + */ +export function toBuffer(parameter: Parameter): Uint8Array { + return parameter.buffer; +} + +/** + * Create a parameter from a schema type and the corresponding schema value. + * @param {SchemaType} schemaType The schema type for some parameter. + * @param {unknown} value The parameter value fitting the schema type. + * @returns {Parameter} A parameter of the provided value encoded using the schema type. + */ +export function fromSchemaType( + schemaType: SchemaType, + value: unknown +): Parameter { + const schemaBytes = serializeSchemaType(schemaType); + return serializeTypeValue(value, schemaBytes); +} + +/** + * Create a parameter from a schema type and the corresponding schema value. + * @param {Base64String} schemaBase64 The schema type for some parameter in base64. + * @param {unknown} value The parameter value fitting the schema type. + * @returns {Parameter} A parameter of the provided value encoded using the schema type. + */ +export function fromBase64SchemaType( + schemaBase64: Base64String, + value: unknown +): Parameter { + const schemaBytes = Buffer.from(schemaBase64, 'base64'); + return serializeTypeValue(value, schemaBytes); +} + +/** + * Convert a smart contract parameter from its protobuf encoding. + * @param {Proto.Parameter} parameter The parameter in protobuf. + * @returns {Parameter} The parameter. + */ +export function fromProto(parameter: Proto.Parameter): Parameter { + return fromBuffer(parameter.value); +} + +/** + * Convert a parameter into its protobuf encoding. + * @param {Parameter} parameter The parameter. + * @returns {Proto.Parameter} The protobuf encoding. + */ +export function toProto(parameter: Parameter): Proto.Parameter { + return { + value: parameter.buffer, + }; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: Parameter): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/common/src/types/PeerInfo.ts b/packages/sdk/src/types/PeerInfo.ts similarity index 91% rename from packages/common/src/types/PeerInfo.ts rename to packages/sdk/src/types/PeerInfo.ts index 8cc9073e8..b45b8714a 100644 --- a/packages/common/src/types/PeerInfo.ts +++ b/packages/sdk/src/types/PeerInfo.ts @@ -1,4 +1,4 @@ -import { HexString, IpAddressString } from '..'; +import type { HexString, IpAddressString } from '../types.js'; export interface PeerInfo { peerId: HexString; diff --git a/packages/sdk/src/types/ReceiveName.ts b/packages/sdk/src/types/ReceiveName.ts new file mode 100644 index 000000000..5861fea3d --- /dev/null +++ b/packages/sdk/src/types/ReceiveName.ts @@ -0,0 +1,217 @@ +import { isAsciiAlphaNumericPunctuation } from '../contractHelpers.js'; +import * as ContractName from './ContractName.js'; +import * as EntrypointName from './EntrypointName.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ReceiveName; +export type Serializable = string; + +/** + * Represents a receive-function in a smart contract module. + * A value of this type is assumed to be a valid receive name which means: + * - It only contains ASCII alpha, numeric and punctuations. + * - It is at most 100 characters. + * - It contains at least one '.' character. + */ +class ReceiveName { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal string value of the receive name. */ + public readonly value: string + ) {} +} + +/** + * Represents a receive-function in a smart contract module. + * A value of this type is assumed to be a valid receive name which means: + * - It only contains ASCII alpha, numeric and punctuations. + * - It is at most 100 characters. + * - It contains at least one '.' character. + */ +export type Type = ReceiveName; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ReceiveName { + return value instanceof ReceiveName; +} + +/** + * Create a ReceiveName. + * @param {ContractName.Type} contractName The name of the smart contract using this receive-function. + * @param {EntrypointName.Type} entrypointName The entrypoint of the smart contract corresponding to this receive-function. + * @throws If the provided value is not a valid receive name. + * @returns {ReceiveName} The receive name. + */ +export function create( + contractName: ContractName.Type, + entrypointName: EntrypointName.Type +): ReceiveName { + return fromString( + `${ContractName.toString(contractName)}.${EntrypointName.toString( + entrypointName + )}` + ); +} + +/** + * Create a smart contract receive-name from a string, ensuring it follows the required format. + * @param {string} value The string of the receive name. + * @throws If the provided value is not a valid receive name. + * @returns {ReceiveName} + */ +export function fromString(value: string): ReceiveName { + if (value.length > 100) { + throw new Error( + 'Invalid ReceiveName: Can be atmost 100 characters long.' + ); + } + if (!value.includes('.')) { + throw new Error( + "Invalid ReceiveName: Must contain at least one '.' character." + ); + } + if (!isAsciiAlphaNumericPunctuation(value)) { + throw new Error( + 'Invalid ReceiveName: Must only contain ASCII alpha, numeric and punctuation characters.' + ); + } + return new ReceiveName(value); +} + +/** + * Create a smart contract receive name from a string, but _without_ ensuring it follows the required format. + * It is up to the caller to ensure the string is a valid receive name. + * @param {string} value The string with the receive name. + * @returns {ReceiveName} + */ +export function fromStringUnchecked(value: string): ReceiveName { + return new ReceiveName(value); +} + +/** + * Convert a receive name to a string + * @param {ReceiveName} receiveName The receive name to stringify. + * @returns {string} + */ +export function toString(receiveName: ReceiveName): string { + return receiveName.value; +} + +/** + * Convert a receive name to a ContractName + * @param {ReceiveName} receiveName The receive name to get the contract name from. + * @returns {ContractName.Type} + */ +export function toContractName(receiveName: ReceiveName): ContractName.Type { + const splitAt = receiveName.value.indexOf('.'); + const contractName = receiveName.value.substring(0, splitAt); + return ContractName.fromStringUnchecked(contractName); +} + +/** + * Convert a receive name to a EntrypointName + * @param {ReceiveName} receiveName The receive name to get the entrypoint name from. + * @returns {EntrypointName.Type} + */ +export function toEntrypointName( + receiveName: ReceiveName +): EntrypointName.Type { + const splitAt = receiveName.value.indexOf('.'); + const entrypointName = receiveName.value.substring(splitAt + 1); + return EntrypointName.fromStringUnchecked(entrypointName); +} + +/** Type used when encoding a receive-name in the JSON format used when serializing using a smart contract schema type. */ +export type SchemaValue = { + contract: string; + func: string; +}; + +/** + * Get receiveName in the JSON format used when serializing using a smart contract schema type. + * @param {ReceiveName} receiveName The receive name. + * @returns {SchemaValue} The schema JSON representation. + */ +export function toSchemaValue(receiveName: ReceiveName): SchemaValue { + const contract = ContractName.toString(toContractName(receiveName)); + const func = EntrypointName.toString(toEntrypointName(receiveName)); + return { contract, func }; +} + +/** + * Convert to smart contract receive name from JSON format used when serializing using a smart contract schema type. + * @param {SchemaValue} receiveName The receive name in schema JSON format. + * @returns {ReceiveName} The receive name. + */ +export function fromSchemaValue(receiveName: SchemaValue): ReceiveName { + return fromString(`${receiveName.contract}.${receiveName.func}`); +} + +/** + * Convert a smart contract receive name from its protobuf encoding. + * @param {Proto.ReceiveName} receiveName The protobuf encoding. + * @returns {ReceiveName} + */ +export function fromProto(receiveName: Proto.ReceiveName): ReceiveName { + return fromStringUnchecked(receiveName.value); +} + +/** + * Convert a smart contract receive name into its protobuf encoding. + * @param {ReceiveName} receiveName The receive name. + * @returns {Proto.ReceiveName} The protobuf encoding. + */ +export function toProto(receiveName: ReceiveName): Proto.ReceiveName { + return { + value: receiveName.value, + }; +} + +/** + * Check if two smart contract receive names represent the same. + * @param {ReceiveName} left + * @param {ReceiveName} right + * @returns {boolean} True if they are equal. + */ +export function equals(left: ReceiveName, right: ReceiveName): boolean { + return left.value === right.value; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON({ value }: ReceiveName): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value, + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromString +); diff --git a/packages/sdk/src/types/ReturnValue.ts b/packages/sdk/src/types/ReturnValue.ts new file mode 100644 index 000000000..0394113ed --- /dev/null +++ b/packages/sdk/src/types/ReturnValue.ts @@ -0,0 +1,139 @@ +import { Buffer } from 'buffer/index.js'; +import { SchemaType, serializeSchemaType } from '../schemaTypes.js'; +import { deserializeTypeValue } from '../schema.js'; +import type { + Base64String, + HexString, + SmartContractTypeValues, +} from '../types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.ReturnValue; +export type Serializable = HexString; + +/** Return value from invoking a smart contract entrypoint. */ +class ReturnValue { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Internal buffer of bytes representing the return type. */ + public readonly buffer: Uint8Array + ) {} +} + +/** Return value from invoking a smart contract entrypoint. */ +export type Type = ReturnValue; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is ReturnValue { + return value instanceof ReturnValue; +} + +/** + * Create an empty return value. + * @returns {ReturnValue} An empty return value. + */ +export function empty(): ReturnValue { + return fromBuffer(new ArrayBuffer(0)); +} + +/** + * Create a return type from invoking a smart contract entrypoint. + * @param {ArrayBuffer} buffer The buffer of bytes representing the return value. + * @returns {ReturnValue} + */ +export function fromBuffer(buffer: ArrayBuffer): ReturnValue { + return new ReturnValue(new Uint8Array(buffer)); +} + +/** + * Create a return type from invoking a smart contract entrypoint from a hex string. + * @param {HexString} hex The hex string representing the return value. + * @returns {ReturnValue} + */ +export function fromHexString(hex: HexString): ReturnValue { + return new ReturnValue(new Uint8Array(Buffer.from(hex, 'hex'))); +} + +/** + * Convert a return value into a hex string. + * @param {ReturnValue} returnValue The return value to encode in a hex string. + * @returns {HexString} The return value encoded in hex. + */ +export function toHexString(returnValue: ReturnValue): HexString { + return Buffer.from(returnValue.buffer).toString('hex'); +} + +/** + * Convert a return value into a buffer. + * @param {ReturnValue} parameter The return value to get the buffer from. + * @returns {Uint8Array} + */ +export function toBuffer(parameter: ReturnValue): Uint8Array { + return parameter.buffer; +} + +/** + * Convert a return value into a more structured representation using a schema type. + * @param {ReturnValue} returnValue The return value. + * @param {SchemaType} schemaType The schema type for the return value. + * @returns {SmartContractTypeValues} + */ +export function parseWithSchemaType( + returnValue: ReturnValue, + schemaType: SchemaType +): SmartContractTypeValues { + const schemaBytes = serializeSchemaType(schemaType); + return deserializeTypeValue(returnValue.buffer, schemaBytes); +} + +/** + * Convert a return value into a more structured representation using a schema type. + * @param {ReturnValue} returnValue The return value. + * @param {Base64String} schemaBase64 The schema type for the return value. + * @returns {SmartContractTypeValues} + */ +export function parseWithSchemaTypeBase64( + returnValue: ReturnValue, + schemaBase64: Base64String +): SmartContractTypeValues { + const schemaBytes = Buffer.from(schemaBase64, 'base64'); + return deserializeTypeValue(returnValue.buffer, schemaBytes); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: ReturnValue): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/sdk/src/types/SequenceNumber.ts b/packages/sdk/src/types/SequenceNumber.ts new file mode 100644 index 000000000..300c322be --- /dev/null +++ b/packages/sdk/src/types/SequenceNumber.ts @@ -0,0 +1,115 @@ +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.SequenceNumber; +export type Serializable = string; + +/** Transaction sequence number. (Formerly refered as Nonce) */ +class SequenceNumber { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Internal value representing the sequence number. */ + public readonly value: bigint + ) {} +} + +/** A transaction sequence number. (Formerly refered as Nonce) */ +export type Type = SequenceNumber; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is SequenceNumber { + return value instanceof SequenceNumber; +} + +/** + * Construct an SequenceNumber type. + * @param {bigint | number} sequenceNumber The account sequence number. + * @throws If `sequenceNumber` is not at least 1. + * @returns {SequenceNumber} + */ +export function create(sequenceNumber: bigint | number): SequenceNumber { + if (sequenceNumber < 1) { + throw new Error( + 'Invalid account sequence number: Must be 1 or higher.' + ); + } + return new SequenceNumber(BigInt(sequenceNumber)); +} + +/** + * Convert a SequenceNumber from its protobuf encoding. + * @param {Proto.SequenceNumber} sequenceNumber The sequence number in protobuf. + * @returns {SequenceNumber} The sequence number. + */ +export function fromProto( + sequenceNumber: Proto.SequenceNumber +): SequenceNumber { + return create(sequenceNumber.value); +} + +/** + * Convert a sequence number into its protobuf encoding. + * @param {SequenceNumber} sequenceNumber The duration. + * @returns {Proto.SequenceNumber} The protobuf encoding. + */ +export function toProto(sequenceNumber: SequenceNumber): Proto.SequenceNumber { + return { + value: sequenceNumber.value, + }; +} + +/** + * Constructs a {@linkcode Type} from {@linkcode Serializable}. + * @param {Serializable} value + * @returns {Type} The duration. + */ +export function fromSerializable(value: Serializable): Type { + return create(BigInt(value)); +} + +/** + * Converts {@linkcode Type} into {@linkcode Serializable} + * @param {Type} value + * @returns {Serializable} The serializable value + */ +export function toSerializable(value: Type): Serializable { + return value.value.toString(); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: SequenceNumber): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toSerializable(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/Timestamp.ts b/packages/sdk/src/types/Timestamp.ts new file mode 100644 index 000000000..daceb757b --- /dev/null +++ b/packages/sdk/src/types/Timestamp.ts @@ -0,0 +1,157 @@ +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.Timestamp; +export type Serializable = string; + +/** Represents a timestamp. */ +class Timestamp { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** The internal value for representing the timestamp as milliseconds since Unix epoch. */ + public readonly value: bigint + ) {} +} + +/** Represents a timestamp. */ +export type Type = Timestamp; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is Timestamp { + return value instanceof Timestamp; +} + +/** + * Create a Timestamp from milliseconds since Unix epoch. + * @param {number} value Milliseconds since Unix epoch. + * @throws If the value is negative. + * @returns {Timestamp} The created timestamp. + */ +export function fromMillis(value: number | bigint): Timestamp { + if (value < 0) { + throw new Error( + 'Invalid timestamp: The value cannot be a negative number.' + ); + } + return new Timestamp(BigInt(value)); +} + +/** + * Create a Timestamp from a Date object. + * @param {Date} date Date representing the timestamp. + * @throws If the date if from before January 1, 1970 UTC. + * @returns {Timestamp} The created timestamp. + */ +export function fromDate(date: Date): Timestamp { + return fromMillis(date.getTime()); +} + +/** Type used when encoding a timestamp in the JSON format used when serializing using a smart contract schema type. */ +export type SchemaValue = string; + +/** + * Get timestamp in the JSON format used when serializing using a smart contract schema type. + * @param {Timestamp} timestamp The timestamp. + * @returns {SchemaValue} The schema value representation. + */ +export function toSchemaValue(timestamp: Timestamp): SchemaValue { + return toDate(timestamp).toISOString(); +} + +/** + * Convert to timestamp from JSON format used when serializing using a smart contract schema type. + * @param {SchemaValue} timestamp The timestamp in schema format. + * @returns {Timestamp} The timestamp + */ +export function fromSchemaValue(timestamp: SchemaValue): Timestamp { + return fromMillis(Date.parse(timestamp)); +} + +/** + * Get timestamp as a Date. + * @param {Timestamp} timestamp The timestamp. + * @returns {Date} Date object. + */ +export function toDate(timestamp: Timestamp): Date { + const number = Number(timestamp.value); + if (isNaN(number)) { + throw new Error('Timestamp cannot be represented as a date.'); + } + return new Date(number); +} + +/** + * Convert a timestamp from its protobuf encoding. + * @param {Proto.Timestamp} timestamp The timestamp in protobuf. + * @returns {Timestamp} The timestamp. + */ +export function fromProto(timestamp: Proto.Timestamp): Timestamp { + return fromMillis(timestamp.value); +} + +/** + * Convert a timestamp into its protobuf encoding. + * @param {Timestamp} timestamp The timestamp. + * @returns {Proto.Timestamp} The protobuf encoding. + */ +export function toProto(timestamp: Timestamp): Proto.Timestamp { + return { + value: timestamp.value, + }; +} + +/** + * Constructs a {@linkcode Type} from {@linkcode Serializable}. + * @param {Serializable} value + * @returns {Type} The duration. + */ +export function fromSerializable(value: Serializable): Type { + return fromMillis(BigInt(value)); +} + +/** + * Converts {@linkcode Type} into {@linkcode Serializable} + * @param {Type} value + * @returns {Serializable} The serializable value + */ +export function toSerializable(value: Type): Serializable { + return value.value.toString(); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: Timestamp): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toSerializable(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/TransactionExpiry.ts b/packages/sdk/src/types/TransactionExpiry.ts new file mode 100644 index 000000000..4f43ed384 --- /dev/null +++ b/packages/sdk/src/types/TransactionExpiry.ts @@ -0,0 +1,151 @@ +import { secondsSinceEpoch } from '../util.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.TransactionExpiry; +export type Serializable = string; + +/** + * Representation of a transaction expiry date. + */ +class TransactionExpiry { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Internal representation of expiry. Seconds since unix epoch */ + public readonly expiryEpochSeconds: bigint + ) {} + + public toJSON(): number { + return Number(this.expiryEpochSeconds); + } +} + +/** + * Representation of a transaction expiry date. + */ +export type Type = TransactionExpiry; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is TransactionExpiry { + return value instanceof TransactionExpiry; +} + +/** + * Construct a TransactionExpiry from a number of seconds since unix epoch. + * @param {bigint | number} seconds Number of seconds since unix epoch. + * @throws If provided a negative number. + * @returns The transaction expiry. + */ +export function fromEpochSeconds(seconds: bigint | number): TransactionExpiry { + if (seconds < 0n) { + throw new Error( + 'Invalid transaction expiry: Expiry cannot be before unix epoch.' + ); + } + return new TransactionExpiry(BigInt(seconds)); +} + +/** + * Construct a TransactionExpiry from a Date object. + * @param {Date} expiry The date representing the expiry time. + * @throws If provided the date is from before unix epoch. + * @returns {TransactionExpiry} The transaction expiry. + */ +export function fromDate(expiry: Date): TransactionExpiry { + return fromEpochSeconds(secondsSinceEpoch(expiry)); +} + +/** + * Convert a TransactionExpiry into a Date object. + * @param {TransactionExpiry} expiry A TransactionExpiry to convert. + * @returns {Date} The date object. + */ +export function toDate(expiry: TransactionExpiry): Date { + return new Date(Number(expiry.expiryEpochSeconds) * 1000); +} + +/** + * Construct a TransactionExpiry minutes in the future from the time of calling this function. + * @param {number} minutes The number of minutes in the future to set as the expiry time. + * @returns {TransactionExpiry} The transaction expiry. + */ +export function futureMinutes(minutes: number): TransactionExpiry { + const expiryMillis = Date.now() + minutes * 60 * 1000; + return fromDate(new Date(expiryMillis)); +} + +/** + * Convert expiry from its protobuf encoding. + * @param {Proto.TransactionTime} expiry The expiry in protobuf. + * @returns {TransactionExpiry} The expiry. + */ +export function fromProto(expiry: Proto.TransactionTime): TransactionExpiry { + return new TransactionExpiry(expiry.value); +} + +/** + * Convert expiry into its protobuf encoding. + * @param {TransactionExpiry} expiry The expiry. + * @returns {Proto.TransactionTime} The protobuf encoding. + */ +export function toProto(expiry: TransactionExpiry): Proto.TransactionTime { + return { + value: expiry.expiryEpochSeconds, + }; +} + +/** + * Constructs a {@linkcode Type} from {@linkcode Serializable}. + * @param {Serializable} value + * @returns {Type} The duration. + */ +export function fromSerializable(value: Serializable): Type { + return fromEpochSeconds(BigInt(value)); +} + +/** + * Converts {@linkcode Type} into {@linkcode Serializable} + * @param {Type} value + * @returns {Serializable} The serializable value + */ +export function toSerializable(value: Type): Serializable { + return value.expiryEpochSeconds.toString(); +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: TransactionExpiry): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toSerializable(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromSerializable +); diff --git a/packages/sdk/src/types/TransactionHash.ts b/packages/sdk/src/types/TransactionHash.ts new file mode 100644 index 000000000..6f904ac81 --- /dev/null +++ b/packages/sdk/src/types/TransactionHash.ts @@ -0,0 +1,151 @@ +import { Buffer } from 'buffer/index.js'; +import type { HexString } from '../types.js'; +import type * as Proto from '../grpc-api/v2/concordium/types.js'; +import { + TypedJson, + TypedJsonDiscriminator, + makeFromTypedJson, +} from './util.js'; + +/** + * The {@linkcode TypedJsonDiscriminator} discriminator associated with {@linkcode Type} type. + */ +export const JSON_DISCRIMINATOR = TypedJsonDiscriminator.TransactionHash; +export type Serializable = HexString; + +/** + * The number of bytes used to represent a transaction hash. + */ +const TRANSACTION_HASH_BYTE_LENGTH = 32; + +/** Hash of a transaction. */ +class TransactionHash { + /** Having a private field prevents similar structured objects to be considered the same type (similar to nominal typing). */ + private __type = JSON_DISCRIMINATOR; + constructor( + /** Internal buffer with the hash. */ + public readonly buffer: Uint8Array + ) {} +} + +/** Hash of a transaction. */ +export type Type = TransactionHash; + +/** + * Type predicate for {@linkcode Type} + * + * @param value value to check. + * @returns whether `value` is of type {@linkcode Type} + */ +export function instanceOf(value: unknown): value is TransactionHash { + return value instanceof TransactionHash; +} + +/** + * Create a TransactionHash from a buffer. + * @param {ArrayBuffer} buffer Bytes for the transaction hash. Must be exactly 32 bytes. + * @throws If the provided buffer does not contain 32 bytes. + * @returns {TransactionHash} + */ +export function fromBuffer(buffer: ArrayBuffer): TransactionHash { + if (buffer.byteLength !== TRANSACTION_HASH_BYTE_LENGTH) { + throw new Error( + `Invalid transaction hash provided: Expected a buffer containing 32 bytes, instead got '${Buffer.from( + buffer + ).toString('hex')}'.` + ); + } + return new TransactionHash(new Uint8Array(buffer)); +} + +/** + * Create a TransactionHash from a hex string. + * @param {HexString} hex String with hex encoding of the transaction hash. + * @throws if the encoding does not correspond to exactly 32 bytes. + * @returns {TransactionHash} + */ +export function fromHexString(hex: HexString): TransactionHash { + return fromBuffer(Buffer.from(hex, 'hex')); +} + +/** + * Convert a transaction hash into a hex encoded string. + * @param {TransactionHash} hash TransactionHash to convert to hex. + * @returns {HexString} String with hex encoding. + */ +export function toHexString(hash: TransactionHash): HexString { + return Buffer.from(hash.buffer).toString('hex'); +} + +/** + * Get byte representation of a TransactionHash. + * @param {TransactionHash} hash The transaction hash. + * @returns {ArrayBuffer} Hash represented as bytes. + */ +export function toBuffer(hash: TransactionHash): Uint8Array { + return hash.buffer; +} + +/** + * Convert a transaction hash from its protobuf encoding. + * @param {Proto.TransactionHash} transactionHash The transaction hash in protobuf. + * @returns {TransactionHash} The transaction hash. + */ +export function fromProto( + transactionHash: Proto.TransactionHash +): TransactionHash { + return fromBuffer(transactionHash.value); +} + +/** + * Convert a transaction hash into its protobuf encoding. + * @param {TransactionHash} transactionHash The transaction hash. + * @returns {Proto.TransactionHash} The protobuf encoding. + */ +export function toProto( + transactionHash: TransactionHash +): Proto.TransactionHash { + return { + value: transactionHash.buffer, + }; +} + +/** + * Check if two transaction hashes are the same. + * @param {TransactionHash} left + * @param {TransactionHash} right + * @returns {boolean} True if they are equal. + */ +export function equals(left: TransactionHash, right: TransactionHash): boolean { + for (let i = 0; i < TRANSACTION_HASH_BYTE_LENGTH; i++) { + if (left.buffer.at(i) !== right.buffer.at(i)) { + return false; + } + } + return true; +} + +/** + * Takes an {@linkcode Type} and transforms it to a {@linkcode TypedJson} format. + * + * @param {Type} value - The account address instance to transform. + * @returns {TypedJson} The transformed object. + */ +export function toTypedJSON(value: TransactionHash): TypedJson { + return { + ['@type']: JSON_DISCRIMINATOR, + value: toHexString(value), + }; +} + +/** + * Takes a {@linkcode TypedJson} object and converts it to instance of type {@linkcode Type}. + * + * @param {TypedJson} json - The typed JSON to convert. + * @throws {TypedJsonParseError} - If unexpected JSON string is passed. + * @returns {Type} The parsed instance. + */ +export const fromTypedJSON = /*#__PURE__*/ makeFromTypedJson( + JSON_DISCRIMINATOR, + fromHexString +); diff --git a/packages/common/src/types/VerifiablePresentation.ts b/packages/sdk/src/types/VerifiablePresentation.ts similarity index 97% rename from packages/common/src/types/VerifiablePresentation.ts rename to packages/sdk/src/types/VerifiablePresentation.ts index d512c6421..1578b5bc6 100644 --- a/packages/common/src/types/VerifiablePresentation.ts +++ b/packages/sdk/src/types/VerifiablePresentation.ts @@ -1,6 +1,6 @@ -import { GenericAtomicStatement, AtomicProof } from '../commonProofTypes'; +import { GenericAtomicStatement, AtomicProof } from '../commonProofTypes.js'; import JSONBigInt from 'json-bigint'; -import { AttributeType } from '../web3ProofTypes'; +import { AttributeType } from '../web3-id/web3IdProofTypes.js'; type DIDString = string; diff --git a/packages/common/src/types/VersionedModuleSource.ts b/packages/sdk/src/types/VersionedModuleSource.ts similarity index 63% rename from packages/common/src/types/VersionedModuleSource.ts rename to packages/sdk/src/types/VersionedModuleSource.ts index fdcaffa4b..a0c27a944 100644 --- a/packages/common/src/types/VersionedModuleSource.ts +++ b/packages/sdk/src/types/VersionedModuleSource.ts @@ -1,9 +1,12 @@ -import { ModuleReference } from './moduleReference'; -import * as H from '../contractHelpers'; -import { sha256 } from '../hash'; -import { Buffer } from 'buffer/'; -import { VersionedModuleSource } from '../types'; -import { Cursor, deserializeUInt32BE } from '../deserializationHelpers'; +import * as ModuleReference from './ModuleReference.js'; +import * as H from '../contractHelpers.js'; +import { sha256 } from '../hash.js'; +import { Buffer } from 'buffer/index.js'; +import { VersionedModuleSource } from '../types.js'; +import { schemaBytesFromWasmModule } from '../util.js'; +import { RawModuleSchema } from '../schemaTypes.js'; +import { Cursor, deserializeUInt32BE } from '../deserializationHelpers.js'; +import { encodeWord32 } from '../serializationHelpers.js'; /** Interface of a smart contract containing the name of the contract and every entrypoint. */ export type ContractInterface = { @@ -39,6 +42,19 @@ export function versionedModuleSourceFromBuffer( }; } +/** + * Serialize a versioned module source. Useful when saving to file. + * @param {VersionedModuleSource} moduleSource The versioned module source to serialize. + * @returns {Uint8Array} Buffer with serialized module source. + */ +export function versionedModuleSourceToBuffer( + moduleSource: VersionedModuleSource +): Uint8Array { + const versionBytes = encodeWord32(moduleSource.version); + const lengthBytes = encodeWord32(moduleSource.source.byteLength); + return Buffer.concat([versionBytes, lengthBytes, moduleSource.source]); +} + /** * Calculate the module reference from the module source. * @param {VersionedModuleSource} moduleSource The smart contract module source. @@ -46,12 +62,12 @@ export function versionedModuleSourceFromBuffer( */ export function calculateModuleReference( moduleSource: VersionedModuleSource -): ModuleReference { +): ModuleReference.Type { const prefix = Buffer.alloc(8); prefix.writeUInt32BE(moduleSource.version, 0); prefix.writeUInt32BE(moduleSource.source.length, 4); const hash = sha256([prefix, moduleSource.source]); - return ModuleReference.fromBytes(hash); + return ModuleReference.fromBuffer(hash); } /** @@ -89,6 +105,39 @@ export async function parseModuleInterface( return map; } +/** + * Extract the embedded smart contract schema bytes. Returns `null` if no schema is embedded. + * @param {VersionedModuleSource} moduleSource The smart contract module source. + * @returns {RawModuleSchema | null} The raw module schema if found. + */ +export async function getEmbeddedModuleSchema( + moduleSource: VersionedModuleSource +): Promise { + const wasmModule = await WebAssembly.compile(moduleSource.source); + const versionedSchema = schemaBytesFromWasmModule( + wasmModule, + 'concordium-schema' + ); + if (versionedSchema !== null) { + return { type: 'versioned', buffer: versionedSchema }; + } + const unversionedSchemaV0 = schemaBytesFromWasmModule( + wasmModule, + 'concordium-schema-v1' + ); + if (unversionedSchemaV0 !== null) { + return { type: 'unversioned', version: 0, buffer: unversionedSchemaV0 }; + } + const unversionedSchemaV1 = schemaBytesFromWasmModule( + wasmModule, + 'concordium-schema-v2' + ); + if (unversionedSchemaV1 !== null) { + return { type: 'unversioned', version: 1, buffer: unversionedSchemaV1 }; + } + return null; +} + /** * Get a key from a map, if not present, insert a new value and return this. * @param map The map to get or insert into. diff --git a/packages/common/src/types/blockItemSummary.ts b/packages/sdk/src/types/blockItemSummary.ts similarity index 89% rename from packages/common/src/types/blockItemSummary.ts rename to packages/sdk/src/types/blockItemSummary.ts index 8b945f2e4..8e24dc4fa 100644 --- a/packages/common/src/types/blockItemSummary.ts +++ b/packages/sdk/src/types/blockItemSummary.ts @@ -20,30 +20,33 @@ import { NewEncryptedAmountEvent, TransactionEventTag, TransferredWithScheduleEvent, -} from './transactionEvent'; -import { UpdateInstructionPayload } from './chainUpdate'; +} from './transactionEvent.js'; +import { UpdateInstructionPayload } from './chainUpdate.js'; import { - HexString, TransactionSummaryType, TransactionStatusEnum, - ContractAddress, - Base58String, AccountTransactionType, -} from '../types'; -import { RejectReason } from './rejectReason'; -import { isDefined } from '../util'; -import { isEqualContractAddress } from '../contractHelpers'; +} from '../types.js'; +import { RejectReason } from './rejectReason.js'; +import { isDefined } from '../util.js'; +import { isEqualContractAddress } from '../contractHelpers.js'; +import type * as ContractAddress from './ContractAddress.js'; +import * as AccountAddress from './AccountAddress.js'; +import type * as BlockHash from './BlockHash.js'; +import type * as TransactionHash from './TransactionHash.js'; +import type * as Energy from './Energy.js'; +import type * as ContractEvent from './ContractEvent.js'; export interface BaseBlockItemSummary { index: bigint; - energyCost: bigint; - hash: HexString; + energyCost: Energy.Type; + hash: TransactionHash.Type; } export interface BaseAccountTransactionSummary extends BaseBlockItemSummary { type: TransactionSummaryType.AccountTransaction; cost: bigint; - sender: string; + sender: AccountAddress.Type; } export enum TransactionKindString { @@ -231,7 +234,7 @@ export type AccountTransactionSummary = BaseAccountTransactionSummary & export interface AccountCreationSummary extends BaseBlockItemSummary { type: TransactionSummaryType.AccountCreation; credentialType: 'initial' | 'normal'; - address: string; + address: AccountAddress.Type; regId: string; } @@ -247,7 +250,7 @@ export type BlockItemSummary = | UpdateSummary; export interface BlockItemSummaryInBlock { - blockHash: string; + blockHash: BlockHash.Type; summary: BlockItemSummary; } @@ -377,7 +380,7 @@ export function getReceiverAccount< | TransferWithMemoSummary | TransferWithScheduleSummary | TransferWithScheduleAndMemoSummary ->(summary: T): Base58String; +>(summary: T): AccountAddress.Type; export function getReceiverAccount( summary: Exclude< AccountTransactionSummary, @@ -392,10 +395,10 @@ export function getReceiverAccount( ): undefined; export function getReceiverAccount( summary: BlockItemSummary -): Base58String | undefined; +): AccountAddress.Type | undefined; export function getReceiverAccount( summary: BlockItemSummary -): Base58String | undefined { +): AccountAddress.Type | undefined { if (summary.type !== TransactionSummaryType.AccountTransaction) { return undefined; } @@ -419,7 +422,7 @@ export function getReceiverAccount( */ export function affectedContracts< T extends InitContractSummary | UpdateContractSummary ->(summary: T): ContractAddress[]; +>(summary: T): ContractAddress.Type[]; export function affectedContracts( summary: Exclude< AccountTransactionSummary, @@ -429,10 +432,12 @@ export function affectedContracts( export function affectedContracts( summary: AccountCreationSummary | UpdateSummary ): never[]; -export function affectedContracts(summary: BlockItemSummary): ContractAddress[]; export function affectedContracts( summary: BlockItemSummary -): ContractAddress[] { +): ContractAddress.Type[]; +export function affectedContracts( + summary: BlockItemSummary +): ContractAddress.Type[] { if (summary.type !== TransactionSummaryType.AccountTransaction) { return []; } @@ -443,7 +448,7 @@ export function affectedContracts( } case TransactionKindString.Update: { return summary.events.reduce( - (addresses: ContractAddress[], event) => { + (addresses: ContractAddress.Type[], event) => { if ( event.tag !== TransactionEventTag.Updated || addresses.some(isEqualContractAddress(event.address)) @@ -467,16 +472,20 @@ export function affectedContracts( * * @param {BlockItemSummary} summary - The block item summary to check. * - * @returns {Base58String[]} List of account addresses affected by the transaction. + * @returns {AccountAddress.Type[]} List of account addresses affected by the transaction. */ export function affectedAccounts( summary: AccountTransactionSummary -): Base58String[]; +): AccountAddress.Type[]; export function affectedAccounts( summary: AccountCreationSummary | UpdateSummary ): never[]; -export function affectedAccounts(summary: BlockItemSummary): Base58String[]; -export function affectedAccounts(summary: BlockItemSummary): Base58String[] { +export function affectedAccounts( + summary: BlockItemSummary +): AccountAddress.Type[]; +export function affectedAccounts( + summary: BlockItemSummary +): AccountAddress.Type[] { if (summary.type !== TransactionSummaryType.AccountTransaction) { return []; } @@ -491,13 +500,14 @@ export function affectedAccounts(summary: BlockItemSummary): Base58String[] { return [summary.removed.account]; case TransactionKindString.Update: { return summary.events.reduce( - (addresses: Base58String[], event) => { + (addresses: AccountAddress.Type[], event) => { if ( event.tag === TransactionEventTag.Transferred && - event.to.type === 'AddressAccount' && - !addresses.includes(event.to.address) + !addresses.some( + AccountAddress.equals.bind(undefined, event.to) + ) ) { - return [...addresses, event.to.address]; + return [...addresses, event.to]; } return addresses; }, @@ -507,7 +517,10 @@ export function affectedAccounts(summary: BlockItemSummary): Base58String[] { default: { const receiver = getReceiverAccount(summary); - if (summary.sender === receiver || receiver === undefined) { + if ( + receiver === undefined || + AccountAddress.equals(summary.sender, receiver) + ) { return [summary.sender]; } @@ -517,12 +530,12 @@ export function affectedAccounts(summary: BlockItemSummary): Base58String[] { } export type SummaryContractUpdateLog = { - address: ContractAddress; - events: HexString[]; + address: ContractAddress.Type; + events: ContractEvent.Type[]; }; /** - * Gets a list of update logs, each consisting of a {@link ContractAddress} and a list of {@link HexString} events. + * Gets a list of update logs, each consisting of a {@link ContractAddress.Type} and a list of {@link ContractEvent.Type} events. * The list will be empty for any transaction type but {@link UpdateContractSummary} contract updates. * * @param {BlockItemSummary} summary - The block item summary to check. diff --git a/packages/common/src/types/chainUpdate.ts b/packages/sdk/src/types/chainUpdate.ts similarity index 92% rename from packages/common/src/types/chainUpdate.ts rename to packages/sdk/src/types/chainUpdate.ts index 44314178a..626d682f2 100644 --- a/packages/common/src/types/chainUpdate.ts +++ b/packages/sdk/src/types/chainUpdate.ts @@ -1,17 +1,12 @@ -import { - Amount, +import type { AuthorizationsV0, AuthorizationsV1, Base58String, - Duration, - Energy, FinalizationCommitteeParameters, GasRewardsV0, GasRewardsV1, HexString, TimeoutParameters, -} from '..'; -import type { IpInfo, ArInfo, VerifyKey, @@ -20,7 +15,11 @@ import type { MintDistribution, MintRate, CommissionRates, -} from '../types'; +} from '../types.js'; +import type * as Energy from './Energy.js'; +import type * as Duration from './Duration.js'; +import type * as CcdAmount from './CcdAmount.js'; +import type * as Timestamp from './Timestamp.js'; type ChainUpdate = { /** The type of the update */ @@ -126,12 +125,15 @@ export type TimeoutParametersUpdate = ChainUpdate< >; /** An update to mininum time between blocks, used from protocol version 6 */ -export type MinBlockTimeUpdate = ChainUpdate; +export type MinBlockTimeUpdate = ChainUpdate< + UpdateType.MinBlockTime, + Duration.Type +>; /** An update to maximum amount of energy per block, used from protocol version 6 */ export type BlockEnergyLimitUpdate = ChainUpdate< UpdateType.BlockEnergyLimit, - Energy + Energy.Type >; /** An update to finalization committee parameters, used from protocol version 6 */ @@ -183,8 +185,16 @@ export type CommonUpdate = /** A union of chain updates */ export type UpdateInstructionPayload = CommonUpdate | RootUpdate | Level1Update; -/** A union of possible pending updates */ -export type PendingUpdate = +/** A pending update */ +export type PendingUpdate = { + /** The effective time of the update */ + effectiveTime: Timestamp.Type; + /** The effect of the update */ + effect: PendingUpdateEffect; +}; + +/** A union of possible effects */ +export type PendingUpdateEffect = | CommonUpdate | PendingHigherLevelKeyUpdate | PendingAuthorizationKeysUpdate; @@ -271,7 +281,7 @@ export interface CommissionRanges { export interface PoolParameters { passiveCommissions: CommissionRates; commissionBounds: CommissionRanges; - minimumEquityCapital: Amount; + minimumEquityCapital: CcdAmount.Type; capitalBound: number; leverageBound: Fraction; } diff --git a/packages/common/src/types/errors.ts b/packages/sdk/src/types/errors.ts similarity index 100% rename from packages/common/src/types/errors.ts rename to packages/sdk/src/types/errors.ts diff --git a/packages/sdk/src/types/json.ts b/packages/sdk/src/types/json.ts new file mode 100644 index 000000000..610c10137 --- /dev/null +++ b/packages/sdk/src/types/json.ts @@ -0,0 +1,163 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import * as Parameter from './Parameter.js'; +import * as ReturnValue from './ReturnValue.js'; +import * as SequenceNumber from './SequenceNumber.js'; +import * as Energy from './Energy.js'; +import * as TransactionHash from './TransactionHash.js'; +import * as BlockHash from './BlockHash.js'; +import * as ContractName from './ContractName.js'; +import * as InitName from './InitName.js'; +import * as ReceiveName from './ReceiveName.js'; +import * as CredentialRegistrationId from './CredentialRegistrationId.js'; +import * as AccountAddress from './AccountAddress.js'; +import * as ContractAddress from './ContractAddress.js'; +import * as EntrypointName from './EntrypointName.js'; +import * as Timestamp from './Timestamp.js'; +import * as Duration from './Duration.js'; +import * as CcdAmount from './CcdAmount.js'; +import * as TransactionExpiry from './TransactionExpiry.js'; +import * as ModuleReference from './ModuleReference.js'; +import { + DataBlob, + JSON_DISCRIMINATOR as DATA_BLOB_DISCRIMINATOR, +} from './DataBlob.js'; +import { isTypedJsonCandidate } from './util.js'; + +function reviveConcordiumTypes(value: unknown) { + if (isTypedJsonCandidate(value)) { + switch (value['@type']) { + case Parameter.JSON_DISCRIMINATOR: + return Parameter.fromTypedJSON(value); + case ReturnValue.JSON_DISCRIMINATOR: + return ReturnValue.fromTypedJSON(value); + case SequenceNumber.JSON_DISCRIMINATOR: + return SequenceNumber.fromTypedJSON(value); + case Energy.JSON_DISCRIMINATOR: + return Energy.fromTypedJSON(value); + case TransactionHash.JSON_DISCRIMINATOR: + return TransactionHash.fromTypedJSON(value); + case BlockHash.JSON_DISCRIMINATOR: + return BlockHash.fromTypedJSON(value); + case ContractName.JSON_DISCRIMINATOR: + return ContractName.fromTypedJSON(value); + case InitName.JSON_DISCRIMINATOR: + return InitName.fromTypedJSON(value); + case ReceiveName.JSON_DISCRIMINATOR: + return ReceiveName.fromTypedJSON(value); + case CredentialRegistrationId.JSON_DISCRIMINATOR: + return CredentialRegistrationId.fromTypedJSON(value); + case AccountAddress.JSON_DISCRIMINATOR: + return AccountAddress.fromTypedJSON(value); + case ContractAddress.JSON_DISCRIMINATOR: + return ContractAddress.fromTypedJSON(value); + case EntrypointName.JSON_DISCRIMINATOR: + return EntrypointName.fromTypedJSON(value); + case Timestamp.JSON_DISCRIMINATOR: + return Timestamp.fromTypedJSON(value); + case Duration.JSON_DISCRIMINATOR: + return Duration.fromTypedJSON(value); + case CcdAmount.JSON_DISCRIMINATOR: + return CcdAmount.fromTypedJSON(value); + case TransactionExpiry.JSON_DISCRIMINATOR: + return TransactionExpiry.fromTypedJSON(value); + case ModuleReference.JSON_DISCRIMINATOR: + return ModuleReference.fromTypedJSON(value); + case DATA_BLOB_DISCRIMINATOR: + return DataBlob.fromTypedJSON(value); + } + } + + return value; +} + +/** + * Acts as an inverse for {@linkcode jsonStringify} + */ +export function jsonParse( + input: string, + reviver?: (this: any, key: string, value: any) => any +): any { + return JSON.parse(input, (k, v) => + reviver === undefined + ? reviveConcordiumTypes(v) + : reviver(k, reviveConcordiumTypes(v)) + ); +} + +/** + * Replaces values of concordium domain types with values that can be revived into their original types. + */ +function transformConcordiumType(value: unknown): unknown | undefined { + switch (true) { + case AccountAddress.instanceOf(value): + return AccountAddress.toTypedJSON(value as AccountAddress.Type); + case BlockHash.instanceOf(value): + return BlockHash.toTypedJSON(value as BlockHash.Type); + case CcdAmount.instanceOf(value): + return CcdAmount.toTypedJSON(value as CcdAmount.Type); + case ContractAddress.instanceOf(value): + return ContractAddress.toTypedJSON(value as ContractAddress.Type); + case ContractName.instanceOf(value): + return ContractName.toTypedJSON(value as ContractName.Type); + case CredentialRegistrationId.instanceOf(value): + return CredentialRegistrationId.toTypedJSON( + value as CredentialRegistrationId.Type + ); + case value instanceof DataBlob: + return (value as DataBlob).toTypedJSON(); + case Duration.instanceOf(value): + return Duration.toTypedJSON(value as Duration.Type); + case Energy.instanceOf(value): + return Energy.toTypedJSON(value as Energy.Type); + case EntrypointName.instanceOf(value): + return EntrypointName.toTypedJSON(value as EntrypointName.Type); + case InitName.instanceOf(value): + return InitName.toTypedJSON(value as InitName.Type); + case ModuleReference.instanceOf(value): + return ModuleReference.toTypedJSON(value as ModuleReference.Type); + case Parameter.instanceOf(value): + return Parameter.toTypedJSON(value as Parameter.Type); + case ReceiveName.instanceOf(value): + return ReceiveName.toTypedJSON(value as ReceiveName.Type); + case ReturnValue.instanceOf(value): + return ReturnValue.toTypedJSON(value as ReturnValue.Type); + case SequenceNumber.instanceOf(value): + return SequenceNumber.toTypedJSON(value as SequenceNumber.Type); + case Timestamp.instanceOf(value): + return Timestamp.toTypedJSON(value as Timestamp.Type); + case TransactionExpiry.instanceOf(value): + return TransactionExpiry.toTypedJSON( + value as TransactionExpiry.Type + ); + case TransactionHash.instanceOf(value): + return TransactionHash.toTypedJSON(value as TransactionHash.Type); + } + + return undefined; +} + +type ReplacerFun = (this: any, key: string, value: any) => any; + +function ccdTypesReplacer(this: any, key: string, value: any): any { + const rawValue = this[key]; + return transformConcordiumType(rawValue) ?? value; +} + +/** + * Stringify, which ensures concordium domain types are stringified in a restorable fashion. + * + * @param value A JavaScript value, usually an object or array, to be converted. + * @param replacer A function that transforms the results. + * @param space Adds indentation, white space, and line break characters to the return-value JSON text to make it easier to read. + */ +export function jsonStringify( + input: any, + replacer?: ReplacerFun, + space?: string | number +): string { + function replacerFunction(this: any, key: string, value: any) { + const transformedValue = ccdTypesReplacer.call(this, key, value); + return replacer?.call(this, key, transformedValue) ?? transformedValue; + } + return JSON.stringify(input, replacerFunction, space); +} diff --git a/packages/common/src/types/rejectReason.ts b/packages/sdk/src/types/rejectReason.ts similarity index 80% rename from packages/common/src/types/rejectReason.ts rename to packages/sdk/src/types/rejectReason.ts index c18f00415..70f9f4d7c 100644 --- a/packages/common/src/types/rejectReason.ts +++ b/packages/sdk/src/types/rejectReason.ts @@ -1,12 +1,10 @@ -import { - Address, - Base58String, - DigitString, - HexString, - ContractAddress, - Amount, - BakerId, -} from '../types'; +import { Address, Base58String, HexString, BakerId } from '../types.js'; +import type * as ContractAddress from './ContractAddress.js'; +import type * as ReceiveName from './ReceiveName.js'; +import type * as CcdAmount from './CcdAmount.js'; +import type * as Parameter from './Parameter.js'; +import type * as InitName from './InitName.js'; +import type * as ModuleReference from './ModuleReference.js'; /* * An enum containing all the possible reject reasons that can be @@ -76,10 +74,10 @@ export enum RejectReasonTag { export interface RejectedReceive { tag: RejectReasonTag.RejectedReceive; - contractAddress: ContractAddress; - receiveName: string; + contractAddress: ContractAddress.Type; + receiveName: ReceiveName.Type; rejectReason: number; - parameter: HexString; + parameter: Parameter.Type; } export interface RejectedInit { @@ -155,14 +153,6 @@ export interface BakerIdRejectReason { contents: BakerId; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface NumberRejectReason { - tag: BakerIdRejectReasonTag; - contents: number; -} - export interface SimpleRejectReason { tag: SimpleRejectReasonTag; } @@ -170,54 +160,30 @@ export interface SimpleRejectReason { export interface InvalidReceiveMethod { tag: RejectReasonTag.InvalidReceiveMethod; contents: { - moduleRef: HexString; - receiveName: string; + moduleRef: ModuleReference.Type; + receiveName: ReceiveName.Type; }; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface InvalidReceiveMethodV1 { - tag: RejectReasonTag.InvalidReceiveMethod; - contents: [HexString, string]; // [moduleRef, receiveName] -} - export interface InvalidInitMethod { tag: RejectReasonTag.InvalidInitMethod; contents: { - moduleRef: HexString; - initName: string; // [moduleRef, initName] + moduleRef: ModuleReference.Type; + initName: InitName.Type; }; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface InvalidInitMethodV1 { - tag: RejectReasonTag.InvalidInitMethod; - contents: [HexString, string]; // [moduleRef, initName] -} - export interface AmountTooLarge { tag: RejectReasonTag.AmountTooLarge; contents: { address: Address; - amount: Amount; + amount: CcdAmount.Type; }; } -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export interface AmountTooLargeV1 { - tag: RejectReasonTag.AmountTooLarge; - contents: [Address, DigitString]; // [address, amount] -} - export interface InvalidContractAddress { tag: RejectReasonTag.InvalidContractAddress; - contents: ContractAddress; + contents: ContractAddress.Type; } export type CredIdsRejectReasonTag = @@ -243,13 +209,3 @@ export type RejectReason = | InvalidReceiveMethod | InvalidInitMethod | AmountTooLarge; - -/** - * @deprecated This is type for describing return types for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export type RejectReasonV1 = - | RejectReasonCommon - | NumberRejectReason - | InvalidReceiveMethodV1 - | InvalidInitMethodV1 - | AmountTooLargeV1; diff --git a/packages/common/src/types/transactionEvent.ts b/packages/sdk/src/types/transactionEvent.ts similarity index 81% rename from packages/common/src/types/transactionEvent.ts rename to packages/sdk/src/types/transactionEvent.ts index 7c40c89e9..fbdffaf42 100644 --- a/packages/common/src/types/transactionEvent.ts +++ b/packages/sdk/src/types/transactionEvent.ts @@ -1,18 +1,22 @@ import type { OpenStatusText, - ContractAddress, ReleaseSchedule, ContractVersion, Address, - Base58String, ModuleRef, HexString, EventDelegationTarget, - Amount, BakerId, DelegatorId, -} from '../types'; -import type { UpdateInstructionPayload } from './chainUpdate'; +} from '../types.js'; +import type { UpdateInstructionPayload } from './chainUpdate.js'; +import type * as ContractAddress from './ContractAddress.js'; +import type * as AccountAddress from './AccountAddress.js'; +import type * as Parameter from './Parameter.js'; +import type * as ReceiveName from './ReceiveName.js'; +import type * as InitName from './InitName.js'; +import type * as ContractEvent from './ContractEvent.js'; +import type * as CcdAmount from './CcdAmount.js'; export enum TransactionEventTag { ModuleDeployed = 'ModuleDeployed', @@ -54,11 +58,7 @@ export enum TransactionEventTag { } export type TransactionEvent = - | TransferredEvent - | UpdatedEvent - | ResumedEvent - | InterruptedEvent - | UpgradedEvent + | AccountTransferredEvent | MemoEvent | TransferredWithScheduleEvent | AccountCreatedEvent @@ -92,30 +92,37 @@ export type TransactionEvent = export interface InterruptedEvent { tag: TransactionEventTag.Interrupted; - address: ContractAddress; - events: HexString[]; + address: ContractAddress.Type; + events: ContractEvent.Type[]; } export interface ResumedEvent { tag: TransactionEventTag.Resumed; - address: ContractAddress; + address: ContractAddress.Type; success: boolean; } export interface UpdatedEvent { tag: TransactionEventTag.Updated; - address: ContractAddress; + address: ContractAddress.Type; instigator: Address; - amount: Amount; + amount: CcdAmount.Type; contractVersion: ContractVersion; - message: HexString; - receiveName: string; - events: HexString[]; + message: Parameter.Type; + receiveName: ReceiveName.Type; + events: ContractEvent.Type[]; +} + +export interface TransferredEvent { + tag: TransactionEventTag.Transferred; + amount: CcdAmount.Type; + to: AccountAddress.Type; + from: ContractAddress.Type; } export interface UpgradedEvent { tag: TransactionEventTag.Upgraded; - address: ContractAddress; + address: ContractAddress.Type; from: ModuleRef; to: ModuleRef; } @@ -127,9 +134,9 @@ export interface DataRegisteredEvent { export interface ContractInitializedEvent { tag: TransactionEventTag.ContractInitialized; - address: ContractAddress; - amount: Amount; - initName: string; + address: ContractAddress.Type; + amount: CcdAmount.Type; + initName: InitName.Type; events: HexString[]; contractVersion: ContractVersion; ref: ModuleRef; @@ -144,21 +151,13 @@ export interface ModuleDeployedEvent { export interface AccountTransferredEvent { tag: TransactionEventTag.Transferred; - amount: Amount; - to: Base58String; -} - -export interface TransferredEvent { - tag: TransactionEventTag.Transferred; - amount: Amount; - to: Address; - from?: Address; + amount: CcdAmount.Type; + to: AccountAddress.Type; } export interface TransferredWithScheduleEvent { tag: TransactionEventTag.TransferredWithSchedule; - to: Base58String; - from?: Base58String; + to: AccountAddress.Type; amount: ReleaseSchedule[]; } @@ -169,25 +168,25 @@ export interface MemoEvent { export interface AccountCreatedEvent { tag: TransactionEventTag.AccountCreated; - account: Base58String; + account: AccountAddress.Type; } export interface AmountAddedByDecryptionEvent { tag: TransactionEventTag.AmountAddedByDecryption; - account: Base58String; - amount: Amount; + account: AccountAddress.Type; + amount: CcdAmount.Type; } export interface EncryptedSelfAmountAddedEvent { tag: TransactionEventTag.EncryptedSelfAmountAdded; - account: Base58String; - amount: Amount; + account: AccountAddress.Type; + amount: CcdAmount.Type; newAmount: string; } export interface EncryptedAmountsRemovedEvent { tag: TransactionEventTag.EncryptedAmountsRemoved; - account: Base58String; + account: AccountAddress.Type; inputAmount: HexString; newAmount: HexString; upToIndex: number; @@ -195,7 +194,7 @@ export interface EncryptedAmountsRemovedEvent { export interface NewEncryptedAmountEvent { tag: TransactionEventTag.NewEncryptedAmount; - account: Base58String; + account: AccountAddress.Type; newIndex: number; encryptedAmount: HexString; } @@ -203,7 +202,7 @@ export interface NewEncryptedAmountEvent { export interface CredentialDeployedEvent { tag: TransactionEventTag.CredentialDeployed; regid: HexString; - account: Base58String; + account: AccountAddress.Type; } export interface CredentialKeysUpdatedEvent { @@ -213,7 +212,7 @@ export interface CredentialKeysUpdatedEvent { export interface CredentialsUpdatedEvent { tag: TransactionEventTag.CredentialsUpdated; - account: Base58String; + account: AccountAddress.Type; newCredIds: HexString[]; removedCredIds: HexString[]; newThreshold: number; @@ -226,20 +225,20 @@ export interface DelegatorEvent { | TransactionEventTag.DelegationAdded | TransactionEventTag.DelegationRemoved; delegatorId: DelegatorId; - account: Base58String; + account: AccountAddress.Type; } export interface DelegationSetDelegationTargetEvent { tag: TransactionEventTag.DelegationSetDelegationTarget; delegatorId: DelegatorId; - account: Base58String; + account: AccountAddress.Type; delegationTarget: EventDelegationTarget; } export interface DelegationSetRestakeEarningsEvent { tag: TransactionEventTag.DelegationSetRestakeEarnings; delegatorId: DelegatorId; - account: Base58String; + account: AccountAddress.Type; restakeEarnings: boolean; } @@ -248,8 +247,8 @@ export interface DelegationStakeChangedEvent { | TransactionEventTag.DelegationStakeDecreased | TransactionEventTag.DelegationStakeIncreased; delegatorId: DelegatorId; - account: Base58String; - newStake: bigint; + account: AccountAddress.Type; + newStake: CcdAmount.Type; } // Baker Events @@ -257,18 +256,18 @@ export interface DelegationStakeChangedEvent { export interface BakerAddedEvent { tag: TransactionEventTag.BakerAdded; bakerId: BakerId; - account: string; + account: AccountAddress.Type; signKey: string; electionKey: string; aggregationKey: string; - stake: bigint; + stake: CcdAmount.Type; restakeEarnings: boolean; } export interface BakerRemovedEvent { tag: TransactionEventTag.BakerRemoved; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; } export interface BakerStakeChangedEvent { @@ -276,21 +275,21 @@ export interface BakerStakeChangedEvent { | TransactionEventTag.BakerStakeIncreased | TransactionEventTag.BakerStakeDecreased; bakerId: BakerId; - account: Base58String; - newStake: bigint; + account: AccountAddress.Type; + newStake: CcdAmount.Type; } export interface BakerSetRestakeEarningsEvent { tag: TransactionEventTag.BakerSetRestakeEarnings; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; restakeEarnings: boolean; } export interface BakerKeysUpdatedEvent { tag: TransactionEventTag.BakerKeysUpdated; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; signKey: HexString; electionKey: HexString; aggregationKey: HexString; @@ -299,35 +298,35 @@ export interface BakerKeysUpdatedEvent { export interface BakerSetOpenStatusEvent { tag: TransactionEventTag.BakerSetOpenStatus; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; openStatus: OpenStatusText; } export interface BakerSetMetadataURLEvent { tag: TransactionEventTag.BakerSetMetadataURL; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; metadataURL: string; } export interface BakerSetFinalizationRewardCommissionEvent { tag: TransactionEventTag.BakerSetFinalizationRewardCommission; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; finalizationRewardCommission: number; } export interface BakerSetBakingRewardCommissionEvent { tag: TransactionEventTag.BakerSetBakingRewardCommission; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; bakingRewardCommission: number; } export interface BakerSetTransactionFeeCommissionEvent { tag: TransactionEventTag.BakerSetTransactionFeeCommission; bakerId: BakerId; - account: Base58String; + account: AccountAddress.Type; transactionFeeCommission: number; } diff --git a/packages/sdk/src/types/util.ts b/packages/sdk/src/types/util.ts new file mode 100644 index 000000000..ca17fa100 --- /dev/null +++ b/packages/sdk/src/types/util.ts @@ -0,0 +1,158 @@ +/** + * Discriminator for {@linkcode TypedJson}. The member used to identify each type is + * exported from each type module and can be accessed through named export `JSON_DISCRIMINATOR`. + */ +export enum TypedJsonDiscriminator { + AccountAddress = 'ccd_account_address', + BlockHash = 'ccd_block_hash', + CcdAmount = 'ccd_ccd_amount', + ContractAddress = 'ccd_contract_address', + ContractName = 'ccd_contract_name', + CredentialRegistrationId = 'ccd_cred_reg_id', + DataBlob = 'ccd_data_blob', + Duration = 'ccd_duration', + Energy = 'ccd_energy', + EntrypointName = 'ccd_entrypoint_name', + InitName = 'ccd_init_name', + ModuleReference = 'ccd_module_reference', + Parameter = 'ccd_parameter', + ReceiveName = 'ccd_receive_name', + ReturnValue = 'ccd_return_value', + SequenceNumber = 'ccd_sequence_number', + Timestamp = 'ccd_timestamp', + TransactionExpiry = 'ccd_transaction_expiry', + TransactionHash = 'ccd_transaction_hash', +} + +/** + * Type describing the JSON representation of strong types used in the SDK. + * + * @template V - The serializable JSON value + */ +export type TypedJson = { + /** The type discriminator */ + ['@type']: TypedJsonDiscriminator; + /** The serializable type value */ + value: V; +}; + +/** + * Type predeicate for {@linkcode TypedJson}. + * + * @param value value to test + * @returns boolean indicating whether `value` is {@linkcode TypedJson} + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function isTypedJsonCandidate(value: unknown): value is TypedJson { + if (typeof value !== 'object' || value === null) { + return false; + } + + return ['@type', 'value'].every((name) => + Object.getOwnPropertyNames(value).includes(name) + ); +} + +/** + * Describes the type of the JsonParseError. + */ +export enum TypedJsonParseErrorCode { + /** Malformed JSON passed to parser function */ + MALFORMED = 'MALFORMED', + /** JSON passed to parser function had unexpected {@linkcode TypedJsonDiscriminator} type discriminator */ + WRONG_TYPE = 'WRONG_TYPE', + /** Value could not be parsed successfully */ + INVALID_VALUE = 'INVALID_VALUE', +} + +/** + * Error thrown from trying to parse objects of type {@linkcode TypedJson} + */ +export abstract class TypedJsonParseError extends Error { + public abstract readonly code: TypedJsonParseErrorCode; + private _name: string = 'TypedJsonParseError'; + + /** + * @param {string} message - The error message. + */ + constructor(message: string) { + super(message); + } + + public override get name() { + return `${this._name}.${this.code}`; + } +} + +export class TypedJsonMalformedError extends TypedJsonParseError { + public code = TypedJsonParseErrorCode.MALFORMED; +} + +export class TypedJsonWrongTypeError extends TypedJsonParseError { + public code = TypedJsonParseErrorCode.WRONG_TYPE; + + /** + * @param {TypedJsonDiscriminator} expected - The discriminator expected by the typed JSON parser. + * @param {TypedJsonDiscriminator} actual - The discriminator received by the typed JSON parser. + */ + constructor( + public readonly expected: TypedJsonDiscriminator, + public readonly actual: TypedJsonDiscriminator + ) { + super( + `Wrong type discriminator found in JSON. Expected "${expected}", found "${actual}"` + ); + } +} + +export class TypedJsonInvalidValueError extends TypedJsonParseError { + public code = TypedJsonParseErrorCode.INVALID_VALUE; + + /** + * @param {string} inner - The original cause of the error. + */ + constructor(public readonly inner: unknown) { + super(`Unable to parse value (${(inner as Error)?.message ?? inner})`); + + if (inner instanceof Error) { + this.stack = inner.stack ?? this.stack; + } + } +} + +/** + * Creates a function to convert {@linkcode TypedJson} to their corresponding type instance. + * + * @template V - The JSON value + * @template T - The type returned + * + * @param {TypedJsonDiscriminator} expectedTypeDiscriminator - The discriminator expected in the JSON string parsed + * @param {Function} toType - A function converting values of type `V` to instances of type `T` + * + * @throws {TypedJsonParseError} {@linkcode TypedJsonParseError} if the returned function fails to parse the passed value. + * + * @returns The JSON parser function + */ +export function makeFromTypedJson( + expectedTypeDiscriminator: TypedJsonDiscriminator, + toType: (value: V) => T +) { + return ({ ['@type']: type, value }: TypedJson): T | V => { + if (type === undefined || value === undefined) { + throw new TypedJsonMalformedError( + 'Expected both "@type" and "value" properties to be available in JSON' + ); + } + + if (expectedTypeDiscriminator !== type) { + throw new TypedJsonWrongTypeError(expectedTypeDiscriminator, type); + } + + try { + return toType(value); + } catch (e) { + // Value cannot be successfully parsed + throw new TypedJsonInvalidValueError(value); + } + }; +} diff --git a/packages/common/src/uleb128.ts b/packages/sdk/src/uleb128.ts similarity index 91% rename from packages/common/src/uleb128.ts rename to packages/sdk/src/uleb128.ts index 67c898aee..1e0ade47e 100644 --- a/packages/common/src/uleb128.ts +++ b/packages/sdk/src/uleb128.ts @@ -1,15 +1,15 @@ -import { Buffer } from 'buffer/'; +import { Buffer } from 'buffer/index.js'; /** * Decodes an unsigned leb128 encoded value to bigint. Note that if buffer * that is provided does not _only_ contain the uleb128 encoded number an * error will be thrown. * - * @param {Buffer} buffer - The buffer to decode + * @param {Uint8Array} buffer - The buffer to decode * * @returns {bigint} the decoded bigint value. */ -export const uleb128Decode = (buffer: Buffer): bigint => { +export const uleb128Decode = (buffer: Uint8Array): bigint => { const [bigint, index] = uleb128DecodeWithIndex(buffer); if (index !== buffer.length) { throw Error( @@ -23,14 +23,14 @@ export const uleb128Decode = (buffer: Buffer): bigint => { * Decodes an unsigned leb128 encoded value to bigint and returns it along * with the index of the end of the encoded uleb128 number + 1. * - * @param {Buffer} bytes - The buffer to decode + * @param {UInt8Array} bytes - The buffer to decode * @param {number} index - A non-negative index to decode at, defaults to 0 * * @returns {[bigint, number]} the decoded bigint value and the index of * the end of the encoded uleb128 number + 1. */ export function uleb128DecodeWithIndex( - bytes: Buffer, + bytes: Uint8Array, index = 0 ): [bigint, number] { if (bytes.length <= index) { diff --git a/packages/common/src/util.ts b/packages/sdk/src/util.ts similarity index 70% rename from packages/common/src/util.ts rename to packages/sdk/src/util.ts index fe0606be0..c383b6655 100644 --- a/packages/common/src/util.ts +++ b/packages/sdk/src/util.ts @@ -1,31 +1,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ +import { Buffer } from 'buffer/index.js'; import { AccountTransactionSignature, HexString, IpAddressString, - ReleaseSchedule, -} from './types'; -import { Buffer } from 'buffer/'; - -/** - * Replaces a number in a JSON string with the same number as a - * string, i.e. with quotes (") prior to and after the number. This - * is needed as the default JSON parser cannot intepret BigInts - * correctly when they arrive as JSON numbers. - * @param jsonStruct the JSON structure as a string - * @param keys the keys where the number has to be quoted - * @returns the same JSON string where the numbers at the supplied keys are quoted - */ -function intToString(jsonStruct: string, keys: string[]): string { - let result = jsonStruct; - for (const key of keys) { - result = result.replace( - new RegExp(`"${key}":\\s*([0-9]+)`, 'g'), - `"${key}":"$1"` - ); - } - return result; -} +} from './types.js'; /** * Replaces a string in a JSON string with the same string as a @@ -48,55 +27,6 @@ export function stringToInt(jsonStruct: string, keys: string[]): string { return result; } -/** - * A transformer that converts all the values provided as keys to - * string values. - * @param json the json to transform - * @param bigIntPropertyKeys the keys in the json that must be converted to strings - * @returns the transformed json where numbers have been replaced with strings - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function intToStringTransformer( - bigIntPropertyKeys: string[] -): (json: string) => string { - return (json: string) => intToString(json, bigIntPropertyKeys); -} - -/** - * Builds a JSON.parse() reviver function used to parse dates and big integers. - * @param datePropertyKeys the JSON keys that must be parsed as dates - * @param bigIntPropertyKeys the JSON keys that must be parsed as big integers - * @returns a reviver function that handles dates and big integers - * @deprecated This is helper intented for the JSON-RPC client and the V1 gRPC client, both of which have been deprecated - */ -export function buildJsonResponseReviver( - datePropertyKeys: (keyof T)[], - bigIntPropertyKeys: (keyof T)[] -): (key: string, value: any) => any { - return function reviver(key: string, value: any) { - if (datePropertyKeys.includes(key as keyof T)) { - // Note that we reduce the time precision from nano to milliseconds when doing this conversion. - return new Date(value); - } else if (bigIntPropertyKeys.includes(key as keyof T)) { - // Handle the special case where amount is a scheduled amount, - // which has an array structure. - if (key === 'amount' && Array.isArray(value)) { - const result: ReleaseSchedule[] = []; - for (const entry of value) { - const schedule: ReleaseSchedule = { - timestamp: new Date(entry[0]), - amount: BigInt(entry[1]), - }; - result.push(schedule); - } - return result; - } - return value === null ? value : BigInt(value); - } - return value; - }; -} - /** * Checks if the input string is a valid hexadecimal string. * @param str the string to check for hexadecimal @@ -139,7 +69,7 @@ export function countSignatures( } /** - * Converts a wasm module to a smart contract schema. + * Compiles a wasm module and extracts the smart contract schema. * * @param wasm the wasm module as a Buffer * @@ -148,16 +78,35 @@ export function countSignatures( * * @returns the smart contract schema as a Buffer */ -export function wasmToSchema(wasm: Buffer): Buffer { +export function wasmToSchema(wasm: ArrayBuffer): Uint8Array { const wasmModule = new WebAssembly.Module(wasm); - const sections = WebAssembly.Module.customSections( + const schemaBytes = schemaBytesFromWasmModule( wasmModule, 'concordium-schema' ); + if (schemaBytes === null) { + throw Error('WASM-Module contains no schema!'); + } + return new Uint8Array(schemaBytes); +} + +/** + * Extracts custom-section containing the smart contract schema if present. + * @param wasmModule the WebAssembly module. + * @returns the smart contract schema as a Buffer or null if not present. + */ +export function schemaBytesFromWasmModule( + wasmModule: WebAssembly.Module, + sectionName: + | 'concordium-schema' + | 'concordium-schema-v1' + | 'concordium-schema-v2' +): ArrayBuffer | null { + const sections = WebAssembly.Module.customSections(wasmModule, sectionName); if (sections.length === 1) { - return Buffer.from(sections[0]); + return sections[0]; } else if (sections.length === 0) { - throw Error('WASM-Module contains no schema!'); + return null; } else { throw Error('Invalid WASM-Module retrieved!'); } @@ -282,3 +231,7 @@ export const makeDynamicFunction = export function isDefined(v?: T): v is T { return v !== undefined; } + +export function toBuffer(s: string, encoding?: string): Buffer { + return Buffer.from(s, encoding); +} diff --git a/packages/sdk/src/versionedTypeHelpers.ts b/packages/sdk/src/versionedTypeHelpers.ts new file mode 100644 index 000000000..62efbd81c --- /dev/null +++ b/packages/sdk/src/versionedTypeHelpers.ts @@ -0,0 +1,133 @@ +import { + Authorizations, + AuthorizationsV1, + BlockInfo, + BlockInfoV0, + BlockInfoV1, + ChainParameters, + ChainParametersV0, + ChainParametersV1, + ChainParametersV2, + ConsensusStatus, + ConsensusStatusV0, + ConsensusStatusV1, + ElectionInfo, + ElectionInfoV0, + ElectionInfoV1, + InstanceInfo, + InstanceInfoV0, + InstanceInfoV1, + RewardStatus, + RewardStatusV1, +} from './types.js'; + +/** + * Whether {@link Authorizations} parameter given is of type {@link AuthorizationsV1} + * + * @deprecated check the `version` member instead. + */ +export const isAuthorizationsV1 = ( + as: Authorizations +): as is AuthorizationsV1 => as.version === 1; + +/** + * Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV0} + * + * @deprecated check the `version` member instead. + */ +export const isChainParametersV0 = ( + cp: ChainParameters +): cp is ChainParametersV0 => cp.version === 0; + +/** + * Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV1} + * + * @deprecated check the `version` member instead. + */ +export const isChainParametersV1 = ( + cp: ChainParameters +): cp is ChainParametersV1 => cp.version === 1; + +/** + * Whether {@link ChainParameters} parameter given is of type {@link ChainParametersV2} + * + * @deprecated check the `version` member instead. + */ +export const isChainParametersV2 = ( + cp: ChainParameters +): cp is ChainParametersV2 => cp.version === 2; + +/** + * Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV0} + * + * @deprecated check the `version` member instead. + */ +export const isBlockInfoV0 = (bi: BlockInfo): bi is BlockInfoV0 => + bi.version === 0; + +/** + * Whether {@link BlockInfo} parameter given is of type {@link BlockInfoV1} + * + * @deprecated check the `version` member instead. + */ +export const isBlockInfoV1 = (bi: BlockInfo): bi is BlockInfoV1 => + bi.version === 1; + +/** + * Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV0} + * + * @deprecated check the `version` member instead. + */ +export const isConsensusStatusV0 = ( + cs: ConsensusStatus +): cs is ConsensusStatusV0 => cs.version === 0; + +/** + * Whether {@link ConensusStatus} parameter given is of type {@link ConsensusStatusV1} + * + * @deprecated check the `version` member instead. + */ +export const isConsensusStatusV1 = ( + cs: ConsensusStatus +): cs is ConsensusStatusV1 => cs.version === 1; + +/** + * Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV0} + * + * @deprecated check the `version` member instead. + */ +export const isElectionInfoV0 = (ei: ElectionInfo): ei is ElectionInfoV0 => + ei.version === 0; + +/** + * Whether {@link ElectionInfo} parameter given is of type {@link ElectionInfoV1} + * + * @deprecated check the `version` member instead. + */ +export const isElectionInfoV1 = (ei: ElectionInfo): ei is ElectionInfoV1 => + ei.version === 1; + +/** + * Whether {@link InstanceInfo} parameter given is of type {@link InstanceInfoV1} + * + * @deprecated check the `version` member instead. + */ +export const isInstanceInfoV1 = (info: InstanceInfo): info is InstanceInfoV1 => + info.version === 1; + +/** + * Whether {@link InstanceInfo} parameter given is of type {@link InstanceInfoV0} + * + * @deprecated check the `version` member instead. + */ +export const isInstanceInfoV0 = (info: InstanceInfo): info is InstanceInfoV0 => + info.version === undefined || info.version === 0; + +/** + * Whether {@link RewardStatus} parameter given is of type {@link RewardStatusV1} + * + * @deprecated check the `version` member instead. + */ +export function isRewardStatusV1(rs: RewardStatus): rs is RewardStatusV1 { + return rs.version === 0; +} diff --git a/packages/common/src/HdWallet.ts b/packages/sdk/src/wasm/HdWallet.ts similarity index 93% rename from packages/common/src/HdWallet.ts rename to packages/sdk/src/wasm/HdWallet.ts index f107e62b9..0fce7e8c7 100644 --- a/packages/common/src/HdWallet.ts +++ b/packages/sdk/src/wasm/HdWallet.ts @@ -1,14 +1,10 @@ -import * as wasm from '@concordium/rust-bindings'; +import * as wasm from '@concordium/rust-bindings/wallet'; import { mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'; import { wordlist } from '@scure/bip39/wordlists/english'; -import { Buffer } from 'buffer/'; -import { - AttributesKeys, - Network, - CryptographicParameters, - ContractAddress, -} from './types'; -import { isHex } from './util'; +import { Buffer } from 'buffer/index.js'; +import { isHex } from '../util.js'; +import { AttributesKeys, Network, CryptographicParameters } from '../types.js'; +import type * as ContractAddress from '../types/ContractAddress.js'; /** * Class for Hierarchical Deterministic key derivation for Concordium identities and accounts. @@ -158,7 +154,7 @@ export class ConcordiumHdWallet { } getVerifiableCredentialSigningKey( - issuer: ContractAddress, + issuer: ContractAddress.Type, verifiableCredentialIndex: number ): Buffer { return Buffer.from( @@ -174,7 +170,7 @@ export class ConcordiumHdWallet { } getVerifiableCredentialPublicKey( - issuer: ContractAddress, + issuer: ContractAddress.Type, verifiableCredentialIndex: number ): Buffer { return Buffer.from( diff --git a/packages/sdk/src/wasm/accountHelpers.ts b/packages/sdk/src/wasm/accountHelpers.ts new file mode 100644 index 000000000..0892aa197 --- /dev/null +++ b/packages/sdk/src/wasm/accountHelpers.ts @@ -0,0 +1,19 @@ +import * as wasm from '@concordium/rust-bindings/wallet'; +import { GenerateBakerKeysOutput } from '../types.js'; +import * as AccountAddress from '../types/AccountAddress.js'; + +/** + * Generates random baker keys for the specified account, that can be used with the configureBaker transaction + * @param account the address of the account that the keys should be added to. + * @returns an object containing the public baker keys, their associated proofs and their associated private keys. + */ +export function generateBakerKeys( + account: AccountAddress.Type +): GenerateBakerKeysOutput { + const rawKeys = wasm.generateBakerKeys(AccountAddress.toBase58(account)); + try { + return JSON.parse(rawKeys); + } catch (e) { + throw new Error(rawKeys); + } +} diff --git a/packages/common/src/credentialDeploymentTransactions.ts b/packages/sdk/src/wasm/credentialDeploymentTransactions.ts similarity index 85% rename from packages/common/src/credentialDeploymentTransactions.ts rename to packages/sdk/src/wasm/credentialDeploymentTransactions.ts index af07e0ace..888dad6f4 100644 --- a/packages/common/src/credentialDeploymentTransactions.ts +++ b/packages/sdk/src/wasm/credentialDeploymentTransactions.ts @@ -1,3 +1,6 @@ +import { Buffer } from 'buffer/index.js'; +import * as ed from '#ed25519'; +import * as wasm from '@concordium/rust-bindings/wallet'; import { AttributeKey, CredentialDeploymentTransaction, @@ -10,21 +13,18 @@ import { IpInfo, ArInfo, IdentityObjectV1, - SignedCredentialDeploymentDetails, Network, CredentialPublicKeys, -} from './types'; -import * as wasm from '@concordium/rust-bindings'; -import { TransactionExpiry } from './types/transactionExpiry'; -import { AccountAddress } from './types/accountAddress'; -import { sha256 } from './hash'; -import * as bs58check from 'bs58check'; -import { Buffer } from 'buffer/'; -import { ConcordiumHdWallet } from './HdWallet'; -import { AttributesKeys, CredentialDeploymentDetails, HexString } from '.'; -import { filterRecord, mapRecord } from './util'; -import { getCredentialDeploymentSignDigest } from './serialization'; -import * as ed from '@noble/ed25519'; + AttributesKeys, + CredentialDeploymentDetails, + HexString, +} from '../types.js'; +import * as TransactionExpiry from '../types/TransactionExpiry.js'; +import * as AccountAddress from '../types/AccountAddress.js'; +import { sha256 } from '../hash.js'; +import { ConcordiumHdWallet } from './HdWallet.js'; +import { filterRecord, mapRecord } from '../util.js'; +import { getCredentialDeploymentSignDigest } from '../serialization.js'; /** * Generates the unsigned credential information that has to be signed when @@ -48,7 +48,7 @@ function createUnsignedCredentialInfo( publicKeys: VerifyKey[], credentialIndex: number, revealedAttributes: AttributeKey[], - address?: AccountAddress + address?: AccountAddress.Type ): UnsignedCdiWithRandomness { if (publicKeys.length > 255) { throw new Error( @@ -106,7 +106,7 @@ export function createCredentialDeploymentTransaction( publicKeys: VerifyKey[], credentialIndex: number, revealedAttributes: AttributeKey[], - expiry: TransactionExpiry + expiry: TransactionExpiry.Type ): CredentialDeploymentTransaction { const unsignedCredentialInfo = createUnsignedCredentialInfo( identity, @@ -141,7 +141,7 @@ export function createUnsignedCredentialForExistingAccount( publicKeys: VerifyKey[], credentialIndex: number, revealedAttributes: AttributeKey[], - address: AccountAddress + address: AccountAddress.Type ): UnsignedCdiWithRandomness { return createUnsignedCredentialInfo( identity, @@ -182,13 +182,9 @@ export function buildSignedCredentialForExistingAccount( * @param credId the credential id from a credential deployment transaction * @returns the account address */ -export function getAccountAddress(credId: string): AccountAddress { +export function getAccountAddress(credId: string): AccountAddress.Type { const hashedCredId = sha256([Buffer.from(credId, 'hex')]); - const prefixedWithVersion = Buffer.concat([Buffer.of(1), hashedCredId]); - const accountAddress = new AccountAddress( - bs58check.encode(prefixedWithVersion) - ); - return accountAddress; + return AccountAddress.fromBuffer(hashedCredId); } type CredentialInputCommon = { @@ -206,26 +202,6 @@ export type CredentialInput = CredentialInputCommon & { identityIndex: number; }; -/** - * Creates a credential for a new account, using the version 1 algorithm, which uses a seed to generate keys and commitments. - * @deprecated This function outputs the format used by the JSON-RPC client, createCredentialTransaction should be used instead. - */ -export function createCredentialV1( - input: CredentialInput & { expiry: number } -): SignedCredentialDeploymentDetails { - const rawRequest = wasm.createCredentialV1(JSON.stringify(input)); - let info: CredentialDeploymentInfo; - try { - info = JSON.parse(rawRequest); - } catch (e) { - throw new Error(rawRequest); - } - return { - expiry: TransactionExpiry.fromEpochSeconds(BigInt(input.expiry)), - cdi: info, - }; -} - export type CredentialInputNoSeed = CredentialInputCommon & { idCredSec: HexString; prfKey: HexString; @@ -239,7 +215,7 @@ export type CredentialInputNoSeed = CredentialInputCommon & { */ export function createCredentialTransaction( input: CredentialInput, - expiry: TransactionExpiry + expiry: TransactionExpiry.Type ): CredentialDeploymentTransaction { const wallet = ConcordiumHdWallet.fromHex(input.seedAsHex, input.net); const publicKey = wallet @@ -306,7 +282,7 @@ export function createCredentialTransaction( */ export function createCredentialTransactionNoSeed( input: CredentialInputNoSeed, - expiry: TransactionExpiry + expiry: TransactionExpiry.Type ): CredentialDeploymentTransaction { const rawRequest = wasm.createUnsignedCredentialV1(JSON.stringify(input)); let info: UnsignedCdiWithRandomness; @@ -326,5 +302,5 @@ export async function signCredentialTransaction( signingKey: HexString ): Promise { const digest = getCredentialDeploymentSignDigest(credDeployment); - return Buffer.from(await ed.sign(digest, signingKey)).toString('hex'); + return Buffer.from(await ed.signAsync(digest, signingKey)).toString('hex'); } diff --git a/packages/sdk/src/wasm/deserialization.ts b/packages/sdk/src/wasm/deserialization.ts new file mode 100644 index 000000000..20d9fc9dc --- /dev/null +++ b/packages/sdk/src/wasm/deserialization.ts @@ -0,0 +1,83 @@ +import * as wasm from '@concordium/rust-bindings/wallet'; +import { + deserializeAccountTransaction, + deserializeUint8, +} from '../deserialization.js'; +import { Cursor } from '../deserializationHelpers.js'; +import { + AccountTransaction, + AccountTransactionSignature, + BlockItemKind, + TypedCredentialDeployment, +} from '../types.js'; + +function deserializeCredentialDeployment(serializedDeployment: Cursor) { + const raw = wasm.deserializeCredentialDeployment( + serializedDeployment.read().toString('hex') + ); + try { + const parsed = JSON.parse(raw); + return { + credential: parsed.credential, + expiry: parsed.messageExpiry, + }; + } catch { + // If the return value is not a proper JSON, it should be an error message. + throw new Error(raw); + } +} + +export type BlockItem = + | { + kind: BlockItemKind.AccountTransactionKind; + transaction: { + accountTransaction: AccountTransaction; + signatures: AccountTransactionSignature; + }; + } + | { + kind: BlockItemKind.CredentialDeploymentKind; + transaction: { + credential: TypedCredentialDeployment; + expiry: number; + }; + }; + +/** + * Deserializes a transaction, from the binary format used to send it to the node, back into an js object. + * @param serializedTransaction A buffer containing the binary transaction. It is expected to start with the version and blockItemKind. + * @returns An object specifiying the blockItemKind that the transaction has. The object also contains the actual transaction under the transaction field. + **/ +export function deserializeTransaction( + serializedTransaction: ArrayBuffer +): BlockItem { + const cursor = Cursor.fromBuffer(serializedTransaction); + + const version = deserializeUint8(cursor); + if (version !== 0) { + throw new Error( + 'Supplied version ' + + version + + ' is not valid. Only transactions with version 0 format are supported' + ); + } + const blockItemKind = deserializeUint8(cursor); + switch (blockItemKind) { + case BlockItemKind.AccountTransactionKind: + return { + kind: BlockItemKind.AccountTransactionKind, + transaction: deserializeAccountTransaction(cursor), + }; + case BlockItemKind.CredentialDeploymentKind: + return { + kind: BlockItemKind.CredentialDeploymentKind, + transaction: deserializeCredentialDeployment(cursor), + }; + case BlockItemKind.UpdateInstructionKind: + throw new Error( + 'deserialization of UpdateInstructions is not supported' + ); + default: + throw new Error('Invalid blockItemKind'); + } +} diff --git a/packages/common/src/identity.ts b/packages/sdk/src/wasm/identity.ts similarity index 67% rename from packages/common/src/identity.ts rename to packages/sdk/src/wasm/identity.ts index 81a3663e1..bf8ea4fca 100644 --- a/packages/common/src/identity.ts +++ b/packages/sdk/src/wasm/identity.ts @@ -1,5 +1,5 @@ -import * as wasm from '@concordium/rust-bindings'; -import { +import * as wasm from '@concordium/rust-bindings/wallet'; +import type { ArInfo, CryptographicParameters, IdObjectRequestV1, @@ -7,7 +7,8 @@ import { IpInfo, Network, Versioned, -} from './types'; +} from '../types.js'; +import type { IdProofInput, IdProofOutput } from '../id/index.js'; export type IdentityRequestInput = { ipInfo: IpInfo; @@ -57,3 +58,17 @@ export function createIdentityRecoveryRequest( throw new Error(rawRequest); } } + +/** + * Given a statement about an identity and the inputs necessary to prove the statement, produces a proof that the associated identity fulfills the statement. + */ +export function getIdProof(input: IdProofInput): IdProofOutput { + const rawRequest = wasm.createIdProof(JSON.stringify(input)); + let out: IdProofOutput; + try { + out = JSON.parse(rawRequest); + } catch (e) { + throw new Error(rawRequest); + } + return out; +} diff --git a/packages/sdk/src/wasm/index.ts b/packages/sdk/src/wasm/index.ts new file mode 100644 index 000000000..16a688cb1 --- /dev/null +++ b/packages/sdk/src/wasm/index.ts @@ -0,0 +1,11 @@ +export { + getCredentialDeploymentTransactionHash, + serializeCredentialDeploymentTransactionForSubmission, + serializeCredentialDeploymentPayload, +} from './serialization.js'; +export { deserializeTransaction } from './deserialization.js'; +export { generateBakerKeys } from './accountHelpers.js'; +export * from './HdWallet.js'; +export * from './identity.js'; +export * from './credentialDeploymentTransactions.js'; +export * from './web3Id.js'; diff --git a/packages/sdk/src/wasm/serialization.ts b/packages/sdk/src/wasm/serialization.ts new file mode 100644 index 000000000..a78b3b760 --- /dev/null +++ b/packages/sdk/src/wasm/serialization.ts @@ -0,0 +1,66 @@ +import * as wasm from '@concordium/rust-bindings/wallet'; +import JSONbig from 'json-bigint'; +import { Buffer } from 'buffer/index.js'; +import type { + CredentialDeploymentDetails, + CredentialDeploymentTransaction, +} from '../types.js'; + +interface DeploymentDetailsResult { + credInfo: string; + serializedTransaction: string; + transactionHash: string; +} + +/** + * Gets the transaction hash that is used to look up the status of a credential + * deployment transaction. + * @param credentialDeployment the transaction to hash + * @param signatures the signatures that will also be part of the hash + * @returns the sha256 hash of the serialized block item kind, signatures, and credential deployment transaction + */ +export function getCredentialDeploymentTransactionHash( + credentialDeployment: CredentialDeploymentDetails, + signatures: string[] +): string { + const credentialDeploymentInfo: DeploymentDetailsResult = JSON.parse( + wasm.getDeploymentDetails( + signatures, + JSONbig.stringify(credentialDeployment.unsignedCdi), + credentialDeployment.expiry.expiryEpochSeconds + ) + ); + return credentialDeploymentInfo.transactionHash; +} + +/** + * Serializes a credential deployment transaction of a new account, so that it is ready for being + * submitted to the node. + * @param credentialDeployment the credenetial deployment transaction + * @param signatures the signatures on the hash of unsigned credential deployment information + * @returns the serialization of the credential deployment transaction ready for being submitted to a node + */ +export function serializeCredentialDeploymentTransactionForSubmission( + credentialDeployment: CredentialDeploymentDetails, + signatures: string[] +): Buffer { + const credentialDeploymentInfo: DeploymentDetailsResult = JSON.parse( + wasm.getDeploymentDetails( + signatures, + JSONbig.stringify(credentialDeployment.unsignedCdi), + credentialDeployment.expiry.expiryEpochSeconds + ) + ); + return Buffer.from(credentialDeploymentInfo.serializedTransaction, 'hex'); +} + +export function serializeCredentialDeploymentPayload( + signatures: string[], + credentialDeploymentTransaction: CredentialDeploymentTransaction +): Buffer { + const payloadByteArray = wasm.serializeCredentialDeploymentPayload( + signatures, + JSONbig.stringify(credentialDeploymentTransaction.unsignedCdi) + ); + return Buffer.from(payloadByteArray); +} diff --git a/packages/sdk/src/wasm/web3Id.ts b/packages/sdk/src/wasm/web3Id.ts new file mode 100644 index 000000000..987e00c0e --- /dev/null +++ b/packages/sdk/src/wasm/web3Id.ts @@ -0,0 +1,32 @@ +import * as wasm from '@concordium/rust-bindings/wallet'; +import { stringify } from 'json-bigint'; +import { VerifyWeb3IdCredentialSignatureInput } from '../web3-id/web3IdHelpers.js'; +import { Web3IdProofInput } from '../web3-id/web3IdProofTypes.js'; +import { VerifiablePresentation } from '../types/VerifiablePresentation.js'; + +/** + * Verifies that the given signature is correct for the given values/randomness/holder/issuerPublicKey/issuerContract + */ +export function verifyWeb3IdCredentialSignature( + input: VerifyWeb3IdCredentialSignatureInput +): boolean { + // Use json-bigint stringify to ensure we can handle bigints + return wasm.verifyWeb3IdCredentialSignature(stringify(input)); +} + +/** + * Given a statement about an identity and the inputs necessary to prove the statement, produces a proof that the associated identity fulfills the statement. + */ +export function getVerifiablePresentation( + input: Web3IdProofInput +): VerifiablePresentation { + try { + const s: VerifiablePresentation = VerifiablePresentation.fromString( + // Use json-bigint stringify to ensure we can handle bigints + wasm.createWeb3IdProof(stringify(input)) + ); + return s; + } catch (e) { + throw new Error(e as string); + } +} diff --git a/packages/sdk/src/web3-id/index.ts b/packages/sdk/src/web3-id/index.ts new file mode 100644 index 000000000..c3606623d --- /dev/null +++ b/packages/sdk/src/web3-id/index.ts @@ -0,0 +1,3 @@ +export * from './web3IdProofTypes.js'; +export * from './web3IdProofs.js'; +export * from './web3IdHelpers.js'; diff --git a/packages/common/src/web3IdHelpers.ts b/packages/sdk/src/web3-id/web3IdHelpers.ts similarity index 77% rename from packages/common/src/web3IdHelpers.ts rename to packages/sdk/src/web3-id/web3IdHelpers.ts index 84abb2a37..3e3f45288 100644 --- a/packages/common/src/web3IdHelpers.ts +++ b/packages/sdk/src/web3-id/web3IdHelpers.ts @@ -1,11 +1,10 @@ -import * as wasm from '@concordium/rust-bindings'; -import { stringify } from 'json-bigint'; -import { ContractAddress, CryptographicParameters } from './types'; +import { CryptographicParameters } from '../types.js'; +import type * as ContractAddress from '../types/ContractAddress.js'; import { AttributeType, StatementAttributeType, TimestampAttribute, -} from './web3ProofTypes'; +} from './web3IdProofTypes.js'; export type VerifyWeb3IdCredentialSignatureInput = { globalContext: CryptographicParameters; @@ -14,19 +13,9 @@ export type VerifyWeb3IdCredentialSignatureInput = { randomness: Record; holder: string; issuerPublicKey: string; - issuerContract: ContractAddress; + issuerContract: ContractAddress.Type; }; -/** - * Verifies that the given signature is correct for the given values/randomness/holder/issuerPublicKey/issuerContract - */ -export function verifyWeb3IdCredentialSignature( - input: VerifyWeb3IdCredentialSignatureInput -): boolean { - // Use json-bigint stringify to ensure we can handle bigints - return wasm.verifyWeb3IdCredentialSignature(stringify(input)); -} - /** * Compares a and b as field elements. * if a < b then compareStringAttributes(a,b) = -1; @@ -34,7 +23,21 @@ export function verifyWeb3IdCredentialSignature( * if a > b then compareStringAttributes(a,b) = 1; */ export function compareStringAttributes(a: string, b: string): number { - return wasm.compareStringAttributes(a, b); + const encoder = new TextEncoder(); + const aBytes = encoder.encode(a); + const bBytes = encoder.encode(b); + + if (aBytes.length < bBytes.length) return -1; + if (aBytes.length > bBytes.length) return 1; + + for (const [i, aByte] of aBytes.entries()) { + const bByte = bBytes[i]; + + if (aByte === bBytes[i]) continue; + return aByte < bByte ? -1 : 1; + } + + return 0; } /** diff --git a/packages/common/src/web3ProofTypes.ts b/packages/sdk/src/web3-id/web3IdProofTypes.ts similarity index 97% rename from packages/common/src/web3ProofTypes.ts rename to packages/sdk/src/web3-id/web3IdProofTypes.ts index c75c497b2..ffb2d38ee 100644 --- a/packages/common/src/web3ProofTypes.ts +++ b/packages/sdk/src/web3-id/web3IdProofTypes.ts @@ -4,8 +4,9 @@ import { GenericMembershipStatement, GenericNonMembershipStatement, GenericRangeStatement, -} from './commonProofTypes'; -import { ContractAddress, CryptographicParameters } from './types'; +} from '../commonProofTypes.js'; +import type { CryptographicParameters } from '../types.js'; +import type * as ContractAddress from '../types/ContractAddress.js'; export type TimestampAttribute = { type: 'date-time'; @@ -187,7 +188,7 @@ export type AtomicStatementV2 = GenericAtomicStatement; export type VerifiableCredentialQualifier = { type: 'sci'; - issuers: ContractAddress[]; + issuers: ContractAddress.Type[]; }; type IdentityProviderIndex = number; diff --git a/packages/common/src/web3Proofs.ts b/packages/sdk/src/web3-id/web3IdProofs.ts similarity index 95% rename from packages/common/src/web3Proofs.ts rename to packages/sdk/src/web3-id/web3IdProofs.ts index ceea274ba..c1f69fa44 100644 --- a/packages/common/src/web3Proofs.ts +++ b/packages/sdk/src/web3-id/web3IdProofs.ts @@ -1,14 +1,13 @@ -import { Buffer } from 'buffer/'; -import * as wasm from '@concordium/rust-bindings'; +import { Buffer } from 'buffer/index.js'; import { AttributeKey, AttributeKeyString, AttributeList, AttributesKeys, - ContractAddress, HexString, Network, -} from './types'; +} from '../types.js'; +import type * as ContractAddress from '../types/ContractAddress.js'; import { AtomicStatementV2, IdentityQualifier, @@ -21,7 +20,6 @@ import { StatementProverQualifier, VerifiableCredentialQualifier, CredentialSchemaSubject, - Web3IdProofInput, AccountCommitmentInput, Web3IssuerCommitmentInput, CredentialStatement, @@ -29,24 +27,22 @@ import { AttributeType, isTimestampAttribute, StatementAttributeType, -} from './web3ProofTypes'; -import { getPastDate } from './idProofs'; +} from './web3IdProofTypes.js'; +import { getPastDate } from '../id/idProofs.js'; import { StatementTypes, StatementBuilder, MIN_DATE, MAX_DATE, EU_MEMBERS, -} from './commonProofTypes'; -import { ConcordiumHdWallet } from './HdWallet'; -import { stringify } from 'json-bigint'; -import { VerifiablePresentation } from './types/VerifiablePresentation'; +} from '../commonProofTypes.js'; +import { ConcordiumHdWallet } from '../wasm/HdWallet.js'; import { compareStringAttributes, isStringAttributeInRange, statementAttributeTypeToAttributeType, timestampToDate, -} from './web3IdHelpers'; +} from './web3IdHelpers.js'; export const MAX_STRING_BYTE_LENGTH = 31; export const MAX_U64 = 18446744073709551615n; // 2n ** 64n - 1n @@ -355,7 +351,7 @@ export function verifyAtomicStatements( } function getWeb3IdCredentialQualifier( - validContractAddresses: ContractAddress[] + validContractAddresses: ContractAddress.Type[] ): VerifiableCredentialQualifier { return { type: 'sci', @@ -573,7 +569,7 @@ export class Web3StatementBuilder { } addForVerifiableCredentials( - validContractAddresses: ContractAddress[], + validContractAddresses: ContractAddress.Type[], builderCallback: (builder: InternalBuilder) => void, schema?: CredentialSchemaSubject ): this { @@ -600,23 +596,6 @@ export class Web3StatementBuilder { } } -/** - * Given a statement about an identity and the inputs necessary to prove the statement, produces a proof that the associated identity fulfills the statement. - */ -export function getVerifiablePresentation( - input: Web3IdProofInput -): VerifiablePresentation { - try { - const s: VerifiablePresentation = VerifiablePresentation.fromString( - // Use json-bigint stringify to ensure we can handle bigints - wasm.createWeb3IdProof(stringify(input)) - ); - return s; - } catch (e) { - throw new Error(e as string); - } -} - /** * Create a DID string for a web3id credential. Used to build a request for a verifiable credential. */ @@ -721,7 +700,7 @@ export function createWeb3CommitmentInput( */ export function createWeb3CommitmentInputWithHdWallet( wallet: ConcordiumHdWallet, - issuer: ContractAddress, + issuer: ContractAddress.Type, credentialIndex: number, credentialSubject: CredentialSubject, randomness: Record, diff --git a/packages/common/test/HdWallet.test.ts b/packages/sdk/test/ci/HdWallet.test.ts similarity index 91% rename from packages/common/test/HdWallet.test.ts rename to packages/sdk/test/ci/HdWallet.test.ts index 49d10fdb8..ce1ca8e20 100644 --- a/packages/common/test/HdWallet.test.ts +++ b/packages/sdk/test/ci/HdWallet.test.ts @@ -1,8 +1,9 @@ -import { Buffer } from 'buffer/'; -import { ConcordiumHdWallet } from '../src/HdWallet'; +import { ContractAddress } from '../../src/index.js'; +import { ConcordiumHdWallet } from '../../src/wasm/HdWallet.js'; +import { Buffer } from 'buffer/index.js'; export const TEST_SEED_1 = 'efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860'; -import * as ed from '@noble/ed25519'; +import * as ed from '#ed25519'; test('Mainnet signing key', () => { const wallet = ConcordiumHdWallet.fromHex(TEST_SEED_1, 'Mainnet'); @@ -29,8 +30,8 @@ test('Mainnet public and signing key match', async () => { const privateKey = wallet.getAccountSigningKey(0, 0, 0); const publicKey = wallet.getAccountPublicKey(0, 0, 0); const message = 'abcd1234abcd5678'; - const signature = await ed.sign(message, privateKey.toString('hex')); - expect(await ed.verify(signature, message, publicKey)).toBeTruthy(); + const signature = await ed.signAsync(message, privateKey.toString('hex')); + expect(await ed.verifyAsync(signature, message, publicKey)).toBeTruthy(); }); test('Mainnet Id Cred Sec', () => { @@ -113,8 +114,8 @@ test('Testnet public and signing key match', async () => { const privateKey = wallet.getAccountSigningKey(0, 0, 0); const publicKey = wallet.getAccountPublicKey(0, 0, 0); const message = 'abcd1234abcd5678'; - const signature = await ed.sign(message, privateKey.toString('hex')); - expect(await ed.verify(signature, message, publicKey)).toBeTruthy(); + const signature = await ed.signAsync(message, privateKey.toString('hex')); + expect(await ed.verifyAsync(signature, message, publicKey)).toBeTruthy(); }); test('Testnet Id Cred Sec', () => { @@ -191,7 +192,10 @@ test('Testnet CredId matches credDeployment test', () => { test('Mainnet verifiable credential signing key', () => { const wallet = ConcordiumHdWallet.fromHex(TEST_SEED_1, 'Mainnet'); expect( - wallet.getVerifiableCredentialSigningKey({ index: 1n, subindex: 2n }, 1) + wallet.getVerifiableCredentialSigningKey( + ContractAddress.create(1n, 2n), + 1 + ) ).toEqual( Buffer.from( '670d904509ce09372deb784e702d4951d4e24437ad3879188d71ae6db51f3301', @@ -204,7 +208,7 @@ test('Mainnet verifiable credential public key', () => { const wallet = ConcordiumHdWallet.fromHex(TEST_SEED_1, 'Mainnet'); expect( wallet.getVerifiableCredentialPublicKey( - { index: 3n, subindex: 1232n }, + ContractAddress.create(3, 1232), 341 ) ).toEqual( @@ -218,10 +222,7 @@ test('Mainnet verifiable credential public key', () => { test('Testnet verifiable credential signing key', () => { const wallet = ConcordiumHdWallet.fromHex(TEST_SEED_1, 'Testnet'); expect( - wallet.getVerifiableCredentialSigningKey( - { index: 13n, subindex: 0n }, - 1 - ) + wallet.getVerifiableCredentialSigningKey(ContractAddress.create(13), 1) ).toEqual( Buffer.from( 'c75a161b97a1e204d9f31202308958e541e14f0b14903bd220df883bd06702bb', @@ -233,10 +234,7 @@ test('Testnet verifiable credential signing key', () => { test('Testnet verifiable credential public key', () => { const wallet = ConcordiumHdWallet.fromHex(TEST_SEED_1, 'Testnet'); expect( - wallet.getVerifiableCredentialPublicKey( - { index: 17n, subindex: 0n }, - 341 - ) + wallet.getVerifiableCredentialPublicKey(ContractAddress.create(17), 341) ).toEqual( Buffer.from( 'c52a30475bac88da9e65471cf9cf59f99dcce22ce31de580b3066597746b394a', diff --git a/packages/common/test/VerifiablePresentation.test.ts b/packages/sdk/test/ci/VerifiablePresentation.test.ts similarity index 93% rename from packages/common/test/VerifiablePresentation.test.ts rename to packages/sdk/test/ci/VerifiablePresentation.test.ts index d61df1aee..23c82dbc3 100644 --- a/packages/common/test/VerifiablePresentation.test.ts +++ b/packages/sdk/test/ci/VerifiablePresentation.test.ts @@ -1,7 +1,7 @@ import { replaceDateWithTimeStampAttribute, reviveDateFromTimeStampAttribute, -} from '../src/types/VerifiablePresentation'; +} from '../../src/types/VerifiablePresentation.js'; const dateValue = '2023-08-24T15:15:29.000Z'; const timestamp = 1692890129000; diff --git a/packages/common/test/accountHelpers.test.ts b/packages/sdk/test/ci/accountHelpers.test.ts similarity index 75% rename from packages/common/test/accountHelpers.test.ts rename to packages/sdk/test/ci/accountHelpers.test.ts index 43c3f730a..624c51a22 100644 --- a/packages/common/test/accountHelpers.test.ts +++ b/packages/sdk/test/ci/accountHelpers.test.ts @@ -1,9 +1,9 @@ -import { AccountAddress } from '../src'; -import { generateBakerKeys } from '../src/accountHelpers'; +import { AccountAddress } from '../../src/index.js'; +import { generateBakerKeys } from '../../src/wasm/accountHelpers.js'; test('generate baker keys', () => { const accountAddress = '3eP94feEdmhYiPC1333F9VoV31KGMswonuHk5tqmZrzf761zK5'; - const keys = generateBakerKeys(new AccountAddress(accountAddress)); + const keys = generateBakerKeys(AccountAddress.fromBase58(accountAddress)); expect(typeof keys.signatureVerifyKey).toBe('string'); expect(typeof keys.electionVerifyKey).toBe('string'); expect(typeof keys.aggregationVerifyKey).toBe('string'); diff --git a/packages/common/test/accountTransactions.test.ts b/packages/sdk/test/ci/accountTransactions.test.ts similarity index 80% rename from packages/common/test/accountTransactions.test.ts rename to packages/sdk/test/ci/accountTransactions.test.ts index b73ee1db1..ad803b49f 100644 --- a/packages/common/test/accountTransactions.test.ts +++ b/packages/sdk/test/ci/accountTransactions.test.ts @@ -1,5 +1,5 @@ -import { AccountAddress } from '../src/types/accountAddress'; -import { TransactionExpiry } from '../src/types/transactionExpiry'; +import * as AccountAddress from '../../src/types/AccountAddress.js'; +import * as TransactionExpiry from '../../src/types/TransactionExpiry.js'; import { OpenStatus, AccountTransaction, @@ -7,7 +7,9 @@ import { CcdAmount, ConfigureBakerPayload, getAccountTransactionSignDigest, -} from '../src'; + AccountTransactionHeader, + SequenceNumber, +} from '../../src/index.js'; test('configureBaker is serialized correctly', async () => { const senderAccountAddress = @@ -16,16 +18,16 @@ test('configureBaker is serialized correctly', async () => { const expectedDigest = 'dcfb92b6e57b1d3e252c52cb8b838f44a33bf8d67301e89753101912f299dffb'; - const expiry = new TransactionExpiry(new Date(1675872215), true); + const expiry = TransactionExpiry.fromDate(new Date(1675872215)); - const header = { + const header: AccountTransactionHeader = { expiry, - nonce: 1n, - sender: new AccountAddress(senderAccountAddress), + nonce: SequenceNumber.create(1), + sender: AccountAddress.fromBase58(senderAccountAddress), }; const payload: Required = { - stake: new CcdAmount(1000000000n), + stake: CcdAmount.fromMicroCcd(1000000000n), restakeEarnings: true, openForDelegation: OpenStatus.ClosedForAll, keys: { diff --git a/packages/sdk/test/ci/alias.test.ts b/packages/sdk/test/ci/alias.test.ts new file mode 100644 index 000000000..130f3a534 --- /dev/null +++ b/packages/sdk/test/ci/alias.test.ts @@ -0,0 +1,137 @@ +import * as AccountAddress from '../../src/types/AccountAddress.js'; + +test('isAlias is reflexive', () => { + const address = AccountAddress.fromBuffer( + Buffer.from( + 'e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', + 'hex' + ) + ); + expect(AccountAddress.isAlias(address, address)).toBeTruthy(); +}); + +test('isAlias: Addresses with first 29 bytes in common are aliases', () => { + const address = AccountAddress.fromBuffer( + Buffer.from( + 'e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', + 'hex' + ) + ); + const alias = AccountAddress.fromBuffer( + Buffer.from( + 'e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ececb467', + 'hex' + ) + ); + expect(AccountAddress.isAlias(address, alias)).toBeTruthy(); +}); + +test('isAlias: Addresses with differences in the 5th byte are not aliases', () => { + const address = AccountAddress.fromBuffer( + Buffer.from( + 'e718721412249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', + 'hex' + ) + ); + const alias = AccountAddress.fromBuffer( + Buffer.from( + 'e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ec000002', + 'hex' + ) + ); + expect(AccountAddress.isAlias(address, alias)).toBeFalsy(); +}); + +test('isAlias: Addresses with differences in the 29th byte are not aliases', () => { + const address = AccountAddress.fromBuffer( + Buffer.from( + 'e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83ececb467', + 'hex' + ) + ); + const alias = AccountAddress.fromBuffer( + Buffer.from( + 'e718721402249e81f8fedcba6027f1c9bcb4445e9433b7905d579d83e1ecb467', + 'hex' + ) + ); + expect(AccountAddress.isAlias(address, alias)).toBeFalsy(); +}); + +test('getAlias: getAlias returns an alias', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + const alias = AccountAddress.getAlias(address, 1); + expect(AccountAddress.isAlias(address, alias)).toBeTruthy(); +}); + +test('getAlias: changing counter makes getAlias return different aliases', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + const alias = AccountAddress.getAlias(address, 1); + const otherAlias = AccountAddress.getAlias(address, 100); + expect(otherAlias.address).not.toBe(alias.address); + expect(AccountAddress.isAlias(otherAlias, alias)).toBeTruthy(); +}); + +test('getAlias: last 3 bytes of alias matches counter', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + let alias = AccountAddress.getAlias(address, 0xaaaaaa); + expect( + Buffer.from(alias.decodedAddress.slice(29, 32)).toString('hex') + ).toBe('aaaaaa'); + alias = AccountAddress.getAlias(address, 0x152637); + expect( + Buffer.from(alias.decodedAddress.slice(29, 32)).toString('hex') + ).toBe('152637'); + alias = AccountAddress.getAlias(address, 0x000000); + expect( + Buffer.from(alias.decodedAddress.slice(29, 32)).toString('hex') + ).toBe('000000'); + alias = AccountAddress.getAlias(address, 0xffffff); + expect( + Buffer.from(alias.decodedAddress.slice(29, 32)).toString('hex') + ).toBe('ffffff'); +}); + +test('getAlias: using counter = "last 3 bytes of address" returns the address', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + const alsoAddress = AccountAddress.getAlias(address, 0xecb198); + expect(alsoAddress.address).toBe(address.address); +}); + +test('getAlias: accepts counter = 0', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + const alias = AccountAddress.getAlias(address, 0); + expect(AccountAddress.isAlias(address, alias)).toBeTruthy(); +}); + +test('getAlias: does not accept counter = -1', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + expect(() => AccountAddress.getAlias(address, -1)).toThrowError(); +}); + +test('getAlias: accepts counter === 2^24 - 1', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + const alias = AccountAddress.getAlias(address, 0xffffff); + expect(AccountAddress.isAlias(address, alias)).toBeTruthy(); +}); + +test('getAlias: does not accept counter === 2^24', () => { + const address = AccountAddress.fromBase58( + '4hXCdgNTxgM7LNm8nFJEfjDhEcyjjqQnPSRyBS9QgmHKQVxKRf' + ); + expect(() => AccountAddress.getAlias(address, 0x01000000)).toThrowError(); +}); diff --git a/packages/common/test/credentialDeployment.test.ts b/packages/sdk/test/ci/credentialDeployment.test.ts similarity index 60% rename from packages/common/test/credentialDeployment.test.ts rename to packages/sdk/test/ci/credentialDeployment.test.ts index 730b7e01a..b1b84d265 100644 --- a/packages/common/test/credentialDeployment.test.ts +++ b/packages/sdk/test/ci/credentialDeployment.test.ts @@ -1,26 +1,25 @@ import { createCredentialTransaction, CredentialInput, - createCredentialV1, -} from '../src/credentialDeploymentTransactions'; +} from '../../src/wasm/credentialDeploymentTransactions.js'; import fs from 'fs'; -import { AttributeKey } from '../src/types'; -import { TransactionExpiry } from '../src'; +import { AttributeKey } from '../../src/types.js'; +import { TransactionExpiry } from '../../src/index.js'; export function createCredentialInput( revealedAttributes: AttributeKey[] ): CredentialInput { const ipInfo = JSON.parse( - fs.readFileSync('./test/resources/ip_info.json').toString() + fs.readFileSync('./test/ci/resources/ip_info.json').toString() ).value; const globalContext = JSON.parse( - fs.readFileSync('./test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; const arsInfos = JSON.parse( - fs.readFileSync('./test/resources/ars_infos.json').toString() + fs.readFileSync('./test/ci/resources/ars_infos.json').toString() ).value; const idObject = JSON.parse( - fs.readFileSync('./test/resources/identity-object.json').toString() + fs.readFileSync('./test/ci/resources/identity-object.json').toString() ).value; const seedAsHex = @@ -39,36 +38,6 @@ export function createCredentialInput( }; } -test('Test createCredentialV1', () => { - const expiry = Math.floor(Date.now() / 1000) + 720; - const revealedAttributes: AttributeKey[] = ['firstName']; - const output = createCredentialV1({ - ...createCredentialInput(revealedAttributes), - expiry, - }); - - expect(output.cdi.credId).toEqual( - 'b317d3fea7de56f8c96f6e72820c5cd502cc0eef8454016ee548913255897c6b52156cc60df965d3efb3f160eff6ced4' - ); - expect(output.cdi.credentialPublicKeys.keys[0].verifyKey).toEqual( - '29723ec9a0b4ca16d5d548b676a1a0adbecdedc5446894151acb7699293d69b1' - ); - expect(output.cdi.credentialPublicKeys.threshold).toEqual(1); - expect(output.cdi.ipIdentity).toEqual(0); - expect(output.cdi.policy.createdAt).toEqual('202208'); - expect(output.cdi.policy.validTo).toEqual('202308'); - expect(Object.keys(output.cdi.policy.revealedAttributes)).toEqual( - revealedAttributes - ); - expect(output.cdi.revocationThreshold).toEqual(1); - expect(typeof output.cdi.proofs).toEqual('string'); - expect(Object.keys(output.cdi.arData)).toEqual(['1', '2', '3']); - expect(typeof output.cdi.arData[1].encIdCredPubShare).toEqual('string'); - expect(typeof output.cdi.arData[2].encIdCredPubShare).toEqual('string'); - expect(typeof output.cdi.arData[3].encIdCredPubShare).toEqual('string'); - expect(output.expiry.expiryEpochSeconds).toEqual(BigInt(expiry)); -}); - test('Test createCredentialTransaction', () => { const expiry = BigInt(Math.floor(Date.now() / 1000)) + BigInt(720); const revealedAttributes: AttributeKey[] = ['firstName']; diff --git a/packages/sdk/test/ci/deserialization.test.ts b/packages/sdk/test/ci/deserialization.test.ts new file mode 100644 index 000000000..328619371 --- /dev/null +++ b/packages/sdk/test/ci/deserialization.test.ts @@ -0,0 +1,161 @@ +import { deserializeTransaction } from '../../src/wasm/deserialization.js'; +import { serializeAccountTransactionForSubmission } from '../../src/serialization.js'; +import { + AccountAddress, + AccountTransaction, + AccountTransactionHeader, + AccountTransactionPayload, + AccountTransactionSignature, + AccountTransactionType, + BlockItemKind, + DataBlob, + CcdAmount, + RegisterDataPayload, + SimpleTransferPayload, + SimpleTransferWithMemoPayload, + TransactionExpiry, + tokenAddressFromBase58, + tokenAddressToBase58, + SequenceNumber, + CIS2, + ContractAddress, +} from '../../src/index.js'; + +function deserializeAccountTransactionBase( + type: AccountTransactionType, + payload: AccountTransactionPayload, + expiry = TransactionExpiry.futureMinutes(20) +) { + const header: AccountTransactionHeader = { + expiry, + nonce: SequenceNumber.create(1), + sender: AccountAddress.fromBase58( + '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' + ), + }; + + const transaction: AccountTransaction = { + header, + payload, + type, + }; + + const signatures: AccountTransactionSignature = { + 0: { + 0: '780e4f5e00554fb4e235c67795fbd6d4ad638f3778199713f03634c846e4dbec496f0b13c4454e1a760c3efffec7cc8c11c6053a632dd32c9714cd26952cda08', + }, + }; + + const deserialized = deserializeTransaction( + serializeAccountTransactionForSubmission(transaction, signatures) + ); + + if (deserialized.kind !== BlockItemKind.AccountTransactionKind) { + throw new Error('Incorrect BlockItemKind'); + } + + expect(deserialized.transaction).toEqual({ + accountTransaction: transaction, + signatures, + }); +} + +test('test deserialize simpleTransfer ', () => { + const payload: SimpleTransferPayload = { + amount: CcdAmount.fromMicroCcd(5100000), + toAddress: AccountAddress.fromBase58( + '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' + ), + }; + deserializeAccountTransactionBase(AccountTransactionType.Transfer, payload); +}); + +test('test deserialize simpleTransfer with memo ', () => { + const payload: SimpleTransferWithMemoPayload = { + amount: CcdAmount.fromMicroCcd(5100000), + toAddress: AccountAddress.fromBase58( + '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' + ), + memo: new DataBlob(Buffer.from('00', 'hex')), + }; + deserializeAccountTransactionBase( + AccountTransactionType.TransferWithMemo, + payload + ); +}); + +test('test deserialize registerData ', () => { + const payload: RegisterDataPayload = { + data: new DataBlob(Buffer.from('00AB5303926810EE', 'hex')), + }; + deserializeAccountTransactionBase( + AccountTransactionType.RegisterData, + payload + ); +}); + +test('Expired transactions can be deserialized', () => { + const payload: SimpleTransferPayload = { + amount: CcdAmount.fromMicroCcd(5100000), + toAddress: AccountAddress.fromBase58( + '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' + ), + }; + deserializeAccountTransactionBase( + AccountTransactionType.Transfer, + payload, + TransactionExpiry.fromDate(new Date(2000, 1)) + ); +}); + +test('Test parsing of Token Addresses', () => { + let base58 = '5Pxr5EUtU'; + let address = tokenAddressFromBase58(base58); + let rebase58 = tokenAddressToBase58(address); + let expectedAddress: CIS2.TokenAddress = { + contract: ContractAddress.create(0), + id: '', + }; + expect(address).toEqual(expectedAddress); + expect(rebase58).toEqual(base58); + + base58 = 'LQMMu3bAg7'; + address = tokenAddressFromBase58(base58); + rebase58 = tokenAddressToBase58(address); + expectedAddress = { + contract: ContractAddress.create(0), + id: 'aa', + }; + expect(address).toEqual(expectedAddress); + expect(rebase58).toEqual(base58); + + base58 = '5QTdu98KF'; + address = tokenAddressFromBase58(base58); + rebase58 = tokenAddressToBase58(address); + const expectedAddress2 = { + contract: ContractAddress.create(1), + id: '', + }; + expect(address).toEqual(expectedAddress2); + expect(rebase58).toEqual(base58); + + base58 = 'LSYqgoQcb6'; + address = tokenAddressFromBase58(base58); + rebase58 = tokenAddressToBase58(address); + expectedAddress = { + contract: ContractAddress.create(1), + id: 'aa', + }; + expect(address).toEqual(expectedAddress); + expect(rebase58).toEqual(base58); + + base58 = 'LSYXivPSWP'; + address = tokenAddressFromBase58(base58); + rebase58 = tokenAddressToBase58(address); + expectedAddress = { + contract: ContractAddress.create(1), + id: '0a', + }; + expect(address).toEqual(expectedAddress); + expect(rebase58).toEqual(base58); +}); diff --git a/packages/common/test/id.test.ts b/packages/sdk/test/ci/id.test.ts similarity index 87% rename from packages/common/test/id.test.ts rename to packages/sdk/test/ci/id.test.ts index 0e32d1614..ded8db24a 100644 --- a/packages/common/test/id.test.ts +++ b/packages/sdk/test/ci/id.test.ts @@ -3,18 +3,18 @@ import { IdentityRequestInput, IdentityRecoveryRequestInput, createIdentityRecoveryRequest, -} from '../src/identity'; +} from '../../src/wasm/identity.js'; import fs from 'fs'; test('idrequest', () => { const ipInfo = JSON.parse( - fs.readFileSync('./test/resources/ip_info.json').toString() + fs.readFileSync('./test/ci/resources/ip_info.json').toString() ).value; const globalContext = JSON.parse( - fs.readFileSync('./test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; const arsInfos = JSON.parse( - fs.readFileSync('./test/resources/ars_infos.json').toString() + fs.readFileSync('./test/ci/resources/ars_infos.json').toString() ).value; const seed = 'efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860'; @@ -49,10 +49,10 @@ test('idrequest', () => { test('Create id recovery request', () => { const ipInfo = JSON.parse( - fs.readFileSync('./test/resources/ip_info.json').toString() + fs.readFileSync('./test/ci/resources/ip_info.json').toString() ).value; const globalContext = JSON.parse( - fs.readFileSync('./test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; const seedAsHex = 'efa5e27326f8fa0902e647b52449bf335b7b605adc387015ec903f41d95080eb71361cbc7fb78721dcd4f3926a337340aa1406df83332c44c1cdcfe100603860'; diff --git a/packages/common/test/idProofs.test.ts b/packages/sdk/test/ci/idProofs.test.ts similarity index 89% rename from packages/common/test/idProofs.test.ts rename to packages/sdk/test/ci/idProofs.test.ts index 94ffea9e6..bedcd8efe 100644 --- a/packages/common/test/idProofs.test.ts +++ b/packages/sdk/test/ci/idProofs.test.ts @@ -1,12 +1,17 @@ -import { AttributeKeyString, AttributesKeys, IdDocType } from '../src/types'; -import { StatementTypes } from '../src/commonProofTypes'; +import { + AttributeKeyString, + AttributesKeys, + IdDocType, +} from '../../src/types.js'; +import { StatementTypes } from '../../src/commonProofTypes.js'; import { attributesWithRange, attributesWithSet, RangeStatement, -} from '../src/idProofTypes'; -import { getIdProof, IdStatementBuilder } from '../src/idProofs'; +} from '../../src/id/idProofTypes.js'; +import { IdStatementBuilder } from '../../src/id/idProofs.js'; import fs from 'fs'; +import { getIdProof } from '../../src/wasm/index.js'; test('Creating a statement with multiple atomic statements on the same attribute fails', () => { const builder = new IdStatementBuilder(true); @@ -93,9 +98,15 @@ test('Upper bound must be greater than lower bound for attribute statement', () test('Unknown attribute tags are rejected', () => { const builder = new IdStatementBuilder(true); - expect(() => builder.addMembership(-1, ['DK'])).toThrow(); - expect(() => builder.addMembership(15, ['DK'])).toThrow(); - expect(() => builder.addMembership(1000, ['DK'])).toThrow(); + expect(() => + builder.addMembership(-1 as unknown as AttributesKeys, ['DK']) + ).toThrow(); + expect(() => + builder.addMembership(15 as unknown as AttributesKeys, ['DK']) + ).toThrow(); + expect(() => + builder.addMembership(1000 as unknown as AttributesKeys, ['DK']) + ).toThrow(); }); test('Empty sets are rejected', () => { @@ -110,10 +121,10 @@ test('Empty sets are rejected', () => { test('Can create id Proof', () => { const idObject = JSON.parse( - fs.readFileSync('./test/resources/identity-object.json').toString() + fs.readFileSync('./test/ci/resources/identity-object.json').toString() ).value; const globalContext = JSON.parse( - fs.readFileSync('./test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; const builder = new IdStatementBuilder(true); diff --git a/packages/sdk/test/ci/module-schema.test.ts b/packages/sdk/test/ci/module-schema.test.ts new file mode 100644 index 000000000..0e225aeb9 --- /dev/null +++ b/packages/sdk/test/ci/module-schema.test.ts @@ -0,0 +1,76 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { + getEmbeddedModuleSchema, + versionedModuleSourceFromBuffer, +} from '../../src/types/VersionedModuleSource.js'; + +// Directory with smart contract modules and schemas for testing. +const testFileDir = path.resolve( + '../../deps/concordium-base/smart-contracts/testdata/schemas' +); + +describe('VersionedModuleSource: getEmbeddedModuleSchema', () => { + test('Smart contract module v1 with versioned schema', async () => { + const contractModule = fs.readFileSync( + path.join( + testFileDir, + 'cis2-wccd-embedded-schema-v1-versioned.wasm.v1' + ) + ); + const moduleSource = versionedModuleSourceFromBuffer(contractModule); + const moduleSchema = await getEmbeddedModuleSchema(moduleSource); + if (moduleSchema === null) { + fail('Failed to find module schame'); + } + expect(moduleSchema.type).toBe('versioned'); + }); + + test('Smart contract module v0 with versioned schema', async () => { + const contractModule = fs.readFileSync( + path.join( + testFileDir, + 'cis1-wccd-embedded-schema-v0-versioned.wasm.v0' + ) + ); + const moduleSource = versionedModuleSourceFromBuffer(contractModule); + const moduleSchema = await getEmbeddedModuleSchema(moduleSource); + if (moduleSchema === null) { + fail('Failed to find module schame'); + } + expect(moduleSchema.type).toBe('versioned'); + }); + + test('Smart contract module v0 with unversioned schema', async () => { + const unversionedContractModule = fs.readFileSync( + path.join( + testFileDir, + 'cis1-wccd-embedded-schema-v0-unversioned.wasm' + ) + ); + const moduleSource = { + version: 0, + source: Buffer.from(unversionedContractModule), + } as const; + const moduleSchema = await getEmbeddedModuleSchema(moduleSource); + if (moduleSchema === null) { + fail('Failed to find module schame'); + } + expect(moduleSchema.type).toBe('unversioned'); + }); + + test('Smart contract module v1 with unversioned schema', async () => { + const contractModule = fs.readFileSync( + path.join( + testFileDir, + 'cis2-wccd-embedded-schema-v1-unversioned.wasm.v1' + ) + ); + const moduleSource = versionedModuleSourceFromBuffer(contractModule); + const moduleSchema = await getEmbeddedModuleSchema(moduleSource); + if (moduleSchema === null) { + fail('Failed to find module schame'); + } + expect(moduleSchema.type).toBe('unversioned'); + }); +}); diff --git a/packages/common/test/resources/ars_infos.json b/packages/sdk/test/ci/resources/ars_infos.json similarity index 100% rename from packages/common/test/resources/ars_infos.json rename to packages/sdk/test/ci/resources/ars_infos.json diff --git a/packages/common/test/resources/auction-with-errors-schema.bin b/packages/sdk/test/ci/resources/auction-with-errors-schema.bin similarity index 100% rename from packages/common/test/resources/auction-with-errors-schema.bin rename to packages/sdk/test/ci/resources/auction-with-errors-schema.bin diff --git a/packages/common/test/resources/cis1-wccd-schema-v0-versioned.bin b/packages/sdk/test/ci/resources/cis1-wccd-schema-v0-versioned.bin similarity index 100% rename from packages/common/test/resources/cis1-wccd-schema-v0-versioned.bin rename to packages/sdk/test/ci/resources/cis1-wccd-schema-v0-versioned.bin diff --git a/packages/common/test/resources/cis2-nft-schema.bin b/packages/sdk/test/ci/resources/cis2-nft-schema.bin similarity index 100% rename from packages/common/test/resources/cis2-nft-schema.bin rename to packages/sdk/test/ci/resources/cis2-nft-schema.bin diff --git a/packages/common/test/resources/cis2-wccd-schema-v1-versioned.bin b/packages/sdk/test/ci/resources/cis2-wccd-schema-v1-versioned.bin similarity index 100% rename from packages/common/test/resources/cis2-wccd-schema-v1-versioned.bin rename to packages/sdk/test/ci/resources/cis2-wccd-schema-v1-versioned.bin diff --git a/packages/common/test/resources/expectedPresentation.ts b/packages/sdk/test/ci/resources/expectedPresentation.ts similarity index 100% rename from packages/common/test/resources/expectedPresentation.ts rename to packages/sdk/test/ci/resources/expectedPresentation.ts diff --git a/packages/common/test/resources/expectedStatements.ts b/packages/sdk/test/ci/resources/expectedStatements.ts similarity index 80% rename from packages/common/test/resources/expectedStatements.ts rename to packages/sdk/test/ci/resources/expectedStatements.ts index 55bb5f935..b5610a16c 100644 --- a/packages/common/test/resources/expectedStatements.ts +++ b/packages/sdk/test/ci/resources/expectedStatements.ts @@ -1,12 +1,13 @@ -import { CredentialStatement } from '../../src/web3ProofTypes'; +import { ContractAddress } from '../../../src/index.js'; +import { CredentialStatement } from '../../../src/web3-id/web3IdProofTypes.js'; export const expectedStatementMixed: CredentialStatement[] = [ { idQualifier: { type: 'sci', issuers: [ - { index: 2101n, subindex: 0n }, - { index: 1337n, subindex: 42n }, + ContractAddress.create(2101), + ContractAddress.create(1337, 42), ], }, statement: [ @@ -26,7 +27,7 @@ export const expectedStatementMixed: CredentialStatement[] = [ { idQualifier: { type: 'sci', - issuers: [{ index: 1338n, subindex: 0n }], + issuers: [ContractAddress.create(1338, 0)], }, statement: [ { diff --git a/packages/common/test/resources/global.json b/packages/sdk/test/ci/resources/global.json similarity index 100% rename from packages/common/test/resources/global.json rename to packages/sdk/test/ci/resources/global.json diff --git a/packages/common/test/resources/icecream-schema.bin b/packages/sdk/test/ci/resources/icecream-schema.bin similarity index 100% rename from packages/common/test/resources/icecream-schema.bin rename to packages/sdk/test/ci/resources/icecream-schema.bin diff --git a/packages/common/test/resources/icecream-with-schema.wasm b/packages/sdk/test/ci/resources/icecream-with-schema.wasm similarity index 100% rename from packages/common/test/resources/icecream-with-schema.wasm rename to packages/sdk/test/ci/resources/icecream-with-schema.wasm diff --git a/packages/common/test/resources/identity-object.json b/packages/sdk/test/ci/resources/identity-object.json similarity index 100% rename from packages/common/test/resources/identity-object.json rename to packages/sdk/test/ci/resources/identity-object.json diff --git a/packages/common/test/resources/ip_info.json b/packages/sdk/test/ci/resources/ip_info.json similarity index 100% rename from packages/common/test/resources/ip_info.json rename to packages/sdk/test/ci/resources/ip_info.json diff --git a/packages/common/test/resources/schema.ts b/packages/sdk/test/ci/resources/schema.ts similarity index 81% rename from packages/common/test/resources/schema.ts rename to packages/sdk/test/ci/resources/schema.ts index 54338098f..7af33e0f3 100644 --- a/packages/common/test/resources/schema.ts +++ b/packages/sdk/test/ci/resources/schema.ts @@ -9,3 +9,7 @@ export const TEST_CONTRACT_RECEIVE_ERROR_SCHEMA = 'Bw'; export const TEST_CONTRACT_INIT_ERROR_SCHEMA = 'Aw'; export const AUCTION_WITH_ERRORS_VIEW_RETURN_VALUE_SCHEMA = 'FAAEAAAADQAAAGF1Y3Rpb25fc3RhdGUVAgAAAAoAAABOb3RTb2xkWWV0AgQAAABTb2xkAQEAAAALDgAAAGhpZ2hlc3RfYmlkZGVyFQIAAAAEAAAATm9uZQIEAAAAU29tZQEBAAAACwQAAABpdGVtFgIDAAAAZW5kDQ'; + +// contract: "test", init = (param = u64), receive = (name: "receive", param = u64, return = u64) +export const TEST_CONTRACT_U64 = + '//8DAQAAAAQAAAB0ZXN0AQAFAQAAAAcAAAByZWNlaXZlAgUFAA=='; diff --git a/packages/common/test/resources/two-step-transfer-schema.bin b/packages/sdk/test/ci/resources/two-step-transfer-schema.bin similarity index 100% rename from packages/common/test/resources/two-step-transfer-schema.bin rename to packages/sdk/test/ci/resources/two-step-transfer-schema.bin diff --git a/packages/sdk/test/ci/schema.test.ts b/packages/sdk/test/ci/schema.test.ts new file mode 100644 index 000000000..83387a335 --- /dev/null +++ b/packages/sdk/test/ci/schema.test.ts @@ -0,0 +1,323 @@ +import * as fs from 'fs'; +import { + displayTypeSchemaTemplate, + getUpdateContractParameterSchema, + deserializeContractState, + deserializeReceiveReturnValue, + deserializeReceiveError, + deserializeInitError, + deserializeTypeValue, + serializeUpdateContractParameters, + serializeTypeValue, + serializeInitContractParameters, +} from '../../src/schema.js'; +import { + CIS2_WCCD_STATE_SCHEMA, + V0_PIGGYBANK_SCHEMA, + CIS2_WCCD_STATE_GET_BALANCE_RETURN_VALUE_SCHEMA, + TEST_CONTRACT_INIT_ERROR_SCHEMA, + TEST_CONTRACT_SCHEMA, + TEST_CONTRACT_RECEIVE_ERROR_SCHEMA, + AUCTION_WITH_ERRORS_VIEW_RETURN_VALUE_SCHEMA, + TEST_CONTRACT_U64, +} from './resources/schema.js'; +import { ContractName, EntrypointName, Parameter } from '../../src/index.js'; + +const U64_MAX = 18446744073709551615n; + +test('U64_MAX can be deserialized', () => { + const returnVal = deserializeReceiveReturnValue( + Buffer.from('ffffffffffffffff', 'hex'), + Buffer.from(TEST_CONTRACT_U64, 'base64'), + ContractName.fromStringUnchecked('test'), + EntrypointName.fromStringUnchecked('receive') + ); + + expect(returnVal).toEqual(U64_MAX); +}); + +test('schema template display', () => { + const fullSchema = Buffer.from( + fs.readFileSync('./test/ci/resources/cis2-nft-schema.bin') + ); + const schemaVersion = 1; + const contractName = ContractName.fromStringUnchecked('CIS2-NFT'); + const functionName = EntrypointName.fromStringUnchecked('transfer'); + const template = displayTypeSchemaTemplate( + getUpdateContractParameterSchema( + fullSchema, + contractName, + functionName, + schemaVersion + ) + ); + expect(template).toBe( + '[{"amount":["",""],"data":[""],"from":{"Enum":[{"Account":[""]},{"Contract":[{"index":"","subindex":""}]}]},"to":{"Enum":[{"Account":[""]},{"Contract":[{"index":"","subindex":""},{"contract":"","func":""}]}]},"token_id":[""]}]' + ); +}); + +test('test that deserializeContractState works', () => { + const state = deserializeContractState( + ContractName.fromStringUnchecked('PiggyBank'), + Buffer.from(V0_PIGGYBANK_SCHEMA, 'base64'), + Buffer.from('00', 'hex') + ); + + expect(state.Intact).toBeDefined(); +}); + +test('Receive return value can be deserialized', () => { + const returnValue = deserializeReceiveReturnValue( + Buffer.from('80f18c27', 'hex'), + Buffer.from(CIS2_WCCD_STATE_SCHEMA, 'base64'), + ContractName.fromStringUnchecked('CIS2-wCCD-State'), + EntrypointName.fromStringUnchecked('getBalance') + ); + + expect(returnValue).toEqual('82000000'); +}); + +/** + * Repeats the "Receive return value can be deserialized" test, using deserializeTypeValue and a type specific schema instead. + */ +test('Receive return value can be deserialized using deserializeTypeValue', () => { + const returnValue = deserializeTypeValue( + Buffer.from('80f18c27', 'hex'), + Buffer.from(CIS2_WCCD_STATE_GET_BALANCE_RETURN_VALUE_SCHEMA, 'base64') + ); + expect(returnValue).toEqual('82000000'); +}); + +const auctionRawReturnValue = Buffer.from( + '00000b0000004120676f6f64206974656d00a4fbca84010000', + 'hex' +); + +/** + * Small helper for expected deserialized value of rawAuctionReturnValue + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const expectAuctionReturnValue = (returnValue: any) => { + expect(returnValue.item).toEqual('A good item'); + expect(returnValue.end).toEqual('2022-12-01T00:00:00+00:00'); + expect(returnValue.auction_state).toHaveProperty('NotSoldYet'); + expect(returnValue.highest_bidder).toHaveProperty('None'); +}; + +test('Return value can be deserialized - auction', () => { + const returnValue = deserializeReceiveReturnValue( + auctionRawReturnValue, + Buffer.from( + fs.readFileSync( + './test/ci/resources/auction-with-errors-schema.bin' + ) + ), + ContractName.fromStringUnchecked('auction'), + EntrypointName.fromStringUnchecked('view') + ); + + expectAuctionReturnValue(returnValue); +}); + +/** + * Repeats the "Return value can be deserialized - auction" test, using deserializeTypeValue and a type specific schema instead. + */ +test('Return value can be deserialized - auction using deserializeTypeValue', () => { + const returnValue = deserializeTypeValue( + auctionRawReturnValue, + Buffer.from(AUCTION_WITH_ERRORS_VIEW_RETURN_VALUE_SCHEMA, 'base64') + ); + + expectAuctionReturnValue(returnValue); +}); + +test('Receive error can be deserialized', () => { + const error = deserializeReceiveError( + Buffer.from('ffff', 'hex'), + Buffer.from(TEST_CONTRACT_SCHEMA, 'base64'), + ContractName.fromStringUnchecked('TestContract'), + EntrypointName.fromStringUnchecked('receive_function') + ); + + expect(error).toEqual(-1n); +}); + +/** + * Repeats the "Receive error can be deserialized" test, using deserializeTypeValue and a type specific schema instead. + */ +test('Receive error can be deserialized using deserializeTypeValue', () => { + const error = deserializeTypeValue( + Buffer.from('ffff', 'hex'), + Buffer.from(TEST_CONTRACT_RECEIVE_ERROR_SCHEMA, 'base64') + ); + expect(error).toEqual(-1n); +}); + +test('Init error can be deserialized', () => { + const error = deserializeInitError( + Buffer.from('0100', 'hex'), + Buffer.from(TEST_CONTRACT_SCHEMA, 'base64'), + ContractName.fromStringUnchecked('TestContract') + ); + + expect(error).toEqual(1n); +}); + +/** + * Repeats the "Init error can be deserialized" test, using deserializeTypeValue and a type specific schema instead. + */ +test('Init error can be deserialized using deserializeTypeValue', () => { + const error = deserializeTypeValue( + Buffer.from('0100', 'hex'), + Buffer.from(TEST_CONTRACT_INIT_ERROR_SCHEMA, 'base64') + ); + expect(error).toEqual(1n); +}); + +test('serialize UpdateContractParameters using CIS2 contract', () => { + const parameter = serializeUpdateContractParameters( + ContractName.fromStringUnchecked('CIS2-NFT'), + EntrypointName.fromStringUnchecked('transfer'), + [ + { + token_id: [], + amount: [200, 0], + from: { + Account: [ + '4RgTGQhg1Y8DAUkC2TpZsKmXdicArDqY9gcgJmBDECg4kkYNg4', + ], + }, + to: { + Account: [ + '3UiNwnmZ64YR423uamgZyY8RnRkD88tfn6SYtKzvWZCkyFdN94', + ], + }, + data: [], + }, + ], + Buffer.from(fs.readFileSync('./test/ci/resources/cis2-nft-schema.bin')), + 1 + ); + + expect(Parameter.toHexString(parameter)).toBe( + '010000c80000c320b41f1997accd5d21c6bf4992370948ed711435e0e2c9302def62afd1295f004651a37c65c8461540decd511e7440d1ff6d4191b7e2133b7239b2485be1a4860000' + ); +}); + +test('serialize UpdateContractParameters using CIS2 contract and incorrect name', () => { + const parameter = function () { + serializeUpdateContractParameters( + ContractName.fromStringUnchecked('CIS2-NFT'), + EntrypointName.fromStringUnchecked('non-existent'), + [ + { + token_id: [], + amount: [200, 0], + from: { + Account: [ + '4RgTGQhg1Y8DAUkC2TpZsKmXdicArDqY9gcgJmBDECg4kkYNg4', + ], + }, + to: { + Account: [ + '3UiNwnmZ64YR423uamgZyY8RnRkD88tfn6SYtKzvWZCkyFdN94', + ], + }, + data: [], + }, + ], + Buffer.from( + fs.readFileSync('./test/ci/resources/cis2-nft-schema.bin') + ), + 1 + ); + }; + + expect(parameter).toThrow(); +}); + +test('serialize type value and serializeUpdateContractParameters give same result', () => { + const parameters = [ + { + token_id: [], + amount: [200, 0], + from: { + Account: ['4RgTGQhg1Y8DAUkC2TpZsKmXdicArDqY9gcgJmBDECg4kkYNg4'], + }, + to: { + Account: ['3UiNwnmZ64YR423uamgZyY8RnRkD88tfn6SYtKzvWZCkyFdN94'], + }, + data: [], + }, + ]; + const fullSchema = Buffer.from( + fs.readFileSync('./test/ci/resources/cis2-nft-schema.bin') + ); + const schemaVersion = 1; + const contractName = ContractName.fromStringUnchecked('CIS2-NFT'); + const functionName = EntrypointName.fromStringUnchecked('transfer'); + + const serializedParameter = serializeUpdateContractParameters( + contractName, + functionName, + parameters, + fullSchema, + schemaVersion + ); + + const serializedType = serializeTypeValue( + parameters, + getUpdateContractParameterSchema( + fullSchema, + contractName, + functionName, + schemaVersion + ) + ); + + expect(Parameter.toHexString(serializedParameter)).toEqual( + Parameter.toHexString(serializedType) + ); +}); + +test('serializeTypeValue throws an error if unable to serialize', () => { + expect(() => serializeTypeValue('test', Buffer.alloc(0))).toThrowError( + Error + ); +}); + +test('Parameter serialization works for U64_MAX', () => { + const updateParam = serializeUpdateContractParameters( + ContractName.fromStringUnchecked('test'), + EntrypointName.fromStringUnchecked('receive'), + U64_MAX, + Buffer.from(TEST_CONTRACT_U64, 'base64') + ); + const initParam = serializeInitContractParameters( + ContractName.fromStringUnchecked('test'), + U64_MAX, + Buffer.from(TEST_CONTRACT_U64, 'base64') + ); + expect(Parameter.toHexString(updateParam)).toEqual('ffffffffffffffff'); + expect(Parameter.toHexString(initParam)).toEqual('ffffffffffffffff'); +}); + +test('Parameter serialization errors on (U64_MAX + 1)', () => { + const errMsg = + 'Unable to serialize parameters, due to: Unsigned integer required'; + const updateParam = () => + serializeUpdateContractParameters( + ContractName.fromStringUnchecked('test'), + EntrypointName.fromStringUnchecked('receive'), + U64_MAX + 1n, + Buffer.from(TEST_CONTRACT_U64, 'base64') + ); + const initParam = () => + serializeInitContractParameters( + ContractName.fromStringUnchecked('test'), + U64_MAX + 1n, + Buffer.from(TEST_CONTRACT_U64, 'base64') + ); + expect(updateParam).toThrow(errMsg); + expect(initParam).toThrow(errMsg); +}); diff --git a/packages/sdk/test/ci/serialization.test.ts b/packages/sdk/test/ci/serialization.test.ts new file mode 100644 index 000000000..476ffb7c5 --- /dev/null +++ b/packages/sdk/test/ci/serialization.test.ts @@ -0,0 +1,61 @@ +import * as AccountAddress from '../../src/types/AccountAddress.js'; +import * as CcdAmount from '../../src/types/CcdAmount.js'; +import { + serializeAccountTransactionForSubmission, + serializeAccountTransactionSignature, +} from '../../src/serialization.js'; +import { + AccountTransaction, + AccountTransactionHeader, + AccountTransactionSignature, + AccountTransactionType, + SimpleTransferPayload, +} from '../../src/types.js'; +import * as TransactionExpiry from '../../src/types/TransactionExpiry.js'; +import * as SequenceNumber from '../../src/types/SequenceNumber.js'; + +test('fail account transaction serialization if no signatures', () => { + const simpleTransferPayload: SimpleTransferPayload = { + amount: CcdAmount.fromMicroCcd(5100000n), + toAddress: AccountAddress.fromBase58( + '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' + ), + }; + + const header: AccountTransactionHeader = { + expiry: TransactionExpiry.futureMinutes(20), + nonce: SequenceNumber.create(1), + sender: AccountAddress.fromBase58( + '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' + ), + }; + + const simpleTransferAccountTransaction: AccountTransaction = { + header: header, + payload: simpleTransferPayload, + type: AccountTransactionType.Transfer, + }; + + expect(() => + serializeAccountTransactionForSubmission( + simpleTransferAccountTransaction, + {} + ) + ).toThrow(); +}); + +test('serialization of an account signature with two credentials', () => { + const signature: AccountTransactionSignature = { + 0: { + 0: '893f2e4a230bcbeee24675454c4ca95a2f55fd33f328958b626c6fa368341e07902c9ffe7864c3bee23b2b2300ed0922eb814ea41fdee25035be8cddc5c3980f', + }, + 1: { + 0: '620d859224c40160c2bb03dbe84e9f57b8ed17f1a5df28b4e21f10658992531ef27655e6b74b8e47923e1ccb0413d563205e8b6c0cd22b3adce5dc7dc1daf603', + }, + }; + + const serializedSignature = serializeAccountTransactionSignature(signature); + expect(serializedSignature.toString('hex')).toBe( + '020001000040893f2e4a230bcbeee24675454c4ca95a2f55fd33f328958b626c6fa368341e07902c9ffe7864c3bee23b2b2300ed0922eb814ea41fdee25035be8cddc5c3980f0101000040620d859224c40160c2bb03dbe84e9f57b8ed17f1a5df28b4e21f10658992531ef27655e6b74b8e47923e1ccb0413d563205e8b6c0cd22b3adce5dc7dc1daf603' + ); +}); diff --git a/packages/common/test/signHelpers.test.ts b/packages/sdk/test/ci/signHelpers.test.ts similarity index 93% rename from packages/common/test/signHelpers.test.ts rename to packages/sdk/test/ci/signHelpers.test.ts index 7f7d96d99..4cc0b3ecf 100644 --- a/packages/common/test/signHelpers.test.ts +++ b/packages/sdk/test/ci/signHelpers.test.ts @@ -2,17 +2,20 @@ import { signMessage, buildBasicAccountSigner, verifyMessageSignature, -} from '../src/signHelpers'; +} from '../../src/signHelpers.js'; import { AccountAddress, AccountInfo, buildAccountSigner, SimpleAccountKeys, -} from '../src'; +} from '../../src/index.js'; -const TEST_ACCOUNT_SINGLE = - '3eP94feEdmhYiPC1333F9VoV31KGMswonuHk5tqmZrzf761zK5'; -const TEST_ACCOUNT_MULTI = '4hTGW1Uz6u2hUgEtwWjJUdZQncVpHGWZPgGdRpgL1VNn5NzyHd'; +const TEST_ACCOUNT_SINGLE = AccountAddress.fromBase58( + '3eP94feEdmhYiPC1333F9VoV31KGMswonuHk5tqmZrzf761zK5' +); +const TEST_ACCOUNT_MULTI = AccountAddress.fromBase58( + '4hTGW1Uz6u2hUgEtwWjJUdZQncVpHGWZPgGdRpgL1VNn5NzyHd' +); const TEST_KEY_SINGLE = 'e1cf504954663e49f4fe884c7c35415b09632cccd82d3d2a62ab2825e67d785d'; @@ -76,14 +79,14 @@ const testEachMessageType = test.each(['test', Buffer.from('test', 'utf8')]); testEachMessageType('[%o] test signMessage', async (message) => { const sign = () => signMessage(account, message, signer); - let account = new AccountAddress(TEST_ACCOUNT_SINGLE); + let account = TEST_ACCOUNT_SINGLE; let signer = buildBasicAccountSigner(TEST_KEY_SINGLE); let signature = await sign(); expect(signature[0][0]).toBe( '445197d79ca90d8cc8440328dac9f307932ade0c03cc7aa575b59b746e26e5f1bca13ade5ff7a56e918ba5a32450fdf52b034cd2580929b21213263e81f7f809' ); - account = new AccountAddress(TEST_ACCOUNT_MULTI); + account = TEST_ACCOUNT_MULTI; signer = buildAccountSigner(TEST_KEYS_MULTI); signature = await sign(); @@ -143,8 +146,9 @@ test('verifyMessageSignature returns false on the incorrect address', async () = }, }, { - accountAddress: - '3dbRxtzhb8MotFBgH5DcdFJy7t4we4N8Ep6Mxdha8XvLhq7YmZ', + accountAddress: AccountAddress.fromBase58( + '3dbRxtzhb8MotFBgH5DcdFJy7t4we4N8Ep6Mxdha8XvLhq7YmZ' + ), accountThreshold: 1, accountCredentials: TEST_CREDENTIALS_SINGLE, } as unknown as AccountInfo diff --git a/packages/common/test/types.test.ts b/packages/sdk/test/ci/types.test.ts similarity index 85% rename from packages/common/test/types.test.ts rename to packages/sdk/test/ci/types.test.ts index 9ac51b63c..a24f49c92 100644 --- a/packages/common/test/types.test.ts +++ b/packages/sdk/test/ci/types.test.ts @@ -1,4 +1,4 @@ -import { isRpcError, RpcError } from '../src'; +import { isRpcError, RpcError } from '../../src/index.js'; test('RPCError', () => { const rpcError: unknown = new RpcError('This is an RpcError'); diff --git a/packages/sdk/test/ci/types/AccountAddress.test.ts b/packages/sdk/test/ci/types/AccountAddress.test.ts new file mode 100644 index 000000000..7c91a674a --- /dev/null +++ b/packages/sdk/test/ci/types/AccountAddress.test.ts @@ -0,0 +1,17 @@ +import { AccountAddress } from '../../../src/index.js'; + +test('Base58 decode-encode results is the same', () => { + const address = AccountAddress.fromBuffer(new Uint8Array(32)); + const base58 = AccountAddress.toBase58(address); + const converted = AccountAddress.fromBase58(base58); + expect(AccountAddress.equals(converted, address)).toBeTruthy(); +}); + +test('Buffer encode-decode results is the same', () => { + const address = AccountAddress.fromBase58( + '3VwCfvVskERFAJ3GeJy2mNFrzfChqUymSJJCvoLAP9rtAwMGYt' + ); + const buffer = AccountAddress.toBuffer(address); + const converted = AccountAddress.fromBuffer(buffer); + expect(AccountAddress.equals(converted, address)).toBeTruthy(); +}); diff --git a/packages/sdk/test/ci/types/Duration.test.ts b/packages/sdk/test/ci/types/Duration.test.ts new file mode 100644 index 000000000..49e6870fe --- /dev/null +++ b/packages/sdk/test/ci/types/Duration.test.ts @@ -0,0 +1,33 @@ +import { Duration } from '../../../src/index.js'; + +describe('fromString', () => { + test('Parsing simple valid string', () => { + const duration = Duration.fromString('100ms'); + const value = Number(Duration.toMillis(duration)); + expect(value).toBe(100); + }); + + test('Parsing valid string', () => { + const duration = Duration.fromString('10d 1h 2m 7s'); + const value = Number(Duration.toMillis(duration)); + expect(value).toBe(867_727_000); + }); + + test('Fails when using invalid unit for a measure', () => { + expect(() => { + Duration.fromString('1w 10d'); + }).toThrow(); + }); + + test('Fails when using decimals in a measure', () => { + expect(() => { + Duration.fromString('10.0d'); + }).toThrow(); + }); + + test('Fails when using negative numbers in a measure', () => { + expect(() => { + Duration.fromString('-10d'); + }).toThrow(); + }); +}); diff --git a/packages/common/test/types/blockItemSummary.test.ts b/packages/sdk/test/ci/types/blockItemSummary.test.ts similarity index 66% rename from packages/common/test/types/blockItemSummary.test.ts rename to packages/sdk/test/ci/types/blockItemSummary.test.ts index 646079584..caa0053a4 100644 --- a/packages/common/test/types/blockItemSummary.test.ts +++ b/packages/sdk/test/ci/types/blockItemSummary.test.ts @@ -20,13 +20,24 @@ import { getSummaryContractUpdateLogs, getTransactionKindString, AccountTransactionType, -} from '../../src'; + Energy, + TransactionHash, + AccountAddress, + ContractAddress, + InitName, + Parameter, + ReceiveName, + ContractEvent, + CcdAmount, +} from '../../../src/index.js'; const chainUpdate: UpdateSummary = { type: TransactionSummaryType.UpdateTransaction, index: 0n, - energyCost: 0n, - hash: '4b4adfbe9a10a83601a1171bff0d9f916d259f744d1283726314482beeab60ee', + energyCost: Energy.create(0), + hash: TransactionHash.fromHexString( + '4b4adfbe9a10a83601a1171bff0d9f916d259f744d1283726314482beeab60ee' + ), effectiveTime: 1655118000n, payload: { updateType: UpdateType.Protocol, @@ -44,17 +55,21 @@ const chainUpdate: UpdateSummary = { const contractInit: InitContractSummary & BaseAccountTransactionSummary = { index: 0n, - energyCost: 1032n, - hash: '00205bab563b31dbd0d6cff9504a325953ac70a428ac7169f66620c40b20c431', + energyCost: Energy.create(1032), + hash: TransactionHash.fromHexString( + '00205bab563b31dbd0d6cff9504a325953ac70a428ac7169f66620c40b20c431' + ), type: TransactionSummaryType.AccountTransaction, cost: 2765192n, - sender: '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', + sender: AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' + ), transactionType: TransactionKindString.InitContract, contractInitialized: { tag: TransactionEventTag.ContractInitialized, - address: { index: 4416n, subindex: 0n }, - amount: 0n, - initName: 'init_cis2-receive-test', + address: ContractAddress.create(4416), + amount: CcdAmount.zero(), + initName: InitName.fromStringUnchecked('init_cis2-receive-test'), events: [], contractVersion: 1, ref: '627d5b8358ecf0eaa0442855d57bd84258aa1e06006cbb59ca03d31ddd5cb8b7', @@ -63,155 +78,200 @@ const contractInit: InitContractSummary & BaseAccountTransactionSummary = { const contractUpdate: UpdateContractSummary & BaseAccountTransactionSummary = { index: 0n, - energyCost: 3183n, - hash: '9f23369ed3f19cb5627f685d7193e58432e9b50e0841469b07b6d02aa7770901', + energyCost: Energy.create(3183), + hash: TransactionHash.fromHexString( + '9f23369ed3f19cb5627f685d7193e58432e9b50e0841469b07b6d02aa7770901' + ), type: TransactionSummaryType.AccountTransaction, cost: 8681698n, - sender: '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', + sender: AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' + ), transactionType: TransactionKindString.Update, events: [ { tag: TransactionEventTag.Updated, contractVersion: 1, - address: { index: 3496n, subindex: 0n }, + address: ContractAddress.create(3496), instigator: { type: 'AddressAccount', - address: '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', + address: AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' + ), }, - amount: 0n, - message: - '0100006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb490000', - receiveName: 'cis2-bridgeable.transfer', + amount: CcdAmount.zero(), + message: Parameter.fromHexString( + '0100006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb490000' + ), + receiveName: ReceiveName.fromStringUnchecked( + 'cis2-bridgeable.transfer' + ), events: [ 'ff006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb49', - ], + ].map(ContractEvent.fromHexString), }, { tag: TransactionEventTag.Interrupted, - address: { index: 3496n, subindex: 0n }, + address: ContractAddress.create(3496), events: [ 'ff006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb49', - ], + ].map(ContractEvent.fromHexString), }, { tag: TransactionEventTag.Updated, contractVersion: 1, - address: { index: 4416n, subindex: 0n }, + address: ContractAddress.create(4416), instigator: { type: 'AddressAccount', - address: '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', + address: AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' + ), }, - amount: 0n, - message: - '0100006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb490000', - receiveName: 'cis2-bridgeable.transfer', + amount: CcdAmount.zero(), + message: Parameter.fromHexString( + '0100006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb490000' + ), + receiveName: ReceiveName.fromStringUnchecked( + 'cis2-bridgeable.transfer' + ), events: [ 'ff006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb49', - ], + ].map(ContractEvent.fromHexString), }, { tag: TransactionEventTag.Transferred, - amount: 0n, - to: { - type: 'AddressAccount', - address: '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', - }, + amount: CcdAmount.zero(), + to: AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' + ), + from: ContractAddress.create(3496), }, { tag: TransactionEventTag.Transferred, - amount: 0n, - to: { - type: 'AddressAccount', - address: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', - }, + amount: CcdAmount.zero(), + to: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), + from: ContractAddress.create(3496), }, ], }; const rejected: FailedTransactionSummary & BaseAccountTransactionSummary = { index: 0n, - energyCost: 4600n, - hash: '9e3eb5a2d36cb125292c553be304d943148c861f284b5d58afd215d1cfbbd8bf', + energyCost: Energy.create(4600), + hash: TransactionHash.fromHexString( + '9e3eb5a2d36cb125292c553be304d943148c861f284b5d58afd215d1cfbbd8bf' + ), type: TransactionSummaryType.AccountTransaction, cost: 12403437n, - sender: '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', + sender: AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' + ), transactionType: TransactionKindString.Failed, failedTransactionType: TransactionKindString.Update, rejectReason: { tag: RejectReasonTag.RejectedReceive, - contractAddress: { index: 3496n, subindex: 0n }, - receiveName: 'cis2-bridgeable.transfer', + contractAddress: ContractAddress.create(3496), + receiveName: ReceiveName.fromStringUnchecked( + 'cis2-bridgeable.transfer' + ), rejectReason: -5, - parameter: - '0100006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f01271100000000000000000000000000000f006f6e526563656976696e67434953320000', + parameter: Parameter.fromHexString( + '0100006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f01271100000000000000000000000000000f006f6e526563656976696e67434953320000' + ), }, }; const transfer: BaseAccountTransactionSummary & TransferSummary = { index: 0n, - energyCost: 601n, - hash: 'a396ae28d1158650d52168ad108e7c5f566831fe5d0695ceab91044ba5eb6b5b', + energyCost: Energy.create(601), + hash: TransactionHash.fromHexString( + 'a396ae28d1158650d52168ad108e7c5f566831fe5d0695ceab91044ba5eb6b5b' + ), type: TransactionSummaryType.AccountTransaction, cost: 1651916n, - sender: '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', + sender: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), transactionType: TransactionKindString.Transfer, transfer: { tag: TransactionEventTag.Transferred, - amount: 2000000000n, - to: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + amount: CcdAmount.fromCcd(2000), + to: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), }, }; const transferToSelf: BaseAccountTransactionSummary & TransferSummary = { index: 0n, - energyCost: 601n, - hash: 'a396ae28d1158650d52168ad108e7c5f566831fe5d0695ceab91044ba5eb6b5b', + energyCost: Energy.create(601), + hash: TransactionHash.fromHexString( + 'a396ae28d1158650d52168ad108e7c5f566831fe5d0695ceab91044ba5eb6b5b' + ), type: TransactionSummaryType.AccountTransaction, cost: 1651916n, - sender: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + sender: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), transactionType: TransactionKindString.Transfer, transfer: { tag: TransactionEventTag.Transferred, - amount: 2000000000n, - to: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + amount: CcdAmount.fromCcd(2000), + to: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), }, }; const configureDelegation: BaseAccountTransactionSummary & ConfigureDelegationSummary = { index: 0n, - energyCost: 601n, - hash: 'a396ae28d1158650d52168ad108e7c5f566831fe5d0695ceab91044ba5eb6b5b', + energyCost: Energy.create(601), + hash: TransactionHash.fromHexString( + 'a396ae28d1158650d52168ad108e7c5f566831fe5d0695ceab91044ba5eb6b5b' + ), type: TransactionSummaryType.AccountTransaction, cost: 1651916n, - sender: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + sender: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), transactionType: TransactionKindString.ConfigureDelegation, events: [ { tag: TransactionEventTag.DelegationAdded, - delegatorId: 2499, - account: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + delegatorId: 2499n, + account: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), }, { tag: TransactionEventTag.DelegationSetDelegationTarget, - delegatorId: 2499, + delegatorId: 2499n, delegationTarget: { delegateType: DelegationTargetType.Baker, bakerId: 15, }, - account: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + account: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), }, { tag: TransactionEventTag.DelegationSetRestakeEarnings, - delegatorId: 2499, + delegatorId: 2499n, restakeEarnings: true, - account: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + account: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), }, { tag: TransactionEventTag.DelegationStakeIncreased, - delegatorId: 2499, - newStake: 240000000n, - account: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + delegatorId: 2499n, + newStake: CcdAmount.fromMicroCcd(240000000n), + account: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), }, ], }; @@ -269,14 +329,14 @@ describe('affectedContracts', () => { test('Returns list of one contract address corresponding to contract init transaction events', () => { const contracts = affectedContracts(contractInit); - expect(contracts).toEqual([{ index: 4416n, subindex: 0n }]); + expect(contracts).toEqual([ContractAddress.create(4416)]); }); test('Returns list of unique contract addresses corresponding to contract update transaction events', () => { const contracts = affectedContracts(contractUpdate); expect(contracts).toEqual([ - { index: 3496n, subindex: 0n }, - { index: 4416n, subindex: 0n }, + ContractAddress.create(3496), + ContractAddress.create(4416), ]); }); }); @@ -289,31 +349,41 @@ describe('affectedAccounts', () => { test('Returns list of unique account addresses corresponding to transaction', () => { let accounts = affectedAccounts(contractUpdate); - expect(accounts).toEqual([ - '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', - '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', - ]); + expect(accounts).toEqual( + [ + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', + ].map(AccountAddress.fromBase58) + ); accounts = affectedAccounts(rejected); expect(accounts).toEqual([ - '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd', + AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' + ), ]); accounts = affectedAccounts(transfer); - expect(accounts).toEqual([ - '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', - '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - ]); + expect(accounts).toEqual( + [ + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + ].map(AccountAddress.fromBase58) + ); accounts = affectedAccounts(transferToSelf); - expect(accounts).toEqual([ - '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - ]); + expect(accounts).toEqual( + ['4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe'].map( + AccountAddress.fromBase58 + ) + ); accounts = affectedAccounts(configureDelegation); - expect(accounts).toEqual([ - '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', - ]); + expect(accounts).toEqual( + ['4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe'].map( + AccountAddress.fromBase58 + ) + ); }); }); @@ -330,22 +400,22 @@ describe('getSummaryContractUpdateLogs', () => { const logs = getSummaryContractUpdateLogs(contractUpdate); expect(logs).toEqual([ { - address: { index: 3496n, subindex: 0n }, + address: ContractAddress.create(3496), events: [ 'ff006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb49', - ], + ].map(ContractEvent.fromHexString), }, { - address: { index: 3496n, subindex: 0n }, + address: ContractAddress.create(3496), events: [ 'ff006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb49', - ], + ].map(ContractEvent.fromHexString), }, { - address: { index: 4416n, subindex: 0n }, + address: ContractAddress.create(4416), events: [ 'ff006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb49', - ], + ].map(ContractEvent.fromHexString), }, ]); }); diff --git a/packages/sdk/test/ci/types/blockSpecialEvents.test.ts b/packages/sdk/test/ci/types/blockSpecialEvents.test.ts new file mode 100644 index 000000000..f3b62df8a --- /dev/null +++ b/packages/sdk/test/ci/types/blockSpecialEvents.test.ts @@ -0,0 +1,192 @@ +import { + BlockSpecialEventBakingRewards, + BlockSpecialEventPaydayPoolReward, + BlockSpecialEventBlockAccrueReward, + BlockSpecialEventPaydayFoundationReward, + BlockSpecialEventFinalizationRewards, + BlockSpecialEventMint, + BlockSpecialEventPaydayAccountReward, + BlockSpecialEventBlockReward, + specialEventAffectedAccounts, + CcdAmount, + AccountAddress, +} from '../../../src/index.js'; + +const bakingRewards: BlockSpecialEventBakingRewards = { + tag: 'bakingRewards', + remainder: CcdAmount.zero(), + bakingRewards: [ + { + account: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), + amount: CcdAmount.fromMicroCcd(400000), + }, + { + account: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), + amount: CcdAmount.fromMicroCcd(400000), + }, + ], +}; + +const finalizationRewards: BlockSpecialEventFinalizationRewards = { + tag: 'finalizationRewards', + remainder: CcdAmount.zero(), + finalizationRewards: [ + { + account: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), + amount: CcdAmount.fromMicroCcd(400000), + }, + { + account: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), + amount: CcdAmount.fromMicroCcd(400000), + }, + ], +}; + +const foundationReward: BlockSpecialEventPaydayFoundationReward = { + tag: 'paydayFoundationReward', + developmentCharge: CcdAmount.fromMicroCcd(123), + foundationAccount: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), +}; + +const mint: BlockSpecialEventMint = { + tag: 'mint', + mintBakingReward: CcdAmount.zero(), + mintFinalizationReward: CcdAmount.zero(), + mintPlatformDevelopmentCharge: CcdAmount.zero(), + foundationAccount: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), +}; + +const paydayAccountReward: BlockSpecialEventPaydayAccountReward = { + tag: 'paydayAccountReward', + account: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), + transactionFees: CcdAmount.fromMicroCcd(123), + bakerReward: CcdAmount.fromMicroCcd(123), + finalizationReward: CcdAmount.fromMicroCcd(123), +}; + +const foundationBlockReward: BlockSpecialEventBlockReward = { + tag: 'blockReward', + transactionFees: CcdAmount.fromMicroCcd(1231241), + bakerReward: CcdAmount.fromMicroCcd(12314), + foundationCharge: CcdAmount.fromMicroCcd(12), + newGasAccount: CcdAmount.fromMicroCcd(1), + oldGasAccount: CcdAmount.zero(), + baker: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), + foundationAccount: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), +}; + +const blockReward: BlockSpecialEventBlockReward = { + tag: 'blockReward', + transactionFees: CcdAmount.fromMicroCcd(1231241), + bakerReward: CcdAmount.fromMicroCcd(12314), + foundationCharge: CcdAmount.fromMicroCcd(12), + newGasAccount: CcdAmount.fromMicroCcd(1), + oldGasAccount: CcdAmount.zero(), + baker: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), + foundationAccount: AccountAddress.fromBase58( + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV' + ), +}; + +const paydayPoolReward: BlockSpecialEventPaydayPoolReward = { + tag: 'paydayPoolReward', + poolOwner: 123n, + finalizationReward: CcdAmount.fromMicroCcd(123), + bakerReward: CcdAmount.fromMicroCcd(12314), + transactionFees: CcdAmount.fromMicroCcd(1231241), +}; + +const accrueReward: BlockSpecialEventBlockAccrueReward = { + tag: 'blockAccrueReward', + transactionFees: CcdAmount.fromMicroCcd(1231241), + bakerReward: CcdAmount.fromMicroCcd(12314), + baker: 0n, + foundationCharge: CcdAmount.fromMicroCcd(12), + newGasAccount: CcdAmount.fromMicroCcd(1), + oldGasAccount: CcdAmount.zero(), + passiveReward: CcdAmount.fromMicroCcd(123), +}; + +describe('specialEventAffectedAccounts', () => { + test('Returns empty list of accounts for events with no account payouts', () => { + let accounts = specialEventAffectedAccounts(paydayPoolReward); + expect(accounts).toEqual([]); + + accounts = specialEventAffectedAccounts(accrueReward); + expect(accounts).toEqual([]); + }); + + test('Returns correct list of accounts for events with account payouts', () => { + let accounts = specialEventAffectedAccounts(bakingRewards); + expect(accounts).toEqual( + [ + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', + ].map(AccountAddress.fromBase58) + ); + + accounts = specialEventAffectedAccounts(finalizationRewards); + expect(accounts).toEqual( + [ + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', + ].map(AccountAddress.fromBase58) + ); + + accounts = specialEventAffectedAccounts(foundationReward); + expect(accounts).toEqual( + ['3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV'].map( + AccountAddress.fromBase58 + ) + ); + + accounts = specialEventAffectedAccounts(mint); + expect(accounts).toEqual( + ['3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV'].map( + AccountAddress.fromBase58 + ) + ); + + accounts = specialEventAffectedAccounts(paydayAccountReward); + expect(accounts).toEqual( + ['4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe'].map( + AccountAddress.fromBase58 + ) + ); + + accounts = specialEventAffectedAccounts(foundationBlockReward); + expect(accounts).toEqual( + ['3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV'].map( + AccountAddress.fromBase58 + ) + ); + + accounts = specialEventAffectedAccounts(blockReward); + expect(accounts).toEqual( + [ + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + '3v1JUB1R1JLFtcKvHqD9QFqe2NXeBF53tp69FLPHYipTjNgLrV', + ].map(AccountAddress.fromBase58) + ); + }); +}); diff --git a/packages/common/test/types/ccdAmount.test.ts b/packages/sdk/test/ci/types/ccdAmount.test.ts similarity index 81% rename from packages/common/test/types/ccdAmount.test.ts rename to packages/sdk/test/ci/types/ccdAmount.test.ts index 0d011ef65..7a0ef3517 100644 --- a/packages/common/test/types/ccdAmount.test.ts +++ b/packages/sdk/test/ci/types/ccdAmount.test.ts @@ -1,5 +1,5 @@ import Big from 'big.js'; -import { CcdAmount } from '../../src'; +import { CcdAmount } from '../../../src/index.js'; describe('To and from ccd as strings', () => { test('Parses one CCD correctly', () => { @@ -29,17 +29,17 @@ describe('To and from ccd as strings', () => { }); test('Returns correct amount of CCD, test 1', () => { - const ccd = new CcdAmount(1000n); - expect(ccd.toCcd()).toEqual(Big('0.001')); + const ccd = CcdAmount.fromMicroCcd(1000); + expect(CcdAmount.toCcd(ccd)).toEqual(Big('0.001')); }); test('Returns correct amount of CCD, test 2', () => { - const ccd = new CcdAmount(123456789n); - expect(ccd.toCcd()).toEqual(Big('123.456789')); + const ccd = CcdAmount.fromMicroCcd(123456789); + expect(CcdAmount.toCcd(ccd)).toEqual(Big('123.456789')); }); test('FromCcd correctly takes comma as a decimal seperator', () => { - expect(CcdAmount.fromCcd('10,000').toCcd()).toEqual(Big('10')); + expect(CcdAmount.toCcd(CcdAmount.fromCcd('10,000'))).toEqual(Big('10')); }); test('CcdAmount constructor correctly rejects multiple comma seperators', () => { @@ -55,7 +55,7 @@ describe('To and from ccd as strings', () => { }); test('toCcd is equal to microCcdToCcd', () => { - const ccd1 = new CcdAmount('1').toCcd(); + const ccd1 = CcdAmount.toCcd(CcdAmount.fromMicroCcd('1')); const ccd2 = CcdAmount.microCcdToCcd('1'); expect(ccd1).toEqual(ccd2); }); diff --git a/packages/sdk/test/ci/types/json.test.ts b/packages/sdk/test/ci/types/json.test.ts new file mode 100644 index 000000000..149649089 --- /dev/null +++ b/packages/sdk/test/ci/types/json.test.ts @@ -0,0 +1,87 @@ +import { + Parameter, + ReturnValue, + SequenceNumber, + Energy, + TransactionHash, + BlockHash, + ContractName, + InitName, + ReceiveName, + CredentialRegistrationId, + AccountAddress, + ContractAddress, + EntrypointName, + Timestamp, + Duration, + CcdAmount, + TransactionExpiry, + ModuleReference, + DataBlob, + jsonStringify, + jsonParse, +} from '../../../src/pub/types.js'; + +describe('JSON ID test', () => { + test('Stringified types are parsed correctly', () => { + const original = { + parameter: Parameter.fromHexString('010203'), + nested: { + returnValue: ReturnValue.fromHexString('020103'), + }, + sequenceNumber: SequenceNumber.create(1), + energy: Energy.create(123), + transactionhash: TransactionHash.fromHexString( + '443682391401cd5938a8b87275e5f5e6d0c8178d512391bca81d9a2a45f11a63' + ), + blockHash: BlockHash.fromHexString( + 'c2ef4acafd8ac8956ad941b4c4b87688baa714eb43510f078427db1b52e824e3 ' + ), + some: { + deeply: { + nested: { + contractName: ContractName.fromString('test-contract'), + }, + }, + }, + initName: InitName.fromString('init_another-contract'), + receiveName: ReceiveName.fromString('some-contract.receive'), + credRegId: CredentialRegistrationId.fromHexString( + '83e4b29e1e2582a6f1dcc93bf2610ce6b0a6ba89c8f03e661f403b4c2e055d3adb80d071c2723530926bb8aed3ed52b1' + ), + accountAddress: AccountAddress.fromBase58( + '35CJPZohio6Ztii2zy1AYzJKvuxbGG44wrBn7hLHiYLoF2nxnh' + ), + contractAddress: ContractAddress.create(1234), + entrypointName: EntrypointName.fromString('entrypoint'), + timestamp: Timestamp.fromDate(new Date()), + duration: Duration.fromMillis(100000), + ccdAmount: CcdAmount.fromMicroCcd(123), + transactionExpiry: TransactionExpiry.futureMinutes(5), + moduleRef: ModuleReference.fromHexString( + '5d99b6dfa7ba9dc0cac8626754985500d51d6d06829210748b3fd24fa30cde4a' + ), + dataBlob: new DataBlob(Buffer.from('030201', 'hex')), + }; + + const json = jsonStringify(original); + const parsed = jsonParse(json); + + expect(parsed).toEqual(original); + }); +}); + +describe('jsonStringify', () => { + test('Throws on circular reference', () => { + const obj = {}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (obj as any)['circular'] = obj; + + expect(() => jsonStringify(obj)).toThrowError(); + }); + + test('Allow non-circular references to same object', () => { + const other = { test: 1 }; + expect(() => jsonStringify([other, other])).not.toThrow(); + }); +}); diff --git a/packages/common/test/uleb128.test.ts b/packages/sdk/test/ci/uleb128.test.ts similarity index 98% rename from packages/common/test/uleb128.test.ts rename to packages/sdk/test/ci/uleb128.test.ts index 16da2a59c..7ac52f129 100644 --- a/packages/common/test/uleb128.test.ts +++ b/packages/sdk/test/ci/uleb128.test.ts @@ -1,9 +1,8 @@ -import { Buffer } from 'buffer/'; import { uleb128Decode, uleb128DecodeWithIndex, uleb128Encode, -} from '../src/uleb128'; +} from '../../src/uleb128.js'; test('uleb128 encodes value as expected', () => { let value = 0n; diff --git a/packages/sdk/test/ci/util.test.ts b/packages/sdk/test/ci/util.test.ts new file mode 100644 index 000000000..7479a1250 --- /dev/null +++ b/packages/sdk/test/ci/util.test.ts @@ -0,0 +1,45 @@ +import { stringToInt, wasmToSchema } from '../../src/util.js'; +import { readFileSync } from 'fs'; + +test('stringToInt transforms chosen field, but not others', () => { + const keysToTransform = ['a']; + const input = + '{ "a":"90071992547409910", "b":"90071992547409911", "aa":"90071992547409912"}'; + const transformed = stringToInt(input, keysToTransform); + expect(transformed).toEqual( + '{ "a":90071992547409910, "b":"90071992547409911", "aa":"90071992547409912"}' + ); +}); + +test('stringToInt transforms multiple fields', () => { + const keysToTransform = ['a', 'b']; + const input = + '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":"1"}}'; + const transformed = stringToInt(input, keysToTransform); + expect(transformed).toEqual( + '{ "a":90071992547409910, "b":90071992547409911, "aa":{"a":12071992547409910,"c":"1"}}' + ); +}); + +test('stringToInt will not change the string if no keys match', () => { + const keysToTransform = ['d', 'aaa']; + const input = + '{ "a":"90071992547409910", "b":"90071992547409911", "aa":{"a":"12071992547409910","c":"1"}}'; + const transformed = stringToInt(input, keysToTransform); + expect(transformed).toEqual(input); +}); + +test('Embedded schema is the same as a seperate schema file', () => { + const versionedWasmModule = readFileSync( + 'test/ci/resources/icecream-with-schema.wasm' + ); + // Strip module version information + const wasmModule = versionedWasmModule.subarray(8); + + const seperateSchema = readFileSync( + 'test/ci/resources/icecream-schema.bin' + ); + const embeddedSchema = wasmToSchema(wasmModule); + + expect(new Uint8Array(seperateSchema)).toEqual(embeddedSchema); +}); diff --git a/packages/common/test/web3IdHelpers.test.ts b/packages/sdk/test/ci/web3IdHelpers.test.ts similarity index 95% rename from packages/common/test/web3IdHelpers.test.ts rename to packages/sdk/test/ci/web3IdHelpers.test.ts index bd48c3838..18a37d1c8 100644 --- a/packages/common/test/web3IdHelpers.test.ts +++ b/packages/sdk/test/ci/web3IdHelpers.test.ts @@ -1,19 +1,18 @@ +import fs from 'fs'; import { AttributeType, StatementAttributeType, TimestampAttribute, statementAttributeTypeToAttributeType, -} from '../src'; -import { compareStringAttributes, isStringAttributeInRange, timestampToDate, verifyWeb3IdCredentialSignature, -} from '../src/web3IdHelpers'; -import fs from 'fs'; + ContractAddress, +} from '../../src/index.js'; const globalContext = JSON.parse( - fs.readFileSync('./test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; const signature = @@ -35,7 +34,7 @@ const holder = '32c0b24855060114c7b781bc94fcb089edc255f16e78ece9b597bf0c6880fa98'; const issuerPublicKey = '2DC9C80EBF73F6EE44F6BD8C067C1FCE660C9B78779A5CD4674A56B59C3474B2'; -const issuerContract = { index: 5463n, subindex: 0n }; +const issuerContract = ContractAddress.create(5463); test('verifyWeb3IdCredentialSignature', async () => { expect( @@ -69,7 +68,7 @@ test('verifyWeb3IdCredentialSignature can reject due to incorrect signature', as }); test('verifyWeb3IdCredentialSignature can reject due to incorrect issuer contract', async () => { - const incorrectIssuerContract = { index: 4463n, subindex: 0n }; + const incorrectIssuerContract = ContractAddress.create(4463); expect( verifyWeb3IdCredentialSignature({ globalContext, @@ -123,7 +122,7 @@ test('verifyWeb3IdCredentialSignature with timestamps', async () => { '666b4811c26b36357186b6c286261930d12a8772776d70c485a9b16059881824'; const issuerPublicKey = '00ee7c443e604fbe6defbbc08ee0bf25e76656037fc189c41e631ac3a0ab136d'; - const issuerContract = { index: 6105n, subindex: 0n }; + const issuerContract = ContractAddress.create(6105); expect( verifyWeb3IdCredentialSignature({ diff --git a/packages/common/test/web3Proofs.test.ts b/packages/sdk/test/ci/web3Proofs.test.ts similarity index 96% rename from packages/common/test/web3Proofs.test.ts rename to packages/sdk/test/ci/web3Proofs.test.ts index 71216fdf4..128ca765b 100644 --- a/packages/common/test/web3Proofs.test.ts +++ b/packages/sdk/test/ci/web3Proofs.test.ts @@ -1,6 +1,7 @@ import { AttributeKeyString, ConcordiumHdWallet, + ContractAddress, createAccountDID, createWeb3IdDID, dateToTimestampAttribute, @@ -13,39 +14,36 @@ import { VerifiablePresentation, verifyAtomicStatements, Web3StatementBuilder, -} from '../src'; +} from '../../src/index.js'; import { expectedAccountCredentialPresentation, expectedWeb3IdCredentialPresentation, -} from './resources/expectedPresentation'; -import { expectedStatementMixed } from './resources/expectedStatements'; +} from './resources/expectedPresentation.js'; +import { expectedStatementMixed } from './resources/expectedStatements.js'; import { CommitmentInput, CredentialSchemaSubject, TimestampAttribute, -} from '../src/web3ProofTypes'; -import { TEST_SEED_1 } from './HdWallet.test'; +} from '../../src/web3-id/web3IdProofTypes.js'; +import { TEST_SEED_1 } from './HdWallet.test.js'; import fs from 'fs'; import { GenericMembershipStatement, GenericNonMembershipStatement, GenericRangeStatement, -} from '../src/commonProofTypes'; +} from '../../src/commonProofTypes.js'; test('Generate V2 statement', () => { const builder = new Web3StatementBuilder(); const statement = builder .addForVerifiableCredentials( - [ - { index: 2101n, subindex: 0n }, - { index: 1337n, subindex: 42n }, - ], + [ContractAddress.create(2101), ContractAddress.create(1337, 42)], (b) => b .addRange('b', 80n, 1237n) .addMembership('c', ['aa', 'ff', 'zz']) ) - .addForVerifiableCredentials([{ index: 1338n, subindex: 0n }], (b) => + .addForVerifiableCredentials([ContractAddress.create(1338)], (b) => b .addRange('a', 80n, 1237n) .addNonMembership('d', ['aa', 'ff', 'zz']) @@ -59,7 +57,7 @@ test('Generate V2 statement', () => { test('create Web3Id proof with account credentials', () => { const globalContext = JSON.parse( - fs.readFileSync('./test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; const values: Record = {}; @@ -137,7 +135,7 @@ test('create Web3Id proof with account credentials', () => { test('create Web3Id proof with Web3Id Credentials', () => { const globalContext = JSON.parse( - fs.readFileSync('./test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; const randomness: Record = {}; @@ -151,7 +149,7 @@ test('create Web3Id proof with Web3Id Credentials', () => { const wallet = ConcordiumHdWallet.fromHex(TEST_SEED_1, 'Testnet'); const publicKey = wallet - .getVerifiableCredentialPublicKey({ index: 1n, subindex: 0n }, 1) + .getVerifiableCredentialPublicKey(ContractAddress.create(1), 1) .toString('hex'); const values: Record = { @@ -181,10 +179,7 @@ test('create Web3Id proof with Web3Id Credentials', () => { { type: 'web3Issuer', signer: wallet - .getVerifiableCredentialSigningKey( - { index: 1n, subindex: 0n }, - 1 - ) + .getVerifiableCredentialSigningKey(ContractAddress.create(1), 1) .toString('hex'), values, randomness, @@ -286,7 +281,7 @@ test('Generate statement with timestamp', () => { const statement = builder .addForVerifiableCredentials( - [{ index: 0n, subindex: 0n }], + [ContractAddress.create(0)], (b) => b.addRange('graduationDate', lower, upper), schemaWithTimeStamp ) @@ -307,7 +302,7 @@ test('Generate statement with timestamp fails if not timestamp attribute', () => expect(() => builder.addForVerifiableCredentials( - [{ index: 0n, subindex: 0n }], + [ContractAddress.create(0)], (b) => b // Use degreeName, which is a string property, not timestamp diff --git a/packages/nodejs/test/CIS2Contract.test.ts b/packages/sdk/test/client/CIS2Contract.test.ts similarity index 64% rename from packages/nodejs/test/CIS2Contract.test.ts rename to packages/sdk/test/client/CIS2Contract.test.ts index b13886fec..a362bd166 100644 --- a/packages/nodejs/test/CIS2Contract.test.ts +++ b/packages/sdk/test/client/CIS2Contract.test.ts @@ -1,25 +1,27 @@ -import { Buffer } from 'buffer/'; import { + AccountAddress, AccountTransactionType, - CIS2Contract, + BlockHash, ContractAddress, - serializeTypeValue, + Energy, + EntrypointName, + Parameter, + ReceiveName, TransactionEventTag, -} from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from './testHelpers'; + CIS2Contract, + serializeTypeValue, +} from '../../src/index.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; -const CIS2_FT_ADDRESS: ContractAddress = { - index: 3496n, - subindex: 0n, -}; -const CIS2_NFT_ADDRESS: ContractAddress = { - index: 1696n, - subindex: 0n, -}; +const CIS2_FT_ADDRESS = ContractAddress.create(3496); +const CIS2_NFT_ADDRESS = ContractAddress.create(1696); -const TEST_BLOCK = - '3e9d90325c61ab190065f3c90364beeb925833319de68d982ec6da7762e8357b'; -const TEST_ACCOUNT = '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd'; +const TEST_BLOCK = BlockHash.fromHexString( + '3e9d90325c61ab190065f3c90364beeb925833319de68d982ec6da7762e8357b' +); +const TEST_ACCOUNT = AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' +); const getCIS2Single = () => CIS2Contract.create(getNodeClient(), CIS2_FT_ADDRESS); @@ -27,10 +29,10 @@ const getCIS2Multi = () => CIS2Contract.create(getNodeClient(), CIS2_NFT_ADDRESS); test('create throws on non cis-2', async () => { - const promise = CIS2Contract.create(getNodeClient(), { - index: 3494n, - subindex: 0n, - }); + const promise = CIS2Contract.create( + getNodeClient(), + ContractAddress.create(3494) + ); expect(promise).rejects.toThrow(); }); @@ -56,7 +58,7 @@ test('balanceOf', async () => { test('operatorOf', async () => { const cis2Single = await getCIS2Single(); const isOperator = await cis2Single.operatorOf( - { owner: TEST_ACCOUNT, address: { index: 3494n, subindex: 0n } }, + { owner: TEST_ACCOUNT, address: ContractAddress.create(3494) }, TEST_BLOCK ); expect(isOperator).toEqual(true); @@ -66,9 +68,11 @@ test('operatorOf', async () => { [ { owner: TEST_ACCOUNT, - address: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', + address: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), }, - { owner: TEST_ACCOUNT, address: { index: 3494n, subindex: 0n } }, + { owner: TEST_ACCOUNT, address: ContractAddress.create(3494) }, ], TEST_BLOCK ); @@ -110,13 +114,13 @@ test('dryRun.transfer', async () => { }, TEST_BLOCK ); - expect(result.usedEnergy).toEqual(2803n); + expect(result.usedEnergy.value).toBe(2803n); // Results in 1 transfer event expect( result.tag === 'success' && result.events[0].tag === TransactionEventTag.Updated && result.events[0].events.length - ).toEqual(1); + ).toBe(1); const resultMulti = await cis2.dryRun.transfer( TEST_ACCOUNT, @@ -124,26 +128,30 @@ test('dryRun.transfer', async () => { { tokenId: '', from: TEST_ACCOUNT, - to: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', + to: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), tokenAmount: 100n, }, { tokenId: '', from: TEST_ACCOUNT, - to: '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe', + to: AccountAddress.fromBase58( + '4owvMHZSKsPW8QGYUEWSdgqxfoPBh3ZwPameBV46pSvmeHDkEe' + ), tokenAmount: 120n, }, ], TEST_BLOCK ); - expect(resultMulti.usedEnergy).toEqual(3278n); + expect(resultMulti.usedEnergy.value).toBe(3278n); // Results in 2 transfer events expect( resultMulti.tag === 'success' && resultMulti.events[0].tag === TransactionEventTag.Updated && resultMulti.events[0].events.length - ).toEqual(2); + ).toBe(2); const resultContractReceiver = await cis2.dryRun.transfer( TEST_ACCOUNT, @@ -151,24 +159,28 @@ test('dryRun.transfer', async () => { tokenId: '', from: TEST_ACCOUNT, to: { - address: { index: 4416n, subindex: 0n }, - hookName: 'onReceivingCIS2', + address: ContractAddress.create(4416), + hookName: EntrypointName.fromStringUnchecked('onReceivingCIS2'), }, tokenAmount: 0n, }, - 'a03ca5112f2bf38bbb4f4d524432ff3a060226d823a3a868aa23b7d0d628e112' + BlockHash.fromHexString( + 'a03ca5112f2bf38bbb4f4d524432ff3a060226d823a3a868aa23b7d0d628e112' + ) ); - expect(resultContractReceiver.tag).toEqual('success'); + expect(resultContractReceiver.tag).toBe('success'); }); describe('createTransfer', () => { test('single update', async () => { const cis2 = await getCIS2Single(); const { type, parameter, payload } = cis2.createTransfer( - { energy: 1000000n }, + { energy: Energy.create(1000000) }, { tokenId: '', - to: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', + to: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), from: TEST_ACCOUNT, tokenAmount: 100n, } @@ -185,7 +197,7 @@ describe('createTransfer', () => { { token_id: '', amount: '100', - from: { Account: [TEST_ACCOUNT] }, + from: { Account: [AccountAddress.toBase58(TEST_ACCOUNT)] }, to: { Account: [ '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', @@ -197,31 +209,48 @@ describe('createTransfer', () => { // Checks that payload contains the expected values expect(payload.amount.microCcdAmount).toEqual(0n); - expect(payload.address).toEqual({ index: 3496n, subindex: 0n }); - expect(payload.message.toString('hex')).toEqual(expectedParameterHex); - expect(payload.receiveName).toEqual('cis2-bridgeable.transfer'); - expect(payload.maxContractExecutionEnergy).toEqual(1000000n); + expect( + ContractAddress.equals( + payload.address, + ContractAddress.create(3496) + ) + ).toBeTruthy(); + expect(Parameter.toHexString(payload.message)).toEqual( + expectedParameterHex + ); + expect(payload.receiveName).toEqual( + ReceiveName.fromStringUnchecked('cis2-bridgeable.transfer') + ); + expect(payload.maxContractExecutionEnergy.value).toEqual(1000000n); }); test('multiple transfers', async () => { const cis2 = await getCIS2Single(); - const { parameter, schema } = cis2.createTransfer({ energy: 10000n }, [ - { - tokenId: '', - to: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', - from: TEST_ACCOUNT, - tokenAmount: 100n, - }, - { - tokenId: '', - from: TEST_ACCOUNT, - to: { - address: { index: 4416n, subindex: 0n }, - hookName: 'onReceivingCIS2', + const { parameter, schema } = cis2.createTransfer( + { energy: Energy.create(10000) }, + [ + { + tokenId: '', + to: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), + from: TEST_ACCOUNT, + tokenAmount: 100n, }, - tokenAmount: 0n, - }, - ]); + { + tokenId: '', + from: TEST_ACCOUNT, + to: { + address: ContractAddress.create(4416), + hookName: + EntrypointName.fromStringUnchecked( + 'onReceivingCIS2' + ), + }, + tokenAmount: 0n, + }, + ] + ); const expectedParameterHex = '0200006400c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f0087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb490000000000c8d4bb7106a96bfa6f069438270bf9748049c24798b13b08f88fc2f46afb435f01401100000000000000000000000000000f006f6e526563656976696e67434953320000'; // Parameter is formatted and serialized as expected @@ -230,7 +259,7 @@ describe('createTransfer', () => { { token_id: '', amount: '100', - from: { Account: [TEST_ACCOUNT] }, + from: { Account: [AccountAddress.toBase58(TEST_ACCOUNT)] }, to: { Account: [ '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', @@ -241,7 +270,7 @@ describe('createTransfer', () => { { token_id: '', amount: '0', - from: { Account: [TEST_ACCOUNT] }, + from: { Account: [AccountAddress.toBase58(TEST_ACCOUNT)] }, to: { Contract: [{ index: 4416, subindex: 0 }, 'onReceivingCIS2'], }, @@ -252,7 +281,9 @@ describe('createTransfer', () => { parameter.json, Buffer.from(schema.value, 'base64') ); - expect(schemaSerialized.toString('hex')).toEqual(expectedParameterHex); + expect(Parameter.toHexString(schemaSerialized)).toEqual( + expectedParameterHex + ); }); }); @@ -262,11 +293,13 @@ test('dryRun.updateOperator', async () => { TEST_ACCOUNT, { type: 'add', - address: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', + address: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), }, TEST_BLOCK ); - expect(result.usedEnergy).toEqual(2735n); + expect(result.usedEnergy.value).toEqual(2735n); // Results in 1 transfer event expect( result.tag === 'success' && @@ -279,17 +312,19 @@ test('dryRun.updateOperator', async () => { [ { type: 'add', - address: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', + address: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), }, { type: 'remove', - address: { index: 3494n, subindex: 0n }, + address: ContractAddress.create(3494), }, ], TEST_BLOCK ); - expect(resultMulti.usedEnergy).toEqual(2960n); + expect(resultMulti.usedEnergy.value).toEqual(2960n); // Results in 2 transfer events expect( resultMulti.tag === 'success' && @@ -302,10 +337,12 @@ describe('createUpdateOperator', () => { test('single update', async () => { const cis2 = await getCIS2Single(); const { type, parameter, payload } = cis2.createUpdateOperator( - { energy: 1000000n }, + { energy: Energy.create(1000000) }, { type: 'add', - address: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', + address: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), } ); @@ -329,24 +366,33 @@ describe('createUpdateOperator', () => { // Checks that payload contains the expected values expect(payload.amount.microCcdAmount).toEqual(0n); - expect(payload.address).toEqual({ index: 3496n, subindex: 0n }); - expect(payload.message.toString('hex')).toEqual(expectedParameterHex); - expect(payload.receiveName).toEqual('cis2-bridgeable.updateOperator'); - expect(payload.maxContractExecutionEnergy).toEqual(1000000n); + expect(payload.address).toEqual(ContractAddress.create(3496)); + expect(Parameter.toHexString(payload.message)).toEqual( + expectedParameterHex + ); + expect(payload.receiveName.value).toEqual( + 'cis2-bridgeable.updateOperator' + ); + expect(payload.maxContractExecutionEnergy.value).toEqual(1000000n); }); test('multiple updates', async () => { const cis2 = await getCIS2Single(); - const { parameter } = cis2.createUpdateOperator({ energy: 1000000n }, [ - { - type: 'add', - address: '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB', - }, - { - type: 'remove', - address: { index: 3494n, subindex: 0n }, - }, - ]); + const { parameter } = cis2.createUpdateOperator( + { energy: Energy.create(1000000) }, + [ + { + type: 'add', + address: AccountAddress.fromBase58( + '3ybJ66spZ2xdWF3avgxQb2meouYa7mpvMWNPmUnczU8FoF8cGB' + ), + }, + { + type: 'remove', + address: ContractAddress.create(3494), + }, + ] + ); const expectedParameterHex = '0200010087e3bec61b8db2fb7389b57d2be4f7dd95d1088dfeb6ef7352c13d2b2d27bb490001a60d0000000000000000000000000000'; // Parameter is formatted and serialized as expected diff --git a/packages/nodejs/test/CIS4Contract.test.ts b/packages/sdk/test/client/CIS4Contract.test.ts similarity index 78% rename from packages/nodejs/test/CIS4Contract.test.ts rename to packages/sdk/test/client/CIS4Contract.test.ts index 930f3ca08..344175135 100644 --- a/packages/nodejs/test/CIS4Contract.test.ts +++ b/packages/sdk/test/client/CIS4Contract.test.ts @@ -1,14 +1,20 @@ -import { Buffer } from 'buffer/'; import { + ContractAddress, + AccountAddress, + Timestamp, + Energy, + BlockHash, + Parameter, CIS4, CIS4Contract, - ContractAddress, - serializeTypeValue, Web3IdSigner, -} from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from './testHelpers'; + serializeTypeValue, +} from '../../src/index.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; -const ISSUER_ACCOUNT = '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd'; +const ISSUER_ACCOUNT = AccountAddress.fromBase58( + '4UC8o4m8AgTxt5VBFMdLwMCwwJQVJwjesNzW7RPXkACynrULmd' +); const ISSUER_PUB_KEY = '23e7b282e69f39f962fa587eb033ca201e09d59c9740f18d5666b390fea9d486'; @@ -32,13 +38,11 @@ const NEW_REVOKER_2_KEYPAIR = { prv: 'cbfa761a29b8d11c5a0b421f402dfc498703d40762007876550beae7727c68c2', pub: 'b9372d7afffa99f7223c622aac78b5cb199c94f3b961feabd6f776d2d0a10b1c', }; -const WEB3ID_ADDRESS_REVOKE: ContractAddress = { - index: 5587n, - subindex: 0n, -}; +const WEB3ID_ADDRESS_REVOKE = ContractAddress.create(5587); -const TEST_BLOCK = - 'bf956ef81bb6a22eda754d490bdb7a3085318b3a1fe9370f83f86649a5f7cb60'; +const TEST_BLOCK = BlockHash.fromHexString( + 'bf956ef81bb6a22eda754d490bdb7a3085318b3a1fe9370f83f86649a5f7cb60' +); const getCIS4 = () => CIS4Contract.create(getNodeClient(), WEB3ID_ADDRESS_REVOKE); @@ -57,8 +61,12 @@ describe('credentialEntry', () => { credentialInfo: { holderPubKey: HOLDER_KEYPAIR.pub, holderRevocable: true, - validFrom: new Date('2023-08-01T13:47:02.260Z'), - validUntil: new Date('2025-08-01T13:47:02.260Z'), + validFrom: Timestamp.fromDate( + new Date('2023-08-01T13:47:02.260Z') + ), + validUntil: Timestamp.fromDate( + new Date('2025-08-01T13:47:02.260Z') + ), metadataUrl: { url: '' }, }, schemaRef: { url: 'http://foo-schema-url.com' }, @@ -130,7 +138,7 @@ describe('registerCredential', () => { const credential: CIS4.CredentialInfo = { holderPubKey: NEW_HOLDER_KEYPAIR.pub, holderRevocable: true, - validFrom: new Date('1/1/2023'), + validFrom: Timestamp.fromDate(new Date('1/1/2023')), metadataUrl: { url: 'http://issuer-metadata-url.com', }, @@ -151,39 +159,45 @@ describe('registerCredential', () => { const credential: CIS4.CredentialInfo = { holderPubKey: NEW_HOLDER_KEYPAIR.pub, holderRevocable: true, - validFrom: new Date('1/1/2023'), + validFrom: Timestamp.fromDate(new Date('1/1/2023')), metadataUrl: { url: 'http://issuer-metadata-url.com', }, }; - let tx = cis4.createRegisterCredential({ energy: 100000n }, credential); + let tx = cis4.createRegisterCredential( + { energy: Energy.create(100000) }, + credential + ); let schemaSerial = serializeTypeValue( tx.parameter.json, Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With `validUntil` + !`holderRevocable` - credential.validUntil = new Date( - new Date().setFullYear(new Date().getFullYear() + 1) + credential.validUntil = Timestamp.fromDate( + new Date(new Date().setFullYear(new Date().getFullYear() + 1)) ); credential.holderRevocable = false; - tx = cis4.createRegisterCredential({ energy: 100000n }, credential); + tx = cis4.createRegisterCredential( + { energy: Energy.create(100000) }, + credential + ); schemaSerial = serializeTypeValue( tx.parameter.json, Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With data const auxData = Buffer.from('Hello world!').toString('hex'); tx = cis4.createRegisterCredential( - { energy: 100000n }, + { energy: Energy.create(100000) }, credential, auxData ); @@ -192,7 +206,7 @@ describe('registerCredential', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); }); }); @@ -202,6 +216,7 @@ describe('registerRevocationKeys', () => { const res = await cis4.dryRun.registerRevocationKeys( ISSUER_ACCOUNT, NEW_REVOKER_1_KEYPAIR.pub, + undefined, TEST_BLOCK ); expect(res.tag).toBe('success'); @@ -212,7 +227,7 @@ describe('registerRevocationKeys', () => { // Single key let tx = cis4.createRegisterRevocationKeys( - { energy: 100000n }, + { energy: Energy.create(100000) }, NEW_REVOKER_1_KEYPAIR.pub ); let schemaSerial = serializeTypeValue( @@ -220,23 +235,23 @@ describe('registerRevocationKeys', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // Multilple keys - tx = cis4.createRegisterRevocationKeys({ energy: 100000n }, [ - NEW_REVOKER_1_KEYPAIR.pub, - NEW_REVOKER_2_KEYPAIR.pub, - ]); + tx = cis4.createRegisterRevocationKeys( + { energy: Energy.create(100000) }, + [NEW_REVOKER_1_KEYPAIR.pub, NEW_REVOKER_2_KEYPAIR.pub] + ); schemaSerial = serializeTypeValue( tx.parameter.json, Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With data tx = cis4.createRegisterRevocationKeys( - { energy: 100000n }, + { energy: Energy.create(100000) }, [NEW_REVOKER_1_KEYPAIR.pub, NEW_REVOKER_2_KEYPAIR.pub], Buffer.from('Test').toString('hex') ); @@ -245,7 +260,7 @@ describe('registerRevocationKeys', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); }); }); @@ -255,6 +270,7 @@ describe('removeRevocationKeys', () => { const res = await cis4.dryRun.removeRevocationKeys( ISSUER_ACCOUNT, REVOKER_KEYPAIR.pub, + undefined, TEST_BLOCK ); expect(res.tag).toBe('success'); @@ -264,7 +280,7 @@ describe('removeRevocationKeys', () => { const cis4 = await getCIS4(); let tx = cis4.createRemoveRevocationKeys( - { energy: 100000n }, + { energy: Energy.create(100000) }, REVOKER_KEYPAIR.pub ); let schemaSerial = serializeTypeValue( @@ -272,23 +288,23 @@ describe('removeRevocationKeys', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // Multiple keys - tx = cis4.createRemoveRevocationKeys({ energy: 100000n }, [ - REVOKER_KEYPAIR.pub, - NEW_REVOKER_1_KEYPAIR.pub, - ]); + tx = cis4.createRemoveRevocationKeys( + { energy: Energy.create(100000) }, + [REVOKER_KEYPAIR.pub, NEW_REVOKER_1_KEYPAIR.pub] + ); schemaSerial = serializeTypeValue( tx.parameter.json, Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With data tx = cis4.createRemoveRevocationKeys( - { energy: 100000n }, + { energy: Energy.create(100000) }, [REVOKER_KEYPAIR.pub, NEW_REVOKER_1_KEYPAIR.pub], Buffer.from('Test').toString('hex') ); @@ -297,7 +313,7 @@ describe('removeRevocationKeys', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); }); }); @@ -318,7 +334,7 @@ describe('revokeCredentialAsIssuer', () => { const cis4 = await getCIS4(); let tx = cis4.createRevokeCredentialAsIssuer( - { energy: 100000n }, + { energy: Energy.create(100000) }, HOLDER_KEYPAIR.pub, undefined, undefined @@ -328,11 +344,11 @@ describe('revokeCredentialAsIssuer', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With reason tx = cis4.createRevokeCredentialAsIssuer( - { energy: 100000n }, + { energy: Energy.create(100000) }, HOLDER_KEYPAIR.pub, 'Because test...', undefined @@ -342,11 +358,11 @@ describe('revokeCredentialAsIssuer', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With data tx = cis4.createRevokeCredentialAsIssuer( - { energy: 100000n }, + { energy: Energy.create(100000) }, HOLDER_KEYPAIR.pub, undefined, Buffer.from('Is anyone watching?').toString('hex') @@ -356,7 +372,7 @@ describe('revokeCredentialAsIssuer', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); }); }); @@ -380,7 +396,7 @@ describe('revokeCredentialAsHolder', () => { const cis4 = await getCIS4(); let tx = await cis4.createRevokeCredentialAsHolder( - { energy: 100000n }, + { energy: Energy.create(100000) }, signer, 0n, in5Minutes(), @@ -391,11 +407,11 @@ describe('revokeCredentialAsHolder', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With reason tx = await cis4.createRevokeCredentialAsHolder( - { energy: 100000n }, + { energy: Energy.create(100000) }, signer, 0n, in5Minutes(), @@ -406,7 +422,7 @@ describe('revokeCredentialAsHolder', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); }); }); @@ -431,7 +447,7 @@ describe('revokeCredentialAsOther', () => { const cis4 = await getCIS4(); let tx = await cis4.createRevokeCredentialAsOther( - { energy: 100000n }, + { energy: Energy.create(100000) }, signer, HOLDER_KEYPAIR.pub, 0n, @@ -443,11 +459,11 @@ describe('revokeCredentialAsOther', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); // With reason tx = await cis4.createRevokeCredentialAsOther( - { energy: 100000n }, + { energy: Energy.create(100000) }, signer, HOLDER_KEYPAIR.pub, 0n, @@ -459,6 +475,6 @@ describe('revokeCredentialAsOther', () => { Buffer.from(tx.schema.value, 'base64'), true ); - expect(tx.parameter.hex).toEqual(schemaSerial.toString('hex')); + expect(tx.parameter.hex).toEqual(Parameter.toHexString(schemaSerial)); }); }); diff --git a/packages/nodejs/test/cis0.test.ts b/packages/sdk/test/client/cis0.test.ts similarity index 73% rename from packages/nodejs/test/cis0.test.ts rename to packages/sdk/test/client/cis0.test.ts index 62df41afa..4e6ce631b 100644 --- a/packages/nodejs/test/cis0.test.ts +++ b/packages/sdk/test/client/cis0.test.ts @@ -1,19 +1,19 @@ -import { CIS0, cis0Supports } from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from './testHelpers'; +import { CIS0, ContractAddress, cis0Supports } from '../../src/index.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; const client = getNodeClient(); test('cis0Supports', async () => { const result = await cis0Supports( client, - { index: 3496n, subindex: 0n }, + ContractAddress.create(3496), 'CIS-0' ); expect(result?.type).toEqual(CIS0.SupportType.Support); const resultMulti = await cis0Supports( client, - { index: 3496n, subindex: 0n }, + ContractAddress.create(3496), ['CIS-0', 'CIS-2'] ); @@ -23,14 +23,14 @@ test('cis0Supports', async () => { const resultCIS1 = await cis0Supports( client, - { index: 3496n, subindex: 0n }, + ContractAddress.create(3496), 'CIS-1' ); expect(resultCIS1?.type).toEqual(CIS0.SupportType.NoSupport); const resultArb = await cis0Supports( client, - { index: 3496n, subindex: 0n }, + ContractAddress.create(3496), 'NON-STANDARD-ID-123' ); expect(resultArb?.type).toEqual(CIS0.SupportType.NoSupport); @@ -39,7 +39,7 @@ test('cis0Supports', async () => { test('cis0Supports throws on non cis-0', async () => { const result = await cis0Supports( client, - { index: 3494n, subindex: 0n }, + ContractAddress.create(3494), 'CIS-0' ); expect(result).toBe(undefined); diff --git a/packages/nodejs/test/clientV2.test.ts b/packages/sdk/test/client/clientV2.test.ts similarity index 80% rename from packages/nodejs/test/clientV2.test.ts rename to packages/sdk/test/client/clientV2.test.ts index 500352778..4310ebc63 100644 --- a/packages/nodejs/test/clientV2.test.ts +++ b/packages/sdk/test/client/clientV2.test.ts @@ -1,11 +1,6 @@ -import * as v1 from '@concordium/common-sdk'; -import * as v2 from '../../common/grpc/v2/concordium/types'; -import { testnetBulletproofGenerators } from './resources/bulletproofgenerators'; -import { - ConcordiumGRPCClient, - getAccountIdentifierInput, - getBlockHashInput, -} from '@concordium/common-sdk/lib/GRPCClient'; +import * as v1 from '../../src/index.js'; +import * as v2 from '../../src/grpc-api/v2/concordium/types.js'; +import { testnetBulletproofGenerators } from './resources/bulletproofgenerators.js'; import { buildBasicAccountSigner, calculateEnergyCost, @@ -14,23 +9,21 @@ import { sha256, signTransaction, serializeAccountTransactionPayload, - streamToList, - deserializeReceiveReturnValue, createCredentialDeploymentTransaction, -} from '@concordium/common-sdk'; + serializeAccountTransaction, + streamToList, + BlockHash, +} from '../../src/index.js'; import { - getModuleBuffer, getIdentityInput, getNodeClientV2, getNodeClientWeb, -} from './testHelpers'; -import * as ed from '@noble/ed25519'; -import * as expected from './resources/expectedJsons'; -import { Buffer } from 'buffer/'; - -import { serializeAccountTransaction } from '@concordium/common-sdk/lib/serialization'; +} from './testHelpers.js'; +import * as ed from '#ed25519'; +import * as expected from './resources/expectedJsons.js'; import { TextEncoder, TextDecoder } from 'util'; +import { getModuleBuffer } from '../../src/nodejs/index.js'; /* eslint-disable @typescript-eslint/no-explicit-any */ global.TextEncoder = TextEncoder as any; @@ -40,29 +33,30 @@ global.TextDecoder = TextDecoder as any; const clientV2 = getNodeClientV2(); const clientWeb = getNodeClientWeb(); -const testAccount = new v1.AccountAddress( +const testAccount = v1.AccountAddress.fromBase58( '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' ); -const testCredId = new v1.CredentialRegistrationId( +const testCredId = v1.CredentialRegistrationId.fromHexString( 'aa730045bcd20bb5c24349db29d949f767e72f7cce459dc163c4b93c780a7d7f65801dda8ff7e4fc06fdf1a1b246276f' ); -const testAccBaker = new v1.AccountAddress( +const testAccBaker = v1.AccountAddress.fromBase58( '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L' ); -const testAccDeleg = new v1.AccountAddress( +const testAccDeleg = v1.AccountAddress.fromBase58( '3bFo43GiPnkk5MmaSdsRVboaX2DNSKaRkLseQbyB3WPW1osPwh' ); -const testBlockHash = - 'fe88ff35454079c3df11d8ae13d5777babd61f28be58494efe51b6593e30716e'; +const testBlockHash = v1.BlockHash.fromHexString( + 'fe88ff35454079c3df11d8ae13d5777babd61f28be58494efe51b6593e30716e' +); // Retrieves the account info for the given account in the GRPCv2 type format. function getAccountInfoV2( - client: ConcordiumGRPCClient, + client: v1.ConcordiumGRPCClient, accountIdentifier: v1.AccountIdentifierInput ): Promise { const accountInfoRequest = { - blockHash: getBlockHashInput(testBlockHash), - accountIdentifier: getAccountIdentifierInput(accountIdentifier), + blockHash: v1.getBlockHashInput(testBlockHash), + accountIdentifier: v1.getAccountIdentifierInput(accountIdentifier), }; return client.client.getAccountInfo(accountInfoRequest).response; @@ -89,7 +83,7 @@ test.each([clientV2, clientWeb])( test.each([clientV2, clientWeb])('nextAccountNonce', async (client) => { const nan = await client.getNextAccountNonce(testAccount); - expect(nan.nonce).toBeGreaterThanOrEqual(19n); + expect(nan.nonce.value).toBeGreaterThanOrEqual(19n); expect(nan.allFinal).toBeDefined(); }); @@ -99,21 +93,6 @@ test.each([clientV2, clientWeb])('getAccountInfo', async (client) => { expect(v2.AccountInfo.toJson(accountInfo)).toEqual(expected.accountInfo); }); -test.each([clientV2, clientWeb])( - 'getAccountInfo: Invalid hash throws error', - async (client) => { - const invalidBlockHash = '1010101010'; - await expect( - client.getAccountInfo(testAccount, invalidBlockHash) - ).rejects.toEqual( - new Error( - 'The input was not a valid hash, must be 32 bytes: ' + - invalidBlockHash - ) - ); - } -); - test.each([clientV2, clientWeb])('getAccountInfo for baker', async (client) => { const accInfo = await getAccountInfoV2(client, testAccBaker); const accountIndexInfo = await getAccountInfoV2(client, 5n); @@ -159,13 +138,13 @@ test.each([clientV2, clientWeb])( ); test.each([clientV2, clientWeb])( + // TODO: fails.. 'accountInfo implementations is the same', async (client) => { const regular = await client.getAccountInfo(testAccount, testBlockHash); const credId = await client.getAccountInfo(testCredId, testBlockHash); const baker = await client.getAccountInfo(testAccBaker, testBlockHash); const deleg = await client.getAccountInfo(testAccDeleg, testBlockHash); - expect(regular).toEqual(expected.regularAccountInfo); expect(credId).toEqual(expected.credIdAccountInfo); expect(baker).toEqual(expected.bakerAccountInfo); @@ -187,8 +166,9 @@ test.each([clientV2, clientWeb])( test.each([clientV2, clientWeb])( 'getChainParameters corresponds to GetBlockSummary subset on protocol level < 4', async (client) => { - const oldBlockHash = - 'ed2507c4d05108038741e87757ab1c3acdeeb3327027cd2972666807c9c4a20d'; + const oldBlockHash = v1.BlockHash.fromHexString( + 'ed2507c4d05108038741e87757ab1c3acdeeb3327027cd2972666807c9c4a20d' + ); const oldChainParameters = await client.getBlockChainParameters( oldBlockHash ); @@ -210,7 +190,6 @@ test.each([clientV2, clientWeb])( 'getPassiveDelegationInfo corresponds to getPoolStatus with no bakerId', async (client) => { const status = await client.getPassiveDelegationInfo(testBlockHash); - expect(status).toEqual(expected.passiveDelegationStatus); } ); @@ -218,8 +197,9 @@ test.each([clientV2, clientWeb])( test.each([clientV2, clientWeb])( 'getPoolInfo corresponds to getPoolStatus with bakerId (with pending change)', async (client) => { - const changeHash = - '2aa7c4a54ad403a9f9b48de2469e5f13a64c95f2cf7a8e72c0f9f7ae0718f642'; + const changeHash = v1.BlockHash.fromHexString( + '2aa7c4a54ad403a9f9b48de2469e5f13a64c95f2cf7a8e72c0f9f7ae0718f642' + ); const changedAccount = 1879n; const poolStatus = await client.getPoolInfo(changedAccount, changeHash); @@ -231,8 +211,9 @@ test.each([clientV2, clientWeb])( test.each([clientV2, clientWeb])( 'getBlockItemStatus on chain update', async (client) => { - const transactionHash = - '3de823b876d05cdd33a311a0f84124079f5f677afb2534c4943f830593edc650'; + const transactionHash = v1.TransactionHash.fromHexString( + '3de823b876d05cdd33a311a0f84124079f5f677afb2534c4943f830593edc650' + ); const blockItemStatus = await client.getBlockItemStatus( transactionHash ); @@ -244,8 +225,9 @@ test.each([clientV2, clientWeb])( test.each([clientV2, clientWeb])( 'getBlockItemStatus on simple transfer', async (client) => { - const transactionHash = - '502332239efc0407eebef5c73c390080e5d7e1b127ff29f786a62b3c9ab6cfe7'; + const transactionHash = v1.TransactionHash.fromHexString( + '502332239efc0407eebef5c73c390080e5d7e1b127ff29f786a62b3c9ab6cfe7' + ); const blockItemStatus = await client.getBlockItemStatus( transactionHash ); @@ -255,10 +237,7 @@ test.each([clientV2, clientWeb])( ); test.each([clientV2, clientWeb])('getInstanceInfo', async (client) => { - const contractAddress = { - index: 0n, - subindex: 0n, - }; + const contractAddress = v1.ContractAddress.create(0, 0); const instanceInfo = await client.getInstanceInfo( contractAddress, testBlockHash @@ -271,14 +250,11 @@ test.each([clientV2, clientWeb])('Failed invoke contract', async (client) => { const result = await client.invokeContract( { invoker: testAccount, - contract: { - index: 6n, - subindex: 0n, - }, - method: 'PiggyBank.smash', - amount: new v1.CcdAmount(0n), + contract: v1.ContractAddress.create(6), + method: v1.ReceiveName.fromStringUnchecked('PiggyBank.smash'), + amount: v1.CcdAmount.zero(), parameter: undefined, - energy: 30000n, + energy: v1.Energy.create(30000), }, testBlockHash ); @@ -287,7 +263,7 @@ test.each([clientV2, clientWeb])('Failed invoke contract', async (client) => { throw new Error('Expected invoke to be fail'); } - expect(result.usedEnergy).toBe(340n); + expect(result.usedEnergy.value).toBe(340n); expect(result.reason.tag).toBe(v1.RejectReasonTag.RejectedReceive); }); @@ -297,14 +273,11 @@ test.each([clientV2, clientWeb])( const result = await client.invokeContract( { invoker: testAccount, - contract: { - index: 6n, - subindex: 0n, - }, - method: 'PiggyBank.insert', - amount: new v1.CcdAmount(1n), + contract: v1.ContractAddress.create(6), + method: v1.ReceiveName.fromStringUnchecked('PiggyBank.insert'), + amount: v1.CcdAmount.fromMicroCcd(1n), parameter: undefined, - energy: 30000n, + energy: v1.Energy.create(30000), }, testBlockHash ); @@ -318,14 +291,11 @@ test.each([clientV2, clientWeb])( async (client) => { const context = { invoker: testAccount, - contract: { - index: 81n, - subindex: 0n, - }, - method: 'PiggyBank.view', - amount: new v1.CcdAmount(0n), + contract: v1.ContractAddress.create(81), + method: v1.ReceiveName.fromStringUnchecked('PiggyBank.view'), + amount: v1.CcdAmount.zero(), parameter: undefined, - energy: 30000n, + energy: v1.Energy.create(30000), }; const result = await client.invokeContract(context, testBlockHash); @@ -334,12 +304,11 @@ test.each([clientV2, clientWeb])( ); test.each([clientV2, clientWeb])('getModuleSource', async (client) => { - const localModuleBytes = getModuleBuffer('test/resources/piggy_bank.wasm'); - const moduleRef = new v1.ModuleReference( - Buffer.from( - 'foOYrcQGqX202GnD/XrcgToxg2Z6On2weOuub33OX2Q=', - 'base64' - ).toString('hex') + const localModuleBytes = getModuleBuffer( + 'test/client/resources/piggy_bank.wasm' + ); + const moduleRef = v1.ModuleReference.fromBuffer( + Buffer.from('foOYrcQGqX202GnD/XrcgToxg2Z6On2weOuub33OX2Q=', 'base64') ); const localModuleHex = Buffer.from(localModuleBytes); @@ -349,12 +318,15 @@ test.each([clientV2, clientWeb])('getModuleSource', async (client) => { ); expect(versionedModuleSource.version).toEqual(0); - expect(localModuleHex).toEqual(versionedModuleSource.source); + expect(new Uint8Array(localModuleHex)).toEqual( + new Uint8Array(versionedModuleSource.source) + ); }); test.each([clientV2, clientWeb])('getConsensusStatus', async (client) => { - const genesisBlock = - '4221332d34e1694168c2a0c0b3fd0f273809612cb13d000d5c2e00e85f50f796'; + const genesisBlock = v1.BlockHash.fromHexString( + '4221332d34e1694168c2a0c0b3fd0f273809612cb13d000d5c2e00e85f50f796' + ); const ci = await client.getConsensusStatus(); @@ -364,7 +336,7 @@ test.each([clientV2, clientWeb])('getConsensusStatus', async (client) => { }); test.each([clientV2, clientWeb])('sendBlockItem', async (client) => { - const senderAccount = new v1.AccountAddress( + const senderAccount = v1.AccountAddress.fromBase58( '37TRfx9PqFX386rFcNThyA3zdoWsjF8Koy6Nh3i8VrPy4duEsA' ); const privateKey = @@ -373,12 +345,12 @@ test.each([clientV2, clientWeb])('sendBlockItem', async (client) => { // Create local transaction const header: v1.AccountTransactionHeader = { - expiry: new v1.TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: v1.TransactionExpiry.futureMinutes(60), nonce: nextNonce.nonce, sender: senderAccount, }; const simpleTransfer: v1.SimpleTransferPayload = { - amount: new v1.CcdAmount(10000000000n), + amount: v1.CcdAmount.fromCcd(10_000), toAddress: testAccount, }; const accountTransaction: v1.AccountTransaction = { @@ -400,7 +372,7 @@ test.each([clientV2, clientWeb])('sendBlockItem', async (client) => { }); test.each([clientV2, clientWeb])('transactionHash', async (client) => { - const senderAccount = new v1.AccountAddress( + const senderAccount = v1.AccountAddress.fromBase58( '37TRfx9PqFX386rFcNThyA3zdoWsjF8Koy6Nh3i8VrPy4duEsA' ); const privateKey = @@ -409,12 +381,12 @@ test.each([clientV2, clientWeb])('transactionHash', async (client) => { // Create local transaction const headerLocal: v1.AccountTransactionHeader = { - expiry: new v1.TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: v1.TransactionExpiry.futureMinutes(60), nonce: nextNonce.nonce, sender: senderAccount, }; const simpleTransfer: v1.SimpleTransferPayload = { - amount: new v1.CcdAmount(10000000000n), + amount: v1.CcdAmount.fromCcd(10_000), toAddress: testAccount, }; const transaction: v1.AccountTransaction = { @@ -447,9 +419,9 @@ test.each([clientV2, clientWeb])('transactionHash', async (client) => { // Put together sendBlockItemRequest const header: v2.AccountTransactionHeader = { - sender: { value: transaction.header.sender.decodedAddress }, - sequenceNumber: { value: transaction.header.nonce }, - energyAmount: { value: energyCost }, + sender: v1.AccountAddress.toProto(transaction.header.sender), + sequenceNumber: v1.SequenceNumber.toProto(transaction.header.nonce), + energyAmount: v1.Energy.toProto(energyCost), expiry: { value: transaction.header.expiry.expiryEpochSeconds }, }; const accountTransaction: v2.PreAccountTransaction = { @@ -494,7 +466,7 @@ test.each([clientV2, clientWeb])('createAccount', async (client) => { const identityInput: v1.IdentityInput = getIdentityInput(); const threshold = 1; const credentialIndex = 1; - const expiry = new v1.TransactionExpiry(new Date(Date.now() + 3600000)); + const expiry = v1.TransactionExpiry.futureMinutes(60); const revealedAttributes: v1.AttributeKey[] = []; const publicKeys: v1.VerifyKey[] = [ { @@ -522,15 +494,16 @@ test.each([clientV2, clientWeb])('createAccount', async (client) => { const signingKey1 = '1053de23867e0f92a48814aabff834e2ca0b518497abaef71cad4e1be506334a'; const signature = Buffer.from( - await ed.sign(hashToSign, signingKey1) + await ed.signAsync(hashToSign, signingKey1) ).toString('hex'); const signatures: string[] = [signature]; + const payload = v1.serializeCredentialDeploymentPayload( + signatures, + credentialDeploymentTransaction + ); expect( - client.sendCredentialDeploymentTransaction( - credentialDeploymentTransaction, - signatures - ) + client.sendCredentialDeploymentTransaction(payload, expiry) ).rejects.toThrow('expired'); }); @@ -555,10 +528,7 @@ test.each([clientV2, clientWeb])('getAncestors', async (client) => { }); test.each([clientV2, clientWeb])('getInstanceState', async (client) => { - const contract = { - index: 602n, - subindex: 0n, - }; + const contract = v1.ContractAddress.create(602); const instanceStateIter = client.getInstanceState(contract, testBlockHash); const instanceStateList = await streamToList(instanceStateIter); @@ -568,10 +538,7 @@ test.each([clientV2, clientWeb])('getInstanceState', async (client) => { test.each([clientV2, clientWeb])('instanceStateLookup', async (client) => { const key = '0000000000000000'; const expectedValue = '0800000000000000'; - const contract = { - index: 601n, - subindex: 0n, - }; + const contract = v1.ContractAddress.create(601); const value = await client.instanceStateLookup( contract, key, @@ -612,8 +579,9 @@ test.each([clientV2, clientWeb])( height: 100n, restrict: true, }; - const expectedBlock = - '956c3bc5c9d10449e13686a4cc69e8bc7dee450608866242075a6ce37331187c'; + const expectedBlock = v1.BlockHash.fromHexString( + '956c3bc5c9d10449e13686a4cc69e8bc7dee450608866242075a6ce37331187c' + ); const blocks = await client.getBlocksAtHeight(request); expect(blocks[0]).toEqual(expectedBlock); @@ -624,16 +592,22 @@ test.each([clientV2, clientWeb])('getBlockInfo', async (client) => { const blockInfo = await client.getBlockInfo(testBlockHash); expect(blockInfo.blockParent).toEqual( - '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' + BlockHash.fromHexString( + '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' + ) ); expect(blockInfo.blockHash).toEqual( - 'fe88ff35454079c3df11d8ae13d5777babd61f28be58494efe51b6593e30716e' + BlockHash.fromHexString( + 'fe88ff35454079c3df11d8ae13d5777babd61f28be58494efe51b6593e30716e' + ) ); expect(blockInfo.blockStateHash).toEqual( '6e602157d76677fc4b630b2701571d2b0166e2b08e0afe8ab92356e4d0b88a6a' ); expect(blockInfo.blockLastFinalized).toEqual( - '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' + BlockHash.fromHexString( + '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' + ) ); expect(blockInfo.blockHeight).toEqual(1259179n); expect(blockInfo.blockBaker).toEqual(4n); @@ -645,7 +619,7 @@ test.each([clientV2, clientWeb])('getBlockInfo', async (client) => { expect(blockInfo.finalized).toEqual(true); expect(blockInfo.transactionCount).toEqual(0n); expect(blockInfo.transactionsSize).toEqual(0n); - expect(blockInfo.transactionEnergyCost).toEqual(0n); + expect(blockInfo.transactionEnergyCost).toEqual(v1.Energy.create(0)); expect(blockInfo.genesisIndex).toEqual(1); expect(blockInfo.eraBlockHeight).toEqual(1258806); expect(blockInfo.protocolVersion).toEqual(4n); @@ -735,8 +709,9 @@ test.each([clientV2, clientWeb])( test.each([clientV2, clientWeb])( 'getBlockTransactionEvents', async (client) => { - const blockHash = - '8f3acabb19ef769db4d13ada858a305cc1a3d64adeb78fcbf3bb9f7583de6362'; + const blockHash = v1.BlockHash.fromHexString( + '8f3acabb19ef769db4d13ada858a305cc1a3d64adeb78fcbf3bb9f7583de6362' + ); const transactionEvents = client.getBlockTransactionEvents(blockHash); const transactionEventList = await streamToList(transactionEvents); @@ -747,8 +722,9 @@ test.each([clientV2, clientWeb])( test.each([clientV2, clientWeb])( 'getBlockTransactionEvents', async (client) => { - const blockHash = - '8f3acabb19ef769db4d13ada858a305cc1a3d64adeb78fcbf3bb9f7583de6362'; + const blockHash = v1.BlockHash.fromHexString( + '8f3acabb19ef769db4d13ada858a305cc1a3d64adeb78fcbf3bb9f7583de6362' + ); const transactionEvents = client.getBlockTransactionEvents(blockHash); const transactionEventList = await streamToList(transactionEvents); @@ -775,13 +751,18 @@ test.each([clientV2, clientWeb])('getBlockSpecialEvents', async (client) => { }); test.each([clientV2, clientWeb])('getBlockPendingUpdates', async (client) => { - const pendingUpdateBlock = - '39122a9c720cae643b999d93dd7bf09bcf50e99bb716767dd35c39690390db54'; + const pendingUpdateBlock = v1.BlockHash.fromHexString( + '39122a9c720cae643b999d93dd7bf09bcf50e99bb716767dd35c39690390db54' + ); const pendingUpdateStream = client.getBlockPendingUpdates(pendingUpdateBlock); const pendingUpdateList = await streamToList(pendingUpdateStream); - expect(pendingUpdateList).toEqual(expected.pendingUpdateList); + expect(pendingUpdateList.length).toEqual(1); + expect(pendingUpdateList[0].effectiveTime.value).toEqual( + expected.pendingUpdate.effectiveTime.value + ); + expect(pendingUpdateList[0].effect).toEqual(expected.pendingUpdate.effect); }); test.each([clientV2, clientWeb])( @@ -796,23 +777,26 @@ test.each([clientV2, clientWeb])( ); test.each([clientV2, clientWeb])('getEmbeddedSchema', async (client) => { - const contract = { index: 4422n, subindex: 0n }; - const moduleRef = new v1.ModuleReference( + const contract = v1.ContractAddress.create(4422); + const moduleRef = v1.ModuleReference.fromHexString( '44434352ddba724930d6b1b09cd58bd1fba6ad9714cf519566d5fe72d80da0d1' ); const schema = await client.getEmbeddedSchema(moduleRef); - const context = { contract, method: 'weather.get' }; + const context = { + contract, + method: v1.ReceiveName.fromStringUnchecked('weather.get'), + }; const invoked = await client.invokeContract(context); if (invoked.tag === 'success' && invoked.returnValue) { - const rawReturnValue = Buffer.from(invoked.returnValue, 'hex'); - const returnValue = deserializeReceiveReturnValue( - rawReturnValue, + const rawReturnValue = invoked.returnValue; + const returnValue = v1.deserializeReceiveReturnValue( + v1.ReturnValue.toBuffer(rawReturnValue), schema, - 'weather', - 'get' + v1.ContractName.fromStringUnchecked('weather'), + v1.EntrypointName.fromStringUnchecked('get') ); expect(returnValue).toEqual({ Sunny: [] }); } else { @@ -821,7 +805,7 @@ test.each([clientV2, clientWeb])('getEmbeddedSchema', async (client) => { }); // For tests that take a long time to run, is skipped by default -describe.skip('Long run-time test suite', () => { +describe('Long run-time test suite', () => { const longTestTime = 45000; // Sometimes fails as there is no guarantee that a new block comes fast enough. @@ -861,15 +845,21 @@ test.each([clientV2, clientWeb])('getFinalizedBlocksFrom', async (client) => { const expectedValues = [ { height: 123n, - hash: 'd2f69ff78b898c4eb0863bcbc179764b3ed20ed142e93eb3ed0cfc730c77f4ca', + hash: v1.BlockHash.fromHexString( + 'd2f69ff78b898c4eb0863bcbc179764b3ed20ed142e93eb3ed0cfc730c77f4ca' + ), }, { height: 124n, - hash: 'fc86847a2482d5eb36028fe4a4702d1cd52d6d6f953d5effe4855acc974dfc64', + hash: v1.BlockHash.fromHexString( + 'fc86847a2482d5eb36028fe4a4702d1cd52d6d6f953d5effe4855acc974dfc64' + ), }, { height: 125n, - hash: 'bc5e6aadad1bd5d107a8a02e7df5532f6c758ec456f709cfba1f402c408e7256', + hash: v1.BlockHash.fromHexString( + 'bc5e6aadad1bd5d107a8a02e7df5532f6c758ec456f709cfba1f402c408e7256' + ), }, ]; @@ -878,7 +868,7 @@ test.each([clientV2, clientWeb])('getFinalizedBlocksFrom', async (client) => { expect(bis.length).toBe(3); bis.forEach((bi, i) => { expect(bi.height).toBe(expectedValues[i].height); - expect(bi.hash).toBe(expectedValues[i].hash); + expect(bi.hash).toEqual(expectedValues[i].hash); }); }); @@ -899,7 +889,10 @@ describe('findEarliestFinalized', () => { if (accounts.length > genesisAccounts.length) { return accounts.filter( - (a) => !genesisAccounts.includes(a) + (a) => + !genesisAccounts.some( + v1.AccountAddress.equals.bind(undefined, a) + ) )[0]; } }, @@ -907,23 +900,36 @@ describe('findEarliestFinalized', () => { 10000n ); - expect(firstAccount).toBe( - '3sPayiQEQHrJUpwYUAnYCLWUTkk3JvEW5x6Vn6mD4raBgPAuSp' - ); + if (firstAccount === undefined) { + throw new Error('Expected firstAccount to be defined'); + } + expect( + v1.AccountAddress.equals( + firstAccount, + v1.AccountAddress.fromBase58( + '3sPayiQEQHrJUpwYUAnYCLWUTkk3JvEW5x6Vn6mD4raBgPAuSp' + ) + ) + ).toBeTruthy(); } ); test.each([clientV2, clientWeb])( 'Works on single block range', async (client) => { - const firstAccount = await client.findEarliestFinalized( + const blockHash = await client.findEarliestFinalized( async (bi) => bi.hash, 10000n, 10000n ); + if (blockHash === undefined) { + throw new Error('Expected blockHash to be defined'); + } - expect(firstAccount).toBe( - 'e4f7f5512e55183f56efe31c1a9da6e5c7f93f24d5b746180e3b5076e54811c1' + expect(blockHash).toEqual( + BlockHash.fromHexString( + 'e4f7f5512e55183f56efe31c1a9da6e5c7f93f24d5b746180e3b5076e54811c1' + ) ); } ); @@ -931,7 +937,7 @@ describe('findEarliestFinalized', () => { test.each([clientV2, clientWeb])('findInstanceCreation', async (client) => { const blockFirstContract = await client.findInstanceCreation( - { index: 0n, subindex: 0n }, + v1.ContractAddress.create(0), 0n, 10000n ); @@ -975,7 +981,7 @@ test.each([clientV2, clientWeb])('getBakerEarliestWinTime', async (client) => { // Arbitrary eraliestWinTime measured at the time of running the test. // Every earliestWinTime measured after this point should be greater than this. - expect(earliestWinTime).toBeGreaterThan(1692792026500n); + expect(earliestWinTime.value).toBeGreaterThan(1692792026500n); }); test.each([clientV2, clientWeb])( @@ -984,7 +990,7 @@ test.each([clientV2, clientWeb])( const blockWithTimeoutCert = 'ac94ab7628d44fd8b4edb3075ae156e4b85d4007f52f147df6936ff70083d1ef'; const blockCertificates = await client.getBlockCertificates( - blockWithTimeoutCert + BlockHash.fromHexString(blockWithTimeoutCert) ); expect(blockCertificates.timeoutCertificate).toEqual( @@ -999,7 +1005,7 @@ test.each([clientV2, clientWeb])( const blockWithEpochFinalizationEntry = '1ba4bcd28a6e014204f79a81a47bac7518066410acbeb7853f20b55e335b947a'; const blockCertificates = await client.getBlockCertificates( - blockWithEpochFinalizationEntry + BlockHash.fromHexString(blockWithEpochFinalizationEntry) ); expect(blockCertificates.quorumCertificate).toEqual( @@ -1027,8 +1033,9 @@ test.each([clientV2, clientWeb])('getBakersRewardPeriod', async (client) => { 'number' ); expect(typeof brpi.commissionRates.transactionCommission).toEqual('number'); - expect(typeof brpi.effectiveStake).toEqual('bigint'); - expect(typeof brpi.equityCapital).toEqual('bigint'); + expect(v1.CcdAmount.instanceOf(brpi.effectiveStake)).toBeTruthy(); + expect(v1.CcdAmount.instanceOf(brpi.equityCapital)).toBeTruthy(); + expect(v1.CcdAmount.instanceOf(brpi.delegatedCapital)).toBeTruthy(); expect(typeof brpi.isFinalizer).toEqual('boolean'); }); @@ -1038,7 +1045,9 @@ test.each([clientV2, clientWeb])( const firstBlockEpoch = await client.getFirstBlockEpoch(testBlockHash); expect(firstBlockEpoch).toEqual( - '1ffd2823aa0dff331cc1ec98cf8269cf22120b94e2087c107874c7e84190317b' + BlockHash.fromHexString( + '1ffd2823aa0dff331cc1ec98cf8269cf22120b94e2087c107874c7e84190317b' + ) ); } ); @@ -1053,7 +1062,9 @@ test.each([clientV2, clientWeb])( const firstBlockEpoch = await client.getFirstBlockEpoch(req); expect(firstBlockEpoch).toEqual( - 'ea2a11db1d20658e9dc91f70116fe3f83a5fc49ac318d8ed1848295ae93c66fa' + BlockHash.fromHexString( + 'ea2a11db1d20658e9dc91f70116fe3f83a5fc49ac318d8ed1848295ae93c66fa' + ) ); } ); @@ -1062,7 +1073,7 @@ test.each([clientV2, clientWeb])('getWinningBakersEpoch', async (client) => { const blockHash = 'ae4a8e864bb71dc2b6043a31c429be4fc4a110955143753ab3963c6a829c8818'; const winningBakers = await streamToList( - client.getWinningBakersEpoch(blockHash) + client.getWinningBakersEpoch(BlockHash.fromHexString(blockHash)) ); const round = winningBakers.filter((x) => x.round === 296651n)[0]; diff --git a/packages/nodejs/test/credentialDeployment.test.ts b/packages/sdk/test/client/credentialDeployment.test.ts similarity index 84% rename from packages/nodejs/test/credentialDeployment.test.ts rename to packages/sdk/test/client/credentialDeployment.test.ts index 8f52ac7c8..d95210ec2 100644 --- a/packages/nodejs/test/credentialDeployment.test.ts +++ b/packages/sdk/test/client/credentialDeployment.test.ts @@ -1,4 +1,4 @@ -import { getIdentityInput } from './testHelpers'; +import { getIdentityInput } from './testHelpers.js'; import { VerifyKey, CredentialDeploymentTransaction, @@ -6,20 +6,19 @@ import { IdentityInput, TransactionExpiry, getCredentialDeploymentSignDigest, - serializeCredentialDeploymentTransactionForSubmission, BlockItemKind, createCredentialDeploymentTransaction, -} from '@concordium/common-sdk'; + deserializeTransaction, + serializeCredentialDeploymentTransactionForSubmission, +} from '../../src/index.js'; import fs from 'fs'; -import * as ed from '@noble/ed25519'; -import { Buffer } from 'buffer/'; -import { deserializeTransaction } from '@concordium/common-sdk/lib/deserialization'; +import * as ed from '#ed25519'; test('test deserialize credentialDeployment ', async () => { const identityInput: IdentityInput = getIdentityInput(); const cryptographicParameters = JSON.parse( - fs.readFileSync('../common/test/resources/global.json').toString() + fs.readFileSync('./test/ci/resources/global.json').toString() ).value; if (!cryptographicParameters) { throw new Error('Missing global'); @@ -47,7 +46,7 @@ test('test deserialize credentialDeployment ', async () => { // The attributes to reveal on the chain. const revealedAttributes: AttributeKey[] = ['firstName', 'nationality']; - const expiry = new TransactionExpiry(new Date(Date.now() + 3600000)); + const expiry = TransactionExpiry.futureMinutes(60); const credentialDeploymentTransaction: CredentialDeploymentTransaction = createCredentialDeploymentTransaction( identityInput, @@ -58,7 +57,7 @@ test('test deserialize credentialDeployment ', async () => { revealedAttributes, expiry ); - const hashToSign: Buffer = getCredentialDeploymentSignDigest( + const hashToSign = getCredentialDeploymentSignDigest( credentialDeploymentTransaction ); @@ -68,10 +67,10 @@ test('test deserialize credentialDeployment ', async () => { 'fcd0e499f5dc7a989a37f8c89536e9af956170d7f502411855052ff75cfc3646'; const signature1 = Buffer.from( - await ed.sign(hashToSign, signingKey1) + await ed.signAsync(hashToSign, signingKey1) ).toString('hex'); const signature2 = Buffer.from( - await ed.sign(hashToSign, signingKey2) + await ed.signAsync(hashToSign, signingKey2) ).toString('hex'); const signatures: string[] = [signature1, signature2]; @@ -98,4 +97,4 @@ test('test deserialize credentialDeployment ', async () => { expect(BigInt(deployment.transaction.expiry)).toEqual( credentialDeploymentTransaction.expiry.expiryEpochSeconds ); -}); +}, 8000); diff --git a/packages/nodejs/test/deserialization.test.ts b/packages/sdk/test/client/deserialization.test.ts similarity index 77% rename from packages/nodejs/test/deserialization.test.ts rename to packages/sdk/test/client/deserialization.test.ts index a6de14f38..7e7ca1986 100644 --- a/packages/nodejs/test/deserialization.test.ts +++ b/packages/sdk/test/client/deserialization.test.ts @@ -1,9 +1,11 @@ -import { getNodeClient } from './testHelpers'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; import { - deserializeContractState, + ContractAddress, + ContractName, isInstanceInfoV0, -} from '@concordium/common-sdk'; -import { Buffer } from 'buffer/'; + BlockHash, + deserializeContractState, +} from '../../src/index.js'; import * as fs from 'fs'; const client = getNodeClient(); @@ -12,11 +14,11 @@ const client = getNodeClient(); test.skip('Deserialize state with schema from file (two-step-transfer)', async () => { const blockHash = 'fad0981b0424c6e1af746a39667628861481ac225f90decd233980311c2e19cb'; - const contractAddress = { index: BigInt(1646), subindex: BigInt(0) }; + const contractAddress = ContractAddress.create(1646); const instanceInfo = await client.getInstanceInfo( contractAddress, - blockHash + BlockHash.fromHexString(blockHash) ); if (!instanceInfo) { throw new Error( @@ -29,10 +31,10 @@ test.skip('Deserialize state with schema from file (two-step-transfer)', async ( } const schema = Buffer.from( - fs.readFileSync('./test/resources/two-step-transfer-schema.bin') + fs.readFileSync('./test/client/resources/two-step-transfer-schema.bin') ); const state = deserializeContractState( - 'two-step-transfer', + ContractName.fromStringUnchecked('two-step-transfer'), schema, instanceInfo.model ); diff --git a/packages/nodejs/test/events.test.ts b/packages/sdk/test/client/events.test.ts similarity index 87% rename from packages/nodejs/test/events.test.ts rename to packages/sdk/test/client/events.test.ts index 00f57bedd..dd20a905d 100644 --- a/packages/nodejs/test/events.test.ts +++ b/packages/sdk/test/client/events.test.ts @@ -1,13 +1,14 @@ -import * as expected from './resources/expectedJsons'; -import { streamToList } from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from './testHelpers'; +import * as expected from './resources/expectedJsons.js'; +import { streamToList, BlockHash } from '../../src/index.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; const client = getNodeClient(); // AccountCreated test('accountCreated', async () => { - const blockHash = - '67fd6360f39ea6d815133878e64070c578a66012b3eaa757cd1dba8a993079ea'; + const blockHash = BlockHash.fromHexString( + '67fd6360f39ea6d815133878e64070c578a66012b3eaa757cd1dba8a993079ea' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); @@ -16,8 +17,9 @@ test('accountCreated', async () => { // EncryptedAmountsRemoved, AmountAddedByDecryption test('transferToPublic', async () => { - const blockHash = - 'e59ba7559e2de14e1bd4c05ddbfca808dd5b870cd89eec3942ae29f842906262'; + const blockHash = BlockHash.fromHexString( + 'e59ba7559e2de14e1bd4c05ddbfca808dd5b870cd89eec3942ae29f842906262' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -37,8 +39,9 @@ test('transferToPublic', async () => { // BakerSetMetadataURL, BakerSetOpenStatus, BakerSetRestakeEarnings // BakerSetTransactionFeeCommission test('configureBaker: Add baker', async () => { - const blockHash = - '04d24b3d44e4ec4681c279424bd276215809a6af64e57fd20cd907a08d998f09'; + const blockHash = BlockHash.fromHexString( + '04d24b3d44e4ec4681c279424bd276215809a6af64e57fd20cd907a08d998f09' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -55,8 +58,9 @@ test('configureBaker: Add baker', async () => { // BakerRemoved test('configureBaker: Remove baker', async () => { - const blockHash = - '2aa7c4a54ad403a9f9b48de2469e5f13a64c95f2cf7a8e72c0f9f7ae0718f642'; + const blockHash = BlockHash.fromHexString( + '2aa7c4a54ad403a9f9b48de2469e5f13a64c95f2cf7a8e72c0f9f7ae0718f642' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -74,8 +78,9 @@ test('configureBaker: Remove baker', async () => { // DelegationAdded, DelegationSetDelegationTarget, DelegationSetRestakeEarnings // DelegationStakeIncreased, test('configureDelegation', async () => { - const blockHash = - '9cf7f3ba97e027f08bc3dc779e6eb4aadaecee0899a532224846196f646921f3'; + const blockHash = BlockHash.fromHexString( + '9cf7f3ba97e027f08bc3dc779e6eb4aadaecee0899a532224846196f646921f3' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -91,9 +96,10 @@ test('configureDelegation', async () => { }); // Interrupted, Resumed, Transferred, Updated -test('contract update', async () => { - const blockHash = - 'a74a3914143eb596132c74685fac1314f6d5e8bb393e3372e83726f0c4654de2'; +test.only('contract update', async () => { + const blockHash = BlockHash.fromHexString( + 'a74a3914143eb596132c74685fac1314f6d5e8bb393e3372e83726f0c4654de2' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -110,8 +116,9 @@ test('contract update', async () => { // EncryptedSelfAmountAdded test('transferToEncrypted', async () => { - const blockHash = - '0254312274ccd192288ca49923c6571ae64d7d0ef57923a68d4c1b055e2ca757'; + const blockHash = BlockHash.fromHexString( + '0254312274ccd192288ca49923c6571ae64d7d0ef57923a68d4c1b055e2ca757' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -128,8 +135,9 @@ test('transferToEncrypted', async () => { // UpdateEnqueued test('UpdateEnqueued', async () => { - const blockHash = - '39122a9c720cae643b999d93dd7bf09bcf50e99bb716767dd35c39690390db54'; + const blockHash = BlockHash.fromHexString( + '39122a9c720cae643b999d93dd7bf09bcf50e99bb716767dd35c39690390db54' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); @@ -138,8 +146,9 @@ test('UpdateEnqueued', async () => { // ContractInitialized test('ContractInitialized', async () => { - const blockHash = - '70dbb294060878220505e928d616dde2d90cf5eeee0a92d3fdc1268334ace89e'; + const blockHash = BlockHash.fromHexString( + '70dbb294060878220505e928d616dde2d90cf5eeee0a92d3fdc1268334ace89e' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -158,8 +167,9 @@ test('ContractInitialized', async () => { // ModuleDeployed test('ModuleDeployed', async () => { - const blockHash = - 'c7fd8efa319942d54336ccdfe8460a0591a2a4b3a6bac65fe552198d530105d1'; + const blockHash = BlockHash.fromHexString( + 'c7fd8efa319942d54336ccdfe8460a0591a2a4b3a6bac65fe552198d530105d1' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -176,8 +186,9 @@ test('ModuleDeployed', async () => { // DelegationRemoved test('DelegationRemoved', async () => { - const blockHash = - '65ad6b6a4c9eaccb99a01e2661fcc588a411beb0ed91d39ac692359d5a666631'; + const blockHash = BlockHash.fromHexString( + '65ad6b6a4c9eaccb99a01e2661fcc588a411beb0ed91d39ac692359d5a666631' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[1]; @@ -194,8 +205,9 @@ test('DelegationRemoved', async () => { // TransferMemo test('TransferMemo', async () => { - const blockHash = - 'df96a12cc515bc863ed7021154494c8747e321565ff8b788066f0308c2963ece'; + const blockHash = BlockHash.fromHexString( + 'df96a12cc515bc863ed7021154494c8747e321565ff8b788066f0308c2963ece' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -212,8 +224,9 @@ test('TransferMemo', async () => { // Upgraded test('Upgraded', async () => { - const blockHash = - '77ffdf2e8e4144a9a39b20ea7211a4aee0a23847778dcc1963c7a85f32b4f27d'; + const blockHash = BlockHash.fromHexString( + '77ffdf2e8e4144a9a39b20ea7211a4aee0a23847778dcc1963c7a85f32b4f27d' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -230,8 +243,9 @@ test('Upgraded', async () => { // DataRegistered test('DataRegistered', async () => { - const blockHash = - 'ac4e60f4a014d823e3bf03859abdb2f9d2317b988dedc9c9621e3b7f5dcffb06'; + const blockHash = BlockHash.fromHexString( + 'ac4e60f4a014d823e3bf03859abdb2f9d2317b988dedc9c9621e3b7f5dcffb06' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -248,8 +262,9 @@ test('DataRegistered', async () => { // NewEncryptedAmountEvent test('NewEncryptedAmountEvent', async () => { - const blockHash = - '4eec1470e133340859dd9cd39187ad5f32c5b59ca3c7277d44f9b30e7a563388'; + const blockHash = BlockHash.fromHexString( + '4eec1470e133340859dd9cd39187ad5f32c5b59ca3c7277d44f9b30e7a563388' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -266,8 +281,9 @@ test('NewEncryptedAmountEvent', async () => { // TransferWithScheduleEvent test('TransferWithScheduleEvent', async () => { - const blockHash = - '7696ce3b5e3c5165572984abb250f8ac7c8f42cdc5ca3e1c1f1c387bb878fc94'; + const blockHash = BlockHash.fromHexString( + '7696ce3b5e3c5165572984abb250f8ac7c8f42cdc5ca3e1c1f1c387bb878fc94' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -284,8 +300,9 @@ test('TransferWithScheduleEvent', async () => { // BakerKeysUpdated test('BakerKeysUpdated', async () => { - const blockHash = - 'ec886543ea454845ce09dcc064d7bc79f7da5a8c74c8f7cce9783681028a47de'; + const blockHash = BlockHash.fromHexString( + 'ec886543ea454845ce09dcc064d7bc79f7da5a8c74c8f7cce9783681028a47de' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[1]; @@ -302,8 +319,9 @@ test('BakerKeysUpdated', async () => { // BakerStakeIncreased test('BakerStakeIncreased', async () => { - const blockHash = - '29c4caa0de1d9fc9da9513635e876aa0db0c6ab37fc30e7d2d7883af51659273'; + const blockHash = BlockHash.fromHexString( + '29c4caa0de1d9fc9da9513635e876aa0db0c6ab37fc30e7d2d7883af51659273' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[1]; @@ -320,8 +338,9 @@ test('BakerStakeIncreased', async () => { // CredentialKeysUpdated test('CredentialKeysUpdated', async () => { - const blockHash = - '387a2f5812b16e4f6543e51007f20b514909de4d7ea39785b83bcd6f1cde9af4'; + const blockHash = BlockHash.fromHexString( + '387a2f5812b16e4f6543e51007f20b514909de4d7ea39785b83bcd6f1cde9af4' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -338,8 +357,9 @@ test('CredentialKeysUpdated', async () => { // CredentialsUpdated test('CredentialsUpdated', async () => { - const blockHash = - '6ce268765af0f59e147a0935980ae3014b9e90b9a43a2d9cf785f19641a9bf64'; + const blockHash = BlockHash.fromHexString( + '6ce268765af0f59e147a0935980ae3014b9e90b9a43a2d9cf785f19641a9bf64' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -358,8 +378,9 @@ test('CredentialsUpdated', async () => { // BakerStakeDecreased test('BakerStakeDecreased', async () => { - const blockHash = - '2103b8c6f1e0608790f241b1ca5d19df16f00abe54e5885d72e60985959826ae'; + const blockHash = BlockHash.fromHexString( + '2103b8c6f1e0608790f241b1ca5d19df16f00abe54e5885d72e60985959826ae' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -376,8 +397,9 @@ test('BakerStakeDecreased', async () => { // DelegationStakeDecreased test('DelegationStakeDecreased', async () => { - const blockHash = - 'f0426a8937438551692bbd777ac61f309fa2adee2dc50c82d6bd6ff151f5ce0a'; + const blockHash = BlockHash.fromHexString( + 'f0426a8937438551692bbd777ac61f309fa2adee2dc50c82d6bd6ff151f5ce0a' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; diff --git a/packages/nodejs/test/getConsensusStatus.test.ts b/packages/sdk/test/client/getConsensusStatus.test.ts similarity index 85% rename from packages/nodejs/test/getConsensusStatus.test.ts rename to packages/sdk/test/client/getConsensusStatus.test.ts index ff9c5d8af..23f35735e 100644 --- a/packages/nodejs/test/getConsensusStatus.test.ts +++ b/packages/sdk/test/client/getConsensusStatus.test.ts @@ -1,14 +1,11 @@ -import { ConsensusStatus, isHex } from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from './testHelpers'; +import { ConsensusStatus } from '../../src/index.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; const client = getNodeClient(); test('retrieves the consensus status from the node with correct types', async () => { const consensusStatus: ConsensusStatus = await client.getConsensusStatus(); return Promise.all([ - expect(isHex(consensusStatus.bestBlock)).toBeTruthy(), - expect(isHex(consensusStatus.genesisBlock)).toBeTruthy(), - expect(isHex(consensusStatus.lastFinalizedBlock)).toBeTruthy(), expect( typeof consensusStatus.finalizationCount === 'bigint' ).toBeTruthy(), @@ -53,7 +50,6 @@ test('retrieves the consensus status from the node with correct types', async () ).toBeFalsy(), expect(Number.isNaN(consensusStatus.genesisIndex)).toBeFalsy(), - expect(typeof consensusStatus.epochDuration === 'bigint').toBeTruthy(), expect( typeof consensusStatus.bestBlockHeight === 'bigint' ).toBeTruthy(), diff --git a/packages/nodejs/test/manualTests.test.ts b/packages/sdk/test/client/manualTests.test.ts similarity index 86% rename from packages/nodejs/test/manualTests.test.ts rename to packages/sdk/test/client/manualTests.test.ts index cb2295935..b97fc6718 100644 --- a/packages/nodejs/test/manualTests.test.ts +++ b/packages/sdk/test/client/manualTests.test.ts @@ -1,17 +1,14 @@ -import * as v2 from '../../common/grpc/v2/concordium/types'; -import * as v1 from '@concordium/common-sdk'; -import { - buildBasicAccountSigner, - signTransaction, -} from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from '../test/testHelpers'; +import * as v2 from '../../src/grpc-api/v2/concordium/types.js'; +import * as v1 from '../../src/index.js'; +import { buildBasicAccountSigner, signTransaction } from '../../src/index.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; const client = getNodeClient(); -const testAccount = new v1.AccountAddress( +const testAccount = v1.AccountAddress.fromBase58( '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' ); -const senderAccount = new v1.AccountAddress( +const senderAccount = v1.AccountAddress.fromBase58( '39zbDo5ycLdugboskzUqjme8uNnDFfAYdyAYB9csegQJ2BqLoe' ); @@ -24,12 +21,12 @@ describe.skip('Manual test suite', () => { // Create local transaction const header: v1.AccountTransactionHeader = { - expiry: new v1.TransactionExpiry(new Date(Date.now() + 3600000)), + expiry: v1.TransactionExpiry.futureMinutes(60), nonce: nonce, sender: senderAccount, }; const simpleTransfer: v1.SimpleTransferPayload = { - amount: new v1.CcdAmount(100n), + amount: v1.CcdAmount.fromMicroCcd(100), toAddress: testAccount, }; const accountTransaction: v1.AccountTransaction = { diff --git a/packages/nodejs/test/rejectReasons.test.ts b/packages/sdk/test/client/rejectReasons.test.ts similarity index 88% rename from packages/nodejs/test/rejectReasons.test.ts rename to packages/sdk/test/client/rejectReasons.test.ts index c6158f344..91a71a649 100644 --- a/packages/nodejs/test/rejectReasons.test.ts +++ b/packages/sdk/test/client/rejectReasons.test.ts @@ -1,13 +1,14 @@ -import * as expected from './resources/expectedJsons'; -import { streamToList } from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from './testHelpers'; +import { BlockHash, streamToList } from '../../src/index.js'; +import * as expected from './resources/expectedJsons.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; const client = getNodeClient(); // EncryptedAmountSelfTransfer test('EncryptedAmountSelfTransfer', async () => { - const blockHash = - 'a68ef25ac9b38dfb76884dc797f0b1f924695218107caed3b3e370479d552c3a'; + const blockHash = BlockHash.fromHexString( + 'a68ef25ac9b38dfb76884dc797f0b1f924695218107caed3b3e370479d552c3a' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -26,8 +27,9 @@ test('EncryptedAmountSelfTransfer', async () => { // FinalizationRewardCommissionNotInRange test('FinalizationRewardCommissionNotInRange', async () => { - const blockHash = - 'bb58a5dbcb77ec5d94d1039724e347a5a06b60bd098bb404c9967531e58ec870'; + const blockHash = BlockHash.fromHexString( + 'bb58a5dbcb77ec5d94d1039724e347a5a06b60bd098bb404c9967531e58ec870' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[1]; @@ -46,8 +48,9 @@ test('FinalizationRewardCommissionNotInRange', async () => { // DelegationTargetNotABaker test('DelegationTargetNotABaker', async () => { - const blockHash = - 'f885db7e2b27953f3f6f10b3c69bf7d9e77bc529768234e4191ecbc6fd4cc47d'; + const blockHash = BlockHash.fromHexString( + 'f885db7e2b27953f3f6f10b3c69bf7d9e77bc529768234e4191ecbc6fd4cc47d' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -66,8 +69,9 @@ test('DelegationTargetNotABaker', async () => { // AlreadyABaker test('AlreadyABaker', async () => { - const blockHash = - '20324be7fdb1dd2556e5492ac0b73df408bda7f237066cee3c3d71a4804327a4'; + const blockHash = BlockHash.fromHexString( + '20324be7fdb1dd2556e5492ac0b73df408bda7f237066cee3c3d71a4804327a4' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -84,8 +88,9 @@ test('AlreadyABaker', async () => { // NonExistentCredentialID test('NonExistentCredentialID', async () => { - const blockHash = - 'be5bd3b147eeababdbf19a0d60b29e2aeddc7eb65e3ab901cbd4f071d5af211c'; + const blockHash = BlockHash.fromHexString( + 'be5bd3b147eeababdbf19a0d60b29e2aeddc7eb65e3ab901cbd4f071d5af211c' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -104,8 +109,9 @@ test('NonExistentCredentialID', async () => { // ModuleNotWF test('ModuleNotWF', async () => { - const blockHash = - 'b100e5568b2db7cce2da671ac17d45911447d86340b40a469717c15fd4098dda'; + const blockHash = BlockHash.fromHexString( + 'b100e5568b2db7cce2da671ac17d45911447d86340b40a469717c15fd4098dda' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -122,8 +128,9 @@ test('ModuleNotWF', async () => { // AmountTooLarge test('AmountTooLarge', async () => { - const blockHash = - '25658e0353cae71a48f25f9ed92682cc096d1463b801676b449cb89c7fa13a1f'; + const blockHash = BlockHash.fromHexString( + '25658e0353cae71a48f25f9ed92682cc096d1463b801676b449cb89c7fa13a1f' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -140,8 +147,9 @@ test('AmountTooLarge', async () => { // ModuleHashAlreadyExists test('ModuleHashAlreadyExists', async () => { - const blockHash = - 'ec85ac5f3b7a39ac277aee9e96837c53be3bd3442068a0970ab3badd80fd88e5'; + const blockHash = BlockHash.fromHexString( + 'ec85ac5f3b7a39ac277aee9e96837c53be3bd3442068a0970ab3badd80fd88e5' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -160,8 +168,9 @@ test('ModuleHashAlreadyExists', async () => { // TransactionFeeCommissionNotInRange test('TransactionFeeCommissionNotInRange', async () => { - const blockHash = - '102ef7df5a6d1502c6e2b864e182cbb10824d017e88bb90a4cb82e3c054e0bba'; + const blockHash = BlockHash.fromHexString( + '102ef7df5a6d1502c6e2b864e182cbb10824d017e88bb90a4cb82e3c054e0bba' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -180,8 +189,9 @@ test('TransactionFeeCommissionNotInRange', async () => { // StakeOverMaximumThresholdForPool test('StakeOverMaximumThresholdForPool', async () => { - const blockHash = - '5284633bd71b4f8840e9f2e86ced6a4615961248347669d7b5a5a7088422a9f0'; + const blockHash = BlockHash.fromHexString( + '5284633bd71b4f8840e9f2e86ced6a4615961248347669d7b5a5a7088422a9f0' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[1]; @@ -200,8 +210,9 @@ test('StakeOverMaximumThresholdForPool', async () => { // BakerInCooldown test('BakerInCooldown', async () => { - const blockHash = - 'dd47761affcc6446306158cd51b8ab117b81ae5d33413af2b3c4c5f20275fb5f'; + const blockHash = BlockHash.fromHexString( + 'dd47761affcc6446306158cd51b8ab117b81ae5d33413af2b3c4c5f20275fb5f' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -220,8 +231,9 @@ test('BakerInCooldown', async () => { // InvalidInitMethod test('InvalidInitMethod', async () => { - const blockHash = - '2830618959b146313cfc596826e59390f6b8907d33a964ec0663c1d7e975fcfa'; + const blockHash = BlockHash.fromHexString( + '2830618959b146313cfc596826e59390f6b8907d33a964ec0663c1d7e975fcfa' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -240,8 +252,9 @@ test('InvalidInitMethod', async () => { // InsufficientBalanceForDelegationStake test('InsufficientBalanceForDelegationStake', async () => { - const blockHash = - 'dce2ce0d5e893e273eb53726e35fb249e3151db2347c624e5d0c5ffce20c4950'; + const blockHash = BlockHash.fromHexString( + 'dce2ce0d5e893e273eb53726e35fb249e3151db2347c624e5d0c5ffce20c4950' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -260,8 +273,9 @@ test('InsufficientBalanceForDelegationStake', async () => { // InvalidAccountReference test('InvalidAccountReference', async () => { - const blockHash = - 'a37e065c239787a4fca3241580dd37ce354ef97224adf1f34afbf92fdd310b69'; + const blockHash = BlockHash.fromHexString( + 'a37e065c239787a4fca3241580dd37ce354ef97224adf1f34afbf92fdd310b69' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -280,8 +294,9 @@ test('InvalidAccountReference', async () => { // MissingBakerAddParameters test('MissingBakerAddParameters', async () => { - const blockHash = - '269d3730dd3813dbe5c8104be20bcfe02ee3fbd4a7a3da4fcca1271c38a6e405'; + const blockHash = BlockHash.fromHexString( + '269d3730dd3813dbe5c8104be20bcfe02ee3fbd4a7a3da4fcca1271c38a6e405' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -300,8 +315,9 @@ test('MissingBakerAddParameters', async () => { // PoolClosed test('PoolClosed', async () => { - const blockHash = - '72c2d0d9634b82ade18616711eb1cb351456b913d1758c4d840759a408b75775'; + const blockHash = BlockHash.fromHexString( + '72c2d0d9634b82ade18616711eb1cb351456b913d1758c4d840759a408b75775' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -318,8 +334,9 @@ test('PoolClosed', async () => { // ScheduledSelfTransfer test('ScheduledSelfTransfer', async () => { - const blockHash = - '917ca9e15667a667cad97c7806ea27b78633d6821cc6f1fa29f8aecd238223c5'; + const blockHash = BlockHash.fromHexString( + '917ca9e15667a667cad97c7806ea27b78633d6821cc6f1fa29f8aecd238223c5' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -338,8 +355,9 @@ test('ScheduledSelfTransfer', async () => { // InvalidModuleReference test('InvalidModuleReference', async () => { - const blockHash = - 'c6ebed14d387e8d0c3f8120f83d69948b39478d7205e468f4db9b089459ff8c4'; + const blockHash = BlockHash.fromHexString( + 'c6ebed14d387e8d0c3f8120f83d69948b39478d7205e468f4db9b089459ff8c4' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -358,8 +376,9 @@ test('InvalidModuleReference', async () => { // FirstScheduledReleaseExpired test('FirstScheduledReleaseExpired', async () => { - const blockHash = - '8692bbfd18983543aace1a04596e27ec8f332243b01ed2b6fed28397bf66ff89'; + const blockHash = BlockHash.fromHexString( + '8692bbfd18983543aace1a04596e27ec8f332243b01ed2b6fed28397bf66ff89' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -378,8 +397,9 @@ test('FirstScheduledReleaseExpired', async () => { // InvalidReceiveMethod test('InvalidReceiveMethod', async () => { - const blockHash = - '0b667b6886760c37a176097b390fd1d655e714f2bf19a507b3242d8ee919ed1a'; + const blockHash = BlockHash.fromHexString( + '0b667b6886760c37a176097b390fd1d655e714f2bf19a507b3242d8ee919ed1a' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -398,8 +418,9 @@ test('InvalidReceiveMethod', async () => { // InsufficientBalanceForBakerStake test('InsufficientBalanceForBakerStake', async () => { - const blockHash = - '1803d84dfaa081e5da1c1dc96bbb65888a65904cba5abcbfc2aad963d2d39097'; + const blockHash = BlockHash.fromHexString( + '1803d84dfaa081e5da1c1dc96bbb65888a65904cba5abcbfc2aad963d2d39097' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -418,8 +439,9 @@ test('InsufficientBalanceForBakerStake', async () => { // RuntimeFailure test('RuntimeFailure', async () => { - const blockHash = - '5072f24f681fc5ff9ae09f0b698f8aed20c02bd6990fc59bcb618252ad257355'; + const blockHash = BlockHash.fromHexString( + '5072f24f681fc5ff9ae09f0b698f8aed20c02bd6990fc59bcb618252ad257355' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -436,8 +458,9 @@ test('RuntimeFailure', async () => { // InvalidContractAddress test('InvalidContractAddress', async () => { - const blockHash = - '30247d68bcca12a0a611bfc412a9a8b28152f501ea957970f1351c528bd58edf'; + const blockHash = BlockHash.fromHexString( + '30247d68bcca12a0a611bfc412a9a8b28152f501ea957970f1351c528bd58edf' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -456,8 +479,9 @@ test('InvalidContractAddress', async () => { // OutOfEnergy test('OutOfEnergy', async () => { - const blockHash = - '57c632333f9373fbc7ea4ce3306269981560fd87c5a6de23b4a7584604e2c6bc'; + const blockHash = BlockHash.fromHexString( + '57c632333f9373fbc7ea4ce3306269981560fd87c5a6de23b4a7584604e2c6bc' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -474,8 +498,9 @@ test('OutOfEnergy', async () => { // InvalidEncryptedAmountTransferProof test('InvalidEncryptedAmountTransferProof', async () => { - const blockHash = - '6a63a548e2d983cafe65f47a785e1e1dde1ba35f6fe16234602936f4fbecb4dd'; + const blockHash = BlockHash.fromHexString( + '6a63a548e2d983cafe65f47a785e1e1dde1ba35f6fe16234602936f4fbecb4dd' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -494,8 +519,9 @@ test('InvalidEncryptedAmountTransferProof', async () => { // RejectedInit test('RejectedInit', async () => { - const blockHash = - 'b95031d150ae90175c203a63b23f8dafd5a8c57defaf5d287a6c534d4a4ad2d5'; + const blockHash = BlockHash.fromHexString( + 'b95031d150ae90175c203a63b23f8dafd5a8c57defaf5d287a6c534d4a4ad2d5' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -512,8 +538,9 @@ test('RejectedInit', async () => { // RejectedReceive test('RejectedReceive', async () => { - const blockHash = - '2141282b7a2ec57f3bcce59dc3b0649c80b872ae21a56c2ad300c4002145f988'; + const blockHash = BlockHash.fromHexString( + '2141282b7a2ec57f3bcce59dc3b0649c80b872ae21a56c2ad300c4002145f988' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -532,8 +559,9 @@ test('RejectedReceive', async () => { // StakeUnderMinimumThresholdForBaking test('StakeUnderMinimumThresholdForBaking', async () => { - const blockHash = - '4d8a001488e2295911b55822c9fb48fae7deff1bb1e2a36aba54c5f61b8e3159'; + const blockHash = BlockHash.fromHexString( + '4d8a001488e2295911b55822c9fb48fae7deff1bb1e2a36aba54c5f61b8e3159' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -552,8 +580,9 @@ test('StakeUnderMinimumThresholdForBaking', async () => { // InvalidTransferToPublicProof test('InvalidTransferToPublicProof', async () => { - const blockHash = - '10f02dba8e75ef25d2eefde19d39624c62600f13a5d91b857283b718017a4471'; + const blockHash = BlockHash.fromHexString( + '10f02dba8e75ef25d2eefde19d39624c62600f13a5d91b857283b718017a4471' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -572,8 +601,9 @@ test('InvalidTransferToPublicProof', async () => { // SerializationFailure test('SerializationFailure', async () => { - const blockHash = - 'd3e2e0a0a6674a56f9e057894fcba2244c21242705f9a95ba1052e6ab156eeb1'; + const blockHash = BlockHash.fromHexString( + 'd3e2e0a0a6674a56f9e057894fcba2244c21242705f9a95ba1052e6ab156eeb1' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; @@ -592,8 +622,9 @@ test('SerializationFailure', async () => { // PoolWouldBecomeOverDelegated test('PoolWouldBecomeOverDelegated', async () => { - const blockHash = - 'c4ae2d1e29ed2dfed7e4a0e08fb419ae6b5cef65cba9ff0c6553ef6377b3e95c'; + const blockHash = BlockHash.fromHexString( + 'c4ae2d1e29ed2dfed7e4a0e08fb419ae6b5cef65cba9ff0c6553ef6377b3e95c' + ); const eventStream = client.getBlockTransactionEvents(blockHash); const events = await streamToList(eventStream); const event = events[0]; diff --git a/packages/nodejs/test/resources/bulletproofgenerators.ts b/packages/sdk/test/client/resources/bulletproofgenerators.ts similarity index 100% rename from packages/nodejs/test/resources/bulletproofgenerators.ts rename to packages/sdk/test/client/resources/bulletproofgenerators.ts diff --git a/packages/nodejs/test/resources/expectedJsons.ts b/packages/sdk/test/client/resources/expectedJsons.ts similarity index 60% rename from packages/nodejs/test/resources/expectedJsons.ts rename to packages/sdk/test/client/resources/expectedJsons.ts index a8c15b921..838763a41 100644 --- a/packages/nodejs/test/resources/expectedJsons.ts +++ b/packages/sdk/test/client/resources/expectedJsons.ts @@ -1,15 +1,81 @@ import { AccountAddress, + AccountCreationSummary, + AccountInfoBaker, + AccountInfoDelegator, + AccountInfoSimple, + AccountInfoType, + AmountAddedByDecryptionEvent, + AmountTooLarge, + BakerEvent, + BakerPoolPendingChangeType, + BakerPoolStatus, + BaseAccountTransactionSummary, + BlockFinalizationSummary, + BlockHash, + BlockInfoV0, + BlockItemStatus, + BlockItemSummary, + BlockSpecialEvent, + BlockSpecialEventBakingRewards, + BlockSpecialEventBlockAccrueReward, + BlockSpecialEventBlockReward, + BlockSpecialEventFinalizationRewards, + BlockSpecialEventPaydayAccountReward, + BlockSpecialEventPaydayFoundationReward, + BlockSpecialEventPaydayPoolReward, CcdAmount, ChainParametersV0, ChainParametersV1, + ContractAddress, + ContractEvent, + ContractInitializedEvent, + ContractTraceEvent, + CredentialKeysUpdatedEvent, + CredentialsUpdatedEvent, + DataRegisteredEvent, + DelegationEvent, + DelegationTargetType, + DelegatorInfo, + DelegatorRewardPeriodInfo, ElectionInfoV0, - EpochFinalizationEntry, + EncryptedAmountsRemovedEvent, + EncryptedSelfAmountAddedEvent, + Energy, + InitName, + InstanceInfo, + InvalidContractAddress, + InvokeContractResult, + ModuleDeployedEvent, ModuleReference, + NewEncryptedAmountEvent, NextUpdateSequenceNumbers, + OpenStatusText, + Parameter, + PassiveDelegationStatus, + PendingUpdate, + PoolStatusType, + ReceiveName, + RejectReason, + RejectReasonTag, + RejectedReceive, + ReturnValue, + SequenceNumber, + StakePendingChangeType, + Timestamp, + TransactionEventTag, + TransactionHash, + TransactionKindString, + TransactionStatusEnum, + TransactionSummaryType, + TransferWithMemoSummary, + TransferredWithScheduleEvent, + UpdateSummary, + UpdateType, QuorumCertificate, TimeoutCertificate, -} from '@concordium/common-sdk'; + EpochFinalizationEntry, +} from '../../../src/index.js'; export const accountInfo = { sequenceNumber: { @@ -139,19 +205,22 @@ export const stakingInfoDelegator = { }, }; -export const blockItemStatusUpdate = { - status: 'finalized', +export const blockItemStatusUpdate: BlockItemStatus = { + status: TransactionStatusEnum.Finalized, outcome: { - blockHash: - '2d9e1a081819ad8dbf81d5a882ea2f5352cb3429fc12b0ec18c131a360751a66', + blockHash: BlockHash.fromHexString( + '2d9e1a081819ad8dbf81d5a882ea2f5352cb3429fc12b0ec18c131a360751a66' + ), summary: { index: 0n, - energyCost: 0n, - hash: '3de823b876d05cdd33a311a0f84124079f5f677afb2534c4943f830593edc650', - type: 'updateTransaction', + energyCost: Energy.create(0), + hash: TransactionHash.fromHexString( + '3de823b876d05cdd33a311a0f84124079f5f677afb2534c4943f830593edc650' + ), + type: TransactionSummaryType.UpdateTransaction, effectiveTime: 0n, payload: { - updateType: 'microGtuPerEuro', + updateType: UpdateType.MicroGtuPerEuro, update: { numerator: 17592435270983729152n, denominator: 163844642115n, @@ -161,66 +230,74 @@ export const blockItemStatusUpdate = { }, }; -export const blockItemStatusTransfer = { - status: 'finalized', +export const blockItemStatusTransfer: BlockItemStatus = { + status: TransactionStatusEnum.Finalized, outcome: { - blockHash: - '577513ab772da146c7abb9f30c521668d7ef4fa01f4838cc51d5f59e27c7a5fc', + blockHash: BlockHash.fromHexString( + '577513ab772da146c7abb9f30c521668d7ef4fa01f4838cc51d5f59e27c7a5fc' + ), summary: { - type: 'accountTransaction', + type: TransactionSummaryType.AccountTransaction, index: 0n, cost: 1480606n, - energyCost: 501n, - hash: '502332239efc0407eebef5c73c390080e5d7e1b127ff29f786a62b3c9ab6cfe7', - sender: '4fKPBDf9r5vhEpoeNY7SJbZv8bAJvYYyJSEggZkNyQPgao8iLy', - transactionType: 'transfer', + energyCost: Energy.create(501), + hash: TransactionHash.fromHexString( + '502332239efc0407eebef5c73c390080e5d7e1b127ff29f786a62b3c9ab6cfe7' + ), + sender: AccountAddress.fromBase58( + '4fKPBDf9r5vhEpoeNY7SJbZv8bAJvYYyJSEggZkNyQPgao8iLy' + ), + transactionType: TransactionKindString.Transfer, transfer: { - amount: 1000000n, - tag: 'Transferred', - to: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', + amount: CcdAmount.fromMicroCcd(1000000), + tag: TransactionEventTag.Transferred, + to: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), }, }, }, }; -export const instanceInfo = { +export const instanceInfo: InstanceInfo = { version: 1, - owner: new AccountAddress( + owner: AccountAddress.fromBase58( '4Y1c27ZRpRut9av69n3i1uhfeDp4XGuvsm9fkEjFvgpoxXWxQB' ), - amount: new CcdAmount(0n), - methods: ['weather.get', 'weather.set'], - name: 'init_weather', - sourceModule: new ModuleReference( + amount: CcdAmount.zero(), + methods: ['weather.get', 'weather.set'].map( + ReceiveName.fromStringUnchecked + ), + name: InitName.fromStringUnchecked('init_weather'), + sourceModule: ModuleReference.fromHexString( '67d568433bd72e4326241f262213d77f446db8ba03dfba351ae35c1b2e7e5109' ), }; -export const invokeInstanceResponseV0 = { +export const invokeInstanceResponseV0: InvokeContractResult = { tag: 'success', - usedEnergy: 342n, + usedEnergy: Energy.create(342), returnValue: undefined, events: [ { - tag: 'Updated', + tag: TransactionEventTag.Updated, events: [], - amount: 1n, - address: { - index: 6n, - subindex: 0n, - }, + amount: CcdAmount.fromMicroCcd(1), + address: ContractAddress.create(6), contractVersion: 0, instigator: { type: 'AddressAccount', - address: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', + address: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), }, - message: '', - receiveName: 'PiggyBank.insert', + message: Parameter.empty(), + receiveName: ReceiveName.fromStringUnchecked('PiggyBank.insert'), }, ], }; -export const accountList = [ +export const accountList: AccountAddress.Type[] = [ '3QK1rxUXV7GRk4Ng7Bs7qnbkdjyBdjzCytpTrSQN7BaJkiEfgZ', '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini', '3gGBYDSpx2zWL3YMcqD48U5jVXYG4pJBDZqeY5CbMMKpxVBbc3', @@ -234,19 +311,19 @@ export const accountList = [ '4AnukgcopMC4crxfL1L9fUYw9MAkoo1yKLvH7eA1NAX7SxgyRY', '4BTFaHx8CioLi8Xe7YiimpAK1oQMkbx5Wj6B8N7d7NXgmLvEZs', '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L', -]; +].map(AccountAddress.fromBase58); -export const moduleList = [ +export const moduleList: ModuleReference.Type[] = [ '67d568433bd72e4326241f262213d77f446db8ba03dfba351ae35c1b2e7e5109', '6f0524700ed808a8fe0d7e23014c5138e4fac1fd8ec85c5e3591096f48609206', 'ceb018e4cd3456c0ccc0bca14285a69fd55f4cb09c322195d49c5c22f85930fe', -]; +].map(ModuleReference.fromHexString); -export const ancestorList = [ +export const ancestorList: BlockHash.Type[] = [ 'fe88ff35454079c3df11d8ae13d5777babd61f28be58494efe51b6593e30716e', '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf', 'abc98d4866e92b0ac4722d523aee96cafcdd127694d565c532e149616dbad96c', -]; +].map(BlockHash.fromHexString); export const instanceStateList = [ { @@ -337,131 +414,176 @@ export const arList = [ }, ]; -export const delegatorInfoList = [ +export const delegatorInfoList: DelegatorInfo[] = [ { - account: '3uX8g2uzQwBjVSJ6ZDU5cQCKhgsET6kMuRoraQH2ANB9Xa84YR', - stake: 40000000000n, + account: AccountAddress.fromBase58( + '3uX8g2uzQwBjVSJ6ZDU5cQCKhgsET6kMuRoraQH2ANB9Xa84YR' + ), + stake: CcdAmount.fromMicroCcd(40_000_000_000), }, { - account: '4mAs6xcFw26fb6u8odkJWoe3fAK8bCJ91BwScUc36DFhh3thwD', - stake: 10000000n, + account: AccountAddress.fromBase58( + '4mAs6xcFw26fb6u8odkJWoe3fAK8bCJ91BwScUc36DFhh3thwD' + ), + stake: CcdAmount.fromMicroCcd(10_000_000), }, { - account: '3NvUNvVm5puDT2EYbo7hCF3d5AwzzCqKE18Ms6BYkKY9UShdf3', - stake: 3000000000n, + account: AccountAddress.fromBase58( + '3NvUNvVm5puDT2EYbo7hCF3d5AwzzCqKE18Ms6BYkKY9UShdf3' + ), + stake: CcdAmount.fromMicroCcd(3000_000_000), }, { - account: '3ivPxmqdRk5TX5mKpFshKzrA44bYUW2tg6EwDPvALszNoBGTK9', - stake: 33000000n, + account: AccountAddress.fromBase58( + '3ivPxmqdRk5TX5mKpFshKzrA44bYUW2tg6EwDPvALszNoBGTK9' + ), + stake: CcdAmount.fromMicroCcd(33_000_000), }, { - account: '37tU96v4MQSaEgVP68M3TBRHMwZpgYSGnMer3ta3FJ8wkXtjDQ', - stake: 94000000n, + account: AccountAddress.fromBase58( + '37tU96v4MQSaEgVP68M3TBRHMwZpgYSGnMer3ta3FJ8wkXtjDQ' + ), + stake: CcdAmount.fromMicroCcd(94_000_000), }, ]; -export const passiveDelegatorInfoList = [ +export const passiveDelegatorInfoList: DelegatorInfo[] = [ { - account: '4gCvJ91EeYzsTzwiC7Kr4AcFzSuDmf5wxev7FRzU3uw49WamBm', - stake: 1900000000n, + account: AccountAddress.fromBase58( + '4gCvJ91EeYzsTzwiC7Kr4AcFzSuDmf5wxev7FRzU3uw49WamBm' + ), + stake: CcdAmount.fromMicroCcd(1_900_000_000), }, { - account: '4mQweXtq3zHwS7CtK5fjWkpJDUvtUSKycNa8xaEbe6kErGeXcL', - stake: 1000000000n, + account: AccountAddress.fromBase58( + '4mQweXtq3zHwS7CtK5fjWkpJDUvtUSKycNa8xaEbe6kErGeXcL' + ), + stake: CcdAmount.fromMicroCcd(1_000_000_000), }, { - account: '3irV7FF3BZbz9ejGTm7EHLUi6CQHdJUELDfyhwkHcLqXmQyUfR', - stake: 100000000n, + account: AccountAddress.fromBase58( + '3irV7FF3BZbz9ejGTm7EHLUi6CQHdJUELDfyhwkHcLqXmQyUfR' + ), + stake: CcdAmount.fromMicroCcd(100_000_000), pendingChange: { effectiveTime: new Date('2022-06-28T11:47:37.750Z'), - change: 'RemoveStake', + change: StakePendingChangeType.RemoveStake, }, }, ]; -export const passiveDelegatorRewardInfoList = [ +export const passiveDelegatorRewardInfoList: DelegatorRewardPeriodInfo[] = [ { - account: '4gCvJ91EeYzsTzwiC7Kr4AcFzSuDmf5wxev7FRzU3uw49WamBm', - stake: 1900000000n, + account: AccountAddress.fromBase58( + '4gCvJ91EeYzsTzwiC7Kr4AcFzSuDmf5wxev7FRzU3uw49WamBm' + ), + stake: CcdAmount.fromMicroCcd(1_900_000_000), }, { - account: '4mQweXtq3zHwS7CtK5fjWkpJDUvtUSKycNa8xaEbe6kErGeXcL', - stake: 1000000000n, + account: AccountAddress.fromBase58( + '4mQweXtq3zHwS7CtK5fjWkpJDUvtUSKycNa8xaEbe6kErGeXcL' + ), + stake: CcdAmount.fromMicroCcd(1_000_000_000), }, ]; export const electionInfoList: ElectionInfoV0 = { + version: 0, electionDifficulty: 0.025, electionNonce: '0bb2121015ddd9026d0c31a8b33499ce6049daf5696fe4e2cd94cff83ad331f2', bakerElectionInfo: [ { baker: 0n, - account: '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn', + account: AccountAddress.fromBase58( + '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn' + ), lotteryPower: 0.09090909090909091, }, { baker: 1n, - account: '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini', + account: AccountAddress.fromBase58( + '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini' + ), lotteryPower: 0.09090909090909091, }, { baker: 2n, - account: '3QK1rxUXV7GRk4Ng7Bs7qnbkdjyBdjzCytpTrSQN7BaJkiEfgZ', + account: AccountAddress.fromBase58( + '3QK1rxUXV7GRk4Ng7Bs7qnbkdjyBdjzCytpTrSQN7BaJkiEfgZ' + ), lotteryPower: 0.09090909090909091, }, { baker: 3n, - account: '3gGBYDSpx2zWL3YMcqD48U5jVXYG4pJBDZqeY5CbMMKpxVBbc3', + account: AccountAddress.fromBase58( + '3gGBYDSpx2zWL3YMcqD48U5jVXYG4pJBDZqeY5CbMMKpxVBbc3' + ), lotteryPower: 0.09090909090909091, }, { baker: 4n, - account: '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg', + account: AccountAddress.fromBase58( + '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg' + ), lotteryPower: 0.09090909090909091, }, { baker: 5n, - account: '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L', + account: AccountAddress.fromBase58( + '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L' + ), lotteryPower: 0.09090909090909091, }, { baker: 6n, - account: '3ntvNGT6tDuLYiSb5gMJSQAZfLPUJnzoizcFiVRWqLoctuXxpK', + account: AccountAddress.fromBase58( + '3ntvNGT6tDuLYiSb5gMJSQAZfLPUJnzoizcFiVRWqLoctuXxpK' + ), lotteryPower: 0.09090909090909091, }, { baker: 7n, - account: '4BTFaHx8CioLi8Xe7YiimpAK1oQMkbx5Wj6B8N7d7NXgmLvEZs', + account: AccountAddress.fromBase58( + '4BTFaHx8CioLi8Xe7YiimpAK1oQMkbx5Wj6B8N7d7NXgmLvEZs' + ), lotteryPower: 0.09090909090909091, }, { baker: 8n, - account: '4AnukgcopMC4crxfL1L9fUYw9MAkoo1yKLvH7eA1NAX7SxgyRY', + account: AccountAddress.fromBase58( + '4AnukgcopMC4crxfL1L9fUYw9MAkoo1yKLvH7eA1NAX7SxgyRY' + ), lotteryPower: 0.09090909090909091, }, { baker: 9n, - account: '3y9DtDUL8xpf8i2yj9k44zMVkf4H1hkpBEQcXbJhrgcwYSGg41', + account: AccountAddress.fromBase58( + '3y9DtDUL8xpf8i2yj9k44zMVkf4H1hkpBEQcXbJhrgcwYSGg41' + ), lotteryPower: 0.09090909090909091, }, { baker: 10n, - account: '42tFTDWvTmBd7hEacohuCfGFa9TsBKhsmXKeViQ7q7NoY7UadV', + account: AccountAddress.fromBase58( + '42tFTDWvTmBd7hEacohuCfGFa9TsBKhsmXKeViQ7q7NoY7UadV' + ), lotteryPower: 0.09090909090909091, }, ], }; -export const transactionEventList = [ +export const transactionEventList: BlockItemSummary[] = [ { - type: 'updateTransaction', + type: TransactionSummaryType.UpdateTransaction, index: 0n, - energyCost: 0n, - hash: '49d7b5c3234dc17bd904af0b63712dc0a6680b96ad556c5ac1103d8cdd128891', + energyCost: Energy.create(0), + hash: TransactionHash.fromHexString( + '49d7b5c3234dc17bd904af0b63712dc0a6680b96ad556c5ac1103d8cdd128891' + ), effectiveTime: 0n, payload: { - updateType: 'microGtuPerEuro', + updateType: UpdateType.MicroGtuPerEuro, update: { denominator: 126230907181n, numerator: 9397474320418127872n, @@ -492,22 +614,23 @@ export const seqNums: NextUpdateSequenceNumbers = { finalizationCommiteeParameters: 1n, }; -export const specialEventList = [ +export const specialEventList: BlockSpecialEvent[] = [ { tag: 'blockAccrueReward', - transactionFees: 0n, - oldGasAccount: 293604n, - newGasAccount: 219102n, - bakerReward: 74502n, - passiveReward: 0n, - foundationCharge: 0n, + transactionFees: CcdAmount.zero(), + oldGasAccount: CcdAmount.fromMicroCcd(293604), + newGasAccount: CcdAmount.fromMicroCcd(219102), + bakerReward: CcdAmount.fromMicroCcd(74502), + passiveReward: CcdAmount.zero(), + foundationCharge: CcdAmount.zero(), baker: 4n, }, ]; -export const pendingUpdateList = [ - { - updateType: 'protocol', +export const pendingUpdate: PendingUpdate = { + effectiveTime: Timestamp.fromMillis(1669115100n), + effect: { + updateType: UpdateType.Protocol, update: { message: 'Enable protocol version 5', specificationHash: @@ -517,12 +640,14 @@ export const pendingUpdateList = [ specificationAuxiliaryData: '', }, }, -]; +}; -export const blockFinalizationSummary = { +export const blockFinalizationSummary: BlockFinalizationSummary = { tag: 'record', record: { - block: '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf', + block: BlockHash.fromHexString( + '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' + ), index: 1131614n, delay: 0n, finalizers: [ @@ -540,20 +665,29 @@ export const blockFinalizationSummary = { }, }; -export const accountCreationEvent = { - type: 'accountCreation', +export const accountCreationEvent: AccountCreationSummary = { + type: TransactionSummaryType.AccountCreation, index: 0n, - energyCost: 54100n, - hash: '9931f541e166d86916354fc98759fcad604d447041142c0897f473093aaafdb5', + energyCost: Energy.create(54100), + hash: TransactionHash.fromHexString( + '9931f541e166d86916354fc98759fcad604d447041142c0897f473093aaafdb5' + ), credentialType: 'normal', - address: '32YGU1j7Z3xwA5URGYFrMamKj7JtfGvXFiXH4p3gAKdrpJcdg2', + address: AccountAddress.fromBase58( + '32YGU1j7Z3xwA5URGYFrMamKj7JtfGvXFiXH4p3gAKdrpJcdg2' + ), regId: '9015cfd6c1bd06f0e0355fc5355a0c18fe0cb37632d469b07d6fbfa9c05facc271c975466d8dfe3144c683f44fd0af71', }; -export const transferToPublicEvent = [ +export const transferToPublicEvent: [ + EncryptedAmountsRemovedEvent, + AmountAddedByDecryptionEvent +] = [ { - tag: 'EncryptedAmountsRemoved', - account: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', + tag: TransactionEventTag.EncryptedAmountsRemoved, + account: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), inputAmount: 'b74e0a607e30eefc5e9323befdbebae158f66c0f6767d3c3f3ff4b1a9d5b9f0e5e565d734141bd82bfca4bcf32e2bec182e895b7afde650cd2e51c2d704d58965b0c462ffef2fca87204ac5248b111290b699dfe84887aa11ab357dab4b2ba00b3301a5e0fbc6cd3f9ac58bbf19abcc9a56ecc83a16738508fb9ec60da2818d5360dbf66839c6a4037e37c2a4a64956ab5c30c0bf1ed90713838dd8a6cce803111698f9c0e145cae6be38e4136ebdc6205ac4ca2f43852dac7e7f6d37fc55cdc', newAmount: @@ -561,16 +695,20 @@ export const transferToPublicEvent = [ upToIndex: 0, }, { - tag: 'AmountAddedByDecryption', - account: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', - amount: 1000000n, + tag: TransactionEventTag.AmountAddedByDecryption, + account: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), + amount: CcdAmount.fromMicroCcd(1_000_000), }, ]; -export const configureBaker = [ +export const configureBaker: BakerEvent[] = [ { - tag: 'BakerAdded', - account: '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS', + tag: TransactionEventTag.BakerAdded, + account: AccountAddress.fromBase58( + '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS' + ), aggregationKey: '802f086e91d71a4d8e1437229b4b7f39c2f45ee1a075e4b786f60af7ec278fa0052318f3f65989cd0cdd0e9ef17b865710b59ef9b894f1f4fb6a58ebbef7b07a04a503421cfa37229c66a9fe8e943be47fba7b15eb263227224e35e0ff088fda', bakerId: 2561n, @@ -579,278 +717,320 @@ export const configureBaker = [ restakeEarnings: true, signKey: '0055703a2615746700b58e312aa428e5526993d6d3f3f109db92436115d63818', - stake: 15000000000n, + stake: CcdAmount.fromMicroCcd(15000000000n), }, { - tag: 'BakerSetRestakeEarnings', - account: '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS', + tag: TransactionEventTag.BakerSetRestakeEarnings, + account: AccountAddress.fromBase58( + '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS' + ), bakerId: 2561n, restakeEarnings: true, }, { - tag: 'BakerSetOpenStatus', - account: '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS', + tag: TransactionEventTag.BakerSetOpenStatus, + account: AccountAddress.fromBase58( + '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS' + ), bakerId: 2561n, - openStatus: 'openForAll', + openStatus: OpenStatusText.OpenForAll, }, { - tag: 'BakerSetMetadataURL', - account: '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS', + tag: TransactionEventTag.BakerSetMetadataURL, + account: AccountAddress.fromBase58( + '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS' + ), bakerId: 2561n, metadataURL: '', }, { - tag: 'BakerSetTransactionFeeCommission', - account: '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS', + tag: TransactionEventTag.BakerSetTransactionFeeCommission, + account: AccountAddress.fromBase58( + '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS' + ), bakerId: 2561n, transactionFeeCommission: 0.1, }, { - tag: 'BakerSetBakingRewardCommission', - account: '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS', + tag: TransactionEventTag.BakerSetBakingRewardCommission, + account: AccountAddress.fromBase58( + '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS' + ), bakerId: 2561n, bakingRewardCommission: 0.1, }, { - tag: 'BakerSetFinalizationRewardCommission', - account: '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS', + tag: TransactionEventTag.BakerSetFinalizationRewardCommission, + account: AccountAddress.fromBase58( + '2zdNDFqqn6pGPzEVRLTLNfBX6FTyQECjAgdLWqYEvBsv7uRjSS' + ), bakerId: 2561n, finalizationRewardCommission: 1, }, ]; -export const bakerRemoved = { - tag: 'BakerRemoved', +export const bakerRemoved: BakerEvent = { + tag: TransactionEventTag.BakerRemoved, bakerId: 1879n, - account: '4aCoaW3qkQRnY3fUGThQcEMGSPLUEQ7XL9Yagx2UR91QpvtoAe', + account: AccountAddress.fromBase58( + '4aCoaW3qkQRnY3fUGThQcEMGSPLUEQ7XL9Yagx2UR91QpvtoAe' + ), }; -export const configureDelegation = [ +export const configureDelegation: DelegationEvent[] = [ { - account: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', - delegatorId: 2059, - tag: 'DelegationAdded', + account: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), + delegatorId: 2059n, + tag: TransactionEventTag.DelegationAdded, }, { - account: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', - delegationTarget: { delegateType: 'Passive' }, - delegatorId: 2059, - tag: 'DelegationSetDelegationTarget', + account: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), + delegationTarget: { + delegateType: DelegationTargetType.PassiveDelegation, + }, + delegatorId: 2059n, + tag: TransactionEventTag.DelegationSetDelegationTarget, }, { - account: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', - delegatorId: 2059, + account: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), + delegatorId: 2059n, restakeEarnings: true, - tag: 'DelegationSetRestakeEarnings', + tag: TransactionEventTag.DelegationSetRestakeEarnings, }, { - account: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', - delegatorId: 2059, - newStake: 1000000n, - tag: 'DelegationStakeIncreased', + account: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), + delegatorId: 2059n, + newStake: CcdAmount.fromMicroCcd(1000000n), + tag: TransactionEventTag.DelegationStakeIncreased, }, ]; -export const updateEvent = [ +export const updateEvent: ContractTraceEvent[] = [ { - address: { index: 866n, subindex: 0n }, + address: ContractAddress.create(866), events: [], - tag: 'Interrupted', + tag: TransactionEventTag.Interrupted, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), events: [], - tag: 'Interrupted', + tag: TransactionEventTag.Interrupted, }, { - address: { index: 864n, subindex: 0n }, - amount: 0n, + address: ContractAddress.create(864), + amount: CcdAmount.zero(), contractVersion: 1, events: [], instigator: { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), type: 'AddressContract', }, - message: '0000', - receiveName: 'CIS2-wCCD-State.getPaused', - tag: 'Updated', + message: Parameter.fromHexString('0000'), + receiveName: ReceiveName.fromStringUnchecked( + 'CIS2-wCCD-State.getPaused' + ), + tag: TransactionEventTag.Updated, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), success: true, - tag: 'Resumed', + tag: TransactionEventTag.Resumed, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), events: [], - tag: 'Interrupted', + tag: TransactionEventTag.Interrupted, }, { - address: { index: 864n, subindex: 0n }, - amount: 0n, + address: ContractAddress.create(864), + amount: CcdAmount.zero(), contractVersion: 1, events: [], instigator: { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), type: 'AddressContract', }, - message: - '00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083', - receiveName: 'CIS2-wCCD-State.getBalance', - tag: 'Updated', + message: Parameter.fromHexString( + '00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083' + ), + receiveName: ReceiveName.fromStringUnchecked( + 'CIS2-wCCD-State.getBalance' + ), + tag: TransactionEventTag.Updated, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), success: true, - tag: 'Resumed', + tag: TransactionEventTag.Resumed, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), events: [], - tag: 'Interrupted', + tag: TransactionEventTag.Interrupted, }, { - address: { index: 864n, subindex: 0n }, - amount: 0n, + address: ContractAddress.create(864), + amount: CcdAmount.zero(), contractVersion: 1, events: [], instigator: { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), type: 'AddressContract', }, - message: - '00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083c0843d00', - receiveName: 'CIS2-wCCD-State.setBalance', - tag: 'Updated', + message: Parameter.fromHexString( + '00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083c0843d00' + ), + receiveName: ReceiveName.fromStringUnchecked( + 'CIS2-wCCD-State.setBalance' + ), + tag: TransactionEventTag.Updated, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), success: true, - tag: 'Resumed', + tag: TransactionEventTag.Resumed, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), events: [], - tag: 'Interrupted', + tag: TransactionEventTag.Interrupted, }, { - address: { index: 866n, subindex: 0n }, + address: ContractAddress.create(866), events: [], - tag: 'Interrupted', + tag: TransactionEventTag.Interrupted, }, { - amount: 1000000n, - from: { - address: { index: 866n, subindex: 0n }, - type: 'AddressContract', - }, - tag: 'Transferred', - to: { - address: '4inf4g36xDEQmjxDbbkqeHD2HNg9v7dohXUDH5S9en4Th53kxm', - type: 'AddressAccount', - }, + amount: CcdAmount.fromMicroCcd(1000000n), + from: ContractAddress.create(866), + tag: TransactionEventTag.Transferred, + to: AccountAddress.fromBase58( + '4inf4g36xDEQmjxDbbkqeHD2HNg9v7dohXUDH5S9en4Th53kxm' + ), }, { - address: { index: 866n, subindex: 0n }, + address: ContractAddress.create(866), success: true, - tag: 'Resumed', + tag: TransactionEventTag.Resumed, }, { - address: { index: 866n, subindex: 0n }, - amount: 0n, + address: ContractAddress.create(866), + amount: CcdAmount.zero(), contractVersion: 1, events: [], instigator: { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), type: 'AddressContract', }, - message: - 'c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be000830000', - receiveName: 'CIS2-wCCD-Proxy.transferCCD', - tag: 'Updated', + message: Parameter.fromHexString( + 'c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be000830000' + ), + receiveName: ReceiveName.fromStringUnchecked( + 'CIS2-wCCD-Proxy.transferCCD' + ), + tag: TransactionEventTag.Updated, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), success: true, - tag: 'Resumed', + tag: TransactionEventTag.Resumed, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), events: [], - tag: 'Interrupted', + tag: TransactionEventTag.Interrupted, }, { - address: { index: 866n, subindex: 0n }, - amount: 0n, + address: ContractAddress.create(866), + amount: CcdAmount.zero(), contractVersion: 1, events: [ 'fd00c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083', - ], + ].map(ContractEvent.fromHexString), instigator: { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), type: 'AddressContract', }, - message: - 'fd00c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083', - receiveName: 'CIS2-wCCD-Proxy.logEvent', - tag: 'Updated', + message: Parameter.fromHexString( + 'fd00c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083' + ), + receiveName: ReceiveName.fromStringUnchecked( + 'CIS2-wCCD-Proxy.logEvent' + ), + tag: TransactionEventTag.Updated, }, { - address: { index: 865n, subindex: 0n }, + address: ContractAddress.create(865), success: true, - tag: 'Resumed', + tag: TransactionEventTag.Resumed, }, { - address: { index: 865n, subindex: 0n }, - amount: 0n, + address: ContractAddress.create(865), + amount: CcdAmount.zero(), contractVersion: 1, events: [], instigator: { - address: { index: 866n, subindex: 0n }, + address: ContractAddress.create(866), type: 'AddressContract', }, - message: - 'c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be0008300e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083000000e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083', - receiveName: 'CIS2-wCCD.unwrap', - tag: 'Updated', + message: Parameter.fromHexString( + 'c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be0008300e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083000000e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be00083' + ), + receiveName: ReceiveName.fromStringUnchecked('CIS2-wCCD.unwrap'), + tag: TransactionEventTag.Updated, }, { - address: { index: 866n, subindex: 0n }, + address: ContractAddress.create(866), success: true, - tag: 'Resumed', + tag: TransactionEventTag.Resumed, }, { - address: { index: 866n, subindex: 0n }, - amount: 0n, + address: ContractAddress.create(866), + amount: CcdAmount.zero(), contractVersion: 1, events: [], instigator: { - address: '4inf4g36xDEQmjxDbbkqeHD2HNg9v7dohXUDH5S9en4Th53kxm', + address: AccountAddress.fromBase58( + '4inf4g36xDEQmjxDbbkqeHD2HNg9v7dohXUDH5S9en4Th53kxm' + ), type: 'AddressAccount', }, - message: - 'c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be0008300e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be000830000', - receiveName: 'CIS2-wCCD-Proxy.unwrap', - tag: 'Updated', + message: Parameter.fromHexString( + 'c0843d00e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be0008300e9f89f76878691716298685f21637d86fd8c98de7baa1d67e0ce11241be000830000' + ), + receiveName: ReceiveName.fromStringUnchecked('CIS2-wCCD-Proxy.unwrap'), + tag: TransactionEventTag.Updated, }, ]; -export const encryptedSelfAmountAddedEvent = { - account: '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj', - amount: 10000000n, +export const encryptedSelfAmountAddedEvent: EncryptedSelfAmountAddedEvent = { + account: AccountAddress.fromBase58( + '3BpVX13dw29JruyMzCfde96hoB7DtQ53WMGVDMrmPtuYAbzADj' + ), + amount: CcdAmount.fromMicroCcd(10000000n), newAmount: 'c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000098c71824023d5fb1bca5accb3ac010551e4af7e9988cd0ef309ee37149ef7843af6f294e79b8fcbda9b4f4ed094d66cbc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', - tag: 'EncryptedSelfAmountAdded', + tag: TransactionEventTag.EncryptedSelfAmountAdded, }; -export const updateEnqueuedEvent = { - type: 'updateTransaction', +export const updateEnqueuedEvent: UpdateSummary = { + type: TransactionSummaryType.UpdateTransaction, index: 0n, - energyCost: 0n, - hash: 'f296a32603fc14aa679001c49a4db1b2133787ae37743536938ec51382fb8392', + energyCost: Energy.create(0), + hash: TransactionHash.fromHexString( + 'f296a32603fc14aa679001c49a4db1b2133787ae37743536938ec51382fb8392' + ), effectiveTime: 1669115100n, payload: { - updateType: 'protocol', + updateType: UpdateType.Protocol, update: { message: 'Enable protocol version 5', specificationHash: @@ -861,275 +1041,356 @@ export const updateEnqueuedEvent = { }, }, }; -export const transferWithScheduleEvent = { - tag: 'TransferredWithSchedule', - to: '3ySdbNTPogmvUBD5g42FaZqYht78jQZ2jose9yZFkCj8zyCGWt', +export const transferWithScheduleEvent: TransferredWithScheduleEvent = { + tag: TransactionEventTag.TransferredWithSchedule, + to: AccountAddress.fromBase58( + '3ySdbNTPogmvUBD5g42FaZqYht78jQZ2jose9yZFkCj8zyCGWt' + ), amount: [ { timestamp: new Date('2023-01-10T12:00:00.919Z'), - amount: 500000n, + amount: CcdAmount.fromMicroCcd(500000n), }, { timestamp: new Date('2023-02-10T12:00:00.919Z'), - amount: 500000n, + amount: CcdAmount.fromMicroCcd(500000n), }, ], }; -export const contractInitializedEvent = { - tag: 'ContractInitialized', - address: { index: 3132n, subindex: 0n }, - amount: 0n, +export const contractInitializedEvent: ContractInitializedEvent = { + tag: TransactionEventTag.ContractInitialized, + address: ContractAddress.create(3132), + amount: CcdAmount.zero(), contractVersion: 1, events: [], - initName: 'init_CIS2-Fractionalizer', + initName: InitName.fromStringUnchecked('init_CIS2-Fractionalizer'), ref: 'e80161061e5074e850dd1fabbabbf80008fc5d3ffae554744aedc4704ee7b412', }; -export const moduleDeployedEvent = { - tag: 'ModuleDeployed', +export const moduleDeployedEvent: ModuleDeployedEvent = { + tag: TransactionEventTag.ModuleDeployed, contents: '3c532ed32dcb3b9f49afb442457a63465987994e400fd5023c8471c26a858ab4', }; -export const delegationRemovedEvent = { - tag: 'DelegationRemoved', - account: '4nvFUvdF3Ki7M6Xc2vHejX7iQW5Gtu7UBu6RaPRZV7LorLToPG', - delegatorId: 4002, +export const delegationRemovedEvent: DelegationEvent = { + tag: TransactionEventTag.DelegationRemoved, + account: AccountAddress.fromBase58( + '4nvFUvdF3Ki7M6Xc2vHejX7iQW5Gtu7UBu6RaPRZV7LorLToPG' + ), + delegatorId: 4002n, }; -export const transferWithMemoSummary = { +export const transferWithMemoSummary: BaseAccountTransactionSummary & + TransferWithMemoSummary = { index: 0n, - energyCost: 508n, - hash: '8bfd6c5d3006ea005531d90e88af1075c1a5d0bce1f2befa7abb3ec8b3fb60b5', - type: 'accountTransaction', + energyCost: Energy.create(508), + hash: TransactionHash.fromHexString( + '8bfd6c5d3006ea005531d90e88af1075c1a5d0bce1f2befa7abb3ec8b3fb60b5' + ), + type: TransactionSummaryType.AccountTransaction, cost: 879395n, - sender: '4nJU5pCM49KmrYQ1tsUTEBNBJVxs3X2qo8nKj8CQYsgBmUACHG', - transactionType: 'transferWithMemo', + sender: AccountAddress.fromBase58( + '4nJU5pCM49KmrYQ1tsUTEBNBJVxs3X2qo8nKj8CQYsgBmUACHG' + ), + transactionType: TransactionKindString.TransferWithMemo, transfer: { - tag: 'Transferred', - amount: 250000000n, - to: '4fxkFceRT3XyUpb4yW3C2c9RnEBhunyNrKprYarr7htKmMvztG', + tag: TransactionEventTag.Transferred, + amount: CcdAmount.fromMicroCcd(250000000n), + to: AccountAddress.fromBase58( + '4fxkFceRT3XyUpb4yW3C2c9RnEBhunyNrKprYarr7htKmMvztG' + ), }, - memo: { tag: 'TransferMemo', memo: '6474657374' }, + memo: { tag: TransactionEventTag.TransferMemo, memo: '6474657374' }, }; -export const upgradedEvent = { - address: { index: 3143n, subindex: 0n }, +export const upgradedEvent: ContractTraceEvent = { + address: ContractAddress.create(3143), from: '7371d1039a0e4587a54b8959eaabf11da83fad24650ee6af380357849648f477', - tag: 'Upgraded', + tag: TransactionEventTag.Upgraded, to: '7371d1039a0e4587a54b8959eaabf11da83fad24650ee6af380357849648f477', }; -export const dataRegisteredEvent = { +export const dataRegisteredEvent: DataRegisteredEvent = { data: '6b68656c6c6f20776f726c64', - tag: 'DataRegistered', + tag: TransactionEventTag.DataRegistered, }; -export const newEncryptedAmountEvent = { - account: '2za2yAXbFiaB151oYqTteZfqiBzibHXizwjNbpdU8hodq9SfEk', +export const newEncryptedAmountEvent: NewEncryptedAmountEvent = { + account: AccountAddress.fromBase58( + '2za2yAXbFiaB151oYqTteZfqiBzibHXizwjNbpdU8hodq9SfEk' + ), encryptedAmount: '8695a917b4404bfa7cb787297662f610f08758f73bc73028a0ec004626b28e28bb82a69e86b9b985e36c588ff2b36089ab40ecae0a199c53f088e6c75012c1c116600dbd22dc33285a22ad63b0a99e5b8b6bad012d1d88568eaddcbac8bf03938762267b06a3353659e436cad83ac2f2b6961ccbf4a77cffaa20757f69f2ef3a2d2c7e9a4bf7c7373e50fbd5da02c46c9565146ac5b56c1a9eb7ae0b9614ed9475e26d4cfc2cb03014f70a4ba82f1aae131b735eec2dcc5ddafe5fac1ab0dbf4', newIndex: 1, - tag: 'NewEncryptedAmount', + tag: TransactionEventTag.NewEncryptedAmount, }; -export const bakerKeysUpdatedEvent = { - account: '4Kmo9keJQaiyAuRM2pRh2xK4e75ph7hp4CzxdFAcRDeQRHfaHT', +export const bakerKeysUpdatedEvent: BakerEvent = { + account: AccountAddress.fromBase58( + '4Kmo9keJQaiyAuRM2pRh2xK4e75ph7hp4CzxdFAcRDeQRHfaHT' + ), aggregationKey: '8cf3c6fe9bebc45e9c9bb34442c5baf7bb612adc193525173d6fe36355be29ad69affeb3937f2b819976ecefeb14c3ae04d9c44d0117eda8c7968602f08f266960226c5fe2014c1bda1794b7fdbd5b5f9d31deb2c053d5f9f3734452e1dcb4b8', bakerId: 15n, electionKey: 'b40185d794485eeb099bdfb7df58fc64fd303847f2a947884648e535b023fe23', signKey: '5cbc1c8ab56047360ff37229760302e03844d48299f4fb1f1247832778f980c0', - tag: 'BakerKeysUpdated', + tag: TransactionEventTag.BakerKeysUpdated, }; -export const bakerStakeIncreasedEvent = { - account: '4JzAXhzJKwG3DGoAbgGhZNnRQqeFdp9zbxv6WUjDbVbyKEie8e', +export const bakerStakeIncreasedEvent: BakerEvent = { + account: AccountAddress.fromBase58( + '4JzAXhzJKwG3DGoAbgGhZNnRQqeFdp9zbxv6WUjDbVbyKEie8e' + ), bakerId: 525n, - newStake: 14001000000n, - tag: 'BakerStakeIncreased', + newStake: CcdAmount.fromMicroCcd(14001000000n), + tag: TransactionEventTag.BakerStakeIncreased, }; -export const credentialKeysUpdatedEvent = { +export const credentialKeysUpdatedEvent: CredentialKeysUpdatedEvent = { credId: 'a643d6082a8f80460fff27f3ff27fedbfdc60039527402b8188fc845a849428b5484c82a1589cab7604c1a2be978c39c', - tag: 'CredentialKeysUpdated', + tag: TransactionEventTag.CredentialKeysUpdated, }; -export const credentialsUpdatedEvent = { - account: '3irV7FF3BZbz9ejGTm7EHLUi6CQHdJUELDfyhwkHcLqXmQyUfR', +export const credentialsUpdatedEvent: CredentialsUpdatedEvent = { + account: AccountAddress.fromBase58( + '3irV7FF3BZbz9ejGTm7EHLUi6CQHdJUELDfyhwkHcLqXmQyUfR' + ), newCredIds: [], newThreshold: 1, removedCredIds: [], - tag: 'CredentialsUpdated', + tag: TransactionEventTag.CredentialsUpdated, }; -export const bakerStakeDecreasedEvent = { - tag: 'BakerStakeDecreased', - account: '4Kmo9keJQaiyAuRM2pRh2xK4e75ph7hp4CzxdFAcRDeQRHfaHT', +export const bakerStakeDecreasedEvent: BakerEvent = { + tag: TransactionEventTag.BakerStakeDecreased, + account: AccountAddress.fromBase58( + '4Kmo9keJQaiyAuRM2pRh2xK4e75ph7hp4CzxdFAcRDeQRHfaHT' + ), bakerId: 15n, - newStake: 950000000000n, + newStake: CcdAmount.fromMicroCcd(950000000000n), }; -export const delegationStakeDecreasedEvent = { - tag: 'DelegationStakeDecreased', - account: '4mAs6xcFw26fb6u8odkJWoe3fAK8bCJ91BwScUc36DFhh3thwD', - delegatorId: 57, - newStake: 10000000n, +export const delegationStakeDecreasedEvent: DelegationEvent = { + tag: TransactionEventTag.DelegationStakeDecreased, + account: AccountAddress.fromBase58( + '4mAs6xcFw26fb6u8odkJWoe3fAK8bCJ91BwScUc36DFhh3thwD' + ), + delegatorId: 57n, + newStake: CcdAmount.fromMicroCcd(10000000n), }; -export const mintSpecialEvent = { +export const mintSpecialEvent: BlockSpecialEvent = { tag: 'mint', - mintBakingReward: 12708081798618n, - mintFinalizationReward: 6354040899309n, - mintPlatformDevelopmentCharge: 2118013633103n, - foundationAccount: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', -}; - -export const paydayFoundationRewardSpecialEvent = { - tag: 'paydayFoundationReward', - foundationAccount: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', - developmentCharge: 103517862n, + mintBakingReward: CcdAmount.fromMicroCcd(12708081798618n), + mintFinalizationReward: CcdAmount.fromMicroCcd(6354040899309n), + mintPlatformDevelopmentCharge: CcdAmount.fromMicroCcd(2118013633103n), + foundationAccount: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), }; -export const paydayPoolRewardSpecialEvent = { +export const paydayFoundationRewardSpecialEvent: BlockSpecialEventPaydayFoundationReward = + { + tag: 'paydayFoundationReward', + foundationAccount: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), + developmentCharge: CcdAmount.fromMicroCcd(103517862n), + }; + +export const paydayPoolRewardSpecialEvent: BlockSpecialEventPaydayPoolReward = { tag: 'paydayPoolReward', - transactionFees: 0n, - bakerReward: 0n, - finalizationReward: 0n, + transactionFees: CcdAmount.zero(), + bakerReward: CcdAmount.zero(), + finalizationReward: CcdAmount.zero(), }; -export const paydayAccountRewardSpecialEvent = { - tag: 'paydayAccountReward', - account: '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn', - transactionFees: 128913265n, - bakerReward: 1139222197711n, - finalizationReward: 577640081755n, -}; - -export const blockAccrueRewardSpecialEvent = { - tag: 'blockAccrueReward', - transactionFees: 0n, - oldGasAccount: 3n, - newGasAccount: 3n, - bakerReward: 0n, - passiveReward: 0n, - foundationCharge: 0n, - baker: 6n, -}; - -export const bakingRewardsSpecialEvent = { +export const paydayAccountRewardSpecialEvent: BlockSpecialEventPaydayAccountReward = + { + tag: 'paydayAccountReward', + account: AccountAddress.fromBase58( + '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn' + ), + transactionFees: CcdAmount.fromMicroCcd(128913265n), + bakerReward: CcdAmount.fromMicroCcd(1139222197711n), + finalizationReward: CcdAmount.fromMicroCcd(577640081755n), + }; + +export const blockAccrueRewardSpecialEvent: BlockSpecialEventBlockAccrueReward = + { + tag: 'blockAccrueReward', + transactionFees: CcdAmount.zero(), + oldGasAccount: CcdAmount.fromMicroCcd(3n), + newGasAccount: CcdAmount.fromMicroCcd(3n), + bakerReward: CcdAmount.zero(), + passiveReward: CcdAmount.zero(), + foundationCharge: CcdAmount.zero(), + baker: 6n, + }; + +export const bakingRewardsSpecialEvent: BlockSpecialEventBakingRewards = { tag: 'bakingRewards', bakingRewards: [ { - account: '3QK1rxUXV7GRk4Ng7Bs7qnbkdjyBdjzCytpTrSQN7BaJkiEfgZ', - amount: 56740641000n, + account: AccountAddress.fromBase58( + '3QK1rxUXV7GRk4Ng7Bs7qnbkdjyBdjzCytpTrSQN7BaJkiEfgZ' + ), + amount: CcdAmount.fromMicroCcd(56740641000n), }, { - account: '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini', - amount: 32625868575n, + account: AccountAddress.fromBase58( + '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini' + ), + amount: CcdAmount.fromMicroCcd(32625868575n), }, { - account: '3gGBYDSpx2zWL3YMcqD48U5jVXYG4pJBDZqeY5CbMMKpxVBbc3', - amount: 39718448700n, + account: AccountAddress.fromBase58( + '3gGBYDSpx2zWL3YMcqD48U5jVXYG4pJBDZqeY5CbMMKpxVBbc3' + ), + amount: CcdAmount.fromMicroCcd(39718448700n), }, { - account: '3ntvNGT6tDuLYiSb5gMJSQAZfLPUJnzoizcFiVRWqLoctuXxpK', - amount: 41136964725n, + account: AccountAddress.fromBase58( + '3ntvNGT6tDuLYiSb5gMJSQAZfLPUJnzoizcFiVRWqLoctuXxpK' + ), + amount: CcdAmount.fromMicroCcd(41136964725n), }, { - account: '3y9DtDUL8xpf8i2yj9k44zMVkf4H1hkpBEQcXbJhrgcwYSGg41', - amount: 35462900625n, + account: AccountAddress.fromBase58( + '3y9DtDUL8xpf8i2yj9k44zMVkf4H1hkpBEQcXbJhrgcwYSGg41' + ), + amount: CcdAmount.fromMicroCcd(35462900625n), }, { - account: '42tFTDWvTmBd7hEacohuCfGFa9TsBKhsmXKeViQ7q7NoY7UadV', - amount: 49648060875n, + account: AccountAddress.fromBase58( + '42tFTDWvTmBd7hEacohuCfGFa9TsBKhsmXKeViQ7q7NoY7UadV' + ), + amount: CcdAmount.fromMicroCcd(49648060875n), }, { - account: '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg', - amount: 51066576900n, + account: AccountAddress.fromBase58( + '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg' + ), + amount: CcdAmount.fromMicroCcd(51066576900n), }, { - account: '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn', - amount: 35462900625n, + account: AccountAddress.fromBase58( + '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn' + ), + amount: CcdAmount.fromMicroCcd(35462900625n), }, { - account: '4AnukgcopMC4crxfL1L9fUYw9MAkoo1yKLvH7eA1NAX7SxgyRY', - amount: 56740641000n, + account: AccountAddress.fromBase58( + '4AnukgcopMC4crxfL1L9fUYw9MAkoo1yKLvH7eA1NAX7SxgyRY' + ), + amount: CcdAmount.fromMicroCcd(56740641000n), }, { - account: '4BTFaHx8CioLi8Xe7YiimpAK1oQMkbx5Wj6B8N7d7NXgmLvEZs', - amount: 66670253175n, + account: AccountAddress.fromBase58( + '4BTFaHx8CioLi8Xe7YiimpAK1oQMkbx5Wj6B8N7d7NXgmLvEZs' + ), + amount: CcdAmount.fromMicroCcd(66670253175n), }, { - account: '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L', - amount: 60996189075n, + account: AccountAddress.fromBase58( + '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L' + ), + amount: CcdAmount.fromMicroCcd(60996189075n), }, ], - remainder: 290n, + remainder: CcdAmount.fromMicroCcd(290n), }; -export const finalizationRewardsSpecialEvent = { - tag: 'finalizationRewards', - finalizationRewards: [ - { - account: '3QK1rxUXV7GRk4Ng7Bs7qnbkdjyBdjzCytpTrSQN7BaJkiEfgZ', - amount: 359306692n, - }, - { - account: '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini', - amount: 359306692n, - }, - { - account: '3gGBYDSpx2zWL3YMcqD48U5jVXYG4pJBDZqeY5CbMMKpxVBbc3', - amount: 359306692n, - }, - { - account: '3ntvNGT6tDuLYiSb5gMJSQAZfLPUJnzoizcFiVRWqLoctuXxpK', - amount: 359306692n, - }, - { - account: '3y9DtDUL8xpf8i2yj9k44zMVkf4H1hkpBEQcXbJhrgcwYSGg41', - amount: 359306692n, - }, - { - account: '42tFTDWvTmBd7hEacohuCfGFa9TsBKhsmXKeViQ7q7NoY7UadV', - amount: 359306692n, - }, - { - account: '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg', - amount: 359306692n, - }, - { - account: '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn', - amount: 359306692n, - }, - { - account: '4AnukgcopMC4crxfL1L9fUYw9MAkoo1yKLvH7eA1NAX7SxgyRY', - amount: 359306692n, - }, - { - account: '4BTFaHx8CioLi8Xe7YiimpAK1oQMkbx5Wj6B8N7d7NXgmLvEZs', - amount: 359306692n, - }, - { - account: '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L', - amount: 359306692n, - }, - ], - remainder: 4n, -}; +export const finalizationRewardsSpecialEvent: BlockSpecialEventFinalizationRewards = + { + tag: 'finalizationRewards', + finalizationRewards: [ + { + account: AccountAddress.fromBase58( + '3QK1rxUXV7GRk4Ng7Bs7qnbkdjyBdjzCytpTrSQN7BaJkiEfgZ' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '3gGBYDSpx2zWL3YMcqD48U5jVXYG4pJBDZqeY5CbMMKpxVBbc3' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '3ntvNGT6tDuLYiSb5gMJSQAZfLPUJnzoizcFiVRWqLoctuXxpK' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '3y9DtDUL8xpf8i2yj9k44zMVkf4H1hkpBEQcXbJhrgcwYSGg41' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '42tFTDWvTmBd7hEacohuCfGFa9TsBKhsmXKeViQ7q7NoY7UadV' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '48XGRnvQoG92T1AwETvW5pnJ1aRSPMKsWtGdKhTqyiNZzMk3Qn' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '4AnukgcopMC4crxfL1L9fUYw9MAkoo1yKLvH7eA1NAX7SxgyRY' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '4BTFaHx8CioLi8Xe7YiimpAK1oQMkbx5Wj6B8N7d7NXgmLvEZs' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + { + account: AccountAddress.fromBase58( + '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L' + ), + amount: CcdAmount.fromMicroCcd(359306692n), + }, + ], + remainder: CcdAmount.fromMicroCcd(4n), + }; -export const blockRewardSpecialEvent = { +export const blockRewardSpecialEvent: BlockSpecialEventBlockReward = { tag: 'blockReward', - transactionFees: 0n, - oldGasAccount: 0n, - newGasAccount: 0n, - bakerReward: 0n, - foundationCharge: 0n, - baker: '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg', - foundationAccount: '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg', + transactionFees: CcdAmount.zero(), + oldGasAccount: CcdAmount.zero(), + newGasAccount: CcdAmount.zero(), + bakerReward: CcdAmount.zero(), + foundationCharge: CcdAmount.zero(), + baker: AccountAddress.fromBase58( + '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg' + ), + foundationAccount: AccountAddress.fromBase58( + '44Axe5eHnMkBinX7GKvUm5w6mX83JGdasijhvsMv5ZW2Wmgphg' + ), }; export const insufficientBalanceForDelegationStakeRejectReason = { @@ -1146,14 +1407,16 @@ export const alreadyABakerRejectReason = { tag: 'AlreadyABaker', }; -export const amountTooLargeRejectReason = { - tag: 'AmountTooLarge', +export const amountTooLargeRejectReason: AmountTooLarge = { + tag: RejectReasonTag.AmountTooLarge, contents: { address: { type: 'AddressAccount', - address: '4Qod7UHWmkyz2ahPrWFH1kCqv1cvhT7NtEFbXG7G2soxXSYuMH', + address: AccountAddress.fromBase58( + '4Qod7UHWmkyz2ahPrWFH1kCqv1cvhT7NtEFbXG7G2soxXSYuMH' + ), }, - amount: 2000000000n, + amount: CcdAmount.fromMicroCcd(2000000000n), }, }; @@ -1189,34 +1452,36 @@ export const insufficientBalanceForBakerStakeRejectReason = { export const outOfEnergyRejectReason = { tag: 'OutOfEnergy' }; -export const invalidInitMethodRejectReason = { +export const invalidInitMethodRejectReason: RejectReason = { contents: { - moduleRef: - 'c2ddbce88a7acae8bd610abf46afbdcf6264c8777163b345468b8e6f2ff8660f', - initName: 'init_cis2_nft', + moduleRef: ModuleReference.fromHexString( + 'c2ddbce88a7acae8bd610abf46afbdcf6264c8777163b345468b8e6f2ff8660f' + ), + initName: InitName.fromStringUnchecked('init_cis2_nft'), }, - tag: 'InvalidInitMethod', + tag: RejectReasonTag.InvalidInitMethod, }; export const runtimeFailureRejectReason = { tag: 'RuntimeFailure' }; -export const rejectedReceiveRejectReason = { - contractAddress: { index: 2372n, subindex: 0n }, - parameter: '', - receiveName: 'auction.finalize', +export const rejectedReceiveRejectReason: RejectedReceive = { + contractAddress: ContractAddress.create(2372), + parameter: Parameter.empty(), + receiveName: ReceiveName.fromStringUnchecked('auction.finalize'), rejectReason: -1, - tag: 'RejectedReceive', + tag: RejectReasonTag.RejectedReceive, }; export const poolClosedRejectReason = { tag: 'PoolClosed' }; -export const invalidReceiveMethodRejectReason = { +export const invalidReceiveMethodRejectReason: RejectReason = { contents: { - moduleRef: - '4065819567755d7d2269acce2a8b0b510cdf30e36f5fb3303be16f8724e9b8b7', - receiveName: 'CIS2-TOKEN.mint', + moduleRef: ModuleReference.fromHexString( + '4065819567755d7d2269acce2a8b0b510cdf30e36f5fb3303be16f8724e9b8b7' + ), + receiveName: ReceiveName.fromStringUnchecked('CIS2-TOKEN.mint'), }, - tag: 'InvalidReceiveMethod', + tag: RejectReasonTag.InvalidReceiveMethod, }; export const transactionFeeCommissionNotInRangeRejectReason = { @@ -1259,9 +1524,9 @@ export const firstScheduledReleaseExpiredRejectReason = { tag: 'FirstScheduledReleaseExpired', }; -export const invalidContractAddressRejectReason = { - contents: { index: 2339n, subindex: 0n }, - tag: 'InvalidContractAddress', +export const invalidContractAddressRejectReason: InvalidContractAddress = { + contents: ContractAddress.create(2339), + tag: RejectReasonTag.InvalidContractAddress, }; export const missingBakerAddParametersRejectReason = { @@ -1275,10 +1540,11 @@ export const invalidTransferToPublicProofRejectReason = { export const poolWouldBecomeOverDelegatedRejectReason = { tag: 'PoolWouldBecomeOverDelegated', }; -export const bakerAccountInfo = { - accountNonce: 1n, - accountAmount: 7449646704751788n, - accountReleaseSchedule: { total: 0n, schedule: [] }, +export const bakerAccountInfo: AccountInfoBaker = { + type: AccountInfoType.Baker, + accountNonce: SequenceNumber.create(1), + accountAmount: CcdAmount.fromMicroCcd(7449646704751788n), + accountReleaseSchedule: { total: CcdAmount.zero(), schedule: [] }, accountCredentials: { '0': { v: 0, @@ -1291,6 +1557,8 @@ export const bakerAccountInfo = { }, }, commitments: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore cmmAttributes: {}, cmmCredCounter: '80e04147024fd25dcab36e535f990d7678fc22d95d3c8b1456e48b8b208289cc00aae180e718d3800d9efb196dccfa7a', @@ -1325,6 +1593,8 @@ export const bakerAccountInfo = { ipIdentity: 0, policy: { createdAt: '202206', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore revealedAttributes: {}, validTo: '202306', }, @@ -1344,8 +1614,11 @@ export const bakerAccountInfo = { accountEncryptionKey: 'b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5a33426f86d431f2312b19213ced9df3cc97da9111a312aa1abdecef9327388d4936bc61ef6b1f7f0064b6dfc0630bbed', accountIndex: 5n, - accountAddress: '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L', + accountAddress: AccountAddress.fromBase58( + '4EJJ1hVhbVZT2sR9xPzWUwFcJWK3fPX54z94zskTozFVk8Xd4L' + ), accountBaker: { + version: 1, bakerAggregationVerifyKey: 'b18a02de74826e55f6eadc0f31d0d9a6edfb2993d030e65136f1e1256a69ba523acb40fa4d304d0668aa307c19257a0a10726e70149c904e1ef29aedb2679c825997e3f14edd303bf276f2c0b0c5a4c4870fff0c043150be06b715466be564c4', bakerElectionVerifyKey: @@ -1358,19 +1631,20 @@ export const bakerAccountInfo = { transactionCommission: 0.1, }, metadataUrl: '', - openStatus: 'closedForAll', + openStatus: OpenStatusText.ClosedForAll, }, bakerSignatureVerifyKey: 'c385ccb5c8a0710a162f2c107123744650ff35f00040bfa262d974bfb3c3f8f1', restakeEarnings: true, - stakedAmount: 7349646704751788n, + stakedAmount: CcdAmount.fromMicroCcd(7349646704751788n), }, }; -export const delegatorAccountInfo = { - accountNonce: 11n, - accountAmount: 620948501142n, - accountReleaseSchedule: { total: 0n, schedule: [] }, +export const delegatorAccountInfo: AccountInfoDelegator = { + type: AccountInfoType.Delegator, + accountNonce: SequenceNumber.create(11), + accountAmount: CcdAmount.fromMicroCcd(620948501142n), + accountReleaseSchedule: { total: CcdAmount.zero(), schedule: [] }, accountCredentials: { '0': { v: 0, @@ -1441,6 +1715,8 @@ export const delegatorAccountInfo = { ipIdentity: 0, policy: { createdAt: '202206', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore revealedAttributes: {}, validTo: '202306', }, @@ -1460,18 +1736,23 @@ export const delegatorAccountInfo = { accountEncryptionKey: 'b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5af9816eae1ab8c733e26e790a7d88c5688472dcd8b536668b0d2182e406321499695ae80c8363505a479ffd89daf5fbb', accountIndex: 276n, - accountAddress: '3bFo43GiPnkk5MmaSdsRVboaX2DNSKaRkLseQbyB3WPW1osPwh', + accountAddress: AccountAddress.fromBase58( + '3bFo43GiPnkk5MmaSdsRVboaX2DNSKaRkLseQbyB3WPW1osPwh' + ), accountDelegation: { - delegationTarget: { delegateType: 'Passive' }, + delegationTarget: { + delegateType: DelegationTargetType.PassiveDelegation, + }, restakeEarnings: true, - stakedAmount: 620942412516n, + stakedAmount: CcdAmount.fromMicroCcd(620942412516n), }, }; -export const credIdAccountInfo = { - accountNonce: 19n, - accountAmount: 35495453082577742n, - accountReleaseSchedule: { total: 0n, schedule: [] }, +export const credIdAccountInfo: AccountInfoSimple = { + type: AccountInfoType.Simple, + accountNonce: SequenceNumber.create(19), + accountAmount: CcdAmount.fromMicroCcd(35495453082577742n), + accountReleaseSchedule: { total: CcdAmount.zero(), schedule: [] }, accountCredentials: { '0': { v: 0, @@ -1484,6 +1765,8 @@ export const credIdAccountInfo = { }, }, commitments: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore cmmAttributes: {}, cmmCredCounter: 'b0aa65f420a9f3fab61d3f875eebcc221e43154bfaf1b6365dace99bff20778de7003437631222e845fd9917c8d2874b', @@ -1518,6 +1801,8 @@ export const credIdAccountInfo = { ipIdentity: 0, policy: { createdAt: '202206', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore revealedAttributes: {}, validTo: '202306', }, @@ -1537,13 +1822,16 @@ export const credIdAccountInfo = { accountEncryptionKey: 'b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5aa730045bcd20bb5c24349db29d949f767e72f7cce459dc163c4b93c780a7d7f65801dda8ff7e4fc06fdf1a1b246276f', accountIndex: 11n, - accountAddress: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', + accountAddress: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), }; -export const regularAccountInfo = { - accountNonce: 19n, - accountAmount: 35495453082577742n, - accountReleaseSchedule: { total: 0n, schedule: [] }, +export const regularAccountInfo: AccountInfoSimple = { + type: AccountInfoType.Simple, + accountNonce: SequenceNumber.create(19), + accountAmount: CcdAmount.fromMicroCcd(35495453082577742n), + accountReleaseSchedule: { total: CcdAmount.zero(), schedule: [] }, accountCredentials: { '0': { v: 0, @@ -1556,6 +1844,8 @@ export const regularAccountInfo = { }, }, commitments: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore cmmAttributes: {}, cmmCredCounter: 'b0aa65f420a9f3fab61d3f875eebcc221e43154bfaf1b6365dace99bff20778de7003437631222e845fd9917c8d2874b', @@ -1590,6 +1880,8 @@ export const regularAccountInfo = { ipIdentity: 0, policy: { createdAt: '202206', + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore revealedAttributes: {}, validTo: '202306', }, @@ -1609,7 +1901,9 @@ export const regularAccountInfo = { accountEncryptionKey: 'b14cbfe44a02c6b1f78711176d5f437295367aa4f2a8c2551ee10d25a03adc69d61a332a058971919dad7312e1fc94c5aa730045bcd20bb5c24349db29d949f767e72f7cce459dc163c4b93c780a7d7f65801dda8ff7e4fc06fdf1a1b246276f', accountIndex: 11n, - accountAddress: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', + accountAddress: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), }; const rootKeys = { @@ -1735,6 +2029,7 @@ const level1Keys = { }; export const chainParameters: ChainParametersV1 = { + version: 1, electionDifficulty: 0.025, euroPerEnergy: { numerator: 1n, denominator: 50000n }, microGTUPerEuro: { @@ -1742,7 +2037,9 @@ export const chainParameters: ChainParametersV1 = { denominator: 7989497115n, }, accountCreationLimit: 10, - foundationAccount: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', + foundationAccount: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), mintPerPayday: 0.000261157877, rewardPeriodLength: 24n, delegatorCooldown: 1209600n, @@ -1753,21 +2050,28 @@ export const chainParameters: ChainParametersV1 = { finalizationCommissionRange: { min: 1, max: 1 }, bakingCommissionRange: { min: 0.1, max: 0.1 }, transactionCommissionRange: { min: 0.1, max: 0.1 }, - minimumEquityCapital: 14000000000n, + minimumEquityCapital: CcdAmount.fromMicroCcd(14000000000n), capitalBound: 0.1, leverageBound: { numerator: 3n, denominator: 1n }, rewardParameters: { + version: 1, transactionFeeDistribution: { baker: 0.45, gasAccount: 0.45 }, gASRewards: { + version: 0, baker: 0.25, finalizationProof: 0.005, accountCreation: 0.02, chainUpdate: 0.005, }, - mintDistribution: { bakingReward: 0.6, finalizationReward: 0.3 }, + mintDistribution: { + version: 1, + bakingReward: 0.6, + finalizationReward: 0.3, + }, }, level1Keys, level2Keys: { + version: 1, addAnonymityRevoker: { authorizedKeys: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], threshold: 7, @@ -1905,45 +2209,53 @@ export const chainParameters: ChainParametersV1 = { rootKeys, }; -const { cooldownParameters, timeParameters, ...oldLevel2Keys } = +const { cooldownParameters, timeParameters, version, ...oldLevel2Keys } = chainParameters.level2Keys; export const oldChainParameters: ChainParametersV0 = { + version: 0, electionDifficulty: 0.025, euroPerEnergy: { numerator: 1n, denominator: 50000n }, microGTUPerEuro: { numerator: 50000000n, denominator: 1n }, accountCreationLimit: 10, - foundationAccount: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', + foundationAccount: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), bakerCooldownEpochs: 166n, - minimumThresholdForBaking: 15000000000n, + minimumThresholdForBaking: CcdAmount.fromMicroCcd(15000000000n), rewardParameters: { + version: 0, transactionFeeDistribution: { baker: 0.45, gasAccount: 0.45 }, gASRewards: { + version: 0, baker: 0.25, finalizationProof: 0.005, accountCreation: 0.02, chainUpdate: 0.005, }, mintDistribution: { + version: 0, bakingReward: 0.6, finalizationReward: 0.3, mintPerSlot: 7.555665e-10, }, }, level1Keys, - level2Keys: oldLevel2Keys, + level2Keys: { version: 0, ...oldLevel2Keys }, rootKeys, }; -export const bakerPoolStatus = { - poolType: 'BakerPool', +export const bakerPoolStatus: BakerPoolStatus = { + poolType: PoolStatusType.BakerPool, bakerId: 1n, - bakerAddress: '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini', - bakerEquityCapital: 7347853372468927n, - delegatedCapital: 0n, - delegatedCapitalCap: 0n, + bakerAddress: AccountAddress.fromBase58( + '3U4sfVSqGG6XK8g6eho2qRYtnHc4MWJBG1dfxdtPGbfHwFxini' + ), + bakerEquityCapital: CcdAmount.fromMicroCcd(7347853372468927n), + delegatedCapital: CcdAmount.zero(), + delegatedCapitalCap: CcdAmount.zero(), poolInfo: { - openStatus: 'openForAll', + openStatus: OpenStatusText.OpenForAll, metadataUrl: '', commissionRates: { transactionCommission: 0.1, @@ -1951,46 +2263,50 @@ export const bakerPoolStatus = { finalizationCommission: 1, }, }, - bakerStakePendingChange: { pendingChangeType: 'NoChange' }, + bakerStakePendingChange: { + pendingChangeType: BakerPoolPendingChangeType.NoChange, + }, currentPaydayStatus: { blocksBaked: 1329n, finalizationLive: true, - transactionFeesEarned: 105169976n, - effectiveStake: 4605214437901336n, + transactionFeesEarned: CcdAmount.fromMicroCcd(105169976n), + effectiveStake: CcdAmount.fromMicroCcd(4605214437901336n), lotteryPower: 0.15552531374613243, - bakerEquityCapital: 7344771840225046n, - delegatedCapital: 0n, + bakerEquityCapital: CcdAmount.fromMicroCcd(7344771840225046n), + delegatedCapital: CcdAmount.zero(), commissionRates: { bakingCommission: 0.1, finalizationCommission: 1, transactionCommission: 0.1, }, }, - allPoolTotalCapital: 46071942529284135n, + allPoolTotalCapital: CcdAmount.fromMicroCcd(46071942529284135n), }; -export const passiveDelegationStatus = { - poolType: 'PassiveDelegation', - delegatedCapital: 698892529615n, +export const passiveDelegationStatus: PassiveDelegationStatus = { + poolType: PoolStatusType.PassiveDelegation, + delegatedCapital: CcdAmount.fromMicroCcd(698892529615n), commissionRates: { transactionCommission: 0.12, bakingCommission: 0.12, finalizationCommission: 1, }, - currentPaydayTransactionFeesEarned: 24070n, - currentPaydayDelegatedCapital: 698618484955n, - allPoolTotalCapital: 46071942529284135n, + currentPaydayTransactionFeesEarned: CcdAmount.fromMicroCcd(24070n), + currentPaydayDelegatedCapital: CcdAmount.fromMicroCcd(698618484955n), + allPoolTotalCapital: CcdAmount.fromMicroCcd(46071942529284135n), }; -export const bakerPoolStatusWithPendingChange = { - poolType: 'BakerPool', +export const bakerPoolStatusWithPendingChange: BakerPoolStatus = { + poolType: PoolStatusType.BakerPool, bakerId: 1879n, - bakerAddress: '4aCoaW3qkQRnY3fUGThQcEMGSPLUEQ7XL9Yagx2UR91QpvtoAe', - bakerEquityCapital: 19999999999n, - delegatedCapital: 0n, - delegatedCapitalCap: 39999999998n, + bakerAddress: AccountAddress.fromBase58( + '4aCoaW3qkQRnY3fUGThQcEMGSPLUEQ7XL9Yagx2UR91QpvtoAe' + ), + bakerEquityCapital: CcdAmount.fromMicroCcd(19999999999n), + delegatedCapital: CcdAmount.zero(), + delegatedCapitalCap: CcdAmount.fromMicroCcd(39999999998n), poolInfo: { - openStatus: 'openForAll', + openStatus: OpenStatusText.OpenForAll, metadataUrl: 'b', commissionRates: { transactionCommission: 0.1, @@ -1999,37 +2315,67 @@ export const bakerPoolStatusWithPendingChange = { }, }, bakerStakePendingChange: { - pendingChangeType: 'RemovePool', + pendingChangeType: BakerPoolPendingChangeType.RemovePool, effectiveTime: new Date('2022-12-08T07:54:00.000Z'), }, currentPaydayStatus: null, - allPoolTotalCapital: 46470271917743628n, + allPoolTotalCapital: CcdAmount.fromMicroCcd(46470271917743628n), }; -export const invokeContractResult = { +export const invokeContractResult: InvokeContractResult = { tag: 'success', - usedEnergy: 502n, - returnValue: '000f17697d00000000', + usedEnergy: Energy.create(502), + returnValue: ReturnValue.fromHexString('000f17697d00000000'), events: [ { - tag: 'Updated', + tag: TransactionEventTag.Updated, contractVersion: 1, - address: { index: 81n, subindex: 0n }, + address: ContractAddress.create(81), instigator: { type: 'AddressAccount', - address: '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G', + address: AccountAddress.fromBase58( + '3kBx2h5Y2veb4hZgAJWPrr8RyQESKm5TjzF3ti1QQ4VSYLwK1G' + ), }, - amount: 0n, - message: '', - receiveName: 'PiggyBank.view', + amount: CcdAmount.zero(), + message: Parameter.empty(), + receiveName: ReceiveName.fromStringUnchecked('PiggyBank.view'), events: [], }, ], }; -export const blocksAtHeight = [ +export const blocksAtHeight: BlockHash.Type[] = [ '99ceb0dfcd36714d9c141fde08e85da1d0d624994e95b35114f14193c811b76e', -]; +].map(BlockHash.fromHexString); + +export const blockInfo: BlockInfoV0 = { + version: 0, + blockParent: BlockHash.fromHexString( + '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' + ), + blockHash: BlockHash.fromHexString( + 'fe88ff35454079c3df11d8ae13d5777babd61f28be58494efe51b6593e30716e' + ), + blockStateHash: + '6e602157d76677fc4b630b2701571d2b0166e2b08e0afe8ab92356e4d0b88a6a', + blockLastFinalized: BlockHash.fromHexString( + '28d92ec42dbda119f0b0207d3400b0573fe8baf4b0d3dbe44b86781ad6b655cf' + ), + blockHeight: 1259179n, + blockBaker: 4n, + blockSlot: 50801674n, + blockArriveTime: new Date('2022-11-07T10:54:10.899Z'), + blockReceiveTime: new Date('2022-11-07T10:54:10.892Z'), + blockSlotTime: new Date('2022-11-07T10:54:10.750Z'), + finalized: true, + transactionCount: 0n, + transactionsSize: 0n, + transactionEnergyCost: Energy.create(0), + genesisIndex: 1, + eraBlockHeight: 1258806, + protocolVersion: 4n, +}; export const bakers = [ 1n, diff --git a/packages/nodejs/test/resources/ipVerifyKeys.ts b/packages/sdk/test/client/resources/ipVerifyKeys.ts similarity index 100% rename from packages/nodejs/test/resources/ipVerifyKeys.ts rename to packages/sdk/test/client/resources/ipVerifyKeys.ts diff --git a/packages/nodejs/test/resources/mobileWalletExport.json b/packages/sdk/test/client/resources/mobileWalletExport.json similarity index 100% rename from packages/nodejs/test/resources/mobileWalletExport.json rename to packages/sdk/test/client/resources/mobileWalletExport.json diff --git a/packages/nodejs/test/resources/piggy_bank.wasm b/packages/sdk/test/client/resources/piggy_bank.wasm similarity index 100% rename from packages/nodejs/test/resources/piggy_bank.wasm rename to packages/sdk/test/client/resources/piggy_bank.wasm diff --git a/packages/nodejs/test/resources/schemaFiles/schema11.bin b/packages/sdk/test/client/resources/schemaFiles/schema11.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema11.bin rename to packages/sdk/test/client/resources/schemaFiles/schema11.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema12.bin b/packages/sdk/test/client/resources/schemaFiles/schema12.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema12.bin rename to packages/sdk/test/client/resources/schemaFiles/schema12.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema14.bin b/packages/sdk/test/client/resources/schemaFiles/schema14.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema14.bin rename to packages/sdk/test/client/resources/schemaFiles/schema14.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema15.bin b/packages/sdk/test/client/resources/schemaFiles/schema15.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema15.bin rename to packages/sdk/test/client/resources/schemaFiles/schema15.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema16.bin b/packages/sdk/test/client/resources/schemaFiles/schema16.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema16.bin rename to packages/sdk/test/client/resources/schemaFiles/schema16.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema17.bin b/packages/sdk/test/client/resources/schemaFiles/schema17.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema17.bin rename to packages/sdk/test/client/resources/schemaFiles/schema17.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema18.bin b/packages/sdk/test/client/resources/schemaFiles/schema18.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema18.bin rename to packages/sdk/test/client/resources/schemaFiles/schema18.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema21.bin b/packages/sdk/test/client/resources/schemaFiles/schema21.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema21.bin rename to packages/sdk/test/client/resources/schemaFiles/schema21.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema30.bin b/packages/sdk/test/client/resources/schemaFiles/schema30.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema30.bin rename to packages/sdk/test/client/resources/schemaFiles/schema30.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema31.bin b/packages/sdk/test/client/resources/schemaFiles/schema31.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema31.bin rename to packages/sdk/test/client/resources/schemaFiles/schema31.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema32.bin b/packages/sdk/test/client/resources/schemaFiles/schema32.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema32.bin rename to packages/sdk/test/client/resources/schemaFiles/schema32.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema33.bin b/packages/sdk/test/client/resources/schemaFiles/schema33.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema33.bin rename to packages/sdk/test/client/resources/schemaFiles/schema33.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema34.bin b/packages/sdk/test/client/resources/schemaFiles/schema34.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema34.bin rename to packages/sdk/test/client/resources/schemaFiles/schema34.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema35.bin b/packages/sdk/test/client/resources/schemaFiles/schema35.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema35.bin rename to packages/sdk/test/client/resources/schemaFiles/schema35.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema6.bin b/packages/sdk/test/client/resources/schemaFiles/schema6.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema6.bin rename to packages/sdk/test/client/resources/schemaFiles/schema6.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema8.bin b/packages/sdk/test/client/resources/schemaFiles/schema8.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema8.bin rename to packages/sdk/test/client/resources/schemaFiles/schema8.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema_test.bin b/packages/sdk/test/client/resources/schemaFiles/schema_test.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema_test.bin rename to packages/sdk/test/client/resources/schemaFiles/schema_test.bin diff --git a/packages/nodejs/test/resources/schemaFiles/schema_two_step_transfer.bin b/packages/sdk/test/client/resources/schemaFiles/schema_two_step_transfer.bin similarity index 100% rename from packages/nodejs/test/resources/schemaFiles/schema_two_step_transfer.bin rename to packages/sdk/test/client/resources/schemaFiles/schema_two_step_transfer.bin diff --git a/packages/nodejs/test/resources/smartcontracts/CAddress/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/CAddress/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/CAddress/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/CAddress/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/ComplexEnum1/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/ComplexEnum1/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/ComplexEnum1/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/ComplexEnum1/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/ComplexEnum2/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/ComplexEnum2/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/ComplexEnum2/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/ComplexEnum2/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/ContractForDuration/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/ContractForDuration/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/ContractForDuration/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/ContractForDuration/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/ContractForTime/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/ContractForTime/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/ContractForTime/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/ContractForTime/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/INDBank/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/INDBank/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/INDBank/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/INDBank/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/INDBankBool1/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/INDBankBool1/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/INDBankBool1/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/INDBankBool1/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/INDBankStringArray/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/INDBankStringArray/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/INDBankStringArray/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/INDBankStringArray/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/INDBankU83/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/INDBankU83/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/INDBankU83/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/INDBankU83/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/ListContract/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/ListContract/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/ListContract/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/ListContract/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/MarkSheet/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/MarkSheet/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/MarkSheet/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/MarkSheet/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/SampleContract1/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/SampleContract1/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/SampleContract1/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/SampleContract1/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/SimpleEnum/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/SimpleEnum/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/SimpleEnum/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/SimpleEnum/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/SimpleU8/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/SimpleU8/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/SimpleU8/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/SimpleU8/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/UserAddress/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/UserAddress/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/UserAddress/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/UserAddress/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/UserAmount/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/UserAmount/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/UserAmount/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/UserAmount/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/UserDetails/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/UserDetails/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/UserDetails/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/UserDetails/src/lib.rs diff --git a/packages/nodejs/test/resources/smartcontracts/schema-test/src/lib.rs b/packages/sdk/test/client/resources/smartcontracts/schema-test/src/lib.rs similarity index 100% rename from packages/nodejs/test/resources/smartcontracts/schema-test/src/lib.rs rename to packages/sdk/test/client/resources/smartcontracts/schema-test/src/lib.rs diff --git a/packages/nodejs/test/resources/two-step-transfer-schema.bin b/packages/sdk/test/client/resources/two-step-transfer-schema.bin similarity index 100% rename from packages/nodejs/test/resources/two-step-transfer-schema.bin rename to packages/sdk/test/client/resources/two-step-transfer-schema.bin diff --git a/packages/nodejs/test/specialEvents.test.ts b/packages/sdk/test/client/specialEvents.test.ts similarity index 77% rename from packages/nodejs/test/specialEvents.test.ts rename to packages/sdk/test/client/specialEvents.test.ts index 206580aa7..b0c5baa76 100644 --- a/packages/nodejs/test/specialEvents.test.ts +++ b/packages/sdk/test/client/specialEvents.test.ts @@ -1,12 +1,13 @@ -import * as expected from './resources/expectedJsons'; -import { streamToList } from '@concordium/common-sdk'; -import { getNodeClientV2 as getNodeClient } from './testHelpers'; +import { streamToList, BlockHash } from '../../src/index.js'; +import * as expected from './resources/expectedJsons.js'; +import { getNodeClientV2 as getNodeClient } from './testHelpers.js'; const client = getNodeClient(); test('mint', async () => { - const blockHash = - '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a'; + const blockHash = BlockHash.fromHexString( + '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); @@ -14,8 +15,9 @@ test('mint', async () => { }); test('paydayFoundationReward', async () => { - const blockHash = - '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a'; + const blockHash = BlockHash.fromHexString( + '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); @@ -23,8 +25,9 @@ test('paydayFoundationReward', async () => { }); test('paydayPoolReward', async () => { - const blockHash = - '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a'; + const blockHash = BlockHash.fromHexString( + '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); @@ -32,8 +35,9 @@ test('paydayPoolReward', async () => { }); test('paydayAccountReward', async () => { - const blockHash = - '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a'; + const blockHash = BlockHash.fromHexString( + '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); @@ -41,8 +45,9 @@ test('paydayAccountReward', async () => { }); test('blockAccrueReward', async () => { - const blockHash = - '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a'; + const blockHash = BlockHash.fromHexString( + '4031d210b35a3fb9f13d1ce6e5c621abd9a26a2de54b71fc19bfb55fe17cce6a' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); @@ -50,8 +55,9 @@ test('blockAccrueReward', async () => { }); test('bakingRewards', async () => { - const blockHash = - 'da7a5401049c8ee0de0b6c66ab4f6167ef770b332df9dd9979ec2c553d1a18dd'; + const blockHash = BlockHash.fromHexString( + 'da7a5401049c8ee0de0b6c66ab4f6167ef770b332df9dd9979ec2c553d1a18dd' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); @@ -59,8 +65,9 @@ test('bakingRewards', async () => { }); test('finalizationRewards', async () => { - const blockHash = - 'da7a5401049c8ee0de0b6c66ab4f6167ef770b332df9dd9979ec2c553d1a18dd'; + const blockHash = BlockHash.fromHexString( + 'da7a5401049c8ee0de0b6c66ab4f6167ef770b332df9dd9979ec2c553d1a18dd' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); @@ -68,8 +75,9 @@ test('finalizationRewards', async () => { }); test('blockReward', async () => { - const blockHash = - 'da7a5401049c8ee0de0b6c66ab4f6167ef770b332df9dd9979ec2c553d1a18dd'; + const blockHash = BlockHash.fromHexString( + 'da7a5401049c8ee0de0b6c66ab4f6167ef770b332df9dd9979ec2c553d1a18dd' + ); const eventStream = client.getBlockSpecialEvents(blockHash); const events = await streamToList(eventStream); diff --git a/packages/nodejs/test/testHelpers.ts b/packages/sdk/test/client/testHelpers.ts similarity index 55% rename from packages/nodejs/test/testHelpers.ts rename to packages/sdk/test/client/testHelpers.ts index f1f2fa25c..b45b2339c 100644 --- a/packages/nodejs/test/testHelpers.ts +++ b/packages/sdk/test/client/testHelpers.ts @@ -1,41 +1,22 @@ /* eslint-disable import/no-extraneous-dependencies */ import * as fs from 'fs'; -import { credentials, Metadata } from '@grpc/grpc-js/'; -import { ConcordiumGRPCClient, IdentityInput } from '@concordium/common-sdk'; -import { decryptMobileWalletExport, EncryptedData } from '../src/wallet/crypto'; -import { MobileWalletExport } from '../src/wallet/types'; -import { createConcordiumClient } from '../src/clientV2'; -import ConcordiumNodeClient from '../src/client'; -import { GrpcWebFetchTransport } from '@protobuf-ts/grpcweb-transport'; +import { credentials } from '@grpc/grpc-js'; +import { + IdentityInput, + ConcordiumGRPCClient, + ConcordiumGRPCWebClient, +} from '../../src/index.js'; +import { + decryptMobileWalletExport, + EncryptedData, + MobileWalletExport, +} from '../../src/nodejs/index.js'; -// This makes sure the necessary types are added to `globalThis` -import 'isomorphic-fetch'; - -export { getModuleBuffer } from '../src/util'; +import { ConcordiumGRPCNodeClient } from '../../src/nodejs/grpc.js'; const TESTNET_NODE = 'node.testnet.concordium.com'; -const GRPCV1_PORT = 10000; const GRPCV2_PORT = 20000; -/** - * Creates a gRPC v1 client (for nodeJS) to communicate with a local concordium-node - * used for automatic tests. - */ -export function getNodeClient( - address = TESTNET_NODE, - port = GRPCV1_PORT -): ConcordiumNodeClient { - const metadata = new Metadata(); - metadata.add('authentication', 'rpcadmin'); - return new ConcordiumNodeClient( - address, - port, - credentials.createInsecure(), - metadata, - 15000 - ); -} - /** * Creates a gRPC v2 client (for nodeJS) to communicate with a local concordium-node * used for automatic tests. @@ -44,9 +25,14 @@ export function getNodeClientV2( address = TESTNET_NODE, port = GRPCV2_PORT ): ConcordiumGRPCClient { - return createConcordiumClient(address, port, credentials.createInsecure(), { - timeout: 15000, - }); + return new ConcordiumGRPCNodeClient( + address, + port, + credentials.createInsecure(), + { + timeout: 15000, + } + ); } // TODO find nice way to move this to web/common @@ -58,11 +44,7 @@ export function getNodeClientWeb( address = 'http://node.testnet.concordium.com', port = GRPCV2_PORT ): ConcordiumGRPCClient { - const transport = new GrpcWebFetchTransport({ - baseUrl: `${address}:${port}`, - timeout: 15000, - }); - return new ConcordiumGRPCClient(transport); + return new ConcordiumGRPCWebClient(address, port, { timeout: 15000 }); } export function isValidDate(date: Date): boolean { @@ -71,7 +53,7 @@ export function isValidDate(date: Date): boolean { export function getIdentityInput(): IdentityInput { const rawData = fs.readFileSync( - './test/resources/mobileWalletExport.json', + './test/client/resources/mobileWalletExport.json', 'utf8' ); const mobileWalletExport: EncryptedData = JSON.parse(rawData); diff --git a/packages/sdk/tsconfig.build.json b/packages/sdk/tsconfig.build.json new file mode 100644 index 000000000..e003f9010 --- /dev/null +++ b/packages/sdk/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "src/**/*" + ], + "compilerOptions": { + "rootDir": "./src", + "outDir": "./lib/esm" + } +} diff --git a/packages/sdk/tsconfig.json b/packages/sdk/tsconfig.json new file mode 100644 index 000000000..dfff192a5 --- /dev/null +++ b/packages/sdk/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig-base.json", + "compilerOptions": { + "lib": [ + "ES2020", + "DOM" // To get WebAssembly type. + ] + }, + "include": [ + "src/**/*", + "scripts/**/*", + "test/**/*", + "webpack.config.ts", + "jest.config.ts" + ] +} diff --git a/packages/sdk/webpack.config.ts b/packages/sdk/webpack.config.ts new file mode 100644 index 000000000..dec381564 --- /dev/null +++ b/packages/sdk/webpack.config.ts @@ -0,0 +1,53 @@ +/* eslint-disable import/no-extraneous-dependencies */ +// eslint-disable-next-line import/no-named-as-default +import webpack from 'webpack'; +import { resolve } from 'path'; +import url from 'url'; + +const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); + +const config: webpack.Configuration = { + mode: 'production', + cache: { + type: 'filesystem', + cacheDirectory: resolve(__dirname, '.webpack-cache'), + }, + entry: { + concordium: resolve(__dirname, 'src/index.ts'), + }, + plugins: [ + new webpack.SourceMapDevToolPlugin({ + filename: '[file].map', + }), + ], + resolve: { + extensionAlias: { + '.js': ['.ts', '.js'], + }, + extensions: ['.tsx', '.ts', '.js'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + use: { + loader: 'ts-loader', + options: { + transpileOnly: true, + configFile: resolve(__dirname, './tsconfig.build.json'), + }, + }, + exclude: /node_modules/, + }, + ], + }, + output: { + filename: '[name].min.js', + path: resolve(__dirname, 'lib/umd'), + library: 'concordiumSDK', + libraryTarget: 'umd', + publicPath: '', + }, +}; + +export default config; diff --git a/packages/web/CHANGELOG.md b/packages/web/CHANGELOG.md deleted file mode 100644 index 46b7a552d..000000000 --- a/packages/web/CHANGELOG.md +++ /dev/null @@ -1,204 +0,0 @@ -# Changelog - -## 6.5.1 - -- Bumped @concordium/common-sdk to 9.5.1. - -## 6.5.0 - -- Bumped @concordium/common-sdk to 9.5.0. - -## 6.4.0 - -- Bumped @concordium/common-sdk to 9.4.0. - -## 6.3.0 - -- Bumped @concordium/common-sdk to 9.3.0. - -## 6.2.1 - -- Bumped @concordium/common-sdk to 9.2.1. - -## 6.2.0 - -- Bumped @concordium/common-sdk to 9.2.0. - -## 6.1.1 - -### Changed - -- Bumped @concordium/common-sdk to 9.1.1. (includes fixes for `verifyWeb3IdCredentialSignature` and `canProveAtomicStatement`) - -## 6.1.0 - -### Changed - -- Bumped @concordium/rust-bindings to 1.2.0 and @concordium/common-sdk to 9.1.0. (adds methods for creating verifiable presentation (proving statements about Web3Id Credentials)) - -## 6.0.0 - -### Breaking changes - -- Bumped @concordium/rust-bindings to 1.1.0 and @concordium/common-sdk to 9.0.0. (adds `displayTypeSchemaTemplate/getTransactionKindString` and renames `AccountTransactionType.TransferWithScheduleWithMemo`) - -## 5.0.0 - -### Breaking changes - -- Bumped @concordium/rust-bindings to 1.0.0. (Throws proper `Error`s when execution fails for any WASM entrypoint, with improved error messages) -- Bumped @concordium/common-sdk to 8.0.0: - - Properly formed errors thrown by functions wrapping WASM execution (from @concordium/rust-bindings) with more helpful error messages. - - Types adapted to changes in protocol version 6. - - and [more](../common/CHANGELOG.md) - -## 4.0.1 2023-05-25 - -### Changed - -- Bumped @concordium/common-sdk to 7.0.1. (Fixes `deployModule` cost) - -## 4.0.0 2023-05-15 - -### Breaking Changes - -- Bumped @concordium/rust-bindings to 0.12.0. (Adds key derivation for verifiable credentials) -- Bumped @concordium/common-sdk to 7.0.0: - - Updated `blockInfo` so that the `bakerId` field is optional, since it will be undefined for genesis blocks. - - `waitForTransactionFinalization` now returns a `BlockItemSummaryInBlock` - - Added missing version return type in `getModuleSchema`. It now returns an object containing the schema source and version. - -## 3.5.0 2023-5-03 - -### Changed - -- Bumped @concordium/common-sdk to 6.5.0. (Adds `CIS2Contract`) - -## 3.4.2 2023-4-21 - -### Changed - -- Bumped @concordium/common-sdk to 6.4.2. (`generateBakerKeys` include private baker keys in output) - -## 3.4.1 2023-3-31 - -### Changed - -- Bumped @concordium/common-sdk to 6.4.1. (Fixes `waitForTransactionFinalization`) - -## 3.4.0 2023-3-22 - -### Changed - -- Bumped @concordium/common-sdk to 6.4.0. (Adds `deserializeTypeValue`) - -## 3.3.1 2023-2-27 - -### Fixed - -- Updated rules in package.json to include missing files. - -## 3.3.0 2023-2-27 - -### Added - -- Added a `createConcordiumClient` function to create the gRPC v2 client. - -### Changed - -- Bumped @concordium/common-sdk to 6.3.0. (Adds the gRPC v2 client) - -### Deprecated - -- The JSON-RPC client (from common-sdk) has been deprecated in favor of the new gRPC v2 client. - -## 3.2.0 2022-1-4 - -### Changed - -- Bumped @concordium/common-sdk to 6.2.0. (adds support serializing smart contract types with only the specific type's schema) - -## 3.1.0 2022-11-30 - -### Changed - -- Bumped @concordium/common-sdk to 6.1.0. (adds support for id statements and proofs) - -## 3.0.0 2022-11-15 - -### Changed - -- Bumped @concordium/common-sdk to 6.0.0. (Which changes transaction type names and field names to be aligned with other implementations) - -## 2.1.0 2022-10-27 - -### Changed - -- Bumped @concordium/common-sdk to 5.1.0. (Which adds cookie support to HttpProvider) - -## 2.0.0 2022-9-29 - -### Breaking Changes - -- Bumped @concordium/common-sdk to 5.0.0. (Which changes the function signature of signMessage and verifySignMessage) - -## 1.0.0 2022-9-2 - -### Breaking Changes - -- Bumped @concordium/common-sdk to 4.0.0. (Which changes the function signature of ConcordiumHdWallet) - -## 0.5.0 2022-8-26 - -### Breaking Changes - -- Bumped @concordium/common-sdk to 3.0.0. (Which includes breaking changes to schema versioning) - -## 0.4.0 2022-8-15 - -### Changed - -- Bumped @concordium/common-sdk to 2.4.0. - -## 0.3.0 2022-7-21 - -### Added - -- Add support for getAccountInfo, InvokeContract, getCryptographicParameters and getModuleSource with JSON-RPC -- Support deserializing new schema types: ULeb128, ILeb128, ByteArray and ByteList. -- Support deserializing schemas with versioning information. - -### Changed - -- The function for deserializing a module schema `deserialModuleFromBuffer` now have the schema version as an optional argument. The function will try to extract the version from the buffer. When a version is provided it falls back to this, otherwise it throws an error. - -## 0.2.1 2022-6-27 - -### Fixed - -- @noble/ed25519 and cross-fetch moved from devDependencies to dependencies. (In common-sdk) - -## 0.2.0 2022-6-24 - -### Added - -- Support deserializing version 2 schemas. -- Support serializing parameters for contracts using version 2 schemas. -- Support for deploying versioned smart contract modules, which is the format used in cargo-concordium v2+. (This is done by not supplying the version field in the payload) - -### Breaking changes - -- `serializeInitContractParameters` and `serializeUpdateContractParameters` each have an additional parameter, which denotes the version of the schema provided. For existing users that are using V0 contracts, that parameter should be `SchemaVersion.V1`. -- Deserialization of schemas have been changed: types and functions have been renamed and `getModuleFromBuffer` have an additional parameter. - -## 0.1.2 - -- Add type file missing from the package - -## 0.1.1 - -- Fixed issue with wasm from rust bindings - -## 0.1.0 - -- Initial release diff --git a/packages/web/README.md b/packages/web/README.md deleted file mode 100644 index 9bd324b00..000000000 --- a/packages/web/README.md +++ /dev/null @@ -1,147 +0,0 @@ -# concordium-web-sdk - -[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.0-4baaaa.svg)](https://github.com/Concordium/.github/blob/main/.github/CODE_OF_CONDUCT.md) - -Wrappers for interacting with the Concordium node, for the web environment. - -Please see the -[documentation](https://developer.concordium.software/concordium-node-sdk-js/index.html) -for more information - -**Table of Contents:** - - -- [concordium-web-sdk](#concordium-web-sdk) - - [ConcordiumGRPCClient](#concordiumgrpcclient) - - [JSON-RPC client](#json-rpc-client) - - [Creating buffers](#creating-buffers) - - [Examples](#examples) - - [SendTransaction.html](#sendtransactionhtml) - - [GetInstanceInfo.html](#getinstanceinfohtml) - - [Alias.html](#aliashtml) - - [GetTransactionStatus.html](#gettransactionstatushtml) - - [GetNonce.html](#getnoncehtml) - - [InvokeContract.html](#invokecontracthtml) - - [GetCryptographicParameters.html](#getcryptographicparametershtml) - - [GetAccountInfo.html](#getaccountinfohtml) - - [GetModuleSource.html](#getmodulesourcehtml) - - [SendCIS2Transfer.html](#sendcis2transferhtml) - - [SendCIS2UpdateOperator.html](#sendcis2updateoperatorhtml) - - -## ConcordiumGRPCClient - -The SDK provides a gRPC client, which can interact with the [Concordium -Node](https://github.com/Concordium/concordium-node) using gRPC-web. - -For an overview of the endpoints, [click -here](https://developer.concordium.software/concordium-node-sdk-js/modules/Common_GRPC_Client.html). - -To create a client, the function `createConcordiumClient` can be used. It -requires the address and port of the concordium node. - -```ts -import { createConcordiumClient } from '@concordium/web-sdk'; -... -return createConcordiumClient( - address, - port, - { timeout: 15000 } -); -``` - -The third argument is additional options. In the example -above we sat the timeout for a call to the node to 15 -seconds. The options allowed here are those allowed by the -[grpcweb-transport](https://www.npmjs.com/package/@protobuf-ts/grpcweb-transport). - -## JSON-RPC client - -> :warning: **The JSON-RPC client has been deprecated**: the gRPC client - should be used instead to communicate directly with a node -> To migrate,the migration guide from the v1 client to the v2 client [can - be found - here](https://developer.concordium.software/concordium-node-sdk-js/pages/misc-pages/grpc-migration.html), - as the JSON-RPC's endpoints shares interface with the equivalent endpoints - in the v1 gRPC client - -The SDK also provides a JSON-RPC client, [check here for the -documentation](https://developer.concordium.software/concordium-node-sdk-js/pages/misc-pages/grpc-v1.html). - -## Creating buffers - -Some of the functions in the SDK expects buffers as -input. For this purpose the SDK exports a `toBuffer` -function, which is a polyfill of the [buffer.from from the Nodejs -API](https://nodejs.org/api/buffer.html#static-method-bufferfromstring-encoding) -for strings. - -```ts -const myBuffer = toBuffer('AB2C2D', 'hex'); -``` - -## Examples - -A few simple webpages have been made, to showcase using the web-sdk. They -can be found in the `example` folder. Note that the project should be built -before running the examples, otherwise they won't work. The examples that -use JSON-RPC expect a JSON-RPC server on running at `http://localhost:9095`. - -### SendTransaction.html - -An example of how to send a transaction using the SDK to a JSON-RPC server. - -### GetInstanceInfo.html - -An example of getting the info of a given smart contract instance using a -JSON-RPC server. - -### Alias.html - -A very minimal example of a webpage showing alias'es of a given address, -using the bundled SDK. - -### GetTransactionStatus.html - -A simple example that allows calling a JSON-RPC server for a given -transaction's status and displays the status. - -### GetNonce.html - -A simple example that allows calling a JSON-RPC server for a given account's -next nonce and displays it. - -### InvokeContract.html - -An simple example of how to invoke a given smart contract instance using a -JSON-RPC server. - -### GetCryptographicParameters.html - -An example of getting the crypgographic parameters of the chain using a -JSON-RPC server. - -### GetAccountInfo.html - -An example of getting the info of a given account using a JSON-RPC server. - -### GetModuleSource.html - -An example of getting the source of a model on the chain using a JSON-RPC -server. - -### SendCIS2Transfer.html - -An example of sending a CIS2 "transfer" transaction -by utilizing the `CIS2Contract` class. Please -note that this example requires the [Concordium Wallet for -Web](https://chrome.google.com/webstore/detail/concordium-wallet/mnnkpffndmickbiakofclnpoiajlegmg) -to be installed to work. - -### SendCIS2UpdateOperator.html - -An example of sending a CIS2 "updateOperator" -transaction by utilizing the `CIS2Contract` class. Please -note that this example requires the [Concordium Wallet for -Web](https://chrome.google.com/webstore/detail/concordium-wallet/mnnkpffndmickbiakofclnpoiajlegmg) -to be installed to work. diff --git a/packages/web/example/Alias.html b/packages/web/example/Alias.html deleted file mode 100644 index a25a41bc3..000000000 --- a/packages/web/example/Alias.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - -
- -
-