Compare commits

..

No commits in common. 'master' and '@types' have entirely different histories.

  1. 10
      .gitignore
  2. 9
      .prettierrc
  3. 25
      .travis.yml
  4. 1
      .yarnrc
  5. 90
      README.md
  6. 70
      RELEASE.md
  7. 100
      TYPEDOC.md
  8. 79
      e2e/fixtures/testAccount.json
  9. 138
      e2e/fixtures/transactions.json
  10. 169
      e2e/src/blockchain.e2e.ts
  11. 207
      e2e/src/transaction.e2e.ts
  12. 317
      e2e/src/txn_rpc.e2e.ts
  13. 39
      e2e/src/woop.ts
  14. 19
      e2e/tsconfig.json
  15. 28
      gulpfile.js
  16. 6
      lerna.json
  17. 1081
      log.txt
  18. 44661
      package-lock.json
  19. 76
      package.json
  20. 12
      packages/README.md
  21. 0
      packages/harmony-account/LICENSE
  22. 58
      packages/harmony-account/README.md
  23. 22
      packages/harmony-account/package.json
  24. 225
      packages/harmony-account/src/account.ts
  25. 7
      packages/harmony-account/src/index.ts
  26. 2
      packages/harmony-account/src/types.ts
  27. 24
      packages/harmony-account/src/utils.ts
  28. 291
      packages/harmony-account/src/wallet.ts
  29. 14
      packages/harmony-account/tsconfig.json
  30. 0
      packages/harmony-account/tsconfig.test.json
  31. 0
      packages/harmony-contract/LICENSE
  32. 9
      packages/harmony-contract/__test__/abiCoder.test.ts
  33. 20683
      packages/harmony-contract/__test__/fixtures/abiv2.ts
  34. 28
      packages/harmony-contract/package.json
  35. 361
      packages/harmony-contract/src/abi/abiCoder.ts
  36. 14
      packages/harmony-contract/src/abi/api.ts
  37. 6
      packages/harmony-contract/src/abi/index.ts
  38. 14
      packages/harmony-contract/src/abi/utils.ts
  39. 33
      packages/harmony-contract/src/contract.ts
  40. 11
      packages/harmony-contract/src/contractFactory.ts
  41. 26
      packages/harmony-contract/src/events/event.ts
  42. 41
      packages/harmony-contract/src/events/eventFactory.ts
  43. 10
      packages/harmony-contract/src/index.ts
  44. 224
      packages/harmony-contract/src/methods/method.ts
  45. 21
      packages/harmony-contract/src/methods/methodFactory.ts
  46. 8
      packages/harmony-contract/src/models/AbiItemModel.ts
  47. 28
      packages/harmony-contract/src/models/AbiModel.ts
  48. 6
      packages/harmony-contract/src/models/types.ts
  49. 12
      packages/harmony-contract/src/utils/decoder.ts
  50. 33
      packages/harmony-contract/src/utils/encoder.ts
  51. 39
      packages/harmony-contract/src/utils/formatter.ts
  52. 19
      packages/harmony-contract/src/utils/mapper.ts
  53. 7
      packages/harmony-contract/src/utils/options.ts
  54. 6
      packages/harmony-contract/src/utils/status.ts
  55. 10
      packages/harmony-contract/tsconfig.json
  56. 0
      packages/harmony-contract/tsconfig.test.json
  57. 0
      packages/harmony-core/LICENSE
  58. 4
      packages/harmony-core/README.md
  59. 91
      packages/harmony-core/__test__/blockchain.test.ts
  60. 22
      packages/harmony-core/package.json
  61. 345
      packages/harmony-core/src/blockchain.ts
  62. 65
      packages/harmony-core/src/harmony.ts
  63. 3
      packages/harmony-core/src/index.ts
  64. 19
      packages/harmony-core/src/types.ts
  65. 16
      packages/harmony-core/tsconfig.json
  66. 0
      packages/harmony-core/tsconfig.test.json
  67. 0
      packages/harmony-crypto/LICENSE
  68. 7
      packages/harmony-crypto/README.md
  69. 39
      packages/harmony-crypto/__test__/base58.test.ts
  70. 140
      packages/harmony-crypto/__test__/base58fixture.ts
  71. 13
      packages/harmony-crypto/package.json
  72. 18
      packages/harmony-crypto/src/base58.ts
  73. 70
      packages/harmony-crypto/src/bytes.ts
  74. 132
      packages/harmony-crypto/src/errors.ts
  75. 8
      packages/harmony-crypto/src/index.ts
  76. 14
      packages/harmony-crypto/src/keccak256.ts
  77. 47
      packages/harmony-crypto/src/keyTool.ts
  78. 171
      packages/harmony-crypto/src/keystore.ts
  79. 17
      packages/harmony-crypto/src/random.ts
  80. 24
      packages/harmony-crypto/src/rlp.ts
  81. 5
      packages/harmony-crypto/src/signature.ts
  82. 10
      packages/harmony-crypto/src/types.ts
  83. 6
      packages/harmony-crypto/tsconfig.json
  84. 2
      packages/harmony-crypto/tsconfig.test.json
  85. 0
      packages/harmony-network/LICENSE
  86. 9
      packages/harmony-network/README.md
  87. 15
      packages/harmony-network/package.json
  88. 9
      packages/harmony-network/src/index.ts
  89. 231
      packages/harmony-network/src/messenger/messenger.ts
  90. 21
      packages/harmony-network/src/messenger/responseMiddleware.ts
  91. 16
      packages/harmony-network/src/providers/baseProvider.ts
  92. 16
      packages/harmony-network/src/providers/baseSocket.ts
  93. 21
      packages/harmony-network/src/providers/defaultFetcher.ts
  94. 7
      packages/harmony-network/src/providers/emitter.ts
  95. 26
      packages/harmony-network/src/providers/http.ts
  96. 88
      packages/harmony-network/src/providers/ws.ts
  97. 10
      packages/harmony-network/src/rpcMethod/net.ts
  98. 89
      packages/harmony-network/src/rpcMethod/rpc.ts
  99. 16
      packages/harmony-network/src/rpcMethod/rpcBuilder.ts
  100. 54
      packages/harmony-network/src/subscriptions/LogSub.ts
  101. Some files were not shown because too many files have changed in this diff Show More

10
.gitignore vendored

