├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README_zh-CN.md ├── docs ├── assets │ ├── css │ │ └── main.css │ ├── images │ │ ├── icons.png │ │ ├── icons@2x.png │ │ ├── widgets.png │ │ └── widgets@2x.png │ └── js │ │ ├── main.js │ │ └── search.js ├── classes │ ├── account.html │ ├── contract.html │ ├── transaction.html │ ├── xupererror1.html │ └── xupersdk.html ├── enums │ ├── cryptography.html │ ├── language.html │ ├── resource_type.html │ └── strength.html ├── index.html └── interfaces │ ├── accountinterface.html │ ├── contractinterface.html │ ├── errorinterface.html │ ├── transactioninterface.html │ └── xupersdkinterface.html ├── jest.config.js ├── package-lock.json ├── package.json ├── scripts ├── webpack.common.js └── webpack.prod.js ├── src ├── account.ts ├── constants.ts ├── contract.ts ├── error.ts ├── index.ts ├── interfaces.ts ├── plugins │ ├── endorsement.ts │ └── index.ts ├── proto │ ├── xendorser.proto │ └── xuper.proto ├── requests.ts ├── transaction.ts ├── types.ts ├── utils.ts └── wordlist.json ├── test ├── account.test.ts ├── cert │ └── cfca.cert ├── contract.test.ts ├── jest │ └── custom-test-env.js ├── native │ ├── counter-native │ └── counter-native-v2 ├── plugins.test.ts ├── transaction.test.ts ├── wasm │ └── counter.wasm └── xuper.test.ts ├── tsconfig.json ├── tsdx.config.js └── typedoc.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts|*.js] 12 | max_line_length = 120 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es6: true, 5 | node: true 6 | }, 7 | extends: [ 8 | 'plugin:@typescript-eslint/recommended' 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly' 13 | }, 14 | parser: '@typescript-eslint/parser', 15 | parserOptions: { 16 | ecmaVersion: 2018, 17 | sourceType: 'module' 18 | }, 19 | plugins: ['@typescript-eslint'], 20 | ignorePatterns: ['node_modules/**', 'scripts/**', 'sdk-1.*/**'], 21 | rules: { 22 | '@typescript-eslint/no-var-requires': 'off', 23 | '@typescript-eslint/ban-ts-ignore': 'off', 24 | '@typescript-eslint/no-explicit-any': 'off', 25 | '@typescript-eslint/indent': ['error', 4], 26 | '@typescript-eslint/camelcase': 'off', 27 | 'indent': ['error', 4], 28 | 'object-curly-spacing': 'off', 29 | 'comma-dangle': 'off', 30 | 'arrow-parens': 'off' 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | 106 | # IDE 107 | .idea/ 108 | 109 | # Output of build 110 | lib/ 111 | example/ 112 | dist/ 113 | 114 | # OS 115 | .DS_Store 116 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_nodules/ 3 | test/ 4 | scripts/ 5 | .editorconfig 6 | .gitignore 7 | .npmignore 8 | .travis.yml 9 | package-lock.json 10 | 11 | src/ 12 | docs/ 13 | scripts/ 14 | jest.config.js 15 | tsconfig.json 16 | typedoc.json 17 | .eslintrc.js 18 | CHANGELOG.md 19 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | dist: trusty 3 | language: node_js 4 | node_js: v10 5 | install: 6 | - npm i 7 | script: 8 | - npm run build 9 | deploy: 10 | provider: npm 11 | api_key: bab647b5-704a-46d7-a4ba-5e3bce53a9d0 12 | email: smilingxinyi@gmail.com 13 | skip_cleanup: true 14 | on: 15 | tags: true 16 | repo: xuperchain/xuper-sdk-js 17 | branch: master 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [2.4.0](https://github.com/xuperchain/xuper-sdk-js/compare/v2.3.0...v2.4.0) (2022-12-08) 6 | 7 | ## [2.3.0](https://github.com/xuperchain/xuper-sdk-js/compare/v2.1.0...v2.3.0) (2022-04-27) 8 | 9 | 10 | ### Features 11 | 12 | * 🎸 obtain public key string ([983ecec](https://github.com/xuperchain/xuper-sdk-js/commit/983ececeeb6c13aa41c6877fac809d8501072595)) 13 | 14 | ## [2.2.0](https://github.com/xuperchain/xuper-sdk-js/compare/v2.1.0...v2.2.0) (2021-08-24) 15 | 16 | 17 | ### Features 18 | 19 | * 🎸 get account public key string ([a6d7736](https://github.com/xuperchain/xuper-sdk-js/commit/a6d7736df969f43655f32d9e2f8f456fda73a999)) 20 | 21 | ## [2.1.0](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0...v2.1.0) (2021-05-14) 22 | 23 | ## [2.0.0](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.11...v2.0.0) (2021-04-02) 24 | 25 | 26 | ### Bug Fixes 27 | 28 | * 🐛 node modules ([425e2d4](https://github.com/xuperchain/xuper-sdk-js/commit/425e2d4b404cde1c28c3b84b16dff72da341f808)) 29 | 30 | ## [2.0.0-beta.11](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.10...v2.0.0-beta.11) (2021-03-08) 31 | 32 | 33 | ### Features 34 | 35 | * 🎸 GetBlochchains - query blockchains ([661bac8](https://github.com/xuperchain/xuper-sdk-js/commit/661bac844189548feee1ba6f990bcf7ac0b8d437)) 36 | * 🎸 GetBlock & GetBlockByHeight - block info ([e146c35](https://github.com/xuperchain/xuper-sdk-js/commit/e146c3577bc943f3d5e89e74741f365de536f67f)) 37 | * 🎸 new gRPC services ([3a3b1fb](https://github.com/xuperchain/xuper-sdk-js/commit/3a3b1fb23d1afa592657ac81261f9e1be6295812)) 38 | * 🎸 QueryACL - query some account info ([a00c0a4](https://github.com/xuperchain/xuper-sdk-js/commit/a00c0a4637a477619afd13eb6074ac99b9b23447)) 39 | * 🎸 QueryContractStat - contarct stat data ([964642d](https://github.com/xuperchain/xuper-sdk-js/commit/964642d5c0b069a3a8871d01c581099ff209ebad)) 40 | * 🎸 support GET method & throw Error Response ([1f1bf54](https://github.com/xuperchain/xuper-sdk-js/commit/1f1bf5499b200a52b3705db27364f09232b3ce6a)) 41 | 42 | ## [2.0.0-beta.10](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.9...v2.0.0-beta.10) (2021-02-25) 43 | 44 | 45 | ### Features 46 | 47 | * 🎸 contract payment ([4a0f121](https://github.com/xuperchain/xuper-sdk-js/commit/4a0f121450f8704ac9bea520d7ee4d20df76541e)) 48 | 49 | ## [2.0.0-beta.9](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.8...v2.0.0-beta.9) (2021-02-22) 50 | 51 | 52 | ### Bug Fixes 53 | 54 | * 🐛 endorsement service ([c4303b6](https://github.com/xuperchain/xuper-sdk-js/commit/c4303b6e44ebc6e06311fc3181f77935c661f200)) 55 | 56 | ## [2.0.0-beta.8](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.7...v2.0.0-beta.8) (2021-02-22) 57 | 58 | 59 | ### Bug Fixes 60 | 61 | * 🐛 endorse service bug ([85db2d9](https://github.com/xuperchain/xuper-sdk-js/commit/85db2d9408670fbe1ca50695e0500bae2b53a489)) 62 | 63 | ## [2.0.0-beta.7](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.6...v2.0.0-beta.7) (2021-02-22) 64 | 65 | 66 | ### Features 67 | 68 | * 🎸 support the solidity contract ([079bbe2](https://github.com/xuperchain/xuper-sdk-js/commit/079bbe23e53a5b4e925dd6319128bdbc787d681d)) 69 | 70 | 71 | ### Bug Fixes 72 | 73 | * 🐛 deployment contract support plugin ([f783d0e](https://github.com/xuperchain/xuper-sdk-js/commit/f783d0e7c1ae2ee60b6d5588529cffc033170712)) 74 | 75 | ## [2.0.0-beta.6](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.5...v2.0.0-beta.6) (2021-02-08) 76 | 77 | 78 | ### Bug Fixes 79 | 80 | * 🐛 preExecWithFee service ([28f9aa0](https://github.com/xuperchain/xuper-sdk-js/commit/28f9aa0eceb251fe68357650043610be6ef9077a)) 81 | 82 | ## [2.0.0-beta.5](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.4...v2.0.0-beta.5) (2021-02-03) 83 | 84 | 85 | ### Bug Fixes 86 | 87 | * 🐛 gRPC - postTX service ([37c480e](https://github.com/xuperchain/xuper-sdk-js/commit/37c480e5a15139bd332d2949d5cbdbcd2be455c4)) 88 | 89 | ## [2.0.0-beta.4](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.3...v2.0.0-beta.4) (2020-12-17) 90 | 91 | 92 | ### Bug Fixes 93 | 94 | * 🐛 invoke contract without plugin service ([6440c0d](https://github.com/xuperchain/xuper-sdk-js/commit/6440c0dde94d02f2999eb778a3355552b3697bf3)) 95 | 96 | ## [2.0.0-beta.3](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2020-12-14) 97 | 98 | 99 | ### Bug Fixes 100 | 101 | * 🐛 type error & export plugins ([d404c9f](https://github.com/xuperchain/xuper-sdk-js/commit/d404c9fd87ad18d7b786e479146e1eb63efbe0a9)) 102 | 103 | ## [2.0.0-beta.2](https://github.com/xuperchain/xuper-sdk-js/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2020-12-10) 104 | 105 | 106 | ### Bug Fixes 107 | 108 | * 🐛 build script ([03f9cf1](https://github.com/xuperchain/xuper-sdk-js/commit/03f9cf193c585ec6818942dac48835a8a86ff27c)) 109 | 110 | ## [2.0.0-beta.1](https://github.com/xuperchain/xuper-sdk-js/compare/v1.1.2...v2.0.0-beta.1) (2020-12-08) 111 | 112 | 113 | ### ⚠ BREAKING CHANGES 114 | 115 | * 🧨 v1.x 116 | 117 | ### Features 118 | 119 | * 🎸 account recovery ([2d9c040](https://github.com/xuperchain/xuper-sdk-js/commit/2d9c0409bd3cf79df4e34acfb522220a792c5fb6)) 120 | * 🎸 balance ([9677c1b](https://github.com/xuperchain/xuper-sdk-js/commit/9677c1b78153842d7b9d63df9cd304875363d111)) 121 | * 🎸 CFCA plugin ([8cfaecc](https://github.com/xuperchain/xuper-sdk-js/commit/8cfaecc8e9f7b79d13d8a43d935425f1b9b665cb)) 122 | * 🎸 contract ([4412ff7](https://github.com/xuperchain/xuper-sdk-js/commit/4412ff72a6d191dafe89629654610cd7813fdeb0)) 123 | * 🎸 contract invoke ([7bbaeba](https://github.com/xuperchain/xuper-sdk-js/commit/7bbaebac350e538c98da009aa166d8e16a11ce5b)) 124 | * 🎸 contract upgrade ([b6c4bc9](https://github.com/xuperchain/xuper-sdk-js/commit/b6c4bc965474537670d78ff662872f39e7635ced)) 125 | * 🎸 grpc support ([76827ad](https://github.com/xuperchain/xuper-sdk-js/commit/76827ad1ccee24a199d6c8a118b904ffdd71312b)) 126 | * 🎸 interfaces ([5ef9b1a](https://github.com/xuperchain/xuper-sdk-js/commit/5ef9b1a1786e242dbcbdc10ab004af6c5457e41d)) 127 | * 🎸 plugins & refactor ([92b0064](https://github.com/xuperchain/xuper-sdk-js/commit/92b0064ddadff47b7d7f4999ce5a59c4ec1167d1)) 128 | * 🎸 post request protocol detection and completion ([9cf9105](https://github.com/xuperchain/xuper-sdk-js/commit/9cf9105d48d826a54ebc8a8a8aa95e1f87a5d71a)) 129 | * contract list & pre-execution ([2c0285c](https://github.com/xuperchain/xuper-sdk-js/commit/2c0285cf2f77c3e04e239c080063979c124571d9)) 130 | * **transaction:** support nodejs & no endorsement service ([27f5dd6](https://github.com/xuperchain/xuper-sdk-js/commit/27f5dd66d216365ea945b54f42518b7ab6387861)) 131 | * **xuper account:** export the encrypted private key string ([fdd941a](https://github.com/xuperchain/xuper-sdk-js/commit/fdd941a153f5ac2c8b4909c1e6d7df3f8dc25f90)) 132 | * **xuper account:** support to import encrypted private key, restore account by password to decrypt ([85841b4](https://github.com/xuperchain/xuper-sdk-js/commit/85841b4706d9d78c2534138053f1c62550cedb33)) 133 | * compatible Node.js ([4d4df29](https://github.com/xuperchain/xuper-sdk-js/commit/4d4df29e3db3f05681453cab4c8a413e21899fea)) 134 | 135 | 136 | ### Bug Fixes 137 | 138 | * 🐛 dependencies ([a094994](https://github.com/xuperchain/xuper-sdk-js/commit/a0949948ecee6979e63db77153ad90ddccc9b7a0)) 139 | * 🐛 invoke contract in plugin mode ([dcd3fec](https://github.com/xuperchain/xuper-sdk-js/commit/dcd3fec5a34976fca25dd69d7b7798d5d7c1392f)) 140 | * function-return-type fix ([86982d8](https://github.com/xuperchain/xuper-sdk-js/commit/86982d8345c5142b26a833e1a0ecd305a9893e0d)) 141 | * text-decoder & text-encoder ([44bc939](https://github.com/xuperchain/xuper-sdk-js/commit/44bc939da59af945ffb57a37f4b2a9f22fa07dd8)) 142 | * **account:** cryptography error ([04e9768](https://github.com/xuperchain/xuper-sdk-js/commit/04e9768ea367b123c1c2e8f4ac0c2aeb2c6eba1b)) 143 | * **account.ts:** generate address - bytes length fix ([1e17a72](https://github.com/xuperchain/xuper-sdk-js/commit/1e17a72d5dbfa0232235aba32f8b3757f4091a2c)) 144 | 145 | ### [1.1.2](https://github.com/xuperchain/xuper-sdk-js/compare/v1.1.1...v1.1.2) (2020-04-30) 146 | 147 | 148 | ### Bug Fixes 149 | 150 | * **src/utils.ts:** replace for-in with for-each ([f400e28](https://github.com/xuperchain/xuper-sdk-js/commit/f400e2817e834dee8d6f4f011278df7ce7a944fd)) 151 | 152 | ### [1.1.1](https://github.com/xuperchain/xuper-sdk-js/compare/v1.1.0...v1.1.1) (2020-03-16) 153 | 154 | ## [1.1.0](https://github.com/xuperchain/xuper-sdk-js/compare/v1.0.3...v1.1.0) (2020-02-17) 155 | 156 | 157 | ### Features 158 | 159 | * smart contract feature ([a46ea9f](https://github.com/xuperchain/xuper-sdk-js/commit/a46ea9f72d71a316dfac53f31861c33c5e5aa43b)) 160 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xuper SDK JS 2 | 3 | [![Build Status](https://travis-ci.org/xuperchain/xuper-sdk-js.svg?branch=master)](https://travis-ci.org/xuperchain/xuper-sdk-js) 4 | [![npm version](https://badge.fury.io/js/%40xuperchain%2Fxuper-sdk.svg)](https://badge.fury.io/js/%40xuperchain%2Fxuper-sdk) 5 | 6 | Xuper SDK (JS/TS) is a software development kit that allows developers to quickly use XuperChain. 7 | 8 | The SDK provides a service interface that includes account, transaction, contract and various query functions. It can be used in a browser and Nodejs environment. 9 | 10 | --- 11 | 12 | English | [简体中文](./README_zh-CN.md) 13 | 14 | ## Usage 15 | 16 | ### Install Npm package 17 | 18 | > npm install --save @xuperchain/xuper-sdk 19 | 20 | ### Quick start 21 | 22 | ```javascript 23 | import XuperSDK from '@xuperchain/xuper-sdk'; 24 | 25 | const node = ''; // node 26 | const chain = ''; // chain 27 | 28 | const xsdk = XuperSDK.getInstance({ 29 | node, 30 | chain 31 | }); 32 | 33 | const start = async () => { 34 | const result = await xsdk.getBlockChains(); 35 | console.log(result); 36 | }; 37 | 38 | start(); 39 | ``` 40 | 41 | *[Details - API reference](#api-reference-documentation)* 42 | 43 | 44 | ### Code examples 45 | 46 | [![Edit xuper-sdk-demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/xuper-sdk-demo-q5m93?fontsize=14&hidenavigation=1&theme=dark) 47 | 48 | ### Environments 49 | 50 | - Browser depends on window.crypto 51 | - Nodejs >= v10.0 52 | 53 | ##### In the Nodejs environment, **gRPC** is supported and used by default, you can choose to close it, and still use Http to request 54 | ```javascript 55 | XuperSDK.getInstance({ 56 | ..., 57 | env: { 58 | node: { 59 | disableGRPC: true // disable gRPC 60 | } 61 | } 62 | }) 63 | ``` 64 | 65 | ### How to build a test environment 66 | 67 | #### Read the [documentation](https://github.com/xuperchain/xuperchain) , compile and deploy XuperChain 68 | 69 | #### Use XuperChain Docker image to build a single-node service 70 | 71 | 1. Pull image (*XuperChian v3.7 - [Repository](https://github.com/SmilingXinyi/xuperchain)*) 72 | > docker pull smilingxinyi/xuperchain 73 | 74 | 2. Start container 75 | > docker run -d -p 8098:8098 -p 37101:37101 -p 47101:47101 --name xc smilingxinyi/xuperchain 76 | 77 | ## API reference documentation 78 | 79 | [Link](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html) 80 | 81 | ## Services 82 | 83 | ### Account 84 | 85 | service|name|link|state 86 | ---|---|---|:---: 87 | Create account|create|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#create)|√ 88 | Retrieve account|retrieve|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#retrieve)|√ 89 | Import private key|import|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#import)|√ 90 | Export private key|export|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#export)|√ 91 | Chekc address|checkAddress|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#checkaddress)|√ 92 | Check mnemonic|checkMnemonic|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#checkmnemonic)|√ 93 | Balance|getBalance|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getbalance)|√ 94 | Balance Detail|getBalanceDetail|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getbalanceDetail)|√ 95 | 96 | ### Blockchain infomation 97 | 98 | service|name|link|state 99 | ---|---|---|:---: 100 | Blockchains|getBlockChains|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getBlockChains)|√ 101 | Status|checkStatus|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#checkStatus)|√ 102 | Block by id|getBlockById|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getBlockById)|√ 103 | Block by height|getBlockByHeight|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getBlockByHeight)|√ 104 | 105 | ### Transaction 106 | 107 | service|name|link|state 108 | ---|---|---|:---: 109 | Make transfer|transfer|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#transfer)|√ 110 | Post tx|postTransaction|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#postTransaction)|√ 111 | Query tx|queryTransaction|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#queryTransaction)|√ 112 | 113 | ### Contract 114 | 115 | service|name|link|state 116 | ---|---|---|:---: 117 | New contract account|createContractAccount|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#createContractAccount)|√ 118 | Contract list|getContracts|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getContracts)|√ 119 | Deploy Wasm contract|deployWasmContract|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#deployWasmContract)|√ 120 | Invoke Wasm contarct|invokeContarct|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#invokeContarct)|√ 121 | Deploy Solidity contract|deploySolidityContract|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#deploySolidityContract)|√ 122 | Invoke Solidity contarct|invokeSolidityContarct|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#invokeSolidityContarct)|√ 123 | Deploy Native contract|deployNativeContract|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#deployNativeContract)|√ 124 | Invoke Native contarct|invokeContarct|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#invokeContarct)|√ 125 | Query ACL|queryACL|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#queryACL)|√ 126 | Query stat data about contract|queryContractStatData|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#queryContractStatData)|√ 127 | 128 | ## Plugin 129 | 130 | #### Endorsement service plugin 131 | 132 | > The plugin must be used on the `public network` 133 | 134 | ##### EndorsementPlugin 135 | 136 | Example: 137 | 138 | ```javascript 139 | 140 | const params = { 141 | server: process.env.ENDORSE_SERVER, // ip, port 142 | fee: process.env.FEE, // fee 143 | endorseServiceCheckAddr: process.env.SERVICE_SIGN_ADDRESS, // sign address 144 | endorseServiceFeeAddr: process.env.SERVICE_FEE_ADDRESS // fee address 145 | } 146 | 147 | const xsdk = new XuperSDK({ 148 | node, 149 | chain, 150 | plugins: [ 151 | EndorsementPlugin({ 152 | transfer: params, 153 | makeTransaction: params 154 | }) 155 | ] 156 | }); 157 | 158 | ``` 159 | -------------------------------------------------------------------------------- /README_zh-CN.md: -------------------------------------------------------------------------------- 1 | # Xuper SDK (JS/TS) 2 | 3 | [![Build Status](https://travis-ci.org/xuperchain/xuper-sdk-js.svg?branch=master)](https://travis-ci.org/xuperchain/xuper-sdk-js) 4 | [![npm version](https://badge.fury.io/js/%40xuperchain%2Fxuper-sdk.svg)](https://badge.fury.io/js/%40xuperchain%2Fxuper-sdk) 5 | 6 | Xuper SDK (JS/TS) 是一个在可以让开发者快速使用 XuperChain 的软件开发工具包。 7 | 8 | 该SDK提供包含账号、交易、合约与各类查询功能的服务接口,可以在浏览器与 Nodejs 环境下使用。 9 | 10 | --- 11 | 12 | [English](./README.md) | 简体中文 13 | 14 | ## 使用方式 15 | 16 | ### 安装Npm依赖包 17 | 18 | > npm install --save @xuperchain/xuper-sdk 19 | 20 | ### 快速开始 21 | 22 | ```javascript 23 | import XuperSDK from '@xuperchain/xuper-sdk'; 24 | 25 | const node = ''; // 节点 26 | const chain = ''; // 链 27 | 28 | const xsdk = XuperSDK.getInstance({ 29 | node, 30 | chain 31 | }); 32 | 33 | const start = async () => { 34 | const result = await xsdk.getBlockChains(); 35 | console.log(result); 36 | }; 37 | 38 | start(); 39 | ``` 40 | 41 | *[具体API查看详情](#API参考文档)* 42 | 43 | ### 使用示例代码 44 | 45 | [![Edit xuper-sdk-demo](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/xuper-sdk-demo-q5m93?fontsize=14&hidenavigation=1&theme=dark) 46 | 47 | ### 支持环境 48 | - Browser 依赖 window.crypto 49 | - Nodejs >= v10.0 50 | 51 | ##### 在Nodejs环境中支持并默认使用**gRPC**,可以选择关闭,依然使用Http方式请求 52 | ```javascript 53 | XuperSDK.getInstance({ 54 | ..., 55 | env: { 56 | node: { 57 | disableGRPC: true // 禁用gRPC 58 | } 59 | } 60 | }) 61 | ``` 62 | 63 | ### 如何搭建一个测试环境 64 | 65 | #### 阅读 [文档](https://github.com/xuperchain/xuperchain) 编译并部署XuperChain 66 | 67 | #### 通过Docker搭建一个搭建单节点服务 68 | 69 | 1. 拉取镜像 (*XuperChian v3.7 - [Repository](https://github.com/SmilingXinyi/xuperchain)*) 70 | > docker pull smilingxinyi/xuperchain 71 | 72 | 2. 启动容器 73 | > docker run -d -p 8098:8098 -p 37101:37101 -p 47101:47101 --name xc smilingxinyi/xuperchain 74 | 75 | ## API参考文档 76 | 77 | [文档链接](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html) 78 | 79 | ## 实现列表 80 | 81 | ### 区块链账号 82 | 83 | 接口|函数名|文档链接|状态 84 | ---|---|---|:---: 85 | 创建区块链账号|create|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#create)|√ 86 | 助记词恢复账号|retrieve|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#retrieve)|√ 87 | 导入私钥|import|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#import)|√ 88 | 导出私钥|export|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#export)|√ 89 | 检查地址|checkAddress|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#checkaddress)|√ 90 | 检查助记词|checkMnemonic|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#checkmnemonic)|√ 91 | 获取余额|balance|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getbalance)|√ 92 | 获取余额详情|getBalanceDetail|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getbalanceDetail)|√ 93 | 94 | ### 链信息 95 | 96 | 接口|函数名|文档链接|状态 97 | ---|---|---|:---: 98 | 获取链|getBlockChains|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getBlockChains)|√ 99 | 查看当前链状态|checkStatus|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#checkStatus)|√ 100 | 通过交易ID获取块信息|getBlockById|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getBlockById)|√ 101 | 通过高度获取块信息|getBlockByHeight|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getBlockByHeight)|√ 102 | 103 | ### 交易 104 | 105 | 接口|函数名|文档链接|状态 106 | ---|---|---|:---: 107 | 转账|transfer|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#transfer)|√ 108 | 发送交易|postTransaction|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#postTransaction)|√ 109 | 查询交易|queryTransaction|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#queryTransaction)|√ 110 | 111 | ### 智能合约 112 | 113 | 接口|函数名|文档链接|状态 114 | ---|---|---|:---: 115 | 创建合约账户|createContractAccount|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#createContractAccount)|√ 116 | 获取合约|getContracts|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#getContracts)|√ 117 | 部署 Wasm 合约|deployWasmContract|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#deployWasmContract)|√ 118 | 调用 Wasm 合约|invokeContarct|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#invokeContarct)|√ 119 | 部署 Solidity 合约|deploySolidityContract|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#deploySolidityContract)|√ 120 | 调用 Solidity 合约|invokeSolidityContarct|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#invokeSolidityContarct)|√ 121 | 部署 Native 合约|deployNativeContract|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#deployNativeContract)|√ 122 | 调用 Native 合约|invokeContarct|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#invokeContarct)|√ 123 | 查询访问控制列表|queryACL|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#queryACL)|√ 124 | 查询合约状态|queryContractStatData|[LINK](https://xuperchain.github.io/xuper-sdk-js/classes/xupersdk.html#queryContractStatData)|√ 125 | 126 | ## 插件与使用 127 | 128 | #### 背书服务插件 129 | 130 | > `公开网络`必须使用该插件 131 | 132 | ##### EndorsementPlugin 133 | 134 | Example: 135 | 136 | ```javascript 137 | 138 | const params = { 139 | server: process.env.ENDORSE_SERVER, // 服务 140 | fee: process.env.FEE, // 服务费 141 | endorseServiceCheckAddr: process.env.SERVICE_SIGN_ADDRESS, // 背书签名地址 142 | endorseServiceFeeAddr: process.env.SERVICE_FEE_ADDRESS // 背书服务费地址 143 | } 144 | 145 | const xsdk = new XuperSDK({ 146 | node, 147 | chain, 148 | plugins: [ 149 | EndorsementPlugin({ 150 | transfer: params, 151 | makeTransaction: params // 两个一样 152 | }) 153 | ] 154 | }); 155 | 156 | ``` 157 | -------------------------------------------------------------------------------- /docs/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuperchain/xuper-sdk-js/495d8b61f5ef9f7b2bd3f0826e017ef0b9ec3e62/docs/assets/images/icons.png -------------------------------------------------------------------------------- /docs/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuperchain/xuper-sdk-js/495d8b61f5ef9f7b2bd3f0826e017ef0b9ec3e62/docs/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuperchain/xuper-sdk-js/495d8b61f5ef9f7b2bd3f0826e017ef0b9ec3e62/docs/assets/images/widgets.png -------------------------------------------------------------------------------- /docs/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuperchain/xuper-sdk-js/495d8b61f5ef9f7b2bd3f0826e017ef0b9ec3e62/docs/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /docs/enums/cryptography.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cryptography | Xuper SDK (JS/TS) 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Enumeration Cryptography

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

Cryptography

73 |
74 |
75 |
76 |
77 |

Index

78 |
79 |
80 |
81 |

Enumeration members

82 | 85 |
86 |
87 |
88 |
89 |
90 |

Enumeration members

91 |
92 | 93 |

EccFIPS

94 |
EccFIPS: = 1
95 | 100 |
101 |
102 |
103 | 128 |
129 |
130 | 190 |
191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /docs/enums/language.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Language | Xuper SDK (JS/TS) 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Enumeration Language

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

Mnemonic language

73 |
74 |
75 |
76 |
77 |

Index

78 |
79 |
80 |
81 |

Enumeration members

82 | 86 |
87 |
88 |
89 |
90 |
91 |

Enumeration members

92 |
93 | 94 |

English

95 |
English: = "English"
96 | 101 |
102 |
103 | 104 |

SimplifiedChinese

105 |
SimplifiedChinese: = "SimplifiedChinese"
106 | 111 |
112 |
113 |
114 | 142 |
143 |
144 | 204 |
205 | 206 | 207 | 208 | -------------------------------------------------------------------------------- /docs/enums/resource_type.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | RESOURCE_TYPE | Xuper SDK (JS/TS) 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Enumeration RESOURCE_TYPE

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Index

71 |
72 |
73 |
74 |

Enumeration members

75 | 81 |
82 |
83 |
84 |
85 |
86 |

Enumeration members

87 |
88 | 89 |

CPU

90 |
CPU:
91 | 96 |
97 |
98 | 99 |

DISK

100 |
DISK:
101 | 106 |
107 |
108 | 109 |

MEMORY

110 |
MEMORY:
111 | 116 |
117 |
118 | 119 |

XFEE

120 |
XFEE:
121 | 126 |
127 |
128 |
129 | 163 |
164 |
165 | 225 |
226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /docs/enums/strength.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Strength | Xuper SDK (JS/TS) 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Enumeration Strength

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |

Mnemonic strength

73 |
74 |
75 |
76 |
77 |

Index

78 |
79 |
80 |
81 |

Enumeration members

82 | 87 |
88 |
89 |
90 |
91 |
92 |

Enumeration members

93 |
94 | 95 |

Easy

96 |
Easy: = 120
97 | 102 |
103 |
104 | 105 |

Hard

106 |
Hard: = 248
107 | 112 |
113 |
114 | 115 |

Middle

116 |
Middle: = 184
117 | 122 |
123 |
124 |
125 | 156 |
157 |
158 | 218 |
219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /docs/interfaces/errorinterface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ErrorInterface | Xuper SDK (JS/TS) 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Interface ErrorInterface

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Hierarchy

71 |
    72 |
  • 73 | ErrorInterface 74 |
  • 75 |
76 |
77 |
78 |

Index

79 |
80 |
81 |
82 |

Properties

83 | 91 |
92 |
93 |
94 |
95 |
96 |

Properties

97 |
98 | 99 |

code

100 |
code: number
101 | 106 |
107 |
108 | 109 |

message

110 |
message: string
111 | 116 |
117 |
118 | 119 |

name

120 |
name: string
121 | 126 |
127 |
128 | 129 |

Optional payload

130 |
payload: Error
131 | 136 |
137 |
138 | 139 |

Optional stack

140 |
stack: undefined | string
141 | 146 |
147 |
148 | 149 |

timestamp

150 |
timestamp: number
151 | 156 |
157 |
158 |
159 | 199 |
200 |
201 | 261 |
262 | 263 | 264 | 265 | -------------------------------------------------------------------------------- /docs/interfaces/transactioninterface.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TransactionInterface | Xuper SDK (JS/TS) 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |
47 | Menu 48 |
49 |
50 |
51 |
52 |
53 |
54 | 62 |

Interface TransactionInterface

63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |

Hierarchy

71 |
    72 |
  • 73 | TransactionInterface 74 | 79 |
  • 80 |
81 |
82 |
83 |

Index

84 |
85 |
86 |
87 |

Methods

88 | 91 |
92 |
93 |
94 |
95 |
96 |

Methods

97 |
98 | 99 |

postTransaction

100 | 103 | 119 |
120 |
121 |
122 | 147 |
148 |
149 | 209 |
210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | const conf = { 2 | preset: 'ts-jest', 3 | testPathIgnorePatterns: ['node_modules', 'sdk-1.*'] 4 | }; 5 | 6 | module.exports = { 7 | projects: [ 8 | { 9 | displayName: 'browser', 10 | testEnvironment: './test/jest/custom-test-env.js', 11 | ...conf 12 | }, 13 | // { 14 | // displayName: 'Nodejs', 15 | // testEnvironment: 'node', 16 | // setupFiles: [ 17 | // 'dotenv/config' 18 | // ], 19 | // ...conf 20 | // } 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@xuperchain/xuper-sdk", 3 | "version": "2.4.0", 4 | "description": "A simple JS(TS) SDK for XuperOS", 5 | "main": "dist/index.js", 6 | "typings": "dist/index.d.ts", 7 | "module": "dist/index.esm.js", 8 | "scripts": { 9 | "docs": "typedoc --mode file --out docs src", 10 | "lint:fix": "eslint src/*.ts --fix", 11 | "lint": "eslint src/*.ts", 12 | "commit": "git-cz", 13 | "release": "standard-version -a", 14 | "start": "tsdx watch", 15 | "build": "tsdx build", 16 | "test": "jest --passWithNoTests --verbose", 17 | "prepare": "tsdx build", 18 | "prerelease": "tsdx build" 19 | }, 20 | "files": [ 21 | "dist", 22 | "src" 23 | ], 24 | "engines": { 25 | "node": ">=10" 26 | }, 27 | "repository": { 28 | "type": "git", 29 | "url": "git+https://github.com/xuperchain/xuper-sdk-js.git" 30 | }, 31 | "keywords": [ 32 | "xuper", 33 | "blockchain", 34 | "xuper-sdk", 35 | "xuper-account", 36 | "xuper-lcv" 37 | ], 38 | "author": "Xinyi", 39 | "license": "Apache-2.0", 40 | "bugs": { 41 | "url": "https://github.com/xuperchain/xuper-sdk-js/issues" 42 | }, 43 | "homepage": "https://github.com/xuperchain/xuper-sdk-js#readme", 44 | "devDependencies": { 45 | "@commitlint/cli": "^12.1.1", 46 | "@commitlint/config-conventional": "^12.1.1", 47 | "@types/jest": "^26.0.4", 48 | "@typescript-eslint/eslint-plugin": "^2.34.0", 49 | "@typescript-eslint/parser": "^2.34.0", 50 | "awesome-typescript-loader": "^5.2.1", 51 | "clean-webpack-plugin": "^3.0.0", 52 | "coveralls": "^3.1.0", 53 | "cz-conventional-changelog": "^3.2.0", 54 | "dotenv": "^8.2.0", 55 | "eslint": "^6.8.0", 56 | "git-cz": "^4.7.0", 57 | "husky": "^3.1.0", 58 | "jest": "^26.1.0", 59 | "jest-environment-jsdom-fourteen": "^1.0.1", 60 | "rollup-plugin-copy": "^3.3.0", 61 | "standard-version": "^9.1.1", 62 | "ts-jest": "^26.1.1", 63 | "ts-loader": "^6.2.2", 64 | "tsdx": "^0.14.1", 65 | "tslib": "^2.0.0", 66 | "typedoc": "^0.15.8", 67 | "typescript": "^3.9.6", 68 | "whatwg-fetch": "^3.1.0" 69 | }, 70 | "husky": { 71 | "hooks": { 72 | "pre-commit": "npm run lint" 73 | } 74 | }, 75 | "publishConfig": { 76 | "access": "public" 77 | }, 78 | "dependencies": { 79 | "@grpc/grpc-js": "^1.2.6", 80 | "@grpc/proto-loader": "^0.5.6", 81 | "@types/aes-js": "^3.1.1", 82 | "@types/bn.js": "^4.11.5", 83 | "@types/elliptic": "^6.4.10", 84 | "@types/json-stable-stringify": "^1.0.32", 85 | "@types/long": "^4.0.1", 86 | "@types/pbkdf2": "^3.0.0", 87 | "@types/sha256": "^0.2.0", 88 | "aes-js": "^3.1.2", 89 | "bn.js": "^5.0.0", 90 | "elliptic": "^6.5.4", 91 | "json-stable-stringify": "^1.0.1", 92 | "long": "^4.0.0", 93 | "node-fetch": "^2.6.1", 94 | "pbkdf2": "^3.0.17", 95 | "ripemd160-min": "0.0.6", 96 | "sha256": "^0.2.0" 97 | }, 98 | "config": { 99 | "commitizen": { 100 | "path": "./node_modules/cz-conventional-changelog" 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /scripts/webpack.common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (webpack.common) 3 | * Created by xinyi on 2019/12/5 4 | */ 5 | /* eslint-disable quote-props */ 6 | 7 | // webpack conf 8 | module.exports = { 9 | module: { 10 | noParse: /@grpc|grpc-js|proto-loader/, 11 | rules: [ 12 | { 13 | test: /\.tsx?$/, 14 | use: 'ts-loader', 15 | exclude: /node_modules/ 16 | } 17 | ] 18 | }, 19 | resolve: { 20 | extensions: ['.tsx', '.ts', '.js'] 21 | }, 22 | externals: { 23 | // 'bn.js': 'bn.js', 24 | // 'sha256': 'sha256', 25 | // 'elliptic': 'elliptic', 26 | // 'ripemd160-min/dist-umd': 'ripemd160-min/dist-umd', 27 | // 'pbkdf2': 'pbkdf2', 28 | // '@grpc/grpc-js': '@grpc/grpc-js', 29 | // '@grpc/proto-loader': '@grpc/proto-loader' 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /scripts/webpack.prod.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (webpack.prod) 3 | * Created by xinyi on 2019/12/5 4 | */ 5 | 6 | const path = require('path'); 7 | const merge = require('webpack-merge'); 8 | const commonConf = require('./webpack.common.js'); 9 | 10 | // plugins 11 | const {CleanWebpackPlugin} = require('clean-webpack-plugin'); 12 | const TerserPlugin = require('terser-webpack-plugin'); 13 | 14 | // webpack conf 15 | const clientConf = { 16 | entry: { 17 | index: './src/index.ts', 18 | 'index.min': './src/index.ts' 19 | }, 20 | target: 'web', 21 | output: { 22 | path: path.join(process.cwd(), 'lib'), 23 | filename: '[name].js', 24 | library: '[name]', 25 | libraryTarget: 'umd' 26 | }, 27 | mode: 'production', 28 | optimization: { 29 | nodeEnv: 'production', 30 | minimizer: [ 31 | new TerserPlugin({ 32 | test: /\.min/i, 33 | parallel: true, 34 | cache: true, 35 | terserOptions: { 36 | output: { 37 | comments: false 38 | } 39 | } 40 | }) 41 | ] 42 | }, 43 | plugins: [new CleanWebpackPlugin({})] 44 | }; 45 | 46 | const serverConf = { 47 | entry: { 48 | index: './src/index.ts' 49 | }, 50 | target: 'node', 51 | output: { 52 | path: path.join(process.cwd(), 'lib'), 53 | filename: '[name].node.js', 54 | library: '[name]', 55 | libraryTarget: 'umd' 56 | }, 57 | mode: 'production', 58 | plugins: [new CleanWebpackPlugin({})] 59 | }; 60 | 61 | module.exports = [ 62 | merge(commonConf, clientConf), 63 | merge(commonConf, serverConf) 64 | ]; 65 | -------------------------------------------------------------------------------- /src/account.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (account.test) 3 | * Created by SmilingXinyi on 2020/7/2 4 | */ 5 | 6 | import BN from 'bn.js'; 7 | import sha256 from 'sha256'; 8 | import pbkdf2 from 'pbkdf2'; 9 | import {ec as EC} from 'elliptic'; 10 | import {RIPEMD160} from 'ripemd160-min'; 11 | import aesjs from 'aes-js'; 12 | 13 | import {PrivateKey, PublicKey, AccountModel} from './types'; 14 | import {Cryptography, Language, Strength} from './constants'; 15 | import { 16 | arrayPadStart, base58Decode, base58Encode, deepEqual, 17 | isBrowser, stringToPublicOrPrivateKey, publicOrPrivateKeyToString 18 | } from './utils'; 19 | 20 | import * as Requests from './requests'; 21 | import wordlist from './wordlist.json'; 22 | 23 | export default class Account { 24 | private last4BitsMask: BN = new BN(15); 25 | private password = 'jingbo is handsome!'; 26 | 27 | public create( 28 | language: Language, 29 | strength: Strength, 30 | cryptography: Cryptography 31 | ): AccountModel { 32 | const entropyByte = this.generateEntropy(strength); 33 | 34 | const cryptographyBit = new Uint8Array(1); 35 | cryptographyBit[0] = cryptography; 36 | 37 | const reservedBit = new Uint8Array(1); 38 | reservedBit[0] = 0; 39 | 40 | const reservedInt = new BN(reservedBit).and(this.last4BitsMask); 41 | 42 | let flagInt = new BN(cryptographyBit).and(this.last4BitsMask).shln(4); 43 | 44 | flagInt = flagInt.xor(reservedInt); 45 | 46 | const newEntropyByteSliceBuffer = new ArrayBuffer(entropyByte.length 47 | + new Uint8Array([flagInt.toNumber()]).byteLength); 48 | const newEntropyByteSlice = new Uint8Array(newEntropyByteSliceBuffer); 49 | 50 | newEntropyByteSlice.set(entropyByte); 51 | newEntropyByteSlice.set([Number(flagInt.toString(10))], entropyByte.length); 52 | 53 | const mnemonic = this.generateMnemonic(newEntropyByteSlice, language); 54 | const seed = this.generateSeed(mnemonic, this.password, 40, language); 55 | 56 | const curve = new EC('p256'); 57 | const privateKey: PrivateKey = this.generateKeyBySeed(curve, seed); 58 | const publicKey: PublicKey = { 59 | X: privateKey.X, 60 | Y: privateKey.Y, 61 | Curvname: privateKey.Curvname 62 | }; 63 | 64 | const address = this.generateAddress(publicKey, cryptography); 65 | 66 | return { 67 | address, 68 | mnemonic, 69 | privateKey, 70 | publicKey 71 | }; 72 | } 73 | 74 | public retrieve( 75 | mnemonic: string, 76 | language: Language = Language.SimplifiedChinese, 77 | cryptography: Cryptography = Cryptography.EccFIPS 78 | ): AccountModel { 79 | const seed = this.generateSeed(mnemonic, this.password, 40, language); 80 | 81 | const curve = new EC('p256'); 82 | 83 | const privateKey: PrivateKey = this.generateKeyBySeed(curve, seed); 84 | const publicKey: PublicKey = { 85 | X: privateKey.X, 86 | Y: privateKey.Y, 87 | Curvname: privateKey.Curvname 88 | }; 89 | 90 | const address = this.generateAddress(publicKey, cryptography); 91 | 92 | return { 93 | address, 94 | mnemonic, 95 | privateKey, 96 | publicKey 97 | }; 98 | } 99 | 100 | checkAddress(address: string): boolean { 101 | try { 102 | const decode = base58Decode(address); 103 | if (decode.length < 1) { 104 | return false; 105 | } 106 | const simpleCheckCode = new Uint8Array(decode.slice(decode.length - 4)); 107 | const checkContent = decode.slice(0, decode.length - 4); 108 | const checkCode = sha256.x2(Array.from(checkContent), {asBytes: true}); 109 | const realCheckCode = new Uint8Array(checkCode.slice(0, 4)); 110 | return (deepEqual(simpleCheckCode, realCheckCode)); 111 | } catch (e) { 112 | return false; 113 | } 114 | } 115 | 116 | checkMnemonic(mnemonic: string, language: Language): boolean { 117 | const words = wordlist[language]; 118 | return mnemonic.split(' ').every(w => words.indexOf(w) > -1); 119 | } 120 | 121 | getBalance(address: string, node: string, chain: string): Promise { 122 | const body = { 123 | address: address, 124 | bcs: [{ 125 | bcname: chain 126 | }] 127 | }; 128 | 129 | return Requests.getBalance(node, body); 130 | } 131 | 132 | getBalanceDetail(address: string, node: string, chain: string): Promise { 133 | const body = { 134 | address: address, 135 | tfds: [{ 136 | bcname: chain 137 | }] 138 | }; 139 | 140 | return Requests.getBalanceDetail(node, body); 141 | } 142 | 143 | import(password: string, privateKeyStr: string): AccountModel { 144 | const decryptStr = this.decryptPrivateKey(password, privateKeyStr); 145 | const privateKeyObj = stringToPublicOrPrivateKey(decryptStr); 146 | 147 | const privateKey: PrivateKey = { 148 | D: privateKeyObj.D, 149 | X: privateKeyObj.X, 150 | Y: privateKeyObj.Y, 151 | Curvname: privateKeyObj.Curvname 152 | }; 153 | 154 | const publicKey: PublicKey = { 155 | X: privateKey.X, 156 | Y: privateKey.Y, 157 | Curvname: privateKey.Curvname 158 | }; 159 | 160 | const address = this.generateAddress(publicKey, Cryptography.EccFIPS); 161 | 162 | return { 163 | address, 164 | privateKey, 165 | publicKey 166 | }; 167 | } 168 | 169 | export(password: string, privateKey: PrivateKey): string { 170 | return btoa( 171 | this.encryptPrivateKey(password, privateKey) 172 | ); 173 | } 174 | 175 | publicKey(publicKey: PublicKey): string { 176 | return publicOrPrivateKeyToString(publicKey); 177 | } 178 | 179 | private decryptPrivateKey(password: string, keyStr: string): string { 180 | const bytes = atob(keyStr).split('').map(s => s.charCodeAt(0)); 181 | const blockSize = 16; 182 | const key = sha256.x2(password, {asBytes: true}); 183 | const padding = blockSize - (bytes.length % blockSize); 184 | bytes.concat(Array(padding).fill(padding)); 185 | const aesCbc = new aesjs.ModeOfOperation.cbc(key, key.slice(0, blockSize)); 186 | let decryptedBytes = aesCbc.decrypt(bytes); 187 | const unpadding = decryptedBytes[decryptedBytes.length - 1]; 188 | if (decryptedBytes.length - unpadding <= 0) { 189 | throw 'password error'; 190 | } 191 | decryptedBytes = decryptedBytes.slice(0, decryptedBytes.length - unpadding); 192 | const td = new TextDecoder(); 193 | return td.decode(decryptedBytes); 194 | } 195 | 196 | 197 | private encryptPrivateKey(password: string, privateKey: PrivateKey): string { 198 | const keyStr = publicOrPrivateKeyToString(privateKey); 199 | const te = new TextEncoder(); 200 | const keyBytes: Uint8Array = te.encode(keyStr); 201 | const blockSize = 16; 202 | const key = sha256.x2(password, {asBytes: true}); 203 | const padding = blockSize - (keyBytes.length % blockSize); 204 | const theBytes = Array.from(keyBytes).concat(Array(padding).fill(padding)); 205 | // eslint-disable-next-line new-cap 206 | const aesCbc = new aesjs.ModeOfOperation.cbc(key, key.slice(0, blockSize)); 207 | const result = aesCbc.encrypt(theBytes); 208 | const ts = Array.from(result).map((s: number) => String.fromCharCode(s)); 209 | return ts.join(''); 210 | } 211 | 212 | private generateEntropy(strength: Strength): Uint8Array { 213 | if ((strength + 8) % 32 !== 0 || strength + 8 < 128 || strength + 8 > 256) { 214 | throw 'Invalid entropy length'; 215 | } 216 | 217 | if (!isBrowser) { 218 | // eslint-disable-next-line @typescript-eslint/no-var-requires,global-require 219 | const crypto = require('crypto'); 220 | return crypto.randomFillSync(new Uint8Array(strength / 8)); 221 | } 222 | 223 | return crypto.getRandomValues(new Uint8Array(strength / 8)); 224 | } 225 | 226 | private generateMnemonic(entropy: ArrayBuffer, language: Language): string { 227 | const entropyBitLength = entropy.byteLength * 8; 228 | 229 | const invalidEntropyLength = new Error('Entropy length must within [120, 248] and after +8 be multiples of 32'); 230 | 231 | if ((entropyBitLength) % 32 !== 0 || entropyBitLength < 128 || entropyBitLength > 256) { 232 | throw invalidEntropyLength; 233 | } 234 | 235 | const wordList = this.getWordListByLanguage(language); 236 | 237 | const checksumBitLength = entropyBitLength / 32; 238 | 239 | const sentenceLength = (entropyBitLength + checksumBitLength) / 11; 240 | 241 | const entropyWithChecksum = this.addChecksum(entropy); 242 | 243 | const words = new Array(sentenceLength); 244 | 245 | const Last11BitsMask = new BN(2047); 246 | 247 | let entropyInt = entropyWithChecksum; 248 | 249 | for (let i = sentenceLength - 1; i >= 0; i--) { 250 | const word = entropyInt.and(Last11BitsMask); 251 | 252 | entropyInt = entropyInt.shrn(11); 253 | 254 | words[i] = wordList[word.toNumber()]; 255 | } 256 | 257 | return words.join(' '); 258 | } 259 | 260 | private addChecksum(data: any) { 261 | const hashByte = sha256(data); 262 | 263 | const firstChecksumByte = new BN(hashByte.toString().substr(2, 2), 16); 264 | 265 | const checksumBitLength = data.length / 4; 266 | 267 | let dataBigInt = new BN(data); 268 | 269 | const BigZero = new BN(0); 270 | const BigOne = new BN(1); 271 | const BigTwo = new BN(2); 272 | 273 | for (let i = 0; i < checksumBitLength; i++) { 274 | dataBigInt = dataBigInt.mul(BigTwo); 275 | if ((firstChecksumByte.and(new BN(1 << 7 - i)).gt(BigZero))) { 276 | dataBigInt = dataBigInt.or(BigOne); 277 | } 278 | } 279 | 280 | return dataBigInt; 281 | } 282 | 283 | private generateSeed(mnemonic: string, password: string, keyLen: number, language: Language): any { 284 | const wordList = this.getWordListByLanguage(language); 285 | const originMnemonic = mnemonic.split(' ').map(word => wordList.indexOf(word)); 286 | 287 | const mnemonicBitSize = originMnemonic.length * 11; 288 | 289 | const checksumBitSize = mnemonicBitSize % 32; 290 | 291 | let bnRes = new BN(0); 292 | const bn2048 = new BN(2048); 293 | originMnemonic.forEach(v => { 294 | const bb = new BN(v); 295 | bnRes = bnRes.mul(bn2048); 296 | bnRes = bnRes.add(bb); 297 | }); 298 | 299 | const checksumModulo = new BN(2).pow(new BN(checksumBitSize)); 300 | 301 | const entropy = bnRes.div(checksumModulo); 302 | 303 | const entropyByteSize = (mnemonicBitSize - checksumBitSize) / 8; 304 | const fullByteSize = entropyByteSize + 1; 305 | 306 | 307 | const entropyBytes = entropy.toArray().slice(0, entropyByteSize); 308 | const entropyWithChecksumBytes = bnRes.toArray().slice(0, fullByteSize); 309 | 310 | const bn = this.addChecksum(entropyBytes).toString(10); 311 | const newBN = new BN(bn); 312 | 313 | if (JSON.stringify(newBN.toArray().slice(0, fullByteSize)) 314 | !== JSON.stringify(entropyWithChecksumBytes)) { 315 | throw 'bug'; 316 | } 317 | 318 | const salt = `mnemonic${password}`; 319 | return pbkdf2.pbkdf2Sync(mnemonic, salt, 2048, keyLen, 'sha512'); 320 | } 321 | 322 | private generateKeyBySeed(curve: EC, seed: any): PrivateKey { 323 | const key = curve.genKeyPair(); 324 | // const pub = key.getPublic(); 325 | let k = new BN(seed); 326 | const keyN = key.ec.n; 327 | const one = new BN(1); 328 | if (!keyN) { 329 | throw 'Generate seed faild'; 330 | } 331 | const n = keyN.sub(one); 332 | k = k.mod(n); 333 | k = k.add(one); 334 | 335 | const ec = new EC('p256'); 336 | const bigD = new BN(k); 337 | 338 | const privKey = ec.keyFromPrivate(bigD.toArray()); 339 | 340 | const pubXY = privKey.getPublic(); 341 | 342 | return { 343 | Curvname: 'P-256', 344 | D: k.toString(10), 345 | X: pubXY.getX().toString(10), 346 | Y: pubXY.getY().toString(10) 347 | }; 348 | } 349 | 350 | private generateAddress(publicKey: PublicKey, cryptography: Cryptography): string { 351 | const flagByte = [4]; 352 | const xBytes = new BN(publicKey.X).toArray(); 353 | const yBytes = new BN(publicKey.Y).toArray(); 354 | 355 | // 256 + 7 >> 3 356 | const data = flagByte 357 | .concat(arrayPadStart(xBytes, 32)) 358 | .concat(arrayPadStart(yBytes, 32)); 359 | 360 | const outputSha256 = sha256(data, {asBytes: true}); 361 | 362 | const createRipemd160 = new RIPEMD160(); 363 | 364 | const outputRipemd160 = createRipemd160.update(outputSha256).digest(); 365 | 366 | const outputRipemd160Buf = new Uint8Array(outputRipemd160.length + 1); 367 | 368 | outputRipemd160Buf[0] = cryptography; 369 | 370 | outputRipemd160Buf.set(outputRipemd160, 1); 371 | const checkCode = sha256.x2(Array.from(outputRipemd160Buf), {asBytes: true}); 372 | 373 | const simpleCheckCode = checkCode.slice(0, 4); 374 | 375 | const checkCodeBuf = new Uint8Array(outputRipemd160Buf.length + simpleCheckCode.length); 376 | 377 | checkCodeBuf.set(outputRipemd160Buf, 0); 378 | checkCodeBuf.set(simpleCheckCode, outputRipemd160Buf.length); 379 | 380 | return base58Encode(checkCodeBuf); 381 | } 382 | 383 | private getWordListByLanguage(language: Language): Array { 384 | if (!wordlist[language]) { 385 | throw new Error('This language has not been supported yet'); 386 | } 387 | return wordlist[language]; 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (constants) 3 | * Created by SmilingXinyi on 2020/7/2 4 | */ 5 | 6 | export const VERSION = 1; 7 | 8 | /** 9 | * Mnemonic language 10 | */ 11 | export enum Language { 12 | SimplifiedChinese = 'SimplifiedChinese', 13 | English = 'English' 14 | } 15 | 16 | /** 17 | * Mnemonic strength 18 | */ 19 | export enum Strength { 20 | Easy = 120, 21 | Middle = 184, 22 | Hard = 248 23 | } 24 | 25 | /** 26 | * Cryptography 27 | */ 28 | export enum Cryptography { 29 | EccFIPS = 1 30 | // Todo: EccGM: 2 31 | } 32 | -------------------------------------------------------------------------------- /src/contract.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (contract) 3 | */ 4 | 5 | import {ContractRequesttModel, ContractInfo} from './types'; 6 | import * as Requests from './requests'; 7 | 8 | export default class Contract { 9 | createContractAccount(contractAccountName: number, address: string): ContractRequesttModel[] { 10 | if (contractAccountName < 10 ** 15 11 | || contractAccountName >= 10 ** 16) { 12 | throw 'Conrtact account must be numbers of length 16'; 13 | } 14 | 15 | const defaultACL = { 16 | pm: { 17 | rule: 1, 18 | acceptValue: 1.0 19 | }, 20 | aksWeight: { 21 | [address]: 1.0 22 | } 23 | }; 24 | 25 | const args = { 26 | account_name: btoa(contractAccountName.toString()), 27 | acl: btoa(JSON.stringify(defaultACL)) 28 | }; 29 | 30 | const invokeRequests: ContractRequesttModel[] = [{ 31 | module_name: 'xkernel', 32 | method_name: 'NewAccount', 33 | args 34 | }]; 35 | 36 | return invokeRequests; 37 | } 38 | 39 | async queryACL(node: string, chain: string, accountName: string, contarctInfo?: ContractInfo) { 40 | let body = { 41 | bcname: chain, 42 | accountName 43 | } 44 | 45 | if (contarctInfo) { 46 | body = { 47 | ...body, 48 | // @ts-ignore 49 | contractName: contarctInfo.contarctName, 50 | methodName: contarctInfo.contractMethod 51 | } 52 | } 53 | 54 | return Requests.queryACL(node, body); 55 | } 56 | 57 | async queryContractStatData(node: string, chain: string) { 58 | const body = { 59 | bcname: chain 60 | } 61 | 62 | return Requests.queryContractStatData(node, body); 63 | } 64 | 65 | contarctAccounts(node: string, chain: string, address: string) { 66 | const body = { 67 | bcname: chain, 68 | address 69 | }; 70 | return Requests.accountList(node, body); 71 | } 72 | 73 | getContracts(node: string, chain: string, isAccount: boolean, target?: string) { 74 | if (isAccount) { 75 | const body = { 76 | bcname: chain, 77 | account: target 78 | }; 79 | return Requests.accountContractList(node, body); 80 | } 81 | else { 82 | const body = { 83 | bcname: chain, 84 | address: target 85 | }; 86 | return Requests.addressContractList(node, body); 87 | } 88 | } 89 | 90 | // update wasm contract requests 91 | 92 | // invoke contract 93 | 94 | deployContractRequests( 95 | contractAccount: string, 96 | contractName: string, 97 | code: string, 98 | lang: string, 99 | initArgs: any 100 | ) { 101 | /* 102 | const newInitArgs = { 103 | ...initArgs 104 | }; 105 | 106 | const te = new TextEncoder(); 107 | 108 | Object.keys(initArgs).forEach(key => { 109 | const bytes = te.encode(initArgs[key]); 110 | const valueBuf: Array = []; 111 | bytes.forEach(b => valueBuf.push(String.fromCharCode(b))); 112 | newInitArgs[key] = btoa(valueBuf.join('')); 113 | }); 114 | 115 | const desc = new Uint8Array([10, 1].concat(lang.split('').map(w => w.charCodeAt(0)))); 116 | const descBuf = Object.values(desc).map(n => String.fromCharCode(n)); 117 | 118 | const args = { 119 | account_name: contractAccount, 120 | contract_name: contractName, 121 | contract_desc: descBuf.join(''), 122 | contract_code: code, 123 | init_args: JSON.stringify(newInitArgs) 124 | }; 125 | 126 | const contractArgs = { 127 | ...args 128 | }; 129 | 130 | Object.keys(contractArgs).forEach(key => { 131 | // @ts-ignore 132 | contractArgs[key] = btoa(contractArgs[key]); 133 | }); 134 | 135 | const invokeRequests: ContractRequesttModel[] = [{ 136 | module_name: 'xkernel', 137 | method_name: 'Deploy', 138 | args: contractArgs 139 | }]; 140 | 141 | return invokeRequests; 142 | 143 | */ 144 | 145 | return this.generateContractRequests('Deploy', contractAccount, contractName, code, lang, initArgs); 146 | } 147 | 148 | deploySolidityContractRequests( 149 | contractAccount: string, 150 | contractName: string, 151 | bin: string, 152 | abi: string, 153 | lang: string, 154 | initArgs: any 155 | ) { 156 | return this.generateSolidityContractRequests('Deploy', contractAccount, contractName, bin, abi, lang, initArgs); 157 | } 158 | 159 | upgradeContractRequests( 160 | contractAccount: string, 161 | contractName: string, 162 | code: string, 163 | lang: string, 164 | initArgs: any 165 | ) { 166 | return this.generateContractRequests('Upgrade', contractAccount, contractName, code, lang, initArgs); 167 | } 168 | 169 | upgradeSolidityContractRequests( 170 | contractAccount: string, 171 | contractName: string, 172 | bin: string, 173 | abi: string, 174 | lang: string, 175 | initArgs: any 176 | ) { 177 | return this.generateSolidityContractRequests('Upgrade', contractAccount, contractName, bin, abi, lang, initArgs); 178 | } 179 | 180 | deployNativeContractRequests( 181 | contractAccount: string, 182 | contractName: string, 183 | code: string, 184 | lang: string, 185 | initArgs: any 186 | ) { 187 | return this.generateNativeRequests('Deploy', contractAccount, contractName, code, lang, initArgs); 188 | } 189 | 190 | upgradeNativeContractRequests( 191 | contractAccount: string, 192 | contractName: string, 193 | code: string, 194 | lang: string, 195 | initArgs: any 196 | ) { 197 | return this.generateNativeRequests('Upgrade', contractAccount, contractName, code, lang, initArgs); 198 | } 199 | 200 | generateContractRequests( 201 | methodName: string, 202 | contractAccount: string, 203 | contractName: string, 204 | code: string, 205 | lang: string, 206 | initArgs: any 207 | ) { 208 | const newInitArgs = { 209 | ...initArgs 210 | }; 211 | 212 | const te = new TextEncoder(); 213 | 214 | Object.keys(initArgs).forEach(key => { 215 | const bytes = te.encode(initArgs[key]); 216 | const valueBuf: Array = []; 217 | bytes.forEach(b => valueBuf.push(String.fromCharCode(b))); 218 | newInitArgs[key] = btoa(valueBuf.join('')); 219 | }); 220 | 221 | const langArray = lang.split('') 222 | const desc = new Uint8Array([10, langArray.length].concat(lang.split('').map(w => w.charCodeAt(0)))); 223 | const descBuf = Object.values(desc).map(n => String.fromCharCode(n)); 224 | 225 | const args = { 226 | account_name: contractAccount, 227 | contract_code: code, 228 | contract_desc: descBuf.join(''), 229 | contract_name: contractName, 230 | init_args: JSON.stringify(newInitArgs) 231 | }; 232 | 233 | const contractArgs = { 234 | ...args 235 | }; 236 | 237 | Object.keys(contractArgs).forEach(key => { 238 | // @ts-ignore 239 | contractArgs[key] = btoa(contractArgs[key]); 240 | }); 241 | 242 | const invokeRequests: ContractRequesttModel[] = [{ 243 | module_name: 'xkernel', 244 | method_name: methodName, 245 | args: contractArgs 246 | }]; 247 | 248 | return invokeRequests; 249 | } 250 | 251 | generateSolidityContractRequests( 252 | methodName: string, 253 | contractAccount: string, 254 | contractName: string, 255 | bin: string, 256 | abi: string, 257 | lang: string, 258 | initArgs: any 259 | ) { 260 | const newInitArgs = { 261 | // ...initArgs, 262 | input: JSON.stringify(initArgs), 263 | jsonEncoded: 'true' 264 | }; 265 | 266 | const te = new TextEncoder(); 267 | 268 | Object.keys(newInitArgs).forEach(key => { 269 | // @ts-ignore 270 | const bytes = te.encode(newInitArgs[key]); 271 | const valueBuf: Array = []; 272 | bytes.forEach(b => valueBuf.push(String.fromCharCode(b))); 273 | // @ts-ignore 274 | newInitArgs[key] = btoa(valueBuf.join('')); 275 | }); 276 | 277 | const langArray = lang.split('') 278 | const desc = new Uint8Array([42, langArray.length].concat(lang.split('').map(w => w.charCodeAt(0)))); 279 | 280 | // const desc = lang.split('').map(w => w.charCodeAt(0)); 281 | const descBuf = Object.values(desc).map(n => String.fromCharCode(n)); 282 | 283 | const args = { 284 | account_name: contractAccount, 285 | contract_abi: abi, 286 | contract_code: bin, 287 | contract_desc: descBuf.join(''), 288 | contract_name: contractName, 289 | init_args: JSON.stringify(newInitArgs) 290 | }; 291 | 292 | const contractArgs = { 293 | ...args 294 | }; 295 | 296 | Object.keys(contractArgs).forEach(key => { 297 | // @ts-ignore 298 | contractArgs[key] = btoa(contractArgs[key]); 299 | }); 300 | 301 | const invokeRequests: ContractRequesttModel[] = [{ 302 | module_name: 'xkernel', 303 | method_name: methodName, 304 | args: contractArgs 305 | }]; 306 | 307 | return invokeRequests; 308 | } 309 | 310 | generateNativeRequests( 311 | methodName: string, 312 | contractAccount: string, 313 | contractName: string, 314 | code: string, 315 | lang: string, 316 | initArgs: any 317 | ) { 318 | const newInitArgs = { 319 | ...initArgs 320 | }; 321 | 322 | const te = new TextEncoder(); 323 | 324 | Object.keys(initArgs).forEach(key => { 325 | const bytes = te.encode(initArgs[key]); 326 | const valueBuf: Array = []; 327 | bytes.forEach(b => valueBuf.push(String.fromCharCode(b))); 328 | newInitArgs[key] = btoa(valueBuf.join('')); 329 | }); 330 | 331 | const langArray = lang.split('') 332 | const desc = new Uint8Array([10, langArray.length].concat(lang.split('').map(w => w.charCodeAt(0)))); 333 | const descBuf = Object.values(desc).map(n => String.fromCharCode(n)); 334 | 335 | const nativeField = new Uint8Array([42, 6, 110, 97, 116, 105, 118, 101]); 336 | const nativeFieldBuf = Object.values(nativeField).map(n => String.fromCharCode(n)); 337 | 338 | const args = { 339 | account_name: contractAccount, 340 | contract_code: code, 341 | contract_desc: descBuf.join('')+nativeFieldBuf.join(''), 342 | contract_name: contractName, 343 | init_args: JSON.stringify(newInitArgs) 344 | }; 345 | 346 | const contractArgs = { 347 | ...args 348 | }; 349 | 350 | Object.keys(contractArgs).forEach(key => { 351 | // @ts-ignore 352 | contractArgs[key] = btoa(contractArgs[key]); 353 | }); 354 | 355 | const invokeRequests: ContractRequesttModel[] = [{ 356 | module_name: 'xkernel', 357 | method_name: methodName, 358 | args: contractArgs 359 | }]; 360 | 361 | return invokeRequests; 362 | } 363 | 364 | invokeContract( 365 | contractName: string, 366 | methodName: string, 367 | moduleName: string, 368 | args: any, 369 | amount: string 370 | ): ContractRequesttModel[] { 371 | 372 | const newArgs = { 373 | ...args 374 | }; 375 | 376 | const te = new TextEncoder(); 377 | 378 | Object.keys(args).forEach(key => { 379 | const bytes = te.encode(args[key]); 380 | const valueBuf: Array = []; 381 | bytes.forEach(b => valueBuf.push(String.fromCharCode(b))); 382 | newArgs[key] = btoa(valueBuf.join('')); 383 | }); 384 | 385 | const invokeRequests: ContractRequesttModel[] = [{ 386 | module_name: moduleName, 387 | method_name: methodName, 388 | contract_name: contractName, 389 | args: newArgs, 390 | amount 391 | }]; 392 | 393 | return invokeRequests; 394 | } 395 | 396 | invokeSolidityContract( 397 | contractName: string, 398 | methodName: string, 399 | moduleName: string, 400 | args: any, 401 | amount: string 402 | ): ContractRequesttModel[] { 403 | 404 | const newArgs = { 405 | input: JSON.stringify(args), 406 | jsonEncoded: 'true' 407 | }; 408 | 409 | const te = new TextEncoder(); 410 | 411 | Object.keys(newArgs).forEach(key => { 412 | // @ts-ignore 413 | const bytes = te.encode(newArgs[key]); 414 | const valueBuf: Array = []; 415 | bytes.forEach(b => valueBuf.push(String.fromCharCode(b))); 416 | // @ts-ignore 417 | newArgs[key] = btoa(valueBuf.join('')); 418 | }); 419 | 420 | const invokeRequests: ContractRequesttModel[] = [{ 421 | module_name: moduleName, 422 | method_name: methodName, 423 | contract_name: contractName, 424 | args: newArgs, 425 | amount 426 | }]; 427 | 428 | return invokeRequests; 429 | } 430 | } 431 | -------------------------------------------------------------------------------- /src/error.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file error 3 | * Created by xinyi on 2020/1/2 4 | */ 5 | 6 | import {ErrorOptions} from './types'; 7 | 8 | interface ErrorInterface { 9 | code: number; 10 | name: string; 11 | timestamp: number; 12 | message: string; 13 | stack?: string; 14 | payload?: Error; 15 | } 16 | 17 | const defaultOpts: ErrorOptions = { 18 | name: 'Xuper SDK Error', 19 | isFront: true 20 | }; 21 | 22 | function XuperError( 23 | errorCode: number | string, 24 | message: string, 25 | name = 'Xuper SDK error', 26 | isFront = true, 27 | payload?: Error 28 | ): ErrorInterface { 29 | return { 30 | code: parseInt(`9${+isFront}${errorCode.toString().padStart(5, '0')}`, 10), 31 | timestamp: Date.now(), 32 | stack: new Error().stack, 33 | name, 34 | message, 35 | payload 36 | }; 37 | } 38 | 39 | XuperError.or = (errors: ErrorInterface[]) => { 40 | const oriError = errors[0]; 41 | oriError.message = errors.map(error => error.message).join(' or '); 42 | return oriError; 43 | }; 44 | 45 | XuperError.and = (errors: ErrorInterface[]) => errors.join(' and '); 46 | 47 | XuperError.prototype = Error; 48 | 49 | class XuperError1 extends Error { 50 | payload: Error | string | undefined; 51 | errorCode: number | string; 52 | 53 | constructor( 54 | errorCode: number | string, 55 | message = '', 56 | payload?: Error | string, 57 | opts?: ErrorOptions 58 | ) { 59 | super(message); 60 | const {name} = Object.assign({}, defaultOpts, opts); 61 | this.name = name!; 62 | this.payload = payload; 63 | this.errorCode = errorCode; 64 | this.stack = XuperError1.fixStackString(this.stack, '\n', opts?.stackIgnore); 65 | } 66 | 67 | static template( 68 | errorCode: number | string, 69 | message: string, 70 | payload?: Error | string, 71 | opts?: ErrorOptions 72 | ) { 73 | const tmpOpts = { 74 | stackIgnore: [1, 3] 75 | }; 76 | if (arguments.length > 2 && !opts) { 77 | // @ts-ignore 78 | opts = Object.assign({}, tmpOpts, payload); 79 | // @ts-ignore 80 | payload = null; 81 | } 82 | opts = Object.assign({}, tmpOpts, opts); 83 | return new this(errorCode, message, payload, opts); 84 | } 85 | 86 | private static fixStackString(stack = '', char = '\n', stackLine?: [number, number]): string { 87 | const [ignoreStart, ignoreCount] = stackLine || [1, 1]; 88 | const stackArr = stack.split(char); 89 | stackArr.splice(ignoreStart, ignoreCount); 90 | return stackArr.join(char); 91 | } 92 | 93 | // Todo: fix error.stack 94 | /* 95 | static new( 96 | errorCode: number | string, 97 | message: string, 98 | payload?: Error | string, 99 | opts?: ErrorOptions 100 | ): XuperError1 { 101 | const tmpOpts = { 102 | stackIgnore: [1, 2] 103 | }; 104 | return new this(errorCode, message, payload, Object.assign({}, tmpOpts, opts)); 105 | } 106 | */ 107 | } 108 | 109 | export { 110 | XuperError, 111 | XuperError1 112 | }; 113 | 114 | const Errors = { 115 | ACCOUNT_NOT_EXIST: XuperError(6001, 'Account not exist'), 116 | PARAMETER_ERROR: XuperError(6002, 'Parameter error'), 117 | UTXO_NOT_ENOUGH: XuperError(1001, 'Utxo is not enough'), 118 | PARAMETER_EMPTY_FUNC: (params: string | string[] = []) => { 119 | if (typeof params === 'string') { 120 | params = [params]; 121 | } 122 | const message = 123 | `Parameter ${params.length > 0 ? `\'${params.join(', ')}\' ` : ''}is empty`; 124 | return XuperError1.template(6002, message); 125 | } 126 | 127 | }; 128 | 129 | export default Errors; 130 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Interfaces 3 | * Created by SmilingXinyi on 2019/12/19 4 | */ 5 | 6 | /* ---------- Xuper SDK ---------- */ 7 | 8 | import {Cryptography, Language, Strength} from './constants'; 9 | 10 | import {AccountModel, TransactionModel, PublicKey} from './types'; 11 | 12 | export default interface XuperSDKInterface extends AccountInterface, TransactionInterface, ContractInterface { 13 | checkStatus(): Promise; 14 | 15 | transactionIdToHex(t: Required): string; 16 | } 17 | 18 | /* ---------- Account ---------- */ 19 | 20 | export interface AccountInterface { 21 | create(language: Language, strength: Strength, cryptography: Cryptography): AccountModel; 22 | 23 | retrieve(mnemonic: string, language: Language, cryptography: Cryptography, cache?: boolean): AccountModel; 24 | 25 | import(password: string, privateKeyStr: string, cache?: boolean): AccountModel; 26 | 27 | export(password: string): string; 28 | 29 | publicKey(publicKey: PublicKey): string; 30 | 31 | checkAddress(address?: string): boolean; 32 | 33 | checkMnemonic(mnemonic: string, language: Language): boolean; 34 | 35 | getBalance(address?: string): Promise; 36 | 37 | getBalanceDetail(address?: string): Promise; 38 | 39 | } 40 | 41 | /* ---------- Transaction ---------- */ 42 | 43 | export interface TransactionInterface { 44 | // preExec(address: string, authRequire: AuthModel[], invokeRequests: any): Promise 45 | // preExecWithUTXO(): Promise; 46 | // makeTransaction(): Promise; 47 | postTransaction(tx: TransactionModel): Promise; 48 | 49 | // queryTransaction(txid: string): Promise; 50 | } 51 | 52 | /* ---------- Contract ---------- */ 53 | 54 | export interface ContractInterface { 55 | createContractAccount(contractAccountName: number, address: string): Promise; 56 | 57 | getContractAccounts(address?: string): Promise; 58 | 59 | getContracts(target: string): Promise; 60 | 61 | deployWasmContract(contractAccount: string, contractName: string, code: string, lang: string, initArgs: any, upgrade: boolean, account?: AccountModel, desc?: string): Promise; 62 | 63 | deploySolidityContract(contractAccount: string, contractName: string, bin: string, abi: string, lang: string, initArgs: any, upgrade: boolean, account?: AccountModel, desc?: string): Promise; 64 | 65 | deployNativeContract(contractAccount: string, contractName: string, code: string, lang: string, initArgs: any, upgrade: boolean, account?: AccountModel, desc?: string): Promise; 66 | 67 | // upgradeContract(): Promise; 68 | // imvokeContract(): Promise; 69 | } 70 | -------------------------------------------------------------------------------- /src/plugins/endorsement.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (endorsement) 3 | */ 4 | 5 | import {UTXO, TXOutput, TransactionModel, AuthModel, AccountModel, Options} from '../types'; 6 | import * as Requests from '../requests'; 7 | import {convert} from '../utils'; 8 | 9 | const plugin = (args: any) => ({ 10 | name: 'Compliance', 11 | init: function(defaultArgs: any, sdkOptions: Options) { 12 | if (!sdkOptions.env?.node?.disableGRPC) { 13 | Requests.initializationEndorseClient(defaultArgs.transfer.server); 14 | } 15 | }, 16 | func: { 17 | makeTransaction: async function(defaultArgs: any, account: AccountModel, ti: TransactionModel, authRequires: AuthModel[], preExecWithUtxosObj: any) { 18 | let tx: TransactionModel; 19 | 20 | let newPreExecWithUtxosObj = {...preExecWithUtxosObj}; 21 | 22 | const {fee: endorseFee, endorseServiceFeeAddr} = defaultArgs; 23 | 24 | const {utxoOutput} = preExecWithUtxosObj; 25 | 26 | // @ts-ignore 27 | const checkTx = this.generateTransaction( 28 | account, {utxoOutput}, [], {amount: endorseFee, fee: 0, to: endorseServiceFeeAddr} 29 | ); 30 | 31 | let totalSelected: number[] = []; 32 | const utxoList: UTXO[] = []; 33 | 34 | checkTx.txOutputs.forEach(( 35 | txOutput: TXOutput, index: number 36 | ) => { 37 | // @ts-ignore 38 | if (txOutput.toAddr === btoa(account.address)) { 39 | const utxo: UTXO = { 40 | amount: txOutput.amount, 41 | toAddr: txOutput.toAddr, 42 | refTxid: checkTx.txid, 43 | refOffset: index 44 | }; 45 | utxoList.push(utxo); 46 | totalSelected = atob(utxo.amount).split('').map(w => w.charCodeAt(0)); 47 | } 48 | }); 49 | 50 | const newUtxoOutput = { 51 | utxoList, 52 | totalSelected 53 | }; 54 | 55 | 56 | newPreExecWithUtxosObj = {...newPreExecWithUtxosObj, utxoOutput: newUtxoOutput}; 57 | 58 | // @ts-ignore 59 | tx = this.generateTransaction( 60 | account, 61 | newPreExecWithUtxosObj, 62 | Object.keys(authRequires), 63 | ti 64 | ); 65 | 66 | 67 | // @ts-ignore 68 | await Object.keys(authRequires).reduce(async (prov: any, cur: any): Promise => { 69 | if (prov) { 70 | tx = prov; 71 | } 72 | const auth = authRequires[cur]; 73 | return auth.sign(checkTx, await tx); 74 | }, 0); 75 | 76 | return tx; 77 | }, 78 | transfer: function(defaultArgs: any, chain: string) { 79 | const {fee: endorseFee, server, endorseServiceCheckAddr} = defaultArgs; 80 | return {[endorseServiceCheckAddr] : { 81 | fee: endorseFee, 82 | sign: async (checkTx: TransactionModel, tx: TransactionModel): Promise => { 83 | const obj = { 84 | bcname: chain, 85 | // @ts-ignore 86 | tx: convert(tx, ['jsonEncoded']) 87 | }; 88 | 89 | const body = { 90 | RequestName: 'ComplianceCheck', 91 | BcName: chain, 92 | Fee: convert(checkTx), 93 | RequestData: btoa(JSON.stringify(obj)) 94 | }; 95 | 96 | const result = await Requests.endorser(server, body); 97 | 98 | if (!tx.authRequireSigns) { 99 | tx.authRequireSigns = []; 100 | } 101 | 102 | tx.authRequireSigns.push(result.EndorserSign); 103 | 104 | return tx; 105 | } 106 | }} 107 | } 108 | }, 109 | hookFuncs: [ 110 | 'makeTransaction', 111 | 'transfer' 112 | ], 113 | args: Object.assign({}, { 114 | makeTransaction: { 115 | server: process.env.ENDORSE_SERVER || '', 116 | fee: process.env.FEE || '', 117 | endorseServiceCheckAddr: process.env.SERVICE_SIGN_ADDRESS || '', 118 | endorseServiceFeeAddr: process.env.SERVICE_FEE_ADDRESS || '' 119 | }, 120 | transfer: { 121 | server: process.env.ENDORSE_SERVER || '', 122 | fee: process.env.FEE || '', 123 | endorseServiceCheckAddr: process.env.SERVICE_SIGN_ADDRESS || '', 124 | endorseServiceFeeAddr: process.env.SERVICE_FEE_ADDRESS || '' 125 | } 126 | }, args) 127 | }); 128 | 129 | export default plugin; 130 | -------------------------------------------------------------------------------- /src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (plugins) 3 | */ 4 | 5 | import Endorsement from './endorsement'; 6 | 7 | export { 8 | Endorsement 9 | } 10 | -------------------------------------------------------------------------------- /src/proto/xendorser.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package pb; 3 | 4 | import "xuper.proto"; 5 | 6 | // 请求参数 7 | message EndorserRequest { 8 | Header header = 1; 9 | string RequestName = 2; // 请求名(类型) 10 | string BcName = 3; // 请求链名 11 | Transaction Fee = 4; // 带签名的交易费Tx 12 | bytes RequestData = 5; // Json打包的数据 13 | } 14 | message EndorserResponse { 15 | Header header = 1; 16 | string ResponseName = 2; 17 | string EndorserAddress = 3; // 背书服务地址 18 | SignatureInfo EndorserSign = 4; // 背书服务签名 19 | bytes ResponseData = 5; 20 | } 21 | 22 | service xendorser { 23 | rpc EndorserCall(EndorserRequest) returns (EndorserResponse) {} 24 | } 25 | -------------------------------------------------------------------------------- /src/requests.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Requests 3 | * Created by SmilingXinyi on 2020/6/2 4 | */ 5 | 6 | import {grpcClient, grpcEndorserClient, postRequest, isBrowser} from './utils'; 7 | 8 | let client: any = null; 9 | let endorsorClient: any = null; 10 | 11 | enum RESOURCE_TYPE { 12 | CPU, 13 | MEMORY, 14 | DISK, 15 | XFEE 16 | } 17 | 18 | export const initializationClient = (node: string) => { 19 | if (!isBrowser) { 20 | client = grpcClient(node); 21 | } 22 | }; 23 | 24 | export const initializationEndorseClient = (node: string) => { 25 | if (!isBrowser) { 26 | endorsorClient = grpcEndorserClient(node); 27 | } 28 | }; 29 | 30 | const gRPCPromise = (service: string, body: any) => { 31 | return new Promise((resolve, reject) => client[service](body, (err: Error, response: any) => { 32 | if (!err) { 33 | resolve(response); 34 | } else { 35 | reject(err); 36 | } 37 | })); 38 | }; 39 | 40 | const wrapperRequest = (request: {gRPCService: string; httpService: string; httpGetMethod?: boolean}, body: any) => { 41 | if (client) { 42 | return gRPCPromise(request.gRPCService, body); 43 | } 44 | else { 45 | return postRequest(request.httpService, body, request.httpGetMethod); 46 | } 47 | } 48 | 49 | export const getStatus = (node: string, body: any): Promise => { 50 | if (client) { 51 | return gRPCPromise('GetBlockChainStatus', body); 52 | } else { 53 | const target = `${node}/v1/get_bcstatus`; 54 | return postRequest(target, body); 55 | } 56 | }; 57 | 58 | export const getBalance = (node: string, body: any): Promise => { 59 | if (client) { 60 | return new Promise((resolve, reject) => 61 | client.GetBalance(body, (err: Error, response: any) => { 62 | if (!err) { 63 | resolve(response); 64 | } else { 65 | reject(err); 66 | } 67 | }) 68 | ); 69 | } else { 70 | const target = `${node}/v1/get_balance`; 71 | return postRequest(target, body); 72 | } 73 | }; 74 | 75 | export const getBalanceDetail = (node: string, body: any): Promise => { 76 | if (client) { 77 | return new Promise((resolve, reject) => 78 | client.GetBalanceDetail(body, (err: Error, response: any) => { 79 | if (!err) { 80 | resolve(response); 81 | } else { 82 | reject(err); 83 | } 84 | }) 85 | ); 86 | } else { 87 | const target = `${node}/v1/get_balance_detail`; 88 | return postRequest(target, body); 89 | } 90 | }; 91 | 92 | export const preExec = (node: string, body: any): Promise => { 93 | if (client) { 94 | return new Promise((resolve, reject) => 95 | client.PreExec(body, (err: Error, response: any) => { 96 | if (!err) { 97 | resolve(response); 98 | } else { 99 | reject(err); 100 | } 101 | }) 102 | ); 103 | } else { 104 | const target = `${node}/v1/preexec`; 105 | return postRequest(target, body); 106 | } 107 | }; 108 | 109 | export const preExecWithFee = async (node: string, body: any): Promise => { 110 | if (client) { 111 | return new Promise((resolve, reject) => 112 | client.PreExecWithSelectUTXO(body, (err: Error, response: any) => { 113 | if (!err) { 114 | if (response.response.requests) { 115 | response.response.requests = response.response.requests.map((request: any) => { 116 | if (request['resource_limits'] && request['resource_limits'].length > 0) { 117 | request['resource_limits'] = request['resource_limits'].map((info: any) => { 118 | const tempInfo = {}; 119 | 120 | 121 | // @ts-ignore 122 | tempInfo.type = info.type; 123 | if (info.limit && info.limit !== "0") { 124 | // @ts-ignore 125 | tempInfo.limit = parseInt(info.limit, 10); 126 | } 127 | return tempInfo; 128 | }); 129 | } 130 | 131 | return request; 132 | }); 133 | } 134 | 135 | resolve(response); 136 | } else { 137 | reject(err); 138 | } 139 | }) 140 | ); 141 | } else { 142 | const target = `${node}/v1/preexec_select_utxo`; 143 | const response = await postRequest(target, body); 144 | if (response.response.requests) { 145 | response.response.requests = response.response.requests.map((request: any) => { 146 | if (request['resource_limits'] && request['resource_limits'].length > 0) { 147 | request['resource_limits'] = request['resource_limits'].map((info: any) => { 148 | const tempInfo = {}; 149 | 150 | // @ts-ignore 151 | tempInfo.type = RESOURCE_TYPE[info.type]; 152 | 153 | if (info.limit && info.limit !== "0") { 154 | // @ts-ignore 155 | tempInfo.limit = parseInt(info.limit, 10); 156 | } 157 | return tempInfo; 158 | }); 159 | } 160 | 161 | return request; 162 | }); 163 | } 164 | return response; 165 | 166 | } 167 | }; 168 | 169 | export const endorser = (node: string, body: any): Promise => { 170 | if (endorsorClient) { 171 | return new Promise((resolve, reject) => 172 | endorsorClient.EndorserCall(body, (err: Error, response: any) => { 173 | if (!err) { 174 | resolve(response); 175 | } else { 176 | reject(err); 177 | } 178 | }) 179 | ); 180 | } else { 181 | const target = `${node}/v1/endorsercall`; 182 | return postRequest(target, body); 183 | } 184 | }; 185 | 186 | export const postTransaction = (node: string, body: any): Promise => { 187 | if (client) { 188 | return new Promise((resolve, reject) => 189 | client.PostTx(body, (err: Error, response: any) => { 190 | if (!err) { 191 | resolve(response); 192 | } else { 193 | reject(err); 194 | } 195 | }) 196 | ); 197 | } else { 198 | const target = `${node}/v1/post_tx`; 199 | return postRequest(target, body); 200 | } 201 | }; 202 | 203 | export const queryTransaction = (node: string, body: any): Promise => { 204 | if (client) { 205 | return new Promise((resolve, reject) => 206 | client.QueryTx(body, (err: Error, response: any) => { 207 | if (!err) { 208 | resolve(response); 209 | } else { 210 | reject(err); 211 | } 212 | }) 213 | ); 214 | } 215 | else { 216 | const target = `${node}/v1/query_tx`; 217 | return postRequest(target, body); 218 | } 219 | }; 220 | 221 | export const accountContractList = (node: string, body: any): Promise => { 222 | if (client) { 223 | return new Promise((resolve, reject) => 224 | client.GetAccountContracts(body, (err: Error, response: any) => { 225 | if (err) { 226 | reject(err); 227 | } 228 | else { 229 | resolve(response); 230 | } 231 | }) 232 | ); 233 | } 234 | else { 235 | const target = `${node}/v1/get_account_contracts`; 236 | return postRequest(target, body); 237 | } 238 | }; 239 | 240 | export const addressContractList = (node: string, body: any): Promise => { 241 | if (client) { 242 | return new Promise((resolve, reject) => 243 | client.GetAddressContracts(body, (err: Error, response: any) => { 244 | if (err) { 245 | reject(err); 246 | } 247 | else { 248 | resolve(response); 249 | } 250 | }) 251 | ); 252 | } 253 | else { 254 | const target = `${node}/v1/get_address_contracts`; 255 | return postRequest(target, body); 256 | } 257 | }; 258 | 259 | export const accountList = (node: string, body: any): Promise => { 260 | if (client) { 261 | return gRPCPromise('GetAccountByAK', body); 262 | } 263 | else { 264 | const target = `${node}/v1/get_account_by_ak`; 265 | return postRequest(target, body); 266 | } 267 | }; 268 | 269 | export const getBlockChains = (node: string, body: any): Promise => 270 | wrapperRequest({gRPCService: 'GetBlockChains', httpService: `${node}/v1/get_bcchains`, httpGetMethod: true}, body); 271 | 272 | export const queryACL = (node: string, body: any): Promise => 273 | wrapperRequest({gRPCService: 'QueryACL', httpService: `${node}/v1/query_acl`}, body); 274 | 275 | export const getBlock = (node: string, body: any): Promise => 276 | wrapperRequest({gRPCService: 'GetBlock', httpService: `${node}/v1/get_block`}, body); 277 | 278 | export const getBlockByHeight = (node: string, body: any): Promise => 279 | wrapperRequest({gRPCService: 'GetBlockByHeight', httpService: `${node}/v1/get_block_by_height`}, body); 280 | 281 | export const queryContractStatData = (node: string, body: any): Promise => 282 | wrapperRequest({gRPCService: 'QueryContractStatData', httpService: `${node}/v1/query_contract_stat_data`}, body); 283 | -------------------------------------------------------------------------------- /src/transaction.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Transaction 3 | * Created by SmilingXinyi on 2020/6/2 4 | */ 5 | 6 | import {ContractRequesttModel, TransactionInfomationModel, AuthModel, AccountModel, TXInput, TXOutput, UTXO, TransactionModel, Plugin} from './types'; 7 | import * as Requests from './requests'; 8 | import BN from 'bn.js'; 9 | import Errors from './error'; 10 | import {convert, getNonce, jsonEncode, publicOrPrivateKeyToString} from './utils'; 11 | import sha256 from 'sha256'; 12 | import {VERSION} from './constants'; 13 | import {ec as EC} from 'elliptic'; 14 | import stringify from 'json-stable-stringify'; 15 | 16 | export default class Transaction { 17 | 18 | private plugins: Plugin[]; 19 | 20 | constructor(plugins: Plugin[] = []) { 21 | this.plugins = plugins; 22 | } 23 | 24 | async preExec ( 25 | node: string, 26 | chain: string, 27 | address: string, 28 | authRequire: string[] = [], 29 | invokeRequests: ContractRequesttModel[] = [] 30 | ): Promise { 31 | const body = { 32 | initiator: address, 33 | bcname: chain, 34 | auth_require: authRequire, 35 | requests: invokeRequests 36 | }; 37 | 38 | return Requests.preExec(node, body); 39 | } 40 | 41 | async preExecWithUTXO( 42 | node: string, 43 | chain: string, 44 | address: string, 45 | sum: string | number | BN, 46 | authRequire: string[] = [], 47 | invokeRequests: ContractRequesttModel[] = [], 48 | account?: AccountModel 49 | ): Promise { 50 | const bnSum = new BN(sum); 51 | 52 | const data: any = { 53 | bcname: chain, 54 | address: address, 55 | totalAmount: bnSum.toNumber(), 56 | request: { 57 | initiator: address, 58 | bcname: chain, 59 | auth_require: authRequire, 60 | requests: invokeRequests 61 | } 62 | }; 63 | 64 | let body = { 65 | RequestName: 'PreExecWithFee', 66 | BcName: chain, 67 | RequestData: btoa(JSON.stringify(data)) 68 | }; 69 | 70 | if (this.plugins.length > 0 && this.plugins.findIndex(item => item.hookFuncs.indexOf('postTx') > -1) > -1) { 71 | for (const plugin of this.plugins) { 72 | if (plugin.func['postTx']) { 73 | body = await plugin.func['postTx'].call(this, plugin.args['postTx'], 74 | node, chain, body, account); 75 | } 76 | } 77 | } 78 | 79 | // return Requests.endorser(node, body); 80 | return Requests.preExecWithFee(node, data); 81 | } 82 | 83 | makeTxOutput( 84 | totalSelected: BN | string | number, 85 | totalNeed: BN | string | number, 86 | toAddress: string 87 | ): TXOutput { 88 | let bnUtxos; 89 | let bnNeed; 90 | 91 | try { 92 | bnUtxos = new BN(totalSelected); 93 | bnNeed = new BN(totalNeed); 94 | } catch (e) { 95 | throw Errors.PARAMETER_ERROR; 96 | } 97 | 98 | if (bnUtxos.gte(bnNeed)) { 99 | const delta = bnUtxos.sub(bnNeed); 100 | return { 101 | amount: btoa(delta.toArray().map(v => String.fromCharCode(v)).join('')), 102 | toAddr: btoa(toAddress) 103 | }; 104 | } 105 | throw Errors.UTXO_NOT_ENOUGH; 106 | } 107 | 108 | makeTxOutputs( 109 | amount: BN | string | number, 110 | fee?: BN | string | number, 111 | to?: string 112 | ): TXOutput[] { 113 | const bnAmount = new BN(amount); 114 | const bnFee = new BN(fee || 0); 115 | const accounts = []; 116 | 117 | to && accounts.push({ 118 | address: to, 119 | amount: bnAmount 120 | }); 121 | 122 | bnFee.gt(new BN(0)) && accounts.push({ 123 | address: '$', 124 | amount: bnFee 125 | }); 126 | 127 | return accounts.map(account => ({ 128 | amount: btoa(account.amount.toArray().map(v => String.fromCharCode(v)).join('')), 129 | toAddr: btoa(account.address) 130 | })); 131 | } 132 | 133 | makeTxInputs( 134 | utxos: UTXO[] 135 | ): TXInput[] { 136 | const txInputs: TXInput[] = []; 137 | utxos.forEach(utxo => txInputs.push({ 138 | refTxid: utxo.refTxid, 139 | refOffset: utxo.refOffset || 0, 140 | fromAddr: utxo.toAddr, 141 | amount: utxo.amount 142 | } as TXInput)); 143 | return txInputs; 144 | } 145 | 146 | encodeDataForDigestHash(tx: TransactionModel, include_signs: boolean) { 147 | const newtx: TransactionModel = JSON.parse(JSON.stringify(tx)); 148 | 149 | let str = ''; 150 | 151 | newtx.txInputs.forEach( 152 | (txInput: TXInput) => { 153 | if (txInput.refTxid) { 154 | str += jsonEncode(txInput.refTxid); 155 | } 156 | str += jsonEncode(txInput.refOffset || 0); 157 | if (txInput.fromAddr) { 158 | str += jsonEncode(txInput.fromAddr); 159 | } 160 | if (txInput.amount) { 161 | str += jsonEncode(txInput.amount); 162 | } 163 | str += jsonEncode(txInput.frozenHeight || 0); 164 | } 165 | ); 166 | 167 | str += jsonEncode(convert(newtx.txOutputs)); 168 | 169 | if (newtx.desc && newtx.desc.length > 0) { 170 | str += jsonEncode(newtx.desc); 171 | } 172 | 173 | str += jsonEncode(newtx.nonce); 174 | str += jsonEncode(newtx.timestamp); 175 | str += jsonEncode(newtx.version); 176 | 177 | if (newtx.txInputsExt && newtx.txInputsExt.length) { 178 | newtx.txInputsExt.forEach(inputExt => { 179 | str += jsonEncode(inputExt.bucket); 180 | if (inputExt.key) { 181 | str += jsonEncode(inputExt.key); 182 | } 183 | if (inputExt.ref_txid) { 184 | str += jsonEncode(inputExt.ref_txid); 185 | } 186 | if (inputExt.ref_offset) { 187 | str += jsonEncode(inputExt.ref_offset); 188 | } else { 189 | str += jsonEncode(0); 190 | } 191 | }); 192 | } 193 | 194 | if (newtx.txOutputsExt && newtx.txOutputsExt.length) { 195 | newtx.txOutputsExt.forEach(outputExt => { 196 | str += jsonEncode(outputExt.bucket); 197 | if (outputExt.key) { 198 | str += jsonEncode(outputExt.key); 199 | } 200 | if (outputExt.value) { 201 | str += jsonEncode(outputExt.value); 202 | } 203 | }); 204 | } 205 | 206 | if (newtx.contractRequests && newtx.contractRequests.length > 0) { 207 | // @ts-ignore 208 | newtx.contractRequests = newtx.contractRequests.map((req: any) => { 209 | if (req.args) { 210 | req.args = JSON.parse(stringify(req.args)); 211 | } 212 | return req; 213 | }); 214 | } 215 | 216 | // @ts-ignore 217 | str += jsonEncode(convert(newtx.contractRequests, ['jsonEncoded'])); 218 | 219 | str += jsonEncode(newtx.initiator); 220 | 221 | str += jsonEncode(newtx.authRequire && newtx.authRequire.length > 0 ? newtx.authRequire : null); 222 | 223 | if (include_signs) { 224 | str += jsonEncode(newtx.initiatorSigns); 225 | str += jsonEncode(newtx.authRequireSigns); 226 | } 227 | 228 | str += jsonEncode(newtx.coinbase); 229 | 230 | str += jsonEncode(newtx.autogen); 231 | 232 | const te = new TextEncoder(); 233 | const bytes = te.encode(str); 234 | 235 | return sha256.x2(Array.from(bytes), {asBytes: true}); 236 | } 237 | 238 | generateTransaction( 239 | account: AccountModel, 240 | preExecWithUtxos: any, 241 | authRequires: any, 242 | ti: TransactionInfomationModel 243 | ): TransactionModel { 244 | const {utxoOutput, response} = preExecWithUtxos; 245 | const {utxoList, totalSelected} = utxoOutput; 246 | 247 | const { 248 | amount, fee, to, desc 249 | } = ti; 250 | 251 | // inputs 252 | const txInputs = this.makeTxInputs(utxoList); 253 | 254 | // outputs 255 | const txOutputs = this.makeTxOutputs(amount, fee, to); 256 | 257 | let totalNeed = new BN(0); 258 | totalNeed = totalNeed.add(new BN(amount)); 259 | 260 | if (fee) 261 | totalNeed = totalNeed.add(new BN(fee)); 262 | 263 | 264 | txOutputs.push(this.makeTxOutput(totalSelected, totalNeed, account.address)); 265 | 266 | // desc 267 | const te = new TextEncoder(); 268 | const descBuff: Uint8Array = te.encode(desc); 269 | const descArr: string[] = []; 270 | descBuff.forEach(n => descArr.push(String.fromCharCode(n))); 271 | 272 | // transaction 273 | const tx = { 274 | version: VERSION, 275 | coinbase: false, 276 | autogen: false, 277 | timestamp: Date.now(), 278 | txInputs, 279 | txOutputs, 280 | initiator: account.address, 281 | authRequire: authRequires, 282 | nonce: getNonce() 283 | } as TransactionModel; 284 | 285 | if (descArr.length > 0) { 286 | tx.desc = btoa(descArr.join('')); 287 | } 288 | 289 | if (response) { 290 | // inputs ext 291 | if (response.inputs && response.inputs.length) { 292 | tx.txInputsExt = response.inputs; 293 | } 294 | 295 | // outputs ext 296 | if (response.outputs && response.outputs.length) { 297 | tx.txOutputsExt = response.outputs; 298 | } 299 | 300 | // contract request 301 | if (response.requests && response.requests.length) { 302 | tx.contractRequests = response.requests; 303 | } 304 | } 305 | 306 | const digestHash = this.encodeDataForDigestHash(tx, false); 307 | 308 | // sign 309 | const ec = new EC('p256'); 310 | const bnD = new BN(account.privateKey.D); 311 | const privKey = ec.keyFromPrivate(bnD.toArray()); 312 | const sign = privKey.sign(digestHash); 313 | const derbuf = sign.toDER().map((v: number) => String.fromCharCode(v)); 314 | const signatureInfos = []; 315 | const signatureInfo = { 316 | PublicKey: publicOrPrivateKeyToString(account.publicKey), 317 | Sign: btoa(derbuf.join('')) 318 | }; 319 | signatureInfos.push(signatureInfo); 320 | tx.initiatorSigns = signatureInfos; 321 | const digest = this.encodeDataForDigestHash(tx, true); 322 | 323 | // txid 324 | tx.txid = btoa(digest.map(v => String.fromCharCode(v)).join('')); 325 | 326 | return tx; 327 | } 328 | 329 | async post(node: string, chain: string, tx: any, account?: AccountModel): Promise { 330 | 331 | let body = { 332 | bcname: chain, 333 | status: 4, 334 | tx, 335 | txid: tx.txid 336 | }; 337 | 338 | if (this.plugins.length > 0 && this.plugins.findIndex(item => item.hookFuncs.indexOf('postTx') > -1) > -1) { 339 | for (const plugin of this.plugins) { 340 | if (plugin.func['postTx']) { 341 | body = await plugin.func['postTx'].call(this, plugin.args['postTx'], 342 | node, chain, body, account); 343 | } 344 | } 345 | } 346 | 347 | return Requests.postTransaction(node, body); 348 | } 349 | 350 | async makeTransaction( 351 | account: AccountModel, 352 | ti: TransactionInfomationModel, 353 | authRequires: { [propName: string]: AuthModel }, 354 | preExecWithUtxosObj: any 355 | ): Promise { 356 | const newPreExecWithUtxosObj = {...preExecWithUtxosObj}; 357 | 358 | let tx: TransactionModel; 359 | 360 | if (this.plugins.length > 0 && this.plugins.every(item => item.hookFuncs.indexOf('makeTransaction') > -1)) { 361 | for (const plugin of this.plugins) { 362 | tx = await plugin.func['makeTransaction'].call(this, plugin.args['makeTransaction'], account, ti, authRequires, preExecWithUtxosObj) 363 | } 364 | } 365 | else { 366 | tx = this.generateTransaction( 367 | account, 368 | newPreExecWithUtxosObj, 369 | Object.keys(authRequires), 370 | ti 371 | ); 372 | 373 | // @ts-ignore 374 | await Object.keys(authRequires).reduce(async (prov: any, cur: any): Promise => { 375 | const auth = authRequires[cur]; 376 | tx = await auth.sign(null, await tx); 377 | return tx; 378 | }, 0); 379 | } 380 | 381 | // @ts-ignore 382 | const res = convert(this.signTx(tx), ['jsonEncoded']); 383 | 384 | return res; 385 | } 386 | 387 | async queryTransaction(node: string, chain: string, txid: string): Promise { 388 | const body = { 389 | bcname: chain, 390 | txid 391 | }; 392 | 393 | return Requests.queryTransaction(node, body); 394 | } 395 | 396 | signTx(tx: TransactionModel): TransactionModel { 397 | const digest = this.encodeDataForDigestHash(tx, true); 398 | tx.txid = btoa(digest.map(v => String.fromCharCode(v)).join('')); 399 | return tx; 400 | } 401 | 402 | async getBlock(node: string, chain: string, blockid: string): Promise { 403 | const body = { 404 | bcname: chain, 405 | blockid, 406 | need_content: true 407 | } 408 | 409 | return Requests.getBlock(node, body); 410 | } 411 | 412 | async getBlockByHeight(node: string, chain: string, height: string): Promise { 413 | const body = { 414 | bcname: chain, 415 | height 416 | } 417 | 418 | return Requests.getBlockByHeight(node, body); 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Types 3 | * Created by SmilingXinyi on 2020/6/2 4 | */ 5 | 6 | /* ---------- Basic ---------- */ 7 | 8 | import BN from 'bn.js'; 9 | 10 | export type ErrorOptions = { 11 | name?: string; 12 | isFront?: boolean; 13 | stackIgnore?: [number, number]; 14 | } 15 | 16 | export type Plugin = { 17 | name: string; 18 | func: PluginFunc; 19 | hookFuncs: string[]; 20 | args: any; 21 | init: Function; 22 | } 23 | 24 | type PluginFunc = { 25 | [prop: string]: Function; 26 | } 27 | 28 | export type Options = { 29 | node: string; 30 | chain: string; 31 | plugins?: Plugin[]; 32 | env?: { 33 | node?: { 34 | disableGRPC?: boolean; 35 | }; 36 | }; 37 | } 38 | 39 | /* ---------- Account ---------- */ 40 | 41 | export type PublicKey = { 42 | X: string; 43 | Y: string; 44 | Curvname: string; 45 | } 46 | 47 | export type PrivateKey = { 48 | D: string; 49 | } & PublicKey 50 | 51 | export type AccountModel = { 52 | publicKey: PublicKey; 53 | privateKey: PrivateKey; 54 | mnemonic?: string; 55 | address: string; 56 | } 57 | 58 | /* ---------- Transaction ---------- */ 59 | 60 | export type TransactionInfomationModel = { 61 | to: string; 62 | amount: string | number | BN; 63 | fee?: string | number | BN; 64 | desc?: string; 65 | } 66 | 67 | export type ContractRequesttModel = { 68 | module_name: string; 69 | contract_name?: string; 70 | method_name: string; 71 | args: any; 72 | amount?: string; 73 | } 74 | 75 | export type AuthModel = { 76 | fee: string | number | BN; 77 | sign: Function; 78 | } 79 | 80 | /** 81 | * @hidden 82 | * Model Interface - transaction input 83 | */ 84 | export type TXInput = { 85 | refTxid: string; 86 | refOffset: number; 87 | fromAddr: string; 88 | amount: string; 89 | frozenHeight?: number; 90 | } 91 | 92 | /** 93 | * @hidden 94 | * Model Interface - transaction output 95 | */ 96 | export type TXOutput = { 97 | amount: string; 98 | toAddr: string; 99 | } 100 | 101 | /** 102 | * @hidden 103 | * Model Interface - UTXO 104 | */ 105 | export type UTXO = { 106 | amount: string; 107 | toAddr: string; 108 | refOffset: number; 109 | refTxid: string; 110 | } 111 | 112 | export type SignInfoModel = { 113 | PublicKey: string; 114 | Sign: string; 115 | } 116 | 117 | export type TransactionModel = { 118 | /** 119 | * SDK versTransactionModelion 120 | */ 121 | version: number; 122 | 123 | /** 124 | * Timestamp 125 | */ 126 | timestamp: number; 127 | 128 | coinbase: boolean; 129 | 130 | autogen: boolean; 131 | 132 | /** 133 | * Transaction description 134 | */ 135 | desc: string; 136 | 137 | /** 138 | * Inputs 139 | */ 140 | txInputs: TXInput[]; 141 | 142 | /** 143 | * Outputs 144 | */ 145 | txOutputs: any[]; 146 | 147 | /** 148 | * Transaction initiator 149 | */ 150 | initiator: string; 151 | 152 | /** 153 | * initiator sign 154 | */ 155 | initiatorSigns?: SignInfoModel[]; 156 | 157 | /** 158 | * Nonce 159 | */ 160 | nonce: string; 161 | 162 | /** 163 | * Auth 164 | */ 165 | authRequire: string[]; 166 | 167 | /** 168 | * Auth sign 169 | */ 170 | authRequireSigns?: any[]; 171 | 172 | /** 173 | * Transaction ID 174 | */ 175 | txid?: string; 176 | 177 | /** 178 | * Inputs 179 | */ 180 | txInputsExt?: any[]; 181 | 182 | /** 183 | * Outputs 184 | */ 185 | txOutputsExt?: any[]; 186 | 187 | /** 188 | * Contract requests 189 | */ 190 | contractRequests?: any[]; 191 | } 192 | 193 | /* ---------- Contract ---------- */ 194 | 195 | export type ContractInfo = { 196 | contarctName: string; 197 | contractMethod: string; 198 | } 199 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Utils 3 | * Created by SmilingXinyi on 2020/6/2 4 | */ 5 | 6 | import {PrivateKey, PublicKey} from './types'; 7 | 8 | export const isBrowser = 9 | typeof window !== 'undefined' && 10 | typeof window.document !== 'undefined'; 11 | 12 | if (!isBrowser) { 13 | // @ts-ignore 14 | global.btoa = (s: string): string => Buffer.from(s, 'binary').toString('base64'); 15 | 16 | // global.btoa = (s: string): Uint16Array => Buffer.from(s, 'utf8'); 17 | // @ts-ignore 18 | global.atob = (e: string): string => Buffer.from(e, 'base64').toString('binary'); 19 | 20 | // @ts-ignore 21 | global.fetch = require('node-fetch').default; 22 | 23 | // @ts-ignore 24 | const {TextEncoder, TextDecoder} = require('util'); 25 | // @ts-ignore 26 | global.TextEncoder = TextEncoder; 27 | // @ts-ignore 28 | global.TextDecoder = TextDecoder; 29 | } 30 | 31 | /** 32 | * Base58 - encode 33 | * @param data 34 | * @param alphabet 35 | */ 36 | export function base58Encode( 37 | data: Uint8Array, 38 | alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 39 | ): string { 40 | let str = ''; 41 | const d: any[] = []; 42 | let c: number; 43 | let j: any; 44 | let n; 45 | 46 | data.forEach((_, i) => { 47 | j = 0; 48 | c = data[i]; 49 | 50 | // @ts-ignore 51 | str += c || str.length ^ i ? '' : 1; 52 | 53 | while (j in d || c) { 54 | n = d[j]; 55 | n = n ? n * 256 + c : c; 56 | c = n / 58 | 0; 57 | d[j] = n % 58; 58 | j++; 59 | } 60 | }); 61 | 62 | while (j--) { 63 | str += alphabet[d[j]]; 64 | } 65 | 66 | return str; 67 | } 68 | 69 | /** 70 | * Base58 - decode 71 | * @param str 72 | * @param alphabet 73 | */ 74 | export function base58Decode( 75 | str: string, 76 | alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' 77 | ): Uint8Array { 78 | const d: any[] = []; 79 | const arr: number[] = []; 80 | let c: number; 81 | let i: any; 82 | let j: any; 83 | let n; 84 | 85 | // @ts-ignore 86 | for (i in str) { 87 | j = 0; 88 | c = alphabet.indexOf(str[i]); 89 | if (c < 0) { 90 | throw undefined; 91 | } 92 | c || arr.length ^ i ? i : arr.push(0); 93 | while (j in d || c) { 94 | n = d[j]; 95 | n = n ? n * 58 + c : c; 96 | c = n >> 8; 97 | d[j] = n % 256; 98 | j++; 99 | } 100 | } 101 | while (j--) { 102 | arr.push(d[j]); 103 | } 104 | return new Uint8Array(arr); 105 | } 106 | 107 | export function arrayPadStart(arr: any[], len: number): any[] { 108 | if (arr.length < len) { 109 | return new Array(len - arr.length).fill(0).concat(arr); 110 | } 111 | return arr; 112 | } 113 | 114 | export async function postRequest(t: string, b: any, simpleGet = false): Promise { 115 | let target = t; 116 | let options = {}; 117 | 118 | if (!(/^http(s?):\/\//gm.test(target))) { 119 | const protocol = typeof location !== 'undefined' ? location.protocol : 'http:'; 120 | target = `${protocol}//${t}`; 121 | } 122 | 123 | if (simpleGet) { 124 | method: 'GET' 125 | } 126 | else { 127 | options = { 128 | method: 'POST', 129 | body: JSON.stringify(b) 130 | } 131 | } 132 | 133 | return fetch(target, options).then( 134 | response => { 135 | if (!response.ok) { 136 | return response.json().then(res => { 137 | throw res; 138 | }).catch(err => { 139 | throw { 140 | error: err, 141 | response 142 | } 143 | }); 144 | } 145 | return response.json(); 146 | } 147 | ).catch(err => { 148 | throw err; 149 | }); 150 | } 151 | 152 | export function toHex(txid: string): string { 153 | return atob(txid).split('').map(s => s.charCodeAt(0).toString(16).padStart(2, '0')).join(''); 154 | } 155 | 156 | export function hexToBase64(txid: string): string { 157 | let tempId = txid; 158 | if (txid.length % 2 > 0) 159 | tempId = '0' + txid; 160 | 161 | const tempIdArr = tempId.split(''); 162 | // @ts-ignore 163 | const hexList = tempIdArr.reduce((prev, curr, i) => { 164 | if (!(i % 2)) { 165 | // @ts-ignore 166 | prev.push(parseInt(curr.toString() + tempIdArr[i + 1].toString(), 16)); 167 | } 168 | return prev; 169 | }, []); 170 | 171 | return btoa(hexList.map(num => String.fromCharCode(num)).join('')); 172 | } 173 | 174 | export const grpcClient = (node: string, PROTO_PATH = `${__dirname}/proto/xuper.proto`): any => { 175 | if (isBrowser) 176 | return null; 177 | 178 | const grpc = require('@grpc/grpc-js'); 179 | const protoLoader = require('@grpc/proto-loader'); 180 | 181 | const packageDefinition = protoLoader.loadSync( 182 | PROTO_PATH, 183 | { 184 | keepCase: true, 185 | longs: String, 186 | bytes: String 187 | } 188 | ); 189 | const xchainProto = grpc.loadPackageDefinition(packageDefinition).pb; 190 | return new xchainProto.Xchain(node, grpc.credentials.createInsecure(), { 191 | "grpc.max_receive_message_length": 64<<20 - 1, // 63M 192 | "grpc.max_send_message_length": 64<<20 - 1 193 | }); 194 | }; 195 | 196 | export const grpcEndorserClient = (node: string, PROTO_PATH = `${__dirname}/proto/xendorser.proto`): any => { 197 | if (isBrowser) 198 | return null; 199 | 200 | const grpc = require('@grpc/grpc-js'); 201 | const protoLoader = require('@grpc/proto-loader'); 202 | 203 | const packageDefinition = protoLoader.loadSync( 204 | PROTO_PATH, 205 | { 206 | keepCase: true, 207 | longs: String, 208 | bytes: String 209 | } 210 | ); 211 | const xendorserProto = grpc.loadPackageDefinition(packageDefinition).pb; 212 | return new xendorserProto.xendorser(node, grpc.credentials.createInsecure(), { 213 | "grpc.max_receive_message_length": 64<<20 - 1, // 63M 214 | "grpc.max_send_message_length": 64<<20 - 1 215 | }); 216 | }; 217 | 218 | export function deepEqual(x: any, y: any): boolean { 219 | if (x === y) { 220 | return true; 221 | } 222 | if (!(typeof x === 'object' && x !== null) 223 | || !(typeof y === 'object' && y !== null)) { 224 | return false; 225 | } 226 | if (Object.keys(x).length !== Object.keys(y).length) { 227 | return false; 228 | } 229 | for (const prop in x) { 230 | if (Object.prototype.hasOwnProperty.call(y, prop)) { 231 | if (!deepEqual(x[prop], y[prop])) { 232 | return false; 233 | } 234 | } else { 235 | return false; 236 | } 237 | } 238 | return true; 239 | } 240 | 241 | /** 242 | * Converting a public key or private key to a string 243 | * @param key 244 | */ 245 | export function publicOrPrivateKeyToString(key: PrivateKey | PublicKey): string { 246 | let str = `\{\"Curvname\":\"${key.Curvname}\",\"X\":${key.X},\"Y\":${key.Y}`; 247 | 248 | // @ts-ignore 249 | if (key.D) { 250 | // @ts-ignore 251 | str += `,\"D\":${key.D}`; 252 | } 253 | str += '\}'; 254 | 255 | return str; 256 | } 257 | 258 | /** 259 | * Converting a public key or private key to a string 260 | * @param key 261 | */ 262 | export function stringToPublicOrPrivateKey(keyStr: string): any { 263 | const replacer = ((_match: string, p1: string, p2: string, p3: string): string => { 264 | const data = { 265 | X: p1, 266 | Y: p2, 267 | D: p3 268 | }; 269 | 270 | return Object.keys(data).map(key => 271 | // @ts-ignore 272 | // eslint-disable-next-line implicit-arrow-linebreak 273 | `\"${key}\":\"${data[key]}\"`).join(','); 274 | }); 275 | 276 | return JSON.parse(keyStr.replace(/"X":(\d+),"Y":(\d+),"D":(\d+)/gi, replacer)); 277 | } 278 | 279 | /** 280 | * Nonce 281 | */ 282 | export function getNonce(): string { 283 | let rs = ''; 284 | if (isBrowser) { 285 | rs = crypto.getRandomValues(new Uint32Array(1))[0].toString(); 286 | } else { 287 | // eslint-disable-next-line @typescript-eslint/no-var-requires,global-require 288 | const crypto = require('crypto'); 289 | rs = crypto.randomFillSync(new Uint32Array(1))[0].toString(); 290 | } 291 | return (~~(Date.now() / 1000).toString()) + rs; 292 | } 293 | 294 | /** 295 | * Camel to underline 296 | * @param tar 297 | * @param exceptions 298 | */ 299 | export function convert(tar: any, exceptions: [] = []): any { 300 | let format: any = {}; 301 | 302 | if (tar instanceof Array) { 303 | const newTar = [...tar]; 304 | format = Object.assign([], newTar.map(v => convert(v, exceptions))); 305 | } else if (tar instanceof Object) { 306 | const newTar = {...tar}; 307 | Object.keys(newTar).forEach(key => { 308 | const value = newTar[key]; 309 | format[/^[a-z]/.test(key) && !(exceptions.length > 0 && exceptions.every(item => key === item)) ? key.replace(/([A-Z]{1})/g, '_$1').toLowerCase() : key] = convert(value, exceptions); 310 | }); 311 | } else { 312 | format = tar; 313 | } 314 | 315 | return format; 316 | } 317 | 318 | /** 319 | * JSON stringify 320 | * @param t 321 | */ 322 | export function jsonEncode(t: any): string { 323 | if (typeof t === 'undefined') return `${JSON.stringify(null)}\n`; 324 | return `${JSON.stringify(t)}\n`; 325 | } 326 | -------------------------------------------------------------------------------- /test/account.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file (account.test) 3 | * Created by SmilingXinyi on 2020/7/2 4 | */ 5 | 6 | import XuperSDK, {Cryptography, Language} from '../src'; 7 | import {AccountModel} from '../src/types'; 8 | import {isBrowser} from '../src/utils'; 9 | 10 | isBrowser && require('whatwg-fetch'); 11 | 12 | const 13 | node = process.env.HOST!, 14 | chain = process.env.CHAIN!, 15 | mnemonic = process.env.TEST_MNEMONIC!, 16 | publicKeyString = process.env.PUBLIC_KEY_STRING!, 17 | address = process.env.TEST_ADDRESS!; 18 | 19 | describe('Xuper SDK account', () => { 20 | test('create an account with mnemonic should return account model', () => { 21 | const xsdk = new XuperSDK({ 22 | node, 23 | chain 24 | }); 25 | 26 | const account: AccountModel = xsdk.create(); 27 | 28 | expect(account).toHaveProperty('mnemonic'); 29 | expect(account).toHaveProperty('privateKey'); 30 | expect(account).toHaveProperty('publicKey'); 31 | expect(account).toHaveProperty('address'); 32 | }); 33 | 34 | test('retrieve the account from mnemonic should return account model', () => { 35 | const xsdk = new XuperSDK({ 36 | node, 37 | chain 38 | }); 39 | 40 | const account: AccountModel = xsdk.retrieve( 41 | mnemonic, 42 | Language.SimplifiedChinese, 43 | Cryptography.EccFIPS 44 | ); 45 | 46 | expect(account).toHaveProperty('mnemonic'); 47 | expect(account).toHaveProperty('privateKey'); 48 | expect(account).toHaveProperty('publicKey'); 49 | expect(account).toHaveProperty('address'); 50 | expect(account.address).toBe(address); 51 | }); 52 | 53 | test('retrieve the account from private key return accout model', () => { 54 | const xsdk = new XuperSDK({ 55 | node, 56 | chain 57 | }); 58 | 59 | const account = xsdk.retrieve( 60 | mnemonic, 61 | Language.SimplifiedChinese, 62 | Cryptography.EccFIPS, 63 | true 64 | ); 65 | 66 | const encryptPrivateKeyString = xsdk.export('123456'); 67 | const retrievedAccount = xsdk.import('123456', encryptPrivateKeyString); 68 | 69 | expect(retrievedAccount.address).toEqual(account.address); 70 | expect(retrievedAccount.privateKey.Curvname).toEqual(account.privateKey.Curvname); 71 | expect(retrievedAccount.privateKey.X).toEqual(account.privateKey.X); 72 | expect(retrievedAccount.privateKey.Y).toEqual(account.privateKey.Y); 73 | }); 74 | 75 | test('get xuper account public key string', () => { 76 | const xsdk = new XuperSDK({chain, node}); 77 | 78 | xsdk.retrieve( 79 | mnemonic, 80 | Language.SimplifiedChinese, 81 | Cryptography.EccFIPS, 82 | true 83 | ); 84 | 85 | expect(xsdk.publicKey()).toEqual(publicKeyString); 86 | }) 87 | 88 | test('check mnemonic', () => { 89 | const xsdk = new XuperSDK({ 90 | node, 91 | chain 92 | }); 93 | 94 | const result = xsdk.checkMnemonic( 95 | mnemonic, 96 | Language.SimplifiedChinese 97 | ); 98 | 99 | expect(result).toBeTruthy(); 100 | }); 101 | 102 | test('check address', () => { 103 | const xsdk = new XuperSDK({ 104 | node, 105 | chain 106 | }); 107 | 108 | const result = xsdk.checkAddress( 109 | address 110 | ); 111 | 112 | expect(result).toBeTruthy(); 113 | }); 114 | }); 115 | -------------------------------------------------------------------------------- /test/cert/cfca.cert: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /test/contract.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(100000000); 2 | 3 | import XuperSDK, {Cryptography, Language} from '../src'; 4 | import {isBrowser} from '../src/utils'; 5 | 6 | isBrowser && require('whatwg-fetch'); 7 | 8 | const 9 | node = process.env.SERVER!, 10 | chain = process.env.CHAIN!, 11 | mnemonic = process.env.TEST_MNEMONIC!, 12 | address = process.env.TEST_ADDRESS!; 13 | 14 | describe('Xuper SDK Contract:', () => { 15 | 16 | const num = 2222222222222222; 17 | const contractAccount = `XC${num}@xuper`; 18 | const wasmName = 'counter666'; 19 | const solidityName = 'counter888'; 20 | 21 | test('create an new contact account', async () => { 22 | const xsdk = new XuperSDK({ 23 | node, 24 | chain 25 | }); 26 | 27 | xsdk.retrieve( 28 | mnemonic, 29 | Language.SimplifiedChinese, 30 | Cryptography.EccFIPS, 31 | true 32 | ); 33 | 34 | try { 35 | const demo = await xsdk.createContractAccount(num); 36 | await xsdk.postTransaction(demo.transaction); 37 | } 38 | catch (err) {} 39 | 40 | const execStruct = await xsdk.createContractAccount( 41 | parseInt('1234567890' + (~~(Math.random() * (999999 - 100000) + 100000).toString())) 42 | ); 43 | const result = await xsdk.postTransaction(execStruct.transaction); 44 | expect(result.header).toHaveProperty('logid'); 45 | expect(result.header).not.toHaveProperty('error'); 46 | }); 47 | 48 | test('get the address contract accounts should return contarct account list', async () => { 49 | const xsdk = new XuperSDK({ 50 | node, 51 | chain 52 | }); 53 | 54 | xsdk.retrieve( 55 | mnemonic, 56 | Language.SimplifiedChinese, 57 | Cryptography.EccFIPS, 58 | true 59 | ); 60 | 61 | const result = await xsdk.getContractAccounts(); 62 | expect(result.header).toHaveProperty('logid'); 63 | expect(result).toHaveProperty('bcname'); 64 | expect(result).toHaveProperty('account'); 65 | }); 66 | 67 | test('get the contract list of current account should return contracts', async () => { 68 | const xsdk = new XuperSDK({ 69 | node, 70 | chain 71 | }); 72 | 73 | xsdk.retrieve( 74 | mnemonic, 75 | Language.SimplifiedChinese, 76 | Cryptography.EccFIPS, 77 | true 78 | ); 79 | 80 | const result = await xsdk.getContracts(address); 81 | expect(result.header).toHaveProperty('logid'); 82 | expect(result.header).not.toHaveProperty('error'); 83 | expect(result).toHaveProperty('contracts'); 84 | }); 85 | 86 | test('deploy native contract', async () => { 87 | console.log("node:",node) 88 | const xsdk = new XuperSDK({ 89 | node, 90 | chain 91 | }); 92 | 93 | xsdk.retrieve( 94 | mnemonic, 95 | Language.SimplifiedChinese, 96 | Cryptography.EccFIPS, 97 | true 98 | ); 99 | 100 | const codeBuf: string[] = []; 101 | 102 | if (isBrowser) { 103 | // @ts-ignore 104 | window.file.forEach(n => codeBuf.push(String.fromCharCode(n))); 105 | } else { 106 | const fs = require('fs'); 107 | const native = Uint8Array.from(fs.readFileSync(`${__dirname}/native/counter-native`)); 108 | native.forEach(n => codeBuf.push(String.fromCharCode(n))); 109 | } 110 | 111 | // const contractName = `counter${~~(Math.random() * 10 ** 3 - 10 ** 3) + 10 ** 3}`; 112 | const contractName = 'counter123'; 113 | console.log("合约名字:",contractName) 114 | console.log("合约账户:",contractAccount) 115 | 116 | try { 117 | console.log("预执行") 118 | const {transaction, preExecutionTransaction} = await xsdk.deployNativeContract( 119 | contractAccount, 120 | contractName, 121 | codeBuf.join(''), 122 | 'go', { 123 | creator: address 124 | } 125 | ); 126 | console.log("预执行结束") 127 | 128 | console.log("preExecutionTransaction",preExecutionTransaction) 129 | 130 | // expect(transaction).toBeTruthy(); 131 | // expect(preExecutionTransaction).toBeTruthy(); 132 | console.log("POST Tx") 133 | const result = await xsdk.postTransaction(transaction); 134 | console.log(result) 135 | } 136 | catch (err) { 137 | console.log(err) 138 | } 139 | 140 | }); 141 | 142 | test('upgrade native contract', async () => { 143 | const xsdk = new XuperSDK({ 144 | node, 145 | chain 146 | }); 147 | 148 | xsdk.retrieve( 149 | mnemonic, 150 | Language.SimplifiedChinese, 151 | Cryptography.EccFIPS, 152 | true 153 | ); 154 | 155 | const codeBuf: string[] = []; 156 | 157 | if (isBrowser) { 158 | // @ts-ignore 159 | window.file.forEach(n => codeBuf.push(String.fromCharCode(n))); 160 | } else { 161 | const fs = require('fs'); 162 | const native = Uint8Array.from(fs.readFileSync(`${__dirname}/native/counter-native-v2`)); 163 | native.forEach(n => codeBuf.push(String.fromCharCode(n))); 164 | } 165 | 166 | // const contractName = `counter${~~(Math.random() * 10 ** 3 - 10 ** 3) + 10 ** 3}`; 167 | const contractName = 'counter123'; 168 | console.log("合约名字:",contractName) 169 | console.log("合约账户:",contractAccount) 170 | 171 | try { 172 | const {transaction, preExecutionTransaction} = await xsdk.deployNativeContract( 173 | contractAccount, 174 | contractName, 175 | codeBuf.join(''), 176 | 'go', { 177 | creator: address 178 | }, 179 | true 180 | ); 181 | 182 | console.log("preExecutionTransaction",preExecutionTransaction) 183 | 184 | // expect(transaction).toBeTruthy(); 185 | // expect(preExecutionTransaction).toBeTruthy(); 186 | const result = await xsdk.postTransaction(transaction); 187 | console.log(result) 188 | } 189 | catch (err) { 190 | console.log(err) 191 | } 192 | }); 193 | 194 | test('invoke native contract', async () => { 195 | const xsdk = new XuperSDK({ 196 | node, 197 | chain 198 | }); 199 | 200 | xsdk.retrieve( 201 | mnemonic, 202 | Language.SimplifiedChinese, 203 | Cryptography.EccFIPS, 204 | true 205 | ); 206 | 207 | // const codeBuf: string[] = []; 208 | 209 | // if (isBrowser) { 210 | // // @ts-ignore 211 | // window.file.forEach(n => codeBuf.push(String.fromCharCode(n))); 212 | // } else { 213 | // const fs = require('fs'); 214 | // const native = Uint8Array.from(fs.readFileSync(`${__dirname}/native/counter-native-v2`)); 215 | // native.forEach(n => codeBuf.push(String.fromCharCode(n))); 216 | // } 217 | 218 | // const contractName = `counter${~~(Math.random() * 10 ** 3 - 10 ** 3) + 10 ** 3}`; 219 | const contractName = 'counter123'; 220 | console.log("合约名字:",contractName) 221 | console.log("合约账户:",contractAccount) 222 | 223 | try { 224 | const {transaction,preExecutionTransaction} = await xsdk.invokeContarct(contractName, 'increase', 'native', {key: 'a'}); 225 | console.log(preExecutionTransaction) 226 | 227 | expect(transaction).toBeTruthy(); 228 | expect(preExecutionTransaction).toBeTruthy(); 229 | 230 | const result = await xsdk.postTransaction(transaction); 231 | console.log(result) 232 | } 233 | catch (err) { 234 | console.log(err) 235 | } 236 | }); 237 | 238 | test('deploy new wasm contract should return transaction info and result of post', async () => { 239 | const xsdk = new XuperSDK({ 240 | node, 241 | chain 242 | }); 243 | 244 | xsdk.retrieve( 245 | mnemonic, 246 | Language.SimplifiedChinese, 247 | Cryptography.EccFIPS, 248 | true 249 | ); 250 | 251 | const codeBuf: string[] = []; 252 | 253 | if (isBrowser) { 254 | // @ts-ignore 255 | window.file.forEach(n => codeBuf.push(String.fromCharCode(n))); 256 | } else { 257 | const fs = require('fs'); 258 | const wasm = Uint8Array.from(fs.readFileSync(`${__dirname}/wasm/counter.wasm`)); 259 | wasm.forEach(n => codeBuf.push(String.fromCharCode(n))); 260 | } 261 | 262 | const contractName = `counter${~~(Math.random() * 10 ** 3 - 10 ** 3) + 10 ** 3}`; 263 | 264 | try { 265 | const demo = await xsdk.deployWasmContract( 266 | contractAccount, 267 | wasmName, 268 | codeBuf.join(''), 269 | 'c', { 270 | creator: address 271 | } 272 | ); 273 | 274 | await xsdk.postTransaction(demo.transaction); 275 | } 276 | catch (err) {} 277 | 278 | const {transaction, preExecutionTransaction} = await xsdk.deployWasmContract( 279 | contractAccount, 280 | contractName, 281 | codeBuf.join(''), 282 | 'c', { 283 | creator: address 284 | } 285 | ); 286 | 287 | expect(transaction).toBeTruthy(); 288 | expect(preExecutionTransaction).toBeTruthy(); 289 | 290 | const result = await xsdk.postTransaction(transaction); 291 | 292 | expect(result.header).toHaveProperty('logid'); 293 | expect(result.header).not.toHaveProperty('error'); 294 | }); 295 | 296 | test('invoke contract', async () => { 297 | const xsdk = new XuperSDK({ 298 | node, 299 | chain 300 | }); 301 | 302 | xsdk.retrieve( 303 | mnemonic, 304 | Language.SimplifiedChinese, 305 | Cryptography.EccFIPS, 306 | true 307 | ); 308 | 309 | const { 310 | transaction, 311 | preExecutionTransaction 312 | } = 313 | // await xsdk.invokeContarct('counter1000', 'increase', 'wasm', { 314 | await xsdk.invokeContarct(wasmName, 'increase', 'wasm', { 315 | key: '11' 316 | }); 317 | 318 | expect(transaction).toBeTruthy(); 319 | expect(preExecutionTransaction).toBeTruthy(); 320 | 321 | const result = await xsdk.postTransaction(transaction); 322 | 323 | expect(result.header).toHaveProperty('logid'); 324 | expect(result.header).not.toHaveProperty('error'); 325 | }); 326 | 327 | test('deploy a solidity contract', async () => { 328 | const xsdk = new XuperSDK({ 329 | node, 330 | chain 331 | }); 332 | 333 | xsdk.retrieve(mnemonic, Language.SimplifiedChinese, Cryptography.EccFIPS, true); 334 | 335 | const bin = '608060405234801561001057600080fd5b5060405161016c38038061016c8339818101604052602081101561003357600080fd5b810190808051906020019092919050505080600081905550506101118061005b6000396000f3fe60806040526004361060305760003560e01c80632e64cec11460355780636057361d14605d5780638995db74146094575b600080fd5b348015604057600080fd5b50604760bf565b6040518082815260200191505060405180910390f35b348015606857600080fd5b50609260048036036020811015607d57600080fd5b810190808035906020019092919050505060c8565b005b60bd6004803603602081101560a857600080fd5b810190808035906020019092919050505060d2565b005b60008054905090565b8060008190555050565b806000819055505056fea265627a7a723158209500c3e12321b837819442c0bc1daa92a4f4377fc7b59c41dbf9c7620b2f961064736f6c63430005110032'; 336 | const abi = '[{"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"constant":false,"inputs":[],"name":"retrieve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"store","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"storepay","outputs":[],"payable":true,"stateMutability":"payable","type":"function"}]'; 337 | 338 | const contractName = `counter${~~(Math.random() * 10 ** 3 - 10 ** 3) + 10 ** 3}`; 339 | 340 | try { 341 | const demo = await xsdk.deploySolidityContract( 342 | contractAccount, 343 | solidityName, 344 | bin, 345 | abi, 346 | 'evm', { 347 | num: '1' 348 | } 349 | ); 350 | 351 | await xsdk.postTransaction(demo.transaction); 352 | } 353 | catch (err) {} 354 | 355 | const {preExecutionTransaction, transaction} = await xsdk.deploySolidityContract( 356 | contractAccount, 357 | contractName, 358 | bin, 359 | abi, 360 | 'evm', { 361 | num: '1' 362 | } 363 | ); 364 | 365 | expect(preExecutionTransaction).toBeTruthy() 366 | expect(transaction).toBeTruthy(); 367 | 368 | const result = await xsdk.postTransaction(transaction); 369 | expect(result.header).toHaveProperty('logid'); 370 | expect(result.header).not.toHaveProperty('error'); 371 | }) 372 | 373 | test('invoke Solidity contract', async () => { 374 | const xsdk = new XuperSDK({ 375 | node, 376 | chain 377 | }); 378 | 379 | xsdk.retrieve( 380 | mnemonic, 381 | Language.SimplifiedChinese, 382 | Cryptography.EccFIPS, 383 | true 384 | ); 385 | 386 | const {transaction, preExecutionTransaction} = 387 | await xsdk.invokeSolidityContarct(solidityName, 'store', 'evm', {num: '2'}); 388 | 389 | expect(preExecutionTransaction).toBeTruthy() 390 | expect(transaction).toBeTruthy(); 391 | 392 | const result = await xsdk.postTransaction(transaction); 393 | 394 | expect(result.header).toHaveProperty('logid'); 395 | expect(result.header).not.toHaveProperty('error'); 396 | }); 397 | 398 | test('query contract info', async () => { 399 | const xsdk = new XuperSDK({ 400 | node, 401 | chain 402 | }); 403 | 404 | xsdk.retrieve( 405 | mnemonic, 406 | Language.SimplifiedChinese, 407 | Cryptography.EccFIPS, 408 | true 409 | ); 410 | 411 | const methodName = 'increase'; 412 | 413 | const result = await xsdk.queryACL( 414 | contractAccount, 415 | { 416 | contarctName: wasmName, 417 | contractMethod: methodName 418 | } 419 | ) 420 | 421 | expect(result).toHaveProperty('header'); 422 | expect(result.header).not.toHaveProperty('error'); 423 | expect(result).toHaveProperty('acl'); 424 | }); 425 | 426 | test('query contract account stat', async () => { 427 | const xsdk = new XuperSDK({ 428 | node, 429 | chain 430 | }); 431 | 432 | const result = await xsdk.queryContractStatData(); 433 | 434 | expect(result).toHaveProperty('header'); 435 | expect(result.header).not.toHaveProperty('error'); 436 | expect(result).toHaveProperty('data'); 437 | }); 438 | 439 | // Todo: Upgrade 440 | 441 | 442 | }); 443 | -------------------------------------------------------------------------------- /test/jest/custom-test-env.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test file - account sdk 3 | * Created by xinyi on 2019/11/27 4 | */ 5 | 6 | require('dotenv').config(); 7 | const fs = require('fs'); 8 | const NodeEnvironment = require('jest-environment-jsdom-fourteen'); 9 | 10 | class CustomEnvironment extends NodeEnvironment { 11 | constructor(config, context) { 12 | super(config, context); 13 | this.testPath = context.testPath; 14 | this.docblockPragmas = context.docblockPragmas; 15 | this.global.process.title = 'Xuper SDK - Test ENV'; 16 | 17 | // TextEncoder & TextDecoder 18 | const {TextEncoder, TextDecoder} = require('util'); 19 | this.global.TextEncoder = TextEncoder; 20 | this.global.TextDecoder = TextDecoder; 21 | 22 | // crypto module 23 | const nodeCrypto = require('crypto'); 24 | this.global.crypto = { 25 | getRandomValues(buffer) { 26 | return nodeCrypto.randomFillSync(buffer); 27 | } 28 | }; 29 | 30 | this.global.cert = Uint8Array.from(fs.readFileSync(`${__dirname}/../cert/cfca.cert`)); 31 | 32 | // contract release 33 | this.global.file = Uint8Array.from(fs.readFileSync(`${__dirname}/../wasm/counter.wasm`)); 34 | } 35 | } 36 | 37 | module.exports = CustomEnvironment; 38 | -------------------------------------------------------------------------------- /test/native/counter-native: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuperchain/xuper-sdk-js/495d8b61f5ef9f7b2bd3f0826e017ef0b9ec3e62/test/native/counter-native -------------------------------------------------------------------------------- /test/native/counter-native-v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuperchain/xuper-sdk-js/495d8b61f5ef9f7b2bd3f0826e017ef0b9ec3e62/test/native/counter-native-v2 -------------------------------------------------------------------------------- /test/plugins.test.ts: -------------------------------------------------------------------------------- 1 | jest.setTimeout(1000000000); 2 | 3 | import XuperSDK from '../src'; 4 | import {isBrowser} from '../src/utils'; 5 | import {Cryptography, Language} from '../src/constants'; 6 | 7 | import EndorsementPlugin from '../src/plugins/endorsement'; 8 | 9 | isBrowser && require('whatwg-fetch'); 10 | 11 | const 12 | node = process.env.SERVER!, 13 | chain = process.env.CHAIN!, 14 | mnemonic = process.env.TEST_MNEMONIC!, 15 | address = process.env.TEST_ADDRESS; 16 | 17 | describe.skip('Xuper SDK ——', () => { 18 | test('make transfer with endorsement', async () => { 19 | const params = { 20 | server: process.env.ENDORSE_SERVER, 21 | fee: process.env.FEE, 22 | endorseServiceCheckAddr: process.env.SERVICE_SIGN_ADDRESS, 23 | endorseServiceFeeAddr: process.env.SERVICE_FEE_ADDRESS 24 | }; 25 | 26 | const plugins = [ 27 | EndorsementPlugin({ 28 | transfer: params, 29 | makeTransaction: params 30 | }) 31 | ]; 32 | 33 | const xsdk = new XuperSDK({ 34 | node, 35 | chain, 36 | plugins 37 | }); 38 | 39 | xsdk.retrieve(mnemonic, Language.SimplifiedChinese, Cryptography.EccFIPS, true); 40 | 41 | const tx = await xsdk.transfer({ 42 | to: process.env.TEST_TARGET_ADDRESS || '', 43 | amount: '100', 44 | fee: '400' 45 | }); 46 | 47 | expect(tx).toHaveProperty('txid'); 48 | expect(tx).toHaveProperty('initiator_signs'); 49 | expect(tx).toHaveProperty('initiator', address); 50 | expect(tx).toHaveProperty('coinbase', false); 51 | expect(tx).toHaveProperty('autogen', false); 52 | expect(tx).toHaveProperty('version', 1); 53 | expect(tx).toHaveProperty('tx_inputs'); 54 | expect(tx).toHaveProperty('tx_outputs'); 55 | 56 | const ressult = await xsdk.postTransaction(tx); 57 | expect(ressult).toHaveProperty('header'); 58 | expect(ressult.header).not.toHaveProperty('error'); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /test/transaction.test.ts: -------------------------------------------------------------------------------- 1 | import XuperSDK from '../src'; 2 | import {TransactionModel} from '../src/types'; 3 | import {isBrowser, toHex, hexToBase64} from '../src/utils'; 4 | import {Cryptography, Language} from '../src/constants'; 5 | 6 | isBrowser && require('whatwg-fetch'); 7 | 8 | const 9 | node = process.env.SERVER!, 10 | chain = process.env.CHAIN!, 11 | mnemonic = process.env.TEST_MNEMONIC!, 12 | address = process.env.TEST_ADDRESS; 13 | 14 | describe('Xuper transcation', () => { 15 | 16 | let tempTx: TransactionModel; 17 | 18 | test('make tracsfer and post', async () => { 19 | const xsdk = new XuperSDK({node, chain}); 20 | 21 | xsdk.retrieve(mnemonic, Language.SimplifiedChinese, Cryptography.EccFIPS, true); 22 | 23 | const tx = await xsdk.transfer({ 24 | to: process.env.TEST_TARGET_ADDRESS || '', 25 | amount: '100', 26 | fee: '100' 27 | }); 28 | 29 | expect(tx).toHaveProperty('txid'); 30 | expect(tx).toHaveProperty('initiator_signs'); 31 | expect(tx).toHaveProperty('initiator', address); 32 | expect(tx).toHaveProperty('coinbase', false); 33 | expect(tx).toHaveProperty('autogen', false); 34 | expect(tx).toHaveProperty('version', 1); 35 | expect(tx).toHaveProperty('tx_inputs'); 36 | expect(tx).toHaveProperty('tx_outputs'); 37 | expect(tx).toHaveProperty('auth_require', []); 38 | 39 | const result = await xsdk.postTransaction(tx); 40 | 41 | expect(result).toHaveProperty('header'); 42 | expect(result.header).not.toHaveProperty('error'); 43 | 44 | tempTx = tx; 45 | }); 46 | 47 | test('query transaction', async () => { 48 | const xsdk = new XuperSDK({node, chain}); 49 | 50 | if (tempTx) { 51 | const result = await xsdk.queryTransaction(tempTx.txid!); 52 | 53 | expect(result).toHaveProperty('header'); 54 | expect(result.header).not.toHaveProperty('error'); 55 | 56 | expect(result.tx).toHaveProperty('txid'); 57 | expect(result.tx).toHaveProperty('initiator_signs'); 58 | expect(result.tx).toHaveProperty('initiator', address); 59 | expect(result.tx).toHaveProperty('version', 1); 60 | expect(result.tx).toHaveProperty('tx_inputs'); 61 | expect(result.tx).toHaveProperty('tx_outputs'); 62 | 63 | } 64 | else { 65 | xsdk.retrieve(mnemonic, Language.SimplifiedChinese, Cryptography.EccFIPS, true); 66 | 67 | const tx = await xsdk.transfer({ 68 | to: process.env.TEST_TARGET_ADDRESS || '', 69 | amount: '100', 70 | fee: '100' 71 | }); 72 | 73 | await xsdk.postTransaction(tx); 74 | 75 | const result = await xsdk.queryTransaction(tx.txid!); 76 | 77 | expect(result).toHaveProperty('header'); 78 | expect(result.header).not.toHaveProperty('error'); 79 | 80 | expect(result.tx).toHaveProperty('txid'); 81 | expect(result.tx).toHaveProperty('initiator_signs'); 82 | expect(result.tx).toHaveProperty('initiator', address); 83 | expect(result.tx).toHaveProperty('version', 1); 84 | expect(result.tx).toHaveProperty('tx_inputs'); 85 | expect(result.tx).toHaveProperty('tx_outputs'); 86 | } 87 | }); 88 | 89 | describe('Get block', () => { 90 | 91 | let tempBlockid: string; 92 | 93 | const blockHeight = '1'; 94 | 95 | test('by height', async () => { 96 | const xsdk = new XuperSDK({ 97 | node, 98 | chain 99 | }); 100 | 101 | const result = await xsdk.getBlockByHeight(blockHeight); 102 | expect(result).toHaveProperty('header'); 103 | expect(result.bcname).toBe(chain); 104 | expect(result).toHaveProperty('block'); 105 | expect(result.block.height).toBe(blockHeight); 106 | 107 | tempBlockid = result.block.blockid; 108 | }); 109 | 110 | test('by blockid', async () => { 111 | const xsdk = new XuperSDK({ 112 | node, 113 | chain 114 | }); 115 | 116 | const blockid = toHex(tempBlockid); 117 | 118 | const result = await xsdk.getBlockById(blockid); 119 | expect(result).toHaveProperty('header'); 120 | expect(result.bcname).toBe(chain); 121 | expect(result).toHaveProperty('block'); 122 | expect(result.block.blockid).toBe(hexToBase64(blockid)); 123 | }); 124 | }) 125 | }); 126 | -------------------------------------------------------------------------------- /test/wasm/counter.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuperchain/xuper-sdk-js/495d8b61f5ef9f7b2bd3f0826e017ef0b9ec3e62/test/wasm/counter.wasm -------------------------------------------------------------------------------- /test/xuper.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @file Xuper SDK test 3 | * Created by SmilingXinyi on 2020/6/2 4 | */ 5 | 6 | import XuperSDK, {Cryptography, Language} from '../src'; 7 | import {isBrowser} from '../src/utils'; 8 | 9 | isBrowser && require('whatwg-fetch'); 10 | 11 | const 12 | node = process.env.SERVER!, 13 | chain = process.env.CHAIN!, 14 | mnemonic = process.env.TEST_MNEMONIC!, 15 | address = process.env.TEST_ADDRESS!; 16 | 17 | describe('Xuper SDK', () => { 18 | test('get blockchains', async () => { 19 | const xsdk = new XuperSDK({node, chain}); 20 | const result = await xsdk.getBlockChains(); 21 | expect(result).toHaveProperty('header'); 22 | expect(result.header).not.toHaveProperty('error'); 23 | expect(result).toHaveProperty('blockchains'); 24 | }); 25 | 26 | test('check status of current chain', async () => { 27 | const xsdk = new XuperSDK({node, chain}); 28 | const status = await xsdk.checkStatus(); 29 | expect(status).toBeTruthy(); 30 | expect(status).toHaveProperty('meta'); 31 | expect(status).toHaveProperty('block'); 32 | expect(status).toHaveProperty('utxoMeta'); 33 | }); 34 | 35 | test('convert txid to hex string', () => { 36 | const xsdk = new XuperSDK({node, chain}); 37 | const hexTxid = xsdk.transactionIdToHex('95z7TJlLI6cFHGL+rQp307LDwPlltErTxrxuN9gaJdI='); 38 | expect(hexTxid).toEqual('f79cfb4c994b23a7051c62fead0a77d3b2c3c0f965b44ad3c6bc6e37d81a25d2'); 39 | }); 40 | 41 | test('get balance', async () => { 42 | const xsdk = new XuperSDK({node, chain}); 43 | xsdk.retrieve(mnemonic, Language.SimplifiedChinese, Cryptography.EccFIPS, true); 44 | const result = await xsdk.getBalance(); 45 | expect(result).toHaveProperty('header'); 46 | expect(result).toHaveProperty('address', address); 47 | expect(result).toHaveProperty('bcs'); 48 | }); 49 | 50 | test('get balance detail', async () => { 51 | const xsdk = new XuperSDK({node, chain}); 52 | xsdk.retrieve(mnemonic, Language.SimplifiedChinese, Cryptography.EccFIPS, true); 53 | const result = await xsdk.getBalanceDetail(); 54 | expect(result).toHaveProperty('header'); 55 | expect(result).toHaveProperty('address', address); 56 | expect(result).toHaveProperty('tfds'); 57 | }) 58 | }); 59 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src", "types"], 3 | "compilerOptions": { 4 | "target": "ES5", 5 | "module": "esnext", 6 | "lib": [ 7 | "dom", 8 | "esnext", 9 | "es2015.promise" 10 | ], 11 | "resolveJsonModule": true, 12 | "importHelpers": true, 13 | "declaration": true, 14 | "sourceMap": true, 15 | "rootDir": "./src", 16 | "strict": true, 17 | "noUnusedLocals": true, 18 | "noUnusedParameters": true, 19 | "noImplicitReturns": true, 20 | "experimentalDecorators": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "moduleResolution": "node", 23 | "baseUrl": "./", 24 | "paths": { 25 | "*": ["src/*", "node_modules/*"] 26 | }, 27 | "jsx": "react", 28 | "esModuleInterop": true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tsdx.config.js: -------------------------------------------------------------------------------- 1 | const copy = require('rollup-plugin-copy'); 2 | 3 | module.exports = { 4 | rollup(config, options) { 5 | config.plugins.push( 6 | copy({ 7 | targets: [ 8 | {src: 'src/proto/**', 'dest': 'dist/proto'} 9 | ] 10 | }) 11 | ); 12 | return config; 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Xuper SDK (JS/TS)", 3 | "mode": "file", 4 | "out": "docs", 5 | "readme": "none", 6 | "hideGenerator": true, 7 | "exclude": ["src/utils.ts"] 8 | } 9 | --------------------------------------------------------------------------------