mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +00:00
Allow specifying amounts in drops (#892)
* Accept "drops" in lieu of "XRP" * Export xrpToDrops() and dropsToXrp() * Throw our own validation errors instead of BigNumber Errors
This commit is contained in:
12
HISTORY.md
12
HISTORY.md
@@ -2,6 +2,18 @@
|
|||||||
|
|
||||||
## 1.0.0 (UNRELEASED)
|
## 1.0.0 (UNRELEASED)
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
+ Amounts in drops (recommended) and XRP are checked for validity. Some
|
||||||
|
methods may now throw a `BigNumber Error` or `ValidationError` if the amount
|
||||||
|
is invalid. This may include methods that previously did not throw.
|
||||||
|
+ Note that 1 drop is equivalent to 0.000001 XRP and 1 XRP is equivalent to 1,000,000 drops.
|
||||||
|
|
||||||
|
### Other Changes
|
||||||
|
|
||||||
|
+ Allow specifying amounts in drops for consistency with the `rippled`
|
||||||
|
APIs.
|
||||||
|
+ Export `xrpToDrops()` and `dropsToXrp()` functions.
|
||||||
+ Potentially breaking change: Improve errors. For example, `RippledError` now includes the full response from
|
+ Potentially breaking change: Improve errors. For example, `RippledError` now includes the full response from
|
||||||
the `rippled` server ([#687](https://github.com/ripple/ripple-lib/issues/687)). `NotConnectedError`
|
the `rippled` server ([#687](https://github.com/ripple/ripple-lib/issues/687)). `NotConnectedError`
|
||||||
may be thrown with a different message than before.
|
may be thrown with a different message than before.
|
||||||
|
|||||||
@@ -221,14 +221,13 @@ Currencies are represented as either 3-character currency codes or 40-character
|
|||||||
## Value
|
## Value
|
||||||
A *value* is a quantity of a currency represented as a decimal string. Be careful: JavaScript's native number format does not have sufficient precision to represent all values. XRP has different precision from other currencies.
|
A *value* is a quantity of a currency represented as a decimal string. Be careful: JavaScript's native number format does not have sufficient precision to represent all values. XRP has different precision from other currencies.
|
||||||
|
|
||||||
**XRP** has 6 significant digits past the decimal point. In other words, XRP cannot be divided into positive values smaller than `0.000001` (1e-6). XRP has a maximum value of `100000000000` (1e11).
|
**XRP** has 6 significant digits past the decimal point. In other words, XRP cannot be divided into positive values smaller than `0.000001` (1e-6). This smallest unit is called a "drop". XRP has a maximum value of `100000000000` (1e11). Some RippleAPI methods accept XRP in order to maintain compatibility with older versions of the API. For consistency with the `rippled` APIs, we recommend formally specifying XRP values in *drops* in all API requests, and converting them to XRP for display. This is similar to Bitcoin's *satoshis* and Ethereum's *wei*. 1 XRP = 1,000,000 drops.
|
||||||
|
|
||||||
**Non-XRP values** have 16 decimal digits of precision, with a maximum value of `9999999999999999e80`. The smallest positive non-XRP value is `1e-81`.
|
**Non-XRP values** have 16 decimal digits of precision, with a maximum value of `9999999999999999e80`. The smallest positive non-XRP value is `1e-81`.
|
||||||
|
|
||||||
|
|
||||||
## Amount
|
## Amount
|
||||||
|
|
||||||
Example amount:
|
Example 100.00 USD amount:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -238,15 +237,16 @@ Example amount:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example XRP amount:
|
Example 3.0 XRP amount, in drops:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "2000"
|
"value": "3000000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
(Requires `ripple-lib` version 1.0.0 or higher.)
|
||||||
|
|
||||||
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value. For XRP, there is no counterparty.
|
An *amount* is an object specifying a currency, a quantity of that currency, and the counterparty (issuer) on the trustline that holds the value. For XRP, there is no counterparty.
|
||||||
|
|
||||||
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
||||||
|
|
||||||
@@ -256,8 +256,8 @@ A *balance* is an amount than can have a negative value.
|
|||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
---- | ---- | -----------
|
---- | ---- | -----------
|
||||||
currency | [currency](#currency) | The three-character code or hexadecimal string used to denote currencies
|
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 Ripple address of the account that owes or is owed the funds (omitted if `currency` is "XRP")
|
counterparty | [address](#address) | *Optional* The Ripple 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
|
value | [value](#value) | *Optional* The quantity of the currency, denoted as a string to retain floating point precision
|
||||||
|
|
||||||
# Transaction Overview
|
# Transaction Overview
|
||||||
@@ -323,7 +323,7 @@ maxLedgerVersionOffset | integer | *Optional* Offset from current validated ledg
|
|||||||
sequence | [sequence](#account-sequence-number) | *Optional* The initiating account's sequence number for this transaction.
|
sequence | [sequence](#account-sequence-number) | *Optional* The initiating account's sequence number for this transaction.
|
||||||
signersCount | integer | *Optional* Number of signers that will be signing this transaction.
|
signersCount | integer | *Optional* Number of signers that will be signing this transaction.
|
||||||
|
|
||||||
We recommended that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the XRP Ledger's consensus-validated ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare*" method.
|
We recommend that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the XRP Ledger's consensus-validated ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare\*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare\*" method.
|
||||||
|
|
||||||
## Transaction ID
|
## Transaction ID
|
||||||
|
|
||||||
@@ -465,10 +465,10 @@ passive | boolean | *Optional* If enabled, the offer will not consume offers tha
|
|||||||
"value": "10.1"
|
"value": "10.1"
|
||||||
},
|
},
|
||||||
"totalPrice": {
|
"totalPrice": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "2"
|
"value": "2000000"
|
||||||
},
|
},
|
||||||
"passive": true,
|
"passive": false,
|
||||||
"fillOrKill": true
|
"fillOrKill": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -632,8 +632,8 @@ invoiceID | string | *Optional* 256-bit hash, as a 64-character hexadecimal stri
|
|||||||
{
|
{
|
||||||
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
||||||
"sendMax": {
|
"sendMax": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "1"
|
"value": "1000000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -673,8 +673,8 @@ deliverMin | [laxAmount](#amount) | *Optional* Redeem the Check for at least thi
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"amount": {
|
"amount": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "1"
|
"value": "1000000"
|
||||||
},
|
},
|
||||||
"checkID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334"
|
"checkID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334"
|
||||||
}
|
}
|
||||||
@@ -4271,6 +4271,8 @@ instructions | object | The instructions for how to execute the transaction afte
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||||
|
|
||||||
|
// Buy 10.10 USD (of the specified issuer) for 2.0 XRP (2000000 drops), fill or kill.
|
||||||
const order = {
|
const order = {
|
||||||
"direction": "buy",
|
"direction": "buy",
|
||||||
"quantity": {
|
"quantity": {
|
||||||
@@ -4279,10 +4281,10 @@ const order = {
|
|||||||
"value": "10.1"
|
"value": "10.1"
|
||||||
},
|
},
|
||||||
"totalPrice": {
|
"totalPrice": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "2"
|
"value": "2000000"
|
||||||
},
|
},
|
||||||
"passive": true,
|
"passive": false,
|
||||||
"fillOrKill": true
|
"fillOrKill": true
|
||||||
};
|
};
|
||||||
return api.prepareOrder(address, order)
|
return api.prepareOrder(address, order)
|
||||||
@@ -4292,7 +4294,7 @@ return api.prepareOrder(address, order)
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"txJSON": "{\"Flags\":2147811328,\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":\"2000000\",\"TakerPays\":{\"value\":\"10.1\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8819954,\"Fee\":\"12\",\"Sequence\":23}",
|
"txJSON": "{\"Flags\":2147745792,\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":\"2000000\",\"TakerPays\":{\"value\":\"10.1\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8819954,\"Fee\":\"12\",\"Sequence\":23}",
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"fee": "0.000012",
|
"fee": "0.000012",
|
||||||
"sequence": 23,
|
"sequence": 23,
|
||||||
@@ -4798,8 +4800,8 @@ const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
|||||||
const checkCreate = {
|
const checkCreate = {
|
||||||
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
||||||
"sendMax": {
|
"sendMax": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "1"
|
"value": "1000000"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return api.prepareCheckCreate(address, checkCreate).then(prepared =>
|
return api.prepareCheckCreate(address, checkCreate).then(prepared =>
|
||||||
@@ -4911,8 +4913,8 @@ instructions | object | The instructions for how to execute the transaction afte
|
|||||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||||
const checkCash = {
|
const checkCash = {
|
||||||
"amount": {
|
"amount": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "1"
|
"value": "1000000"
|
||||||
},
|
},
|
||||||
"checkID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334"
|
"checkID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334"
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,14 +19,13 @@ Currencies are represented as either 3-character currency codes or 40-character
|
|||||||
## Value
|
## Value
|
||||||
A *value* is a quantity of a currency represented as a decimal string. Be careful: JavaScript's native number format does not have sufficient precision to represent all values. XRP has different precision from other currencies.
|
A *value* is a quantity of a currency represented as a decimal string. Be careful: JavaScript's native number format does not have sufficient precision to represent all values. XRP has different precision from other currencies.
|
||||||
|
|
||||||
**XRP** has 6 significant digits past the decimal point. In other words, XRP cannot be divided into positive values smaller than `0.000001` (1e-6). XRP has a maximum value of `100000000000` (1e11).
|
**XRP** has 6 significant digits past the decimal point. In other words, XRP cannot be divided into positive values smaller than `0.000001` (1e-6). This smallest unit is called a "drop". XRP has a maximum value of `100000000000` (1e11). Some RippleAPI methods accept XRP in order to maintain compatibility with older versions of the API. For consistency with the `rippled` APIs, we recommend formally specifying XRP values in *drops* in all API requests, and converting them to XRP for display. This is similar to Bitcoin's *satoshis* and Ethereum's *wei*. 1 XRP = 1,000,000 drops.
|
||||||
|
|
||||||
**Non-XRP values** have 16 decimal digits of precision, with a maximum value of `9999999999999999e80`. The smallest positive non-XRP value is `1e-81`.
|
**Non-XRP values** have 16 decimal digits of precision, with a maximum value of `9999999999999999e80`. The smallest positive non-XRP value is `1e-81`.
|
||||||
|
|
||||||
|
|
||||||
## Amount
|
## Amount
|
||||||
|
|
||||||
Example amount:
|
Example 100.00 USD amount:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -36,15 +35,16 @@ Example amount:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Example XRP amount:
|
Example 3.0 XRP amount, in drops:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "2000"
|
"value": "3000000"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
(Requires `ripple-lib` version 1.0.0 or higher.)
|
||||||
|
|
||||||
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value. For XRP, there is no counterparty.
|
An *amount* is an object specifying a currency, a quantity of that currency, and the counterparty (issuer) on the trustline that holds the value. For XRP, there is no counterparty.
|
||||||
|
|
||||||
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ All "prepare*" methods have the same return type.
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||||
|
|
||||||
|
// Buy 10.10 USD (of the specified issuer) for 2.0 XRP (2000000 drops), fill or kill.
|
||||||
const order = <%- importFile('test/fixtures/requests/prepare-order.json') %>;
|
const order = <%- importFile('test/fixtures/requests/prepare-order.json') %>;
|
||||||
return api.prepareOrder(address, order)
|
return api.prepareOrder(address, order)
|
||||||
.then(prepared => {/* ... */});
|
.then(prepared => {/* ... */});
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ Transaction instructions indicate how to execute a transaction, complementary wi
|
|||||||
|
|
||||||
<%- renderSchema("objects/instructions.json") %>
|
<%- renderSchema("objects/instructions.json") %>
|
||||||
|
|
||||||
We recommended that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the XRP Ledger's consensus-validated ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare*" method.
|
We recommend that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the XRP Ledger's consensus-validated ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare\*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare\*" method.
|
||||||
|
|
||||||
## Transaction ID
|
## Transaction ID
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {EventEmitter} from 'events'
|
import {EventEmitter} from 'events'
|
||||||
import {Connection, errors, validate} from './common'
|
import {Connection, errors, validate, xrpToDrops, dropsToXrp} from './common'
|
||||||
import {
|
import {
|
||||||
connect,
|
connect,
|
||||||
disconnect,
|
disconnect,
|
||||||
@@ -300,6 +300,9 @@ class RippleAPI extends EventEmitter {
|
|||||||
signPaymentChannelClaim = signPaymentChannelClaim
|
signPaymentChannelClaim = signPaymentChannelClaim
|
||||||
verifyPaymentChannelClaim = verifyPaymentChannelClaim
|
verifyPaymentChannelClaim = verifyPaymentChannelClaim
|
||||||
errors = errors
|
errors = errors
|
||||||
|
|
||||||
|
xrpToDrops = xrpToDrops
|
||||||
|
dropsToXrp = dropsToXrp
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
"$ref": "value"
|
"$ref": "value"
|
||||||
},
|
},
|
||||||
"currency": {
|
"currency": {
|
||||||
"description": "The three-character code or hexadecimal string used to denote currencies",
|
"description": "The three-character code or hexadecimal string used to denote currencies, or \"drops\" for the smallest unit of XRP.",
|
||||||
"$ref": "currency"
|
"$ref": "currency"
|
||||||
},
|
},
|
||||||
"counterparty": {
|
"counterparty": {
|
||||||
"description": "The Ripple address of the account that owes or is owed the funds (omitted if `currency` is \"XRP\")",
|
"description": "The Ripple address of the account that owes or is owed the funds (omitted if `currency` is \"XRP\" or \"drops\")",
|
||||||
"$ref": "address"
|
"$ref": "address"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"currency": {
|
"currency": {
|
||||||
"not": {
|
"not": {
|
||||||
"enum": ["XRP"]
|
"enum": ["XRP", "drops"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
{
|
{
|
||||||
"properties": {
|
"properties": {
|
||||||
"currency": {
|
"currency": {
|
||||||
"enum": ["XRP"]
|
"enum": ["XRP", "drops"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"not": {
|
"not": {
|
||||||
|
|||||||
@@ -4,5 +4,5 @@
|
|||||||
"description": "The three-character code or hexadecimal string used to denote currencies",
|
"description": "The three-character code or hexadecimal string used to denote currencies",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"link": "currency",
|
"link": "currency",
|
||||||
"pattern": "^([a-zA-Z0-9<>(){}[\\]|?!@#$%^&*]{3}|[A-F0-9]{40})$"
|
"pattern": "^([a-zA-Z0-9<>(){}[\\]|?!@#$%^&*]{3}|[A-F0-9]{40}|drops)$"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import * as _ from 'lodash'
|
import * as _ from 'lodash'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
const {deriveKeypair} = require('ripple-keypairs')
|
import {deriveKeypair} from 'ripple-keypairs'
|
||||||
|
|
||||||
import {Amount, RippledAmount} from './types/objects'
|
import {Amount, RippledAmount} from './types/objects'
|
||||||
|
import {ValidationError} from './errors'
|
||||||
|
|
||||||
function isValidSecret(secret: string): boolean {
|
function isValidSecret(secret: string): boolean {
|
||||||
try {
|
try {
|
||||||
@@ -13,18 +13,86 @@ function isValidSecret(secret: string): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function dropsToXrp(drops: string): string {
|
function dropsToXrp(drops: string | BigNumber): string {
|
||||||
return (new BigNumber(drops)).dividedBy(1000000.0).toString()
|
if (typeof drops === 'string') {
|
||||||
|
if (!drops.match(/^-?[0-9]*\.?[0-9]*$/)) {
|
||||||
|
throw new ValidationError(`dropsToXrp: invalid value '${drops}',` +
|
||||||
|
` should be a number matching (^-?[0-9]*\.?[0-9]*$).`)
|
||||||
|
} else if (drops === '.') {
|
||||||
|
throw new ValidationError(`dropsToXrp: invalid value '${drops}',` +
|
||||||
|
` should be a BigNumber or string-encoded number.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converting to BigNumber and then back to string should remove any
|
||||||
|
// decimal point followed by zeros, e.g. '1.00'.
|
||||||
|
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
|
||||||
|
drops = (new BigNumber(drops)).toString(10)
|
||||||
|
|
||||||
|
// drops are only whole units
|
||||||
|
if (drops.includes('.')) {
|
||||||
|
throw new ValidationError(`dropsToXrp: value '${drops}' has` +
|
||||||
|
` too many decimal places.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should never happen; the value has already been
|
||||||
|
// validated above. This just ensures BigNumber did not do
|
||||||
|
// something unexpected.
|
||||||
|
if (!drops.match(/^-?[0-9]+$/)) {
|
||||||
|
throw new ValidationError(`dropsToXrp: failed sanity check -` +
|
||||||
|
` value '${drops}',` +
|
||||||
|
` does not match (^-?[0-9]+$).`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new BigNumber(drops)).dividedBy(1000000.0).toString(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
function xrpToDrops(xrp: string): string {
|
function xrpToDrops(xrp: string | BigNumber): string {
|
||||||
return (new BigNumber(xrp)).times(1000000.0).floor().toString()
|
if (typeof xrp === 'string') {
|
||||||
|
if (!xrp.match(/^-?[0-9]*\.?[0-9]*$/)) {
|
||||||
|
throw new ValidationError(`xrpToDrops: invalid value '${xrp}',` +
|
||||||
|
` should be a number matching (^-?[0-9]*\.?[0-9]*$).`)
|
||||||
|
} else if (xrp === '.') {
|
||||||
|
throw new ValidationError(`xrpToDrops: invalid value '${xrp}',` +
|
||||||
|
` should be a BigNumber or string-encoded number.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
|
||||||
|
xrp = (new BigNumber(xrp)).toString(10)
|
||||||
|
|
||||||
|
// This should never happen; the value has already been
|
||||||
|
// validated above. This just ensures BigNumber did not do
|
||||||
|
// something unexpected.
|
||||||
|
if (!xrp.match(/^-?[0-9.]+$/)) {
|
||||||
|
throw new ValidationError(`xrpToDrops: failed sanity check -` +
|
||||||
|
` value '${xrp}',` +
|
||||||
|
` does not match (^-?[0-9.]+$).`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const components = xrp.split('.')
|
||||||
|
if (components.length > 2) {
|
||||||
|
throw new ValidationError(`xrpToDrops: failed sanity check -` +
|
||||||
|
` value '${xrp}' has` +
|
||||||
|
` too many decimal points.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const fraction = components[1] || '0'
|
||||||
|
if (fraction.length > 6) {
|
||||||
|
throw new ValidationError(`xrpToDrops: value '${xrp}' has` +
|
||||||
|
` too many decimal places.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new BigNumber(xrp)).times(1000000.0).floor().toString(10)
|
||||||
}
|
}
|
||||||
|
|
||||||
function toRippledAmount(amount: Amount): RippledAmount {
|
function toRippledAmount(amount: Amount): RippledAmount {
|
||||||
if (amount.currency === 'XRP') {
|
if (amount.currency === 'XRP') {
|
||||||
return xrpToDrops(amount.value)
|
return xrpToDrops(amount.value)
|
||||||
}
|
}
|
||||||
|
if (amount.currency === 'drops') {
|
||||||
|
return amount.value
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
currency: amount.currency,
|
currency: amount.currency,
|
||||||
issuer: amount.counterparty ? amount.counterparty :
|
issuer: amount.counterparty ? amount.counterparty :
|
||||||
|
|||||||
219
test/api-test.js
219
test/api-test.js
@@ -15,6 +15,7 @@ const utils = RippleAPI._PRIVATE.ledgerUtils;
|
|||||||
const ledgerClosed = require('./fixtures/rippled/ledger-close-newer');
|
const ledgerClosed = require('./fixtures/rippled/ledger-close-newer');
|
||||||
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
|
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
|
||||||
const binary = require('ripple-binary-codec');
|
const binary = require('ripple-binary-codec');
|
||||||
|
const BigNumber = require('bignumber.js')
|
||||||
assert.options.strict = true;
|
assert.options.strict = true;
|
||||||
|
|
||||||
// how long before each test case times out
|
// how long before each test case times out
|
||||||
@@ -51,6 +52,224 @@ describe('RippleAPI', function () {
|
|||||||
assert.strictEqual(error.inspect(), '[RippleError(mess, { data: 1 })]');
|
assert.strictEqual(error.inspect(), '[RippleError(mess, { data: 1 })]');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('xrpToDrops', function () {
|
||||||
|
it('works with a typical amount', function () {
|
||||||
|
const drops = this.api.xrpToDrops('2')
|
||||||
|
assert.strictEqual(drops, '2000000', '2 XRP equals 2 million drops')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with fractions', function () {
|
||||||
|
let drops = this.api.xrpToDrops('3.456789')
|
||||||
|
assert.strictEqual(drops, '3456789', '3.456789 XRP equals 3,456,789 drops')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops('3.400000')
|
||||||
|
assert.strictEqual(drops, '3400000', '3.400000 XRP equals 3,400,000 drops')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops('0.000001')
|
||||||
|
assert.strictEqual(drops, '1', '0.000001 XRP equals 1 drop')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops('0.0000010')
|
||||||
|
assert.strictEqual(drops, '1', '0.0000010 XRP equals 1 drop')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with zero', function () {
|
||||||
|
let drops = this.api.xrpToDrops('0')
|
||||||
|
assert.strictEqual(drops, '0', '0 XRP equals 0 drops')
|
||||||
|
|
||||||
|
// negative zero is equivalent to zero
|
||||||
|
drops = this.api.xrpToDrops('-0')
|
||||||
|
assert.strictEqual(drops, '0', '-0 XRP equals 0 drops')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops('0.000000')
|
||||||
|
assert.strictEqual(drops, '0', '0.000000 XRP equals 0 drops')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops('0.0000000')
|
||||||
|
assert.strictEqual(drops, '0', '0.0000000 XRP equals 0 drops')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with a negative value', function () {
|
||||||
|
const drops = this.api.xrpToDrops('-2')
|
||||||
|
assert.strictEqual(drops, '-2000000', '-2 XRP equals -2 million drops')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with a value ending with a decimal point', function () {
|
||||||
|
let drops = this.api.xrpToDrops('2.')
|
||||||
|
assert.strictEqual(drops, '2000000', '2. XRP equals 2000000 drops')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops('-2.')
|
||||||
|
assert.strictEqual(drops, '-2000000', '-2. XRP equals -2000000 drops')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with BigNumber objects', function () {
|
||||||
|
let drops = this.api.xrpToDrops(new BigNumber(2))
|
||||||
|
assert.strictEqual(drops, '2000000', '(BigNumber) 2 XRP equals 2 million drops')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops(new BigNumber(-2))
|
||||||
|
assert.strictEqual(drops, '-2000000', '(BigNumber) -2 XRP equals -2 million drops')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with a number', function() {
|
||||||
|
// This is not recommended. Use strings or BigNumber objects to avoid precision errors.
|
||||||
|
|
||||||
|
let drops = this.api.xrpToDrops(2)
|
||||||
|
assert.strictEqual(drops, '2000000', '(number) 2 XRP equals 2 million drops')
|
||||||
|
|
||||||
|
drops = this.api.xrpToDrops(-2)
|
||||||
|
assert.strictEqual(drops, '-2000000', '(number) -2 XRP equals -2 million drops')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws with an amount with too many decimal places', function () {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('1.1234567')
|
||||||
|
}, /has too many decimal places/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('0.0000001')
|
||||||
|
}, /has too many decimal places/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws with an invalid value', function () {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('FOO')
|
||||||
|
}, /invalid value/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('1e-7')
|
||||||
|
}, /invalid value/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('2,0')
|
||||||
|
}, /invalid value/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('.')
|
||||||
|
}, /xrpToDrops\: invalid value '\.', should be a BigNumber or string-encoded number\./)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws with an amount more than one decimal point', function () {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('1.0.0')
|
||||||
|
}, /xrpToDrops:\ invalid\ value\ '1\.0\.0'\,\ should\ be\ a\ number\ matching\ \(\^\-\?\[0\-9\]\*\.\?\[0\-9\]\*\$\)\./)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.xrpToDrops('...')
|
||||||
|
}, /xrpToDrops:\ invalid\ value\ '\.\.\.'\,\ should\ be\ a\ number\ matching\ \(\^\-\?\[0\-9\]\*\.\?\[0\-9\]\*\$\)\./)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('dropsToXrp', function () {
|
||||||
|
it('works with a typical amount', function () {
|
||||||
|
const xrp = this.api.dropsToXrp('2000000')
|
||||||
|
assert.strictEqual(xrp, '2', '2 million drops equals 2 XRP')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with fractions', function () {
|
||||||
|
let xrp = this.api.dropsToXrp('3456789')
|
||||||
|
assert.strictEqual(xrp, '3.456789', '3,456,789 drops equals 3.456789 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp('3400000')
|
||||||
|
assert.strictEqual(xrp, '3.4', '3,400,000 drops equals 3.4 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp('1')
|
||||||
|
assert.strictEqual(xrp, '0.000001', '1 drop equals 0.000001 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp('1.0')
|
||||||
|
assert.strictEqual(xrp, '0.000001', '1.0 drops equals 0.000001 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp('1.00')
|
||||||
|
assert.strictEqual(xrp, '0.000001', '1.00 drops equals 0.000001 XRP')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with zero', function () {
|
||||||
|
let xrp = this.api.dropsToXrp('0')
|
||||||
|
assert.strictEqual(xrp, '0', '0 drops equals 0 XRP')
|
||||||
|
|
||||||
|
// negative zero is equivalent to zero
|
||||||
|
xrp = this.api.dropsToXrp('-0')
|
||||||
|
assert.strictEqual(xrp, '0', '-0 drops equals 0 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp('0.00')
|
||||||
|
assert.strictEqual(xrp, '0', '0.00 drops equals 0 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp('000000000')
|
||||||
|
assert.strictEqual(xrp, '0', '000000000 drops equals 0 XRP')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with a negative value', function () {
|
||||||
|
const xrp = this.api.dropsToXrp('-2000000')
|
||||||
|
assert.strictEqual(xrp, '-2', '-2 million drops equals -2 XRP')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with a value ending with a decimal point', function () {
|
||||||
|
let xrp = this.api.dropsToXrp('2000000.')
|
||||||
|
assert.strictEqual(xrp, '2', '2000000. drops equals 2 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp('-2000000.')
|
||||||
|
assert.strictEqual(xrp, '-2', '-2000000. drops equals -2 XRP')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with BigNumber objects', function () {
|
||||||
|
let xrp = this.api.dropsToXrp(new BigNumber(2000000))
|
||||||
|
assert.strictEqual(xrp, '2', '(BigNumber) 2 million drops equals 2 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp(new BigNumber(-2000000))
|
||||||
|
assert.strictEqual(xrp, '-2', '(BigNumber) -2 million drops equals -2 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp(new BigNumber(2345678))
|
||||||
|
assert.strictEqual(xrp, '2.345678', '(BigNumber) 2,345,678 drops equals 2.345678 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp(new BigNumber(-2345678))
|
||||||
|
assert.strictEqual(xrp, '-2.345678', '(BigNumber) -2,345,678 drops equals -2.345678 XRP')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works with a number', function() {
|
||||||
|
// This is not recommended. Use strings or BigNumber objects to avoid precision errors.
|
||||||
|
|
||||||
|
let xrp = this.api.dropsToXrp(2000000)
|
||||||
|
assert.strictEqual(xrp, '2', '(number) 2 million drops equals 2 XRP')
|
||||||
|
|
||||||
|
xrp = this.api.dropsToXrp(-2000000)
|
||||||
|
assert.strictEqual(xrp, '-2', '(number) -2 million drops equals -2 XRP')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws with an amount with too many decimal places', function () {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('1.2')
|
||||||
|
}, /has too many decimal places/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('0.10')
|
||||||
|
}, /has too many decimal places/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws with an invalid value', function () {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('FOO')
|
||||||
|
}, /invalid value/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('1e-7')
|
||||||
|
}, /invalid value/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('2,0')
|
||||||
|
}, /invalid value/)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('.')
|
||||||
|
}, /dropsToXrp\: invalid value '\.', should be a BigNumber or string-encoded number\./)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('throws with an amount more than one decimal point', function () {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('1.0.0')
|
||||||
|
}, /dropsToXrp:\ invalid\ value\ '1\.0\.0'\,\ should\ be\ a\ number\ matching\ \(\^\-\?\[0\-9\]\*\.\?\[0\-9\]\*\$\)\./)
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.dropsToXrp('...')
|
||||||
|
}, /dropsToXrp:\ invalid\ value\ '\.\.\.'\,\ should\ be\ a\ number\ matching\ \(\^\-\?\[0\-9\]\*\.\?\[0\-9\]\*\$\)\./)
|
||||||
|
})
|
||||||
|
})
|
||||||
describe('pagination', function () {
|
describe('pagination', function () {
|
||||||
|
|
||||||
describe('hasNextPage', function () {
|
describe('hasNextPage', function () {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"amount": {
|
"amount": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "1"
|
"value": "1000000"
|
||||||
},
|
},
|
||||||
"checkID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334"
|
"checkID": "838766BA2B995C00744175F69A1B11E32C3DBC40E64801A4056FCBD657F57334"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
"destination": "rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW",
|
||||||
"sendMax": {
|
"sendMax": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "1"
|
"value": "1000000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
test/fixtures/requests/prepare-order.json
vendored
6
test/fixtures/requests/prepare-order.json
vendored
@@ -6,9 +6,9 @@
|
|||||||
"value": "10.1"
|
"value": "10.1"
|
||||||
},
|
},
|
||||||
"totalPrice": {
|
"totalPrice": {
|
||||||
"currency": "XRP",
|
"currency": "drops",
|
||||||
"value": "2"
|
"value": "2000000"
|
||||||
},
|
},
|
||||||
"passive": true,
|
"passive": false,
|
||||||
"fillOrKill": true
|
"fillOrKill": true
|
||||||
}
|
}
|
||||||
|
|||||||
2
test/fixtures/responses/prepare-order.json
vendored
2
test/fixtures/responses/prepare-order.json
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"txJSON": "{\"Flags\":2147811328,\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":\"2000000\",\"TakerPays\":{\"value\":\"10.1\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8819954,\"Fee\":\"12\",\"Sequence\":23}",
|
"txJSON": "{\"Flags\":2147745792,\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":\"2000000\",\"TakerPays\":{\"value\":\"10.1\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8819954,\"Fee\":\"12\",\"Sequence\":23}",
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"fee": "0.000012",
|
"fee": "0.000012",
|
||||||
"sequence": 23,
|
"sequence": 23,
|
||||||
|
|||||||
Reference in New Issue
Block a user