@ -4,8 +4,13 @@
*.lock
*.tsbuildinfo
.vscode/
devTestOnly/
# package.json
# babel
package-lock.json
packages/*/package-lock.json
/packages/*/package-lock.json
packages/*/babel.rc
# lerna
@ -17,7 +22,6 @@ node/
#cache
.rpt2_cache/
includes/
devTestOnly/
# Xcode
#
build/
@ -77,4 +81,4 @@ buck-out/
*.jsbundle
# Jest coverage
coverage/
coverage/

@ -1,8 +1,7 @@
{
"parser": "typescript",
"printWidth": 100,
"printWidth": 80,
"singleQuote": true,
"bracketSpacing": true,
"trailingComma": "all",
"arrowParens": "always",
"overrides": [
@ -11,12 +10,6 @@
"options": {
"parser": "json"
}
},
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
}
]
}

@ -1,25 +0,0 @@
language: node_js
sudo: true
dist: trusty
node_js:
- 16
branches:
except:
- /^v[0-9]/
install:
- yarn
- yarn bootstrap
script:
- yarn test:src
- yarn dist
cache:
directories:
- '$HOME/.yarn-cache'
deploy:
provider: pages
skip-cleanup: true
github-token: $GITHUB_TOKEN # Set in the settings page of your repository, as a secure variable
keep-history: true
on:
branch: master

@ -1 +0,0 @@
registry "https://registry.npmjs.org"

@ -1,88 +1,60 @@
# Woop JavaScript SDK
[![npm version](https://img.shields.io/npm/v/@harmony-js/core.svg?style=flat-square)](https://www.npmjs.com/package/@harmony-js/core)
[![npm version](https://img.shields.io/npm/v/@woop-js/core.svg?style=flat-square)](https://www.npmjs.com/package/@woop-js/core)
[![Build Status](https://travis-ci.com/FireStack-Lab/Woop-sdk-core.svg?branch=master)](https://travis-ci.com/FireStack-Lab/Woop-sdk-core)
This is the Woop Javascript SDK which provides an easier way to interact with Woop blockchain.
# Harmony-SDK-Core
Please read the [documentation](https://jssdk.doc.hmny.io/) for full API doc.
A Harmony's blockchain javascript library
The SDK includes following packages with package-level documentation and examples inside each package.
It's a mono-repo library, not yet published to npm.
1. [@woop-js/core](https://github.com/woop-chain/sdk/tree/master/packages/woop-core)
2. [@woop-js/account](https://github.com/woop-chain/sdk/tree/master/packages/woop-account)
3. [@woop-js/crypto](https://github.com/woop-chain/sdk/tree/master/packages/woop-crypto)
4. [@woop-js/network](https://github.com/woop-chain/sdk/tree/master/packages/woop-network)
5. [@woop-js/utils](https://github.com/woop-chain/sdk/tree/master/packages/woop-utils)
6. [@woop-js/transaction](https://github.com/woop-chain/sdk/tree/master/packages/woop-transaction)
7. [@woop-js/contract](https://github.com/woop-chain/sdk/tree/master/packages/woop-contract)
8. [@woop-js/staking](https://github.com/woop-chain/sdk/tree/master/packages/woop-staking)
# Installation
This library works on both nodejs and browser. Please use it according to your use case.
## Enviorment requirement
* Nodejs: 10.0+
* Browser: Latest Chrome and Firefox
## Install from npm/yarn
**Note: we added a @next tag to npm package, please use the following command to install with npm/yarn**
# Install from npm/yarn
```bash
# npm
npm install @woop-js/core@next
npm install @harmony-js/core@next
# yarn
yarn add @woop-js/core@next
yarn add @harmony-js/core@next
# tslib is required, we'd better install it as well
# tslib may be required, we'd better install it as well
npm install tslib
yarn add tslib
```
# Building from source files
# Packages
## Install `lerna` and `typescript` globally
1. [@harmony-js/core](https://github.com/FireStack-Lab/Harmony-sdk-core/tree/master/packages/harmony-core)
2. [@harmony-js/account](https://github.com/FireStack-Lab/Harmony-sdk-core/tree/master/packages/harmony-account)
3. [@harmony-js/crypto](https://github.com/FireStack-Lab/Harmony-sdk-core/tree/master/packages/harmony-crypto)
4. [@harmony-js/network](https://github.com/FireStack-Lab/Harmony-sdk-core/tree/master/packages/harmony-network)
5. [@harmony-js/utils](https://github.com/FireStack-Lab/Harmony-sdk-core/tree/master/packages/harmony-utils)
6. [@harmony-js/transaction](https://github.com/FireStack-Lab/Harmony-sdk-core/tree/master/packages/harmony-transaction)
7. [@harmony-js/contract](https://github.com/FireStack-Lab/Harmony-sdk-core/tree/master/packages/harmony-contract)
```bash
yarn global add lerna && yarn global add typescript
```
## Bootstrap and build
```bash
yarn bootstrap
```
## Bundle
# Manually Build
1. make sure you have latest `node.js` and `yarn` installed
Build `umd` and `esm` version javascript for each sub-packages, which can be accessed by `import` or `require`
```bash
yarn dist
```
All files are exported in `packages/dist` folder, use `**.esm.js` or `**.umd.js` format
2. git clone
```bash
git clone git@github.com:FireStack-Lab/Harmony-sdk-core.git
cd Harmony-sdk-core
```
3. cleanup and build
```bash
yarn global add lerna && yarn install && lerna bootstrap && lerna link && yarn dist
```
# Running Tests
## Unit tests
```bash
yarn test:src
```
## e2e tests
1. Remove the `'cross-fetch': 'jest-fetch-mock'` line from `scripts/jest/jest.e2e.config.js`
1. Run woop node locally, follow the instructions: https://github.com/woop-chain/woop
1. Wait for 1-2 mins, and run this:
```bash
yarn build && yarn test:e2e
```
# Examples
* [harmony-sdk-examples](https://github.com/FireStack-Lab/harmony-sdk-examples)

@ -1,70 +0,0 @@
# Release Guidelines
## Before Release
1. Build source first
```bash
yarn build:ts
```
2. Run unit tests
```bash
yarn test:src
```
3. Run e2e tests
```bash
yarn test:e2e
```
4. Clean and build bundle
```bash
yarn dist
```
## Publish to npm using `dev:publish`
The packages is to be published to npm, using `@next` tag using script in `package.json`
Follow steps below to publish a npm verion using `@next` tag
1. Commit all changes to github master
2. Run publish script
```bash
yarn dev:publish
```
3. Select version and confirm all prompts with `Y`
4. See version changes in `npmjs.com`
This will not change the release version of current npm packages(currently 0.0.7), developers have to use `@next` to install from npm.
For example.
```bash
npm install @woop-js/core@next
```
## Publish to npm with `lerna`
Follow steps below to publish a npm verion with latest version
1. Commit all changes to github master
2. Run `lerna publish`, `lerna` is required globally.
```bash
lerna publish
```
3. Select version and confirm all prompts with `Y`
4. See version changes in `npmjs.com`
This will change the release version of current npm packages to the latest version, developers can install from npm directly
For example.
```bash
npm install @woop-js/core
```

@ -1,100 +0,0 @@
# Woop JS-SDK Documentation
## [CLICK ME!!](https://woop-js-sdk-doc.s3-us-west-1.amazonaws.com/index.html) to see the documentation
# metaDocumentation
## Summary
The following content demonstrate how to generate our documentation!
## Step 1: Generate Documentation
### Introduction of TypeDoc
[TypeDoc is used to generate HTML](https://typedoc.org/api/index.html)
> See [TypeDoc command line arguments](https://typedoc.org/guides/options/), to understand how to use them.
> Using `typedoc --help` to see them
>
> **For example:**
> `typedoc --name <Name>` to set the name of header
> `typedoc --theme <default | minimal | path/to/theme>` to set the theme of documation
> `typedoc --readme <path/to/readme | none>` path to readme file that should be displayed on the index page.
> `typedoc --ignoreCompilerErrors` Should TypeDoc generate documentation pages even after the compiler has returned errors?
### Install TypeDoc
Local installation (prefered)
```
$ npm install typedoc --save-dev
```
Golbal CLI installation
```
$ npm install --global typedoc
```
### Install Environemnt
```
$ npm install
```
### Generate HTML
```
$ cd docs
$ npx typedoc --out ./build ../packages/ --ignoreCompilerErrors --theme default --name Woop_SDK_Doc --readme ../README.md
```
### See the generated doc at local
>open the `index.html` under the path `sdk/docs/build/index.html`
## Step 2: Deploy on AWS (woop core only!)
### Create an AWS s3 bucket
Actually, there are just two points needed!
1. Create an AWS S3 bucket, **UNCHECK** `Block all public access`
2. Put the files into the bucket, and set the **public permission** to `Grant public read access to this object(s)`
### Method 1: Use Console
[Here](https://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html) is the documentation of AWS, just follow it!
>Don't forget the two points mentioned above
### Method 2: Use AWS CLI
Reference: [AWS CLI documentation](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html)
If you have never used AWS CLI, you need follow these to set up your environment first!
- [Install the AWS CLI version 1](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html)
- [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html)
After that, use AWS CLI to do following
1. Create a Bucket
```
aws s3 mb s3://woop-js-sdk-doc
```
2. List all buckets you have created
```
aws s3 ls
```
3. Uploade the files into bucket
```
$ cd build
$ aws s3 cp ./ s3://your-bucket-name --grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers --recursive
```
Here is some explanations
> **./account**
> the path of folder which we want to upload
>
> **s3://woop-js-sdk-doc**
> the bucket name on AWS S3
>
> **--grants read=uri=http://acs.amazonaws.com/groups/global/AllUsers**
> Grant read access to all user
>
> **--recursive**
> Command is performed on all files or objects under the specified directory or prefix.
4. Open the folder in S3 bucket and find `index.html`, get the
`Object URL`, then make it public!

@ -1,79 +0,0 @@
{
"Accounts": [
{
"Address": "0x1a3e7a44ee21101d7D64FBf29B0F6F1fc295F723",
"Private": "27978f895b11d9c737e1ab1623fde722c04b4f9ccb4ab776bf15932cc72d7c66",
"Public": "0x1a3e7a44ee21101d7D64FBf29B0F6F1fc295F723"
},
{
"Address": "0x10A02A0a6e95a676AE23e2db04BEa3D1B8b7ca2E",
"Private": "371cb68abe6a6101ac88603fc847e0c013a834253acee5315884d2c4e387ebca",
"Public": "0x10A02A0a6e95a676AE23e2db04BEa3D1B8b7ca2E"
},
{
"Address": "0x3e881F6C36A3A14a2D1816b0A5471d1caBB16F33",
"Private": "3f8af52063c6648be37d4b33559f784feb16d8e5ffaccf082b3657ea35b05977",
"Public": "0x3e881F6C36A3A14a2D1816b0A5471d1caBB16F33"
},
{
"Address": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"Private": "df77927961152e6a080ac299e7af2135fc0fb02eb044d0d7bbb1e8c5ad523809",
"Public": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52"
},
{
"Address": "0x67957240b6eB045E17B47dcE98102f09aaC03435",
"Private": "fcff43741ad2dd0b232efb159dc47736bbb16f11a79aaeec39b388d06f91116d",
"Public": "0x67957240b6eB045E17B47dcE98102f09aaC03435"
},
{
"Address": "0xf70fBDB1AD002baDF19024785b1a4bf6F841F558",
"Private": "916d3d78b7f413452434e89f9c1f1d136995ef02d7dc8038e84cc9cef4a02b96",
"Public": "0xf70fBDB1AD002baDF19024785b1a4bf6F841F558"
},
{
"Address": "0x3f1A559be93C9456Ca75712535Fd522f5EC22c6B",
"Private": "f5967bd87fd2b9dbf51855a2a75ef0a811c84953b3b300ffe90c430a5c856303",
"Public": "0x3f1A559be93C9456Ca75712535Fd522f5EC22c6B"
},
{
"Address": "0xedD257B4e0F5e7d632c737f4277e93b64DC268FC",
"Private": "f02f7b3bb5aa03aa97f9e030020dd9ca306b209742fafe018104a3207a70a3c9",
"Public": "0xedD257B4e0F5e7d632c737f4277e93b64DC268FC"
},
{
"Address": "0x66A74477FC1dd0F4924ed943C1d2F1Dece3Ab138",
"Private": "0436864cc15772448f88dd40554592ff6c91a6c1a389d965ad26ee143db1234d",
"Public": "0x66A74477FC1dd0F4924ed943C1d2F1Dece3Ab138"
},
{
"Address": "0x04178CdbCe3a9Ff9Ea385777aFc4b78B3E745281",
"Private": "dea956e530073ab23d9cae704f5d068482b1977c3173c9efd697c48a7fd3ce83",
"Public": "0x04178CdbCe3a9Ff9Ea385777aFc4b78B3E745281"
},
{
"Address": "0x46C61d50874A7A06D29FF89a710AbBD0856265be",
"Private": "af539d4ace07a9f601a8d3a6ca6f914d5a9fabe09cfe7d62ebc2348fc95f03a4",
"Public": "0x46C61d50874A7A06D29FF89a710AbBD0856265be"
},
{
"Address": "0xfE9BABE6904C28E31971337738FBCBAF8c72873e",
"Private": "7d24797eeba0cdac9bf943f0d82c4b18eb206108d6e1b7f610471594c0c94306",
"Public": "0xfE9BABE6904C28E31971337738FBCBAF8c72873e"
},
{
"Address": "0x3f78622de8D8f87EAa0E8b28C2851e2450E91250",
"Private": "4fa2fecce1becfaf7e5fba5394caacb318333b04071462b5ca850ee5a406dcfe",
"Public": "0x3f78622de8D8f87EAa0E8b28C2851e2450E91250"
},
{
"Address": "0xd2Cb501B40D3a9a013A38267a4d2A4Cf6bD2CAa8",
"Private": "3c8642f7188e05acc4467d9e2aa7fd539e82aa90a5497257cf0ecbb98ed3b88f",
"Public": "0xd2Cb501B40D3a9a013A38267a4d2A4Cf6bD2CAa8"
},
{
"Address": "0x2676e6dd2d7618be14cb4c18a355c81bf7aac647",
"Private": "bf29f6a33b2c24a8b5182ef44cc35ce87534ef827c8dfbc1e6bb536aa52f8563",
"Public": "0x2676e6dd2d7618be14cb4c18a355c81bf7aac647"
}
]
}

@ -1,138 +0,0 @@
{
"transactions": [
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x0",
"value": "0x186a0",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8698085174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee52830186a08027a0ab8229ff5d5240948098f26372eaed9ab2e9be23e8594b08e358ca56a47f8ae9a0084e5c4d1fec496af444423d8a714f65c079260ff01a1be1de7005dd424adf44"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x1",
"value": "0x30d40",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690185174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee5283030d408028a06c6a5900a44c13c4011ecf48d3149f7114e81d168e11422ce157475053083e94a039ce7ff13a2b79a81e98fb035a4068de54477d4bc671f4695fbd42f416a74cc3"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x2",
"value": "0x493e0",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690285174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee52830493e08028a0ccc0c2abb00a3133c570cea9281f6a134aa9e3b287b64ec42b608102d70a3f97a02418b19b7b567e6bdfbf49afb6b0980ee16fcf199ef587b62ac2187dd966868f"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x3",
"value": "0x61a80",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690385174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee5283061a808027a054705b19391ca26d6b13d78cd81fd48d172dbd7b3498edb575d0f4afaa67a8aaa044bb3117d943f6ecfff97d3d45df0ff9bdb1899c0b69f93e6e4a81685a4a7ea0"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x4",
"value": "0x7a120",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690485174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee528307a1208027a0bf688cfcdc88e07d700ccaab88a1856a33ea74ba10188dbde369cd326b629801a074db255ddf1b9cbf95980ff0433bdbb1f1282cf8a4cb0a02d240fba82992fb0d"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x5",
"value": "0x927c0",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690585174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee52830927c08028a092648d648460e9ebc56597dbe6cdccad3465f0f81aa47d00d1ae1b4319c72977a0565ea274e20717a3b9eafd4e4adfaba25590ced567087d034253a48098ad9590"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x6",
"value": "0xaae60",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690685174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee52830aae608027a0522cee53d8b50d745c748f8d3be146d2c1c96a9b14f036f1fe6ab5e34a20afe0a00f6a048024bb665440b56414c1c3592c91a01287ef79fbf058d0dc10cc48c673"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x7",
"value": "0xc3500",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690785174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee52830c35008027a0a45f535b7ca45b8073a9cc5065a2cb58bc5574395a05f10883f71d6184d86d0ca04ef79ebcbef8aa266a51e8ebe2d2c203fd033c8032f2243a0ab6b14f8e45700f"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x8",
"value": "0xdbba0",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690885174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee52830dbba08027a08e63da2b95526b271536e1e53ccb81b5a580da234d59887cda8f69adc1d97dd7a0660db05c1685ba6978fa14c0da92ce19b89e6f973b0590cfed828082b962cdf3"
},
{
"senderAddress": "0x7c41E0668B551f4f902cFaec05B5Bdca68b124CE",
"receiverAddress": "0x9d72989b68777a1f3FfD6F1DB079f1928373eE52",
"senderAddressBech32": "one103q7qe5t2505lypvltkqtddaef5tzfxwsse4z7",
"receiverAddressBech32": "one1n4ef3xmgwaap70laduwmq703j2ph8mjjucrkf8",
"nonce": "0x9",
"value": "0xf4240",
"gasLimit": "0x5208",
"gasPrice": "0x174876e800",
"rawTransaction": "0xf8690985174876e8008252088080949d72989b68777a1f3ffd6f1db079f1928373ee52830f42408028a022efd1decb6e80e88fd33f5e189f48105acc72f7e302931221183f86ca8a7015a02001dee44471a5397a4579f733b3a13a86ba1d6c78ad3312187513d6a424f72b"
}
],
"hashes": [
"0x5aE91eCA3BF7CB97FCd1305bD6e030e24bda9bbfED3b8c741Efe808e8D4F95Ff",
"0xf4CA9DdED0bC36B5AFBCf4fC6De082DFd0F7FCFad1c2aFC1aF52d16d9Dbd733f",
"0x56C2408c5F58ac9aDFE824B7755f3b54CB0848c7A837Ea1CC5a0FdB521D4A1a2",
"0xACFD09AD729E0C9f23EC9eEf27CC68bA37D7EE9D53aABace2dDD42616Eb8351A",
"0xB8baee4EfB120254Afab3092c8Ccc458fF49eBda12ead9950CD39ac8Bb26D4C1",
"0x412D8DBF0BC3dCAf91A0339EEadd1EEB44b44cD0cEBFaea0d35656d31F96Dde9",
"0xbbeDcd6e4dCDef0c666C0C0c7914ba1E5617F8d57ddc40C32cCb31D4BC5CAfBF",
"0x6E008bEbC8F2CE750d208dbF987edEf7476DcFDF8Eb8607f3B3c2E1ce8675ba9",
"0x0Ff0AcBD45cdDf3A8A148eb2F4Fe6dBf74B422eabdD0766ba9284f0Fe8F6dc8a",
"0x7FDCEE6B5EEf5C3F82B2Ffe8a088E0d3d485bEbBcdBCac16ECCb9d1cbfe50c91"
],
"blockHashes": [
"0x9dD4e063d8cc7A6Fe74a0E3AC4feD5F84eB8Bdc4647Ba8DAd651FD1b4a3979cE",
"0xEF5F7D7ca60bE76c1Cc5aADD8DC9e1bC0F7a3BCD0EEDA6e9Cca0EACAd99BE3Bb",
"0x4261B409CCcDA3ab97aABCFe8dECCfd65D1aB9Bb536dD57Fdafd191FC6BbFbbc",
"0x1fb05a17794cAd1ac025469eA1Bf5af4F33f9bdbD3c0e14bCeca323BC5DF0EC3",
"0xEA405dB5dc0E7e2DB2CfdBf272DCeAf69E9CDa2DFD47229b38A3C5b7b9cc41fB",
"0xCC1ADfef1bb2E496A3BbDb181ec5c8EA047AE340FaD6fF4FeF3CCE452c1975B6",
"0x80dffAe15B6619Cc7aD74Bc132C1b29EFc38B8d8CAD1AfbDb9E37f7357E2EBe4",
"0x6BcB9858d8cCE9de2a6ECd6392A5eC712be9b4A79F6Cf84De8aa5ae9C04f60A8",
"0xB6De43A866dFcE1f7AA7DA0E8e9eb7Fe545cB6becF7cf5fdC564B22C4cBb72b9",
"0xc4b4CeAEAEadADfa8CbCcDeFdcBaBeF999dBF42D14C9AFEDd1de2956B571CebD"
]
}

@ -1,169 +0,0 @@
import fetch from 'jest-fetch-mock';
import { woop, checkCalledMethod } from './woop';
import demoAccounts from '../fixtures/testAccount.json';
import { RPCMethod } from '@woop-js/network';
const bc = woop.blockchain;
const testAccount = demoAccounts.Accounts[1];
describe('e2e test blockchain', () => {
beforeEach(() => {
fetch.resetMocks();
});
// net_*
it('should test net_peerCount', async () => {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x0"}),
);
const peerCount = await bc.net_peerCount();
expect(checkCalledMethod(0, RPCMethod.PeerCount)).toEqual(true);
expect(woop.utils.isHex(peerCount.result)).toEqual(true);
});
it('should test net_version', async () => {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "5"}),
);
const netVersion = await bc.net_version();
const versionNumber = parseInt(netVersion.result as string, 10);
expect(netVersion.result).toEqual(`${versionNumber}`);
expect(checkCalledMethod(0, RPCMethod.NetVersion)).toEqual(true);
});
it('should test wiki_protocolVersion', async () => {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x10"}),
);
const protocolVersion = await bc.getProtocolVersion();
expect(woop.utils.isHex(protocolVersion.result)).toEqual(true);
expect(checkCalledMethod(0, RPCMethod.ProtocolVersion)).toEqual(true);
});
// block chain info
it('should test wiki_blockNumber', async () => {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x10"}),
);
const res = await bc.getBlockNumber();
expect(res.responseType).toEqual('raw');
expect(woop.utils.isHex(res.result)).toEqual(true);
expect(checkCalledMethod(0, RPCMethod.BlockNumber)).toEqual(true);
});
it('should test wiki_getBlockByNumber', async () => {
fetch.mockResponse(
JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"size": "0x1",
"difficulty": 5,
"extraData": "0x",
"gasLimit": "0x80",
"gasUsed": "0x40",
"hash": "0x8a3390ab500Fbca6514eB326d2fcD9B3BFCFbA7DA392593cB4885b8e3399a2D8",
"logsBloom": "0x0",
"miner": "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3",
"mixHash": "0x3A7c1Ae14AfecFf55Da298F66b75F4FfB771c3EaBDeAa267FF33A77d4d0be220",
"nonce": 1,
"number": "0x1",
"parentHash": "0x7CebC07e456F0bCD09dbc9A6f271074d93E27B40A4C67Dcc402e6513e12B9aF9",
"receiptsRoot": "0x02B82e11eDC07775Dc6fCF706be2cdAF9165750Ea7bC1B3Eb48ea16Bb3072F4D",
"stateRoot": "0xAaDc89C8bA4e3fCfC140cFcc8D3efD3BE7a49ab31534A5a3F0E1DEA09aae0f4a",
"timestamp": "0x62c44c0a",
"transactionsRoot": "0xc4bfa888fDCC8ca70E2b0CcdCEcc2fF545acCC2D655Ba33DaF4aBc31cFDBd9Ac",
"uncles": []
}
}),
);
const res = await bc.getBlockByNumber({ blockNumber: 'latest' });
const size = res.result.size;
expect(res.responseType).toEqual('raw');
expect(woop.utils.isHex(size)).toEqual(true);
expect(checkBlockData(res.result)).toEqual(true);
const res2 = await bc.getBlockByNumber({ blockNumber: res.result.number });
expect(res2.responseType).toEqual('raw');
expect(woop.utils.isHex(res2.result.size)).toEqual(true);
expect(checkBlockData(res2.result)).toEqual(true);
const res3 = await bc.getBlockByNumber({ returnObject: true });
expect(res3.responseType).toEqual('raw');
expect(checkBlockData(res3.result)).toEqual(true);
for(let i = 0; i < 3; i++) {
expect(checkCalledMethod(i, RPCMethod.GetBlockByNumber)).toEqual(true);
}
});
it('should test wiki_getBlockByHash', async () => {
fetch.mockResponse(
JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"size": "0x1",
"difficulty": 5,
"extraData": "0x",
"gasLimit": "0x80",
"gasUsed": "0x40",
"hash": "0x8a3390ab500Fbca6514eB326d2fcD9B3BFCFbA7DA392593cB4885b8e3399a2D8",
"logsBloom": "0x0",
"miner": "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3",
"mixHash": "0x3A7c1Ae14AfecFf55Da298F66b75F4FfB771c3EaBDeAa267FF33A77d4d0be220",
"nonce": 1,
"number": "0x1",
"parentHash": "0x7CebC07e456F0bCD09dbc9A6f271074d93E27B40A4C67Dcc402e6513e12B9aF9",
"receiptsRoot": "0x02B82e11eDC07775Dc6fCF706be2cdAF9165750Ea7bC1B3Eb48ea16Bb3072F4D",
"stateRoot": "0xAaDc89C8bA4e3fCfC140cFcc8D3efD3BE7a49ab31534A5a3F0E1DEA09aae0f4a",
"timestamp": "0x62c44c0a",
"transactionsRoot": "0xc4bfa888fDCC8ca70E2b0CcdCEcc2fF545acCC2D655Ba33DaF4aBc31cFDBd9Ac",
"uncles": []
}
}),
);
const latestBlock = await bc.getBlockByNumber({ blockNumber: 'latest' });
const res = await bc.getBlockByHash({ blockHash: latestBlock.result.hash });
expect(res.responseType).toEqual('raw');
expect(latestBlock.result.hash).toEqual(res.result.hash);
expect(woop.utils.isHex(res.result.size)).toEqual(true);
expect(checkBlockData(res.result)).toEqual(true);
expect(checkCalledMethod(0, RPCMethod.GetBlockByNumber)).toEqual(true);
expect(checkCalledMethod(1, RPCMethod.GetBlockByHash)).toEqual(true);
});
// account related
it('should test wiki_getBalance', async () => {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x10"}),
);
const balance = await bc.getBalance({ address: testAccount.Address });
expect(woop.utils.isHex(balance.result)).toEqual(true);
expect(checkCalledMethod(0, RPCMethod.GetBalance)).toEqual(true);
});
});
function checkBlockData(data: any) {
return woop.utils.validateArgs(
data,
{
difficulty: [woop.utils.isNumber],
// tslint:disable-next-line: no-shadowed-variable
extraData: [(data: any) => data === '0x' || woop.utils.isHex(data)],
gasLimit: [woop.utils.isHex],
gasUsed: [woop.utils.isHex],
hash: [woop.utils.isHash],
logsBloom: [woop.utils.isHex],
miner: [woop.utils.isBech32Address],
mixHash: [woop.utils.isHash],
nonce: [woop.utils.isNumber],
number: [woop.utils.isHex],
parentHash: [woop.utils.isHash],
receiptsRoot: [woop.utils.isHash],
size: [woop.utils.isHex],
stateRoot: [woop.utils.isHash],
timestamp: [woop.utils.isHex],
transactionsRoot: [woop.utils.isHash],
uncles: [woop.utils.isArray],
},
{ transactions: [woop.utils.isArray] },
);
}

@ -1,207 +0,0 @@
import fetch from 'jest-fetch-mock';
import { woop, checkCalledMethod } from './woop';
// tslint:disable-next-line: no-implicit-dependencies
import { Transaction, TxStatus } from '@woop-js/transaction';
// tslint:disable-next-line: no-implicit-dependencies
import { isHash, numberToHex } from '@woop-js/utils';
import txnJsons from '../fixtures/transactions.json';
import demoAccounts from '../fixtures/testAccount.json';
import { RPCMethod } from '@woop-js/network';
const receiver = demoAccounts.Accounts[3];
jest.useRealTimers();
describe('test Transaction using SDK', () => {
beforeEach(() => {
fetch.resetMocks();
});
let signed: Transaction;
let sent: Transaction;
let txId: string;
it('should test recover signedTransaction', () => {
const txns = txnJsons.transactions;
// tslint:disable-next-line: prefer-for-of
for (let i = 0; i < txns.length; i += 1) {
const newTxn = woop.transactions.newTx();
newTxn.recover(txns[i].rawTransaction);
expect(newTxn.txParams.from).toEqual(txns[i].senderAddress);
expect(newTxn.txParams.to).toEqual(txns[i].receiverAddress);
expect(`0x${newTxn.txParams.gasLimit.toString(16)}`).toEqual(txns[i].gasLimit);
expect(`0x${newTxn.txParams.gasPrice.toString(16)}`).toEqual(txns[i].gasPrice);
expect(`0x${newTxn.txParams.value.toString(16)}`).toEqual(txns[i].value);
expect(`${numberToHex(newTxn.txParams.nonce)}`).toEqual(txns[i].nonce);
}
});
it('should test signTransaction', async () => {
const txnObject = {
to: woop.crypto.getAddress(receiver.Address).bech32,
value: '0x64',
gasLimit: '210000',
gasPrice: new woop.utils.Unit('100').asGwei().toWei(),
};
const txn = woop.transactions.newTx(txnObject);
signed = await woop.wallet.signTransaction(txn, undefined, undefined, false);
expect(signed.isSigned()).toEqual(true);
});
it('should send transaction', async () => {
fetch.mockResponseOnce(
JSON.stringify({
"jsonrpc": "2.0", "id": 1,
"result": "0x323A2B2C81d8948E5109FC32f9d0e4e6d178d14cC732C8E0a7Af74E81C7653eA"
}),
);
const [sentTxn, id] = await signed.sendTransaction();
expect(sentTxn.isPending()).toEqual(true);
expect(woop.utils.isHash(id)).toEqual(true);
expect(checkCalledMethod(0, RPCMethod.SendRawTransaction)).toEqual(true);
txId = id;
sent = sentTxn;
});
it('should confirm a transaction', async () => {
fetch.mockResponses(
[
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x1"}),
{ status: 200 },
],
[
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x2"}),
{ status: 200 },
],
[
JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"contractAddress": null,
"blockNumber": woop.utils.numberToHex(2),
"from": woop.wallet.accounts[0],
"gasUsed": woop.utils.numberToHex(5),
"cumulativeGasUsed": woop.utils.numberToHex(5),
"logs": [],
"logsBloom": woop.utils.numberToHex(5),
"shardID": 0,
"to": demoAccounts.Accounts[3].Address,
"transactionHash": "0x8c26EFdb6e4cAC6F8BeACE59F52fd95beD4Bfbfa8fF30F4a7cEd511fE5f869d9",
"transactionIndex": woop.utils.numberToHex(10),
"blockHash": "0xFECCCCBFd5AC71902BcfE65dDB0b88EEbbD15AD6cDAE7A9FAEb773bF827320fd",
}
}),
{status: 200},
]
)
const toConfirm = await sent.confirm(txId, 20, 1000);
expect(toConfirm.receipt !== undefined).toEqual(true);
expect(checkTransactionReceipt(toConfirm.receipt)).toEqual(true);
if (toConfirm.isConfirmed()) {
expect(toConfirm.txStatus).toEqual(TxStatus.CONFIRMED);
} else if (toConfirm.isRejected()) {
expect(toConfirm.txStatus).toEqual(TxStatus.REJECTED);
}
expect(checkCalledMethod(0, RPCMethod.BlockNumber)).toEqual(true);
expect(checkCalledMethod(1, RPCMethod.BlockNumber)).toEqual(true);
expect(checkCalledMethod(2, RPCMethod.GetTransactionReceipt)).toEqual(true);
});
it('should test transaction observed events', async () => {
const txnObject = {
to: woop.crypto.getAddress(receiver.Address).bech32,
value: new woop.utils.Unit('100').asGwei().toWei(),
gasLimit: new woop.utils.Unit('210000').asWei().toWei(),
gasPrice: new woop.utils.Unit('100').asGwei().toWei(),
};
const txn = woop.transactions.newTx(txnObject);
txn
.observed()
.on('transactionHash', (transactionHash) => {
expect(isHash(transactionHash)).toEqual(true);
})
.on('receipt', (receipt) => {
expect(checkTransactionReceipt(receipt)).toEqual(true);
})
.on('confirmation', (confirmation) => {
expect(confirmation === TxStatus.REJECTED || confirmation === TxStatus.CONFIRMED).toBe(
true,
);
})
.on('error', (error) => {
expect(error).toBeTruthy();
});
const txnSigned = await woop.wallet.signTransaction(txn, undefined, undefined, false);
fetch.mockResponseOnce(
JSON.stringify({
"jsonrpc": "2.0", "id": 1,
"result": "0x323A2B2C81d8948E5109FC32f9d0e4e6d178d14cC732C8E0a7Af74E81C7653eA"
}),
);
const [txnSent, id] = await txnSigned.sendTransaction();
expect(txnSent.txStatus).toEqual(TxStatus.PENDING);
expect(checkCalledMethod(0, RPCMethod.SendRawTransaction)).toEqual(true);
console.log('Here');
fetch.mockResponses(
[
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x1"}),
{ status: 200 },
],
[
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x2"}),
{ status: 200 },
],
[
JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"contractAddress": null,
"blockNumber": woop.utils.numberToHex(2),
"from": woop.wallet.accounts[0],
"gasUsed": woop.utils.numberToHex(5),
"cumulativeGasUsed": woop.utils.numberToHex(5),
"logs": [],
"logsBloom": woop.utils.numberToHex(5),
"shardID": 0,
"to": demoAccounts.Accounts[3].Address,
"transactionHash": "0x8c26EFdb6e4cAC6F8BeACE59F52fd95beD4Bfbfa8fF30F4a7cEd511fE5f869d9",
"transactionIndex": woop.utils.numberToHex(10),
"blockHash": "0xFECCCCBFd5AC71902BcfE65dDB0b88EEbbD15AD6cDAE7A9FAEb773bF827320fd",
"status": "0x1",
}
}),
{status: 200},
]
);
await txnSigned.confirm(id);
expect(checkCalledMethod(1, RPCMethod.BlockNumber)).toEqual(true);
expect(checkCalledMethod(2, RPCMethod.BlockNumber)).toEqual(true);
expect(checkCalledMethod(3, RPCMethod.GetTransactionReceipt)).toEqual(true);
});
});
function checkTransactionReceipt(data: any) {
return woop.utils.validateArgs(
data,
{
blockHash: [woop.utils.isHash],
blockNumber: [woop.utils.isHex],
contractAddress: [
// tslint:disable-next-line: no-shadowed-variable
(data: any) => data === null || woop.utils.isValidAddress,
],
cumulativeGasUsed: [woop.utils.isHex],
from: [woop.utils.isValidAddress],
gasUsed: [woop.utils.isHex],
logs: [woop.utils.isArray],
logsBloom: [woop.utils.isHex],
shardID: [woop.utils.isNumber],
// tslint:disable-next-line: no-shadowed-variable
to: [(data: any) => data === '0x' || woop.utils.isValidAddress],
transactionHash: [woop.utils.isHash],
transactionIndex: [woop.utils.isHex],
},
{ root: [woop.utils.isHash] },
);
}

@ -1,317 +0,0 @@
import fetch from 'jest-fetch-mock';
import { woop, checkCalledMethod } from './woop';
import txnJsons from '../fixtures/transactions.json';
import { RPCMethod } from '@woop-js/network';
const messenger = woop.messenger;
interface TransactionInfo {
blockHash: string;
index: string;
blockNumber: string;
}
describe('e2e test transactions by RPC Method', () => {
beforeEach(() => {
fetch.resetMocks();
});
const txnHashesFixtures: any = [];
const transactionInfoList: any = [];
const { transactions, hashes, blockHashes } = txnJsons;
// net_*
it('should test wiki_sendRawTransaction', async () => {
for(let index = 0; index < transactions.length; index++) {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": hashes[index]}),
);
const sent = await messenger.send('wiki_sendRawTransaction', transactions[index].rawTransaction);
expect(woop.utils.isHash(sent.result)).toEqual(true);
txnHashesFixtures.push(sent.result);
expect(checkCalledMethod(index, 'wiki_sendRawTransaction')).toEqual(true);
}
});
it('should test wiki_getTransactionByHash', async () => {
for(let index: number = 0; index < txnHashesFixtures.length; index++) {
const txnHash = txnHashesFixtures[index];
fetch.mockResponseOnce(
JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"hash": hashes[index],
"blockHash": blockHashes[index],
"blockNumber": woop.utils.numberToHex(index),
"transactionIndex": woop.utils.numberToHex(index),
"from": transactions[index].senderAddress,
"gas": transactions[index].gasLimit,
"gasPrice": transactions[index].gasPrice,
"input": "0x",
"nonce": transactions[index].nonce,
"to": transactions[index].receiverAddressBech32,
"value": transactions[index].value,
"v": woop.utils.numberToHex(index),
"r": woop.utils.numberToHex(index),
"s": woop.utils.numberToHex(index),
}
})
);
const txnDetail = await woop.blockchain.getTransactionByHash({
txnHash
});
expect(checkCalledMethod(index, RPCMethod.GetTransactionByHash)).toEqual(true);
if (txnDetail.result !== null) {
expect(checkTransactionDetail(txnDetail.result)).toEqual(true);
expect(txnDetail.result.hash).toEqual(txnHash);
const transactionInfo = {
blockHash: txnDetail.result.blockHash,
blockNumber: txnDetail.result.blockNumber,
index: txnDetail.result.transactionIndex,
};
transactionInfoList.push(transactionInfo);
} else {
fail(`txnDetail for ${txnHash} is null`);
}
}
});
it('should test wiki_getTransactionByBlockHashAndIndex', async () => {
for (let index: number = 0; index < transactionInfoList.length; index++) {
fetch.mockResponseOnce((req) => {
if (!(Buffer.isBuffer(req.body))) {
fail("POST request body not a buffer");
}
const body: any = JSON.parse(req.body.toString());
// validate that the block hash is as expected
if (body.params[0] !== blockHashes[index]) {
fail(`Expected block hash ${blockHashes[index]} but got ${body.params[0]}`);
}
// validate that the transaction index is as expected
let expectedTransactionIndex: string = woop.utils.numberToHex(index);
if (expectedTransactionIndex !== body.params[1]) {
fail(`Expected transactionIndex ${expectedTransactionIndex} but got ${body.params[1]}`);
}
return Promise.resolve(JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"hash": hashes[index],
"blockHash": blockHashes[index],
"blockNumber": woop.utils.numberToHex(index),
"transactionIndex": woop.utils.numberToHex(index),
"from": transactions[index].senderAddress,
"gas": transactions[index].gasLimit,
"gasPrice": transactions[index].gasPrice,
"input": "0x",
"nonce": transactions[index].nonce,
"to": transactions[index].receiverAddressBech32,
"value": transactions[index].value,
"v": woop.utils.numberToHex(index),
"r": woop.utils.numberToHex(index),
"s": woop.utils.numberToHex(index),
}
}));
});
const transactionInfo: TransactionInfo = transactionInfoList[index];
const txnDetail: any = await woop.blockchain.getTransactionByBlockHashAndIndex({
blockHash: transactionInfo.blockHash,
index: transactionInfo.index,
});
expect(checkCalledMethod(index, RPCMethod.GetTransactionByBlockHashAndIndex)).toEqual(true);
if (txnDetail.result !== null) {
expect(checkTransactionDetail(txnDetail.result)).toEqual(true);
expect(txnDetail.result.blockHash).toEqual(transactionInfo.blockHash);
expect(txnDetail.result.transactionIndex).toEqual(transactionInfo.index);
} else {
fail(`txnDetail for ${transactionInfo.blockHash}_${transactionInfo.index} is null`);
}
}
});
it('should test wiki_getTransactionByBlockNumberAndIndex', async () => {
for (let index: number = 0; index < transactionInfoList.length; index++) {
fetch.mockResponseOnce((req) => {
if (!(Buffer.isBuffer(req.body))) {
fail("POST request body not a buffer");
}
const body: any = JSON.parse(req.body.toString());
// validate that the block number is as expected
let expectedBlockNumber: string = woop.utils.numberToHex(index);
if (body.params[0] !== expectedBlockNumber) {
fail(`Expected block number ${index} but got ${body.params[0]}`);
}
// validate that the transaction index is as expected
let expectedTransactionIndex: string = woop.utils.numberToHex(index);
if (expectedTransactionIndex !== body.params[1]) {
fail(`Expected transactionIndex ${expectedTransactionIndex} but got ${body.params[1]}`);
}
return Promise.resolve(JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"hash": hashes[index],
"blockHash": blockHashes[index],
"blockNumber": woop.utils.numberToHex(index),
"transactionIndex": woop.utils.numberToHex(index),
"from": transactions[index].senderAddress,
"gas": transactions[index].gasLimit,
"gasPrice": transactions[index].gasPrice,
"input": "0x",
"nonce": transactions[index].nonce,
"to": transactions[index].receiverAddressBech32,
"value": transactions[index].value,
"v": woop.utils.numberToHex(index),
"r": woop.utils.numberToHex(index),
"s": woop.utils.numberToHex(index),
}
}));
});
const transactionInfo: TransactionInfo = transactionInfoList[index];
const txnDetail: any = await woop.blockchain.getTransactionByBlockNumberAndIndex({
blockNumber: transactionInfo.blockNumber,
index: transactionInfo.index,
});
expect(checkCalledMethod(index, RPCMethod.GetTransactionByBlockNumberAndIndex)).toEqual(true);
if (txnDetail.result !== null) {
expect(checkTransactionDetail(txnDetail.result)).toEqual(true);
expect(txnDetail.result.blockNumber).toEqual(transactionInfo.blockNumber);
expect(txnDetail.result.transactionIndex).toEqual(transactionInfo.index);
} else {
fail(`txnDetail for ${transactionInfo.blockNumber}_${transactionInfo.index} is null`);
}
}
});
it('should test wiki_getTransactionCountByHash', async () => {
for (const some of transactionInfoList) {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x1"}),
);
const transactionInfo: TransactionInfo = some;
const txnCount: any = await woop.blockchain.getBlockTransactionCountByHash({
blockHash: transactionInfo.blockHash,
});
expect(checkCalledMethod(0, RPCMethod.GetBlockTransactionCountByHash)).toEqual(true);
expect(woop.utils.isHex(txnCount.result)).toEqual(true);
}
});
it('should test wiki_getTransactionCountByNumber', async () => {
for (const some of transactionInfoList) {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x1"}),
);
const transactionInfo: TransactionInfo = some;
const txnCount: any = await woop.blockchain.getBlockTransactionCountByNumber({
blockNumber: transactionInfo.blockNumber,
});
expect(checkCalledMethod(0, RPCMethod.GetBlockTransactionCountByNumber)).toEqual(true);
expect(woop.utils.isHex(txnCount.result)).toEqual(true);
}
});
it('should test wiki_getTransactionReceipt', async () => {
// tslint:disable-next-line: prefer-for-of
for (let index = 0; index < txnHashesFixtures.length; index += 1) {
const txnHash = txnHashesFixtures[index];
fetch.mockResponseOnce(
JSON.stringify({
"jsonrpc": "2.0",
"id": 1,
"result": {
"contractAddress": null,
"blockNumber": woop.utils.numberToHex(index),
"from": transactions[index].senderAddress,
"gasUsed": woop.utils.numberToHex(index),
"cumulativeGasUsed": woop.utils.numberToHex(index),
"logs": [],
"logsBloom": woop.utils.numberToHex(index),
"shardID": 0,
"to": transactions[index].receiverAddress,
"transactionHash": hashes[index],
"transactionIndex": woop.utils.numberToHex(index),
"blockHash": blockHashes[index]
}
})
);
const receipt: any = await woop.blockchain.getTransactionReceipt({
txnHash,
});
expect(checkCalledMethod(index, RPCMethod.GetTransactionReceipt)).toEqual(true);
if (receipt.result !== null) {
expect(checkTransactionReceipt(receipt.result)).toEqual(true);
expect(woop.crypto.getAddress(receipt.result.from).checksum).toEqual(
transactions[index].senderAddress,
);
expect(woop.crypto.getAddress(receipt.result.to).checksum).toEqual(
transactions[index].receiverAddress,
);
expect(receipt.result.blockHash).toEqual(transactionInfoList[index].blockHash);
expect(receipt.result.blockNumber).toEqual(transactionInfoList[index].blockNumber);
expect(receipt.result.transactionIndex).toEqual(transactionInfoList[index].index);
} else {
fail(`receipt for ${txnHash} is null`);
}
}
});
it('should test wiki_getTransactionCount', async () => {
for (let i = 0; i < transactionInfoList; i += 1) {
fetch.mockResponseOnce(
JSON.stringify({"jsonrpc": "2.0", "id": 1, "result": "0x1"}),
);
const transactionInfo: TransactionInfo = transactionInfoList[i];
const nonce: any = await woop.blockchain.getTransactionCount({
address: transactions[i].senderAddressBech32,
blockNumber: transactionInfo.blockNumber,
});
expect(nonce.result).toEqual(transactions[i].nonce);
}
});
});
function checkTransactionDetail(data: any) {
return woop.utils.validateArgs(
data,
{
blockHash: [woop.utils.isHash],
blockNumber: [woop.utils.isHex],
// tslint:disable-next-line: no-shadowed-variable
from: [woop.utils.isValidAddress],
gas: [woop.utils.isHex],
gasPrice: [woop.utils.isHex],
hash: [woop.utils.isHash],
// tslint:disable-next-line: no-shadowed-variable
input: [(data: any) => data === '0x' || woop.utils.isHex(data)],
nonce: [woop.utils.isHex],
// tslint:disable-next-line: no-shadowed-variable
to: [(data: any) => data === '0x' || woop.utils.isValidAddress(data)],
transactionIndex: [woop.utils.isHex],
value: [woop.utils.isHex],
v: [woop.utils.isHex],
r: [woop.utils.isHex],
s: [woop.utils.isHex],
},
{},
);
}
function checkTransactionReceipt(data: any) {
return woop.utils.validateArgs(
data,
{
blockNumber: [woop.utils.isHex],
contractAddress: [
// tslint:disable-next-line: no-shadowed-variable
(data: any) => data === null || woop.utils.isValidAddress,
],
cumulativeGasUsed: [woop.utils.isHex],
from: [woop.utils.isValidAddress],
gasUsed: [woop.utils.isHex],
logs: [woop.utils.isArray],
logsBloom: [woop.utils.isHex],
shardID: [woop.utils.isNumber],
// tslint:disable-next-line: no-shadowed-variable
to: [(data: any) => data === '0x' || woop.utils.isValidAddress],
transactionHash: [woop.utils.isHash],
transactionIndex: [woop.utils.isHex],
},
{ blockHash: [woop.utils.isHash], root: [woop.utils.isHash] },
);
}

@ -1,39 +0,0 @@
import fetch from 'jest-fetch-mock';
// tslint:disable-next-line: no-implicit-dependencies
import { Woop } from '@woop-js/core';
// tslint:disable-next-line: no-implicit-dependencies
import { ChainType } from '@woop-js/utils';
// tslint:disable-next-line: no-implicit-dependencies
import { Account } from '@woop-js/account';
const CHAIN_ID: number = 2;
const CHAIN_TYPE: string = 'wiki';
const HTTP_PROVIDER: string = 'http://localhost:9500';
const GENESIS_PRIV_KEY: string = '45e497bd45a9049bcb649016594489ac67b9f052a6cdf5cb74ee2427a60bf25e';
let chainType: ChainType = ChainType.Woop;
if (CHAIN_TYPE === 'wiki') {
chainType = ChainType.Woop;
} else if (CHAIN_TYPE === 'eth') {
chainType = ChainType.Ethereum;
}
export const woop: Woop = new Woop(HTTP_PROVIDER, {
chainId: CHAIN_ID,
chainType,
chainUrl: HTTP_PROVIDER,
});
export const myAccount: Account = woop.wallet.addByPrivateKey(
GENESIS_PRIV_KEY,
);
export function checkCalledMethod(i: number, s: string) {
let params: (string | undefined) = fetch.mock.calls[i][1]?.body?.toString();
if (params) {
let method: string = JSON.parse(params).method;
return method === s;
}
return false;
}

@ -1,19 +0,0 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src",
"declarationDir": "dist",
"resolveJsonModule": true,
"esModuleInterop": true
},
"include": ["src", "../typings/**/*.d.ts", "fixtures"],
"references": [
{ "path": "../packages/woop-account" },
{ "path": "../packages/woop-crypto" },
{ "path": "../packages/woop-utils" },
{ "path": "../packages/woop-network" },
{ "path": "../packages/woop-transaction" },
{ "path": "../packages/woop-contract" }
]
}

