├── .DS_Store ├── .editorconfig ├── .github └── stale.yml ├── .gitignore ├── .mocharc ├── .npmignore ├── .vscode └── settings.json ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs ├── assets │ ├── css │ │ ├── main.css │ │ └── main.css.map │ ├── images │ │ ├── icons.png │ │ ├── icons@2x.png │ │ ├── widgets.png │ │ └── widgets@2x.png │ └── js │ │ ├── main.js │ │ └── search.js ├── classes │ ├── _ark_.client.html │ ├── api.accountapi.html │ ├── api.blockapi.html │ ├── api.delegateapi.html │ ├── api.loaderapi.html │ ├── api.peerapi.html │ ├── api.transactionapi.html │ ├── core.hdnode.html │ ├── core.privatekey.html │ ├── core.publickey.html │ ├── core.tx.html │ ├── model.account.html │ ├── model.accountbalanceresponse.html │ ├── model.accountqueryparams.html │ ├── model.accountresponse.html │ ├── model.accountvoter.html │ ├── model.accountvotesresponse.html │ ├── model.block.html │ ├── model.blockfee.html │ ├── model.blockfees.html │ ├── model.blockheight.html │ ├── model.blockqueryparams.html │ ├── model.blockresponse.html │ ├── model.blockstatus.html │ ├── model.delegate.html │ ├── model.delegatequeryparams.html │ ├── model.delegateresponse.html │ ├── model.delegatevoters.html │ ├── model.fees.html │ ├── model.forgeddetails.html │ ├── model.loaderautoconfigure.html │ ├── model.loadernetworkresponse.html │ ├── model.loaderstatus.html │ ├── model.loaderstatussync.html │ ├── model.network.html │ ├── model.peer.html │ ├── model.peerqueryparams.html │ ├── model.peerresponse.html │ ├── model.peerversion2configdataresponse.html │ ├── model.peerversion2configresponse.html │ ├── model.peerversionresponse.html │ ├── model.transaction.html │ ├── model.transactiondelegate.html │ ├── model.transactionpayload.html │ ├── model.transactionpostdataresponse.html │ ├── model.transactionpostresponse.html │ ├── model.transactionqueryparams.html │ ├── model.transactionresponse.html │ ├── model.transactionsend.html │ ├── model.transactionvote.html │ ├── services.http.html │ ├── services.rxrequest.html │ ├── utils.crypto.html │ └── utils.slot.html ├── enums │ ├── model.networktype.html │ ├── model.transactiontype.html │ └── model.votetype.html ├── globals.html ├── index.html └── modules │ ├── _ark_.html │ ├── api.html │ ├── core.html │ ├── model.html │ ├── services.html │ └── utils.html ├── lib └── .gitkeep ├── package.json ├── renovate.json ├── src ├── Ark.ts ├── api │ ├── AccountApi.ts │ ├── BlockApi.ts │ ├── DelegateApi.ts │ ├── LoaderApi.ts │ ├── PeerApi.ts │ ├── TransactionApi.ts │ ├── index.ts │ └── test │ │ ├── AccountApi.spec.ts │ │ ├── BlockApi.spec.ts │ │ ├── DelegateApi.spec.ts │ │ ├── LoaderApi.spec.ts │ │ ├── PeerApi.spec.ts │ │ └── TransactionApi.spec.ts ├── config │ └── index.ts ├── core │ ├── HDNode.ts │ ├── Key.ts │ ├── Tx.ts │ └── index.ts ├── index.ts ├── model │ ├── Account.ts │ ├── Block.ts │ ├── Delegate.ts │ ├── Loader.ts │ ├── Network.ts │ ├── Peer.ts │ ├── Transaction.ts │ ├── index.ts │ └── test │ │ └── Network.spec.ts ├── services │ ├── Http.ts │ ├── RxRequest.ts │ └── index.ts └── utils │ ├── Crypto.ts │ ├── Slot.ts │ ├── index.ts │ └── test │ ├── Crypto.spec.ts │ └── Slot.spec.ts ├── tsconfig.json ├── tslint.json ├── typedoc.json └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArkEcosystemArchive/ark-ts/47cd596081b8eb410e38322d15d5238741633d2e/.DS_Store -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs 2 | # editorconfig.org 3 | 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | 10 | # We recommend you to keep these unchanged 11 | end_of_line = lf 12 | charset = utf-8 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | _extends: .github 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | package-lock.json 2 | node_modules/ 3 | typings/ 4 | lib/* 5 | !lib/.gitkeep 6 | 7 | # Microsoft Visual Studio 8 | .vs 9 | -------------------------------------------------------------------------------- /.mocharc: -------------------------------------------------------------------------------- 1 | --compilers ts:ts-node/register 2 | --require source-map-support/register 3 | --full-trace 4 | --bail 5 | --timeout 6000 6 | src/**/*.spec.ts 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | src/ 3 | docs/ 4 | node_modules/ 5 | tsconfig.js 6 | typedoc.json 7 | tslint.json 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.tabSize": 2, 3 | "editor.insertSpaces": true, 4 | "files.eol": "\n", 5 | "files.trimTrailingWhitespace": true, 6 | "files.exclude": { 7 | "**/.git": true, 8 | "**/package-lock.json": true 9 | }, 10 | "search.exclude":{ 11 | "**/node_modules": true 12 | }, 13 | "tslint.enable": true, 14 | "tslint.exclude": "./node_modules/**/*.ts" 15 | } 16 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @alexbarnsley @luciorubeens 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # BOUNTY Program 2 | ARK has a bounty program for all accepted PR (Pull Requests) for this repository 3 | 4 | More information can be found at https://blog.ark.io/ark-github-development-bounty-113806ae9ffe 5 | 6 | Before pushing PR, please [jump in our slack #development](https://ark.io/slack) channel in order to discuss your contributions or to connect with other ARKvelopers. 7 | 8 | # Guidelines 9 | - pickup any of the existing issues or if you find an issue make a PR, 10 | - only one PR reward will be awarded per issue it fixes, 11 | - solving an open issue will increase your chances to be picked up as any of the monthly bounty winners. 12 | 13 | # Accepted PR 14 | - increase general code quality, 15 | - add meaningfull tests, 16 | - correct bug, 17 | - add new features, 18 | - improve documentation, 19 | - create something new for ARK. 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ark.io, Lúcio Rubens 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **:warning: SOON TO BE DEPRECATED IN FAVOR OF https://github.com/ArkEcosystem/core/tree/master/packages/crypto - PLEASE SUBMIT PULL REQUESTS TO THE CORE V2 REPOSITORY :warning:*** 2 | 3 | ![TSARK Logo](https://i.imgur.com/AyhlVoZ.png) 4 | 5 | # TSARK 6 | 7 | > An ARK API wrapper, written in TypeScript to interact with ARK blockchain. 8 | 9 | [![npm](https://img.shields.io/npm/dt/ark-ts.svg)]() 10 | [![npm](https://img.shields.io/npm/v/ark-ts.svg)]() 11 | [![license](https://img.shields.io/github/license/arkecosystem/ark-ts.svg)]() 12 | 13 | > Lead Maintainer: [Lúcio Rubens](https://github.com/luciorubeens) 14 | 15 | TSARK is a library client designed to facilitate how you interact with the ARK blockchain. 16 | 17 | ## Why TypeScript 18 | 19 | * TypeScript is is a superset of JavaScript which mainly offers optional static typing, classes, and interfaces. The learning curve is not that steep. 20 | * Types are optional, TSARK compiles into ES5 so you can work with both, ECMAScript or TypeScript. 21 | * A better development experience, including auto-complete and fully documented. 22 | 23 | ## Documentation 24 | 25 | > [API documentation](https://arkecosystem.github.io/ark-ts/) is hosted on github pages, and is generated from [TypeDoc](https://github.com/TypeStrong/typedoc). 26 | 27 | ## Installation 28 | 29 | TSARK is avaliable from `npm`. 30 | 31 | ```bash 32 | yarn add ark-ts 33 | ``` 34 | 35 | or 36 | 37 | ```bash 38 | npm i ark-ts --save 39 | ``` 40 | 41 | ## Usage 42 | 43 | For the best TypeScript experience, you should either use [Visual Studio Code](http://code.visualstudio.com/), or a [plug-in for your favorite text editor](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Editor-Support). 44 | 45 | ### Basic Examples 46 | 47 | > Get delegate list from Devnet network. 48 | 49 | ```js 50 | import { Client, Network, NetworkType } from 'ark-ts'; 51 | 52 | const devnet = Network.getDefault(NetworkType.Devnet); 53 | const client = new Client(devnet); 54 | 55 | client.delegate.list().subscribe((list) => { 56 | console.log(list); 57 | }); 58 | ``` 59 | 60 | > Get address from passphrase. 61 | 62 | ```js 63 | import { PrivateKey } from 'ark-ts/core'; 64 | 65 | // if no specify a second param, default is mainnet 66 | const key = PrivateKey.fromSeed('my secret passphrase'); 67 | console.log(key.getPublicKey().getAddress()); // AaWU6X3pGdtSCK3s9weo9tjth64F3hixgT 68 | ``` 69 | 70 | For more examples please see documentation or look for tests in each directory. 71 | 72 | ## Running the tests 73 | 74 | ```bash 75 | npm run test 76 | ``` 77 | 78 | ## Security 79 | 80 | If you discover a security vulnerability within this project, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. 81 | 82 | ## Contributing 83 | 84 | * If you find any bugs, submit an [issue](../../issues) or open [pull-request](../../pulls), helping us catch and fix them. 85 | * Engage with other users and developers on [ARK Slack](https://ark.io/slack/). 86 | * Join the #development channel on Slack or contact our developer Lúcio (@lorenzo). 87 | * [Contribute bounties](./CONTRIBUTING.md). 88 | 89 | ## Security 90 | 91 | If you discover a security vulnerability within this package, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. 92 | 93 | ## Credits 94 | 95 | This project exists thanks to all the people who [contribute](../../contributors). 96 | 97 | ## License 98 | 99 | [MIT](LICENSE) © [ARK Ecosystem](https://ark.io) 100 | -------------------------------------------------------------------------------- /docs/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArkEcosystemArchive/ark-ts/47cd596081b8eb410e38322d15d5238741633d2e/docs/assets/images/icons.png -------------------------------------------------------------------------------- /docs/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArkEcosystemArchive/ark-ts/47cd596081b8eb410e38322d15d5238741633d2e/docs/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArkEcosystemArchive/ark-ts/47cd596081b8eb410e38322d15d5238741633d2e/docs/assets/images/widgets.png -------------------------------------------------------------------------------- /docs/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArkEcosystemArchive/ark-ts/47cd596081b8eb410e38322d15d5238741633d2e/docs/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /docs/classes/model.accountqueryparams.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | AccountQueryParams | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 61 |

Class AccountQueryParams

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

Hierarchy

70 |
    71 |
  • 72 | AccountQueryParams 73 |
  • 74 |
75 |
76 |
77 |

Index

78 |
79 |
80 |
81 |

Properties

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

Properties

91 |
92 | 93 |

address

94 |
address: string
95 | 100 |
101 |
102 |
103 | 146 |
147 |
148 | 207 |
208 |

Generated using TypeDoc

209 |
210 |
211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /docs/classes/model.transactionpayload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TransactionPayload | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 61 |

Class TransactionPayload

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

Hierarchy

70 |
    71 |
  • 72 | TransactionPayload 73 |
  • 74 |
75 |
76 |
77 |

Index

78 |
79 |
80 |
81 |

Properties

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

Properties

91 |
92 | 93 |

transactions

94 |
transactions: Transaction[]
95 | 100 |
101 |
102 |
103 | 146 |
147 |
148 | 207 |
208 |

Generated using TypeDoc

209 |
210 |
211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /docs/enums/model.networktype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NetworkType | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 61 |

Enumeration NetworkType

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

Index

70 |
71 |
72 |
73 |

Enumeration members

74 | 78 |
79 |
80 |
81 |
82 |
83 |

Enumeration members

84 |
85 | 86 |

Devnet

87 |
Devnet:
88 | 93 |
94 |
95 | 96 |

Mainnet

97 |
Mainnet:
98 | 103 |
104 |
105 |
106 | 152 |
153 |
154 | 213 |
214 |

Generated using TypeDoc

215 |
216 |
217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /docs/enums/model.votetype.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | VoteType | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 61 |

Enumeration VoteType

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

Index

70 |
71 |
72 |
73 |

Enumeration members

74 | 78 |
79 |
80 |
81 |
82 |
83 |

Enumeration members

84 |
85 | 86 |

Add

87 |
Add:
88 | 93 |
94 |
95 | 96 |

Remove

97 |
Remove:
98 | 103 |
104 |
105 |
106 | 152 |
153 |
154 | 213 |
214 |

Generated using TypeDoc

215 |
216 |
217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /docs/globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 55 |

ark-ts

56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |

Index

64 |
65 |
66 |
67 |

External modules

68 | 76 |
77 |
78 |
79 |
80 |
81 | 112 |
113 |
114 | 173 |
174 |

Generated using TypeDoc

175 |
176 |
177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /docs/modules/_ark_.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | "Ark" | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 58 |

External module "Ark"

59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |

Index

67 |
68 |
69 |
70 |

Classes

71 | 74 |
75 |
76 |
77 |
78 |
79 | 113 |
114 |
115 | 174 |
175 |

Generated using TypeDoc

176 |
177 |
178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /docs/modules/api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | api | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 58 |

External module api

59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |

Index

67 |
68 |
69 |
70 |

Classes

71 | 79 |
80 |
81 |
82 |
83 |
84 | 133 |
134 |
135 | 194 |
195 |

Generated using TypeDoc

196 |
197 |
198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /docs/modules/core.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | core | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 58 |

External module core

59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |

Index

67 |
68 |
69 |
70 |

Classes

71 | 77 |
78 |
79 |
80 |
81 |
82 | 125 |
126 |
127 | 186 |
187 |

Generated using TypeDoc

188 |
189 |
190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /docs/modules/services.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | services | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 58 |

External module services

59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |

Index

67 |
68 |
69 |
70 |

Classes

71 | 75 |
76 |
77 |
78 |
79 |
80 | 117 |
118 |
119 | 178 |
179 |

Generated using TypeDoc

180 |
181 |
182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /docs/modules/utils.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | utils | ark-ts 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 |
42 |
43 | Menu 44 |
45 |
46 |
47 |
48 |
49 |
50 | 58 |

External module utils

59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |

Index

67 |
68 |
69 |
70 |

Classes

71 | 75 |
76 |
77 |
78 |
79 |
80 | 117 |
118 |
119 | 178 |
179 |

Generated using TypeDoc

