Compare commits
No commits in common. 'master' and '@types' have entirely different histories.
@ -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,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,6 +1,8 @@ |
||||
{ |
||||
"packages": ["packages/*"], |
||||
"packages": [ |
||||
"packages/*" |
||||
], |
||||
"npmClient": "yarn", |
||||
"useWorkspaces": true, |
||||
"version": "0.1.58" |
||||
"version": "0.0.15" |
||||
} |
||||
|
File diff suppressed because it is too large
Load Diff
@ -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 |
||||
|
||||
``` |
@ -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,9 +1,3 @@ |
||||
/** |
||||
* @packageDocumentation |
||||
* @module woop-contract |
||||
* @hidden |
||||
*/ |
||||
|
||||
import { AbiCoderClass } from './api'; |
||||
import { AbiCoder as EtherCoder } from './abiCoder'; |
||||
|
@ -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) { |
@ -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,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 |
||||
*/ |
||||
|
||||
// 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,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 |
@ -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', |
||||
}, |
||||
]; |
@ -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'); |
||||
}; |
@ -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,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 |
@ -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
|
||||
} |
@ -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…
Reference in new issue