├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .github ├── release.yml └── workflows │ ├── build.yml │ ├── package-lock-checks.yml │ ├── publish-not-main.yml │ ├── publish.yml │ ├── test-localnet.yml │ └── update-docs.yml ├── .gitignore ├── .mocharc.json ├── .npmignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── Makefile ├── README.md ├── browser-tests └── index.html ├── cookbook ├── account.ts ├── accountManagement.ts ├── addresses.ts ├── cookbook.md ├── delegation.ts ├── entrypoints.ts ├── generate.py ├── guarded.ts ├── networkProviders.ts ├── relayed.ts ├── signingObjects.ts ├── smartContracts.ts ├── tokens.ts ├── transactions.ts ├── verifySignatures.ts ├── wallet.pem └── wallets.ts ├── localnet.toml ├── package-lock.json ├── package.json ├── src ├── abi │ ├── argSerializer.spec.ts │ ├── argSerializer.ts │ ├── argumentErrorContext.ts │ ├── code.spec.ts │ ├── code.ts │ ├── codec │ │ ├── address.ts │ │ ├── arrayVec.ts │ │ ├── binary.spec.ts │ │ ├── binary.ts │ │ ├── binaryCodecUtils.ts │ │ ├── boolean.ts │ │ ├── bytes.ts │ │ ├── codemetadata.ts │ │ ├── constants.ts │ │ ├── enum.ts │ │ ├── explicit-enum.ts │ │ ├── fields.ts │ │ ├── h256.ts │ │ ├── index.ts │ │ ├── list.ts │ │ ├── managedDecimal.ts │ │ ├── managedDecimalSigned.ts │ │ ├── nothing.ts │ │ ├── numerical.ts │ │ ├── option.ts │ │ ├── primitive.ts │ │ ├── string.ts │ │ ├── struct.ts │ │ ├── tokenIdentifier.ts │ │ ├── tuple.ts │ │ └── utils.ts │ ├── function.ts │ ├── index.ts │ ├── interaction.local.net.spec.ts │ ├── interaction.spec.ts │ ├── interaction.ts │ ├── interface.ts │ ├── nativeSerializer.spec.ts │ ├── nativeSerializer.ts │ ├── query.spec.ts │ ├── query.ts │ ├── returnCode.ts │ ├── smartContract.local.net.spec.ts │ ├── smartContract.spec.ts │ ├── smartContract.ts │ ├── smartContractResults.local.net.spec.ts │ ├── typeFormula.ts │ ├── typeFormulaParser.spec.ts │ ├── typeFormulaParser.ts │ └── typesystem │ │ ├── abi.spec.ts │ │ ├── abi.ts │ │ ├── address.ts │ │ ├── algebraic.ts │ │ ├── boolean.ts │ │ ├── bytes.ts │ │ ├── codeMetadata.ts │ │ ├── collections.ts │ │ ├── composite.spec.ts │ │ ├── composite.ts │ │ ├── endpoint.spec.ts │ │ ├── endpoint.ts │ │ ├── enum.spec.ts │ │ ├── enum.ts │ │ ├── event.ts │ │ ├── explicit-enum.spec.ts │ │ ├── explicit-enum.ts │ │ ├── factory.spec.ts │ │ ├── factory.ts │ │ ├── fields.ts │ │ ├── generic.ts │ │ ├── genericArray.ts │ │ ├── h256.ts │ │ ├── index.ts │ │ ├── managedDecimal.spec.ts │ │ ├── managedDecimal.ts │ │ ├── managedDecimalSigned.ts │ │ ├── matchers.ts │ │ ├── nothing.ts │ │ ├── numerical.ts │ │ ├── string.ts │ │ ├── struct.spec.ts │ │ ├── struct.ts │ │ ├── tokenIdentifier.ts │ │ ├── tuple.ts │ │ ├── typeExpressionParser.spec.ts │ │ ├── typeExpressionParser.ts │ │ ├── typeMapper.spec.ts │ │ ├── typeMapper.ts │ │ ├── types.spec.ts │ │ ├── types.ts │ │ └── variadic.ts ├── accountManagement │ ├── accountController.ts │ ├── accountTransactionsFactory.spec.ts │ ├── accountTransactionsFactory.ts │ ├── index.ts │ └── resources.ts ├── accounts │ ├── account.spec.ts │ ├── account.ts │ └── index.ts ├── core │ ├── address.spec.ts │ ├── address.ts │ ├── asyncTimer.spec.ts │ ├── asyncTimer.ts │ ├── baseController.spec.ts │ ├── baseController.ts │ ├── codeMetadata.spec.ts │ ├── codeMetadata.ts │ ├── compatibility.ts │ ├── config.ts │ ├── constants.ts │ ├── errors.ts │ ├── globals.ts │ ├── index.ts │ ├── interfaces.ts │ ├── logger.ts │ ├── message.spec.ts │ ├── message.ts │ ├── networkParams.spec.ts │ ├── networkParams.ts │ ├── reflection.ts │ ├── signature.ts │ ├── smartContractQuery.ts │ ├── tokenTransfersDataBuilder.ts │ ├── tokens.spec.ts │ ├── tokens.ts │ ├── transaction.local.net.spec.ts │ ├── transaction.spec.ts │ ├── transaction.ts │ ├── transactionBuilder.ts │ ├── transactionComputer.ts │ ├── transactionEvents.ts │ ├── transactionLogs.ts │ ├── transactionOnNetwork.ts │ ├── transactionStatus.ts │ ├── transactionWatcher.spec.ts │ ├── transactionWatcher.ts │ ├── transactionsFactoryConfig.ts │ ├── utils.codec.spec.ts │ ├── utils.codec.ts │ └── utils.ts ├── delegation │ ├── delegationController.ts │ ├── delegationTransactionsFactory.spec.ts │ ├── delegationTransactionsFactory.ts │ ├── delegationTransactionsOutcomeParser.spec.ts │ ├── delegationTransactionsOutcomeParser.ts │ ├── index.ts │ └── resources.ts ├── entrypoints │ ├── config.ts │ ├── entrypoints.spec.ts │ ├── entrypoints.ts │ └── index.ts ├── governance │ ├── governanceController.spec.ts │ ├── governanceController.ts │ ├── governanceTransactionsFactory.spec.ts │ ├── governanceTransactionsFactory.ts │ ├── governanceTransactionsOutcomeParser.spec.ts │ ├── governanceTransactionsOutcomeParser.ts │ ├── index.ts │ └── resources.ts ├── index.ts ├── multisig │ ├── index.ts │ ├── multisigController.spec.ts │ ├── multisigController.ts │ ├── multisigTransactionsFactory.spec.ts │ ├── multisigTransactionsFactory.ts │ ├── multisigTransactionsOutcomeParser.ts │ ├── proposeTransferExecuteContractInput.ts │ └── resources.ts ├── networkProviders │ ├── accountAwaiter.dev.net.spec.ts │ ├── accountAwaiter.ts │ ├── accounts.ts │ ├── apiNetworkProvider.dev.net.spec.ts │ ├── apiNetworkProvider.ts │ ├── blocks.ts │ ├── config.ts │ ├── constants.ts │ ├── contractQueryRequest.ts │ ├── index.ts │ ├── interface.ts │ ├── networkConfig.ts │ ├── networkProviderConfig.ts │ ├── networkStatus.ts │ ├── providers.dev.net.spec.ts │ ├── proxyNetworkProvider.dev.net.spec.ts │ ├── proxyNetworkProvider.ts │ ├── resources.ts │ ├── serialization.spec.ts │ ├── tokenDefinitions.ts │ ├── tokens.ts │ └── userAgent.ts ├── proto │ ├── compiled.js │ ├── index.ts │ ├── serializer.spec.ts │ ├── serializer.ts │ └── transaction.proto ├── smartContracts │ ├── index.ts │ ├── resources.ts │ ├── smartContractController.spec.ts │ ├── smartContractController.ts │ ├── smartContractTransactionsFactory.spec.ts │ ├── smartContractTransactionsFactory.ts │ ├── smartContractTransactionsOutcomeParser.dev.net.spec.ts │ ├── smartContractTransactionsOutcomeParser.main.net.spec.ts │ ├── smartContractTransactionsOutcomeParser.spec.ts │ └── smartContractTransactionsOutcomeParser.ts ├── testdata │ ├── README.md │ ├── adder.abi.json │ ├── adder.wasm │ ├── answer.abi.json │ ├── answer.wasm │ ├── array-in-nested-structs.abi.json │ ├── basic-features.abi.json │ ├── basic-features.wasm │ ├── counted-variadic.abi.json │ ├── counter.abi.json │ ├── counter.wasm │ ├── custom-types-out-of-order-a.abi.json │ ├── custom-types-out-of-order-b.abi.json │ ├── custom-types-out-of-order-c.abi.json │ ├── custom-types-out-of-order-d.abi.json │ ├── erc20.wasm │ ├── esdt-nft-marketplace.abi.json │ ├── esdt-safe.abi.json │ ├── explicit-enum.abi.json │ ├── lottery-esdt.abi.json │ ├── lottery-esdt.wasm │ ├── multisig-full.abi.json │ ├── multisig-full.wasm │ ├── testwallets │ │ ├── alice.json │ │ ├── alice.pem │ │ ├── bob.json │ │ ├── bob.pem │ │ ├── carol.json │ │ ├── carol.pem │ │ ├── dan.json │ │ ├── dan.pem │ │ ├── eve.json │ │ ├── eve.pem │ │ ├── frank.json │ │ ├── frank.pem │ │ ├── grace.json │ │ ├── grace.pem │ │ ├── heidi.json │ │ ├── heidi.pem │ │ ├── ivan.json │ │ ├── ivan.pem │ │ ├── judy.json │ │ ├── judy.pem │ │ ├── mallory.json │ │ ├── mallory.pem │ │ ├── mike.json │ │ ├── mike.pem │ │ ├── mnemonic.txt │ │ ├── multipleUserKeys.pem │ │ ├── multipleValidatorKeys.pem │ │ ├── password.txt │ │ ├── validatorKey00.pem │ │ ├── validatorKey00WithExtraLines.pem │ │ ├── withDummyMnemonic.json │ │ ├── withDummySecretKey.json │ │ └── withoutKind.json │ └── transactions.mainnet.json ├── testutils │ ├── files.ts │ ├── index.ts │ ├── mockNetworkProvider.ts │ ├── networkProviders.ts │ ├── utils.ts │ └── wallets.ts ├── tokenManagement │ ├── index.ts │ ├── resources.ts │ ├── tokenManagementController.ts │ ├── tokenManagementTransactionFactory.spec.ts │ ├── tokenManagementTransactionsFactory.ts │ ├── tokenManagementTransactionsOutcomeParser.spec.ts │ └── tokenManagementTransactionsOutcomeParser.ts ├── tokenOperations │ ├── codec.spec.ts │ └── codec.ts ├── transactionsOutcomeParsers │ ├── index.ts │ ├── resources.spec.ts │ ├── resources.ts │ ├── transactionEventsParser.spec.ts │ └── transactionEventsParser.ts ├── transfers │ ├── index.ts │ ├── resources.ts │ ├── transferTransactionsFactory.spec.ts │ ├── transferTransactionsFactory.ts │ └── transfersControllers.ts └── wallet │ ├── assertions.ts │ ├── crypto │ ├── constants.ts │ ├── decryptor.ts │ ├── derivationParams.ts │ ├── encrypt.spec.ts │ ├── encryptedData.ts │ ├── encryptor.ts │ ├── index.ts │ ├── pubkeyDecryptor.ts │ ├── pubkeyEncrypt.spec.ts │ ├── pubkeyEncryptor.ts │ ├── randomness.ts │ └── x25519EncryptedData.ts │ ├── index.ts │ ├── keypair.spec.ts │ ├── keypair.ts │ ├── mnemonic.ts │ ├── pem.spec.ts │ ├── pem.ts │ ├── pemEntry.spec.ts │ ├── pemEntry.ts │ ├── userKeys.ts │ ├── userPem.ts │ ├── userSigner.ts │ ├── userVerifier.ts │ ├── userWallet.ts │ ├── users.spec.ts │ ├── usersBenchmark.spec.ts │ ├── validatorKeys.ts │ ├── validatorSigner.ts │ └── validators.spec.ts ├── tsconfig.json └── tsconfig.tests.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # See: https://EditorConfig.org 2 | # VSCode plugin: https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig 3 | 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | insert_final_newline = true 10 | indent_style = space 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", 3 | parserOptions: { 4 | project: ["tsconfig.json", "tsconfig.tests.json"], 5 | sourceType: "module", 6 | }, 7 | plugins: ["@typescript-eslint/eslint-plugin"], 8 | extends: ["plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier"], 9 | root: true, 10 | env: { 11 | node: true, 12 | }, 13 | ignorePatterns: [ 14 | ".eslintrc.js", 15 | "node_modules", 16 | "out", 17 | "out-tests", 18 | "out-browser", 19 | "out-browser-tests", 20 | "cookbook", 21 | ], 22 | rules: { 23 | "@typescript-eslint/interface-name-prefix": "off", 24 | "@typescript-eslint/explicit-function-return-type": "off", 25 | "@typescript-eslint/no-explicit-any": "off", 26 | "@typescript-eslint/no-use-before-define": "off", 27 | "@typescript-eslint/quotes": "off", 28 | "@typescript-eslint/no-empty-interface": "off", 29 | "@typescript-eslint/no-non-null-assertion": "off", 30 | "@typescript-eslint/no-inferrable-types": "off", 31 | "@typescript-eslint/no-namespace": "warn", 32 | "@typescript-eslint/no-var-requires": "warn", 33 | "@typescript-eslint/no-empty-function": "off", 34 | "@typescript-eslint/no-unused-vars": [ 35 | "warn", 36 | { 37 | argsIgnorePattern: "^_", 38 | varsIgnorePattern: "^_", 39 | }, 40 | ], 41 | "prefer-const": "off", 42 | }, 43 | }; 44 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # https://github.com/github-linguist/linguist/blob/master/docs/overrides.md 2 | # https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github 3 | src/proto/compiled.js linguist-generated 4 | src/proto/compiled.d.ts linguist-generated 5 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release-notes 5 | categories: 6 | - title: What's Changed 7 | labels: 8 | - "*" 9 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Build and Test 5 | 6 | on: 7 | pull_request: 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | node-version: [16.x] 18 | 19 | steps: 20 | - uses: actions/checkout@v2 21 | - name: Use Node.js ${{ matrix.node-version }} 22 | uses: actions/setup-node@v1 23 | with: 24 | node-version: ${{ matrix.node-version }} 25 | - run: npm ci 26 | - run: npm run lint 27 | - run: npm run compile 28 | - run: npm install esmify && npm run compile-browser 29 | - run: npm run tests-unit 30 | - run: npm run tests-devnet 31 | -------------------------------------------------------------------------------- /.github/workflows/package-lock-checks.yml: -------------------------------------------------------------------------------- 1 | name: Check package-lock.json 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | compare-package-lock: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | 13 | - name: Use Node.js LTS 14 | uses: actions/setup-node@v1 15 | with: 16 | node-version: '16.14.2' 17 | 18 | - name: Copy package-lock.json file 19 | run: cp package-lock.json package-lock-copy.json 20 | 21 | - name: Remove package-lock.json file 22 | run: rm -f package-lock.json 23 | 24 | - name: Run npm install 25 | run: npm install 26 | 27 | - name: Compare old package-lock with generated package-lock 28 | run: cmp package-lock.json package-lock-copy.json 29 | -------------------------------------------------------------------------------- /.github/workflows/publish-not-main.yml: -------------------------------------------------------------------------------- 1 | name: Publish (not main) 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | channel: 7 | type: choice 8 | description: NPM channel 9 | options: 10 | - alpha 11 | - beta 12 | - previous 13 | 14 | permissions: 15 | contents: write 16 | 17 | jobs: 18 | publish-npm: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v2 22 | - uses: actions/setup-node@v1 23 | with: 24 | node-version: 16 25 | registry-url: https://registry.npmjs.org/ 26 | 27 | - name: Create release 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | run: | 31 | RELEASE_TAG=v$(node -p "require('./package.json').version") 32 | gh release create --prerelease $RELEASE_TAG --target=$GITHUB_SHA --title="$RELEASE_TAG" --generate-notes 33 | 34 | - run: npm ci 35 | - run: npm test 36 | 37 | - name: Publish to npmjs 38 | env: 39 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 40 | run: npm publish --access=public --tag=${{ github.event.inputs.channel }} 41 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: write 8 | 9 | jobs: 10 | publish-npm: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: 16 17 | registry-url: https://registry.npmjs.org/ 18 | 19 | - name: Create release 20 | env: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | run: | 23 | RELEASE_TAG=v$(node -p "require('./package.json').version") 24 | gh release create $RELEASE_TAG --target=$GITHUB_SHA --title="$RELEASE_TAG" --generate-notes 25 | 26 | - run: npm ci 27 | - run: npm test 28 | 29 | - name: Publish to npmjs 30 | env: 31 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 32 | run: npm publish --access=public 33 | -------------------------------------------------------------------------------- /.github/workflows/test-localnet.yml: -------------------------------------------------------------------------------- 1 | name: MultiversX Integration Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | integration_tests: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | # Step 1: Checkout the repository 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | 18 | # Step 2: Set up Python environment 19 | - name: Set up Python 3.x 20 | uses: actions/setup-python@v4 21 | with: 22 | python-version: '3.x' 23 | 24 | # Step 3: Install pipx (to manage Python tools) 25 | - name: Install pipx 26 | run: | 27 | python3 -m pip install --user pipx 28 | python3 -m pipx ensurepath 29 | # Add the pipx binary location to PATH 30 | echo "$HOME/.local/bin" >> $GITHUB_PATH 31 | shell: bash 32 | 33 | # Step 4: Install mxpy (MultiversX Python SDK) 34 | - name: Install mxpy (MultiversX SDK) 35 | run: | 36 | pipx install multiversx-sdk-cli --force 37 | 38 | # Step 5: Set up MultiversX localnet using mxpy 39 | - name: Set up MultiversX localnet 40 | run: | 41 | # Start the local testnet with mxpy 42 | mkdir -p ~/localnet && cd ~/localnet 43 | mxpy localnet setup --configfile=${GITHUB_WORKSPACE}/localnet.toml 44 | nohup mxpy localnet start --configfile=${GITHUB_WORKSPACE}/localnet.toml > localnet.log 2>&1 & echo $! > localnet.pid 45 | sleep 120 # Allow time for the testnet to fully start 46 | 47 | # Step 6: Install Node.js and dependencies 48 | - name: Set up Node.js environment 49 | uses: actions/setup-node@v3 50 | with: 51 | node-version: '16.x' 52 | 53 | - name: Install Node.js dependencies 54 | run: npm install 55 | 56 | # Step 7: Run integration tests 57 | - name: Run integration tests 58 | run: | 59 | npm run tests-localnet 60 | 61 | # Step 8: Stop the testnet using the stored PID 62 | - name: Stop MultiversX local testnet 63 | if: success() || failure() 64 | run: | 65 | kill $(cat localnet.pid) || echo "Testnet already stopped" 66 | -------------------------------------------------------------------------------- /.github/workflows/update-docs.yml: -------------------------------------------------------------------------------- 1 | name: Update docs 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [released] 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | update-docs: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | 17 | - uses: actions/checkout@v4 18 | with: 19 | ref: "gh-pages" 20 | repository: ${{ github.event.pull_request.head.repo.full_name }} 21 | path: "docs" 22 | 23 | - uses: actions/setup-node@v4 24 | with: 25 | node-version: 18 26 | registry-url: https://registry.npmjs.org/ 27 | 28 | - name: Install dependencies 29 | run: | 30 | npm install -g typedoc 31 | 32 | - name: Re-generate docs 33 | run: | 34 | MAJOR_VERSION=v$(node -p "require('./package.json').version.split('.')[0]") 35 | DOCS_OUTPUT_FOLDER=${GITHUB_WORKSPACE}/docs/${MAJOR_VERSION} 36 | 37 | mkdir -p $DOCS_OUTPUT_FOLDER 38 | 39 | npm ci 40 | typedoc --out $DOCS_OUTPUT_FOLDER src/index.ts --includeVersion 41 | 42 | cd ${GITHUB_WORKSPACE}/docs 43 | 44 | # See: https://github.com/actions/checkout/blob/main/README.md#push-a-commit-using-the-built-in-token 45 | git config user.name github-actions 46 | git config user.email github-actions@github.com 47 | git add . 48 | git commit -m "Re-generated docs." --allow-empty 49 | git push 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | out 3 | out-tests 4 | out-browser 5 | out-browser-tests 6 | node_modules/ 7 | tags 8 | -------------------------------------------------------------------------------- /.mocharc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extension": [ 3 | "ts" 4 | ], 5 | "require": "ts-node/register" 6 | } 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/testdata/** 2 | src/testutils/** 3 | localnet.toml 4 | cookbook 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": false, 3 | "trailingComma": "all", 4 | "tabWidth": 4, 5 | "printWidth": 120 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.formatOnSaveMode": "file", 4 | "[javascript]": { 5 | "editor.defaultFormatter": "esbenp.prettier-vscode" 6 | }, 7 | "[typescript]": { 8 | "editor.defaultFormatter": "esbenp.prettier-vscode" 9 | }, 10 | "editor.codeActionsOnSave": { 11 | "source.organizeImports": "explicit" 12 | }, 13 | "prettier.printWidth": 120 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) MultiversX 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean browser-tests 2 | 3 | browser-tests: out-browser-tests/tests-unit.js out-browser-tests/tests-localnet.js out-browser-tests/tests-devnet.js out-browser-tests/tests-testnet.js 4 | 5 | out-browser-tests/tests-unit.js: out-tests 6 | npx browserify $(shell find out-tests -type f -name '*.js' ! -name '*.net.spec.*') --require buffer/:buffer -o out-browser-tests/tests-unit.js --standalone tests -p esmify 7 | 8 | out-browser-tests/tests-localnet.js: out-tests 9 | npx browserify $(shell find out-tests -type f -name '*.js' ! -name '*.spec.*') $(shell find out-tests -type f -name '*.local.net.spec.js') --require buffer/:buffer -o out-browser-tests/tests-localnet.js --standalone tests -p esmify 10 | 11 | out-browser-tests/tests-devnet.js: out-tests 12 | npx browserify $(shell find out-tests -type f -name '*.js' ! -name '*.spec.*') $(shell find out-tests -type f -name '*.dev.net.spec.js') --require buffer/:buffer -o out-browser-tests/tests-devnet.js --standalone tests -p esmify 13 | 14 | out-browser-tests/tests-testnet.js: out-tests 15 | npx browserify $(shell find out-tests -type f -name '*.js' ! -name '*.spec.*') $(shell find out-tests -type f -name '*.test.net.spec.js') --require buffer/:buffer -o out-browser-tests/tests-testnet.js --standalone tests -p esmify 16 | 17 | out-tests: 18 | npx tsc -p tsconfig.tests.json 19 | 20 | clean: 21 | rm -rf out-tests 22 | rm -rf out-browser-tests 23 | -------------------------------------------------------------------------------- /cookbook/entrypoints.ts: -------------------------------------------------------------------------------- 1 | import { DevnetEntrypoint } from "../src"; // md-ignore 2 | // md-start 3 | (async () => { 4 | // ## Overview 5 | 6 | // This guide walks you through handling common tasks using the MultiversX Javascript SDK (v14, latest stable version). 7 | 8 | // ## Creating an Entrypoint 9 | 10 | // An Entrypoint represents a network client that simplifies access to the most common operations. 11 | // There is a dedicated entrypoint for each network: `MainnetEntrypoint`, `DevnetEntrypoint`, `TestnetEntrypoint`, `LocalnetEntrypoint`. 12 | 13 | // For example, to create a Devnet entrypoint you have the following command: 14 | 15 | // ```js 16 | const entrypoint = new DevnetEntrypoint(); 17 | // ``` 18 | 19 | // #### Using a Custom API 20 | // If you'd like to connect to a third-party API, you can specify the url parameter: 21 | 22 | // ```js 23 | const apiEntrypoint = new DevnetEntrypoint("https://custom-multiversx-devnet-api.com"); 24 | // ``` 25 | 26 | // #### Using a Proxy 27 | 28 | // By default, the DevnetEntrypoint uses the standard API. However, you can create a custom entrypoint that interacts with a proxy by specifying the kind parameter: 29 | 30 | // ```js 31 | const customEntrypoint = new DevnetEntrypoint("https://devnet-gateway.multiversx.com", "proxy"); 32 | // ``` 33 | })().catch((e) => { 34 | console.log({ e }); 35 | }); 36 | -------------------------------------------------------------------------------- /cookbook/wallet.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- 2 | NDEzZjQyNTc1ZjdmMjZmYWQzMzE3YTc3ODc3MTIxMmZkYjgwMjQ1ODUwOTgxZTQ4 3 | YjU4YTRmMjVlMzQ0ZThmOTAxMzk0NzJlZmY2ODg2NzcxYTk4MmYzMDgzZGE1ZDQy 4 | MWYyNGMyOTE4MWU2Mzg4ODIyOGRjODFjYTYwZDY5ZTE= 5 | -----END PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- -------------------------------------------------------------------------------- /localnet.toml: -------------------------------------------------------------------------------- 1 | [general] 2 | log_level = "*:DEBUG" 3 | genesis_delay_seconds = 10 4 | rounds_per_epoch = 50 5 | round_duration_milliseconds = 6000 6 | 7 | [metashard] 8 | consensus_size = 1 9 | num_observers = 0 10 | num_validators = 1 11 | 12 | [shards] 13 | num_shards = 3 14 | consensus_size = 1 15 | num_observers_per_shard = 0 16 | num_validators_per_shard = 1 17 | 18 | [networking] 19 | host = "127.0.0.1" 20 | port_seednode = 9999 21 | port_seednode_rest_api = 10000 22 | p2p_id_seednode = "16Uiu2HAkx4QqgXXDdHdUWbLu5kxhd3Uo2hqB2FfCxmxH5Sd7bZFk" 23 | port_proxy = 7950 24 | port_first_observer = 21100 25 | port_first_observer_rest_api = 10100 26 | port_first_validator = 21500 27 | port_first_validator_rest_api = 10200 28 | 29 | [software.mx_chain_go] 30 | resolution = "remote" 31 | archive_url = "https://github.com/multiversx/mx-chain-go/archive/refs/heads/master.zip" 32 | archive_download_folder = "~/multiversx-sdk/localnet_software_remote/downloaded/mx-chain-go" 33 | archive_extraction_folder = "~/multiversx-sdk/localnet_software_remote/extracted/mx-chain-go" 34 | local_path = "~/multiversx-sdk/localnet_software_local/mx-chain-go" 35 | 36 | [software.mx_chain_proxy_go] 37 | resolution = "remote" 38 | archive_url = "https://github.com/multiversx/mx-chain-proxy-go/archive/refs/heads/master.zip" 39 | archive_download_folder = "~/multiversx-sdk/localnet_software_remote/downloaded/mx-chain-proxy-go" 40 | archive_extraction_folder = "~/multiversx-sdk/localnet_software_remote/extracted/mx-chain-proxy-go" 41 | local_path = "~/multiversx-sdk/localnet_software_local/mx-chain-proxy-go" 42 | -------------------------------------------------------------------------------- /src/abi/argumentErrorContext.ts: -------------------------------------------------------------------------------- 1 | import { ErrInvalidArgument } from "../core/errors"; 2 | import { EndpointParameterDefinition, Type } from "./typesystem"; 3 | 4 | export class ArgumentErrorContext { 5 | endpointName: string; 6 | argumentIndex: number; 7 | parameterDefinition: EndpointParameterDefinition; 8 | 9 | constructor(endpointName: string, argumentIndex: number, parameterDefinition: EndpointParameterDefinition) { 10 | this.endpointName = endpointName; 11 | this.argumentIndex = argumentIndex; 12 | this.parameterDefinition = parameterDefinition; 13 | } 14 | 15 | throwError(specificError: string): never { 16 | throw new ErrInvalidArgument( 17 | `Error when converting arguments for endpoint (endpoint name: ${this.endpointName}, argument index: ${this.argumentIndex}, name: ${this.parameterDefinition.name}, type: ${this.parameterDefinition.type})\nNested error: ${specificError}`, 18 | ); 19 | } 20 | 21 | convertError(native: any, typeName: string): never { 22 | this.throwError( 23 | `Can't convert argument (argument: ${native}, type ${typeof native}), wanted type: ${typeName})`, 24 | ); 25 | } 26 | 27 | unhandledType(functionName: string, type: Type): never { 28 | this.throwError(`Unhandled type (function: ${functionName}, type: ${type})`); 29 | } 30 | 31 | guardSameLength(native: any[], valueTypes: T[]) { 32 | native = native || []; 33 | if (native.length != valueTypes.length) { 34 | this.throwError( 35 | `Incorrect composite type length: have ${native.length}, expected ${valueTypes.length} (argument: ${native})`, 36 | ); 37 | } 38 | } 39 | 40 | guardHasField(native: any, fieldName: string) { 41 | native = native || {}; 42 | if (!(fieldName in native)) { 43 | this.throwError( 44 | `Struct argument does not contain a field named "${fieldName}" (argument: ${JSON.stringify(native)})`, 45 | ); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/abi/code.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { Code } from "./code"; 3 | 4 | describe("Code Class Tests", function () { 5 | const sampleHex = "abcdef0123456789"; 6 | const sampleBuffer = Buffer.from(sampleHex, "hex"); 7 | 8 | it("should create Code from buffer", function () { 9 | const code = Code.fromBuffer(sampleBuffer); 10 | 11 | assert.instanceOf(code, Code); 12 | assert.equal(code.toString(), sampleHex); 13 | }); 14 | 15 | it("should create Code from hex string", function () { 16 | const code = Code.fromHex(sampleHex); 17 | 18 | assert.instanceOf(code, Code); 19 | assert.equal(code.toString(), sampleHex); 20 | }); 21 | 22 | it("should return the correct buffer from valueOf", function () { 23 | const code = Code.fromHex(sampleHex); 24 | const buffer = code.valueOf(); 25 | 26 | assert.isTrue(Buffer.isBuffer(buffer)); 27 | assert.equal(buffer.toString("hex"), sampleHex); 28 | }); 29 | 30 | it("should compute hash correctly", function () { 31 | const code = Code.fromHex(sampleHex); 32 | const hash = code.computeHash(); 33 | 34 | assert.instanceOf(hash, Buffer); 35 | assert.equal(hash.toString("hex"), "ac86b78afd9bdda3641a47a4aff2a7ee26acd40cc534d63655e9dfbf3f890a02"); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/abi/code.ts: -------------------------------------------------------------------------------- 1 | const createHasher = require("blake2b"); 2 | const CODE_HASH_LENGTH = 32; 3 | 4 | /** 5 | * * @deprecated Use the bytecode directly 6 | * Bytecode of a Smart Contract, as an abstraction. 7 | */ 8 | export class Code { 9 | private readonly hex: string; 10 | 11 | private constructor(hex: string) { 12 | this.hex = hex; 13 | } 14 | 15 | /** 16 | * Creates a Code object from a buffer (sequence of bytes). 17 | */ 18 | static fromBuffer(code: Buffer): Code { 19 | return new Code(code.toString("hex")); 20 | } 21 | 22 | /** 23 | * Creates a Code object from a hex-encoded string. 24 | */ 25 | static fromHex(hex: string): Code { 26 | return new Code(hex); 27 | } 28 | 29 | /** 30 | * Returns the bytecode as a hex-encoded string. 31 | */ 32 | toString(): string { 33 | return this.hex; 34 | } 35 | 36 | valueOf(): Buffer { 37 | return Buffer.from(this.hex, "hex"); 38 | } 39 | 40 | computeHash(): Buffer { 41 | const hash = createHasher(CODE_HASH_LENGTH).update(this.valueOf()).digest(); 42 | 43 | return Buffer.from(hash); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/abi/codec/address.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../../core/address"; 2 | import { AddressValue } from "../typesystem"; 3 | 4 | export class AddressBinaryCodec { 5 | /** 6 | * Reads and decodes an AddressValue from a given buffer. 7 | * 8 | * @param buffer the input buffer 9 | */ 10 | decodeNested(buffer: Buffer): [AddressValue, number] { 11 | // We don't check the size of the buffer, we just read 32 bytes. 12 | 13 | let slice = buffer.slice(0, 32); 14 | let value = new Address(slice); 15 | return [new AddressValue(value), 32]; 16 | } 17 | 18 | /** 19 | * Reads and decodes an AddressValue from a given buffer. 20 | * 21 | * @param buffer the input buffer 22 | */ 23 | decodeTopLevel(buffer: Buffer): AddressValue { 24 | let [decoded, _length] = this.decodeNested(buffer); 25 | return decoded; 26 | } 27 | 28 | /** 29 | * Encodes an AddressValue to a buffer. 30 | */ 31 | encodeNested(primitive: AddressValue): Buffer { 32 | return primitive.valueOf().getPublicKey(); 33 | } 34 | 35 | /** 36 | * Encodes an AddressValue to a buffer. 37 | */ 38 | encodeTopLevel(primitive: AddressValue): Buffer { 39 | return primitive.valueOf().getPublicKey(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/abi/codec/arrayVec.ts: -------------------------------------------------------------------------------- 1 | import { TypedValue, ArrayVec, ArrayVecType } from "../typesystem"; 2 | import { BinaryCodec } from "./binary"; 3 | 4 | export class ArrayVecBinaryCodec { 5 | private readonly binaryCodec: BinaryCodec; 6 | 7 | constructor(binaryCodec: BinaryCodec) { 8 | this.binaryCodec = binaryCodec; 9 | } 10 | 11 | decodeNested(buffer: Buffer, type: ArrayVecType): [ArrayVec, number] { 12 | let arrayLength = type.length; 13 | let typeParameter = type.getFirstTypeParameter(); 14 | let result: TypedValue[] = []; 15 | let totalLength = 0; 16 | 17 | for (let i = 0; i < arrayLength; i++) { 18 | let [decoded, decodedLength] = this.binaryCodec.decodeNested(buffer, typeParameter); 19 | result.push(decoded); 20 | totalLength += decodedLength; 21 | buffer = buffer.slice(decodedLength); 22 | } 23 | 24 | return [new ArrayVec(type, result), totalLength]; 25 | } 26 | 27 | decodeTopLevel(buffer: Buffer, type: ArrayVecType): ArrayVec { 28 | let [result, _] = this.decodeNested(buffer, type); 29 | return result; 30 | } 31 | 32 | encodeNested(array: ArrayVec): Buffer { 33 | let itemsBuffers: Buffer[] = []; 34 | 35 | for (const item of array.getItems()) { 36 | let itemBuffer = this.binaryCodec.encodeNested(item); 37 | itemsBuffers.push(itemBuffer); 38 | } 39 | 40 | return Buffer.concat(itemsBuffers); 41 | } 42 | 43 | encodeTopLevel(array: ArrayVec): Buffer { 44 | return this.encodeNested(array); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/abi/codec/binaryCodecUtils.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { BigUIntType, BooleanType, BytesType } from "../typesystem"; 3 | import { BinaryCodec } from "./binary"; 4 | 5 | const Codec = new BinaryCodec(); 6 | 7 | export function decodeUnsignedNumber(buffer: Buffer): number { 8 | let value = Codec.decodeTopLevel(buffer, new BigUIntType()); 9 | let raw = Number(value.valueOf()); 10 | return raw; 11 | } 12 | 13 | export function decodeBool(buffer: Buffer): boolean { 14 | let value = Codec.decodeTopLevel(buffer, new BooleanType()); 15 | let raw = Boolean(value.valueOf()); 16 | return raw; 17 | } 18 | 19 | export function decodeString(buffer: Buffer): string { 20 | let value = Codec.decodeTopLevel(buffer, new BytesType()); 21 | let raw = String(value.valueOf()); 22 | return raw; 23 | } 24 | 25 | export function decodeBigNumber(buffer: Buffer): BigNumber { 26 | let value = Codec.decodeTopLevel(buffer, new BigUIntType()); 27 | let raw = new BigNumber(value.valueOf()); 28 | return raw; 29 | } 30 | -------------------------------------------------------------------------------- /src/abi/codec/boolean.ts: -------------------------------------------------------------------------------- 1 | import * as errors from "../../core/errors"; 2 | import { BooleanValue } from "../typesystem"; 3 | 4 | /** 5 | * Encodes and decodes "BooleanValue" objects. 6 | */ 7 | export class BooleanBinaryCodec { 8 | private static readonly TRUE: number = 0x01; 9 | private static readonly FALSE: number = 0x00; 10 | 11 | decodeNested(buffer: Buffer): [BooleanValue, number] { 12 | // We don't check the size of the buffer, we just read the first byte. 13 | 14 | let byte = buffer.readUInt8(0); 15 | return [new BooleanValue(byte == BooleanBinaryCodec.TRUE), 1]; 16 | } 17 | 18 | decodeTopLevel(buffer: Buffer): BooleanValue { 19 | if (buffer.length > 1) { 20 | throw new errors.ErrInvalidArgument("buffer should be of size <= 1"); 21 | } 22 | 23 | let firstByte = buffer[0]; 24 | return new BooleanValue(firstByte == BooleanBinaryCodec.TRUE); 25 | } 26 | 27 | encodeNested(primitive: BooleanValue): Buffer { 28 | if (primitive.isTrue()) { 29 | return Buffer.from([BooleanBinaryCodec.TRUE]); 30 | } 31 | 32 | return Buffer.from([BooleanBinaryCodec.FALSE]); 33 | } 34 | 35 | encodeTopLevel(primitive: BooleanValue): Buffer { 36 | if (primitive.isTrue()) { 37 | return Buffer.from([BooleanBinaryCodec.TRUE]); 38 | } 39 | 40 | return Buffer.from([]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/abi/codec/bytes.ts: -------------------------------------------------------------------------------- 1 | import { BytesValue } from "../typesystem/bytes"; 2 | import { SizeOfU32 } from "./constants"; 3 | 4 | /** 5 | * Encodes and decodes "BytesValue" objects. 6 | */ 7 | export class BytesBinaryCodec { 8 | decodeNested(buffer: Buffer): [BytesValue, number] { 9 | let length = buffer.readUInt32BE(0); 10 | let payload = buffer.slice(SizeOfU32, SizeOfU32 + length); 11 | let result = new BytesValue(payload); 12 | return [result, SizeOfU32 + length]; 13 | } 14 | 15 | decodeTopLevel(buffer: Buffer): BytesValue { 16 | return new BytesValue(buffer); 17 | } 18 | 19 | encodeNested(bytes: BytesValue): Buffer { 20 | let lengthBuffer = Buffer.alloc(SizeOfU32); 21 | lengthBuffer.writeUInt32BE(bytes.getLength()); 22 | let buffer = Buffer.concat([lengthBuffer, bytes.valueOf()]); 23 | return buffer; 24 | } 25 | 26 | encodeTopLevel(bytes: BytesValue): Buffer { 27 | return bytes.valueOf(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/abi/codec/codemetadata.ts: -------------------------------------------------------------------------------- 1 | import { CodeMetadata, CodeMetadataLength } from "../../core/codeMetadata"; 2 | import { CodeMetadataValue } from "../typesystem/codeMetadata"; 3 | 4 | export class CodeMetadataCodec { 5 | decodeNested(buffer: Buffer): [CodeMetadataValue, number] { 6 | const codeMetadata = CodeMetadata.newFromBytes(buffer.slice(0, CodeMetadataLength)); 7 | return [new CodeMetadataValue(codeMetadata), CodeMetadataLength]; 8 | } 9 | 10 | decodeTopLevel(buffer: Buffer): CodeMetadataValue { 11 | const codeMetadata = CodeMetadata.newFromBytes(buffer); 12 | return new CodeMetadataValue(codeMetadata); 13 | } 14 | 15 | encodeNested(codeMetadata: CodeMetadataValue): Buffer { 16 | return Buffer.from(codeMetadata.valueOf().toBytes()); 17 | } 18 | 19 | encodeTopLevel(codeMetadata: CodeMetadataValue): Buffer { 20 | return Buffer.from(codeMetadata.valueOf().toBytes()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/abi/codec/constants.ts: -------------------------------------------------------------------------------- 1 | export const SizeOfU32 = 4; 2 | -------------------------------------------------------------------------------- /src/abi/codec/explicit-enum.ts: -------------------------------------------------------------------------------- 1 | import { StringValue } from "../typesystem"; 2 | import { ExplicitEnumType, ExplicitEnumValue, ExplicitEnumVariantDefinition } from "../typesystem/explicit-enum"; 3 | import { StringBinaryCodec } from "./string"; 4 | 5 | export class ExplicitEnumBinaryCodec { 6 | private readonly stringCodec: StringBinaryCodec; 7 | 8 | constructor() { 9 | this.stringCodec = new StringBinaryCodec(); 10 | } 11 | 12 | decodeTopLevel(buffer: Buffer, type: ExplicitEnumType): ExplicitEnumValue { 13 | const stringValue = this.stringCodec.decodeTopLevel(buffer); 14 | return new ExplicitEnumValue(type, new ExplicitEnumVariantDefinition(stringValue.valueOf())); 15 | } 16 | 17 | decodeNested(buffer: Buffer, type: ExplicitEnumType): [ExplicitEnumValue, number] { 18 | const [value, length] = this.stringCodec.decodeNested(buffer); 19 | const enumValue = new ExplicitEnumValue(type, new ExplicitEnumVariantDefinition(value.valueOf())); 20 | 21 | return [enumValue, length]; 22 | } 23 | 24 | encodeNested(enumValue: ExplicitEnumValue): Buffer { 25 | const buffer = this.stringCodec.encodeNested(new StringValue(enumValue.valueOf().name)); 26 | return buffer; 27 | } 28 | 29 | encodeTopLevel(enumValue: ExplicitEnumValue): Buffer { 30 | const buffer = this.stringCodec.encodeTopLevel(new StringValue(enumValue.valueOf().name)); 31 | return buffer; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/abi/codec/fields.ts: -------------------------------------------------------------------------------- 1 | import { Field, FieldDefinition } from "../typesystem"; 2 | import { BinaryCodec } from "./binary"; 3 | 4 | export class FieldsBinaryCodec { 5 | private readonly binaryCodec: BinaryCodec; 6 | 7 | constructor(binaryCodec: BinaryCodec) { 8 | this.binaryCodec = binaryCodec; 9 | } 10 | 11 | decodeNested(buffer: Buffer, fieldDefinitions: FieldDefinition[]): [Field[], number] { 12 | let fields: Field[] = []; 13 | let totalLength = 0; 14 | 15 | for (const fieldDefinition of fieldDefinitions) { 16 | let [decoded, decodedLength] = this.binaryCodec.decodeNested(buffer, fieldDefinition.type); 17 | buffer = buffer.slice(decodedLength); 18 | totalLength += decodedLength; 19 | 20 | let field = new Field(decoded, fieldDefinition.name); 21 | fields.push(field); 22 | } 23 | 24 | return [fields, totalLength]; 25 | } 26 | 27 | encodeNested(fields: ReadonlyArray): Buffer { 28 | let buffers: Buffer[] = []; 29 | 30 | for (const field of fields) { 31 | let fieldBuffer = this.binaryCodec.encodeNested(field.value); 32 | buffers.push(fieldBuffer); 33 | } 34 | 35 | return Buffer.concat(buffers); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/abi/codec/h256.ts: -------------------------------------------------------------------------------- 1 | import { H256Value } from "../typesystem/h256"; 2 | 3 | export class H256BinaryCodec { 4 | /** 5 | * Reads and decodes a H256Value from a given buffer. 6 | * 7 | * @param buffer the input buffer 8 | */ 9 | decodeNested(buffer: Buffer): [H256Value, number] { 10 | // We don't check the size of the buffer, we just read 32 bytes. 11 | let slice = buffer.slice(0, 32); 12 | return [new H256Value(slice), 32]; 13 | } 14 | 15 | /** 16 | * Reads and decodes a H256Value from a given buffer. 17 | * 18 | * @param buffer the input buffer 19 | */ 20 | decodeTopLevel(buffer: Buffer): H256Value { 21 | let [decoded, _length] = this.decodeNested(buffer); 22 | return decoded; 23 | } 24 | 25 | /** 26 | * Encodes a H256Value to a buffer. 27 | */ 28 | encodeNested(primitive: H256Value): Buffer { 29 | return primitive.valueOf(); 30 | } 31 | 32 | /** 33 | * Encodes a H256Value to a buffer. 34 | */ 35 | encodeTopLevel(primitive: H256Value): Buffer { 36 | return primitive.valueOf(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/abi/codec/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @packageDocumentation 3 | * @module codec 4 | */ 5 | 6 | export * from "./binary"; 7 | export * from "./binaryCodecUtils"; 8 | -------------------------------------------------------------------------------- /src/abi/codec/managedDecimal.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { BigUIntType, BigUIntValue, ManagedDecimalType, ManagedDecimalValue, U32Value } from "../typesystem"; 3 | import { BinaryCodec } from "./binary"; 4 | import { bufferToBigInt } from "./utils"; 5 | import { SizeOfU32 } from "./constants"; 6 | 7 | export class ManagedDecimalCodec { 8 | private readonly binaryCodec: BinaryCodec; 9 | 10 | constructor(binaryCodec: BinaryCodec) { 11 | this.binaryCodec = binaryCodec; 12 | } 13 | 14 | decodeNested(buffer: Buffer, type: ManagedDecimalType): [ManagedDecimalValue, number] { 15 | const length = buffer.readUInt32BE(0); 16 | const payload = buffer.slice(0, length); 17 | 18 | const result = this.decodeTopLevel(payload, type); 19 | return [result, length]; 20 | } 21 | 22 | decodeTopLevel(buffer: Buffer, type: ManagedDecimalType): ManagedDecimalValue { 23 | if (buffer.length === 0) { 24 | return new ManagedDecimalValue(new BigNumber(0), 0); 25 | } 26 | 27 | if (type.isVariable()) { 28 | const bigUintSize = buffer.length - SizeOfU32; 29 | 30 | const [value] = this.binaryCodec.decodeNested(buffer.slice(0, bigUintSize), new BigUIntType()); 31 | const scale = buffer.readUInt32BE(bigUintSize); 32 | return new ManagedDecimalValue(value.valueOf().shiftedBy(-scale), scale); 33 | } 34 | 35 | const value = bufferToBigInt(buffer); 36 | const metadata = type.getMetadata(); 37 | const scale = metadata !== "usize" ? parseInt(metadata.toString()) : 0; 38 | return new ManagedDecimalValue(value.shiftedBy(-scale), scale); 39 | } 40 | 41 | encodeNested(value: ManagedDecimalValue): Buffer { 42 | let buffers: Buffer[] = []; 43 | const rawValue = new BigUIntValue(value.valueOf().shiftedBy(value.getScale())); 44 | if (value.isVariable()) { 45 | buffers.push(Buffer.from(this.binaryCodec.encodeNested(rawValue))); 46 | buffers.push(Buffer.from(this.binaryCodec.encodeNested(new U32Value(value.getScale())))); 47 | } else { 48 | buffers.push(this.binaryCodec.encodeTopLevel(rawValue)); 49 | } 50 | return Buffer.concat(buffers); 51 | } 52 | 53 | encodeTopLevel(value: ManagedDecimalValue): Buffer { 54 | return this.encodeNested(value); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/abi/codec/nothing.ts: -------------------------------------------------------------------------------- 1 | import { NothingValue } from "../typesystem"; 2 | 3 | export class NothingCodec { 4 | decodeNested(): [NothingValue, number] { 5 | return [new NothingValue(), 0]; 6 | } 7 | 8 | decodeTopLevel(): NothingValue { 9 | return new NothingValue(); 10 | } 11 | 12 | encodeNested(): Buffer { 13 | return Buffer.from([]); 14 | } 15 | 16 | encodeTopLevel(): Buffer { 17 | return Buffer.from([]); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/abi/codec/option.ts: -------------------------------------------------------------------------------- 1 | import * as errors from "../../core/errors"; 2 | import { OptionValue, Type } from "../typesystem"; 3 | import { BinaryCodec } from "./binary"; 4 | 5 | /** 6 | * Encodes and decodes "OptionValue" objects 7 | */ 8 | export class OptionValueBinaryCodec { 9 | private readonly binaryCodec: BinaryCodec; 10 | 11 | constructor(binaryCodec: BinaryCodec) { 12 | this.binaryCodec = binaryCodec; 13 | } 14 | 15 | decodeNested(buffer: Buffer, type: Type): [OptionValue, number] { 16 | if (buffer[0] == 0x00) { 17 | return [OptionValue.newMissingTyped(type), 1]; 18 | } 19 | 20 | if (buffer[0] != 0x01) { 21 | throw new errors.ErrCodec("invalid buffer for optional value"); 22 | } 23 | 24 | let [decoded, decodedLength] = this.binaryCodec.decodeNested(buffer.slice(1), type); 25 | return [OptionValue.newProvided(decoded), decodedLength + 1]; 26 | } 27 | 28 | decodeTopLevel(buffer: Buffer, type: Type): OptionValue { 29 | if (buffer.length == 0) { 30 | return new OptionValue(type); 31 | } 32 | 33 | if (buffer[0] != 0x01) { 34 | throw new errors.ErrCodec("invalid buffer for optional value"); 35 | } 36 | 37 | let [decoded, _decodedLength] = this.binaryCodec.decodeNested(buffer.slice(1), type); 38 | return new OptionValue(type, decoded); 39 | } 40 | 41 | encodeNested(optionValue: OptionValue): Buffer { 42 | if (optionValue.isSet()) { 43 | return Buffer.concat([Buffer.from([1]), this.binaryCodec.encodeNested(optionValue.getTypedValue())]); 44 | } 45 | 46 | return Buffer.from([0]); 47 | } 48 | 49 | encodeTopLevel(optionValue: OptionValue): Buffer { 50 | if (optionValue.isSet()) { 51 | return Buffer.concat([Buffer.from([1]), this.binaryCodec.encodeNested(optionValue.getTypedValue())]); 52 | } 53 | 54 | return Buffer.from([]); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/abi/codec/string.ts: -------------------------------------------------------------------------------- 1 | import { StringValue } from "../typesystem"; 2 | import { BytesValue } from "../typesystem/bytes"; 3 | import { BytesBinaryCodec } from "./bytes"; 4 | 5 | export class StringBinaryCodec { 6 | private readonly bytesBinaryCodec = new BytesBinaryCodec(); 7 | 8 | decodeNested(buffer: Buffer): [StringValue, number] { 9 | let [decoded, length] = this.bytesBinaryCodec.decodeNested(buffer); 10 | let decodedAsString = new StringValue(decoded.valueOf().toString()); 11 | return [decodedAsString, length]; 12 | } 13 | 14 | decodeTopLevel(buffer: Buffer): StringValue { 15 | return new StringValue(buffer.toString()); 16 | } 17 | 18 | encodeNested(value: StringValue): Buffer { 19 | let valueAsBytes = BytesValue.fromUTF8(value.valueOf()); 20 | return this.bytesBinaryCodec.encodeNested(valueAsBytes); 21 | } 22 | 23 | encodeTopLevel(value: StringValue): Buffer { 24 | return Buffer.from(value.valueOf()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/abi/codec/struct.ts: -------------------------------------------------------------------------------- 1 | import { StructType, Struct, Field } from "../typesystem"; 2 | import { BinaryCodec } from "./binary"; 3 | import { FieldsBinaryCodec } from "./fields"; 4 | 5 | export class StructBinaryCodec { 6 | private readonly fieldsCodec: FieldsBinaryCodec; 7 | 8 | constructor(binaryCodec: BinaryCodec) { 9 | this.fieldsCodec = new FieldsBinaryCodec(binaryCodec); 10 | } 11 | 12 | decodeTopLevel(buffer: Buffer, type: StructType): Struct { 13 | let [decoded] = this.decodeNested(buffer, type); 14 | return decoded; 15 | } 16 | 17 | decodeNested(buffer: Buffer, type: StructType): [Struct, number] { 18 | let fieldDefinitions = type.getFieldsDefinitions(); 19 | let [fields, offset]: [Field[], number] = this.fieldsCodec.decodeNested(buffer, fieldDefinitions); 20 | let struct = new Struct(type, fields); 21 | return [struct, offset]; 22 | } 23 | 24 | encodeNested(struct: Struct): Buffer { 25 | let fields = struct.getFields(); 26 | let buffer = this.fieldsCodec.encodeNested(fields); 27 | return buffer; 28 | } 29 | 30 | encodeTopLevel(struct: Struct): Buffer { 31 | return this.encodeNested(struct); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/abi/codec/tokenIdentifier.ts: -------------------------------------------------------------------------------- 1 | import { BytesValue } from "../typesystem/bytes"; 2 | import { TokenIdentifierValue } from "../typesystem/tokenIdentifier"; 3 | import { BytesBinaryCodec } from "./bytes"; 4 | 5 | export class TokenIdentifierCodec { 6 | private readonly bytesCodec = new BytesBinaryCodec(); 7 | 8 | decodeNested(buffer: Buffer): [TokenIdentifierValue, number] { 9 | let [bytesValue, length] = this.bytesCodec.decodeNested(buffer); 10 | return [new TokenIdentifierValue(bytesValue.toString()), length]; 11 | } 12 | 13 | decodeTopLevel(buffer: Buffer): TokenIdentifierValue { 14 | let bytesValue = this.bytesCodec.decodeTopLevel(buffer); 15 | return new TokenIdentifierValue(bytesValue.toString()); 16 | } 17 | 18 | encodeNested(tokenIdentifier: TokenIdentifierValue): Buffer { 19 | let bytesValue = BytesValue.fromUTF8(tokenIdentifier.valueOf()); 20 | return this.bytesCodec.encodeNested(bytesValue); 21 | } 22 | 23 | encodeTopLevel(tokenIdentifier: TokenIdentifierValue): Buffer { 24 | return Buffer.from(tokenIdentifier.valueOf()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/abi/codec/tuple.ts: -------------------------------------------------------------------------------- 1 | import { Struct, TupleType, Tuple } from "../typesystem"; 2 | import { BinaryCodec } from "./binary"; 3 | import { StructBinaryCodec } from "./struct"; 4 | 5 | export class TupleBinaryCodec { 6 | private structCodec: StructBinaryCodec; 7 | 8 | constructor(binaryCodec: BinaryCodec) { 9 | this.structCodec = new StructBinaryCodec(binaryCodec); 10 | } 11 | 12 | decodeTopLevel(buffer: Buffer, type: TupleType): Tuple { 13 | return this.structCodec.decodeTopLevel(buffer, type); 14 | } 15 | 16 | decodeNested(buffer: Buffer, type: TupleType): [Tuple, number] { 17 | return this.structCodec.decodeNested(buffer, type); 18 | } 19 | 20 | encodeNested(struct: Tuple): Buffer { 21 | return this.structCodec.encodeNested(struct); 22 | } 23 | 24 | encodeTopLevel(struct: Struct): Buffer { 25 | return this.structCodec.encodeTopLevel(struct); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/abi/codec/utils.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { numberToPaddedHex } from "../../core/utils.codec"; 3 | 4 | /** 5 | * Returns whether the most significant bit of a given byte (within a buffer) is 1. 6 | * @param buffer the buffer to test 7 | * @param byteIndex the index of the byte to test 8 | */ 9 | export function isMsbOne(buffer: Buffer, byteIndex: number = 0): boolean { 10 | let byte = buffer[byteIndex]; 11 | let bit = byte >> 7; 12 | let isSet = bit == 1; 13 | return isSet; 14 | } 15 | 16 | /** 17 | * Returns whether the most significant bit of a given byte (within a buffer) is 0. 18 | * @param buffer the buffer to test 19 | * @param byteIndex the index of the byte to test 20 | */ 21 | export function isMsbZero(buffer: Buffer, byteIndex: number = 0): boolean { 22 | return !isMsbOne(buffer, byteIndex); 23 | } 24 | 25 | export function cloneBuffer(buffer: Buffer) { 26 | let clone = Buffer.alloc(buffer.length); 27 | buffer.copy(clone); 28 | return clone; 29 | } 30 | 31 | export function bufferToBigInt(buffer: Buffer): BigNumber { 32 | // Currently, in JavaScript, this is the feasible way to achieve reliable, arbitrary-size Buffer to BigInt conversion. 33 | let hex = buffer.toString("hex"); 34 | return new BigNumber(`0x${hex}`, 16); 35 | } 36 | 37 | export function bigIntToBuffer(value: BigNumber.Value): Buffer { 38 | // Currently, in JavaScript, this is the feasible way to achieve reliable, arbitrary-size BigInt to Buffer conversion. 39 | let hex = getHexMagnitudeOfBigInt(value); 40 | return Buffer.from(hex, "hex"); 41 | } 42 | 43 | export function getHexMagnitudeOfBigInt(value: BigNumber.Value): string { 44 | value = new BigNumber(value); 45 | 46 | if (!value) { 47 | return ""; 48 | } 49 | 50 | if (value.isNegative()) { 51 | value = value.multipliedBy(new BigNumber(-1)); 52 | } 53 | 54 | return numberToPaddedHex(value); 55 | } 56 | 57 | export function flipBufferBitsInPlace(buffer: Buffer) { 58 | for (let i = 0; i < buffer.length; i++) { 59 | buffer[i] = ~buffer[i]; 60 | } 61 | } 62 | 63 | export function prependByteToBuffer(buffer: Buffer, byte: number) { 64 | return Buffer.concat([Buffer.from([byte]), buffer]); 65 | } 66 | -------------------------------------------------------------------------------- /src/abi/function.ts: -------------------------------------------------------------------------------- 1 | import * as errors from "../core/errors"; 2 | 3 | /** 4 | * A function of a Smart Contract, as an abstraction. 5 | */ 6 | export class ContractFunction { 7 | /** 8 | * The name of the function. 9 | */ 10 | readonly name: string; 11 | 12 | /** 13 | * Creates a ContractFunction object, given its name. 14 | * 15 | * @param name the name of the function 16 | */ 17 | constructor(name: string) { 18 | this.name = name; 19 | 20 | if (name == null) { 21 | throw new errors.ErrInvalidFunctionName(); 22 | } 23 | } 24 | 25 | /** 26 | * Null-object pattern: creates an empty ContractFunction object. 27 | */ 28 | static none(): ContractFunction { 29 | return new ContractFunction("untitled"); 30 | } 31 | 32 | /** 33 | * Returns the name of the function. 34 | */ 35 | toString() { 36 | return this.name; 37 | } 38 | 39 | valueOf(): string { 40 | return this.name; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/abi/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./argSerializer"; 2 | export * from "./argumentErrorContext"; 3 | export * from "./code"; 4 | export * from "./codec"; 5 | export * from "./function"; 6 | export * from "./interaction"; 7 | export * from "./interface"; 8 | export * from "./nativeSerializer"; 9 | export * from "./query"; 10 | export * from "./returnCode"; 11 | export * from "./smartContract"; 12 | export * from "./typesystem"; 13 | -------------------------------------------------------------------------------- /src/abi/query.spec.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { assert } from "chai"; 3 | import { Address } from "../core/address"; 4 | import { ContractFunction } from "./function"; 5 | import { Query } from "./query"; 6 | import { BigUIntValue, U32Value } from "./typesystem"; 7 | import { BytesValue } from "./typesystem/bytes"; 8 | 9 | describe("test smart contract queries", () => { 10 | it("should getEncodedArguments()", async () => { 11 | let query = new Query({ 12 | func: new ContractFunction("foo"), 13 | address: new Address("erd1qqqqqqqqqqqqqpgq3ytm9m8dpeud35v3us20vsafp77smqghd8ss4jtm0q"), 14 | args: [ 15 | new U32Value(100), 16 | BytesValue.fromUTF8("!"), 17 | BytesValue.fromHex("abba"), 18 | new BigUIntValue(new BigNumber("1000000000000000000000000000000000")), 19 | ], 20 | }); 21 | 22 | let args = query.getEncodedArguments(); 23 | assert.lengthOf(args, 4); 24 | assert.equal(args[0], "64"); 25 | assert.equal(args[1], "21"); 26 | assert.equal(args[2], "abba"); 27 | assert.equal(args[3], "314dc6448d9338c15b0a00000000"); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/abi/query.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { ArgSerializer } from "./argSerializer"; 3 | import { IContractFunction } from "./interface"; 4 | import { TypedValue } from "./typesystem"; 5 | 6 | export class Query { 7 | caller: Address; 8 | address: Address; 9 | func: IContractFunction; 10 | args: TypedValue[]; 11 | value: bigint; 12 | 13 | constructor(obj: { 14 | caller?: Address; 15 | address: Address; 16 | func: IContractFunction; 17 | args?: TypedValue[]; 18 | value?: bigint; 19 | }) { 20 | this.caller = obj.caller || Address.empty(); 21 | this.address = obj.address; 22 | this.func = obj.func; 23 | this.args = obj.args || []; 24 | this.value = obj.value || 0n; 25 | } 26 | 27 | getEncodedArguments(): string[] { 28 | return new ArgSerializer().valuesToStrings(this.args); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/abi/returnCode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Also see: https://github.com/multiversx/mx-chain-vm-common-go/blob/master/returnCodes.go 3 | */ 4 | export class ReturnCode { 5 | static None = new ReturnCode(""); 6 | static Ok = new ReturnCode("ok"); 7 | static FunctionNotFound = new ReturnCode("function not found"); 8 | static FunctionWrongSignature = new ReturnCode("wrong signature for function"); 9 | static ContractNotFound = new ReturnCode("contract not found"); 10 | static UserError = new ReturnCode("user error"); 11 | static OutOfGas = new ReturnCode("out of gas"); 12 | static AccountCollision = new ReturnCode("account collision"); 13 | static OutOfFunds = new ReturnCode("out of funds"); 14 | static CallStackOverFlow = new ReturnCode("call stack overflow"); 15 | static ContractInvalid = new ReturnCode("contract invalid"); 16 | static ExecutionFailed = new ReturnCode("execution failed"); 17 | static Unknown = new ReturnCode("unknown"); 18 | 19 | private readonly text: string; 20 | 21 | constructor(text: string) { 22 | this.text = text; 23 | } 24 | 25 | static fromBuffer(buffer: Buffer): ReturnCode { 26 | let text = buffer.toString(); 27 | return new ReturnCode(text); 28 | } 29 | 30 | toString(): string { 31 | return this.text; 32 | } 33 | 34 | valueOf(): string { 35 | return this.text; 36 | } 37 | 38 | equals(other: ReturnCode): boolean { 39 | if (!other) { 40 | return false; 41 | } 42 | 43 | return this.text == other.text; 44 | } 45 | 46 | isSuccess(): boolean { 47 | return this.equals(ReturnCode.Ok) || this.equals(ReturnCode.None); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/abi/typeFormula.ts: -------------------------------------------------------------------------------- 1 | export class TypeFormula { 2 | name: string; 3 | metadata: any; 4 | typeParameters: TypeFormula[]; 5 | 6 | constructor(name: string, typeParameters: TypeFormula[], metadata?: any) { 7 | this.name = name; 8 | this.typeParameters = typeParameters; 9 | this.metadata = metadata; 10 | } 11 | 12 | toString(): string { 13 | const hasTypeParameters = this.typeParameters.length > 0; 14 | const typeParameters = hasTypeParameters 15 | ? `<${this.typeParameters.map((tp) => tp.toString()).join(", ")}>` 16 | : ""; 17 | const baseName = `${this.name}${typeParameters}`; 18 | 19 | return this.metadata !== undefined ? `${baseName}*${this.metadata}*` : baseName; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/abi/typeFormulaParser.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { TypeFormulaParser } from "./typeFormulaParser"; 3 | 4 | describe("test type formula parser", () => { 5 | it("should parse expression", async () => { 6 | const parser = new TypeFormulaParser(); 7 | 8 | const testVectors = [ 9 | ["i64", "i64"], 10 | [" i64 ", "i64"], 11 | ["utf-8 string", "utf-8 string"], 12 | ["MultiResultVec>", "MultiResultVec>"], 13 | ["tuple3>", "tuple3>"], 14 | ["tuple2", "tuple2"], 15 | ["tuple2 ", "tuple2"], 16 | ["tuple, List>", "tuple, List>"], 17 | ]; 18 | 19 | for (const [inputExpression, expectedExpression] of testVectors) { 20 | const typeFormula = parser.parseExpression(inputExpression); 21 | const outputExpression = typeFormula.toString(); 22 | assert.equal(outputExpression, expectedExpression); 23 | } 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /src/abi/typesystem/address.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../../core/address"; 2 | import { PrimitiveType, PrimitiveValue } from "./types"; 3 | 4 | export class AddressType extends PrimitiveType { 5 | static ClassName = "AddressType"; 6 | 7 | constructor() { 8 | super("Address"); 9 | } 10 | 11 | getClassName(): string { 12 | return AddressType.ClassName; 13 | } 14 | } 15 | 16 | /** 17 | * An address fed to or fetched from a Smart Contract contract, as an immutable abstraction. 18 | */ 19 | export class AddressValue extends PrimitiveValue { 20 | static ClassName = "AddressValue"; 21 | private readonly value: Address; 22 | 23 | constructor(value: Address) { 24 | super(new AddressType()); 25 | this.value = Address.newFromBech32(value.toBech32()); 26 | } 27 | 28 | getClassName(): string { 29 | return AddressValue.ClassName; 30 | } 31 | 32 | /** 33 | * Returns whether two objects have the same value. 34 | * 35 | * @param other another AddressValue 36 | */ 37 | equals(other: AddressValue): boolean { 38 | return this.value.equals(other.value); 39 | } 40 | 41 | valueOf(): Address { 42 | return this.value; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/abi/typesystem/algebraic.ts: -------------------------------------------------------------------------------- 1 | import { guardValueIsSet } from "../../core/utils"; 2 | import { NullType, Type, TypeCardinality, TypedValue } from "./types"; 3 | 4 | /** 5 | * An optional is an algebraic type. It holds zero or one values. 6 | */ 7 | export class OptionalType extends Type { 8 | static ClassName = "OptionalType"; 9 | 10 | constructor(typeParameter: Type) { 11 | super("Optional", [typeParameter], TypeCardinality.variable(1)); 12 | } 13 | 14 | getClassName(): string { 15 | return OptionalType.ClassName; 16 | } 17 | 18 | isAssignableFrom(type: Type): boolean { 19 | if (!type.hasExactClass(OptionalType.ClassName)) { 20 | return false; 21 | } 22 | 23 | let invariantTypeParameters = this.getFirstTypeParameter().equals(type.getFirstTypeParameter()); 24 | let fakeCovarianceToNull = type.getFirstTypeParameter().hasExactClass(NullType.ClassName); 25 | return invariantTypeParameters || fakeCovarianceToNull; 26 | } 27 | } 28 | 29 | export class OptionalValue extends TypedValue { 30 | static ClassName = "OptionalValue"; 31 | private readonly value: TypedValue | null; 32 | 33 | constructor(type: OptionalType, value: TypedValue | null = null) { 34 | super(type); 35 | 36 | // TODO: assert value is of type type.getFirstTypeParameter() 37 | 38 | this.value = value; 39 | } 40 | 41 | getClassName(): string { 42 | return OptionalValue.ClassName; 43 | } 44 | 45 | /** 46 | * Creates an OptionalValue, as not provided (missing). 47 | */ 48 | static newMissing(): OptionalValue { 49 | let type = new OptionalType(new NullType()); 50 | return new OptionalValue(type); 51 | } 52 | 53 | isSet(): boolean { 54 | return this.value ? true : false; 55 | } 56 | 57 | getTypedValue(): TypedValue { 58 | guardValueIsSet("value", this.value); 59 | return this.value!; 60 | } 61 | 62 | valueOf(): any { 63 | return this.value ? this.value.valueOf() : null; 64 | } 65 | 66 | equals(other: OptionalValue): boolean { 67 | return this.value?.equals(other.value) || false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/abi/typesystem/boolean.ts: -------------------------------------------------------------------------------- 1 | import { PrimitiveType, PrimitiveValue } from "./types"; 2 | 3 | export class BooleanType extends PrimitiveType { 4 | static ClassName = "BooleanType"; 5 | 6 | constructor() { 7 | super("bool"); 8 | } 9 | 10 | getClassName(): string { 11 | return BooleanType.ClassName; 12 | } 13 | } 14 | 15 | /** 16 | * A boolean value fed to or fetched from a Smart Contract contract, as an immutable abstraction. 17 | */ 18 | export class BooleanValue extends PrimitiveValue { 19 | static ClassName = "BooleanValue"; 20 | private readonly value: boolean; 21 | 22 | constructor(value: boolean) { 23 | super(new BooleanType()); 24 | this.value = value; 25 | } 26 | 27 | getClassName(): string { 28 | return BooleanValue.ClassName; 29 | } 30 | 31 | /** 32 | * Returns whether two objects have the same value. 33 | * 34 | * @param other another BooleanValue 35 | */ 36 | equals(other: BooleanValue): boolean { 37 | return this.value === other.value; 38 | } 39 | 40 | isTrue(): boolean { 41 | return this.value === true; 42 | } 43 | 44 | isFalse(): boolean { 45 | return !this.isTrue(); 46 | } 47 | 48 | valueOf(): boolean { 49 | return this.value; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/abi/typesystem/bytes.ts: -------------------------------------------------------------------------------- 1 | import { PrimitiveType, PrimitiveValue } from "./types"; 2 | 3 | export class BytesType extends PrimitiveType { 4 | static ClassName = "BytesType"; 5 | 6 | constructor() { 7 | super("bytes"); 8 | } 9 | 10 | getClassName(): string { 11 | return BytesType.ClassName; 12 | } 13 | } 14 | 15 | export class BytesValue extends PrimitiveValue { 16 | static ClassName = "BytesValue"; 17 | private readonly value: Buffer; 18 | 19 | constructor(value: Buffer) { 20 | super(new BytesType()); 21 | this.value = value; 22 | } 23 | 24 | getClassName(): string { 25 | return BytesValue.ClassName; 26 | } 27 | 28 | /** 29 | * Creates a BytesValue from a utf-8 string. 30 | */ 31 | static fromUTF8(value: string): BytesValue { 32 | let buffer = Buffer.from(value, "utf-8"); 33 | return new BytesValue(buffer); 34 | } 35 | 36 | /** 37 | * Creates a BytesValue from a hex-encoded string. 38 | */ 39 | static fromHex(value: string): BytesValue { 40 | let buffer = Buffer.from(value, "hex"); 41 | return new BytesValue(buffer); 42 | } 43 | 44 | getLength(): number { 45 | return this.value.length; 46 | } 47 | 48 | /** 49 | * Returns whether two objects have the same value. 50 | */ 51 | equals(other: BytesValue): boolean { 52 | if (this.getLength() != other.getLength()) { 53 | return false; 54 | } 55 | 56 | return this.value.equals(other.value); 57 | } 58 | 59 | valueOf(): Buffer { 60 | return this.value; 61 | } 62 | 63 | toString() { 64 | return this.value.toString(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/abi/typesystem/codeMetadata.ts: -------------------------------------------------------------------------------- 1 | import { CodeMetadata } from "../../core"; 2 | import { PrimitiveType, PrimitiveValue } from "./types"; 3 | 4 | export class CodeMetadataType extends PrimitiveType { 5 | constructor() { 6 | super("CodeMetadata"); 7 | } 8 | } 9 | 10 | export class CodeMetadataValue extends PrimitiveValue { 11 | private readonly value: CodeMetadata; 12 | 13 | constructor(value: CodeMetadata) { 14 | super(new CodeMetadataType()); 15 | this.value = value; 16 | } 17 | 18 | equals(other: CodeMetadataValue): boolean { 19 | return this.value.equals(other.value); 20 | } 21 | 22 | valueOf(): CodeMetadata { 23 | return this.value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/abi/typesystem/collections.ts: -------------------------------------------------------------------------------- 1 | import { TypedValue } from "./types"; 2 | 3 | export class CollectionOfTypedValues { 4 | private readonly items: TypedValue[]; 5 | 6 | constructor(items: TypedValue[]) { 7 | this.items = items; 8 | } 9 | 10 | getLength(): number { 11 | return this.items.length; 12 | } 13 | 14 | getItems(): ReadonlyArray { 15 | return this.items; 16 | } 17 | 18 | valueOf(): any[] { 19 | return this.items.map((item) => item.valueOf()); 20 | } 21 | 22 | equals(other: CollectionOfTypedValues): boolean { 23 | if (this.getLength() != other.getLength()) { 24 | return false; 25 | } 26 | 27 | for (let i = 0; i < this.getLength(); i++) { 28 | let selfItem = this.items[i]; 29 | let otherItem = other.items[i]; 30 | 31 | if (!selfItem.equals(otherItem)) { 32 | return false; 33 | } 34 | } 35 | 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/abi/typesystem/composite.ts: -------------------------------------------------------------------------------- 1 | import { guardLength } from "../../core/utils"; 2 | import { Type, TypeCardinality, TypedValue } from "./types"; 3 | 4 | export class CompositeType extends Type { 5 | static ClassName = "CompositeType"; 6 | 7 | constructor(...typeParameters: Type[]) { 8 | super("Composite", typeParameters, TypeCardinality.variable(typeParameters.length)); 9 | } 10 | 11 | getClassName(): string { 12 | return CompositeType.ClassName; 13 | } 14 | } 15 | 16 | export class CompositeValue extends TypedValue { 17 | static ClassName = "CompositeValue"; 18 | private readonly items: TypedValue[]; 19 | 20 | constructor(type: CompositeType, items: TypedValue[]) { 21 | super(type); 22 | 23 | guardLength(items, type.getTypeParameters().length); 24 | 25 | // TODO: assert type of each item (wrt. type.getTypeParameters()). 26 | 27 | this.items = items; 28 | } 29 | 30 | getClassName(): string { 31 | return CompositeValue.ClassName; 32 | } 33 | 34 | static fromItems(...items: TypedValue[]): CompositeValue { 35 | let typeParameters = items.map((value) => value.getType()); 36 | let type = new CompositeType(...typeParameters); 37 | return new CompositeValue(type, items); 38 | } 39 | 40 | getItems(): ReadonlyArray { 41 | return this.items; 42 | } 43 | 44 | valueOf(): any[] { 45 | return this.items.map((item) => item?.valueOf()); 46 | } 47 | 48 | equals(other: CompositeValue): boolean { 49 | if (this.getType().differs(other.getType())) { 50 | return false; 51 | } 52 | 53 | for (let i = 0; i < this.items.length; i++) { 54 | let selfItem = this.items[i]; 55 | let otherItem = other.items[i]; 56 | 57 | if (!selfItem.equals(otherItem)) { 58 | return false; 59 | } 60 | } 61 | 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/abi/typesystem/endpoint.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { EndpointDefinition } from "./endpoint"; 3 | 4 | describe("test endpoint", () => { 5 | it("should handle an only-owner modifier", async () => { 6 | const actual = EndpointDefinition.fromJSON({ 7 | name: "foo", 8 | onlyOwner: true, 9 | mutability: "payable", 10 | payableInTokens: [], 11 | inputs: [], 12 | outputs: [], 13 | }); 14 | 15 | assert.isTrue(actual.modifiers.onlyOwner); 16 | assert.isTrue(actual.modifiers.isOnlyOwner()); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/abi/typesystem/event.ts: -------------------------------------------------------------------------------- 1 | import { TypeExpressionParser } from "./typeExpressionParser"; 2 | import { Type } from "./types"; 3 | 4 | const NamePlaceholder = "?"; 5 | 6 | export class EventDefinition { 7 | readonly identifier: string; 8 | readonly inputs: EventTopicDefinition[] = []; 9 | 10 | constructor(identifier: string, inputs: EventTopicDefinition[]) { 11 | this.identifier = identifier; 12 | this.inputs = inputs || []; 13 | } 14 | 15 | static fromJSON(json: { identifier: string; inputs: any[] }): EventDefinition { 16 | json.identifier = json.identifier == null ? NamePlaceholder : json.identifier; 17 | json.inputs = json.inputs || []; 18 | 19 | const inputs = json.inputs.map((param) => EventTopicDefinition.fromJSON(param)); 20 | return new EventDefinition(json.identifier, inputs); 21 | } 22 | } 23 | 24 | export class EventTopicDefinition { 25 | readonly name: string; 26 | readonly type: Type; 27 | readonly indexed: boolean; 28 | 29 | constructor(options: { name: string; type: Type; indexed: boolean }) { 30 | this.name = options.name; 31 | this.type = options.type; 32 | this.indexed = options.indexed; 33 | } 34 | 35 | static fromJSON(json: { name?: string; type: string; indexed: boolean }): EventTopicDefinition { 36 | const parsedType = new TypeExpressionParser().parse(json.type); 37 | 38 | return new EventTopicDefinition({ 39 | name: json.name || NamePlaceholder, 40 | type: parsedType, 41 | indexed: json.indexed, 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/abi/typesystem/explicit-enum.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { ExplicitEnumType, ExplicitEnumValue, ExplicitEnumVariantDefinition } from "./explicit-enum"; 3 | 4 | describe("test explicit-enums", () => { 5 | it("should get valueOf()", () => { 6 | // Define variants 7 | let greenVariant = new ExplicitEnumVariantDefinition("Green"); 8 | 9 | let orangeVariant = new ExplicitEnumVariantDefinition("Orange"); 10 | 11 | let yellowVariant = new ExplicitEnumVariantDefinition("Yellow"); 12 | 13 | // Define enum type 14 | let explicitEnumType = new ExplicitEnumType("Colour", [greenVariant, orangeVariant, yellowVariant]); 15 | 16 | // Create enum values 17 | let green = new ExplicitEnumValue(explicitEnumType, greenVariant); 18 | 19 | // Test valueOf() 20 | assert.deepEqual(green.valueOf(), { name: "Green" }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/abi/typesystem/factory.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { Address } from "../../core/address"; 3 | import { AddressType } from "./address"; 4 | import { createListOfAddresses, createListOfTokenIdentifiers } from "./factory"; 5 | import { ListType } from "./generic"; 6 | import { TokenIdentifierType } from "./tokenIdentifier"; 7 | 8 | describe("test factory", () => { 9 | it("should create lists of addresses", () => { 10 | let addresses = [ 11 | new Address("erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha"), 12 | new Address("erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede"), 13 | new Address("erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan"), 14 | ]; 15 | 16 | let list = createListOfAddresses(addresses); 17 | assert.deepEqual(list.getType(), new ListType(new AddressType())); 18 | assert.deepEqual(list.valueOf(), addresses); 19 | }); 20 | 21 | it("should create lists of token identifiers", () => { 22 | let identifiers = ["RIDE-7d18e9", "MEX-455c57"]; 23 | let list = createListOfTokenIdentifiers(identifiers); 24 | assert.deepEqual(list.getType(), new ListType(new TokenIdentifierType())); 25 | assert.deepEqual(list.valueOf(), identifiers); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/abi/typesystem/factory.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../../core/address"; 2 | import { AddressValue } from "./address"; 3 | import { List } from "./generic"; 4 | import { TokenIdentifierValue } from "./tokenIdentifier"; 5 | 6 | export function createListOfAddresses(addresses: Address[]): List { 7 | let addressesTyped = addresses.map((address) => new AddressValue(address)); 8 | let list = List.fromItems(addressesTyped); 9 | return list; 10 | } 11 | 12 | export function createListOfTokenIdentifiers(identifiers: string[]): List { 13 | let identifiersTyped = identifiers.map((identifier) => new TokenIdentifierValue(identifier)); 14 | let list = List.fromItems(identifiersTyped); 15 | return list; 16 | } 17 | -------------------------------------------------------------------------------- /src/abi/typesystem/genericArray.ts: -------------------------------------------------------------------------------- 1 | import { guardLength, guardTrue } from "../../core/utils"; 2 | import { CollectionOfTypedValues } from "./collections"; 3 | import { Type, TypedValue } from "./types"; 4 | 5 | // A type for known-length arrays. E.g. "array20", "array32", "array64" etc. 6 | export class ArrayVecType extends Type { 7 | static ClassName = "ArrayVecType"; 8 | readonly length: number; 9 | 10 | constructor(length: number, typeParameter: Type) { 11 | super("Array", [typeParameter]); 12 | 13 | guardTrue(length > 0, "array length > 0"); 14 | this.length = length; 15 | } 16 | 17 | getClassName(): string { 18 | return ArrayVecType.ClassName; 19 | } 20 | } 21 | 22 | export class ArrayVec extends TypedValue { 23 | static ClassName = "ArrayVec"; 24 | private readonly backingCollection: CollectionOfTypedValues; 25 | 26 | constructor(type: ArrayVecType, items: TypedValue[]) { 27 | super(type); 28 | guardLength(items, type.length); 29 | this.backingCollection = new CollectionOfTypedValues(items); 30 | } 31 | 32 | getClassName(): string { 33 | return ArrayVec.ClassName; 34 | } 35 | 36 | getLength(): number { 37 | return this.backingCollection.getLength(); 38 | } 39 | 40 | getItems(): ReadonlyArray { 41 | return this.backingCollection.getItems(); 42 | } 43 | 44 | valueOf(): any[] { 45 | return this.backingCollection.valueOf(); 46 | } 47 | 48 | equals(other: ArrayVec): boolean { 49 | return this.backingCollection.equals(other.backingCollection); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/abi/typesystem/h256.ts: -------------------------------------------------------------------------------- 1 | import { PrimitiveType, PrimitiveValue } from "./types"; 2 | 3 | export class H256Type extends PrimitiveType { 4 | static ClassName = "H256Type"; 5 | 6 | constructor() { 7 | super("H256"); 8 | } 9 | 10 | getClassName(): string { 11 | return H256Type.ClassName; 12 | } 13 | } 14 | 15 | export class H256Value extends PrimitiveValue { 16 | static ClassName = "H256Value"; 17 | private readonly value: Buffer; 18 | 19 | constructor(value: Buffer) { 20 | super(new H256Type()); 21 | this.value = value; 22 | } 23 | 24 | getClassName(): string { 25 | return H256Value.ClassName; 26 | } 27 | 28 | /** 29 | * Returns whether two objects have the same value. 30 | */ 31 | equals(other: H256Value): boolean { 32 | return this.value.equals(other.value); 33 | } 34 | 35 | valueOf(): Buffer { 36 | return this.value; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/abi/typesystem/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @packageDocumentation 3 | * @module typesystem 4 | */ 5 | 6 | export * from "./abi"; 7 | export * from "./address"; 8 | export * from "./algebraic"; 9 | export * from "./boolean"; 10 | export * from "./bytes"; 11 | export * from "./codeMetadata"; 12 | export * from "./composite"; 13 | export * from "./endpoint"; 14 | export * from "./enum"; 15 | export * from "./event"; 16 | export * from "./explicit-enum"; 17 | export * from "./factory"; 18 | export * from "./fields"; 19 | export * from "./generic"; 20 | export * from "./genericArray"; 21 | export * from "./h256"; 22 | export * from "./managedDecimal"; 23 | export * from "./managedDecimalSigned"; 24 | export * from "./matchers"; 25 | export * from "./nothing"; 26 | export * from "./numerical"; 27 | export * from "./string"; 28 | export * from "./struct"; 29 | export * from "./tokenIdentifier"; 30 | export * from "./tuple"; 31 | export * from "./typeExpressionParser"; 32 | export * from "./typeMapper"; 33 | export * from "./types"; 34 | export * from "./variadic"; 35 | -------------------------------------------------------------------------------- /src/abi/typesystem/managedDecimal.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { ManagedDecimalType, ManagedDecimalValue } from "./managedDecimal"; 3 | 4 | describe("test managed decimal", () => { 5 | it("should get correct metadata set", () => { 6 | const type = new ManagedDecimalType(8); 7 | const expectedMetadata = 8; 8 | 9 | assert.equal(type.getMetadata(), expectedMetadata); 10 | assert.isFalse(type.isVariable()); 11 | }); 12 | 13 | it("should get correct metadata set when variable", () => { 14 | let type = new ManagedDecimalType("usize"); 15 | const expectedMetadata = "usize"; 16 | 17 | assert.equal(type.getMetadata(), expectedMetadata); 18 | assert.isTrue(type.isVariable()); 19 | }); 20 | 21 | it("should return the expected values for scale and metadata", () => { 22 | const firstValue = new ManagedDecimalValue("1", 2, false); 23 | const secondValue = new ManagedDecimalValue("2", 2, false); 24 | const expectedMetadata = 2; 25 | const type = firstValue.getType(); 26 | 27 | assert.equal(type.getMetadata(), expectedMetadata); 28 | assert.isFalse(firstValue.isVariable()); 29 | assert.equal(firstValue.getScale(), 2); 30 | assert.equal(firstValue.toString(), "1.00"); 31 | assert.isFalse(firstValue.equals(secondValue)); 32 | }); 33 | 34 | it("should compare correctly two managed decimals even with different scale", () => { 35 | const firstValue = new ManagedDecimalValue("1.234", 3, false); 36 | const secondValue = new ManagedDecimalValue("12.34", 2, false); 37 | 38 | assert.isFalse(firstValue.equals(secondValue)); 39 | }); 40 | 41 | it("should compare correctly two managed decimals even with different scale", () => { 42 | const firstValue = new ManagedDecimalValue("1.234", 3, false); 43 | const secondValue = new ManagedDecimalValue("1.234", 3, false); 44 | 45 | assert.isTrue(firstValue.equals(secondValue)); 46 | }); 47 | 48 | it("should set the correct scale when variable decimals", () => { 49 | const value = new ManagedDecimalValue("1.3", 2, true); 50 | 51 | assert.isTrue(value.isVariable()); 52 | assert.equal(value.toString(), "1.30"); 53 | assert.equal(value.getScale(), 2); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/abi/typesystem/managedDecimal.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { Type, TypedValue } from "./types"; 3 | 4 | export class ManagedDecimalType extends Type { 5 | static ClassName = "ManagedDecimalType"; 6 | 7 | constructor(metadata: number | "usize") { 8 | super("ManagedDecimal", undefined, undefined, metadata); 9 | } 10 | 11 | getClassName(): string { 12 | return ManagedDecimalType.ClassName; 13 | } 14 | 15 | getMetadata(): number | "usize" { 16 | return this.metadata; 17 | } 18 | 19 | isVariable(): boolean { 20 | return this.metadata == "usize"; 21 | } 22 | } 23 | 24 | export class ManagedDecimalValue extends TypedValue { 25 | static ClassName = "ManagedDecimalValue"; 26 | private readonly value: BigNumber; 27 | private readonly scale: number; 28 | private readonly variable: boolean; 29 | 30 | constructor(value: BigNumber.Value, scale: number, isVariable: boolean = false) { 31 | super(new ManagedDecimalType(isVariable ? "usize" : scale)); 32 | this.value = new BigNumber(value); 33 | this.scale = scale; 34 | this.variable = isVariable; 35 | } 36 | 37 | getClassName(): string { 38 | return ManagedDecimalValue.ClassName; 39 | } 40 | 41 | getScale(): number { 42 | return this.scale; 43 | } 44 | 45 | getPrecision(): number { 46 | return this.value.toFixed(this.scale).replace(".", "").length; 47 | } 48 | 49 | /** 50 | * Returns whether two objects have the same value. 51 | */ 52 | equals(other: ManagedDecimalValue): boolean { 53 | if (this.getPrecision() != other.getPrecision()) { 54 | return false; 55 | } 56 | 57 | return new BigNumber(this.value).eq(other.value); 58 | } 59 | 60 | valueOf(): BigNumber { 61 | return this.value; 62 | } 63 | 64 | toString(): string { 65 | return this.value.toFixed(this.scale); 66 | } 67 | 68 | isVariable(): boolean { 69 | return this.variable; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/abi/typesystem/managedDecimalSigned.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { Type, TypedValue } from "./types"; 3 | 4 | export class ManagedDecimalSignedType extends Type { 5 | static ClassName = "ManagedDecimalSignedType"; 6 | 7 | constructor(metadata: number | "usize") { 8 | super("ManagedDecimalSigned", undefined, undefined, metadata); 9 | } 10 | 11 | getClassName(): string { 12 | return ManagedDecimalSignedType.ClassName; 13 | } 14 | 15 | getMetadata(): number | "usize" { 16 | return this.metadata; 17 | } 18 | 19 | isVariable(): boolean { 20 | return this.metadata == "usize"; 21 | } 22 | } 23 | 24 | export class ManagedDecimalSignedValue extends TypedValue { 25 | static ClassName = "ManagedDecimalSignedValue"; 26 | private readonly value: BigNumber; 27 | private readonly scale: number; 28 | private readonly variable: boolean; 29 | 30 | constructor(value: BigNumber.Value, scale: number, isVariable: boolean = false) { 31 | super(new ManagedDecimalSignedType(isVariable ? "usize" : scale)); 32 | this.value = new BigNumber(value); 33 | this.scale = scale; 34 | this.variable = isVariable; 35 | } 36 | 37 | getClassName(): string { 38 | return ManagedDecimalSignedValue.ClassName; 39 | } 40 | 41 | getPrecision(): number { 42 | return this.value.toFixed(this.scale).replace(".", "").length; 43 | } 44 | 45 | getScale(): number { 46 | return this.scale; 47 | } 48 | 49 | /** 50 | * Returns whether two objects have the same value. 51 | */ 52 | equals(other: ManagedDecimalSignedValue): boolean { 53 | if (this.getPrecision() != other.getPrecision()) { 54 | return false; 55 | } 56 | 57 | return new BigNumber(this.value).eq(other.value); 58 | } 59 | 60 | valueOf(): BigNumber { 61 | return this.value; 62 | } 63 | 64 | toString(): string { 65 | return this.value.toFixed(this.scale); 66 | } 67 | 68 | isVariable(): boolean { 69 | return this.variable; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/abi/typesystem/nothing.ts: -------------------------------------------------------------------------------- 1 | import { PrimitiveType, PrimitiveValue } from "./types"; 2 | 3 | export class NothingType extends PrimitiveType { 4 | static ClassName = "NothingType"; 5 | 6 | constructor() { 7 | super("nothing"); 8 | } 9 | 10 | getClassName(): string { 11 | return NothingType.ClassName; 12 | } 13 | } 14 | 15 | export class NothingValue extends PrimitiveValue { 16 | static ClassName = "NothingValue"; 17 | 18 | constructor() { 19 | super(new NothingType()); 20 | } 21 | 22 | getClassName(): string { 23 | return NothingValue.ClassName; 24 | } 25 | 26 | equals(_other: NothingValue): boolean { 27 | return false; 28 | } 29 | 30 | valueOf(): any { 31 | return {}; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/abi/typesystem/string.ts: -------------------------------------------------------------------------------- 1 | import { PrimitiveType, PrimitiveValue } from "./types"; 2 | 3 | export class StringType extends PrimitiveType { 4 | static ClassName = "StringType"; 5 | 6 | constructor() { 7 | super("utf-8 string"); 8 | } 9 | 10 | getClassName(): string { 11 | return StringType.ClassName; 12 | } 13 | } 14 | 15 | export class StringValue extends PrimitiveValue { 16 | static ClassName = "StringValue"; 17 | private readonly value: string; 18 | 19 | constructor(value: string) { 20 | super(new StringType()); 21 | this.value = value; 22 | } 23 | 24 | getClassName(): string { 25 | return StringValue.ClassName; 26 | } 27 | 28 | /** 29 | * Creates a StringValue from a utf-8 string. 30 | */ 31 | static fromUTF8(value: string): StringValue { 32 | return new StringValue(value); 33 | } 34 | 35 | /** 36 | * Creates a StringValue from a hex-encoded string. 37 | */ 38 | static fromHex(value: string): StringValue { 39 | let decodedValue = Buffer.from(value, "hex").toString(); 40 | return new StringValue(decodedValue); 41 | } 42 | 43 | getLength(): number { 44 | return this.value.length; 45 | } 46 | 47 | /** 48 | * Returns whether two objects have the same value. 49 | */ 50 | equals(other: StringValue): boolean { 51 | return this.value === other.value; 52 | } 53 | 54 | valueOf(): string { 55 | return this.value; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/abi/typesystem/struct.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import * as errors from "../../core/errors"; 3 | import { BytesType, BytesValue } from "./bytes"; 4 | import { Field, FieldDefinition } from "./fields"; 5 | import { BigUIntType, BigUIntValue, U32Type, U32Value } from "./numerical"; 6 | import { Struct, StructType } from "./struct"; 7 | import { TokenIdentifierType, TokenIdentifierValue } from "./tokenIdentifier"; 8 | 9 | describe("test structs", () => { 10 | it("should get fields", () => { 11 | let fooType = new StructType("Foo", [ 12 | new FieldDefinition("a", "", new TokenIdentifierType()), 13 | new FieldDefinition("b", "", new BigUIntType()), 14 | new FieldDefinition("c", "", new U32Type()), 15 | new FieldDefinition("d", "", new BytesType()), 16 | ]); 17 | 18 | let fooStruct = new Struct(fooType, [ 19 | new Field(new TokenIdentifierValue("lucky-token"), "a"), 20 | new Field(new BigUIntValue(1), "b"), 21 | new Field(new U32Value(42), "c"), 22 | new Field(new BytesValue(Buffer.from([0x64])), "d"), 23 | ]); 24 | 25 | assert.lengthOf(fooStruct.getFields(), 4); 26 | assert.deepEqual(fooStruct.getFieldValue("a"), "lucky-token"); 27 | assert.deepEqual(fooStruct.getFieldValue("b").toNumber(), 1); 28 | assert.deepEqual(fooStruct.getFieldValue("c").toNumber(), 42); 29 | assert.deepEqual(fooStruct.getFieldValue("d"), Buffer.from([0x64])); 30 | assert.throw(() => fooStruct.getFieldValue("e"), errors.ErrMissingFieldOnStruct); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src/abi/typesystem/tokenIdentifier.ts: -------------------------------------------------------------------------------- 1 | import { PrimitiveType, PrimitiveValue } from "./types"; 2 | 3 | const EGLDTokenIdentifier = "EGLD"; 4 | 5 | export class TokenIdentifierType extends PrimitiveType { 6 | static ClassName = "TokenIdentifierType"; 7 | 8 | constructor() { 9 | super("TokenIdentifier"); 10 | } 11 | 12 | getClassName(): string { 13 | return TokenIdentifierType.ClassName; 14 | } 15 | } 16 | 17 | export class TokenIdentifierValue extends PrimitiveValue { 18 | static ClassName = "TokenIdentifierValue"; 19 | private readonly value: string; 20 | 21 | constructor(value: string) { 22 | super(new TokenIdentifierType()); 23 | this.value = value; 24 | } 25 | 26 | static egld(): TokenIdentifierValue { 27 | return new TokenIdentifierValue(EGLDTokenIdentifier); 28 | } 29 | 30 | static esdtTokenIdentifier(identifier: string): TokenIdentifierValue { 31 | return new TokenIdentifierValue(identifier); 32 | } 33 | 34 | getClassName(): string { 35 | return TokenIdentifierValue.ClassName; 36 | } 37 | 38 | getLength(): number { 39 | return this.value.length; 40 | } 41 | 42 | /** 43 | * Returns whether two objects have the same value. 44 | */ 45 | equals(other: TokenIdentifierValue): boolean { 46 | if (this.getLength() != other.getLength()) { 47 | return false; 48 | } 49 | 50 | return this.value == other.value; 51 | } 52 | 53 | valueOf(): string { 54 | return this.value; 55 | } 56 | 57 | toString(): string { 58 | return this.value.toString(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/abi/typesystem/tuple.ts: -------------------------------------------------------------------------------- 1 | import * as errors from "../../core/errors"; 2 | import { Field, FieldDefinition } from "./fields"; 3 | import { Struct, StructType } from "./struct"; 4 | import { Type, TypedValue } from "./types"; 5 | 6 | export class TupleType extends StructType { 7 | static ClassName = "TupleType"; 8 | 9 | constructor(...typeParameters: Type[]) { 10 | super(TupleType.prepareName(typeParameters), TupleType.prepareFieldDefinitions(typeParameters)); 11 | } 12 | 13 | getClassName(): string { 14 | return TupleType.ClassName; 15 | } 16 | 17 | private static prepareName(typeParameters: Type[]): string { 18 | let fields: string = typeParameters.map((type) => type.toString()).join(", "); 19 | let result = `tuple<${fields}>`; 20 | return result; 21 | } 22 | 23 | private static prepareFieldDefinitions(typeParameters: Type[]): FieldDefinition[] { 24 | let result = typeParameters.map( 25 | (type, i) => new FieldDefinition(prepareFieldName(i), "anonymous tuple field", type), 26 | ); 27 | return result; 28 | } 29 | } 30 | 31 | function prepareFieldName(fieldIndex: number) { 32 | return `field${fieldIndex}`; 33 | } 34 | 35 | // TODO: Perhaps add a common base class for Struct and Tuple, called FieldsHolder? 36 | // Or let Tuple be the base class, but have Struct as a specialization of it, "named tuple"? 37 | // Or leave as it is? 38 | export class Tuple extends Struct { 39 | static ClassName = "Tuple"; 40 | 41 | constructor(type: TupleType, fields: Field[]) { 42 | super(type, fields); 43 | } 44 | 45 | getClassName(): string { 46 | return Tuple.ClassName; 47 | } 48 | 49 | static fromItems(items: TypedValue[]): Tuple { 50 | if (items.length < 1) { 51 | // TODO: Define a better error. 52 | throw new errors.ErrTypingSystem("bad tuple items"); 53 | } 54 | 55 | let fieldsTypes = items.map((item) => item.getType()); 56 | let tupleType = new TupleType(...fieldsTypes); 57 | let fields = items.map((item, i) => new Field(item, prepareFieldName(i))); 58 | 59 | return new Tuple(tupleType, fields); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/abi/typesystem/typeExpressionParser.ts: -------------------------------------------------------------------------------- 1 | import { TypeFormula } from "../../abi/typeFormula"; 2 | import { TypeFormulaParser } from "../../abi/typeFormulaParser"; 3 | import { ErrTypingSystem } from "../../core/errors"; 4 | import { Type } from "./types"; 5 | 6 | export class TypeExpressionParser { 7 | private readonly backingTypeFormulaParser: TypeFormulaParser; 8 | 9 | constructor() { 10 | this.backingTypeFormulaParser = new TypeFormulaParser(); 11 | } 12 | 13 | parse(expression: string): Type { 14 | try { 15 | return this.doParse(expression); 16 | } catch (e) { 17 | throw new ErrTypingSystem(`Failed to parse type expression: ${expression}. Error: ${e}`); 18 | } 19 | } 20 | 21 | private doParse(expression: string): Type { 22 | const typeFormula = this.backingTypeFormulaParser.parseExpression(expression); 23 | const type = this.typeFormulaToType(typeFormula); 24 | return type; 25 | } 26 | 27 | private typeFormulaToType(typeFormula: TypeFormula): Type { 28 | const typeParameters = typeFormula.typeParameters.map((typeFormula) => this.typeFormulaToType(typeFormula)); 29 | return new Type(typeFormula.name, typeParameters, undefined, typeFormula.metadata); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/accountManagement/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./accountController"; 2 | export * from "./accountTransactionsFactory"; 3 | export * from "./resources"; 4 | -------------------------------------------------------------------------------- /src/accountManagement/resources.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | 3 | export type SetGuardianInput = { guardianAddress: Address; serviceID: string }; 4 | export type SaveKeyValueInput = { keyValuePairs: Map }; 5 | -------------------------------------------------------------------------------- /src/accounts/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./account"; 2 | -------------------------------------------------------------------------------- /src/core/asyncTimer.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import * as errors from "./errors"; 3 | import { AsyncTimer } from "./asyncTimer"; 4 | 5 | describe("test asyncTimer", () => { 6 | it("should start timer and resolve promise", async () => { 7 | let timer = new AsyncTimer("test"); 8 | await timer.start(42); 9 | 10 | // No assertion needed. 11 | }); 12 | 13 | it("should abort a very long-running timer", async () => { 14 | let error: Error | null = null; 15 | 16 | let shortTimer = new AsyncTimer("short"); 17 | let longTimer = new AsyncTimer("long"); 18 | 19 | let shortPromise = shortTimer.start(42); 20 | let longPromise = longTimer.start(42000); 21 | 22 | let shortTimerThenAbortLongTimer = shortPromise.then(() => longTimer.abort()); 23 | let longTimerThenCatchAbort = longPromise.catch((reason) => (error = reason)); 24 | 25 | await Promise.all([shortTimerThenAbortLongTimer, longTimerThenCatchAbort]); 26 | 27 | assert.instanceOf(error, errors.ErrAsyncTimerAborted); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/core/baseController.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "./address"; 2 | import { EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS, EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS } from "./constants"; 3 | import { Transaction } from "./transaction"; 4 | import { TransactionComputer } from "./transactionComputer"; 5 | 6 | export type BaseControllerInput = { 7 | guardian?: Address; 8 | relayer?: Address; 9 | gasPrice?: bigint; 10 | gasLimit?: bigint; 11 | }; 12 | 13 | export class BaseController { 14 | protected setTransactionGasOptions(transaction: Transaction, options: { gasLimit?: bigint; gasPrice?: bigint }) { 15 | if (options.gasLimit) { 16 | transaction.gasLimit = options.gasLimit; 17 | } else { 18 | this.addExtraGasLimitIfRequired(transaction); 19 | } 20 | if (options.gasPrice) { 21 | transaction.gasPrice = options.gasPrice; 22 | } 23 | } 24 | 25 | protected addExtraGasLimitIfRequired(transaction: Transaction): void { 26 | if (transaction.guardian && !transaction.guardian.isEmpty()) { 27 | transaction.gasLimit += BigInt(EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS); 28 | } 29 | 30 | if (transaction.relayer && !transaction.relayer.isEmpty()) { 31 | transaction.gasLimit += BigInt(EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS); 32 | } 33 | } 34 | 35 | protected setVersionAndOptionsForGuardian(transaction: Transaction): void { 36 | if (transaction.guardian && !transaction.guardian.isEmpty()) { 37 | const txComputer = new TransactionComputer(); 38 | txComputer.applyGuardian(transaction, transaction.guardian); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/core/compatibility.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "./address"; 2 | 3 | /** 4 | * For internal use only. 5 | */ 6 | export class Compatibility { 7 | /** 8 | * For internal use only. 9 | */ 10 | static guardAddressIsSetAndNonZero(address: Address | undefined, context: string, resolution: string) { 11 | if (!address || address.toBech32() == "") { 12 | console.warn( 13 | `${context}: address should be set; ${resolution}. In the future, this will throw an exception instead of emitting a WARN.`, 14 | ); 15 | } else if (address.toBech32() == Address.Zero().toBech32()) { 16 | console.warn( 17 | `${context}: address should not be the 'zero' address (also known as the 'contracts deployment address'); ${resolution}. In the future, this will throw an exception instead of emitting a WARN.`, 18 | ); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/core/config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Global configuration of the library. 3 | * 4 | * Generally speaking, this configuration should only be altered on exotic use cases; 5 | * it can be seen as a collection of constants (or, to be more precise, rarely changed variables) that are used throughout the library. 6 | * 7 | * Never alter the configuration within a library! 8 | * Only alter the configuration (if needed) within an (end) application that uses this library. 9 | */ 10 | export class LibraryConfig { 11 | /** 12 | * The human-readable-part of the bech32 addresses. 13 | */ 14 | public static DefaultAddressHrp: string = "erd"; 15 | } 16 | -------------------------------------------------------------------------------- /src/core/constants.ts: -------------------------------------------------------------------------------- 1 | export const TRANSACTION_MIN_GAS_PRICE = 1000000000; 2 | export const TRANSACTION_OPTIONS_DEFAULT = 0; 3 | export const TRANSACTION_OPTIONS_TX_HASH_SIGN = 0b0001; 4 | export const TRANSACTION_OPTIONS_TX_GUARDED = 0b0010; 5 | export const TRANSACTION_VERSION_DEFAULT = 2; 6 | export const MIN_TRANSACTION_VERSION_THAT_SUPPORTS_OPTIONS = 2; 7 | export const ESDT_TRANSFER_GAS_LIMIT = 500000; 8 | export const ESDT_TRANSFER_FUNCTION_NAME = "ESDTTransfer"; 9 | export const ESDTNFT_TRANSFER_FUNCTION_NAME = "ESDTNFTTransfer"; 10 | export const MULTI_ESDTNFT_TRANSFER_FUNCTION_NAME = "MultiESDTNFTTransfer"; 11 | export const ESDT_TRANSFER_VALUE = "0"; 12 | export const ARGUMENTS_SEPARATOR = "@"; 13 | export const VM_TYPE_WASM_VM = new Uint8Array([0x05, 0x00]); 14 | export const CONTRACT_DEPLOY_ADDRESS_HEX = "0000000000000000000000000000000000000000000000000000000000000000"; 15 | export const DELEGATION_MANAGER_SC_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000004ffff"; 16 | export const ESDT_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000002ffff"; 17 | export const GOVERNANCE_CONTRACT_ADDRESS_HEX = "000000000000000000010000000000000000000000000000000000000003ffff"; 18 | 19 | export const DEFAULT_MESSAGE_VERSION = 1; 20 | export const MESSAGE_PREFIX = "\x17Elrond Signed Message:\n"; 21 | export const HEX_TRANSACTION_HASH_LENGTH = 64; 22 | 23 | export const CURRENT_NUMBER_OF_SHARDS_WITHOUT_META = 3; 24 | export const WasmVirtualMachine = "0500"; 25 | export const METACHAIN_ID = 4294967295; 26 | export const SDK_JS_SIGNER = "sdk-js"; 27 | export const UNKNOWN_SIGNER = "unknown"; 28 | 29 | export const EGLD_IDENTIFIER_FOR_MULTI_ESDTNFT_TRANSFER = "EGLD-000000"; 30 | export const EXTRA_GAS_LIMIT_FOR_GUARDED_TRANSACTIONS = 50_000; 31 | export const EXTRA_GAS_LIMIT_FOR_RELAYED_TRANSACTIONS = 50_000; 32 | -------------------------------------------------------------------------------- /src/core/globals.ts: -------------------------------------------------------------------------------- 1 | if (!global.Buffer) { 2 | global.Buffer = require("buffer").Buffer; 3 | } 4 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | require("./globals"); 2 | 3 | export * from "./address"; 4 | export * from "./asyncTimer"; 5 | export * from "./baseController"; 6 | export * from "./codeMetadata"; 7 | export * from "./config"; 8 | export * from "./errors"; 9 | export * from "./interfaces"; 10 | export * from "./logger"; 11 | export * from "./message"; 12 | export * from "./networkParams"; 13 | export * from "./smartContractQuery"; 14 | export * from "./tokens"; 15 | export * from "./transaction"; 16 | export * from "./transactionComputer"; 17 | export * from "./transactionEvents"; 18 | export * from "./transactionLogs"; 19 | export * from "./transactionOnNetwork"; 20 | export * from "./transactionsFactoryConfig"; 21 | export * from "./transactionStatus"; 22 | export * from "./transactionWatcher"; 23 | export * from "./utils"; 24 | -------------------------------------------------------------------------------- /src/core/interfaces.ts: -------------------------------------------------------------------------------- 1 | import { Address, Message, Transaction, TransactionOnNetwork } from "."; 2 | 3 | export interface IAccount { 4 | readonly address: Address; 5 | 6 | sign(data: Uint8Array): Promise; 7 | signTransaction(transaction: Transaction): Promise; 8 | verifyTransactionSignature(transaction: Transaction, signature: Uint8Array): Promise; 9 | signMessage(message: Message): Promise; 10 | verifyMessageSignature(message: Message, signature: Uint8Array): Promise; 11 | } 12 | 13 | export interface ITransactionFetcher { 14 | /** 15 | * Fetches the state of a {@link Transaction}. 16 | */ 17 | getTransaction(txHash: string): Promise; 18 | } 19 | 20 | export interface IPlainTransactionObject { 21 | nonce: number; 22 | value: string; 23 | receiver: string; 24 | sender: string; 25 | receiverUsername?: string; 26 | senderUsername?: string; 27 | guardian?: string; 28 | relayer?: string; 29 | gasPrice: number; 30 | gasLimit: number; 31 | data?: string; 32 | chainID: string; 33 | version: number; 34 | options?: number; 35 | signature?: string; 36 | guardianSignature?: string; 37 | relayerSignature?: string; 38 | } 39 | 40 | export interface INetworkConfig { 41 | minGasLimit: bigint; 42 | gasPerDataByte: bigint; 43 | gasPriceModifier: number; 44 | chainID: string; 45 | } 46 | -------------------------------------------------------------------------------- /src/core/logger.ts: -------------------------------------------------------------------------------- 1 | export enum LogLevel { 2 | Trace = 0, 3 | Debug = 1, 4 | Info = 2, 5 | Warn = 3, 6 | Error = 4, 7 | None = 5, 8 | } 9 | 10 | export class Logger { 11 | static logLevel: LogLevel = LogLevel.Debug; 12 | 13 | static setLevel(logLevel: LogLevel) { 14 | Logger.logLevel = logLevel; 15 | } 16 | 17 | static trace(message?: any, ...optionalParams: any[]) { 18 | if (Logger.logLevel >= LogLevel.Debug) { 19 | return; 20 | } 21 | 22 | console.debug(message, optionalParams); 23 | } 24 | 25 | static debug(message?: any, ...optionalParams: any[]) { 26 | if (Logger.logLevel >= LogLevel.Debug) { 27 | return; 28 | } 29 | 30 | console.debug(message, optionalParams); 31 | } 32 | 33 | static info(message?: any, ...optionalParams: any[]) { 34 | if (Logger.logLevel >= LogLevel.Info) { 35 | return; 36 | } 37 | 38 | console.log(message, optionalParams); 39 | } 40 | 41 | static warn(message?: any, ...optionalParams: any[]) { 42 | if (Logger.logLevel >= LogLevel.Warn) { 43 | return; 44 | } 45 | 46 | console.warn(message, optionalParams); 47 | } 48 | 49 | static error(message?: any, ...optionalParams: any[]) { 50 | if (Logger.logLevel >= LogLevel.Error) { 51 | return; 52 | } 53 | 54 | console.error(message, optionalParams); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/core/reflection.ts: -------------------------------------------------------------------------------- 1 | export function getJavascriptPrototypesInHierarchy(obj: any, filter: (prototype: any) => boolean): any[] { 2 | let prototypes: any[] = []; 3 | let prototype: any = Object.getPrototypeOf(obj); 4 | 5 | while (prototype && filter(prototype)) { 6 | prototypes.push(prototype); 7 | prototype = Object.getPrototypeOf(prototype); 8 | } 9 | 10 | return prototypes; 11 | } 12 | -------------------------------------------------------------------------------- /src/core/signature.ts: -------------------------------------------------------------------------------- 1 | import * as errors from "./errors"; 2 | 3 | const SIGNATURE_LENGTH = 64; 4 | 5 | /** 6 | * Signature, as an immutable object. 7 | */ 8 | export class Signature { 9 | private valueHex: string = ""; 10 | 11 | constructor(value?: string | Buffer | Uint8Array) { 12 | if (!value) { 13 | return; 14 | } 15 | if (typeof value === "string") { 16 | return Signature.fromHex(value); 17 | } 18 | 19 | if (ArrayBuffer.isView(value)) { 20 | return Signature.fromBuffer(Buffer.from(value)); 21 | } 22 | } 23 | 24 | static empty(): Signature { 25 | return new Signature(); 26 | } 27 | 28 | static fromHex(value: string): Signature { 29 | if (value.startsWith("0x")) { 30 | value = value.slice(2); 31 | } 32 | if (!Signature.isValidHex(value)) { 33 | throw new errors.ErrSignatureCannotCreate(value); 34 | } 35 | 36 | return Signature.fromValidHex(value); 37 | } 38 | 39 | private static isValidHex(value: string) { 40 | return Buffer.from(value, "hex").length == SIGNATURE_LENGTH; 41 | } 42 | 43 | private static fromValidHex(value: string): Signature { 44 | let result = new Signature(); 45 | result.valueHex = value; 46 | return result; 47 | } 48 | 49 | static fromBuffer(buffer: Buffer): Signature { 50 | if (buffer.length != SIGNATURE_LENGTH) { 51 | throw new errors.ErrSignatureCannotCreate(buffer); 52 | } 53 | 54 | return Signature.fromValidHex(buffer.toString("hex")); 55 | } 56 | 57 | hex() { 58 | return this.valueHex; 59 | } 60 | } 61 | 62 | export function interpretSignatureAsBuffer(signature: { hex(): string } | Uint8Array): Buffer { 63 | if (ArrayBuffer.isView(signature)) { 64 | return Buffer.from(signature); 65 | } else if ((signature).hex != null) { 66 | return Buffer.from(signature.hex(), "hex"); 67 | } 68 | 69 | throw new Error(`Object cannot be interpreted as a signature: ${signature}`); 70 | } 71 | -------------------------------------------------------------------------------- /src/core/smartContractQuery.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "./address"; 2 | 3 | export class SmartContractQuery { 4 | contract: Address; 5 | caller?: Address; 6 | value?: bigint; 7 | function: string; 8 | arguments?: Uint8Array[]; 9 | 10 | constructor(options: { 11 | contract: Address; 12 | caller?: Address; 13 | value?: bigint; 14 | function: string; 15 | arguments?: Uint8Array[]; 16 | }) { 17 | this.contract = options.contract; 18 | this.caller = options.caller; 19 | this.value = options.value; 20 | this.function = options.function; 21 | this.arguments = options.arguments; 22 | } 23 | } 24 | 25 | export type SmartContractQueryInput = { 26 | contract: Address; 27 | caller?: Address; 28 | value?: bigint; 29 | function: string; 30 | arguments: any[]; 31 | }; 32 | 33 | export class SmartContractQueryResponse { 34 | function: string; 35 | returnCode: string; 36 | returnMessage: string; 37 | returnDataParts: Uint8Array[]; 38 | 39 | constructor(obj: { function: string; returnCode: string; returnMessage: string; returnDataParts: Uint8Array[] }) { 40 | this.function = obj.function; 41 | this.returnCode = obj.returnCode; 42 | this.returnMessage = obj.returnMessage; 43 | this.returnDataParts = obj.returnDataParts; 44 | } 45 | 46 | static fromHttpResponse(payload: any, functionName: string): SmartContractQueryResponse { 47 | let returnData = payload["returnData"] || payload["ReturnData"]; 48 | let returnCode = payload["returnCode"] || payload["ReturnCode"]; 49 | let returnMessage = payload["returnMessage"] || payload["ReturnMessage"]; 50 | 51 | return new SmartContractQueryResponse({ 52 | returnDataParts: returnData?.map((item) => Buffer.from(item || "", "base64")) ?? [], 53 | returnCode: returnCode, 54 | returnMessage: returnMessage, 55 | function: functionName, 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/core/tokenTransfersDataBuilder.ts: -------------------------------------------------------------------------------- 1 | import { AddressValue, ArgSerializer, BigUIntValue, TokenIdentifierValue, TypedValue, U32Value } from "../abi"; 2 | import { Address } from "./address"; 3 | import { TokenComputer, TokenTransfer } from "./tokens"; 4 | 5 | export class TokenTransfersDataBuilder { 6 | private tokenComputer: TokenComputer; 7 | private argsSerializer: ArgSerializer; 8 | 9 | constructor() { 10 | this.tokenComputer = new TokenComputer(); 11 | this.argsSerializer = new ArgSerializer(); 12 | } 13 | 14 | buildDataPartsForESDTTransfer(transfer: TokenTransfer): string[] { 15 | const args = this.argsSerializer.valuesToStrings([ 16 | new TokenIdentifierValue(transfer.token.identifier), 17 | new BigUIntValue(transfer.amount), 18 | ]); 19 | 20 | return ["ESDTTransfer", ...args]; 21 | } 22 | 23 | buildDataPartsForSingleESDTNFTTransfer(transfer: TokenTransfer, receiver: Address) { 24 | const token = transfer.token; 25 | const identifier = this.tokenComputer.extractIdentifierFromExtendedIdentifier(token.identifier); 26 | 27 | const args = this.argsSerializer.valuesToStrings([ 28 | new TokenIdentifierValue(identifier), 29 | new BigUIntValue(token.nonce), 30 | new BigUIntValue(transfer.amount), 31 | new AddressValue(receiver), 32 | ]); 33 | 34 | return ["ESDTNFTTransfer", ...args]; 35 | } 36 | 37 | buildDataPartsForMultiESDTNFTTransfer(receiver: Address, transfers: TokenTransfer[]) { 38 | const argsTyped: TypedValue[] = [new AddressValue(receiver), new U32Value(transfers.length)]; 39 | 40 | for (const transfer of transfers) { 41 | const identifier = this.tokenComputer.extractIdentifierFromExtendedIdentifier(transfer.token.identifier); 42 | 43 | argsTyped.push( 44 | ...[ 45 | new TokenIdentifierValue(identifier), 46 | new BigUIntValue(transfer.token.nonce), 47 | new BigUIntValue(transfer.amount), 48 | ], 49 | ); 50 | } 51 | 52 | const args = this.argsSerializer.valuesToStrings(argsTyped); 53 | return ["MultiESDTNFTTransfer", ...args]; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/core/transactionBuilder.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "./address"; 2 | import { ARGUMENTS_SEPARATOR } from "./constants"; 3 | import { Transaction } from "./transaction"; 4 | 5 | interface Config { 6 | chainID: string; 7 | minGasLimit: bigint; 8 | gasLimitPerByte: bigint; 9 | } 10 | 11 | /** 12 | * @internal 13 | */ 14 | export class TransactionBuilder { 15 | private config: Config; 16 | private sender: Address; 17 | private receiver: Address; 18 | private dataParts: string[]; 19 | private providedGasLimit: bigint; 20 | private addDataMovementGas: boolean; 21 | private amount?: bigint; 22 | 23 | constructor(options: { 24 | config: Config; 25 | sender: Address; 26 | receiver: Address; 27 | dataParts: string[]; 28 | gasLimit: bigint; 29 | addDataMovementGas: boolean; 30 | amount?: bigint; 31 | }) { 32 | this.config = options.config; 33 | this.sender = options.sender; 34 | this.receiver = options.receiver; 35 | this.dataParts = options.dataParts; 36 | this.providedGasLimit = options.gasLimit; 37 | this.addDataMovementGas = options.addDataMovementGas; 38 | this.amount = options.amount; 39 | } 40 | 41 | private computeGasLimit(payload: Uint8Array): bigint { 42 | if (!this.addDataMovementGas) { 43 | return this.providedGasLimit; 44 | } 45 | 46 | const dataMovementGas = this.config.minGasLimit + this.config.gasLimitPerByte * BigInt(payload.length); 47 | const gasLimit = dataMovementGas + this.providedGasLimit; 48 | return gasLimit; 49 | } 50 | 51 | private buildTransactionPayload(): Uint8Array { 52 | const data = this.dataParts.join(ARGUMENTS_SEPARATOR); 53 | return Buffer.from(data); 54 | } 55 | 56 | build(): Transaction { 57 | const data = this.buildTransactionPayload(); 58 | const gasLimit = this.computeGasLimit(data); 59 | 60 | return new Transaction({ 61 | sender: this.sender, 62 | receiver: this.receiver, 63 | gasLimit: gasLimit, 64 | value: this.amount || 0n, 65 | data: data.valueOf(), 66 | chainID: this.config.chainID, 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/core/transactionEvents.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "./address"; 2 | 3 | export class TransactionEvent { 4 | raw: Record = {}; 5 | address: Address = Address.empty(); 6 | identifier: string = ""; 7 | topics: Uint8Array[] = []; 8 | 9 | data: Uint8Array = new Uint8Array(); 10 | additionalData: Uint8Array[] = []; 11 | 12 | constructor(init?: Partial) { 13 | Object.assign(this, init); 14 | } 15 | 16 | static fromHttpResponse(responsePart: { 17 | address: string; 18 | identifier: string; 19 | topics: string[]; 20 | data: string; 21 | additionalData?: string[]; 22 | }): TransactionEvent { 23 | let result = new TransactionEvent(); 24 | result.address = new Address(responsePart.address); 25 | result.identifier = responsePart.identifier || ""; 26 | result.topics = (responsePart.topics || []).map((topic) => Buffer.from(topic, "base64")); 27 | 28 | result.data = Buffer.from(responsePart.data ?? "", "base64"); 29 | result.additionalData = (responsePart.additionalData || []).map((data) => Buffer.from(data, "base64")); 30 | result.raw = responsePart; 31 | 32 | return result; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/core/transactionLogs.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "./address"; 2 | import { ErrUnexpectedCondition } from "./errors"; 3 | import { TransactionEvent } from "./transactionEvents"; 4 | 5 | export class TransactionLogs { 6 | address: Address = Address.empty(); 7 | events: TransactionEvent[] = []; 8 | 9 | constructor(init?: Partial) { 10 | Object.assign(this, init); 11 | } 12 | 13 | static fromHttpResponse(logs: any): TransactionLogs { 14 | let result = new TransactionLogs(); 15 | result.address = new Address(logs.address); 16 | result.events = (logs.events || []).map((event: any) => TransactionEvent.fromHttpResponse(event)); 17 | 18 | return result; 19 | } 20 | 21 | findSingleOrNoneEvent( 22 | identifier: string, 23 | predicate?: (event: TransactionEvent) => boolean, 24 | ): TransactionEvent | undefined { 25 | let events = this.findEvents(identifier, predicate); 26 | 27 | if (events.length > 1) { 28 | throw new ErrUnexpectedCondition(`more than one event of type ${identifier}`); 29 | } 30 | 31 | return events[0]; 32 | } 33 | 34 | findFirstOrNoneEvent( 35 | identifier: string, 36 | predicate?: (event: TransactionEvent) => boolean, 37 | ): TransactionEvent | undefined { 38 | return this.findEvents(identifier, predicate)[0]; 39 | } 40 | 41 | findEvents(identifier: string, predicate?: (event: TransactionEvent) => boolean): TransactionEvent[] { 42 | let events = this.events.filter((event) => event.identifier == identifier); 43 | 44 | if (predicate) { 45 | events = events.filter((event) => predicate(event)); 46 | } 47 | 48 | return events; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/core/utils.codec.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { byteArrayToHex, isPaddedHex, numberToPaddedHex, utf8ToHex, zeroPadStringIfOddLength } from "./utils.codec"; 3 | 4 | describe("test codec utils", () => { 5 | it("should convert numberToPaddedHex", () => { 6 | assert.equal(numberToPaddedHex(0), "00"); 7 | assert.equal(numberToPaddedHex(1), "01"); 8 | assert.equal(numberToPaddedHex(10), "0a"); 9 | assert.equal(numberToPaddedHex(256), "0100"); 10 | 11 | assert.equal(numberToPaddedHex(0n), "00"); 12 | assert.equal(numberToPaddedHex(1n), "01"); 13 | assert.equal(numberToPaddedHex(10n), "0a"); 14 | assert.equal(numberToPaddedHex(256n), "0100"); 15 | 16 | assert.equal(numberToPaddedHex("0"), "00"); 17 | assert.equal(numberToPaddedHex("1"), "01"); 18 | assert.equal(numberToPaddedHex("10"), "0a"); 19 | assert.equal(numberToPaddedHex("256"), "0100"); 20 | }); 21 | 22 | it("should check if isPaddedHex", () => { 23 | assert.isTrue(isPaddedHex("0A")); 24 | assert.isTrue(isPaddedHex("0a")); 25 | assert.isTrue(isPaddedHex("00ABCD")); 26 | assert.isTrue(isPaddedHex("0000")); 27 | assert.isFalse(isPaddedHex("000")); 28 | assert.isFalse(isPaddedHex("1")); 29 | }); 30 | 31 | it("should zeroPadStringIfOddLength", () => { 32 | assert.equal(zeroPadStringIfOddLength("1"), "01"); 33 | assert.equal(zeroPadStringIfOddLength("01"), "01"); 34 | }); 35 | 36 | it("should convert byteArrayToHex", () => { 37 | const firstArray = new Uint8Array([0x05, 0x00]); 38 | const secondArray = new Uint8Array([0x7]); 39 | 40 | assert.equal(byteArrayToHex(firstArray), "0500"); 41 | assert.equal(byteArrayToHex(secondArray), "07"); 42 | }); 43 | 44 | it("should convert utf8ToHex", () => { 45 | assert.equal(utf8ToHex("stringandnumber7"), "737472696e67616e646e756d62657237"); 46 | assert.equal(utf8ToHex("somestring"), "736f6d65737472696e67"); 47 | assert.equal(utf8ToHex("aaa"), "616161"); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /src/core/utils.codec.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import * as contractsCodecUtils from "../abi/codec/utils"; 3 | 4 | export function numberToPaddedHex(value: bigint | number | BigNumber.Value) { 5 | let hexableNumber: { toString(radix?: number): string }; 6 | 7 | if (typeof value === "bigint" || typeof value === "number") { 8 | hexableNumber = value; 9 | } else { 10 | hexableNumber = new BigNumber(value); 11 | } 12 | 13 | const hex = hexableNumber.toString(16); 14 | return zeroPadStringIfOddLength(hex); 15 | } 16 | 17 | export function isPaddedHex(input: string) { 18 | input = input || ""; 19 | let decodedThenEncoded = Buffer.from(input, "hex").toString("hex"); 20 | return input.toUpperCase() == decodedThenEncoded.toUpperCase(); 21 | } 22 | 23 | export function zeroPadStringIfOddLength(input: string): string { 24 | input = input || ""; 25 | 26 | if (input.length % 2 == 1) { 27 | return "0" + input; 28 | } 29 | 30 | return input; 31 | } 32 | 33 | export function utf8ToHex(value: string) { 34 | const hex = Buffer.from(value).toString("hex"); 35 | return zeroPadStringIfOddLength(hex); 36 | } 37 | 38 | export function boolToHex(value: boolean) { 39 | return utf8ToHex(value.toString()); 40 | } 41 | 42 | export function byteArrayToHex(byteArray: Uint8Array): string { 43 | const hexString = Buffer.from(byteArray).toString("hex"); 44 | return zeroPadStringIfOddLength(hexString); 45 | } 46 | 47 | export function bigIntToHex(value: BigNumber.Value): string { 48 | if (value == 0) { 49 | return ""; 50 | } 51 | 52 | return contractsCodecUtils.getHexMagnitudeOfBigInt(value); 53 | } 54 | -------------------------------------------------------------------------------- /src/core/utils.ts: -------------------------------------------------------------------------------- 1 | import * as errors from "./errors"; 2 | 3 | // TODO: Create a class called "Guard". Add the following as member functions. 4 | 5 | export function guardTrue(value: boolean, what: string) { 6 | if (!value) { 7 | throw new errors.ErrInvariantFailed(`[<${what}>] isn't true`); 8 | } 9 | } 10 | 11 | // TODO: merge with guardValueIsSetWithMessage 12 | export function guardValueIsSet(name: string, value?: any | null | undefined) { 13 | guardValueIsSetWithMessage(`${name} isn't set (null or undefined)`, value); 14 | } 15 | 16 | // TODO: merge with guardValueIsSet 17 | export function guardValueIsSetWithMessage(message: string, value?: any | null | undefined) { 18 | if (value == null || value === undefined) { 19 | throw new errors.ErrInvariantFailed(message); 20 | } 21 | } 22 | 23 | export function guardSameLength(a: any[], b: any[]) { 24 | a = a || []; 25 | b = b || []; 26 | 27 | if (a.length != b.length) { 28 | throw new errors.ErrInvariantFailed("arrays do not have the same length"); 29 | } 30 | } 31 | 32 | export function guardLength(withLength: { length?: number }, expectedLength: number) { 33 | let actualLength = withLength.length || 0; 34 | 35 | if (actualLength != expectedLength) { 36 | throw new errors.ErrInvariantFailed(`wrong length, expected: ${expectedLength}, actual: ${actualLength}`); 37 | } 38 | } 39 | 40 | export function guardNotEmpty(value: { isEmpty?: () => boolean; length?: number }, what: string) { 41 | if (isEmpty(value)) { 42 | throw new errors.ErrInvariantFailed(`${what} is empty`); 43 | } 44 | } 45 | 46 | export function guardEmpty(value: { isEmpty?: () => boolean; length?: number }, what: string) { 47 | if (!isEmpty(value)) { 48 | throw new errors.ErrInvariantFailed(`${what} is not empty`); 49 | } 50 | } 51 | 52 | export function isEmpty(value: { isEmpty?: () => boolean; length?: number }): boolean { 53 | if (value.isEmpty) { 54 | return value.isEmpty(); 55 | } 56 | 57 | return value.length === 0; 58 | } 59 | 60 | export function getAxios() { 61 | try { 62 | return require("axios"); 63 | } catch (error) { 64 | throw new Error("axios is required but not installed. Please install axios to make network requests."); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/delegation/delegationTransactionsOutcomeParser.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { ErrParseTransactionOutcome } from "../core/errors"; 3 | import { TransactionEvent } from "../core/transactionEvents"; 4 | import { TransactionOnNetwork } from "../core/transactionOnNetwork"; 5 | import { findEventsByIdentifier } from "../transactionsOutcomeParsers/resources"; 6 | 7 | export class DelegationTransactionsOutcomeParser { 8 | constructor() {} 9 | 10 | parseCreateNewDelegationContract(transaction: TransactionOnNetwork): { contractAddress: string }[] { 11 | this.ensureNoError(transaction.logs.events); 12 | 13 | const events = findEventsByIdentifier(transaction, "SCDeploy"); 14 | 15 | return events.map((event) => ({ contractAddress: this.extractContractAddress(event) })); 16 | } 17 | 18 | private ensureNoError(transactionEvents: TransactionEvent[]) { 19 | for (const event of transactionEvents) { 20 | if (event.identifier == "signalError") { 21 | const data = Buffer.from(event.additionalData[0]?.toString().slice(1)).toString() || ""; 22 | const message = this.decodeTopicAsString(event.topics[1]); 23 | 24 | throw new ErrParseTransactionOutcome( 25 | `encountered signalError: ${message} (${Buffer.from(data, "hex").toString()})`, 26 | ); 27 | } 28 | } 29 | } 30 | 31 | private extractContractAddress(event: TransactionEvent): string { 32 | if (!event.topics[0]?.length) { 33 | return ""; 34 | } 35 | const address = Buffer.from(event.topics[0]); 36 | return new Address(address).toBech32(); 37 | } 38 | 39 | private decodeTopicAsString(topic: Uint8Array): string { 40 | return Buffer.from(topic).toString(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/delegation/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./delegationController"; 2 | export * from "./delegationTransactionsFactory"; 3 | export * from "./delegationTransactionsOutcomeParser"; 4 | export * from "./resources"; 5 | -------------------------------------------------------------------------------- /src/delegation/resources.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { ValidatorPublicKey } from "../wallet"; 3 | 4 | export type NewDelegationContractInput = { totalDelegationCap: bigint; serviceFee: bigint; amount: bigint }; 5 | export type AddNodesInput = ManageNodesInput & { signedMessages: Uint8Array[] }; 6 | export type UnjailingNodesInput = ManageNodesInput & { amount: bigint }; 7 | export type ManageNodesInput = { delegationContract: Address; publicKeys: ValidatorPublicKey[] }; 8 | export type ChangeServiceFee = { delegationContract: Address; serviceFee: bigint }; 9 | export type ModifyDelegationCapInput = { delegationContract: Address; delegationCap: bigint }; 10 | export type ManageDelegationContractInput = { delegationContract: Address }; 11 | export type DelegateActionsInput = { delegationContract: Address; amount: bigint }; 12 | export type SetContractMetadataInput = { 13 | delegationContract: Address; 14 | name: string; 15 | website: string; 16 | identifier: string; 17 | }; 18 | -------------------------------------------------------------------------------- /src/entrypoints/config.ts: -------------------------------------------------------------------------------- 1 | export interface EntrypointConfig { 2 | networkProviderUrl: string; 3 | networkProviderKind: string; 4 | chainId: string; 5 | } 6 | 7 | export class TestnetEntrypointConfig { 8 | networkProviderUrl: string; 9 | networkProviderKind: string; 10 | chainId: string; 11 | 12 | constructor({ 13 | networkProviderUrl = "https://testnet-api.multiversx.com", 14 | networkProviderKind = "api", 15 | chainId = "T", 16 | }: Partial = {}) { 17 | this.networkProviderUrl = networkProviderUrl; 18 | this.networkProviderKind = networkProviderKind; 19 | this.chainId = chainId; 20 | } 21 | } 22 | 23 | export class DevnetEntrypointConfig { 24 | networkProviderUrl: string; 25 | networkProviderKind: string; 26 | chainId: string; 27 | constructor({ 28 | networkProviderUrl = "https://devnet-api.multiversx.com", 29 | networkProviderKind = "api", 30 | chainId = "D", 31 | }: Partial = {}) { 32 | this.networkProviderUrl = networkProviderUrl; 33 | this.networkProviderKind = networkProviderKind; 34 | this.chainId = chainId; 35 | } 36 | } 37 | 38 | export class MainnetEntrypointConfig { 39 | networkProviderUrl: string; 40 | networkProviderKind: string; 41 | chainId: string; 42 | 43 | constructor({ 44 | networkProviderUrl = "https://api.multiversx.com", 45 | networkProviderKind = "api", 46 | chainId = "1", 47 | }: Partial = {}) { 48 | this.networkProviderUrl = networkProviderUrl; 49 | this.networkProviderKind = networkProviderKind; 50 | this.chainId = chainId; 51 | } 52 | } 53 | 54 | export class LocalnetEntrypointConfig { 55 | networkProviderUrl: string; 56 | networkProviderKind: string; 57 | chainId: string; 58 | 59 | constructor({ 60 | networkProviderUrl = "http://localhost:7950", 61 | networkProviderKind = "proxy", 62 | chainId = "localnet", 63 | }: Partial = {}) { 64 | this.networkProviderUrl = networkProviderUrl; 65 | this.networkProviderKind = networkProviderKind; 66 | this.chainId = chainId; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/entrypoints/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./config"; 2 | export * from "./entrypoints"; 3 | -------------------------------------------------------------------------------- /src/governance/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./governanceController"; 2 | export * from "./governanceTransactionsFactory"; 3 | export * from "./governanceTransactionsOutcomeParser"; 4 | export * from "./resources"; 5 | -------------------------------------------------------------------------------- /src/governance/resources.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core"; 2 | 3 | export type NewProposalInput = { 4 | commitHash: string; 5 | startVoteEpoch: number; 6 | endVoteEpoch: number; 7 | nativeTokenAmount: bigint; 8 | }; 9 | 10 | export type VoteProposalInput = { 11 | proposalNonce: number; 12 | vote: Vote; 13 | }; 14 | 15 | export enum Vote { 16 | YES = "yes", 17 | NO = "no", 18 | ABSTAIN = "abstain", 19 | VETO = "veto", 20 | } 21 | 22 | export type CloseProposalInput = { 23 | proposalNonce: number; 24 | }; 25 | 26 | export type ClearEndedProposalsInput = { 27 | proposers: Address[]; 28 | }; 29 | 30 | export type ChangeConfigInput = { 31 | proposalFee: bigint; 32 | lastProposalFee: bigint; 33 | minQuorum: number; 34 | minVetoThreshold: number; 35 | minPassThreshold: number; 36 | }; 37 | 38 | export type NewProposalOutcome = { 39 | proposalNonce: number; 40 | commitHash: string; 41 | startVoteEpoch: number; 42 | endVoteEpoch: number; 43 | }; 44 | 45 | export type VoteOutcome = { 46 | proposalNonce: number; 47 | vote: string; 48 | totalStake: bigint; 49 | votingPower: bigint; 50 | }; 51 | 52 | export type DelegateVoteOutcome = { 53 | proposalNonce: number; 54 | vote: string; 55 | voter: Address; 56 | userStake: bigint; 57 | votingPower: bigint; 58 | }; 59 | 60 | export type CloseProposalOutcome = { 61 | commitHash: string; 62 | passed: boolean; 63 | }; 64 | 65 | export type GovernanceConfig = { 66 | proposalFee: bigint; 67 | minQuorum: number; 68 | minPassThreshold: number; 69 | minVetoThreshold: number; 70 | lastProposalNonce: number; 71 | }; 72 | 73 | export type ProposalInfo = { 74 | cost: bigint; 75 | commitHash: string; 76 | nonce: number; 77 | issuer: Address; 78 | startVoteEpoch: number; 79 | endVoteEpoch: number; 80 | quorumStake: bigint; 81 | numYesVotes: bigint; 82 | numNoVotes: bigint; 83 | numVetoVotes: bigint; 84 | numAbstainVotes: bigint; 85 | isClosed: boolean; 86 | isPassed: boolean; 87 | }; 88 | 89 | export type DelegatedVoteInfo = { 90 | usedStake: bigint; 91 | usedPower: bigint; 92 | totalStake: bigint; 93 | totalPower: bigint; 94 | }; 95 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A library for interacting with the MultiversX blockchain (in general) and Smart Contracts (in particular). 3 | * 4 | * @packageDocumentation 5 | */ 6 | 7 | export * from "./abi"; 8 | export * from "./accountManagement"; 9 | export * from "./accounts"; 10 | export * from "./core"; 11 | export * from "./delegation"; 12 | export * from "./entrypoints"; 13 | export * from "./governance"; 14 | export * from "./multisig"; 15 | export * from "./networkProviders"; 16 | export * from "./smartContracts"; 17 | export * from "./tokenManagement"; 18 | export * from "./transactionsOutcomeParsers"; 19 | export * from "./transfers"; 20 | export * from "./wallet"; 21 | -------------------------------------------------------------------------------- /src/multisig/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./multisigTransactionsFactory"; 2 | export * from "./proposeTransferExecuteContractInput"; 3 | export * from "./resources"; 4 | -------------------------------------------------------------------------------- /src/multisig/multisigTransactionsOutcomeParser.ts: -------------------------------------------------------------------------------- 1 | import { Abi } from "../abi"; 2 | import { Address, TransactionOnNetwork } from "../core"; 3 | import { SmartContractDeployOutcome } from "../smartContracts/resources"; 4 | import { SmartContractTransactionsOutcomeParser } from "../transactionsOutcomeParsers"; 5 | 6 | /** 7 | * Parses the outcome of multisig contract operations 8 | */ 9 | export class MultisigTransactionsOutcomeParser { 10 | private parser: SmartContractTransactionsOutcomeParser; 11 | private readonly abi: Abi; 12 | 13 | constructor(options: { abi: Abi }) { 14 | this.abi = options?.abi; 15 | this.parser = new SmartContractTransactionsOutcomeParser({ abi: this.abi }); 16 | } 17 | 18 | /** 19 | * Parses the outcome of creating a new multisig contract 20 | * @param transactionOnNetwork The completed transaction 21 | * @returns An array of objects containing the new contract addresses 22 | */ 23 | parseDeploy(transactionOnNetwork: TransactionOnNetwork): SmartContractDeployOutcome { 24 | return this.parser.parseDeploy({ transactionOnNetwork }); 25 | } 26 | 27 | /** 28 | * Parses the outcome of a multisig action proposal 29 | * @param transactionOnNetwork The completed transaction 30 | * @returns The action ID that was created 31 | */ 32 | parseProposeAction(transactionOnNetwork: TransactionOnNetwork): number { 33 | const result = this.parser.parseExecute({ transactionOnNetwork }); 34 | 35 | return result.values[0]; 36 | } 37 | 38 | /** 39 | * Parses the outcome of a multisig action proposal 40 | * @param transactionOnNetwork The completed transaction 41 | * @returns In case of scDeploy returns address else undefined 42 | */ 43 | parsePerformAction(transactionOnNetwork: TransactionOnNetwork): Address | undefined { 44 | const result = this.parser.parseExecute({ transactionOnNetwork }); 45 | 46 | return result.values[0]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/networkProviders/blocks.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An object holding network status configuration parameters. 3 | */ 4 | 5 | export class BlockOnNetwork { 6 | /** 7 | * The raw data return by provider. 8 | */ 9 | public raw: Record = {}; 10 | 11 | /** 12 | * The shard number. 13 | */ 14 | public shard: number = 0; 15 | 16 | /** 17 | * The shard nonce. 18 | */ 19 | public nonce: bigint = 0n; 20 | 21 | /** 22 | * The block hash. 23 | */ 24 | public hash: string = ""; 25 | 26 | /** 27 | * The block previous hash. 28 | */ 29 | public previousHash: string = ""; 30 | 31 | /** 32 | * The block timestamp. 33 | */ 34 | public timestamp: number = 0; 35 | 36 | /** 37 | * The block timestamp. 38 | */ 39 | public round: number = 0; 40 | 41 | /** 42 | * The block timestamp. 43 | */ 44 | public epoch: number = 0; 45 | 46 | /** 47 | * Constructs a configuration object from a HTTP response (as returned by the provider). 48 | */ 49 | static fromHttpResponse(payload: any): BlockOnNetwork { 50 | let blockOnNetwork = new BlockOnNetwork(); 51 | 52 | blockOnNetwork.raw = payload; 53 | blockOnNetwork.shard = Number(payload["shard"]) ?? 0; 54 | blockOnNetwork.nonce = BigInt(payload["nonce"] ?? 0); 55 | blockOnNetwork.hash = payload["hash"] ?? ""; 56 | blockOnNetwork.previousHash = payload["prevBlockHash"] ?? payload["prevHash"] ?? ""; 57 | blockOnNetwork.timestamp = Number(payload["timestamp"] ?? 0); 58 | blockOnNetwork.round = Number(payload["round"] ?? 0); 59 | blockOnNetwork.epoch = Number(payload["epoch"] ?? 0); 60 | 61 | return blockOnNetwork; 62 | } 63 | } 64 | 65 | export class BlockCoordinates { 66 | nonce: bigint = 0n; 67 | hash: string = ""; 68 | rootHash: string = ""; 69 | constructor(init?: Partial) { 70 | Object.assign(this, init); 71 | } 72 | static fromHttpResponse(payload: any): BlockCoordinates { 73 | const result = new BlockCoordinates(); 74 | const value = payload["blockInfo"] || {}; 75 | 76 | result.nonce = value["nonce"] || 0n; 77 | result.hash = value["hash"] || ""; 78 | result.rootHash = value["rootHash"] || ""; 79 | 80 | return result; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/networkProviders/config.ts: -------------------------------------------------------------------------------- 1 | import { IPagination } from "./interface"; 2 | 3 | const JSONbig = require("json-bigint")({ constructorAction: "ignore" }); 4 | 5 | export const defaultAxiosConfig = { 6 | timeout: 5000, 7 | // See: https://github.com/axios/axios/issues/983 regarding transformResponse 8 | transformResponse: [ 9 | function (data: any) { 10 | return JSONbig.parse(data); 11 | }, 12 | ], 13 | }; 14 | 15 | export const defaultPagination: IPagination = { 16 | from: 0, 17 | size: 100, 18 | }; 19 | -------------------------------------------------------------------------------- /src/networkProviders/constants.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { Address } from "../core/address"; 3 | 4 | export const MaxUint64AsBigNumber = new BigNumber("18446744073709551615"); 5 | export const EsdtContractAddress = new Address("erd1qqqqqqqqqqqqqqqpqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzllls8a5w6u"); 6 | export const BaseUserAgent = "multiversx-sdk"; 7 | export const UnknownClientName = "unknown"; 8 | 9 | export const DEFAULT_ACCOUNT_AWAITING_POLLING_TIMEOUT_IN_MILLISECONDS = 6000; 10 | export const DEFAULT_ACCOUNT_AWAITING_TIMEOUT_IN_MILLISECONDS = 11 | 15 * DEFAULT_ACCOUNT_AWAITING_POLLING_TIMEOUT_IN_MILLISECONDS; 12 | export const DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS = 0; 13 | -------------------------------------------------------------------------------- /src/networkProviders/contractQueryRequest.ts: -------------------------------------------------------------------------------- 1 | import { SmartContractQuery } from "../core/smartContractQuery"; 2 | 3 | export class ContractQueryRequest { 4 | private readonly query: SmartContractQuery; 5 | 6 | constructor(query: SmartContractQuery) { 7 | this.query = query; 8 | } 9 | 10 | toHttpRequest() { 11 | let request: any = {}; 12 | let query = this.query; 13 | request.scAddress = query.contract.toBech32(); 14 | request.caller = query.caller?.toBech32() ? query.caller.toBech32() : undefined; 15 | request.funcName = query.function; 16 | request.value = query.value ? query.value.toString() : undefined; 17 | request.args = query.arguments?.map((x) => Buffer.from(x).toString("hex")); 18 | return request; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/networkProviders/index.ts: -------------------------------------------------------------------------------- 1 | export { ApiNetworkProvider } from "./apiNetworkProvider"; 2 | export * from "./interface"; 3 | export { ProxyNetworkProvider } from "./proxyNetworkProvider"; 4 | 5 | export * from "./accounts"; 6 | export * from "./blocks"; 7 | export { NetworkConfig } from "./networkConfig"; 8 | export { NetworkProviderConfig } from "./networkProviderConfig"; 9 | export { NetworkStatus } from "./networkStatus"; 10 | export * from "./resources"; 11 | export * from "./tokenDefinitions"; 12 | export * from "./tokens"; 13 | -------------------------------------------------------------------------------- /src/networkProviders/networkProviderConfig.ts: -------------------------------------------------------------------------------- 1 | import { AxiosRequestConfig } from "axios"; 2 | 3 | export interface NetworkProviderConfig extends AxiosRequestConfig { 4 | clientName?: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/networkProviders/networkStatus.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * An object holding network status configuration parameters. 3 | */ 4 | export class NetworkStatus { 5 | raw: Record = {}; 6 | 7 | /** 8 | * The block timestamp. 9 | */ 10 | public blockTimestamp: number; 11 | 12 | /** 13 | * The block nonce. 14 | */ 15 | public blockNonce: bigint; 16 | 17 | /** 18 | * The highest final nonce. 19 | */ 20 | public highestFinalNonce: bigint; 21 | 22 | /** 23 | * The current round. 24 | */ 25 | public currentRound: bigint; 26 | 27 | /** 28 | * The current epoch. 29 | */ 30 | public currentEpoch: number; 31 | 32 | constructor() { 33 | this.currentRound = 0n; 34 | this.currentEpoch = 0; 35 | this.highestFinalNonce = 0n; 36 | this.blockNonce = 0n; 37 | this.blockTimestamp = 0; 38 | } 39 | 40 | /** 41 | * Constructs a configuration object from a HTTP response (as returned by the provider). 42 | */ 43 | static fromHttpResponse(payload: any): NetworkStatus { 44 | let networkStatus = new NetworkStatus(); 45 | 46 | networkStatus.raw = payload; 47 | networkStatus.currentRound = BigInt(payload["erd_current_round"]); 48 | networkStatus.currentEpoch = Number(payload["erd_epoch_number"]); 49 | networkStatus.highestFinalNonce = BigInt(payload["erd_highest_final_nonce"]); 50 | networkStatus.blockNonce = BigInt(payload["erd_nonce"]); 51 | networkStatus.blockTimestamp = Number(payload["erd_block_timestamp"]); 52 | 53 | return networkStatus; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/networkProviders/resources.ts: -------------------------------------------------------------------------------- 1 | import { TransactionStatus } from "../core/transactionStatus"; 2 | import { 3 | DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS, 4 | DEFAULT_ACCOUNT_AWAITING_POLLING_TIMEOUT_IN_MILLISECONDS, 5 | DEFAULT_ACCOUNT_AWAITING_TIMEOUT_IN_MILLISECONDS, 6 | } from "./constants"; 7 | 8 | export class AwaitingOptions { 9 | pollingIntervalInMilliseconds: number = DEFAULT_ACCOUNT_AWAITING_POLLING_TIMEOUT_IN_MILLISECONDS; 10 | timeoutInMilliseconds: number = DEFAULT_ACCOUNT_AWAITING_TIMEOUT_IN_MILLISECONDS; 11 | patienceInMilliseconds: number = DEFAULT_ACCOUNT_AWAITING_PATIENCE_IN_MILLISECONDS; 12 | } 13 | 14 | export class TransactionCostResponse { 15 | raw: Record = {}; 16 | gasLimit: number = 0; 17 | status: TransactionStatus = TransactionStatus.createUnknown(); 18 | 19 | static fromHttpResponse(payload: any): TransactionCostResponse { 20 | const result = new TransactionCostResponse(); 21 | result.raw = payload; 22 | result.gasLimit = payload["txGasUnits"] ?? 0; 23 | result.status = new TransactionStatus(""); 24 | 25 | return result; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/networkProviders/serialization.spec.ts: -------------------------------------------------------------------------------- 1 | import assert from "assert"; 2 | 3 | describe("test JSON serialization", function () { 4 | it("should not deserialize", async function () { 5 | const JSONbig = require("json-bigint"); 6 | const data = `{"Costum":{"foo_constructor":1}}`; 7 | assert.throws(() => JSONbig.parse(data)); 8 | }); 9 | 10 | it("should deserialize", async function () { 11 | const JSONbig = require("json-bigint")({ constructorAction: "ignore" }); 12 | const data = `{"Costum":{"foo_constructor":1}}`; 13 | JSONbig.parse(data); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/networkProviders/userAgent.ts: -------------------------------------------------------------------------------- 1 | import { AxiosHeaders } from "axios"; 2 | import { UnknownClientName } from "./constants"; 3 | import { NetworkProviderConfig } from "./networkProviderConfig"; 4 | 5 | export function extendUserAgentIfBackend(userAgentPrefix: string, config: NetworkProviderConfig) { 6 | if (isBackend()) { 7 | extendUserAgent(userAgentPrefix, config); 8 | } 9 | } 10 | 11 | function extendUserAgent(userAgentPrefix: string, config: NetworkProviderConfig) { 12 | if (!config.headers) { 13 | config.headers = new AxiosHeaders({}); 14 | } 15 | if (!config.clientName) { 16 | console.log( 17 | "We recommend providing the `clientName` when instantiating a NetworkProvider (e.g. ProxyNetworkProvider, ApiNetworkProvider). This information will be used for metrics collection and improving our services.", 18 | ); 19 | } 20 | const headers = AxiosHeaders.from(config.headers as AxiosHeaders).normalize(true); 21 | const resolvedClientName = config.clientName || UnknownClientName; 22 | 23 | const currentUserAgent = headers.hasUserAgent() ? headers.getUserAgent() : ""; 24 | const newUserAgent = currentUserAgent 25 | ? `${currentUserAgent} ${userAgentPrefix}/${resolvedClientName}` 26 | : `${userAgentPrefix}/${resolvedClientName}`; 27 | 28 | headers.setUserAgent(newUserAgent, true); 29 | } 30 | 31 | function isBackend(): boolean { 32 | return typeof window === "undefined"; 33 | } 34 | -------------------------------------------------------------------------------- /src/proto/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @packageDocumentation 3 | * @module proto 4 | */ 5 | 6 | export * from "./serializer"; 7 | -------------------------------------------------------------------------------- /src/proto/transaction.proto: -------------------------------------------------------------------------------- 1 | // This file holds the data structures related with the functionality of a shard block 2 | // 3 | // MiniBlock structure represents the body of a transaction block, holding an array of miniblocks 4 | // each of the miniblocks has a different destination shard 5 | // The body can be transmitted even before having built the heder and go through a prevalidation of each transaction 6 | 7 | syntax = "proto3"; 8 | 9 | package proto; 10 | 11 | // Transaction holds all the data needed for a value transfer or SC call 12 | message Transaction { 13 | uint64 Nonce = 1; 14 | bytes Value = 2; 15 | bytes RcvAddr = 3; 16 | bytes RcvUserName = 4; 17 | bytes SndAddr = 5; 18 | bytes SndUserName = 6; 19 | uint64 GasPrice = 7; 20 | uint64 GasLimit = 8; 21 | bytes Data = 9; 22 | bytes ChainID = 10; 23 | uint32 Version = 11; 24 | bytes Signature = 12; 25 | uint32 Options = 13; 26 | bytes GuardianAddr = 14; 27 | bytes GuardianSignature = 15; 28 | bytes Relayer = 16; 29 | bytes RelayerSignature = 17; 30 | } 31 | -------------------------------------------------------------------------------- /src/smartContracts/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resources"; 2 | export * from "./smartContractController"; 3 | export * from "./smartContractTransactionsFactory"; 4 | export * from "./smartContractTransactionsOutcomeParser"; 5 | -------------------------------------------------------------------------------- /src/smartContracts/resources.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { TokenTransfer } from "../core/tokens"; 3 | 4 | export type ContractDeployInput = { 5 | bytecode: Uint8Array; 6 | gasLimit: bigint; 7 | arguments?: any[]; 8 | nativeTransferAmount?: bigint; 9 | isUpgradeable?: boolean; 10 | isReadable?: boolean; 11 | isPayable?: boolean; 12 | isPayableBySmartContract?: boolean; 13 | }; 14 | 15 | export type ContractExecuteInput = { 16 | contract: Address; 17 | gasLimit: bigint; 18 | function: string; 19 | arguments?: any[]; 20 | nativeTransferAmount?: bigint; 21 | tokenTransfers?: TokenTransfer[]; 22 | }; 23 | 24 | export type ContractUpgradeInput = ContractDeployInput & { contract: Address }; 25 | 26 | export interface SmartContractDeployOutcome { 27 | returnCode: string; 28 | returnMessage: string; 29 | contracts: DeployedSmartContract[]; 30 | } 31 | export type ParsedSmartContractCallOutcome = { 32 | values: any[]; 33 | returnCode: string; 34 | returnMessage: string; 35 | }; 36 | 37 | export type DeployedSmartContract = { 38 | address: Address; 39 | ownerAddress: Address; 40 | codeHash: Uint8Array; 41 | }; 42 | -------------------------------------------------------------------------------- /src/smartContracts/smartContractTransactionsOutcomeParser.dev.net.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { Address } from "../core"; 3 | import { createDevnetProvider } from "../testutils/networkProviders"; 4 | import { SmartContractTransactionsOutcomeParser } from "./smartContractTransactionsOutcomeParser"; 5 | 6 | describe("test smart contract transactions outcome parser on devnet", () => { 7 | const networkProvider = createDevnetProvider(); 8 | const parser = new SmartContractTransactionsOutcomeParser(); 9 | 10 | it("should parse outcome of deploy transactions (1)", async () => { 11 | const transactionHash = "5d2ff2af8eb3fe7f2acb7e29c0436854b4c6c44de02878b6afff582888024a55"; 12 | const transactionOnNetwork = await networkProvider.getTransaction(transactionHash); 13 | const parsedGivenTransactionOnNetwork = parser.parseDeploy({ transactionOnNetwork }); 14 | 15 | assert.equal(parsedGivenTransactionOnNetwork.returnCode, "ok"); 16 | assert.deepEqual(parsedGivenTransactionOnNetwork.contracts, [ 17 | { 18 | address: Address.newFromBech32("erd1qqqqqqqqqqqqqpgqpayq2es08gq8798xhnpr0kzgn7495qt5q6uqd7lpwf"), 19 | ownerAddress: Address.newFromBech32("erd1tn62hjp72rznp8vq0lplva5csav6rccpqqdungpxtqz0g2hcq6uq9k4cc6"), 20 | codeHash: Buffer.from("c876625ec34a04445cfd99067777ebe488afdbc6899cd958f4c1d36107ca02d9", "hex"), 21 | }, 22 | ]); 23 | }); 24 | 25 | it("should parse outcome of deploy transactions (2)", async () => { 26 | const transactionHash = "76683e926dad142fc9651afca208487f2a80d327fc87e5c876eec9d028196352"; 27 | const transactionOnNetwork = await networkProvider.getTransaction(transactionHash); 28 | const parsedGivenTransactionOnNetwork = parser.parseDeploy({ transactionOnNetwork }); 29 | 30 | assert.equal(parsedGivenTransactionOnNetwork.returnCode, "execution failed"); 31 | assert.lengthOf(parsedGivenTransactionOnNetwork.contracts, 0); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/testdata/adder.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "buildInfo": { 3 | "rustc": { 4 | "version": "1.76.0-nightly", 5 | "commitHash": "d86d65bbc19b928387f68427fcc3a0da498d8a19", 6 | "commitDate": "2023-12-10", 7 | "channel": "Nightly", 8 | "short": "rustc 1.76.0-nightly (d86d65bbc 2023-12-10)" 9 | }, 10 | "contractCrate": { 11 | "name": "adder", 12 | "version": "0.0.0", 13 | "gitVersion": "v0.50.1-3-gbed74682a" 14 | }, 15 | "framework": { 16 | "name": "multiversx-sc", 17 | "version": "0.50.1" 18 | } 19 | }, 20 | "docs": [ 21 | "One of the simplest smart contracts possible,", 22 | "it holds a single variable in storage, which anyone can increment." 23 | ], 24 | "name": "Adder", 25 | "constructor": { 26 | "inputs": [ 27 | { 28 | "name": "initial_value", 29 | "type": "BigUint" 30 | } 31 | ], 32 | "outputs": [] 33 | }, 34 | "upgradeConstructor": { 35 | "inputs": [ 36 | { 37 | "name": "initial_value", 38 | "type": "BigUint" 39 | } 40 | ], 41 | "outputs": [] 42 | }, 43 | "endpoints": [ 44 | { 45 | "name": "getSum", 46 | "mutability": "readonly", 47 | "inputs": [], 48 | "outputs": [ 49 | { 50 | "type": "BigUint" 51 | } 52 | ] 53 | }, 54 | { 55 | "docs": [ 56 | "Add desired amount to the storage variable." 57 | ], 58 | "name": "add", 59 | "mutability": "mutable", 60 | "inputs": [ 61 | { 62 | "name": "value", 63 | "type": "BigUint" 64 | } 65 | ], 66 | "outputs": [] 67 | } 68 | ], 69 | "esdtAttributes": [], 70 | "hasCallback": false, 71 | "types": {} 72 | } 73 | -------------------------------------------------------------------------------- /src/testdata/adder.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multiversx/mx-sdk-js-core/56ec80065612e57e26dfe8a5f53994023dd067f7/src/testdata/adder.wasm -------------------------------------------------------------------------------- /src/testdata/answer.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "answer", 3 | "constructor": { 4 | "inputs": [], 5 | "outputs": [] 6 | }, 7 | "endpoints": [ 8 | { 9 | "name": "getUltimateAnswer", 10 | "inputs": [], 11 | "outputs": [ 12 | { 13 | "type": "i64" 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /src/testdata/answer.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multiversx/mx-sdk-js-core/56ec80065612e57e26dfe8a5f53994023dd067f7/src/testdata/answer.wasm -------------------------------------------------------------------------------- /src/testdata/array-in-nested-structs.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ArraysSample", 3 | "endpoints": [], 4 | "types": { 5 | "Dummy": { 6 | "type": "struct", 7 | "fields": [ 8 | { 9 | "name": "raw", 10 | "type": "array20" 11 | } 12 | ] 13 | }, 14 | "Foo": { 15 | "type": "struct", 16 | "fields": [ 17 | { 18 | "name": "dummy", 19 | "type": "Dummy" 20 | } 21 | ] 22 | }, 23 | "Bar": { 24 | "type": "struct", 25 | "fields": [ 26 | { 27 | "name": "foo", 28 | "type": "Foo" 29 | } 30 | ] 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/testdata/basic-features.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multiversx/mx-sdk-js-core/56ec80065612e57e26dfe8a5f53994023dd067f7/src/testdata/basic-features.wasm -------------------------------------------------------------------------------- /src/testdata/counted-variadic.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoints": [ 3 | { 4 | "name": "foo", 5 | "inputs": [ 6 | { 7 | "type": "counted-variadic" 8 | } 9 | ], 10 | "outputs": [] 11 | }, 12 | { 13 | "name": "bar", 14 | "inputs": [ 15 | { 16 | "type": "counted-variadic" 17 | }, 18 | { 19 | "type": "counted-variadic" 20 | } 21 | ], 22 | "outputs": [ 23 | { 24 | "type": "counted-variadic" 25 | }, 26 | { 27 | "type": "counted-variadic" 28 | } 29 | ] 30 | } 31 | ], 32 | "types": { 33 | "Dummy": { 34 | "type": "struct", 35 | "fields": [ 36 | { 37 | "name": "a", 38 | "type": "u32" 39 | } 40 | ] 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/testdata/counter.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "counter", 3 | "constructor": { 4 | "inputs": [], 5 | "outputs": [] 6 | }, 7 | "endpoints": [ 8 | { 9 | "name": "increment", 10 | "inputs": [], 11 | "outputs": [ 12 | { 13 | "type": "i64" 14 | } 15 | ] 16 | }, 17 | { 18 | "name": "decrement", 19 | "inputs": [], 20 | "outputs": [ 21 | { 22 | "type": "i64" 23 | } 24 | ] 25 | }, 26 | { 27 | "name": "get", 28 | "inputs": [], 29 | "outputs": [ 30 | { 31 | "type": "i64" 32 | } 33 | ] 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /src/testdata/counter.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multiversx/mx-sdk-js-core/56ec80065612e57e26dfe8a5f53994023dd067f7/src/testdata/counter.wasm -------------------------------------------------------------------------------- /src/testdata/custom-types-out-of-order-a.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample", 3 | "types": { 4 | "EsdtTokenTransfer": { 5 | "type": "struct", 6 | "fields": [ 7 | { 8 | "name": "token_type", 9 | "type": "EsdtTokenType" 10 | }, 11 | { 12 | "name": "token_identifier", 13 | "type": "TokenIdentifier" 14 | }, 15 | { 16 | "name": "token_nonce", 17 | "type": "u64" 18 | }, 19 | { 20 | "name": "amount", 21 | "type": "BigUint" 22 | } 23 | ] 24 | }, 25 | "TypeC": { 26 | "type": "struct", 27 | "fields": [ 28 | { 29 | "name": "foobar", 30 | "type": "u64" 31 | } 32 | ] 33 | }, 34 | "EsdtTokenType": { 35 | "type": "enum", 36 | "variants": [ 37 | { 38 | "name": "Fungible", 39 | "discriminant": 0 40 | }, 41 | { 42 | "name": "NonFungible", 43 | "discriminant": 1 44 | } 45 | ] 46 | }, 47 | "TypeB": { 48 | "type": "struct", 49 | "fields": [ 50 | { 51 | "name": "c", 52 | "type": "TypeC" 53 | } 54 | ] 55 | }, 56 | "TypeA": { 57 | "type": "struct", 58 | "fields": [ 59 | { 60 | "name": "b", 61 | "type": "TypeB" 62 | } 63 | ] 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/testdata/custom-types-out-of-order-b.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Sample", 3 | "types": { 4 | "EsdtTokenTransfer": { 5 | "type": "struct", 6 | "fields": [ 7 | { 8 | "name": "token_type", 9 | "type": "EsdtTokenType" 10 | }, 11 | { 12 | "name": "token_identifier", 13 | "type": "TokenIdentifier" 14 | }, 15 | { 16 | "name": "token_nonce", 17 | "type": "u64" 18 | }, 19 | { 20 | "name": "amount", 21 | "type": "BigUint" 22 | } 23 | ] 24 | }, 25 | "TypeC": { 26 | "type": "struct", 27 | "fields": [ 28 | { 29 | "name": "foobar", 30 | "type": "u64" 31 | } 32 | ] 33 | }, 34 | "EsdtTokenType": { 35 | "type": "enum", 36 | "variants": [ 37 | { 38 | "name": "Fungible", 39 | "discriminant": 0 40 | }, 41 | { 42 | "name": "NonFungible", 43 | "discriminant": 1 44 | } 45 | ] 46 | }, 47 | "TypeB": { 48 | "type": "struct", 49 | "fields": [ 50 | { 51 | "name": "c", 52 | "type": "TypeC" 53 | } 54 | ] 55 | }, 56 | "TypeA": { 57 | "type": "struct", 58 | "fields": [ 59 | { 60 | "name": "b", 61 | "type": "TypeB" 62 | } 63 | ] 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/testdata/erc20.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multiversx/mx-sdk-js-core/56ec80065612e57e26dfe8a5f53994023dd067f7/src/testdata/erc20.wasm -------------------------------------------------------------------------------- /src/testdata/explicit-enum.abi.json: -------------------------------------------------------------------------------- 1 | { 2 | "endpoints": [ 3 | { 4 | "name": "foobar", 5 | "inputs": [], 6 | "outputs": [ 7 | { 8 | "type": "OperationCompletionStatus" 9 | } 10 | ] 11 | } 12 | ], 13 | "types": { 14 | "OperationCompletionStatus": { 15 | "type": "explicit-enum", 16 | "variants": [ 17 | { 18 | "docs": [ 19 | "indicates that operation was completed" 20 | ], 21 | "name": "completed" 22 | }, 23 | { 24 | "docs": [ 25 | "indicates that operation was interrupted prematurely, due to low gas" 26 | ], 27 | "name": "interrupted" 28 | } 29 | ] 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/testdata/lottery-esdt.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multiversx/mx-sdk-js-core/56ec80065612e57e26dfe8a5f53994023dd067f7/src/testdata/lottery-esdt.wasm -------------------------------------------------------------------------------- /src/testdata/multisig-full.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multiversx/mx-sdk-js-core/56ec80065612e57e26dfe8a5f53994023dd067f7/src/testdata/multisig-full.wasm -------------------------------------------------------------------------------- /src/testdata/testwallets/alice.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "0dc10c02-b59b-4bac-9710-6b2cfa4284ba", 5 | "address": "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", 6 | "bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", 7 | "crypto": { 8 | "ciphertext": "4c41ef6fdfd52c39b1585a875eb3c86d30a315642d0e35bb8205b6372c1882f135441099b11ff76345a6f3a930b5665aaf9f7325a32c8ccd60081c797aa2d538", 9 | "cipherparams": { 10 | "iv": "033182afaa1ebaafcde9ccc68a5eac31" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "4903bd0e7880baa04fc4f886518ac5c672cdc745a6bd13dcec2b6c12e9bffe8d", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "5b4a6f14ab74ba7ca23db6847e28447f0e6a7724ba9664cf425df707a84f5a8b" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/alice.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- 2 | NDEzZjQyNTc1ZjdmMjZmYWQzMzE3YTc3ODc3MTIxMmZkYjgwMjQ1ODUwOTgxZTQ4 3 | YjU4YTRmMjVlMzQ0ZThmOTAxMzk0NzJlZmY2ODg2NzcxYTk4MmYzMDgzZGE1ZDQy 4 | MWYyNGMyOTE4MWU2Mzg4ODIyOGRjODFjYTYwZDY5ZTE= 5 | -----END PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- -------------------------------------------------------------------------------- /src/testdata/testwallets/bob.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "85fdc8a7-7119-479d-b7fb-ab4413ed038d", 5 | "address": "8049d639e5a6980d1cd2392abcce41029cda74a1563523a202f09641cc2618f8", 6 | "bech32": "erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx", 7 | "crypto": { 8 | "ciphertext": "c2664a31350aaf6a00525560db75c254d0aea65dc466441356c1dd59253cceb9e83eb05730ef3f42a11573c9a0e33dd952d488f00535b35357bb41d127b1eb82", 9 | "cipherparams": { 10 | "iv": "18378411e31f6c4e99f1435d9ab82831" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "18304455ac2dbe2a2018bda162bd03ef95b81622e99d8275c34a6d5e6932a68b", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "23756172195ac483fa29025dc331bc7aa2c139533922a8dc08642eb0a677541f" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/bob.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx----- 2 | YjhjYTZmODIwM2ZiNGI1NDVhOGU4M2M1Mzg0ZGEwMzNjNDE1ZGIxNTViNTNmYjVi 3 | OGViYTdmZjVhMDM5ZDYzOTgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAy 4 | OWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg= 5 | -----END PRIVATE KEY for erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx----- -------------------------------------------------------------------------------- /src/testdata/testwallets/carol.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "65894f35-d142-41d2-9335-6ad02e0ed0be", 5 | "address": "b2a11555ce521e4944e09ab17549d85b487dcd26c84b5017a39e31a3670889ba", 6 | "bech32": "erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8", 7 | "crypto": { 8 | "ciphertext": "bdfb984a1e7c7460f0a289749609730cdc99d7ce85b59305417c2c0f007b2a6aaa7203dd94dbf27315bced39b0b281769fbc70b01e6e57f89ae2f2a9e9100007", 9 | "cipherparams": { 10 | "iv": "258ed2b4dc506b4dc9d274b0449b0eb0" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "4f2f5530ce28dc0210962589b908f52714f75c8fb79ff18bdd0024c43c7a220b", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "f8de52e2627024eaa33f2ee5eadcd3d3815e10dd274ea966dc083d000cc8b258" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/carol.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8----- 2 | ZTI1M2E1NzFjYTE1M2RjMmFlZTg0NTgxOWY3NGJjYzk3NzNiMDU4NmVkZWFkMTVh 3 | OTRjYjcyMzVhNTAyNzQzNmIyYTExNTU1Y2U1MjFlNDk0NGUwOWFiMTc1NDlkODVi 4 | NDg3ZGNkMjZjODRiNTAxN2EzOWUzMWEzNjcwODg5YmE= 5 | -----END PRIVATE KEY for erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8----- -------------------------------------------------------------------------------- /src/testdata/testwallets/dan.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "fc8b9b89-2227-41ec-afd1-5e6853feb7b2", 5 | "address": "b13a017423c366caff8cecfb77a12610a130f4888134122c7937feae0d6d7d17", 6 | "bech32": "erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7", 7 | "crypto": { 8 | "ciphertext": "b2a6fffd935ea3c9bc8ac136fdb548554faf0c5e4b78b347305499c29a4ae71dfca565297bfcf85df6faf850d2a42368178937b69a0e95ee62d8f112f98ad918", 9 | "cipherparams": { 10 | "iv": "46e398d62d85faa06d2f424303772ea3" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "58b3e16f0fa600ca004bddd68c6487e63beb0df42d050e2eea3c838d9c664224", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "141fc44faa4376187278cef7e6a5b8b1a3d5d4932f0590bac9f0772bf21dd358" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/dan.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7----- 2 | NTRlNDc5NzQzZTUxMjhlMGUyOWRiNGQ0YTIxZDc1OWI2OTAxMjQxZmFjNzdjNTAy 3 | YzhlZjcwZGM3OWExMmE4YmIxM2EwMTc0MjNjMzY2Y2FmZjhjZWNmYjc3YTEyNjEw 4 | YTEzMGY0ODg4MTM0MTIyYzc5MzdmZWFlMGQ2ZDdkMTc= 5 | -----END PRIVATE KEY for erd1kyaqzaprcdnv4luvanah0gfxzzsnpaygsy6pytrexll2urtd05ts9vegu7----- -------------------------------------------------------------------------------- /src/testdata/testwallets/eve.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "e770c455-a23b-4dcd-a7a5-0e22375dc233", 5 | "address": "3af8d9c9423b2577c6252722c1d90212a4111f7203f9744f76fcfa1d0a310033", 6 | "bech32": "erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4", 7 | "crypto": { 8 | "ciphertext": "ebabff99b80b867154cfc24726d47a58a6dfc2bf056e9fe0257841287f2f496c443533583d2cbf28e796f38186b3145774007876e93eca83d815b396cfe7f89a", 9 | "cipherparams": { 10 | "iv": "abc1ada087b472a0154e869f16ad3b2f" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "d1a57ea919706bdad2916bc4ebf5bc072843b1333818808a424d34b8cd851491", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "55b8bd2cada5766704ac9b70742cace95f5faa66a7f5edf7c9e3b31755710caf" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/eve.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4----- 2 | NjliODJmMzlhMTQ2MWI0MzFmOGZhMjY3Zjg3YTBiZjc5NDAyN2NlYTM3ZGM2NjE5 3 | MGU2NzQwYTZlNGNjY2RmMDNhZjhkOWM5NDIzYjI1NzdjNjI1MjcyMmMxZDkwMjEy 4 | YTQxMTFmNzIwM2Y5NzQ0Zjc2ZmNmYTFkMGEzMTAwMzM= 5 | -----END PRIVATE KEY for erd18tudnj2z8vjh0339yu3vrkgzz2jpz8mjq0uhgnmklnap6z33qqeszq2yn4----- -------------------------------------------------------------------------------- /src/testdata/testwallets/frank.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "df70f3ef-bb40-4afd-8751-77b26b29356d", 5 | "address": "b37f5d130beb8885b90ab574a8bfcdd894ca531a7d3d1f3431158d77d6185fbb", 6 | "bech32": "erd1kdl46yctawygtwg2k462307dmz2v55c605737dp3zkxh04sct7asqylhyv", 7 | "crypto": { 8 | "ciphertext": "50e0992457078c3b1b5512e003ab911065fb64429dbc0dbec4d425670ecf410807de7ac5558568f70fac0c003ee9d090831e4e0801add602b89f0163735d11e8", 9 | "cipherparams": { 10 | "iv": "264b13aee12e96a25ad5dc91518cdfdb" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "fc05efc44099f837d3fdf1741f86f97abe394ed6c9882ff26d35bc6deab72a53", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "5308637cdac70085b775f4c7e55229530b8139aef4c3f26b7855cbc96317584c" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/frank.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1kdl46yctawygtwg2k462307dmz2v55c605737dp3zkxh04sct7asqylhyv----- 2 | NzgyMGE5MTE4OWM3MjdjMDc1YjQ5OWM1MjNmMGRjNDAzZmVkMzc5OTBlYzhkMzdm 3 | NGJkZWNiOGI2OGZkMmE5N2IzN2Y1ZDEzMGJlYjg4ODViOTBhYjU3NGE4YmZjZGQ4 4 | OTRjYTUzMWE3ZDNkMWYzNDMxMTU4ZDc3ZDYxODVmYmI= 5 | -----END PRIVATE KEY for erd1kdl46yctawygtwg2k462307dmz2v55c605737dp3zkxh04sct7asqylhyv----- -------------------------------------------------------------------------------- /src/testdata/testwallets/grace.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "9aff338f-f504-403c-86cd-5e623bc81c42", 5 | "address": "1e8a8b6b49de5b7be10aaa158a5a6a4abb4b56cc08f524bb5e6cd5f211ad3e13", 6 | "bech32": "erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede", 7 | "crypto": { 8 | "ciphertext": "b8f4fe8790a980fb78e5b126c52e81009910101210420da1da0d558313a45eb8803b57825bc19a2a8b92d7af147f97a0bf261ce152bc61cccd6e34df4aa9ab09", 9 | "cipherparams": { 10 | "iv": "0be21668939564c5b2ac122a71ce97a4" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "7c5b1a6f91d257a11b327b9745a27bb4708867e17a1cce942cd9a3a3d74e7bf0", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "a3c251303a253d82ae2be6535a65d0584f6bb92a84199d7f0c6eff249b46ce9a" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/grace.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- 2 | ODY5Yzk4ZGYwYjExZmRhMDgwNGQ3ODM1NGVjYTQzMWY4N2E2ZTkxMGZhZTE0MDgx 3 | ZjczOGMzMTZhMjVlYTQwOTFlOGE4YjZiNDlkZTViN2JlMTBhYWExNThhNWE2YTRh 4 | YmI0YjU2Y2MwOGY1MjRiYjVlNmNkNWYyMTFhZDNlMTM= 5 | -----END PRIVATE KEY for erd1r69gk66fmedhhcg24g2c5kn2f2a5k4kvpr6jfw67dn2lyydd8cfswy6ede----- -------------------------------------------------------------------------------- /src/testdata/testwallets/heidi.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "1b55836f-946f-4dc3-946d-3c27e5096873", 5 | "address": "6e224118d9068ae626878a1cfbebcb6a95a4715db86d1b51e06a04226cf30fd6", 6 | "bech32": "erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha", 7 | "crypto": { 8 | "ciphertext": "04d8c15a32a8f5917980f8a1cc16adae29c1241ac42c917d154dbaced47c3208d372378eaa39e201785db891dfe397e33639cf23abcbeddd7e2ce38944e7e30c", 9 | "cipherparams": { 10 | "iv": "053caa5b1b236e786af178421fc0b829" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "ec9bbfb8bceb60545b4500cb7990897aaf1ab51fa0154e7a2917c30759a4cfe5", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "b326a517fea8e38af156e2c7d256144ada0f8ab6740d04faf2a8f98a54dff6c9" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/heidi.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha----- 2 | YjMzODcyMjMzMjFjNTg3YzM1MTE5OGFkYzI1MzFjODIwYTM3OGQzNzQ4OTU2YjNk 3 | YWUyZTBmNWExZGFhN2NhMTZlMjI0MTE4ZDkwNjhhZTYyNjg3OGExY2ZiZWJjYjZh 4 | OTVhNDcxNWRiODZkMWI1MWUwNmEwNDIyNmNmMzBmZDY= 5 | -----END PRIVATE KEY for erd1dc3yzxxeq69wvf583gw0h67td226gu2ahpk3k50qdgzzym8npltq7ndgha----- -------------------------------------------------------------------------------- /src/testdata/testwallets/ivan.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "0b80d732-d4e6-4145-b5bb-698bdd323b3c", 5 | "address": "899451b361a83e89d73b4096d3c90c209b27874c9c7cb01bb08b0bb4dc15693d", 6 | "bech32": "erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx", 7 | "crypto": { 8 | "ciphertext": "2c7b56ea9ebec244382018818065a78f19cec328596b28eca2fa51defd22098f3c0164fbcb63b144f0c416fbefbf147e4df17c41eaed7caba2feb7fcdb7ec4e5", 9 | "cipherparams": { 10 | "iv": "f20e0d3fa965ef6c3084fdb712c7fcd9" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "a12bab6dd6df4eb7b841214ab26ad7724b8be3a52873f7a3565ba65b85831028", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "0a1c6c7a17c39b4882d00910b4bca532dd215b1e0b914f9624920ae1ac8b38c6" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/ivan.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx----- 2 | MDhjNjE1NTUyMjFmMWYxMjE0M2Q3MWJiZWQ4MzFkMjk0N2U0OTRhMjJjZmQxN2I1 3 | NzQxMzhiY2M5ODQ1MzZkMzg5OTQ1MWIzNjFhODNlODlkNzNiNDA5NmQzYzkwYzIw 4 | OWIyNzg3NGM5YzdjYjAxYmIwOGIwYmI0ZGMxNTY5M2Q= 5 | -----END PRIVATE KEY for erd13x29rvmp4qlgn4emgztd8jgvyzdj0p6vn37tqxas3v9mfhq4dy7shalqrx----- -------------------------------------------------------------------------------- /src/testdata/testwallets/judy.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "a3108434-5d2c-4dca-9f94-29e822697e20", 5 | "address": "4a101a0f8f95f1218683900801cd971c6028b1597a771b2ed367d1ede09d9d2a", 6 | "bech32": "erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan", 7 | "crypto": { 8 | "ciphertext": "8ae4807bc0d836606bf015ffde34274ddc454a61448bff6fc7a250a8530c5d478bc3988b8c3a3c631eb657e7fc5289cdfdcbb8f77be6faefb32ca77179636c73", 9 | "cipherparams": { 10 | "iv": "41f1833a14d4675cab19dd96bca5eb44" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "6f5f765b00ece2f9dc654f008a21f7458d11aa53b56d119c90a29a9cc46c64f6", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "01314543ab6866bba660952b697bc4555bc040770537be7409ceff83057b170f" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/judy.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan----- 2 | MDgzZmQ2ZjAxYzRjMGZiMjUzY2QzOGMxMTE5OThiNGEzYjUzZDQyOTAwOGI4Mjhl 3 | NzZhYzk3ZGE2ZjU4NTgyYTRhMTAxYTBmOGY5NWYxMjE4NjgzOTAwODAxY2Q5NzFj 4 | NjAyOGIxNTk3YTc3MWIyZWQzNjdkMWVkZTA5ZDlkMmE= 5 | -----END PRIVATE KEY for erd1fggp5ru0jhcjrp5rjqyqrnvhr3sz3v2e0fm3ktknvlg7mcyan54qzccnan----- -------------------------------------------------------------------------------- /src/testdata/testwallets/mallory.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "6756aa76-5934-4dc6-90c7-c261db98fe44", 5 | "address": "1454931ffa758ab35654a5206b28e9dbf1fb8df8f9ced093bb2887eb39f7e7af", 6 | "bech32": "erd1z32fx8l6wk9tx4j555sxk28fm0clhr0cl88dpyam9zr7kw0hu7hsx2j524", 7 | "crypto": { 8 | "ciphertext": "b05925b7f3c3eaec07258987848346355f66be347e748defb7064b34f3104a625e1f0b0046ec89618dcf70caa75a5c62a1a8b10563a0854bb068d99427697d4a", 9 | "cipherparams": { 10 | "iv": "673d0b58985d74e061288a41e9a5c0d8" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "a313394c586b66d5fb1fad55fb02528113e2cce9f5663a9af4ca8fb794ad7ddf", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "54039bd973c972e7d21b87589fc6cb5758e72a73afcc92944d5d14ed403a895c" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/mallory.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1z32fx8l6wk9tx4j555sxk28fm0clhr0cl88dpyam9zr7kw0hu7hsx2j524----- 2 | OTVjMWMwMzRlM2NmNzczNzdmMTU3NTFhYWZjZmZiMmEyZWIyZDE2NDAxYzY3MTQ0 3 | NDIyZTFkMDNlYzIwYWM1MjE0NTQ5MzFmZmE3NThhYjM1NjU0YTUyMDZiMjhlOWRi 4 | ZjFmYjhkZjhmOWNlZDA5M2JiMjg4N2ViMzlmN2U3YWY= 5 | -----END PRIVATE KEY for erd1z32fx8l6wk9tx4j555sxk28fm0clhr0cl88dpyam9zr7kw0hu7hsx2j524----- -------------------------------------------------------------------------------- /src/testdata/testwallets/mike.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "3f6adbc3-1215-4c31-9a61-a049b430e6f7", 5 | "address": "e32afedc904fe1939746ad973beb383563cf63642ba669b3040f9b9428a5ed60", 6 | "bech32": "erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa", 7 | "crypto": { 8 | "ciphertext": "39a3ee873e39b30dd486dfe36fb9f8d8fb7bdaa83b30a35ac823acb0f69fb97413ff916afc6184888b60ffbe068a99c9109bb0291e21eae8c9556527263bbde7", 9 | "cipherparams": { 10 | "iv": "e99cc4008d995919b0da66aa94f95991" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "44f6da2344cf56a23c7ebdc22033caab21adf0ff18144ddf513b5a56f7f04eb5", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "eba324243f326ac2fd78c339962822147b8f01cd13c40fe019cf3b7834657d31" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/mike.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa----- 2 | Y2JkMjYzNThmMDg0YWZlMDA0ZDUzZjQ2NzMwM2Q3MTMzZjU1MzBiYjhkODE2NzUx 3 | MWI3YzEyNjg1YWJjNTY4N2UzMmFmZWRjOTA0ZmUxOTM5NzQ2YWQ5NzNiZWIzODM1 4 | NjNjZjYzNjQyYmE2NjliMzA0MGY5Yjk0MjhhNWVkNjA= 5 | -----END PRIVATE KEY for erd1uv40ahysflse896x4ktnh6ecx43u7cmy9wnxnvcyp7deg299a4sq6vaywa----- -------------------------------------------------------------------------------- /src/testdata/testwallets/mnemonic.txt: -------------------------------------------------------------------------------- 1 | moral volcano peasant pass circle pen over picture flat shop clap goat never lyrics gather prepare woman film husband gravity behind test tiger improve -------------------------------------------------------------------------------- /src/testdata/testwallets/multipleUserKeys.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- 2 | NDEzZjQyNTc1ZjdmMjZmYWQzMzE3YTc3ODc3MTIxMmZkYjgwMjQ1ODUwOTgxZTQ4 3 | YjU4YTRmMjVlMzQ0ZThmOTAxMzk0NzJlZmY2ODg2NzcxYTk4MmYzMDgzZGE1ZDQy 4 | MWYyNGMyOTE4MWU2Mzg4ODIyOGRjODFjYTYwZDY5ZTE= 5 | -----END PRIVATE KEY for erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th----- 6 | -----BEGIN PRIVATE KEY for erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx----- 7 | YjhjYTZmODIwM2ZiNGI1NDVhOGU4M2M1Mzg0ZGEwMzNjNDE1ZGIxNTViNTNmYjVi 8 | OGViYTdmZjVhMDM5ZDYzOTgwNDlkNjM5ZTVhNjk4MGQxY2QyMzkyYWJjY2U0MTAy 9 | OWNkYTc0YTE1NjM1MjNhMjAyZjA5NjQxY2MyNjE4Zjg= 10 | -----END PRIVATE KEY for erd1spyavw0956vq68xj8y4tenjpq2wd5a9p2c6j8gsz7ztyrnpxrruqzu66jx----- 11 | -----BEGIN PRIVATE KEY for erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8----- 12 | ZTI1M2E1NzFjYTE1M2RjMmFlZTg0NTgxOWY3NGJjYzk3NzNiMDU4NmVkZWFkMTVh 13 | OTRjYjcyMzVhNTAyNzQzNmIyYTExNTU1Y2U1MjFlNDk0NGUwOWFiMTc1NDlkODVi 14 | NDg3ZGNkMjZjODRiNTAxN2EzOWUzMWEzNjcwODg5YmE= 15 | -----END PRIVATE KEY for erd1k2s324ww2g0yj38qn2ch2jwctdy8mnfxep94q9arncc6xecg3xaq6mjse8----- 16 | -------------------------------------------------------------------------------- /src/testdata/testwallets/multipleValidatorKeys.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d----- 2 | N2MxOWJmM2EwYzU3Y2RkMWZiMDhlNDYwN2NlYmFhMzY0N2Q2YjkyNjFiNDY5M2Y2 3 | MWU5NmU1NGIyMThkNDQyYQ== 4 | -----END PRIVATE KEY for f8910e47cf9464777c912e6390758bb39715fffcb861b184017920e4a807b42553f2f21e7f3914b81bcf58b66a72ab16d97013ae1cff807cefc977ef8cbf116258534b9e46d19528042d16ef8374404a89b184e0a4ee18c77c49e454d04eae8d----- 5 | -----BEGIN PRIVATE KEY for 1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d----- 6 | MzAzNGIxZDU4NjI4YTg0Mjk4NGRhMGM3MGRhMGI1YTI1MWViYjJhZWJmNTFhZmM1 7 | YjU4NmUyODM5YjVlNTI2Mw== 8 | -----END PRIVATE KEY for 1b4e60e6d100cdf234d3427494dac55fbac49856cadc86bcb13a01b9bb05a0d9143e86c186c948e7ae9e52427c9523102efe9019a2a9c06db02993f2e3e6756576ae5a3ec7c235d548bc79de1a6990e1120ae435cb48f7fc436c9f9098b92a0d----- 9 | -----BEGIN PRIVATE KEY for e5dc552b4b170cdec4405ff8f9af20313bf0e2756d06c35877b6fbcfa6b354a7b3e2d439ea87999befb09a8fa1b3f014e57ec747bf738c4199338fcd4a87b373dd62f5c8329f1f5f245956bbb06685596a2e83dc38befa63e4a2b5c4ce408506----- 10 | ZGU3ZTFiMzg1ZWRiYjBlMWU4ZjlmYzI1ZDkxYmQ4ZWVkNzFhMWRhN2NhYWI3MzJl 11 | NmI0N2E0ODA0MmQ4NTIzZA== 12 | -----END PRIVATE KEY for e5dc552b4b170cdec4405ff8f9af20313bf0e2756d06c35877b6fbcfa6b354a7b3e2d439ea87999befb09a8fa1b3f014e57ec747bf738c4199338fcd4a87b373dd62f5c8329f1f5f245956bbb06685596a2e83dc38befa63e4a2b5c4ce408506----- 13 | -----BEGIN PRIVATE KEY for 12773304cb718250edd89770cedcbf675ccdb7fe2b30bd3185ca65ffa0d516879768ed03f92e41a6e5bc5340b78a9d02655e3b727c79730ead791fb68eaa02b84e1be92a816a9604a1ab9a6d3874b638487e2145239438a4bafac3889348d405----- 14 | OGViZWIwN2QyOTZhZDI1Mjk0MDBiNDA2ODdhNzQxYTEzNWY4MzU3Zjc5ZjM5ZmNi 15 | Mjg5NGE2Zjk3MDNhNTgxNg== 16 | -----END PRIVATE KEY for 12773304cb718250edd89770cedcbf675ccdb7fe2b30bd3185ca65ffa0d516879768ed03f92e41a6e5bc5340b78a9d02655e3b727c79730ead791fb68eaa02b84e1be92a816a9604a1ab9a6d3874b638487e2145239438a4bafac3889348d405----- 17 | -------------------------------------------------------------------------------- /src/testdata/testwallets/password.txt: -------------------------------------------------------------------------------- 1 | password -------------------------------------------------------------------------------- /src/testdata/testwallets/validatorKey00.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- 2 | N2NmZjk5YmQ2NzE1MDJkYjdkMTViYzhhYmMwYzlhODA0ZmI5MjU0MDZmYmRkNTBm 3 | MWU0YzE3YTRjZDc3NDI0Nw== 4 | -----END PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- 5 | -------------------------------------------------------------------------------- /src/testdata/testwallets/validatorKey00WithExtraLines.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- 2 | N2NmZjk5YmQ2NzE1MDJkYjdkMTViYzhhYmMwYzlhODA0ZmI5MjU0MDZmYmRkNTBm 3 | 4 | MWU0YzE3YTRjZDc3NDI0Nw== 5 | 6 | -----END PRIVATE KEY for e7beaa95b3877f47348df4dd1cb578a4f7cabf7a20bfeefe5cdd263878ff132b765e04fef6f40c93512b666c47ed7719b8902f6c922c04247989b7137e837cc81a62e54712471c97a2ddab75aa9c2f58f813ed4c0fa722bde0ab718bff382208----- 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/testdata/testwallets/withDummyMnemonic.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "id": "5b448dbc-5c72-4d83-8038-938b1f8dff19", 4 | "kind": "mnemonic", 5 | "crypto": { 6 | "ciphertext": "6d70fbdceba874f56f15af4b1d060223799288cfc5d276d9ebb91732f5a38c3c59f83896fa7e7eb6a04c05475a6fe4d154de9b9441864c507abd0eb6987dac521b64c0c82783a3cd1e09270cd6cb5ae493f9af694b891253ac1f1ffded68b5ef39c972307e3c33a8354337540908acc795d4df72298dda1ca28ac920983e6a39a01e2bc988bd0b21f864c6de8b5356d11e4b77bc6f75ef", 7 | "cipherparams": { 8 | "iv": "2da5620906634972d9a623bc249d63d4" 9 | }, 10 | "cipher": "aes-128-ctr", 11 | "kdf": "scrypt", 12 | "kdfparams": { 13 | "dklen": 32, 14 | "salt": "aa9e0ba6b188703071a582c10e5331f2756279feb0e2768f1ba0fd38ec77f035", 15 | "n": 4096, 16 | "r": 8, 17 | "p": 1 18 | }, 19 | "mac": "5bc1b20b6d903b8ef3273eedf028112d65eaf85a5ef4215917c1209ec2df715a" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/testdata/testwallets/withDummySecretKey.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "kind": "secretKey", 4 | "id": "c1d4b111-b8d2-4916-a213-bcfd237edd29", 5 | "address": "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", 6 | "bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", 7 | "crypto": { 8 | "ciphertext": "75fbe213fc1964ce03100cf7d873748edf83a02631c8af9abdb23d210b9a2a15940bea2e56718f7bd710a938df5eb424c629e6a39b6ee056ed80d6e5f3b97791", 9 | "cipherparams": { 10 | "iv": "226d13be12373603af2b4edefcaa436f" 11 | }, 12 | "cipher": "aes-128-ctr", 13 | "kdf": "scrypt", 14 | "kdfparams": { 15 | "dklen": 32, 16 | "salt": "d57862c212bac142a89da97fb9bf9f5c91c8e8ddba952262dafe928e1c8a9906", 17 | "n": 4096, 18 | "r": 8, 19 | "p": 1 20 | }, 21 | "mac": "5bab92263237c5d595f565622dd2e61ea3dfd43580cecda7fd2f42d469b42e7f" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testdata/testwallets/withoutKind.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 4, 3 | "id": "0dc10c02-b59b-4bac-9710-6b2cfa4284ba", 4 | "address": "0139472eff6886771a982f3083da5d421f24c29181e63888228dc81ca60d69e1", 5 | "bech32": "erd1qyu5wthldzr8wx5c9ucg8kjagg0jfs53s8nr3zpz3hypefsdd8ssycr6th", 6 | "crypto": { 7 | "ciphertext": "4c41ef6fdfd52c39b1585a875eb3c86d30a315642d0e35bb8205b6372c1882f135441099b11ff76345a6f3a930b5665aaf9f7325a32c8ccd60081c797aa2d538", 8 | "cipherparams": { 9 | "iv": "033182afaa1ebaafcde9ccc68a5eac31" 10 | }, 11 | "cipher": "aes-128-ctr", 12 | "kdf": "scrypt", 13 | "kdfparams": { 14 | "dklen": 32, 15 | "salt": "4903bd0e7880baa04fc4f886518ac5c672cdc745a6bd13dcec2b6c12e9bffe8d", 16 | "n": 4096, 17 | "r": 8, 18 | "p": 1 19 | }, 20 | "mac": "5b4a6f14ab74ba7ca23db6847e28447f0e6a7724ba9664cf425df707a84f5a8b" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/testutils/files.ts: -------------------------------------------------------------------------------- 1 | import * as fs from "fs"; 2 | 3 | export async function readTestFile(filePath: string): Promise { 4 | if (isOnBrowserTests()) { 5 | return await downloadTextFile(filePath); 6 | } 7 | 8 | return await fs.promises.readFile(filePath, { encoding: "utf8" }); 9 | } 10 | 11 | export function isOnBrowserTests() { 12 | const BROWSER_TESTS_URL = "browser-tests"; 13 | 14 | let noWindow = typeof window === "undefined"; 15 | if (noWindow) { 16 | return false; 17 | } 18 | 19 | let isOnTests = window.location.href.includes(BROWSER_TESTS_URL); 20 | return isOnTests; 21 | } 22 | 23 | export async function downloadTextFile(url: string) { 24 | const response = await fetch(url); 25 | const text = await response.text(); 26 | return text; 27 | } 28 | -------------------------------------------------------------------------------- /src/testutils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./mockNetworkProvider"; 2 | export * from "./utils"; 3 | export * from "./wallets"; 4 | -------------------------------------------------------------------------------- /src/testutils/networkProviders.ts: -------------------------------------------------------------------------------- 1 | import { ApiNetworkProvider, ProxyNetworkProvider } from "../networkProviders"; 2 | import { INetworkProvider } from "../networkProviders/interface"; 3 | 4 | export function createLocalnetProvider(): INetworkProvider { 5 | return new ProxyNetworkProvider("http://localhost:7950", { timeout: 5000 }); 6 | } 7 | 8 | export function createTestnetProvider(): INetworkProvider { 9 | return new ApiNetworkProvider("https://testnet-api.multiversx.com", { 10 | timeout: 5000, 11 | clientName: "mx-sdk-js-core/tests", 12 | }); 13 | } 14 | 15 | export function createDevnetProvider(): INetworkProvider { 16 | return new ProxyNetworkProvider("https://devnet-gateway.multiversx.com", { 17 | timeout: 5000, 18 | clientName: "mx-sdk-js-core/tests", 19 | }); 20 | } 21 | 22 | export function createMainnetProvider(): INetworkProvider { 23 | return new ProxyNetworkProvider("https://gateway.multiversx.com", { 24 | timeout: 10000, 25 | clientName: "mx-sdk-js-core/tests", 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /src/tokenManagement/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resources"; 2 | export * from "./tokenManagementController"; 3 | export * from "./tokenManagementTransactionsFactory"; 4 | export * from "./tokenManagementTransactionsOutcomeParser"; 5 | -------------------------------------------------------------------------------- /src/tokenOperations/codec.spec.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import { assert } from "chai"; 3 | import { bigIntToBuffer, bigIntToHex, bufferToBigInt, bufferToHex, stringToBuffer, utf8ToHex } from "./codec"; 4 | 5 | describe("test codec", () => { 6 | it("should properly encode and decode values", () => { 7 | assert.deepEqual(stringToBuffer("hello"), Buffer.from("hello")); 8 | assert.deepEqual(bufferToBigInt(Buffer.from("075bcd15", "hex")), new BigNumber("123456789")); 9 | assert.deepEqual(bufferToBigInt(Buffer.from([])), new BigNumber("0")); 10 | assert.deepEqual(bigIntToBuffer("123456789"), Buffer.from("075bcd15", "hex")); 11 | assert.deepEqual(bigIntToBuffer(0), Buffer.from([])); 12 | assert.equal(bigIntToHex("123456789"), "075bcd15"); 13 | assert.equal(bigIntToHex(0), ""); 14 | assert.equal(utf8ToHex("hello"), "68656c6c6f"); 15 | assert.equal(utf8ToHex(""), ""); 16 | assert.equal(bufferToHex(Buffer.from("hello")), "68656c6c6f"); 17 | assert.equal(bufferToHex(Buffer.from([])), ""); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/tokenOperations/codec.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from "bignumber.js"; 2 | import * as contractsCodecUtils from "../abi/codec/utils"; 3 | import * as codecUtils from "../core/utils.codec"; 4 | 5 | export function stringToBuffer(value: string): Buffer { 6 | return Buffer.from(value); 7 | } 8 | 9 | export function bufferToBigInt(buffer: Buffer): BigNumber { 10 | if (buffer.length == 0) { 11 | return new BigNumber(0); 12 | } 13 | 14 | return contractsCodecUtils.bufferToBigInt(buffer); 15 | } 16 | 17 | export function bigIntToBuffer(value: BigNumber.Value): Buffer { 18 | if (value == 0) { 19 | return Buffer.from([]); 20 | } 21 | 22 | return contractsCodecUtils.bigIntToBuffer(value); 23 | } 24 | 25 | export { bigIntToHex, utf8ToHex } from "../core/utils.codec"; 26 | 27 | export function bufferToHex(value: Buffer) { 28 | const hex = value.toString("hex"); 29 | return codecUtils.zeroPadStringIfOddLength(hex); 30 | } 31 | -------------------------------------------------------------------------------- /src/transactionsOutcomeParsers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "../smartContracts/smartContractTransactionsOutcomeParser"; 2 | export * from "../tokenManagement/tokenManagementTransactionsOutcomeParser"; 3 | export * from "./resources"; 4 | export * from "./transactionEventsParser"; 5 | -------------------------------------------------------------------------------- /src/transactionsOutcomeParsers/resources.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { TransactionEvent } from "../core/transactionEvents"; 3 | import { TransactionLogs } from "../core/transactionLogs"; 4 | import { TransactionOnNetwork } from "../core/transactionOnNetwork"; 5 | 6 | export class SmartContractResult { 7 | raw: Record; 8 | sender: Address; 9 | receiver: Address; 10 | data: Uint8Array; 11 | logs: TransactionLogs; 12 | 13 | constructor(init: Partial) { 14 | this.sender = Address.empty(); 15 | this.receiver = Address.empty(); 16 | this.data = new Uint8Array(); 17 | this.logs = new TransactionLogs({}); 18 | this.raw = {}; 19 | 20 | Object.assign(this, init); 21 | } 22 | } 23 | 24 | export class SmartContractCallOutcome { 25 | function: string; 26 | returnDataParts: Uint8Array[]; 27 | returnMessage: string; 28 | returnCode: string; 29 | 30 | constructor(init: Partial) { 31 | this.function = ""; 32 | this.returnDataParts = []; 33 | this.returnMessage = ""; 34 | this.returnCode = ""; 35 | 36 | Object.assign(this, init); 37 | } 38 | } 39 | 40 | export function findEventsByPredicate( 41 | transactionOutcome: TransactionOnNetwork, 42 | predicate: (event: TransactionEvent) => boolean, 43 | ): TransactionEvent[] { 44 | return gatherAllEvents(transactionOutcome).filter(predicate); 45 | } 46 | 47 | export function findEventsByIdentifier( 48 | transactionOutcome: TransactionOnNetwork, 49 | identifier: string, 50 | ): TransactionEvent[] { 51 | return findEventsByPredicate(transactionOutcome, (event) => event.identifier == identifier); 52 | } 53 | 54 | export function findEventsByFirstTopic(transactionOutcome: TransactionOnNetwork, topic: string): TransactionEvent[] { 55 | return findEventsByPredicate(transactionOutcome, (event) => event.topics[0]?.toString() == topic); 56 | } 57 | 58 | export function gatherAllEvents(transactionOutcome: TransactionOnNetwork): TransactionEvent[] { 59 | const allEvents = []; 60 | 61 | allEvents.push(...transactionOutcome.logs.events); 62 | 63 | for (const item of transactionOutcome.smartContractResults) { 64 | allEvents.push(...item.logs.events); 65 | } 66 | 67 | return allEvents; 68 | } 69 | -------------------------------------------------------------------------------- /src/transfers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./resources"; 2 | export * from "./transfersControllers"; 3 | export * from "./transferTransactionsFactory"; 4 | -------------------------------------------------------------------------------- /src/transfers/resources.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { TokenTransfer } from "../core/tokens"; 3 | 4 | export type NativeTokenTransferInput = { 5 | receiver: Address; 6 | nativeAmount?: bigint; 7 | data?: Uint8Array; 8 | }; 9 | 10 | export type CustomTokenTransferInput = { 11 | receiver: Address; 12 | tokenTransfers: TokenTransfer[]; 13 | }; 14 | 15 | export type CreateTransferTransactionInput = { 16 | receiver: Address; 17 | nativeAmount?: bigint; 18 | tokenTransfers?: TokenTransfer[]; 19 | data?: Uint8Array; 20 | }; 21 | -------------------------------------------------------------------------------- /src/wallet/assertions.ts: -------------------------------------------------------------------------------- 1 | import { ErrInvariantFailed } from "../core/errors"; 2 | 3 | export function guardLength(withLength: { length?: number }, expectedLength: number) { 4 | let actualLength = withLength.length || 0; 5 | 6 | if (actualLength != expectedLength) { 7 | throw new ErrInvariantFailed(`wrong length, expected: ${expectedLength}, actual: ${actualLength}`); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/wallet/crypto/constants.ts: -------------------------------------------------------------------------------- 1 | export const CipherAlgorithm = "aes-128-ctr"; 2 | export const DigestAlgorithm = "sha256"; 3 | export const KeyDerivationFunction = "scrypt"; 4 | 5 | // X25519 public key encryption 6 | export const PubKeyEncVersion = 1; 7 | export const PubKeyEncNonceLength = 24; 8 | export const PubKeyEncCipher = "x25519-xsalsa20-poly1305"; 9 | -------------------------------------------------------------------------------- /src/wallet/crypto/decryptor.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | import { Err } from "../../core/errors"; 3 | import { DigestAlgorithm } from "./constants"; 4 | import { EncryptedData } from "./encryptedData"; 5 | 6 | export class Decryptor { 7 | static decrypt(data: EncryptedData, password: string): Buffer { 8 | const kdfparams = data.kdfparams; 9 | const salt = Buffer.from(data.salt, "hex"); 10 | const iv = Buffer.from(data.iv, "hex"); 11 | const ciphertext = Buffer.from(data.ciphertext, "hex"); 12 | const derivedKey = kdfparams.generateDerivedKey(Buffer.from(password), salt); 13 | const derivedKeyFirstHalf = derivedKey.slice(0, 16); 14 | const derivedKeySecondHalf = derivedKey.slice(16, 32); 15 | 16 | const computedMAC = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest(); 17 | const actualMAC = data.mac; 18 | 19 | if (computedMAC.toString("hex") !== actualMAC) { 20 | throw new Err("MAC mismatch, possibly wrong password"); 21 | } 22 | 23 | const decipher = crypto.createDecipheriv(data.cipher, derivedKeyFirstHalf, iv); 24 | 25 | return Buffer.concat([decipher.update(ciphertext), decipher.final()]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/wallet/crypto/derivationParams.ts: -------------------------------------------------------------------------------- 1 | import scryptsy from "scryptsy"; 2 | 3 | export class ScryptKeyDerivationParams { 4 | /** 5 | * numIterations 6 | */ 7 | n = 4096; 8 | 9 | /** 10 | * memFactor 11 | */ 12 | r = 8; 13 | 14 | /** 15 | * pFactor 16 | */ 17 | p = 1; 18 | 19 | dklen = 32; 20 | 21 | constructor(n = 4096, r = 8, p = 1, dklen = 32) { 22 | this.n = n; 23 | this. r = r; 24 | this.p = p; 25 | this.dklen = dklen; 26 | } 27 | 28 | /** 29 | * Will take about: 30 | * - 80-90 ms in Node.js, on a i3-8100 CPU @ 3.60GHz 31 | * - 350-360 ms in browser (Firefox), on a i3-8100 CPU @ 3.60GHz 32 | */ 33 | public generateDerivedKey(password: Buffer, salt: Buffer): Buffer { 34 | return scryptsy(password, salt, this.n, this.r, this.p, this.dklen); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/wallet/crypto/encrypt.spec.ts: -------------------------------------------------------------------------------- 1 | import { assert } from "chai"; 2 | import { Decryptor } from "./decryptor"; 3 | import { EncryptedData } from "./encryptedData"; 4 | import { Encryptor } from "./encryptor"; 5 | 6 | describe("test address", () => { 7 | it("encrypts/decrypts", () => { 8 | const sensitiveData = Buffer.from("my mnemonic"); 9 | const encryptedData = Encryptor.encrypt(sensitiveData, "password123"); 10 | const decryptedBuffer = Decryptor.decrypt(encryptedData, "password123"); 11 | 12 | assert.equal(sensitiveData.toString('hex'), decryptedBuffer.toString('hex')); 13 | }); 14 | 15 | it("encodes/decodes kdfparams", () => { 16 | const sensitiveData = Buffer.from("my mnemonic"); 17 | const encryptedData = Encryptor.encrypt(sensitiveData, "password123"); 18 | const decodedData = EncryptedData.fromJSON(encryptedData.toJSON()); 19 | 20 | assert.deepEqual(decodedData, encryptedData, "invalid decoded data"); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/wallet/crypto/encryptedData.ts: -------------------------------------------------------------------------------- 1 | import { ScryptKeyDerivationParams } from "./derivationParams"; 2 | 3 | export class EncryptedData { 4 | id: string; 5 | version: number; 6 | cipher: string; 7 | ciphertext: string; 8 | iv: string; 9 | kdf: string; 10 | kdfparams: ScryptKeyDerivationParams; 11 | salt: string; 12 | mac: string; 13 | 14 | constructor(data: Omit) { 15 | this.id = data.id; 16 | this.version = data.version; 17 | this.ciphertext = data.ciphertext; 18 | this.iv = data.iv; 19 | this.cipher = data.cipher; 20 | this.kdf = data.kdf; 21 | this.kdfparams = data.kdfparams; 22 | this.mac = data.mac; 23 | this.salt = data.salt; 24 | } 25 | 26 | toJSON(): any { 27 | return { 28 | version: this.version, 29 | id: this.id, 30 | crypto: { 31 | ciphertext: this.ciphertext, 32 | cipherparams: { iv: this.iv }, 33 | cipher: this.cipher, 34 | kdf: this.kdf, 35 | kdfparams: { 36 | dklen: this.kdfparams.dklen, 37 | salt: this.salt, 38 | n: this.kdfparams.n, 39 | r: this.kdfparams.r, 40 | p: this.kdfparams.p 41 | }, 42 | mac: this.mac, 43 | } 44 | }; 45 | } 46 | 47 | static fromJSON(data: any): EncryptedData { 48 | return new EncryptedData({ 49 | version: data.version, 50 | id: data.id, 51 | ciphertext: data.crypto.ciphertext, 52 | iv: data.crypto.cipherparams.iv, 53 | cipher: data.crypto.cipher, 54 | kdf: data.crypto.kdf, 55 | kdfparams: new ScryptKeyDerivationParams( 56 | data.crypto.kdfparams.n, 57 | data.crypto.kdfparams.r, 58 | data.crypto.kdfparams.p, 59 | data.crypto.kdfparams.dklen, 60 | ), 61 | salt: data.crypto.kdfparams.salt, 62 | mac: data.crypto.mac, 63 | }); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/wallet/crypto/encryptor.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | import { CipherAlgorithm, DigestAlgorithm, KeyDerivationFunction } from "./constants"; 3 | import { ScryptKeyDerivationParams } from "./derivationParams"; 4 | import { EncryptedData } from "./encryptedData"; 5 | import { Randomness } from "./randomness"; 6 | 7 | interface IRandomness { 8 | id: string; 9 | iv: Buffer; 10 | salt: Buffer; 11 | } 12 | 13 | export enum EncryptorVersion { 14 | V4 = 4, 15 | } 16 | 17 | export class Encryptor { 18 | static encrypt(data: Buffer, password: string, randomness: IRandomness = new Randomness()): EncryptedData { 19 | const kdParams = new ScryptKeyDerivationParams(); 20 | const derivedKey = kdParams.generateDerivedKey(Buffer.from(password), randomness.salt); 21 | const derivedKeyFirstHalf = derivedKey.slice(0, 16); 22 | const derivedKeySecondHalf = derivedKey.slice(16, 32); 23 | const cipher = crypto.createCipheriv(CipherAlgorithm, derivedKeyFirstHalf, randomness.iv); 24 | 25 | const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]); 26 | const mac = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest(); 27 | 28 | return new EncryptedData({ 29 | version: EncryptorVersion.V4, 30 | id: randomness.id, 31 | ciphertext: ciphertext.toString('hex'), 32 | iv: randomness.iv.toString('hex'), 33 | cipher: CipherAlgorithm, 34 | kdf: KeyDerivationFunction, 35 | kdfparams: kdParams, 36 | mac: mac.toString('hex'), 37 | salt: randomness.salt.toString('hex') 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/wallet/crypto/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./constants"; 2 | export * from "./encryptor"; 3 | export * from "./decryptor"; 4 | export * from "./pubkeyEncryptor"; 5 | export * from "./pubkeyDecryptor"; 6 | export * from "./encryptedData"; 7 | export * from "./randomness"; 8 | -------------------------------------------------------------------------------- /src/wallet/crypto/pubkeyDecryptor.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | import ed2curve from "ed2curve"; 3 | import nacl from "tweetnacl"; 4 | import { UserPublicKey, UserSecretKey } from "../userKeys"; 5 | import { X25519EncryptedData } from "./x25519EncryptedData"; 6 | 7 | export class PubkeyDecryptor { 8 | static async decrypt(data: X25519EncryptedData, decryptorSecretKey: UserSecretKey): Promise { 9 | const ciphertext = Buffer.from(data.ciphertext, "hex"); 10 | const edhPubKey = Buffer.from(data.identities.ephemeralPubKey, "hex"); 11 | const originatorPubKeyBuffer = Buffer.from(data.identities.originatorPubKey, "hex"); 12 | const originatorPubKey = new UserPublicKey(originatorPubKeyBuffer); 13 | 14 | const authMessage = crypto 15 | .createHash("sha256") 16 | .update(Buffer.concat([ciphertext, edhPubKey])) 17 | .digest(); 18 | 19 | if (!(await originatorPubKey.verify(authMessage, Buffer.from(data.mac, "hex")))) { 20 | throw new Error("Invalid authentication for encrypted message originator"); 21 | } 22 | 23 | const nonce = Buffer.from(data.nonce, "hex"); 24 | const x25519Secret = ed2curve.convertSecretKey(decryptorSecretKey.valueOf()); 25 | const x25519EdhPubKey = ed2curve.convertPublicKey(edhPubKey); 26 | if (x25519EdhPubKey === null) { 27 | throw new Error("Could not convert ed25519 public key to x25519"); 28 | } 29 | 30 | const decryptedMessage = nacl.box.open(ciphertext, nonce, x25519EdhPubKey, x25519Secret); 31 | if (decryptedMessage === null) { 32 | throw new Error("Failed authentication for given ciphertext"); 33 | } 34 | 35 | return Buffer.from(decryptedMessage); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/wallet/crypto/randomness.ts: -------------------------------------------------------------------------------- 1 | import { utils } from "@noble/ed25519"; 2 | import { v4 as uuidv4 } from "uuid"; 3 | const crypto = require("crypto"); 4 | 5 | export class Randomness { 6 | salt: Buffer; 7 | iv: Buffer; 8 | id: string; 9 | 10 | constructor(init?: Partial) { 11 | this.salt = init?.salt || Buffer.from(utils.randomBytes(32)); 12 | this.iv = init?.iv || Buffer.from(utils.randomBytes(16)); 13 | this.id = init?.id || uuidv4({ random: crypto.randomBytes(16) }); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/wallet/crypto/x25519EncryptedData.ts: -------------------------------------------------------------------------------- 1 | export class X25519EncryptedData { 2 | nonce: string; 3 | version: number; 4 | cipher: string; 5 | ciphertext: string; 6 | mac: string; 7 | identities: { 8 | recipient: string, 9 | ephemeralPubKey: string, 10 | originatorPubKey: string, 11 | }; 12 | 13 | constructor(data: Omit) { 14 | this.nonce = data.nonce; 15 | this.version = data.version; 16 | this.cipher = data.cipher; 17 | this.ciphertext = data.ciphertext; 18 | this.mac = data.mac; 19 | this.identities = data.identities; 20 | } 21 | 22 | toJSON(): any { 23 | return { 24 | version: this.version, 25 | nonce: this.nonce, 26 | identities: this.identities, 27 | crypto: { 28 | ciphertext: this.ciphertext, 29 | cipher: this.cipher, 30 | mac: this.mac, 31 | } 32 | }; 33 | } 34 | 35 | static fromJSON(data: any): X25519EncryptedData { 36 | return new X25519EncryptedData({ 37 | nonce: data.nonce, 38 | version: data.version, 39 | ciphertext: data.crypto.ciphertext, 40 | cipher: data.crypto.cipher, 41 | mac: data.crypto.mac, 42 | identities: data.identities, 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/wallet/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./crypto"; 2 | export * from "./keypair"; 3 | export * from "./mnemonic"; 4 | export * from "./pem"; 5 | export * from "./userKeys"; 6 | export * from "./userPem"; 7 | export * from "./userSigner"; 8 | export * from "./userVerifier"; 9 | export * from "./userWallet"; 10 | export * from "./validatorKeys"; 11 | export * from "./validatorSigner"; 12 | -------------------------------------------------------------------------------- /src/wallet/keypair.ts: -------------------------------------------------------------------------------- 1 | import { UserPublicKey, UserSecretKey } from "./userKeys"; 2 | 3 | export class KeyPair { 4 | readonly secretKey: UserSecretKey; 5 | readonly publicKey: UserPublicKey; 6 | 7 | constructor(secretKey: UserSecretKey) { 8 | this.secretKey = secretKey; 9 | this.publicKey = this.secretKey.generatePublicKey(); 10 | } 11 | 12 | static generate(): KeyPair { 13 | const secretKey = UserSecretKey.generate(); 14 | return new KeyPair(secretKey); 15 | } 16 | 17 | static newFromBytes(data: Uint8Array): KeyPair { 18 | const secretKey = new UserSecretKey(data); 19 | return new KeyPair(secretKey); 20 | } 21 | 22 | async sign(data: Uint8Array): Promise { 23 | return this.secretKey.sign(data); 24 | } 25 | 26 | async verify(data: Uint8Array, signature: Uint8Array): Promise { 27 | return this.publicKey.verify(data, signature); 28 | } 29 | 30 | getSecretKey(): UserSecretKey { 31 | return this.secretKey; 32 | } 33 | 34 | getPublicKey(): UserPublicKey { 35 | return this.publicKey; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/wallet/userPem.ts: -------------------------------------------------------------------------------- 1 | import { PathLike, readFileSync, writeFileSync } from "fs"; 2 | import { isAbsolute, join, resolve } from "path"; 3 | import { PemEntry } from "./pemEntry"; 4 | import { USER_SEED_LENGTH, UserPublicKey, UserSecretKey } from "./userKeys"; 5 | 6 | export class UserPem { 7 | label: string; 8 | secretKey: UserSecretKey; 9 | publicKey: UserPublicKey; 10 | 11 | constructor(label: string, secretKey: UserSecretKey) { 12 | this.label = label; 13 | this.secretKey = secretKey; 14 | this.publicKey = secretKey.generatePublicKey(); 15 | } 16 | 17 | static fromFile(path: PathLike, index: number = 0): UserPem { 18 | return this.fromFileAll(path)[index]; 19 | } 20 | 21 | static fromFileAll(path: PathLike): UserPem[] { 22 | const resolvedPath = isAbsolute(path.toString()) 23 | ? resolve(path.toString()) 24 | : resolve(join(process.cwd(), path.toString())); 25 | const text = readFileSync(resolvedPath, "utf-8"); 26 | return this.fromTextAll(text); 27 | } 28 | 29 | static fromText(text: string, index: number = 0): UserPem { 30 | const items = this.fromTextAll(text); 31 | return items[index]; 32 | } 33 | 34 | static fromTextAll(text: string): UserPem[] { 35 | const entries = PemEntry.fromTextAll(text); 36 | const resultItems: UserPem[] = []; 37 | 38 | for (const entry of entries) { 39 | const secretKey = new UserSecretKey(entry.message.slice(0, USER_SEED_LENGTH)); 40 | const item = new UserPem(entry.label, secretKey); 41 | resultItems.push(item); 42 | } 43 | 44 | return resultItems; 45 | } 46 | 47 | save(path: PathLike): void { 48 | const resolvedPath = isAbsolute(path.toString()) 49 | ? resolve(path.toString()) 50 | : resolve(join(process.cwd(), path.toString())); 51 | writeFileSync(resolvedPath, this.toText(), { encoding: "utf-8" }); 52 | } 53 | 54 | toText(): string { 55 | const message = new Uint8Array([...this.secretKey.valueOf(), ...this.publicKey.valueOf()]); 56 | const pemEntry = new PemEntry(this.label, message); 57 | return pemEntry.toText(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/wallet/userSigner.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { ErrSignerCannotSign } from "../core/errors"; 3 | import { UserSecretKey } from "./userKeys"; 4 | import { UserWallet } from "./userWallet"; 5 | 6 | /** 7 | * ed25519 signer 8 | */ 9 | export class UserSigner { 10 | readonly secretKey: UserSecretKey; 11 | 12 | constructor(secretKey: UserSecretKey) { 13 | this.secretKey = secretKey; 14 | } 15 | 16 | static fromWallet(keyFileObject: any, password: string, addressIndex?: number): UserSigner { 17 | const secretKey = UserWallet.decrypt(keyFileObject, password, addressIndex); 18 | return new UserSigner(secretKey); 19 | } 20 | 21 | static fromPem(text: string, index: number = 0) { 22 | let secretKey = UserSecretKey.fromPem(text, index); 23 | return new UserSigner(secretKey); 24 | } 25 | 26 | async sign(data: Uint8Array): Promise { 27 | try { 28 | const signature = this.secretKey.sign(data); 29 | return signature; 30 | } catch (err: any) { 31 | throw new ErrSignerCannotSign(err); 32 | } 33 | } 34 | 35 | /** 36 | * Gets the address of the signer. 37 | */ 38 | getAddress(hrp?: string): Address { 39 | const bech32 = this.secretKey.generatePublicKey().toAddress(hrp).toBech32(); 40 | return Address.newFromBech32(bech32); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/wallet/userVerifier.ts: -------------------------------------------------------------------------------- 1 | import { Address } from "../core/address"; 2 | import { UserPublicKey } from "./userKeys"; 3 | 4 | /** 5 | * ed25519 signature verification 6 | */ 7 | export class UserVerifier { 8 | publicKey: UserPublicKey; 9 | 10 | constructor(publicKey: UserPublicKey) { 11 | this.publicKey = publicKey; 12 | } 13 | 14 | static fromAddress(address: Address): UserVerifier { 15 | let publicKey = new UserPublicKey(address.getPublicKey()); 16 | return new UserVerifier(publicKey); 17 | } 18 | 19 | /** 20 | * 21 | * @param data the raw data to be verified (e.g. an already-serialized enveloped message) 22 | * @param signature the signature to be verified 23 | * @returns true if the signature is valid, false otherwise 24 | */ 25 | async verify(data: Buffer | Uint8Array, signature: Buffer | Uint8Array): Promise { 26 | return this.publicKey.verify(data, signature); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/wallet/usersBenchmark.spec.ts: -------------------------------------------------------------------------------- 1 | import { utils } from "@noble/ed25519"; 2 | import { assert } from "chai"; 3 | import { UserPublicKey, UserSecretKey } from "./userKeys"; 4 | 5 | describe("behchmark sign and verify", () => { 6 | it("should sign and verify", async function () { 7 | this.timeout(60000); 8 | 9 | const n = 1000; 10 | const secretKeys: UserSecretKey[] = []; 11 | const publicKeys: UserPublicKey[] = []; 12 | const messages: Buffer[] = []; 13 | const goodSignatures: Uint8Array[] = []; 14 | 15 | for (let i = 0; i < n; i++) { 16 | const secretKey = new UserSecretKey(Buffer.from(utils.randomBytes(32))); 17 | const publicKey = secretKey.generatePublicKey(); 18 | const message = Buffer.from(utils.randomBytes(256)); 19 | 20 | secretKeys.push(secretKey); 21 | publicKeys.push(publicKey); 22 | messages.push(message); 23 | } 24 | 25 | console.info(`N = ${n}`); 26 | 27 | console.time("sign"); 28 | 29 | for (let i = 0; i < n; i++) { 30 | const signature = secretKeys[i].sign(messages[i]); 31 | goodSignatures.push(signature); 32 | } 33 | 34 | console.timeEnd("sign"); 35 | 36 | console.time("verify (good)"); 37 | 38 | for (let i = 0; i < n; i++) { 39 | const ok = await publicKeys[i].verify(messages[i], goodSignatures[i]); 40 | assert.isTrue(ok); 41 | } 42 | 43 | console.timeEnd("verify (good)"); 44 | 45 | console.time("verify (bad)"); 46 | 47 | for (let i = 0; i < n; i++) { 48 | const ok = await publicKeys[i].verify(messages[messages.length - i - 1], goodSignatures[i]); 49 | assert.isFalse(ok); 50 | } 51 | 52 | console.timeEnd("verify (bad)"); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /src/wallet/validatorSigner.ts: -------------------------------------------------------------------------------- 1 | import { ErrSignerCannotSign } from "../core/errors"; 2 | import { BLS, ValidatorSecretKey } from "./validatorKeys"; 3 | 4 | /** 5 | * Validator signer (BLS signer) 6 | */ 7 | export class ValidatorSigner { 8 | /** 9 | * Signs a message. 10 | */ 11 | async signUsingPem(pemText: string, pemIndex: number = 0, signable: Buffer | Uint8Array): Promise { 12 | await BLS.initIfNecessary(); 13 | 14 | try { 15 | let secretKey = ValidatorSecretKey.fromPem(pemText, pemIndex); 16 | secretKey.sign(signable); 17 | } catch (err: any) { 18 | throw new ErrSignerCannotSign(err); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "ES2020", 5 | "outDir": "out", 6 | "lib": [ 7 | "ES2020", 8 | "DOM" 9 | ], 10 | "sourceMap": true, 11 | "allowJs": true, 12 | "strict": true, 13 | "strictPropertyInitialization": true, 14 | "strictNullChecks": true, 15 | "skipLibCheck": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noUnusedParameters": true, 19 | "esModuleInterop": true, 20 | "declaration": true 21 | }, 22 | "include": [ 23 | "src/**/*" 24 | ], 25 | "exclude": [ 26 | "node_modules", 27 | "out", 28 | "out-tests", 29 | "out-browser", 30 | "out-browser-tests", 31 | "**/*.spec.ts", 32 | "src/testutils", 33 | "cookbook" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /tsconfig.tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "target": "ES2020", 5 | "outDir": "out-tests", 6 | "lib": [ 7 | "ES2020", 8 | "DOM" 9 | ], 10 | "sourceMap": true, 11 | "allowJs": true, 12 | "strict": true, 13 | "strictPropertyInitialization": true, 14 | "strictNullChecks": true, 15 | "skipLibCheck": true, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "noUnusedParameters": true, 19 | "esModuleInterop": true, 20 | "declaration": true 21 | }, 22 | "include": [ 23 | "src/**/*", 24 | ], 25 | "exclude": [ 26 | "node_modules", 27 | "out", 28 | "out-tests", 29 | "out-browser", 30 | "out-browser-tests", 31 | "cookbook" 32 | ] 33 | } 34 | --------------------------------------------------------------------------------