@ -1,17 +1,14 @@
const { task, src } = require('gulp');
const { task } = require('gulp');
const del = require('del');
const fs = require('fs');
const path = require('path');
const packages = [
'woop-core',
'woop-crypto',
'woop-account',
'woop-network',
'woop-contract',
'woop-utils',
'woop-transaction',
'woop-staking',
'harmony-core',
'harmony-crypto',
'harmony-account',
'harmony-network',
'harmony-contract',
'harmony-utils',
'harmony-transaction',
];
task('cleanBrowser', async () => {
@ -28,16 +25,9 @@ task('cleanServer', async () => {
});
});
task('cleanUnexpected', async () => {
await packages.map((p) => {
const pathToLib = `packages/${p}/tsconfig.tsbuildinfo`;
return del.sync([pathToLib]);
});
});
task('cleanDocs', async () => {
await packages.map((p) => {
const pathToLib = `docs/${p}`;
const pathToLib = `packages/${p}/doc`;
return del.sync([pathToLib]);
});
});

@ -1,6 +1,8 @@
{
"packages": ["packages/*"],
"packages": [
"packages/*"
],
"npmClient": "yarn",
"useWorkspaces": true,
"version": "0.1.58"
"version": "0.0.15"
}

1081
log.txt

File diff suppressed because it is too large Load Diff

44661
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,41 +1,29 @@
{
"description": "Root repository for the woop-sdk-core",
"description": "Root repository for the harmony-sdk-core",
"private": true,
"license": "MIT",
"workspaces": [
"packages/*"
],
"scripts": {
"bootstrap": "lerna bootstrap && yarn build",
"watch": "yarn build:ts -w",
"build": "yarn packages:clean && lerna clean -y && lerna link && yarn build:ts",
"packages:cleanBrowser": "gulp cleanBrowser",
"packages:cleanServer": "gulp cleanServer",
"packages:browser": "yarn packages:cleanBrowser && rollup --c scripts/rollup/bundleBrowser.js",
"packages:bundler": "yarn packages:cleanServer && yarn build:ts && ts-node -P scripts/tsconfig.json scripts/bundle.ts",
"dist": "yarn packages:bundler && rm -rf dist && cross-env NODE_ENV=production webpack --config webpack.config.js",
"bootstrap": "lerna bootstrap",
"build": "yarn build:proto && yarn build:ts",
"build:ts": "tsc -b tsconfig.json",
"build:test": "tsc -b tsconfig.test.json",
"build:e2e": "tsc -b tsconfig.e2e.json",
"bundle": "ts-node -P scripts/tsconfig.json scripts/bundle.ts umd,esm",
"bundle:webpack": "rm -rf dist && cross-env NODE_ENV=production ts-node -P scripts/tsconfig.json scripts/webpack.ts",
"clean": "lerna clean --yes && lerna run clean && rimraf includes && yarn packages:clean",
"dist": "yarn packages:bundler && yarn bundle:webpack",
"packages:cleanUnexpected": "gulp cleanUnexpected",
"packages:clean": "gulp cleanServer && yarn packages:cleanUnexpected",
"packages:bundler": "yarn packages:clean && yarn build:ts && ts-node -P scripts/tsconfig.json scripts/bundle.ts",
"clean": "lerna clean --yes && lerna run clean && rimraf includes",
"schema": "ts-node -P scripts/tsconfig.json scripts/typings/schema.ts core",
"test": "cross-env TEST_ENV=unit jest -c scripts/jest/jest.config.js --rootDir=.",
"test:src": "cross-env NODE_ENV=test jest --config ./scripts/jest/jest.src.config.js --verbose --runInBand --no-cache",
"test:src": "cross-env NODE_ENV=test jest --config ./scripts/jest/jest.src.config.js --silent --runInBand --no-cache",
"test:build": "cross-env TEST_ENV=unit jest -c jest.build.config.js",
"test:integration": "cross-env TEST_ENV=integration jest -c jest.iconfig.js --runInBand --verbose --collectCoverage=false",
"test:e2e": "cross-env TEST_ENV=e2e jest -c ./scripts/jest/jest.e2e.config.js --runInBand --verbose",
"docs:vue": "ts-node -P scripts/tsconfig.json scripts/docs.ts vuepress",
"docs:gitbook": "ts-node -P scripts/tsconfig.json scripts/docs.ts gitbook",
"docs:docusaurus": "ts-node -P scripts/tsconfig.json scripts/docs.ts docusaurus",
"docs:bitbucket": "ts-node -P scripts/tsconfig.json scripts/docs.ts bitbucket",
"docs:default": "ts-node -P scripts/tsconfig.json scripts/docs.ts default",
"docs:clean": "gulp cleanDocs",
"docs:bundler": "yarn packages:cleanDocs && yarn docs:build",
"release": "yarn bootstrap && yarn bundle && lerna publish --exact",
"format": "prettier --write '**/*.{ts,tsx,js}' --config .prettierrc",
"formatSol": "prettier --list-different **/*.sol",
"dev:publish": "yarn dist && yarn packages:cleanUnexpected && lerna publish --dist-tag next --exact --no-verify-access --no-verify-registry"
"dev:publish": "lerna publish --dist-tag next --exact --no-verify-access --no-verify-registry"
},
"devDependencies": {
"@babel/cli": "^7.0.0-beta.56",
@ -60,6 +48,7 @@
"@babel/preset-env": "7.0.0-beta.54",
"@babel/preset-typescript": "^7.0.0-beta.56",
"@babel/runtime": "^7.0.0-beta.56",
"@trust/webcrypto": "^0.9.2",
"@types/base-x": "^3.0.0",
"@types/bip39": "^2.4.2",
"@types/bn.js": "^4.11.3",
@ -72,78 +61,57 @@
"@types/jest-json-schema": "^1.2.0",
"@types/node": "^10.5.6",
"@types/pbkdf2": "^3.0.0",
"@types/text-encoding": "0.0.35",
"@types/uuid": "^3.4.4",
"@types/valid-url": "^1.0.2",
"@types/webpack": "^4.4.17",
"@types/websocket": "^0.0.40",
"arg": "^4.1.1",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^23.4.2",
"babel-loader": "^8.0.0-beta.0",
"babel-plugin-transform-remove-console": "^6.9.4",
"camelcase": "^5.0.0",
"cross-env": "^5.2.0",
"cross-spawn": "^6.0.5",
"del": "^4.0.0",
"dotenv": "^6.0.0",
"fancy-log": "^1.3.2",
"fbjs-scripts": "^0.8.3",
"ganache-cli": "^6.12.1",
"glob": "^7.1.3",
"glob-parent": "^3.1.0",
"gulp": "^4.0.0",
"gulp-typedoc": "^2.2.2",
"husky": "^1.1.2",
"jest": "^23.4.2",
"jest-fetch-mock": "^3.0.3",
"jest-fetch-mock": "^1.6.6",
"jest-json-schema": "^2.0.1",
"jest-watch-typeahead": "^0.2.0",
"jsdoc": "^3.6.3",
"lerna": "^3.20.2",
"lerna": "^3.2.1",
"mitt": "^1.1.3",
"mkdirp": "^0.5.1",
"prettier": "^1.18.2",
"prettier": "^1.14.3",
"prettier-plugin-solidity": "^1.0.0-alpha.22",
"pretty-quick": "^1.8.0",
"protobufjs": "^6.8.8",
"rimraf": "^2.6.2",
"rollup": "1.26.3",
"rollup": "^0.66.6",
"rollup-plugin-alias": "^1.4.0",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.2.0",
"rollup-plugin-jsdoc": "^0.1.1",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-license": "^0.8.1",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-typescript2": "^0.29.0",
"solc": "^0.5.8",
"terser-webpack-plugin": "^2.1.2",
"rollup-plugin-typescript2": "^0.17.1",
"ts-jest": "^23.1.3",
"ts-node": "^7.0.1",
"tslib": "^2.4.0",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0",
"typedoc": "^0.17.0-3",
"typedoc-plugin-external-module-name": "^3.0.0",
"typedoc-plugin-no-inherit": "^1.1.10",
"typescript": "^3.8.3",
"typescript-json-schema": "^0.43.0",
"typescript": "^3.2",
"typescript-json-schema": "^0.36.0",
"uglifyjs-webpack-plugin": "^2.1.2",
"webpack": "^4.20.2",
"webpack-command": "^0.4.1",
"webpack-node-externals": "^1.7.2",
"websocket": "^1.0.28"
},
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
},
"name": "woop-sdk-core",
"dependencies": {
"@woop-js/core": "^0.1.36",
"tslib": "^2.4.0",
"typedoc-plugin-internal-external": "^2.1.1"
}
"name": "harmony-sdk-core"
}

