mirror of
				https://github.com/Xahau/xahau.js.git
				synced 2025-11-04 04:55:48 +00:00 
			
		
		
		
	Add support for the X-address format (#1041)
* Update schema-validator * Update to ripple-address-codec 4.0.0 * Update ./src/common/hashes/index.ts * Add generateXAddress method * Deprecate generateAddress method * Add unit tests * Add documentation
This commit is contained in:
		@@ -1,5 +1,12 @@
 | 
			
		||||
# ripple-lib Release History
 | 
			
		||||
 | 
			
		||||
## 1.4.0-b1 (2019-09-26)
 | 
			
		||||
 | 
			
		||||
* Add support for the new X-address format.
 | 
			
		||||
* Some error messages have changed slightly. For example:
 | 
			
		||||
  * `-instance.Account is not of a type(s) string,instance.Account does not conform to the "address" format`
 | 
			
		||||
  * `+instance.Account is not of a type(s) string,instance.Account is not exactly one from <xAddress>,<classicAddress>`
 | 
			
		||||
 | 
			
		||||
## 1.3.4 (2019-10-18)
 | 
			
		||||
 | 
			
		||||
* Update ripple-lib-transactionparser
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,7 @@
 | 
			
		||||
  - [sign](#sign)
 | 
			
		||||
  - [combine](#combine)
 | 
			
		||||
  - [submit](#submit)
 | 
			
		||||
  - [generateXAddress](#generatexaddress)
 | 
			
		||||
  - [generateAddress](#generateaddress)
 | 
			
		||||
  - [isValidAddress](#isvalidaddress)
 | 
			
		||||
  - [isValidSecret](#isvalidsecret)
 | 
			
		||||
@@ -225,7 +226,19 @@ Methods that depend on the state of the XRP Ledger are unavailable in offline mo
 | 
			
		||||
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Every XRP Ledger account has an *address*, which is a base58-encoding of a hash of the account's public key. XRP Ledger addresses always start with the lowercase letter `r`.
 | 
			
		||||
```json
 | 
			
		||||
"X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
An *address* refers to a specific XRP Ledger account. It is a base-58 encoding of a hash of the account's public key. There are two kinds of addresses in common use:
 | 
			
		||||
 | 
			
		||||
### Classic Address
 | 
			
		||||
 | 
			
		||||
A *classic address* encodes a hash of the account's public key and a checksum. It has no other data. This kind of address always starts with the lowercase letter `r`.
 | 
			
		||||
 | 
			
		||||
### X-address
 | 
			
		||||
 | 
			
		||||
An *X-address* encodes a hash of the account's public key, a tag, and a checksum. This kind of address starts with the uppercase letter `X` if it is intended for use on the production XRP Ledger (mainnet). It starts with the uppercase letter `T` if it is intended for use on a test network such as Testnet or Devnet.
 | 
			
		||||
 | 
			
		||||
## Account Sequence Number
 | 
			
		||||
 | 
			
		||||
@@ -377,12 +390,12 @@ Name | Type | Description
 | 
			
		||||
source | object | The source of the funds to be sent.
 | 
			
		||||
*source.* address | [address](#address) | The address to send from.
 | 
			
		||||
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with source.maxAmount)
 | 
			
		||||
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
 | 
			
		||||
*source.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
 | 
			
		||||
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field cannot be used with source.amount)
 | 
			
		||||
destination | object | The destination of the funds to be sent.
 | 
			
		||||
*destination.* address | [address](#address) | An address representing the destination of the transaction.
 | 
			
		||||
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with `destination.minAmount`.)
 | 
			
		||||
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
 | 
			
		||||
*destination.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
 | 
			
		||||
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field cannot be used with destination.amount)
 | 
			
		||||
allowPartialPayment | boolean | *Optional* If true, this payment should proceed even if the whole amount cannot be delivered due to a lack of liquidity or a lack of funds in the source account.
 | 
			
		||||
invoiceID | string | *Optional* A 256-bit hash that can be used to identify a particular payment.
 | 
			
		||||
@@ -538,7 +551,7 @@ signers | object | *Optional* Settings that determine what sets of accounts can
 | 
			
		||||
*signers.* threshold | integer | A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`.
 | 
			
		||||
*signers.* weights | array | *Optional* Weights of signatures for each signer.
 | 
			
		||||
*signers.* weights[] | object | An association of an address and a weight.
 | 
			
		||||
*signers.weights[].* address | [address](#address) | A Ripple account address
 | 
			
		||||
*signers.weights[].* address | [address](#address) | An account address on the XRP Ledger
 | 
			
		||||
*signers.weights[].* weight | integer | The weight that the signature of this account counts as towards the threshold.
 | 
			
		||||
transferRate | number,null | *Optional*  The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.
 | 
			
		||||
 | 
			
		||||
@@ -851,7 +864,7 @@ Returns the response from invoking the specified command, with the specified opt
 | 
			
		||||
 | 
			
		||||
Refer to [rippled APIs](https://ripple.com/build/rippled-apis/) for commands and options. All XRP amounts must be specified in drops. One drop is equal to 0.000001 XRP. See [Specifying Currency Amounts](https://ripple.com/build/rippled-apis/#specifying-currency-amounts).
 | 
			
		||||
 | 
			
		||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://ripple.com/build/rippled-apis/#specifying-ledgers).
 | 
			
		||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://xrpl.org/basic-data-types.html#specifying-ledgers).
 | 
			
		||||
 | 
			
		||||
### Return Value
 | 
			
		||||
 | 
			
		||||
@@ -2289,12 +2302,12 @@ Name | Type | Description
 | 
			
		||||
source | object | Properties of the source of the payment.
 | 
			
		||||
*source.* address | [address](#address) | The address to send from.
 | 
			
		||||
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with source.maxAmount)
 | 
			
		||||
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
 | 
			
		||||
*source.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
 | 
			
		||||
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field cannot be used with source.amount)
 | 
			
		||||
destination | object | Properties of the destination of the payment.
 | 
			
		||||
*destination.* address | [address](#address) | An address representing the destination of the transaction.
 | 
			
		||||
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with `destination.minAmount`.)
 | 
			
		||||
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
 | 
			
		||||
*destination.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
 | 
			
		||||
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field cannot be used with destination.amount)
 | 
			
		||||
paths | string | The paths of trustlines and orders to use in executing the payment.
 | 
			
		||||
 | 
			
		||||
@@ -3906,7 +3919,7 @@ signers | object | *Optional* Settings that determine what sets of accounts can
 | 
			
		||||
*signers.* threshold | integer | A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`.
 | 
			
		||||
*signers.* weights | array | *Optional* Weights of signatures for each signer.
 | 
			
		||||
*signers.* weights[] | object | An association of an address and a weight.
 | 
			
		||||
*signers.weights[].* address | [address](#address) | A Ripple account address
 | 
			
		||||
*signers.weights[].* address | [address](#address) | An account address on the XRP Ledger
 | 
			
		||||
*signers.weights[].* weight | integer | The weight that the signature of this account counts as towards the threshold.
 | 
			
		||||
transferRate | number,null | *Optional*  The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.
 | 
			
		||||
 | 
			
		||||
@@ -5403,7 +5416,7 @@ options | object | *Optional* Options that control the type of signature that wi
 | 
			
		||||
*options.* signAs | [address](#address) | *Optional* The account that the signature should count for in multisigning.
 | 
			
		||||
secret | secret string | *Optional* The secret of the account that is initiating the transaction. (This field cannot be used with keypair).
 | 
			
		||||
 | 
			
		||||
Please note that when this method is used for multisigning, the `options` parameter is not *Optional* anymore. It will be compulsory. See the multisigning example in this section for more details. 
 | 
			
		||||
When this method is used for multisigning, the `options` parameter is required. See the multisigning example in this section for more details.
 | 
			
		||||
 | 
			
		||||
### Return Value
 | 
			
		||||
 | 
			
		||||
@@ -5431,6 +5444,7 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Example (multisigning)
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
@@ -5620,9 +5634,9 @@ return api.submit(signedTransaction)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## generateAddress
 | 
			
		||||
## generateXAddress
 | 
			
		||||
 | 
			
		||||
`generateAddress(): {address: string, secret: string}`
 | 
			
		||||
`generateXAddress(options?: object): {address: string, secret: string}`
 | 
			
		||||
 | 
			
		||||
Generate a new XRP Ledger address and corresponding secret.
 | 
			
		||||
 | 
			
		||||
@@ -5633,6 +5647,7 @@ Name | Type | Description
 | 
			
		||||
options | object | *Optional* Options to control how the address and secret are generated.
 | 
			
		||||
*options.* algorithm | string | *Optional* The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
 | 
			
		||||
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed.
 | 
			
		||||
*options.* test | boolean | *Optional* Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`.
 | 
			
		||||
 | 
			
		||||
### Return Value
 | 
			
		||||
 | 
			
		||||
@@ -5640,8 +5655,8 @@ This method returns an object with the following structure:
 | 
			
		||||
 | 
			
		||||
Name | Type | Description
 | 
			
		||||
---- | ---- | -----------
 | 
			
		||||
address | [address](#address) | A randomly generated Ripple account address.
 | 
			
		||||
secret | secret string | The secret corresponding to the `address`.
 | 
			
		||||
xAddress | [xAddress](#x-address) | A randomly generated XRP Ledger address in X-address format.
 | 
			
		||||
secret | secret string | The secret corresponding to the address.
 | 
			
		||||
 | 
			
		||||
### Example
 | 
			
		||||
 | 
			
		||||
@@ -5652,6 +5667,52 @@ return api.generateAddress();
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "xAddress": "XVLcsWWNiFdUEqoDmSwgxh1abfddG1LtbGFk7omPgYpbyE8",
 | 
			
		||||
  "secret": "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## generateAddress
 | 
			
		||||
 | 
			
		||||
`generateAddress(options?: object): {address: string, secret: string}`
 | 
			
		||||
 | 
			
		||||
Deprecated: This method returns a classic address. If you do not need the classic address, use `generateXAddress` instead.
 | 
			
		||||
 | 
			
		||||
Generate a new XRP Ledger address and corresponding secret.
 | 
			
		||||
 | 
			
		||||
### Parameters
 | 
			
		||||
 | 
			
		||||
Name | Type | Description
 | 
			
		||||
---- | ---- | -----------
 | 
			
		||||
options | object | *Optional* Options to control how the address and secret are generated.
 | 
			
		||||
*options.* algorithm | string | *Optional* The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
 | 
			
		||||
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed.
 | 
			
		||||
*options.* includeClassicAddress | boolean | *Optional* If `true`, return the classic address, in addition to the X-address.
 | 
			
		||||
*options.* test | boolean | *Optional* Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`.
 | 
			
		||||
 | 
			
		||||
### Return Value
 | 
			
		||||
 | 
			
		||||
This method returns an object with the following structure:
 | 
			
		||||
 | 
			
		||||
Name | Type | Description
 | 
			
		||||
---- | ---- | -----------
 | 
			
		||||
xAddress | [xAddress](#x-address) | A randomly generated XRP Ledger address in X-address format.
 | 
			
		||||
classicAddress | [classicAddress](#classic-address) | A randomly generated XRP Ledger Account ID (classic address).
 | 
			
		||||
address | [classicAddress](#classic-address) | Deprecated: Use `classicAddress` instead.
 | 
			
		||||
secret | secret string | The secret corresponding to the address.
 | 
			
		||||
 | 
			
		||||
### Example
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
return api.generateAddress();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
  "xAddress": "XVLcsWWNiFdUEqoDmSwgxh1abfddG1LtbGFk7omPgYpbyE8",
 | 
			
		||||
  "classicAddress": "rGCkuB7PBr5tNy68tPEABEtcdno4hE6Y7f",
 | 
			
		||||
  "address": "rGCkuB7PBr5tNy68tPEABEtcdno4hE6Y7f",
 | 
			
		||||
  "secret": "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
 | 
			
		||||
}
 | 
			
		||||
@@ -5662,7 +5723,7 @@ return api.generateAddress();
 | 
			
		||||
 | 
			
		||||
`isValidAddress(address: string): boolean`
 | 
			
		||||
 | 
			
		||||
Checks if the specified string contains a valid address.
 | 
			
		||||
Checks if the specified string contains a valid address. X-addresses are considered valid with ripple-lib v1.4.0 and higher.
 | 
			
		||||
 | 
			
		||||
### Parameters
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,19 @@
 | 
			
		||||
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Every XRP Ledger account has an *address*, which is a base58-encoding of a hash of the account's public key. XRP Ledger addresses always start with the lowercase letter `r`.
 | 
			
		||||
```json
 | 
			
		||||
"X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
An *address* refers to a specific XRP Ledger account. It is a base-58 encoding of a hash of the account's public key. There are two kinds of addresses in common use:
 | 
			
		||||
 | 
			
		||||
### Classic Address
 | 
			
		||||
 | 
			
		||||
A *classic address* encodes a hash of the account's public key and a checksum. It has no other data. This kind of address always starts with the lowercase letter `r`.
 | 
			
		||||
 | 
			
		||||
### X-address
 | 
			
		||||
 | 
			
		||||
An *X-address* encodes a hash of the account's public key, a tag, and a checksum. This kind of address starts with the uppercase letter `X` if it is intended for use on the production XRP Ledger (mainnet). It starts with the uppercase letter `T` if it is intended for use on a test network such as Testnet or Devnet.
 | 
			
		||||
 | 
			
		||||
## Account Sequence Number
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
## generateAddress
 | 
			
		||||
 | 
			
		||||
`generateAddress(): {address: string, secret: string}`
 | 
			
		||||
`generateAddress(options?: object): {address: string, secret: string}`
 | 
			
		||||
 | 
			
		||||
Deprecated: This method returns a classic address. If you do not need the classic address, use `generateXAddress` instead.
 | 
			
		||||
 | 
			
		||||
Generate a new XRP Ledger address and corresponding secret.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								docs/src/generateXAddress.md.ejs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								docs/src/generateXAddress.md.ejs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
## generateXAddress
 | 
			
		||||
 | 
			
		||||
`generateXAddress(options?: object): {address: string, secret: string}`
 | 
			
		||||
 | 
			
		||||
Generate a new XRP Ledger address and corresponding secret.
 | 
			
		||||
 | 
			
		||||
### Parameters
 | 
			
		||||
 | 
			
		||||
<%- renderSchema('input/generate-x-address.json') %>
 | 
			
		||||
 | 
			
		||||
### Return Value
 | 
			
		||||
 | 
			
		||||
This method returns an object with the following structure:
 | 
			
		||||
 | 
			
		||||
<%- renderSchema('output/generate-x-address.json') %>
 | 
			
		||||
 | 
			
		||||
### Example
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
return api.generateAddress();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<%- renderFixture('responses/generate-x-address.json') %>
 | 
			
		||||
@@ -52,6 +52,7 @@
 | 
			
		||||
<% 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 %>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
`isValidAddress(address: string): boolean`
 | 
			
		||||
 | 
			
		||||
Checks if the specified string contains a valid address.
 | 
			
		||||
Checks if the specified string contains a valid address. X-addresses are considered valid with ripple-lib v1.4.0 and higher.
 | 
			
		||||
 | 
			
		||||
### Parameters
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ Returns the response from invoking the specified command, with the specified opt
 | 
			
		||||
 | 
			
		||||
Refer to [rippled APIs](https://ripple.com/build/rippled-apis/) for commands and options. All XRP amounts must be specified in drops. One drop is equal to 0.000001 XRP. See [Specifying Currency Amounts](https://ripple.com/build/rippled-apis/#specifying-currency-amounts).
 | 
			
		||||
 | 
			
		||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://ripple.com/build/rippled-apis/#specifying-ledgers).
 | 
			
		||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://xrpl.org/basic-data-types.html#specifying-ledgers).
 | 
			
		||||
 | 
			
		||||
### Return Value
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,8 @@ This method can sign any of [the transaction types supported by ripple-binary-co
 | 
			
		||||
 | 
			
		||||
<%- renderSchema("input/sign.json") %>
 | 
			
		||||
 | 
			
		||||
When this method is used for multisigning, the `options` parameter is required. See the multisigning example in this section for more details.
 | 
			
		||||
 | 
			
		||||
### Return Value
 | 
			
		||||
 | 
			
		||||
This method returns an object with the following structure:
 | 
			
		||||
@@ -31,3 +33,91 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
<%- renderFixture("responses/sign.json") %>
 | 
			
		||||
 | 
			
		||||
### Example (multisigning)
 | 
			
		||||
 | 
			
		||||
```javascript
 | 
			
		||||
const RippleAPI = require('ripple-lib').RippleAPI;
 | 
			
		||||
 | 
			
		||||
// jon's address will have a multi-signing setup with a quorum of 2
 | 
			
		||||
const jon = {
 | 
			
		||||
    account: 'rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj',
 | 
			
		||||
    secret: 'sh4Va7b1wQof8knHFV2sxwX12fSgK'
 | 
			
		||||
};
 | 
			
		||||
const aya = {
 | 
			
		||||
    account: 'rnrPdBjs98fFFfmRpL6hM7exT788SWQPFN',
 | 
			
		||||
    secret: 'snaMuMrXeVc2Vd4NYvHofeGNjgYoe'
 | 
			
		||||
};
 | 
			
		||||
const bran = {
 | 
			
		||||
    account: 'rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH',
 | 
			
		||||
    secret: 'shQtQ8Um5MS218yvEU3Ehy1eZQKqH'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Setup the signers list with a quorum of 2
 | 
			
		||||
const multiSignSetupTransaction = {
 | 
			
		||||
    "Flags": 0,
 | 
			
		||||
    "TransactionType": "SignerListSet",
 | 
			
		||||
    "Account": "rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj",
 | 
			
		||||
    "Fee": "120",
 | 
			
		||||
    "SignerQuorum": 2,
 | 
			
		||||
    "SignerEntries": [
 | 
			
		||||
        {
 | 
			
		||||
            "SignerEntry": {
 | 
			
		||||
                "Account": "rnrPdBjs98fFFfmRpL6hM7exT788SWQPFN",
 | 
			
		||||
                "SignerWeight": 2
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
            "SignerEntry": {
 | 
			
		||||
                "Account": "rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH",
 | 
			
		||||
                "SignerWeight": 1
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
    ]
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// a transaction which requires multi signing
 | 
			
		||||
const multiSignPaymentTransaction = {
 | 
			
		||||
    TransactionType: 'Payment',
 | 
			
		||||
    Account: 'rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj',
 | 
			
		||||
    Destination: 'rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH',
 | 
			
		||||
    Amount: '88000000'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const api = new RippleAPI({
 | 
			
		||||
    server: 'wss://s.altnet.rippletest.net:51233'
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
api.connect().then(() => {
 | 
			
		||||
    // adding the multi signing feature to jon's account
 | 
			
		||||
    api.prepareTransaction(multiSignSetupTransaction).then((prepared) => {
 | 
			
		||||
        console.log(prepared);
 | 
			
		||||
        jonSign = api.sign(prepared.txJSON, jon.secret).signedTransaction;
 | 
			
		||||
        api.submit(jonSign).then( response => {
 | 
			
		||||
            console.log(response.resultCode, response.resultMessage);
 | 
			
		||||
 | 
			
		||||
            // multi sign a transaction
 | 
			
		||||
            api.prepareTransaction(multiSignPaymentTransaction).then(prepared => {
 | 
			
		||||
                console.log(prepared);
 | 
			
		||||
 | 
			
		||||
                // Aya and Bran sign it too but with 'signAs' set to their own account
 | 
			
		||||
                let ayaSign = api.sign(prepared.txJSON, aya.secret, {'signAs': aya.account}).signedTransaction;
 | 
			
		||||
                let branSign = api.sign(prepared.txJSON, bran.secret, {'signAs': bran.account}).signedTransaction;
 | 
			
		||||
 | 
			
		||||
                // signatures are combined and submitted
 | 
			
		||||
                let combinedTx = api.combine([ayaSign, branSign]);
 | 
			
		||||
                api.submit(combinedTx.signedTransaction).then(response => {
 | 
			
		||||
                    console.log(response.tx_json.hash);
 | 
			
		||||
                    return api.disconnect();
 | 
			
		||||
                }).catch(console.error);
 | 
			
		||||
            }).catch(console.error);
 | 
			
		||||
        }).catch(console.error)
 | 
			
		||||
    }).catch(console.error);
 | 
			
		||||
}).catch(console.error);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Assuming the multisigning account was setup properly, the above example will respond with `resultCode: 'tesSUCCESS'` and the hash for the transaction.
 | 
			
		||||
If any of `{signAs: some_address}` options were missing the code will return a validation error as follow:
 | 
			
		||||
```
 | 
			
		||||
[ValidationError(txJSON is not the same for all signedTransactions)]
 | 
			
		||||
```
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "ripple-lib",
 | 
			
		||||
  "version": "1.3.4",
 | 
			
		||||
  "version": "1.4.0-b2",
 | 
			
		||||
  "license": "ISC",
 | 
			
		||||
  "description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
 | 
			
		||||
  "files": [
 | 
			
		||||
@@ -23,8 +23,8 @@
 | 
			
		||||
    "https-proxy-agent": "^3.0.0",
 | 
			
		||||
    "jsonschema": "1.2.2",
 | 
			
		||||
    "lodash": "^4.17.4",
 | 
			
		||||
    "ripple-address-codec": "^4.0.0",
 | 
			
		||||
    "lodash.isequal": "^4.5.0",
 | 
			
		||||
    "ripple-address-codec": "^3.0.4",
 | 
			
		||||
    "ripple-binary-codec": "^0.2.4",
 | 
			
		||||
    "ripple-keypairs": "^0.10.1",
 | 
			
		||||
    "ripple-lib-transactionparser": "0.8.0",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								src/api.ts
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								src/api.ts
									
									
									
									
									
								
							@@ -7,7 +7,8 @@ import {
 | 
			
		||||
  dropsToXrp,
 | 
			
		||||
  rippleTimeToISO8601,
 | 
			
		||||
  iso8601ToRippleTime,
 | 
			
		||||
  txFlags
 | 
			
		||||
  txFlags,
 | 
			
		||||
  ensureClassicAddress
 | 
			
		||||
} from './common'
 | 
			
		||||
import {
 | 
			
		||||
  connect,
 | 
			
		||||
@@ -46,8 +47,8 @@ import prepareSettings from './transaction/settings'
 | 
			
		||||
import sign from './transaction/sign'
 | 
			
		||||
import combine from './transaction/combine'
 | 
			
		||||
import submit from './transaction/submit'
 | 
			
		||||
import {generateAddressAPI} from './offline/generate-address'
 | 
			
		||||
import {deriveKeypair, deriveAddress} from './offline/derive'
 | 
			
		||||
import {generateAddressAPI, GenerateAddressOptions, GeneratedAddress} from './offline/generate-address'
 | 
			
		||||
import {deriveKeypair, deriveAddress, deriveXAddress} from './offline/derive'
 | 
			
		||||
import computeLedgerHash from './offline/ledgerhash'
 | 
			
		||||
import signPaymentChannelClaim from './offline/sign-payment-channel-claim'
 | 
			
		||||
import verifyPaymentChannelClaim from './offline/verify-payment-channel-claim'
 | 
			
		||||
@@ -74,6 +75,7 @@ import {getServerInfo, getFee} from './common/serverinfo'
 | 
			
		||||
import {clamp, renameCounterpartyToIssuer} from './ledger/utils'
 | 
			
		||||
import {TransactionJSON, Instructions, Prepare} from './transaction/types'
 | 
			
		||||
import {ConnectionOptions} from './common/connection'
 | 
			
		||||
import {isValidXAddress, isValidClassicAddress} from 'ripple-address-codec'
 | 
			
		||||
 | 
			
		||||
export interface APIOptions extends ConnectionOptions {
 | 
			
		||||
  server?: string,
 | 
			
		||||
@@ -175,7 +177,8 @@ class RippleAPI extends EventEmitter {
 | 
			
		||||
  async request(command: string, params: any = {}): Promise<any> {
 | 
			
		||||
    return this.connection.request({
 | 
			
		||||
      ...params,
 | 
			
		||||
      command
 | 
			
		||||
      command,
 | 
			
		||||
      account: params.account ? ensureClassicAddress(params.account) : undefined
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -288,6 +291,15 @@ class RippleAPI extends EventEmitter {
 | 
			
		||||
    return results
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // @deprecated Use X-addresses instead
 | 
			
		||||
  generateAddress(options: GenerateAddressOptions = {}): GeneratedAddress {
 | 
			
		||||
    return generateAddressAPI({...options, includeClassicAddress: true})
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  generateXAddress(options: GenerateAddressOptions = {}): GeneratedAddress {
 | 
			
		||||
    return generateAddressAPI(options)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  connect = connect
 | 
			
		||||
  disconnect = disconnect
 | 
			
		||||
  isConnected = isConnected
 | 
			
		||||
@@ -328,7 +340,6 @@ class RippleAPI extends EventEmitter {
 | 
			
		||||
  combine = combine
 | 
			
		||||
  submit = submit
 | 
			
		||||
 | 
			
		||||
  generateAddress = generateAddressAPI
 | 
			
		||||
  deriveKeypair = deriveKeypair
 | 
			
		||||
  deriveAddress = deriveAddress
 | 
			
		||||
  computeLedgerHash = computeLedgerHash
 | 
			
		||||
@@ -336,6 +347,14 @@ class RippleAPI extends EventEmitter {
 | 
			
		||||
  verifyPaymentChannelClaim = verifyPaymentChannelClaim
 | 
			
		||||
  errors = errors
 | 
			
		||||
 | 
			
		||||
  static deriveXAddress = deriveXAddress
 | 
			
		||||
 | 
			
		||||
  // RippleAPI.deriveClassicAddress (static) is a new name for api.deriveAddress
 | 
			
		||||
  static deriveClassicAddress = deriveAddress
 | 
			
		||||
 | 
			
		||||
  static isValidXAddress = isValidXAddress
 | 
			
		||||
  static isValidClassicAddress = isValidClassicAddress
 | 
			
		||||
 | 
			
		||||
  xrpToDrops = xrpToDrops
 | 
			
		||||
  dropsToXrp = dropsToXrp
 | 
			
		||||
  rippleTimeToISO8601 = rippleTimeToISO8601
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import BigNumber from 'bignumber.js'
 | 
			
		||||
import {decodeAddress} from 'ripple-address-codec'
 | 
			
		||||
import {decodeAccountID} from 'ripple-address-codec'
 | 
			
		||||
import sha512Half from './sha512Half'
 | 
			
		||||
import HashPrefix from './hash-prefix'
 | 
			
		||||
import {SHAMap, NodeType} from './shamap'
 | 
			
		||||
@@ -28,12 +28,12 @@ const ledgerSpaceHex = (name: string): string => {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const addressToHex = (address: string): string => {
 | 
			
		||||
  return (Buffer.from(decodeAddress(address))).toString('hex')
 | 
			
		||||
  return (Buffer.from(decodeAccountID(address))).toString('hex')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const currencyToHex = (currency: string): string => {
 | 
			
		||||
  if (currency.length === 3) {
 | 
			
		||||
    let bytes = new Array(20 + 1).join('0').split('').map(parseFloat)
 | 
			
		||||
    const bytes = new Array(20 + 1).join('0').split('').map(parseFloat)
 | 
			
		||||
    bytes[12] = currency.charCodeAt(0) & 0xff
 | 
			
		||||
    bytes[13] = currency.charCodeAt(1) & 0xff
 | 
			
		||||
    bytes[14] = currency.charCodeAt(2) & 0xff
 | 
			
		||||
@@ -81,7 +81,7 @@ export const computeAccountHash = (address: string): string => {
 | 
			
		||||
export const computeSignerListHash = (address: string): string => {
 | 
			
		||||
  return sha512Half(ledgerSpaceHex('signerList') +
 | 
			
		||||
              addressToHex(address) +
 | 
			
		||||
              '00000000' /* uint32(0) signer list index */)
 | 
			
		||||
              '00000000') // uint32(0) signer list index
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const computeOrderHash = (address: string, sequence: number): string => {
 | 
			
		||||
@@ -106,7 +106,7 @@ export const computeTrustlineHash = (address1: string, address2: string, currenc
 | 
			
		||||
export const computeTransactionTreeHash = (transactions: any[]): string => {
 | 
			
		||||
  const shamap = new SHAMap()
 | 
			
		||||
 | 
			
		||||
  transactions.forEach((txJSON) => {
 | 
			
		||||
  transactions.forEach(txJSON => {
 | 
			
		||||
    const txBlobHex = encode(txJSON)
 | 
			
		||||
    const metaHex = encode(txJSON.metaData)
 | 
			
		||||
    const txHash = computeBinaryTransactionHash(txBlobHex)
 | 
			
		||||
@@ -120,7 +120,7 @@ export const computeTransactionTreeHash = (transactions: any[]): string => {
 | 
			
		||||
export const computeStateTreeHash = (entries: any[]): string => {
 | 
			
		||||
  const shamap = new SHAMap()
 | 
			
		||||
 | 
			
		||||
  entries.forEach((ledgerEntry) => {
 | 
			
		||||
  entries.forEach(ledgerEntry => {
 | 
			
		||||
    const data = encode(ledgerEntry)
 | 
			
		||||
    shamap.addItem(ledgerEntry.index, data, NodeType.ACCOUNT_STATE)
 | 
			
		||||
  })
 | 
			
		||||
 
 | 
			
		||||
@@ -2,13 +2,35 @@ import * as constants from './constants'
 | 
			
		||||
import * as errors from './errors'
 | 
			
		||||
import * as validate from './validate'
 | 
			
		||||
import * as serverInfo from './serverinfo'
 | 
			
		||||
import {xAddressToClassicAddress, isValidXAddress} from 'ripple-address-codec'
 | 
			
		||||
 | 
			
		||||
export function ensureClassicAddress(account: string): string {
 | 
			
		||||
  if (isValidXAddress(account)) {
 | 
			
		||||
    const {
 | 
			
		||||
      classicAddress,
 | 
			
		||||
      tag
 | 
			
		||||
    } = xAddressToClassicAddress(account)
 | 
			
		||||
 | 
			
		||||
    // Except for special cases, X-addresses used for requests
 | 
			
		||||
    // must not have an embedded tag. In other words,
 | 
			
		||||
    // `tag` should be `false`.
 | 
			
		||||
    if (tag !== false) {
 | 
			
		||||
      throw new Error('This command does not support the use of a tag. Use an address without a tag.')
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // For rippled requests that use an account, always use a classic address.
 | 
			
		||||
    return classicAddress
 | 
			
		||||
  } else {
 | 
			
		||||
    return account
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  constants,
 | 
			
		||||
  errors,
 | 
			
		||||
  validate,
 | 
			
		||||
  serverInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  dropsToXrp,
 | 
			
		||||
  xrpToDrops,
 | 
			
		||||
@@ -20,4 +42,3 @@ export {
 | 
			
		||||
} from './utils'
 | 
			
		||||
export {default as Connection} from './connection'
 | 
			
		||||
export {txFlags} from './txflags'
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import * as _ from 'lodash'
 | 
			
		||||
import * as assert from 'assert'
 | 
			
		||||
const {Validator} = require('jsonschema')
 | 
			
		||||
import {ValidationError} from './errors'
 | 
			
		||||
import {isValidAddress} from 'ripple-address-codec'
 | 
			
		||||
import {isValidClassicAddress, isValidXAddress} from 'ripple-address-codec'
 | 
			
		||||
import {isValidSecret} from './utils'
 | 
			
		||||
 | 
			
		||||
function loadSchemas() {
 | 
			
		||||
@@ -34,6 +34,8 @@ function loadSchemas() {
 | 
			
		||||
    require('./schemas/objects/destination-address-tag.json'),
 | 
			
		||||
    require('./schemas/objects/transaction-hash.json'),
 | 
			
		||||
    require('./schemas/objects/address.json'),
 | 
			
		||||
    require('./schemas/objects/x-address.json'),
 | 
			
		||||
    require('./schemas/objects/classic-address.json'),
 | 
			
		||||
    require('./schemas/objects/adjustment.json'),
 | 
			
		||||
    require('./schemas/objects/quality.json'),
 | 
			
		||||
    require('./schemas/objects/amount.json'),
 | 
			
		||||
@@ -126,12 +128,23 @@ function loadSchemas() {
 | 
			
		||||
  // Register custom format validators that ignore undefined instances
 | 
			
		||||
  // since jsonschema will still call the format validator on a missing
 | 
			
		||||
  // (optional)  property
 | 
			
		||||
  validator.customFormats.address = function(instance) {
 | 
			
		||||
 | 
			
		||||
  // This relies on "format": "xAddress" in `x-address.json`!
 | 
			
		||||
  validator.customFormats.xAddress = function(instance) {
 | 
			
		||||
    if (instance === undefined) {
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return isValidXAddress(instance)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // This relies on "format": "classicAddress" in `classic-address.json`!
 | 
			
		||||
  validator.customFormats.classicAddress = function(instance) {
 | 
			
		||||
    if (instance === undefined) {
 | 
			
		||||
      return true
 | 
			
		||||
    }
 | 
			
		||||
    return isValidAddress(instance)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  validator.customFormats.secret = function(instance) {
 | 
			
		||||
    if (instance === undefined) {
 | 
			
		||||
      return true
 | 
			
		||||
@@ -158,6 +171,10 @@ function schemaValidate(schemaName: string, object: any): void {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function isValidAddress(address: string): boolean {
 | 
			
		||||
  return isValidXAddress(address) || isValidClassicAddress(address)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  schemaValidate,
 | 
			
		||||
  isValidSecret,
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,14 @@
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "enum": ["ecdsa-secp256k1", "ed25519"],
 | 
			
		||||
          "description": "The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`."
 | 
			
		||||
        },
 | 
			
		||||
        "test": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "description": "Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`."
 | 
			
		||||
        },
 | 
			
		||||
        "includeClassicAddress": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "description": "If `true`, return the classic address, in addition to the X-address."
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "additionalProperties": false
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										33
									
								
								src/common/schemas/input/generate-x-address.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/common/schemas/input/generate-x-address.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "http://json-schema.org/draft-04/schema#",
 | 
			
		||||
  "title": "generateXAddressParameters",
 | 
			
		||||
  "type": "object",
 | 
			
		||||
  "properties": {
 | 
			
		||||
    "options": {
 | 
			
		||||
      "type": "object",
 | 
			
		||||
      "description": "Options to control how the address and secret are generated.",
 | 
			
		||||
      "properties": {
 | 
			
		||||
        "entropy": {
 | 
			
		||||
          "type": "array",
 | 
			
		||||
          "items": {
 | 
			
		||||
            "type": "integer",
 | 
			
		||||
            "minimum": 0,
 | 
			
		||||
            "maximum": 255
 | 
			
		||||
          },
 | 
			
		||||
          "description": "The entropy to use to generate the seed."
 | 
			
		||||
        },
 | 
			
		||||
        "algorithm": {
 | 
			
		||||
          "type": "string",
 | 
			
		||||
          "enum": ["ecdsa-secp256k1", "ed25519"],
 | 
			
		||||
          "description": "The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`."
 | 
			
		||||
        },
 | 
			
		||||
        "test": {
 | 
			
		||||
          "type": "boolean",
 | 
			
		||||
          "description": "Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`."
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "additionalProperties": false
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "additionalProperties": false
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "http://json-schema.org/draft-04/schema#",
 | 
			
		||||
  "title": "address",
 | 
			
		||||
  "description": "A Ripple account address",
 | 
			
		||||
  "description": "An account address on the XRP Ledger",
 | 
			
		||||
  "type": "string",
 | 
			
		||||
  "format": "address",
 | 
			
		||||
  "link": "address",
 | 
			
		||||
  "pattern": "^r[1-9A-HJ-NP-Za-km-z]{25,34}$"
 | 
			
		||||
  "oneOf": [
 | 
			
		||||
    {"$ref": "xAddress"},
 | 
			
		||||
    {"$ref": "classicAddress"}
 | 
			
		||||
  ]
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								src/common/schemas/objects/classic-address.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/common/schemas/objects/classic-address.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "http://json-schema.org/draft-04/schema#",
 | 
			
		||||
  "title": "classicAddress",
 | 
			
		||||
  "description": "A classic address (Account ID) for the XRP Ledger",
 | 
			
		||||
  "type": "string",
 | 
			
		||||
  "format": "classicAddress",
 | 
			
		||||
  "link": "classic-address",
 | 
			
		||||
  "pattern": "^r[1-9A-HJ-NP-Za-km-z]{24,34}$"
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "http://json-schema.org/draft-04/schema#",
 | 
			
		||||
  "title": "tag",
 | 
			
		||||
  "description": "An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.",
 | 
			
		||||
  "description": "An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.",
 | 
			
		||||
  "type": "integer",
 | 
			
		||||
  "$ref": "uint32"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								src/common/schemas/objects/x-address.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/common/schemas/objects/x-address.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "http://json-schema.org/draft-04/schema#",
 | 
			
		||||
  "title": "xAddress",
 | 
			
		||||
  "description": "An XRP Ledger address in X-address format",
 | 
			
		||||
  "type": "string",
 | 
			
		||||
  "format": "xAddress",
 | 
			
		||||
  "link": "x-address",
 | 
			
		||||
  "pattern": "^[XT][1-9A-HJ-NP-Za-km-z]{46}$"
 | 
			
		||||
}
 | 
			
		||||
@@ -3,16 +3,24 @@
 | 
			
		||||
  "title": "generateAddress",
 | 
			
		||||
  "type": "object",
 | 
			
		||||
  "properties": {
 | 
			
		||||
    "xAddress": {
 | 
			
		||||
      "$ref": "xAddress",
 | 
			
		||||
      "description": "A randomly generated XRP Ledger address in X-address format."
 | 
			
		||||
    },
 | 
			
		||||
    "classicAddress": {
 | 
			
		||||
      "$ref": "classicAddress",
 | 
			
		||||
      "description": "A randomly generated XRP Ledger Account ID (classic address)."
 | 
			
		||||
    },
 | 
			
		||||
    "address": {
 | 
			
		||||
      "$ref": "address",
 | 
			
		||||
      "description": "A randomly generated Ripple account address."
 | 
			
		||||
      "$ref": "classicAddress",
 | 
			
		||||
      "description": "Deprecated: Use `classicAddress` instead."
 | 
			
		||||
    },
 | 
			
		||||
    "secret": {
 | 
			
		||||
      "type": "string",
 | 
			
		||||
      "format": "secret",
 | 
			
		||||
      "description": "The secret corresponding to the `address`."
 | 
			
		||||
      "description": "The secret corresponding to the address."
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "required": ["address", "secret"],
 | 
			
		||||
  "required": ["xAddress", "classicAddress", "address", "secret"],
 | 
			
		||||
  "additionalProperties": false
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								src/common/schemas/output/generate-x-address.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/common/schemas/output/generate-x-address.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
{
 | 
			
		||||
  "$schema": "http://json-schema.org/draft-04/schema#",
 | 
			
		||||
  "title": "generateXAddress",
 | 
			
		||||
  "type": "object",
 | 
			
		||||
  "properties": {
 | 
			
		||||
    "xAddress": {
 | 
			
		||||
      "$ref": "xAddress",
 | 
			
		||||
      "description": "A randomly generated XRP Ledger address in X-address format."
 | 
			
		||||
    },
 | 
			
		||||
    "secret": {
 | 
			
		||||
      "type": "string",
 | 
			
		||||
      "format": "secret",
 | 
			
		||||
      "description": "The secret corresponding to the address."
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  "required": ["xAddress", "secret"],
 | 
			
		||||
  "additionalProperties": false
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
import {validate, removeUndefined, dropsToXrp} from '../common'
 | 
			
		||||
import {validate, removeUndefined, dropsToXrp, ensureClassicAddress} from '../common'
 | 
			
		||||
import {RippleAPI} from '..'
 | 
			
		||||
import {AccountInfoResponse} from '../common/types/commands/account_info'
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +34,11 @@ export default async function getAccountInfo(
 | 
			
		||||
): Promise<FormattedGetAccountInfoResponse> {
 | 
			
		||||
  // 1. Validate
 | 
			
		||||
  validate.getAccountInfo({address, options})
 | 
			
		||||
 | 
			
		||||
  // Only support retrieving account info without a tag,
 | 
			
		||||
  // since account info is not distinguished by tag.
 | 
			
		||||
  address = ensureClassicAddress(address)
 | 
			
		||||
 | 
			
		||||
  // 2. Make Request
 | 
			
		||||
  const response = await this.request('account_info', {
 | 
			
		||||
    account: address,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
import * as utils from './utils'
 | 
			
		||||
import {validate} from '../common'
 | 
			
		||||
import {validate, ensureClassicAddress} from '../common'
 | 
			
		||||
import {Connection} from '../common'
 | 
			
		||||
import {GetTrustlinesOptions} from './trustlines'
 | 
			
		||||
import {FormattedTrustline} from '../common/types/objects/trustlines'
 | 
			
		||||
import {RippleAPI} from '..'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export type Balance = {
 | 
			
		||||
  value: string,
 | 
			
		||||
  currency: string,
 | 
			
		||||
@@ -52,6 +51,13 @@ function getBalances(this: RippleAPI, address: string, options: GetTrustlinesOpt
 | 
			
		||||
): Promise<GetBalances> {
 | 
			
		||||
  validate.getTrustlines({address, options})
 | 
			
		||||
 | 
			
		||||
  // Only support retrieving balances without a tag,
 | 
			
		||||
  // since we currently do not calculate balances
 | 
			
		||||
  // on a per-tag basis. Apps must interpret and
 | 
			
		||||
  // use tags independent of the XRP Ledger, comparing
 | 
			
		||||
  // with the XRP Ledger's balance as an accounting check.
 | 
			
		||||
  address = ensureClassicAddress(address)
 | 
			
		||||
 | 
			
		||||
  return Promise.all([
 | 
			
		||||
    getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
 | 
			
		||||
      ledgerVersion =>
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,6 @@ export default async function getOrders(
 | 
			
		||||
    ledger_index: options.ledgerVersion || await this.getLedgerVersion(),
 | 
			
		||||
    limit: options.limit
 | 
			
		||||
  })
 | 
			
		||||
  // 3. Return Formatted Response
 | 
			
		||||
  // 3. Return Formatted Response, from the perspective of `address`
 | 
			
		||||
  return formatResponse(address, responses)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
import * as _ from 'lodash'
 | 
			
		||||
import parseFields from './parse/fields'
 | 
			
		||||
import {validate, constants} from '../common'
 | 
			
		||||
import {validate, constants, ensureClassicAddress} from '../common'
 | 
			
		||||
import {FormattedSettings} from '../common/types/objects'
 | 
			
		||||
import {AccountInfoResponse} from '../common/types/commands'
 | 
			
		||||
import {RippleAPI} from '..'
 | 
			
		||||
 | 
			
		||||
const AccountFlags = constants.AccountFlags
 | 
			
		||||
 | 
			
		||||
export type SettingsOptions = {
 | 
			
		||||
@@ -38,6 +39,11 @@ export async function getSettings(
 | 
			
		||||
): Promise<FormattedSettings> {
 | 
			
		||||
  // 1. Validate
 | 
			
		||||
  validate.getSettings({address, options})
 | 
			
		||||
 | 
			
		||||
  // Only support retrieving settings without a tag,
 | 
			
		||||
  // since settings do not distinguish by tag.
 | 
			
		||||
  address = ensureClassicAddress(address)
 | 
			
		||||
 | 
			
		||||
  // 2. Make Request
 | 
			
		||||
  const response = await this.request('account_info', {
 | 
			
		||||
    account: address,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,10 @@ import {computeTransactionHash} from '../common/hashes'
 | 
			
		||||
import * as utils from './utils'
 | 
			
		||||
import parseTransaction from './parse/transaction'
 | 
			
		||||
import getTransaction from './transaction'
 | 
			
		||||
import {validate, errors, Connection} from '../common'
 | 
			
		||||
import {validate, errors, Connection, ensureClassicAddress} from '../common'
 | 
			
		||||
import {FormattedTransactionType} from '../transaction/types'
 | 
			
		||||
import {RippleAPI} from '..'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export type TransactionsOptions = {
 | 
			
		||||
  start?: string,
 | 
			
		||||
  limit?: number,
 | 
			
		||||
@@ -167,6 +166,12 @@ function getTransactions(this: RippleAPI, address: string, options: Transactions
 | 
			
		||||
): Promise<GetTransactionsResponse> {
 | 
			
		||||
  validate.getTransactions({address, options})
 | 
			
		||||
 | 
			
		||||
  // Only support retrieving transactions without a tag,
 | 
			
		||||
  // since we currently do not filter transactions based
 | 
			
		||||
  // on tag. Apps must interpret and use tags
 | 
			
		||||
  // independently, filtering transactions if needed.
 | 
			
		||||
  address = ensureClassicAddress(address)
 | 
			
		||||
 | 
			
		||||
  const defaults = {maxLedgerVersion: -1}
 | 
			
		||||
  if (options.start) {
 | 
			
		||||
    return getTransaction.call(this, options.start).then(tx => {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import * as _ from 'lodash'
 | 
			
		||||
import {validate} from '../common'
 | 
			
		||||
import {validate, ensureClassicAddress} from '../common'
 | 
			
		||||
import parseAccountTrustline from './parse/account-trustline'
 | 
			
		||||
import {RippleAPI} from '..'
 | 
			
		||||
import {FormattedTrustline} from '../common/types/objects/trustlines'
 | 
			
		||||
@@ -20,11 +20,16 @@ async function getTrustlines(
 | 
			
		||||
): Promise<FormattedTrustline[]> {
 | 
			
		||||
  // 1. Validate
 | 
			
		||||
  validate.getTrustlines({address, options})
 | 
			
		||||
  const ledgerVersion = await this.getLedgerVersion()
 | 
			
		||||
 | 
			
		||||
  // Only support retrieving trustlines without a tag,
 | 
			
		||||
  // since it does not make sense to filter trustlines
 | 
			
		||||
  // by tag.
 | 
			
		||||
  address = ensureClassicAddress(address)
 | 
			
		||||
 | 
			
		||||
  // 2. Make Request
 | 
			
		||||
  const responses = await this._requestAll('account_lines', {
 | 
			
		||||
    account: address,
 | 
			
		||||
    ledger_index: ledgerVersion,
 | 
			
		||||
    ledger_index: await this.getLedgerVersion(),
 | 
			
		||||
    limit: options.limit,
 | 
			
		||||
    peer: options.counterparty
 | 
			
		||||
  })
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,13 @@
 | 
			
		||||
import {deriveKeypair, deriveAddress} from 'ripple-keypairs'
 | 
			
		||||
import {classicAddressToXAddress} from 'ripple-address-codec'
 | 
			
		||||
 | 
			
		||||
function deriveXAddress(options: {publicKey: string, tag: number | false, test: boolean}): string {
 | 
			
		||||
  const classicAddress = deriveAddress(options.publicKey)
 | 
			
		||||
  return classicAddressToXAddress(classicAddress, options.tag, options.test)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export {
 | 
			
		||||
  deriveKeypair,
 | 
			
		||||
  deriveAddress
 | 
			
		||||
  deriveAddress,
 | 
			
		||||
  deriveXAddress
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,45 @@
 | 
			
		||||
import {classicAddressToXAddress} from 'ripple-address-codec'
 | 
			
		||||
import keypairs from 'ripple-keypairs'
 | 
			
		||||
import * as common from '../common'
 | 
			
		||||
const {errors, validate} = common
 | 
			
		||||
import {errors, validate} from '../common'
 | 
			
		||||
 | 
			
		||||
export type GeneratedAddress = {
 | 
			
		||||
  secret: string,
 | 
			
		||||
  address: string
 | 
			
		||||
  xAddress: string,
 | 
			
		||||
  classicAddress?: string,
 | 
			
		||||
  address?: string, // @deprecated Use `classicAddress` instead.
 | 
			
		||||
  secret: string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateAddressAPI(options?: any): GeneratedAddress {
 | 
			
		||||
export interface GenerateAddressOptions {
 | 
			
		||||
  // The entropy to use to generate the seed.
 | 
			
		||||
  entropy?: Uint8Array,
 | 
			
		||||
 | 
			
		||||
  // The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
 | 
			
		||||
  algorithm?: 'ecdsa-secp256k1' | 'ed25519',
 | 
			
		||||
 | 
			
		||||
  // Specifies whether the address is intended for use on a test network such as Testnet or Devnet.
 | 
			
		||||
  // If `true`, the address should only be used for testing, and will start with `T`.
 | 
			
		||||
  // If `false` (default), the address should only be used on mainnet, and will start with `X`.
 | 
			
		||||
  test?: boolean,
 | 
			
		||||
 | 
			
		||||
  // If `true`, return the classic address, in addition to the X-address.
 | 
			
		||||
  includeClassicAddress?: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateAddressAPI(options: GenerateAddressOptions): GeneratedAddress {
 | 
			
		||||
  validate.generateAddress({options})
 | 
			
		||||
  try {
 | 
			
		||||
    const secret = keypairs.generateSeed(options)
 | 
			
		||||
    const keypair = keypairs.deriveKeypair(secret)
 | 
			
		||||
    const address = keypairs.deriveAddress(keypair.publicKey)
 | 
			
		||||
    return {secret, address}
 | 
			
		||||
    const classicAddress = keypairs.deriveAddress(keypair.publicKey)
 | 
			
		||||
    const returnValue: any = {
 | 
			
		||||
      xAddress: classicAddressToXAddress(classicAddress, false, options && options.test),
 | 
			
		||||
      secret
 | 
			
		||||
    }
 | 
			
		||||
    if (options.includeClassicAddress) {
 | 
			
		||||
      returnValue.classicAddress = classicAddress
 | 
			
		||||
      returnValue.address = classicAddress
 | 
			
		||||
    }
 | 
			
		||||
    return returnValue
 | 
			
		||||
  } catch (error) {
 | 
			
		||||
    throw new errors.UnexpectedError(error.message)
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,12 @@ import * as _ from 'lodash'
 | 
			
		||||
import binary from 'ripple-binary-codec'
 | 
			
		||||
import * as utils from './utils'
 | 
			
		||||
import BigNumber from 'bignumber.js'
 | 
			
		||||
import {decodeAddress} from 'ripple-address-codec'
 | 
			
		||||
import {decodeAccountID} from 'ripple-address-codec'
 | 
			
		||||
import {validate} from '../common'
 | 
			
		||||
import {computeBinaryTransactionHash} from '../common/hashes'
 | 
			
		||||
 | 
			
		||||
function addressToBigNumber(address) {
 | 
			
		||||
  const hex = (Buffer.from(decodeAddress(address))).toString('hex')
 | 
			
		||||
  const hex = (Buffer.from(decodeAccountID(address))).toString('hex')
 | 
			
		||||
  return new BigNumber(hex, 16)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,7 @@ import {Amount, Adjustment, MaxAdjustment,
 | 
			
		||||
  MinAdjustment, Memo} from '../common/types/objects'
 | 
			
		||||
import {xrpToDrops} from '../common'
 | 
			
		||||
import {RippleAPI} from '..'
 | 
			
		||||
import {getClassicAccountAndTag, ClassicAccountAndTag} from './utils'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
export interface Payment {
 | 
			
		||||
@@ -84,15 +85,49 @@ function createMaximalAmount(amount: Amount): Amount {
 | 
			
		||||
  return _.assign({}, amount, {value: maxValue})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given an address and tag:
 | 
			
		||||
 * 1. Get the classic account and tag;
 | 
			
		||||
 * 2. If a tag is provided:
 | 
			
		||||
 *    2a. If the address was an X-address, validate that the X-address has the expected tag;
 | 
			
		||||
 *    2b. If the address was a classic address, return `expectedTag` as the tag.
 | 
			
		||||
 * 3. If we do not want to use a tag in this case,
 | 
			
		||||
 *    set the tag in the return value to `undefined`.
 | 
			
		||||
 *
 | 
			
		||||
 * @param address The address to parse.
 | 
			
		||||
 * @param expectedTag If provided, and the `Account` is an X-address,
 | 
			
		||||
 *                    this method throws an error if `expectedTag`
 | 
			
		||||
 *                    does not match the tag of the X-address.
 | 
			
		||||
 * @returns {ClassicAccountAndTag}
 | 
			
		||||
 *          The classic account and tag.
 | 
			
		||||
 */
 | 
			
		||||
function validateAndNormalizeAddress(address: string, expectedTag: number | undefined): ClassicAccountAndTag {
 | 
			
		||||
  const classicAddress = getClassicAccountAndTag(address, expectedTag)
 | 
			
		||||
  classicAddress.tag = classicAddress.tag === false ? undefined : classicAddress.tag
 | 
			
		||||
  return classicAddress
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function createPaymentTransaction(address: string, paymentArgument: Payment
 | 
			
		||||
): TransactionJSON {
 | 
			
		||||
  const payment = _.cloneDeep(paymentArgument)
 | 
			
		||||
  applyAnyCounterpartyEncoding(payment)
 | 
			
		||||
 | 
			
		||||
  if (address !== payment.source.address) {
 | 
			
		||||
  const sourceAddressAndTag = validateAndNormalizeAddress(payment.source.address, payment.source.tag)
 | 
			
		||||
  const addressToVerifyAgainst = validateAndNormalizeAddress(address, undefined)
 | 
			
		||||
 | 
			
		||||
  if (addressToVerifyAgainst.classicAccount !== sourceAddressAndTag.classicAccount) {
 | 
			
		||||
    throw new ValidationError('address must match payment.source.address')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (addressToVerifyAgainst.tag !== undefined &&
 | 
			
		||||
      sourceAddressAndTag.tag !== undefined &&
 | 
			
		||||
      addressToVerifyAgainst.tag !== sourceAddressAndTag.tag) {
 | 
			
		||||
    throw new ValidationError(
 | 
			
		||||
      'address includes a tag that does not match payment.source.tag')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const destinationAddressAndTag = validateAndNormalizeAddress(payment.destination.address, payment.destination.tag)
 | 
			
		||||
 | 
			
		||||
  if (
 | 
			
		||||
    (isMaxAdjustment(payment.source) && isMinAdjustment(payment.destination))
 | 
			
		||||
    ||
 | 
			
		||||
@@ -119,8 +154,8 @@ function createPaymentTransaction(address: string, paymentArgument: Payment
 | 
			
		||||
 | 
			
		||||
  const txJSON: any = {
 | 
			
		||||
    TransactionType: 'Payment',
 | 
			
		||||
    Account: payment.source.address,
 | 
			
		||||
    Destination: payment.destination.address,
 | 
			
		||||
    Account: sourceAddressAndTag.classicAccount,
 | 
			
		||||
    Destination: destinationAddressAndTag.classicAccount,
 | 
			
		||||
    Amount: toRippledAmount(amount),
 | 
			
		||||
    Flags: 0
 | 
			
		||||
  }
 | 
			
		||||
@@ -128,11 +163,11 @@ function createPaymentTransaction(address: string, paymentArgument: Payment
 | 
			
		||||
  if (payment.invoiceID !== undefined) {
 | 
			
		||||
    txJSON.InvoiceID = payment.invoiceID
 | 
			
		||||
  }
 | 
			
		||||
  if (payment.source.tag !== undefined) {
 | 
			
		||||
    txJSON.SourceTag = payment.source.tag
 | 
			
		||||
  if (sourceAddressAndTag.tag !== undefined) {
 | 
			
		||||
    txJSON.SourceTag = sourceAddressAndTag.tag
 | 
			
		||||
  }
 | 
			
		||||
  if (payment.destination.tag !== undefined) {
 | 
			
		||||
    txJSON.DestinationTag = payment.destination.tag
 | 
			
		||||
  if (destinationAddressAndTag.tag !== undefined) {
 | 
			
		||||
    txJSON.DestinationTag = destinationAddressAndTag.tag
 | 
			
		||||
  }
 | 
			
		||||
  if (payment.memos !== undefined) {
 | 
			
		||||
    txJSON.Memos = _.map(payment.memos, utils.convertMemo)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,13 @@
 | 
			
		||||
import BigNumber from 'bignumber.js'
 | 
			
		||||
import * as common from '../common'
 | 
			
		||||
import {Memo, RippledAmount} from '../common/types/objects'
 | 
			
		||||
const txFlags = common.txFlags
 | 
			
		||||
import {Instructions, Prepare} from './types'
 | 
			
		||||
import {RippleAPI} from '..'
 | 
			
		||||
import {ValidationError} from '../common/errors'
 | 
			
		||||
import {xAddressToClassicAddress, isValidXAddress} from 'ripple-address-codec'
 | 
			
		||||
 | 
			
		||||
const txFlags = common.txFlags
 | 
			
		||||
const TRANSACTION_TYPES_WITH_DESTINATION_TAG_FIELD = ['Payment', 'CheckCreate', 'EscrowCreate', 'PaymentChannelCreate']
 | 
			
		||||
 | 
			
		||||
export type ApiMemo = {
 | 
			
		||||
  MemoData?: string,
 | 
			
		||||
@@ -56,6 +59,49 @@ function scaleValue(value, multiplier, extra = 0) {
 | 
			
		||||
  return (new BigNumber(value)).times(multiplier).plus(extra).toString()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @typedef {Object} ClassicAccountAndTag
 | 
			
		||||
 * @property {string} classicAccount - The classic account address.
 | 
			
		||||
 * @property {number | false | undefined } tag - The destination tag;
 | 
			
		||||
 *                    `false` if no tag should be used;
 | 
			
		||||
 *                    `undefined` if the input could not specify whether a tag should be used.
 | 
			
		||||
 */
 | 
			
		||||
interface ClassicAccountAndTag {
 | 
			
		||||
  classicAccount: string,
 | 
			
		||||
  tag: number | false | undefined
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Given an address (account), get the classic account and tag.
 | 
			
		||||
 * If an `expectedTag` is provided:
 | 
			
		||||
 * 1. If the `Account` is an X-address, validate that the tags match.
 | 
			
		||||
 * 2. If the `Account` is a classic address, return `expectedTag` as the tag.
 | 
			
		||||
 *
 | 
			
		||||
 * @param Account The address to parse.
 | 
			
		||||
 * @param expectedTag If provided, and the `Account` is an X-address,
 | 
			
		||||
 *                    this method throws an error if `expectedTag`
 | 
			
		||||
 *                    does not match the tag of the X-address.
 | 
			
		||||
 * @returns {ClassicAccountAndTag}
 | 
			
		||||
 *          The classic account and tag.
 | 
			
		||||
 */
 | 
			
		||||
function getClassicAccountAndTag(Account: string, expectedTag?: number): ClassicAccountAndTag {
 | 
			
		||||
  if (isValidXAddress(Account)) {
 | 
			
		||||
    const classic = xAddressToClassicAddress(Account)
 | 
			
		||||
    if (expectedTag !== undefined && classic.tag !== expectedTag) {
 | 
			
		||||
      throw new ValidationError('address includes a tag that does not match the tag specified in the transaction')
 | 
			
		||||
    }
 | 
			
		||||
    return {
 | 
			
		||||
      classicAccount: classic.classicAddress,
 | 
			
		||||
      tag: classic.tag
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    return {
 | 
			
		||||
      classicAccount: Account,
 | 
			
		||||
      tag: expectedTag
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function prepareTransaction(txJSON: TransactionJSON, api: RippleAPI,
 | 
			
		||||
  instructions: Instructions
 | 
			
		||||
): Promise<Prepare> {
 | 
			
		||||
@@ -68,56 +114,105 @@ function prepareTransaction(txJSON: TransactionJSON, api: RippleAPI,
 | 
			
		||||
      '" exists in instance when not allowed'))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // To remove the signer list, SignerEntries field should be omitted.
 | 
			
		||||
  const newTxJSON = Object.assign({}, txJSON)
 | 
			
		||||
 | 
			
		||||
  // To remove the signer list, `SignerEntries` field should be omitted.
 | 
			
		||||
  if (txJSON['SignerQuorum'] === 0) {
 | 
			
		||||
    delete txJSON.SignerEntries
 | 
			
		||||
    delete newTxJSON.SignerEntries
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const account = txJSON.Account
 | 
			
		||||
  setCanonicalFlag(txJSON)
 | 
			
		||||
  // Sender:
 | 
			
		||||
  const {classicAccount, tag: sourceTag} = getClassicAccountAndTag(txJSON.Account)
 | 
			
		||||
  newTxJSON.Account = classicAccount
 | 
			
		||||
  if (sourceTag !== undefined) {
 | 
			
		||||
    if (txJSON.SourceTag && txJSON.SourceTag !== sourceTag) {
 | 
			
		||||
      return Promise.reject(new ValidationError(
 | 
			
		||||
        'The `SourceTag`, if present, must match the tag of the `Account` X-address'))
 | 
			
		||||
    }
 | 
			
		||||
    if (sourceTag) {
 | 
			
		||||
      newTxJSON.SourceTag = sourceTag
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function prepareMaxLedgerVersion(): Promise<TransactionJSON> {
 | 
			
		||||
  // Destination:
 | 
			
		||||
  if (typeof txJSON.Destination === 'string') {
 | 
			
		||||
    const {classicAccount: destinationAccount, tag: destinationTag} = getClassicAccountAndTag(txJSON.Destination)
 | 
			
		||||
    newTxJSON.Destination = destinationAccount
 | 
			
		||||
    if (destinationTag !== undefined) {
 | 
			
		||||
      if (TRANSACTION_TYPES_WITH_DESTINATION_TAG_FIELD.includes(txJSON.TransactionType)) {
 | 
			
		||||
        if (txJSON.DestinationTag && txJSON.DestinationTag !== destinationTag) {
 | 
			
		||||
          return Promise.reject(new ValidationError(
 | 
			
		||||
            'The Payment `DestinationTag`, if present, must match the tag of the `Destination` X-address'))
 | 
			
		||||
        }
 | 
			
		||||
        if (destinationTag) {
 | 
			
		||||
          newTxJSON.DestinationTag = destinationTag
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function convertToClassicAccountIfPresent(fieldName: string): void {
 | 
			
		||||
    const account = txJSON[fieldName]
 | 
			
		||||
    if (typeof account === 'string') {
 | 
			
		||||
      const {classicAccount: ca} = getClassicAccountAndTag(account)
 | 
			
		||||
      newTxJSON[fieldName] = ca
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // DepositPreauth:
 | 
			
		||||
  convertToClassicAccountIfPresent('Authorize')
 | 
			
		||||
  convertToClassicAccountIfPresent('Unauthorize')
 | 
			
		||||
 | 
			
		||||
  // EscrowCancel, EscrowFinish:
 | 
			
		||||
  convertToClassicAccountIfPresent('Owner')
 | 
			
		||||
 | 
			
		||||
  // SetRegularKey:
 | 
			
		||||
  convertToClassicAccountIfPresent('RegularKey')
 | 
			
		||||
 | 
			
		||||
  setCanonicalFlag(newTxJSON)
 | 
			
		||||
 | 
			
		||||
  function prepareMaxLedgerVersion(): Promise<void> {
 | 
			
		||||
    // Up to one of the following is allowed:
 | 
			
		||||
    //   txJSON.LastLedgerSequence
 | 
			
		||||
    //   instructions.maxLedgerVersion
 | 
			
		||||
    //   instructions.maxLedgerVersionOffset
 | 
			
		||||
    if (txJSON.LastLedgerSequence && instructions.maxLedgerVersion) {
 | 
			
		||||
    if (newTxJSON.LastLedgerSequence && instructions.maxLedgerVersion) {
 | 
			
		||||
      return Promise.reject(new ValidationError('`LastLedgerSequence` in txJSON and `maxLedgerVersion`' +
 | 
			
		||||
        ' in `instructions` cannot both be set'))
 | 
			
		||||
    }
 | 
			
		||||
    if (txJSON.LastLedgerSequence && instructions.maxLedgerVersionOffset) {
 | 
			
		||||
    if (newTxJSON.LastLedgerSequence && instructions.maxLedgerVersionOffset) {
 | 
			
		||||
      return Promise.reject(new ValidationError('`LastLedgerSequence` in txJSON and `maxLedgerVersionOffset`' +
 | 
			
		||||
        ' in `instructions` cannot both be set'))
 | 
			
		||||
    }
 | 
			
		||||
    if (txJSON.LastLedgerSequence) {
 | 
			
		||||
      return Promise.resolve(txJSON)
 | 
			
		||||
    if (newTxJSON.LastLedgerSequence) {
 | 
			
		||||
      return Promise.resolve()
 | 
			
		||||
    }
 | 
			
		||||
    if (instructions.maxLedgerVersion !== undefined) {
 | 
			
		||||
      if (instructions.maxLedgerVersion !== null) {
 | 
			
		||||
        txJSON.LastLedgerSequence = instructions.maxLedgerVersion
 | 
			
		||||
        newTxJSON.LastLedgerSequence = instructions.maxLedgerVersion
 | 
			
		||||
      }
 | 
			
		||||
      return Promise.resolve(txJSON)
 | 
			
		||||
      return Promise.resolve()
 | 
			
		||||
    }
 | 
			
		||||
    const offset = instructions.maxLedgerVersionOffset !== undefined ?
 | 
			
		||||
      instructions.maxLedgerVersionOffset : 3
 | 
			
		||||
    return api.connection.getLedgerVersion().then(ledgerVersion => {
 | 
			
		||||
      txJSON.LastLedgerSequence = ledgerVersion + offset
 | 
			
		||||
      return txJSON
 | 
			
		||||
      newTxJSON.LastLedgerSequence = ledgerVersion + offset
 | 
			
		||||
      return
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function prepareFee(): Promise<TransactionJSON> {
 | 
			
		||||
  function prepareFee(): Promise<void> {
 | 
			
		||||
    // instructions.fee is scaled (for multi-signed transactions) while txJSON.Fee is not.
 | 
			
		||||
    // Due to this difference, we do NOT allow both to be set, as the behavior would be complex and
 | 
			
		||||
    // potentially ambiguous.
 | 
			
		||||
    // Furthermore, txJSON.Fee is in drops while instructions.fee is in XRP, which would just add to
 | 
			
		||||
    // the confusion. It is simpler to require that only one is used.
 | 
			
		||||
    if (txJSON.Fee && instructions.fee) {
 | 
			
		||||
    if (newTxJSON.Fee && instructions.fee) {
 | 
			
		||||
      return Promise.reject(new ValidationError('`Fee` in txJSON and `fee` in `instructions` cannot both be set'))
 | 
			
		||||
    }
 | 
			
		||||
    if (txJSON.Fee) {
 | 
			
		||||
    if (newTxJSON.Fee) {
 | 
			
		||||
      // txJSON.Fee is set. Use this value and do not scale it.
 | 
			
		||||
      return Promise.resolve(txJSON)
 | 
			
		||||
      return Promise.resolve()
 | 
			
		||||
    }
 | 
			
		||||
    const multiplier = instructions.signersCount === undefined ? 1 :
 | 
			
		||||
      instructions.signersCount + 1
 | 
			
		||||
@@ -128,50 +223,50 @@ function prepareTransaction(txJSON: TransactionJSON, api: RippleAPI,
 | 
			
		||||
          `max of ${api._maxFeeXRP} XRP. To use this fee, increase ` +
 | 
			
		||||
          '`maxFeeXRP` in the RippleAPI constructor.'))
 | 
			
		||||
      }
 | 
			
		||||
      txJSON.Fee = scaleValue(common.xrpToDrops(instructions.fee), multiplier)
 | 
			
		||||
      return Promise.resolve(txJSON)
 | 
			
		||||
      newTxJSON.Fee = scaleValue(common.xrpToDrops(instructions.fee), multiplier)
 | 
			
		||||
      return Promise.resolve()
 | 
			
		||||
    }
 | 
			
		||||
    const cushion = api._feeCushion
 | 
			
		||||
    return api.getFee(cushion).then(fee => {
 | 
			
		||||
      return api.connection.getFeeRef().then(feeRef => {
 | 
			
		||||
        const extraFee =
 | 
			
		||||
          (txJSON.TransactionType !== 'EscrowFinish' ||
 | 
			
		||||
            txJSON.Fulfillment === undefined) ? 0 :
 | 
			
		||||
          (newTxJSON.TransactionType !== 'EscrowFinish' ||
 | 
			
		||||
          newTxJSON.Fulfillment === undefined) ? 0 :
 | 
			
		||||
            (cushion * feeRef * (32 + Math.floor(
 | 
			
		||||
              Buffer.from(txJSON.Fulfillment, 'hex').length / 16)))
 | 
			
		||||
              Buffer.from(newTxJSON.Fulfillment, 'hex').length / 16)))
 | 
			
		||||
        const feeDrops = common.xrpToDrops(fee)
 | 
			
		||||
        const maxFeeXRP = instructions.maxFee ?
 | 
			
		||||
          BigNumber.min(api._maxFeeXRP, instructions.maxFee) : api._maxFeeXRP
 | 
			
		||||
        const maxFeeDrops = common.xrpToDrops(maxFeeXRP)
 | 
			
		||||
        const normalFee = scaleValue(feeDrops, multiplier, extraFee)
 | 
			
		||||
        txJSON.Fee = BigNumber.min(normalFee, maxFeeDrops).toString(10)
 | 
			
		||||
        newTxJSON.Fee = BigNumber.min(normalFee, maxFeeDrops).toString(10)
 | 
			
		||||
 | 
			
		||||
        return txJSON
 | 
			
		||||
        return
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async function prepareSequence(): Promise<TransactionJSON> {
 | 
			
		||||
  async function prepareSequence(): Promise<void> {
 | 
			
		||||
    if (instructions.sequence !== undefined) {
 | 
			
		||||
      if (txJSON.Sequence === undefined || instructions.sequence === txJSON.Sequence) {
 | 
			
		||||
        txJSON.Sequence = instructions.sequence
 | 
			
		||||
        return Promise.resolve(txJSON)
 | 
			
		||||
      if (newTxJSON.Sequence === undefined || instructions.sequence === newTxJSON.Sequence) {
 | 
			
		||||
        newTxJSON.Sequence = instructions.sequence
 | 
			
		||||
        return Promise.resolve()
 | 
			
		||||
      } else {
 | 
			
		||||
        // Both txJSON.Sequence and instructions.sequence are defined, and they are NOT equal
 | 
			
		||||
        return Promise.reject(new ValidationError('`Sequence` in txJSON must match `sequence` in `instructions`'))
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (txJSON.Sequence !== undefined) {
 | 
			
		||||
      return Promise.resolve(txJSON)
 | 
			
		||||
    if (newTxJSON.Sequence !== undefined) {
 | 
			
		||||
      return Promise.resolve()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      // Consider requesting from the 'current' ledger (instead of 'validated').
 | 
			
		||||
      const response = await api.request('account_info', {
 | 
			
		||||
        account
 | 
			
		||||
        account: classicAccount
 | 
			
		||||
      })
 | 
			
		||||
      txJSON.Sequence = response.account_data.Sequence
 | 
			
		||||
      return Promise.resolve(txJSON)
 | 
			
		||||
      newTxJSON.Sequence = response.account_data.Sequence
 | 
			
		||||
      return Promise.resolve()
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      return Promise.reject(e)
 | 
			
		||||
    }
 | 
			
		||||
@@ -181,7 +276,7 @@ function prepareTransaction(txJSON: TransactionJSON, api: RippleAPI,
 | 
			
		||||
    prepareMaxLedgerVersion(),
 | 
			
		||||
    prepareFee(),
 | 
			
		||||
    prepareSequence()
 | 
			
		||||
  ]).then(() => formatPrepareResponse(txJSON))
 | 
			
		||||
  ]).then(() => formatPrepareResponse(newTxJSON))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function convertStringToHex(string: string): string {
 | 
			
		||||
@@ -203,5 +298,7 @@ export {
 | 
			
		||||
  convertMemo,
 | 
			
		||||
  prepareTransaction,
 | 
			
		||||
  common,
 | 
			
		||||
  setCanonicalFlag
 | 
			
		||||
  setCanonicalFlag,
 | 
			
		||||
  getClassicAccountAndTag,
 | 
			
		||||
  ClassicAccountAndTag
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -996,7 +996,7 @@ describe('RippleAPI', function () {
 | 
			
		||||
        done(new Error('Expected method to reject. Prepared transaction: ' + JSON.stringify(response)));
 | 
			
		||||
      }).catch(err => {
 | 
			
		||||
        assert.strictEqual(err.name, 'ValidationError');
 | 
			
		||||
        assert.strictEqual(err.message, 'instance.Account is not of a type(s) string,instance.Account does not conform to the "address" format');
 | 
			
		||||
        assert.strictEqual(err.message, 'instance.Account is not of a type(s) string,instance.Account is not exactly one from <xAddress>,<classicAddress>');
 | 
			
		||||
        done();
 | 
			
		||||
      }).catch(done); // Finish test with assertion failure immediately instead of waiting for timeout.
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
@@ -1020,7 +1020,7 @@ describe('RippleAPI', function () {
 | 
			
		||||
        done(new Error('Expected method to reject. Prepared transaction: ' + JSON.stringify(response)));
 | 
			
		||||
      }).catch(err => {
 | 
			
		||||
        assert.strictEqual(err.name, 'ValidationError');
 | 
			
		||||
        assert.strictEqual(err.message, 'instance.Account does not conform to the "address" format');
 | 
			
		||||
        assert.strictEqual(err.message, 'instance.Account is not exactly one from <xAddress>,<classicAddress>');
 | 
			
		||||
        done();
 | 
			
		||||
      }).catch(done); // Finish test with assertion failure immediately instead of waiting for timeout.
 | 
			
		||||
    } catch (err) {
 | 
			
		||||
@@ -1581,7 +1581,7 @@ describe('RippleAPI', function () {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('prepareOrder - invalid', function (done) {
 | 
			
		||||
    const request = requests.prepareOrder.sell;
 | 
			
		||||
    const request = Object.assign({}, requests.prepareOrder.sell);
 | 
			
		||||
    delete request.direction; // Make invalid
 | 
			
		||||
    try {
 | 
			
		||||
      this.api.prepareOrder(address, request, instructionsWithMaxLedgerVersionOffset).then(prepared => {
 | 
			
		||||
@@ -1620,7 +1620,7 @@ describe('RippleAPI', function () {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('prepareOrderCancellation - invalid', function (done) {
 | 
			
		||||
    const request = requests.prepareOrderCancellation.withMemos;
 | 
			
		||||
    const request = Object.assign({}, requests.prepareOrderCancellation.withMemos);
 | 
			
		||||
    delete request.orderSequence; // Make invalid
 | 
			
		||||
    try {
 | 
			
		||||
      this.api.prepareOrderCancellation(address, request).then(prepared => {
 | 
			
		||||
@@ -1654,7 +1654,7 @@ describe('RippleAPI', function () {
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('prepareTrustline - invalid', function (done) {
 | 
			
		||||
    const trustline = requests.prepareTrustline.complex;
 | 
			
		||||
    const trustline = Object.assign({}, requests.prepareTrustline.complex);
 | 
			
		||||
    delete trustline.limit; // Make invalid
 | 
			
		||||
    try {
 | 
			
		||||
      this.api.prepareTrustline(
 | 
			
		||||
@@ -2958,7 +2958,7 @@ describe('RippleAPI', function () {
 | 
			
		||||
      const options = {
 | 
			
		||||
        includeRawTransaction: true
 | 
			
		||||
      }
 | 
			
		||||
      const expected = responses.getTransaction.settings
 | 
			
		||||
      const expected = Object.assign({}, responses.getTransaction.settings) // Avoid mutating test fixture
 | 
			
		||||
      expected.rawTransaction = "{\"Account\":\"rLVKsA4F9iJBbA6rX2x4wCmkj6drgtqpQe\",\"Fee\":\"10\",\"Flags\":2147483648,\"Sequence\":1,\"SetFlag\":2,\"SigningPubKey\":\"03EA3ADCA632F125EC2CC4F7F6A82DE0DCE2B65290CAC1F22242C5163F0DA9652D\",\"TransactionType\":\"AccountSet\",\"TxnSignature\":\"3045022100DE8B666B1A31EA65011B0F32130AB91A5747E32FA49B3054CEE8E8362DBAB98A022040CF0CF254677A8E5CD04C59CA2ED7F6F15F7E184641BAE169C561650967B226\",\"date\":460832270,\"hash\":\"4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B\",\"inLedger\":8206418,\"ledger_index\":8206418,\"meta\":{\"AffectedNodes\":[{\"ModifiedNode\":{\"FinalFields\":{\"Account\":\"rLVKsA4F9iJBbA6rX2x4wCmkj6drgtqpQe\",\"Balance\":\"29999990\",\"Flags\":786432,\"OwnerCount\":0,\"Sequence\":2},\"LedgerEntryType\":\"AccountRoot\",\"LedgerIndex\":\"3F5072C4875F32ED770DAF3610A716600ED7C7BB0348FADC7A98E011BB2CD36F\",\"PreviousFields\":{\"Balance\":\"30000000\",\"Flags\":4194304,\"Sequence\":1},\"PreviousTxnID\":\"3FB0350A3742BBCC0D8AA3C5247D1AEC01177D0A24D9C34762BAA2FEA8AD88B3\",\"PreviousTxnLgrSeq\":8206397}}],\"TransactionIndex\":5,\"TransactionResult\":\"tesSUCCESS\"},\"validated\":true}"
 | 
			
		||||
      return this.api.getTransaction(hash, options).then(
 | 
			
		||||
        _.partial(checkResult, expected,
 | 
			
		||||
@@ -3447,6 +3447,7 @@ describe('RippleAPI', function () {
 | 
			
		||||
      _.partial(checkResult, responses.getTrustlines.all, 'getTrustlines'));
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // @deprecated See corresponding test in `x-address-api-test.js`
 | 
			
		||||
  it('generateAddress', function () {
 | 
			
		||||
    function random() {
 | 
			
		||||
      return _.fill(Array(16), 0);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								test/fixtures/addresses.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								test/fixtures/addresses.js
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,8 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
module.exports = {
 | 
			
		||||
  ACCOUNT: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
 | 
			
		||||
  ACCOUNT_X: 'X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ',
 | 
			
		||||
  ACCOUNT_T: 'T719a5UwUCnEs54UsxG9CJYYDhwmFCqkr7wxCcNcfZ6p5GZ',
 | 
			
		||||
  OTHER_ACCOUNT: 'rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo',
 | 
			
		||||
  THIRD_ACCOUNT: 'rwBYyfufTzk77zUSKEu4MvixfarC35av1J',
 | 
			
		||||
  FOURTH_ACCOUNT: 'rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "xAddress": "XVLcsWWNiFdUEqoDmSwgxh1abfddG1LtbGFk7omPgYpbyE8",
 | 
			
		||||
  "classicAddress": "rGCkuB7PBr5tNy68tPEABEtcdno4hE6Y7f",
 | 
			
		||||
  "address": "rGCkuB7PBr5tNy68tPEABEtcdno4hE6Y7f",
 | 
			
		||||
  "secret": "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								test/fixtures/responses/generate-x-address.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								test/fixtures/responses/generate-x-address.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
  "xAddress": "XVLcsWWNiFdUEqoDmSwgxh1abfddG1LtbGFk7omPgYpbyE8",
 | 
			
		||||
  "secret": "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1
									
								
								test/fixtures/responses/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								test/fixtures/responses/index.js
									
									
									
									
										vendored
									
									
								
							@@ -5,6 +5,7 @@ function buildList(options) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module.exports = {
 | 
			
		||||
  generateXAddress: require('./generate-x-address.json'),
 | 
			
		||||
  generateAddress: require('./generate-address.json'),
 | 
			
		||||
  getAccountInfo: require('./get-account-info.json'),
 | 
			
		||||
  getAccountObjects: require('./get-account-objects.json'),
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4034
									
								
								test/x-address-api-test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4034
									
								
								test/x-address-api-test.js
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										16
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								yarn.lock
									
									
									
									
									
								
							@@ -99,6 +99,13 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
 | 
			
		||||
  integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
 | 
			
		||||
 | 
			
		||||
"@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/lodash@^4.14.136":
 | 
			
		||||
  version "4.14.144"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.144.tgz#12e57fc99064bce45e5ab3c8bc4783feb75eab8e"
 | 
			
		||||
@@ -4389,6 +4396,15 @@ ripple-address-codec@^3.0.4:
 | 
			
		||||
    base-x "3.0.4"
 | 
			
		||||
    create-hash "^1.1.2"
 | 
			
		||||
 | 
			
		||||
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==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@types/base-x" "^3.0.0"
 | 
			
		||||
    base-x "3.0.4"
 | 
			
		||||
    create-hash "^1.1.2"
 | 
			
		||||
 | 
			
		||||
ripple-binary-codec@^0.2.4:
 | 
			
		||||
  version "0.2.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/ripple-binary-codec/-/ripple-binary-codec-0.2.4.tgz#555d64c52182a215222af6045b5170e43f0530b1"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user