180 |
181 |
182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /lib/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArkEcosystemArchive/ark-ts/47cd596081b8eb410e38322d15d5238741633d2e/lib/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ark-ts", 3 | "version": "0.4.1", 4 | "description": "An ARK API wrapper, written in TypeScript to interact with ARK blockchain.", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "scripts": { 8 | "build:docs": "typedoc --options typedoc.json src/index.ts", 9 | "build:ts": "shx rm -r lib && tsc -p ./ && touch lib/.gitkeep", 10 | "build:prod": "npm run build:ts && npm run build:docs && npm run copypackage", 11 | "copypackage": "shx cp package.json ./lib/package.json", 12 | "test": "mocha --opts .mocharc", 13 | "lint": "tslint src/**/*.ts" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/arkecosystem/ark-ts.git" 18 | }, 19 | "bugs": { 20 | "url": "https://github.com/ArkEcosystem/ark-ts/issues" 21 | }, 22 | "homepage": "https://github.com/ArkEcosystem/ark-ts#readme", 23 | "keywords": [ 24 | "ark", 25 | "api", 26 | "blockchain", 27 | "cryptocurrency", 28 | "typescript" 29 | ], 30 | "author": "Lúcio Rubens ", 31 | "license": "MIT", 32 | "dependencies": { 33 | "@types/bigi": "^1.4.0", 34 | "@types/node": "^8.0.12", 35 | "@types/request": "^2.0.0", 36 | "@types/rx": "^4.1.1", 37 | "axios": "^0.18.0", 38 | "bigi": "^1.4.2", 39 | "bs58check": "^2.0.2", 40 | "bytebuffer": "^5.0.1", 41 | "create-hash": "^1.1.3", 42 | "create-hmac": "^1.1.6", 43 | "ecurve": "^1.0.5", 44 | "json-typescript-mapper": "^1.1.3", 45 | "randombytes": "^2.0.5", 46 | "rxjs": "^5.5.12", 47 | "secp256k1": "^3.3.0", 48 | "wif": "^2.0.6" 49 | }, 50 | "devDependencies": { 51 | "@types/chai": "^4.0.1", 52 | "@types/mocha": "^2.2.41", 53 | "chai": "^4.1.0", 54 | "mocha": "^3.4.2", 55 | "shx": "^0.2.2", 56 | "source-map-support": "^0.4.15", 57 | "ts-node": "^3.3.0", 58 | "tslint": "^5.6.0", 59 | "typedoc": "^0.8.0", 60 | "typedoc-plugin-external-module-map": "^0.0.6", 61 | "typescript": "^2.4.1" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base", ":preserveSemverRanges"] 3 | } 4 | -------------------------------------------------------------------------------- /src/Ark.ts: -------------------------------------------------------------------------------- 1 | import * as api from './api'; 2 | import { Network } from './model/Network'; 3 | import Http from './services/Http'; 4 | 5 | export default class Client { 6 | 7 | public account: api.AccountApi; 8 | public delegate: api.DelegateApi; 9 | public peer: api.PeerApi; 10 | public loader: api.LoaderApi; 11 | public block: api.BlockApi; 12 | public transaction: api.TransactionApi; 13 | 14 | constructor(network: Network) { 15 | const request = new Http(network); 16 | 17 | this.account = new api.AccountApi(request); 18 | this.delegate = new api.DelegateApi(request); 19 | this.peer = new api.PeerApi(request); 20 | this.loader = new api.LoaderApi(request); 21 | this.block = new api.BlockApi(request); 22 | this.transaction = new api.TransactionApi(request); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/api/AccountApi.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | 3 | import * as model from '../model/Account'; 4 | import Http from '../services/Http'; 5 | 6 | /** Account related API calls. */ 7 | export default class AccountApi { 8 | 9 | constructor(private http: Http) {} 10 | 11 | /** 12 | * Returns account information of an address. 13 | */ 14 | public get(params: model.AccountQueryParams) { 15 | return this.http.get('/accounts', params, model.AccountResponse); 16 | } 17 | 18 | /** 19 | * Get the balance of an account. 20 | */ 21 | public balance(params: model.AccountQueryParams) { 22 | return this.http.get('/accounts/getBalance', params, model.AccountBalanceResponse); 23 | } 24 | 25 | /** 26 | * Get the public key of an account. If the account does not exist the API call will return an error. 27 | */ 28 | public publicKey(params: model.AccountQueryParams) { 29 | return this.http.get('/accounts/getPublicKey', params, model.AccountResponse); 30 | } 31 | 32 | /** 33 | * Get votes by account address. 34 | */ 35 | public votes(params: model.AccountQueryParams) { 36 | return this.http.get('/accounts/delegates', params, model.AccountVotesResponse); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/api/BlockApi.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | 3 | import * as model from '../model/Block'; 4 | import { Network } from '../model/Network'; 5 | import Http from '../services/Http'; 6 | 7 | /** Blocks related API calls. */ 8 | export default class BlockApi { 9 | 10 | constructor(private http: Http) {} 11 | 12 | /** 13 | * Get network fees. 14 | */ 15 | public static networkFees(network: Network) { 16 | return new Http(network).get('/blocks/getfees', null, model.BlockFees); 17 | } 18 | 19 | /** 20 | * Get block by id. 21 | */ 22 | public get(params: model.BlockQueryParams) { 23 | return this.http.get('/blocks/get', params, model.BlockResponse); 24 | } 25 | 26 | /** 27 | * Get all blocks. 28 | */ 29 | public list(params?: model.BlockQueryParams) { 30 | return this.http.get('/blocks', params, model.BlockResponse); 31 | } 32 | 33 | /** 34 | * Get transaction fee for sending "normal" transactions. 35 | */ 36 | public blockchainFee() { 37 | return this.http.get('/blocks/getFee', null, model.BlockFee); 38 | } 39 | 40 | /** 41 | * Get blockchain height. 42 | */ 43 | public blockchainHeight() { 44 | return this.http.get('/blocks/getHeight', null, model.BlockHeight); 45 | } 46 | 47 | /** 48 | * Get blockchain status. 49 | */ 50 | public blockchainStatus() { 51 | return this.http.get('/blocks/getStatus', null, model.BlockStatus); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/api/DelegateApi.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | 3 | import * as model from '../model/Delegate'; 4 | import Http from '../services/Http'; 5 | 6 | /** Delegate related API calls. */ 7 | export default class DelegateApi { 8 | 9 | constructor(private http: Http) {} 10 | 11 | /** 12 | * Get delegate by username. 13 | */ 14 | public get(params: model.DelegateQueryParams) { 15 | return this.http.get('/delegates/get', params, model.DelegateResponse); 16 | } 17 | 18 | /** 19 | * Get delegates list. 20 | */ 21 | public list(params?: model.DelegateQueryParams) { 22 | return this.http.get('/delegates', params, model.DelegateResponse); 23 | } 24 | 25 | /** 26 | * Get voters of delegate. 27 | */ 28 | public voters(params: model.DelegateQueryParams) { 29 | /* To find voters directly by model */ 30 | if (params.delegate && params.delegate instanceof model.Delegate) { 31 | params = {publicKey: params.delegate.publicKey}; 32 | } 33 | 34 | return this.http.get('/delegates/voters', params, model.DelegateVoters); 35 | } 36 | 37 | /** 38 | * Get forged data of delegate. 39 | */ 40 | public forgedData(params: model.DelegateQueryParams) { 41 | /* To find result directly by model */ 42 | if (params.delegate) { 43 | params.generatorPublicKey = params.delegate.publicKey; 44 | } 45 | 46 | if (params.publicKey && !params.generatorPublicKey) { 47 | params.generatorPublicKey = params.publicKey; 48 | } 49 | 50 | return this.http.get('/delegates/forging/getForgedByAccount', params, model.ForgedDetails); 51 | } 52 | 53 | /** 54 | * Search delegates 55 | */ 56 | public search(params: model.DelegateQueryParams) { 57 | return this.http.get('/delegates/search', params, model.DelegateResponse); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/api/LoaderApi.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | 3 | import * as model from '../model/Loader'; 4 | import Http from '../services/Http'; 5 | 6 | /** Loader related API calls. */ 7 | export default class LoaderApi { 8 | 9 | constructor(private http: Http) {} 10 | 11 | /** 12 | * Get configuration info of peer 13 | */ 14 | public autoConfigure(fromPeerUrl?: string) { 15 | if (fromPeerUrl) { 16 | return this.http.getNative( 17 | `${fromPeerUrl}/api/loader/autoconfigure`, null, model.LoaderAutoConfigure); 18 | } 19 | 20 | return this.http.get('/loader/autoconfigure', null, model.LoaderAutoConfigure); 21 | } 22 | /** 23 | * Get status blockchain. 24 | */ 25 | public loadingStatus() { 26 | return this.http.get('/loader/status', null, model.LoaderStatus); 27 | } 28 | 29 | /** 30 | * Get the synchronisation status of the client. 31 | */ 32 | public synchronisationStatus(fromPeerUrl?: string) { 33 | if (fromPeerUrl) { 34 | return this.http.getNative( 35 | `${fromPeerUrl}/api/loader/status/sync`, null, model.LoaderStatusSync); 36 | } 37 | 38 | return this.http.get('/loader/status/sync', null, model.LoaderStatusSync); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/api/PeerApi.ts: -------------------------------------------------------------------------------- 1 | import 'rxjs/add/observable/empty'; 2 | import 'rxjs/add/operator/catch'; 3 | import { Observable } from 'rxjs/Observable'; 4 | 5 | import * as model from '../model'; 6 | import Http from '../services/Http'; 7 | import LoaderApi from './LoaderApi'; 8 | 9 | import config from '../config'; 10 | 11 | /** Peer related API calls. */ 12 | export default class PeerApi { 13 | 14 | constructor(private http: Http) {} 15 | 16 | /** 17 | * Find good peer ordered by synchronized blocks. 18 | */ 19 | public static findGoodPeer(network: model.Network, http?: Http): Observable { 20 | if (http === undefined) { 21 | http = new Http(network); 22 | } 23 | return Observable.create((observer) => { 24 | const networkType = model.NetworkType[http.network.type].toLowerCase(); 25 | const peersList = config.networks[networkType].peers; 26 | const loader = new LoaderApi(http); 27 | const blockList = []; 28 | let completed = false; 29 | 30 | peersList.forEach((element, index) => { 31 | loader 32 | .synchronisationStatus(`http://${element}`) 33 | .subscribe((status) => { 34 | blockList.push([element, status.blocks]); 35 | 36 | // when find a good peer or at the end 37 | if (!completed && (status.blocks <= 0 || peersList.length - 1 === index)) { 38 | completed = true; 39 | blockList.sort((a, b) => a[1] > b[1] ? 1 : -1); // sort by better to the worst 40 | const host = blockList[0][0].split(':'); 41 | 42 | const peer: model.Peer = new model.Peer; 43 | peer.ip = host[0]; 44 | peer.port = host[1]; 45 | 46 | observer.next(peer); 47 | observer.complete(); 48 | } 49 | }, (e) => Observable.empty()); 50 | }); 51 | }); 52 | } 53 | 54 | /** 55 | * Get peer by ip and port. 56 | */ 57 | public get(ip: string, port: number): Observable { 58 | const params = {ip, port}; 59 | return this.http.get('/peers/get', params, model.PeerResponse); 60 | } 61 | 62 | /** 63 | * Get peers list by parameters. 64 | */ 65 | public list(params?: model.PeerQueryParams): Observable { 66 | return this.http.get('/peers', params, model.PeerResponse); 67 | } 68 | 69 | /** 70 | * Find good peer ordered by synchronized blocks. 71 | */ 72 | public findGoodPeer(): Observable { 73 | return PeerApi.findGoodPeer(this.http.network, this.http); 74 | } 75 | 76 | /** 77 | * Get peer version 78 | */ 79 | public version(params?: model.PeerQueryParams): Observable { 80 | return this.http.get('/peers/version', params, model.PeerVersionResponse); 81 | } 82 | 83 | /** 84 | * Get config for version 2 peers. 85 | */ 86 | public getVersion2Config(ip: string, p2pPort: number): Observable { 87 | return Observable.create((observer) => { 88 | this.http.getNative( 89 | `http://${ip}:4040/config`, null, model.PeerVersion2ConfigResponse).subscribe((response) => { 90 | observer.next(response) 91 | observer.complete() 92 | }, () => { 93 | this.http.getNative( 94 | `http://${ip}:${p2pPort}/config`, null, model.PeerVersion2ConfigResponse).subscribe((response) => { 95 | observer.next(response) 96 | observer.complete() 97 | }, (e) => { 98 | observer.error(e) 99 | }) 100 | }) 101 | }) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/api/TransactionApi.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | 3 | import {deserialize, serialize} from 'json-typescript-mapper'; 4 | 5 | import * as model from '../model/Transaction'; 6 | 7 | import Http from '../services/Http'; 8 | import BlockApi from './BlockApi'; 9 | import { Peer } from '../model/Peer'; 10 | 11 | import { PrivateKey, PublicKey } from '../core/Key'; 12 | import Tx from '../core/Tx'; 13 | 14 | /** Transaction related API calls. */ 15 | export default class TransactionApi { 16 | 17 | constructor(private http: Http) {} 18 | 19 | /** 20 | * Transaction used to transfer amounts to specific address. 21 | */ 22 | public createTransaction(params: model.TransactionSend): Observable { 23 | return Observable.create((observer: any) => { 24 | 25 | if (!PublicKey.validateAddress(params.recipientId, this.http.network)) { 26 | observer.error('Wrong recipientId'); 27 | } 28 | 29 | if (!params.fee) { 30 | return observer.error('Missing "send" transaction fee'); 31 | } 32 | let data = { 33 | amount: params.amount, 34 | fee: params.fee, 35 | recipientId: params.recipientId, 36 | timestamp: params.timestamp, 37 | type: model.TransactionType.SendArk, 38 | vendorField: params.vendorField, 39 | }; 40 | 41 | const tx = new Tx(data, this.http.network, params.passphrase, params.secondPassphrase); 42 | data = tx.generate(); 43 | const typedTx = deserialize(model.Transaction, data); 44 | 45 | observer.next(typedTx); 46 | observer.complete(); 47 | }); 48 | } 49 | 50 | /** 51 | * Transaction used to vote for a chosen Delegate. 52 | */ 53 | public createVote(params: model.TransactionVote) { 54 | return Observable.create((observer: any) => { 55 | if (!params.fee) { 56 | return observer.error('Missing "vote" transaction fee'); 57 | } 58 | const updown = model.VoteType[params.type] === 'Add' ? '+' : '-'; 59 | 60 | let data = { 61 | asset: { 62 | votes: [updown + params.delegatePublicKey], 63 | }, 64 | fee: params.fee, 65 | type: model.TransactionType.Vote, 66 | vendorField: params.vendorField, 67 | }; 68 | 69 | const tx = new Tx(data, this.http.network, params.passphrase, params.secondPassphrase); 70 | tx.setAddress(); 71 | data = tx.generate(); 72 | 73 | const typedTx = deserialize(model.Transaction, data); 74 | typedTx.asset = data.asset; 75 | 76 | observer.next(typedTx); 77 | observer.complete(); 78 | }); 79 | } 80 | 81 | /** 82 | * Transaction used to register as a Delegate. 83 | */ 84 | public createDelegate(params: model.TransactionDelegate) { 85 | return Observable.create((observer: any) => { 86 | if (params.username.length > 20) { 87 | observer.error('Delegate name is too long, 20 characters maximum'); 88 | } 89 | 90 | if (!params.fee) { 91 | return observer.error('Missing "delegate" transaction fee'); 92 | } 93 | let data = { 94 | asset: { 95 | delegate: { 96 | publicKey: params.publicKey, 97 | username: params.username, 98 | }, 99 | }, 100 | fee: params.fee, 101 | type: model.TransactionType.CreateDelegate, 102 | vendorField: params.vendorField, 103 | }; 104 | 105 | const tx = new Tx(data, this.http.network, params.passphrase, params.secondPassphrase); 106 | data = tx.generate(); 107 | 108 | const typedTx = deserialize(model.Transaction, data); 109 | typedTx.asset = data.asset; 110 | 111 | observer.next(typedTx); 112 | observer.complete(); 113 | }); 114 | } 115 | 116 | /** 117 | * Transaction used to create second passphrase. 118 | */ 119 | public createSignature(passphrase: string | PrivateKey, secondPassphrase: string, vendorField?: string, fee?: number) { 120 | return Observable.create((observer: any) => { 121 | if (!fee) { 122 | return observer.error('Missing "secondsignature" transaction fee'); 123 | } 124 | let data = {}; 125 | data.asset = {}; 126 | data.fee = fee; 127 | data.type = model.TransactionType.SecondSignature; 128 | data.vendorField = vendorField; 129 | 130 | const tx = new Tx(data, this.http.network, passphrase, secondPassphrase); 131 | tx.setAssetSignature(); 132 | data = tx.generate(); 133 | 134 | const typedTx = deserialize(model.Transaction, data); 135 | typedTx.asset = tx.transaction.asset; 136 | 137 | observer.next(typedTx); 138 | observer.complete(); 139 | }); 140 | } 141 | 142 | /** 143 | * Post transaction to broadcast 144 | */ 145 | public post(transaction: model.Transaction, peer?: Peer) { 146 | const params = {transactions: [transaction]}; 147 | const host = peer ? peer.ip : this.http.network.activePeer.ip; 148 | let endpoint = '/peer/transactions'; 149 | 150 | let port = peer ? peer.port : this.http.network.activePeer.port; 151 | 152 | let options = {} 153 | if (this.http.network.isV2) { 154 | endpoint = '/api/transactions'; 155 | options = { 156 | headers: { 157 | 'api-version': 2 158 | }, 159 | }; 160 | } else { 161 | options = { 162 | headers: { 163 | nethash: this.http.network.nethash, 164 | port, 165 | version: this.http.network.p2pVersion || '', 166 | }, 167 | }; 168 | } 169 | 170 | const url = `http://${host}:${port}${endpoint}`; 171 | return this.http.postNative(url, params, model.TransactionPostResponse, options); 172 | } 173 | 174 | /** 175 | * Transaction matched by id. 176 | */ 177 | public get(id: string) { 178 | const params = {id}; 179 | return this.http.get('/transactions/get', params, model.TransactionResponse); 180 | } 181 | 182 | /** 183 | * Get unconfirmed transaction by id. 184 | */ 185 | public getUnconfirmed(id: string) { 186 | const params = {id}; 187 | return this.http.get('/transactions/unconfirmed/get', params, model.TransactionResponse); 188 | } 189 | 190 | /** 191 | * Transactions list matched by provided parameters. 192 | */ 193 | public list(params?: model.TransactionQueryParams) { 194 | return this.http.get('/transactions', params, model.TransactionResponse); 195 | } 196 | 197 | /** 198 | * Transactions unconfirmed list matched by provided parameters. 199 | */ 200 | public listUnconfirmed(params?: model.TransactionQueryParams) { 201 | return this.http.get('/transactions/unconfirmed', params, model.TransactionResponse); 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | export {default as AccountApi} from './AccountApi'; 2 | export {default as BlockApi} from './BlockApi'; 3 | export {default as DelegateApi} from './DelegateApi'; 4 | export {default as LoaderApi} from './LoaderApi'; 5 | export {default as PeerApi} from './PeerApi'; 6 | export {default as TransactionApi} from './TransactionApi'; 7 | -------------------------------------------------------------------------------- /src/api/test/AccountApi.spec.ts: -------------------------------------------------------------------------------- 1 | import AccountApi from '../AccountApi'; 2 | 3 | import Http from '../../services/Http'; 4 | 5 | import { Network, NetworkType } from '../../model/Network'; 6 | 7 | import { expect } from 'chai'; 8 | 9 | /* tslint:disable:no-unused-expression */ 10 | 11 | describe('AccountApi', () => { 12 | const network = Network.getDefault(NetworkType.Devnet); 13 | const http = new Http(network); 14 | const api = new AccountApi(http); 15 | const address = 'DPTj92butfhy527V13bSXMj9SVYZGAVZ1R'; 16 | 17 | it('should be instance of AccountApi', () => { 18 | expect(api).to.be.instanceOf(AccountApi); 19 | }); 20 | 21 | it('should have properties', () => { 22 | expect(api).to.have.property('get'); 23 | expect(api).to.have.property('balance'); 24 | expect(api).to.have.property('publicKey'); 25 | expect(api).to.have.property('votes'); 26 | }); 27 | 28 | it('should return success from get', () => { 29 | return api.get({address}).forEach((response) => { 30 | expect(response).to.have.property('success', true); 31 | }); 32 | }); 33 | 34 | it('should return success from balance', () => { 35 | return api.balance({address}).forEach((response) => { 36 | expect(response).to.have.property('success', true); 37 | }); 38 | }); 39 | 40 | it('should return success from publicKey', () => { 41 | return api.publicKey({address}).forEach((response) => { 42 | expect(response).to.have.property('success', true); 43 | }); 44 | }); 45 | 46 | it('should return success from votes', () => { 47 | return api.votes({address}).forEach((response) => { 48 | expect(response).to.have.property('success', true); 49 | }); 50 | }); 51 | 52 | }); 53 | -------------------------------------------------------------------------------- /src/api/test/BlockApi.spec.ts: -------------------------------------------------------------------------------- 1 | import BlockApi from '../BlockApi'; 2 | 3 | import Http from '../../services/Http'; 4 | 5 | import { Network, NetworkType } from '../../model/Network'; 6 | 7 | import { expect } from 'chai'; 8 | 9 | /* tslint:disable:no-unused-expression */ 10 | 11 | describe('BlockApi', () => { 12 | const network = Network.getDefault(NetworkType.Devnet); 13 | const http = new Http(network); 14 | const api = new BlockApi(http); 15 | const address = 'DPTj92butfhy527V13bSXMj9SVYZGAVZ1R'; 16 | 17 | it('should have static property', () => { 18 | expect(BlockApi).to.have.property('networkFees'); 19 | }); 20 | 21 | it('should return success from networkFees', () => { 22 | return BlockApi.networkFees(network).forEach((response) => { 23 | expect(response).to.have.property('success', true); 24 | }); 25 | }); 26 | 27 | it('should be instance of BlockApi', () => { 28 | expect(api).to.be.instanceOf(BlockApi); 29 | }); 30 | 31 | it('should have properties', () => { 32 | expect(api).to.have.property('get'); 33 | expect(api).to.have.property('list'); 34 | expect(api).to.have.property('blockchainFee'); 35 | expect(api).to.have.property('blockchainHeight'); 36 | }); 37 | 38 | it('should return sucess from get', () => { 39 | return api.get({id: '8911180696223033455'}).forEach((response) => { 40 | expect(response).to.have.property('success', true); 41 | }); 42 | }); 43 | 44 | it('should return sucess from list', () => { 45 | return api.list().forEach((response) => { 46 | expect(response).to.have.property('success', true); 47 | }); 48 | }); 49 | 50 | it('should return sucess from blockchainFee', () => { 51 | return api.blockchainFee().forEach((response) => { 52 | expect(response).to.have.property('success', true); 53 | }); 54 | }); 55 | 56 | it('should return sucess from blockchainHeight', () => { 57 | return api.blockchainHeight().forEach((response) => { 58 | expect(response).to.have.property('success', true); 59 | }); 60 | }); 61 | 62 | }); 63 | -------------------------------------------------------------------------------- /src/api/test/DelegateApi.spec.ts: -------------------------------------------------------------------------------- 1 | import DelegateApi from '../DelegateApi'; 2 | 3 | import Http from '../../services/Http'; 4 | 5 | import { Network, NetworkType } from '../../model/Network'; 6 | 7 | import { expect } from 'chai'; 8 | 9 | /* tslint:disable:no-unused-expression */ 10 | 11 | describe('DelegateApi', () => { 12 | const network = Network.getDefault(NetworkType.Devnet); 13 | const http = new Http(network); 14 | const api = new DelegateApi(http); 15 | const address = 'DPTj92butfhy527V13bSXMj9SVYZGAVZ1R'; 16 | 17 | it('should be instance of DelegateApi', () => { 18 | expect(api).to.be.instanceOf(DelegateApi); 19 | }); 20 | 21 | it('should have properties', () => { 22 | expect(api).to.have.property('get'); 23 | expect(api).to.have.property('list'); 24 | expect(api).to.have.property('voters'); 25 | expect(api).to.have.property('forgedData'); 26 | }); 27 | 28 | it('should return sucess from get', () => { 29 | return api.get({username: 'genesis_14'}).forEach((response) => { 30 | expect(response).to.have.property('success', true); 31 | }); 32 | }); 33 | 34 | it('should return sucess from list', () => { 35 | return api.list().forEach((response) => { 36 | expect(response).to.have.property('success', true); 37 | }); 38 | }); 39 | 40 | it('should return sucess from voters', () => { 41 | return api.voters({ 42 | publicKey: '03bd4f16e39aaba5cba6a87b7498b08ce540f279be367e68ae96fb05dfabe203ad', 43 | }).forEach((response) => { 44 | expect(response).to.have.property('success', true); 45 | }); 46 | }); 47 | 48 | it('should return instance of ForgedDetails from forgedData', () => { 49 | return api.forgedData({ 50 | publicKey: '03bd4f16e39aaba5cba6a87b7498b08ce540f279be367e68ae96fb05dfabe203ad', 51 | }).forEach((response) => { 52 | expect(response).to.be.property('success', true); 53 | }); 54 | }); 55 | 56 | it('should return success from search', () => { 57 | return api.search({ 58 | q: 'genesis', 59 | }).forEach((response) => { 60 | expect(response).to.be.property('success', true); 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /src/api/test/LoaderApi.spec.ts: -------------------------------------------------------------------------------- 1 | import LoaderApi from '../LoaderApi'; 2 | 3 | import Http from '../../services/Http'; 4 | 5 | import { Network, NetworkType } from '../../model/Network'; 6 | 7 | import { expect } from 'chai'; 8 | 9 | /* tslint:disable:no-unused-expression */ 10 | 11 | describe('LoaderApi', () => { 12 | const network = Network.getDefault(NetworkType.Devnet); 13 | const http = new Http(network); 14 | const api = new LoaderApi(http); 15 | const address = 'DPTj92butfhy527V13bSXMj9SVYZGAVZ1R'; 16 | const peerUrl = `http://${network.activePeer.ip}:${network.activePeer.port}` 17 | 18 | it('should be instance of LoaderApi', () => { 19 | expect(api).to.be.instanceOf(LoaderApi); 20 | }); 21 | 22 | it('should have properties', () => { 23 | expect(api).to.have.property('loadingStatus'); 24 | expect(api).to.have.property('synchronisationStatus'); 25 | expect(api).to.have.property('autoConfigure'); 26 | }); 27 | 28 | it ('should return success from autoConfigure (manual http)', () => { 29 | const newHttp = new Http(); 30 | return new LoaderApi(newHttp).autoConfigure(peerUrl).forEach((response) => { 31 | expect(response).to.have.property('success', true); 32 | }); 33 | }); 34 | 35 | it('should return success from autoConfigure', () => { 36 | return api.autoConfigure().forEach((response) => { 37 | expect(response).to.have.property('success', true); 38 | }); 39 | }); 40 | 41 | it('should return success from autoConfigure (manual peer url)', () => { 42 | return api.autoConfigure(peerUrl).forEach((response) => { 43 | expect(response).to.have.property('success', true); 44 | }); 45 | }); 46 | 47 | it('should return sucess from loadingStatus', () => { 48 | return api.loadingStatus().forEach((response) => { 49 | expect(response).to.have.property('success', true); 50 | }); 51 | }); 52 | 53 | it('should return sucess from synchronisationStatus', () => { 54 | return api.synchronisationStatus().forEach((response) => { 55 | expect(response).to.have.property('success', true); 56 | }); 57 | }); 58 | 59 | it('should return sucess from synchronisationStatus (manual peer url)', () => { 60 | return api.synchronisationStatus(peerUrl).forEach((response) => { 61 | expect(response).to.have.property('success', true); 62 | }); 63 | }); 64 | 65 | }); 66 | -------------------------------------------------------------------------------- /src/api/test/PeerApi.spec.ts: -------------------------------------------------------------------------------- 1 | import PeerApi from '../PeerApi'; 2 | 3 | import Http from '../../services/Http'; 4 | 5 | import { Network, NetworkType } from '../../model/Network'; 6 | import { Peer } from '../../model/Peer'; 7 | 8 | import { expect } from 'chai'; 9 | 10 | /* tslint:disable:no-unused-expression */ 11 | 12 | describe('PeerApi', () => { 13 | const network = Network.getDefault(NetworkType.Devnet); 14 | const http = new Http(network); 15 | const api = new PeerApi(http); 16 | const address = 'DPTj92butfhy527V13bSXMj9SVYZGAVZ1R'; 17 | 18 | it('should be instance of PeerApi', () => { 19 | expect(api).to.be.instanceOf(PeerApi); 20 | }); 21 | 22 | it('should have properties', () => { 23 | expect(api).to.have.property('get'); 24 | expect(api).to.have.property('list'); 25 | expect(api).to.have.property('findGoodPeer'); 26 | }); 27 | 28 | it('should return success from get', () => { 29 | return api.get('213.32.9.97', 4002).forEach((response) => { 30 | expect(response).to.have.property('success', true); 31 | }); 32 | }); 33 | 34 | it('should return success from list', () => { 35 | return api.list().forEach((response) => { 36 | expect(response).to.have.property('success', true); 37 | }); 38 | }); 39 | 40 | it('should return instance of Peer from findGoodPeer', (done) => { 41 | return api.findGoodPeer().subscribe((peer) => { 42 | expect(peer).to.be.instanceOf(Peer); 43 | done(); 44 | }); 45 | }); 46 | 47 | it('should return instance of Peer from static findGoodPeer', (done) => { 48 | return PeerApi.findGoodPeer(network).subscribe((peer) => { 49 | expect(peer).to.be.instanceOf(Peer); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('should return sucess from version', () => { 55 | return api.version().forEach((response) => { 56 | expect(response).to.have.property('success', true); 57 | }); 58 | }); 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /src/api/test/TransactionApi.spec.ts: -------------------------------------------------------------------------------- 1 | import TransactionApi from '../TransactionApi'; 2 | import PeerApi from '../PeerApi'; 3 | 4 | import Http from '../../services/Http'; 5 | 6 | import { Network, NetworkType } from '../../model/Network'; 7 | import { Transaction, TransactionType, VoteType, TransactionVote, TransactionDelegate } from '../../model/Transaction'; 8 | 9 | import { expect } from 'chai'; 10 | import { PrivateKey } from '../../index'; 11 | 12 | import 'rxjs/add/operator/take' 13 | import 'rxjs/add/operator/toPromise' 14 | 15 | /* tslint:disable:no-unused-expression */ 16 | const network = Network.getDefault(NetworkType.Devnet); 17 | const http = new Http(network); 18 | 19 | let api: TransactionApi; 20 | 21 | describe('TransactionApi', () => { 22 | const address = 'DPTj92butfhy527V13bSXMj9SVYZGAVZ1R'; 23 | 24 | before(async () => { 25 | const peerApi = new PeerApi(http); 26 | const goodPeer = await peerApi.findGoodPeer().take(1).toPromise(); 27 | 28 | network.activePeer = goodPeer; 29 | api = new TransactionApi(new Http(network)); 30 | }); 31 | 32 | it('should be instance of TransactionApi', () => { 33 | expect(api).to.be.instanceOf(TransactionApi); 34 | }); 35 | 36 | it('should have properties', () => { 37 | expect(api).to.have.property('createTransaction'); 38 | expect(api).to.have.property('createVote'); 39 | expect(api).to.have.property('createDelegate'); 40 | expect(api).to.have.property('createSignature'); 41 | expect(api).to.have.property('post'); 42 | expect(api).to.have.property('get'); 43 | expect(api).to.have.property('getUnconfirmed'); 44 | expect(api).to.have.property('list'); 45 | expect(api).to.have.property('listUnconfirmed'); 46 | }); 47 | 48 | describe('signature check', () => { 49 | 50 | it('should correctly sign a tx with PrivateKey', () => { 51 | const key = PrivateKey.fromWIF('SCxryiz5hhkfJ4bZ7RGVzkdLqyvU7UfNFaT1ak9Gg9PSeqCAWy3h'); 52 | 53 | return api.createTransaction({ 54 | amount: 10, 55 | passphrase: key, 56 | recipientId: 'DRKaLgq8jKYQEdN2EJ7aGEh8hMDvMzd3CW', 57 | timestamp: 1, 58 | }).forEach((transaction) => { 59 | // tslint:disable 60 | expect(transaction.signature).to.be.deep.eq('3045022100a3bc590b6b80b69070799ffb7fb08ecaff209f834c72a0f28c815d46eb3123b6022029c22350c72d4e42c4f39654629cd3b8d5ac377afdb338457a57bead65e83055'); 61 | expect(transaction.id).to.be.deep.eq('2b9debcedd717ccfe68e0786c7c3ee244ccec3181c85c709196315643350d61d'); 62 | // tslint:enable 63 | }); 64 | }); 65 | 66 | it('should correctly sign a send tx', () => { 67 | return api.createTransaction({ 68 | amount: 10, 69 | passphrase: 'mysecret', 70 | recipientId: 'DRKaLgq8jKYQEdN2EJ7aGEh8hMDvMzd3CW', 71 | timestamp: 1, 72 | }).forEach((transaction) => { 73 | // tslint:disable 74 | expect(transaction.signature).to.be.deep.eq('3045022100a3bc590b6b80b69070799ffb7fb08ecaff209f834c72a0f28c815d46eb3123b6022029c22350c72d4e42c4f39654629cd3b8d5ac377afdb338457a57bead65e83055'); 75 | expect(transaction.id).to.be.deep.eq('2b9debcedd717ccfe68e0786c7c3ee244ccec3181c85c709196315643350d61d'); 76 | // tslint:enable 77 | }); 78 | }); 79 | 80 | it('should correctly sign a tx with vendorField', () => { 81 | return api.createTransaction({ 82 | amount: 10, 83 | passphrase: 'mysecret', 84 | recipientId: 'DRKaLgq8jKYQEdN2EJ7aGEh8hMDvMzd3CW', 85 | timestamp: 1, 86 | vendorField: 'hi from vekexasia', 87 | }).forEach((transaction) => { 88 | // tslint:disable 89 | expect(transaction.signature).to.be.deep.eq('3044022035a591c9b8eb42732f1a87f6c535265fdc8015afd5105f1cf31012daeb3fffd50220705ac531bafc15d74ee263223c6346937c5fe31407c100cd732e8fd7780c8072'); 90 | expect(transaction.id).to.be.deep.eq('6bd6d69320efcf04d442b53034e07d3127905c353412d4a2c2215a115ca4795f'); 91 | // tslint:enable 92 | }); 93 | }); 94 | 95 | }); 96 | 97 | it('should create a instance of Transaction from createTransaction', () => { 98 | return api.createTransaction({ 99 | amount: 100000000, 100 | passphrase: 'my secret', 101 | recipientId: address, 102 | vendorField: 'Send transaction by ark-tsc', 103 | }).forEach((transaction) => { 104 | expect(transaction).to.be.instanceOf(Transaction); 105 | }); 106 | }); 107 | 108 | it('should create an instance of transaction with given parameters', () => { 109 | return api.createTransaction({ 110 | amount: 100000000, 111 | passphrase: 'my secret', 112 | recipientId: address, 113 | vendorField: 'Send transaction by ark-tsc', 114 | }).forEach((transaction) => { 115 | expect(transaction).to.be.instanceOf(Transaction); 116 | expect(transaction.amount).to.be.eq(100000000); 117 | expect(transaction.recipientId).to.be.eq(address); 118 | expect(transaction.vendorField).to.be.eq('Send transaction by ark-tsc'); 119 | expect(transaction.type).to.be.eq(0); 120 | 121 | }); 122 | }); 123 | 124 | it('should create a instance of Transaction from createVote', () => { 125 | const delegatePublicKey = '021e6d971e5885a3147ddf1e45bf5c8d0887ad9fc659e24bdf95c2c9607e7e3fe8'; 126 | const vote = {}; 127 | vote.delegatePublicKey = delegatePublicKey; 128 | vote.passphrase = 'my secret'; 129 | vote.type = VoteType.Add; 130 | vote.vendorField = 'Send vote transaction by ark-tsc'; 131 | return api.createVote(vote).forEach((transaction) => { 132 | expect(transaction).to.be.instanceOf(Transaction); 133 | expect(transaction.type).to.be.eq(TransactionType.Vote); 134 | expect(transaction.asset.votes[0]).to.be.eq('+' + delegatePublicKey); 135 | expect(transaction.vendorField).to.be.eq('Send vote transaction by ark-tsc'); 136 | }); 137 | }); 138 | 139 | it('should create a instance of Transaction from createDelegate', () => { 140 | const publicKey = '03dcd9356b9f4e13a70fed664753e86ddbaf3d362ea8b35b6a9f4325ceda52ca7e'; 141 | const username = 'lorenzo'; 142 | const delegate = {}; 143 | delegate.passphrase = 'my secret'; 144 | delegate.publicKey = publicKey; 145 | delegate.username = username; 146 | delegate.vendorField = 'Send delegate transaction by ark-tsc'; 147 | 148 | return api.createDelegate(delegate).forEach((transaction) => { 149 | expect(transaction).to.be.instanceOf(Transaction); 150 | expect(transaction.type).to.be.eq(TransactionType.CreateDelegate); 151 | expect(transaction.asset.delegate.publicKey).to.be.eq(publicKey); 152 | expect(transaction.asset.delegate.username).to.be.eq(username); 153 | expect(transaction.vendorField).to.be.eq('Send delegate transaction by ark-tsc'); 154 | }); 155 | }); 156 | 157 | it('should create a instance of Transaction from createSignature', () => { 158 | return api.createSignature('my secret', 'my second secret passphrase', 'Send signature transaction by ark-tsc').forEach((transaction) => { 159 | expect(transaction).to.be.instanceOf(Transaction); 160 | expect(transaction.type).to.be.eq(TransactionType.SecondSignature); 161 | expect(transaction.vendorField).to.be.eq('Send signature transaction by ark-tsc'); 162 | }); 163 | }); 164 | 165 | it('should create a instance of Transaction from createSignature with PrivateKey', () => { 166 | return api.createSignature(PrivateKey.fromSeed('my secret'), 'my second secret passphrase') 167 | .forEach((transaction) => { 168 | expect(transaction).to.be.instanceOf(Transaction); 169 | }); 170 | }); 171 | 172 | it('should return success from get', () => { 173 | return api.get('a5e8ad49c9d8d074490f20cc4de960baa42c5db91f11513ddd7ccd853e5a49d9').forEach((response) => { 174 | expect(response).to.have.property('success', true); 175 | }); 176 | }); 177 | 178 | it('should return false on success field from getUnconfirmed', () => { 179 | return api.getUnconfirmed( 180 | 'a5e8ad49c9d8d074490f20cc4de960baa42c5db91f11513ddd7ccd853e5a49d9', 181 | ).forEach((response) => { 182 | expect(response).to.have.property('success', false); 183 | }); 184 | }); 185 | 186 | it('should return success from list', () => { 187 | return api.list({orderBy: 'timestamp:desc', limit: 10}).forEach((response) => { 188 | expect(response).to.have.property('success', true); 189 | }); 190 | }); 191 | 192 | it('should return success from listUnconfirmed', () => { 193 | return api.listUnconfirmed().forEach((response) => { 194 | expect(response).to.have.property('success', true); 195 | }); 196 | }); 197 | 198 | it('should return false on success field from post send transaction', () => { 199 | //tslint:disable 200 | const transaction = { 201 | amount: 100000000, 202 | fee: 10000000, 203 | id: '5b9be5f9b1280d542e856e84758312780fe0061366592e579cbed8639511cac0', 204 | recipientId: 'DPTj92butfhy527V13bSXMj9SVYZGAVZ1R', 205 | senderPublicKey: '026c75159ccf36ffc639fdfcba7c6e798f90b2767b54b8a99f2eeec534c92a32e9', 206 | signature: '304402203d971b4e50e27e7fec8fb6d42523b82a70a82af9b9488d8f4aa16cb7936162ea022077e072b21e78cf24b7a9b8b653042dcb218b226f1b18e9a7a8462bc49e48255b', 207 | timestamp: 9870360, 208 | type: 0, 209 | vendorField: 'Send transaction by ark-tsc' 210 | }; 211 | 212 | return api.post(transaction).forEach((response) => { 213 | if (network.isV2) { 214 | expect(response.data).to.have.property('invalid'); 215 | expect(response.data.invalid[0]).to.be.eq(transaction.id); 216 | } else { 217 | expect(response).to.have.property('success', false); 218 | } 219 | }); 220 | }); 221 | 222 | }); 223 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable:object-literal-sort-keys 2 | 3 | export default { 4 | networks: { 5 | mainnet: { 6 | bip32: { 7 | private: 0x2bf4530, 8 | public: 0x2bf4968, 9 | }, 10 | name: 'mainnet', 11 | nethash: '6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988', 12 | token: 'ARK', 13 | symbol: 'Ѧ', 14 | version: 0x17, 15 | explorer: 'https://explorer.ark.io', 16 | wif: 0xaa, 17 | p2pPort: 4001, 18 | apiPort: 4003, 19 | p2pVersion: '2.0.0', 20 | isV2: true, 21 | activePeer: { 22 | ip: '5.196.105.32', 23 | port: 4003, 24 | }, 25 | peers: [ 26 | '5.196.105.32:4003', 27 | '5.196.105.33:4003', 28 | '5.196.105.34:4003', 29 | '5.196.105.35:4003', 30 | '5.196.105.36:4003', 31 | '5.196.105.37:4003', 32 | '5.196.105.38:4003', 33 | '5.196.105.39:4003', 34 | '178.32.65.136:4003', 35 | '178.32.65.137:4003', 36 | '178.32.65.138:4003', 37 | '178.32.65.139:4003', 38 | '178.32.65.140:4003', 39 | '178.32.65.141:4003', 40 | '178.32.65.142:4003', 41 | '178.32.65.143:4003', 42 | '5.196.105.40:4003', 43 | '5.196.105.41:4003', 44 | '5.196.105.42:4003', 45 | '5.196.105.43:4003', 46 | '5.196.105.44:4003', 47 | '5.196.105.45:4003', 48 | '5.196.105.46:4003', 49 | '5.196.105.47:4003', 50 | '54.38.120.32:4003', 51 | '54.38.120.33:4003', 52 | '54.38.120.34:4003', 53 | '54.38.120.35:4003', 54 | '54.38.120.36:4003', 55 | '54.38.120.37:4003', 56 | '54.38.120.38:4003', 57 | '54.38.120.39:4003', 58 | '151.80.125.32:4003', 59 | '151.80.125.33:4003', 60 | '151.80.125.34:4003', 61 | '151.80.125.35:4003', 62 | '151.80.125.36:4003', 63 | '151.80.125.37:4003', 64 | '151.80.125.38:4003', 65 | '151.80.125.39:4003', 66 | '213.32.41.104:4003', 67 | '213.32.41.105:4003', 68 | '213.32.41.106:4003', 69 | '213.32.41.107:4003', 70 | '213.32.41.108:4003', 71 | '213.32.41.109:4003', 72 | '213.32.41.110:4003', 73 | '213.32.41.111:4003', 74 | '5.135.22.92:4003', 75 | '5.135.22.93:4003', 76 | '5.135.22.94:4003', 77 | '5.135.22.95:4003', 78 | '5.135.52.96:4003', 79 | '5.135.52.97:4003', 80 | '5.135.52.98:4003', 81 | '5.135.52.99:4003', 82 | '51.255.105.52:4003', 83 | '51.255.105.53:4003', 84 | '51.255.105.54:4003', 85 | '51.255.105.55:4003', 86 | '46.105.160.104:4003', 87 | '46.105.160.105:4003', 88 | '46.105.160.106:4003', 89 | '46.105.160.107:4003' 90 | ], 91 | }, 92 | devnet: { 93 | bip32: { 94 | public: 0x043587cf, 95 | private: 0x04358394, 96 | }, 97 | name: 'devnet', 98 | nethash: '2a44f340d76ffc3df204c5f38cd355b7496c9065a1ade2ef92071436bd72e867', 99 | token: 'DARK', 100 | symbol: 'DѦ', 101 | version: 30, 102 | explorer: 'https://dexplorer.ark.io', 103 | wif: 0xaa, 104 | p2pPort: 4002, 105 | apiPort: 4003, 106 | p2pVersion: '2.0.0', 107 | isV2: true, 108 | activePeer: { 109 | ip: '167.114.29.51', 110 | port: 4003, 111 | }, 112 | peers: [ 113 | '167.114.29.51:4003', 114 | '167.114.29.52:4003', 115 | '167.114.29.53:4003', 116 | '167.114.29.54:4003', 117 | '167.114.29.55:4003', 118 | ], 119 | }, 120 | }, 121 | blockchain: { 122 | interval: 8, 123 | delegates: 51, 124 | date: new Date(Date.UTC(2017, 2, 21, 13, 0, 0, 0)), 125 | }, 126 | }; 127 | -------------------------------------------------------------------------------- /src/core/HDNode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module core 3 | */ 4 | /** HD Wallet. */ 5 | 6 | import * as bigi from 'bigi'; 7 | import * as bytebuffer from 'bytebuffer'; 8 | 9 | import config from '../config'; 10 | import * as model from '../model'; 11 | 12 | import Crypto from '../utils/Crypto'; 13 | import { PrivateKey, PublicKey } from './Key'; 14 | 15 | const MASTER_SECRET: string = 'Bitcoin seed'; 16 | const HIGHEST_BIT: number = 0x80000000; 17 | 18 | export class HDNode { 19 | public chainCode: Buffer; // 32 bytes 20 | public childNumber: Buffer; // 4 bytes 21 | public depth: number; 22 | public fingerPrint: Buffer; // 4 bytes 23 | public isPrivate: boolean; 24 | public key: PrivateKey; // 33 bytes 25 | public version: Buffer; // 4 bytes, 26 | public network: string; 27 | 28 | constructor() { 29 | // pass 30 | } 31 | 32 | public static createMasterKey(seed: string | Buffer, networkType: model.NetworkType): HDNode { 33 | const networkName = model.NetworkType[networkType].toLowerCase(); 34 | const networkConfig = config.networks[networkName].bip32; 35 | 36 | if (typeof seed === 'string') { 37 | seed = new Buffer(seed, 'hex'); 38 | } 39 | 40 | const hmac = Crypto.hmacSha512(MASTER_SECRET, seed); 41 | const keyBytes = hmac.slice(0, 32); 42 | const chainCode = hmac.slice(32); 43 | 44 | const wallet = new HDNode(); 45 | wallet.chainCode = chainCode; 46 | wallet.childNumber = new Buffer(4); 47 | wallet.depth = 0; 48 | wallet.fingerPrint = new Buffer(4); 49 | wallet.isPrivate = true; 50 | wallet.key = new PrivateKey(keyBytes); 51 | wallet.version = Crypto.int32toBuffer(networkConfig['private']); 52 | wallet.network = networkName; 53 | 54 | return wallet; 55 | } 56 | 57 | public static unserialize(hash: string, networkType: model.NetworkType): HDNode { 58 | const networkName = model.NetworkType[networkType].toLowerCase(); 59 | const networkConfig = config.networks[networkName].bip32; 60 | 61 | const buffer = Crypto.bs58decode(hash); 62 | if (buffer.length !== 78) { 63 | throw new Error('Invalid buffer length'); 64 | } 65 | 66 | const version = buffer.readUInt32BE(0); 67 | 68 | if (version !== networkConfig.public && version !== networkConfig.private) { 69 | throw new Error('Invalid network version'); 70 | } 71 | 72 | const depth = buffer[4]; 73 | const parentFingerprint = buffer.slice(5, 9); 74 | 75 | if (depth === 0 && parentFingerprint.toString('hex') !== '00000000') { 76 | throw new Error('Invalid parent fingerprint'); 77 | } 78 | 79 | const index = buffer.readUInt32BE(9); 80 | if (depth === 0 && index !== 0) { 81 | throw new Error('Invalid index'); 82 | } 83 | 84 | const chainCode = buffer.slice(13, 45); 85 | let key; 86 | 87 | if (version === networkConfig.private) { 88 | if (buffer.readUInt8(45) !== 0x00) { 89 | throw new Error('Invalid private key'); 90 | } 91 | 92 | key = new PrivateKey(buffer.slice(46, 78)); 93 | } else { 94 | const curve = Crypto.decodeCurvePoint(buffer.slice(45, 78)); 95 | 96 | Crypto.validateCurve(curve); 97 | 98 | key = new PrivateKey(null, curve); 99 | } 100 | 101 | const wallet = new HDNode(); 102 | wallet.depth = depth; 103 | wallet.childNumber = Crypto.int32toBuffer(index); 104 | wallet.fingerPrint = parentFingerprint; 105 | wallet.key = key; 106 | wallet.chainCode = chainCode; 107 | wallet.network = networkName; 108 | 109 | const networkCapitalize = networkName.charAt(0).toUpperCase() + networkName.slice(1); 110 | wallet.key.getPublicKey().network = model.Network.getDefault(model.NetworkType[networkCapitalize]); 111 | 112 | return wallet; 113 | } 114 | 115 | public static generateSeed() { 116 | return Crypto.randomSeed(32); 117 | } 118 | 119 | public childKey(index: number) { 120 | const networkConfig = config.networks[this.network].bip32; 121 | 122 | const hardenedChild = index >= HIGHEST_BIT; 123 | const childIndexBytes = Crypto.int32toBuffer(index); 124 | 125 | if (!this.isPrivate && hardenedChild) { 126 | throw new Error('Could not derive hardened child key'); 127 | } 128 | 129 | let data: Buffer; 130 | 131 | if (this.isPrivate) { 132 | if (hardenedChild) { 133 | data = Buffer.concat([new Buffer(1), this.key.hash]); 134 | } else { 135 | data = this.getPublicHash(); 136 | } 137 | } else { 138 | data = this.key.getPublicKey().hash; 139 | } 140 | 141 | data = Buffer.concat([data, childIndexBytes]); 142 | 143 | const hmac = Crypto.hmacSha512(this.chainCode, data); 144 | const iL = hmac.slice(0, 32); 145 | const iR = hmac.slice(32); 146 | 147 | Crypto.validateKey(iL); 148 | 149 | const childKey = new HDNode(); 150 | childKey.childNumber = childIndexBytes; 151 | childKey.chainCode = iR; 152 | childKey.depth = this.depth + 1; 153 | childKey.isPrivate = this.isPrivate; 154 | childKey.network = this.network; 155 | 156 | let newKey: PrivateKey; 157 | 158 | if (this.isPrivate) { 159 | childKey.version = Crypto.int32toBuffer(networkConfig['private']); 160 | childKey.fingerPrint = this.getFingerPrint(); 161 | const priv = Crypto.addPrivateKeys(iL, this.key.hash); 162 | newKey = new PrivateKey(priv); 163 | } else { 164 | childKey.version = Crypto.int32toBuffer(networkConfig['public']); 165 | childKey.fingerPrint = Crypto.hash160(this.getPublicHash()); 166 | const pub = Crypto.addPublicKeys(iL, this.getPublicHash()); 167 | newKey = new PrivateKey(null, pub); 168 | } 169 | 170 | const networkCapitalize = this.network.charAt(0).toUpperCase() + this.network.slice(1); 171 | newKey.getPublicKey().setNetwork(model.Network.getDefault(model.NetworkType[networkCapitalize])); 172 | 173 | childKey.key = newKey; 174 | 175 | return childKey; 176 | } 177 | 178 | public serialize(): string { 179 | let keyBytes; 180 | 181 | if (this.isPrivate) { 182 | keyBytes = Buffer.concat([new Buffer(1), this.key.hash]); 183 | } else { 184 | keyBytes = this.getPublicHash(); 185 | } 186 | 187 | const buffer = new bytebuffer(78, true); 188 | buffer.append(this.version); 189 | buffer.writeInt8(this.depth); 190 | buffer.append(this.fingerPrint); 191 | buffer.append(this.childNumber); 192 | buffer.append(this.chainCode); 193 | buffer.append(keyBytes); 194 | 195 | buffer.flip(); 196 | 197 | const serializedKey = Crypto.bs58encode(buffer.toBuffer()); 198 | 199 | return serializedKey; 200 | } 201 | 202 | public toPublic(): HDNode { 203 | const wallet = new HDNode(); 204 | wallet.isPrivate = false; 205 | 206 | wallet.chainCode = this.chainCode; 207 | wallet.childNumber = this.childNumber; 208 | wallet.depth = this.depth; 209 | wallet.fingerPrint = this.fingerPrint; 210 | wallet.network = this.network; 211 | wallet.version = this.version; 212 | wallet.key = this.key; 213 | 214 | return wallet; 215 | } 216 | 217 | private getPublicHash(): Buffer { 218 | return this.key.getPublicKey().hash; 219 | } 220 | 221 | private getFingerPrint() { 222 | return Crypto.hash160(this.getPublicHash()).slice(0, 4); 223 | } 224 | 225 | } 226 | -------------------------------------------------------------------------------- /src/core/Key.ts: -------------------------------------------------------------------------------- 1 | import * as secp256k1 from 'secp256k1'; 2 | import * as wif from 'wif'; 3 | 4 | import * as model from '../model'; 5 | 6 | import Crypto from '../utils/Crypto'; 7 | 8 | /** Public and private keys. */ 9 | export class PublicKey { 10 | 11 | constructor(public hash: Buffer, public isCompressed: boolean = true, public network?: model.Network) {} 12 | 13 | public static fromAddress(address: string): PublicKey { 14 | const hash = Crypto.bs58decode(address); 15 | return new PublicKey(hash); 16 | } 17 | 18 | public static fromHex(hex: string): PublicKey { 19 | const buf = new Buffer(hex, 'hex'); 20 | return new PublicKey(buf); 21 | } 22 | 23 | public static validateAddress(address: string, network: model.Network) { 24 | try { 25 | const decode = this.fromAddress(address); 26 | return decode.hash[0] === network.version; 27 | } catch (e) { 28 | return false; 29 | } 30 | } 31 | 32 | public getAddress(): string { 33 | if (!this.network) { 34 | throw new Error('Network not defined'); 35 | } 36 | 37 | const payload = new Buffer(21); 38 | const buf = Crypto.ripemd160(this.hash); 39 | payload.writeUInt8(this.network.version, 0); 40 | buf.copy(payload, 1); 41 | 42 | return Crypto.bs58encode(payload); 43 | } 44 | 45 | /** 46 | * Set a network to publicKey 47 | * Useful to get address from specific version 48 | */ 49 | public setNetwork(network: model.Network): void { 50 | this.network = network; 51 | } 52 | 53 | public toHex() { 54 | return this.hash.toString('hex'); 55 | } 56 | 57 | public verifySignature(signature: Buffer, data: Buffer) { 58 | const sig = secp256k1.signatureImport(signature); 59 | return secp256k1.verify(data, sig, this.hash); 60 | } 61 | 62 | } 63 | 64 | export class PrivateKey { 65 | 66 | private publicKey: PublicKey; 67 | 68 | constructor(public hash?: Buffer, publicKey?: PublicKey | Buffer) { 69 | if (publicKey instanceof Buffer) { 70 | this.publicKey = new PublicKey(publicKey); 71 | } 72 | 73 | if (!publicKey && hash) { 74 | this.publicKey = this.getPublicKey(); 75 | } 76 | } 77 | 78 | public static fromWIF( 79 | wifString: string, 80 | network: model.Network = model.Network.getDefault(model.NetworkType.Mainnet), 81 | ): PrivateKey { 82 | if (!network) { 83 | network = model.Network.getDefault(); 84 | } 85 | 86 | const decoded = wif.decode(wifString); 87 | const version = decoded.version; 88 | 89 | return new PrivateKey(decoded.privateKey); 90 | } 91 | 92 | public static fromSeed( 93 | passphrase: string | Buffer, 94 | network: model.Network = model.Network.getDefault(model.NetworkType.Mainnet), 95 | ): PrivateKey { 96 | let password; 97 | 98 | if (typeof passphrase === 'string') { 99 | password = new Buffer(passphrase, 'utf-8'); 100 | } else { 101 | password = passphrase; 102 | } 103 | 104 | const hash = Crypto.sha256(password); 105 | const newKey = new PrivateKey(hash); 106 | 107 | newKey.getPublicKey().setNetwork(network); 108 | return newKey; 109 | } 110 | 111 | public getPublicKey(): PublicKey { 112 | if (this.publicKey) { 113 | return this.publicKey; 114 | } 115 | 116 | const compressed = secp256k1.publicKeyCreate(this.hash); 117 | const pub = secp256k1.publicKeyConvert(compressed, true); 118 | 119 | this.publicKey = new PublicKey(pub); 120 | return this.publicKey; 121 | } 122 | 123 | public sign(data: Buffer) { 124 | const sig = secp256k1.sign(data, this.hash).signature; 125 | const exp = secp256k1.signatureExport(sig); 126 | return exp; 127 | } 128 | 129 | public toHex() { 130 | return this.hash.toString('hex'); 131 | } 132 | 133 | public toWIF() { 134 | return wif.encode(this.publicKey.network.wif, this.hash, this.publicKey.isCompressed); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/core/Tx.ts: -------------------------------------------------------------------------------- 1 | import * as bytebuffer from 'bytebuffer'; 2 | 3 | import * as model from '../model'; 4 | 5 | import { PrivateKey, PublicKey } from './Key'; 6 | 7 | import Crypto from '../utils/Crypto'; 8 | import Slot from '../utils/Slot'; 9 | 10 | function padBytes(value: string, buf: Buffer) { 11 | const valBuffer = new Buffer(value.length > buf.length ? value.substr(0, buf.length) : value); 12 | 13 | valBuffer.copy(buf, 0); 14 | for (let i=0; i < buf.length - valBuffer.length; i++) { 15 | buf.writeInt8(0, i + valBuffer.length); 16 | } 17 | 18 | return buf; 19 | } 20 | 21 | /** Communicate between transaction and keypair. */ 22 | export default class Tx { 23 | public transaction: model.Transaction; 24 | 25 | private privKey: PrivateKey; 26 | private secondPrivKey: PrivateKey; 27 | 28 | constructor( 29 | transaction: model.Transaction, 30 | network: model.Network, 31 | key: string | PrivateKey, 32 | secondKey?: string | PrivateKey, 33 | ) { 34 | this.transaction = transaction; 35 | 36 | if (typeof key === 'string') { 37 | key = PrivateKey.fromSeed(key, network); 38 | } 39 | 40 | if (secondKey) { 41 | if (typeof secondKey === 'string') { 42 | secondKey = PrivateKey.fromSeed(secondKey, network); 43 | } 44 | 45 | this.secondPrivKey = secondKey; 46 | } 47 | 48 | this.privKey = key; 49 | this.privKey.getPublicKey().setNetwork(network); 50 | } 51 | 52 | /** 53 | * Generate transaction 54 | * Call all steps to generate a id. 55 | */ 56 | public static fromBytes(hash: string) { 57 | const buf = new bytebuffer.fromHex(hash, true, false); 58 | const type = buf.readByte(); 59 | const timestamp = buf.readInt(); 60 | const senderPublicKey = buf.readBytes(33).toBuffer(); 61 | 62 | let recipientBegin = buf.readBytes(21); 63 | 64 | if (type === 0 || type === 3) { 65 | recipientBegin = buf.readBytes(13).prepend(recipientBegin); 66 | } 67 | 68 | recipientBegin = recipientBegin.toBuffer(); 69 | 70 | const vendorField = buf.readBytes(64).toBuffer(); 71 | const amount = buf.readLong().low; 72 | const fee = buf.readLong().low; 73 | 74 | let asset; 75 | 76 | switch (type) { 77 | case model.TransactionType.CreateDelegate: 78 | asset = buf.readBytes(20); 79 | case model.TransactionType.Vote: 80 | asset = buf.readBytes(67); 81 | case model.TransactionType.SecondSignature: 82 | asset = buf.readBytes(33); 83 | } 84 | 85 | // TODO 86 | // signature 87 | 88 | } 89 | 90 | /** 91 | * Generate transaction 92 | * Call all steps to generate a id. 93 | */ 94 | public generate(): model.Transaction { 95 | const tx = this.transaction; 96 | tx.timestamp = tx.timestamp || Slot.getTime(); 97 | tx.senderPublicKey = this.privKey.getPublicKey().toHex(); 98 | 99 | if (!tx.amount) { 100 | tx.amount = 0; 101 | } 102 | 103 | tx.signature = this.sign().toString('hex'); 104 | 105 | if (this.secondPrivKey && (tx.asset && !tx.asset.hasOwnProperty('signature'))) { // if is not to create second signature 106 | tx.secondSenderPublicKey = this.secondPrivKey.getPublicKey().toHex(); 107 | tx.signSignature = this.secondSign().toString('hex'); 108 | } 109 | 110 | tx.id = this.getId().toString('hex'); 111 | 112 | this.transaction = tx; 113 | 114 | return tx; 115 | } 116 | 117 | /** 118 | * Set address by current publicKey. 119 | * To reference transaction without a recipient. 120 | */ 121 | public setAddress(): void { 122 | this.transaction.recipientId = this.privKey.getPublicKey().getAddress(); 123 | } 124 | 125 | /** 126 | * Sign transaction. 127 | */ 128 | public sign(): Buffer { 129 | return this.privKey.sign(this.getHash(true, true)); 130 | } 131 | 132 | /** 133 | * Sign transaction with second passphrase. 134 | */ 135 | public secondSign(): Buffer { 136 | return this.secondPrivKey.sign(this.getHash(false, false)); 137 | } 138 | 139 | /** 140 | * Set asset to create second passphrase in current Tranasction. 141 | */ 142 | public setAssetSignature(): void { 143 | this.transaction.asset = { 144 | signature: { 145 | publicKey: this.secondPrivKey.getPublicKey().toHex(), 146 | }, 147 | }; 148 | } 149 | 150 | /** 151 | * Returns bytearray of the Transaction object to be signed and send to blockchain 152 | */ 153 | public toBytes(skipSignature: boolean = false, skipSecondSignature: boolean = false): Buffer { 154 | const tx = this.transaction; 155 | const buf = new bytebuffer(1 + 4 + 32 + 8 + 8 + 21 + 64 + 64 + 64, true); 156 | 157 | buf.writeByte(tx.type); 158 | buf.writeInt(tx.timestamp); 159 | 160 | buf.append(tx.senderPublicKey, 'hex'); 161 | 162 | if (tx.requesterPublicKey) { 163 | buf.append(tx.requesterPublicKey, 'hex'); 164 | } 165 | 166 | if (typeof tx.recipientId !== 'undefined') { 167 | buf.append(PublicKey.fromAddress(tx.recipientId).hash); 168 | } else { 169 | buf.append(new Buffer(21)); 170 | } 171 | 172 | let padVendor = new Buffer(64); 173 | padVendor = padBytes(tx.vendorField || '', padVendor); 174 | 175 | buf.append(padVendor); 176 | 177 | buf.writeLong(tx.amount); 178 | buf.writeLong(tx.fee); 179 | 180 | if (tx.asset && Object.keys(tx.asset).length > 0) { 181 | const asset = tx.asset; 182 | if (tx.type === model.TransactionType.CreateDelegate) { 183 | buf.append(padBytes(asset['delegate']['username'], new Buffer(20)), 'utf-8'); 184 | } else if (tx.type === model.TransactionType.SecondSignature) { 185 | buf.append(new Buffer(asset['signature']['publicKey'], 'utf-8')); 186 | } else if (tx.type === model.TransactionType.Vote) { 187 | buf.append(new Buffer(asset['votes'].join(''), 'utf-8')); 188 | } 189 | } 190 | 191 | if (!skipSignature && tx.signature) { 192 | buf.append(tx.signature, 'hex'); 193 | } 194 | 195 | if (!skipSecondSignature && tx.signSignature) { 196 | buf.append(tx.signSignature, 'hex'); 197 | } 198 | 199 | buf.flip(); 200 | 201 | const txBytes = buf.toBuffer(); 202 | 203 | return txBytes; 204 | } 205 | 206 | public getHash(skipSignature: boolean = false, skipSecondSignature: boolean = false) { 207 | return Crypto.sha256(this.toBytes(skipSignature, skipSecondSignature)); 208 | } 209 | 210 | /** 211 | * Verify an ECDSA signature from transaction 212 | */ 213 | public verify(): boolean { 214 | const txBytes = this.getHash(true, true); 215 | const signBytes = new Buffer(this.transaction.signature, 'hex'); 216 | 217 | return this.privKey.getPublicKey().verifySignature(signBytes, txBytes); 218 | } 219 | 220 | /** 221 | * Verify an ECDSA second signature from transaction. 222 | */ 223 | public secondVerify(): boolean { 224 | const txBytes = Crypto.hash256(this.getHash(false, false)); 225 | const signBytes = new Buffer(this.transaction.signSignature, 'hex'); 226 | const pub = PublicKey.fromHex(this.transaction.secondSenderPublicKey); 227 | 228 | return pub.verifySignature(signBytes, txBytes); 229 | } 230 | 231 | /** 232 | * Returns calculated ID of transaction - hashed 256. 233 | */ 234 | public getId() { 235 | return this.getHash(); 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/core/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HDNode'; 2 | export * from './Key'; 3 | export {default as Tx} from './Tx'; 4 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | export * from './core'; 3 | export * from './model'; 4 | export * from './services'; 5 | export * from './utils'; 6 | 7 | export {default as Client} from './Ark'; 8 | export {default as default} from './Ark'; 9 | -------------------------------------------------------------------------------- /src/model/Account.ts: -------------------------------------------------------------------------------- 1 | import { JsonProperty } from 'json-typescript-mapper'; 2 | import { Delegate } from './Delegate'; 3 | 4 | /** Account model. */ 5 | export class Account { 6 | @JsonProperty('address') 7 | public address: string; 8 | 9 | @JsonProperty('unconfirmedBalance') 10 | public unconfirmedBalance: string; 11 | 12 | @JsonProperty('balance') 13 | public balance: string; 14 | 15 | @JsonProperty('publicKey') 16 | public publicKey: string; 17 | 18 | @JsonProperty('unconfirmedSignature') 19 | public unconfirmedSignature: number; 20 | 21 | @JsonProperty('secondSignature') 22 | public secondSignature: number; 23 | 24 | @JsonProperty('secondPublicKey') 25 | public secondPublicKey: string; 26 | 27 | @JsonProperty({clazz: Object, name: 'multiSignatures'}) 28 | public multiSignatures: object[]; 29 | 30 | @JsonProperty({clazz: Object, name: 'u_multisignatures'}) 31 | public uMultiSignatures: object[]; 32 | 33 | constructor() { 34 | this.address = void 0; 35 | this.unconfirmedBalance = void 0; 36 | this.balance = void 0; 37 | this.publicKey = void 0; 38 | this.unconfirmedSignature = void 0; 39 | this.secondSignature = void 0; 40 | this.secondPublicKey = void 0; 41 | this.multiSignatures = void 0; 42 | this.uMultiSignatures = void 0; 43 | } 44 | } 45 | 46 | export class AccountResponse { 47 | @JsonProperty('success') 48 | public success: boolean; 49 | 50 | @JsonProperty({clazz: Account, name: 'account'}) 51 | public account?: Account; 52 | 53 | @JsonProperty('publicKey') 54 | public publicKey?: string; 55 | 56 | constructor() { 57 | this.success = void 0; 58 | this.account = void 0; 59 | this.publicKey = void 0; 60 | } 61 | } 62 | 63 | export class AccountBalanceResponse { 64 | @JsonProperty('success') 65 | public success: boolean; 66 | 67 | @JsonProperty('balance') 68 | public balance: string; 69 | 70 | @JsonProperty('unconfirmedBalance') 71 | public unconfirmedBalance: string; 72 | 73 | constructor() { 74 | this.success = void 0; 75 | this.balance = void 0; 76 | this.unconfirmedBalance = void 0; 77 | } 78 | } 79 | 80 | export class AccountVotesResponse { 81 | @JsonProperty('success') 82 | public success: boolean; 83 | 84 | @JsonProperty({clazz: Delegate, name: 'delegates'}) 85 | public delegates: Delegate[]; 86 | 87 | constructor() { 88 | this.success = void 0; 89 | this.delegates = void 0; 90 | } 91 | } 92 | 93 | export class AccountQueryParams { 94 | public address: string; 95 | } 96 | -------------------------------------------------------------------------------- /src/model/Block.ts: -------------------------------------------------------------------------------- 1 | import { JsonProperty } from 'json-typescript-mapper'; 2 | 3 | /** Block model. */ 4 | export class Block { 5 | @JsonProperty('id') 6 | public id: number; 7 | 8 | @JsonProperty('version') 9 | public version: number; 10 | 11 | @JsonProperty('timestamp') 12 | public timestamp: number; 13 | 14 | @JsonProperty('height') 15 | public height: number; 16 | 17 | @JsonProperty('previousBlock') 18 | public previousBlock: number; 19 | 20 | @JsonProperty('numberOfTransactions') 21 | public numberOfTransactions: number; 22 | 23 | @JsonProperty('totalAmount') 24 | public totalAmount: number; 25 | 26 | @JsonProperty('totalFee') 27 | public totalFee: number; 28 | 29 | @JsonProperty('reward') 30 | public reward: number; 31 | 32 | @JsonProperty('payloadLength') 33 | public payloadLength: number; 34 | 35 | @JsonProperty('payloadHash') 36 | public payloadHash: string; 37 | 38 | @JsonProperty('generatorPublicKey') 39 | public generatorPublicKey: string; 40 | 41 | @JsonProperty('generatorId') 42 | public generatorId: string; 43 | 44 | @JsonProperty('blockSignature') 45 | public blockSignature: string; 46 | 47 | @JsonProperty('confirmations') 48 | public confirmations: number; 49 | 50 | @JsonProperty('totalForged') 51 | public totalForged: string; 52 | 53 | constructor() { 54 | this.version = void 0; 55 | this.blockSignature = void 0; 56 | this.confirmations = void 0; 57 | this.generatorId = void 0; 58 | this.generatorPublicKey = void 0; 59 | this.generatorId = void 0; 60 | this.height = void 0; 61 | this.id = void 0; 62 | this.numberOfTransactions = void 0; 63 | this.payloadHash = void 0; 64 | this.payloadLength = void 0; 65 | this.previousBlock = void 0; 66 | this.reward = void 0; 67 | this.timestamp = void 0; 68 | this.totalAmount = void 0; 69 | this.totalFee = void 0; 70 | this.totalForged = void 0; 71 | } 72 | } 73 | 74 | export class BlockQueryParams { 75 | public id?: string; 76 | public limit?: number; 77 | public offset?: number; 78 | public orderBy?: string; 79 | } 80 | 81 | export class BlockResponse { 82 | @JsonProperty('success') 83 | public success: boolean; 84 | 85 | @JsonProperty({clazz: Block, name: 'blocks'}) 86 | public blocks?: Block[]; 87 | 88 | @JsonProperty({clazz: Block, name: 'block'}) 89 | public block?: Block; 90 | 91 | constructor() { 92 | this.success = void 0; 93 | this.blocks = void 0; 94 | this.block = void 0; 95 | } 96 | } 97 | 98 | export class BlockFee { 99 | @JsonProperty('success') 100 | public success: boolean; 101 | 102 | @JsonProperty('fee') 103 | public fee: number; 104 | 105 | constructor() { 106 | this.success = void 0; 107 | this.fee = void 0; 108 | } 109 | } 110 | 111 | export class Fees { 112 | @JsonProperty('send') 113 | public send: number; 114 | 115 | @JsonProperty('vote') 116 | public vote: number; 117 | 118 | @JsonProperty('secondsignature') 119 | public secondsignature: number; 120 | 121 | @JsonProperty('delegate') 122 | public delegate: number; 123 | 124 | @JsonProperty('multisignature') 125 | public multisignature: number; 126 | 127 | constructor() { 128 | this.send = void 0; 129 | this.vote = void 0; 130 | this.secondsignature = void 0; 131 | this.delegate = void 0; 132 | this.multisignature = void 0; 133 | } 134 | } 135 | 136 | export class BlockFees { 137 | @JsonProperty('success') 138 | public success: boolean; 139 | 140 | @JsonProperty({clazz: Fees, name: 'fees'}) 141 | public fees: Fees; 142 | 143 | constructor() { 144 | this.success = void 0; 145 | this.fees = void 0; 146 | } 147 | } 148 | 149 | export class BlockHeight { 150 | @JsonProperty('success') 151 | public success: boolean; 152 | 153 | @JsonProperty('height') 154 | public height: number; 155 | 156 | @JsonProperty('id') 157 | public id: number; 158 | 159 | constructor() { 160 | this.success = void 0; 161 | this.height = void 0; 162 | this.id = void 0; 163 | } 164 | } 165 | 166 | export class BlockStatus { 167 | @JsonProperty('success') 168 | public success: boolean; 169 | 170 | @JsonProperty('epoch') 171 | public epoch: Date; 172 | 173 | @JsonProperty('height') 174 | public height: number; 175 | 176 | @JsonProperty('fee') 177 | public fee: number; 178 | 179 | @JsonProperty('milestone') 180 | public milestone: number; 181 | 182 | @JsonProperty('nethash') 183 | public nethash: string; 184 | 185 | @JsonProperty('reward') 186 | public reward: number; 187 | 188 | @JsonProperty('supply') 189 | public supply: number; 190 | 191 | constructor() { 192 | this.success = void 0; 193 | this.epoch = void 0; 194 | this.height = void 0; 195 | this.fee = void 0; 196 | this.milestone = void 0; 197 | this.nethash = void 0; 198 | this.reward = void 0; 199 | this.supply = void 0; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/model/Delegate.ts: -------------------------------------------------------------------------------- 1 | import { JsonProperty } from 'json-typescript-mapper'; 2 | 3 | /** Delegate model. */ 4 | export class Delegate { 5 | @JsonProperty('username') 6 | public username: string; 7 | 8 | @JsonProperty('address') 9 | public address: string; 10 | 11 | @JsonProperty('publicKey') 12 | public publicKey: string; 13 | 14 | @JsonProperty('vote') 15 | public vote: string; 16 | 17 | @JsonProperty('producedblocks') 18 | public producedBlocks: number; 19 | 20 | @JsonProperty('missedblocks') 21 | public missedBlocks: number; 22 | 23 | @JsonProperty('rate') 24 | public rate: number; 25 | 26 | @JsonProperty('approval') 27 | public approval: number; 28 | 29 | @JsonProperty('productivity') 30 | public productivity: number; 31 | 32 | constructor() { 33 | this.address = void 0; 34 | this.approval = void 0; 35 | this.missedBlocks = void 0; 36 | this.producedBlocks = void 0; 37 | this.productivity = void 0; 38 | this.publicKey = void 0; 39 | this.rate = void 0; 40 | this.username = void 0; 41 | this.vote = void 0; 42 | } 43 | } 44 | 45 | export class DelegateResponse { 46 | @JsonProperty('success') 47 | public success: boolean; 48 | 49 | @JsonProperty({clazz: Delegate, name: 'delegates'}) 50 | public delegates?: Delegate[]; 51 | 52 | @JsonProperty({clazz: Delegate, name: 'delegate'}) 53 | public delegate?: Delegate; 54 | 55 | @JsonProperty('totalCount') 56 | public totalCount: number; 57 | 58 | constructor() { 59 | this.success = void 0; 60 | this.delegates = void 0; 61 | this.delegate = void 0; 62 | this.totalCount = void 0; 63 | } 64 | } 65 | 66 | export class DelegateQueryParams { 67 | @JsonProperty('username') 68 | public username?: string; 69 | 70 | @JsonProperty('publicKey') 71 | public publicKey?: string; 72 | 73 | @JsonProperty('offset') 74 | public offset?: number; 75 | 76 | @JsonProperty('orderBy') 77 | public orderBy?: string; 78 | 79 | @JsonProperty('limit') 80 | public limit?: number; 81 | 82 | @JsonProperty({clazz: Delegate, name: 'delegate'}) 83 | public delegate?: Delegate; 84 | 85 | @JsonProperty('generatorPublicKey') 86 | public generatorPublicKey?: string; 87 | 88 | @JsonProperty('q') 89 | public q?: string; 90 | 91 | constructor() { 92 | this.username = void 0; 93 | this.publicKey = void 0; 94 | this.offset = void 0; 95 | this.orderBy = void 0; 96 | this.limit = void 0; 97 | this.delegate = void 0; 98 | this.generatorPublicKey = void 0; 99 | this.q = void 0; 100 | } 101 | } 102 | 103 | export class AccountVoter { 104 | public username: string; 105 | public address: string; 106 | public publicKey: string; 107 | public balance: string; 108 | 109 | constructor() { 110 | this.username = void 0; 111 | this.address = void 0; 112 | this.publicKey = void 0; 113 | this.balance = void 0; 114 | } 115 | } 116 | 117 | export class DelegateVoters { 118 | @JsonProperty('success') 119 | public success: boolean; 120 | 121 | @JsonProperty({clazz: AccountVoter, name: 'accounts'}) 122 | public accounts: AccountVoter[]; 123 | 124 | constructor() { 125 | this.success = void 0; 126 | this.accounts = void 0; 127 | } 128 | } 129 | 130 | export class ForgedDetails { 131 | @JsonProperty('success') 132 | public success: boolean; 133 | 134 | @JsonProperty('fees') 135 | public fees: string; 136 | 137 | @JsonProperty('rewards') 138 | public rewards: string; 139 | 140 | @JsonProperty('forged') 141 | public forged: string; 142 | 143 | constructor() { 144 | this.success = void 0; 145 | this.fees = void 0; 146 | this.rewards = void 0; 147 | this.forged = void 0; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/model/Loader.ts: -------------------------------------------------------------------------------- 1 | import { JsonProperty } from 'json-typescript-mapper'; 2 | 3 | /** Loader model. */ 4 | export class LoaderStatus { 5 | @JsonProperty('success') 6 | public success: boolean; 7 | 8 | @JsonProperty('loaded') 9 | public loaded: false; 10 | 11 | @JsonProperty('now') 12 | public now: number; 13 | 14 | @JsonProperty('blocksCount') 15 | public blocksCount: number; 16 | 17 | constructor() { 18 | this.success = void 0; 19 | this.loaded = void 0; 20 | this.now = void 0; 21 | this.blocksCount = void 0; 22 | } 23 | } 24 | 25 | export class LoaderStatusSync { 26 | @JsonProperty('success') 27 | public success: boolean; 28 | 29 | @JsonProperty('syncing') 30 | public syncing: false; 31 | 32 | @JsonProperty('blocks') 33 | public blocks: number; 34 | 35 | @JsonProperty('height') 36 | public height: number; 37 | 38 | @JsonProperty('id') 39 | public id: string; 40 | 41 | constructor() { 42 | this.success = void 0; 43 | this.syncing = void 0; 44 | this.blocks = void 0; 45 | this.height = void 0; 46 | this.id = void 0; 47 | } 48 | } 49 | 50 | export class LoaderNetworkResponse { 51 | @JsonProperty('nethash') 52 | public nethash: string; 53 | 54 | @JsonProperty('token') 55 | public token: string; 56 | 57 | @JsonProperty('symbol') 58 | public symbol: string; 59 | 60 | @JsonProperty('explorer') 61 | public explorer: string; 62 | 63 | @JsonProperty('version') 64 | public version: number; 65 | 66 | constructor() { 67 | this.nethash = void 0; 68 | this.token = void 0; 69 | this.symbol = void 0; 70 | this.explorer = void 0; 71 | this.version = void 0; 72 | } 73 | } 74 | 75 | export class LoaderAutoConfigure { 76 | @JsonProperty('success') 77 | public success: boolean; 78 | 79 | @JsonProperty({clazz: LoaderNetworkResponse, name: 'network'}) 80 | public network: LoaderNetworkResponse; 81 | 82 | constructor() { 83 | this.success = void 0; 84 | this.network = void 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/model/Network.ts: -------------------------------------------------------------------------------- 1 | import config from '../config'; 2 | import { Peer } from './Peer'; 3 | 4 | export enum NetworkType { 5 | Mainnet, 6 | Devnet, 7 | } 8 | 9 | /** Network model. */ 10 | export class Network { 11 | public type: NetworkType; 12 | public name: string; 13 | public nethash: string; 14 | public token: string; 15 | public symbol: string; 16 | public version: number; 17 | public explorer: string; 18 | public wif?: number; 19 | public activePeer: Peer; 20 | public bip32: string; 21 | public p2pPort: number; 22 | public apiPort: number; 23 | public p2pVersion: string; 24 | public isV2: boolean = false; 25 | 26 | constructor() { 27 | // pass 28 | } 29 | 30 | /** 31 | * Get list of all defaults networks. 32 | */ 33 | public static getAll(): Network[] { 34 | const networks = config.networks; 35 | const list = []; 36 | 37 | Object.keys(networks).forEach((item) => { 38 | const name = networks[item]; 39 | const {peers, ...defaultNetwork} = name; // to remove peers list 40 | 41 | const network = new Network(); 42 | Object.assign(network, defaultNetwork); 43 | 44 | const type = NetworkType[item.charAt(0).toUpperCase() + item.substr(1).toLowerCase()]; 45 | network.type = type; 46 | 47 | list.push(network); 48 | }); 49 | 50 | return list; 51 | } 52 | 53 | /** 54 | * Get network from default config file based on type. 55 | */ 56 | public static getDefault(type: NetworkType = NetworkType.Mainnet): Network { 57 | const item = NetworkType[type].toLowerCase(); 58 | const networks = config.networks; 59 | 60 | const name = networks[item]; 61 | const {peers, ...defaultNetwork} = name; // to remove peers list 62 | 63 | const network = new Network(); 64 | Object.assign(network, defaultNetwork); 65 | 66 | network.type = type; 67 | 68 | return network; 69 | } 70 | 71 | /** 72 | * Set peer to current network. 73 | */ 74 | public setPeer(peer: Peer) { 75 | this.activePeer = peer; 76 | } 77 | 78 | /** 79 | * Get formated peer url. 80 | */ 81 | public getPeerAPIUrl(): string { 82 | return `http://${this.activePeer.ip}:${this.apiPort || this.activePeer.port}`; 83 | } 84 | 85 | /** 86 | * Get formated peer url. 87 | */ 88 | public getPeerP2PUrl(): string { 89 | return `http://${this.activePeer.ip}:${this.p2pPort || this.activePeer.port}`; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/model/Peer.ts: -------------------------------------------------------------------------------- 1 | import { ICustomConverter, JsonProperty } from 'json-typescript-mapper'; 2 | 3 | /** Peer model. */ 4 | export class Peer { 5 | @JsonProperty('ip') 6 | public ip: string; 7 | 8 | @JsonProperty('port') 9 | public port: number; 10 | 11 | @JsonProperty('version') 12 | public version?: string; 13 | 14 | @JsonProperty('os') 15 | public os?: string; 16 | 17 | @JsonProperty('height') 18 | public height?: number; 19 | 20 | @JsonProperty('status') 21 | public status?: string; 22 | 23 | @JsonProperty('delay') 24 | public delay?: number; 25 | 26 | constructor() { 27 | this.delay = void 0; 28 | this.height = void 0; 29 | this.ip = void 0; 30 | this.os = void 0; 31 | this.port = void 0; 32 | this.status = void 0; 33 | this.version = void 0; 34 | } 35 | } 36 | 37 | export class PeerQueryParams { 38 | public status?: string; 39 | public os?: string; 40 | public shared?: string; 41 | public version?: string; 42 | public limit?: number; 43 | public orderBy?: string; 44 | public offset?: number; 45 | public ip?: string; 46 | public port?: number; 47 | } 48 | 49 | export class PeerResponse { 50 | @JsonProperty('success') 51 | public success: boolean; 52 | 53 | @JsonProperty({clazz: Peer, name: 'peers'}) 54 | public peers: Peer[]; 55 | 56 | @JsonProperty({clazz: Peer, name: 'peer'}) 57 | public peer: Peer; 58 | 59 | constructor() { 60 | this.success = void 0; 61 | this.peers = void 0; 62 | this.peer = void 0; 63 | } 64 | } 65 | 66 | export class PeerVersionResponse { 67 | @JsonProperty('success') 68 | public success: boolean; 69 | 70 | @JsonProperty('version') 71 | public version: string; 72 | 73 | @JsonProperty('build') 74 | public build: string; 75 | 76 | constructor() { 77 | this.success = void 0; 78 | this.version = void 0; 79 | this.build = void 0; 80 | } 81 | } 82 | 83 | const genericConverter: ICustomConverter = { 84 | fromJson(data: any): any { 85 | return data; 86 | }, 87 | toJson(data: any): any { 88 | return JSON.stringify(data); 89 | }, 90 | }; 91 | 92 | export class PeerVersion2ConfigDataResponse { 93 | @JsonProperty('version') 94 | public version: string; 95 | 96 | @JsonProperty({customConverter: genericConverter, name: 'network'}) 97 | public network?: object; 98 | 99 | @JsonProperty({customConverter: genericConverter, name: 'plugins'}) 100 | public plugins?: object; 101 | 102 | constructor() { 103 | this.version = void 0; 104 | this.network = void 0; 105 | this.plugins = void 0; 106 | } 107 | } 108 | 109 | export class PeerVersion2ConfigResponse { 110 | @JsonProperty({clazz: PeerVersion2ConfigDataResponse, name: 'data'}) 111 | public data: PeerVersion2ConfigDataResponse; 112 | 113 | constructor() { 114 | this.data = void 0; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/model/Transaction.ts: -------------------------------------------------------------------------------- 1 | import { ICustomConverter, JsonProperty } from 'json-typescript-mapper'; 2 | import { PrivateKey } from '../index'; 3 | 4 | export enum TransactionType { 5 | SendArk = 0, 6 | SecondSignature = 1, 7 | CreateDelegate = 2, 8 | Vote = 3, 9 | MultiSignature = 4, 10 | } 11 | 12 | const genericConverter: ICustomConverter = { 13 | fromJson(data: any): any { 14 | return data; 15 | }, 16 | toJson(data: any): any { 17 | return JSON.stringify(data); 18 | }, 19 | }; 20 | 21 | /** Transaction model. */ 22 | export class Transaction { 23 | @JsonProperty('id') 24 | public id?: string; 25 | 26 | @JsonProperty('timestamp') 27 | public timestamp?: number; 28 | 29 | @JsonProperty('recipientId') 30 | public recipientId?: string; 31 | 32 | @JsonProperty('amount') 33 | public amount?: number; 34 | 35 | @JsonProperty({customConverter: genericConverter, name: 'asset'}) 36 | public asset?: object; 37 | 38 | @JsonProperty('fee') 39 | public fee?: number; 40 | 41 | @JsonProperty('type') 42 | public type?: TransactionType; 43 | 44 | @JsonProperty('vendorField') 45 | public vendorField?: string; 46 | 47 | @JsonProperty('signature') 48 | public signature?: string; 49 | 50 | @JsonProperty('signSignature') 51 | public signSignature?: string; 52 | 53 | @JsonProperty('senderPublicKey') 54 | public senderPublicKey?: string; 55 | 56 | @JsonProperty('secondSenderPublicKey') 57 | public secondSenderPublicKey?: string; 58 | 59 | @JsonProperty('requesterPublicKey') 60 | public requesterPublicKey?: string; 61 | 62 | @JsonProperty('blockid') 63 | public blockId?: string; 64 | 65 | @JsonProperty('height') 66 | public height?: number; 67 | 68 | @JsonProperty('senderId') 69 | public senderId?: string; 70 | 71 | @JsonProperty('confirmations') 72 | public confirmations?: number; 73 | 74 | constructor() { 75 | this.amount = void 0; 76 | this.asset = void 0; 77 | this.blockId = void 0; 78 | this.confirmations = void 0; 79 | this.fee = void 0; 80 | this.height = void 0; 81 | this.id = void 0; 82 | this.recipientId = void 0; 83 | this.requesterPublicKey = void 0; 84 | this.secondSenderPublicKey = void 0; 85 | this.senderId = void 0; 86 | this.senderPublicKey = void 0; 87 | this.signature = void 0; 88 | this.signSignature = void 0; 89 | this.timestamp = void 0; 90 | this.type = void 0; 91 | this.vendorField = void 0; 92 | } 93 | } 94 | 95 | export class TransactionQueryParams { 96 | public id?: string; 97 | public blockId?: string; 98 | public senderId?: string; 99 | public recipientId?: string; 100 | public limit?: number; 101 | public offset?: number; 102 | public orderBy?: string; 103 | public type?: TransactionType; 104 | } 105 | 106 | export class TransactionSend { 107 | @JsonProperty('amount') 108 | public amount: number; 109 | 110 | @JsonProperty('fee') 111 | public fee?: number; 112 | 113 | @JsonProperty('recipientId') 114 | public recipientId: string; 115 | 116 | @JsonProperty('passphrase') 117 | public passphrase: string | PrivateKey; 118 | 119 | @JsonProperty('publicKey') 120 | public publicKey?: string; 121 | 122 | @JsonProperty('secondPassphrase') 123 | public secondPassphrase?: string | PrivateKey; 124 | 125 | @JsonProperty('vendorField') 126 | public vendorField?: string; 127 | 128 | @JsonProperty('timestamp') 129 | public timestamp?: number; 130 | 131 | constructor() { 132 | this.amount = void 0; 133 | this.fee = void 0; 134 | this.passphrase = void 0; 135 | this.publicKey = void 0; 136 | this.recipientId = void 0; 137 | this.secondPassphrase = void 0; 138 | this.vendorField = void 0; 139 | } 140 | } 141 | 142 | export class TransactionPostDataResponse { 143 | @JsonProperty('accept') 144 | public accept: string[]; 145 | 146 | @JsonProperty('excess') 147 | public excess: string[]; 148 | 149 | @JsonProperty('invalid') 150 | public invalid: string[]; 151 | 152 | @JsonProperty('broadcast') 153 | public broadcast: string[]; 154 | 155 | constructor() { 156 | this.accept = void 0; 157 | this.excess = void 0; 158 | this.invalid = void 0; 159 | this.broadcast = void 0; 160 | } 161 | } 162 | 163 | export class TransactionPostResponse { 164 | @JsonProperty('success') 165 | public success: boolean; 166 | 167 | @JsonProperty('transactionIds') 168 | public transactionIds: string[]; 169 | 170 | @JsonProperty({clazz: TransactionPostDataResponse, name: 'data'}) 171 | public data: TransactionPostDataResponse; 172 | 173 | @JsonProperty('error') 174 | public error: string; 175 | 176 | @JsonProperty({customConverter: genericConverter, name: 'errors'}) 177 | public errors: object; 178 | 179 | constructor() { 180 | this.success = void 0; 181 | this.transactionIds = void 0; 182 | this.data = void 0; 183 | this.error = void 0; 184 | this.errors = void 0; 185 | } 186 | } 187 | 188 | export class TransactionResponse { 189 | @JsonProperty('success') 190 | public success: boolean; 191 | 192 | @JsonProperty({clazz: Transaction, name: 'transactions'}) 193 | public transactions: Transaction[]; 194 | 195 | @JsonProperty({clazz: Transaction, name: 'transaction'}) 196 | public transaction: Transaction; 197 | 198 | @JsonProperty('count') 199 | public count: string; 200 | 201 | @JsonProperty('error') 202 | public error: string; 203 | 204 | constructor() { 205 | this.success = void 0; 206 | this.transactions = void 0; 207 | this.transaction = void 0; 208 | this.count = void 0; 209 | this.error = void 0; 210 | } 211 | } 212 | 213 | export class TransactionPayload { 214 | @JsonProperty({clazz: Transaction, name: 'transactions'}) 215 | public transactions: Transaction[]; 216 | } 217 | 218 | export enum VoteType { 219 | Add, 220 | Remove, 221 | } 222 | 223 | export class TransactionVote { 224 | @JsonProperty('type') 225 | public type: VoteType; 226 | 227 | @JsonProperty('fee') 228 | public fee?: number; 229 | 230 | @JsonProperty('delegatePublicKey') 231 | public delegatePublicKey: string; 232 | 233 | @JsonProperty('passphrase') 234 | public passphrase: string | PrivateKey; 235 | 236 | @JsonProperty('secondPassphrase') 237 | public secondPassphrase?: string | PrivateKey; 238 | 239 | @JsonProperty('vendorField') 240 | public vendorField?: string; 241 | 242 | constructor() { 243 | this.type = void 0; 244 | this.fee = void 0; 245 | this.delegatePublicKey = void 0; 246 | this.passphrase = void 0; 247 | this.secondPassphrase = void 0; 248 | this.vendorField = void 0; 249 | } 250 | } 251 | 252 | export class TransactionDelegate { 253 | @JsonProperty('username') 254 | public username: string; 255 | 256 | @JsonProperty('fee') 257 | public fee?: number; 258 | 259 | @JsonProperty('publicKey') 260 | public publicKey: string; 261 | 262 | @JsonProperty('passphrase') 263 | public passphrase: string | PrivateKey; 264 | 265 | @JsonProperty('secondPassphrase') 266 | public secondPassphrase?: string | PrivateKey; 267 | 268 | @JsonProperty('vendorField') 269 | public vendorField?: string; 270 | 271 | constructor() { 272 | this.username = void 0; 273 | this.fee = void 0; 274 | this.publicKey = void 0; 275 | this.passphrase = void 0; 276 | this.secondPassphrase = void 0; 277 | this.vendorField = void 0; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Account'; 2 | export * from './Block'; 3 | export * from './Delegate'; 4 | export * from './Loader'; 5 | export * from './Network'; 6 | export * from './Peer'; 7 | export * from './Transaction'; 8 | -------------------------------------------------------------------------------- /src/model/test/Network.spec.ts: -------------------------------------------------------------------------------- 1 | import { Network, NetworkType } from '../Network'; 2 | import { Peer } from '../Peer'; 3 | 4 | import { expect } from 'chai'; 5 | 6 | /* tslint:disable:no-unused-expression */ 7 | 8 | describe('Network', () => { 9 | 10 | it ('should be a object', () => { 11 | expect(NetworkType).to.be.a('object'); 12 | }); 13 | 14 | it ('should have properties', () => { 15 | expect(NetworkType).have.property('Mainnet'); 16 | expect(Network).have.property('getDefault'); 17 | }); 18 | 19 | it('should create a instance of mainnet network', () => { 20 | const network = Network.getDefault(NetworkType.Mainnet); 21 | expect(network.name).to.be.equal('mainnet'); 22 | }); 23 | 24 | it('should create a instance of devnet network', () => { 25 | const network = Network.getDefault(NetworkType.Devnet); 26 | expect(network.name).to.be.equal('devnet'); 27 | }); 28 | 29 | it('should create a manual instance of network', () => { 30 | const network = new Network; 31 | 32 | network.type = NetworkType.Mainnet; 33 | network.name = 'testnet'; 34 | network.nethash = '6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988'; 35 | 36 | expect(network).to.be.instanceOf(Network); 37 | }); 38 | 39 | it('should set a active peer', () => { 40 | const network = new Network; 41 | const peer = new Peer; 42 | peer.ip = '5.39.9.251'; 43 | peer.port = 4001; 44 | 45 | network.type = NetworkType.Mainnet; 46 | network.name = 'testnet'; 47 | network.nethash = '6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988'; 48 | 49 | network.setPeer(peer); 50 | expect(network.activePeer).to.be.instanceOf(Peer); 51 | }); 52 | 53 | it('should return string url from active peer', () => { 54 | const network = new Network; 55 | const peer = new Peer; 56 | peer.ip = '5.39.9.251'; 57 | peer.port = 4001; 58 | 59 | network.type = NetworkType.Mainnet; 60 | network.name = 'testnet'; 61 | network.nethash = '6e84d08bd299ed97c212c886c98a57e36545c8f5d645ca7eeae63a8bd62d8988'; 62 | 63 | network.setPeer(peer); 64 | expect(network.getPeerAPIUrl()).to.be.a('string').and.not.empty; 65 | }); 66 | 67 | it('should return array of two Networks', () => { 68 | expect(Network.getAll()).to.be.an('array').and.to.have.lengthOf(2); 69 | }); 70 | 71 | }); 72 | -------------------------------------------------------------------------------- /src/services/Http.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | import 'rxjs/add/operator/map'; 3 | 4 | import { RxRequest } from './RxRequest'; 5 | 6 | import { deserialize, serialize } from 'json-typescript-mapper'; 7 | 8 | import * as model from '../model'; 9 | 10 | /** Http calls from peer url. */ 11 | export default class Http { 12 | 13 | private baseRequest; 14 | private timeout = 6000; 15 | 16 | public constructor(public network?: model.Network) { 17 | const options = { 18 | timeout: this.timeout, 19 | }; 20 | 21 | if (network) { 22 | options['baseURL'] = network.getPeerAPIUrl(); 23 | } 24 | 25 | this.baseRequest = new RxRequest(options); 26 | } 27 | 28 | public getNative(url: string, params: any = {}, responseType?: new() => T): Observable { 29 | const r = new RxRequest({ timeout: this.timeout }); 30 | 31 | return r.get(url, this.formatParams(params)).map((data) => this.formatResponse(data, responseType)); 32 | } 33 | 34 | public get(url: string, params: any = {}, responseType?: new() => T): Observable { 35 | return this.baseRequest.get(`/api${url}`, this.formatParams(params)) 36 | .map((data) => this.formatResponse(data, responseType)); 37 | } 38 | 39 | public post(url: string, body: any, responseType?: new() => T): Observable { 40 | return this.baseRequest.post(url, body) 41 | .map((data) => this.formatResponse(data, responseType)); 42 | } 43 | 44 | public postNative(url: string, body: any, responseType?: new() => T, options: any = {}): Observable { 45 | const r = new RxRequest(options); 46 | 47 | return r.post(url, body).map((data) => this.formatResponse(data, responseType)); 48 | } 49 | 50 | public put(url: string, data: any) { 51 | const options = { 52 | data, 53 | }; 54 | 55 | return this.baseRequest.put(url, options); 56 | } 57 | 58 | /** 59 | * Convert JSON response to specific interface. 60 | */ 61 | private formatResponse(response: any, responseType: any) { 62 | try { 63 | let result: typeof responseType; 64 | const body = typeof response === 'string' ? JSON.parse(response) : response; 65 | 66 | result = deserialize(responseType, body); 67 | 68 | return result; 69 | } catch (e) { 70 | throw new Error(e); 71 | } 72 | } 73 | 74 | /** 75 | * Convert property from interface to JSON 76 | */ 77 | private formatParams(params: any): any { 78 | const options = JSON.parse(JSON.stringify(params) || '{}'); 79 | 80 | return { params: options }; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/services/RxRequest.ts: -------------------------------------------------------------------------------- 1 | import { Observable } from 'rxjs/Observable'; 2 | import axios, { AxiosInstance, AxiosRequestConfig, AxiosPromise } from 'axios'; 3 | 4 | /** Based on @waldojeffers/rx-request. */ 5 | export class RxRequest { 6 | 7 | private req: AxiosInstance; 8 | public get; 9 | public post; 10 | public put; 11 | 12 | constructor(options: any) { 13 | const headers = { 14 | ...options.headers, 15 | 'Content-Type': 'application/json', 16 | }; 17 | 18 | this.req = axios.create({ 19 | ...options, 20 | headers, 21 | }); 22 | 23 | this.get = this.toObservable(this.req.get); 24 | this.post = this.toObservable(this.req.post); 25 | this.put = this.toObservable(this.req.put); 26 | } 27 | 28 | private toObservable(method: any): (url: string, AxiosRequestConfig) => Observable { 29 | 30 | return (url: string, options: AxiosRequestConfig): Observable => { 31 | return Observable.create((observer) => { 32 | method(url, options) 33 | .then((res) => { 34 | if (res.status < 200 || res.status >= 300) { 35 | observer.error({ 36 | ...res.data 37 | }); 38 | } else { 39 | observer.next(res.data); 40 | observer.complete(); 41 | } 42 | }) 43 | .catch((err) => { 44 | observer.error(err); 45 | observer.complete(); 46 | }); 47 | }); 48 | }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/services/index.ts: -------------------------------------------------------------------------------- 1 | export {default as Http} from './Http'; 2 | -------------------------------------------------------------------------------- /src/utils/Crypto.ts: -------------------------------------------------------------------------------- 1 | import * as bigi from 'bigi'; 2 | import * as bs58check from 'bs58check'; 3 | import * as createHash from 'create-hash'; 4 | import * as createHmac from 'create-hmac'; 5 | import * as ecurve from 'ecurve'; 6 | import * as randomBytes from 'randombytes'; 7 | 8 | const curveParams = ecurve.getCurveByName('secp256k1'); 9 | 10 | /** Crypto related functions. */ 11 | function assert(condition: boolean, message: string = 'Assertion failed') { 12 | if (!condition) { 13 | throw new Error(message); 14 | } 15 | } 16 | 17 | export default class Crypto { 18 | 19 | public static ripemd160(buffer: Buffer): Buffer { 20 | return createHash('rmd160').update(buffer).digest(); 21 | } 22 | 23 | public static sha1(buffer: Buffer): Buffer { 24 | return createHash('sha1').update(buffer).digest(); 25 | } 26 | 27 | public static sha256(buffer: Buffer): Buffer { 28 | return createHash('sha256').update(buffer).digest(); 29 | } 30 | 31 | public static hash160(buffer: Buffer): Buffer { 32 | return this.ripemd160(this.sha256(buffer)); 33 | } 34 | 35 | public static hash256(buffer: Buffer): Buffer { 36 | return this.sha256(this.sha256(buffer)); 37 | } 38 | 39 | public static hmacSha512(key: string | Buffer, buffer: Buffer) { 40 | return createHmac('sha512', key).update(buffer).digest(); 41 | } 42 | 43 | public static randomSeed(size: number): Buffer { 44 | return randomBytes(size); 45 | } 46 | 47 | public static bs58encode(buffer: Buffer): string { 48 | return bs58check.encode(buffer); 49 | } 50 | 51 | public static bs58decode(hash: string): Buffer { 52 | return bs58check.decode(hash); 53 | } 54 | 55 | public static int32toBuffer(size: number): Buffer { 56 | const buf = new Buffer(4); 57 | buf.writeInt32BE(size, 0); 58 | return buf; 59 | } 60 | 61 | public static decodeCurvePoint(buffer: Buffer) { 62 | return ecurve.Point.decodeFrom(curveParams, buffer); 63 | } 64 | 65 | public static validateCurve(buffer: Buffer): boolean { 66 | return curveParams.validate(buffer); 67 | } 68 | 69 | public static validateKey(key: Buffer): boolean { 70 | const buf = bigi.fromBuffer(key); 71 | assert(Number(buf.signum()) > 0, 'Private key must be greather than 0'); 72 | assert(Number(buf.compareTo(curveParams.n)) <= 0, 'Private key must be less than the curve order'); 73 | assert(key.length === 32, 'Private key must be equals to 32 byte'); 74 | 75 | return true; 76 | } 77 | 78 | public static addPrivateKeys(key: Buffer, priKey: Buffer): Buffer { 79 | const keyBigi = bigi.fromBuffer(key); 80 | const privKeyBigi = bigi.fromBuffer(priKey); 81 | 82 | return keyBigi.add(privKeyBigi).mod(curveParams.n).toBuffer(32); 83 | } 84 | 85 | public static addPublicKeys(key: Buffer, pubKey: Buffer): Buffer { 86 | const keyBigi = bigi.fromBuffer(key); 87 | const pubKeyBigi = bigi.fromBuffer(pubKey); 88 | 89 | const keyPoint = curveParams.G.multiply(keyBigi); 90 | const pubKeyPoint = curveParams.G.multiply(pubKeyBigi); 91 | 92 | const expanded = keyPoint.add(pubKeyPoint); 93 | 94 | return expanded.getEncoded(true); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /src/utils/Slot.ts: -------------------------------------------------------------------------------- 1 | import config from '../config'; 2 | 3 | /** Time to interact with blockchain. */ 4 | export default class Slot { 5 | 6 | /* 7 | * Return time slot difference in secods. 8 | * This timestamp is added to the transaction. 9 | */ 10 | public static getTime(): number { 11 | const now = (new Date()).getTime(); 12 | const time = config.blockchain.date.getTime(); 13 | 14 | return Math.floor((now - time) / 1000); 15 | } 16 | 17 | /** 18 | * Calculates duration between now and provided timestamp 19 | */ 20 | public static getDurationTime(timestamp: number): number { 21 | const now = (new Date()).getTime(); 22 | 23 | return Math.floor((now - timestamp) / 1000); 24 | } 25 | 26 | /** 27 | * Get transaction time from timestamp 28 | */ 29 | public static getTransactionTime(timestamp: number): Date { 30 | const time = Math.floor(config.blockchain.date.getTime() / 1000) * 1000; 31 | 32 | return new Date(time + timestamp * 1000); 33 | } 34 | 35 | public static getSlotNumber(epochTime: number = this.getTime()): number { 36 | return Math.floor(epochTime / config.blockchain.interval); 37 | } 38 | 39 | public static getSlotTime(slot: number): number { 40 | return slot * config.blockchain.interval; 41 | } 42 | 43 | public static getNextSlot(): number { 44 | const slot = this.getSlotNumber(); 45 | 46 | return slot + 1; 47 | } 48 | 49 | public static getLastSlot(nextSlot: number): number { 50 | return nextSlot + config.blockchain.delegates; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export {default as Crypto} from './Crypto'; 2 | export {default as Slot} from './Slot'; 3 | -------------------------------------------------------------------------------- /src/utils/test/Crypto.spec.ts: -------------------------------------------------------------------------------- 1 | import Crypto from '../Crypto'; 2 | 3 | import { expect } from 'chai'; 4 | 5 | /* tslint:disable:no-unused-expression */ 6 | 7 | describe('Crypto', () => { 8 | 9 | it('should return a 4-byte Buffer from int32', () => { 10 | const expectBuffer = new Buffer([0x00, 0x00, 0x00, 0x01]); 11 | expect(Crypto.int32toBuffer(1).equals(expectBuffer)).to.be.true; 12 | }); 13 | 14 | describe('Private keys', () => { 15 | const privBytes = Crypto.randomSeed(32); 16 | const secondPrivBytes = Crypto.randomSeed(32); 17 | 18 | it('should be equal to 32-byte', () => { 19 | expect(privBytes.length).to.be.equal(32); 20 | expect(secondPrivBytes.length).to.be.equal(32); 21 | }); 22 | 23 | it('should validate key Buffer to be true', () => { 24 | expect(Crypto.validateKey(privBytes)).to.be.true; 25 | expect(Crypto.validateKey(secondPrivBytes)).to.be.true; 26 | }); 27 | 28 | describe('Merge keys', () => { 29 | const merged = Crypto.addPrivateKeys(privBytes, secondPrivBytes); 30 | 31 | it('should be Buffer', () => { 32 | expect(merged).to.be.instanceOf(Buffer); 33 | }); 34 | 35 | it ('should be equal to 32-byte', () => { 36 | expect(merged.length).to.be.equal(32); 37 | }); 38 | 39 | it ('should validate merged key Buffer to be true', () => { 40 | expect(Crypto.validateKey(merged)).to.be.true; 41 | }); 42 | }); 43 | 44 | }); 45 | 46 | describe('Public keys', () => { 47 | const pubBytes = Crypto.randomSeed(33); 48 | const secondPubBytes = Crypto.randomSeed(33); 49 | 50 | it('should be equal to 33-byte', () => { 51 | expect(pubBytes.length).to.be.equal(33); 52 | expect(secondPubBytes.length).to.be.equal(33); 53 | }); 54 | 55 | describe('Merge keys', () => { 56 | const merged = Crypto.addPublicKeys(pubBytes, secondPubBytes); 57 | 58 | it('should be Buffer', () => { 59 | expect(merged).to.be.instanceOf(Buffer); 60 | }); 61 | 62 | it ('should be equal to 33-byte', () => { 63 | expect(merged.length).to.be.equal(33); 64 | }); 65 | }); 66 | 67 | }); 68 | 69 | }); 70 | -------------------------------------------------------------------------------- /src/utils/test/Slot.spec.ts: -------------------------------------------------------------------------------- 1 | import Slot from '../Slot'; 2 | 3 | import { expect } from 'chai'; 4 | 5 | /* tslint:disable:no-unused-expression */ 6 | 7 | describe('Slot', () => { 8 | 9 | it ('getTime should be number', () => { 10 | expect(Slot.getTime()).to.satisfy(Number.isInteger); 11 | }); 12 | 13 | it ('getDurationTime should be number', () => { 14 | expect(Slot.getDurationTime(9711613)).to.satisfy(Number.isInteger); 15 | }); 16 | 17 | it ('getTransactionTime should be date', () => { 18 | expect(Slot.getTransactionTime(9711613)).to.be.instanceOf(Date); 19 | }); 20 | 21 | it ('getSlotNumber should return number, equal to 1', () => { 22 | const slot = Slot.getSlotNumber(10); 23 | expect(slot).to.be.a('number').and.equal(1); 24 | }); 25 | 26 | it ('getSlotTime should return number, equal to 156912', () => { 27 | const slot = Slot.getSlotTime(19614); 28 | expect(slot).to.be.a('number').and.equal(156912); 29 | }); 30 | 31 | it ('getNextSlot should return number, is not NaN', () => { 32 | const slot = Slot.getNextSlot(); 33 | expect(slot).to.be.a('number').and.not.NaN; 34 | }); 35 | 36 | it ('getLastSlot should return number, is not NaN', () => { 37 | const slot = Slot.getLastSlot(Slot.getNextSlot()); 38 | expect(slot).to.be.a('number').and.not.NaN; 39 | }); 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "declaration": true, 4 | "module": "commonjs", 5 | "target": "es5", 6 | "moduleResolution": "node", 7 | "lib": [ 8 | "dom", 9 | "es2015", 10 | "es2016", 11 | "esnext" 12 | ], 13 | "noImplicitAny": false, 14 | "sourceMap": true, 15 | "emitDecoratorMetadata": true, 16 | "experimentalDecorators": true, 17 | "outDir": "./lib", 18 | "typeRoots": [ 19 | "node_modules/@types/" 20 | ], 21 | "types":[ 22 | "chai", 23 | "node", 24 | "mocha", 25 | "request", 26 | "rx" 27 | ] 28 | }, 29 | "exclude": [ 30 | "lib/", 31 | "node_modules", 32 | "src/**/*.spec.ts" 33 | ], 34 | "include": [ 35 | "src/**/*.ts" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:recommended" 4 | ], 5 | "rules": { 6 | "indent": [ 7 | true, 8 | "spaces", 9 | 2 10 | ], 11 | "interface-name": [ 12 | true, 13 | "never-prefix" 14 | ], 15 | "no-angle-bracket-type-assertion": false, 16 | "new-parens": false, 17 | "no-string-literal": false, 18 | "max-classes-per-file": [false], 19 | "max-line-length": [false], 20 | "member-access": [true, "check-accessor"], 21 | "member-ordering": [ 22 | true, 23 | { 24 | "order": [ 25 | "static-field", 26 | "instance-field", 27 | "constructor", 28 | "public-static-method", 29 | "private-static-method", 30 | "public-instance-method", 31 | "private-instance-method" 32 | ] 33 | } 34 | ], 35 | "quotemark": [ 36 | true, 37 | "single" 38 | ], 39 | "ordered-imports": [ 40 | false 41 | ], 42 | "one-line": [ 43 | true, 44 | "check-open-brace", 45 | "check-catch", 46 | "check-else", 47 | "check-finally", 48 | "check-whitespace" 49 | ], 50 | "semicolon": [true, "always"], 51 | "whitespace": [ 52 | true, 53 | "check-branch", 54 | "check-operator", 55 | "check-separator", 56 | "check-typecast" 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "mode": "modules", 3 | "out": "docs", 4 | "name": "ark-ts", 5 | "theme": "default", 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "excludeExternals": true, 9 | "excludeNotExported": true, 10 | "excludePrivate": true, 11 | "exclude": "**/*+(e2e|spec|index).ts", 12 | "listInvalidSymbolLinks": true, 13 | "target": "ES6", 14 | "moduleResolution": "node", 15 | "preserveConstEnums": true, 16 | "module": "commonjs", 17 | "external-modulemap": ".*\/src\/([\\w\\-_]+)\/" 18 | } 19 | --------------------------------------------------------------------------------