mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-05 05:15:48 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
80b96d9bc9 | ||
|
|
8fa30f71eb | ||
|
|
804094b1ce | ||
|
|
9caf077b58 | ||
|
|
1a5ba06ca3 | ||
|
|
657cad9ffd | ||
|
|
a338a936db | ||
|
|
226e10bca2 | ||
|
|
3a5a989011 | ||
|
|
c9720ef061 | ||
|
|
b6927f178f | ||
|
|
6b40e4fe9d | ||
|
|
59ec56db4c | ||
|
|
a8119d678a | ||
|
|
8e38e313b2 | ||
|
|
b7b75d78ae | ||
|
|
824efb6b59 | ||
|
|
c151ca202c | ||
|
|
b9a64c92e7 | ||
|
|
bcaa06721a | ||
|
|
06227ef12b | ||
|
|
c17827e030 | ||
|
|
97ca0f0b21 | ||
|
|
e4e6419e50 | ||
|
|
fd8c883cf4 | ||
|
|
6b66a59673 | ||
|
|
46177338c2 | ||
|
|
6fcff9b106 | ||
|
|
208f5f6c5c | ||
|
|
de3e2a9867 | ||
|
|
40eea3c659 | ||
|
|
4f9b6b9186 | ||
|
|
4a848ec527 | ||
|
|
10414e169c | ||
|
|
1a6c68d028 | ||
|
|
9156734ced | ||
|
|
0dfe3ff4ac | ||
|
|
3a8c7f02cc | ||
|
|
e03b192fcc | ||
|
|
cf40bd2c30 | ||
|
|
4082e88416 | ||
|
|
27abc10d93 | ||
|
|
d6474d71f2 | ||
|
|
d57603e854 | ||
|
|
ac92584678 |
29
HISTORY.md
29
HISTORY.md
@@ -1,7 +1,36 @@
|
||||
# ripple-lib Release History
|
||||
|
||||
## 1.6.4 (2020-02-18)
|
||||
|
||||
* Fix generateXAddress() and generateAddress() with no entropy (#1211, #1209)
|
||||
* Internal
|
||||
* Improve unit tests
|
||||
* Dependencies
|
||||
* Update webpack-cli, @types/node, webpack, @typescript-eslint/eslint-plugin,
|
||||
typescript, ripple-keypairs
|
||||
|
||||
## 1.6.3 (2020-02-05)
|
||||
|
||||
* Update ripple-keypairs to 1.0.0
|
||||
* Bug fix: Assign event listener to socket close event on open before attempting post-open logic (#1186)
|
||||
* Protects against possible unhandled rejection in disconnect
|
||||
* Adds the Connection `_ws.close` event listener post `_ws.open` before executing any post `_ws.open` logic, i.e. `Connection._subscribeToLedger`
|
||||
* This prevents a reconnection error loop that occurs if `Connection._ws` is never cleaned up by the unreachable `_ws.close` event listener
|
||||
* Also ensures that a possible disconnect() promise rejection is not unhandled if any `_ws.open` logic in `Connection.connect()` throws
|
||||
* Dependencies
|
||||
* Update mocha-junit-reporter, @types/node, mocha, @typescript-eslint/eslint-plugin, ripple-address-codec
|
||||
|
||||
## 1.6.2 (2020-01-17)
|
||||
|
||||
* Bug fix: Catch possible error in reconnect() on _heartbeat(), emit reconnect error (#1179)
|
||||
* Docs: Fix whitespace
|
||||
* Dependencies
|
||||
* Update @typescript-eslint/eslint-plugin, ts-node, @types/node, @types/ws, typescript
|
||||
|
||||
## 1.6.1 (2020-01-14)
|
||||
|
||||
> **Update Note:** In this release we refactored the internal connection logic of ripple-lib to improve resiliency across dropped messages and reconnects. The external interface remains the same, with zero changes to public methods/properties. However, If you experience any problems around connections, requests, and request retries, please [file an issue]( https://github.com/ripple/ripple-lib/issues/new) with the repo (and be sure to test against v1.6.0 to confirm that the problem was introduced in this version).
|
||||
|
||||
* Documentation
|
||||
* Document message type 'path_find' (#1121) (thanks @r0bertz)
|
||||
* Improve documentation for address generation; functions that can be called offline; generateXAddress() (#1158, #1159, #1169) (thanks @hbergren)
|
||||
|
||||
@@ -6,7 +6,13 @@ A JavaScript/TypeScript API for interacting with the XRP Ledger
|
||||
|
||||
This is the recommended library for integrating a JavaScript/TypeScript app with the XRP Ledger, especially if you intend to use advanced functionality such as IOUs, payment paths, the decentralized exchange, account settings, payment channels, escrows, multi-signing, and more.
|
||||
|
||||
**What is ripple-lib used for?** Here's a [list of applications](APPLICATIONS.md) that use `ripple-lib`. Open a PR to add your app or project to the list!
|
||||
## [➡️ Reference Documentation](https://xrpl.org/rippleapi-reference.html)
|
||||
|
||||
See the full reference documentation on the XRP Ledger Dev Portal.
|
||||
|
||||
## [➡️ Applications and Projects](APPLICATIONS.md)
|
||||
|
||||
What is ripple-lib used for? The applications on the list linked above use `ripple-lib`. Open a PR to add your app or project to the list!
|
||||
|
||||
### Features
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ Using RippleAPI, you can:
|
||||
This page contains documentation for ripple-lib. To use ripple-lib with npm/yarn, begin with the [Getting Started](https://github.com/ripple/ripple-lib#getting-started) steps.
|
||||
|
||||
**What is ripple-lib used for?** Here's a [list of applications that use `ripple-lib`](https://github.com/ripple/ripple-lib/blob/develop/APPLICATIONS.md). Open a PR to add your app or project to the list!
|
||||
|
||||
## Boilerplate
|
||||
|
||||
Use the following [boilerplate code](https://en.wikipedia.org/wiki/Boilerplate_code) to wrap your custom code using RippleAPI.
|
||||
@@ -194,6 +195,7 @@ If you omit the `server` parameter, RippleAPI operates [offline](#offline-functi
|
||||
After you have installed ripple-lib, you can create scripts using the [boilerplate](#boilerplate) and run them using the Node.js executable, typically named `node`:
|
||||
|
||||
`node script.js`
|
||||
|
||||
## Offline functionality
|
||||
|
||||
RippleAPI can also function without internet connectivity. This can be useful in order to generate secrets and sign transactions from a secure, isolated machine.
|
||||
@@ -221,6 +223,7 @@ Methods that depend on the state of the XRP Ledger are unavailable in offline mo
|
||||
* [generateAddress](#generateaddress)
|
||||
* [generateXAddress](#generatexaddress)
|
||||
* [computeLedgerHash](#computeledgerhash)
|
||||
|
||||
# Basic Types
|
||||
|
||||
## Address
|
||||
@@ -292,6 +295,7 @@ Name | Type | Description
|
||||
currency | [currency](#currency) | The three-character code or hexadecimal string used to denote currencies, or "drops" for the smallest unit of XRP.
|
||||
counterparty | [address](#address) | *Optional* The XRP Ledger address of the account that owes or is owed the funds (omitted if `currency` is "XRP" or "drops")
|
||||
value | [value](#value) | *Optional* The quantity of the currency, denoted as a string to retain floating point precision
|
||||
|
||||
# Transaction Overview
|
||||
|
||||
## Transaction Types
|
||||
@@ -378,6 +382,7 @@ Name | Type | Description
|
||||
data | string | *Optional* Arbitrary string, conventionally containing the content of the memo.
|
||||
format | string | *Optional* Conventionally containing information on how the memo is encoded, for example as a [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml). Only characters allowed in URLs are permitted.
|
||||
type | string | *Optional* Conventionally, a unique relation (according to [RFC 5988](http://tools.ietf.org/html/rfc5988#section-4)) that defines the format of this memo. Only characters allowed in URLs are permitted.
|
||||
|
||||
# Transaction Specifications
|
||||
|
||||
A *transaction specification* specifies what a transaction should do. Each [Transaction Type](#transaction-types) has its own type of specification.
|
||||
@@ -788,6 +793,7 @@ signature | string | *Optional* Signed claim authorizing withdrawal of XRP from
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# rippled APIs
|
||||
|
||||
ripple-lib relies on [rippled APIs](https://ripple.com/build/rippled-apis/) for online functionality. In addition to ripple-lib's own methods, you can also access rippled APIs through ripple-lib. Use the `request()`, `hasNextPage()`, and `requestNextPage()` methods:
|
||||
@@ -860,6 +866,7 @@ api.connect().then(() => { // Omit this if you are already connected
|
||||
The subscription ends when you unsubscribe or the WebSocket connection is closed.
|
||||
|
||||
For full details, see [rippled Subscriptions](https://ripple.com/build/rippled-apis/#subscriptions).
|
||||
|
||||
## request
|
||||
|
||||
`request(command: string, options: object): Promise<object>`
|
||||
@@ -913,6 +920,7 @@ return api.request('ledger', {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## hasNextPage
|
||||
|
||||
`hasNextPage(currentResponse): boolean`
|
||||
@@ -940,6 +948,7 @@ return api.request('ledger_data', {
|
||||
}
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
## requestNextPage
|
||||
|
||||
`requestNextPage(command: string, params: object = {}, currentResponse: object): Promise<object>`
|
||||
@@ -970,7 +979,9 @@ return api.request(command, params).then(response => {
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
|
||||
# Static Methods
|
||||
|
||||
## renameCounterpartyToIssuer
|
||||
|
||||
`renameCounterpartyToIssuer(issue: {currency: string, counterparty: address}): {currency: string, issuer: address}`
|
||||
@@ -1008,6 +1019,7 @@ console.log(RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter))
|
||||
{ currency: 'USD', issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' }
|
||||
{ currency: 'BTC', issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' }
|
||||
```
|
||||
|
||||
## formatBidsAndAsks
|
||||
|
||||
`formatBidsAndAsks(orderbookInfo: {base: Issue, counter: Issue}, offers: BookOffer[]): orderbook`
|
||||
@@ -1286,7 +1298,9 @@ return Promise.all(
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# API Methods
|
||||
|
||||
## connect
|
||||
|
||||
`connect(): Promise<void>`
|
||||
@@ -1304,6 +1318,7 @@ This method returns a promise that resolves with a void value when a connection
|
||||
### Example
|
||||
|
||||
See [Boilerplate](#boilerplate) for code sample.
|
||||
|
||||
## disconnect
|
||||
|
||||
`disconnect(): Promise<void>`
|
||||
@@ -1321,6 +1336,7 @@ This method returns a promise that resolves with a void value when a connection
|
||||
### Example
|
||||
|
||||
See [Boilerplate](#boilerplate) for code sample
|
||||
|
||||
## isConnected
|
||||
|
||||
`isConnected(): boolean`
|
||||
@@ -1344,6 +1360,7 @@ return api.isConnected();
|
||||
```json
|
||||
true
|
||||
```
|
||||
|
||||
## getServerInfo
|
||||
|
||||
`getServerInfo(): Promise<object>`
|
||||
@@ -1417,6 +1434,7 @@ return api.getServerInfo().then(info => {/* ... */});
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getFee
|
||||
|
||||
`getFee(): Promise<string>`
|
||||
@@ -1444,6 +1462,7 @@ return api.getFee().then(fee => {/* ... */});
|
||||
```json
|
||||
"0.000012"
|
||||
```
|
||||
|
||||
## getLedgerVersion
|
||||
|
||||
`getLedgerVersion(): Promise<number>`
|
||||
@@ -1470,6 +1489,7 @@ return api.getLedgerVersion().then(ledgerVersion => {
|
||||
16869039
|
||||
```
|
||||
|
||||
|
||||
## getTransaction
|
||||
|
||||
`getTransaction(id: string, options: object): Promise<object>`
|
||||
@@ -1621,6 +1641,7 @@ return api.getTransaction(id).then(transaction => {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getTransactions
|
||||
|
||||
`getTransactions(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -1854,6 +1875,7 @@ return api.getTransactions(address).then(transaction => {
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getTrustlines
|
||||
|
||||
`getTrustlines(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -1998,6 +2020,7 @@ return api.getTrustlines(address).then(trustlines =>
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getBalances
|
||||
|
||||
`getBalances(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -2164,6 +2187,7 @@ return api.getBalances(address).then(balances =>
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getBalanceSheet
|
||||
|
||||
`getBalanceSheet(address: string, options: object): Promise<object>`
|
||||
@@ -2259,6 +2283,7 @@ return api.getBalanceSheet(address).then(balanceSheet =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getPaths
|
||||
|
||||
`getPaths(pathfind: object): Promise<Array<object>>`
|
||||
@@ -2379,6 +2404,7 @@ return api.getPaths(pathfind)
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getOrders
|
||||
|
||||
`getOrders(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -2759,6 +2785,7 @@ return api.getOrders(address).then(orders =>
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getOrderbook
|
||||
|
||||
`getOrderbook(address: string, orderbook: object, options: object): Promise<object>`
|
||||
@@ -3864,6 +3891,7 @@ return api.getOrderbook(address, orderbook)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getSettings
|
||||
|
||||
`getSettings(address: string, options: object): Promise<object>`
|
||||
@@ -3946,6 +3974,7 @@ return api.getSettings(address).then(settings =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getAccountInfo
|
||||
|
||||
`getAccountInfo(address: string, options: object): Promise<object>`
|
||||
@@ -3994,6 +4023,7 @@ return api.getAccountInfo(address).then(info =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getAccountObjects
|
||||
|
||||
`getAccountObjects(address: string, options: object): Promise<AccountObjectsResponse>`
|
||||
@@ -4318,6 +4348,7 @@ return api.getAccountObjects(address: address).then(objects =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getPaymentChannel
|
||||
|
||||
`getPaymentChannel(id: string): Promise<object>`
|
||||
@@ -4373,6 +4404,7 @@ return api.getPaymentChannel(channelId).then(channel =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getLedger
|
||||
|
||||
`getLedger(options: object): Promise<object>`
|
||||
@@ -4436,6 +4468,7 @@ return api.getLedger()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## parseAccountFlags
|
||||
|
||||
`parseAccountFlags(Flags: number): object`
|
||||
@@ -4471,6 +4504,7 @@ console.log(JSON.stringify(flags, null, 2))
|
||||
"defaultRipple": false
|
||||
}
|
||||
```
|
||||
|
||||
## prepareTransaction
|
||||
|
||||
`prepareTransaction(transaction: object, instructions: object): Promise<object>`
|
||||
@@ -4528,6 +4562,7 @@ async function preparedPreauth() {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## preparePayment
|
||||
|
||||
`preparePayment(address: string, payment: object, instructions: object): Promise<object>`
|
||||
@@ -4600,6 +4635,7 @@ return api.preparePayment(address, payment).then(prepared => {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareTrustline
|
||||
|
||||
`prepareTrustline(address: string, trustline: object, instructions: object): Promise<object>`
|
||||
@@ -4667,6 +4703,7 @@ return api.prepareTrustline(address, trustline).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareOrder
|
||||
|
||||
`prepareOrder(address: string, order: object, instructions: object): Promise<object>`
|
||||
@@ -4734,6 +4771,7 @@ return api.prepareOrder(address, order)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareOrderCancellation
|
||||
|
||||
`prepareOrderCancellation(address: string, orderCancellation: object, instructions: object): Promise<object>`
|
||||
@@ -4786,6 +4824,7 @@ return api.prepareOrderCancellation(address, orderCancellation)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareSettings
|
||||
|
||||
`prepareSettings(address: string, settings: object, instructions: object): Promise<object>`
|
||||
@@ -4849,6 +4888,7 @@ return api.prepareSettings(address, settings)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareEscrowCreation
|
||||
|
||||
`prepareEscrowCreation(address: string, escrowCreation: object, instructions: object): Promise<object>`
|
||||
@@ -4912,6 +4952,7 @@ return api.prepareEscrowCreation(address, escrowCreation).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareEscrowCancellation
|
||||
|
||||
`prepareEscrowCancellation(address: string, escrowCancellation: object, instructions: object): Promise<object>`
|
||||
@@ -4967,6 +5008,7 @@ return api.prepareEscrowCancellation(address, escrowCancellation).then(prepared
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareEscrowExecution
|
||||
|
||||
`prepareEscrowExecution(address: string, escrowExecution: object, instructions: object): Promise<object>`
|
||||
@@ -5024,6 +5066,7 @@ return api.prepareEscrowExecution(address, escrowExecution).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## preparePaymentChannelCreate
|
||||
|
||||
`preparePaymentChannelCreate(address: string, paymentChannelCreate: object, instructions: object): Promise<object>`
|
||||
@@ -5081,6 +5124,7 @@ return api.preparePaymentChannelCreate(address, paymentChannelCreate).then(prepa
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## preparePaymentChannelClaim
|
||||
|
||||
`preparePaymentChannelClaim(address: string, paymentChannelClaim: object, instructions: object): Promise<object>`
|
||||
@@ -5135,6 +5179,7 @@ return api.preparePaymentChannelClaim(address, paymentChannelClaim).then(prepare
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## preparePaymentChannelFund
|
||||
|
||||
`preparePaymentChannelFund(address: string, paymentChannelFund: object, instructions: object): Promise<object>`
|
||||
@@ -5190,6 +5235,7 @@ return api.preparePaymentChannelFund(address, paymentChannelFund).then(prepared
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareCheckCreate
|
||||
|
||||
`prepareCheckCreate(address: string, checkCreate: object, instructions: object): Promise<object>`
|
||||
@@ -5248,6 +5294,7 @@ return api.prepareCheckCreate(address, checkCreate).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareCheckCancel
|
||||
|
||||
`prepareCheckCancel(address: string, checkCancel: object, instructions: object): Promise<object>`
|
||||
@@ -5302,6 +5349,7 @@ return api.prepareCheckCancel(address, checkCancel).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareCheckCash
|
||||
|
||||
`prepareCheckCash(address: string, checkCash: object, instructions: object): Promise<object>`
|
||||
@@ -5360,6 +5408,7 @@ return api.prepareCheckCash(address, checkCash).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## sign
|
||||
|
||||
```
|
||||
@@ -5501,6 +5550,7 @@ If any of `{signAs: some_address}` options were missing the code will return a v
|
||||
```
|
||||
[ValidationError(txJSON is not the same for all signedTransactions)]
|
||||
```
|
||||
|
||||
## combine
|
||||
|
||||
`combine(signedTransactions: Array<string>): {signedTransaction: string, id: string}`
|
||||
@@ -5538,6 +5588,7 @@ return api.combine(signedTransactions);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## submit
|
||||
|
||||
`submit(signedTransaction: string): Promise<object>`
|
||||
@@ -5600,6 +5651,7 @@ return api.submit(signedTransaction)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## generateXAddress
|
||||
|
||||
`generateXAddress(options?: object): {address: string, secret: string}`
|
||||
@@ -5639,6 +5691,7 @@ return api.generateAddress();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## generateAddress
|
||||
|
||||
`generateAddress(options?: object): {address: string, secret: string}`
|
||||
@@ -5684,6 +5737,7 @@ return api.generateAddress();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## isValidAddress
|
||||
|
||||
`isValidAddress(address: string): boolean`
|
||||
@@ -5703,6 +5757,7 @@ This method returns `true` if the address is valid and `false` if it is not.
|
||||
```javascript
|
||||
return api.isValidAddress("address")
|
||||
```
|
||||
|
||||
## isValidSecret
|
||||
|
||||
`isValidSecret(secret: string): boolean`
|
||||
@@ -5722,6 +5777,7 @@ This method returns `true` if the secret is valid and `false` if it is not.
|
||||
```javascript
|
||||
return api.isValidSecret("secret")
|
||||
```
|
||||
|
||||
## deriveKeypair
|
||||
|
||||
`deriveKeypair(seed: string): {privateKey: string, publicKey: string}`
|
||||
@@ -5743,6 +5799,7 @@ var keypair = api.deriveKeypair(seed)
|
||||
var public_key = keypair.publicKey;
|
||||
var private_key = keypair.privateKey;
|
||||
```
|
||||
|
||||
## deriveAddress
|
||||
|
||||
`deriveAddress(publicKey: string): string`
|
||||
@@ -5762,6 +5819,7 @@ This method returns a string corresponding to the address derived from the publi
|
||||
```javascript
|
||||
var address = api.deriveAddress(public_key);
|
||||
```
|
||||
|
||||
## signPaymentChannelClaim
|
||||
|
||||
`signPaymentChannelClaim(channel: string, amount: string, privateKey: string): string`
|
||||
@@ -5800,6 +5858,7 @@ return api.signPaymentChannelClaim(channel, amount, privateKey);
|
||||
"3045022100B5C54654221F154347679B97AE7791CBEF5E6772A3F894F9C781B8F1B400F89F022021E466D29DC5AEB5DFAFC76E8A88D2E388EBD25A84143B6AC3B647F479CB89B7"
|
||||
```
|
||||
|
||||
|
||||
## verifyPaymentChannelClaim
|
||||
|
||||
`verifyPaymentChannelClaim(channel: string, amount: string, signature: string, publicKey: string): boolean`
|
||||
@@ -5838,6 +5897,7 @@ return api.verifyPaymentChannelClaim(channel, amount, signature, publicKey);
|
||||
```json
|
||||
true
|
||||
```
|
||||
|
||||
## computeLedgerHash
|
||||
|
||||
`computeLedgerHash(ledger: object): string`
|
||||
@@ -5893,6 +5953,7 @@ return api.computeLedgerHash(ledger);
|
||||
```json
|
||||
"F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349"
|
||||
```
|
||||
|
||||
## xrpToDrops
|
||||
|
||||
`xrpToDrops(xrp: string | BigNumber): string`
|
||||
@@ -5940,6 +6001,7 @@ return api.dropsToXrp('1');
|
||||
```json
|
||||
'0.000001'
|
||||
```
|
||||
|
||||
## iso8601ToRippleTime
|
||||
|
||||
`iso8601ToRippleTime(iso8601: string): number`
|
||||
@@ -5967,6 +6029,7 @@ api.iso8601ToRippleTime('2017-02-17T15:04:57Z');
|
||||
```json
|
||||
540659097
|
||||
```
|
||||
|
||||
## rippleTimeToISO8601
|
||||
|
||||
`rippleTimeToISO8601(rippleTime: number): string`
|
||||
@@ -5994,6 +6057,7 @@ api.rippleTimeToISO8601(540659097);
|
||||
```json
|
||||
'2017-02-17T15:04:57.000Z'
|
||||
```
|
||||
|
||||
## txFlags
|
||||
|
||||
`txFlags.TRANSACTION_TYPE.FLAG`
|
||||
@@ -6075,6 +6139,7 @@ The remaining transaction types do not have any flags at this time.
|
||||
* EscrowCancel
|
||||
* PaymentChannelCreate
|
||||
* PaymentChannelFund
|
||||
|
||||
## schemaValidator
|
||||
|
||||
Unlike the rest of the ripple-lib API, schemaValidator is a static object on RippleAPI. It provides utility methods that do not use a server.
|
||||
@@ -6110,6 +6175,7 @@ RippleAPI.schemaValidator.schemaValidate('sign', {
|
||||
```
|
||||
[ValidationError(instance.id does not match pattern "^[A-F0-9]{64}$")]
|
||||
```
|
||||
|
||||
# API Events
|
||||
|
||||
## ledger
|
||||
@@ -6214,3 +6280,4 @@ api.on('disconnected', (code) => {
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
<%- include('introduction.md.ejs') -%>
|
||||
<%- include('boilerplate.md.ejs') -%>
|
||||
<%- include('offline.md.ejs') -%>
|
||||
<%- include('basictypes.md.ejs') -%>
|
||||
<%- include('transactions.md.ejs') -%>
|
||||
<%- include('specifications.md.ejs') -%>
|
||||
<%- include('rippledAPIs.md.ejs') -%>
|
||||
<%- include('request.md.ejs') -%>
|
||||
<%- include('hasNextPage.md.ejs') -%>
|
||||
<%- include('requestNextPage.md.ejs') -%>
|
||||
<%- include('introduction.md.ejs') %>
|
||||
<%- include('boilerplate.md.ejs') %>
|
||||
<%- include('offline.md.ejs') %>
|
||||
<%- include('basictypes.md.ejs') %>
|
||||
<%- include('transactions.md.ejs') %>
|
||||
<%- include('specifications.md.ejs') %>
|
||||
<%- include('rippledAPIs.md.ejs') %>
|
||||
<%- include('request.md.ejs') %>
|
||||
<%- include('hasNextPage.md.ejs') %>
|
||||
<%- include('requestNextPage.md.ejs') %>
|
||||
|
||||
<%- include('staticMethods.md.ejs') -%>
|
||||
<%- include('renameCounterpartyToIssuer.md.ejs') -%>
|
||||
<%- include('formatBidsAndAsks.md.ejs') -%>
|
||||
<%- include('staticMethods.md.ejs') %>
|
||||
<%- include('renameCounterpartyToIssuer.md.ejs') %>
|
||||
<%- include('formatBidsAndAsks.md.ejs') %>
|
||||
|
||||
<%- include('methods.md.ejs') -%>
|
||||
<%- include('connect.md.ejs') -%>
|
||||
<%- include('disconnect.md.ejs') -%>
|
||||
<%- include('isConnected.md.ejs') -%>
|
||||
<%- include('getServerInfo.md.ejs') -%>
|
||||
<%- include('getFee.md.ejs') -%>
|
||||
<%- include('getLedgerVersion.md.ejs') -%>
|
||||
<%- include('getTransaction.md.ejs') -%>
|
||||
<%- include('getTransactions.md.ejs') -%>
|
||||
<%- include('getTrustlines.md.ejs') -%>
|
||||
<%- include('getBalances.md.ejs') -%>
|
||||
<%- include('getBalanceSheet.md.ejs') -%>
|
||||
<%- include('getPaths.md.ejs') -%>
|
||||
<%- include('getOrders.md.ejs') -%>
|
||||
<%- include('getOrderbook.md.ejs') -%>
|
||||
<%- include('getSettings.md.ejs') -%>
|
||||
<%- include('getAccountInfo.md.ejs') -%>
|
||||
<%- include('getAccountObjects.md.ejs') -%>
|
||||
<%- include('getPaymentChannel.md.ejs') -%>
|
||||
<%- include('getLedger.md.ejs') -%>
|
||||
<%- include('parseAccountFlags.md.ejs') -%>
|
||||
<%- include('prepareTransaction.md.ejs') -%>
|
||||
<%- include('preparePayment.md.ejs') -%>
|
||||
<%- include('prepareTrustline.md.ejs') -%>
|
||||
<%- include('prepareOrder.md.ejs') -%>
|
||||
<%- include('prepareOrderCancellation.md.ejs') -%>
|
||||
<%- include('prepareSettings.md.ejs') -%>
|
||||
<%- include('prepareEscrowCreation.md.ejs') -%>
|
||||
<%- include('prepareEscrowCancellation.md.ejs') -%>
|
||||
<%- include('prepareEscrowExecution.md.ejs') -%>
|
||||
<%- include('preparePaymentChannelCreate.md.ejs') -%>
|
||||
<%- include('preparePaymentChannelClaim.md.ejs') -%>
|
||||
<%- include('preparePaymentChannelFund.md.ejs') -%>
|
||||
<%- include('prepareCheckCreate.md.ejs') -%>
|
||||
<%- include('prepareCheckCancel.md.ejs') -%>
|
||||
<%- include('prepareCheckCash.md.ejs') -%>
|
||||
<%- include('sign.md.ejs') -%>
|
||||
<%- include('combine.md.ejs') -%>
|
||||
<%- include('submit.md.ejs') -%>
|
||||
<%- include('generateXAddress.md.ejs') -%>
|
||||
<%- include('generateAddress.md.ejs') -%>
|
||||
<%- include('isValidAddress.md.ejs') -%>
|
||||
<%- include('isValidSecret.md.ejs') -%>
|
||||
<%- include('deriveKeypair.md.ejs') -%>
|
||||
<%- include('deriveAddress.md.ejs') -%>
|
||||
<%- include('signPaymentChannelClaim.md.ejs') -%>
|
||||
<%- include('verifyPaymentChannelClaim.md.ejs') -%>
|
||||
<%- include('computeLedgerHash.md.ejs') -%>
|
||||
<%- include('xrpToDropsAndDropsToXrp.md.ejs') -%>
|
||||
<%- include('iso8601ToRippleTime.md.ejs') -%>
|
||||
<%- include('rippleTimeToISO8601.md.ejs') -%>
|
||||
<%- include('txFlags.md.ejs') -%>
|
||||
<%- include('schemaValidator.md.ejs') -%>
|
||||
<%- include('events.md.ejs') -%>
|
||||
<%- include('methods.md.ejs') %>
|
||||
<%- include('connect.md.ejs') %>
|
||||
<%- include('disconnect.md.ejs') %>
|
||||
<%- include('isConnected.md.ejs') %>
|
||||
<%- include('getServerInfo.md.ejs') %>
|
||||
<%- include('getFee.md.ejs') %>
|
||||
<%- include('getLedgerVersion.md.ejs') %>
|
||||
<%- include('getTransaction.md.ejs') %>
|
||||
<%- include('getTransactions.md.ejs') %>
|
||||
<%- include('getTrustlines.md.ejs') %>
|
||||
<%- include('getBalances.md.ejs') %>
|
||||
<%- include('getBalanceSheet.md.ejs') %>
|
||||
<%- include('getPaths.md.ejs') %>
|
||||
<%- include('getOrders.md.ejs') %>
|
||||
<%- include('getOrderbook.md.ejs') %>
|
||||
<%- include('getSettings.md.ejs') %>
|
||||
<%- include('getAccountInfo.md.ejs') %>
|
||||
<%- include('getAccountObjects.md.ejs') %>
|
||||
<%- include('getPaymentChannel.md.ejs') %>
|
||||
<%- include('getLedger.md.ejs') %>
|
||||
<%- include('parseAccountFlags.md.ejs') %>
|
||||
<%- include('prepareTransaction.md.ejs') %>
|
||||
<%- include('preparePayment.md.ejs') %>
|
||||
<%- include('prepareTrustline.md.ejs') %>
|
||||
<%- include('prepareOrder.md.ejs') %>
|
||||
<%- include('prepareOrderCancellation.md.ejs') %>
|
||||
<%- include('prepareSettings.md.ejs') %>
|
||||
<%- include('prepareEscrowCreation.md.ejs') %>
|
||||
<%- include('prepareEscrowCancellation.md.ejs') %>
|
||||
<%- include('prepareEscrowExecution.md.ejs') %>
|
||||
<%- include('preparePaymentChannelCreate.md.ejs') %>
|
||||
<%- include('preparePaymentChannelClaim.md.ejs') %>
|
||||
<%- include('preparePaymentChannelFund.md.ejs') %>
|
||||
<%- include('prepareCheckCreate.md.ejs') %>
|
||||
<%- include('prepareCheckCancel.md.ejs') %>
|
||||
<%- include('prepareCheckCash.md.ejs') %>
|
||||
<%- include('sign.md.ejs') %>
|
||||
<%- include('combine.md.ejs') %>
|
||||
<%- include('submit.md.ejs') %>
|
||||
<%- include('generateXAddress.md.ejs') %>
|
||||
<%- include('generateAddress.md.ejs') %>
|
||||
<%- include('isValidAddress.md.ejs') %>
|
||||
<%- include('isValidSecret.md.ejs') %>
|
||||
<%- include('deriveKeypair.md.ejs') %>
|
||||
<%- include('deriveAddress.md.ejs') %>
|
||||
<%- include('signPaymentChannelClaim.md.ejs') %>
|
||||
<%- include('verifyPaymentChannelClaim.md.ejs') %>
|
||||
<%- include('computeLedgerHash.md.ejs') %>
|
||||
<%- include('xrpToDropsAndDropsToXrp.md.ejs') %>
|
||||
<%- include('iso8601ToRippleTime.md.ejs') %>
|
||||
<%- include('rippleTimeToISO8601.md.ejs') %>
|
||||
<%- include('txFlags.md.ejs') %>
|
||||
<%- include('schemaValidator.md.ejs') %>
|
||||
<%- include('events.md.ejs') %>
|
||||
|
||||
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "1.6.1",
|
||||
"version": "1.6.4",
|
||||
"license": "ISC",
|
||||
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
|
||||
"files": [
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash": "^4.14.136",
|
||||
"@types/ws": "^6.0.3",
|
||||
"@types/ws": "^7.2.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"https-proxy-agent": "^4.0.0",
|
||||
"jsonschema": "1.2.2",
|
||||
@@ -29,12 +29,12 @@
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"ripple-address-codec": "^4.0.0",
|
||||
"ripple-binary-codec": "^0.2.5",
|
||||
"ripple-keypairs": "^0.11.0",
|
||||
"ripple-keypairs": "^1.0.0",
|
||||
"ripple-lib-transactionparser": "0.8.2",
|
||||
"ws": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/mocha": "^7.0.1",
|
||||
"@types/node": "^13.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.3.3",
|
||||
"@typescript-eslint/parser": "^2.3.3",
|
||||
@@ -44,12 +44,12 @@
|
||||
"eslint": "^6.5.1",
|
||||
"eventemitter2": "^6.0.0",
|
||||
"json-schema-to-markdown-table": "^0.4.0",
|
||||
"mocha": "7.0.0",
|
||||
"mocha": "7.0.1",
|
||||
"mocha-junit-reporter": "^1.9.1",
|
||||
"nyc": "^15.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-node": "^8.4.1",
|
||||
"typescript": "^3.6.4",
|
||||
"typescript": "^3.7.5",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-bundle-analyzer": "^3.6.0",
|
||||
"webpack-cli": "^3.3.9"
|
||||
|
||||
@@ -371,7 +371,11 @@ export class Connection extends EventEmitter {
|
||||
* If this succeeds, we're good. If it fails, disconnect so that the consumer can reconnect, if desired.
|
||||
*/
|
||||
private _heartbeat = () => {
|
||||
return this.request({command: 'ping'}).catch(() => this.reconnect())
|
||||
return this.request({command: 'ping'}).catch(() => {
|
||||
this.reconnect().catch((error) => {
|
||||
this.emit('error', 'reconnect', error.message, error)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,18 +489,6 @@ export class Connection extends EventEmitter {
|
||||
this._ws.on('error', error =>
|
||||
this.emit('error', 'websocket', error.message, error)
|
||||
)
|
||||
// Finalize the connection and resolve all awaiting connect() requests
|
||||
try {
|
||||
this._retryConnectionBackoff.reset()
|
||||
await this._subscribeToLedger()
|
||||
this._startHeartbeatInterval()
|
||||
this._connectionManager.resolveAllAwaiting()
|
||||
this.emit('connected')
|
||||
} catch (error) {
|
||||
this._connectionManager.rejectAllAwaiting(error)
|
||||
this.disconnect()
|
||||
return
|
||||
}
|
||||
// Handle a closed connection: reconnect if it was unexpected
|
||||
this._ws.once('close', code => {
|
||||
this._clearHeartbeatInterval()
|
||||
@@ -520,6 +512,17 @@ export class Connection extends EventEmitter {
|
||||
}, retryTimeout)
|
||||
}
|
||||
})
|
||||
// Finalize the connection and resolve all awaiting connect() requests
|
||||
try {
|
||||
this._retryConnectionBackoff.reset()
|
||||
await this._subscribeToLedger()
|
||||
this._startHeartbeatInterval()
|
||||
this._connectionManager.resolveAllAwaiting()
|
||||
this.emit('connected')
|
||||
} catch (error) {
|
||||
this._connectionManager.rejectAllAwaiting(error)
|
||||
await this.disconnect().catch(() => {}) // Ignore this error, propagate the root cause.
|
||||
}
|
||||
})
|
||||
return this._connectionManager.awaitConnection()
|
||||
}
|
||||
|
||||
@@ -28,7 +28,13 @@ export interface GenerateAddressOptions {
|
||||
function generateAddressAPI(options: GenerateAddressOptions): GeneratedAddress {
|
||||
validate.generateAddress({options})
|
||||
try {
|
||||
const secret = keypairs.generateSeed(options)
|
||||
const generateSeedOptions: { entropy?: Uint8Array; algorithm?: "ecdsa-secp256k1" | "ed25519"; } = {
|
||||
algorithm: options.algorithm
|
||||
}
|
||||
if (options.entropy) {
|
||||
generateSeedOptions.entropy = Uint8Array.from(options.entropy)
|
||||
}
|
||||
const secret = keypairs.generateSeed(generateSeedOptions)
|
||||
const keypair = keypairs.deriveKeypair(secret)
|
||||
const classicAddress = keypairs.deriveAddress(keypair.publicKey)
|
||||
const returnValue: any = {
|
||||
|
||||
@@ -7,7 +7,7 @@ function verifyPaymentChannelClaim(
|
||||
amount: string,
|
||||
signature: string,
|
||||
publicKey: string
|
||||
): string {
|
||||
): boolean {
|
||||
validate.verifyPaymentChannelClaim({channel, amount, signature, publicKey})
|
||||
|
||||
const signingData = binary.encodeForSigningClaim({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from 'assert-diff'
|
||||
import responses from '../../fixtures/responses'
|
||||
import {TestSuite} from '../../utils'
|
||||
import { GenerateAddressOptions } from '../../../src/offline/generate-address'
|
||||
const {generateAddress: RESPONSE_FIXTURES} = responses
|
||||
|
||||
/**
|
||||
@@ -9,22 +10,192 @@ const {generateAddress: RESPONSE_FIXTURES} = responses
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'generateAddress': async (api, address) => {
|
||||
function random(): number[] {
|
||||
'generateAddress': async (api) => {
|
||||
// GIVEN entropy of all zeros
|
||||
function random() {
|
||||
return new Array(16).fill(0)
|
||||
}
|
||||
|
||||
assert.deepEqual(
|
||||
// WHEN generating an address
|
||||
api.generateAddress({entropy: random()}),
|
||||
|
||||
// THEN we get the expected return value
|
||||
RESPONSE_FIXTURES
|
||||
)
|
||||
},
|
||||
|
||||
'generateAddress invalid': async (api, address) => {
|
||||
'generateAddress invalid entropy': async (api) => {
|
||||
assert.throws(() => {
|
||||
// GIVEN entropy of 1 byte
|
||||
function random() {
|
||||
return new Array(1).fill(0)
|
||||
}
|
||||
|
||||
// WHEN generating an address
|
||||
api.generateAddress({entropy: random()})
|
||||
|
||||
// THEN an UnexpectedError is thrown
|
||||
// because 16 bytes of entropy are required
|
||||
}, api.errors.UnexpectedError)
|
||||
},
|
||||
|
||||
'generateAddress with no options object': async (api) => {
|
||||
// GIVEN no options
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress()
|
||||
|
||||
// THEN we get an object with an address starting with 'r' and a secret starting with 's'
|
||||
assert(account.address.startsWith('r'), 'Address must start with `r`')
|
||||
assert(account.secret.startsWith('s'), 'Secret must start with `s`')
|
||||
},
|
||||
|
||||
'generateAddress with empty options object': async (api) => {
|
||||
// GIVEN an empty options object
|
||||
const options = {}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get an object with an address starting with 'r' and a secret starting with 's'
|
||||
assert(account.address.startsWith('r'), 'Address must start with `r`')
|
||||
assert(account.secret.startsWith('s'), 'Secret must start with `s`')
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1`': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1'}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get an object with an address starting with 'r' and a secret starting with 's' (not 'sEd')
|
||||
assert(account.address.startsWith('r'), 'Address must start with `r`')
|
||||
assert.deepEqual(account.secret.slice(0, 1), 's', `Secret ${account.secret} must start with 's'`)
|
||||
assert.notStrictEqual(account.secret.slice(0, 3), 'sEd', `secp256k1 secret ${account.secret} must not start with 'sEd'`)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519`': async (api) => {
|
||||
// GIVEN we want to use 'ed25519'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519'}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get an object with an address starting with 'r' and a secret starting with 'sEd'
|
||||
assert(account.address.startsWith('r'), 'Address must start with `r`')
|
||||
assert.deepEqual(account.secret.slice(0, 3), 'sEd', `Ed25519 secret ${account.secret} must start with 'sEd'`)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1', entropy: new Array(16).fill(0)}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, responses.generateAddress)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519` and given entropy': async (api) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519', entropy: new Array(16).fill(0)}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, {
|
||||
|
||||
// generateAddress return value always includes xAddress to encourage X-address adoption
|
||||
xAddress: 'X7xq1YJ4xmLSGGLhuakFQB9CebWYthQkgsvFC4LGFH871HB',
|
||||
|
||||
classicAddress: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7",
|
||||
address: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7",
|
||||
secret: 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE'
|
||||
})
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1', entropy: new Array(16).fill(0), includeClassicAddress: true}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, responses.generateAddress)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519` and given entropy; include classic address': async (api) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519', entropy: new Array(16).fill(0), includeClassicAddress: true}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, {
|
||||
|
||||
// generateAddress return value always includes xAddress to encourage X-address adoption
|
||||
xAddress: 'X7xq1YJ4xmLSGGLhuakFQB9CebWYthQkgsvFC4LGFH871HB',
|
||||
|
||||
secret: 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE',
|
||||
classicAddress: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7",
|
||||
address: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7"
|
||||
})
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address; for test network use': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1', entropy: new Array(16).fill(0), includeClassicAddress: true, test: true}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
const response = Object.assign({}, responses.generateAddress, {
|
||||
|
||||
// generateAddress return value always includes xAddress to encourage X-address adoption
|
||||
xAddress: 'TVG3TcCD58BD6MZqsNuTihdrhZwR8SzvYS8U87zvHsAcNw4'
|
||||
|
||||
})
|
||||
assert.deepEqual(account, response)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519` and given entropy; include classic address; for test network use': async (api) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519', entropy: new Array(16).fill(0), includeClassicAddress: true, test: true}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, {
|
||||
|
||||
// generateAddress return value always includes xAddress to encourage X-address adoption
|
||||
xAddress: 'T7t4HeTMF5tT68agwuVbJwu23ssMPeh8dDtGysZoQiij1oo',
|
||||
|
||||
secret: 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE',
|
||||
classicAddress: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7",
|
||||
address: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7"
|
||||
})
|
||||
},
|
||||
|
||||
'generateAddress for test network use': async (api) => {
|
||||
// GIVEN we want an address for test network use
|
||||
const options: GenerateAddressOptions = {test: true}
|
||||
|
||||
// WHEN generating an address
|
||||
const account = api.generateAddress(options)
|
||||
|
||||
// THEN we get an object with xAddress starting with 'T' and a secret starting with 's'
|
||||
|
||||
// generateAddress return value always includes xAddress to encourage X-address adoption
|
||||
assert.deepEqual(account.xAddress.slice(0, 1), 'T', 'Test addresses start with T')
|
||||
|
||||
assert.deepEqual(account.secret.slice(0, 1), 's', `Secret ${account.secret} must start with 's'`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import assert from 'assert-diff'
|
||||
import responses from '../../fixtures/responses'
|
||||
import {TestSuite} from '../../utils'
|
||||
import { GenerateAddressOptions } from '../../../src/offline/generate-address'
|
||||
|
||||
/**
|
||||
* Every test suite exports their tests in the default object.
|
||||
@@ -8,22 +9,175 @@ import {TestSuite} from '../../utils'
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'generateXAddress': async (api, address) => {
|
||||
'generateXAddress': async (api) => {
|
||||
// GIVEN entropy of all zeros
|
||||
function random() {
|
||||
return new Array(16).fill(0)
|
||||
}
|
||||
|
||||
assert.deepEqual(
|
||||
// WHEN generating an X-address
|
||||
api.generateXAddress({entropy: random()}),
|
||||
|
||||
// THEN we get the expected return value
|
||||
responses.generateXAddress
|
||||
)
|
||||
},
|
||||
|
||||
'generateXAddress invalid': async (api, address) => {
|
||||
'generateXAddress invalid entropy': async (api) => {
|
||||
assert.throws(() => {
|
||||
// GIVEN entropy of 1 byte
|
||||
function random() {
|
||||
return new Array(1).fill(0)
|
||||
}
|
||||
|
||||
// WHEN generating an X-address
|
||||
api.generateXAddress({entropy: random()})
|
||||
|
||||
// THEN an UnexpectedError is thrown
|
||||
// because 16 bytes of entropy are required
|
||||
}, api.errors.UnexpectedError)
|
||||
},
|
||||
|
||||
'generateXAddress with no options object': async (api) => {
|
||||
// GIVEN no options
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress()
|
||||
|
||||
// THEN we get an object with an xAddress starting with 'X' and a secret starting with 's'
|
||||
assert(account.xAddress.startsWith('X'), 'By default X-addresses start with X')
|
||||
assert(account.secret.startsWith('s'), 'Secrets start with s')
|
||||
},
|
||||
|
||||
'generateXAddress with empty options object': async (api) => {
|
||||
// GIVEN an empty options object
|
||||
const options = {}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get an object with an xAddress starting with 'X' and a secret starting with 's'
|
||||
assert(account.xAddress.startsWith('X'), 'By default X-addresses start with X')
|
||||
assert(account.secret.startsWith('s'), 'Secrets start with s')
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1`': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1'}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get an object with an xAddress starting with 'X' and a secret starting with 's'
|
||||
assert(account.xAddress.startsWith('X'), 'By default X-addresses start with X')
|
||||
assert.deepEqual(account.secret.slice(0, 1), 's', `Secret ${account.secret} must start with 's'`)
|
||||
assert.notStrictEqual(account.secret.slice(0, 3), 'sEd', `secp256k1 secret ${account.secret} must not start with 'sEd'`)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519`': async (api) => {
|
||||
// GIVEN we want to use 'ed25519'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519'}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get an object with an xAddress starting with 'X' and a secret starting with 'sEd'
|
||||
assert(account.xAddress.startsWith('X'), 'By default X-addresses start with X')
|
||||
assert.deepEqual(account.secret.slice(0, 3), 'sEd', `Ed25519 secret ${account.secret} must start with 'sEd'`)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1', entropy: new Array(16).fill(0)}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, responses.generateXAddress)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519` and given entropy': async (api) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519', entropy: new Array(16).fill(0)}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, {
|
||||
xAddress: 'X7xq1YJ4xmLSGGLhuakFQB9CebWYthQkgsvFC4LGFH871HB',
|
||||
secret: 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE'
|
||||
})
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1', entropy: new Array(16).fill(0), includeClassicAddress: true}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, responses.generateAddress)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519` and given entropy; include classic address': async (api) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519', entropy: new Array(16).fill(0), includeClassicAddress: true}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, {
|
||||
xAddress: 'X7xq1YJ4xmLSGGLhuakFQB9CebWYthQkgsvFC4LGFH871HB',
|
||||
secret: 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE',
|
||||
classicAddress: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7",
|
||||
address: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7"
|
||||
})
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address; for test network use': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1', entropy: new Array(16).fill(0), includeClassicAddress: true, test: true}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
const response = Object.assign({}, responses.generateAddress, {
|
||||
xAddress: 'TVG3TcCD58BD6MZqsNuTihdrhZwR8SzvYS8U87zvHsAcNw4'
|
||||
})
|
||||
assert.deepEqual(account, response)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519` and given entropy; include classic address; for test network use': async (api) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519', entropy: new Array(16).fill(0), includeClassicAddress: true, test: true}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get the expected return value
|
||||
assert.deepEqual(account, {
|
||||
xAddress: 'T7t4HeTMF5tT68agwuVbJwu23ssMPeh8dDtGysZoQiij1oo',
|
||||
secret: 'sEdSJHS4oiAdz7w2X2ni1gFiqtbJHqE',
|
||||
classicAddress: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7",
|
||||
address: "r9zRhGr7b6xPekLvT6wP4qNdWMryaumZS7"
|
||||
})
|
||||
},
|
||||
|
||||
'generateXAddress for test network use': async (api) => {
|
||||
// GIVEN we want an X-address for test network use
|
||||
const options: GenerateAddressOptions = {test: true}
|
||||
|
||||
// WHEN generating an X-address
|
||||
const account = api.generateXAddress(options)
|
||||
|
||||
// THEN we get an object with xAddress starting with 'T' and a secret starting with 's'
|
||||
assert.deepEqual(account.xAddress.slice(0, 1), 'T', 'Test X-addresses start with T')
|
||||
assert.deepEqual(account.secret.slice(0, 1), 's', `Secret ${account.secret} must start with 's'`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +360,34 @@ describe('Connection', function() {
|
||||
})
|
||||
})
|
||||
|
||||
it('heartbeat failure and reconnect failure', function(done) {
|
||||
if (isBrowser) {
|
||||
const phantomTest = /PhantomJS/
|
||||
if (phantomTest.test(navigator.userAgent)) {
|
||||
// inside PhantomJS this one just hangs, so skip as not very relevant
|
||||
done()
|
||||
return
|
||||
}
|
||||
}
|
||||
// Set the heartbeat to less than the 1 second ping response
|
||||
this.api.connection._config.timeout = 500
|
||||
// Drop the test runner timeout, since this should be a quick test
|
||||
this.timeout(5000)
|
||||
// fail on reconnect/connection
|
||||
this.api.connection.reconnect = async () => {
|
||||
throw new Error('error on reconnect')
|
||||
}
|
||||
// Hook up a listener for the reconnect error event
|
||||
this.api.on('error', (error, message) => {
|
||||
if(error === 'reconnect' && message === 'error on reconnect') {
|
||||
return done()
|
||||
}
|
||||
return done(new Error('Expected error on reconnect'))
|
||||
})
|
||||
// Trigger a heartbeat
|
||||
this.api.connection._heartbeat()
|
||||
})
|
||||
|
||||
it('should emit disconnected event with code 1000 (CLOSE_NORMAL)', function(done) {
|
||||
this.api.once('disconnected', code => {
|
||||
assert.strictEqual(code, 1000)
|
||||
@@ -562,6 +590,24 @@ describe('Connection', function() {
|
||||
}
|
||||
)
|
||||
|
||||
it('should clean up websocket connection if error after websocket is opened', async function() {
|
||||
await this.api.disconnect();
|
||||
// fail on connection
|
||||
this.api.connection._subscribeToLedger = async () => {
|
||||
throw new Error('error on _subscribeToLedger')
|
||||
}
|
||||
try {
|
||||
await this.api.connect();
|
||||
throw new Error('expected connect() to reject, but it resolved')
|
||||
} catch (err) {
|
||||
assert(err.message === 'error on _subscribeToLedger');
|
||||
// _ws.close event listener should have cleaned up the socket when disconnect _ws.close is run on connection error
|
||||
// do not fail on connection anymore
|
||||
this.api.connection._subscribeToLedger = async () => {}
|
||||
await this.api.connection.reconnect();
|
||||
}
|
||||
})
|
||||
|
||||
it('should try to reconnect on empty subscribe response on reconnect', function(done) {
|
||||
this.timeout(23000)
|
||||
this.api.on('error', error => {
|
||||
|
||||
@@ -32,21 +32,33 @@ describe('RippleAPI [Test Runner]', function() {
|
||||
// Run all the tests:
|
||||
for (const {name: methodName, tests, config} of allTestSuites) {
|
||||
describe(`api.${methodName}`, () => {
|
||||
// Run each test with the original-style address.
|
||||
describe(`[Original Address]`, () => {
|
||||
for (const [testName, fn] of tests) {
|
||||
// Run each test that does not use an address.
|
||||
for (const [testName, fn] of tests) {
|
||||
if (fn.length === 1) {
|
||||
it(testName, function() {
|
||||
return fn(this.api, addresses.ACCOUNT)
|
||||
})
|
||||
}
|
||||
}
|
||||
// Run each test with a classic address.
|
||||
describe(`[Classic Address]`, () => {
|
||||
for (const [testName, fn] of tests) {
|
||||
if (fn.length === 2) {
|
||||
it(testName, function() {
|
||||
return fn(this.api, addresses.ACCOUNT)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
// Run each test with the newer, x-address style.
|
||||
// Run each test with an X-address.
|
||||
if (!config.skipXAddress) {
|
||||
describe(`[X-address]`, () => {
|
||||
for (const [testName, fn] of tests) {
|
||||
it(testName, function() {
|
||||
return fn(this.api, addresses.ACCOUNT_X)
|
||||
})
|
||||
if (fn.length === 2) {
|
||||
it(testName, function() {
|
||||
return fn(this.api, addresses.ACCOUNT_X)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
"moduleResolution": "node",
|
||||
|
||||
"declaration": true,
|
||||
"declarationMap": true /* Added 2019-04-13 */,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
"strict": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"noUnusedLocals": true,
|
||||
|
||||
209
yarn.lock
209
yarn.lock
@@ -151,13 +151,6 @@
|
||||
traverse "^0.6.6"
|
||||
unified "^6.1.6"
|
||||
|
||||
"@types/base-x@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/base-x/-/base-x-3.0.0.tgz#a1365259d1d3fa3ff973ab543192a6bdd4cb2f90"
|
||||
integrity sha512-vnqSlpsv9uFX5/z8GyKWAfWHhLGJDBkrgRRsnxlsX23DHOlNyqP/eHQiv4TwnYcZULzQIxaWA/xRWU9Dyy4qzw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
@@ -178,43 +171,34 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440"
|
||||
integrity sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==
|
||||
|
||||
"@types/mocha@^5.2.7":
|
||||
version "5.2.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-5.2.7.tgz#315d570ccb56c53452ff8638738df60726d5b6ea"
|
||||
integrity sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==
|
||||
"@types/mocha@^7.0.1":
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.1.tgz#5d7ec2a789a1f77c59b7ad071b9d50bf1abbfc9e"
|
||||
integrity sha512-L/Nw/2e5KUaprNJoRA33oly+M8X8n0K+FwLTbYqwTcR14wdPWeRkigBLfSFpN/Asf9ENZTMZwLxjtjeYucAA4Q==
|
||||
|
||||
"@types/node@*", "@types/node@^13.1.1":
|
||||
version "13.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.6.tgz#076028d0b0400be8105b89a0a55550c86684ffec"
|
||||
integrity sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==
|
||||
version "13.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.1.tgz#238eb34a66431b71d2aaddeaa7db166f25971a0d"
|
||||
integrity sha512-Zq8gcQGmn4txQEJeiXo/KiLpon8TzAl0kmKH4zdWctPj05nWwp1ClMdAVEloqrQKfaC48PNLdgN/aVaLqUrluA==
|
||||
|
||||
"@types/ws@^6.0.3":
|
||||
version "6.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.4.tgz#7797707c8acce8f76d8c34b370d4645b70421ff1"
|
||||
integrity sha512-PpPrX7SZW9re6+Ha8ojZG4Se8AZXgf0GK6zmfqEuCsY49LFDNXO3SByp44X3dFEqtB73lkCDAdUazhAjVPiNwg==
|
||||
"@types/ws@^7.2.0":
|
||||
version "7.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.1.tgz#b800f2b8aee694e2b581113643e20d79dd3b8556"
|
||||
integrity sha512-UEmRNbXFGvfs/sLncf01GuVv6U1mZP3Df0iXWx4kUlikJxbFyFADp95mDn1XDTE2mXpzzoHcKlfFcbytLq4vaA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^2.3.3":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.13.0.tgz#57e933fe16a2fc66dbac059af0d6d85d921d748e"
|
||||
integrity sha512-QoiANo0MMGNa8ej/yX3BrW5dZj5d8HYcKiM2fyYUlezECqn8Xc7T/e4EUdiGinn8jhBrn+9X47E9TWaaup3u1g==
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.20.0.tgz#a522d0e1e4898f7c9c6a8e1ed3579b60867693fa"
|
||||
integrity sha512-cimIdVDV3MakiGJqMXw51Xci6oEDEoPkvh8ggJe2IIzcc0fYqAxOXN6Vbeanahz6dLZq64W+40iUEc9g32FLDQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "2.13.0"
|
||||
"@typescript-eslint/experimental-utils" "2.20.0"
|
||||
eslint-utils "^1.4.3"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz#958614faa6f77599ee2b241740e0ea402482533d"
|
||||
integrity sha512-+Hss3clwa6aNiC8ZjA45wEm4FutDV5HsVXPl/rDug1THq6gEtOYRGLqS3JlTk7mSnL5TbJz0LpEbzbPnKvY6sw==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.13.0"
|
||||
eslint-scope "^5.0.0"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.14.0.tgz#e9179fa3c44e00b3106b85d7b69342901fb43e3b"
|
||||
@@ -224,6 +208,15 @@
|
||||
"@typescript-eslint/typescript-estree" "2.14.0"
|
||||
eslint-scope "^5.0.0"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.20.0":
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.20.0.tgz#3b6fa5a6b8885f126d5a4280e0d44f0f41e73e32"
|
||||
integrity sha512-fEBy9xYrwG9hfBLFEwGW2lKwDRTmYzH3DwTmYbT+SMycmxAoPl0eGretnBFj/s+NfYBG63w/5c3lsvqqz5mYag==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.20.0"
|
||||
eslint-scope "^5.0.0"
|
||||
|
||||
"@typescript-eslint/parser@^2.3.3":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.14.0.tgz#30fa0523d86d74172a5e32274558404ba4262cd6"
|
||||
@@ -234,19 +227,6 @@
|
||||
"@typescript-eslint/typescript-estree" "2.14.0"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz#a2e746867da772c857c13853219fced10d2566bc"
|
||||
integrity sha512-t21Mg5cc8T3ADEUGwDisHLIubgXKjuNRbkpzDMLb7/JMmgCe/gHM9FaaujokLey+gwTuLF5ndSQ7/EfQqrQx4g==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
glob "^7.1.6"
|
||||
is-glob "^4.0.1"
|
||||
lodash.unescape "4.0.1"
|
||||
semver "^6.3.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.14.0.tgz#c67698acdc14547f095eeefe908958d93e1a648d"
|
||||
@@ -260,6 +240,19 @@
|
||||
semver "^6.3.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.20.0":
|
||||
version "2.20.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.20.0.tgz#90a0f5598826b35b966ca83483b1a621b1a4d0c9"
|
||||
integrity sha512-WlFk8QtI8pPaE7JGQGxU7nGcnk1ccKAJkhbVookv94ZcAef3m6oCE/jEDL6dGte3JcD7reKrA0o55XhBRiVT3A==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
glob "^7.1.6"
|
||||
is-glob "^4.0.1"
|
||||
lodash "^4.17.15"
|
||||
semver "^6.3.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@webassemblyjs/ast@1.8.5":
|
||||
version "1.8.5"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
|
||||
@@ -651,13 +644,6 @@ atob@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
babel-runtime@^5.8.20:
|
||||
version "5.8.38"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-5.8.38.tgz#1c0b02eb63312f5f087ff20450827b425c9d4c19"
|
||||
integrity sha1-HAsC62MxL18If/IEUIJ7QlydTBk=
|
||||
dependencies:
|
||||
core-js "^1.0.0"
|
||||
|
||||
babel-runtime@^6.6.1:
|
||||
version "6.26.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
|
||||
@@ -676,18 +662,13 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
base-x@3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.4.tgz#94c1788736da065edb1d68808869e357c977fa77"
|
||||
integrity sha512-UYOadoSIkEI/VrRGSG6qp93rp2WdokiAiNYDfGW5qURAY8GiAQkvMbwNNSDYiVJopqv4gCna7xqf4rrNGp+5AA==
|
||||
base-x@3.0.7:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.7.tgz#1c5a7fafe8f66b4114063e8da102799d4e7c408f"
|
||||
integrity sha512-zAKJGuQPihXW22fkrfOclUUZXM2g92z5GzlSMHxhO6r6Qj+Nm0ccaGNBzDZojzwOMkpjAv4J0fOv1U4go+a4iw==
|
||||
dependencies:
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
base-x@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-1.1.0.tgz#42d3d717474f9ea02207f6d1aa1f426913eeb7ac"
|
||||
integrity sha1-QtPXF0dPnqAiB/bRqh9CaRPut6w=
|
||||
|
||||
base64-js@^1.0.2:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
@@ -748,11 +729,6 @@ bluebird@^3.5.5:
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
|
||||
integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==
|
||||
|
||||
bn.js@^3.1.1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-3.3.0.tgz#1138e577889fdc97bbdab51844f2190dfc0ae3d7"
|
||||
integrity sha1-ETjld4if3Je72rUYRPIZDfwK49c=
|
||||
|
||||
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
||||
version "4.11.8"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
||||
@@ -1237,11 +1213,6 @@ copy-descriptor@^0.1.0:
|
||||
resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
|
||||
integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=
|
||||
|
||||
core-js@^1.0.0:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
|
||||
|
||||
core-js@^2.4.0:
|
||||
version "2.6.11"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
|
||||
@@ -1550,7 +1521,7 @@ ejs@^3.0.1:
|
||||
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.0.1.tgz#30c8f6ee9948502cc32e85c37a3f8b39b5a614a5"
|
||||
integrity sha512-cuIMtJwxvzumSAkqaaoGY/L6Fc/t6YvoP9/VIaK0V/CyqKLEQ8sqODmYfy/cjXEdZ9+OOL8TecbJu+1RsofGDw==
|
||||
|
||||
elliptic@^6.0.0:
|
||||
elliptic@^6.0.0, elliptic@^6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762"
|
||||
integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==
|
||||
@@ -1563,19 +1534,6 @@ elliptic@^6.0.0:
|
||||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.0"
|
||||
|
||||
elliptic@^6.4.0:
|
||||
version "6.5.1"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.1.tgz#c380f5f909bf1b9b4428d028cd18d3b0efd6b52b"
|
||||
integrity sha512-xvJINNLbTeWQjrl6X+7eQCrIy/YPv5XCpKW6kB5mKvtnGILoLDcySuwomfdzt0BMdLNVnuRNTuzKNHj0bva1Cg==
|
||||
dependencies:
|
||||
bn.js "^4.4.0"
|
||||
brorand "^1.0.1"
|
||||
hash.js "^1.0.0"
|
||||
hmac-drbg "^1.0.0"
|
||||
inherits "^2.0.1"
|
||||
minimalistic-assert "^1.0.0"
|
||||
minimalistic-crypto-utils "^1.0.0"
|
||||
|
||||
emoji-regex@^7.0.1:
|
||||
version "7.0.3"
|
||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
|
||||
@@ -3259,9 +3217,9 @@ mkdirp@0.5.1, mkdirp@^0.5.1, mkdirp@~0.5.1:
|
||||
minimist "0.0.8"
|
||||
|
||||
mocha-junit-reporter@^1.9.1:
|
||||
version "1.23.2"
|
||||
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.2.tgz#268f47856677d5ae3d7a24afb5cc198ab74bc586"
|
||||
integrity sha512-ro39KUheimNkqdtjk1qXNw4B8b/REkCjlNYAsZ5Eso7tsVIX5BoQzA24vWSnWBdP/KNEKDoMoldzYyCwHuHZlw==
|
||||
version "1.23.3"
|
||||
resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.23.3.tgz#941e219dd759ed732f8641e165918aa8b167c981"
|
||||
integrity sha512-ed8LqbRj1RxZfjt/oC9t12sfrWsjZ3gNnbhV1nuj9R/Jb5/P3Xb4duv2eCfCDMYH+fEu0mqca7m4wsiVjsxsvA==
|
||||
dependencies:
|
||||
debug "^2.2.0"
|
||||
md5 "^2.1.0"
|
||||
@@ -3269,10 +3227,10 @@ mocha-junit-reporter@^1.9.1:
|
||||
strip-ansi "^4.0.0"
|
||||
xml "^1.0.0"
|
||||
|
||||
mocha@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.0.0.tgz#c60d14bf3de9601f549b3ff5be657eb8381c54bf"
|
||||
integrity sha512-CirsOPbO3jU86YKjjMzFLcXIb5YiGLUrjrXFHoJ3e2z9vWiaZVCZQ2+gtRGMPWF+nFhN6AWwLM/juzAQ6KRkbA==
|
||||
mocha@7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.0.1.tgz#276186d35a4852f6249808c6dd4a1376cbf6c6ce"
|
||||
integrity sha512-9eWmWTdHLXh72rGrdZjNbG3aa1/3NRPpul1z0D979QpEnFdCG0Q5tv834N+94QEN2cysfV72YocQ3fn87s70fg==
|
||||
dependencies:
|
||||
ansi-colors "3.2.3"
|
||||
browser-stdout "1.3.1"
|
||||
@@ -4142,21 +4100,12 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
hash-base "^3.0.0"
|
||||
inherits "^2.0.1"
|
||||
|
||||
ripple-address-codec@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz#eddbe3a7960d2e02c5c1c74fb9a9fa0d2dfb6571"
|
||||
integrity sha1-7dvjp5YNLgLFwcdPuan6DS37ZXE=
|
||||
dependencies:
|
||||
hash.js "^1.0.3"
|
||||
x-address-codec "^0.7.0"
|
||||
|
||||
ripple-address-codec@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.0.0.tgz#c20f39eea38def43d2379462e47bff4adabece30"
|
||||
integrity sha512-PsKl9aytg6fZG2F4RtfPT0c1gj42suAQY9VvJVGz+DfQTdXQaTT9V/StVhaJ6jhVpl7oCd981BB9p2Kq+Kyrng==
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ripple-address-codec/-/ripple-address-codec-4.1.0.tgz#7a119638527f1ccf1eb4aea3d4a8886bc9ccd5c5"
|
||||
integrity sha512-C72gJpwvDagaOUiHyh67otqNqFduB4hjvJFiiPz/8I3FCiUYuvFLXeLhb29CEkoAEdoN9p7pPreLgoHUvwzt9w==
|
||||
dependencies:
|
||||
"@types/base-x" "^3.0.0"
|
||||
base-x "3.0.4"
|
||||
base-x "3.0.7"
|
||||
create-hash "^1.1.2"
|
||||
|
||||
ripple-binary-codec@^0.2.5:
|
||||
@@ -4172,17 +4121,16 @@ ripple-binary-codec@^0.2.5:
|
||||
lodash "^4.17.15"
|
||||
ripple-address-codec "^4.0.0"
|
||||
|
||||
ripple-keypairs@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-0.11.0.tgz#ee9f4172d615106f4d3c0af15805960742dab045"
|
||||
integrity sha512-kzYwlWaLu1fl+StJdfZuEmcooJI18QuWxatII+Rcd3QeWXne16ml7N1e4HpYI/aJ69Cn4npsJef7kHSaA83Fgg==
|
||||
ripple-keypairs@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ripple-keypairs/-/ripple-keypairs-1.0.0.tgz#8f1c604f89daeac5e61b7eebbbca2da99da2bacf"
|
||||
integrity sha512-MQ3d6fU3D+Cqu5ma4dfkfa+KakN2sKpVVVN0FeJyAYPVIGXu8Rcvd1g028TdwYAZcSYk0tGn5UhHxd0gUG3T8g==
|
||||
dependencies:
|
||||
babel-runtime "^5.8.20"
|
||||
bn.js "^3.1.1"
|
||||
bn.js "^5.1.1"
|
||||
brorand "^1.0.5"
|
||||
elliptic "^6.4.0"
|
||||
elliptic "^6.5.2"
|
||||
hash.js "^1.0.3"
|
||||
ripple-address-codec "^2.0.1"
|
||||
ripple-address-codec "^4.0.0"
|
||||
|
||||
ripple-lib-transactionparser@0.8.2:
|
||||
version "0.8.2"
|
||||
@@ -4794,15 +4742,15 @@ tryer@^1.0.1:
|
||||
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
|
||||
|
||||
ts-node@^8.4.1:
|
||||
version "8.5.4"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.5.4.tgz#a152add11fa19c221d0b48962c210cf467262ab2"
|
||||
integrity sha512-izbVCRV68EasEPQ8MSIGBNK9dc/4sYJJKYA+IarMQct1RtEot6Xp0bXuClsbUSnKpg50ho+aOAx8en5c+y4OFw==
|
||||
version "8.6.2"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.6.2.tgz#7419a01391a818fbafa6f826a33c1a13e9464e35"
|
||||
integrity sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==
|
||||
dependencies:
|
||||
arg "^4.1.0"
|
||||
diff "^4.0.1"
|
||||
make-error "^1.1.1"
|
||||
source-map-support "^0.5.6"
|
||||
yn "^3.0.0"
|
||||
yn "3.1.1"
|
||||
|
||||
tslib@^1.8.1, tslib@^1.9.0:
|
||||
version "1.10.0"
|
||||
@@ -4853,10 +4801,10 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript@^3.6.4:
|
||||
version "3.7.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.4.tgz#1743a5ec5fef6a1fa9f3e4708e33c81c73876c19"
|
||||
integrity sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==
|
||||
typescript@^3.7.5:
|
||||
version "3.7.5"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
|
||||
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
|
||||
|
||||
underscore@~1.8.3:
|
||||
version "1.8.3"
|
||||
@@ -5086,9 +5034,9 @@ webpack-bundle-analyzer@^3.6.0:
|
||||
ws "^6.0.0"
|
||||
|
||||
webpack-cli@^3.3.9:
|
||||
version "3.3.10"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.10.tgz#17b279267e9b4fb549023fae170da8e6e766da13"
|
||||
integrity sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==
|
||||
version "3.3.11"
|
||||
resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.11.tgz#3bf21889bf597b5d82c38f215135a411edfdc631"
|
||||
integrity sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==
|
||||
dependencies:
|
||||
chalk "2.4.2"
|
||||
cross-spawn "6.0.5"
|
||||
@@ -5111,9 +5059,9 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
|
||||
source-map "~0.6.1"
|
||||
|
||||
webpack@^4.41.2:
|
||||
version "4.41.4"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.4.tgz#4bec4125224bdf50efa8be6226c19047599cd034"
|
||||
integrity sha512-Lc+2uB6NjpCWsHI3trkoISOI64h9QYIXenbEWj3bn3oyjfB1lEBXjWAfAyY2sM0rZn41oD5V91OLwKRwS6Wp8Q==
|
||||
version "4.41.6"
|
||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.6.tgz#12f2f804bf6542ef166755050d4afbc8f66ba7e1"
|
||||
integrity sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==
|
||||
dependencies:
|
||||
"@webassemblyjs/ast" "1.8.5"
|
||||
"@webassemblyjs/helper-module-context" "1.8.5"
|
||||
@@ -5234,13 +5182,6 @@ ws@^7.2.0:
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e"
|
||||
integrity sha512-sucePNSafamSKoOqoNfBd8V0StlkzJKL2ZAhGQinCfNQ+oacw+Pk7lcdAElecBF2VkLNZRiIb5Oi1Q5lVUVt2A==
|
||||
|
||||
x-address-codec@^0.7.0:
|
||||
version "0.7.2"
|
||||
resolved "https://registry.yarnpkg.com/x-address-codec/-/x-address-codec-0.7.2.tgz#2a2f7bb00278520bd13733a7959a05443d6802e0"
|
||||
integrity sha1-Ki97sAJ4UgvRNzOnlZoFRD1oAuA=
|
||||
dependencies:
|
||||
base-x "^1.0.1"
|
||||
|
||||
x-is-string@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82"
|
||||
@@ -5341,7 +5282,7 @@ yargs@^15.0.2:
|
||||
y18n "^4.0.0"
|
||||
yargs-parser "^16.1.0"
|
||||
|
||||
yn@^3.0.0:
|
||||
yn@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||
|
||||
Reference in New Issue
Block a user