mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-04 21:15:47 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1eddbf995f | ||
|
|
592385ac73 | ||
|
|
56d626f5b1 | ||
|
|
7a14300409 | ||
|
|
30fa8d658e | ||
|
|
c0101cb5e7 | ||
|
|
fd640cd65e | ||
|
|
11528eff92 | ||
|
|
3c9175459d | ||
|
|
bd4e0e01e2 | ||
|
|
cfcf6e473c | ||
|
|
50c6af3158 | ||
|
|
1d4310cd3a | ||
|
|
2191596e68 | ||
|
|
acf8f87a88 | ||
|
|
cfac146620 | ||
|
|
f3234ad853 | ||
|
|
b65fee3d85 | ||
|
|
82b294bc7d | ||
|
|
2a58573823 | ||
|
|
a96f71b7fd | ||
|
|
077f4a4c79 | ||
|
|
9a495467fb | ||
|
|
db2e62b219 | ||
|
|
2fafa493a2 | ||
|
|
7617c3005c | ||
|
|
5cdbb71277 | ||
|
|
f9339c36bf | ||
|
|
67dc57e9d0 | ||
|
|
757f3190d1 | ||
|
|
0d94a15ee7 | ||
|
|
7f1c80da1b | ||
|
|
f74e11bce0 | ||
|
|
d4c843e8e3 | ||
|
|
bae190b282 | ||
|
|
d2cbd70da8 | ||
|
|
5da78ce583 | ||
|
|
14bbe3e30b | ||
|
|
e52e2bbc68 | ||
|
|
b56752e45b | ||
|
|
4632f511ab | ||
|
|
e17b6f172d | ||
|
|
eb04e878ba | ||
|
|
c71febd116 | ||
|
|
69c1ccbb6b | ||
|
|
499b8c8d8b | ||
|
|
ea009f9a84 | ||
|
|
dc784d4567 | ||
|
|
9ffc8a2c0b | ||
|
|
5b20fe573e | ||
|
|
7e466bb80f | ||
|
|
1f8418b447 | ||
|
|
ccfc57fc62 | ||
|
|
1d31fccd72 |
@@ -30,7 +30,7 @@ Then see the [documentation](https://github.com/ripple/ripple-lib/blob/develop/d
|
||||
|
||||
##Generating Documentation
|
||||
|
||||
The continuous integration tests require that the documentation stays up-to-date. If you make changes the the JSON schemas, fixtures, or documentation sources, you must update the documentation by running `npm run docgen`.
|
||||
The continuous integration tests require that the documentation stays up-to-date. If you make changes to the JSON schemas, fixtures, or documentation sources, you must update the documentation by running `npm run docgen`.
|
||||
|
||||
##More Information
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ machine:
|
||||
testripple.circleci.com: 127.0.0.1
|
||||
dependencies:
|
||||
pre:
|
||||
- npm -g install npm@latest-2
|
||||
- wget https://s3-us-west-2.amazonaws.com/ripple-debs/rippled_0.30.1-b11-1.deb
|
||||
- sudo dpkg -i rippled_0.30.1-b11-1.deb
|
||||
test:
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
- [API Events](#api-events)
|
||||
- [ledger](#ledger)
|
||||
- [error](#error)
|
||||
- [connected](#connected)
|
||||
- [disconnected](#disconnected)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
@@ -90,6 +92,14 @@ const api = new RippleAPI({
|
||||
api.on('error', (errorCode, errorMessage) => {
|
||||
console.log(errorCode + ': ' + errorMessage);
|
||||
});
|
||||
api.on('connected', () => {
|
||||
console.log('connected');
|
||||
});
|
||||
api.on('disconnected', (code) => {
|
||||
// code - [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) sent by the server
|
||||
// will be 1000 if this was normal closure
|
||||
console.log('disconnected, code:', code);
|
||||
});
|
||||
api.connect().then(() => {
|
||||
/* insert code here */
|
||||
}).then(() => {
|
||||
@@ -201,7 +211,7 @@ A *value* is a quantity of a currency represented as a decimal string. Be carefu
|
||||
|
||||
**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).
|
||||
|
||||
**Non-XRP values** have 15 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
|
||||
@@ -420,6 +430,7 @@ expirationTime | date-time string | *Optional* Time after which the offer is no
|
||||
fillOrKill | boolean | *Optional* Treat the offer as a [Fill or Kill order](http://en.wikipedia.org/wiki/Fill_or_kill). Only attempt to match existing offers in the ledger, and only do so if the entire quantity can be exchanged.
|
||||
immediateOrCancel | boolean | *Optional* Treat the offer as an [Immediate or Cancel order](http://en.wikipedia.org/wiki/Immediate_or_cancel). If enabled, the offer will never become a ledger node: it only attempts to match existing offers in the ledger.
|
||||
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||
orderToReplace | [sequence](#account-sequence-number) | *Optional* The [account sequence number](#account-sequence-number) of an order to cancel before the new order is created, effectively replacing the old order.
|
||||
passive | boolean | *Optional* If enabled, the offer will not consume offers that exactly match it, and instead becomes an Offer node in the ledger. It will still consume offers that cross it.
|
||||
|
||||
### Example
|
||||
@@ -488,7 +499,7 @@ signers | object | *Optional* Settings that determine what sets of accounts can
|
||||
*signers.* weights[] | object | An association of an address and a weight.
|
||||
*signers.weights[].* address | [address](#ripple-address) | A Ripple account address
|
||||
*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, represented as billionths of a unit. Use `null` to set no fee.
|
||||
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.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -1501,7 +1512,7 @@ options | object | *Optional* Options to determine how the balances will be calc
|
||||
|
||||
### Return Value
|
||||
|
||||
This method returns a promise that resolves with an array of objects with the following structure:
|
||||
This method returns a promise that resolves with an object with the following structure:
|
||||
|
||||
Name | Type | Description
|
||||
---- | ---- | -----------
|
||||
@@ -2674,7 +2685,7 @@ signers | object | *Optional* Settings that determine what sets of accounts can
|
||||
*signers.* weights[] | object | An association of an address and a weight.
|
||||
*signers.weights[].* address | [address](#ripple-address) | A Ripple account address
|
||||
*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, represented as billionths of a unit. Use `null` to set no fee.
|
||||
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.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -3041,7 +3052,7 @@ instructions | object | The instructions for how to execute the transaction afte
|
||||
```javascript
|
||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||
const orderCancellation = {orderSequence: 123};
|
||||
return api.prepareOrderCancellation(address, sequence)
|
||||
return api.prepareOrderCancellation(address, orderCancellation)
|
||||
.then(prepared => {/* ... */});
|
||||
```
|
||||
|
||||
@@ -3593,3 +3604,35 @@ api.on('error', (errorCode, errorMessage, data) => {
|
||||
tooBusy: The server is too busy to help you now.
|
||||
```
|
||||
|
||||
## connected
|
||||
|
||||
This event is emitted after connection successfully opened.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
api.on('connected', () => {
|
||||
console.log('Connection is open now.');
|
||||
});
|
||||
```
|
||||
|
||||
## disconnected
|
||||
|
||||
This event is emitted when connection is closed.
|
||||
|
||||
### Return Value
|
||||
|
||||
The only parameter is a number containing the [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) send by the server.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
api.on('disconnected', (code) => {
|
||||
if (code !== 1000) {
|
||||
console.log('Connection is closed due to error.');
|
||||
} else {
|
||||
console.log('Connection is closed normally.');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ A *value* is a quantity of a currency represented as a decimal string. Be carefu
|
||||
|
||||
**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).
|
||||
|
||||
**Non-XRP values** have 15 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
|
||||
|
||||
@@ -11,6 +11,14 @@ const api = new RippleAPI({
|
||||
api.on('error', (errorCode, errorMessage) => {
|
||||
console.log(errorCode + ': ' + errorMessage);
|
||||
});
|
||||
api.on('connected', () => {
|
||||
console.log('connected');
|
||||
});
|
||||
api.on('disconnected', (code) => {
|
||||
// code - [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) sent by the server
|
||||
// will be 1000 if this was normal closure
|
||||
console.log('disconnected, code:', code);
|
||||
});
|
||||
api.connect().then(() => {
|
||||
/* insert code here */
|
||||
}).then(() => {
|
||||
|
||||
@@ -47,3 +47,35 @@ api.on('error', (errorCode, errorMessage, data) => {
|
||||
```
|
||||
tooBusy: The server is too busy to help you now.
|
||||
```
|
||||
|
||||
## connected
|
||||
|
||||
This event is emitted after connection successfully opened.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
api.on('connected', () => {
|
||||
console.log('Connection is open now.');
|
||||
});
|
||||
```
|
||||
|
||||
## disconnected
|
||||
|
||||
This event is emitted when connection is closed.
|
||||
|
||||
### Return Value
|
||||
|
||||
The only parameter is a number containing the [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) send by the server.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
api.on('disconnected', (code) => {
|
||||
if (code !== 1000) {
|
||||
console.log('Connection is closed due to error.');
|
||||
} else {
|
||||
console.log('Connection is closed normally.');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -10,7 +10,7 @@ Returns aggregate balances by currency plus a breakdown of assets and obligation
|
||||
|
||||
### Return Value
|
||||
|
||||
This method returns a promise that resolves with an array of objects with the following structure:
|
||||
This method returns a promise that resolves with an object with the following structure:
|
||||
|
||||
<%- renderSchema('output/get-balance-sheet.json') %>
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ All "prepare*" methods have the same return type.
|
||||
```javascript
|
||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||
const orderCancellation = {orderSequence: 123};
|
||||
return api.prepareOrderCancellation(address, sequence)
|
||||
return api.prepareOrderCancellation(address, orderCancellation)
|
||||
.then(prepared => {/* ... */});
|
||||
```
|
||||
|
||||
|
||||
4332
npm-shrinkwrap.json
generated
4332
npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.16.8",
|
||||
"version": "0.17.5",
|
||||
"license": "ISC",
|
||||
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
||||
"files": [
|
||||
@@ -15,7 +15,8 @@
|
||||
"test": "test"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": "^1.4.8",
|
||||
"ajv": "^4.0.5",
|
||||
"ajv-i18n": "^1.2.0",
|
||||
"babel-polyfill": "^6.3.14",
|
||||
"babel-runtime": "^6.3.19",
|
||||
"bignumber.js": "^2.0.3",
|
||||
@@ -23,8 +24,8 @@
|
||||
"jayson": "^1.2.2",
|
||||
"lodash": "^3.1.0",
|
||||
"ripple-address-codec": "^2.0.1",
|
||||
"ripple-binary-codec": "^0.1.2",
|
||||
"ripple-hashes": "^0.1.0",
|
||||
"ripple-binary-codec": "^0.1.8",
|
||||
"ripple-hashes": "^0.2.0",
|
||||
"ripple-keypairs": "^0.10.0",
|
||||
"ripple-lib-transactionparser": "^0.6.0",
|
||||
"ws": "^1.0.1"
|
||||
@@ -33,31 +34,29 @@
|
||||
"assert-diff": "^1.0.1",
|
||||
"babel-cli": "^6.4.0",
|
||||
"babel-core": "^6.4.0",
|
||||
"babel-eslint": "^4.1.6",
|
||||
"babel-eslint": "^6.0.4",
|
||||
"babel-loader": "^6.2.1",
|
||||
"babel-plugin-syntax-flow": "^6.3.13",
|
||||
"babel-plugin-transform-flow-strip-types": "^6.4.0",
|
||||
"babel-preset-es2015": "^6.3.13",
|
||||
"babel-preset-stage-1": "^6.3.13",
|
||||
"babel-register": "^6.3.13",
|
||||
"coveralls": "^2.10.0",
|
||||
"coveralls": "^2.11.9",
|
||||
"doctoc": "^0.15.0",
|
||||
"ejs": "^2.3.4",
|
||||
"eslint": "^2.1.0",
|
||||
"eslint": "^2.9.0",
|
||||
"eventemitter2": "^0.4.14",
|
||||
"flow-bin": "^0.14",
|
||||
"gulp": "^3.8.10",
|
||||
"gulp-bump": "^0.1.13",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-uglify": "^1.1.0",
|
||||
"http-server": "^0.8.5",
|
||||
"isparta": "^4.0.0",
|
||||
"istanbul": "^1.1.0-alpha.1",
|
||||
"json-loader": "^0.5.2",
|
||||
"json-schema-to-markdown-table": "^0.4.0",
|
||||
"mocha": "^2.1.0",
|
||||
"mocha-junit-reporter": "^1.9.1",
|
||||
"mocha-phantomjs": "^4.0.1",
|
||||
"mocha-in-sauce": "^0.0.1",
|
||||
"mocha-junit-reporter": "^1.9.1",
|
||||
"null-loader": "^0.1.1",
|
||||
"webpack": "^1.5.3",
|
||||
"yargs": "^1.3.1"
|
||||
@@ -72,7 +71,7 @@
|
||||
"watch": "babel -w -D --optional runtime -d dist/npm/ src/",
|
||||
"compile-with-source-maps": "babel -D --optional runtime -s -t -d dist/npm/ src/",
|
||||
"prepublish": "npm run clean && npm run compile",
|
||||
"test": "babel-node node_modules/isparta/lib/cli cover ./node_modules/mocha/bin/_mocha",
|
||||
"test": "babel-node ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_mocha",
|
||||
"coveralls": "cat ./coverage/lcov.info | coveralls",
|
||||
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/",
|
||||
"perf": "./scripts/perf_test.sh",
|
||||
|
||||
@@ -35,21 +35,21 @@ unittest() {
|
||||
ln -nfs ../../dist/npm test-compiled/node_modules/ripple-api
|
||||
mocha --opts test-compiled/mocha.opts test-compiled
|
||||
|
||||
# compile tests for browser testing
|
||||
gulp build-min build-tests
|
||||
node --harmony test-compiled/mocked-server.js > /dev/null &
|
||||
#compile tests for browser testing
|
||||
#gulp build-min build-tests
|
||||
#node --harmony test-compiled/mocked-server.js > /dev/null &
|
||||
|
||||
echo "Running tests in PhantomJS"
|
||||
mocha-phantomjs test/localrunner.html
|
||||
echo "Running tests using minified version in PhantomJS"
|
||||
mocha-phantomjs test/localrunnermin.html
|
||||
#echo "Running tests in PhantomJS"
|
||||
#mocha-phantomjs test/localrunner.html
|
||||
#echo "Running tests using minified version in PhantomJS"
|
||||
#mocha-phantomjs test/localrunnermin.html
|
||||
|
||||
echo "Running tests in SauceLabs"
|
||||
http-server &
|
||||
npm run sauce
|
||||
#echo "Running tests in SauceLabs"
|
||||
#http-server &
|
||||
#npm run sauce
|
||||
|
||||
pkill -f mocked-server.js
|
||||
pkill -f http-server
|
||||
#pkill -f mocked-server.js
|
||||
#pkill -f http-server
|
||||
rm -rf test-compiled
|
||||
}
|
||||
|
||||
@@ -58,9 +58,9 @@ integrationtest() {
|
||||
mocha test/integration/http-integration-test.js
|
||||
|
||||
# run integration tests in PhantomJS
|
||||
gulp build-tests build-min
|
||||
echo "Running integragtion tests in PhantomJS"
|
||||
mocha-phantomjs test/localintegrationrunner.html
|
||||
#gulp build-tests build-min
|
||||
#echo "Running integragtion tests in PhantomJS"
|
||||
#mocha-phantomjs test/localintegrationrunner.html
|
||||
}
|
||||
|
||||
doctest() {
|
||||
@@ -76,7 +76,6 @@ oneNode() {
|
||||
checkEOL
|
||||
doctest
|
||||
lint
|
||||
typecheck
|
||||
unittest
|
||||
integrationtest
|
||||
}
|
||||
@@ -84,7 +83,7 @@ oneNode() {
|
||||
twoNodes() {
|
||||
case "$NODE_INDEX" in
|
||||
0) doctest; lint; integrationtest;;
|
||||
1) checkEOL; typecheck; unittest;;
|
||||
1) checkEOL; unittest;;
|
||||
*) echo "ERROR: invalid usage"; exit 2;;
|
||||
esac
|
||||
}
|
||||
@@ -92,7 +91,7 @@ twoNodes() {
|
||||
threeNodes() {
|
||||
case "$NODE_INDEX" in
|
||||
0) doctest; lint; integrationtest;;
|
||||
1) checkEOL; typecheck;;
|
||||
1) checkEOL;;
|
||||
2) unittest;;
|
||||
*) echo "ERROR: invalid usage"; exit 2;;
|
||||
esac
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const MochaSauce = require('mocha-in-sauce');
|
||||
@@ -18,7 +17,6 @@ function main() {
|
||||
maxDuration: 180000,
|
||||
// the current build name (optional)
|
||||
build: Date.now(),
|
||||
seleniumVersion: '2.50.1',
|
||||
url: testUrl,
|
||||
runSauceConnect: true
|
||||
};
|
||||
@@ -42,6 +40,10 @@ function main() {
|
||||
version: '43'});
|
||||
sauce.browser({browserName: 'safari', platform: 'OS X 10.11',
|
||||
version: '9'});
|
||||
sauce.browser({browserName: 'safari', platform: 'OS X 10.10',
|
||||
version: '8'});
|
||||
sauce.browser({browserName: 'safari', platform: 'OS X 10.9',
|
||||
version: '7'});
|
||||
sauce.browser({browserName: 'chrome', platform: 'OS X 10.11',
|
||||
version: '47'});
|
||||
sauce.browser({browserName: 'chrome', platform: 'Linux',
|
||||
|
||||
112
src/api.js
112
src/api.js
@@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
/* eslint-disable max-len */
|
||||
// Enable core-js polyfills. This allows use of ES6/7 extensions listed here:
|
||||
@@ -9,48 +9,48 @@
|
||||
// In node.js env, polyfill might be already loaded (from any npm package),
|
||||
// that's why we do this check.
|
||||
if (!global._babelPolyfill) {
|
||||
require('babel-polyfill');
|
||||
require('babel-polyfill')
|
||||
}
|
||||
|
||||
const _ = require('lodash');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const common = require('./common');
|
||||
const server = require('./server/server');
|
||||
const connect = server.connect;
|
||||
const disconnect = server.disconnect;
|
||||
const getServerInfo = server.getServerInfo;
|
||||
const getFee = server.getFee;
|
||||
const isConnected = server.isConnected;
|
||||
const getLedgerVersion = server.getLedgerVersion;
|
||||
const getTransaction = require('./ledger/transaction');
|
||||
const getTransactions = require('./ledger/transactions');
|
||||
const getTrustlines = require('./ledger/trustlines');
|
||||
const getBalances = require('./ledger/balances');
|
||||
const getBalanceSheet = require('./ledger/balance-sheet');
|
||||
const getPaths = require('./ledger/pathfind');
|
||||
const getOrders = require('./ledger/orders');
|
||||
const getOrderbook = require('./ledger/orderbook');
|
||||
const getSettings = require('./ledger/settings');
|
||||
const getAccountInfo = require('./ledger/accountinfo');
|
||||
const preparePayment = require('./transaction/payment');
|
||||
const prepareTrustline = require('./transaction/trustline');
|
||||
const prepareOrder = require('./transaction/order');
|
||||
const prepareOrderCancellation = require('./transaction/ordercancellation');
|
||||
const _ = require('lodash')
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const common = require('./common')
|
||||
const server = require('./server/server')
|
||||
const connect = server.connect
|
||||
const disconnect = server.disconnect
|
||||
const getServerInfo = server.getServerInfo
|
||||
const getFee = server.getFee
|
||||
const isConnected = server.isConnected
|
||||
const getLedgerVersion = server.getLedgerVersion
|
||||
const getTransaction = require('./ledger/transaction')
|
||||
const getTransactions = require('./ledger/transactions')
|
||||
const getTrustlines = require('./ledger/trustlines')
|
||||
const getBalances = require('./ledger/balances')
|
||||
const getBalanceSheet = require('./ledger/balance-sheet')
|
||||
const getPaths = require('./ledger/pathfind')
|
||||
const getOrders = require('./ledger/orders')
|
||||
const getOrderbook = require('./ledger/orderbook')
|
||||
const getSettings = require('./ledger/settings')
|
||||
const getAccountInfo = require('./ledger/accountinfo')
|
||||
const preparePayment = require('./transaction/payment')
|
||||
const prepareTrustline = require('./transaction/trustline')
|
||||
const prepareOrder = require('./transaction/order')
|
||||
const prepareOrderCancellation = require('./transaction/ordercancellation')
|
||||
const prepareSuspendedPaymentCreation =
|
||||
require('./transaction/suspended-payment-creation');
|
||||
require('./transaction/suspended-payment-creation')
|
||||
const prepareSuspendedPaymentExecution =
|
||||
require('./transaction/suspended-payment-execution');
|
||||
require('./transaction/suspended-payment-execution')
|
||||
const prepareSuspendedPaymentCancellation =
|
||||
require('./transaction/suspended-payment-cancellation');
|
||||
const prepareSettings = require('./transaction/settings');
|
||||
const sign = require('./transaction/sign');
|
||||
const combine = require('./transaction/combine');
|
||||
const submit = require('./transaction/submit');
|
||||
const errors = require('./common').errors;
|
||||
require('./transaction/suspended-payment-cancellation')
|
||||
const prepareSettings = require('./transaction/settings')
|
||||
const sign = require('./transaction/sign')
|
||||
const combine = require('./transaction/combine')
|
||||
const submit = require('./transaction/submit')
|
||||
const errors = require('./common').errors
|
||||
const generateAddress =
|
||||
require('./offline/generate-address').generateAddressAPI;
|
||||
const computeLedgerHash = require('./offline/ledgerhash');
|
||||
const getLedger = require('./ledger/ledger');
|
||||
require('./offline/generate-address').generateAddressAPI
|
||||
const computeLedgerHash = require('./offline/ledgerhash')
|
||||
const getLedger = require('./ledger/ledger')
|
||||
|
||||
type APIOptions = {
|
||||
server?: string,
|
||||
@@ -63,36 +63,42 @@ type APIOptions = {
|
||||
// prevent access to non-validated ledger versions
|
||||
class RestrictedConnection extends common.Connection {
|
||||
request(request, timeout) {
|
||||
const ledger_index = request.ledger_index;
|
||||
const ledger_index = request.ledger_index
|
||||
if (ledger_index !== undefined && ledger_index !== 'validated') {
|
||||
if (!_.isNumber(ledger_index) || ledger_index > this._ledgerVersion) {
|
||||
return Promise.reject(new errors.LedgerVersionError(
|
||||
`ledgerVersion ${ledger_index} is greater than server\'s ` +
|
||||
`most recent validated ledger: ${this._ledgerVersion}`));
|
||||
`most recent validated ledger: ${this._ledgerVersion}`))
|
||||
}
|
||||
}
|
||||
return super.request(request, timeout);
|
||||
return super.request(request, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
class RippleAPI extends EventEmitter {
|
||||
constructor(options: APIOptions = {}) {
|
||||
common.validate.apiOptions(options);
|
||||
super();
|
||||
this._feeCushion = options.feeCushion || 1.2;
|
||||
const serverURL = options.server;
|
||||
common.validate.apiOptions(options)
|
||||
super()
|
||||
this._feeCushion = options.feeCushion || 1.2
|
||||
const serverURL = options.server
|
||||
if (serverURL !== undefined) {
|
||||
this.connection = new RestrictedConnection(serverURL, options);
|
||||
this.connection = new RestrictedConnection(serverURL, options)
|
||||
this.connection.on('ledgerClosed', message => {
|
||||
this.emit('ledger', server.formatLedgerClose(message));
|
||||
});
|
||||
this.emit('ledger', server.formatLedgerClose(message))
|
||||
})
|
||||
this.connection.on('error', (errorCode, errorMessage, data) => {
|
||||
this.emit('error', errorCode, errorMessage, data);
|
||||
});
|
||||
this.emit('error', errorCode, errorMessage, data)
|
||||
})
|
||||
this.connection.on('connected', () => {
|
||||
this.emit('connected')
|
||||
})
|
||||
this.connection.on('disconnected', code => {
|
||||
this.emit('disconnected', code)
|
||||
})
|
||||
} else {
|
||||
// use null object pattern to provide better error message if user
|
||||
// tries to call a method that requires a connection
|
||||
this.connection = new RestrictedConnection(null, options);
|
||||
this.connection = new RestrictedConnection(null, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,7 +138,7 @@ _.assign(RippleAPI.prototype, {
|
||||
generateAddress,
|
||||
computeLedgerHash,
|
||||
errors
|
||||
});
|
||||
})
|
||||
|
||||
// these are exposed only for use by unit tests; they are not part of the API
|
||||
RippleAPI._PRIVATE = {
|
||||
@@ -140,8 +146,8 @@ RippleAPI._PRIVATE = {
|
||||
RangeSet: require('./common/rangeset').RangeSet,
|
||||
ledgerUtils: require('./ledger/utils'),
|
||||
schemaValidator: require('./common/schema-validator')
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RippleAPI
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,70 +1,70 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const RippleAPI = require('./api').RippleAPI;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const RippleAPI = require('./api').RippleAPI
|
||||
|
||||
class RippleAPIBroadcast extends RippleAPI {
|
||||
constructor(servers, options) {
|
||||
super(options);
|
||||
this.ledgerVersion = 0;
|
||||
super(options)
|
||||
this.ledgerVersion = 0
|
||||
|
||||
const apis = servers.map(server => new RippleAPI(
|
||||
_.assign({}, options, {server})
|
||||
));
|
||||
))
|
||||
|
||||
// exposed for testing
|
||||
this._apis = apis;
|
||||
this._apis = apis
|
||||
|
||||
this.getMethodNames().forEach(name => {
|
||||
this[name] = function() { // eslint-disable-line no-loop-func
|
||||
return Promise.race(apis.map(api => api[name].apply(api, arguments)));
|
||||
};
|
||||
});
|
||||
return Promise.race(apis.map(api => api[name](...arguments)))
|
||||
}
|
||||
})
|
||||
|
||||
// connection methods must be overridden to apply to all api instances
|
||||
this.connect = function() {
|
||||
return Promise.all(apis.map(api => api.connect()));
|
||||
};
|
||||
return Promise.all(apis.map(api => api.connect()))
|
||||
}
|
||||
this.disconnect = function() {
|
||||
return Promise.all(apis.map(api => api.disconnect()));
|
||||
};
|
||||
return Promise.all(apis.map(api => api.disconnect()))
|
||||
}
|
||||
this.isConnected = function() {
|
||||
return _.every(apis.map(api => api.isConnected()));
|
||||
};
|
||||
return _.every(apis.map(api => api.isConnected()))
|
||||
}
|
||||
|
||||
// synchronous methods are all passed directly to the first api instance
|
||||
const defaultAPI = apis[0];
|
||||
const syncMethods = ['sign', 'generateAddress', 'computeLedgerHash'];
|
||||
const defaultAPI = apis[0]
|
||||
const syncMethods = ['sign', 'generateAddress', 'computeLedgerHash']
|
||||
syncMethods.forEach(name => {
|
||||
this[name] = defaultAPI[name].bind(defaultAPI);
|
||||
});
|
||||
this[name] = defaultAPI[name].bind(defaultAPI)
|
||||
})
|
||||
|
||||
apis.forEach(api => {
|
||||
api.on('ledger', this.onLedgerEvent.bind(this));
|
||||
api.on('ledger', this.onLedgerEvent.bind(this))
|
||||
api.on('error', (errorCode, errorMessage, data) =>
|
||||
this.emit('error', errorCode, errorMessage, data));
|
||||
});
|
||||
this.emit('error', errorCode, errorMessage, data))
|
||||
})
|
||||
}
|
||||
|
||||
onLedgerEvent(ledger) {
|
||||
if (ledger.ledgerVersion > this.ledgerVersion) {
|
||||
this.ledgerVersion = ledger.ledgerVersion;
|
||||
this.emit('ledger', ledger);
|
||||
this.ledgerVersion = ledger.ledgerVersion
|
||||
this.emit('ledger', ledger)
|
||||
}
|
||||
}
|
||||
|
||||
getMethodNames() {
|
||||
const methodNames = [];
|
||||
const methodNames = []
|
||||
for (const name in RippleAPI.prototype) {
|
||||
if (RippleAPI.prototype.hasOwnProperty(name)) {
|
||||
if (typeof RippleAPI.prototype[name] === 'function') {
|
||||
methodNames.push(name);
|
||||
methodNames.push(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return methodNames;
|
||||
return methodNames
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
RippleAPIBroadcast
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
function setPrototypeOf(object, prototype) {
|
||||
// Object.setPrototypeOf not supported on Internet Explorer 9
|
||||
@@ -12,10 +12,10 @@ function getConstructorName(object) {
|
||||
// hack for internet explorer
|
||||
return process.browser ?
|
||||
object.constructor.toString().match(/^function\s+([^(]*)/)[1] :
|
||||
object.constructor.name;
|
||||
object.constructor.name
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getConstructorName,
|
||||
setPrototypeOf
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,140 +1,224 @@
|
||||
const _ = require('lodash');
|
||||
const {EventEmitter} = require('events');
|
||||
const WebSocket = require('ws');
|
||||
const parseURL = require('url').parse;
|
||||
const RangeSet = require('./rangeset').RangeSet;
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
const _ = require('lodash')
|
||||
const {EventEmitter} = require('events')
|
||||
const WebSocket = require('ws')
|
||||
const parseURL = require('url').parse
|
||||
const RangeSet = require('./rangeset').RangeSet
|
||||
const {RippledError, DisconnectedError, NotConnectedError,
|
||||
TimeoutError, ResponseFormatError, ConnectionError} = require('./errors');
|
||||
TimeoutError, ResponseFormatError, ConnectionError,
|
||||
RippledNotInitializedError} = require('./errors')
|
||||
|
||||
function isStreamMessageType(type) {
|
||||
return type === 'ledgerClosed' ||
|
||||
type === 'transaction' ||
|
||||
type === 'path_find';
|
||||
type === 'path_find'
|
||||
}
|
||||
|
||||
class Connection extends EventEmitter {
|
||||
constructor(url, options = {}) {
|
||||
super();
|
||||
this.setMaxListeners(Infinity);
|
||||
this._url = url;
|
||||
this._trace = options.trace;
|
||||
super()
|
||||
this.setMaxListeners(Infinity)
|
||||
this._url = url
|
||||
this._trace = options.trace
|
||||
if (this._trace) {
|
||||
// for easier unit testing
|
||||
this._console = console;
|
||||
this._console = console
|
||||
}
|
||||
this._proxyURL = options.proxy;
|
||||
this._proxyAuthorization = options.proxyAuthorization;
|
||||
this._authorization = options.authorization;
|
||||
this._trustedCertificates = options.trustedCertificates;
|
||||
this._key = options.key;
|
||||
this._passphrase = options.passphrase;
|
||||
this._certificate = options.certificate;
|
||||
this._timeout = options.timeout || (20 * 1000);
|
||||
this._isReady = false;
|
||||
this._ws = null;
|
||||
this._ledgerVersion = null;
|
||||
this._availableLedgerVersions = new RangeSet();
|
||||
this._nextRequestID = 1;
|
||||
this._proxyURL = options.proxy
|
||||
this._proxyAuthorization = options.proxyAuthorization
|
||||
this._authorization = options.authorization
|
||||
this._trustedCertificates = options.trustedCertificates
|
||||
this._key = options.key
|
||||
this._passphrase = options.passphrase
|
||||
this._certificate = options.certificate
|
||||
this._timeout = options.timeout || (20 * 1000)
|
||||
this._isReady = false
|
||||
this._ws = null
|
||||
this._ledgerVersion = null
|
||||
this._availableLedgerVersions = new RangeSet()
|
||||
this._nextRequestID = 1
|
||||
this._retry = 0
|
||||
this._retryTimer = null
|
||||
this._onOpenErrorBound = null
|
||||
this._onUnexpectedCloseBound = null
|
||||
}
|
||||
|
||||
_updateLedgerVersions(data) {
|
||||
this._ledgerVersion = Number(data.ledger_index);
|
||||
this._ledgerVersion = Number(data.ledger_index)
|
||||
if (data.validated_ledgers) {
|
||||
this._availableLedgerVersions.reset();
|
||||
this._availableLedgerVersions.reset()
|
||||
this._availableLedgerVersions.parseAndAddRanges(
|
||||
data.validated_ledgers);
|
||||
data.validated_ledgers)
|
||||
} else {
|
||||
this._availableLedgerVersions.addValue(this._ledgerVersion);
|
||||
this._availableLedgerVersions.addValue(this._ledgerVersion)
|
||||
}
|
||||
}
|
||||
|
||||
// return value is array of arguments to Connection.emit
|
||||
_parseMessage(message) {
|
||||
const data = JSON.parse(message);
|
||||
const data = JSON.parse(message)
|
||||
if (data.type === 'response') {
|
||||
if (!(Number.isInteger(data.id) && data.id >= 0)) {
|
||||
throw new ResponseFormatError('valid id not found in response');
|
||||
throw new ResponseFormatError('valid id not found in response')
|
||||
}
|
||||
return [data.id.toString(), data];
|
||||
return [data.id.toString(), data]
|
||||
} else if (isStreamMessageType(data.type)) {
|
||||
if (data.type === 'ledgerClosed') {
|
||||
this._updateLedgerVersions(data);
|
||||
this._updateLedgerVersions(data)
|
||||
}
|
||||
return [data.type, data];
|
||||
return [data.type, data]
|
||||
} else if (data.type === undefined && data.error) {
|
||||
return ['error', data.error, data.error_message, data]; // e.g. slowDown
|
||||
return ['error', data.error, data.error_message, data] // e.g. slowDown
|
||||
}
|
||||
throw new ResponseFormatError('unrecognized message type: ' + data.type);
|
||||
throw new ResponseFormatError('unrecognized message type: ' + data.type)
|
||||
}
|
||||
|
||||
_onMessage(message) {
|
||||
let parameters;
|
||||
let parameters
|
||||
if (this._trace) {
|
||||
this._console.log(message);
|
||||
this._console.log(message)
|
||||
}
|
||||
try {
|
||||
parameters = this._parseMessage(message);
|
||||
parameters = this._parseMessage(message)
|
||||
} catch (error) {
|
||||
this.emit('error', 'badMessage', error.message, message);
|
||||
return;
|
||||
this.emit('error', 'badMessage', error.message, message)
|
||||
return
|
||||
}
|
||||
// we don't want this inside the try/catch or exceptions in listener
|
||||
// will be caught
|
||||
this.emit(...parameters);
|
||||
this.emit(...parameters)
|
||||
}
|
||||
|
||||
get _state() {
|
||||
return this._ws ? this._ws.readyState : WebSocket.CLOSED;
|
||||
return this._ws ? this._ws.readyState : WebSocket.CLOSED
|
||||
}
|
||||
|
||||
get _shouldBeConnected() {
|
||||
return this._ws !== null;
|
||||
return this._ws !== null
|
||||
}
|
||||
|
||||
isConnected() {
|
||||
return this._state === WebSocket.OPEN && this._isReady;
|
||||
return this._state === WebSocket.OPEN && this._isReady
|
||||
}
|
||||
|
||||
_onUnexpectedClose(resolve = function() {}, reject = function() {}) {
|
||||
_onUnexpectedClose(beforeOpen, resolve, reject, code) {
|
||||
if (this._onOpenErrorBound) {
|
||||
this._ws.removeListener('error', this._onOpenErrorBound);
|
||||
this._onOpenErrorBound = null;
|
||||
this._ws.removeListener('error', this._onOpenErrorBound)
|
||||
this._onOpenErrorBound = null
|
||||
}
|
||||
this._ws = null;
|
||||
this._isReady = false;
|
||||
this.connect().then(resolve, reject);
|
||||
// just in case
|
||||
this._ws.removeAllListeners('open')
|
||||
this._ws = null
|
||||
this._isReady = false
|
||||
if (beforeOpen) {
|
||||
// connection was closed before it was properly opened, so we must return
|
||||
// error to connect's caller
|
||||
this.connect().then(resolve, reject)
|
||||
} else {
|
||||
// if first parameter ws lib sends close code,
|
||||
// but sometimes it forgots about it, so default to 1006 - CLOSE_ABNORMAL
|
||||
this.emit('disconnected', code || 1006)
|
||||
this._retryConnect()
|
||||
}
|
||||
}
|
||||
|
||||
_calculateTimeout(retriesCount) {
|
||||
return (retriesCount < 40)
|
||||
// First, for 2 seconds: 20 times per second
|
||||
? (1000 / 20)
|
||||
: (retriesCount < 40 + 60)
|
||||
// Then, for 1 minute: once per second
|
||||
? (1000)
|
||||
: (retriesCount < 40 + 60 + 60)
|
||||
// Then, for 10 minutes: once every 10 seconds
|
||||
? (10 * 1000)
|
||||
// Then: once every 30 seconds
|
||||
: (30 * 1000)
|
||||
}
|
||||
|
||||
_retryConnect() {
|
||||
this._retry += 1
|
||||
const retryTimeout = this._calculateTimeout(this._retry)
|
||||
this._retryTimer = setTimeout(() => {
|
||||
this.emit('reconnecting', this._retry)
|
||||
this.connect().catch(this._retryConnect.bind(this))
|
||||
}, retryTimeout)
|
||||
}
|
||||
|
||||
_clearReconnectTimer() {
|
||||
clearTimeout(this._retryTimer)
|
||||
this._retryTimer = null
|
||||
}
|
||||
|
||||
_onOpen() {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound);
|
||||
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this);
|
||||
this._ws.once('close', this._onUnexpectedCloseBound);
|
||||
|
||||
this._ws.removeListener('error', this._onOpenErrorBound);
|
||||
this._onOpenErrorBound = null;
|
||||
this._ws.on('error', error =>
|
||||
this.emit('error', 'websocket', error.message, error));
|
||||
if (!this._ws) {
|
||||
return Promise.reject(new DisconnectedError())
|
||||
}
|
||||
if (this._onOpenErrorBound) {
|
||||
this._ws.removeListener('error', this._onOpenErrorBound)
|
||||
this._onOpenErrorBound = null
|
||||
}
|
||||
|
||||
const request = {
|
||||
command: 'subscribe',
|
||||
streams: ['ledger']
|
||||
};
|
||||
}
|
||||
return this.request(request).then(data => {
|
||||
this._updateLedgerVersions(data);
|
||||
this._isReady = true;
|
||||
this.emit('connected');
|
||||
});
|
||||
if (_.isEmpty(data) || !data.ledger_index) {
|
||||
// rippled instance doesn't have validated ledgers
|
||||
return this._disconnect(false).then(() => {
|
||||
throw new RippledNotInitializedError('Rippled not initialized')
|
||||
})
|
||||
}
|
||||
|
||||
this._updateLedgerVersions(data)
|
||||
this._rebindOnUnxpectedClose()
|
||||
|
||||
this._retry = 0
|
||||
this._ws.on('error', error => {
|
||||
if (process.browser && error && error.type === 'error') {
|
||||
// we are in browser, ignore error - `close` event will be fired
|
||||
// after error
|
||||
return
|
||||
}
|
||||
this.emit('error', 'websocket', error.message, error)
|
||||
})
|
||||
|
||||
this._isReady = true
|
||||
this.emit('connected')
|
||||
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
|
||||
_rebindOnUnxpectedClose() {
|
||||
if (this._onUnexpectedCloseBound) {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound)
|
||||
}
|
||||
this._onUnexpectedCloseBound =
|
||||
this._onUnexpectedClose.bind(this, false, null, null)
|
||||
this._ws.once('close', this._onUnexpectedCloseBound)
|
||||
}
|
||||
|
||||
_unbindOnUnxpectedClose() {
|
||||
if (this._onUnexpectedCloseBound) {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound)
|
||||
}
|
||||
this._onUnexpectedCloseBound = null
|
||||
}
|
||||
|
||||
_onOpenError(reject, error) {
|
||||
reject(new NotConnectedError(error && error.message));
|
||||
this._onOpenErrorBound = null
|
||||
this._unbindOnUnxpectedClose()
|
||||
reject(new NotConnectedError(error && error.message))
|
||||
}
|
||||
|
||||
_createWebSocket() {
|
||||
const options = {};
|
||||
const options = {}
|
||||
if (this._proxyURL !== undefined) {
|
||||
const parsedURL = parseURL(this._url);
|
||||
const parsedProxyURL = parseURL(this._proxyURL);
|
||||
const parsedURL = parseURL(this._url)
|
||||
const parsedProxyURL = parseURL(this._proxyURL)
|
||||
const proxyOverrides = _.omit({
|
||||
secureEndpoint: (parsedURL.protocol === 'wss:'),
|
||||
secureProxy: (parsedProxyURL.protocol === 'https:'),
|
||||
@@ -143,48 +227,49 @@ class Connection extends EventEmitter {
|
||||
key: this._key,
|
||||
passphrase: this._passphrase,
|
||||
cert: this._certificate
|
||||
}, _.isUndefined);
|
||||
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides);
|
||||
let HttpsProxyAgent;
|
||||
}, _.isUndefined)
|
||||
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides)
|
||||
let HttpsProxyAgent
|
||||
try {
|
||||
HttpsProxyAgent = require('https-proxy-agent');
|
||||
HttpsProxyAgent = require('https-proxy-agent')
|
||||
} catch (error) {
|
||||
throw new Error('"proxy" option is not supported in the browser');
|
||||
throw new Error('"proxy" option is not supported in the browser')
|
||||
}
|
||||
options.agent = new HttpsProxyAgent(proxyOptions);
|
||||
options.agent = new HttpsProxyAgent(proxyOptions)
|
||||
}
|
||||
if (this._authorization !== undefined) {
|
||||
const base64 = new Buffer(this._authorization).toString('base64');
|
||||
options.headers = {Authorization: `Basic ${base64}`};
|
||||
const base64 = new Buffer(this._authorization).toString('base64')
|
||||
options.headers = {Authorization: `Basic ${base64}`}
|
||||
}
|
||||
const optionsOverrides = _.omit({
|
||||
ca: this._trustedCertificates,
|
||||
key: this._key,
|
||||
passphrase: this._passphrase,
|
||||
cert: this._certificate
|
||||
}, _.isUndefined);
|
||||
const websocketOptions = _.assign({}, options, optionsOverrides);
|
||||
const websocket = new WebSocket(this._url, null, websocketOptions);
|
||||
}, _.isUndefined)
|
||||
const websocketOptions = _.assign({}, options, optionsOverrides)
|
||||
const websocket = new WebSocket(this._url, null, websocketOptions)
|
||||
// we will have a listener for each outstanding request,
|
||||
// so we have to raise the limit (the default is 10)
|
||||
if (typeof websocket.setMaxListeners === 'function') {
|
||||
websocket.setMaxListeners(Infinity);
|
||||
websocket.setMaxListeners(Infinity)
|
||||
}
|
||||
return websocket;
|
||||
return websocket
|
||||
}
|
||||
|
||||
connect() {
|
||||
this._clearReconnectTimer()
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._url) {
|
||||
reject(new ConnectionError(
|
||||
'Cannot connect because no server was specified'));
|
||||
'Cannot connect because no server was specified'))
|
||||
}
|
||||
if (this._state === WebSocket.OPEN) {
|
||||
resolve();
|
||||
resolve()
|
||||
} else if (this._state === WebSocket.CONNECTING) {
|
||||
this._ws.once('open', resolve);
|
||||
this._ws.once('open', resolve)
|
||||
} else {
|
||||
this._ws = this._createWebSocket();
|
||||
this._ws = this._createWebSocket()
|
||||
// when an error causes the connection to close, the close event
|
||||
// should still be emitted; the "ws" documentation says: "The close
|
||||
// event is also emitted when then underlying net.Socket closes the
|
||||
@@ -192,142 +277,156 @@ class Connection extends EventEmitter {
|
||||
// In case if there is connection error (say, server is not responding)
|
||||
// we must return this error to connection's caller. After successful
|
||||
// opening, we will forward all errors to main api object.
|
||||
this._onOpenErrorBound = this._onOpenError.bind(this, reject);
|
||||
this._ws.once('error', this._onOpenErrorBound);
|
||||
this._ws.on('message', this._onMessage.bind(this));
|
||||
this._onOpenErrorBound = this._onOpenError.bind(this, reject)
|
||||
this._ws.once('error', this._onOpenErrorBound)
|
||||
this._ws.on('message', this._onMessage.bind(this))
|
||||
// in browser close event can came before open event, so we must
|
||||
// resolve connect's promise after reconnect in that case.
|
||||
// after open event we will rebound _onUnexpectedCloseBound
|
||||
// without resolve and reject functions
|
||||
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this,
|
||||
resolve, reject);
|
||||
this._ws.once('close', this._onUnexpectedCloseBound);
|
||||
this._ws.once('open', () => this._onOpen().then(resolve, reject));
|
||||
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this, true,
|
||||
resolve, reject)
|
||||
this._ws.once('close', this._onUnexpectedCloseBound)
|
||||
this._ws.once('open', () => this._onOpen().then(resolve, reject))
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
return this._disconnect(true)
|
||||
}
|
||||
|
||||
_disconnect(calledByUser) {
|
||||
if (calledByUser) {
|
||||
this._clearReconnectTimer()
|
||||
this._retry = 0
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
if (this._state === WebSocket.CLOSED) {
|
||||
resolve();
|
||||
resolve()
|
||||
} else if (this._state === WebSocket.CLOSING) {
|
||||
this._ws.once('close', resolve);
|
||||
this._ws.once('close', resolve)
|
||||
} else {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound);
|
||||
this._ws.once('close', () => {
|
||||
this._ws = null;
|
||||
this._isReady = false;
|
||||
resolve();
|
||||
});
|
||||
this._ws.close();
|
||||
if (this._onUnexpectedCloseBound) {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound)
|
||||
this._onUnexpectedCloseBound = null
|
||||
}
|
||||
this._ws.once('close', code => {
|
||||
this._ws = null
|
||||
this._isReady = false
|
||||
if (calledByUser) {
|
||||
this.emit('disconnected', code || 1000) // 1000 - CLOSE_NORMAL
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
this._ws.close()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
reconnect() {
|
||||
return this.disconnect().then(() => this.connect());
|
||||
return this.disconnect().then(() => this.connect())
|
||||
}
|
||||
|
||||
_whenReady(promise) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._shouldBeConnected) {
|
||||
reject(new NotConnectedError());
|
||||
reject(new NotConnectedError())
|
||||
} else if (this._state === WebSocket.OPEN && this._isReady) {
|
||||
promise.then(resolve, reject);
|
||||
promise.then(resolve, reject)
|
||||
} else {
|
||||
this.once('connected', () => promise.then(resolve, reject));
|
||||
this.once('connected', () => promise.then(resolve, reject))
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
getLedgerVersion() {
|
||||
return this._whenReady(Promise.resolve(this._ledgerVersion));
|
||||
return this._whenReady(Promise.resolve(this._ledgerVersion))
|
||||
}
|
||||
|
||||
hasLedgerVersions(lowLedgerVersion, highLedgerVersion) {
|
||||
return this._whenReady(Promise.resolve(
|
||||
this._availableLedgerVersions.containsRange(
|
||||
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)));
|
||||
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)))
|
||||
}
|
||||
|
||||
hasLedgerVersion(ledgerVersion) {
|
||||
return this.hasLedgerVersions(ledgerVersion, ledgerVersion);
|
||||
return this.hasLedgerVersions(ledgerVersion, ledgerVersion)
|
||||
}
|
||||
|
||||
_send(message) {
|
||||
if (this._trace) {
|
||||
this._console.log(message);
|
||||
this._console.log(message)
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._ws.send(message, undefined, (error, result) => {
|
||||
if (error) {
|
||||
reject(new DisconnectedError(error.message));
|
||||
reject(new DisconnectedError(error.message))
|
||||
} else {
|
||||
resolve(result);
|
||||
resolve(result)
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
request(request, timeout) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._shouldBeConnected) {
|
||||
reject(new NotConnectedError());
|
||||
reject(new NotConnectedError())
|
||||
}
|
||||
|
||||
let timer = null;
|
||||
const self = this;
|
||||
const id = this._nextRequestID;
|
||||
this._nextRequestID += 1;
|
||||
const eventName = id.toString();
|
||||
let timer = null
|
||||
const self = this
|
||||
const id = this._nextRequestID
|
||||
this._nextRequestID += 1
|
||||
const eventName = id.toString()
|
||||
|
||||
function onDisconnect() {
|
||||
clearTimeout(timer);
|
||||
self.removeAllListeners(eventName);
|
||||
reject(new DisconnectedError());
|
||||
clearTimeout(timer)
|
||||
self.removeAllListeners(eventName)
|
||||
reject(new DisconnectedError())
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
clearTimeout(timer);
|
||||
self.removeAllListeners(eventName);
|
||||
clearTimeout(timer)
|
||||
self.removeAllListeners(eventName)
|
||||
if (self._ws !== null) {
|
||||
self._ws.removeListener('close', onDisconnect);
|
||||
self._ws.removeListener('close', onDisconnect)
|
||||
}
|
||||
}
|
||||
|
||||
function _resolve(response) {
|
||||
cleanup();
|
||||
resolve(response);
|
||||
cleanup()
|
||||
resolve(response)
|
||||
}
|
||||
|
||||
function _reject(error) {
|
||||
cleanup();
|
||||
reject(error);
|
||||
cleanup()
|
||||
reject(error)
|
||||
}
|
||||
|
||||
this.once(eventName, response => {
|
||||
if (response.status === 'error') {
|
||||
_reject(new RippledError(response.error));
|
||||
_reject(new RippledError(response.error))
|
||||
} else if (response.status === 'success') {
|
||||
_resolve(response.result);
|
||||
_resolve(response.result)
|
||||
} else {
|
||||
_reject(new ResponseFormatError(
|
||||
'unrecognized status: ' + response.status));
|
||||
'unrecognized status: ' + response.status))
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
this._ws.once('close', onDisconnect);
|
||||
this._ws.once('close', onDisconnect)
|
||||
|
||||
// JSON.stringify automatically removes keys with value of 'undefined'
|
||||
const message = JSON.stringify(Object.assign({}, request, {id}));
|
||||
const message = JSON.stringify(Object.assign({}, request, {id}))
|
||||
|
||||
this._whenReady(this._send(message)).then(() => {
|
||||
const delay = timeout || this._timeout;
|
||||
timer = setTimeout(() => _reject(new TimeoutError()), delay);
|
||||
}).catch(_reject);
|
||||
});
|
||||
const delay = timeout || this._timeout
|
||||
timer = setTimeout(() => _reject(new TimeoutError()), delay)
|
||||
}).catch(_reject)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Connection;
|
||||
module.exports = Connection
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
const flagIndices = require('./txflags').txFlagIndices.AccountSet;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const flagIndices = require('./txflags').txFlagIndices.AccountSet
|
||||
|
||||
const accountRootFlags = {
|
||||
PasswordSpent: 0x00010000, // password set fee is spent
|
||||
@@ -10,7 +10,7 @@ const accountRootFlags = {
|
||||
NoFreeze: 0x00200000, // permanently disallowed freezing trustlines
|
||||
GlobalFreeze: 0x00400000, // trustlines globally frozen
|
||||
DefaultRipple: 0x00800000
|
||||
};
|
||||
}
|
||||
|
||||
const AccountFlags = {
|
||||
passwordSpent: accountRootFlags.PasswordSpent,
|
||||
@@ -21,7 +21,7 @@ const AccountFlags = {
|
||||
noFreeze: accountRootFlags.NoFreeze,
|
||||
globalFreeze: accountRootFlags.GlobalFreeze,
|
||||
defaultRipple: accountRootFlags.DefaultRipple
|
||||
};
|
||||
}
|
||||
|
||||
const AccountFlagIndices = {
|
||||
requireDestinationTag: flagIndices.asfRequireDest,
|
||||
@@ -32,7 +32,7 @@ const AccountFlagIndices = {
|
||||
noFreeze: flagIndices.asfNoFreeze,
|
||||
globalFreeze: flagIndices.asfGlobalFreeze,
|
||||
defaultRipple: flagIndices.asfDefaultRipple
|
||||
};
|
||||
}
|
||||
|
||||
const AccountFields = {
|
||||
EmailHash: {name: 'emailHash', encoding: 'hex',
|
||||
@@ -40,10 +40,10 @@ const AccountFields = {
|
||||
MessageKey: {name: 'messageKey'},
|
||||
Domain: {name: 'domain', encoding: 'hex'},
|
||||
TransferRate: {name: 'transferRate', defaults: 0, shift: 9}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
AccountFields,
|
||||
AccountFlagIndices,
|
||||
AccountFlags
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
'use strict';
|
||||
const util = require('util');
|
||||
const browserHacks = require('./browser-hacks');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const util = require('util')
|
||||
const browserHacks = require('./browser-hacks')
|
||||
|
||||
// this is needed because extending builtins doesn't work in babel 6.x
|
||||
function extendableBuiltin(cls) {
|
||||
function ExtendableBuiltin() {
|
||||
cls.apply(this, arguments);
|
||||
cls.apply(this, arguments)
|
||||
}
|
||||
ExtendableBuiltin.prototype = Object.create(cls.prototype);
|
||||
browserHacks.setPrototypeOf(ExtendableBuiltin, cls);
|
||||
return ExtendableBuiltin;
|
||||
ExtendableBuiltin.prototype = Object.create(cls.prototype)
|
||||
browserHacks.setPrototypeOf(ExtendableBuiltin, cls)
|
||||
return ExtendableBuiltin
|
||||
}
|
||||
|
||||
class RippleError extends extendableBuiltin(Error) {
|
||||
constructor(message, data) {
|
||||
super(message);
|
||||
super(message)
|
||||
|
||||
this.name = browserHacks.getConstructorName(this);
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
this.name = browserHacks.getConstructorName(this)
|
||||
this.message = message
|
||||
this.data = data
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, this.constructor.name);
|
||||
Error.captureStackTrace(this, this.constructor.name)
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
let result = '[' + this.name + '(' + this.message;
|
||||
let result = '[' + this.name + '(' + this.message
|
||||
if (this.data) {
|
||||
result += ', ' + util.inspect(this.data);
|
||||
result += ', ' + util.inspect(this.data)
|
||||
}
|
||||
result += ')]';
|
||||
return result;
|
||||
result += ')]'
|
||||
return result
|
||||
}
|
||||
|
||||
/* console.log in node uses util.inspect on object, and util.inspect allows
|
||||
us to cutomize its output:
|
||||
https://nodejs.org/api/util.html#util_custom_inspect_function_on_objects */
|
||||
inspect() {
|
||||
return this.toString();
|
||||
return this.toString()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ class NotConnectedError extends ConnectionError {}
|
||||
|
||||
class DisconnectedError extends ConnectionError {}
|
||||
|
||||
class RippledNotInitializedError extends ConnectionError {}
|
||||
|
||||
class TimeoutError extends ConnectionError {}
|
||||
|
||||
class ResponseFormatError extends ConnectionError {}
|
||||
@@ -61,20 +63,20 @@ class ValidationError extends RippleError {}
|
||||
|
||||
class NotFoundError extends RippleError {
|
||||
constructor(message) {
|
||||
super(message || 'Not found');
|
||||
super(message || 'Not found')
|
||||
}
|
||||
}
|
||||
|
||||
class MissingLedgerHistoryError extends RippleError {
|
||||
constructor(message) {
|
||||
super(message || 'Server is missing ledger history in the specified range');
|
||||
super(message || 'Server is missing ledger history in the specified range')
|
||||
}
|
||||
}
|
||||
|
||||
class PendingLedgerVersionError extends RippleError {
|
||||
constructor(message) {
|
||||
super(message || 'maxLedgerVersion is greater than server\'s'
|
||||
+ ' most recent validated ledger');
|
||||
+ ' most recent validated ledger')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +87,7 @@ module.exports = {
|
||||
RippledError,
|
||||
NotConnectedError,
|
||||
DisconnectedError,
|
||||
RippledNotInitializedError,
|
||||
TimeoutError,
|
||||
ResponseFormatError,
|
||||
ValidationError,
|
||||
@@ -92,4 +95,4 @@ module.exports = {
|
||||
PendingLedgerVersionError,
|
||||
MissingLedgerHistoryError,
|
||||
LedgerVersionError
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('./utils')
|
||||
|
||||
module.exports = {
|
||||
Connection: require('./connection'),
|
||||
@@ -19,4 +19,4 @@ module.exports = {
|
||||
iso8601ToRippleTime: utils.iso8601ToRippleTime,
|
||||
rippleTimeToISO8601: utils.rippleTimeToISO8601,
|
||||
isValidSecret: utils.isValidSecret
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const ranges = Symbol();
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const assert = require('assert')
|
||||
const ranges = Symbol()
|
||||
|
||||
function mergeIntervals(intervals: Array<[number, number]>) {
|
||||
const stack = [[-Infinity, -Infinity]];
|
||||
const stack = [[-Infinity, -Infinity]]
|
||||
_.forEach(_.sortBy(intervals, x => x[0]), interval => {
|
||||
const lastInterval = stack.pop();
|
||||
const lastInterval = stack.pop()
|
||||
if (interval[0] <= lastInterval[1] + 1) {
|
||||
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])]);
|
||||
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])])
|
||||
} else {
|
||||
stack.push(lastInterval);
|
||||
stack.push(interval);
|
||||
stack.push(lastInterval)
|
||||
stack.push(interval)
|
||||
}
|
||||
});
|
||||
return stack.slice(1);
|
||||
})
|
||||
return stack.slice(1)
|
||||
}
|
||||
|
||||
class RangeSet {
|
||||
constructor() {
|
||||
this.reset();
|
||||
this.reset()
|
||||
}
|
||||
|
||||
reset() {
|
||||
this[ranges] = [];
|
||||
this[ranges] = []
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return this[ranges].map(range =>
|
||||
range[0].toString() + '-' + range[1].toString()).join(',');
|
||||
range[0].toString() + '-' + range[1].toString()).join(',')
|
||||
}
|
||||
|
||||
addRange(start: number, end: number) {
|
||||
assert(start <= end, 'invalid range');
|
||||
this[ranges] = mergeIntervals(this[ranges].concat([[start, end]]));
|
||||
assert(start <= end, 'invalid range')
|
||||
this[ranges] = mergeIntervals(this[ranges].concat([[start, end]]))
|
||||
}
|
||||
|
||||
addValue(value: number) {
|
||||
this.addRange(value, value);
|
||||
this.addRange(value, value)
|
||||
}
|
||||
|
||||
parseAndAddRanges(rangesString: string) {
|
||||
const rangeStrings = rangesString.split(',');
|
||||
const rangeStrings = rangesString.split(',')
|
||||
_.forEach(rangeStrings, rangeString => {
|
||||
const range = rangeString.split('-').map(Number);
|
||||
this.addRange(range[0], range.length === 1 ? range[0] : range[1]);
|
||||
});
|
||||
const range = rangeString.split('-').map(Number)
|
||||
this.addRange(range[0], range.length === 1 ? range[0] : range[1])
|
||||
})
|
||||
}
|
||||
|
||||
containsRange(start: number, end: number) {
|
||||
return _.some(this[ranges], range => range[0] <= start && range[1] >= end);
|
||||
return _.some(this[ranges], range => range[0] <= start && range[1] >= end)
|
||||
}
|
||||
|
||||
containsValue(value: number) {
|
||||
return this.containsRange(value, value);
|
||||
return this.containsRange(value, value)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.RangeSet = RangeSet;
|
||||
module.exports.RangeSet = RangeSet
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// flow is disabled for this file until support for requiring json is added:
|
||||
// https://github.com/facebook/flow/issues/167
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const Ajv = require('ajv');
|
||||
const ValidationError = require('./errors').ValidationError;
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
const {isValidSecret} = require('./utils');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const assert = require('assert')
|
||||
const Ajv = require('ajv')
|
||||
const ValidationError = require('./errors').ValidationError
|
||||
const {isValidAddress} = require('ripple-address-codec')
|
||||
const {isValidSecret} = require('./utils')
|
||||
|
||||
function loadSchemas() {
|
||||
// listed explicitly for webpack (instead of scanning schemas directory)
|
||||
@@ -56,7 +56,7 @@ function loadSchemas() {
|
||||
require('./schemas/output/submit.json'),
|
||||
require('./schemas/output/get-account-info.json'),
|
||||
require('./schemas/output/get-balances.json'),
|
||||
require('./schemas/output/get-balance-sheet'),
|
||||
require('./schemas/output/get-balance-sheet.json'),
|
||||
require('./schemas/output/get-ledger.json'),
|
||||
require('./schemas/output/get-orderbook.json'),
|
||||
require('./schemas/output/get-orders.json'),
|
||||
@@ -91,32 +91,32 @@ function loadSchemas() {
|
||||
require('./schemas/input/prepare-suspended-payment-creation.json'),
|
||||
require('./schemas/input/prepare-suspended-payment-cancellation.json'),
|
||||
require('./schemas/input/prepare-suspended-payment-execution.json'),
|
||||
require('./schemas/input/compute-ledger-hash'),
|
||||
require('./schemas/input/compute-ledger-hash.json'),
|
||||
require('./schemas/input/sign.json'),
|
||||
require('./schemas/input/submit.json'),
|
||||
require('./schemas/input/generate-address.json'),
|
||||
require('./schemas/input/combine.json')
|
||||
];
|
||||
const titles = _.map(schemas, schema => schema.title);
|
||||
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
|
||||
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates);
|
||||
const ajv = new Ajv();
|
||||
_.forEach(schemas, schema => ajv.addSchema(schema, schema.title));
|
||||
ajv.addFormat('address', isValidAddress);
|
||||
ajv.addFormat('secret', isValidSecret);
|
||||
return ajv;
|
||||
]
|
||||
const titles = _.map(schemas, schema => schema.title)
|
||||
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1))
|
||||
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates)
|
||||
const ajv = new Ajv()
|
||||
_.forEach(schemas, schema => ajv.addSchema(schema, schema.title))
|
||||
ajv.addFormat('address', isValidAddress)
|
||||
ajv.addFormat('secret', isValidSecret)
|
||||
return ajv
|
||||
}
|
||||
|
||||
const ajv = loadSchemas();
|
||||
const ajv = loadSchemas()
|
||||
|
||||
function schemaValidate(schemaName: string, object: any): void {
|
||||
const isValid = ajv.validate(schemaName, object);
|
||||
const isValid = ajv.validate(schemaName, object)
|
||||
if (!isValid) {
|
||||
throw new ValidationError(ajv.errorsText());
|
||||
throw new ValidationError(ajv.errorsText())
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
schemaValidate,
|
||||
isValidSecret
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"description": " The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase."
|
||||
},
|
||||
"transferRate": {
|
||||
"description": " The fee to charge when users transfer this account’s issuances, represented as billionths of a unit. Use `null` to set no fee.",
|
||||
"description": " 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.",
|
||||
"oneOf": [
|
||||
{"type": "null"},
|
||||
{"type": "number", "minimum": 1, "maximum": 4.294967295}
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
"format": "date-time",
|
||||
"description": "Time after which the offer is no longer active, as an [ISO 8601 date-time](https://en.wikipedia.org/wiki/ISO_8601)."
|
||||
},
|
||||
"orderToReplace": {
|
||||
"$ref": "sequence",
|
||||
"description": "The [account sequence number](#account-sequence-number) of an order to cancel before the new order is created, effectively replacing the old order."
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"required": ["direction", "quantity", "totalPrice"],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils');
|
||||
import type {Connection} from './connection';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils')
|
||||
import type {Connection} from './connection'
|
||||
|
||||
export type GetServerInfoResponse = {
|
||||
buildVersion: string,
|
||||
@@ -34,44 +34,46 @@ export type GetServerInfoResponse = {
|
||||
|
||||
function renameKeys(object, mapping) {
|
||||
_.forEach(mapping, (to, from) => {
|
||||
object[to] = object[from];
|
||||
delete object[from];
|
||||
});
|
||||
object[to] = object[from]
|
||||
delete object[from]
|
||||
})
|
||||
}
|
||||
|
||||
function getServerInfo(connection: Connection): Promise<GetServerInfoResponse> {
|
||||
return connection.request({command: 'server_info'}).then(response => {
|
||||
const info = convertKeysFromSnakeCaseToCamelCase(response.info);
|
||||
renameKeys(info, {hostid: 'hostID'});
|
||||
renameKeys(info.validatedLedger, {
|
||||
baseFeeXrp: 'baseFeeXRP',
|
||||
reserveBaseXrp: 'reserveBaseXRP',
|
||||
reserveIncXrp: 'reserveIncrementXRP',
|
||||
seq: 'ledgerVersion'
|
||||
});
|
||||
info.validatedLedger.baseFeeXRP =
|
||||
info.validatedLedger.baseFeeXRP.toString();
|
||||
info.validatedLedger.reserveBaseXRP =
|
||||
info.validatedLedger.reserveBaseXRP.toString();
|
||||
info.validatedLedger.reserveIncrementXRP =
|
||||
info.validatedLedger.reserveIncrementXRP.toString();
|
||||
return info;
|
||||
});
|
||||
const info = convertKeysFromSnakeCaseToCamelCase(response.info)
|
||||
renameKeys(info, {hostid: 'hostID'})
|
||||
if (info.validatedLedger) {
|
||||
renameKeys(info.validatedLedger, {
|
||||
baseFeeXrp: 'baseFeeXRP',
|
||||
reserveBaseXrp: 'reserveBaseXRP',
|
||||
reserveIncXrp: 'reserveIncrementXRP',
|
||||
seq: 'ledgerVersion'
|
||||
})
|
||||
info.validatedLedger.baseFeeXRP =
|
||||
info.validatedLedger.baseFeeXRP.toString()
|
||||
info.validatedLedger.reserveBaseXRP =
|
||||
info.validatedLedger.reserveBaseXRP.toString()
|
||||
info.validatedLedger.reserveIncrementXRP =
|
||||
info.validatedLedger.reserveIncrementXRP.toString()
|
||||
}
|
||||
return info
|
||||
})
|
||||
}
|
||||
|
||||
function computeFeeFromServerInfo(cushion: number,
|
||||
serverInfo: GetServerInfoResponse
|
||||
): number {
|
||||
return (Number(serverInfo.validatedLedger.baseFeeXRP)
|
||||
* Number(serverInfo.loadFactor) * cushion).toString();
|
||||
* Number(serverInfo.loadFactor) * cushion).toString()
|
||||
}
|
||||
|
||||
function getFee(connection: Connection, cushion: number) {
|
||||
return getServerInfo(connection).then(
|
||||
_.partial(computeFeeFromServerInfo, cushion));
|
||||
_.partial(computeFeeFromServerInfo, cushion))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getServerInfo,
|
||||
getFee
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
const txFlags = {
|
||||
// Universal flags can apply to any transaction type
|
||||
@@ -36,7 +36,7 @@ const txFlags = {
|
||||
PartialPayment: 0x00020000,
|
||||
LimitQuality: 0x00040000
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// The following are integer (as opposed to bit) flags
|
||||
// that can be set for particular transactions in the
|
||||
@@ -52,9 +52,9 @@ const txFlagIndices = {
|
||||
asfGlobalFreeze: 7,
|
||||
asfDefaultRipple: 8
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
txFlags,
|
||||
txFlagIndices
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
export type RippledAmountIOU = {
|
||||
currency: string,
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const {deriveKeypair} = require('ripple-keypairs');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const {deriveKeypair} = require('ripple-keypairs')
|
||||
|
||||
import type {Amount, RippledAmount} from './types.js';
|
||||
import type {Amount, RippledAmount} from './types.js'
|
||||
|
||||
function isValidSecret(secret: string): boolean {
|
||||
try {
|
||||
deriveKeypair(secret);
|
||||
return true;
|
||||
deriveKeypair(secret)
|
||||
return true
|
||||
} catch (err) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function dropsToXrp(drops: string): string {
|
||||
return (new BigNumber(drops)).dividedBy(1000000.0).toString();
|
||||
return (new BigNumber(drops)).dividedBy(1000000.0).toString()
|
||||
}
|
||||
|
||||
function xrpToDrops(xrp: string): string {
|
||||
return (new BigNumber(xrp)).times(1000000.0).floor().toString();
|
||||
return (new BigNumber(xrp)).times(1000000.0).floor().toString()
|
||||
}
|
||||
|
||||
function toRippledAmount(amount: Amount): RippledAmount {
|
||||
if (amount.currency === 'XRP') {
|
||||
return xrpToDrops(amount.value);
|
||||
return xrpToDrops(amount.value)
|
||||
}
|
||||
return {
|
||||
currency: amount.currency,
|
||||
issuer: amount.counterparty ? amount.counterparty :
|
||||
(amount.issuer ? amount.issuer : undefined),
|
||||
value: amount.value
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
|
||||
if (typeof obj === 'object') {
|
||||
let newKey;
|
||||
let newKey
|
||||
return _.reduce(obj, (result, value, key) => {
|
||||
newKey = key;
|
||||
newKey = key
|
||||
// taking this out of function leads to error in PhantomJS
|
||||
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
|
||||
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g
|
||||
if (FINDSNAKE.test(key)) {
|
||||
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase());
|
||||
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
|
||||
}
|
||||
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value);
|
||||
return result;
|
||||
}, {});
|
||||
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
|
||||
return result
|
||||
}, {})
|
||||
}
|
||||
return obj;
|
||||
return obj
|
||||
}
|
||||
|
||||
function removeUndefined(obj: Object): Object {
|
||||
return _.omit(obj, _.isUndefined);
|
||||
return _.omit(obj, _.isUndefined)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +62,7 @@ function removeUndefined(obj: Object): Object {
|
||||
*
|
||||
*/
|
||||
function rippleToUnixTimestamp(rpepoch: number): number {
|
||||
return (rpepoch + 0x386D4380) * 1000;
|
||||
return (rpepoch + 0x386D4380) * 1000
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,15 +70,15 @@ function rippleToUnixTimestamp(rpepoch: number): number {
|
||||
* @return {Number} seconds since ripple epoch ( 1/1/2000 GMT)
|
||||
*/
|
||||
function unixToRippleTimestamp(timestamp: number): number {
|
||||
return Math.round(timestamp / 1000) - 0x386D4380;
|
||||
return Math.round(timestamp / 1000) - 0x386D4380
|
||||
}
|
||||
|
||||
function rippleTimeToISO8601(rippleTime: number): string {
|
||||
return new Date(rippleToUnixTimestamp(rippleTime)).toISOString();
|
||||
return new Date(rippleToUnixTimestamp(rippleTime)).toISOString()
|
||||
}
|
||||
|
||||
function iso8601ToRippleTime(iso8601: string): number {
|
||||
return unixToRippleTimestamp(Date.parse(iso8601));
|
||||
return unixToRippleTimestamp(Date.parse(iso8601))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -90,4 +90,4 @@ module.exports = {
|
||||
rippleTimeToISO8601,
|
||||
iso8601ToRippleTime,
|
||||
isValidSecret
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const ValidationError = require('./errors').ValidationError;
|
||||
const schemaValidate = require('./schema-validator').schemaValidate;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const ValidationError = require('./errors').ValidationError
|
||||
const schemaValidate = require('./schema-validator').schemaValidate
|
||||
|
||||
function error(text) {
|
||||
return new ValidationError(text);
|
||||
return new ValidationError(text)
|
||||
}
|
||||
|
||||
function validateLedgerRange(options) {
|
||||
if (!_.isUndefined(options) && !_.isUndefined(options.minLedgerVersion)
|
||||
&& !_.isUndefined(options.maxLedgerVersion)) {
|
||||
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
|
||||
throw error('minLedgerVersion must not be greater than maxLedgerVersion');
|
||||
throw error('minLedgerVersion must not be greater than maxLedgerVersion')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateOptions(schema, instance) {
|
||||
schemaValidate(schema, instance);
|
||||
validateLedgerRange(instance.options);
|
||||
schemaValidate(schema, instance)
|
||||
validateLedgerRange(instance.options)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -53,4 +53,4 @@ module.exports = {
|
||||
generateAddress: _.partial(schemaValidate, 'generateAddressParameters'),
|
||||
apiOptions: _.partial(schemaValidate, 'api-options'),
|
||||
instructions: _.partial(schemaValidate, 'instructions')
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
const {EventEmitter} = require('events');
|
||||
const {EventEmitter} = require('events')
|
||||
|
||||
function unsused() {}
|
||||
|
||||
@@ -10,50 +10,50 @@ function unsused() {}
|
||||
*/
|
||||
class WSWrapper extends EventEmitter {
|
||||
constructor(url, protocols = null, websocketOptions = {}) {
|
||||
super();
|
||||
unsused(protocols);
|
||||
unsused(websocketOptions);
|
||||
this.setMaxListeners(Infinity);
|
||||
super()
|
||||
unsused(protocols)
|
||||
unsused(websocketOptions)
|
||||
this.setMaxListeners(Infinity)
|
||||
|
||||
this._ws = new WebSocket(url);
|
||||
this._ws = new WebSocket(url)
|
||||
|
||||
this._ws.onclose = () => {
|
||||
this.emit('close');
|
||||
};
|
||||
this.emit('close')
|
||||
}
|
||||
|
||||
this._ws.onopen = () => {
|
||||
this.emit('open');
|
||||
};
|
||||
this.emit('open')
|
||||
}
|
||||
|
||||
this._ws.onerror = error => {
|
||||
this.emit('error', error);
|
||||
};
|
||||
this.emit('error', error)
|
||||
}
|
||||
|
||||
this._ws.onmessage = message => {
|
||||
this.emit('message', message.data);
|
||||
};
|
||||
this.emit('message', message.data)
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.readyState === 1) {
|
||||
this._ws.close();
|
||||
this._ws.close()
|
||||
}
|
||||
}
|
||||
|
||||
send(message) {
|
||||
this._ws.send(message);
|
||||
this._ws.send(message)
|
||||
}
|
||||
|
||||
get readyState() {
|
||||
return this._ws.readyState;
|
||||
return this._ws.readyState
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WSWrapper.CONNECTING = 0;
|
||||
WSWrapper.OPEN = 1;
|
||||
WSWrapper.CLOSING = 2;
|
||||
WSWrapper.CLOSED = 3;
|
||||
WSWrapper.CONNECTING = 0
|
||||
WSWrapper.OPEN = 1
|
||||
WSWrapper.CLOSING = 2
|
||||
WSWrapper.CLOSED = 3
|
||||
|
||||
module.exports = WSWrapper;
|
||||
module.exports = WSWrapper
|
||||
|
||||
|
||||
78
src/http.js
78
src/http.js
@@ -1,86 +1,86 @@
|
||||
/* eslint-disable new-cap */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
const assert = require('assert');
|
||||
const _ = require('lodash');
|
||||
const jayson = require('jayson');
|
||||
const assert = require('assert')
|
||||
const _ = require('lodash')
|
||||
const jayson = require('jayson')
|
||||
|
||||
const RippleAPI = require('./api').RippleAPI;
|
||||
const RippleAPI = require('./api').RippleAPI
|
||||
|
||||
|
||||
/* istanbul ignore next */
|
||||
function createHTTPServer(options, httpPort) {
|
||||
const rippleAPI = new RippleAPI(options);
|
||||
const rippleAPI = new RippleAPI(options)
|
||||
|
||||
const methodNames = _.filter(_.keys(RippleAPI.prototype), k => {
|
||||
return typeof RippleAPI.prototype[k] === 'function'
|
||||
&& k !== 'connect'
|
||||
&& k !== 'disconnect'
|
||||
&& k !== 'constructor'
|
||||
&& k !== 'RippleAPI';
|
||||
});
|
||||
&& k !== 'RippleAPI'
|
||||
})
|
||||
|
||||
function applyPromiseWithCallback(fnName, callback, args_) {
|
||||
try {
|
||||
let args = args_;
|
||||
let args = args_
|
||||
if (!_.isArray(args_)) {
|
||||
const fnParameters = jayson.Utils.getParameterNames(rippleAPI[fnName]);
|
||||
args = fnParameters.map(name => args_[name]);
|
||||
const defaultArgs = _.omit(args_, fnParameters);
|
||||
const fnParameters = jayson.Utils.getParameterNames(rippleAPI[fnName])
|
||||
args = fnParameters.map(name => args_[name])
|
||||
const defaultArgs = _.omit(args_, fnParameters)
|
||||
assert(_.size(defaultArgs) <= 1,
|
||||
'Function must have no more than one default argument');
|
||||
'Function must have no more than one default argument')
|
||||
if (_.size(defaultArgs) > 0) {
|
||||
args.push(defaultArgs[_.keys(defaultArgs)[0]]);
|
||||
args.push(defaultArgs[_.keys(defaultArgs)[0]])
|
||||
}
|
||||
}
|
||||
Promise.resolve(rippleAPI[fnName].apply(rippleAPI, args))
|
||||
Promise.resolve(rippleAPI[fnName](...args))
|
||||
.then(res => callback(null, res))
|
||||
.catch(err => {
|
||||
callback({code: 99, message: err.message, data: {name: err.name}});
|
||||
});
|
||||
callback({code: 99, message: err.message, data: {name: err.name}})
|
||||
})
|
||||
} catch (err) {
|
||||
callback({code: 99, message: err.message, data: {name: err.name}});
|
||||
callback({code: 99, message: err.message, data: {name: err.name}})
|
||||
}
|
||||
}
|
||||
|
||||
const methods = {};
|
||||
const methods = {}
|
||||
_.forEach(methodNames, fn => {
|
||||
methods[fn] = jayson.Method((args, cb) => {
|
||||
applyPromiseWithCallback(fn, cb, args);
|
||||
}, {collect: true});
|
||||
});
|
||||
applyPromiseWithCallback(fn, cb, args)
|
||||
}, {collect: true})
|
||||
})
|
||||
|
||||
const server = jayson.server(methods);
|
||||
let httpServer = null;
|
||||
const server = jayson.server(methods)
|
||||
let httpServer = null
|
||||
|
||||
return {
|
||||
server: server,
|
||||
start: function() {
|
||||
if (httpServer !== null) {
|
||||
return Promise.reject('Already started');
|
||||
return Promise.reject('Already started')
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
return new Promise(resolve => {
|
||||
rippleAPI.connect().then(() => {
|
||||
httpServer = server.http();
|
||||
httpServer.listen(httpPort, resolve);
|
||||
});
|
||||
});
|
||||
httpServer = server.http()
|
||||
httpServer.listen(httpPort, resolve)
|
||||
})
|
||||
})
|
||||
},
|
||||
stop: function() {
|
||||
if (httpServer === null) {
|
||||
return Promise.reject('Not started');
|
||||
return Promise.reject('Not started')
|
||||
}
|
||||
return new Promise((resolve) => {
|
||||
rippleAPI.disconnect();
|
||||
return new Promise(resolve => {
|
||||
rippleAPI.disconnect()
|
||||
httpServer.close(() => {
|
||||
httpServer = null;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
httpServer = null
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
createHTTPServer
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
module.exports = {
|
||||
RippleAPI: require('./api').RippleAPI,
|
||||
@@ -6,4 +6,4 @@ module.exports = {
|
||||
RippleAPIBroadcast: require('./broadcast').RippleAPIBroadcast,
|
||||
// HTTP server is experimental
|
||||
createHTTPServer: require('./http').createHTTPServer
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const {validate, removeUndefined} = utils.common;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('./utils')
|
||||
const {validate, removeUndefined} = utils.common
|
||||
|
||||
type AccountData = {
|
||||
Sequence: number,
|
||||
@@ -39,7 +39,7 @@ type AccountInfoResponse = {
|
||||
}
|
||||
|
||||
function formatAccountInfo(response: AccountDataResponse) {
|
||||
const data = response.account_data;
|
||||
const data = response.account_data
|
||||
return removeUndefined({
|
||||
sequence: data.Sequence,
|
||||
xrpBalance: utils.common.dropsToXrp(data.Balance),
|
||||
@@ -47,20 +47,20 @@ function formatAccountInfo(response: AccountDataResponse) {
|
||||
previousInitiatedTransactionID: data.AccountTxnID,
|
||||
previousAffectingTransactionID: data.PreviousTxnID,
|
||||
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function getAccountInfo(address: string, options: AccountInfoOptions = {}
|
||||
): Promise<AccountInfoResponse> {
|
||||
validate.getAccountInfo({address, options});
|
||||
validate.getAccountInfo({address, options})
|
||||
|
||||
const request = {
|
||||
command: 'account_info',
|
||||
account: address,
|
||||
ledger_index: options.ledgerVersion || 'validated'
|
||||
};
|
||||
}
|
||||
|
||||
return this.connection.request(request).then(formatAccountInfo);
|
||||
return this.connection.request(request).then(formatAccountInfo)
|
||||
}
|
||||
|
||||
module.exports = getAccountInfo;
|
||||
module.exports = getAccountInfo
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const {validate} = utils.common;
|
||||
import type {Amount} from '../common/types.js';
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const {validate} = utils.common
|
||||
import type {Amount} from '../common/types.js'
|
||||
|
||||
type BalanceSheetOptions = {
|
||||
excludeAddresses?: Array<string>,
|
||||
@@ -21,35 +21,35 @@ type GetBalanceSheet = {
|
||||
}
|
||||
|
||||
function formatBalanceSheet(balanceSheet): GetBalanceSheet {
|
||||
const result = {};
|
||||
const result = {}
|
||||
|
||||
if (!_.isUndefined(balanceSheet.balances)) {
|
||||
result.balances = [];
|
||||
result.balances = []
|
||||
_.forEach(balanceSheet.balances, (balances, counterparty) => {
|
||||
_.forEach(balances, (balance) => {
|
||||
result.balances.push(Object.assign({counterparty}, balance));
|
||||
});
|
||||
});
|
||||
_.forEach(balances, balance => {
|
||||
result.balances.push(Object.assign({counterparty}, balance))
|
||||
})
|
||||
})
|
||||
}
|
||||
if (!_.isUndefined(balanceSheet.assets)) {
|
||||
result.assets = [];
|
||||
result.assets = []
|
||||
_.forEach(balanceSheet.assets, (assets, counterparty) => {
|
||||
_.forEach(assets, (balance) => {
|
||||
result.assets.push(Object.assign({counterparty}, balance));
|
||||
});
|
||||
});
|
||||
_.forEach(assets, balance => {
|
||||
result.assets.push(Object.assign({counterparty}, balance))
|
||||
})
|
||||
})
|
||||
}
|
||||
if (!_.isUndefined(balanceSheet.obligations)) {
|
||||
result.obligations = _.map(balanceSheet.obligations, (value, currency) =>
|
||||
({currency, value}));
|
||||
({currency, value}))
|
||||
}
|
||||
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
|
||||
): Promise<GetBalanceSheet> {
|
||||
validate.getBalanceSheet({address, options});
|
||||
validate.getBalanceSheet({address, options})
|
||||
|
||||
return utils.ensureLedgerVersion.call(this, options).then(_options => {
|
||||
const request = {
|
||||
@@ -58,10 +58,10 @@ function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
|
||||
strict: true,
|
||||
hotwallet: _options.excludeAddresses,
|
||||
ledger_index: _options.ledgerVersion
|
||||
};
|
||||
}
|
||||
|
||||
return this.connection.request(request).then(formatBalanceSheet);
|
||||
});
|
||||
return this.connection.request(request).then(formatBalanceSheet)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = getBalanceSheet;
|
||||
module.exports = getBalanceSheet
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const {validate} = utils.common;
|
||||
import type {Connection} from '../common/connection.js';
|
||||
import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('./utils')
|
||||
const {validate} = utils.common
|
||||
import type {Connection} from '../common/connection.js'
|
||||
import type {TrustlinesOptions, Trustline} from './trustlines-types.js'
|
||||
|
||||
|
||||
type Balance = {
|
||||
@@ -19,38 +19,38 @@ function getTrustlineBalanceAmount(trustline: Trustline) {
|
||||
currency: trustline.specification.currency,
|
||||
counterparty: trustline.specification.counterparty,
|
||||
value: trustline.state.balance
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function formatBalances(options, balances) {
|
||||
const result = balances.trustlines.map(getTrustlineBalanceAmount);
|
||||
const result = balances.trustlines.map(getTrustlineBalanceAmount)
|
||||
if (!(options.counterparty ||
|
||||
(options.currency && options.currency !== 'XRP')
|
||||
)) {
|
||||
const xrpBalance = {
|
||||
currency: 'XRP',
|
||||
value: balances.xrp
|
||||
};
|
||||
result.unshift(xrpBalance);
|
||||
}
|
||||
result.unshift(xrpBalance)
|
||||
}
|
||||
if (options.limit && result.length > options.limit) {
|
||||
const toRemove = result.length - options.limit;
|
||||
result.splice(-toRemove, toRemove);
|
||||
const toRemove = result.length - options.limit
|
||||
result.splice(-toRemove, toRemove)
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
function getLedgerVersionHelper(connection: Connection, optionValue?: number
|
||||
): Promise<number> {
|
||||
if (optionValue !== undefined && optionValue !== null) {
|
||||
return Promise.resolve(optionValue);
|
||||
return Promise.resolve(optionValue)
|
||||
}
|
||||
return connection.getLedgerVersion();
|
||||
return connection.getLedgerVersion()
|
||||
}
|
||||
|
||||
function getBalances(address: string, options: TrustlinesOptions = {}
|
||||
): Promise<GetBalances> {
|
||||
validate.getTrustlines({address, options});
|
||||
validate.getTrustlines({address, options})
|
||||
|
||||
return Promise.all([
|
||||
getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
|
||||
@@ -58,7 +58,7 @@ function getBalances(address: string, options: TrustlinesOptions = {}
|
||||
utils.getXRPBalance(this.connection, address, ledgerVersion)),
|
||||
this.getTrustlines(address, options)
|
||||
]).then(results =>
|
||||
formatBalances(options, {xrp: results[0], trustlines: results[1]}));
|
||||
formatBalances(options, {xrp: results[0], trustlines: results[1]}))
|
||||
}
|
||||
|
||||
module.exports = getBalances;
|
||||
module.exports = getBalances
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const {validate} = utils.common;
|
||||
const parseLedger = require('./parse/ledger');
|
||||
import type {GetLedger} from './types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('./utils')
|
||||
const {validate} = utils.common
|
||||
const parseLedger = require('./parse/ledger')
|
||||
import type {GetLedger} from './types.js'
|
||||
|
||||
type LedgerOptions = {
|
||||
ledgerVersion?: number,
|
||||
@@ -14,7 +14,7 @@ type LedgerOptions = {
|
||||
|
||||
|
||||
function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
|
||||
validate.getLedger({options});
|
||||
validate.getLedger({options})
|
||||
|
||||
const request = {
|
||||
command: 'ledger',
|
||||
@@ -22,10 +22,10 @@ function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
|
||||
expand: options.includeAllData,
|
||||
transactions: options.includeTransactions,
|
||||
accounts: options.includeState
|
||||
};
|
||||
}
|
||||
|
||||
return this.connection.request(request).then(response =>
|
||||
parseLedger(response.ledger));
|
||||
parseLedger(response.ledger))
|
||||
}
|
||||
|
||||
module.exports = getLedger;
|
||||
module.exports = getLedger
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const {validate} = utils.common;
|
||||
const parseOrderbookOrder = require('./parse/orderbook-order');
|
||||
import type {Connection} from '../common/connection.js';
|
||||
import type {OrdersOptions, OrderSpecification} from './types.js';
|
||||
import type {Amount, Issue} from '../common/types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const {validate} = utils.common
|
||||
const parseOrderbookOrder = require('./parse/orderbook-order')
|
||||
import type {Connection} from '../common/connection.js'
|
||||
import type {OrdersOptions, OrderSpecification} from './types.js'
|
||||
import type {Amount, Issue} from '../common/types.js'
|
||||
|
||||
type Orderbook = {
|
||||
base: Issue,
|
||||
@@ -46,31 +46,31 @@ function getBookOffers(connection: Connection, account: string,
|
||||
ledger_index: ledgerVersion || 'validated',
|
||||
limit: limit,
|
||||
taker: account
|
||||
})).then(data => data.offers);
|
||||
})).then(data => data.offers)
|
||||
}
|
||||
|
||||
function isSameIssue(a: Amount, b: Amount) {
|
||||
return a.currency === b.currency && a.counterparty === b.counterparty;
|
||||
return a.currency === b.currency && a.counterparty === b.counterparty
|
||||
}
|
||||
|
||||
function directionFilter(direction: string, order: OrderbookItem) {
|
||||
return order.specification.direction === direction;
|
||||
return order.specification.direction === direction
|
||||
}
|
||||
|
||||
function flipOrder(order: OrderbookItem) {
|
||||
const specification = order.specification;
|
||||
const specification = order.specification
|
||||
const flippedSpecification = {
|
||||
quantity: specification.totalPrice,
|
||||
totalPrice: specification.quantity,
|
||||
direction: specification.direction === 'buy' ? 'sell' : 'buy'
|
||||
};
|
||||
const newSpecification = _.merge({}, specification, flippedSpecification);
|
||||
return _.merge({}, order, {specification: newSpecification});
|
||||
}
|
||||
const newSpecification = _.merge({}, specification, flippedSpecification)
|
||||
return _.merge({}, order, {specification: newSpecification})
|
||||
}
|
||||
|
||||
function alignOrder(base: Amount, order: OrderbookItem) {
|
||||
const quantity = order.specification.quantity;
|
||||
return isSameIssue(quantity, base) ? order : flipOrder(order);
|
||||
const quantity = order.specification.quantity
|
||||
return isSameIssue(quantity, base) ? order : flipOrder(order)
|
||||
}
|
||||
|
||||
function formatBidsAndAsks(orderbook: Orderbook, offers) {
|
||||
@@ -84,24 +84,24 @@ function formatBidsAndAsks(orderbook: Orderbook, offers) {
|
||||
// for asks: lowest quality => lowest totalPrice/quantity => lowest price
|
||||
// for both bids and asks, lowest quality is closest to mid-market
|
||||
// we sort the orders so that earlier orders are closer to mid-market
|
||||
const orders = _.sortBy(offers, 'quality').map(parseOrderbookOrder);
|
||||
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base));
|
||||
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'));
|
||||
const asks = alignedOrders.filter(_.partial(directionFilter, 'sell'));
|
||||
return {bids, asks};
|
||||
const orders = _.sortBy(offers, 'quality').map(parseOrderbookOrder)
|
||||
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base))
|
||||
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'))
|
||||
const asks = alignedOrders.filter(_.partial(directionFilter, 'sell'))
|
||||
return {bids, asks}
|
||||
}
|
||||
|
||||
function getOrderbook(address: string, orderbook: Orderbook,
|
||||
options: OrdersOptions = {}
|
||||
): Promise<GetOrderbook> {
|
||||
validate.getOrderbook({address, orderbook, options});
|
||||
validate.getOrderbook({address, orderbook, options})
|
||||
|
||||
const getter = _.partial(getBookOffers, this.connection, address,
|
||||
options.ledgerVersion, options.limit);
|
||||
const getOffers = _.partial(getter, orderbook.base, orderbook.counter);
|
||||
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base);
|
||||
options.ledgerVersion, options.limit)
|
||||
const getOffers = _.partial(getter, orderbook.base, orderbook.counter)
|
||||
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base)
|
||||
return Promise.all([getOffers(), getReverseOffers()]).then(data =>
|
||||
formatBidsAndAsks(orderbook, _.flatten(data)));
|
||||
formatBidsAndAsks(orderbook, _.flatten(data)))
|
||||
}
|
||||
|
||||
module.exports = getOrderbook;
|
||||
module.exports = getOrderbook
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const {validate} = utils.common;
|
||||
const parseAccountOrder = require('./parse/account-order');
|
||||
import type {Connection} from '../common/connection.js';
|
||||
import type {OrdersOptions, Order} from './types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const {validate} = utils.common
|
||||
const parseAccountOrder = require('./parse/account-order')
|
||||
import type {Connection} from '../common/connection.js'
|
||||
import type {OrdersOptions, Order} from './types.js'
|
||||
|
||||
type GetOrders = Array<Order>
|
||||
|
||||
@@ -22,20 +22,20 @@ function requestAccountOffers(connection: Connection, address: string,
|
||||
return {
|
||||
marker: data.marker,
|
||||
results: data.offers.map(_.partial(parseAccountOrder, address))
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getOrders(address: string, options: OrdersOptions = {}
|
||||
): Promise<GetOrders> {
|
||||
validate.getOrders({address, options});
|
||||
validate.getOrders({address, options})
|
||||
|
||||
return utils.ensureLedgerVersion.call(this, options).then(_options => {
|
||||
const getter = _.partial(requestAccountOffers, this.connection, address,
|
||||
_options.ledgerVersion);
|
||||
_options.ledgerVersion)
|
||||
return utils.getRecursive(getter, _options.limit).then(orders =>
|
||||
_.sortBy(orders, (order) => order.properties.sequence));
|
||||
});
|
||||
_.sortBy(orders, order => order.properties.sequence))
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = getOrders;
|
||||
module.exports = getOrders
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const flags = require('./flags').orderFlags;
|
||||
const parseAmount = require('./amount');
|
||||
const BigNumber = require('bignumber.js');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('./utils')
|
||||
const flags = require('./flags').orderFlags
|
||||
const parseAmount = require('./amount')
|
||||
const BigNumber = require('bignumber.js')
|
||||
|
||||
// TODO: remove this function once rippled provides quality directly
|
||||
function computeQuality(takerGets, takerPays) {
|
||||
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value);
|
||||
return quotient.toDigits(16, BigNumber.ROUND_HALF_UP).toString();
|
||||
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value)
|
||||
return quotient.toDigits(16, BigNumber.ROUND_HALF_UP).toString()
|
||||
}
|
||||
|
||||
// rippled 'account_offers' returns a different format for orders than 'tx'
|
||||
// the flags are also different
|
||||
function parseAccountOrder(address: string, order: Object): Object {
|
||||
const direction = (order.flags & flags.Sell) === 0 ? 'buy' : 'sell';
|
||||
const takerGetsAmount = parseAmount(order.taker_gets);
|
||||
const takerPaysAmount = parseAmount(order.taker_pays);
|
||||
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
|
||||
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
|
||||
const direction = (order.flags & flags.Sell) === 0 ? 'buy' : 'sell'
|
||||
const takerGetsAmount = parseAmount(order.taker_gets)
|
||||
const takerPaysAmount = parseAmount(order.taker_pays)
|
||||
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
|
||||
// so we can omit those flags here
|
||||
@@ -29,16 +29,19 @@ function parseAccountOrder(address: string, order: Object): Object {
|
||||
passive: ((order.flags & flags.Passive) !== 0) || undefined,
|
||||
// rippled currently does not provide "expiration" in account_offers
|
||||
expirationTime: utils.parseTimestamp(order.expiration)
|
||||
});
|
||||
})
|
||||
|
||||
const makerExchangeRate = order.quality ?
|
||||
utils.adjustQualityForXRP(order.quality.toString(),
|
||||
takerGetsAmount.currency, takerPaysAmount.currency) :
|
||||
computeQuality(takerGetsAmount, takerPaysAmount)
|
||||
const properties = {
|
||||
maker: address,
|
||||
sequence: order.seq,
|
||||
makerExchangeRate: order.quality ? order.quality.toString()
|
||||
: computeQuality(takerGetsAmount, takerPaysAmount)
|
||||
};
|
||||
makerExchangeRate: makerExchangeRate
|
||||
}
|
||||
|
||||
return {specification, properties};
|
||||
return {specification, properties}
|
||||
}
|
||||
|
||||
module.exports = parseAccountOrder;
|
||||
module.exports = parseAccountOrder
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('./utils')
|
||||
|
||||
type Trustline = {
|
||||
account: string, limit: number, currency: string, quality_in: ?number,
|
||||
@@ -29,18 +29,18 @@ function parseAccountTrustline(trustline: Trustline): AccountTrustline {
|
||||
ripplingDisabled: trustline.no_ripple || undefined,
|
||||
frozen: trustline.freeze || undefined,
|
||||
authorized: trustline.authorized || undefined
|
||||
});
|
||||
})
|
||||
// rippled doesn't provide the counterparty's qualities
|
||||
const counterparty = utils.removeUndefined({
|
||||
limit: trustline.limit_peer,
|
||||
ripplingDisabled: trustline.no_ripple_peer || undefined,
|
||||
frozen: trustline.freeze_peer || undefined,
|
||||
authorized: trustline.peer_authorized || undefined
|
||||
});
|
||||
})
|
||||
const state = {
|
||||
balance: trustline.balance
|
||||
};
|
||||
return {specification, counterparty, state};
|
||||
}
|
||||
return {specification, counterparty, state}
|
||||
}
|
||||
|
||||
module.exports = parseAccountTrustline;
|
||||
module.exports = parseAccountTrustline
|
||||
|
||||
9
src/ledger/parse/amendment.js
Normal file
9
src/ledger/parse/amendment.js
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
function parseAmendment(tx: Object) {
|
||||
return {
|
||||
amendment: tx.Amendment
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parseAmendment
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('../utils');
|
||||
import type {Amount, RippledAmount} from '../../common/types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('../utils')
|
||||
import type {Amount, RippledAmount} from '../../common/types.js'
|
||||
|
||||
|
||||
function parseAmount(amount: RippledAmount): Amount {
|
||||
@@ -9,13 +9,13 @@ function parseAmount(amount: RippledAmount): Amount {
|
||||
return {
|
||||
currency: 'XRP',
|
||||
value: utils.common.dropsToXrp(amount)
|
||||
};
|
||||
}
|
||||
}
|
||||
return {
|
||||
currency: amount.currency,
|
||||
value: amount.value,
|
||||
counterparty: amount.issuer
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parseAmount;
|
||||
module.exports = parseAmount
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const assert = require('assert')
|
||||
|
||||
function parseOrderCancellation(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'OfferCancel');
|
||||
assert(tx.TransactionType === 'OfferCancel')
|
||||
return {
|
||||
orderSequence: tx.OfferSequence
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parseOrderCancellation;
|
||||
module.exports = parseOrderCancellation
|
||||
|
||||
15
src/ledger/parse/fee-update.js
Normal file
15
src/ledger/parse/fee-update.js
Normal file
@@ -0,0 +1,15 @@
|
||||
'use strict' // eslint-disable-line strict
|
||||
const BigNumber = require('bignumber.js')
|
||||
const {dropsToXrp} = require('./utils')
|
||||
|
||||
function parseFeeUpdate(tx: Object) {
|
||||
const baseFeeDrops = (new BigNumber(tx.BaseFee, 16)).toString()
|
||||
return {
|
||||
baseFeeXRP: dropsToXrp(baseFeeDrops),
|
||||
referenceFeeUnits: tx.ReferenceFeeUnits,
|
||||
reserveBaseXRP: dropsToXrp(tx.ReserveBase),
|
||||
reserveIncrementXRP: dropsToXrp(tx.ReserveIncrement)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parseFeeUpdate
|
||||
@@ -1,49 +1,49 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const AccountFields = require('./utils').constants.AccountFields;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const AccountFields = require('./utils').constants.AccountFields
|
||||
|
||||
function parseField(info, value) {
|
||||
if (info.encoding === 'hex' && !info.length) { // e.g. "domain"
|
||||
return new Buffer(value, 'hex').toString('ascii');
|
||||
return new Buffer(value, 'hex').toString('ascii')
|
||||
}
|
||||
if (info.shift) {
|
||||
return (new BigNumber(value)).shift(-info.shift).toNumber();
|
||||
return (new BigNumber(value)).shift(-info.shift).toNumber()
|
||||
}
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
|
||||
function parseFields(data: Object): Object {
|
||||
const settings = {};
|
||||
const settings = {}
|
||||
for (const fieldName in AccountFields) {
|
||||
const fieldValue = data[fieldName];
|
||||
const fieldValue = data[fieldName]
|
||||
if (fieldValue !== undefined) {
|
||||
const info = AccountFields[fieldName];
|
||||
settings[info.name] = parseField(info, fieldValue);
|
||||
const info = AccountFields[fieldName]
|
||||
settings[info.name] = parseField(info, fieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
if (data.RegularKey) {
|
||||
settings.regularKey = data.RegularKey;
|
||||
settings.regularKey = data.RegularKey
|
||||
}
|
||||
|
||||
// TODO: this isn't implemented in rippled yet, may have to change this later
|
||||
if (data.SignerQuorum || data.SignerEntries) {
|
||||
settings.signers = {};
|
||||
settings.signers = {}
|
||||
if (data.SignerQuorum) {
|
||||
settings.signers.threshold = data.SignerQuorum;
|
||||
settings.signers.threshold = data.SignerQuorum
|
||||
}
|
||||
if (data.SignerEntries) {
|
||||
settings.signers.weights = _.map(data.SignerEntries, entry => {
|
||||
return {
|
||||
address: entry.SignerEntry.Account,
|
||||
weight: entry.SignerEntry.SignerWeight
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
return settings
|
||||
}
|
||||
|
||||
module.exports = parseFields;
|
||||
module.exports = parseFields
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
const orderFlags = {
|
||||
Passive: 0x00010000,
|
||||
Sell: 0x00020000 // offer was placed as a sell
|
||||
};
|
||||
}
|
||||
|
||||
const trustlineFlags = {
|
||||
LowReserve: 0x00010000, // entry counts toward reserve
|
||||
@@ -14,9 +14,9 @@ const trustlineFlags = {
|
||||
HighNoRipple: 0x00200000,
|
||||
LowFreeze: 0x00400000,
|
||||
HighFreeze: 0x00800000
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
orderFlags,
|
||||
trustlineFlags
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,46 +1,48 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const {removeUndefined, rippleTimeToISO8601} = require('./utils');
|
||||
const parseTransaction = require('./transaction');
|
||||
import type {GetLedger} from '../types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const {removeUndefined, rippleTimeToISO8601} = require('./utils')
|
||||
const parseTransaction = require('./transaction')
|
||||
import type {GetLedger} from '../types.js'
|
||||
|
||||
function parseTransactionWrapper(ledgerVersion, tx) {
|
||||
const transaction = _.assign({}, _.omit(tx, 'metaData'),
|
||||
{meta: tx.metaData});
|
||||
const result = parseTransaction(transaction);
|
||||
const transaction = _.assign({}, _.omit(tx, 'metaData'), {
|
||||
meta: tx.metaData,
|
||||
ledger_index: ledgerVersion
|
||||
})
|
||||
const result = parseTransaction(transaction)
|
||||
if (!result.outcome.ledgerVersion) {
|
||||
result.outcome.ledgerVersion = ledgerVersion;
|
||||
result.outcome.ledgerVersion = ledgerVersion
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
function parseTransactions(transactions, ledgerVersion) {
|
||||
if (_.isEmpty(transactions)) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
if (_.isString(transactions[0])) {
|
||||
return {transactionHashes: transactions};
|
||||
return {transactionHashes: transactions}
|
||||
}
|
||||
return {
|
||||
transactions: _.map(transactions,
|
||||
_.partial(parseTransactionWrapper, ledgerVersion)),
|
||||
rawTransactions: JSON.stringify(transactions)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function parseState(state) {
|
||||
if (_.isEmpty(state)) {
|
||||
return {};
|
||||
return {}
|
||||
}
|
||||
if (_.isString(state[0])) {
|
||||
return {stateHashes: state};
|
||||
return {stateHashes: state}
|
||||
}
|
||||
return {rawState: JSON.stringify(state)};
|
||||
return {rawState: JSON.stringify(state)}
|
||||
}
|
||||
|
||||
function parseLedger(ledger: Object): GetLedger {
|
||||
const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10);
|
||||
const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10)
|
||||
return removeUndefined(_.assign({
|
||||
stateHash: ledger.account_hash,
|
||||
closeTime: rippleTimeToISO8601(ledger.close_time),
|
||||
@@ -55,7 +57,7 @@ function parseLedger(ledger: Object): GetLedger {
|
||||
},
|
||||
parseTransactions(ledger.transactions, ledgerVersion),
|
||||
parseState(ledger.accountState)
|
||||
));
|
||||
))
|
||||
}
|
||||
|
||||
module.exports = parseLedger;
|
||||
module.exports = parseLedger
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
const parseAmount = require('./amount');
|
||||
const flags = utils.txFlags.OfferCreate;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const assert = require('assert')
|
||||
const utils = require('./utils')
|
||||
const parseAmount = require('./amount')
|
||||
const flags = utils.txFlags.OfferCreate
|
||||
|
||||
function parseOrder(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'OfferCreate');
|
||||
assert(tx.TransactionType === 'OfferCreate')
|
||||
|
||||
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
|
||||
const takerGetsAmount = parseAmount(tx.TakerGets);
|
||||
const takerPaysAmount = parseAmount(tx.TakerPays);
|
||||
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
|
||||
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
|
||||
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell'
|
||||
const takerGetsAmount = parseAmount(tx.TakerGets)
|
||||
const takerPaysAmount = parseAmount(tx.TakerPays)
|
||||
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
return utils.removeUndefined({
|
||||
direction: direction,
|
||||
@@ -23,7 +23,7 @@ function parseOrder(tx: Object): Object {
|
||||
|| undefined,
|
||||
fillOrKill: ((tx.Flags & flags.FillOrKill) !== 0) || undefined,
|
||||
expirationTime: utils.parseTimestamp(tx.Expiration)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = parseOrder;
|
||||
module.exports = parseOrder
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const flags = require('./flags').orderFlags;
|
||||
const parseAmount = require('./amount');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const flags = require('./flags').orderFlags
|
||||
const parseAmount = require('./amount')
|
||||
|
||||
function parseOrderbookOrder(order: Object): Object {
|
||||
const direction = (order.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
|
||||
const takerGetsAmount = parseAmount(order.TakerGets);
|
||||
const takerPaysAmount = parseAmount(order.TakerPays);
|
||||
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
|
||||
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
|
||||
const direction = (order.Flags & flags.Sell) === 0 ? 'buy' : 'sell'
|
||||
const takerGetsAmount = parseAmount(order.TakerGets)
|
||||
const takerPaysAmount = parseAmount(order.TakerPays)
|
||||
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
|
||||
// so we can omit those flags here
|
||||
@@ -20,25 +20,25 @@ function parseOrderbookOrder(order: Object): Object {
|
||||
totalPrice: totalPrice,
|
||||
passive: ((order.Flags & flags.Passive) !== 0) || undefined,
|
||||
expirationTime: utils.parseTimestamp(order.Expiration)
|
||||
});
|
||||
})
|
||||
|
||||
const properties = {
|
||||
maker: order.Account,
|
||||
sequence: order.Sequence,
|
||||
makerExchangeRate: utils.adjustQualityForXRP(order.quality,
|
||||
takerGetsAmount.currency, takerPaysAmount.currency)
|
||||
};
|
||||
}
|
||||
|
||||
const takerGetsFunded = order.taker_gets_funded ?
|
||||
parseAmount(order.taker_gets_funded) : undefined;
|
||||
parseAmount(order.taker_gets_funded) : undefined
|
||||
const takerPaysFunded = order.taker_pays_funded ?
|
||||
parseAmount(order.taker_pays_funded) : undefined;
|
||||
parseAmount(order.taker_pays_funded) : undefined
|
||||
const available = utils.removeUndefined({
|
||||
fundedAmount: takerGetsFunded,
|
||||
priceOfFundedAmount: takerPaysFunded
|
||||
});
|
||||
const state = _.isEmpty(available) ? undefined : available;
|
||||
return utils.removeUndefined({specification, properties, state});
|
||||
})
|
||||
const state = _.isEmpty(available) ? undefined : available
|
||||
return utils.removeUndefined({specification, properties, state})
|
||||
}
|
||||
|
||||
module.exports = parseOrderbookOrder;
|
||||
module.exports = parseOrderbookOrder
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const parseAmount = require('./amount');
|
||||
import type {Amount, RippledAmount} from '../../common/types.js';
|
||||
import type {GetPaths, RippledPathsResponse} from '../pathfind-types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const parseAmount = require('./amount')
|
||||
import type {Amount, RippledAmount} from '../../common/types.js'
|
||||
import type {GetPaths, RippledPathsResponse} from '../pathfind-types.js'
|
||||
|
||||
function parsePaths(paths) {
|
||||
return paths.map(steps => steps.map(step =>
|
||||
_.omit(step, ['type', 'type_hex'])));
|
||||
_.omit(step, ['type', 'type_hex'])))
|
||||
}
|
||||
|
||||
function removeAnyCounterpartyEncoding(address: string, amount: Amount) {
|
||||
return amount.counterparty === address ?
|
||||
_.omit(amount, 'counterparty') : amount;
|
||||
_.omit(amount, 'counterparty') : amount
|
||||
}
|
||||
|
||||
function createAdjustment(address: string, adjustmentWithoutAddress: Object) {
|
||||
const amountKey = _.keys(adjustmentWithoutAddress)[0];
|
||||
const amount = adjustmentWithoutAddress[amountKey];
|
||||
const amountKey = _.keys(adjustmentWithoutAddress)[0]
|
||||
const amount = adjustmentWithoutAddress[amountKey]
|
||||
return _.set({address: address}, amountKey,
|
||||
removeAnyCounterpartyEncoding(address, amount));
|
||||
removeAnyCounterpartyEncoding(address, amount))
|
||||
}
|
||||
|
||||
function parseAlternative(sourceAddress: string, destinationAddress: string,
|
||||
@@ -31,21 +31,21 @@ function parseAlternative(sourceAddress: string, destinationAddress: string,
|
||||
{source: {amount: parseAmount(alternative.source_amount)},
|
||||
destination: {minAmount: parseAmount(alternative.destination_amount)}} :
|
||||
{source: {maxAmount: parseAmount(alternative.source_amount)},
|
||||
destination: {amount: parseAmount(destinationAmount)}};
|
||||
destination: {amount: parseAmount(destinationAmount)}}
|
||||
|
||||
return {
|
||||
source: createAdjustment(sourceAddress, amounts.source),
|
||||
destination: createAdjustment(destinationAddress, amounts.destination),
|
||||
paths: JSON.stringify(parsePaths(alternative.paths_computed))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function parsePathfind(pathfindResult: RippledPathsResponse): GetPaths {
|
||||
const sourceAddress = pathfindResult.source_account;
|
||||
const destinationAddress = pathfindResult.destination_account;
|
||||
const destinationAmount = pathfindResult.destination_amount;
|
||||
const sourceAddress = pathfindResult.source_account
|
||||
const destinationAddress = pathfindResult.destination_account
|
||||
const destinationAmount = pathfindResult.destination_amount
|
||||
return pathfindResult.alternatives.map(_.partial(parseAlternative,
|
||||
sourceAddress, destinationAddress, destinationAmount));
|
||||
sourceAddress, destinationAddress, destinationAmount))
|
||||
}
|
||||
|
||||
module.exports = parsePathfind;
|
||||
module.exports = parsePathfind
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
const parseAmount = require('./amount');
|
||||
const txFlags = utils.txFlags;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const assert = require('assert')
|
||||
const utils = require('./utils')
|
||||
const parseAmount = require('./amount')
|
||||
const txFlags = utils.txFlags
|
||||
|
||||
function isNoDirectRipple(tx) {
|
||||
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0;
|
||||
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0
|
||||
}
|
||||
|
||||
function isQualityLimited(tx) {
|
||||
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0;
|
||||
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0
|
||||
}
|
||||
|
||||
function removeGenericCounterparty(amount, address) {
|
||||
return amount.counterparty === address ?
|
||||
_.omit(amount, 'counterparty') : amount;
|
||||
_.omit(amount, 'counterparty') : amount
|
||||
}
|
||||
|
||||
function parsePayment(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'Payment');
|
||||
assert(tx.TransactionType === 'Payment')
|
||||
|
||||
const source = {
|
||||
address: tx.Account,
|
||||
maxAmount: removeGenericCounterparty(
|
||||
parseAmount(tx.SendMax || tx.Amount), tx.Account),
|
||||
tag: tx.SourceTag
|
||||
};
|
||||
}
|
||||
|
||||
const destination = {
|
||||
address: tx.Destination,
|
||||
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
|
||||
tag: tx.DestinationTag
|
||||
};
|
||||
}
|
||||
|
||||
return utils.removeUndefined({
|
||||
source: utils.removeUndefined(source),
|
||||
@@ -44,7 +44,7 @@ function parsePayment(tx: Object): Object {
|
||||
allowPartialPayment: utils.isPartialPayment(tx) || undefined,
|
||||
noDirectRipple: isNoDirectRipple(tx) || undefined,
|
||||
limitQuality: isQualityLimited(tx) || undefined
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = parsePayment;
|
||||
module.exports = parsePayment
|
||||
|
||||
@@ -1,61 +1,61 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const AccountFlags = require('./utils').constants.AccountFlags;
|
||||
const parseFields = require('./fields');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const assert = require('assert')
|
||||
const AccountFlags = require('./utils').constants.AccountFlags
|
||||
const parseFields = require('./fields')
|
||||
|
||||
function getAccountRootModifiedNode(tx: Object) {
|
||||
const modifiedNodes = tx.meta.AffectedNodes.filter(node =>
|
||||
node.ModifiedNode.LedgerEntryType === 'AccountRoot');
|
||||
assert(modifiedNodes.length === 1);
|
||||
return modifiedNodes[0].ModifiedNode;
|
||||
node.ModifiedNode.LedgerEntryType === 'AccountRoot')
|
||||
assert(modifiedNodes.length === 1)
|
||||
return modifiedNodes[0].ModifiedNode
|
||||
}
|
||||
|
||||
function parseFlags(tx: Object) {
|
||||
const settings = {};
|
||||
const settings = {}
|
||||
if (tx.TransactionType !== 'AccountSet') {
|
||||
return settings;
|
||||
return settings
|
||||
}
|
||||
|
||||
const node = getAccountRootModifiedNode(tx);
|
||||
const oldFlags = _.get(node.PreviousFields, 'Flags');
|
||||
const newFlags = _.get(node.FinalFields, 'Flags');
|
||||
const node = getAccountRootModifiedNode(tx)
|
||||
const oldFlags = _.get(node.PreviousFields, 'Flags')
|
||||
const newFlags = _.get(node.FinalFields, 'Flags')
|
||||
|
||||
if (oldFlags !== undefined && newFlags !== undefined) {
|
||||
const changedFlags = oldFlags ^ newFlags;
|
||||
const setFlags = newFlags & changedFlags;
|
||||
const clearedFlags = oldFlags & changedFlags;
|
||||
const changedFlags = oldFlags ^ newFlags
|
||||
const setFlags = newFlags & changedFlags
|
||||
const clearedFlags = oldFlags & changedFlags
|
||||
_.forEach(AccountFlags, (flagValue, flagName) => {
|
||||
if (setFlags & flagValue) {
|
||||
settings[flagName] = true;
|
||||
settings[flagName] = true
|
||||
} else if (clearedFlags & flagValue) {
|
||||
settings[flagName] = false;
|
||||
settings[flagName] = false
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
// enableTransactionIDTracking requires a special case because it
|
||||
// does not affect the Flags field; instead it adds/removes a field called
|
||||
// "AccountTxnID" to/from the account root.
|
||||
|
||||
const oldField = _.get(node.PreviousFields, 'AccountTxnID');
|
||||
const newField = _.get(node.FinalFields, 'AccountTxnID');
|
||||
const oldField = _.get(node.PreviousFields, 'AccountTxnID')
|
||||
const newField = _.get(node.FinalFields, 'AccountTxnID')
|
||||
if (newField && !oldField) {
|
||||
settings.enableTransactionIDTracking = true;
|
||||
settings.enableTransactionIDTracking = true
|
||||
} else if (oldField && !newField) {
|
||||
settings.enableTransactionIDTracking = false;
|
||||
settings.enableTransactionIDTracking = false
|
||||
}
|
||||
|
||||
return settings;
|
||||
return settings
|
||||
}
|
||||
|
||||
function parseSettings(tx: Object) {
|
||||
const txType = tx.TransactionType;
|
||||
const txType = tx.TransactionType
|
||||
assert(txType === 'AccountSet' || txType === 'SetRegularKey' ||
|
||||
txType === 'SignerListSet');
|
||||
txType === 'SignerListSet')
|
||||
|
||||
return _.assign({}, parseFlags(tx), parseFields(tx));
|
||||
return _.assign({}, parseFlags(tx), parseFields(tx))
|
||||
}
|
||||
|
||||
module.exports = parseSettings;
|
||||
module.exports = parseSettings
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const assert = require('assert')
|
||||
const utils = require('./utils')
|
||||
|
||||
function parseSuspendedPaymentCancellation(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'SuspendedPaymentCancel');
|
||||
assert(tx.TransactionType === 'SuspendedPaymentCancel')
|
||||
|
||||
return utils.removeUndefined({
|
||||
memos: utils.parseMemos(tx),
|
||||
owner: tx.Owner,
|
||||
suspensionSequence: tx.OfferSequence
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = parseSuspendedPaymentCancellation;
|
||||
module.exports = parseSuspendedPaymentCancellation
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
const parseAmount = require('./amount');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const assert = require('assert')
|
||||
const utils = require('./utils')
|
||||
const parseAmount = require('./amount')
|
||||
|
||||
function removeGenericCounterparty(amount, address) {
|
||||
return amount.counterparty === address ?
|
||||
_.omit(amount, 'counterparty') : amount;
|
||||
_.omit(amount, 'counterparty') : amount
|
||||
}
|
||||
|
||||
function parseSuspendedPaymentCreation(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'SuspendedPaymentCreate');
|
||||
assert(tx.TransactionType === 'SuspendedPaymentCreate')
|
||||
|
||||
const source = {
|
||||
address: tx.Account,
|
||||
maxAmount: removeGenericCounterparty(
|
||||
parseAmount(tx.SendMax || tx.Amount), tx.Account),
|
||||
tag: tx.SourceTag
|
||||
};
|
||||
}
|
||||
|
||||
const destination = {
|
||||
address: tx.Destination,
|
||||
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
|
||||
tag: tx.DestinationTag
|
||||
};
|
||||
}
|
||||
|
||||
return utils.removeUndefined({
|
||||
source: utils.removeUndefined(source),
|
||||
@@ -33,7 +33,7 @@ function parseSuspendedPaymentCreation(tx: Object): Object {
|
||||
digest: tx.Digest,
|
||||
allowCancelAfter: utils.parseTimestamp(tx.CancelAfter),
|
||||
allowExecuteAfter: utils.parseTimestamp(tx.FinishAfter)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = parseSuspendedPaymentCreation;
|
||||
module.exports = parseSuspendedPaymentCreation
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const assert = require('assert')
|
||||
const utils = require('./utils')
|
||||
|
||||
function parseSuspendedPaymentExecution(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'SuspendedPaymentFinish');
|
||||
assert(tx.TransactionType === 'SuspendedPaymentFinish')
|
||||
|
||||
return utils.removeUndefined({
|
||||
memos: utils.parseMemos(tx),
|
||||
@@ -13,7 +13,7 @@ function parseSuspendedPaymentExecution(tx: Object): Object {
|
||||
method: tx.Method,
|
||||
digest: tx.Digest,
|
||||
proof: tx.Proof ? utils.hexToString(tx.Proof) : undefined
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = parseSuspendedPaymentExecution;
|
||||
module.exports = parseSuspendedPaymentExecution
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
const parsePayment = require('./payment');
|
||||
const parseTrustline = require('./trustline');
|
||||
const parseOrder = require('./order');
|
||||
const parseOrderCancellation = require('./cancellation');
|
||||
const parseSettings = require('./settings');
|
||||
const parseSuspendedPaymentCreation = require('./suspended-payment-creation');
|
||||
const parseSuspendedPaymentExecution = require('./suspended-payment-execution');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const assert = require('assert')
|
||||
const utils = require('./utils')
|
||||
const parsePayment = require('./payment')
|
||||
const parseTrustline = require('./trustline')
|
||||
const parseOrder = require('./order')
|
||||
const parseOrderCancellation = require('./cancellation')
|
||||
const parseSettings = require('./settings')
|
||||
const parseSuspendedPaymentCreation = require('./suspended-payment-creation')
|
||||
const parseSuspendedPaymentExecution = require('./suspended-payment-execution')
|
||||
const parseSuspendedPaymentCancellation =
|
||||
require('./suspended-payment-cancellation');
|
||||
require('./suspended-payment-cancellation')
|
||||
const parseFeeUpdate = require('./fee-update')
|
||||
const parseAmendment = require('./amendment')
|
||||
|
||||
function parseTransactionType(type) {
|
||||
const mapping = {
|
||||
@@ -23,13 +25,15 @@ function parseTransactionType(type) {
|
||||
SuspendedPaymentCreate: 'suspendedPaymentCreation',
|
||||
SuspendedPaymentFinish: 'suspendedPaymentExecution',
|
||||
SuspendedPaymentCancel: 'suspendedPaymentCancellation',
|
||||
SignerListSet: 'settings'
|
||||
};
|
||||
return mapping[type] || null;
|
||||
SignerListSet: 'settings',
|
||||
SetFee: 'feeUpdate', // pseudo-transaction
|
||||
EnableAmendment: 'amendment' // pseudo-transaction
|
||||
}
|
||||
return mapping[type] || null
|
||||
}
|
||||
|
||||
function parseTransaction(tx: Object): Object {
|
||||
const type = parseTransactionType(tx.TransactionType);
|
||||
const type = parseTransactionType(tx.TransactionType)
|
||||
const mapping = {
|
||||
'payment': parsePayment,
|
||||
'trustline': parseTrustline,
|
||||
@@ -38,12 +42,14 @@ function parseTransaction(tx: Object): Object {
|
||||
'settings': parseSettings,
|
||||
'suspendedPaymentCreation': parseSuspendedPaymentCreation,
|
||||
'suspendedPaymentExecution': parseSuspendedPaymentExecution,
|
||||
'suspendedPaymentCancellation': parseSuspendedPaymentCancellation
|
||||
};
|
||||
const parser = mapping[type];
|
||||
assert(parser !== undefined, 'Unrecognized transaction type');
|
||||
const specification = parser(tx);
|
||||
const outcome = utils.parseOutcome(tx);
|
||||
'suspendedPaymentCancellation': parseSuspendedPaymentCancellation,
|
||||
'feeUpdate': parseFeeUpdate,
|
||||
'amendment': parseAmendment
|
||||
}
|
||||
const parser = mapping[type]
|
||||
assert(parser !== undefined, 'Unrecognized transaction type')
|
||||
const specification = parser(tx)
|
||||
const outcome = utils.parseOutcome(tx)
|
||||
return utils.removeUndefined({
|
||||
type: type,
|
||||
address: tx.Account,
|
||||
@@ -51,7 +57,7 @@ function parseTransaction(tx: Object): Object {
|
||||
id: tx.hash,
|
||||
specification: utils.removeUndefined(specification),
|
||||
outcome: outcome ? utils.removeUndefined(outcome) : undefined
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = parseTransaction;
|
||||
module.exports = parseTransaction
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
const flags = utils.txFlags.TrustSet;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const assert = require('assert')
|
||||
const utils = require('./utils')
|
||||
const flags = utils.txFlags.TrustSet
|
||||
|
||||
function parseFlag(flagsValue, trueValue, falseValue) {
|
||||
if (flagsValue & trueValue) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
if (flagsValue & falseValue) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
function parseTrustline(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'TrustSet');
|
||||
assert(tx.TransactionType === 'TrustSet')
|
||||
|
||||
return utils.removeUndefined({
|
||||
limit: tx.LimitAmount.value,
|
||||
@@ -27,7 +27,7 @@ function parseTrustline(tx: Object): Object {
|
||||
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
|
||||
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
|
||||
authorized: parseFlag(tx.Flags, flags.SetAuth, 0)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = parseTrustline;
|
||||
module.exports = parseTrustline
|
||||
|
||||
@@ -1,85 +1,109 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const transactionParser = require('ripple-lib-transactionparser');
|
||||
const utils = require('../utils');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const parseAmount = require('./amount');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const transactionParser = require('ripple-lib-transactionparser')
|
||||
const utils = require('../utils')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const parseAmount = require('./amount')
|
||||
|
||||
import type {Amount} from '../common/types.js';
|
||||
import type {Amount} from '../common/types.js'
|
||||
|
||||
function adjustQualityForXRP(
|
||||
quality: string, takerGetsCurrency: string, takerPaysCurrency: string
|
||||
) {
|
||||
// quality = takerPays.value/takerGets.value
|
||||
// using drops (1e-6 XRP) for XRP values
|
||||
const numeratorShift = (takerPaysCurrency === 'XRP' ? -6 : 0);
|
||||
const denominatorShift = (takerGetsCurrency === 'XRP' ? -6 : 0);
|
||||
const shift = numeratorShift - denominatorShift;
|
||||
const numeratorShift = (takerPaysCurrency === 'XRP' ? -6 : 0)
|
||||
const denominatorShift = (takerGetsCurrency === 'XRP' ? -6 : 0)
|
||||
const shift = numeratorShift - denominatorShift
|
||||
return shift === 0 ? quality :
|
||||
(new BigNumber(quality)).shift(shift).toString();
|
||||
(new BigNumber(quality)).shift(shift).toString()
|
||||
}
|
||||
|
||||
function parseQuality(quality: ?number) {
|
||||
if (typeof quality === 'number') {
|
||||
return (new BigNumber(quality)).shift(-9).toNumber();
|
||||
return (new BigNumber(quality)).shift(-9).toNumber()
|
||||
}
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
|
||||
function parseTimestamp(rippleTime: number): string | void {
|
||||
return rippleTime ? utils.common.rippleTimeToISO8601(rippleTime) : undefined;
|
||||
return rippleTime ? utils.common.rippleTimeToISO8601(rippleTime) : undefined
|
||||
}
|
||||
|
||||
function removeEmptyCounterparty(amount) {
|
||||
if (amount.counterparty === '') {
|
||||
delete amount.counterparty;
|
||||
delete amount.counterparty
|
||||
}
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInBalanceChanges(balanceChanges) {
|
||||
_.forEach(balanceChanges, changes => {
|
||||
_.forEach(changes, removeEmptyCounterparty);
|
||||
});
|
||||
_.forEach(changes, removeEmptyCounterparty)
|
||||
})
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInOrderbookChanges(orderbookChanges) {
|
||||
_.forEach(orderbookChanges, changes => {
|
||||
_.forEach(changes, change => {
|
||||
_.forEach(change, removeEmptyCounterparty);
|
||||
});
|
||||
});
|
||||
_.forEach(change, removeEmptyCounterparty)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function isPartialPayment(tx) {
|
||||
return (tx.Flags & utils.common.txFlags.Payment.PartialPayment) !== 0;
|
||||
return (tx.Flags & utils.common.txFlags.Payment.PartialPayment) !== 0
|
||||
}
|
||||
|
||||
function parseDeliveredAmount(tx: Object): Amount | void {
|
||||
let deliveredAmount;
|
||||
|
||||
// TODO: Workaround for existing rippled bug where delivered_amount may not be
|
||||
// provided for account_tx
|
||||
if (tx.TransactionType === 'Payment') {
|
||||
if (tx.meta.delivered_amount) {
|
||||
deliveredAmount = parseAmount(tx.meta.delivered_amount);
|
||||
} else if (tx.Amount && !isPartialPayment(tx)) {
|
||||
deliveredAmount = parseAmount(tx.Amount);
|
||||
}
|
||||
if (tx.TransactionType !== 'Payment' ||
|
||||
tx.meta.TransactionResult !== 'tesSUCCESS') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return deliveredAmount;
|
||||
if (tx.meta.delivered_amount &&
|
||||
tx.meta.delivered_amount === 'unavailable') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// parsable delivered_amount
|
||||
if (tx.meta.delivered_amount) {
|
||||
return parseAmount(tx.meta.delivered_amount)
|
||||
}
|
||||
|
||||
// DeliveredAmount only present on partial payments
|
||||
if (tx.meta.DeliveredAmount) {
|
||||
return parseAmount(tx.meta.DeliveredAmount)
|
||||
}
|
||||
|
||||
// no partial payment flag, use tx.Amount
|
||||
if (tx.Amount && !isPartialPayment(tx)) {
|
||||
return parseAmount(tx.Amount)
|
||||
}
|
||||
|
||||
// DeliveredAmount field was introduced at
|
||||
// ledger 4594095 - after that point its absence
|
||||
// on a tx flagged as partial payment indicates
|
||||
// the full amount was transferred. The amount
|
||||
// transferred with a partial payment before
|
||||
// that date must be derived from metadata.
|
||||
if (tx.Amount && tx.ledger_index > 4594094) {
|
||||
return parseAmount(tx.Amount)
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
function parseOutcome(tx: Object): ?Object {
|
||||
const metadata = tx.meta || tx.metaData;
|
||||
const metadata = tx.meta || tx.metaData
|
||||
if (!metadata) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
const balanceChanges = transactionParser.parseBalanceChanges(metadata);
|
||||
const orderbookChanges = transactionParser.parseOrderbookChanges(metadata);
|
||||
removeEmptyCounterpartyInBalanceChanges(balanceChanges);
|
||||
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges);
|
||||
const balanceChanges = transactionParser.parseBalanceChanges(metadata)
|
||||
const orderbookChanges = transactionParser.parseOrderbookChanges(metadata)
|
||||
removeEmptyCounterpartyInBalanceChanges(balanceChanges)
|
||||
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges)
|
||||
|
||||
return utils.common.removeUndefined({
|
||||
result: tx.meta.TransactionResult,
|
||||
@@ -90,24 +114,24 @@ function parseOutcome(tx: Object): ?Object {
|
||||
ledgerVersion: tx.ledger_index,
|
||||
indexInLedger: tx.meta.TransactionIndex,
|
||||
deliveredAmount: parseDeliveredAmount(tx)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function hexToString(hex: string): ?string {
|
||||
return hex ? new Buffer(hex, 'hex').toString('utf-8') : undefined;
|
||||
return hex ? new Buffer(hex, 'hex').toString('utf-8') : undefined
|
||||
}
|
||||
|
||||
function parseMemos(tx: Object): ?Array<Object> {
|
||||
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
return tx.Memos.map(m => {
|
||||
return utils.common.removeUndefined({
|
||||
type: m.Memo.parsed_memo_type || hexToString(m.Memo.MemoType),
|
||||
format: m.Memo.parsed_memo_format || hexToString(m.Memo.MemoFormat),
|
||||
data: m.Memo.parsed_memo_data || hexToString(m.Memo.MemoData)
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -123,4 +147,4 @@ module.exports = {
|
||||
txFlags: utils.common.txFlags,
|
||||
removeUndefined: utils.common.removeUndefined,
|
||||
rippleTimeToISO8601: utils.common.rippleTimeToISO8601
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
import type {Amount, LaxLaxAmount, RippledAmount, Adjustment, MaxAdjustment,
|
||||
MinAdjustment} from '../common/types.js';
|
||||
MinAdjustment} from '../common/types.js'
|
||||
|
||||
|
||||
type Path = {
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const utils = require('./utils');
|
||||
const parsePathfind = require('./parse/pathfind');
|
||||
const {validate, toRippledAmount} = utils.common;
|
||||
const NotFoundError = utils.common.errors.NotFoundError;
|
||||
const ValidationError = utils.common.errors.ValidationError;
|
||||
import type {Connection} from '../common/connection';
|
||||
import type {RippledAmount} from '../common/types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const utils = require('./utils')
|
||||
const parsePathfind = require('./parse/pathfind')
|
||||
const {validate, toRippledAmount} = utils.common
|
||||
const NotFoundError = utils.common.errors.NotFoundError
|
||||
const ValidationError = utils.common.errors.ValidationError
|
||||
import type {Connection} from '../common/connection'
|
||||
import type {RippledAmount} from '../common/types.js'
|
||||
import type {GetPaths, PathFind, RippledPathsResponse, PathFindRequest}
|
||||
from './pathfind-types.js';
|
||||
from './pathfind-types.js'
|
||||
|
||||
|
||||
function addParams(request: PathFindRequest, result: RippledPathsResponse) {
|
||||
return _.defaults(_.assign({}, result, {
|
||||
source_account: request.source_account,
|
||||
source_currencies: request.source_currencies
|
||||
}), {destination_amount: request.destination_amount});
|
||||
}), {destination_amount: request.destination_amount})
|
||||
}
|
||||
|
||||
function requestPathFind(connection: Connection, pathfind: PathFind): Promise {
|
||||
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount);
|
||||
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount)
|
||||
const request: PathFindRequest = {
|
||||
command: 'ripple_path_find',
|
||||
source_account: pathfind.source.address,
|
||||
destination_account: pathfind.destination.address,
|
||||
destination_amount: toRippledAmount(destinationAmount)
|
||||
};
|
||||
}
|
||||
if (typeof request.destination_amount === 'object'
|
||||
&& !request.destination_amount.issuer) {
|
||||
// Convert blank issuer to sender's address
|
||||
@@ -35,43 +35,43 @@ function requestPathFind(connection: Connection, pathfind: PathFind): Promise {
|
||||
// https://ripple.com/build/transactions/
|
||||
// #special-issuer-values-for-sendmax-and-amount
|
||||
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
|
||||
request.destination_amount.issuer = request.destination_account;
|
||||
request.destination_amount.issuer = request.destination_account
|
||||
}
|
||||
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
|
||||
request.source_currencies = pathfind.source.currencies.map(amount =>
|
||||
_.omit(toRippledAmount(amount), 'value'));
|
||||
_.omit(toRippledAmount(amount), 'value'))
|
||||
}
|
||||
if (pathfind.source.amount) {
|
||||
if (pathfind.destination.amount.value !== undefined) {
|
||||
throw new ValidationError('Cannot specify both source.amount'
|
||||
+ ' and destination.amount.value in getPaths');
|
||||
+ ' and destination.amount.value in getPaths')
|
||||
}
|
||||
request.send_max = toRippledAmount(pathfind.source.amount);
|
||||
request.send_max = toRippledAmount(pathfind.source.amount)
|
||||
if (request.send_max.currency && !request.send_max.issuer) {
|
||||
request.send_max.issuer = pathfind.source.address;
|
||||
request.send_max.issuer = pathfind.source.address
|
||||
}
|
||||
}
|
||||
|
||||
return connection.request(request).then(paths => addParams(request, paths));
|
||||
return connection.request(request).then(paths => addParams(request, paths))
|
||||
}
|
||||
|
||||
function addDirectXrpPath(paths: RippledPathsResponse, xrpBalance: string
|
||||
): RippledPathsResponse {
|
||||
// Add XRP "path" only if the source acct has enough XRP to make the payment
|
||||
const destinationAmount = paths.destination_amount;
|
||||
const destinationAmount = paths.destination_amount
|
||||
if ((new BigNumber(xrpBalance)).greaterThanOrEqualTo(destinationAmount)) {
|
||||
paths.alternatives.unshift({
|
||||
paths_computed: [],
|
||||
source_amount: paths.destination_amount
|
||||
});
|
||||
})
|
||||
}
|
||||
return paths;
|
||||
return paths
|
||||
}
|
||||
|
||||
function isRippledIOUAmount(amount: RippledAmount) {
|
||||
// rippled XRP amounts are specified as decimal strings
|
||||
return (typeof amount === 'object') &&
|
||||
amount.currency && (amount.currency !== 'XRP');
|
||||
amount.currency && (amount.currency !== 'XRP')
|
||||
}
|
||||
|
||||
function conditionallyAddDirectXRPPath(connection: Connection, address: string,
|
||||
@@ -79,10 +79,10 @@ function conditionallyAddDirectXRPPath(connection: Connection, address: string,
|
||||
): Promise {
|
||||
if (isRippledIOUAmount(paths.destination_amount)
|
||||
|| !_.includes(paths.destination_currencies, 'XRP')) {
|
||||
return Promise.resolve(paths);
|
||||
return Promise.resolve(paths)
|
||||
}
|
||||
return utils.getXRPBalance(connection, address, undefined).then(
|
||||
xrpBalance => addDirectXrpPath(paths, xrpBalance));
|
||||
xrpBalance => addDirectXrpPath(paths, xrpBalance))
|
||||
}
|
||||
|
||||
function filterSourceFundsLowPaths(pathfind: PathFind,
|
||||
@@ -93,15 +93,15 @@ function filterSourceFundsLowPaths(pathfind: PathFind,
|
||||
paths.alternatives = _.filter(paths.alternatives, alt => {
|
||||
return alt.source_amount &&
|
||||
pathfind.source.amount &&
|
||||
alt.source_amount.value === pathfind.source.amount.value;
|
||||
});
|
||||
new BigNumber(alt.source_amount.value).eq(pathfind.source.amount.value)
|
||||
})
|
||||
}
|
||||
return paths;
|
||||
return paths
|
||||
}
|
||||
|
||||
function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
|
||||
if (paths.alternatives && paths.alternatives.length > 0) {
|
||||
return parsePathfind(paths);
|
||||
return parsePathfind(paths)
|
||||
}
|
||||
if (paths.destination_currencies !== undefined &&
|
||||
!_.includes(paths.destination_currencies,
|
||||
@@ -109,30 +109,30 @@ function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
|
||||
throw new NotFoundError('No paths found. ' +
|
||||
'The destination_account does not accept ' +
|
||||
pathfind.destination.amount.currency + ', they only accept: ' +
|
||||
paths.destination_currencies.join(', '));
|
||||
paths.destination_currencies.join(', '))
|
||||
} else if (paths.source_currencies && paths.source_currencies.length > 0) {
|
||||
throw new NotFoundError('No paths found. Please ensure' +
|
||||
' that the source_account has sufficient funds to execute' +
|
||||
' the payment in one of the specified source_currencies. If it does' +
|
||||
' there may be insufficient liquidity in the network to execute' +
|
||||
' this payment right now');
|
||||
' this payment right now')
|
||||
} else {
|
||||
throw new NotFoundError('No paths found.' +
|
||||
' Please ensure that the source_account has sufficient funds to' +
|
||||
' execute the payment. If it does there may be insufficient liquidity' +
|
||||
' in the network to execute this payment right now');
|
||||
' in the network to execute this payment right now')
|
||||
}
|
||||
}
|
||||
|
||||
function getPaths(pathfind: PathFind): Promise<GetPaths> {
|
||||
validate.getPaths({pathfind});
|
||||
validate.getPaths({pathfind})
|
||||
|
||||
const address = pathfind.source.address;
|
||||
const address = pathfind.source.address
|
||||
return requestPathFind(this.connection, pathfind).then(paths =>
|
||||
conditionallyAddDirectXRPPath(this.connection, address, paths)
|
||||
)
|
||||
.then(paths => filterSourceFundsLowPaths(pathfind, paths))
|
||||
.then(paths => formatResponse(pathfind, paths));
|
||||
.then(paths => formatResponse(pathfind, paths))
|
||||
}
|
||||
|
||||
module.exports = getPaths;
|
||||
module.exports = getPaths
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const parseFields = require('./parse/fields');
|
||||
const {validate} = utils.common;
|
||||
const AccountFlags = utils.common.constants.AccountFlags;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const parseFields = require('./parse/fields')
|
||||
const {validate} = utils.common
|
||||
const AccountFlags = utils.common.constants.AccountFlags
|
||||
|
||||
type SettingsOptions = {
|
||||
ledgerVersion?: number
|
||||
@@ -29,33 +29,33 @@ type GetSettings = {
|
||||
|
||||
|
||||
function parseFlags(value) {
|
||||
const settings = {};
|
||||
const settings = {}
|
||||
for (const flagName in AccountFlags) {
|
||||
if (value & AccountFlags[flagName]) {
|
||||
settings[flagName] = true;
|
||||
settings[flagName] = true
|
||||
}
|
||||
}
|
||||
return settings;
|
||||
return settings
|
||||
}
|
||||
|
||||
function formatSettings(response) {
|
||||
const data = response.account_data;
|
||||
const parsedFlags = parseFlags(data.Flags);
|
||||
const parsedFields = parseFields(data);
|
||||
return _.assign({}, parsedFlags, parsedFields);
|
||||
const data = response.account_data
|
||||
const parsedFlags = parseFlags(data.Flags)
|
||||
const parsedFields = parseFields(data)
|
||||
return _.assign({}, parsedFlags, parsedFields)
|
||||
}
|
||||
|
||||
function getSettings(address: string, options: SettingsOptions = {}
|
||||
): Promise<GetSettings> {
|
||||
validate.getSettings({address, options});
|
||||
validate.getSettings({address, options})
|
||||
|
||||
const request = {
|
||||
command: 'account_info',
|
||||
account: address,
|
||||
ledger_index: options.ledgerVersion || 'validated'
|
||||
};
|
||||
}
|
||||
|
||||
return this.connection.request(request).then(formatSettings);
|
||||
return this.connection.request(request).then(formatSettings)
|
||||
}
|
||||
|
||||
module.exports = getSettings;
|
||||
module.exports = getSettings
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
import type {Amount, Memo} from '../common/types.js';
|
||||
import type {Amount, Memo} from '../common/types.js'
|
||||
|
||||
type Outcome = {
|
||||
result: string,
|
||||
|
||||
@@ -1,54 +1,57 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const parseTransaction = require('./parse/transaction');
|
||||
const {validate, errors} = utils.common;
|
||||
import type {Connection} from '../common/connection.js';
|
||||
import type {TransactionType, TransactionOptions} from './transaction-types';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const parseTransaction = require('./parse/transaction')
|
||||
const {validate, errors} = utils.common
|
||||
import type {Connection} from '../common/connection.js'
|
||||
import type {TransactionType, TransactionOptions} from './transaction-types'
|
||||
|
||||
function attachTransactionDate(connection: Connection, tx: Object
|
||||
): Promise<TransactionType> {
|
||||
if (tx.date) {
|
||||
return Promise.resolve(tx);
|
||||
return Promise.resolve(tx)
|
||||
}
|
||||
|
||||
if (!tx.ledger_index) {
|
||||
const ledgerVersion = tx.ledger_index || tx.LedgerSequence
|
||||
|
||||
if (!ledgerVersion) {
|
||||
return new Promise(() => {
|
||||
throw new errors.NotFoundError('ledger_index not found in tx');
|
||||
});
|
||||
throw new errors.NotFoundError(
|
||||
'ledger_index and LedgerSequence not found in tx')
|
||||
})
|
||||
}
|
||||
|
||||
const request = {
|
||||
command: 'ledger',
|
||||
ledger_index: tx.ledger_index
|
||||
};
|
||||
ledger_index: ledgerVersion
|
||||
}
|
||||
|
||||
return connection.request(request).then(data => {
|
||||
if (typeof data.ledger.close_time === 'number') {
|
||||
return _.assign({date: data.ledger.close_time}, tx);
|
||||
return _.assign({date: data.ledger.close_time}, tx)
|
||||
}
|
||||
throw new errors.UnexpectedError('Ledger missing close_time');
|
||||
throw new errors.UnexpectedError('Ledger missing close_time')
|
||||
}).catch(error => {
|
||||
if (error instanceof errors.UnexpectedError) {
|
||||
throw error;
|
||||
throw error
|
||||
}
|
||||
throw new errors.NotFoundError('Transaction ledger not found');
|
||||
});
|
||||
throw new errors.NotFoundError('Transaction ledger not found')
|
||||
})
|
||||
}
|
||||
|
||||
function isTransactionInRange(tx: Object, options: TransactionOptions) {
|
||||
return (!options.minLedgerVersion
|
||||
|| tx.ledger_index >= options.minLedgerVersion)
|
||||
&& (!options.maxLedgerVersion
|
||||
|| tx.ledger_index <= options.maxLedgerVersion);
|
||||
|| tx.ledger_index <= options.maxLedgerVersion)
|
||||
}
|
||||
|
||||
function convertError(connection: Connection, options: TransactionOptions,
|
||||
error: Error
|
||||
): Promise<Error> {
|
||||
const _error = (error.message === 'txnNotFound') ?
|
||||
new errors.NotFoundError('Transaction not found') : error;
|
||||
new errors.NotFoundError('Transaction not found') : error
|
||||
if (_error instanceof errors.NotFoundError) {
|
||||
return utils.hasCompleteLedgerRange(connection, options.minLedgerVersion,
|
||||
options.maxLedgerVersion).then(hasCompleteLedgerRange => {
|
||||
@@ -58,32 +61,32 @@ function convertError(connection: Connection, options: TransactionOptions,
|
||||
.then(isPendingLedgerVersion => {
|
||||
return isPendingLedgerVersion ?
|
||||
new errors.PendingLedgerVersionError() :
|
||||
new errors.MissingLedgerHistoryError();
|
||||
});
|
||||
new errors.MissingLedgerHistoryError()
|
||||
})
|
||||
}
|
||||
return _error;
|
||||
});
|
||||
return _error
|
||||
})
|
||||
}
|
||||
return Promise.resolve(_error);
|
||||
return Promise.resolve(_error)
|
||||
}
|
||||
|
||||
function formatResponse(options: TransactionOptions, tx: TransactionType
|
||||
): TransactionType {
|
||||
if (tx.validated !== true || !isTransactionInRange(tx, options)) {
|
||||
throw new errors.NotFoundError('Transaction not found');
|
||||
throw new errors.NotFoundError('Transaction not found')
|
||||
}
|
||||
return parseTransaction(tx);
|
||||
return parseTransaction(tx)
|
||||
}
|
||||
|
||||
function getTransaction(id: string, options: TransactionOptions = {}
|
||||
): Promise<TransactionType> {
|
||||
validate.getTransaction({id, options});
|
||||
validate.getTransaction({id, options})
|
||||
|
||||
const request = {
|
||||
command: 'tx',
|
||||
transaction: id,
|
||||
binary: false
|
||||
};
|
||||
}
|
||||
|
||||
return utils.ensureLedgerVersion.call(this, options).then(_options => {
|
||||
return this.connection.request(request).then(tx =>
|
||||
@@ -91,10 +94,10 @@ function getTransaction(id: string, options: TransactionOptions = {}
|
||||
).then(_.partial(formatResponse, _options))
|
||||
.catch(error => {
|
||||
return convertError(this.connection, _options, error).then(_error => {
|
||||
throw _error;
|
||||
});
|
||||
});
|
||||
});
|
||||
throw _error
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = getTransaction;
|
||||
module.exports = getTransaction
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/* @flow */
|
||||
/* eslint-disable max-params */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const binary = require('ripple-binary-codec');
|
||||
const {computeTransactionHash} = require('ripple-hashes');
|
||||
const utils = require('./utils');
|
||||
const parseTransaction = require('./parse/transaction');
|
||||
const getTransaction = require('./transaction');
|
||||
const {validate} = utils.common;
|
||||
import type {Connection} from '../common/connection.js';
|
||||
import type {TransactionType} from './transaction-types';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const binary = require('ripple-binary-codec')
|
||||
const {computeTransactionHash} = require('ripple-hashes')
|
||||
const utils = require('./utils')
|
||||
const parseTransaction = require('./parse/transaction')
|
||||
const getTransaction = require('./transaction')
|
||||
const {validate} = utils.common
|
||||
import type {Connection} from '../common/connection.js'
|
||||
import type {TransactionType} from './transaction-types'
|
||||
|
||||
|
||||
type TransactionsOptions = {
|
||||
@@ -29,21 +29,21 @@ type TransactionsOptions = {
|
||||
type GetTransactionsResponse = Array<TransactionType>
|
||||
|
||||
function parseBinaryTransaction(transaction) {
|
||||
const tx = binary.decode(transaction.tx_blob);
|
||||
tx.hash = computeTransactionHash(tx);
|
||||
tx.ledger_index = transaction.ledger_index;
|
||||
const tx = binary.decode(transaction.tx_blob)
|
||||
tx.hash = computeTransactionHash(tx)
|
||||
tx.ledger_index = transaction.ledger_index
|
||||
return {
|
||||
tx: tx,
|
||||
meta: binary.decode(transaction.meta),
|
||||
validated: transaction.validated
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function parseAccountTxTransaction(tx) {
|
||||
const _tx = tx.tx_blob ? parseBinaryTransaction(tx) : tx;
|
||||
const _tx = tx.tx_blob ? parseBinaryTransaction(tx) : tx
|
||||
// rippled uses a different response format for 'account_tx' than 'tx'
|
||||
return parseTransaction(_.assign({}, _tx.tx,
|
||||
{meta: _tx.meta, validated: _tx.validated}));
|
||||
{meta: _tx.meta, validated: _tx.validated}))
|
||||
}
|
||||
|
||||
function counterpartyFilter(filters, tx: TransactionType) {
|
||||
@@ -53,36 +53,36 @@ function counterpartyFilter(filters, tx: TransactionType) {
|
||||
tx.specification.destination.address === filters.counterparty) ||
|
||||
(tx.specification.counterparty === filters.counterparty)
|
||||
))) {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
function transactionFilter(address: string, filters: TransactionsOptions,
|
||||
tx: TransactionType
|
||||
) {
|
||||
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
if (filters.types && !_.includes(filters.types, tx.type)) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
if (filters.initiated === true && tx.address !== address) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
if (filters.initiated === false && tx.address === address) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
if (filters.counterparty && !counterpartyFilter(filters, tx)) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
function orderFilter(options: TransactionsOptions, tx: TransactionType) {
|
||||
return !options.startTx || (options.earliestFirst ?
|
||||
utils.compareTransactions(tx, options.startTx) > 0 :
|
||||
utils.compareTransactions(tx, options.startTx) < 0);
|
||||
utils.compareTransactions(tx, options.startTx) < 0)
|
||||
}
|
||||
|
||||
function formatPartialResponse(address: string,
|
||||
@@ -91,11 +91,11 @@ function formatPartialResponse(address: string,
|
||||
return {
|
||||
marker: data.marker,
|
||||
results: data.transactions
|
||||
.filter((tx) => tx.validated)
|
||||
.filter(tx => tx.validated)
|
||||
.map(parseAccountTxTransaction)
|
||||
.filter(_.partial(transactionFilter, address, options))
|
||||
.filter(_.partial(orderFilter, options))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getAccountTx(connection: Connection, address: string,
|
||||
@@ -112,70 +112,70 @@ function getAccountTx(connection: Connection, address: string,
|
||||
binary: options.binary,
|
||||
limit: utils.clamp(limit, 10, 400),
|
||||
marker: marker
|
||||
};
|
||||
}
|
||||
|
||||
return connection.request(request).then(response =>
|
||||
formatPartialResponse(address, options, response));
|
||||
formatPartialResponse(address, options, response))
|
||||
}
|
||||
|
||||
function checkForLedgerGaps(connection: Connection,
|
||||
options: TransactionsOptions, transactions: GetTransactionsResponse
|
||||
) {
|
||||
let {minLedgerVersion, maxLedgerVersion} = options;
|
||||
let {minLedgerVersion, maxLedgerVersion} = options
|
||||
|
||||
// if we reached the limit on number of transactions, then we can shrink
|
||||
// the required ledger range to only guarantee that there are no gaps in
|
||||
// the range of ledgers spanned by those transactions
|
||||
if (options.limit && transactions.length === options.limit) {
|
||||
if (options.earliestFirst) {
|
||||
maxLedgerVersion = _.last(transactions).outcome.ledgerVersion;
|
||||
maxLedgerVersion = _.last(transactions).outcome.ledgerVersion
|
||||
} else {
|
||||
minLedgerVersion = _.last(transactions).outcome.ledgerVersion;
|
||||
minLedgerVersion = _.last(transactions).outcome.ledgerVersion
|
||||
}
|
||||
}
|
||||
|
||||
return utils.hasCompleteLedgerRange(connection, minLedgerVersion,
|
||||
maxLedgerVersion).then(hasCompleteLedgerRange => {
|
||||
if (!hasCompleteLedgerRange) {
|
||||
throw new utils.common.errors.MissingLedgerHistoryError();
|
||||
throw new utils.common.errors.MissingLedgerHistoryError()
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function formatResponse(connection: Connection, options: TransactionsOptions,
|
||||
transactions: GetTransactionsResponse
|
||||
) {
|
||||
const compare = options.earliestFirst ? utils.compareTransactions :
|
||||
_.rearg(utils.compareTransactions, 1, 0);
|
||||
const sortedTransactions = transactions.sort(compare);
|
||||
_.rearg(utils.compareTransactions, 1, 0)
|
||||
const sortedTransactions = transactions.sort(compare)
|
||||
return checkForLedgerGaps(connection, options, sortedTransactions).then(
|
||||
() => sortedTransactions);
|
||||
() => sortedTransactions)
|
||||
}
|
||||
|
||||
function getTransactionsInternal(connection: Connection, address: string,
|
||||
options: TransactionsOptions
|
||||
): Promise<GetTransactionsResponse> {
|
||||
const getter = _.partial(getAccountTx, connection, address, options);
|
||||
const format = _.partial(formatResponse, connection, options);
|
||||
return utils.getRecursive(getter, options.limit).then(format);
|
||||
const getter = _.partial(getAccountTx, connection, address, options)
|
||||
const format = _.partial(formatResponse, connection, options)
|
||||
return utils.getRecursive(getter, options.limit).then(format)
|
||||
}
|
||||
|
||||
function getTransactions(address: string, options: TransactionsOptions = {}
|
||||
): Promise<GetTransactionsResponse> {
|
||||
validate.getTransactions({address, options});
|
||||
validate.getTransactions({address, options})
|
||||
|
||||
const defaults = {maxLedgerVersion: -1};
|
||||
const defaults = {maxLedgerVersion: -1}
|
||||
if (options.start) {
|
||||
return getTransaction.call(this, options.start).then(tx => {
|
||||
const ledgerVersion = tx.outcome.ledgerVersion;
|
||||
const ledgerVersion = tx.outcome.ledgerVersion
|
||||
const bound = options.earliestFirst ?
|
||||
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
|
||||
const newOptions = _.assign({}, defaults, options, {startTx: tx}, bound);
|
||||
return getTransactionsInternal(this.connection, address, newOptions);
|
||||
});
|
||||
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion}
|
||||
const newOptions = _.assign({}, defaults, options, {startTx: tx}, bound)
|
||||
return getTransactionsInternal(this.connection, address, newOptions)
|
||||
})
|
||||
}
|
||||
const newOptions = _.assign({}, defaults, options);
|
||||
return getTransactionsInternal(this.connection, address, newOptions);
|
||||
const newOptions = _.assign({}, defaults, options)
|
||||
return getTransactionsInternal(this.connection, address, newOptions)
|
||||
}
|
||||
|
||||
module.exports = getTransactions;
|
||||
module.exports = getTransactions
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
export type TrustLineSpecification = {
|
||||
currency: string,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const {validate} = utils.common;
|
||||
const parseAccountTrustline = require('./parse/account-trustline');
|
||||
import type {Connection} from '../common/connection.js';
|
||||
import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const {validate} = utils.common
|
||||
const parseAccountTrustline = require('./parse/account-trustline')
|
||||
import type {Connection} from '../common/connection.js'
|
||||
import type {TrustlinesOptions, Trustline} from './trustlines-types.js'
|
||||
|
||||
|
||||
type GetTrustlinesResponse = Array<Trustline>
|
||||
|
||||
function currencyFilter(currency: string, trustline: Trustline) {
|
||||
return currency === null || trustline.specification.currency === currency;
|
||||
return currency === null || trustline.specification.currency === currency
|
||||
}
|
||||
|
||||
function formatResponse(options: TrustlinesOptions, data) {
|
||||
@@ -19,7 +19,7 @@ function formatResponse(options: TrustlinesOptions, data) {
|
||||
marker: data.marker,
|
||||
results: data.lines.map(parseAccountTrustline)
|
||||
.filter(_.partial(currencyFilter, options.currency || null))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getAccountLines(connection: Connection, address: string,
|
||||
@@ -33,20 +33,20 @@ function getAccountLines(connection: Connection, address: string,
|
||||
marker: marker,
|
||||
limit: utils.clamp(limit, 10, 400),
|
||||
peer: options.counterparty
|
||||
};
|
||||
}
|
||||
|
||||
return connection.request(request).then(_.partial(formatResponse, options));
|
||||
return connection.request(request).then(_.partial(formatResponse, options))
|
||||
}
|
||||
|
||||
function getTrustlines(address: string, options: TrustlinesOptions = {}
|
||||
): Promise<GetTrustlinesResponse> {
|
||||
validate.getTrustlines({address, options});
|
||||
validate.getTrustlines({address, options})
|
||||
|
||||
return this.getLedgerVersion().then(ledgerVersion => {
|
||||
const getter = _.partial(getAccountLines, this.connection, address,
|
||||
options.ledgerVersion || ledgerVersion, options);
|
||||
return utils.getRecursive(getter, options.limit);
|
||||
});
|
||||
options.ledgerVersion || ledgerVersion, options)
|
||||
return utils.getRecursive(getter, options.limit)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = getTrustlines;
|
||||
module.exports = getTrustlines
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
import type {Amount} from '../common/types.js';
|
||||
import type {Amount} from '../common/types.js'
|
||||
|
||||
export type OrdersOptions = {
|
||||
limit?: number,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const common = require('../common');
|
||||
const dropsToXrp = common.dropsToXrp;
|
||||
import type {TransactionType} from './transaction-types';
|
||||
import type {Issue} from '../common/types.js';
|
||||
import type {Connection} from '../common/connection';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const assert = require('assert')
|
||||
const common = require('../common')
|
||||
const dropsToXrp = common.dropsToXrp
|
||||
import type {TransactionType} from './transaction-types'
|
||||
import type {Issue} from '../common/types.js'
|
||||
import type {Connection} from '../common/connection'
|
||||
|
||||
type RecursiveData = {
|
||||
marker: string,
|
||||
@@ -16,8 +16,8 @@ type RecursiveData = {
|
||||
type Getter = (marker: ?string, limit: number) => Promise<RecursiveData>
|
||||
|
||||
function clamp(value: number, min: number, max: number): number {
|
||||
assert(min <= max, 'Illegal clamp bounds');
|
||||
return Math.min(Math.max(value, min), max);
|
||||
assert(min <= max, 'Illegal clamp bounds')
|
||||
return Math.min(Math.max(value, min), max)
|
||||
}
|
||||
|
||||
function getXRPBalance(connection: Connection, address: string,
|
||||
@@ -27,51 +27,51 @@ function getXRPBalance(connection: Connection, address: string,
|
||||
command: 'account_info',
|
||||
account: address,
|
||||
ledger_index: ledgerVersion
|
||||
};
|
||||
}
|
||||
return connection.request(request).then(data =>
|
||||
dropsToXrp(data.account_data.Balance));
|
||||
dropsToXrp(data.account_data.Balance))
|
||||
}
|
||||
|
||||
// If the marker is omitted from a response, you have reached the end
|
||||
function getRecursiveRecur(getter: Getter, marker?: string, limit: number
|
||||
): Promise {
|
||||
return getter(marker, limit).then(data => {
|
||||
const remaining = limit - data.results.length;
|
||||
const remaining = limit - data.results.length
|
||||
if (remaining > 0 && data.marker !== undefined) {
|
||||
return getRecursiveRecur(getter, data.marker, remaining).then(results =>
|
||||
data.results.concat(results)
|
||||
);
|
||||
)
|
||||
}
|
||||
return data.results.slice(0, limit);
|
||||
});
|
||||
return data.results.slice(0, limit)
|
||||
})
|
||||
}
|
||||
|
||||
function getRecursive(getter: Getter, limit?: number): Promise {
|
||||
return getRecursiveRecur(getter, undefined, limit || Infinity);
|
||||
return getRecursiveRecur(getter, undefined, limit || Infinity)
|
||||
}
|
||||
|
||||
function renameCounterpartyToIssuer(amount?: Issue): ?{issuer?: string} {
|
||||
if (amount === undefined) {
|
||||
return undefined;
|
||||
return undefined
|
||||
}
|
||||
const issuer = amount.counterparty === undefined ?
|
||||
(amount.issuer !== undefined ? amount.issuer : undefined) :
|
||||
amount.counterparty;
|
||||
const withIssuer = _.assign({}, amount, {issuer: issuer});
|
||||
return _.omit(withIssuer, 'counterparty');
|
||||
amount.counterparty
|
||||
const withIssuer = _.assign({}, amount, {issuer: issuer})
|
||||
return _.omit(withIssuer, 'counterparty')
|
||||
}
|
||||
|
||||
type RequestBookOffersArgs = {taker_gets: Issue, taker_pays: Issue}
|
||||
|
||||
function renameCounterpartyToIssuerInOrder(order: RequestBookOffersArgs) {
|
||||
const taker_gets = renameCounterpartyToIssuer(order.taker_gets);
|
||||
const taker_pays = renameCounterpartyToIssuer(order.taker_pays);
|
||||
const changes = {taker_gets: taker_gets, taker_pays: taker_pays};
|
||||
return _.assign({}, order, _.omit(changes, _.isUndefined));
|
||||
const taker_gets = renameCounterpartyToIssuer(order.taker_gets)
|
||||
const taker_pays = renameCounterpartyToIssuer(order.taker_pays)
|
||||
const changes = {taker_gets: taker_gets, taker_pays: taker_pays}
|
||||
return _.assign({}, order, _.omit(changes, _.isUndefined))
|
||||
}
|
||||
|
||||
function signum(num) {
|
||||
return (num === 0) ? 0 : (num > 0 ? 1 : -1);
|
||||
return (num === 0) ? 0 : (num > 0 ? 1 : -1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,27 +88,27 @@ function signum(num) {
|
||||
function compareTransactions(first: TransactionType, second: TransactionType
|
||||
): number {
|
||||
if (!first.outcome || !second.outcome) {
|
||||
return 0;
|
||||
return 0
|
||||
}
|
||||
if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) {
|
||||
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger);
|
||||
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger)
|
||||
}
|
||||
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1;
|
||||
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1
|
||||
}
|
||||
|
||||
function hasCompleteLedgerRange(connection: Connection,
|
||||
minLedgerVersion?: number, maxLedgerVersion?: number
|
||||
): Promise<boolean> {
|
||||
const firstLedgerVersion = 32570; // earlier versions have been lost
|
||||
const firstLedgerVersion = 32570 // earlier versions have been lost
|
||||
return connection.hasLedgerVersions(
|
||||
minLedgerVersion || firstLedgerVersion, maxLedgerVersion);
|
||||
minLedgerVersion || firstLedgerVersion, maxLedgerVersion)
|
||||
}
|
||||
|
||||
function isPendingLedgerVersion(connection: Connection,
|
||||
maxLedgerVersion: ?number
|
||||
): Promise<boolean> {
|
||||
return connection.getLedgerVersion().then(ledgerVersion =>
|
||||
ledgerVersion < (maxLedgerVersion || 0));
|
||||
ledgerVersion < (maxLedgerVersion || 0))
|
||||
}
|
||||
|
||||
function ensureLedgerVersion(options: Object
|
||||
@@ -116,10 +116,10 @@ function ensureLedgerVersion(options: Object
|
||||
if (Boolean(options) && options.ledgerVersion !== undefined &&
|
||||
options.ledgerVersion !== null
|
||||
) {
|
||||
return Promise.resolve(options);
|
||||
return Promise.resolve(options)
|
||||
}
|
||||
return this.getLedgerVersion().then(ledgerVersion =>
|
||||
_.assign({}, options, {ledgerVersion}));
|
||||
_.assign({}, options, {ledgerVersion}))
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -132,4 +132,4 @@ module.exports = {
|
||||
isPendingLedgerVersion,
|
||||
clamp: clamp,
|
||||
common: common
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
'use strict';
|
||||
const keypairs = require('ripple-keypairs');
|
||||
const common = require('../common');
|
||||
const {errors, validate} = common;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const keypairs = require('ripple-keypairs')
|
||||
const common = require('../common')
|
||||
const {errors, validate} = common
|
||||
|
||||
function generateAddress(options?: Object): Object {
|
||||
const secret = keypairs.generateSeed(options);
|
||||
const keypair = keypairs.deriveKeypair(secret);
|
||||
const address = keypairs.deriveAddress(keypair.publicKey);
|
||||
return {secret, address};
|
||||
const secret = keypairs.generateSeed(options)
|
||||
const keypair = keypairs.deriveKeypair(secret)
|
||||
const address = keypairs.deriveAddress(keypair.publicKey)
|
||||
return {secret, address}
|
||||
}
|
||||
|
||||
function generateAddressAPI(options?: Object): Object {
|
||||
validate.generateAddress({options});
|
||||
validate.generateAddress({options})
|
||||
try {
|
||||
return generateAddress(options);
|
||||
return generateAddress(options)
|
||||
} catch (error) {
|
||||
throw new errors.UnexpectedError(error.message);
|
||||
throw new errors.UnexpectedError(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
generateAddressAPI
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const common = require('../common');
|
||||
const hashes = require('ripple-hashes');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const common = require('../common')
|
||||
const hashes = require('ripple-hashes')
|
||||
|
||||
function convertLedgerHeader(header) {
|
||||
return {
|
||||
@@ -19,53 +19,56 @@ function convertLedgerHeader(header) {
|
||||
total_coins: header.totalDrops,
|
||||
totalCoins: header.totalDrops,
|
||||
transaction_hash: header.transactionHash
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function hashLedgerHeader(ledgerHeader) {
|
||||
const header = convertLedgerHeader(ledgerHeader);
|
||||
return hashes.computeLedgerHash(header);
|
||||
const header = convertLedgerHeader(ledgerHeader)
|
||||
return hashes.computeLedgerHash(header)
|
||||
}
|
||||
|
||||
function computeTransactionHash(ledger) {
|
||||
function computeTransactionHash(ledger, version) {
|
||||
if (ledger.rawTransactions === undefined) {
|
||||
return ledger.transactionHash;
|
||||
return ledger.transactionHash
|
||||
}
|
||||
const transactions = JSON.parse(ledger.rawTransactions);
|
||||
const transactions = JSON.parse(ledger.rawTransactions)
|
||||
const txs = _.map(transactions, tx => {
|
||||
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {});
|
||||
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {})
|
||||
const renameMeta = _.assign({}, _.omit(mergeTx, 'meta'),
|
||||
tx.meta ? {metaData: tx.meta} : {});
|
||||
return renameMeta;
|
||||
});
|
||||
const transactionHash = hashes.computeTransactionTreeHash(txs);
|
||||
tx.meta ? {metaData: tx.meta} : {})
|
||||
return renameMeta
|
||||
})
|
||||
const transactionHash = hashes.computeTransactionTreeHash(txs, version)
|
||||
if (ledger.transactionHash !== undefined
|
||||
&& ledger.transactionHash !== transactionHash) {
|
||||
throw new common.errors.ValidationError('transactionHash in header'
|
||||
+ ' does not match computed hash of transactions');
|
||||
+ ' does not match computed hash of transactions')
|
||||
}
|
||||
return transactionHash;
|
||||
return transactionHash
|
||||
}
|
||||
|
||||
function computeStateHash(ledger) {
|
||||
function computeStateHash(ledger, version) {
|
||||
if (ledger.rawState === undefined) {
|
||||
return ledger.stateHash;
|
||||
return ledger.stateHash
|
||||
}
|
||||
const state = JSON.parse(ledger.rawState);
|
||||
const stateHash = hashes.computeStateTreeHash(state);
|
||||
const state = JSON.parse(ledger.rawState)
|
||||
const stateHash = hashes.computeStateTreeHash(state, version)
|
||||
if (ledger.stateHash !== undefined && ledger.stateHash !== stateHash) {
|
||||
throw new common.errors.ValidationError('stateHash in header'
|
||||
+ ' does not match computed hash of state');
|
||||
+ ' does not match computed hash of state')
|
||||
}
|
||||
return stateHash;
|
||||
return stateHash
|
||||
}
|
||||
|
||||
const sLCF_SHAMapV2 = 0x02
|
||||
|
||||
function computeLedgerHash(ledger: Object): string {
|
||||
const version = ((ledger.closeFlags & sLCF_SHAMapV2) === 0) ? 1 : 2
|
||||
const subhashes = {
|
||||
transactionHash: computeTransactionHash(ledger),
|
||||
stateHash: computeStateHash(ledger)
|
||||
};
|
||||
return hashLedgerHeader(_.assign({}, ledger, subhashes));
|
||||
transactionHash: computeTransactionHash(ledger, version),
|
||||
stateHash: computeStateHash(ledger, version)
|
||||
}
|
||||
return hashLedgerHeader(_.assign({}, ledger, subhashes))
|
||||
}
|
||||
|
||||
module.exports = computeLedgerHash;
|
||||
module.exports = computeLedgerHash
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const common = require('../common');
|
||||
import type {GetServerInfoResponse} from '../common/serverinfo';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const common = require('../common')
|
||||
import type {GetServerInfoResponse} from '../common/serverinfo'
|
||||
|
||||
function isConnected(): boolean {
|
||||
return this.connection.isConnected();
|
||||
return this.connection.isConnected()
|
||||
}
|
||||
|
||||
function getLedgerVersion(): Promise<number> {
|
||||
return this.connection.getLedgerVersion();
|
||||
return this.connection.getLedgerVersion()
|
||||
}
|
||||
|
||||
function connect(): Promise<void> {
|
||||
return this.connection.connect();
|
||||
return this.connection.connect()
|
||||
}
|
||||
|
||||
function disconnect(): Promise<void> {
|
||||
return this.connection.disconnect();
|
||||
return this.connection.disconnect()
|
||||
}
|
||||
|
||||
function getServerInfo(): Promise<GetServerInfoResponse> {
|
||||
return common.serverInfo.getServerInfo(this.connection);
|
||||
return common.serverInfo.getServerInfo(this.connection)
|
||||
}
|
||||
|
||||
function getFee(): Promise<number> {
|
||||
const cushion = this._feeCushion || 1.2;
|
||||
return common.serverInfo.getFee(this.connection, cushion);
|
||||
const cushion = this._feeCushion || 1.2
|
||||
return common.serverInfo.getFee(this.connection, cushion)
|
||||
}
|
||||
|
||||
function formatLedgerClose(ledgerClose: Object): Object {
|
||||
@@ -38,7 +38,7 @@ function formatLedgerClose(ledgerClose: Object): Object {
|
||||
reserveIncrementXRP: common.dropsToXrp(ledgerClose.reserve_inc),
|
||||
transactionCount: ledgerClose.txn_count,
|
||||
validatedLedgerVersions: ledgerClose.validated_ledgers
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -49,4 +49,4 @@ module.exports = {
|
||||
getFee,
|
||||
getLedgerVersion,
|
||||
formatLedgerClose
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const binary = require('ripple-binary-codec');
|
||||
const utils = require('./utils');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const {decodeAddress} = require('ripple-address-codec');
|
||||
const {validate} = utils.common;
|
||||
const {computeBinaryTransactionHash} = require('ripple-hashes');
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const binary = require('ripple-binary-codec')
|
||||
const utils = require('./utils')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const {decodeAddress} = require('ripple-address-codec')
|
||||
const {validate} = utils.common
|
||||
const {computeBinaryTransactionHash} = require('ripple-hashes')
|
||||
|
||||
function addressToBigNumber(address) {
|
||||
const hex = (new Buffer(decodeAddress(address))).toString('hex');
|
||||
return new BigNumber(hex, 16);
|
||||
const hex = (new Buffer(decodeAddress(address))).toString('hex')
|
||||
return new BigNumber(hex, 16)
|
||||
}
|
||||
|
||||
function compareSigners(a, b) {
|
||||
return addressToBigNumber(a.Signer.Account)
|
||||
.comparedTo(addressToBigNumber(b.Signer.Account));
|
||||
.comparedTo(addressToBigNumber(b.Signer.Account))
|
||||
}
|
||||
|
||||
function combine(signedTransactions: Array<string>): Object {
|
||||
validate.combine({signedTransactions});
|
||||
validate.combine({signedTransactions})
|
||||
|
||||
const txs = _.map(signedTransactions, binary.decode);
|
||||
const tx = _.omit(txs[0], 'Signers');
|
||||
const txs = _.map(signedTransactions, binary.decode)
|
||||
const tx = _.omit(txs[0], 'Signers')
|
||||
if (!_.every(txs, _tx => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
|
||||
throw new utils.common.errors.ValidationError(
|
||||
'txJSON is not the same for all signedTransactions');
|
||||
'txJSON is not the same for all signedTransactions')
|
||||
}
|
||||
const unsortedSigners = _.reduce(txs, (accumulator, _tx) =>
|
||||
accumulator.concat(_tx.Signers || []), []);
|
||||
const signers = unsortedSigners.sort(compareSigners);
|
||||
const signedTx = _.assign({}, tx, {Signers: signers});
|
||||
const signedTransaction = binary.encode(signedTx);
|
||||
const id = computeBinaryTransactionHash(signedTransaction);
|
||||
return {signedTransaction, id};
|
||||
accumulator.concat(_tx.Signers || []), [])
|
||||
const signers = unsortedSigners.sort(compareSigners)
|
||||
const signedTx = _.assign({}, tx, {Signers: signers})
|
||||
const signedTransaction = binary.encode(signedTx)
|
||||
const id = computeBinaryTransactionHash(signedTransaction)
|
||||
return {signedTransaction, id}
|
||||
}
|
||||
|
||||
module.exports = combine;
|
||||
module.exports = combine
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const offerFlags = utils.common.txFlags.OfferCreate;
|
||||
const {validate, iso8601ToRippleTime} = utils.common;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
import type {Order} from '../ledger/transaction-types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const offerFlags = utils.common.txFlags.OfferCreate
|
||||
const {validate, iso8601ToRippleTime} = utils.common
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
import type {Order} from '../ledger/transaction-types.js'
|
||||
|
||||
function createOrderTransaction(account: string, order: Order): Object {
|
||||
const takerPays = utils.common.toRippledAmount(order.direction === 'buy' ?
|
||||
order.quantity : order.totalPrice);
|
||||
order.quantity : order.totalPrice)
|
||||
const takerGets = utils.common.toRippledAmount(order.direction === 'buy' ?
|
||||
order.totalPrice : order.quantity);
|
||||
order.totalPrice : order.quantity)
|
||||
|
||||
const txJSON: Object = {
|
||||
TransactionType: 'OfferCreate',
|
||||
@@ -19,34 +19,37 @@ function createOrderTransaction(account: string, order: Order): Object {
|
||||
TakerGets: takerGets,
|
||||
TakerPays: takerPays,
|
||||
Flags: 0
|
||||
};
|
||||
}
|
||||
if (order.direction === 'sell') {
|
||||
txJSON.Flags |= offerFlags.Sell;
|
||||
txJSON.Flags |= offerFlags.Sell
|
||||
}
|
||||
if (order.passive === true) {
|
||||
txJSON.Flags |= offerFlags.Passive;
|
||||
txJSON.Flags |= offerFlags.Passive
|
||||
}
|
||||
if (order.immediateOrCancel === true) {
|
||||
txJSON.Flags |= offerFlags.ImmediateOrCancel;
|
||||
txJSON.Flags |= offerFlags.ImmediateOrCancel
|
||||
}
|
||||
if (order.fillOrKill === true) {
|
||||
txJSON.Flags |= offerFlags.FillOrKill;
|
||||
txJSON.Flags |= offerFlags.FillOrKill
|
||||
}
|
||||
if (order.expirationTime !== undefined) {
|
||||
txJSON.Expiration = iso8601ToRippleTime(order.expirationTime);
|
||||
txJSON.Expiration = iso8601ToRippleTime(order.expirationTime)
|
||||
}
|
||||
if (order.orderToReplace !== undefined) {
|
||||
txJSON.OfferSequence = order.orderToReplace
|
||||
}
|
||||
if (order.memos !== undefined) {
|
||||
txJSON.Memos = _.map(order.memos, utils.convertMemo);
|
||||
txJSON.Memos = _.map(order.memos, utils.convertMemo)
|
||||
}
|
||||
return txJSON;
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareOrder(address: string, order: Order,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.prepareOrder({address, order, instructions});
|
||||
const txJSON = createOrderTransaction(address, order);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
validate.prepareOrder({address, order, instructions})
|
||||
const txJSON = createOrderTransaction(address, order)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = prepareOrder;
|
||||
module.exports = prepareOrder
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const validate = utils.common.validate
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
|
||||
function createOrderCancellationTransaction(account: string,
|
||||
orderCancellation: Object
|
||||
@@ -12,19 +12,19 @@ function createOrderCancellationTransaction(account: string,
|
||||
TransactionType: 'OfferCancel',
|
||||
Account: account,
|
||||
OfferSequence: orderCancellation.orderSequence
|
||||
};
|
||||
if (orderCancellation.memos !== undefined) {
|
||||
txJSON.Memos = _.map(orderCancellation.memos, utils.convertMemo);
|
||||
}
|
||||
return txJSON;
|
||||
if (orderCancellation.memos !== undefined) {
|
||||
txJSON.Memos = _.map(orderCancellation.memos, utils.convertMemo)
|
||||
}
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareOrderCancellation(address: string, orderCancellation: Object,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.prepareOrderCancellation({address, orderCancellation, instructions});
|
||||
const txJSON = createOrderCancellationTransaction(address, orderCancellation);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
validate.prepareOrderCancellation({address, orderCancellation, instructions})
|
||||
const txJSON = createOrderCancellationTransaction(address, orderCancellation)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = prepareOrderCancellation;
|
||||
module.exports = prepareOrderCancellation
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const toRippledAmount = utils.common.toRippledAmount;
|
||||
const paymentFlags = utils.common.txFlags.Payment;
|
||||
const ValidationError = utils.common.errors.ValidationError;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const validate = utils.common.validate
|
||||
const toRippledAmount = utils.common.toRippledAmount
|
||||
const paymentFlags = utils.common.txFlags.Payment
|
||||
const ValidationError = utils.common.errors.ValidationError
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
import type {Amount, Adjustment, MaxAdjustment,
|
||||
MinAdjustment, Memo} from '../common/types.js';
|
||||
MinAdjustment, Memo} from '../common/types.js'
|
||||
|
||||
|
||||
type Payment = {
|
||||
@@ -34,15 +34,15 @@ type Payment = {
|
||||
|
||||
function isXRPToXRPPayment(payment: Payment): boolean {
|
||||
const sourceCurrency = _.get(payment, 'source.maxAmount.currency',
|
||||
_.get(payment, 'source.amount.currency'));
|
||||
_.get(payment, 'source.amount.currency'))
|
||||
const destinationCurrency = _.get(payment, 'destination.amount.currency',
|
||||
_.get(payment, 'destination.minAmount.currency'));
|
||||
return sourceCurrency === 'XRP' && destinationCurrency === 'XRP';
|
||||
_.get(payment, 'destination.minAmount.currency'))
|
||||
return sourceCurrency === 'XRP' && destinationCurrency === 'XRP'
|
||||
}
|
||||
|
||||
function isIOUWithoutCounterparty(amount: Amount): boolean {
|
||||
return amount && amount.currency !== 'XRP'
|
||||
&& amount.counterparty === undefined;
|
||||
&& amount.counterparty === undefined
|
||||
}
|
||||
|
||||
function applyAnyCounterpartyEncoding(payment: Payment): void {
|
||||
@@ -51,35 +51,35 @@ function applyAnyCounterpartyEncoding(payment: Payment): void {
|
||||
// https://ripple.com/build/transactions/
|
||||
// #special-issuer-values-for-sendmax-and-amount
|
||||
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
|
||||
_.forEach([payment.source, payment.destination], (adjustment) => {
|
||||
_.forEach(['amount', 'minAmount', 'maxAmount'], (key) => {
|
||||
_.forEach([payment.source, payment.destination], adjustment => {
|
||||
_.forEach(['amount', 'minAmount', 'maxAmount'], key => {
|
||||
if (isIOUWithoutCounterparty(adjustment[key])) {
|
||||
adjustment[key].counterparty = adjustment.address;
|
||||
adjustment[key].counterparty = adjustment.address
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function createMaximalAmount(amount: Amount): Amount {
|
||||
const maxXRPValue = '100000000000';
|
||||
const maxIOUValue = '9999999999999999e80';
|
||||
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue;
|
||||
return _.assign({}, amount, {value: maxValue});
|
||||
const maxXRPValue = '100000000000'
|
||||
const maxIOUValue = '9999999999999999e80'
|
||||
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue
|
||||
return _.assign({}, amount, {value: maxValue})
|
||||
}
|
||||
|
||||
function createPaymentTransaction(address: string, paymentArgument: Payment
|
||||
): Object {
|
||||
const payment = _.cloneDeep(paymentArgument);
|
||||
applyAnyCounterpartyEncoding(payment);
|
||||
const payment = _.cloneDeep(paymentArgument)
|
||||
applyAnyCounterpartyEncoding(payment)
|
||||
|
||||
if (address !== payment.source.address) {
|
||||
throw new ValidationError('address must match payment.source.address');
|
||||
throw new ValidationError('address must match payment.source.address')
|
||||
}
|
||||
|
||||
if ((payment.source.maxAmount && payment.destination.minAmount) ||
|
||||
(payment.source.amount && payment.destination.amount)) {
|
||||
throw new ValidationError('payment must specify either (source.maxAmount '
|
||||
+ 'and destination.amount) or (source.amount and destination.minAmount)');
|
||||
+ 'and destination.amount) or (source.amount and destination.minAmount)')
|
||||
}
|
||||
|
||||
// when using destination.minAmount, rippled still requires that we set
|
||||
@@ -90,7 +90,7 @@ function createPaymentTransaction(address: string, paymentArgument: Payment
|
||||
// cap could be hit before the source cap.
|
||||
const amount = payment.destination.minAmount && !isXRPToXRPPayment(payment) ?
|
||||
createMaximalAmount(payment.destination.minAmount) :
|
||||
(payment.destination.amount || payment.destination.minAmount);
|
||||
(payment.destination.amount || payment.destination.minAmount)
|
||||
|
||||
const txJSON: Object = {
|
||||
TransactionType: 'Payment',
|
||||
@@ -98,25 +98,25 @@ function createPaymentTransaction(address: string, paymentArgument: Payment
|
||||
Destination: payment.destination.address,
|
||||
Amount: toRippledAmount(amount),
|
||||
Flags: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (payment.invoiceID !== undefined) {
|
||||
txJSON.InvoiceID = payment.invoiceID;
|
||||
txJSON.InvoiceID = payment.invoiceID
|
||||
}
|
||||
if (payment.source.tag !== undefined) {
|
||||
txJSON.SourceTag = payment.source.tag;
|
||||
txJSON.SourceTag = payment.source.tag
|
||||
}
|
||||
if (payment.destination.tag !== undefined) {
|
||||
txJSON.DestinationTag = payment.destination.tag;
|
||||
txJSON.DestinationTag = payment.destination.tag
|
||||
}
|
||||
if (payment.memos !== undefined) {
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo);
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo)
|
||||
}
|
||||
if (payment.noDirectRipple === true) {
|
||||
txJSON.Flags |= paymentFlags.NoRippleDirect;
|
||||
txJSON.Flags |= paymentFlags.NoRippleDirect
|
||||
}
|
||||
if (payment.limitQuality === true) {
|
||||
txJSON.Flags |= paymentFlags.LimitQuality;
|
||||
txJSON.Flags |= paymentFlags.LimitQuality
|
||||
}
|
||||
if (!isXRPToXRPPayment(payment)) {
|
||||
// Don't set SendMax for XRP->XRP payment
|
||||
@@ -125,32 +125,32 @@ function createPaymentTransaction(address: string, paymentArgument: Payment
|
||||
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
|
||||
if (payment.allowPartialPayment === true
|
||||
|| payment.destination.minAmount !== undefined) {
|
||||
txJSON.Flags |= paymentFlags.PartialPayment;
|
||||
txJSON.Flags |= paymentFlags.PartialPayment
|
||||
}
|
||||
|
||||
txJSON.SendMax = toRippledAmount(
|
||||
payment.source.maxAmount || payment.source.amount);
|
||||
payment.source.maxAmount || payment.source.amount)
|
||||
|
||||
if (payment.destination.minAmount !== undefined) {
|
||||
txJSON.DeliverMin = toRippledAmount(payment.destination.minAmount);
|
||||
txJSON.DeliverMin = toRippledAmount(payment.destination.minAmount)
|
||||
}
|
||||
|
||||
if (payment.paths !== undefined) {
|
||||
txJSON.Paths = JSON.parse(payment.paths);
|
||||
txJSON.Paths = JSON.parse(payment.paths)
|
||||
}
|
||||
} else if (payment.allowPartialPayment === true) {
|
||||
throw new ValidationError('XRP to XRP payments cannot be partial payments');
|
||||
throw new ValidationError('XRP to XRP payments cannot be partial payments')
|
||||
}
|
||||
|
||||
return txJSON;
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function preparePayment(address: string, payment: Payment,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.preparePayment({address, payment, instructions});
|
||||
const txJSON = createPaymentTransaction(address, payment);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
validate.preparePayment({address, payment, instructions})
|
||||
const txJSON = createPaymentTransaction(address, payment)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = preparePayment;
|
||||
module.exports = preparePayment
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
type SettingPasswordSpent = {
|
||||
passwordSpent?: boolean,
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const AccountFlagIndices = utils.common.constants.AccountFlagIndices;
|
||||
const AccountFields = utils.common.constants.AccountFields;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
import type {Settings} from './settings-types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const assert = require('assert')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const utils = require('./utils')
|
||||
const validate = utils.common.validate
|
||||
const AccountFlagIndices = utils.common.constants.AccountFlagIndices
|
||||
const AccountFields = utils.common.constants.AccountFields
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
import type {Settings} from './settings-types.js'
|
||||
|
||||
// Emptry string passed to setting will clear it
|
||||
const CLEAR_SETTING = null;
|
||||
const CLEAR_SETTING = null
|
||||
|
||||
|
||||
function setTransactionFlags(txJSON: Object, values: Settings) {
|
||||
const keys = Object.keys(values);
|
||||
assert(keys.length === 1, 'ERROR: can only set one setting per transaction');
|
||||
const flagName = keys[0];
|
||||
const value = values[flagName];
|
||||
const index = AccountFlagIndices[flagName];
|
||||
const keys = Object.keys(values)
|
||||
assert(keys.length === 1, 'ERROR: can only set one setting per transaction')
|
||||
const flagName = keys[0]
|
||||
const value = values[flagName]
|
||||
const index = AccountFlagIndices[flagName]
|
||||
if (index !== undefined) {
|
||||
if (value) {
|
||||
txJSON.SetFlag = index;
|
||||
txJSON.SetFlag = index
|
||||
} else {
|
||||
txJSON.ClearFlag = index;
|
||||
txJSON.ClearFlag = index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setTransactionFields(txJSON: Object, input: Settings) {
|
||||
const fieldSchema = AccountFields;
|
||||
const fieldSchema = AccountFields
|
||||
for (const fieldName in fieldSchema) {
|
||||
const field = fieldSchema[fieldName];
|
||||
let value = input[field.name];
|
||||
const field = fieldSchema[fieldName]
|
||||
let value = input[field.name]
|
||||
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
// The value required to clear an account root field varies
|
||||
if (value === CLEAR_SETTING && field.hasOwnProperty('defaults')) {
|
||||
value = field.defaults;
|
||||
value = field.defaults
|
||||
}
|
||||
|
||||
if (field.encoding === 'hex' && !field.length) {
|
||||
// This is currently only used for Domain field
|
||||
value = new Buffer(value, 'ascii').toString('hex').toUpperCase();
|
||||
value = new Buffer(value, 'ascii').toString('hex').toUpperCase()
|
||||
}
|
||||
|
||||
txJSON[fieldName] = value;
|
||||
txJSON[fieldName] = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ function setTransactionFields(txJSON: Object, input: Settings) {
|
||||
*/
|
||||
|
||||
function convertTransferRate(transferRate: number | string): number | string {
|
||||
return (new BigNumber(transferRate)).shift(9).toNumber();
|
||||
return (new BigNumber(transferRate)).shift(9).toNumber()
|
||||
}
|
||||
|
||||
function formatSignerEntry(signer: Object): Object {
|
||||
@@ -76,7 +76,7 @@ function formatSignerEntry(signer: Object): Object {
|
||||
Account: signer.address,
|
||||
SignerWeight: signer.weight
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function createSettingsTransactionWithoutMemos(
|
||||
@@ -86,11 +86,11 @@ function createSettingsTransactionWithoutMemos(
|
||||
const removeRegularKey = {
|
||||
TransactionType: 'SetRegularKey',
|
||||
Account: account
|
||||
};
|
||||
if (settings.regularKey === null) {
|
||||
return removeRegularKey;
|
||||
}
|
||||
return _.assign({}, removeRegularKey, {RegularKey: settings.regularKey});
|
||||
if (settings.regularKey === null) {
|
||||
return removeRegularKey
|
||||
}
|
||||
return _.assign({}, removeRegularKey, {RegularKey: settings.regularKey})
|
||||
}
|
||||
|
||||
if (settings.signers !== undefined) {
|
||||
@@ -99,38 +99,38 @@ function createSettingsTransactionWithoutMemos(
|
||||
Account: account,
|
||||
SignerQuorum: settings.signers.threshold,
|
||||
SignerEntries: _.map(settings.signers.weights, formatSignerEntry)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const txJSON: Object = {
|
||||
TransactionType: 'AccountSet',
|
||||
Account: account
|
||||
};
|
||||
}
|
||||
|
||||
setTransactionFlags(txJSON, _.omit(settings, 'memos'));
|
||||
setTransactionFields(txJSON, settings);
|
||||
setTransactionFlags(txJSON, _.omit(settings, 'memos'))
|
||||
setTransactionFields(txJSON, settings)
|
||||
|
||||
if (txJSON.TransferRate !== undefined) {
|
||||
txJSON.TransferRate = convertTransferRate(txJSON.TransferRate);
|
||||
txJSON.TransferRate = convertTransferRate(txJSON.TransferRate)
|
||||
}
|
||||
return txJSON;
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function createSettingsTransaction(account: string, settings: Settings
|
||||
): Object {
|
||||
const txJSON = createSettingsTransactionWithoutMemos(account, settings);
|
||||
const txJSON = createSettingsTransactionWithoutMemos(account, settings)
|
||||
if (settings.memos !== undefined) {
|
||||
txJSON.Memos = _.map(settings.memos, utils.convertMemo);
|
||||
txJSON.Memos = _.map(settings.memos, utils.convertMemo)
|
||||
}
|
||||
return txJSON;
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareSettings(address: string, settings: Settings,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.prepareSettings({address, settings, instructions});
|
||||
const txJSON = createSettingsTransaction(address, settings);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
validate.prepareSettings({address, settings, instructions})
|
||||
const txJSON = createSettingsTransaction(address, settings)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = prepareSettings;
|
||||
module.exports = prepareSettings
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const keypairs = require('ripple-keypairs');
|
||||
const binary = require('ripple-binary-codec');
|
||||
const {computeBinaryTransactionHash} = require('ripple-hashes');
|
||||
const validate = utils.common.validate;
|
||||
'use strict' // eslint-disable-line strict
|
||||
const utils = require('./utils')
|
||||
const keypairs = require('ripple-keypairs')
|
||||
const binary = require('ripple-binary-codec')
|
||||
const {computeBinaryTransactionHash} = require('ripple-hashes')
|
||||
const validate = utils.common.validate
|
||||
|
||||
function computeSignature(tx: Object, privateKey: string, signAs: ?string) {
|
||||
const signingData = signAs ?
|
||||
binary.encodeForMultisigning(tx, signAs) : binary.encodeForSigning(tx);
|
||||
return keypairs.sign(signingData, privateKey);
|
||||
binary.encodeForMultisigning(tx, signAs) : binary.encodeForSigning(tx)
|
||||
return keypairs.sign(signingData, privateKey)
|
||||
}
|
||||
|
||||
function sign(txJSON: string, secret: string, options: Object = {}
|
||||
): {signedTransaction: string; id: string} {
|
||||
validate.sign({txJSON, secret});
|
||||
validate.sign({txJSON, secret})
|
||||
// we can't validate that the secret matches the account because
|
||||
// the secret could correspond to the regular key
|
||||
|
||||
const tx = JSON.parse(txJSON);
|
||||
const tx = JSON.parse(txJSON)
|
||||
if (tx.TxnSignature || tx.Signers) {
|
||||
throw new utils.common.errors.ValidationError(
|
||||
'txJSON must not contain "TxnSignature" or "Signers" properties');
|
||||
'txJSON must not contain "TxnSignature" or "Signers" properties')
|
||||
}
|
||||
|
||||
const keypair = keypairs.deriveKeypair(secret);
|
||||
tx.SigningPubKey = options.signAs ? '' : keypair.publicKey;
|
||||
const keypair = keypairs.deriveKeypair(secret)
|
||||
tx.SigningPubKey = options.signAs ? '' : keypair.publicKey
|
||||
|
||||
if (options.signAs) {
|
||||
const signer = {
|
||||
Account: options.signAs,
|
||||
SigningPubKey: keypair.publicKey,
|
||||
TxnSignature: computeSignature(tx, keypair.privateKey, options.signAs)
|
||||
};
|
||||
tx.Signers = [{Signer: signer}];
|
||||
}
|
||||
tx.Signers = [{Signer: signer}]
|
||||
} else {
|
||||
tx.TxnSignature = computeSignature(tx, keypair.privateKey);
|
||||
tx.TxnSignature = computeSignature(tx, keypair.privateKey)
|
||||
}
|
||||
|
||||
const serialized = binary.encode(tx);
|
||||
const serialized = binary.encode(tx)
|
||||
return {
|
||||
signedTransaction: serialized,
|
||||
id: computeBinaryTransactionHash(serialized)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = sign;
|
||||
module.exports = sign
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const {validate} = utils.common;
|
||||
import type {Submit} from './types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const {validate} = utils.common
|
||||
import type {Submit} from './types.js'
|
||||
|
||||
function isImmediateRejection(engineResult: string): boolean {
|
||||
// note: "tel" errors mean the local server refused to process the
|
||||
@@ -12,28 +12,28 @@ function isImmediateRejection(engineResult: string): boolean {
|
||||
// if the required fee changes (this does not occur at the time of
|
||||
// this writing, but it could change in the future)
|
||||
// all other error classes can potentially result in transcation validation
|
||||
return _.startsWith(engineResult, 'tem') || _.startsWith(engineResult, 'tej');
|
||||
return _.startsWith(engineResult, 'tem')
|
||||
}
|
||||
|
||||
function formatSubmitResponse(response) {
|
||||
const data = {
|
||||
resultCode: response.engine_result,
|
||||
resultMessage: response.engine_result_message
|
||||
};
|
||||
if (isImmediateRejection(response.engine_result)) {
|
||||
throw new utils.common.errors.RippledError('Submit failed', data);
|
||||
}
|
||||
return data;
|
||||
if (isImmediateRejection(response.engine_result)) {
|
||||
throw new utils.common.errors.RippledError('Submit failed', data)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
function submit(signedTransaction: string): Promise<Submit> {
|
||||
validate.submit({signedTransaction});
|
||||
validate.submit({signedTransaction})
|
||||
|
||||
const request = {
|
||||
command: 'submit',
|
||||
tx_blob: signedTransaction
|
||||
};
|
||||
return this.connection.request(request).then(formatSubmitResponse);
|
||||
}
|
||||
return this.connection.request(request).then(formatSubmitResponse)
|
||||
}
|
||||
|
||||
module.exports = submit;
|
||||
module.exports = submit
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
import type {Memo} from '../common/types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const validate = utils.common.validate
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
import type {Memo} from '../common/types.js'
|
||||
|
||||
type SuspendedPaymentCancellation = {
|
||||
owner: string,
|
||||
@@ -20,11 +20,11 @@ function createSuspendedPaymentCancellationTransaction(account: string,
|
||||
Account: account,
|
||||
Owner: payment.owner,
|
||||
OfferSequence: payment.suspensionSequence
|
||||
};
|
||||
if (payment.memos !== undefined) {
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo);
|
||||
}
|
||||
return txJSON;
|
||||
if (payment.memos !== undefined) {
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo)
|
||||
}
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentCancellation(address: string,
|
||||
@@ -32,10 +32,10 @@ function prepareSuspendedPaymentCancellation(address: string,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.prepareSuspendedPaymentCancellation(
|
||||
{address, suspendedPaymentCancellation, instructions});
|
||||
{address, suspendedPaymentCancellation, instructions})
|
||||
const txJSON = createSuspendedPaymentCancellationTransaction(
|
||||
address, suspendedPaymentCancellation);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
address, suspendedPaymentCancellation)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = prepareSuspendedPaymentCancellation;
|
||||
module.exports = prepareSuspendedPaymentCancellation
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const {validate, iso8601ToRippleTime, toRippledAmount} = utils.common;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
import type {Adjustment, MaxAdjustment, Memo} from '../common/types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const {validate, iso8601ToRippleTime, toRippledAmount} = utils.common
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
import type {Adjustment, MaxAdjustment, Memo} from '../common/types.js'
|
||||
|
||||
type SuspendedPaymentCreation = {
|
||||
source: MaxAdjustment,
|
||||
@@ -23,27 +23,27 @@ function createSuspendedPaymentCreationTransaction(account: string,
|
||||
Account: account,
|
||||
Destination: payment.destination.address,
|
||||
Amount: toRippledAmount(payment.destination.amount)
|
||||
};
|
||||
}
|
||||
|
||||
if (payment.digest !== undefined) {
|
||||
txJSON.Digest = payment.digest;
|
||||
txJSON.Digest = payment.digest
|
||||
}
|
||||
if (payment.allowCancelAfter !== undefined) {
|
||||
txJSON.CancelAfter = iso8601ToRippleTime(payment.allowCancelAfter);
|
||||
txJSON.CancelAfter = iso8601ToRippleTime(payment.allowCancelAfter)
|
||||
}
|
||||
if (payment.allowExecuteAfter !== undefined) {
|
||||
txJSON.FinishAfter = iso8601ToRippleTime(payment.allowExecuteAfter);
|
||||
txJSON.FinishAfter = iso8601ToRippleTime(payment.allowExecuteAfter)
|
||||
}
|
||||
if (payment.source.tag !== undefined) {
|
||||
txJSON.SourceTag = payment.source.tag;
|
||||
txJSON.SourceTag = payment.source.tag
|
||||
}
|
||||
if (payment.destination.tag !== undefined) {
|
||||
txJSON.DestinationTag = payment.destination.tag;
|
||||
txJSON.DestinationTag = payment.destination.tag
|
||||
}
|
||||
if (payment.memos !== undefined) {
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo);
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo)
|
||||
}
|
||||
return txJSON;
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentCreation(address: string,
|
||||
@@ -51,10 +51,10 @@ function prepareSuspendedPaymentCreation(address: string,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.prepareSuspendedPaymentCreation(
|
||||
{address, suspendedPaymentCreation, instructions});
|
||||
{address, suspendedPaymentCreation, instructions})
|
||||
const txJSON = createSuspendedPaymentCreationTransaction(
|
||||
address, suspendedPaymentCreation);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
address, suspendedPaymentCreation)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = prepareSuspendedPaymentCreation;
|
||||
module.exports = prepareSuspendedPaymentCreation
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
import type {Memo} from '../common/types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const validate = utils.common.validate
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
import type {Memo} from '../common/types.js'
|
||||
|
||||
type SuspendedPaymentExecution = {
|
||||
owner: string,
|
||||
@@ -23,21 +23,21 @@ function createSuspendedPaymentExecutionTransaction(account: string,
|
||||
Account: account,
|
||||
Owner: payment.owner,
|
||||
OfferSequence: payment.suspensionSequence
|
||||
};
|
||||
}
|
||||
|
||||
if (payment.method !== undefined) {
|
||||
txJSON.Method = payment.method;
|
||||
txJSON.Method = payment.method
|
||||
}
|
||||
if (payment.digest !== undefined) {
|
||||
txJSON.Digest = payment.digest;
|
||||
txJSON.Digest = payment.digest
|
||||
}
|
||||
if (payment.proof !== undefined) {
|
||||
txJSON.Proof = utils.convertStringToHex(payment.proof);
|
||||
txJSON.Proof = utils.convertStringToHex(payment.proof)
|
||||
}
|
||||
if (payment.memos !== undefined) {
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo);
|
||||
txJSON.Memos = _.map(payment.memos, utils.convertMemo)
|
||||
}
|
||||
return txJSON;
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentExecution(address: string,
|
||||
@@ -45,10 +45,10 @@ function prepareSuspendedPaymentExecution(address: string,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.prepareSuspendedPaymentExecution(
|
||||
{address, suspendedPaymentExecution, instructions});
|
||||
{address, suspendedPaymentExecution, instructions})
|
||||
const txJSON = createSuspendedPaymentExecutionTransaction(
|
||||
address, suspendedPaymentExecution);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
address, suspendedPaymentExecution)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = prepareSuspendedPaymentExecution;
|
||||
module.exports = prepareSuspendedPaymentExecution
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const trustlineFlags = utils.common.txFlags.TrustSet;
|
||||
const BigNumber = require('bignumber.js');
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
import type {TrustLineSpecification} from '../ledger/trustlines-types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const utils = require('./utils')
|
||||
const validate = utils.common.validate
|
||||
const trustlineFlags = utils.common.txFlags.TrustSet
|
||||
const BigNumber = require('bignumber.js')
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
import type {TrustLineSpecification} from '../ledger/trustlines-types.js'
|
||||
|
||||
function convertQuality(quality) {
|
||||
return (new BigNumber(quality)).shift(9).truncated().toNumber();
|
||||
return (new BigNumber(quality)).shift(9).truncated().toNumber()
|
||||
}
|
||||
|
||||
function createTrustlineTransaction(account: string,
|
||||
@@ -19,43 +19,43 @@ function createTrustlineTransaction(account: string,
|
||||
currency: trustline.currency,
|
||||
issuer: trustline.counterparty,
|
||||
value: trustline.limit
|
||||
};
|
||||
}
|
||||
|
||||
const txJSON: Object = {
|
||||
TransactionType: 'TrustSet',
|
||||
Account: account,
|
||||
LimitAmount: limit,
|
||||
Flags: 0
|
||||
};
|
||||
}
|
||||
if (trustline.qualityIn !== undefined) {
|
||||
txJSON.QualityIn = convertQuality(trustline.qualityIn);
|
||||
txJSON.QualityIn = convertQuality(trustline.qualityIn)
|
||||
}
|
||||
if (trustline.qualityOut !== undefined) {
|
||||
txJSON.QualityOut = convertQuality(trustline.qualityOut);
|
||||
txJSON.QualityOut = convertQuality(trustline.qualityOut)
|
||||
}
|
||||
if (trustline.authorized === true) {
|
||||
txJSON.Flags |= trustlineFlags.SetAuth;
|
||||
txJSON.Flags |= trustlineFlags.SetAuth
|
||||
}
|
||||
if (trustline.ripplingDisabled !== undefined) {
|
||||
txJSON.Flags |= trustline.ripplingDisabled ?
|
||||
trustlineFlags.NoRipple : trustlineFlags.ClearNoRipple;
|
||||
trustlineFlags.NoRipple : trustlineFlags.ClearNoRipple
|
||||
}
|
||||
if (trustline.frozen !== undefined) {
|
||||
txJSON.Flags |= trustline.frozen ?
|
||||
trustlineFlags.SetFreeze : trustlineFlags.ClearFreeze;
|
||||
trustlineFlags.SetFreeze : trustlineFlags.ClearFreeze
|
||||
}
|
||||
if (trustline.memos !== undefined) {
|
||||
txJSON.Memos = _.map(trustline.memos, utils.convertMemo);
|
||||
txJSON.Memos = _.map(trustline.memos, utils.convertMemo)
|
||||
}
|
||||
return txJSON;
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareTrustline(address: string,
|
||||
trustline: TrustLineSpecification, instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
validate.prepareTrustline({address, trustline, instructions});
|
||||
const txJSON = createTrustlineTransaction(address, trustline);
|
||||
return utils.prepareTransaction(txJSON, this, instructions);
|
||||
validate.prepareTrustline({address, trustline, instructions})
|
||||
const txJSON = createTrustlineTransaction(address, trustline)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
module.exports = prepareTrustline;
|
||||
module.exports = prepareTrustline
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
'use strict' // eslint-disable-line strict
|
||||
|
||||
export type Instructions = {
|
||||
sequence?: number,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const common = require('../common');
|
||||
const txFlags = common.txFlags;
|
||||
import type {Instructions, Prepare} from './types.js';
|
||||
'use strict' // eslint-disable-line strict
|
||||
const _ = require('lodash')
|
||||
const BigNumber = require('bignumber.js')
|
||||
const common = require('../common')
|
||||
const txFlags = common.txFlags
|
||||
import type {Instructions, Prepare} from './types.js'
|
||||
|
||||
function formatPrepareResponse(txJSON: Object): Object {
|
||||
const instructions = {
|
||||
@@ -12,94 +12,94 @@ function formatPrepareResponse(txJSON: Object): Object {
|
||||
sequence: txJSON.Sequence,
|
||||
maxLedgerVersion: txJSON.LastLedgerSequence === undefined ?
|
||||
null : txJSON.LastLedgerSequence
|
||||
};
|
||||
}
|
||||
return {
|
||||
txJSON: JSON.stringify(txJSON),
|
||||
instructions: _.omit(instructions, _.isUndefined)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function setCanonicalFlag(txJSON) {
|
||||
txJSON.Flags |= txFlags.Universal.FullyCanonicalSig;
|
||||
txJSON.Flags |= txFlags.Universal.FullyCanonicalSig
|
||||
|
||||
// JavaScript converts operands to 32-bit signed ints before doing bitwise
|
||||
// operations. We need to convert it back to an unsigned int.
|
||||
txJSON.Flags = txJSON.Flags >>> 0;
|
||||
txJSON.Flags = txJSON.Flags >>> 0
|
||||
}
|
||||
|
||||
function scaleValue(value, multiplier) {
|
||||
return (new BigNumber(value)).times(multiplier).toString();
|
||||
return (new BigNumber(value)).times(multiplier).toString()
|
||||
}
|
||||
|
||||
function prepareTransaction(txJSON: Object, api: Object,
|
||||
instructions: Instructions
|
||||
): Promise<Prepare> {
|
||||
common.validate.instructions(instructions);
|
||||
common.validate.instructions(instructions)
|
||||
|
||||
const account = txJSON.Account;
|
||||
setCanonicalFlag(txJSON);
|
||||
const account = txJSON.Account
|
||||
setCanonicalFlag(txJSON)
|
||||
|
||||
function prepareMaxLedgerVersion(): Promise<Object> {
|
||||
if (instructions.maxLedgerVersion !== undefined) {
|
||||
if (instructions.maxLedgerVersion !== null) {
|
||||
txJSON.LastLedgerSequence = instructions.maxLedgerVersion;
|
||||
txJSON.LastLedgerSequence = instructions.maxLedgerVersion
|
||||
}
|
||||
return Promise.resolve(txJSON);
|
||||
return Promise.resolve(txJSON)
|
||||
}
|
||||
const offset = instructions.maxLedgerVersionOffset !== undefined ?
|
||||
instructions.maxLedgerVersionOffset : 3;
|
||||
instructions.maxLedgerVersionOffset : 3
|
||||
return api.connection.getLedgerVersion().then(ledgerVersion => {
|
||||
txJSON.LastLedgerSequence = ledgerVersion + offset;
|
||||
return txJSON;
|
||||
});
|
||||
txJSON.LastLedgerSequence = ledgerVersion + offset
|
||||
return txJSON
|
||||
})
|
||||
}
|
||||
|
||||
function prepareFee(): Promise<Object> {
|
||||
const multiplier = instructions.signersCount === undefined ? 1 :
|
||||
instructions.signersCount + 1;
|
||||
instructions.signersCount + 1
|
||||
if (instructions.fee !== undefined) {
|
||||
txJSON.Fee = scaleValue(common.xrpToDrops(instructions.fee), multiplier);
|
||||
return Promise.resolve(txJSON);
|
||||
txJSON.Fee = scaleValue(common.xrpToDrops(instructions.fee), multiplier)
|
||||
return Promise.resolve(txJSON)
|
||||
}
|
||||
const cushion = api._feeCushion;
|
||||
const cushion = api._feeCushion
|
||||
return common.serverInfo.getFee(api.connection, cushion).then(fee => {
|
||||
const feeDrops = common.xrpToDrops(fee);
|
||||
const feeDrops = common.xrpToDrops(fee)
|
||||
if (instructions.maxFee !== undefined) {
|
||||
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
|
||||
const normalFee = BigNumber.min(feeDrops, maxFeeDrops).toString();
|
||||
txJSON.Fee = scaleValue(normalFee, multiplier);
|
||||
const maxFeeDrops = common.xrpToDrops(instructions.maxFee)
|
||||
const normalFee = BigNumber.min(feeDrops, maxFeeDrops).toString()
|
||||
txJSON.Fee = scaleValue(normalFee, multiplier)
|
||||
} else {
|
||||
txJSON.Fee = scaleValue(feeDrops, multiplier);
|
||||
txJSON.Fee = scaleValue(feeDrops, multiplier)
|
||||
}
|
||||
return txJSON;
|
||||
});
|
||||
return txJSON
|
||||
})
|
||||
}
|
||||
|
||||
function prepareSequence(): Promise<Object> {
|
||||
if (instructions.sequence !== undefined) {
|
||||
txJSON.Sequence = instructions.sequence;
|
||||
return Promise.resolve(txJSON);
|
||||
txJSON.Sequence = instructions.sequence
|
||||
return Promise.resolve(txJSON)
|
||||
}
|
||||
const request = {
|
||||
command: 'account_info',
|
||||
account: account
|
||||
};
|
||||
}
|
||||
return api.connection.request(request).then(response => {
|
||||
txJSON.Sequence = response.account_data.Sequence;
|
||||
return txJSON;
|
||||
});
|
||||
txJSON.Sequence = response.account_data.Sequence
|
||||
return txJSON
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.all([
|
||||
prepareMaxLedgerVersion(),
|
||||
prepareFee(),
|
||||
prepareSequence()
|
||||
]).then(() => formatPrepareResponse(txJSON));
|
||||
]).then(() => formatPrepareResponse(txJSON))
|
||||
}
|
||||
|
||||
function convertStringToHex(string: string) {
|
||||
return string ? (new Buffer(string, 'utf8')).toString('hex').toUpperCase() :
|
||||
undefined;
|
||||
undefined
|
||||
}
|
||||
|
||||
function convertMemo(memo: Object): Object {
|
||||
@@ -109,7 +109,7 @@ function convertMemo(memo: Object): Object {
|
||||
MemoType: convertStringToHex(memo.type),
|
||||
MemoFormat: convertStringToHex(memo.format)
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -117,4 +117,4 @@ module.exports = {
|
||||
convertMemo,
|
||||
prepareTransaction,
|
||||
common
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable max-nested-callbacks */
|
||||
'use strict';
|
||||
'use strict'; // eslint-disable-line
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert-diff');
|
||||
const setupAPI = require('./setup-api');
|
||||
@@ -708,6 +708,21 @@ describe('RippleAPI', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - amendment', function() {
|
||||
const hash =
|
||||
'A971B83ABED51D83749B73F3C1AAA627CD965AFF74BE8CD98299512D6FB0658F';
|
||||
return this.api.getTransaction(hash).then(result => {
|
||||
assert.deepEqual(result, responses.getTransaction.amendment);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - feeUpdate', function() {
|
||||
const hash =
|
||||
'C6A40F56127436DCD830B1B35FF939FD05B5747D30D6542572B7A835239817AF';
|
||||
return this.api.getTransaction(hash).then(result => {
|
||||
assert.deepEqual(result, responses.getTransaction.feeUpdate);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransactions', function() {
|
||||
@@ -981,6 +996,19 @@ describe('RippleAPI', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getServerInfo - no validated ledger', function() {
|
||||
this.api.connection._send(JSON.stringify({
|
||||
command: 'config',
|
||||
data: {serverInfoWithoutValidated: true}
|
||||
}));
|
||||
|
||||
return this.api.getServerInfo().then(info => {
|
||||
assert.strictEqual(info.networkLedger, 'waiting');
|
||||
}).catch(error => {
|
||||
assert(false, 'Should not throw Error, got ' + String(error));
|
||||
});
|
||||
});
|
||||
|
||||
it('getFee', function() {
|
||||
return this.api.getFee().then(fee => {
|
||||
assert.strictEqual(fee, '0.000012');
|
||||
@@ -1143,6 +1171,28 @@ describe('RippleAPI', function() {
|
||||
_.partial(checkResult, responses.getLedger.withSettingsTx, 'getLedger'));
|
||||
});
|
||||
|
||||
it('getLedger - with partial payment', function() {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeAllData: true,
|
||||
ledgerVersion: 100000
|
||||
};
|
||||
return this.api.getLedger(request).then(
|
||||
_.partial(checkResult, responses.getLedger.withPartial, 'getLedger'));
|
||||
});
|
||||
|
||||
it('getLedger - pre 2014 with partial payment', function() {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeAllData: true,
|
||||
ledgerVersion: 100001
|
||||
};
|
||||
return this.api.getLedger(request).then(
|
||||
_.partial(checkResult,
|
||||
responses.getLedger.pre2014withPartial,
|
||||
'getLedger'));
|
||||
});
|
||||
|
||||
it('getLedger - full, then computeLedgerHash', function() {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use strict'; // eslint-disable-line
|
||||
/* eslint-disable max-nested-callbacks */
|
||||
|
||||
const _ = require('lodash');
|
||||
@@ -117,7 +118,9 @@ describe('Connection', function() {
|
||||
}
|
||||
}
|
||||
|
||||
const connection = new utils.common.Connection('ws://127.0.0.1:321');
|
||||
// Address where no one listens
|
||||
const connection =
|
||||
new utils.common.Connection('ws://testripple.circleci.com:129');
|
||||
connection.on('error', done);
|
||||
connection.connect().catch(error => {
|
||||
assert(error instanceof this.api.errors.NotConnectedError);
|
||||
@@ -191,6 +194,114 @@ describe('Connection', function() {
|
||||
}, 1);
|
||||
});
|
||||
|
||||
describe('reconnection test', function() {
|
||||
beforeEach(function() {
|
||||
this.api.connection.__workingUrl = this.api.connection._url;
|
||||
this.api.connection.__doReturnBad = function() {
|
||||
this._url = this.__badUrl;
|
||||
const self = this;
|
||||
function onReconnect(num) {
|
||||
if (num >= 2) {
|
||||
self._url = self.__workingUrl;
|
||||
self.removeListener('reconnecting', onReconnect);
|
||||
}
|
||||
}
|
||||
this.on('reconnecting', onReconnect);
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
||||
});
|
||||
|
||||
it('reconnect on several unexpected close', function(done) {
|
||||
if (process.browser) {
|
||||
const phantomTest = /PhantomJS/;
|
||||
if (phantomTest.test(navigator.userAgent)) {
|
||||
// inside PhantomJS this one just hangs, so skip as not very relevant
|
||||
done();
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.timeout(70001);
|
||||
const self = this;
|
||||
self.api.connection.__badUrl = 'ws://testripple.circleci.com:129';
|
||||
function breakConnection() {
|
||||
self.api.connection.__doReturnBad();
|
||||
self.api.connection._send(JSON.stringify({
|
||||
command: 'test_command',
|
||||
data: {disconnectIn: 10}
|
||||
}));
|
||||
}
|
||||
|
||||
let connectsCount = 0;
|
||||
let disconnectsCount = 0;
|
||||
let reconnectsCount = 0;
|
||||
let code = 0;
|
||||
this.api.connection.on('reconnecting', () => {
|
||||
reconnectsCount += 1;
|
||||
});
|
||||
this.api.connection.on('disconnected', _code => {
|
||||
code = _code;
|
||||
disconnectsCount += 1;
|
||||
});
|
||||
const num = 3;
|
||||
this.api.connection.on('connected', () => {
|
||||
connectsCount += 1;
|
||||
if (connectsCount < num) {
|
||||
breakConnection();
|
||||
}
|
||||
if (connectsCount === num) {
|
||||
if (disconnectsCount !== num) {
|
||||
done(new Error('disconnectsCount must be equal to ' + num +
|
||||
'(got ' + disconnectsCount + ' instead)'));
|
||||
} else if (reconnectsCount !== num * 2) {
|
||||
done(new Error('reconnectsCount must be equal to ' + num * 2 +
|
||||
' (got ' + reconnectsCount + ' instead)'));
|
||||
} else if (code !== 1006) {
|
||||
done(new Error('disconnect must send code 1006 (got ' + code +
|
||||
' instead)'));
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
breakConnection();
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit disconnected event with code 1000 (CLOSE_NORMAL)',
|
||||
function(done
|
||||
) {
|
||||
this.api.once('disconnected', code => {
|
||||
assert.strictEqual(code, 1000);
|
||||
done();
|
||||
});
|
||||
this.api.disconnect();
|
||||
});
|
||||
|
||||
it('should emit disconnected event with code 1006 (CLOSE_ABNORMAL)',
|
||||
function(done
|
||||
) {
|
||||
this.api.once('error', error => {
|
||||
done(new Error('should not throw error, got ' + String(error)));
|
||||
});
|
||||
this.api.once('disconnected', code => {
|
||||
assert.strictEqual(code, 1006);
|
||||
done();
|
||||
});
|
||||
this.api.connection._send(JSON.stringify({
|
||||
command: 'test_command',
|
||||
data: {disconnectIn: 10}
|
||||
}));
|
||||
});
|
||||
|
||||
it('should emit connected event on after reconnect', function(done) {
|
||||
this.api.once('connected', done);
|
||||
this.api.connection._ws.close();
|
||||
});
|
||||
|
||||
it('Multiply connect calls', function() {
|
||||
return this.api.connect().then(() => {
|
||||
return this.api.connect();
|
||||
@@ -301,4 +412,50 @@ describe('Connection', function() {
|
||||
});
|
||||
this.api.connection._ws.emit('message', JSON.stringify(message));
|
||||
});
|
||||
|
||||
it('should throw RippledNotInitializedError if server does not have ' +
|
||||
'validated ledgers',
|
||||
function() {
|
||||
this.timeout(3000);
|
||||
|
||||
this.api.connection._send(JSON.stringify({
|
||||
command: 'global_config',
|
||||
data: {returnEmptySubscribeRequest: 1}
|
||||
}));
|
||||
|
||||
const api = new RippleAPI({server: this.api.connection._url});
|
||||
return api.connect().then(() => {
|
||||
assert(false, 'Must have thrown!');
|
||||
}, error => {
|
||||
assert(error instanceof this.api.errors.RippledNotInitializedError,
|
||||
'Must throw RippledNotInitializedError, got instead ' + String(error));
|
||||
});
|
||||
});
|
||||
|
||||
it('should try to reconnect on empty subscribe response on reconnect',
|
||||
function(done) {
|
||||
this.timeout(23000);
|
||||
|
||||
this.api.on('error', error => {
|
||||
done(error || new Error('Should not emit error.'));
|
||||
});
|
||||
let disconncedCount = 0;
|
||||
this.api.on('connected', () => {
|
||||
done(disconncedCount !== 1 ?
|
||||
new Error('Wrong number of disconnects') : undefined);
|
||||
});
|
||||
this.api.on('disconnected', () => {
|
||||
disconncedCount++;
|
||||
});
|
||||
|
||||
this.api.connection._send(JSON.stringify({
|
||||
command: 'global_config',
|
||||
data: {returnEmptySubscribeRequest: 3}
|
||||
}));
|
||||
|
||||
this.api.connection._send(JSON.stringify({
|
||||
command: 'test_command',
|
||||
data: {disconnectIn: 10}
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
"value": "5.00"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"value": "2"
|
||||
},
|
||||
"immediateOrCancel": true,
|
||||
"orderToReplace": 12345,
|
||||
"memos": [
|
||||
{
|
||||
"type": "test",
|
||||
|
||||
2
test/fixtures/requests/sign-suspended.json
vendored
2
test/fixtures/requests/sign-suspended.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"txJSON": "{\"TransactionType\":\"SuspendedPaymentFinish\",\"Account\":\"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh\",\"Owner\":\"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh\",\"OfferSequence\":2,\"Method\":1,\"Digest\":\"712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D\",\"Proof\":\"74686973206D757374206861766520333220636861726163746572732E2E2E2E\",\"Flags\":2147483648,\"LastLedgerSequence\":102,\"Fee\":\"12\",\"Sequence\":1}",
|
||||
"txJSON": "{\"TransactionType\":\"EscrowFinish\",\"Account\":\"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh\",\"Owner\":\"rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh\",\"OfferSequence\":2,\"Method\":1,\"Digest\":\"712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D\",\"Proof\":\"74686973206D757374206861766520333220636861726163746572732E2E2E2E\",\"Flags\":2147483648,\"LastLedgerSequence\":102,\"Fee\":\"12\",\"Sequence\":1}",
|
||||
"instructions": {
|
||||
"fee": "0.000012",
|
||||
"sequence": 1,
|
||||
|
||||
79
test/fixtures/responses/get-ledger-pre2014-with-partial.json
vendored
Normal file
79
test/fixtures/responses/get-ledger-pre2014-with-partial.json
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"stateHash": "334EE5F2209538C3099E133D25725E5BFEB40A198EA7028E6317F13E95D533DF",
|
||||
"closeTime": "2016-07-07T16:53:51.000Z",
|
||||
"closeTimeResolution": 10,
|
||||
"closeFlags": 0,
|
||||
"ledgerHash": "4F6C0495378FF68A15749C0D51D097EB638DA70319FDAC7A97A27CE63E0BFFED",
|
||||
"ledgerVersion": 12345,
|
||||
"parentLedgerHash": "4F636662B714CD9CCE965E9C23BB2E1058A2DF496F5A2416299317AE03F1CD35",
|
||||
"parentCloseTime": "2016-07-07T16:53:50.000Z",
|
||||
"totalDrops": "99997302532397566",
|
||||
"transactionHash": "C72A2BDCB471F3AEEB917ABC6019407CAE6DA4B858903A8AB2335A0EB077125D",
|
||||
"transactions": [
|
||||
{
|
||||
"type": "payment",
|
||||
"address": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX",
|
||||
"sequence": 23295,
|
||||
"id": "A0A074D10355223CBE2520A42F93A52E3CC8B4D692570EB4841084F9BBB39F7A",
|
||||
"specification": {
|
||||
"source": {
|
||||
"address": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX",
|
||||
"maxAmount": {
|
||||
"currency": "USD",
|
||||
"value": "10",
|
||||
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "10",
|
||||
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B"
|
||||
}
|
||||
},
|
||||
"allowPartialPayment": true
|
||||
},
|
||||
"outcome": {
|
||||
"result": "tesSUCCESS",
|
||||
"fee": "0.01",
|
||||
"balanceChanges": {
|
||||
"rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX": [
|
||||
{
|
||||
"currency": "XRP",
|
||||
"value": "-0.01"
|
||||
},
|
||||
{
|
||||
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"currency": "USD",
|
||||
"value": "-10"
|
||||
}
|
||||
],
|
||||
"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B": [
|
||||
{
|
||||
"counterparty": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4",
|
||||
"currency": "USD",
|
||||
"value": "-9.980039920159681"
|
||||
},
|
||||
{
|
||||
"counterparty": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX",
|
||||
"currency": "USD",
|
||||
"value": "10"
|
||||
}
|
||||
],
|
||||
"rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4": [
|
||||
{
|
||||
"counterparty": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"currency": "USD",
|
||||
"value": "9.980039920159681"
|
||||
}
|
||||
]
|
||||
},
|
||||
"orderbookChanges": {},
|
||||
"ledgerVersion": 12345,
|
||||
"indexInLedger": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"rawTransactions": "[{\"Account\":\"rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX\",\"Amount\":{\"currency\":\"USD\",\"issuer\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\",\"value\":\"10\"},\"Destination\":\"rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4\",\"Fee\":\"10000\",\"Flags\":131072,\"Sequence\":23295,\"SigningPubKey\":\"02B205F4B92351AC0EEB04254B636F4C49EF922CFA3CAAD03C6477DA1E04E94B53\",\"TransactionType\":\"Payment\",\"TxnSignature\":\"3045022100FAF247A836D601DE74A515B2AADE31186D8B0DA9C23DE489E09753F5CF4BB81F0220477C5B5BC3AC89F2347744F9E00CCA62267E198489D747578162C4C7D156211D\",\"hash\":\"A0A074D10355223CBE2520A42F93A52E3CC8B4D692570EB4841084F9BBB39F7A\",\"metaData\":{\"AffectedNodes\":[{\"ModifiedNode\":{\"FinalFields\":{\"Account\":\"rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX\",\"Balance\":\"1930599790\",\"Flags\":0,\"OwnerCount\":2,\"Sequence\":23296},\"LedgerEntryType\":\"AccountRoot\",\"LedgerIndex\":\"267C16D24EC42EEF8B03D5BE4E94266B1675FA54AFCE42DE795E02AB61031CBD\",\"PreviousFields\":{\"Balance\":\"1930609790\",\"Sequence\":23295},\"PreviousTxnID\":\"0F5396388E91D37BB26C8E24073A57E7C5D51E79AEE4CD855653B8499AE4E3DD\",\"PreviousTxnLgrSeq\":22419806}},{\"ModifiedNode\":{\"FinalFields\":{\"Balance\":{\"currency\":\"USD\",\"issuer\":\"rrrrrrrrrrrrrrrrrrrrBZbvji\",\"value\":\"-9.980959751659681\"},\"Flags\":2228224,\"HighLimit\":{\"currency\":\"USD\",\"issuer\":\"rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4\",\"value\":\"1000000\"},\"HighNode\":\"0000000000000000\",\"LowLimit\":{\"currency\":\"USD\",\"issuer\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\",\"value\":\"0\"},\"LowNode\":\"0000000000000423\"},\"LedgerEntryType\":\"RippleState\",\"LedgerIndex\":\"C66957AF25229357F9C2D2BA17CE47D88169788EDA7610AD0F29AD5BCB225EE5\",\"PreviousFields\":{\"Balance\":{\"currency\":\"USD\",\"issuer\":\"rrrrrrrrrrrrrrrrrrrrBZbvji\",\"value\":\"-0.0009198315\"}},\"PreviousTxnID\":\"2A01E994D7000000B43DD63825A081B4440A44AB2F6FA0D506158AC9CA6B2869\",\"PreviousTxnLgrSeq\":22420532}},{\"ModifiedNode\":{\"FinalFields\":{\"Balance\":{\"currency\":\"USD\",\"issuer\":\"rrrrrrrrrrrrrrrrrrrrBZbvji\",\"value\":\"-276666.975959\"},\"Flags\":131072,\"HighLimit\":{\"currency\":\"USD\",\"issuer\":\"rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX\",\"value\":\"1000000\"},\"HighNode\":\"0000000000000000\",\"LowLimit\":{\"currency\":\"USD\",\"issuer\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\",\"value\":\"0\"},\"LowNode\":\"00000000000002D7\"},\"LedgerEntryType\":\"RippleState\",\"LedgerIndex\":\"FFD710AE2074A98D920D00CC352F25744899F069A6C1B9E31DD32D2C6606E615\",\"PreviousFields\":{\"Balance\":{\"currency\":\"USD\",\"issuer\":\"rrrrrrrrrrrrrrrrrrrrBZbvji\",\"value\":\"-276676.975959\"}},\"PreviousTxnID\":\"BB9DFC87E9D4ED09CA2726DDFE83A4A396ED0D6545536322DE17CDACF45C0D5B\",\"PreviousTxnLgrSeq\":22419307}}],\"TransactionIndex\":1,\"TransactionResult\":\"tesSUCCESS\"}}]"
|
||||
}
|
||||
354
test/fixtures/responses/get-ledger-with-partial-payment.json
vendored
Normal file
354
test/fixtures/responses/get-ledger-with-partial-payment.json
vendored
Normal file
File diff suppressed because one or more lines are too long
17
test/fixtures/responses/get-transaction-amendment.json
vendored
Normal file
17
test/fixtures/responses/get-transaction-amendment.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"type": "amendment",
|
||||
"address": "rrrrrrrrrrrrrrrrrrrrrhoLvTp",
|
||||
"sequence": 0,
|
||||
"id": "A971B83ABED51D83749B73F3C1AAA627CD965AFF74BE8CD98299512D6FB0658F",
|
||||
"specification": {
|
||||
"amendment": "42426C4D4F1009EE67080A9B7965B44656D7714D104A72F9B4369F97ABF044EE"
|
||||
},
|
||||
"outcome": {
|
||||
"result": "tesSUCCESS",
|
||||
"timestamp": "2016-05-05T16:33:30.000Z",
|
||||
"fee": "0",
|
||||
"balanceChanges": {},
|
||||
"orderbookChanges": {},
|
||||
"indexInLedger": 0
|
||||
}
|
||||
}
|
||||
21
test/fixtures/responses/get-transaction-fee-update.json
vendored
Normal file
21
test/fixtures/responses/get-transaction-fee-update.json
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"type": "feeUpdate",
|
||||
"address": "rrrrrrrrrrrrrrrrrrrrrhoLvTp",
|
||||
"sequence": 0,
|
||||
"id": "C6A40F56127436DCD830B1B35FF939FD05B5747D30D6542572B7A835239817AF",
|
||||
"specification": {
|
||||
"baseFeeXRP": "0.00001",
|
||||
"referenceFeeUnits": 10,
|
||||
"reserveBaseXRP": "50",
|
||||
"reserveIncrementXRP": "12.5"
|
||||
},
|
||||
"outcome": {
|
||||
"result": "tesSUCCESS",
|
||||
"timestamp": "2014-08-08T16:57:50.000Z",
|
||||
"fee": "0",
|
||||
"balanceChanges": {},
|
||||
"orderbookChanges": {},
|
||||
"ledgerVersion": 3717633,
|
||||
"indexInLedger": 3
|
||||
}
|
||||
}
|
||||
10
test/fixtures/responses/index.js
vendored
10
test/fixtures/responses/index.js
vendored
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
|
||||
module.exports = {
|
||||
generateAddress: require('./generate-address.json'),
|
||||
@@ -43,7 +43,9 @@ module.exports = {
|
||||
suspendedPaymentExecution:
|
||||
require('./get-transaction-suspended-payment-execution.json'),
|
||||
suspendedPaymentExecutionSimple:
|
||||
require('./get-transaction-suspended-payment-execution-simple.json')
|
||||
require('./get-transaction-suspended-payment-execution-simple.json'),
|
||||
amendment: require('./get-transaction-amendment.json'),
|
||||
feeUpdate: require('./get-transaction-fee-update.json')
|
||||
},
|
||||
getTransactions: {
|
||||
normal: require('./get-transactions.json'),
|
||||
@@ -57,7 +59,9 @@ module.exports = {
|
||||
header: require('./get-ledger'),
|
||||
full: require('./get-ledger-full'),
|
||||
withSettingsTx: require('./get-ledger-with-settings-tx'),
|
||||
withStateAsHashes: require('./get-ledger-with-state-as-hashes')
|
||||
withStateAsHashes: require('./get-ledger-with-state-as-hashes'),
|
||||
withPartial: require('./get-ledger-with-partial-payment'),
|
||||
pre2014withPartial: require('./get-ledger-pre2014-with-partial')
|
||||
},
|
||||
prepareOrder: {
|
||||
buy: require('./prepare-order.json'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"txJSON": "{\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\",\"value\":\"10.1\"},\"TakerPays\":\"2000000\",\"Flags\":2148139008,\"Memos\":[{\"Memo\":{\"MemoData\":\"7465787465642064617461\",\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
||||
"txJSON": "{\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\",\"value\":\"10.1\"},\"TakerPays\":\"2000000\",\"Flags\":2148139008,\"Memos\":[{\"Memo\":{\"MemoData\":\"7465787465642064617461\",\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23,\"OfferSequence\":12345}",
|
||||
"instructions": {
|
||||
"fee": "0.000012",
|
||||
"sequence": 23,
|
||||
|
||||
4
test/fixtures/responses/sign-suspended.json
vendored
4
test/fixtures/responses/sign-suspended.json
vendored
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"signedTransaction": "12000222800000002400000001201900000002201B000000665015712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D68400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020744630440220690785C69BCF4CDA2DD5E82D2F9A9C7BDCBAB68A1E927DC03C0C32900938AC1702202A25E42E9920DB2F46E378F6F6598828D441E99F7D828010AD91674A5E7C0AED70112074686973206D757374206861766520333220636861726163746572732E2E2E2E8114B5F762798A53D543A014CAF8B297CFF8F2F937E88214B5F762798A53D543A014CAF8B297CFF8F2F937E8021001",
|
||||
"id": "B0ADFCC7925588AB2C224A7693222B3397158EA9206FBD614763FB6D00C1F6C1"
|
||||
"signedTransaction": "12000222800000002400000001201900000002201B000000665015712C36933822AD3A3D136C5DF97AA863B69F9CE88B2D6CE6BDD11BFDE290C19D68400000000000000C73210330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD02074473045022100E1DAAD071DC0E556FEC472E0C6CABBAB064901D97FEAD61CA9990DCC17326FFC022051DF3331D340B3E635024D7B9684602EFDEE2D178AAC9E4824722B4DAB6036718114B5F762798A53D543A014CAF8B297CFF8F2F937E88214B5F762798A53D543A014CAF8B297CFF8F2F937E8021001",
|
||||
"id": "D40E312D9CD2EE8C5940D1D4918E9F5778870CB2FBF1D188061EB21B2E7CF371"
|
||||
}
|
||||
|
||||
3
test/fixtures/rippled/account-offers.js
vendored
3
test/fixtures/rippled/account-offers.js
vendored
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict'; // eslint-disable-line strict
|
||||
const _ = require('lodash');
|
||||
const addresses = require('../addresses');
|
||||
|
||||
@@ -160,6 +160,7 @@ module.exports = function(request, options = {}) {
|
||||
{
|
||||
'flags': 0,
|
||||
'seq': 814018,
|
||||
'quality': '16922629533.64839',
|
||||
'taker_gets': {
|
||||
'currency': 'NZD',
|
||||
'issuer': 'rsP3mgGb2tcYUrxiLFiHJiQXhsziegtwBc',
|
||||
|
||||
7
test/fixtures/rippled/empty.json
vendored
Normal file
7
test/fixtures/rippled/empty.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"id": 0,
|
||||
"status": "success",
|
||||
"type": "response",
|
||||
"result": {
|
||||
}
|
||||
}
|
||||
13
test/fixtures/rippled/index.js
vendored
13
test/fixtures/rippled/index.js
vendored
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
'use strict'; // eslint-disable-line
|
||||
|
||||
module.exports = {
|
||||
submit: {
|
||||
@@ -10,8 +10,11 @@ module.exports = {
|
||||
notFound: require('./ledger-not-found'),
|
||||
withoutCloseTime: require('./ledger-without-close-time'),
|
||||
withSettingsTx: require('./ledger-with-settings-tx'),
|
||||
withStateAsHashes: require('./ledger-with-state-as-hashes')
|
||||
withStateAsHashes: require('./ledger-with-state-as-hashes'),
|
||||
withPartialPayment: require('./ledger-with-partial-payment'),
|
||||
pre2014withPartial: require('./ledger-pre2014-with-partial')
|
||||
},
|
||||
empty: require('./empty'),
|
||||
subscribe: require('./subscribe'),
|
||||
unsubscribe: require('./unsubscribe'),
|
||||
account_info: {
|
||||
@@ -31,6 +34,8 @@ module.exports = {
|
||||
},
|
||||
server_info: {
|
||||
normal: require('./server-info'),
|
||||
noValidated: require('./server-info-no-validated'),
|
||||
syncing: require('./server-info-syncing'),
|
||||
error: require('./server-info-error')
|
||||
},
|
||||
path_find: {
|
||||
@@ -69,6 +74,8 @@ module.exports = {
|
||||
require('./tx/suspended-payment-execution-simple.json'),
|
||||
Unrecognized: require('./tx/unrecognized.json'),
|
||||
NoMeta: require('./tx/no-meta.json'),
|
||||
LedgerZero: require('./tx/ledger-zero.json')
|
||||
LedgerZero: require('./tx/ledger-zero.json'),
|
||||
Amendment: require('./tx/amendment.json'),
|
||||
SetFee: require('./tx/set-fee.json')
|
||||
}
|
||||
};
|
||||
|
||||
141
test/fixtures/rippled/ledger-pre2014-with-partial.json
vendored
Normal file
141
test/fixtures/rippled/ledger-pre2014-with-partial.json
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"id": 1,
|
||||
"status": "success",
|
||||
"type": "response",
|
||||
"result": {
|
||||
"ledger": {
|
||||
"accepted": true,
|
||||
"account_hash": "334EE5F2209538C3099E133D25725E5BFEB40A198EA7028E6317F13E95D533DF",
|
||||
"close_flags": 0,
|
||||
"close_time": 521225631,
|
||||
"close_time_human": "2016-Jul-07 16:53:51",
|
||||
"close_time_resolution": 10,
|
||||
"closed": true,
|
||||
"hash": "4F6C0495378FF68A15749C0D51D097EB638DA70319FDAC7A97A27CE63E0BFFED",
|
||||
"ledger_hash": "4F6C0495378FF68A15749C0D51D097EB638DA70319FDAC7A97A27CE63E0BFFED",
|
||||
"ledger_index": "12345",
|
||||
"parent_close_time": 521225630,
|
||||
"parent_hash": "4F636662B714CD9CCE965E9C23BB2E1058A2DF496F5A2416299317AE03F1CD35",
|
||||
"seqNum": "22420574",
|
||||
"totalCoins": "99997302532397566",
|
||||
"total_coins": "99997302532397566",
|
||||
"transaction_hash": "C72A2BDCB471F3AEEB917ABC6019407CAE6DA4B858903A8AB2335A0EB077125D",
|
||||
"transactions": [
|
||||
{
|
||||
"Account": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX",
|
||||
"Amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"value": "10"
|
||||
},
|
||||
"Destination": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4",
|
||||
"Fee": "10000",
|
||||
"Flags": 131072,
|
||||
"Sequence": 23295,
|
||||
"SigningPubKey": "02B205F4B92351AC0EEB04254B636F4C49EF922CFA3CAAD03C6477DA1E04E94B53",
|
||||
"TransactionType": "Payment",
|
||||
"TxnSignature": "3045022100FAF247A836D601DE74A515B2AADE31186D8B0DA9C23DE489E09753F5CF4BB81F0220477C5B5BC3AC89F2347744F9E00CCA62267E198489D747578162C4C7D156211D",
|
||||
"hash": "A0A074D10355223CBE2520A42F93A52E3CC8B4D692570EB4841084F9BBB39F7A",
|
||||
"metaData": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Account": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX",
|
||||
"Balance": "1930599790",
|
||||
"Flags": 0,
|
||||
"OwnerCount": 2,
|
||||
"Sequence": 23296
|
||||
},
|
||||
"LedgerEntryType": "AccountRoot",
|
||||
"LedgerIndex": "267C16D24EC42EEF8B03D5BE4E94266B1675FA54AFCE42DE795E02AB61031CBD",
|
||||
"PreviousFields": {
|
||||
"Balance": "1930609790",
|
||||
"Sequence": 23295
|
||||
},
|
||||
"PreviousTxnID": "0F5396388E91D37BB26C8E24073A57E7C5D51E79AEE4CD855653B8499AE4E3DD",
|
||||
"PreviousTxnLgrSeq": 22419806
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "-9.980959751659681"
|
||||
},
|
||||
"Flags": 2228224,
|
||||
"HighLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4",
|
||||
"value": "1000000"
|
||||
},
|
||||
"HighNode": "0000000000000000",
|
||||
"LowLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"value": "0"
|
||||
},
|
||||
"LowNode": "0000000000000423"
|
||||
},
|
||||
"LedgerEntryType": "RippleState",
|
||||
"LedgerIndex": "C66957AF25229357F9C2D2BA17CE47D88169788EDA7610AD0F29AD5BCB225EE5",
|
||||
"PreviousFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "-0.0009198315"
|
||||
}
|
||||
},
|
||||
"PreviousTxnID": "2A01E994D7000000B43DD63825A081B4440A44AB2F6FA0D506158AC9CA6B2869",
|
||||
"PreviousTxnLgrSeq": 22420532
|
||||
}
|
||||
},
|
||||
{
|
||||
"ModifiedNode": {
|
||||
"FinalFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "-276666.975959"
|
||||
},
|
||||
"Flags": 131072,
|
||||
"HighLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX",
|
||||
"value": "1000000"
|
||||
},
|
||||
"HighNode": "0000000000000000",
|
||||
"LowLimit": {
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"value": "0"
|
||||
},
|
||||
"LowNode": "00000000000002D7"
|
||||
},
|
||||
"LedgerEntryType": "RippleState",
|
||||
"LedgerIndex": "FFD710AE2074A98D920D00CC352F25744899F069A6C1B9E31DD32D2C6606E615",
|
||||
"PreviousFields": {
|
||||
"Balance": {
|
||||
"currency": "USD",
|
||||
"issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji",
|
||||
"value": "-276676.975959"
|
||||
}
|
||||
},
|
||||
"PreviousTxnID": "BB9DFC87E9D4ED09CA2726DDFE83A4A396ED0D6545536322DE17CDACF45C0D5B",
|
||||
"PreviousTxnLgrSeq": 22419307
|
||||
}
|
||||
}
|
||||
],
|
||||
"TransactionIndex": 1,
|
||||
"TransactionResult": "tesSUCCESS"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"ledger_hash": "4F6C0495378FF68A15749C0D51D097EB638DA70319FDAC7A97A27CE63E0BFFED",
|
||||
"ledger_index": 12345,
|
||||
"validated": true
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user