@ -1,12 +0,0 @@
# Packages available are:
1. [@woop-js/core](https://github.com/woop-chain/sdk/tree/master/packages/woop-core)
2. [@woop-js/account](https://github.com/woop-chain/sdk/tree/master/packages/woop-account)
3. [@woop-js/crypto](https://github.com/woop-chain/sdk/tree/master/packages/woop-crypto)
4. [@woop-js/network](https://github.com/woop-chain/sdk/tree/master/packages/woop-network)
5. [@woop-js/utils](https://github.com/woop-chain/sdk/tree/master/packages/woop-utils)
6. [@woop-js/transaction](https://github.com/woop-chain/sdk/tree/master/packages/woop-transaction)
7. [@woop-js/contract](https://github.com/woop-chain/sdk/tree/master/packages/woop-contract)
8. [@woop-js/staking](https://github.com/woop-chain/sdk/tree/master/packages/woop-staking)
<mark>Package level documentation and examples are inside each package</mark>

@ -0,0 +1,58 @@
# Features
1. [x] Account instance
2. [x] Create Account
3. [x] Import prv key
4. [x] Import/Export keystore file
5. [x] BIP39
6. [x] BIP44
7. [WIP] Sign txn/message
8. [x] getBalance
9. [x] Wallet CRUD
# Wallet Usage
```js
import { Wallet } from '@harmony-js/account';
const wallet = new Wallet();
const mnes = wallet.generateMnemonic();
// add mnemonic and use index=0
const accountA = wallet.addByMnemonic(mnes,0);
// this account instance will be the wallet's signer by default
console.log(wallet.signer.address === accountA.address)
// true
wallet.encryptAccount(accountA.address,'easy password')
.then((encrypted)=>{
console.log(encrypted.privateKey);
// private key now is keyStoreV3 format string
wallet.decryptAccount(accountA.address,'easy password')
.then((decrypted)=>{
console.log(decrypted.privateKey);
// now private key is recovered
})
});
wallet.accounts
// it will display accounts addresses live in wallet
wallet.getAccount(accountA.address);
// it will get account instance by using address as reference
wallet.removeAccount(accountA.address);
// it will remove account instance from wallet
wallet.createAccount();
// it will create a new acount instance
wallet.addByPrivateKey(key);
// you can add private key directly as well
```

@ -1,7 +1,7 @@
{
"name": "@woop-js/contract",
"version": "0.1.58",
"description": "contract libraries for woop",
"name": "@harmony-js/account",
"version": "0.0.15",
"description": "account and wallet for harmony",
"main": "dist/index.js",
"node": "dist/index.js",
"browser": "dist/index.js",
@ -12,17 +12,17 @@
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "neeboo@firestack.one",
"publishConfig": {
"access": "public"
},
"license": "MIT",
"author": "neeboo@firestack.one",
"license": "ISC",
"dependencies": {
"@woop-js/account": "0.1.58",
"@woop-js/crypto": "0.1.58",
"@woop-js/network": "0.1.58",
"@woop-js/transaction": "0.1.58",
"@woop-js/utils": "0.1.58"
"@harmony-js/core": "0.0.15",
"@harmony-js/crypto": "0.0.15",
"@harmony-js/network": "0.0.15",
"@harmony-js/transaction": "0.0.15",
"@harmony-js/utils": "0.0.15"
},
"gitHead": "56606e9365721729a490c27d6a294e0daf90fbdf"
"gitHead": "938a044229b662d99c61e57417d1702d44aa7fe2"
}

@ -0,0 +1,225 @@
import {
generatePrivateKey,
getAddressFromPrivateKey,
getPubkeyFromPrivateKey,
toChecksumAddress,
encrypt,
decrypt,
EncryptOptions,
Keystore,
Signature,
} from '@harmony-js/crypto';
import { isPrivateKey, add0xToString, hexToNumber } from '@harmony-js/utils';
import { Transaction } from '@harmony-js/transaction';
import { Messenger, RPCMethod } from '@harmony-js/network';
import { Shards } from './types';
import { RLPSign, defaultMessenger } from './utils';
class Account {
/**
* @function new static method create account
* @return {Account} Account instance
*/
static new(): Account {
const newAcc = new Account()._new();
return newAcc;
}
/**
* @function add static method add a private key to Account
* @param {string} key - private Key
* @return {Account} Account instance
*/
static add(key: string): Account {
const newAcc = new Account()._import(key);
return newAcc;
}
privateKey?: string;
publicKey?: string;
address?: string;
balance?: string = '0';
nonce?: number = 0;
shards: Shards = new Map().set('default', '');
messenger: Messenger;
encrypted: boolean = false;
/**
* @function checksumAddress checsumAddress getter
* @return {string} get the checksumAddress
*/
get checksumAddress(): string {
return this.address ? toChecksumAddress(this.address) : '';
}
/**
* @function getShardsCount getShards number with this Account
* @return {number} shard size
*/
get getShardsCount(): number {
return this.shards.size;
}
constructor(key?: string, messenger: Messenger = defaultMessenger) {
this.messenger = messenger;
if (!key) {
this._new();
} else {
this._import(key);
}
}
async toFile(password: string, options?: EncryptOptions): Promise<string> {
if (this.privateKey && isPrivateKey(this.privateKey)) {
const file = await encrypt(this.privateKey, password, options);
this.privateKey = file;
this.encrypted = true;
return file;
} else {
throw new Error('Encryption failed because PrivateKey is not correct');
}
}
async fromFile(keyStore: string, password: string): Promise<Account> {
try {
if (!password) {
throw new Error('you must provide password');
}
const file: Keystore = JSON.parse(keyStore);
const decyptedPrivateKey = await decrypt(file, password);
if (isPrivateKey(decyptedPrivateKey)) {
return this._import(decyptedPrivateKey);
} else {
throw new Error('decrypted failed');
}
} catch (error) {
throw error;
}
}
/**
* @function getBalance get Account's balance
* @return {type} {description}
*/
async getBalance(blockNumber: string = 'latest'): Promise<object> {
try {
if (this.messenger) {
const balance = await this.messenger.send(RPCMethod.GetBalance, [
this.address,
blockNumber,
]);
const nonce = await this.messenger.send(RPCMethod.GetTransactionCount, [
this.address,
blockNumber,
]);
if (balance.isError()) {
throw balance.error.message;
}
if (nonce.isError()) {
throw nonce.error.message;
}
this.balance = hexToNumber(balance.result);
this.nonce = Number.parseInt(hexToNumber(nonce.result), 10);
} else {
throw new Error('No Messenger found');
}
return {
balance: this.balance,
nonce: this.nonce,
shards: this.shards,
};
} catch (error) {
throw error;
}
}
/**
* @function updateShards
* @return {Promise<string>} {description}
*/
async updateShards(): Promise<string> {
return '';
}
async signTransaction(
transaction: Transaction,
updateNonce: boolean = false,
encodeMode: string = 'rlp',
blockNumber: string = 'latest',
): Promise<Transaction> {
if (!this.privateKey || !isPrivateKey(this.privateKey)) {
throw new Error(`${this.privateKey} is not found or not correct`);
}
// let signed = '';
if (updateNonce) {
const balanceObject: any = await this.getBalance(blockNumber);
transaction.setParams({
...transaction.txParams,
from: this.address || '0x',
// nonce is different from Zilliqa's setting, would be current nonce, not nonce + 1
nonce: balanceObject.nonce,
});
}
if (encodeMode === 'rlp') {
const [signature, txnHash]: [Signature, string] = RLPSign(
transaction,
this.privateKey,
);
return transaction.map((obj: any) => {
return { ...obj, signature, txnHash, from: this.address };
});
} else {
// TODO: if we use other encode method, eg. protobuf, we should implement this
return transaction;
}
}
setMessenger(messenger: Messenger) {
this.messenger = messenger;
}
/**
* @function _new private method create Account
* @return {Account} Account instance
*/
private _new(): Account {
const prv = generatePrivateKey();
if (!isPrivateKey(prv)) {
throw new Error('key gen failed');
}
return this._import(prv);
}
/**
* @function _import private method import a private Key
* @param {string} key - private key
* @return {Account} Account instance
*/
private _import(key: string): Account {
if (!isPrivateKey(key)) {
throw new Error(`${key} is not PrivateKey`);
}
this.privateKey = add0xToString(key);
this.publicKey = getPubkeyFromPrivateKey(this.privateKey);
this.address = getAddressFromPrivateKey(this.privateKey);
this.shards = new Map().set('default', '');
this.encrypted = false;
return this;
}
// /**
// * @function addShard add shard to this Account
// * @param {ShardId} id - ShardId to the Account
// */
// private addShard(id: ShardId): void {
// if (this.shards && this.shards.has('default')) {
// this.shards.set(id, '');
// } else {
// throw new Error(
// 'This account has no default shard or shard is not exist',
// );
// }
// }
}
export { Account };

@ -1,11 +1,4 @@
/**
* @packageDocumentation
* @module woop-account
* @ignore
*/
export * from './account';
export * from './wallet';
export * from './types';
export * from './utils';
export * from './hdnode';

@ -0,0 +1,2 @@
export type ShardId = string | number;
export type Shards = Map<ShardId, string>;

@ -0,0 +1,24 @@
import { HttpProvider, Messenger } from '@harmony-js/network';
import { Transaction, TxParams } from '@harmony-js/transaction';
import { sign, keccak256, Signature } from '@harmony-js/crypto';
import { ChainType } from '@harmony-js/utils';
export const RLPSign = (
transaction: Transaction,
prv: string,
): [Signature, string] => {
const [unsignedTxnHash, raw] = transaction.getRLPUnsigned();
const regroup: TxParams = {
...transaction.txParams,
unsignedTxnHash,
};
transaction.setParams(regroup);
const signature = sign(keccak256(unsignedTxnHash), prv);
const signed = transaction.getRLPSigned(raw, signature);
return [signature, signed];
};
export const defaultMessenger = new Messenger(
new HttpProvider('http://localhost:8545'),
ChainType.Harmony,
);

@ -0,0 +1,291 @@
import { bip39, hdkey, EncryptOptions } from '@harmony-js/crypto';
import { Messenger } from '@harmony-js/network';
import { isPrivateKey, isAddress } from '@harmony-js/utils';
import { Account } from './account';
import { Transaction } from '@harmony-js/transaction';
import { defaultMessenger } from './utils';
class Wallet {
messenger: Messenger;
protected defaultSigner?: string;
/**
* @memberof Wallet
*
*/
private accountMap: Map<string, Account> = new Map();
/**
* @memberof Wallet
* @return {string[]} accounts addresses
*/
get accounts(): string[] {
return [...this.accountMap.keys()];
}
get signer(): Account | undefined {
if (this.defaultSigner) {
return this.getAccount(this.defaultSigner);
} else if (!this.defaultSigner && this.accounts.length > 0) {
this.setSigner(this.accounts[0]);
return this.getAccount(this.accounts[0]);
} else {
return undefined;
}
}
constructor(messenger: Messenger = defaultMessenger) {
this.messenger = messenger;
}
/**
* @function generateMnemonic
* @memberof Wallet
* @return {string} Mnemonics
*/
generateMnemonic(): string {
return bip39.generateMnemonic();
}
/**
* @function addByMnemonic
* @memberof Wallet
* @description add account using Mnemonic phrases
* @param {string} phrase - Mnemonic phrase
* @param {index} index - index to hdKey root
*/
addByMnemonic(phrase: string, index: number = 0) {
if (!this.isValidMnemonic(phrase)) {
throw new Error(`Invalid mnemonic phrase: ${phrase}`);
}
const seed = bip39.mnemonicToSeed(phrase);
const hdKey = hdkey.fromMasterSeed(seed);
// TODO:hdkey should apply to Harmony's settings
const path = '60';
const childKey = hdKey.derive(`m/44'/${path}'/0'/0/${index}`);
const privateKey = childKey.privateKey.toString('hex');
return this.addByPrivateKey(privateKey);
}
/**
* @function addByPrivateKey
* @memberof Wallet
* @description add an account using privateKey
* @param {string} privateKey - privateKey to add
* @return {Account} return added Account
*/
addByPrivateKey(privateKey: string): Account {
try {
const newAcc = Account.add(privateKey);
newAcc.setMessenger(this.messenger);
if (newAcc.address) {
this.accountMap.set(newAcc.address, newAcc);
if (!this.defaultSigner) {
this.setSigner(newAcc.address);
}
return newAcc;
} else {
throw new Error('add account failed');
}
} catch (error) {
throw error;
}
}
/**
* @function createAccount
* @description create a new account using Mnemonic
* @return {Account} {description}
*/
async createAccount(
password?: string,
options?: EncryptOptions,
): Promise<Account> {
const words = this.generateMnemonic();
const acc = this.addByMnemonic(words);
if (acc.address && password) {
const encrypted = await this.encryptAccount(
acc.address,
password,
options,
);
return encrypted;
} else if (acc.address && !password) {
return acc;
} else {
throw new Error('create acount failed');
}
}
/**
* @function encryptAccount
* @memberof Wallet
* @description to encrypt an account that lives in the wallet,
* if encrypted, returns original one, if not found, throw error
* @param {string} address - address in accounts
* @param {string} password - string that used to encrypt
* @param {EncryptOptions} options - encryption options
* @return {Promise<Account>}
*/
async encryptAccount(
address: string,
password: string,
options?: EncryptOptions,
): Promise<Account> {
try {
const foundAcc = this.getAccount(address);
if (
foundAcc &&
foundAcc.privateKey &&
isPrivateKey(foundAcc.privateKey)
) {
await foundAcc.toFile(password, options);
return foundAcc;
} else if (
foundAcc &&
foundAcc.privateKey &&
!isPrivateKey(foundAcc.privateKey)
) {
return foundAcc;
} else {
throw new Error('encrypt account failed');
}
} catch (error) {
throw error;
}
}
/**
* @function decryptAccount
* @memberof Wallet
* @description to decrypt an account that lives in the wallet,if not encrypted, return original,
* if not found, throw error
* @param {string} address - address in accounts
* @param {string} password - string that used to encrypt
* @return {Promise<Account>}
*/
async decryptAccount(address: string, password: string): Promise<Account> {
try {
const foundAcc = this.getAccount(address);
if (
foundAcc &&
foundAcc.privateKey &&
!isPrivateKey(foundAcc.privateKey)
) {
await foundAcc.fromFile(foundAcc.privateKey, password);
return foundAcc;
} else if (
foundAcc &&
foundAcc.privateKey &&
isPrivateKey(foundAcc.privateKey)
) {
foundAcc.encrypted = false;
return foundAcc;
} else {
throw new Error('decrypt account failed');
}
} catch (error) {
throw error;
}
}
/**
* @function getAccount
* @memberof Wallet
* @description get Account instance using address as param
* @param {string} address - address hex
* @return {Account} Account instance which lives in Wallet
*/
getAccount(address: string): Account | undefined {
return this.accountMap.get(address);
}
/**
* @function removeAccount
* @memberof Wallet
* @description remove Account using address as param
* @param {string} address: - address hex
*/
removeAccount(address: string): void {
this.accountMap.delete(address);
if (this.defaultSigner === address) {
this.defaultSigner = undefined;
}
}
setMessenger(messenger: Messenger): void {
this.messenger = messenger;
}
setSigner(address: string): void {
if (!isAddress(address) || !this.getAccount(address)) {
throw new Error('could not set signer');
}
this.defaultSigner = address;
}
async signTransaction(
transaction: Transaction,
account?: Account,
password?: string,
updateNonce: boolean = true,
encodeMode: string = 'rlp',
blockNumber: string = 'latest',
): Promise<Transaction> {
const toSignWith = account || this.signer;
if (!toSignWith) {
throw new Error('no signer found or did not provide correct account');
}
if (
toSignWith instanceof Account &&
toSignWith.encrypted &&
toSignWith.address
) {
if (!password) {
throw new Error('must provide password to further execution');
}
try {
const decrypted = await this.decryptAccount(
toSignWith.address,
password,
);
const signed = await decrypted.signTransaction(
transaction,
updateNonce,
encodeMode,
blockNumber,
);
await this.encryptAccount(toSignWith.address, password);
return signed;
} catch (error) {
throw error;
}
} else if (
toSignWith instanceof Account &&
!toSignWith.encrypted &&
toSignWith.address
) {
try {
const signed = await toSignWith.signTransaction(
transaction,
updateNonce,
encodeMode,
blockNumber,
);
return signed;
} catch (error) {
throw error;
}
} else {
throw new Error('sign transaction failed');
}
}
/**
* @function isValidMnemonic
* @memberof Wallet
* @description check if Mnemonic is valid
* @param {string} phrase - Mnemonic phrase
* @return {boolean}
*/
private isValidMnemonic(phrase: string): boolean {
if (phrase.trim().split(/\s+/g).length < 12) {
return false;
}
return bip39.validateMnemonic(phrase);
}
}
export { Wallet };

@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src", "../../typings/**/*.d.ts"],
"references": [
{ "path": "../harmony-crypto" },
{ "path": "../harmony-utils" },
{ "path": "../harmony-transaction" },
{ "path": "../harmony-network" }
]
}

@ -1,11 +1,6 @@
/**
* @packageDocumentation
* @ignore
*/
import { AbiCoder } from '../src/abi/abiCoder';
import { BN } from '@woop-js/crypto';
import { isArray } from '@woop-js/utils';
import { BN } from '@harmony-js/crypto';
import { isArray } from '@harmony-js/utils';
import { abis } from './fixtures/abiv2';
function getValues(object: any, format?: any, named?: any): any {

File diff suppressed because one or more lines are too long

@ -0,0 +1,28 @@
{
"name": "@harmony-js/contract",
"version": "0.0.15",
"description": "contract libraries for harmony",
"main": "dist/index.js",
"node": "dist/index.js",
"browser": "dist/index.js",
"module": "dist/index.esm.js",
"jsnext:main": "dist/index.esm.js",
"typings": "dist/index.d.ts",
"types": "dist/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "neeboo@firestack.one",
"publishConfig": {
"access": "public"
},
"license": "ISC",
"dependencies": {
"@harmony-js/account": "0.0.15",
"@harmony-js/crypto": "0.0.15",
"@harmony-js/network": "0.0.15",
"@harmony-js/transaction": "0.0.15",
"@harmony-js/utils": "0.0.15"
},
"gitHead": "938a044229b662d99c61e57417d1702d44aa7fe2"
}

@ -1,156 +1,3 @@
/**
# @woop-js/contract
This package provides a collection of apis to create, deploy, and interact with smart contracts. In Woop, smart contracts all fully EVM compatible and the formats and terminologies match 1-to-1 with EVM smart contracts.
## Installation
```
npm install @woop-js/contract
```
## Usage
Deploying a contract using `contractConstructor`
```javascript
const { ContractFactory } = require('@woop-js/contract');
const { Wallet } = require('@woop-js/account');
const { Messenger, HttpProvider } = require('@woop-js/network');
const { ChainID, ChainType, hexToNumber } = require('@woop-js/utils');
* const wallet = new Wallet(
* new Messenger(
* new HttpProvider('https://api.s0.b.hmny.io'),
* ChainType.Woop,
* ChainID.WikiTestnet,
* ),
* );
* const factory = new ContractFactory(wallet);
* const contractJson = require("./Counter.json");
* const contract = factory.createContract(contractJson.abi);
* const options1 = { gasPrice: '0x3B9ACA00' }; // gas price in hex corresponds to 1 Gwei or 1000000000
* let options2 = { gasPrice: 1000000000, gasLimit: 21000 }; // setting the default gas limit, but changing later based on estimate gas
* const options3 = { data: contractJson.bytecode }; // contractConstructor needs contract bytecode to deploy
* contract.wallet.addByPrivateKey('1f054c21a0f57ebc402c00e14bd1707ddf45542d4ed9989933dbefc4ea96ca68');
* contract.methods.contractConstructor(options3).estimateGas(options1).then(gas => {
* options2 = {...options2, gasLimit: hexToNumber(gas)};
* contract.methods.contractConstructor(options3).send(options2).then(response => {
* console.log('contract deployed at ' + response.transaction.receipt.contractAddress);
* });
* });
```
Instead of `contract.methods.contractConstructor`, `contract.deploy` could be used and it will work.
Loading a contract object using the contract json and contract address for interacting with it
```javascript
* const { Woop } = require("@woop-js/core");
* const { ChainID, ChainType } = require("@woop-js/utils");
* const wiki = new Woop("https://api.s0.b.hmny.io", {
* chainType: ChainType.Woop,
* chainId: ChainID.WikiTestnet,
* });
const contractJson = require("./Counter.json");
const contractAddr = "0x19f64050e6b2d376e52AC426E366c49EEb0724B1";
const contract = wiki.contracts.createContract(contractJson.abi, contractAddr);
console.log(contract.methods);
```
Directly loading contract using `ContractFactory`
```javascript
const { ContractFactory } = require('@woop-js/contract');
const { Wallet } = require('@woop-js/account');
const { Messenger, HttpProvider } = require('@woop-js/network');
const { ChainID, ChainType, hexToNumber } = require('@woop-js/utils');
* const wallet = new Wallet(new Messenger(
* new HttpProvider('https://api.s0.b.hmny.io'),
* ChainType.Woop,
* ChainID.WikiTestnet,
* ));
const factory = new ContractFactory(wallet);
const contract = factory.createContract(contractJson.abi, contractAddr);
```
Estimate gas for contract methods
```javascript
* const options1 = { gasPrice: '0x3B9ACA00' }; // gas price in hex corresponds to 1 Gwei or 1000000000
* contract.methods.getCount().estimateGas(options1).then(gas => {
* console.log('gas required for getCount is ' + hexToNumber(gas));
* });
```
Call contract read-only methods. Woop uses 1 Gwei gas price and gas limit of 21000 by default. Use the estimate gas api to correctly set the gas limit.
```javascript
* const options1 = { gasPrice: '0x3B9ACA00' }; // gas price in hex corresponds to 1 Gwei or 1000000000
* let options2 = { gasPrice: 1000000000, gasLimit: 21000 }; // setting the default gas limit, but changing later based on estimate gas
* contract.methods.getCount().estimateGas(options1).then(gas => {
* options2 = {...options2, gasLimit: hexToNumber(gas)};
* contract.methods.getCount().call(options2).then(count => {
* console.log('counter value: ' + count);
* });
* });
```
Invoking contract modification methods using `send` api. Need to add a signing account to the contract wallet, otherwise `send` api will not work.
```javascript
* const options1 = { gasPrice: '0x3B9ACA00' }; // gas price in hex corresponds to 1 Gwei or 1000000000
* let options2 = { gasPrice: 1000000000, gasLimit: 21000 }; // setting the default gas limit, but changing later based on estimate gas
* contract.wallet.addByPrivateKey('1f054c21a0f57ebc402c00e14bd1707ddf45542d4ed9989933dbefc4ea96ca68');
* contract.methods.incrementCounter().estimateGas(options1).then(gas => {
* options2 = {...options2, gasLimit: hexToNumber(gas)};
* contract.methods.incrementCounter().send(options2).then(response => {
* console.log(response.transaction.receipt);
* });
* });
```
All the above apis can also be asynchronously executed using `async` and `await`.
Subscribing to the contract events requires web socket based messenger.
```javascript
* const { ContractFactory } = require('@woop-js/contract');
* const { Wallet } = require('@woop-js/account');
* const { Messenger, WSProvider } = require('@woop-js/network');
* const { ChainID, ChainType, hexToNumber } = require('@woop-js/utils');
* const ws = new WSProvider('wss://ws.s0.b.hmny.io');
* const wallet = new Wallet(
* new Messenger(
* ws,
* ChainType.Woop,
* ChainID.WikiTestnet,
* ),
* );
* const factory = new ContractFactory(wallet);
* const contractJson = require("./Counter.json");
* const contractAddr = '0x8ada52172abda19b9838eb00498a40952be6a019';
* const contract = factory.createContract(contractJson.abi, contractAddr);
* contract.events
* .IncrementedBy()
* .on('data', (event) => {
* console.log(event);
* })
* .on('error', console.error);
```
*
* @packageDocumentation
* @module woop-contract
*/
// this file is mainly ported from `ethers.js`, but done some fixes
// 1. added bytesPadRight support
// 2. ts-lint
@ -171,26 +18,22 @@ import {
Arrayish,
checkNew,
bytesPadRight,
} from '@woop-js/crypto';
import { hexToBN, defineReadOnly } from '@woop-js/utils';
} from '@harmony-js/crypto';
import { hexToBN } from '@harmony-js/utils';
/** @hidden */
const NegativeOne: BN = new BN(-1);
/** @hidden */
const One: BN = new BN(1);
/** @hidden */
// const Two: BN = new BN(2);
const Zero: BN = new BN(0);
/** @hidden */
const HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000';
/** @hidden */
const HashZero =
'0x0000000000000000000000000000000000000000000000000000000000000000';
const MaxUint256: BN = hexToBN(
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
);
/** @hidden */
export type CoerceFunc = (type: string, value: any) => any;
/** @hidden */
export interface ParamType {
name?: string;
type: string;
@ -200,7 +43,6 @@ export interface ParamType {
// @TODO: should this just be a combined Fragment?
/** @hidden */
export interface EventFragment {
type: string;
name: string;
@ -210,7 +52,6 @@ export interface EventFragment {
inputs: ParamType[];
}
/** @hidden */
export interface FunctionFragment {
type: string;
name: string;
@ -227,15 +68,15 @@ export interface FunctionFragment {
}
///////////////////////////////
/** @hidden */
const paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
/** @hidden */
const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
/** @hidden */
const paramTypeArray = new RegExp(/^(.*)\[([0-9]*)\]$/);
/** @hidden */
export const defaultCoerceFunc: CoerceFunc = (type: string, value: any): any => {
export const defaultCoerceFunc: CoerceFunc = (
type: string,
value: any,
): any => {
const match = type.match(paramTypeNumber);
if (match && parseInt(match[2], 10) <= 48) {
// return value.toNumber();
@ -250,12 +91,9 @@ export const defaultCoerceFunc: CoerceFunc = (type: string, value: any): any =>
///////////////////////////////////
// Parsing for Solidity Signatures
/** @hidden */
const regexParen = new RegExp('^([^)(]*)\\((.*)\\)([^)(]*)$');
/** @hidden */
const regexIdentifier = new RegExp('^[A-Za-z_][A-Za-z0-9_]*$');
/** @hidden */
function verifyType(type: string): string {
// These need to be transformed to their full description
if (type.match(/^uint($|[^1-9])/)) {
@ -267,7 +105,6 @@ function verifyType(type: string): string {
return type;
}
/** @hidden */
interface ParseState {
allowArray?: boolean;
allowName?: boolean;
@ -276,7 +113,6 @@ interface ParseState {
readArray?: boolean;
}
/** @hidden */
interface ParseNode {
parent?: any;
type?: string;
@ -286,7 +122,6 @@ interface ParseNode {
components?: any[];
}
/** @hidden */
function parseParam(param: string, allowIndexed?: boolean): ParamType {
const originalParam = param;
// tslint:disable-next-line: no-shadowed-variable
@ -320,7 +155,9 @@ function parseParam(param: string, allowIndexed?: boolean): ParamType {
node.type = verifyType(node.type);
}
node.components = [{ type: '', name: '', parent: node, state: { allowType: true } }];
node.components = [
{ type: '', name: '', parent: node, state: { allowType: true } },
];
node = node.components[0];
break;
@ -461,7 +298,6 @@ function parseParam(param: string, allowIndexed?: boolean): ParamType {
}
// @TODO: Better return type
/** @hidden */
function parseSignatureEvent(fragment: string): EventFragment {
const abi: EventFragment = {
anonymous: false,
@ -502,18 +338,15 @@ function parseSignatureEvent(fragment: string): EventFragment {
return abi;
}
/** @hidden */
export function parseParamType(type: string): ParamType {
return parseParam(type, true);
}
// @TODO: Allow a second boolean to expose names
/** @hidden */
export function formatParamType(paramType: ParamType): string {
return getParamCoder(defaultCoerceFunc, paramType).type;
}
/** @hidden */
function parseSignatureFunction(fragment: string): FunctionFragment {
const abi: FunctionFragment = {
constant: false,
@ -582,7 +415,7 @@ function parseSignatureFunction(fragment: string): FunctionFragment {
// We have outputs
if (comps.length > 1) {
const right = comps[1].match(regexParen);
if (right === null || right[1].trim() !== '' || right[3].trim() !== '') {
if (right === null || (right[1].trim() !== '' || right[3].trim() !== '')) {
throw new Error('unexpected tokens');
}
@ -606,13 +439,20 @@ function parseSignatureFunction(fragment: string): FunctionFragment {
}
// @TODO: Allow a second boolean to expose names and modifiers
/** @hidden */
export function formatSignature(fragment: EventFragment | FunctionFragment): string {
return fragment.name + '(' + fragment.inputs.map((i) => formatParamType(i)).join(',') + ')';
export function formatSignature(
fragment: EventFragment | FunctionFragment,
): string {
return (
fragment.name +
'(' +
fragment.inputs.map((i) => formatParamType(i)).join(',') +
')'
);
}
/** @hidden */
export function parseSignature(fragment: string): EventFragment | FunctionFragment {
export function parseSignature(
fragment: string,
): EventFragment | FunctionFragment {
if (typeof fragment === 'string') {
// Make sure the "returns" is surrounded by a space and all whitespace is exactly one space
fragment = fragment.replace(/\s/g, ' ');
@ -637,13 +477,11 @@ export function parseSignature(fragment: string): EventFragment | FunctionFragme
///////////////////////////////////
// Coders
/** @hidden */
interface DecodedResult {
consumed: number;
value: any;
}
/** @hidden */
abstract class Coder {
readonly coerceFunc: CoerceFunc;
readonly name: string;
@ -670,7 +508,6 @@ abstract class Coder {
// Clones the functionality of an existing Coder, but without a localName
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderAnonymous extends Coder {
private coder: Coder;
constructor(coder: Coder) {
@ -686,7 +523,6 @@ class CoderAnonymous extends Coder {
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderNull extends Coder {
constructor(coerceFunc: CoerceFunc, localName: string) {
super(coerceFunc, 'null', '', localName, false);
@ -709,11 +545,15 @@ class CoderNull extends Coder {
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderNumber extends Coder {
readonly size: number;
readonly signed: boolean;
constructor(coerceFunc: CoerceFunc, size: number, signed: boolean, localName: string) {
constructor(
coerceFunc: CoerceFunc,
size: number,
signed: boolean,
localName: string,
) {
const name = (signed ? 'int' : 'uint') + size * 8;
super(coerceFunc, name, name, localName, false);
@ -724,12 +564,7 @@ class CoderNumber extends Coder {
encode(value: BN | number | string): Uint8Array {
let result;
try {
let v: BN;
if (typeof value == 'string' && value.startsWith('0x')) {
v = new BN(value.slice(2), 'hex');
} else {
v = new BN(value);
}
let v = new BN(value);
if (this.signed) {
let bounds = MaxUint256.maskn(this.size * 8 - 1);
if (v.gt(bounds)) {
@ -762,11 +597,15 @@ class CoderNumber extends Coder {
decode(data: Uint8Array, offset: number): DecodedResult {
if (data.length < offset + 32) {
throwError('insufficient data for ' + this.name + ' type', INVALID_ARGUMENT, {
arg: this.localName,
coderType: this.name,
value: hexlify(data.slice(offset, offset + 32)),
});
throwError(
'insufficient data for ' + this.name + ' type',
INVALID_ARGUMENT,
{
arg: this.localName,
coderType: this.name,
value: hexlify(data.slice(offset, offset + 32)),
},
);
}
const junkLength = 32 - this.size;
const dataValue = hexlify(data.slice(offset + junkLength, offset + 32));
@ -785,8 +624,6 @@ class CoderNumber extends Coder {
};
}
}
/** @hidden */
const uint256Coder = new CoderNumber(
(type: string, value: any) => {
return value;
@ -797,7 +634,6 @@ const uint256Coder = new CoderNumber(
);
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderBoolean extends Coder {
constructor(coerceFunc: CoerceFunc, localName: string) {
super(coerceFunc, 'bool', 'bool', localName, false);
@ -829,7 +665,6 @@ class CoderBoolean extends Coder {
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderFixedBytes extends Coder {
readonly length: number;
constructor(coerceFunc: CoerceFunc, length: number, localName: string) {
@ -876,13 +711,15 @@ class CoderFixedBytes extends Coder {
return {
consumed: 32,
value: this.coerceFunc(this.name, hexlify(data.slice(offset, offset + this.length))),
value: this.coerceFunc(
this.name,
hexlify(data.slice(offset, offset + this.length)),
),
};
}
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderAddress extends Coder {
constructor(coerceFunc: CoerceFunc, localName: string) {
super(coerceFunc, 'address', 'address', localName, false);
@ -919,7 +756,6 @@ class CoderAddress extends Coder {
}
}
/** @hidden */
function _encodeDynamicBytes(value: Uint8Array): Uint8Array {
const dataLength = 32 * Math.ceil(value.length / 32);
const padding = new Uint8Array(dataLength - value.length);
@ -927,8 +763,11 @@ function _encodeDynamicBytes(value: Uint8Array): Uint8Array {
return concat([uint256Coder.encode(new BN(value.length)), value, padding]);
}
/** @hidden */
function _decodeDynamicBytes(data: Uint8Array, offset: number, localName: string): DecodedResult {
function _decodeDynamicBytes(
data: Uint8Array,
offset: number,
localName: string,
): DecodedResult {
if (data.length < offset + 32) {
throwError('insufficient data for dynamicBytes length', INVALID_ARGUMENT, {
arg: localName,
@ -964,7 +803,6 @@ function _decodeDynamicBytes(data: Uint8Array, offset: number, localName: string
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderDynamicBytes extends Coder {
constructor(coerceFunc: CoerceFunc, localName: string) {
super(coerceFunc, 'bytes', 'bytes', localName, true);
@ -991,7 +829,6 @@ class CoderDynamicBytes extends Coder {
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderString extends Coder {
constructor(coerceFunc: CoerceFunc, localName: string) {
super(coerceFunc, 'string', 'string', localName, true);
@ -1015,12 +852,10 @@ class CoderString extends Coder {
}
}
/** @hidden */
function alignSize(size: number): number {
return 32 * Math.ceil(size / 32);
}
/** @hidden */
function pack(coders: Coder[], values: any[]): Uint8Array {
if (Array.isArray(values)) {
// do nothing
@ -1085,8 +920,11 @@ function pack(coders: Coder[], values: any[]): Uint8Array {
return data;
}
/** @hidden */
function unpack(coders: Coder[], data: Uint8Array, offset: number): DecodedResult {
function unpack(
coders: Coder[],
data: Uint8Array,
offset: number,
): DecodedResult {
const baseOffset = offset;
let consumed = 0;
const value: any = [];
@ -1133,11 +971,15 @@ function unpack(coders: Coder[], data: Uint8Array, offset: number): DecodedResul
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderArray extends Coder {
readonly coder: Coder;
readonly length: number;
constructor(coerceFunc: CoerceFunc, coder: Coder, length: number, localName: string) {
constructor(
coerceFunc: CoerceFunc,
coder: Coder,
length: number,
localName: string,
) {
const type = coder.type + '[' + (length >= 0 ? length : '') + ']';
const dynamic = length === -1 || coder.dynamic;
super(coerceFunc, 'array', type, localName, dynamic);
@ -1191,11 +1033,15 @@ class CoderArray extends Coder {
try {
decodedLength = uint256Coder.decode(data, offset);
} catch (error) {
throwError('insufficient data for dynamic array length', INVALID_ARGUMENT, {
arg: this.localName,
coderType: 'array',
value: error.value,
});
throwError(
'insufficient data for dynamic array length',
INVALID_ARGUMENT,
{
arg: this.localName,
coderType: 'array',
value: error.value,
},
);
}
try {
count = decodedLength.value.toNumber();
@ -1223,7 +1069,6 @@ class CoderArray extends Coder {
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
class CoderTuple extends Coder {
readonly coders: Coder[];
constructor(coerceFunc: CoerceFunc, coders: Coder[], localName: string) {
@ -1253,7 +1098,6 @@ class CoderTuple extends Coder {
}
}
/** @hidden */
function splitNesting(value: string): any[] {
value = value.trim();
@ -1286,8 +1130,15 @@ function splitNesting(value: string): any[] {
return result;
}
export function defineReadOnly(object: any, name: string, value: any): void {
Object.defineProperty(object, name, {
enumerable: true,
value,
writable: false,
});
}
// @TODO: Is there a way to return "class"?
/** @hidden */
const paramTypeSimple: { [key: string]: any } = {
address: CoderAddress,
bool: CoderBoolean,
@ -1295,7 +1146,6 @@ const paramTypeSimple: { [key: string]: any } = {
bytes: CoderDynamicBytes,
};
/** @hidden */
function getTupleParamCoder(
coerceFunc: CoerceFunc,
components: any[],
@ -1312,7 +1162,6 @@ function getTupleParamCoder(
return new CoderTuple(coerceFunc, coders, localName);
}
/** @hidden */
function getParamCoder(coerceFunc: CoerceFunc, param: ParamType | any): any {
const coder = paramTypeSimple[param.type];
if (coder) {
@ -1327,7 +1176,12 @@ function getParamCoder(coerceFunc: CoerceFunc, param: ParamType | any): any {
value: param,
});
}
return new CoderNumber(coerceFunc, size / 8, matcher[1] === 'int', param.name || '');
return new CoderNumber(
coerceFunc,
size / 8,
matcher[1] === 'int',
param.name || '',
);
}
const matcher2 = param.type.match(paramTypeBytes);
@ -1348,11 +1202,20 @@ function getParamCoder(coerceFunc: CoerceFunc, param: ParamType | any): any {
param = shallowCopy(param);
param.type = matcher3[1];
param = deepCopy(param);
return new CoderArray(coerceFunc, getParamCoder(coerceFunc, param), size, param.name || '');
return new CoderArray(
coerceFunc,
getParamCoder(coerceFunc, param),
size,
param.name || '',
);
}
if (param.type.substring(0, 5) === 'tuple') {
return getTupleParamCoder(coerceFunc, param.components || [], param.name || '');
return getTupleParamCoder(
coerceFunc,
param.components || [],
param.name || '',
);
}
if (param.type === '') {
@ -1365,7 +1228,6 @@ function getParamCoder(coerceFunc: CoerceFunc, param: ParamType | any): any {
});
}
/** @hidden */
export enum UnicodeNormalizationForm {
current = '',
NFC = 'NFC',
@ -1374,7 +1236,6 @@ export enum UnicodeNormalizationForm {
NFKD = 'NFKD',
}
/** @hidden */
export function toUtf8Bytes(
str: string,
form: UnicodeNormalizationForm = UnicodeNormalizationForm.current,
@ -1418,7 +1279,6 @@ export function toUtf8Bytes(
}
// http://stackoverflow.com/questions/13356493/decode-utf-8-with-javascript#13691499
/** @hidden */
export function toUtf8String(bytes: Arrayish, ignoreErrors?: boolean): string {
bytes = arrayify(bytes) || new Uint8Array();
@ -1455,7 +1315,9 @@ export function toUtf8String(bytes: Arrayish, ignoreErrors?: boolean): string {
} else {
if (!ignoreErrors) {
if ((c & 0xc0) === 0x80) {
throw new Error('invalid utf8 byte sequence; unexpected continuation byte');
throw new Error(
'invalid utf8 byte sequence; unexpected continuation byte',
);
}
throw new Error('invalid utf8 byte sequence; invalid prefix');
}
@ -1496,7 +1358,9 @@ export function toUtf8String(bytes: Arrayish, ignoreErrors?: boolean): string {
if (res === null) {
if (!ignoreErrors) {
throw new Error('invalid utf8 byte sequence; invalid continuation byte');
throw new Error(
'invalid utf8 byte sequence; invalid continuation byte',
);
}
continue;
}
@ -1531,13 +1395,15 @@ export function toUtf8String(bytes: Arrayish, ignoreErrors?: boolean): string {
}
res -= 0x10000;
result += String.fromCharCode(((res >> 10) & 0x3ff) + 0xd800, (res & 0x3ff) + 0xdc00);
result += String.fromCharCode(
((res >> 10) & 0x3ff) + 0xd800,
(res & 0x3ff) + 0xdc00,
);
}
return result;
}
/** @hidden */
export function formatBytes32String(text: string): string {
// Get the bytes
const bytes = toUtf8Bytes(text);
@ -1551,7 +1417,6 @@ export function formatBytes32String(text: string): string {
return hexlify(concat([bytes, HashZero]).slice(0, 32));
}
/** @hidden */
export function parseBytes32String(bytes: Arrayish): string {
const data = arrayify(bytes) || new Uint8Array();
@ -1573,12 +1438,10 @@ export function parseBytes32String(bytes: Arrayish): string {
return toUtf8String(data.slice(0, length));
}
/** @hidden */
export function isType(object: any, type: string): boolean {
return object && object._ethersType === type;
}
/** @hidden */
export function shallowCopy(object: any): any {
const result: any = {};
// tslint:disable-next-line: forin
@ -1588,14 +1451,12 @@ export function shallowCopy(object: any): any {
return result;
}
/** @hidden */
const opaque: { [key: string]: boolean } = {
boolean: true,
number: true,
string: true,
};
/** @hidden */
export function deepCopy(object: any, frozen?: boolean): any {
// Opaque objects are not mutable, so safe to copy by assignment
if (object === undefined || object === null || opaque[typeof object]) {
@ -1649,7 +1510,6 @@ export function deepCopy(object: any, frozen?: boolean): any {
}
// tslint:disable-next-line: max-classes-per-file
/** @hidden */
export class AbiCoder {
coerceFunc: CoerceFunc;
constructor(coerceFunc?: CoerceFunc) {
@ -1685,7 +1545,9 @@ export class AbiCoder {
coders.push(getParamCoder(this.coerceFunc, typeObject));
}, this);
const encodedArray = new CoderTuple(this.coerceFunc, coders, '_').encode(values);
const encodedArray = new CoderTuple(this.coerceFunc, coders, '_').encode(
values,
);
return hexlify(encodedArray);
}
@ -1711,5 +1573,4 @@ export class AbiCoder {
}
}
/** @hidden */
export const defaultAbiCoder: AbiCoder = new AbiCoder();

@ -1,12 +1,6 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { AbiCoder as ABICoder, ParamType, toUtf8Bytes } from './abiCoder';
import { isObject, isArray } from '@woop-js/utils';
import { keccak256, Arrayish } from '@woop-js/crypto';
import { isObject, isArray } from '@harmony-js/utils';
import { keccak256, Arrayish } from '@harmony-js/crypto';
import { jsonInterfaceMethodToString, bnToString } from './utils';
export class AbiCoderClass {
@ -130,7 +124,9 @@ export class AbiCoderClass {
decodedValue = values[index];
returnValues[itemKey] = bnToString(decodedValue);
returnValues[nonIndexedInputItems[index].name] = bnToString(decodedValue);
returnValues[nonIndexedInputItems[index].name] = bnToString(
decodedValue,
);
});
}

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { AbiCoderClass } from './api';
import { AbiCoder as EtherCoder } from './abiCoder';

@ -1,11 +1,5 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { isObject, isArray } from '@woop-js/utils';
import { BN } from '@woop-js/crypto';
import { isObject, isArray } from '@harmony-js/utils';
import { BN } from '@harmony-js/crypto';
export const jsonInterfaceMethodToString = (json: any): string => {
if (isObject(json) && json.name && json.name.includes('(')) {
@ -22,7 +16,9 @@ export const flattenTypes = (includeTuple: any, puts: any[]) => {
puts.forEach((param: any) => {
if (typeof param.components === 'object') {
if (param.type.substring(0, 5) !== 'tuple') {
throw new Error('components found but type is not tuple; report on GitHub');
throw new Error(
'components found but type is not tuple; report on GitHub',
);
}
let suffix = '';
const arrayBracket = param.type.indexOf('[');

@ -1,12 +1,6 @@
/**
* @packageDocumentation
* @module woop-contract
*
*/
import { Wallet } from '@woop-js/account';
import { Messenger } from '@woop-js/network';
import { Transaction } from '@woop-js/transaction';
import { Wallet } from '@harmony-js/account';
// import { Emitter } from '@harmony-js/network';
import { Transaction } from '@harmony-js/transaction';
import { AbiCoder } from './abi/index';
import { abiMapper } from './utils/mapper';
import { ContractOptions } from './utils/options';
@ -20,18 +14,12 @@ import { ContractStatus } from './utils/status';
export class Contract {
methods: any;
events: any;
fallback: any = undefined;
receive: any = undefined;
abi: any = [];
abiModel: any | AbiModel;
abiCoder: AbiCoderClass;
options: ContractOptions | any;
wallet: Wallet | any;
wallet: Wallet;
transaction?: Transaction;
status: ContractStatus;
shardID: number;
errorFunc: string = 'Error(string)';
errorFuncSig: string;
constructor(
abi: any = [],
@ -41,19 +29,16 @@ export class Contract {
status: ContractStatus = ContractStatus.INITIALISED,
) {
// super();
this.abi = abi;
this.abiCoder = AbiCoder();
this.abiModel = abiMapper(abi, this.abiCoder);
this.options = options;
this.address = this.options.address || address;
this.shardID = this.options.shardID || wallet.messenger.currentShard;
this.wallet = wallet;
this.methods = {};
this.events = {};
this.runMethodFactory();
this.runEventFactory();
this.status = status;
this.errorFuncSig = this.abiCoder.encodeFunctionSignature(this.errorFunc);
// tslint:disable-next-line: no-unused-expression
}
isInitialised() {
@ -115,14 +100,4 @@ export class Contract {
runEventFactory(): Contract {
return new EventFactory(this).addEventsToContract();
}
connect(wallet: Wallet): void {
this.wallet = wallet;
}
setMessenger(messenger: Messenger) {
if (this.wallet instanceof Wallet) {
this.wallet.setMessenger(messenger);
} else {
this.wallet.messenger = messenger;
}
}
}

@ -1,16 +1,11 @@
/**
* @packageDocumentation
* @module woop-contract
*/
import { Wallet } from '@woop-js/account';
import { Wallet } from '@harmony-js/account';
import { Contract } from './contract';
import { ContractOptions } from './utils/options';
export class ContractFactory {
wallet: Wallet | any;
wallet: Wallet;
constructor(wallet: Wallet | any) {
constructor(wallet: Wallet) {
this.wallet = wallet;
}
createContract(abi: any[], address?: string, options?: ContractOptions) {

@ -1,10 +1,5 @@
/**
* @packageDocumentation
* @module woop-contract
*/
import { LogSub } from '@woop-js/network';
import { AbiItemModel } from '../models/types';
import { LogSub } from '@harmony-js/network';
import { Contract } from '../contract';
import { decode as eventLogDecoder } from '../utils/decoder';
import { inputLogFormatter, outputLogFormatter } from '../utils/formatter';
@ -13,13 +8,18 @@ export class EventMethod extends LogSub {
methodKey: string;
contract: Contract;
abiItem: AbiItemModel;
constructor(methodKey: string, params: any, abiItem: AbiItemModel, contract: Contract) {
super(inputLogFormatter(params), contract.wallet.messenger, contract.shardID);
constructor(
methodKey: string,
params: any,
abiItem: AbiItemModel,
contract: Contract,
) {
super(inputLogFormatter(params), contract.wallet.messenger);
this.methodKey = methodKey;
this.contract = contract;
this.params = params;
this.abiItem = abiItem;
// this.subscribe();
super.subscribe();
}
// call() {}
@ -27,10 +27,12 @@ export class EventMethod extends LogSub {
// encodeABI() {}
onNewSubscriptionItem(subscriptionItem: any) {
const formatted = outputLogFormatter(
subscriptionItem.method !== undefined ? subscriptionItem.params.result : subscriptionItem,
// const log = outputLogFormatter(subscriptionItem);
const log = eventLogDecoder(
this.contract.abiCoder,
this.abiItem,
outputLogFormatter(subscriptionItem),
);
const log = eventLogDecoder(this.contract.abiCoder, this.abiItem, formatted);
if (log.removed && this.emitter) {
this.emitter.emit('changed', log);

@ -0,0 +1,41 @@
import { AbiCoderClass } from '../abi/api';
import { AbiModel } from '../models/types';
import { Contract } from '../contract';
import { EventMethod } from './event';
export class EventFactory {
contract: Contract;
abiModel: any | AbiModel;
abiCoder: AbiCoderClass;
private eventKeys: string[];
// constructor
constructor(contract: Contract) {
this.contract = contract;
this.abiModel = this.contract.abiModel;
this.abiCoder = this.contract.abiCoder;
this.eventKeys = this.mapEventKeys();
}
addEventsToContract() {
this.eventKeys.forEach((key: string) => {
const newObject: any = {};
newObject[key] = (params: any) =>
new EventMethod(
key,
params,
this.abiModel.getEvent(key),
this.contract,
);
Object.assign(this.contract.events, newObject);
});
return this.contract;
}
/**
* @function mapMethodKeys
* @return {string[]} {description}
*/
private mapEventKeys(): string[] {
return Object.keys(this.abiModel.abi.events);
}
}

@ -0,0 +1,10 @@
export * from './abi/index';
export {
toUtf8Bytes,
toUtf8String,
formatBytes32String,
parseBytes32String,
} from './abi/abiCoder';
export { Contract } from './contract';
export { ContractFactory } from './contractFactory';

@ -0,0 +1,224 @@
import { Wallet } from '@harmony-js/account';
import {
TransactionFactory,
Transaction,
// TxStatus,
} from '@harmony-js/transaction';
import { RPCMethod, getResultForData, Emitter } from '@harmony-js/network';
import { hexToNumber, hexToBN } from '@harmony-js/utils';
import { AbiItemModel } from '../models/types';
import { Contract } from '../contract';
import { methodEncoder } from '../utils/encoder';
import { ContractStatus } from '../utils/status';
// todo: have to judge if it is contractConstructor
export class ContractMethod {
contract: Contract;
params: any;
methodKey: string;
wallet: Wallet;
abiItem: AbiItemModel;
protected transaction: Transaction;
constructor(
methodKey: string,
params: any,
abiItem: AbiItemModel,
contract: Contract,
) {
this.methodKey = methodKey;
this.contract = contract;
this.wallet = contract.wallet;
this.params = params;
this.abiItem = abiItem;
this.transaction = this.createTransaction();
// this.addEventListeners();
}
send(params: any): Emitter {
try {
this.transaction = this.transaction.map((tx: any) => {
return { ...tx, ...params };
});
this.signTransaction().then((signed) => {
this.sendTransaction(signed).then((sent) => {
const [txn, id] = sent;
this.transaction = txn;
this.contract.transaction = this.transaction;
this.confirm(id).then(() => {
this.transaction.emitter.resolve(this.contract);
});
});
});
return this.transaction.emitter;
} catch (error) {
throw error;
}
}
async call(options: any, blockNumber: any = 'latest') {
try {
const nonce = getResultForData(
await this.wallet.messenger.send(RPCMethod.GetTransactionCount, [
this.wallet.signer ? this.wallet.signer.address : options.from,
blockNumber,
]),
);
let gasLimit: any;
// tslint:disable-next-line: prefer-conditional-expression
if (options) {
gasLimit = options.gas || options.gasLimit;
} else {
gasLimit = hexToBN(await this.estimateGas());
}
this.transaction = this.transaction.map((tx: any) => {
return {
...tx,
...options,
from: options
? options.from
: this.wallet.signer
? this.wallet.signer.address
: tx.from,
gasPrice: options ? options.gasPrice : tx.gasPrice,
gasLimit: gasLimit || tx.gasLimit,
nonce: Number.parseInt(hexToNumber(nonce), 10),
};
});
const result = await this.wallet.messenger.send(RPCMethod.Call, [
this.transaction.txPayload,
blockNumber,
]);
if (result.isError()) {
throw result.message;
} else if (result.isResult()) {
if (result.result === null) {
return this.afterCall(undefined);
} else {
return this.afterCall(result.result);
}
}
} catch (error) {
throw error;
}
}
async estimateGas() {
try {
const result = getResultForData(
await this.wallet.messenger.send(RPCMethod.EstimateGas, [
{
to: this.transaction.txParams.to,
data: this.transaction.txParams.data,
},
]),
);
if (result.responseType === 'error') {
throw result.message;
} else if (result.responseType === 'raw') {
throw new Error('Get estimateGas fail');
} else {
return result;
}
} catch (error) {
throw error;
}
}
encodeABI() {
return methodEncoder(
this.contract.abiCoder,
this.abiItem,
this.contract.data,
);
}
protected async signTransaction() {
try {
const signed = await this.wallet.signTransaction(
this.transaction,
undefined,
undefined,
true,
'rlp',
'pending',
);
this.contract.address = TransactionFactory.getContractAddress(signed);
this.contract.setStatus(ContractStatus.SIGNED);
return signed;
} catch (error) {
throw error;
}
}
protected async sendTransaction(signed: Transaction) {
try {
const result = await signed.sendTransaction();
this.contract.setStatus(ContractStatus.SENT);
return result;
} catch (error) {
throw error;
}
}
protected async confirm(id: string) {
try {
const result = await this.transaction.confirm(id);
if (result.receipt && result.receipt.status === '0x1') {
if (this.methodKey === 'contractConstructor') {
this.contract.setStatus(ContractStatus.DEPLOYED);
} else {
this.contract.setStatus(ContractStatus.CALLED);
}
} else {
this.contract.setStatus(ContractStatus.REJECTED);
}
} catch (error) {
throw error;
}
}
protected createTransaction() {
if (this.wallet.messenger) {
if (this.methodKey === 'contractConstructor') {
// tslint:disable-next-line: no-string-literal
this.contract.data = this.params[0]['data'] || '0x';
this.abiItem.contractMethodParameters =
// tslint:disable-next-line: no-string-literal
this.params[0]['arguments'] || [];
} else {
this.abiItem.contractMethodParameters = this.params || [];
}
const txObject = {
...this.params[0],
to: this.contract.address,
data: this.encodeABI(),
};
const result = new TransactionFactory(this.wallet.messenger).newTx(
txObject,
);
return result;
} else {
throw new Error('Messenger is not found');
}
}
protected afterCall(response: any) {
if (!response || response === '0x') {
return null;
}
const outputs = this.abiItem.getOutputs();
if (outputs.length > 1) {
return this.contract.abiCoder.decodeParameters(outputs, response);
}
return this.contract.abiCoder.decodeParameter(outputs[0], response);
// return outputs;
}
}

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { AbiCoderClass } from '../abi/api';
import { AbiModel } from '../models/types';
import { Contract } from '../contract';
@ -27,18 +21,15 @@ export class MethodFactory {
this.methodKeys.forEach((key: string) => {
const newObject: any = {};
newObject[key] = (...params: any[]) =>
new ContractMethod(key, params, this.abiModel.getMethod(key), this.contract);
new ContractMethod(
key,
params,
this.abiModel.getMethod(key),
this.contract,
);
Object.assign(this.contract.methods, newObject);
});
if (this.abiModel.hasFallback()) {
this.contract.fallback = (calldata: string) =>
new ContractMethod('fallback', [calldata], this.abiModel.getFallback(), this.contract);
}
if (this.abiModel.hasReceive()) {
this.contract.receive = () =>
new ContractMethod('receive', [], this.abiModel.getReceive(), this.contract);
}
return this.contract;
}
/**

@ -1,10 +1,4 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { isArray } from '@woop-js/utils';
import { isArray } from '@harmony-js/utils';
import { AbiItemModel, AbiOutput, AbiInput } from './types';
export class AbiItem {

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { AbiItemModel } from './types';
export class AbiModel {
@ -33,20 +27,6 @@ export class AbiModel {
return false;
}
getFallback(): AbiItemModel | false {
if (this.hasFallback()) {
return this.abi.fallback;
}
return false;
}
getReceive(): AbiItemModel | false {
if (this.hasReceive()) {
return this.abi.receive;
}
return false;
}
getEvents(): AbiItemModel[] {
return this.abi.events;
}
@ -67,14 +47,6 @@ export class AbiModel {
return typeof this.abi.methods[name] !== 'undefined';
}
hasFallback(): boolean {
return typeof this.abi.fallback !== 'undefined';
}
hasReceive(): boolean {
return typeof this.abi.receive !== 'undefined';
}
hasEvent(name: string): boolean {
return typeof this.abi.events[name] !== 'undefined';
}

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
// defined by web3.js
// fixed
export interface AbiModel {

@ -1,13 +1,11 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { AbiItemModel } from '../models/types';
import { AbiCoderClass } from '../abi/api';
export const decode = (abiCoder: AbiCoderClass, abiItemModel: AbiItemModel, response: any) => {
export const decode = (
abiCoder: AbiCoderClass,
abiItemModel: AbiItemModel,
response: any,
) => {
let argumentTopics = response.topics;
if (!abiItemModel.anonymous) {

@ -0,0 +1,33 @@
import { AbiItemModel } from '../models/types';
import { AbiCoderClass } from '../abi/api';
export const methodEncoder = (
abiCoder: AbiCoderClass,
abiItemModel: AbiItemModel,
deployData: string,
) => {
let encodedParameters = abiCoder.encodeParameters(
abiItemModel.getInputs(),
abiItemModel.contractMethodParameters,
);
if (encodedParameters.startsWith('0x')) {
encodedParameters = encodedParameters.slice(2);
}
if (abiItemModel.isOfType('constructor')) {
if (!deployData) {
throw new Error(
'The contract has no contract data option set. This is necessary to append the constructor parameters.',
);
}
return deployData + encodedParameters;
}
if (abiItemModel.isOfType('function')) {
return abiItemModel.signature + encodedParameters;
}
return encodedParameters;
};

@ -1,18 +1,16 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { hexlify, isHexString, keccak256, toChecksumAddress } from '@woop-js/crypto';
import {
hexlify,
isHexString,
keccak256,
toChecksumAddress,
} from '@harmony-js/crypto';
import {
numberToHex,
isArray,
// hexToNumber,
hexToNumber,
isString,
isAddress,
hexToBN,
} from '@woop-js/utils';
} from '@harmony-js/utils';
import { toUtf8Bytes } from '../abi/abiCoder';
export const inputLogFormatter = (options: any) => {
@ -60,8 +58,7 @@ export const outputLogFormatter = (log: any) => {
typeof log.logIndex === 'string'
) {
const shaId = keccak256(
'0x' +
log.blockHash.replace('0x', '') +
log.blockHash.replace('0x', '') +
log.transactionHash.replace('0x', '') +
log.logIndex.replace('0x', ''),
);
@ -74,15 +71,15 @@ export const outputLogFormatter = (log: any) => {
}
if (log.blockNumber !== null) {
log.blockNumber = hexToBN(log.blockNumber).toNumber();
log.blockNumber = hexToNumber(log.blockNumber);
}
if (log.transactionIndex !== null) {
log.transactionIndex = hexToBN(log.transactionIndex).toNumber();
log.transactionIndex = hexToNumber(log.transactionIndex);
}
if (log.logIndex !== null) {
log.logIndex = hexToBN(log.logIndex).toNumber();
log.logIndex = hexToNumber(log.logIndex);
}
if (log.address) {
@ -93,7 +90,11 @@ export const outputLogFormatter = (log: any) => {
};
export const inputBlockNumberFormatter = (blockNumber: any) => {
if (blockNumber === undefined || blockNumber === null || isPredefinedBlockNumber(blockNumber)) {
if (
blockNumber === undefined ||
blockNumber === null ||
isPredefinedBlockNumber(blockNumber)
) {
return blockNumber;
}
@ -109,7 +110,11 @@ export const inputBlockNumberFormatter = (blockNumber: any) => {
};
export const isPredefinedBlockNumber = (blockNumber: string) => {
return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest';
return (
blockNumber === 'latest' ||
blockNumber === 'pending' ||
blockNumber === 'earliest'
);
};
export const inputAddressFormatter = (address: string) => {

@ -1,10 +1,4 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
import { isArray } from '@woop-js/utils';
import { isArray } from '@harmony-js/utils';
import { AbiItem } from '../models/AbiItemModel';
import { AbiModel } from '../models/AbiModel';
import { AbiItemModel } from '../models/types';
@ -15,8 +9,6 @@ export const abiMapper = (abi: any[], abiCoder: AbiCoderClass): AbiModel => {
const mappedAbiItems: any = {
methods: {},
events: {},
fallback: undefined,
receive: undefined,
};
let hasConstructor = false;
@ -72,11 +64,6 @@ export const abiMapper = (abi: any[], abiCoder: AbiCoderClass): AbiModel => {
mappedAbiItems.events[abiItem.funcName] = abiItemModel;
}
if (abiItem.type === 'fallback' || abiItem.type === 'receive') {
abiItem.signature = abiItem.type;
mappedAbiItems[abiItem.type] = new AbiItem(abiItem);
}
if (abiItem.type === 'constructor') {
abiItem.signature = abiItem.type;
// tslint:disable-next-line: no-string-literal
@ -99,7 +86,9 @@ export const abiMapper = (abi: any[], abiCoder: AbiCoderClass): AbiModel => {
export const isConstant = (abiItem: AbiItemModel) => {
return (
abiItem.stateMutability === 'view' || abiItem.stateMutability === 'pure' || abiItem.constant
abiItem.stateMutability === 'view' ||
abiItem.stateMutability === 'pure' ||
abiItem.constant
);
};

@ -1,12 +1,5 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
export interface ContractOptions {
data?: string;
shardID?: number;
address?: string;
defaultAccount?: string;
defaultBlock?: string;

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-contract
* @hidden
*/
export enum ContractStatus {
INITIALISED = 'initialised',
TESTED = 'tested',

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": { "*": ["types/*"] },
"outDir": "dist",
"rootDir": "src"
},
"include": ["src", "../../typings/**/*.d.ts"]
}

@ -0,0 +1,4 @@
# features
1. [x] Harmony constructor
2. [x] Blockchain with RPC methods

@ -1,23 +1,18 @@
/**
* @packageDocumentation
* @ignore
*/
// tslint:disable-next-line: no-implicit-dependencies
import fetch from 'jest-fetch-mock';
import { Blockchain } from '../src/blockchain';
import { HttpProvider, Messenger } from '@woop-js/network';
import { HttpProvider, Messenger } from '@harmony-js/network';
const provider = new HttpProvider('https://mock.com');
const messenger = new Messenger(provider);
function runMocks(mockRpcResponse: any, repeat: number): void {
const mocks: any = [];
const mocks = [];
for (let i = 0; i < 5; i++) {
mocks.push(mockRpcResponse);
}
// tslint:disable-next-line: no-shadowed-variable
const responses = mocks.map((res: any) => [JSON.stringify(res), {status: 200}]);
const responses = mocks.map((res) => [JSON.stringify(res)]);
fetch.mockResponses(...responses);
}
@ -41,19 +36,25 @@ describe('test Blockchain', () => {
extraData: '0x',
gasLimit: '0x254a0e6f8',
gasUsed: '0x22da16',
hash: '0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
hash:
'0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
logsBloom:
'0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
miner: '0x0000000000000000000000000000000000000000',
mixHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
mixHash:
'0x0000000000000000000000000000000000000000000000000000000000000000',
nonce: '0x0000000000000000',
number: '0x0',
parentHash: '0x057e52af22d94cd40626d1e157e3468219d8236847c13bb3aa11f31fd6c4e71a',
receiptsRoot: '0xf9c8a65a3224a4c6e6ae3b9f0ee992c4cefdbe4db318c4ec52880e324f1b77bd',
parentHash:
'0x057e52af22d94cd40626d1e157e3468219d8236847c13bb3aa11f31fd6c4e71a',
receiptsRoot:
'0xf9c8a65a3224a4c6e6ae3b9f0ee992c4cefdbe4db318c4ec52880e324f1b77bd',
size: '0x2de9',
stateRoot: '0x112322fee869910b9a0e390ae536addca7a2a82bac7c17a61ed43a715e845218',
stateRoot:
'0x112322fee869910b9a0e390ae536addca7a2a82bac7c17a61ed43a715e845218',
timestamp: 1556265598,
transactionsRoot: '0x7687794ce8479d36c1a6d8b161dca37d90bd824da1c36d6d8f33f7bf4015c1d0',
transactionsRoot:
'0x7687794ce8479d36c1a6d8b161dca37d90bd824da1c36d6d8f33f7bf4015c1d0',
uncles: [],
},
};
@ -72,12 +73,14 @@ describe('test Blockchain', () => {
expect(res3.result.timestamp).toEqual(1556265598);
const res4 = await bc.getBlockByHash({
blockHash: '0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
blockHash:
'0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
});
expect(res4.responseType).toEqual('raw');
expect(res4.result.size).toEqual('0x2de9');
const res5 = await bc.getBlockByHash({
blockHash: '0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
blockHash:
'0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
returnObject: true,
});
expect(res5.responseType).toEqual('raw');
@ -91,12 +94,14 @@ describe('test Blockchain', () => {
jsonrpc: '2.0',
id: 1,
result: {
blockHash: '0x359036996c7ad7fdaa42b18de2fc157ae97d4cd3f32c688af349225f7e8f8fc6',
blockHash:
'0x359036996c7ad7fdaa42b18de2fc157ae97d4cd3f32c688af349225f7e8f8fc6',
blockNumber: '0x1',
from: '0x15a128e599b74842bccba860311efa92991bffb5',
gas: '0x81650',
gasPrice: '0x0',
hash: '0x9a71ea0839511c95b0818bd54a38ab56a05337a8282245d853d0ae3a1aedd7da',
hash:
'0x9a71ea0839511c95b0818bd54a38ab56a05337a8282245d853d0ae3a1aedd7da',
input:
'0x6080604052678ac7230489e80000600155600280546001600160a01b0319163317905561014d806100316000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806327c78c421461003b5780634ddd108a14610063575b600080fd5b6100616004803603602081101561005157600080fd5b50356001600160a01b031661007d565b005b61006b61011c565b60408051918252519081900360200190f35b6002546001600160a01b0316331461009457600080fd5b600154303110156100a457600080fd5b6001600160a01b03811660009081526020819052604090205460ff16156100ca57600080fd5b6001600160a01b038116600081815260208190526040808220805460ff1916600190811790915554905181156108fc0292818181858888f19350505050158015610118573d6000803e3d6000fd5b5050565b30319056fea165627a7a72305820b83c347551d539b44b318fb7b4601a635a7d3a6d7063d11e6e05e4886b6d88050029',
nonce: '0x0',
@ -112,14 +117,16 @@ describe('test Blockchain', () => {
runMocks(mockRpcResponse, 4);
const res1 = await bc.getTransactionByHash({
txnHash: '0x9a71ea0839511c95b0818bd54a38ab56a05337a8282245d853d0ae3a1aedd7da',
txnHash:
'0x9a71ea0839511c95b0818bd54a38ab56a05337a8282245d853d0ae3a1aedd7da',
});
expect(res1.responseType).toEqual('raw');
expect(res1.result.blockHash).toEqual(
'0x359036996c7ad7fdaa42b18de2fc157ae97d4cd3f32c688af349225f7e8f8fc6',
);
const res2 = await bc.getTransactionByBlockHashAndIndex({
blockHash: '0x359036996c7ad7fdaa42b18de2fc157ae97d4cd3f32c688af349225f7e8f8fc6',
blockHash:
'0x359036996c7ad7fdaa42b18de2fc157ae97d4cd3f32c688af349225f7e8f8fc6',
index: '0x0',
});
expect(res2.responseType).toEqual('raw');
@ -154,7 +161,8 @@ describe('test Blockchain', () => {
runMocks(mockRpcResponse, 5);
const res1 = await bc.getBlockTransactionCountByHash({
blockHash: '0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
blockHash:
'0x7e1ef610f700805b93cf85b1e55bce84fcbd04373252a968755366a8d2215424',
});
expect(res1.result).toEqual('0x65');
try {
@ -215,7 +223,8 @@ describe('test Blockchain', () => {
const mockRpcResponse = {
jsonrpc: '2.0',
id: 1,
result: '0x0000000000000000000000000000000000000000000000000000000000000000',
result:
'0x0000000000000000000000000000000000000000000000000000000000000000',
};
// set mocks to test methods
runMocks(mockRpcResponse, 2);
@ -256,24 +265,24 @@ describe('test Blockchain', () => {
});
expect(res2.result).toEqual('0xd3c21bcecceda1000000');
});
it('test get transaction receipt', async () => {
const bc = new Blockchain(messenger);
const mockRpcResponse = {
jsonrpc: '2.0',
id: 1,
result: '0xd3c21bcecceda1000000',
};
// set mocks to test methods
runMocks(mockRpcResponse, 2);
// it('test get transaction receipt', async () => {
// const bc = new Blockchain(messenger);
// const mockRpcResponse = {
// jsonrpc: '2.0',
// id: 1,
// result: '0xd3c21bcecceda1000000',
// };
// // set mocks to test methods
// runMocks(mockRpcResponse, 2);
const res1 = await bc.getBalance({
address: '0x15a128e599b74842bccba860311efa92991bffb5',
blockNumber: '0x1',
});
expect(res1.result).toEqual('0xd3c21bcecceda1000000');
const res2 = await bc.getBalance({
address: '0x15a128e599b74842bccba860311efa92991bffb5',
});
expect(res2.result).toEqual('0xd3c21bcecceda1000000');
});
// const res1 = await bc.getBalance({
// address: '0x15a128e599b74842bccba860311efa92991bffb5',
// blockNumber: '0x1',
// });
// expect(res1).toEqual('0xd3c21bcecceda1000000');
// const res2 = await bc.getBalance({
// address: '0x15a128e599b74842bccba860311efa92991bffb5',
// });
// expect(res2).toEqual('0xd3c21bcecceda1000000');
// });
});

@ -1,7 +1,7 @@
{
"name": "@woop-js/account",
"version": "0.1.58",
"description": "account and wallet for woop",
"name": "@harmony-js/core",
"version": "0.0.15",
"description": "harmony core package",
"main": "dist/index.js",
"node": "dist/index.js",
"browser": "dist/index.js",
@ -16,14 +16,14 @@
"access": "public"
},
"author": "neeboo@firestack.one",
"license": "MIT",
"license": "ISC",
"dependencies": {
"@woop-js/core": "0.1.58",
"@woop-js/crypto": "0.1.58",
"@woop-js/network": "0.1.58",
"@woop-js/staking": "0.1.58",
"@woop-js/transaction": "0.1.58",
"@woop-js/utils": "0.1.58"
"@harmony-js/account": "0.0.15",
"@harmony-js/contract": "0.0.15",
"@harmony-js/crypto": "0.0.15",
"@harmony-js/network": "0.0.15",
"@harmony-js/transaction": "0.0.15",
"@harmony-js/utils": "0.0.15"
},
"gitHead": "56606e9365721729a490c27d6a294e0daf90fbdf"
"gitHead": "938a044229b662d99c61e57417d1702d44aa7fe2"
}

@ -0,0 +1,345 @@
import {
RPCMethod,
Messenger,
ResponseMiddleware,
WSProvider,
NewPendingTransactions,
NewHeaders,
LogSub,
Syncing,
} from '@harmony-js/network';
import {
assertObject,
AssertType,
HarmonyCore,
DefaultBlockParams,
} from '@harmony-js/utils';
import { Transaction } from '@harmony-js/transaction';
class Blockchain extends HarmonyCore {
messenger: Messenger;
constructor(messenger: Messenger) {
super(messenger.chainType);
this.messenger = messenger;
}
setMessenger(messenger: Messenger) {
this.messenger = messenger;
}
getRpcResult(result: any) {
if (result instanceof ResponseMiddleware) {
return result.getRaw;
} else {
return result;
}
}
/**
*
*/
@assertObject({
address: ['isAddress', AssertType.required],
blockNumber: ['isBlockNumber', AssertType.optional],
})
async getBalance({
address,
blockNumber = DefaultBlockParams.latest,
}: {
address: string;
blockNumber?: string;
}) {
const result = await this.messenger.send(
RPCMethod.GetBalance,
[address, blockNumber],
this.chainPrefix,
);
return this.getRpcResult(result);
}
/**
*
*/
@assertObject({
blockHash: ['isHash', AssertType.required],
returnObject: ['isBoolean', AssertType.optional],
})
async getBlockByHash({
blockHash,
returnObject = true,
}: {
blockHash: string;
returnObject?: boolean;
}) {
const result = await this.messenger.send(
RPCMethod.GetBlockByHash,
[blockHash, returnObject],
this.chainPrefix,
);
return this.getRpcResult(result);
}
/**
*
*/
@assertObject({
blockNumber: ['isBlockNumber', AssertType.optional],
returnObject: ['isBoolean', AssertType.optional],
})
async getBlockByNumber({
blockNumber = DefaultBlockParams.latest,
returnObject = true,
}: {
blockNumber?: string;
returnObject?: boolean;
}) {
const result = await this.messenger.send(
RPCMethod.GetBlockByNumber,
[blockNumber, returnObject],
this.chainPrefix,
);
return this.getRpcResult(result);
}
@assertObject({
blockHash: ['isHash', AssertType.required],
})
async getBlockTransactionCountByHash({ blockHash }: { blockHash: string }) {
const result = await this.messenger.send(
RPCMethod.GetBlockTransactionCountByHash,
[blockHash],
this.chainPrefix,
);
return this.getRpcResult(result);
}
@assertObject({
blockNumber: ['isBlockNumber', AssertType.required],
})
async getBlockTransactionCountByNumber({
blockNumber,
}: {
blockNumber: string;
}) {
const result = await this.messenger.send(
RPCMethod.GetBlockTransactionCountByNumber,
[blockNumber],
this.chainPrefix,
);
return this.getRpcResult(result);
}
/**
*
*/
@assertObject({
blockHash: ['isHash', AssertType.required],
index: ['isHex', AssertType.required],
})
async getTransactionByBlockHashAndIndex({
blockHash,
index,
}: {
blockHash: string;
index: string;
}) {
const result = await this.messenger.send(
RPCMethod.GetTransactionByBlockHashAndIndex,
[blockHash, index],
this.chainPrefix,
);
return this.getRpcResult(result);
}
@assertObject({
blockNumber: ['isBlockNumber', AssertType.optional],
index: ['isHex', AssertType.required],
})
async getTransactionByBlockNumberAndIndex({
blockNumber = DefaultBlockParams.latest,
index,
}: {
blockNumber?: string;
index: string;
}) {
const result = await this.messenger.send(
RPCMethod.GetTransactionByBlockNumberAndIndex,
[blockNumber, index],
this.chainPrefix,
);
return this.getRpcResult(result);
}
@assertObject({
txnHash: ['isHash', AssertType.required],
})
async getTransactionByHash({ txnHash }: { txnHash: string }) {
const result = await this.messenger.send(
RPCMethod.GetTransactionByHash,
[txnHash],
this.chainPrefix,
);
return this.getRpcResult(result);
}
/**
*
*/
@assertObject({
txnHash: ['isString', AssertType.required],
})
async getTransactionReceipt({ txnHash }: { txnHash: string }) {
const result = await this.messenger.send(
RPCMethod.GetTransactionReceipt,
[txnHash],
this.chainPrefix,
);
return this.getRpcResult(result);
}
/**
*
*/
@assertObject({
address: ['isAddress', AssertType.required],
blockNumber: ['isBlockNumber', AssertType.optional],
})
async getCode({
address,
blockNumber = DefaultBlockParams.latest,
}: {
address: string;
blockNumber?: string;
}) {
const result = await this.messenger.send(
RPCMethod.GetCode,
[address, blockNumber],
this.chainPrefix,
);
return this.getRpcResult(result);
}
async net_peerCount() {
const result = await this.messenger.send(RPCMethod.PeerCount, [], 'net');
return this.getRpcResult(result);
}
@assertObject({
address: ['isAddress', AssertType.required],
position: ['isHex', AssertType.required],
blockNumber: ['isBlockNumber', AssertType.optional],
})
async getStorageAt({
address,
position,
blockNumber = DefaultBlockParams.latest,
}: {
address: string;
position: string;
blockNumber?: string;
}) {
const result = await this.messenger.send(
RPCMethod.GetStorageAt,
[address, position, blockNumber],
this.chainPrefix,
);
return this.getRpcResult(result);
}
@assertObject({
address: ['isAddress', AssertType.required],
blockNumber: ['isBlockNumber', AssertType.optional],
})
async getTransactionCount({
address,
blockNumber = DefaultBlockParams.latest,
}: {
address: string;
blockNumber?: string;
}) {
const result = await this.messenger.send(
RPCMethod.GetTransactionCount,
[address, blockNumber],
this.chainPrefix,
);
return this.getRpcResult(result);
}
async sendTransaction(transaction: Transaction) {
if (!transaction.isSigned() || !transaction) {
throw new Error('transaction is not signed or not exist');
}
const result = await this.messenger.send(
RPCMethod.SendTransaction,
[transaction.txPayload],
this.chainPrefix,
);
return this.getRpcResult(result);
}
async sendRawTransaction(transaction: Transaction) {
if (!transaction.isSigned() || !transaction) {
throw new Error('transaction is not signed or not exist');
}
const [txn, result] = await transaction.sendTransaction();
if (txn.isPending) {
return result;
}
}
@assertObject({
to: ['isAddress', AssertType.optional],
data: ['isHex', AssertType.optional],
})
async estimateGas({ to, data }: { to: string; data: string }) {
const result = await this.messenger.send(
RPCMethod.EstimateGas,
[{ to, data }],
this.chainPrefix,
);
return this.getRpcResult(result);
}
async gasPrice() {
const result = await this.messenger.send(
RPCMethod.GasPrice,
[],
this.chainPrefix,
);
return this.getRpcResult(result);
}
newPendingTransactions() {
if (this.messenger.provider instanceof WSProvider) {
return new NewPendingTransactions(this.messenger);
} else {
throw new Error('HttpProvider does not support this feature');
}
}
newBlockHeaders() {
if (this.messenger.provider instanceof WSProvider) {
return new NewHeaders(this.messenger);
} else {
throw new Error('HttpProvider does not support this feature');
}
}
syncing() {
if (this.messenger.provider instanceof WSProvider) {
return new Syncing(this.messenger);
} else {
throw new Error('HttpProvider does not support this feature');
}
}
logs(options: any) {
if (this.messenger.provider instanceof WSProvider) {
return new LogSub(options, this.messenger);
} else {
throw new Error('HttpProvider does not support this feature');
}
}
}
export { Blockchain };

@ -0,0 +1,65 @@
import * as crypto from '@harmony-js/crypto';
import * as utils from '@harmony-js/utils';
import { HttpProvider, Messenger, WSProvider } from '@harmony-js/network';
import { TransactionFactory, Transaction } from '@harmony-js/transaction';
import { ContractFactory, Contract } from '@harmony-js/contract';
import { Wallet, Account } from '@harmony-js/account';
import { Blockchain } from './blockchain';
export class Harmony extends utils.HarmonyCore {
Modules = {
HttpProvider,
WSProvider,
Messenger,
Blockchain,
TransactionFactory,
Wallet,
Transaction,
Account,
Contract,
};
messenger: Messenger;
transactions: TransactionFactory;
wallet: Wallet;
blockchain: Blockchain;
contracts: ContractFactory;
crypto: any;
utils: any;
private provider: HttpProvider | WSProvider;
constructor(
url: string,
chainType: utils.ChainType = utils.ChainType.Harmony,
chainId: utils.ChainID = utils.ChainID.Default,
) {
super(chainType, chainId);
this.provider = utils.isHttp(url)
? new HttpProvider(url)
: utils.isWs(url)
? new WSProvider(url)
: new HttpProvider('http://localhost:9128');
this.messenger = new Messenger(this.provider, this.chainType, this.chainId);
this.blockchain = new Blockchain(this.messenger);
this.transactions = new TransactionFactory(this.messenger);
this.wallet = new Wallet(this.messenger);
this.contracts = new ContractFactory(this.wallet);
this.crypto = crypto;
this.utils = utils;
}
setProvider(provider: string | HttpProvider | WSProvider): void {
if (utils.isHttp(provider) && typeof provider === 'string') {
this.provider = new HttpProvider(provider);
} else if (provider instanceof HttpProvider) {
this.provider = provider;
} else if (utils.isWs(provider) && typeof provider === 'string') {
this.provider = new WSProvider(provider);
} else if (provider instanceof WSProvider) {
this.provider = provider;
}
this.messenger.setProvider(this.provider);
this.blockchain.setMessenger(this.messenger);
this.wallet.setMessenger(this.messenger);
this.transactions.setMessenger(this.messenger);
}
}

@ -0,0 +1,3 @@
export * from './harmony';
export * from './blockchain';
export * from './types';

@ -0,0 +1,19 @@
import { HttpProvider, Messenger } from '@harmony-js/network';
import { TransactionFactory, Transaction } from '@harmony-js/transaction';
import { Wallet, Account } from '@harmony-js/account';
import { Blockchain } from './blockchain';
export interface HarmonyModule {
HttpProvider: HttpProvider;
Messenger: Messenger;
Blockchain: Blockchain;
TransactionFactory: TransactionFactory;
Wallet: Wallet;
Transaction: Transaction;
Account: Account;
}
export const enum UrlType {
http,
ws,
}

@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "dist",
"rootDir": "src"
},
"include": ["src", "../../typings/**/*.d.ts", "../harmony-utils/src/core.ts"],
"references": [
{ "path": "../harmony-account" },
{ "path": "../harmony-crypto" },
{ "path": "../harmony-utils" },
{ "path": "../harmony-network" },
{ "path": "../harmony-transaction" },
{ "path": "../harmony-contract" }
]
}

@ -0,0 +1,7 @@
# Features
1. [x] ECDSA
2. [x] Random bytes
3. [x] AES/DES for web3
4. [x] Keccak256 ~~DRBG~~
5. [x] keyTools(prvK/pubK/addr/checksumAddr)

@ -0,0 +1,39 @@
import { bs58Decode, bs58Encode } from '../src/base58';
import { fixtures, testData } from './base58fixture';
describe('test bs58', () => {
it('should test encode', () => {
fixtures.valid.forEach((f: any) => {
const actual = bs58Encode(f.hex);
expect(actual).toEqual(f.string);
});
});
it('should test decode', () => {
fixtures.valid.forEach((f: any) => {
const actual = bs58Decode(f.string);
expect(actual).toEqual(f.hex);
});
});
it('should throw error', () => {
fixtures.invalid.forEach((f: any) => {
try {
bs58Decode(f.string);
} catch (error) {
expect(error.message).toEqual('Non-base58 character');
}
});
});
it('another test', () => {
testData.forEach((f: any) => {
const actual = bs58Encode(f.raw);
expect(actual).toEqual(f.res);
const actualDecode = bs58Decode(f.res);
expect(actualDecode).toEqual(f.raw);
});
});
it('manual test', () => {
const raw = '0x';
const actual = bs58Encode(raw);
expect(actual).toEqual('');
});
});

@ -0,0 +1,140 @@
export const fixtures = {
valid: [
{
hex: '',
string: '',
},
{
hex: '61',
string: '2g',
},
{
hex: '626262',
string: 'a3gV',
},
{
hex: '636363',
string: 'aPEr',
},
{
hex: '73696d706c792061206c6f6e6720737472696e67',
string: '2cFupjhnEsSn59qHXstmK2ffpLv2',
},
{
hex: '00eb15231dfceb60925886b67d065299925915aeb172c06647',
string: '1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L',
},
{
hex: '516b6fcd0f',
string: 'ABnLTmg',
},
{
hex: 'bf4f89001e670274dd',
string: '3SEo3LWLoPntC',
},
{
hex: '572e4794',
string: '3EFU7m',
},
{
hex: 'ecac89cad93923c02321',
string: 'EJDM8drfXA6uyA',
},
{
hex: '10c8511e',
string: 'Rt5zm',
},
{
hex: '00000000000000000000',
string: '1111111111',
},
{
hex:
'801184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd206ec97e',
string: '5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD',
},
{
hex: '003c176e659bea0f29a3e9bf7880c112b1b31b4dc826268187',
string: '16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS',
},
],
invalid: [
{
description: 'non-base58 string',
string: 'invalid',
},
{
description: 'non-base58 alphabet',
string: 'c2F0b3NoaQo=',
},
{
description: 'leading whitespace',
string: ' 1111111111',
},
{
description: 'trailing whitespace',
string: '1111111111 ',
},
{
description: 'unexpected character after whitespace',
string: ' \t\n\u000b\f\r skip \r\f\u000b\n\t a',
},
],
};
export const testData = [
{
raw: '',
res: '',
},
{
raw: '61',
res: '2g',
},
{
raw: '626262',
res: 'a3gV',
},
{
raw: '636363',
res: 'aPEr',
},
{
raw: '73696d706c792061206c6f6e6720737472696e67',
res: '2cFupjhnEsSn59qHXstmK2ffpLv2',
},
{
raw: '00eb15231dfceb60925886b67d065299925915aeb172c06647',
res: '1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L',
},
{
raw: '516b6fcd0f',
res: 'ABnLTmg',
},
{
raw: '572e4794',
res: '3EFU7m',
},
{
raw: 'ecac89cad93923c02321',
res: 'EJDM8drfXA6uyA',
},
{
raw: '10c8511e',
res: 'Rt5zm',
},
{
raw: '00000000000000000000',
res: '1111111111',
},
{
raw:
'801184cd2cdd640ca42cfc3a091c51d549b2f016d454b2774019c2b2d2e08529fd206ec97e',
res: '5Hx15HFGyep2CfPxsJKe2fXJsCVn5DEiyoeGGF6JZjGbTRnqfiD',
},
{
raw: '003c176e659bea0f29a3e9bf7880c112b1b31b4dc826268187',
res: '16UjcYNBG9GTK4uq2f7yYEbuifqCzoLMGS',
},
];

@ -1,7 +1,7 @@
{
"name": "@woop-js/crypto",
"version": "0.1.58",
"description": "crypto libraries for woop",
"name": "@harmony-js/crypto",
"version": "0.0.15",
"description": "crypto libraries for harmony",
"main": "dist/index.js",
"node": "dist/index.js",
"browser": "dist/index.js",
@ -16,10 +16,11 @@
"publishConfig": {
"access": "public"
},
"license": "MIT",
"license": "ISC",
"dependencies": {
"@woop-js/utils": "0.1.58",
"@harmony-js/utils": "0.0.15",
"aes-js": "^3.1.2",
"base-x": "^3.0.5",
"bip39": "^2.5.0",
"bn.js": "^4.11.8",
"elliptic": "^6.4.1",
@ -30,5 +31,5 @@
"scrypt.js": "^0.3.0",
"uuid": "^3.3.2"
},
"gitHead": "56606e9365721729a490c27d6a294e0daf90fbdf"
"gitHead": "938a044229b662d99c61e57417d1702d44aa7fe2"
}

@ -0,0 +1,18 @@
import base from 'base-x';
const BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
const base58 = base(BASE58);
export const bs58Encode = (hex: string): string => {
if (hex.startsWith('0x') || hex.startsWith('0X')) {
hex = hex.substring(2);
}
const hexBuff = Buffer.from(hex, 'hex');
return base58.encode(hexBuff);
};
export const bs58Decode = (baseString: string): string => {
const baseBuf = base58.decode(baseString);
return baseBuf.toString('hex');
};

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-crypto
* @hidden
*/
// This file is ported from ether.js/src.ts/utils/bytes.ts
// and done some fixes
@ -31,14 +25,16 @@ export function isHexable(value: any): value is Hexable {
}
function addSlice(array: Uint8Array): Uint8Array {
if (typeof array === 'object' && typeof array.slice === 'function') {
if (array.slice) {
return array;
}
// tslint:disable-next-line: only-arrow-functions
array.slice = function() {
const args = Array.prototype.slice.call(arguments);
return addSlice(new Uint8Array(Array.prototype.slice.apply(array, [args[0], args[1]])));
return addSlice(
new Uint8Array(Array.prototype.slice.apply(array, [args[0], args[1]])),
);
};
return array;
@ -68,10 +64,11 @@ export function isArrayish(value: any): value is Arrayish {
export function arrayify(value: Arrayish | Hexable): Uint8Array | null {
if (value == null) {
errors.throwError('cannot convert null value to array', errors.INVALID_ARGUMENT, {
arg: 'value',
value,
});
errors.throwError(
'cannot convert null value to array',
errors.INVALID_ARGUMENT,
{ arg: 'value', value },
);
}
if (isHexable(value)) {
@ -89,10 +86,11 @@ export function arrayify(value: Arrayish | Hexable): Uint8Array | null {
}
if (match !== null && match[1] !== '0x') {
errors.throwError('hex string must have 0x prefix', errors.INVALID_ARGUMENT, {
arg: 'value',
value,
});
errors.throwError(
'hex string must have 0x prefix',
errors.INVALID_ARGUMENT,
{ arg: 'value', value },
);
}
value = value.substring(2);
@ -205,10 +203,11 @@ export function hexlify(value: Arrayish | Hexable | number): string {
if (typeof value === 'number') {
if (value < 0) {
errors.throwError('cannot hexlify negative value', errors.INVALID_ARGUMENT, {
arg: 'value',
value,
});
errors.throwError(
'cannot hexlify negative value',
errors.INVALID_ARGUMENT,
{ arg: 'value', value },
);
}
// @TODO: Roll this into the above error as a numeric fault (overflow); next version, not backward compatible
@ -247,10 +246,11 @@ export function hexlify(value: Arrayish | Hexable | number): string {
}
if (match !== null && match[1] !== '0x') {
errors.throwError('hex string must have 0x prefix', errors.INVALID_ARGUMENT, {
arg: 'value',
value,
});
errors.throwError(
'hex string must have 0x prefix',
errors.INVALID_ARGUMENT,
{ arg: 'value', value },
);
}
if (value.length % 2) {
@ -283,7 +283,11 @@ export function hexDataLength(data: string) {
return (data.length - 2) / 2;
}
export function hexDataSlice(data: string, offset: number, endOffset?: number): string {
export function hexDataSlice(
data: string,
offset: number,
endOffset?: number,
): string {
if (!isHexString(data)) {
errors.throwError('invalid hex data', errors.INVALID_ARGUMENT, {
arg: 'value',
@ -342,7 +346,9 @@ export function bytesPadLeft(value: string, byteLength: number): string {
const striped = value.substring(2);
if (striped.length > byteLength * 2) {
throw new Error(`hex string length = ${striped.length} beyond byteLength=${byteLength}`);
throw new Error(
`hex string length = ${striped.length} beyond byteLength=${byteLength}`,
);
}
const padLength = byteLength * 2 - striped.length;
const returnValue = '0x' + '0'.repeat(padLength) + striped;
@ -358,7 +364,9 @@ export function bytesPadRight(value: string, byteLength: number): string {
const striped = value.substring(2);
if (striped.length > byteLength * 2) {
throw new Error(`hex string length = ${striped.length} beyond byteLength=${byteLength}`);
throw new Error(
`hex string length = ${striped.length} beyond byteLength=${byteLength}`,
);
}
const padLength = byteLength * 2 - striped.length;
const returnValue = '0x' + striped + '0'.repeat(padLength);
@ -424,7 +432,13 @@ export function splitSignature(signature: Arrayish | Signature): Signature {
export function joinSignature(signature: Signature): string {
signature = splitSignature(signature);
return hexlify(concat([signature.r, signature.s, signature.recoveryParam ? '0x1c' : '0x1b']));
return hexlify(
concat([
signature.r,
signature.s,
signature.recoveryParam ? '0x1c' : '0x1b',
]),
);
}
/**

@ -1,97 +1,13 @@
/**
# @woop-js/crypto
This package provides a collection of apis related to address management, kestore, encoding, and encrypt/decrypt.
## Installation
```
npm install @woop-js/crypto
```
## Usage
```javascript
* const {
* encode,
* decode,
* randomBytes,
* toBech32,
* fromBech32,
* WoopAddress,
* generatePrivateKey,
* getPubkeyFromPrivateKey,
* getAddressFromPublicKey,
* getAddressFromPrivateKey,
* encryptPhrase,
* decryptPhrase
* } = require('@woop-js/crypto');
* const { isPrivateKey, isAddress, isPublicKey } = require('@woop-js/utils');
```
Address apis
```javascript
const bytes = randomBytes(20);
const addr = new WoopAddress(bytes);
console.log(addr.checksum);
console.log(addr.bech32);
console.log(WoopAddress.isValidBech32(addr.bech32));
```
RLP apis
```javascript
const encoded = '0x89010101010101010101';
const decoded = '0x010101010101010101';
console.log(encode(decoded));
console.log(decode(encoded));
```
Keystore apis
```javascript
const prv = generatePrivateKey();
const pub = getPubkeyFromPrivateKey(prv);
const addr = getAddressFromPublicKey(pub);
const addrPrv = getAddressFromPrivateKey(prv);
console.log(isPrivateKey(prv));
console.log(isPublicKey(pub));
console.log(isAddress(addr));
console.log(isAddress(addrPrv));
```
Encrypt/decrypt apis
```javascript
* const { Wallet } = require('@woop-js/account');
* const myPhrase = new Wallet().newMnemonic();
* console.log(myPhrase);
* const pwd = '1234';
* encryptPhrase(myPhrase, pwd).then((value) => {
* console.log(value);
* decryptPhrase(JSON.parse(value), pwd).then(value => {
* console.log(value);
* });
* });
```
*
* @packageDocumentation
* @module woop-crypto
*/
// This file is ported from ether.js/src.ts/errors.ts
// Unknown Error
/** @hidden */
export const UNKNOWN_ERROR = 'UNKNOWN_ERROR';
// Not implemented
/** @hidden */
export const NOT_IMPLEMENTED = 'NOT_IMPLEMENTED';
// Missing new operator to an object
// - name: The name of the class
/** @hidden */
export const MISSING_NEW = 'MISSING_NEW';
// Call exception
@ -102,63 +18,55 @@ export const MISSING_NEW = 'MISSING_NEW';
// - errorSignature?: The EIP848 error signature
// - errorArgs?: The EIP848 error parameters
// - reason: The reason (only for EIP848 "Error(string)")
/** @hidden */
export const CALL_EXCEPTION = 'CALL_EXCEPTION';
// Invalid argument (e.g. value is incompatible with type) to a function:
// - argument: The argument name that was invalid
// - value: The value of the argument
/** @hidden */
export const INVALID_ARGUMENT = 'INVALID_ARGUMENT';
// Missing argument to a function:
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
/** @hidden */
export const MISSING_ARGUMENT = 'MISSING_ARGUMENT';
// Too many arguments
// - count: The number of arguments received
// - expectedCount: The number of arguments expected
/** @hidden */
export const UNEXPECTED_ARGUMENT = 'UNEXPECTED_ARGUMENT';
// Numeric Fault
// - operation: the operation being executed
// - fault: the reason this faulted
/** @hidden */
export const NUMERIC_FAULT = 'NUMERIC_FAULT';
// Insufficien funds (< value + gasLimit * gasPrice)
// - transaction: the transaction attempted
/** @hidden */
export const INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS';
// Nonce has already been used
// - transaction: the transaction attempted
/** @hidden */
export const NONCE_EXPIRED = 'NONCE_EXPIRED';
// The replacement fee for the transaction is too low
// - transaction: the transaction attempted
/** @hidden */
export const REPLACEMENT_UNDERPRICED = 'REPLACEMENT_UNDERPRICED';
// Unsupported operation
// - operation
/** @hidden */
export const UNSUPPORTED_OPERATION = 'UNSUPPORTED_OPERATION';
// tslint:disable-next-line: variable-name
/** @hidden */
let _permanentCensorErrors = false;
// tslint:disable-next-line: variable-name
/** @hidden */
let _censorErrors = false;
// @TODO: Enum
/** @hidden */
export function throwError(message: string, code: string | null | undefined, params: any): never {
export function throwError(
message: string,
code: string | null | undefined,
params: any,
): never {
if (_censorErrors) {
throw new Error('unknown error');
}
@ -197,15 +105,17 @@ export function throwError(message: string, code: string | null | undefined, par
throw error;
}
/** @hidden */
export function checkNew(self: any, kind: any): void {
if (!(self instanceof kind)) {
throwError('missing new', MISSING_NEW, { name: kind.name });
}
}
/** @hidden */
export function checkArgumentCount(count: number, expectedCount: number, suffix?: string): void {
export function checkArgumentCount(
count: number,
expectedCount: number,
suffix?: string,
): void {
if (!suffix) {
suffix = '';
}
@ -223,7 +133,6 @@ export function checkArgumentCount(count: number, expectedCount: number, suffix?
}
}
/** @hidden */
export function setCensorship(censorship: boolean, permanent?: boolean): void {
if (_permanentCensorErrors) {
throwError('error censorship permanent', UNSUPPORTED_OPERATION, {
@ -235,7 +144,6 @@ export function setCensorship(censorship: boolean, permanent?: boolean): void {
_permanentCensorErrors = !!permanent;
}
/** @hidden */
export function checkNormalize(): void {
try {
// Make sure all forms of normalization are supported
@ -247,18 +155,21 @@ export function checkNormalize(): void {
}
});
if (String.fromCharCode(0xe9).normalize('NFD') !== String.fromCharCode(0x65, 0x0301)) {
if (
String.fromCharCode(0xe9).normalize('NFD') !==
String.fromCharCode(0x65, 0x0301)
) {
throw new Error('broken implementation');
}
} catch (error) {
throwError('platform missing String.prototype.normalize', UNSUPPORTED_OPERATION, {
operation: 'String.prototype.normalize',
form: error.message,
});
throwError(
'platform missing String.prototype.normalize',
UNSUPPORTED_OPERATION,
{ operation: 'String.prototype.normalize', form: error.message },
);
}
}
/** @hidden */
const LogLevels: { [name: string]: number } = {
debug: 1,
default: 2,
@ -267,10 +178,8 @@ const LogLevels: { [name: string]: number } = {
error: 4,
off: 5,
};
/** @hidden */
let LogLevel = LogLevels.default;
/** @hidden */
export function setLogLevel(logLevel: string): void {
const level = LogLevels[logLevel];
if (level == null) {
@ -280,7 +189,6 @@ export function setLogLevel(logLevel: string): void {
LogLevel = level;
}
/** @hidden */
function log(logLevel: string, args: [any?, ...any[]]): void {
if (LogLevel > LogLevels[logLevel]) {
return;
@ -288,12 +196,10 @@ function log(logLevel: string, args: [any?, ...any[]]): void {
console.log.apply(console, args);
}
/** @hidden */
export function warn(...args: [any?, ...any[]]): void {
log('warn', args);
}
/** @hidden */
export function info(...args: [any?, ...any[]]): void {
log('info', args);
}

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-crypto
* @ignore
*/
import hdkey from 'hdkey';
import bip39 from 'bip39';
import BN from 'bn.js';
@ -15,10 +9,8 @@ export * from './bytes';
export * from './rlp';
export * from './keccak256';
export * from './errors';
export * from './bech32';
// export types
export * from './types';
export * from './address';
export { hdkey, bip39, BN };

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-crypto
* @ignore
*/
// this file is ported from 'ether.js' and done some fixes
import * as sha3 from 'js-sha3';
@ -16,11 +10,3 @@ export function keccak256(data: Arrayish): string {
}
throw new Error('arrayify failed');
}
// export function sha3_256(data: Arrayish): string {
// const arrayified = arrayify(data);
// if (arrayified) {
// return '0x' + sha3.sha3_256(arrayified);
// }
// throw new Error('arrayify failed');
// }

@ -1,16 +1,10 @@
/**
* @packageDocumentation
* @module woop-crypto
*/
import elliptic from 'elliptic';
import * as bytes from './bytes';
import * as errors from './errors';
import { keccak256 } from './keccak256';
import { randomBytes } from './random';
import { isPrivateKey, strip0x, isAddress, isBech32Address } from '@woop-js/utils';
import { fromBech32 } from './bech32';
import { isPrivateKey, strip0x } from '@harmony-js/utils';
import { encode } from './rlp';
const secp256k1 = elliptic.ec('secp256k1');
@ -56,11 +50,10 @@ export const getAddressFromPrivateKey = (privateKey: string): string => {
};
export const getPublic = (privateKey: string, compress?: boolean): string => {
if (!isPrivateKey(privateKey) || !validatePrivateKey(privateKey)) {
if (!isPrivateKey(privateKey)) {
throw new Error(`${privateKey} is not PrivateKey`);
}
const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex');
return ecKey.getPublic(compress || false, 'hex');
};
@ -82,9 +75,6 @@ export const getAddressFromPublicKey = (publicKey: string): string => {
* @return {string} checksumed address
*/
export const toChecksumAddress = (address: string): string => {
if (typeof address === 'string' && isBech32Address(address)) {
address = fromBech32(address);
}
if (typeof address !== 'string' || !address.match(/^0x[0-9A-Fa-f]{40}$/)) {
errors.throwError('invalid address', errors.INVALID_ARGUMENT, {
arg: 'address',
@ -114,7 +104,10 @@ export const toChecksumAddress = (address: string): string => {
return '0x' + chars.join('');
};
export const sign = (digest: bytes.Arrayish | string, privateKey: string): bytes.Signature => {
export const sign = (
digest: bytes.Arrayish | string,
privateKey: string,
): bytes.Signature => {
if (!isPrivateKey(privateKey)) {
throw new Error(`${privateKey} is not PrivateKey`);
}
@ -141,7 +134,9 @@ export function getContractAddress(from: string, nonce: number): string {
throw new Error('missing from address');
}
const addr = keccak256(encode([from, bytes.stripZeros(bytes.hexlify(nonce))]));
const addr = keccak256(
encode([from, bytes.stripZeros(bytes.hexlify(nonce))]),
);
return '0x' + addr.substring(26);
}
@ -161,7 +156,11 @@ export function recoverPublicKey(
const rs = { r: bytes.arrayify(sig.r), s: bytes.arrayify(sig.s) };
////
const recovered = secp256k1.recoverPubKey(bytes.arrayify(digest), rs, sig.recoveryParam);
const recovered = secp256k1.recoverPubKey(
bytes.arrayify(digest),
rs,
sig.recoveryParam,
);
const key = recovered.encode('hex', false);
const ecKey = secp256k1.keyFromPublic(key, 'hex');
@ -180,21 +179,3 @@ export function recoverAddress(
recoverPublicKey(bytes.arrayify(digest) || new Uint8Array(), signature),
);
}
/**
* isValidChecksumAddress
*
* takes hex-encoded string and returns boolean if address is checksumed
*
* @param {string} address
* @returns {boolean}
*/
export const isValidChecksumAddress = (address: string): boolean => {
return isAddress(address.replace('0x', '')) && toChecksumAddress(address) === address;
};
export const validatePrivateKey = (privateKey: string): boolean => {
const ecKey = secp256k1.keyFromPrivate(strip0x(privateKey), 'hex');
const { result } = ecKey.validate();
return result;
};

@ -0,0 +1,171 @@
import aes from 'aes-js';
import scrypt from 'scrypt.js';
import { pbkdf2Sync } from 'pbkdf2';
import uuid from 'uuid';
import { isPrivateKey } from '@harmony-js/utils';
import { randomBytes } from './random';
import { getAddressFromPrivateKey } from './keyTool';
import {
concat,
// arrayify,
// hexDataLength,
// hexToByteArray,
hexToIntArray,
} from './bytes';
import { keccak256 } from './keccak256';
import {
KDF,
KDFParams,
EncryptOptions,
PBKDF2Params,
ScryptParams,
Keystore,
} from './types';
const DEFAULT_ALGORITHM = 'aes-128-ctr';
/**
* getDerivedKey
*
* NOTE: only scrypt and pbkdf2 are supported.
*
* @param {Buffer} key - the passphrase
* @param {KDF} kdf - the key derivation function to be used
* @param {KDFParams} params - params for the kdf
*
* @returns {Promise<Buffer>}
*/
async function getDerivedKey(
key: Buffer,
kdf: KDF,
params: KDFParams,
): Promise<Buffer> {
const salt = Buffer.from(params.salt, 'hex');
if (kdf === 'pbkdf2') {
const { c, dklen } = params as PBKDF2Params;
return pbkdf2Sync(key, salt, c, dklen, 'sha256');
}
if (kdf === 'scrypt') {
const { n, r, p, dklen } = params as ScryptParams;
return scrypt(key, salt, n, r, p, dklen);
}
throw new Error('Only pbkdf2 and scrypt are supported');
}
/**
* This method will map the current Account object to V3Keystore object.
*
* @method encrypt
*
* @param {string} privateKey
* @param {string} password
* @param {object} options
*
* @return {{version, id, address, crypto}}
*/
export const encrypt = async (
privateKey: string,
password: string,
options?: EncryptOptions,
): Promise<string> => {
if (!isPrivateKey(privateKey)) {
throw new Error('privateKey is not correct');
}
// TODO: should use isString() to implement this
if (!password) {
throw new Error('password is not found');
}
const address = getAddressFromPrivateKey(privateKey);
const salt = randomBytes(32);
const iv = Buffer.from(randomBytes(16), 'hex');
const kdf =
options !== undefined ? (options.kdf ? options.kdf : 'scrypt') : 'scrypt';
const level =
options !== undefined ? (options.level ? options.level : 8192) : 8192;
const uuidRandom = options !== undefined ? options.uuid : undefined;
const n = kdf === 'pbkdf2' ? 262144 : level;
const kdfparams = {
salt,
n,
r: 8,
p: 1,
dklen: 32,
};
const derivedKey = await getDerivedKey(Buffer.from(password), kdf, kdfparams);
const cipher = new aes.ModeOfOperation.ctr(
derivedKey.slice(0, 16),
new aes.Counter(iv),
);
if (!cipher) {
throw new Error('Unsupported cipher');
}
const ciphertext = Buffer.from(
cipher.encrypt(Buffer.from(privateKey.replace('0x', ''), 'hex')),
);
const mac = keccak256(concat([derivedKey.slice(16, 32), ciphertext]));
return JSON.stringify({
version: 3,
id: uuid.v4({ random: uuidRandom || hexToIntArray(randomBytes(16)) }),
address: address.toLowerCase().replace('0x', ''),
Crypto: {
ciphertext: ciphertext.toString('hex'),
cipherparams: {
iv: iv.toString('hex'),
},
cipher: DEFAULT_ALGORITHM,
kdf,
kdfparams,
mac: mac.replace('0x', ''),
},
});
};
/**
* @function decrypt
* @param {Keystore} keystore - Keystore file
* @param {string} password - password string
* @return {string} privateKey
*/
export const decrypt = async (
keystore: Keystore,
password: string,
): Promise<string> => {
const ciphertext = Buffer.from(keystore.Crypto.ciphertext, 'hex');
const iv = Buffer.from(keystore.Crypto.cipherparams.iv, 'hex');
const { kdfparams } = keystore.Crypto;
const derivedKey = await getDerivedKey(
Buffer.from(password),
keystore.Crypto.kdf,
kdfparams,
);
const mac = keccak256(concat([derivedKey.slice(16, 32), ciphertext])).replace(
'0x',
'',
);
if (mac.toUpperCase() !== keystore.Crypto.mac.toUpperCase()) {
return Promise.reject(new Error('Failed to decrypt.'));
}
const CTR = aes.ModeOfOperation.ctr;
const cipher = new CTR(derivedKey.slice(0, 16), new aes.Counter(iv));
const decrypted =
'0x' + Buffer.from(cipher.decrypt(ciphertext)).toString('hex');
return decrypted;
};

@ -1,19 +1,18 @@
/**
* @packageDocumentation
* @module woop-crypto
*/
/**
* Uses JS-native CSPRNG to generate a specified number of bytes.
* @NOTE
* this method throws if no PRNG is available.
* @function randomBytes
* @description Uses JS-native CSPRNG to generate a specified number of bytes.
* NOTE: this method throws if no PRNG is available.
* @param {Number} bytes bytes number to generate
* @return {String} ramdom hex string
*/
export const randomBytes = (bytes: number): string => {
let randBz: number[] | Uint8Array;
if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
if (
typeof window !== 'undefined' &&
window.crypto &&
window.crypto.getRandomValues
) {
randBz = window.crypto.getRandomValues(new Uint8Array(bytes));
} else if (typeof require !== 'undefined') {
randBz = require('crypto').randomBytes(bytes);

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-crypto
* @hidden
*/
// this file is ported from https://github.com/ethers-io/ethers.js/blob/master/src.ts/utils/rlp.ts
// and done some fixes
import { arrayify, hexlify, Arrayish } from './bytes';
@ -17,7 +11,11 @@ function arrayifyInteger(value: number): number[] {
return result;
}
function unarrayifyInteger(data: Uint8Array, offset: number, length: number): number {
function unarrayifyInteger(
data: Uint8Array,
offset: number,
length: number,
): number {
let result = 0;
for (let i = 0; i < length; i++) {
result = result * 256 + data[offset + i];
@ -91,7 +89,10 @@ function _decodeChildren(
}
// returns { consumed: number, result: Object }
function _decode(data: Uint8Array, offset: number): { consumed: number; result: any } {
function _decode(
data: Uint8Array,
offset: number,
): { consumed: number; result: any } {
if (data.length === 0) {
throw new Error('invalid rlp data');
}
@ -108,7 +109,12 @@ function _decode(data: Uint8Array, offset: number): { consumed: number; result:
throw new Error('to short');
}
return _decodeChildren(data, offset, offset + 1 + lengthLength, lengthLength + length);
return _decodeChildren(
data,
offset,
offset + 1 + lengthLength,
lengthLength + length,
);
} else if (data[offset] >= 0xc0) {
const length = data[offset] - 0xc0;
if (offset + 1 + length > data.length) {

@ -2,8 +2,3 @@
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
/**
* @packageDocumentation
* @ignore
*/

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-crypto
* @ignore
*/
export type KDF = 'pbkdf2' | 'scrypt';
export interface PBKDF2Params {
@ -29,8 +23,8 @@ export interface EncryptOptions {
}
export interface Keystore {
address?: string;
crypto: {
address: string;
Crypto: {
cipher: string;
cipherparams: {
iv: string;

@ -1,9 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
"outDir": "dist",
"rootDir": "src"
},
"include": ["src", "../../typings/**/*.d.ts"],
"references": [{"path": "../woop-utils"}]
"references": [{ "path": "../harmony-utils" }]
}

@ -1,5 +1,5 @@
{
"extends": "../../tsconfig.test.json",
"include": ["src", "__test__", "../../typings/**/*.d.ts"],
"include": ["src", "test", "../../typings/**/*.d.ts"],
"references": []
}

@ -0,0 +1,9 @@
# Features
1. [x] RPC methods
2. [x] Request and Response type def.
3. [x] Middleware for provider
4. [x] Base Provider
5. [x] Http Provider
6. [x] Messenger for request manager
7. [x] rpc builder

@ -1,7 +1,7 @@
{
"name": "@woop-js/network",
"version": "0.1.58",
"description": "network suites for woop",
"name": "@harmony-js/network",
"version": "0.0.15",
"description": "network suites for harmony",
"main": "dist/index.js",
"node": "dist/index.js",
"browser": "dist/index.js",
@ -16,12 +16,13 @@
"access": "public"
},
"author": "neeboo@firestack.one",
"license": "MIT",
"license": "ISC",
"dependencies": {
"@woop-js/utils": "0.1.58",
"@harmony-js/core": "0.0.15",
"@harmony-js/utils": "0.0.15",
"cross-fetch": "^3.0.2",
"mitt": "^1.2.0",
"mitt": "^1.1.3",
"websocket": "^1.0.28"
},
"gitHead": "56606e9365721729a490c27d6a294e0daf90fbdf"
"gitHead": "938a044229b662d99c61e57417d1702d44aa7fe2"
}

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-network
* @ignore
*/
import mitt from 'mitt';
export { mitt };
// provider related
@ -13,12 +7,11 @@ export * from './providers/defaultFetcher';
export * from './providers/http';
export * from './providers/ws';
export * from './providers/emitter';
export * from './providers/provider';
// messenger and middlewares
export * from './messenger/messenger';
export * from './messenger/responseMiddleware';
// rpc builder and blockchain method
export * from './rpcMethod/builder';
export * from './rpcMethod/rpcBuilder';
export * from './rpcMethod/net';
export * from './rpcMethod/rpc';
// trackers

@ -1,58 +1,56 @@
/**
* @packageDocumentation
* @module woop-network
*/
import { WoopCore, ChainType, isString, ChainID, defaultConfig } from '@woop-js/utils';
import { JsonRpc } from '../rpcMethod/builder';
import { HarmonyCore, ChainType, isString, ChainID } from '@harmony-js/utils';
import { JsonRpc } from '../rpcMethod/rpcbuilder';
import { ResponseMiddleware } from './responseMiddleware';
import { HttpProvider } from '../providers/http';
import { WSProvider } from '../providers/ws';
// import { getResultForData } from '../util';
import { RPCMethod } from '../rpcMethod/rpc';
import { SubscribeReturns, ShardingItem } from '../types';
/** @hidden */
export interface ShardingProvider {
current: boolean;
shardID: number;
http: string;
ws: string;
}
import { SubscribeReturns } from '../types';
const defaultConfig = {
Default: {
CHAIN_ID: 0,
Network_ID: 'Default',
nodeProviderUrl: 'http://localhost:9128',
},
DevNet: {
CHAIN_ID: 333,
Network_ID: 'DevNet',
nodeProviderUrl: 'https://devnet.harmony.one',
},
TestNet: {
CHAIN_ID: 2,
Network_ID: 'TestNet',
nodeProviderUrl: 'https://devnet.harmony.one',
},
MainNet: {
CHAIN_ID: 1,
Network_ID: 'MainNet',
nodeProviderUrl: 'https://mainnet.harmony.one',
},
};
/**
* ## How to Create a Massage
* @example
* ```
* const { HttpProvider, Messenger } = require('@woop-js/network');
* const { ChainType, ChainID } = require('@woop-js/utils');
*
* // create a custom messenger
* const customMessenger = new Messenger(
* new HttpProvider('http://localhost:9500'),
* ChainType.Woop, // if you are connected to Woop's blockchain
* ChainID.WikiLocal, // check if the chainId is correct
* )
* ```
* @class Messenger
* @description Messenger instance
* @param {HttpProvider} provider HttpProvider
* @param {Object} config config object
* @return {Messenger} Messenger instance
*/
class Messenger extends WoopCore {
class Messenger extends HarmonyCore {
provider: HttpProvider | WSProvider;
config?: object;
// tslint:disable-next-line: variable-name
Network_ID: string = 'Default';
shardProviders: Map<number, ShardingProvider>;
JsonRpc: JsonRpc;
defaultShardID?: number;
constructor(
provider: HttpProvider | WSProvider,
chainType: ChainType = defaultConfig.Default.Chain_Type,
chainId: ChainID = defaultConfig.Default.Chain_ID,
config: object = defaultConfig,
chainType: ChainType = ChainType.Harmony,
chainId: ChainID = ChainID.Default,
config?: object,
) {
super(chainType, chainId);
/**
* @var {Provider} provider
* @memberof Messenger.prototype
@ -65,7 +63,7 @@ class Messenger extends WoopCore {
* @memberof Messenger.prototype
* @description Messenger config
*/
this.config = config;
this.config = config || defaultConfig;
/**
* @var {Number} Network_ID
* @memberof Messenger.prototype
@ -81,31 +79,6 @@ class Messenger extends WoopCore {
// set Network ID
this.setNetworkID(defaultConfig.Default.Network_ID);
// set shardingProviders
this.shardProviders = new Map();
// this.setShardingProviders();
}
/**
* @example
* ```
* customMessenger.currentShard
* ```
*/
get currentShard(): number {
return this.getCurrentShardID() || this.defaultShardID || 0;
}
/**
* @example
* ```
* customMessenger.shardCount
* ```
*/
get shardCount(): number {
return this.shardProviders.size;
}
/**
@ -119,7 +92,6 @@ class Messenger extends WoopCore {
method: RPCMethod | string,
params?: string | any[] | undefined,
rpcPrefix?: string,
shardID: number = this.currentShard,
) => {
this.providerCheck();
let rpcMethod = method;
@ -130,20 +102,14 @@ class Messenger extends WoopCore {
}
try {
const payload = this.JsonRpc.toPayload(rpcMethod, params);
const provider = this.getShardProvider(shardID);
this.setResMiddleware(
(data: any) => {
if (!(data instanceof ResponseMiddleware)) {
return new ResponseMiddleware(data);
} else {
return data;
}
},
'*',
provider,
);
const result = await provider.send(payload);
this.setResMiddleware((data: any) => {
if (!(data instanceof ResponseMiddleware)) {
return new ResponseMiddleware(data);
} else {
return data;
}
});
const result = await this.provider.send(payload);
return result;
// return getResultForData(result); // getResultForData(result)
} catch (e) {
@ -179,10 +145,9 @@ class Messenger extends WoopCore {
* @memberof Messenger
* @param {any} middleware - middle ware for req
* @param {String} method - method name
* @hidden
*/
setReqMiddleware(middleware: any, method = '*', provider: HttpProvider | WSProvider) {
provider.middlewares.request.use(middleware, method);
setReqMiddleware(middleware: any, method = '*') {
this.provider.middlewares.request.use(middleware, method);
}
/**
@ -191,10 +156,9 @@ class Messenger extends WoopCore {
* @memberof Messenger
* @param {any} middleware - middle ware for req
* @param {String} method - method name
* @hidden
*/
setResMiddleware(middleware: any, method = '*', provider: HttpProvider | WSProvider) {
provider.middlewares.response.use(middleware, method);
setResMiddleware(middleware: any, method = '*') {
this.provider.middlewares.response.use(middleware, method);
}
/**
@ -221,7 +185,6 @@ class Messenger extends WoopCore {
params?: string | any[] | undefined,
returnType: SubscribeReturns = SubscribeReturns.all,
rpcPrefix: string = this.chainPrefix,
shardID: number = this.currentShard,
) => {
let rpcMethod = method;
if (rpcPrefix && isString(rpcPrefix) && rpcPrefix !== this.chainPrefix) {
@ -230,29 +193,28 @@ class Messenger extends WoopCore {
rpcMethod = this.setRPCPrefix(method, this.chainPrefix);
}
let id: any = null;
const provider = this.getShardProvider(shardID);
if (provider instanceof WSProvider) {
const reProvider = provider;
if (this.provider instanceof WSProvider) {
const provider = this.provider;
try {
const payload = this.JsonRpc.toPayload(rpcMethod, params);
id = await reProvider.subscribe(payload);
reProvider.on(id, (result: any) => {
reProvider.emitter.emit('data', result);
id = await provider.subscribe(payload);
provider.on(id, (result: any) => {
provider.emitter.emit('data', result);
});
reProvider.once('error', (error) => {
reProvider.removeEventListener(id);
reProvider.emitter.emit('error', error);
reProvider.removeEventListener('*');
provider.once('error', (error) => {
provider.removeEventListener(id);
provider.emitter.emit('error', error);
provider.removeEventListener('*');
});
} catch (error) {
reProvider.emitter.emit('error', error);
reProvider.removeEventListener('*');
provider.emitter.emit('error', error);
provider.removeEventListener('*');
}
if (returnType === SubscribeReturns.all) {
return [reProvider, id];
return [provider, id];
} else if (returnType === SubscribeReturns.method) {
return reProvider;
return provider;
} else if (returnType === SubscribeReturns.id) {
return id;
} else {
@ -267,7 +229,6 @@ class Messenger extends WoopCore {
method: RPCMethod | string,
params?: string | any[] | undefined,
rpcPrefix?: string,
shardID: number = this.currentShard,
) => {
let rpcMethod = method;
if (rpcPrefix && isString(rpcPrefix) && rpcPrefix !== this.chainPrefix) {
@ -275,12 +236,11 @@ class Messenger extends WoopCore {
} else if (!rpcPrefix || rpcPrefix === this.chainPrefix) {
rpcMethod = this.setRPCPrefix(method, this.chainPrefix);
}
const provider = this.getShardProvider(shardID);
if (provider instanceof WSProvider) {
const reProvider = this.provider;
if (this.provider instanceof WSProvider) {
const provider = this.provider;
try {
const payload = this.JsonRpc.toPayload(rpcMethod, params);
const response = await reProvider.unsubscribe(payload);
const response = await provider.unsubscribe(payload);
return response;
} catch (error) {
throw error;
@ -289,68 +249,5 @@ class Messenger extends WoopCore {
throw new Error('HttpProvider does not support this');
}
};
async setShardingProviders() {
if (this.chainPrefix !== ChainType.Woop) {
return;
}
try {
const response = await this.send(RPCMethod.GetShardingStructure, [], this.chainPrefix);
if (response.result) {
const shardingStructures: ShardingItem[] = response.result;
for (const shard of shardingStructures) {
const shardID =
typeof shard.shardID === 'string' ? Number.parseInt(shard.shardID, 10) : shard.shardID;
this.shardProviders.set(shardID, {
current: shard.current,
shardID,
http: shard.http,
ws: shard.ws,
});
}
}
} catch (error) {
return;
}
}
/**
* @example
* ```
* wiki.messenger.getShardProvider()
* ```
*/
getShardProvider(shardID: number): HttpProvider | WSProvider {
const provider = this.shardProviders.get(shardID);
if (provider) {
return this.provider instanceof HttpProvider
? new HttpProvider(provider.http)
: new WSProvider(provider.ws);
}
return this.provider;
}
/**
* @example
* ```
* wiki.messenger.getCurrentShardID()
* ```
*/
getCurrentShardID() {
for (const shard of this.shardProviders) {
if (
shard[1].current === true ||
shard[1].http === this.provider.url ||
shard[1].ws === this.provider.url
) {
return shard[1].shardID;
}
}
}
setDefaultShardID(shardID: number) {
this.defaultShardID = shardID;
}
}
export { Messenger };

@ -1,11 +1,5 @@
/**
* @packageDocumentation
* @module woop-network
* @hidden
*/
import { RPCResponseBody } from '../types';
import { isObject } from '@woop-js/utils';
import { isObject } from '@harmony-js/utils';
/**
* @class ResponseMiddleware
* @description Response middleware of RPC
@ -25,11 +19,15 @@ class ResponseMiddleware {
}
get getResult() {
return isObject(this.result) ? { ...this.result, responseType: 'result' } : this.result;
return isObject(this.result)
? { ...this.result, responseType: 'result' }
: this.result;
}
get getError() {
return isObject(this.error) ? { ...this.error, responseType: 'error' } : this.error;
return isObject(this.error)
? { ...this.error, responseType: 'error' }
: this.error;
}
get getRaw() {
@ -39,7 +37,10 @@ class ResponseMiddleware {
getResponseType(): string {
if (this.error) {
return 'error';
} else if (this.result || (this.result === null && this.result !== undefined)) {
} else if (
this.result ||
(this.result === null && this.result !== undefined)
) {
return 'result';
} else {
return 'raw';

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-network
* @hidden
*/
import { ReqMiddleware, ResMiddleware, MiddlewareType } from '../types';
import { RPCMethod } from '../rpcMethod/rpc';
@ -34,7 +28,11 @@ class BaseProvider {
this.url = url;
}
protected pushMiddleware(fn: any, type: MiddlewareType, match: string | RPCMethod | RegExp) {
protected pushMiddleware(
fn: any,
type: MiddlewareType,
match: string | RPCMethod | RegExp,
) {
if (type !== MiddlewareType.REQ && type !== MiddlewareType.RES) {
throw new Error('Please specify the type of middleware being added');
}
@ -46,7 +44,9 @@ class BaseProvider {
this.resMiddleware.set(match, [...current, <ResMiddleware>fn]);
}
}
protected getMiddleware(method: RPCMethod | string): [ReqMiddleware[], ResMiddleware[]] {
protected getMiddleware(
method: RPCMethod | string,
): [ReqMiddleware[], ResMiddleware[]] {
const requests: ReqMiddleware[] = [];
const responses: ResMiddleware[] = [];

@ -1,21 +1,15 @@
/**
* @packageDocumentation
* @module woop-network
* @hidden
*/
import { isWs } from '@woop-js/utils';
import { isWs } from '@harmony-js/utils';
import mitt from 'mitt';
import { BaseProvider } from './baseProvider';
export enum SocketConnection {
export const enum SocketConnection {
READY = 'ready',
CONNECT = 'connect',
ERROR = 'error',
CLOSE = 'close',
}
export enum SocketState {
export const enum SocketState {
SOCKET_MESSAGE = 'socket_message',
SOCKET_READY = 'socket_ready',
SOCKET_CLOSE = 'socket_close',
@ -25,7 +19,7 @@ export enum SocketState {
SOCKET_ACCOUNTS_CHANGED = 'socket_accountsChanged',
}
export enum EmittType {
export const enum EmittType {
INSTANCE = 'instance',
PUBSUB = 'pubsub',
}
@ -40,7 +34,7 @@ class BaseSocket extends BaseProvider {
throw new Error(`${url} is not websocket`);
}
this.url = url;
this.emitter = mitt(this.handlers);
this.emitter = new mitt(this.handlers);
}
resetHandlers() {
// tslint:disable-next-line: forin

@ -1,16 +1,13 @@
/**
* @packageDocumentation
* @module woop-network
* @hidden
*/
import fetch from 'cross-fetch';
import { RPCRequest, RPCResponseBody, RPCError, RPCResult } from '../types';
export const fetchRPC = {
requestHandler: (request: RPCRequest<any[]>, headers: any) =>
fetch(request.url, {
method: request.options && request.options.method ? request.options.method : 'POST',
method:
request.options && request.options.method
? request.options.method
: 'POST',
cache: 'no-cache',
mode: 'cors',
redirect: 'follow',
@ -18,10 +15,16 @@ export const fetchRPC = {
body: JSON.stringify(request.payload),
headers: {
...headers,
...(request.options && request.options.headers ? request.options.headers : {}),
...(request.options && request.options.headers
? request.options.headers
: {}),
},
}),
responseHandler: (response: Response, request: RPCRequest<any>, handler: any) =>
responseHandler: (
response: Response,
request: RPCRequest<any>,
handler: any,
) =>
response
.json()
.then((body: RPCResponseBody<RPCResult, RPCError>) => {

@ -1,8 +1,3 @@
/**
* @packageDocumentation
* @module woop-network
*/
import mitt from 'mitt';
class Emitter {
@ -15,7 +10,7 @@ class Emitter {
reject?: any;
then?: any;
constructor() {
this.emitter = mitt(this.handlers);
this.emitter = new mitt(this.handlers);
this.off = this.emitter.off.bind(this);
this.emit = this.emitter.emit.bind(this);
// tslint:disable-next-line: no-empty

@ -1,15 +1,14 @@
/**
* @packageDocumentation
* @module woop-network
*/
import { BaseProvider } from './baseProvider';
import { fetchRPC } from './defaultFetcher';
import { composeMiddleware, performRPC, DEFAULT_TIMEOUT, DEFAULT_HEADERS } from '../rpcMethod/net';
import {
composeMiddleware,
performRPC,
DEFAULT_TIMEOUT,
DEFAULT_HEADERS,
} from '../rpcMethod/net';
import { RPCRequestPayload } from '../types';
/** @hidden */
const defaultOptions = {
method: 'POST',
timeout: DEFAULT_TIMEOUT,
@ -24,7 +23,7 @@ class HttpProvider extends BaseProvider {
options?: any;
constructor(url: string, options?: any, fetcher?: any) {
super(url);
this.url = url || 'http://localhost:9500';
this.url = url || 'http://localhost:9128';
this.fetcher = fetcher || fetchRPC;
if (options) {
this.options = {
@ -58,7 +57,11 @@ class HttpProvider extends BaseProvider {
* @param {Function} callback - callback function
* @return {Function} - RPC Response
*/
sendServer(endpoint: string, payload: RPCRequestPayload<object>, callback: any): Promise<any> {
sendServer(
endpoint: string,
payload: RPCRequestPayload<object>,
callback: any,
): Promise<any> {
return this.requestFunc({ endpoint, payload, callback });
}
@ -108,7 +111,10 @@ class HttpProvider extends BaseProvider {
endpointHandler(obj: object, endpoint?: string): object {
return {
...obj,
url: endpoint !== null && endpoint !== undefined ? `${this.url}${endpoint}` : this.url,
url:
endpoint !== null && endpoint !== undefined
? `${this.url}${endpoint}`
: this.url,
};
}

@ -1,8 +1,3 @@
/**
* @packageDocumentation
* @module woop-network
*/
// TODO: implement Websocket Provider
import { w3cwebsocket as W3CWebsocket } from 'websocket';
import {
@ -11,8 +6,8 @@ import {
SocketState,
// EmittType,
} from './baseSocket';
import { isWs, isObject, isArray } from '@woop-js/utils';
import { JsonRpc } from '../rpcMethod/builder';
import { isWs, isObject, isArray } from '@harmony-js/utils';
import { JsonRpc } from '../rpcMethod/rpcbuilder';
import { composeMiddleware } from '../rpcMethod/net';
import { RPCRequestPayload } from '../types';
@ -26,6 +21,7 @@ class WSProvider extends BaseSocket {
options: any;
connection: W3CWebsocket | WebSocket;
jsonRpc: JsonRpc;
on: any;
// ws: w3cwebsocket;
constructor(url: string, options: any = {}) {
@ -39,31 +35,13 @@ class WSProvider extends BaseSocket {
this.jsonRpc = new JsonRpc();
this.subscriptions = {};
this.registerEventListeners();
// this.on = this.emitter.on.bind(this);
this.on = this.emitter.on.bind(this);
}
on(type: string, handler: mitt.Handler) {
this.emitter.on(type, handler);
return this;
}
onData(handler: any) {
this.emitter.on('data', handler);
return this;
}
onError(event: any) {
if (event.code === 'ECONNREFUSED') {
this.reconnect();
return;
}
super.onError(event);
}
onClose(closeEvent: any) {
if (closeEvent.code !== 1000 || closeEvent.wasClean === false) {
this.reconnect();
return;
}
super.onClose();
}
createWebsocketProvider(url: string, options: any = {}) {
// tslint:disable-next-line: no-string-literal
@ -75,9 +53,9 @@ class WSProvider extends BaseSocket {
const urlObject = new URL(url);
if (!headers.authorization && urlObject.username && urlObject.password) {
const authToken = Buffer.from(`${urlObject.username}:${urlObject.password}`).toString(
'base64',
);
const authToken = Buffer.from(
`${urlObject.username}:${urlObject.password}`,
).toString('base64');
headers.authorization = `Basic ${authToken}`;
}
@ -109,31 +87,22 @@ class WSProvider extends BaseSocket {
const resMiddleware = composeMiddleware(...tRes);
return new Promise((resolve, reject) => {
// TODO: test on Error
if (this.connected) {
if (!this.isConnecting()) {
try {
this.connection.send(reqMiddleware(JSON.stringify(payload)));
} catch (error) {
// TODO !isConnecting then reconnect?
this.removeEventListener(SocketConnection.ERROR);
throw error;
}
this.emitter.on(`${payload.id}`, (data) => {
resolve(resMiddleware(data));
this.removeEventListener(`${payload.id}`);
});
}
this.emitter.on(SocketConnection.CONNECT, () => {
try {
this.connection.send(reqMiddleware(JSON.stringify(payload)));
} catch (error) {
// TODO !isConnecting then reconnect?
this.removeEventListener(SocketConnection.ERROR);
throw error;
}
});
this.emitter.on(`${payload.id}`, (data) => {
resolve(resMiddleware(data));
this.removeEventListener(`${payload.id}`);
this.send(payload)
.then(resolve)
.catch(reject);
});
this.emitter.on(SocketConnection.ERROR, reject);
});
}
@ -149,7 +118,6 @@ class WSProvider extends BaseSocket {
parameters: payload.params,
payload,
};
return response.result;
}
@ -159,6 +127,7 @@ class WSProvider extends BaseSocket {
return this.send(payload).then((response) => {
if (response) {
this.removeEventListener(this.getSubscriptionEvent(subscriptionId));
delete this.subscriptions[subscriptionId];
}
@ -167,7 +136,9 @@ class WSProvider extends BaseSocket {
}
return Promise.reject(
new Error(`Provider error: Subscription with ID ${subscriptionId} does not exist.`),
new Error(
`Provider error: Subscription with ID ${subscriptionId} does not exist.`,
),
);
}
@ -177,13 +148,17 @@ class WSProvider extends BaseSocket {
Object.keys(this.subscriptions).forEach((key) => {
this.removeEventListener(key);
unsubscribePromises.push(
this.unsubscribe(this.jsonRpc.toPayload(unsubscribeMethod, this.subscriptions[key].id)),
this.unsubscribe(
this.jsonRpc.toPayload(unsubscribeMethod, this.subscriptions[key].id),
),
);
});
const results = await Promise.all(unsubscribePromises);
if (results.includes(false)) {
throw new Error(`Could not unsubscribe all subscriptions: ${JSON.stringify(results)}`);
throw new Error(
`Could not unsubscribe all subscriptions: ${JSON.stringify(results)}`,
);
}
return true;
}
@ -206,10 +181,10 @@ class WSProvider extends BaseSocket {
if (isArray(result)) {
event = result[0].id;
}
// tslint:disable-next-line: prefer-conditional-expression
if (typeof result.id === 'undefined') {
event =
this.getSubscriptionEvent(result.params.subscription) || result.params.subscription;
this.getSubscriptionEvent(result.params.subscription) ||
result.params.subscription;
// result = result.params;
} else {
event = result.id;
@ -217,6 +192,7 @@ class WSProvider extends BaseSocket {
} catch (error) {
throw error;
}
this.emitter.emit(SocketState.SOCKET_MESSAGE, result);
this.emitter.emit(`${event}`, result);
} else {
@ -231,7 +207,9 @@ class WSProvider extends BaseSocket {
if (subscriptionKeys.length > 0) {
for (const key of subscriptionKeys) {
const subscriptionId: any = await this.subscribe(this.subscriptions[key].payload);
const subscriptionId: any = await this.subscribe(
this.subscriptions[key].payload,
);
delete this.subscriptions[subscriptionId];
this.subscriptions[key].id = subscriptionId;
}
@ -269,7 +247,9 @@ class WSProvider extends BaseSocket {
if (payload && response.id !== payload.id) {
return new Error(
`Validation error: Invalid JSON-RPC response ID (request: ${payload.id} / response: ${response.id})`,
`Validation error: Invalid JSON-RPC response ID (request: ${
payload.id
} / response: ${response.id})`,
);
}

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-network
* @ignore
*/
export const DEFAULT_TIMEOUT: number = 120000;
export const DEFAULT_HEADERS: object = { 'Content-Type': 'application/json' };
@ -27,7 +21,9 @@ export const performRPC = async (request: any, handler: any, fetcher: any) => {
try {
const response = await _fetch(
fetcher.requestHandler(request, DEFAULT_HEADERS),
request.options && request.options.timeout ? request.options.timeout : DEFAULT_TIMEOUT,
request.options && request.options.timeout
? request.options.timeout
: DEFAULT_TIMEOUT,
);
return fetcher.responseHandler(response, request, handler);
} catch (err) {

@ -0,0 +1,89 @@
export const enum RPCMethod {
// 1. hmy_getBlockByHash
GetBlockByHash = 'hmy_getBlockByHash',
// 2. hmy_getBlockByNumber
GetBlockByNumber = 'hmy_getBlockByNumber',
// 3. hmy_getBlockTransactionCountByHash
GetBlockTransactionCountByHash = 'hmy_getBlockTransactionCountByHash',
// 4. hmy_getBlockTransactionCountByNumber
GetBlockTransactionCountByNumber = 'hmy_getBlockTransactionCountByNumber',
// 5. hmy_getCode
GetCode = 'hmy_getCode',
// 6. hmy_getTransactionByBlockHashAndIndex
GetTransactionByBlockHashAndIndex = 'hmy_getTransactionByBlockHashAndIndex',
// 7. hmy_getTransactionByBlockNumberAndIndex
GetTransactionByBlockNumberAndIndex = 'hmy_getTransactionByBlockNumberAndIndex',
// 8. hmy_getTransactionByHash
GetTransactionByHash = 'hmy_getTransactionByHash',
GetTransactionReceipt = 'hmy_getTransactionReceipt',
// 9. hmy_syncing
Syncing = 'hmy_syncing',
// 10. net_peerCount
PeerCount = 'net_peerCount',
// 11. hmy_getBalance
GetBalance = 'hmy_getBalance',
// 12. hmy_getStorageAt
GetStorageAt = 'hmy_getStorageAt',
// 13. hmy_getTransactionCount
GetTransactionCount = 'hmy_getTransactionCount',
// 14. hmy_sendTransaction
SendTransaction = 'hmy_sendTransaction',
// 15. hmy_sendRawTransaction
SendRawTransaction = 'hmy_sendRawTransaction',
// 16. hmy_subscribe
Subscribe = 'hmy_subscribe',
// 17. hmy_getlogs
GetPastLogs = 'hmy_getLogs',
// 18. hmy_getWork
GetWork = 'hmy_getWork',
// 19. hmy_submitWork
// SubmitWork = 'hmy_submitWork',
// 20. hmy_getProof
GetProof = 'hmy_getProof',
// 21, hmy_getFilterChanges
GetFilterChanges = 'hmy_getFilterChanges',
// 22. hmy_newPendingTransactionFilter
NewPendingTransactionFilter = 'hmy_newPendingTransactionFilter',
// 23. hmy_newBlockFilter
NewBlockFilter = 'hmy_newBlockFilter',
// 24. hmy_newFilter
NewFilter = 'hmy_newFilter',
// 25. hmy_call
Call = 'hmy_call',
// 26. hmy_estimateGas
EstimateGas = 'hmy_estimateGas',
// 27. hmy_gasPrice
GasPrice = 'hmy_gasPrice',
// 28. hmy_blockNumber
BlockNumber = 'hmy_blockNumber',
// 29. hmy_unsubscribe
UnSubscribe = 'hmy_unsubscribe',
}
export const enum RPCErrorCode {
// Standard JSON-RPC 2.0 errors
// RPC_INVALID_REQUEST is internally mapped to HTTP_BAD_REQUEST (400).
// It should not be used for application-layer errors.
RPC_INVALID_REQUEST = -32600,
// RPC_METHOD_NOT_FOUND is internally mapped to HTTP_NOT_FOUND (404).
// It should not be used for application-layer errors.
RPC_METHOD_NOT_FOUND = -32601,
RPC_INVALID_PARAMS = -32602,
// RPC_INTERNAL_ERROR should only be used for genuine errors in bitcoind
// (for example datadir corruption).
RPC_INTERNAL_ERROR = -32603,
RPC_PARSE_ERROR = -32700,
// General application defined errors
RPC_MISC_ERROR = -1, // std::exception thrown in command handling
RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter
RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key
RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter
RPC_DATABASE_ERROR = -20, // Database error
RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format
RPC_VERIFY_ERROR = -25, // General error during transaction or block submission
RPC_VERIFY_REJECTED = -26, // Transaction or block was rejected by network rules
RPC_IN_WARMUP = -28, // Client still warming up
RPC_METHOD_DEPRECATED = -32, // RPC method is deprecated
}

@ -1,9 +1,3 @@
/**
* @packageDocumentation
* @module woop-network
* @hidden
*/
import { RPCRequestPayload } from '../types';
import { RPCMethod } from './rpc';
/**
@ -35,15 +29,17 @@ class JsonRpc {
params: string | undefined | any[],
): RPCRequestPayload<any> => {
// FIXME: error to be done by shared/errors
if (!method) {
throw new Error('jsonrpc method should be specified!');
}
if (!method) throw new Error('jsonrpc method should be specified!');
// advance message ID
this.messageId += 1;
const sendParams =
params === undefined ? [] : typeof params === 'string' ? [params] : [...params];
params === undefined
? []
: typeof params === 'string'
? [params]
: [...params];
return {
jsonrpc: '2.0',

@ -0,0 +1,54 @@
import { Messenger } from '../messenger/messenger';
import { SubscriptionMethod } from './Subscription';
import { RPCMethod } from '../rpcMethod/rpc';
export class LogSub extends SubscriptionMethod {
constructor(options: any, messenger: Messenger) {
super('logs', options, messenger);
this.subscribe();
}
async subscribe() {
// if (
// (this.options.fromBlock && this.options.fromBlock !== 'latest') ||
// this.options.fromBlock === 0
// ) {
try {
const getPastLogs = await this.messenger.send(RPCMethod.GetPastLogs, [
...this.options,
]);
if (getPastLogs.isError()) {
this.emitter.emit('error', getPastLogs.message);
}
const logs = getPastLogs.result;
logs.forEach((log: any) => {
const formattedLog = this.onNewSubscriptionItem(log);
this.emitter.emit('data', formattedLog);
});
delete this.options.fromBlock;
super.start();
return this;
} catch (error) {
this.emitter.emit('error', error);
}
// }
// return this;
}
onNewSubscriptionItem(subscriptionItem: any) {
// todo log formatter
const log = subscriptionItem;
if (log.removed) {
this.emitter.emit('changed', log);
}
return log;
}
// todo formatter
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save