mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-11 00:05:49 +00:00
Compare commits
128 Commits
0.13.0-rc6
...
0.13.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed40eec711 | ||
|
|
43488c55f1 | ||
|
|
2f727b553c | ||
|
|
29c37aa6da | ||
|
|
64baef431d | ||
|
|
634fe5683a | ||
|
|
225ca3f852 | ||
|
|
ff2ac6c3cd | ||
|
|
607777f2a3 | ||
|
|
78eeb40322 | ||
|
|
772f79ae21 | ||
|
|
806a4e823f | ||
|
|
323e402e0c | ||
|
|
9ebb59580d | ||
|
|
bafab6eb18 | ||
|
|
35acbb62c3 | ||
|
|
4676ade4ee | ||
|
|
8edc3b1f36 | ||
|
|
8acfb1a537 | ||
|
|
ac78171099 | ||
|
|
d573c5746b | ||
|
|
51e8f9a87a | ||
|
|
bfe590d96d | ||
|
|
60e2d10775 | ||
|
|
2f432cef62 | ||
|
|
5217b66396 | ||
|
|
b8bb191d24 | ||
|
|
8070a52dc7 | ||
|
|
3205f3cf8c | ||
|
|
e0cdd610dd | ||
|
|
ed3b04ed6f | ||
|
|
62a2d2ae39 | ||
|
|
e2c853e40d | ||
|
|
b9b5a71869 | ||
|
|
87fdbc932f | ||
|
|
84838b2e9f | ||
|
|
c2ca37a790 | ||
|
|
c6805b9f0d | ||
|
|
b1dbdc03dd | ||
|
|
88a3f3d43b | ||
|
|
5f8dcd71a5 | ||
|
|
45db95da79 | ||
|
|
c79b12b27f | ||
|
|
135da6108d | ||
|
|
0d6dda579f | ||
|
|
e641a347db | ||
|
|
3e17d91edf | ||
|
|
715c648d52 | ||
|
|
d0ebed9822 | ||
|
|
a3775f18ba | ||
|
|
7b5d6e9fc5 | ||
|
|
368ac0b9e0 | ||
|
|
0448696bd8 | ||
|
|
deb75ed0d7 | ||
|
|
fcc9bacb4e | ||
|
|
9a5e8fd2ba | ||
|
|
1c023c4377 | ||
|
|
c213b98329 | ||
|
|
27d2e6e519 | ||
|
|
7ee368965c | ||
|
|
d8b5b825b3 | ||
|
|
de67570230 | ||
|
|
60c604fbe6 | ||
|
|
2f6d25ed01 | ||
|
|
b134081293 | ||
|
|
a0528d7f9c | ||
|
|
348335ddf0 | ||
|
|
01752e5486 | ||
|
|
3e758e1b86 | ||
|
|
fb0b30a9a7 | ||
|
|
0c9aea454e | ||
|
|
f282585c3f | ||
|
|
ae5ff31c96 | ||
|
|
20fa8bc953 | ||
|
|
778f59b4fd | ||
|
|
49623cb4dd | ||
|
|
90b53002aa | ||
|
|
93c12af305 | ||
|
|
60f2419b5c | ||
|
|
80494ad813 | ||
|
|
b43c4a7ad4 | ||
|
|
3c608de5bb | ||
|
|
fe5bc1d215 | ||
|
|
580bf9a755 | ||
|
|
c7df5df163 | ||
|
|
a08c52af55 | ||
|
|
e11db0f0f3 | ||
|
|
c6e0582729 | ||
|
|
6e98629f9b | ||
|
|
2243760442 | ||
|
|
91dd6877aa | ||
|
|
e73bcd8fc1 | ||
|
|
7e886b3260 | ||
|
|
5c9451d3ed | ||
|
|
c6c2dcc6c0 | ||
|
|
0bdd37090e | ||
|
|
c745faaaf0 | ||
|
|
9ad03ca873 | ||
|
|
138914384e | ||
|
|
77068667e4 | ||
|
|
c57cef4a21 | ||
|
|
50acc4c708 | ||
|
|
b5f8ba4817 | ||
|
|
a53249ccd7 | ||
|
|
0c62fa2112 | ||
|
|
806547dd15 | ||
|
|
fb1669b2b3 | ||
|
|
0cda15f2b5 | ||
|
|
b88e9370c6 | ||
|
|
e343f3beb8 | ||
|
|
a13bfae714 | ||
|
|
877c6bbb2a | ||
|
|
30d5134394 | ||
|
|
fae5c74487 | ||
|
|
255332ea2e | ||
|
|
15c0e6db19 | ||
|
|
2b600a1e4e | ||
|
|
297fb2483d | ||
|
|
5049822415 | ||
|
|
e3787e0f4f | ||
|
|
683199044b | ||
|
|
4f3c3e9f66 | ||
|
|
fc0240c06b | ||
|
|
6bfa284bac | ||
|
|
dfee9bc578 | ||
|
|
0838a0e865 | ||
|
|
5f61d80e2d | ||
|
|
c4fa4c237c |
@@ -26,6 +26,9 @@ function webpackConfig(extension, overrides) {
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader?optional=runtime'
|
||||
}, {
|
||||
test: /\.json/,
|
||||
loader: 'json-loader'
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
19
HISTORY.md
19
HISTORY.md
@@ -1,14 +1,29 @@
|
||||
##0.13.0 (release candidate)
|
||||
|
||||
+ [Fix: Emit error events and return error on pathfind](https://github.com/ripple/ripple-lib/commit/1ccbaf677631a1944eb05d90f7afc5f3690a03dd)
|
||||
+ [Deprecate core and remove snake case method copying](https://github.com/ripple/ripple-lib/commit/fb8dc44ec1d49bb05cd0cdbe6dd4ab211195868a)
|
||||
**Breaking Changes**
|
||||
+ [Removed timeout method of Request and added default timeout](https://github.com/ripple/ripple-lib/commit/634fe5683a9082e57682ff7d5c4fb9483b4af818)
|
||||
+ Add new RippleAPI interface
|
||||
- [RippleAPI README and samples](https://github.com/ripple/ripple-lib/tree/develop/docs/samples)
|
||||
- [Method documentation](https://rawgit.com/ripple/ripple-lib/develop/docs/api.html)
|
||||
|
||||
**Changes**
|
||||
+ [Implement Balance Sheet API](https://github.com/ripple/ripple-lib/pull/579)
|
||||
+ [Fix crash due to rippled slowDown error](https://github.com/ripple/ripple-lib/commit/84838b2e9f6969b593b8462a62a6b8f516ada937)
|
||||
+ [Fix: Emit error events and return error on pathfind](https://github.com/ripple/ripple-lib/commit/1ccbaf677631a1944eb05d90f7afc5f3690a03dd)
|
||||
+ [Deprecate core and remove snake case method copying](https://github.com/ripple/ripple-lib/commit/fb8dc44ec1d49bb05cd0cdbe6dd4ab211195868a)
|
||||
|
||||
+ [Fix RangeSet for validated_ledger as single ledger](https://github.com/ripple/ripple-lib/commit/9f9e76f8b933201651af59307135f67cfa7d60e8)
|
||||
+ [Fix bug where the paths would be set with an empty array](https://github.com/ripple/ripple-lib/commit/83874ec0962da311b76f2385623e51c68bc39035)
|
||||
+ [Fix reserve calculation](https://github.com/ripple/ripple-lib/commit/52879febb92d876f01f2e4d70871baa07af631fb)
|
||||
|
||||
##0.12.7 and 0.12.8
|
||||
|
||||
+ [Improve performance of orderbook](https://github.com/ripple/ripple-lib/commit/c745faaaf0956ca98448a754b4fe97fb50574fc7)
|
||||
|
||||
+ [Remove Firefox warning about prototype overwrite](https://github.com/ripple/ripple-lib/commit/0c62fa21123b220b066871e1c41a3b4fe6f51885)
|
||||
|
||||
+ [Fix compare bug in Amount class](https://github.com/ripple/ripple-lib/commit/806547dd154e1b0bf252e8a74ad3ac6aa8a97660)
|
||||
|
||||
##0.12.6
|
||||
|
||||
+ [Fix webpack require failure due to "./" notation](https://github.com/ripple/ripple-lib/commit/8d9746d7b10be203ee613df523c2522012ff1baf)
|
||||
|
||||
10
bin/ci.sh
10
bin/ci.sh
@@ -24,9 +24,13 @@ unittest() {
|
||||
npm run coveralls
|
||||
|
||||
# test compiled version in "dist/npm"
|
||||
ln -nfs ../../dist/npm/core test/node_modules/ripple-lib
|
||||
ln -nfs ../../dist/npm test/node_modules/ripple-api
|
||||
npm test
|
||||
babel -D --optional runtime --ignore "**/node_modules/**" -d test-compiled/ test/
|
||||
echo "--reporter spec --timeout 5000 --slow 500" > test-compiled/mocha.opts
|
||||
mkdir -p test-compiled/node_modules
|
||||
ln -nfs ../../dist/npm/core test-compiled/node_modules/ripple-lib
|
||||
ln -nfs ../../dist/npm test-compiled/node_modules/ripple-api
|
||||
mocha --opts test-compiled/mocha.opts test-compiled
|
||||
rm -rf test-compiled
|
||||
}
|
||||
|
||||
oneNode() {
|
||||
|
||||
72
npm-shrinkwrap.json
generated
72
npm-shrinkwrap.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.13.0-rc6",
|
||||
"version": "0.13.0-rc12",
|
||||
"npm-shrinkwrap-version": "5.4.0",
|
||||
"node-version": "v0.12.7",
|
||||
"dependencies": {
|
||||
@@ -9,12 +9,12 @@
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "5.8.20",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.20.tgz",
|
||||
"version": "5.8.25",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.25.tgz",
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.0.1.tgz"
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.1.4.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -23,8 +23,8 @@
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
|
||||
},
|
||||
"bn.js": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.1.tgz"
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.1.2.tgz"
|
||||
},
|
||||
"es6-promisify": {
|
||||
"version": "2.0.0",
|
||||
@@ -55,12 +55,12 @@
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
|
||||
"dependencies": {
|
||||
"agent-base": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.0.tgz",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.1.tgz",
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "4.3.6",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -81,8 +81,8 @@
|
||||
}
|
||||
},
|
||||
"is-my-json-valid": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.1.tgz",
|
||||
"version": "2.12.2",
|
||||
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.2.tgz",
|
||||
"dependencies": {
|
||||
"generate-function": {
|
||||
"version": "2.0.0",
|
||||
@@ -99,8 +99,8 @@
|
||||
}
|
||||
},
|
||||
"jsonpointer": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-1.1.0.tgz"
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-2.0.0.tgz"
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.0",
|
||||
@@ -117,12 +117,12 @@
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
|
||||
},
|
||||
"ripple-address-codec": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-1.6.0.tgz",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz",
|
||||
"dependencies": {
|
||||
"x-address-codec": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.6.0.tgz",
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.0.tgz",
|
||||
"dependencies": {
|
||||
"base-x": {
|
||||
"version": "1.0.1",
|
||||
@@ -133,8 +133,8 @@
|
||||
}
|
||||
},
|
||||
"ripple-keypairs": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.8.0.tgz",
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.9.0.tgz",
|
||||
"dependencies": {
|
||||
"brorand": {
|
||||
"version": "1.0.5",
|
||||
@@ -149,18 +149,32 @@
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-address-codec": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-1.6.0.tgz",
|
||||
"dependencies": {
|
||||
"x-address-codec": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.6.0.tgz",
|
||||
"dependencies": {
|
||||
"base-x": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-lib-transactionparser": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.0.tgz",
|
||||
"dependencies": {
|
||||
"bignumber.js": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-1.4.1.tgz"
|
||||
}
|
||||
}
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.1.tgz"
|
||||
},
|
||||
"ripple-lib-value": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-value/-/ripple-lib-value-0.1.0.tgz"
|
||||
},
|
||||
"sjcl-codec": {
|
||||
"version": "0.1.0",
|
||||
|
||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.13.0-rc6",
|
||||
"version": "0.13.0-rc12",
|
||||
"license": "ISC",
|
||||
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
||||
"files": [
|
||||
@@ -23,12 +23,13 @@
|
||||
"extend": "~1.2.1",
|
||||
"hash.js": "^1.0.3",
|
||||
"https-proxy-agent": "^1.0.0",
|
||||
"is-my-json-valid": "^2.12.0",
|
||||
"is-my-json-valid": "^2.12.2",
|
||||
"lodash": "^3.1.0",
|
||||
"lru-cache": "~2.5.0",
|
||||
"ripple-address-codec": "^1.6.0",
|
||||
"ripple-keypairs": "^0.8.0",
|
||||
"ripple-lib-transactionparser": "^0.5.0",
|
||||
"ripple-address-codec": "^2.0.1",
|
||||
"ripple-keypairs": "^0.9.0",
|
||||
"ripple-lib-transactionparser": "^0.5.1",
|
||||
"ripple-lib-value": "0.1.0",
|
||||
"sjcl-codec": "0.1.0",
|
||||
"ws": "~0.7.1"
|
||||
},
|
||||
@@ -39,7 +40,7 @@
|
||||
"babel-eslint": "^4.0.5",
|
||||
"babel-loader": "^5.3.2",
|
||||
"coveralls": "~2.10.0",
|
||||
"eslint": "^1.2.0",
|
||||
"eslint": "^1.3.0",
|
||||
"eslint-plugin-flowtype": "^1.0.0",
|
||||
"eventemitter2": "^0.4.14",
|
||||
"flow-bin": "^0.14",
|
||||
@@ -48,6 +49,7 @@
|
||||
"gulp-rename": "~1.2.0",
|
||||
"gulp-uglify": "~1.1.0",
|
||||
"istanbul": "~0.3.5",
|
||||
"json-loader": "^0.5.2",
|
||||
"mocha": "~2.1.0",
|
||||
"webpack": "~1.5.3",
|
||||
"yargs": "~1.3.1"
|
||||
|
||||
@@ -58,6 +58,13 @@ function MissingLedgerHistoryError(message) {
|
||||
MissingLedgerHistoryError.prototype = new RippleError();
|
||||
MissingLedgerHistoryError.prototype.name = 'MissingLedgerHistoryError';
|
||||
|
||||
function PendingLedgerVersionError(message) {
|
||||
this.message = message ||
|
||||
'maxLedgerVersion is greater than server\'s most recent validated ledger';
|
||||
}
|
||||
PendingLedgerVersionError.prototype = new RippleError();
|
||||
PendingLedgerVersionError.prototype.name = 'PendingLedgerVersionError';
|
||||
|
||||
/**
|
||||
* Request timed out
|
||||
*/
|
||||
@@ -82,6 +89,7 @@ module.exports = {
|
||||
TransactionError,
|
||||
RippledNetworkError,
|
||||
NotFoundError,
|
||||
PendingLedgerVersionError,
|
||||
MissingLedgerHistoryError,
|
||||
TimeOutError,
|
||||
ApiError,
|
||||
|
||||
@@ -16,6 +16,5 @@ module.exports = {
|
||||
convertExceptions: utils.convertExceptions,
|
||||
convertKeysFromSnakeCaseToCamelCase:
|
||||
utils.convertKeysFromSnakeCaseToCamelCase,
|
||||
promisify: utils.promisify,
|
||||
isValidAddress: require('./schema-validator').isValidAddress
|
||||
promisify: utils.promisify
|
||||
};
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
/* @flow */
|
||||
// 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 fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
const validator = require('is-my-json-valid');
|
||||
const core = require('./utils').core;
|
||||
@@ -21,21 +19,73 @@ function isValidLedgerHash(ledgerHash) {
|
||||
return core.UInt256.is_valid(ledgerHash);
|
||||
}
|
||||
|
||||
function loadSchema(filepath: string): {} {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filepath, 'utf8'));
|
||||
} catch (e) {
|
||||
throw new Error('Failed to parse schema: ' + filepath);
|
||||
}
|
||||
}
|
||||
|
||||
function endsWith(str, suffix) {
|
||||
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
||||
}
|
||||
|
||||
function loadSchemas(dir) {
|
||||
const filenames = fs.readdirSync(dir).filter(name => endsWith(name, '.json'));
|
||||
const schemas = filenames.map(name => loadSchema(path.join(dir, name)));
|
||||
function loadSchemas() {
|
||||
// listed explicitly for webpack (instead of scanning schemas directory)
|
||||
const schemas = [
|
||||
require('./schemas/address.json'),
|
||||
require('./schemas/adjustment.json'),
|
||||
require('./schemas/amount.json'),
|
||||
require('./schemas/amountbase.json'),
|
||||
require('./schemas/balance.json'),
|
||||
require('./schemas/blob.json'),
|
||||
require('./schemas/currency.json'),
|
||||
require('./schemas/get-account-info.json'),
|
||||
require('./schemas/get-balances.json'),
|
||||
require('./schemas/get-balance-sheet'),
|
||||
require('./schemas/balance-sheet-options.json'),
|
||||
require('./schemas/get-ledger.json'),
|
||||
require('./schemas/get-orderbook.json'),
|
||||
require('./schemas/get-orders.json'),
|
||||
require('./schemas/get-paths.json'),
|
||||
require('./schemas/get-server-info.json'),
|
||||
require('./schemas/get-settings.json'),
|
||||
require('./schemas/get-transaction.json'),
|
||||
require('./schemas/get-transactions.json'),
|
||||
require('./schemas/get-trustlines.json'),
|
||||
require('./schemas/hash128.json'),
|
||||
require('./schemas/hash256.json'),
|
||||
require('./schemas/instructions.json'),
|
||||
require('./schemas/issue.json'),
|
||||
require('./schemas/ledger-options.json'),
|
||||
require('./schemas/ledgerversion.json'),
|
||||
require('./schemas/max-adjustment.json'),
|
||||
require('./schemas/memo.json'),
|
||||
require('./schemas/order-cancellation-transaction.json'),
|
||||
require('./schemas/order-cancellation.json'),
|
||||
require('./schemas/order-change.json'),
|
||||
require('./schemas/order-transaction.json'),
|
||||
require('./schemas/order.json'),
|
||||
require('./schemas/orderbook-orders.json'),
|
||||
require('./schemas/orderbook.json'),
|
||||
require('./schemas/orders-options.json'),
|
||||
require('./schemas/outcome.json'),
|
||||
require('./schemas/pathfind.json'),
|
||||
require('./schemas/payment-transaction.json'),
|
||||
require('./schemas/payment.json'),
|
||||
require('./schemas/quality.json'),
|
||||
require('./schemas/remote-options.json'),
|
||||
require('./schemas/sequence.json'),
|
||||
require('./schemas/settings-options.json'),
|
||||
require('./schemas/settings-transaction.json'),
|
||||
require('./schemas/settings.json'),
|
||||
require('./schemas/sign.json'),
|
||||
require('./schemas/signed-value.json'),
|
||||
require('./schemas/submit.json'),
|
||||
require('./schemas/suspended-payment-cancellation.json'),
|
||||
require('./schemas/suspended-payment-execution.json'),
|
||||
require('./schemas/suspended-payment-creation.json'),
|
||||
require('./schemas/timestamp.json'),
|
||||
require('./schemas/transaction-options.json'),
|
||||
require('./schemas/transactions-options.json'),
|
||||
require('./schemas/trustline-transaction.json'),
|
||||
require('./schemas/trustline.json'),
|
||||
require('./schemas/trustlines-options.json'),
|
||||
require('./schemas/tx.json'),
|
||||
require('./schemas/uint32.json'),
|
||||
require('./schemas/value.json'),
|
||||
require('./schemas/prepare.json'),
|
||||
require('./schemas/ledger-closed.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);
|
||||
@@ -67,10 +117,8 @@ function schemaValidate(schemaName: string, object: any): void {
|
||||
}
|
||||
}
|
||||
|
||||
SCHEMAS = loadSchemas(path.join(__dirname, './schemas'));
|
||||
SCHEMAS = loadSchemas();
|
||||
module.exports = {
|
||||
schemaValidate: schemaValidate,
|
||||
isValidAddress: isValidAddress,
|
||||
loadSchema: loadSchema,
|
||||
SCHEMAS: SCHEMAS
|
||||
};
|
||||
|
||||
@@ -4,20 +4,8 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"amount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tag": {
|
||||
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
"amount": {"$ref": "amount"},
|
||||
"tag": {"$ref": "tag"}
|
||||
},
|
||||
"required": ["address", "amount"],
|
||||
"additionalProperties": false
|
||||
|
||||
15
src/api/common/schemas/balance-sheet-options.json
Normal file
15
src/api/common/schemas/balance-sheet-options.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "balance-sheet-options",
|
||||
"description": "Options for getBalanceSheet",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"excludeAddresses": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "address"},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
9
src/api/common/schemas/destinationAdjustment.json
Normal file
9
src/api/common/schemas/destinationAdjustment.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "destinationAdjustment",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{"$ref": "adjustment"},
|
||||
{"$ref": "minAdjustment"}
|
||||
]
|
||||
}
|
||||
29
src/api/common/schemas/get-balance-sheet.json
Normal file
29
src/api/common/schemas/get-balance-sheet.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getBalanceSheet",
|
||||
"description": "getBalanceSheet response",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"balances": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "amount"}
|
||||
},
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "amount"}
|
||||
},
|
||||
"obligations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"value": {"$ref": "value"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "adjustment"},
|
||||
"destination": {"$ref": "adjustment"},
|
||||
"source": {"$ref": "sourceAdjustment"},
|
||||
"destination": {"$ref": "destinationAdjustment"},
|
||||
"paths": {"type": "string"}
|
||||
},
|
||||
"required": ["source", "destination", "paths"],
|
||||
|
||||
13
src/api/common/schemas/lax-amount.json
Normal file
13
src/api/common/schemas/lax-amount.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "laxAmount",
|
||||
"description": "Amount where counterparty is optional",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
13
src/api/common/schemas/lax-lax-amount.json
Normal file
13
src/api/common/schemas/lax-lax-amount.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "laxLaxAmount",
|
||||
"description": "Amount where counterparty and value are optional",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
21
src/api/common/schemas/ledger-closed.json
Normal file
21
src/api/common/schemas/ledger-closed.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "ledgerClosed",
|
||||
"description": "A ledgerClosed event message",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"feeBase": {"type": "integer", "minimum": 0},
|
||||
"feeReference": {"type": "integer", "minimum": 0},
|
||||
"ledgerHash": {"$ref": "ledgerHash"},
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"},
|
||||
"ledgerTimestamp": {"type": "string", "format": "date-time"},
|
||||
"reserveBase": {"type": "integer", "minimum": 0},
|
||||
"reserveIncrement": {"type": "integer", "minimum": 0},
|
||||
"transactionCount": {"type": "integer", "minimum": 0},
|
||||
"validatedLedgerVersions": {"type": "string"}
|
||||
},
|
||||
"addtionalProperties": false,
|
||||
"required": ["feeBase", "feeReference", "ledgerHash", "ledgerTimestamp",
|
||||
"reserveBase", "reserveIncrement", "transactionCount",
|
||||
"ledgerVersion", "validatedLedgerVersions"]
|
||||
}
|
||||
@@ -4,20 +4,8 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"maxAmount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tag": {
|
||||
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
"maxAmount": {"$ref": "laxAmount"},
|
||||
"tag": {"$ref": "tag"}
|
||||
},
|
||||
"required": ["address", "maxAmount"],
|
||||
"additionalProperties": false
|
||||
|
||||
12
src/api/common/schemas/min-adjustment.json
Normal file
12
src/api/common/schemas/min-adjustment.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "minAdjustment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"minAmount": {"$ref": "laxAmount"},
|
||||
"tag": {"$ref": "tag"}
|
||||
},
|
||||
"required": ["address", "minAmount"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"quantity": {"$ref": "balance"},
|
||||
"totalPrice": {"$ref": "balance"},
|
||||
"makerExchangeRate": {"$ref": "value"},
|
||||
"sequence": {"$ref": "sequence"},
|
||||
"status": {"enum": ["created", "open", "closed", "canceled"]}
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"result": {"type": "string"},
|
||||
"timestamp": {"type": "string"},
|
||||
"timestamp": {"type": "string", "format": "date-time"},
|
||||
"fee": {"$ref": "value"},
|
||||
"balanceChanges": {
|
||||
"type": "object",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"amount": {"$ref": "laxAmount"},
|
||||
"currencies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -19,12 +20,23 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"not": {
|
||||
"required": ["amount", "currencies"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["address"]
|
||||
},
|
||||
"destination": {"$ref": "adjustment"}
|
||||
"destination": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"amount": {"$ref": "laxLaxAmount"}
|
||||
},
|
||||
"required": ["address", "amount"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["source", "destination"],
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"title": "payment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "maxAdjustment"},
|
||||
"destination": {"$ref": "adjustment"},
|
||||
"source": {"$ref": "sourceAdjustment"},
|
||||
"destination": {"$ref": "destinationAdjustment"},
|
||||
"paths": {"type": "string"},
|
||||
"memos": {
|
||||
"type": "array",
|
||||
|
||||
21
src/api/common/schemas/prepare.json
Normal file
21
src/api/common/schemas/prepare.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "prepare",
|
||||
"description": "Result of prepare function",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"txJSON": {"type": "string"},
|
||||
"instructions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fee": {"$ref": "value"},
|
||||
"sequence": {"$ref": "sequence"},
|
||||
"maxLedgerVersion": {"$ref": "ledgerVersion"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["fee", "sequence"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["txJSON", "instructions"]
|
||||
}
|
||||
@@ -4,7 +4,14 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"trace": {"type": "boolean"},
|
||||
"servers": {"type": "array", "items": {"type": "string", "format": "uri"}}
|
||||
"servers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"format": "uri",
|
||||
"pattern": "^wss?://"
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
9
src/api/common/schemas/sourceAdjustment.json
Normal file
9
src/api/common/schemas/sourceAdjustment.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "sourceAdjustment",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{"$ref": "adjustment"},
|
||||
{"$ref": "maxAdjustment"}
|
||||
]
|
||||
}
|
||||
17
src/api/common/schemas/suspended-payment-cancellation.json
Normal file
17
src/api/common/schemas/suspended-payment-cancellation.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "suspended-payment-cancellation",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"memos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "memo"
|
||||
}
|
||||
},
|
||||
"owner": {"$ref": "address"},
|
||||
"paymentSequence": {"$ref": "uint32"}
|
||||
},
|
||||
"required": ["owner", "paymentSequence"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
28
src/api/common/schemas/suspended-payment-creation.json
Normal file
28
src/api/common/schemas/suspended-payment-creation.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "suspended-payment-creation",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "maxAdjustment"},
|
||||
"destination": {"$ref": "adjustment"},
|
||||
"memos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "memo"
|
||||
}
|
||||
},
|
||||
"digest": {"$ref": "hash256"},
|
||||
"allowCancelAfter": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "milliseconds since unix epoch"
|
||||
},
|
||||
"allowExecuteAfter": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "milliseconds since unix epoch"
|
||||
}
|
||||
},
|
||||
"required": ["source", "destination"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
20
src/api/common/schemas/suspended-payment-execution.json
Normal file
20
src/api/common/schemas/suspended-payment-execution.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "suspended-payment-execution",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"memos": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "memo"
|
||||
}
|
||||
},
|
||||
"owner": {"$ref": "address"},
|
||||
"paymentSequence": {"$ref": "uint32"},
|
||||
"method": {"type": "integer", "minimum": 0, "maximum": 255},
|
||||
"digest": {"$ref": "hash256"},
|
||||
"proof": {"type": "string"}
|
||||
},
|
||||
"required": ["owner", "paymentSequence"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
6
src/api/common/schemas/tag.json
Normal file
6
src/api/common/schemas/tag.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "tag",
|
||||
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
@@ -29,8 +29,10 @@ function toRippledAmount(amount: Amount): string|Amount {
|
||||
}
|
||||
|
||||
function generateAddress(options?: Object): Object {
|
||||
const {accountID, seed} = keypairs.generateWallet(options);
|
||||
return {secret: seed, address: accountID};
|
||||
const secret = keypairs.generateSeed(options);
|
||||
const keypair = keypairs.deriveKeypair(secret);
|
||||
const address = keypairs.deriveAddress(keypair.publicKey);
|
||||
return {secret, address};
|
||||
}
|
||||
|
||||
type AsyncFunction = (...x: any) => void
|
||||
@@ -52,7 +54,7 @@ type Wrapper = (data: any) => any
|
||||
function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
|
||||
return function(error, data) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
callback(error, data);
|
||||
return;
|
||||
}
|
||||
let result;
|
||||
@@ -66,10 +68,16 @@ function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
|
||||
};
|
||||
}
|
||||
|
||||
function convertErrors(callback: () => void): () => void {
|
||||
function convertErrors(callback: Callback): () => void {
|
||||
return function(error, data) {
|
||||
if (error && !(error instanceof errors.RippleError)) {
|
||||
callback(new errors.RippleError(error));
|
||||
const message = _.get(error, ['remote', 'error_message'], error.message);
|
||||
const error_ = new errors.RippleError(message);
|
||||
error_.data = data;
|
||||
callback(error_, data);
|
||||
} else if (error) {
|
||||
error.data = data;
|
||||
callback(error, data);
|
||||
} else {
|
||||
callback(error, data);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const core = require('./utils').core;
|
||||
const deriveKeypair = require('ripple-keypairs').deriveKeypair;
|
||||
const ValidationError = require('./errors').ValidationError;
|
||||
const schemaValidate = require('./schema-validator').schemaValidate;
|
||||
|
||||
@@ -9,6 +9,15 @@ function error(text) {
|
||||
return new ValidationError(text);
|
||||
}
|
||||
|
||||
function isValidSecret(secret) {
|
||||
try {
|
||||
deriveKeypair(secret);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function validateAddressAndSecret(obj: {address: string, secret: string}
|
||||
): void {
|
||||
const address = obj.address;
|
||||
@@ -17,8 +26,8 @@ function validateAddressAndSecret(obj: {address: string, secret: string}
|
||||
if (!secret) {
|
||||
throw error('Parameter missing: secret');
|
||||
}
|
||||
if (!core.Seed.from_json(secret).is_valid()) {
|
||||
throw error('secret is invalid');
|
||||
if (!isValidSecret(secret)) {
|
||||
throw error('Invalid parameter: secret');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +35,9 @@ function validateSecret(secret: string): void {
|
||||
if (!secret) {
|
||||
throw error('Parameter missing: secret');
|
||||
}
|
||||
if (typeof secret !== 'string' || secret[0] !== 's') {
|
||||
throw error('Invalid parameter');
|
||||
}
|
||||
|
||||
const seed = new core.Seed().parse_base58(secret);
|
||||
if (!seed.is_valid()) {
|
||||
throw error('invalid seed');
|
||||
if (typeof secret !== 'string' || secret[0] !== 's'
|
||||
|| !isValidSecret(secret)) {
|
||||
throw error('Invalid parameter: secret');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +66,12 @@ module.exports = {
|
||||
order: _.partial(schemaValidate, 'order'),
|
||||
orderbook: _.partial(schemaValidate, 'orderbook'),
|
||||
payment: _.partial(schemaValidate, 'payment'),
|
||||
suspendedPaymentCreation:
|
||||
_.partial(schemaValidate, 'suspended-payment-creation'),
|
||||
suspendedPaymentExecution:
|
||||
_.partial(schemaValidate, 'suspended-payment-execution'),
|
||||
suspendedPaymentCancellation:
|
||||
_.partial(schemaValidate, 'suspended-payment-cancellation'),
|
||||
pathfind: _.partial(schemaValidate, 'pathfind'),
|
||||
settings: _.partial(schemaValidate, 'settings'),
|
||||
trustline: _.partial(schemaValidate, 'trustline'),
|
||||
@@ -71,6 +82,7 @@ module.exports = {
|
||||
getAccountInfoOptions: _.partial(validateOptions, 'settings-options'),
|
||||
getTrustlinesOptions: _.partial(validateOptions, 'trustlines-options'),
|
||||
getBalancesOptions: _.partial(validateOptions, 'trustlines-options'),
|
||||
getBalanceSheetOptions: _.partial(validateOptions, 'balance-sheet-options'),
|
||||
getOrdersOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getOrderbookOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getTransactionOptions: _.partial(validateOptions, 'transaction-options'),
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const util = require('util');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const common = require('./common');
|
||||
const server = require('./server/server');
|
||||
const connect = server.connect;
|
||||
@@ -14,6 +16,7 @@ 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');
|
||||
@@ -23,6 +26,12 @@ 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');
|
||||
const prepareSuspendedPaymentExecution =
|
||||
require('./transaction/suspended-payment-execution');
|
||||
const prepareSuspendedPaymentCancellation =
|
||||
require('./transaction/suspended-payment-cancellation');
|
||||
const prepareSettings = require('./transaction/settings');
|
||||
const sign = require('./transaction/sign');
|
||||
const submit = require('./transaction/submit');
|
||||
@@ -31,15 +40,22 @@ const convertExceptions = require('./common').convertExceptions;
|
||||
const generateAddress = convertExceptions(common.generateAddress);
|
||||
const computeLedgerHash = require('./offline/ledgerhash');
|
||||
const getLedger = require('./ledger/ledger');
|
||||
const isValidAddress = common.isValidAddress;
|
||||
|
||||
function RippleAPI(options: {}) {
|
||||
common.validate.remoteOptions(options);
|
||||
if (EventEmitter instanceof Function) { // always true, needed for flow
|
||||
EventEmitter.call(this);
|
||||
}
|
||||
const _options = _.assign({}, options, {automatic_resubmission: false});
|
||||
this.remote = new common.core.Remote(_options);
|
||||
this.remote.on('ledger_closed', message => {
|
||||
this.emit('ledgerClosed', server.formatLedgerClose(message));
|
||||
});
|
||||
}
|
||||
|
||||
RippleAPI.prototype = {
|
||||
util.inherits(RippleAPI, EventEmitter);
|
||||
|
||||
_.assign(RippleAPI.prototype, {
|
||||
connect,
|
||||
disconnect,
|
||||
isConnected,
|
||||
@@ -51,6 +67,7 @@ RippleAPI.prototype = {
|
||||
getTransactions,
|
||||
getTrustlines,
|
||||
getBalances,
|
||||
getBalanceSheet,
|
||||
getPaths,
|
||||
getOrders,
|
||||
getOrderbook,
|
||||
@@ -62,19 +79,21 @@ RippleAPI.prototype = {
|
||||
prepareTrustline,
|
||||
prepareOrder,
|
||||
prepareOrderCancellation,
|
||||
prepareSuspendedPaymentCreation,
|
||||
prepareSuspendedPaymentExecution,
|
||||
prepareSuspendedPaymentCancellation,
|
||||
prepareSettings,
|
||||
sign,
|
||||
submit,
|
||||
|
||||
computeLedgerHash,
|
||||
isValidAddress,
|
||||
generateAddress,
|
||||
errors
|
||||
};
|
||||
});
|
||||
|
||||
// these are exposed only for use by unit tests; they are not part of the API
|
||||
RippleAPI._PRIVATE = {
|
||||
common: common,
|
||||
common,
|
||||
computeLedgerHash,
|
||||
ledgerUtils: require('./ledger/utils'),
|
||||
schemaValidator: require('./common/schema-validator')
|
||||
};
|
||||
|
||||
68
src/api/ledger/balance-sheet.js
Normal file
68
src/api/ledger/balance-sheet.js
Normal file
@@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const convertErrors = utils.common.convertErrors;
|
||||
|
||||
function formatBalanceSheet(balanceSheet) {
|
||||
const result = {};
|
||||
|
||||
if (!_.isUndefined(balanceSheet.balances)) {
|
||||
result.balances = [];
|
||||
_.forEach(balanceSheet.balances, (balances, counterparty) => {
|
||||
_.forEach(balances, (balance) => {
|
||||
result.balances.push(Object.assign({counterparty}, balance));
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!_.isUndefined(balanceSheet.assets)) {
|
||||
result.assets = [];
|
||||
_.forEach(balanceSheet.assets, (assets, counterparty) => {
|
||||
_.forEach(assets, (balance) => {
|
||||
result.assets.push(Object.assign({counterparty}, balance));
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!_.isUndefined(balanceSheet.obligations)) {
|
||||
result.obligations = _.map(balanceSheet.obligations, (value, currency) =>
|
||||
({currency, value}));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getBalanceSheetAsync(address, options, callback) {
|
||||
validate.address(address);
|
||||
validate.getBalanceSheetOptions(options);
|
||||
|
||||
const requestOptions = Object.assign({}, {
|
||||
account: address,
|
||||
strict: true,
|
||||
hotwallet: options.excludeAddresses,
|
||||
ledger: options.ledgerVersion
|
||||
});
|
||||
|
||||
const requestCallback = composeAsync(
|
||||
formatBalanceSheet, convertErrors(callback));
|
||||
|
||||
this.remote.getLedgerSequence((err, ledgerVersion) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isUndefined(requestOptions.ledger)) {
|
||||
requestOptions.ledger = ledgerVersion;
|
||||
}
|
||||
|
||||
this.remote.requestGatewayBalances(requestOptions, requestCallback);
|
||||
});
|
||||
}
|
||||
|
||||
function getBalanceSheet(address: string, options = {}) {
|
||||
return utils.promisify(getBalanceSheetAsync).call(this, address, options);
|
||||
}
|
||||
|
||||
module.exports = getBalanceSheet;
|
||||
@@ -7,6 +7,9 @@ const getTrustlines = require('./trustlines');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const convertErrors = utils.common.convertErrors;
|
||||
import type {Remote} from '../../core/remote';
|
||||
import type {GetLedgerSequenceCallback} from '../../core/remote';
|
||||
|
||||
|
||||
function getTrustlineBalanceAmount(trustline) {
|
||||
return {
|
||||
@@ -31,14 +34,25 @@ function getTrustlinesAsync(account, options, callback) {
|
||||
.catch(callback);
|
||||
}
|
||||
|
||||
function getLedgerVersionHelper(remote: Remote, optionValue?: number,
|
||||
callback: GetLedgerSequenceCallback
|
||||
) {
|
||||
if (optionValue !== undefined && optionValue !== null) {
|
||||
callback(null, optionValue);
|
||||
} else {
|
||||
remote.getLedgerSequence(callback);
|
||||
}
|
||||
}
|
||||
|
||||
function getBalancesAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getBalancesOptions(options);
|
||||
|
||||
const ledgerVersion = options.ledgerVersion
|
||||
|| this.remote.getLedgerSequence();
|
||||
async.parallel({
|
||||
xrp: _.partial(utils.getXRPBalance, this.remote, account, ledgerVersion),
|
||||
xrp: async.seq(
|
||||
_.partial(getLedgerVersionHelper, this.remote, options.ledgerVersion),
|
||||
_.partial(utils.getXRPBalance, this.remote, account)
|
||||
),
|
||||
trustlines: _.partial(getTrustlinesAsync.bind(this), account, options)
|
||||
}, composeAsync(formatBalances, convertErrors(callback)));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
@@ -26,17 +27,17 @@ function getOrdersAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getOrdersOptions(options);
|
||||
|
||||
const ledgerVersion = options.ledgerVersion
|
||||
|| this.remote.getLedgerSequence();
|
||||
const getter = _.partial(requestAccountOffers, this.remote, account,
|
||||
ledgerVersion);
|
||||
options.ledgerVersion);
|
||||
utils.getRecursive(getter, options.limit,
|
||||
composeAsync((orders) => _.sortBy(orders,
|
||||
(order) => order.properties.sequence), callback));
|
||||
}
|
||||
|
||||
function getOrders(account: string, options = {}) {
|
||||
return utils.promisify(getOrdersAsync).call(this, account, options);
|
||||
return utils.promisify(async.seq(
|
||||
utils.getLedgerOptionsWithLedgerVersion,
|
||||
getOrdersAsync)).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getOrders;
|
||||
|
||||
@@ -8,21 +8,42 @@ function parsePaths(paths) {
|
||||
_.omit(step, ['type', 'type_hex'])));
|
||||
}
|
||||
|
||||
function parsePathfind(sourceAddress: string,
|
||||
destinationAmount: Object, pathfindResult: Object): Object {
|
||||
return pathfindResult.alternatives.map(function(alternative) {
|
||||
return {
|
||||
source: {
|
||||
address: sourceAddress,
|
||||
amount: parseAmount(alternative.source_amount)
|
||||
},
|
||||
destination: {
|
||||
address: pathfindResult.destination_account,
|
||||
amount: destinationAmount
|
||||
},
|
||||
paths: JSON.stringify(parsePaths(alternative.paths_computed))
|
||||
};
|
||||
});
|
||||
function removeAnyCounterpartyEncoding(address: string, amount: Object) {
|
||||
return amount.counterparty === address ?
|
||||
_.omit(amount, 'counterparty') : amount;
|
||||
}
|
||||
|
||||
function createAdjustment(address: string, adjustmentWithoutAddress: Object) {
|
||||
const amountKey = _.keys(adjustmentWithoutAddress)[0];
|
||||
const amount = adjustmentWithoutAddress[amountKey];
|
||||
return _.set({address: address}, amountKey,
|
||||
removeAnyCounterpartyEncoding(address, amount));
|
||||
}
|
||||
|
||||
function parseAlternative(sourceAddress: string, destinationAddress: string,
|
||||
destinationAmount: Object, alternative: Object
|
||||
) {
|
||||
// we use "maxAmount"/"minAmount" here so that the result can be passed
|
||||
// directly to preparePayment
|
||||
const amounts = (alternative.destination_amount !== undefined) ?
|
||||
{source: {amount: parseAmount(alternative.source_amount)},
|
||||
destination: {minAmount: parseAmount(alternative.destination_amount)}} :
|
||||
{source: {maxAmount: parseAmount(alternative.source_amount)},
|
||||
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: Object): Object {
|
||||
const sourceAddress = pathfindResult.source_account;
|
||||
const destinationAddress = pathfindResult.destination_account;
|
||||
const destinationAmount = pathfindResult.destination_amount;
|
||||
return pathfindResult.alternatives.map(_.partial(parseAlternative,
|
||||
sourceAddress, destinationAddress, destinationAmount));
|
||||
}
|
||||
|
||||
module.exports = parsePathfind;
|
||||
|
||||
@@ -18,19 +18,6 @@ function isQualityLimited(tx) {
|
||||
return (tx.Flags & Transaction.flags.Payment.LimitQuality) !== 0;
|
||||
}
|
||||
|
||||
function parsePaymentMemos(tx) {
|
||||
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return tx.Memos.map((m) => {
|
||||
return utils.removeUndefined({
|
||||
type: m.Memo.parsed_memo_type,
|
||||
format: m.Memo.parsed_memo_format,
|
||||
data: m.Memo.parsed_memo_data
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function removeGenericCounterparty(amount, address) {
|
||||
return amount.counterparty === address ?
|
||||
_.omit(amount, 'counterparty') : amount;
|
||||
@@ -55,7 +42,7 @@ function parsePayment(tx: Object): Object {
|
||||
return utils.removeUndefined({
|
||||
source: utils.removeUndefined(source),
|
||||
destination: utils.removeUndefined(destination),
|
||||
memos: parsePaymentMemos(tx),
|
||||
memos: utils.parseMemos(tx),
|
||||
invoiceID: tx.InvoiceID,
|
||||
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
|
||||
allowPartialPayment: isPartialPayment(tx) || undefined,
|
||||
|
||||
16
src/api/ledger/parse/suspended-payment-cancellation.js
Normal file
16
src/api/ledger/parse/suspended-payment-cancellation.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const utils = require('./utils');
|
||||
|
||||
function parseSuspendedPaymentCancellation(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'SuspendedPaymentCancel');
|
||||
|
||||
return utils.removeUndefined({
|
||||
memos: utils.parseMemos(tx),
|
||||
owner: tx.Owner,
|
||||
paymentSequence: tx.OfferSequence
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = parseSuspendedPaymentCancellation;
|
||||
39
src/api/ledger/parse/suspended-payment-creation.js
Normal file
39
src/api/ledger/parse/suspended-payment-creation.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/* @flow */
|
||||
'use 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;
|
||||
}
|
||||
|
||||
function parseSuspendedPaymentCreation(tx: Object): Object {
|
||||
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),
|
||||
destination: utils.removeUndefined(destination),
|
||||
memos: utils.parseMemos(tx),
|
||||
digest: tx.Digest,
|
||||
allowCancelAfter: tx.CancelAfter,
|
||||
allowExecuteAfter: tx.FinishAfter
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = parseSuspendedPaymentCreation;
|
||||
25
src/api/ledger/parse/suspended-payment-execution.js
Normal file
25
src/api/ledger/parse/suspended-payment-execution.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const utils = require('./utils');
|
||||
|
||||
function convertHexToString(hexString) {
|
||||
const bits = sjclcodec.hex.toBits(hexString);
|
||||
return sjclcodec.utf8String.fromBits(bits);
|
||||
}
|
||||
|
||||
function parseSuspendedPaymentExecution(tx: Object): Object {
|
||||
assert(tx.TransactionType === 'SuspendedPaymentFinish');
|
||||
|
||||
return utils.removeUndefined({
|
||||
memos: utils.parseMemos(tx),
|
||||
owner: tx.Owner,
|
||||
paymentSequence: tx.OfferSequence,
|
||||
method: tx.Method,
|
||||
digest: tx.Digest,
|
||||
proof: tx.Proof ? convertHexToString(tx.Proof) : undefined
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = parseSuspendedPaymentExecution;
|
||||
@@ -7,6 +7,10 @@ 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');
|
||||
|
||||
function parseTransactionType(type) {
|
||||
const mapping = {
|
||||
@@ -15,7 +19,10 @@ function parseTransactionType(type) {
|
||||
OfferCreate: 'order',
|
||||
OfferCancel: 'orderCancellation',
|
||||
AccountSet: 'settings',
|
||||
SetRegularKey: 'settings'
|
||||
SetRegularKey: 'settings',
|
||||
SuspendedPaymentCreate: 'suspendedPaymentCreation',
|
||||
SuspendedPaymentFinish: 'suspendedPaymentExecution',
|
||||
SuspendedPaymentCancel: 'suspendedPaymentCancellation'
|
||||
};
|
||||
return mapping[type] || null;
|
||||
}
|
||||
@@ -27,7 +34,10 @@ function parseTransaction(tx: Object): Object {
|
||||
'trustline': parseTrustline,
|
||||
'order': parseOrder,
|
||||
'orderCancellation': parseOrderCancellation,
|
||||
'settings': parseSettings
|
||||
'settings': parseSettings,
|
||||
'suspendedPaymentCreation': parseSuspendedPaymentCreation,
|
||||
'suspendedPaymentExecution': parseSuspendedPaymentExecution,
|
||||
'suspendedPaymentCancellation': parseSuspendedPaymentCancellation
|
||||
};
|
||||
const parser = mapping[type];
|
||||
assert(parser !== undefined, 'Unrecognized transaction type');
|
||||
|
||||
@@ -67,8 +67,22 @@ function parseOutcome(tx: Object): ?Object {
|
||||
};
|
||||
}
|
||||
|
||||
function parseMemos(tx: Object): ?Array<Object> {
|
||||
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return tx.Memos.map((m) => {
|
||||
return removeUndefined({
|
||||
type: m.Memo.parsed_memo_type,
|
||||
format: m.Memo.parsed_memo_format,
|
||||
data: m.Memo.parsed_memo_data
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
parseOutcome,
|
||||
parseMemos,
|
||||
removeUndefined,
|
||||
adjustQualityForXRP,
|
||||
dropsToXrp: utils.common.dropsToXrp,
|
||||
|
||||
@@ -4,15 +4,18 @@ const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const parsePathfind = require('./parse/pathfind');
|
||||
const validate = utils.common.validate;
|
||||
const NotFoundError = utils.common.errors.NotFoundError;
|
||||
const ValidationError = utils.common.errors.ValidationError;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const convertErrors = utils.common.convertErrors;
|
||||
const toRippledAmount = utils.common.toRippledAmount;
|
||||
|
||||
type PathFindParams = {
|
||||
src_currencies?: Array<string>, src_account: string, dst_amount: string,
|
||||
dst_account?: string
|
||||
src_currencies?: Array<string>, src_account: string,
|
||||
dst_amount: string | Object, dst_account?: string,
|
||||
src_amount?: string | Object
|
||||
}
|
||||
|
||||
function addParams(params: PathFindParams, result: {}) {
|
||||
@@ -29,10 +32,11 @@ type PathFind = {
|
||||
}
|
||||
|
||||
function requestPathFind(remote, pathfind: PathFind, callback) {
|
||||
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount);
|
||||
const params: PathFindParams = {
|
||||
src_account: pathfind.source.address,
|
||||
dst_account: pathfind.destination.address,
|
||||
dst_amount: utils.common.toRippledAmount(pathfind.destination.amount)
|
||||
dst_amount: toRippledAmount(destinationAmount)
|
||||
};
|
||||
if (typeof params.dst_amount === 'object' && !params.dst_amount.issuer) {
|
||||
// Convert blank issuer to sender's address
|
||||
@@ -44,7 +48,17 @@ function requestPathFind(remote, pathfind: PathFind, callback) {
|
||||
}
|
||||
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
|
||||
params.src_currencies = pathfind.source.currencies.map(amount =>
|
||||
_.omit(utils.common.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');
|
||||
}
|
||||
params.src_amount = toRippledAmount(pathfind.source.amount);
|
||||
if (params.src_amount.currency && !params.src_amount.issuer) {
|
||||
params.src_amount.issuer = pathfind.source.address;
|
||||
}
|
||||
}
|
||||
|
||||
remote.createPathFind(params,
|
||||
@@ -81,10 +95,10 @@ function conditionallyAddDirectXRPPath(remote, address, paths, callback) {
|
||||
|
||||
function formatResponse(pathfind, paths) {
|
||||
if (paths.alternatives && paths.alternatives.length > 0) {
|
||||
const address = pathfind.source.address;
|
||||
return parsePathfind(address, pathfind.destination.amount, paths);
|
||||
return parsePathfind(paths);
|
||||
}
|
||||
if (!_.includes(paths.destination_currencies,
|
||||
if (paths.destination_currencies !== undefined &&
|
||||
!_.includes(paths.destination_currencies,
|
||||
pathfind.destination.amount.currency)) {
|
||||
throw new NotFoundError('No paths found. ' +
|
||||
'The destination_account does not accept ' +
|
||||
|
||||
@@ -52,23 +52,34 @@ function getTransactionAsync(identifier: string, options: TransactionOptions,
|
||||
validate.getTransactionOptions(options);
|
||||
|
||||
const remote = this.remote;
|
||||
const maxLedgerVersion = Math.min(options.maxLedgerVersion || Infinity,
|
||||
remote.getLedgerSequence());
|
||||
|
||||
function callbackWrapper(error_?: Error, tx?: Object) {
|
||||
function callbackWrapper(error_?: Error, tx?: Object,
|
||||
maxLedgerVersion?: number
|
||||
) {
|
||||
let error = error_;
|
||||
|
||||
if (!error && tx && tx.validated !== true) {
|
||||
return callback(new errors.NotFoundError('Transaction not found'));
|
||||
}
|
||||
|
||||
if (error instanceof RippleError && error.remote &&
|
||||
error.remote.error === 'txnNotFound') {
|
||||
error = new errors.NotFoundError('Transaction not found');
|
||||
}
|
||||
|
||||
// Missing complete ledger range
|
||||
if (error instanceof errors.NotFoundError
|
||||
&& !utils.hasCompleteLedgerRange(remote,
|
||||
options.minLedgerVersion, maxLedgerVersion)) {
|
||||
callback(new errors.MissingLedgerHistoryError('Transaction not found,'
|
||||
+ ' but the server\'s ledger history is incomplete'));
|
||||
&& !utils.hasCompleteLedgerRange(remote, options.minLedgerVersion,
|
||||
maxLedgerVersion)) {
|
||||
if (utils.isPendingLedgerVersion(remote, maxLedgerVersion)) {
|
||||
callback(new errors.PendingLedgerVersionError());
|
||||
} else {
|
||||
callback(new errors.MissingLedgerHistoryError());
|
||||
}
|
||||
// Transaction is found, but not in specified range
|
||||
} else if (!error && tx && !isTransactionInRange(tx, options)) {
|
||||
callback(new errors.NotFoundError('Transaction not found'));
|
||||
// Transaction is not found
|
||||
} else if (error) {
|
||||
convertErrors(callback)(error);
|
||||
} else if (!tx) {
|
||||
@@ -78,11 +89,19 @@ function getTransactionAsync(identifier: string, options: TransactionOptions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function maxLedgerGetter(error_?: Error, tx?: Object) {
|
||||
this.getLedgerVersion().then((version) => {
|
||||
const maxLedgerVersion = options.maxLedgerVersion || version;
|
||||
callbackWrapper(error_, tx, maxLedgerVersion);
|
||||
}, callbackWrapper);
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
_.partial(remote.requestTx.bind(remote),
|
||||
{hash: identifier, binary: false}),
|
||||
_.partial(attachTransactionDate, remote)
|
||||
], callbackWrapper);
|
||||
], maxLedgerGetter.bind(this));
|
||||
}
|
||||
|
||||
function getTransaction(identifier: string,
|
||||
|
||||
@@ -70,7 +70,9 @@ function formatPartialResponse(address, options, data) {
|
||||
function getAccountTx(remote, address, options, marker, limit, callback) {
|
||||
const params = {
|
||||
account: address,
|
||||
// -1 is equivalent to earliest available validated ledger
|
||||
ledger_index_min: options.minLedgerVersion || -1,
|
||||
// -1 is equivalent to most recent available validated ledger
|
||||
ledger_index_max: options.maxLedgerVersion || -1,
|
||||
forward: options.earliestFirst,
|
||||
binary: options.binary,
|
||||
@@ -121,7 +123,7 @@ function getTransactionsAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getTransactionsOptions(options);
|
||||
|
||||
const defaults = {maxLedgerVersion: this.remote.getLedgerSequence()};
|
||||
const defaults = {maxLedgerVersion: -1};
|
||||
if (options.start) {
|
||||
getTransaction.call(this, options.start).then(tx => {
|
||||
const ledgerVersion = tx.outcome.ledgerVersion;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
@@ -42,15 +43,15 @@ function getTrustlinesAsync(account: string, options: {currency: string,
|
||||
validate.address(account);
|
||||
validate.getTrustlinesOptions(options);
|
||||
|
||||
const ledgerVersion = options.ledgerVersion
|
||||
|| this.remote.getLedgerSequence();
|
||||
const getter = _.partial(getAccountLines, this.remote, account,
|
||||
ledgerVersion, options);
|
||||
options.ledgerVersion, options);
|
||||
utils.getRecursive(getter, options.limit, callback);
|
||||
}
|
||||
|
||||
function getTrustlines(account: string, options = {}) {
|
||||
return utils.promisify(getTrustlinesAsync).call(this, account, options);
|
||||
return utils.promisify(async.seq(
|
||||
utils.getLedgerOptionsWithLedgerVersion,
|
||||
getTrustlinesAsync)).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getTrustlines;
|
||||
|
||||
@@ -99,19 +99,42 @@ function compareTransactions(first: Outcome, second: Outcome): number {
|
||||
function hasCompleteLedgerRange(remote: Remote, minLedgerVersion?: number,
|
||||
maxLedgerVersion?: number
|
||||
): boolean {
|
||||
|
||||
const firstLedgerVersion = 32570; // earlier versions have been lost
|
||||
return remote.getServer().hasLedgerRange(
|
||||
minLedgerVersion || firstLedgerVersion,
|
||||
maxLedgerVersion || remote.getLedgerSequence());
|
||||
maxLedgerVersion || remote.getLedgerSequenceSync());
|
||||
}
|
||||
|
||||
function isPendingLedgerVersion(remote: Remote, maxLedgerVersion: ?number
|
||||
): boolean {
|
||||
const currentLedger = remote.getLedgerSequenceSync();
|
||||
return currentLedger < (maxLedgerVersion || 0);
|
||||
}
|
||||
|
||||
function getLedgerOptionsWithLedgerVersion(account: string, options: Object,
|
||||
callback: (err?: ?Error, account?: string, options: Object) => void
|
||||
) {
|
||||
if (Boolean(options) && options.ledgerVersion !== undefined &&
|
||||
options.ledgerVersion !== null
|
||||
) {
|
||||
callback(null, account, options);
|
||||
} else {
|
||||
this.getLedgerVersion().then((version) => {
|
||||
callback(null, account, _.assign({}, options, {ledgerVersion: version}));
|
||||
}, callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getXRPBalance,
|
||||
getLedgerOptionsWithLedgerVersion,
|
||||
compareTransactions,
|
||||
renameCounterpartyToIssuer,
|
||||
renameCounterpartyToIssuerInOrder,
|
||||
getRecursive,
|
||||
hasCompleteLedgerRange,
|
||||
isPendingLedgerVersion,
|
||||
promisify: common.promisify,
|
||||
clamp: clamp,
|
||||
common: common
|
||||
|
||||
@@ -53,8 +53,7 @@ function getServerInfoAsync(
|
||||
): void {
|
||||
this.remote.requestServerInfo((error, response) => {
|
||||
if (error) {
|
||||
const message =
|
||||
_.get(error, ['remote', 'error_message'], error.message);
|
||||
const message = _.get(error, ['remote', 'error_message'], error.message);
|
||||
callback(new common.errors.RippledNetworkError(message));
|
||||
} else {
|
||||
callback(null,
|
||||
@@ -63,28 +62,58 @@ function getServerInfoAsync(
|
||||
});
|
||||
}
|
||||
|
||||
function getFee(): number {
|
||||
return common.dropsToXrp(this.remote.createTransaction()._computeFee());
|
||||
function getFee(): ?number {
|
||||
if (!this.remote.getConnectedServers().length) {
|
||||
throw new common.errors.RippledNetworkError('No servers available.');
|
||||
}
|
||||
const fee = this.remote.createTransaction()._computeFee();
|
||||
return fee === undefined ? undefined : common.dropsToXrp(fee);
|
||||
}
|
||||
|
||||
function getLedgerVersion(): number {
|
||||
return this.remote.getLedgerSequence();
|
||||
function getLedgerVersion(): Promise<number> {
|
||||
return common.promisify(this.remote.getLedgerSequence).call(this.remote);
|
||||
}
|
||||
|
||||
function connect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.connect(() => callback(null));
|
||||
try {
|
||||
this.remote.connect(() => callback(null));
|
||||
} catch(error) {
|
||||
callback(new common.errors.RippledNetworkError(error.message));
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function disconnect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.disconnect(() => callback(null));
|
||||
try {
|
||||
this.remote.disconnect(() => callback(null));
|
||||
} catch(error) {
|
||||
callback(new common.errors.RippledNetworkError(error.message));
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function getServerInfo(): Promise<GetServerInfoResponse> {
|
||||
return common.promisify(getServerInfoAsync.bind(this))();
|
||||
return common.promisify(getServerInfoAsync).call(this);
|
||||
}
|
||||
|
||||
function rippleTimeToISO8601(rippleTime: string): string {
|
||||
return new Date(common.core.utils.toTimestamp(rippleTime)).toISOString();
|
||||
}
|
||||
|
||||
function formatLedgerClose(ledgerClose: Object): Object {
|
||||
return {
|
||||
feeBase: ledgerClose.fee_base,
|
||||
feeReference: ledgerClose.fee_ref,
|
||||
ledgerHash: ledgerClose.ledger_hash,
|
||||
ledgerVersion: ledgerClose.ledger_index,
|
||||
ledgerTimestamp: rippleTimeToISO8601(ledgerClose.ledger_time),
|
||||
reserveBase: ledgerClose.reserve_base,
|
||||
reserveIncrement: ledgerClose.reserve_inc,
|
||||
transactionCount: ledgerClose.txn_count,
|
||||
validatedLedgerVersions: ledgerClose.validated_ledgers
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
@@ -93,5 +122,6 @@ module.exports = {
|
||||
isConnected,
|
||||
getServerInfo,
|
||||
getFee,
|
||||
getLedgerVersion
|
||||
getLedgerVersion,
|
||||
formatLedgerClose
|
||||
};
|
||||
|
||||
@@ -32,10 +32,10 @@ function createOrderTransaction(account, order) {
|
||||
|
||||
function prepareOrderAsync(account, order, instructions, callback) {
|
||||
const transaction = createOrderTransaction(account, order);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function prepareOrder(account: string, order: Object, instructions={}) {
|
||||
function prepareOrder(account: string, order: Object, instructions = {}) {
|
||||
return utils.promisify(prepareOrderAsync.bind(this))(
|
||||
account, order, instructions);
|
||||
}
|
||||
|
||||
@@ -14,13 +14,15 @@ function createOrderCancellationTransaction(account, sequence) {
|
||||
}
|
||||
|
||||
function prepareOrderCancellationAsync(account, sequence, instructions,
|
||||
callback) {
|
||||
callback
|
||||
) {
|
||||
const transaction = createOrderCancellationTransaction(account, sequence);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function prepareOrderCancellation(account: string, sequence: number,
|
||||
instructions={}) {
|
||||
instructions = {}
|
||||
) {
|
||||
return utils.promisify(prepareOrderCancellationAsync.bind(this))(
|
||||
account, sequence, instructions);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const toRippledAmount = utils.common.toRippledAmount;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
const ValidationError = utils.common.errors.ValidationError;
|
||||
|
||||
function isXRPToXRPPayment(payment) {
|
||||
const sourceCurrency = _.get(payment, 'source.maxAmount.currency');
|
||||
@@ -23,24 +24,49 @@ function applyAnyCounterpartyEncoding(payment) {
|
||||
// https://ripple.com/build/transactions/
|
||||
// #special-issuer-values-for-sendmax-and-amount
|
||||
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
|
||||
if (isIOUWithoutCounterparty(payment.source.maxAmount)) {
|
||||
payment.source.maxAmount.counterparty = payment.source.address;
|
||||
}
|
||||
if (isIOUWithoutCounterparty(payment.destination.amount)) {
|
||||
payment.destination.amount.counterparty = payment.destination.address;
|
||||
}
|
||||
_.forEach([payment.source, payment.destination], (adjustment) => {
|
||||
_.forEach(['amount', 'minAmount', 'maxAmount'], (key) => {
|
||||
if (isIOUWithoutCounterparty(adjustment[key])) {
|
||||
adjustment[key].counterparty = adjustment.address;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createPaymentTransaction(account, payment) {
|
||||
function createMaximalAmount(amount) {
|
||||
const maxXRPValue = '100000000000';
|
||||
const maxIOUValue = '9999999999999999e80';
|
||||
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue;
|
||||
return _.assign(amount, {value: maxValue});
|
||||
}
|
||||
|
||||
function createPaymentTransaction(account, paymentArgument) {
|
||||
const payment = _.cloneDeep(paymentArgument);
|
||||
applyAnyCounterpartyEncoding(payment);
|
||||
validate.address(account);
|
||||
validate.payment(payment);
|
||||
|
||||
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)');
|
||||
}
|
||||
|
||||
// when using destination.minAmount, rippled still requires that we set
|
||||
// a destination amount in addition to DeliverMin. the destination amount
|
||||
// is interpreted as the maximum amount to send. we want to be sure to
|
||||
// send the whole source amount, so we set the destination amount to the
|
||||
// maximum possible amount. otherwise it's possible that the destination
|
||||
// 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);
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.payment({
|
||||
from: payment.source.address,
|
||||
to: payment.destination.address,
|
||||
amount: toRippledAmount(payment.destination.amount)
|
||||
amount: toRippledAmount(amount)
|
||||
});
|
||||
|
||||
if (payment.invoiceID) {
|
||||
@@ -57,9 +83,6 @@ function createPaymentTransaction(account, payment) {
|
||||
transaction.addMemo(memo.type, memo.format, memo.data)
|
||||
);
|
||||
}
|
||||
if (payment.allowPartialPayment) {
|
||||
transaction.setFlags(['PartialPayment']);
|
||||
}
|
||||
if (payment.noDirectRipple) {
|
||||
transaction.setFlags(['NoRippleDirect']);
|
||||
}
|
||||
@@ -71,11 +94,22 @@ function createPaymentTransaction(account, payment) {
|
||||
// temREDUNDANT_SEND_MAX removed in:
|
||||
// https://github.com/ripple/rippled/commit/
|
||||
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
|
||||
transaction.sendMax(toRippledAmount(payment.source.maxAmount));
|
||||
if (payment.allowPartialPayment || payment.destination.minAmount) {
|
||||
transaction.setFlags(['PartialPayment']);
|
||||
}
|
||||
|
||||
transaction.setSendMax(toRippledAmount(
|
||||
payment.source.maxAmount || payment.source.amount));
|
||||
|
||||
if (payment.destination.minAmount) {
|
||||
transaction.setDeliverMin(toRippledAmount(payment.destination.minAmount));
|
||||
}
|
||||
|
||||
if (payment.paths) {
|
||||
transaction.paths(JSON.parse(payment.paths));
|
||||
}
|
||||
} else if (payment.allowPartialPayment) {
|
||||
throw new ValidationError('XRP to XRP payments cannot be partial payments');
|
||||
}
|
||||
|
||||
return transaction;
|
||||
@@ -83,10 +117,10 @@ function createPaymentTransaction(account, payment) {
|
||||
|
||||
function preparePaymentAsync(account, payment, instructions, callback) {
|
||||
const transaction = createPaymentTransaction(account, payment);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function preparePayment(account: string, payment: Object, instructions={}) {
|
||||
function preparePayment(account: string, payment: Object, instructions = {}) {
|
||||
return utils.promisify(preparePaymentAsync.bind(this))(
|
||||
account, payment, instructions);
|
||||
}
|
||||
|
||||
@@ -92,10 +92,10 @@ function createSettingsTransaction(account, settings) {
|
||||
|
||||
function prepareSettingsAsync(account, settings, instructions, callback) {
|
||||
const transaction = createSettingsTransaction(account, settings);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function prepareSettings(account: string, settings: Object, instructions={}) {
|
||||
function prepareSettings(account: string, settings: Object, instructions = {}) {
|
||||
return utils.promisify(prepareSettingsAsync.bind(this))(
|
||||
account, settings, instructions);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const keypairs = require('ripple-keypairs');
|
||||
const core = utils.common.core;
|
||||
const validate = utils.common.validate;
|
||||
|
||||
@@ -16,14 +17,6 @@ const validate = utils.common.validate;
|
||||
*/
|
||||
const HASH_TX_ID = 0x54584E00; // 'TXN'
|
||||
|
||||
function getKeyPair(secret) {
|
||||
return core.Seed.from_json(secret).get_key();
|
||||
}
|
||||
|
||||
function getPublicKeyHex(keypair) {
|
||||
return keypair.pubKeyHex();
|
||||
}
|
||||
|
||||
function serialize(txJSON) {
|
||||
return core.SerializedObject.from_json(txJSON);
|
||||
}
|
||||
@@ -36,22 +29,22 @@ function signingData(txJSON) {
|
||||
return core.Transaction.from_json(txJSON).signingData().buffer;
|
||||
}
|
||||
|
||||
function computeSignature(txJSON, keypair) {
|
||||
return keypair.signHex(signingData(txJSON));
|
||||
function computeSignature(txJSON, privateKey) {
|
||||
return keypairs.sign(signingData(txJSON), privateKey);
|
||||
}
|
||||
|
||||
function sign(txJSON: {Account: string; SigningPubKey: string,
|
||||
TxnSignature: string}, secret: string):
|
||||
{signedTransaction: string; id: string} {
|
||||
validate.txJSON(txJSON);
|
||||
function sign(txJSON: string, secret: string
|
||||
): {signedTransaction: string; id: string} {
|
||||
const tx = JSON.parse(txJSON);
|
||||
validate.txJSON(tx);
|
||||
validate.secret(secret);
|
||||
|
||||
const keypair = getKeyPair(secret);
|
||||
if (txJSON.SigningPubKey === undefined) {
|
||||
txJSON.SigningPubKey = getPublicKeyHex(keypair);
|
||||
const keypair = keypairs.deriveKeypair(secret);
|
||||
if (tx.SigningPubKey === undefined) {
|
||||
tx.SigningPubKey = keypair.publicKey;
|
||||
}
|
||||
txJSON.TxnSignature = computeSignature(txJSON, keypair);
|
||||
const serialized = serialize(txJSON);
|
||||
tx.TxnSignature = computeSignature(tx, keypair.privateKey);
|
||||
const serialized = serialize(tx);
|
||||
return {
|
||||
signedTransaction: serialized.to_hex(),
|
||||
id: hashSerialization(serialized, HASH_TX_ID)
|
||||
|
||||
@@ -1,10 +1,31 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const Request = utils.common.core.Request;
|
||||
const convertErrors = utils.common.convertErrors;
|
||||
|
||||
function isImmediateRejection(engineResult) {
|
||||
// note: "tel" errors mean the local server refused to process the
|
||||
// transaction *at that time*, but it could potentially buffer the
|
||||
// transaction and then process it at a later time, for example
|
||||
// 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');
|
||||
}
|
||||
|
||||
function convertSubmitErrors(callback) {
|
||||
return function(error, data) {
|
||||
if (!error && isImmediateRejection(data.engineResult)) {
|
||||
callback(new utils.common.errors.RippleError('Submit failed'), data);
|
||||
} else {
|
||||
callback(error, data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function submitAsync(txBlob: string, callback: (err: any, data: any) => void
|
||||
): void {
|
||||
validate.blob(txBlob);
|
||||
@@ -13,7 +34,7 @@ function submitAsync(txBlob: string, callback: (err: any, data: any) => void
|
||||
request.request(null,
|
||||
utils.common.composeAsync(
|
||||
data => utils.common.convertKeysFromSnakeCaseToCamelCase(data),
|
||||
convertErrors(callback)));
|
||||
convertSubmitErrors(convertErrors(callback))));
|
||||
}
|
||||
|
||||
function submit(txBlob: string) {
|
||||
|
||||
40
src/api/transaction/suspended-payment-cancellation.js
Normal file
40
src/api/transaction/suspended-payment-cancellation.js
Normal file
@@ -0,0 +1,40 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
|
||||
function createSuspendedPaymentCancellationTransaction(account, payment) {
|
||||
validate.address(account);
|
||||
validate.suspendedPaymentCancellation(payment);
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.suspendedPaymentCancel({
|
||||
account: account,
|
||||
owner: payment.owner,
|
||||
paymentSequence: payment.paymentSequence
|
||||
});
|
||||
|
||||
if (payment.memos) {
|
||||
_.forEach(payment.memos, memo =>
|
||||
transaction.addMemo(memo.type, memo.format, memo.data)
|
||||
);
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentCancellationAsync(account, payment,
|
||||
instructions, callback) {
|
||||
const transaction =
|
||||
createSuspendedPaymentCancellationTransaction(account, payment);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentCancellation(account: string, payment: Object,
|
||||
instructions = {}) {
|
||||
return utils.promisify(prepareSuspendedPaymentCancellationAsync)
|
||||
.call(this, account, payment, instructions);
|
||||
}
|
||||
|
||||
module.exports = prepareSuspendedPaymentCancellation;
|
||||
57
src/api/transaction/suspended-payment-creation.js
Normal file
57
src/api/transaction/suspended-payment-creation.js
Normal file
@@ -0,0 +1,57 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const toRippledAmount = utils.common.toRippledAmount;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
|
||||
function createSuspendedPaymentCreationTransaction(account, payment) {
|
||||
validate.address(account);
|
||||
validate.suspendedPaymentCreation(payment);
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.suspendedPaymentCreate({
|
||||
account: account,
|
||||
destination: payment.destination.address,
|
||||
amount: toRippledAmount(payment.destination.amount)
|
||||
});
|
||||
|
||||
if (payment.digest) {
|
||||
transaction.setDigest(payment.digest);
|
||||
}
|
||||
if (payment.allowCancelAfter) {
|
||||
transaction.setAllowCancelAfter(payment.allowCancelAfter);
|
||||
}
|
||||
if (payment.allowExecuteAfter) {
|
||||
transaction.setAllowExecuteAfter(payment.allowExecuteAfter);
|
||||
}
|
||||
|
||||
if (payment.source.tag) {
|
||||
transaction.sourceTag(payment.source.tag);
|
||||
}
|
||||
if (payment.destination.tag) {
|
||||
transaction.destinationTag(payment.destination.tag);
|
||||
}
|
||||
if (payment.memos) {
|
||||
_.forEach(payment.memos, memo =>
|
||||
transaction.addMemo(memo.type, memo.format, memo.data)
|
||||
);
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentCreationAsync(account, payment, instructions,
|
||||
callback) {
|
||||
const transaction =
|
||||
createSuspendedPaymentCreationTransaction(account, payment);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentCreation(account: string, payment: Object,
|
||||
instructions = {}) {
|
||||
return utils.promisify(prepareSuspendedPaymentCreationAsync)
|
||||
.call(this, account, payment, instructions);
|
||||
}
|
||||
|
||||
module.exports = prepareSuspendedPaymentCreation;
|
||||
50
src/api/transaction/suspended-payment-execution.js
Normal file
50
src/api/transaction/suspended-payment-execution.js
Normal file
@@ -0,0 +1,50 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
|
||||
function createSuspendedPaymentExecutionTransaction(account, payment) {
|
||||
validate.address(account);
|
||||
validate.suspendedPaymentExecution(payment);
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.suspendedPaymentFinish({
|
||||
account: account,
|
||||
owner: payment.owner,
|
||||
paymentSequence: payment.paymentSequence
|
||||
});
|
||||
|
||||
if (payment.method) {
|
||||
transaction.setMethod(payment.method);
|
||||
}
|
||||
if (payment.digest) {
|
||||
transaction.setDigest(payment.digest);
|
||||
}
|
||||
if (payment.proof) {
|
||||
transaction.setProof(payment.proof);
|
||||
}
|
||||
|
||||
if (payment.memos) {
|
||||
_.forEach(payment.memos, memo =>
|
||||
transaction.addMemo(memo.type, memo.format, memo.data)
|
||||
);
|
||||
}
|
||||
return transaction;
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentExecutionAsync(account, payment, instructions,
|
||||
callback) {
|
||||
const transaction =
|
||||
createSuspendedPaymentExecutionTransaction(account, payment);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function prepareSuspendedPaymentExecution(account: string, payment: Object,
|
||||
instructions = {}) {
|
||||
return utils.promisify(prepareSuspendedPaymentExecutionAsync)
|
||||
.call(this, account, payment, instructions);
|
||||
}
|
||||
|
||||
module.exports = prepareSuspendedPaymentExecution;
|
||||
@@ -35,10 +35,11 @@ function createTrustlineTransaction(account, trustline) {
|
||||
|
||||
function prepareTrustlineAsync(account, trustline, instructions, callback) {
|
||||
const transaction = createTrustlineTransaction(account, trustline);
|
||||
utils.createTxJSON(transaction, this.remote, instructions, callback);
|
||||
utils.prepareTransaction(transaction, this.remote, instructions, callback);
|
||||
}
|
||||
|
||||
function prepareTrustline(account: string, trustline: Object, instructions={}) {
|
||||
function prepareTrustline(account: string, trustline: Object, instructions = {}
|
||||
) {
|
||||
return utils.promisify(prepareTrustlineAsync.bind(this))(
|
||||
account, trustline, instructions);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const common = require('../common');
|
||||
const composeAsync = common.composeAsync;
|
||||
|
||||
function setTransactionBitFlags(transaction: any, values: any, flags: any):
|
||||
void {
|
||||
function setTransactionBitFlags(transaction: any, values: any, flags: any
|
||||
): void {
|
||||
for (const flagName in flags) {
|
||||
const flagValue = values[flagName];
|
||||
const flagConversions = flags[flagName];
|
||||
@@ -18,53 +21,91 @@ function setTransactionBitFlags(transaction: any, values: any, flags: any):
|
||||
}
|
||||
}
|
||||
|
||||
function getFeeDrops(remote) {
|
||||
function getFeeDrops(remote, callback) {
|
||||
const feeUnits = 10; // all transactions currently have a fee of 10 fee units
|
||||
return remote.feeTx(feeUnits).to_text();
|
||||
remote.feeTxAsync(feeUnits, (err, data) => {
|
||||
callback(err, data ? data.to_text() : undefined);
|
||||
});
|
||||
}
|
||||
|
||||
function createTxJSON(transaction: any, remote: any, instructions: any,
|
||||
callback: (err: ?(typeof Error), data: {tx_json: any}) => void): void {
|
||||
function formatPrepareResponse(txJSON) {
|
||||
const instructions = {
|
||||
fee: txJSON.Fee,
|
||||
sequence: txJSON.Sequence,
|
||||
maxLedgerVersion: txJSON.LastLedgerSequence
|
||||
};
|
||||
return {
|
||||
txJSON: JSON.stringify(txJSON),
|
||||
instructions: _.omit(instructions, _.isUndefined)
|
||||
};
|
||||
}
|
||||
|
||||
type Callback = (err: ?(typeof Error),
|
||||
data: {txJSON: string, instructions: any}) => void;
|
||||
function prepareTransaction(transaction: any, remote: any, instructions: any,
|
||||
callback: Callback
|
||||
): void {
|
||||
common.validate.instructions(instructions);
|
||||
|
||||
transaction.complete();
|
||||
const account = transaction.getAccount();
|
||||
const txJSON = transaction.tx_json;
|
||||
|
||||
if (instructions.maxLedgerVersion !== undefined) {
|
||||
txJSON.LastLedgerSequence = parseInt(instructions.maxLedgerVersion, 10);
|
||||
} else {
|
||||
const offset = instructions.maxLedgerVersionOffset !== undefined ?
|
||||
parseInt(instructions.maxLedgerVersionOffset, 10) : 3;
|
||||
txJSON.LastLedgerSequence = remote.getLedgerSequence() + offset;
|
||||
}
|
||||
|
||||
if (instructions.fee !== undefined) {
|
||||
txJSON.Fee = common.xrpToDrops(instructions.fee);
|
||||
} else {
|
||||
const serverFeeDrops = getFeeDrops(remote);
|
||||
if (instructions.maxFee !== undefined) {
|
||||
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
|
||||
txJSON.Fee = BigNumber.min(serverFeeDrops, maxFeeDrops).toString();
|
||||
function prepareMaxLedgerVersion(callback_) {
|
||||
if (instructions.maxLedgerVersion !== undefined) {
|
||||
txJSON.LastLedgerSequence = parseInt(instructions.maxLedgerVersion, 10);
|
||||
callback_();
|
||||
} else {
|
||||
txJSON.Fee = serverFeeDrops;
|
||||
const offset = instructions.maxLedgerVersionOffset !== undefined ?
|
||||
parseInt(instructions.maxLedgerVersionOffset, 10) : 3;
|
||||
remote.getLedgerSequence((error, ledgerVersion) => {
|
||||
txJSON.LastLedgerSequence = ledgerVersion + offset;
|
||||
callback_(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (instructions.sequence !== undefined) {
|
||||
txJSON.Sequence = parseInt(instructions.sequence, 10);
|
||||
callback(null, txJSON);
|
||||
} else {
|
||||
remote.findAccount(account).getNextSequence(function(error, sequence) {
|
||||
txJSON.Sequence = sequence;
|
||||
callback(null, txJSON);
|
||||
});
|
||||
function prepareFee(callback_) {
|
||||
if (instructions.fee !== undefined) {
|
||||
txJSON.Fee = common.xrpToDrops(instructions.fee);
|
||||
callback_();
|
||||
} else {
|
||||
getFeeDrops(remote, composeAsync((serverFeeDrops) => {
|
||||
if (instructions.maxFee !== undefined) {
|
||||
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
|
||||
txJSON.Fee = BigNumber.min(serverFeeDrops, maxFeeDrops).toString();
|
||||
} else {
|
||||
txJSON.Fee = serverFeeDrops;
|
||||
}
|
||||
}, callback_));
|
||||
}
|
||||
}
|
||||
|
||||
function prepareSequence(callback_) {
|
||||
if (instructions.sequence !== undefined) {
|
||||
txJSON.Sequence = parseInt(instructions.sequence, 10);
|
||||
callback_(null, formatPrepareResponse(txJSON));
|
||||
} else {
|
||||
remote.findAccount(account).getNextSequence(function(error, sequence) {
|
||||
txJSON.Sequence = sequence;
|
||||
callback_(error, formatPrepareResponse(txJSON));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async.series([
|
||||
prepareMaxLedgerVersion,
|
||||
prepareFee,
|
||||
prepareSequence
|
||||
], common.convertErrors(function(error, results) {
|
||||
callback(error, results && results[2]);
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
setTransactionBitFlags: setTransactionBitFlags,
|
||||
createTxJSON: createTxJSON,
|
||||
common: common,
|
||||
setTransactionBitFlags,
|
||||
prepareTransaction,
|
||||
common,
|
||||
promisify: common.promisify
|
||||
};
|
||||
|
||||
@@ -14,12 +14,10 @@ const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const extend = require('extend');
|
||||
const util = require('util');
|
||||
const {createAccountID} = require('ripple-keypairs');
|
||||
const {encodeAccountID} = require('ripple-address-codec');
|
||||
const {deriveAddress} = require('ripple-keypairs');
|
||||
const {EventEmitter} = require('events');
|
||||
const {hexToArray} = require('./utils');
|
||||
const {TransactionManager} = require('./transactionmanager');
|
||||
const {UInt160} = require('./uint160');
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
|
||||
/**
|
||||
* @constructor Account
|
||||
@@ -27,14 +25,13 @@ const {UInt160} = require('./uint160');
|
||||
* @param {String} account
|
||||
*/
|
||||
|
||||
function Account(remote, account) {
|
||||
function Account(remote, address) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
const self = this;
|
||||
|
||||
this._remote = remote;
|
||||
this._account = UInt160.from_json(account);
|
||||
this._account_id = this._account.to_json();
|
||||
this._address = address;
|
||||
this._subs = 0;
|
||||
|
||||
// Ledger entry object
|
||||
@@ -45,7 +42,7 @@ function Account(remote, account) {
|
||||
if (_.includes(Account.subscribeEvents, type)) {
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestSubscribe()
|
||||
.addAccount(self._account_id)
|
||||
.addAccount(self._address)
|
||||
.broadcast().request();
|
||||
}
|
||||
self._subs += 1;
|
||||
@@ -59,7 +56,7 @@ function Account(remote, account) {
|
||||
self._subs -= 1;
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestUnsubscribe()
|
||||
.addAccount(self._account_id)
|
||||
.addAccount(self._address)
|
||||
.broadcast().request();
|
||||
}
|
||||
}
|
||||
@@ -68,8 +65,8 @@ function Account(remote, account) {
|
||||
this.on('removeListener', listenerRemoved);
|
||||
|
||||
function attachAccount(request) {
|
||||
if (self._account.is_valid() && self._subs) {
|
||||
request.addAccount(self._account_id);
|
||||
if (isValidAddress(self._address) && self._subs) {
|
||||
request.addAccount(self._address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +80,7 @@ function Account(remote, account) {
|
||||
let changed = false;
|
||||
|
||||
transaction.mmeta.each(function(an) {
|
||||
const isAccount = an.fields.Account === self._account_id;
|
||||
const isAccount = an.fields.Account === self._address;
|
||||
const isAccountRoot = isAccount && (an.entryType === 'AccountRoot');
|
||||
|
||||
if (isAccountRoot) {
|
||||
@@ -113,7 +110,7 @@ util.inherits(Account, EventEmitter);
|
||||
Account.subscribeEvents = ['transaction', 'entry'];
|
||||
|
||||
Account.prototype.toJson = function() {
|
||||
return this._account.to_json();
|
||||
return this._address;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -123,7 +120,7 @@ Account.prototype.toJson = function() {
|
||||
*/
|
||||
|
||||
Account.prototype.isValid = function() {
|
||||
return this._account.is_valid();
|
||||
return isValidAddress(this._address);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -133,7 +130,7 @@ Account.prototype.isValid = function() {
|
||||
*/
|
||||
|
||||
Account.prototype.getInfo = function(callback) {
|
||||
return this._remote.requestAccountInfo({account: this._account_id}, callback);
|
||||
return this._remote.requestAccountInfo({account: this._address}, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -212,7 +209,7 @@ Account.prototype.lines = function(callback_) {
|
||||
}
|
||||
}
|
||||
|
||||
this._remote.requestAccountLines({account: this._account_id}, accountLines);
|
||||
this._remote.requestAccountLines({account: this._address}, accountLines);
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -277,7 +274,7 @@ Account.prototype.notifyTx = function(transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isThisAccount = (account === this._account_id);
|
||||
const isThisAccount = (account === this._address);
|
||||
|
||||
this.emit(isThisAccount ? 'transaction-outbound' : 'transaction-inbound',
|
||||
transaction);
|
||||
@@ -333,7 +330,7 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
|
||||
// Catch the case of unfunded accounts
|
||||
if (!account_info_res) {
|
||||
|
||||
if (public_key_as_uint160 === self._account_id) {
|
||||
if (public_key_as_uint160 === self._address) {
|
||||
async_callback(null, true);
|
||||
} else {
|
||||
async_callback(null, false);
|
||||
@@ -375,20 +372,17 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
|
||||
* @returns {RippleAddress} Ripple Address
|
||||
*/
|
||||
Account._publicKeyToAddress = function(public_key) {
|
||||
// Based on functions in /src/js/ripple/keypair.js
|
||||
function hexToUInt160(publicKey) {
|
||||
return encodeAccountID(createAccountID(hexToArray(publicKey)));
|
||||
}
|
||||
|
||||
if (UInt160.is_valid(public_key)) {
|
||||
if (isValidAddress(public_key)) {
|
||||
return public_key;
|
||||
} else if (/^[0-9a-fA-F]+$/.test(public_key)) {
|
||||
return hexToUInt160(public_key);
|
||||
return deriveAddress(public_key);
|
||||
} else { // eslint-disable-line no-else-return
|
||||
throw new Error('Public key is invalid. Must be a UInt160 or a hex string');
|
||||
}
|
||||
};
|
||||
|
||||
exports.Account = Account;
|
||||
module.exports = {
|
||||
Account
|
||||
};
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -6,23 +6,23 @@
|
||||
const assert = require('assert');
|
||||
const extend = require('extend');
|
||||
const utils = require('./utils');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Seed = require('./seed').Seed;
|
||||
const Currency = require('./currency').Currency;
|
||||
const Value = require('./value').Value;
|
||||
const IOUValue = require('./iouvalue').IOUValue;
|
||||
const XRPValue = require('./xrpvalue').XRPValue;
|
||||
const {XRPValue, IOUValue} = require('ripple-lib-value');
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
const {ACCOUNT_ONE, ACCOUNT_ZERO} = require('./constants');
|
||||
|
||||
type Value = XRPValue | IOUValue;
|
||||
|
||||
function Amount(value = new XRPValue(NaN)) {
|
||||
// Json format:
|
||||
// integer : XRP
|
||||
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
|
||||
assert(value instanceof Value);
|
||||
assert(value instanceof XRPValue || value instanceof IOUValue);
|
||||
|
||||
this._value = value;
|
||||
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
|
||||
this._currency = new Currency();
|
||||
this._issuer = new UInt160();
|
||||
this._issuer = 'NaN';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,13 +49,13 @@ const consts = {
|
||||
// Maximum possible amount for non-XRP currencies using the maximum mantissa
|
||||
// with maximum exponent. Corresponds to hex 0xEC6386F26FC0FFFF.
|
||||
max_value: '9999999999999999e80',
|
||||
// Minimum possible amount for non-XRP currencies.
|
||||
min_value: '-1000000000000000e-96'
|
||||
// Minimum nonzero absolute value for non-XRP currencies.
|
||||
min_value: '1000000000000000e-96'
|
||||
};
|
||||
|
||||
const MAX_XRP_VALUE = new XRPValue(1e11);
|
||||
const MAX_IOU_VALUE = new IOUValue(consts.max_value);
|
||||
const MIN_IOU_VALUE = new IOUValue(consts.min_value).abs();
|
||||
const MIN_IOU_VALUE = new IOUValue(consts.min_value);
|
||||
|
||||
const bi_xns_unit = new IOUValue(1e6);
|
||||
|
||||
@@ -105,44 +105,56 @@ Amount.NaN = function() {
|
||||
return result; // but let's be careful
|
||||
};
|
||||
|
||||
Amount.from_components_unsafe = function(value: Value, currency: Currency,
|
||||
issuer: string, isNative: boolean
|
||||
) {
|
||||
const result = new Amount(value);
|
||||
result._is_native = isNative;
|
||||
result._currency = currency;
|
||||
result._issuer = issuer;
|
||||
|
||||
result._value = value.isZero() && value.isNegative() ?
|
||||
value.negate() : value;
|
||||
return result;
|
||||
};
|
||||
|
||||
// be sure that _is_native is set properly BEFORE calling _set_value
|
||||
Amount.prototype._set_value = function(value: Value) {
|
||||
|
||||
this._value = value.isZero() && value.isNegative() ?
|
||||
value.negate() : value;
|
||||
this._check_limits();
|
||||
|
||||
};
|
||||
|
||||
// Returns a new value which is the absolute value of this.
|
||||
Amount.prototype.abs = function() {
|
||||
|
||||
return this._copy(this._value.abs());
|
||||
|
||||
};
|
||||
|
||||
Amount.prototype.add = function(addend) {
|
||||
const addendAmount = Amount.from_json(addend);
|
||||
const addendAmount = addend instanceof Amount ?
|
||||
addend : Amount.from_json(addend);
|
||||
|
||||
if (!this.is_comparable(addendAmount)) {
|
||||
return new Amount();
|
||||
}
|
||||
|
||||
return this._copy(this._value.add(addendAmount._value));
|
||||
|
||||
};
|
||||
|
||||
Amount.prototype.subtract = function(subtrahend) {
|
||||
// Correctness over speed, less code has less bugs, reuse add code.
|
||||
return this.add(Amount.from_json(subtrahend).negate());
|
||||
const subsAmount = subtrahend instanceof Amount ?
|
||||
subtrahend : Amount.from_json(subtrahend);
|
||||
return this.add(subsAmount.negate());
|
||||
};
|
||||
|
||||
// XXX Diverges from cpp.
|
||||
Amount.prototype.multiply = function(multiplicand) {
|
||||
const multiplicandValue = multiplicand instanceof Amount ?
|
||||
multiplicand._value :
|
||||
Amount.from_json(multiplicand)._value;
|
||||
|
||||
const multiplicandAmount = Amount.from_json(multiplicand);
|
||||
|
||||
return this._copy(this._value.multiply(multiplicandAmount._value));
|
||||
return this._copy(this._value.multiply(multiplicandValue));
|
||||
|
||||
};
|
||||
|
||||
@@ -151,9 +163,11 @@ Amount.prototype.scale = function(scaleFactor) {
|
||||
};
|
||||
|
||||
Amount.prototype.divide = function(divisor) {
|
||||
const divisorAmount = Amount.from_json(divisor);
|
||||
const divisorValue = divisor instanceof Amount ?
|
||||
divisor._value :
|
||||
Amount.from_json(divisor)._value;
|
||||
|
||||
return this._copy(this._value.divide(divisorAmount._value));
|
||||
return this._copy(this._value.divide(divisorValue));
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -211,7 +225,7 @@ Amount.prototype.ratio_human = function(denom, opts) {
|
||||
//
|
||||
// To compensate, we multiply the numerator by 10^xns_precision.
|
||||
if (denominator._is_native) {
|
||||
numerator._set_value(numerator.multiply(bi_xns_unit));
|
||||
numerator._set_value(numerator._value.multiply(bi_xns_unit));
|
||||
}
|
||||
|
||||
return numerator.divide(denominator);
|
||||
@@ -344,7 +358,7 @@ Amount.prototype._check_limits = function() {
|
||||
};
|
||||
|
||||
Amount.prototype.clone = function(negate) {
|
||||
return this.copyTo(new Amount(), negate);
|
||||
return this.copyTo(new Amount(this._value), negate);
|
||||
};
|
||||
|
||||
Amount.prototype._copy = function(value) {
|
||||
@@ -354,9 +368,10 @@ Amount.prototype._copy = function(value) {
|
||||
};
|
||||
|
||||
Amount.prototype.compareTo = function(to) {
|
||||
const toAmount = Amount.from_json(to);
|
||||
const toAmount = to instanceof Amount ? to : Amount.from_json(to);
|
||||
|
||||
if (!this.is_comparable(toAmount)) {
|
||||
return new Amount();
|
||||
throw new Error('Not comparable');
|
||||
}
|
||||
return this._value.comparedTo(toAmount._value);
|
||||
};
|
||||
@@ -384,7 +399,7 @@ Amount.prototype.equals = function(d, ignore_issuer) {
|
||||
&& this._is_native === d._is_native
|
||||
&& this._value.equals(d._value)
|
||||
&& (this._is_native || (this._currency.equals(d._currency)
|
||||
&& (ignore_issuer || this._issuer.equals(d._issuer))));
|
||||
&& (ignore_issuer || this._issuer === d._issuer)));
|
||||
};
|
||||
|
||||
// True if Amounts are valid and both native or non-native.
|
||||
@@ -410,9 +425,8 @@ Amount.prototype.is_valid = function() {
|
||||
};
|
||||
|
||||
Amount.prototype.is_valid_full = function() {
|
||||
return this.is_valid()
|
||||
&& this._currency.is_valid()
|
||||
&& this._issuer.is_valid();
|
||||
return this.is_valid() && this._currency.is_valid()
|
||||
&& isValidAddress(this._issuer) && this._issuer !== ACCOUNT_ZERO;
|
||||
};
|
||||
|
||||
Amount.prototype.is_zero = function() {
|
||||
@@ -514,7 +528,7 @@ Amount.prototype.parse_human = function(j, options) {
|
||||
};
|
||||
|
||||
Amount.prototype.parse_issuer = function(issuer) {
|
||||
this._issuer = UInt160.from_json(issuer);
|
||||
this._issuer = issuer;
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -565,7 +579,7 @@ function(quality, counterCurrency, counterIssuer, opts) {
|
||||
const offset = parseInt(offset_hex, 16) - 100;
|
||||
|
||||
this._currency = Currency.from_json(counterCurrency);
|
||||
this._issuer = UInt160.from_json(counterIssuer);
|
||||
this._issuer = counterIssuer;
|
||||
this._is_native = this._currency.is_native();
|
||||
|
||||
if (this._is_native && baseCurrency.is_native()) {
|
||||
@@ -607,7 +621,8 @@ function(quality, counterCurrency, counterIssuer, opts) {
|
||||
}
|
||||
if (this._is_native) {
|
||||
this._set_value(
|
||||
new XRPValue(nativeAdjusted.round(6, Value.getBNRoundDown()).toString()));
|
||||
new XRPValue(nativeAdjusted.round(6, XRPValue.getBNRoundDown())
|
||||
.toString()));
|
||||
} else {
|
||||
this._set_value(nativeAdjusted);
|
||||
}
|
||||
@@ -624,7 +639,7 @@ function(quality, counterCurrency, counterIssuer, opts) {
|
||||
Amount.prototype.parse_number = function(n) {
|
||||
this._is_native = false;
|
||||
this._currency = Currency.from_json(1);
|
||||
this._issuer = UInt160.from_json(1);
|
||||
this._issuer = ACCOUNT_ONE;
|
||||
this._set_value(new IOUValue(n));
|
||||
return this;
|
||||
};
|
||||
@@ -640,15 +655,15 @@ Amount.prototype.parse_json = function(j) {
|
||||
if (m) {
|
||||
this._currency = Currency.from_json(m[2]);
|
||||
if (m[3]) {
|
||||
this._issuer = UInt160.from_json(m[3]);
|
||||
this._issuer = m[3];
|
||||
} else {
|
||||
this._issuer = UInt160.from_json('1');
|
||||
this._issuer = 'NaN';
|
||||
}
|
||||
this.parse_value(m[1]);
|
||||
} else {
|
||||
this.parse_native(j);
|
||||
this._currency = Currency.from_json('0');
|
||||
this._issuer = UInt160.from_json('0');
|
||||
this._issuer = ACCOUNT_ZERO;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -667,9 +682,10 @@ Amount.prototype.parse_json = function(j) {
|
||||
// Parse the passed value to sanitize and copy it.
|
||||
this._currency.parse_json(j.currency, true); // Never XRP.
|
||||
|
||||
if (typeof j.issuer === 'string') {
|
||||
this._issuer.parse_json(j.issuer);
|
||||
if (typeof j.issuer !== 'string') {
|
||||
throw new Error('issuer must be a string');
|
||||
}
|
||||
this._issuer = j.issuer;
|
||||
|
||||
this.parse_value(j.value);
|
||||
}
|
||||
@@ -705,7 +721,7 @@ Amount.prototype.parse_native = function(j) {
|
||||
// Requires _currency to be set!
|
||||
Amount.prototype.parse_value = function(j) {
|
||||
this._is_native = false;
|
||||
const newValue = new IOUValue(j, Value.getBNRoundDown());
|
||||
const newValue = new IOUValue(j, IOUValue.getBNRoundDown());
|
||||
this._set_value(newValue);
|
||||
return this;
|
||||
};
|
||||
@@ -717,12 +733,7 @@ Amount.prototype.set_currency = function(c) {
|
||||
};
|
||||
|
||||
Amount.prototype.set_issuer = function(issuer) {
|
||||
if (issuer instanceof UInt160) {
|
||||
this._issuer = issuer;
|
||||
} else {
|
||||
this._issuer = UInt160.from_json(issuer);
|
||||
}
|
||||
|
||||
this._issuer = issuer;
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -730,6 +741,14 @@ Amount.prototype.to_number = function() {
|
||||
return Number(this.to_text());
|
||||
};
|
||||
|
||||
|
||||
// this one is needed because Value.abs creates new BigNumber,
|
||||
// and BigNumber constructor is very slow, so we want to
|
||||
// call it only if absolutely necessary
|
||||
function absValue(value: Value): Value {
|
||||
return value.isNegative() ? value.abs() : value;
|
||||
}
|
||||
|
||||
// Convert only value to JSON wire format.
|
||||
Amount.prototype.to_text = function() {
|
||||
if (!this.is_valid()) {
|
||||
@@ -743,8 +762,8 @@ Amount.prototype.to_text = function() {
|
||||
// not native
|
||||
const offset = this._value.getExponent() - 15;
|
||||
const sign = this._value.isNegative() ? '-' : '';
|
||||
const mantissa = utils.getMantissa16FromString(
|
||||
this._value.abs().toString());
|
||||
const mantissa =
|
||||
utils.getMantissa16FromString(absValue(this._value).toString());
|
||||
if (offset !== 0 && (offset < -25 || offset > -4)) {
|
||||
// Use e notation.
|
||||
// XXX Clamp output.
|
||||
@@ -912,7 +931,7 @@ Amount.prototype.to_human_full = function(options) {
|
||||
const opts = options || {};
|
||||
const value = this.to_human(opts);
|
||||
const currency = this._currency.to_human();
|
||||
const issuer = this._issuer.to_json(opts);
|
||||
const issuer = this._issuer;
|
||||
const base = value + '/' + currency;
|
||||
return this.is_native() ? base : (base + '/' + issuer);
|
||||
};
|
||||
@@ -928,21 +947,21 @@ Amount.prototype.to_json = function() {
|
||||
this._currency.to_hex() : this._currency.to_json()
|
||||
};
|
||||
|
||||
if (this._issuer.is_valid()) {
|
||||
amount_json.issuer = this._issuer.to_json();
|
||||
if (isValidAddress(this._issuer)) {
|
||||
amount_json.issuer = this._issuer;
|
||||
}
|
||||
|
||||
return amount_json;
|
||||
};
|
||||
|
||||
Amount.prototype.to_text_full = function(opts) {
|
||||
Amount.prototype.to_text_full = function() {
|
||||
if (!this.is_valid()) {
|
||||
return 'NaN';
|
||||
}
|
||||
return this._is_native
|
||||
? this.to_human() + '/XRP'
|
||||
: this.to_text() + '/' + this._currency.to_json()
|
||||
+ '/' + this._issuer.to_json(opts);
|
||||
+ '/' + this._issuer;
|
||||
};
|
||||
|
||||
// For debugging.
|
||||
@@ -971,20 +990,11 @@ Amount.prototype.not_equals_why = function(d, ignore_issuer) {
|
||||
if (!this._currency.equals(d._currency)) {
|
||||
return 'Non-XRP currency differs.';
|
||||
}
|
||||
if (!ignore_issuer && !this._issuer.equals(d._issuer)) {
|
||||
return 'Non-XRP issuer differs: '
|
||||
+ d._issuer.to_json()
|
||||
+ '/'
|
||||
+ this._issuer.to_json();
|
||||
if (!ignore_issuer && this._issuer !== d._issuer) {
|
||||
return 'Non-XRP issuer differs: ' + d._issuer + '/' + this._issuer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.Amount = Amount;
|
||||
|
||||
// DEPRECATED: Include the corresponding files instead.
|
||||
exports.Currency = Currency;
|
||||
exports.Seed = Seed;
|
||||
exports.UInt160 = UInt160;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Amount = require('./amount').Amount;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Utils = require('./orderbookutils');
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
@@ -18,32 +18,70 @@ function assertValidLegOneOffer(legOneOffer, message) {
|
||||
}
|
||||
|
||||
function AutobridgeCalculator(currencyGets, currencyPays,
|
||||
legOneOffers, legTwoOffers, issuerGets, issuerPays) {
|
||||
legOneOffers, legTwoOffers, issuerGets, issuerPays
|
||||
) {
|
||||
this._currencyGets = currencyGets;
|
||||
this._currencyPays = currencyPays;
|
||||
this._currencyGetsHex = currencyGets.to_hex();
|
||||
this._currencyPaysHex = currencyPays.to_hex();
|
||||
this._issuerGets = issuerGets;
|
||||
this._issuerGetsObj = UInt160.from_json(issuerGets);
|
||||
this._issuerPays = issuerPays;
|
||||
this._issuerPaysObj = UInt160.from_json(issuerPays);
|
||||
this.legOneOffers = _.cloneDeep(legOneOffers);
|
||||
this.legTwoOffers = _.cloneDeep(legTwoOffers);
|
||||
|
||||
this._ownerFundsLeftover = {};
|
||||
}
|
||||
|
||||
const NULL_AMOUNT = Utils.normalizeAmount('0');
|
||||
|
||||
/**
|
||||
* Calculates an ordered array of autobridged offers by quality
|
||||
*
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.calculate = function() {
|
||||
let legOnePointer = 0;
|
||||
let legTwoPointer = 0;
|
||||
AutobridgeCalculator.prototype.calculate = function(callback) {
|
||||
|
||||
let offersAutobridged = [];
|
||||
const legOnePointer = 0;
|
||||
const legTwoPointer = 0;
|
||||
|
||||
const offersAutobridged = [];
|
||||
|
||||
this.clearOwnerFundsLeftover();
|
||||
|
||||
this._calculateInternal(legOnePointer, legTwoPointer, offersAutobridged,
|
||||
callback);
|
||||
};
|
||||
|
||||
AutobridgeCalculator.prototype._calculateInternal = function(
|
||||
legOnePointer_, legTwoPointer_, offersAutobridged, callback
|
||||
) {
|
||||
|
||||
// Amount class is calling _check_limits after each operation in strict mode,
|
||||
// and _check_limits is very computationally expensive, so we turning it off
|
||||
// whle doing calculations
|
||||
this._oldMode = Amount.strict_mode;
|
||||
Amount.strict_mode = false;
|
||||
|
||||
let legOnePointer = legOnePointer_;
|
||||
let legTwoPointer = legTwoPointer_;
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
while (this.legOneOffers[legOnePointer] && this.legTwoOffers[legTwoPointer]) {
|
||||
// manually implement cooperative multitasking that yields after 30ms
|
||||
// of execution so user's browser stays responsive
|
||||
const lasted = (Date.now() - startTime);
|
||||
if (lasted > 30) {
|
||||
setTimeout(this._calculateInternal.bind(this, legOnePointer,
|
||||
legTwoPointer, offersAutobridged, callback), 0);
|
||||
|
||||
Amount.strict_mode = this._oldMode;
|
||||
return;
|
||||
}
|
||||
|
||||
const legOneOffer = this.legOneOffers[legOnePointer];
|
||||
const legTwoOffer = this.legTwoOffers[legTwoPointer];
|
||||
const leftoverFunds = this.getLeftoverOwnerFunds(legOneOffer.Account);
|
||||
@@ -55,8 +93,10 @@ AutobridgeCalculator.prototype.calculate = function() {
|
||||
this.adjustLegOneFundedAmount(legOneOffer);
|
||||
}
|
||||
|
||||
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
|
||||
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
|
||||
this._currencyGets, this._issuerGetsObj);
|
||||
|
||||
if (legOneTakerGetsFunded.is_zero()) {
|
||||
legOnePointer++;
|
||||
@@ -70,14 +110,17 @@ AutobridgeCalculator.prototype.calculate = function() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (legOneTakerGetsFunded.compareTo(legTwoTakerPaysFunded) > 0) {
|
||||
// using private fields for speed
|
||||
if (legOneTakerGetsFunded._value.comparedTo(
|
||||
legTwoTakerPaysFunded._value) > 0) {
|
||||
autobridgedOffer = this.getAutobridgedOfferWithClampedLegOne(
|
||||
legOneOffer,
|
||||
legTwoOffer
|
||||
);
|
||||
|
||||
legTwoPointer++;
|
||||
} else if (legTwoTakerPaysFunded.compareTo(legOneTakerGetsFunded) > 0) {
|
||||
} else if (legTwoTakerPaysFunded._value.comparedTo(
|
||||
legOneTakerGetsFunded._value) > 0) {
|
||||
autobridgedOffer = this.getAutobridgedOfferWithClampedLegTwo(
|
||||
legOneOffer,
|
||||
legTwoOffer
|
||||
@@ -97,7 +140,8 @@ AutobridgeCalculator.prototype.calculate = function() {
|
||||
offersAutobridged.push(autobridgedOffer);
|
||||
}
|
||||
|
||||
return offersAutobridged;
|
||||
Amount.strict_mode = this._oldMode;
|
||||
callback(offersAutobridged);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -112,15 +156,20 @@ AutobridgeCalculator.prototype.calculate = function() {
|
||||
|
||||
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegOne =
|
||||
function(legOneOffer, legTwoOffer) {
|
||||
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
|
||||
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
|
||||
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
|
||||
this._currencyGets, this._issuerGetsObj);
|
||||
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
|
||||
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
|
||||
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer,
|
||||
this._currencyGets, this._issuerGetsObj);
|
||||
const autobridgedTakerPays = legTwoTakerPaysFunded.multiply(legOneQuality);
|
||||
|
||||
if (legOneOffer.Account === legTwoOffer.Account) {
|
||||
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
|
||||
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
const updatedTakerGets = legOneTakerGets.subtract(legTwoTakerPaysFunded);
|
||||
|
||||
this.setLegOneTakerGets(legOneOffer, updatedTakerGets);
|
||||
@@ -152,15 +201,20 @@ function(legOneOffer, legTwoOffer) {
|
||||
|
||||
AutobridgeCalculator.prototype.getAutobridgedOfferWithClampedLegTwo =
|
||||
function(legOneOffer, legTwoOffer) {
|
||||
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer);
|
||||
const legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets);
|
||||
const legOneTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
const legTwoTakerPaysFunded = Utils.getOfferTakerPaysFunded(legTwoOffer,
|
||||
this._currencyGets, this._issuerGetsObj);
|
||||
const legTwoQuality = Utils.getOfferQuality(legTwoOffer, this._currencyGets,
|
||||
this._currencyGets, this._issuerGetsObj);
|
||||
|
||||
const autobridgedTakerGets = legOneTakerGetsFunded.divide(legTwoQuality);
|
||||
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
|
||||
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
|
||||
// Update funded amount since leg two offer was not completely consumed
|
||||
legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer)
|
||||
legTwoOffer.taker_gets_funded = Utils.getOfferTakerGetsFunded(legTwoOffer,
|
||||
this._currencyGets, this._issuerGetsObj)
|
||||
.subtract(autobridgedTakerGets)
|
||||
.to_text();
|
||||
legTwoOffer.taker_pays_funded = legTwoTakerPaysFunded
|
||||
@@ -184,8 +238,10 @@ function(legOneOffer, legTwoOffer) {
|
||||
|
||||
AutobridgeCalculator.prototype.getAutobridgedOfferWithoutClamps =
|
||||
function(legOneOffer, legTwoOffer) {
|
||||
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer);
|
||||
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer);
|
||||
const autobridgedTakerGets = Utils.getOfferTakerGetsFunded(legTwoOffer,
|
||||
this._currencyGets, this._issuerGetsObj);
|
||||
const autobridgedTakerPays = Utils.getOfferTakerPaysFunded(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
|
||||
return this.formatAutobridgedOffer(
|
||||
autobridgedTakerGets,
|
||||
@@ -210,9 +266,7 @@ AutobridgeCalculator.prototype.clearOwnerFundsLeftover = function() {
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
this._ownerFundsLeftover[account] = Utils.normalizeAmount('0');
|
||||
this._ownerFundsLeftover[account] = NULL_AMOUNT.clone();
|
||||
|
||||
return this._ownerFundsLeftover[account];
|
||||
};
|
||||
@@ -226,12 +280,10 @@ AutobridgeCalculator.prototype.resetOwnerFundsLeftover = function(account) {
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.getLeftoverOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
let amount = this._ownerFundsLeftover[account];
|
||||
|
||||
if (!amount) {
|
||||
amount = Utils.normalizeAmount('0');
|
||||
amount = NULL_AMOUNT.clone();
|
||||
}
|
||||
|
||||
return amount;
|
||||
@@ -248,7 +300,6 @@ AutobridgeCalculator.prototype.getLeftoverOwnerFunds = function(account) {
|
||||
|
||||
AutobridgeCalculator.prototype.addLeftoverOwnerFunds =
|
||||
function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
assert(amount instanceof Amount, 'Amount is invalid');
|
||||
|
||||
this._ownerFundsLeftover[account] = this.getLeftoverOwnerFunds(account)
|
||||
@@ -266,7 +317,6 @@ function(account, amount) {
|
||||
|
||||
AutobridgeCalculator.prototype.setLeftoverOwnerFunds =
|
||||
function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
assert(amount instanceof Amount, 'Amount is invalid');
|
||||
|
||||
this._ownerFundsLeftover[account] = amount;
|
||||
@@ -291,13 +341,13 @@ function(takerGets, takerPays) {
|
||||
|
||||
autobridgedOffer.TakerGets = {
|
||||
value: takerGets.to_text(),
|
||||
currency: this._currencyGets.to_hex(),
|
||||
currency: this._currencyGetsHex,
|
||||
issuer: this._issuerGets
|
||||
};
|
||||
|
||||
autobridgedOffer.TakerPays = {
|
||||
value: takerPays.to_text(),
|
||||
currency: this._currencyPays.to_hex(),
|
||||
currency: this._currencyPaysHex,
|
||||
issuer: this._issuerPays
|
||||
};
|
||||
|
||||
@@ -308,7 +358,9 @@ function(takerGets, takerPays) {
|
||||
|
||||
autobridgedOffer.autobridged = true;
|
||||
|
||||
autobridgedOffer.BookDirectory = Utils.convertOfferQualityToHex(quality);
|
||||
autobridgedOffer.BookDirectory =
|
||||
Utils.convertOfferQualityToHexFromText(autobridgedOffer.quality);
|
||||
autobridgedOffer.qualityHex = autobridgedOffer.BookDirectory;
|
||||
|
||||
return autobridgedOffer;
|
||||
};
|
||||
@@ -325,11 +377,13 @@ function(takerGets, takerPays) {
|
||||
AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
|
||||
legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer);
|
||||
legOneOffer.initTakerGetsFunded = Utils.getOfferTakerGetsFunded(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
|
||||
this.setLegOneTakerGetsFunded(
|
||||
legOneOffer,
|
||||
Utils.getOfferTakerGets(legOneOffer)
|
||||
Utils.getOfferTakerGets(legOneOffer, this._currencyPays,
|
||||
this._issuerPaysObj)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -350,7 +404,8 @@ AutobridgeCalculator.prototype.unclampLegOneOwnerFunds = function(legOneOffer) {
|
||||
AutobridgeCalculator.prototype.clampLegOneOwnerFunds = function(legOneOffer) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
|
||||
const takerGets = Utils.getOfferTakerGets(legOneOffer);
|
||||
const takerGets = Utils.getOfferTakerGets(legOneOffer, this._currencyPays,
|
||||
this._issuerPaysObj);
|
||||
|
||||
if (takerGets.compareTo(legOneOffer.initTakerGetsFunded) > 0) {
|
||||
// After clamping, TakerGets is still greater than initial funded amount
|
||||
@@ -375,12 +430,16 @@ function(legOneOffer) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
assert(!legOneOffer.is_fully_funded, 'Leg one offer cannot be fully funded');
|
||||
|
||||
const fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer)
|
||||
const fundedSum = Utils.getOfferTakerGetsFunded(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj)
|
||||
.add(this.getLeftoverOwnerFunds(legOneOffer.Account));
|
||||
|
||||
if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer)) >= 0) {
|
||||
if (fundedSum.compareTo(Utils.getOfferTakerGets(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj)) >= 0
|
||||
) {
|
||||
// There are enough extra funds to fully fund the offer
|
||||
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer);
|
||||
const legOneTakerGets = Utils.getOfferTakerGets(legOneOffer,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
const updatedLeftover = fundedSum.subtract(legOneTakerGets);
|
||||
|
||||
this.setLegOneTakerGetsFunded(legOneOffer, legOneTakerGets);
|
||||
@@ -407,8 +466,9 @@ function setLegOneTakerGetsFunded(legOneOffer, takerGetsFunded) {
|
||||
|
||||
legOneOffer.taker_gets_funded = takerGetsFunded.to_text();
|
||||
legOneOffer.taker_pays_funded = takerGetsFunded
|
||||
.multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets))
|
||||
.to_text();
|
||||
.multiply(Utils.getOfferQuality(legOneOffer, this._currencyGets,
|
||||
this._currencyPays, this._issuerPaysObj))
|
||||
.to_text();
|
||||
|
||||
if (legOneOffer.taker_gets_funded === legOneOffer.TakerGets.value) {
|
||||
legOneOffer.is_fully_funded = true;
|
||||
@@ -428,10 +488,11 @@ function(legOneOffer, takerGets) {
|
||||
assertValidLegOneOffer(legOneOffer, 'Leg one offer is invalid');
|
||||
assert(takerGets instanceof Amount, 'Taker gets funded is invalid');
|
||||
|
||||
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets);
|
||||
const legOneQuality = Utils.getOfferQuality(legOneOffer, this._currencyGets,
|
||||
this._currencyPays, this._issuerPaysObj);
|
||||
|
||||
legOneOffer.TakerGets = takerGets.to_text();
|
||||
legOneOffer.TakerPays = takerGets.multiply(legOneQuality);
|
||||
legOneOffer.TakerPays = takerGets.multiply(legOneQuality).to_json();
|
||||
};
|
||||
|
||||
module.exports = AutobridgeCalculator;
|
||||
|
||||
@@ -1,28 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
|
||||
function normalize(digitArray) {
|
||||
while (digitArray[0] === 0) {
|
||||
digitArray.shift();
|
||||
let i = 0;
|
||||
while (digitArray[i] === 0) {
|
||||
++i;
|
||||
}
|
||||
if (i > 0) {
|
||||
digitArray.splice(0, i);
|
||||
}
|
||||
return digitArray;
|
||||
}
|
||||
|
||||
function divmod(digitArray, base, divisor) {
|
||||
var remainder = 0;
|
||||
var quotient = [];
|
||||
for (var j = 0; j < digitArray.length; j++) {
|
||||
var temp = remainder * base + parseInt(digitArray[j], 10);
|
||||
quotient.push(Math.floor(temp / divisor));
|
||||
let remainder = 0;
|
||||
let temp;
|
||||
let divided;
|
||||
let j = -1;
|
||||
|
||||
const length = digitArray.length;
|
||||
const quotient = new Array(length);
|
||||
|
||||
while (++j < length) {
|
||||
temp = remainder * base + digitArray[j];
|
||||
divided = temp / divisor;
|
||||
quotient[j] = divided << 0;
|
||||
remainder = temp % divisor;
|
||||
}
|
||||
return {quotient: normalize(quotient), remainder: remainder};
|
||||
}
|
||||
|
||||
function convertBase(digitArray, fromBase, toBase) {
|
||||
var result = [];
|
||||
var dividend = digitArray;
|
||||
const result = [];
|
||||
let dividend = digitArray;
|
||||
let qr;
|
||||
while (dividend.length > 0) {
|
||||
var qr = divmod(dividend, fromBase, toBase);
|
||||
qr = divmod(dividend, fromBase, toBase);
|
||||
result.unshift(qr.remainder);
|
||||
dividend = qr.quotient;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
/*eslint no-multi-spaces:0,space-in-brackets:0,key-spacing:0,comma-spacing:0*/
|
||||
/*eslint-disable max-len,spaced-comment,array-bracket-spacing,key-spacing*/
|
||||
/*eslint-disable no-multi-spaces,comma-spacing*/
|
||||
/*eslint-disable no-multi-spaces:0,space-in-brackets:0,key-spacing:0,comma-spacing:0*/
|
||||
|
||||
/**
|
||||
* Data type map.
|
||||
@@ -52,7 +54,8 @@ const FIELDS_MAP = exports.fields = {
|
||||
// Common types
|
||||
1: { // Int16
|
||||
1: 'LedgerEntryType',
|
||||
2: 'TransactionType'
|
||||
2: 'TransactionType',
|
||||
3: 'SignerWeight'
|
||||
},
|
||||
2: { // Int32
|
||||
2: 'Flags',
|
||||
@@ -87,7 +90,11 @@ const FIELDS_MAP = exports.fields = {
|
||||
31: 'ReserveBase',
|
||||
32: 'ReserveIncrement',
|
||||
33: 'SetFlag',
|
||||
34: 'ClearFlag'
|
||||
34: 'ClearFlag',
|
||||
35: 'SignerQuorum',
|
||||
36: 'CancelAfter',
|
||||
37: 'FinishAfter',
|
||||
38: 'SignerListID'
|
||||
},
|
||||
3: { // Int64
|
||||
1: 'IndexNext',
|
||||
@@ -116,7 +123,8 @@ const FIELDS_MAP = exports.fields = {
|
||||
17: 'InvoiceID',
|
||||
18: 'Nickname',
|
||||
19: 'Amendment',
|
||||
20: 'TicketID'
|
||||
20: 'TicketID',
|
||||
21: 'Digest'
|
||||
},
|
||||
6: { // Amount
|
||||
1: 'Amount',
|
||||
@@ -146,7 +154,8 @@ const FIELDS_MAP = exports.fields = {
|
||||
11: 'CreateCode',
|
||||
12: 'MemoType',
|
||||
13: 'MemoData',
|
||||
14: 'MemoFormat'
|
||||
14: 'MemoFormat',
|
||||
17: 'Proof'
|
||||
},
|
||||
8: { // Account
|
||||
1: 'Account',
|
||||
@@ -166,13 +175,15 @@ const FIELDS_MAP = exports.fields = {
|
||||
7: 'FinalFields',
|
||||
8: 'NewFields',
|
||||
9: 'TemplateEntry',
|
||||
10: 'Memo'
|
||||
10: 'Memo',
|
||||
11: 'SignerEntry',
|
||||
16: 'Signer'
|
||||
},
|
||||
15: { // Array
|
||||
1: undefined, // end of Array
|
||||
2: 'SigningAccounts',
|
||||
3: 'TxnSignatures',
|
||||
4: 'Signatures',
|
||||
3: 'Signers',
|
||||
4: 'SignerEntries',
|
||||
5: 'Template',
|
||||
6: 'Necessary',
|
||||
7: 'Sufficient',
|
||||
@@ -183,7 +194,7 @@ const FIELDS_MAP = exports.fields = {
|
||||
// Uncommon types
|
||||
16: { // Int8
|
||||
1: 'CloseResolution',
|
||||
2: 'TemplateEntryType',
|
||||
2: 'Method',
|
||||
3: 'TransactionResult'
|
||||
},
|
||||
17: { // Hash160
|
||||
@@ -202,17 +213,17 @@ const FIELDS_MAP = exports.fields = {
|
||||
}
|
||||
};
|
||||
|
||||
let INVERSE_FIELDS_MAP = exports.fieldsInverseMap = { };
|
||||
const INVERSE_FIELDS_MAP = exports.fieldsInverseMap = { };
|
||||
|
||||
Object.keys(FIELDS_MAP).forEach(function(k1) {
|
||||
Object.keys(FIELDS_MAP[k1]).forEach(function(k2) {
|
||||
INVERSE_FIELDS_MAP[FIELDS_MAP[k1][k2]] = [ Number(k1), Number(k2) ];
|
||||
INVERSE_FIELDS_MAP[FIELDS_MAP[k1][k2]] = [Number(k1), Number(k2)];
|
||||
});
|
||||
});
|
||||
|
||||
const REQUIRED = exports.REQUIRED = 0,
|
||||
OPTIONAL = exports.OPTIONAL = 1,
|
||||
DEFAULT = exports.DEFAULT = 2;
|
||||
const REQUIRED = exports.REQUIRED = 0;
|
||||
const OPTIONAL = exports.OPTIONAL = 1;
|
||||
const DEFAULT = exports.DEFAULT = 2;
|
||||
|
||||
const base = [
|
||||
[ 'TransactionType' , REQUIRED ],
|
||||
@@ -226,76 +237,100 @@ const base = [
|
||||
[ 'SigningPubKey' , REQUIRED ],
|
||||
[ 'TxnSignature' , OPTIONAL ],
|
||||
[ 'AccountTxnID' , OPTIONAL ],
|
||||
[ 'Memos' , OPTIONAL ]
|
||||
[ 'Memos' , OPTIONAL ],
|
||||
[ 'Signers' , OPTIONAL ]
|
||||
];
|
||||
|
||||
exports.tx = {
|
||||
AccountSet: [3].concat(base, [
|
||||
[ 'EmailHash' , OPTIONAL ],
|
||||
[ 'WalletLocator' , OPTIONAL ],
|
||||
[ 'WalletSize' , OPTIONAL ],
|
||||
[ 'MessageKey' , OPTIONAL ],
|
||||
[ 'Domain' , OPTIONAL ],
|
||||
[ 'TransferRate' , OPTIONAL ],
|
||||
[ 'SetFlag' , OPTIONAL ],
|
||||
[ 'ClearFlag' , OPTIONAL ]
|
||||
['EmailHash' , OPTIONAL],
|
||||
['WalletLocator' , OPTIONAL],
|
||||
['WalletSize' , OPTIONAL],
|
||||
['MessageKey' , OPTIONAL],
|
||||
['Domain' , OPTIONAL],
|
||||
['TransferRate' , OPTIONAL],
|
||||
['SetFlag' , OPTIONAL],
|
||||
['ClearFlag' , OPTIONAL]
|
||||
]),
|
||||
TrustSet: [20].concat(base, [
|
||||
[ 'LimitAmount' , OPTIONAL ],
|
||||
[ 'QualityIn' , OPTIONAL ],
|
||||
[ 'QualityOut' , OPTIONAL ]
|
||||
['LimitAmount' , OPTIONAL],
|
||||
['QualityIn' , OPTIONAL],
|
||||
['QualityOut' , OPTIONAL]
|
||||
]),
|
||||
OfferCreate: [7].concat(base, [
|
||||
[ 'TakerPays' , REQUIRED ],
|
||||
[ 'TakerGets' , REQUIRED ],
|
||||
[ 'Expiration' , OPTIONAL ],
|
||||
[ 'OfferSequence' , OPTIONAL ]
|
||||
['TakerPays' , REQUIRED],
|
||||
['TakerGets' , REQUIRED],
|
||||
['Expiration' , OPTIONAL],
|
||||
['OfferSequence' , OPTIONAL]
|
||||
]),
|
||||
OfferCancel: [8].concat(base, [
|
||||
[ 'OfferSequence' , REQUIRED ]
|
||||
['OfferSequence' , REQUIRED]
|
||||
]),
|
||||
SetRegularKey: [5].concat(base, [
|
||||
[ 'RegularKey' , OPTIONAL ]
|
||||
['RegularKey' , OPTIONAL]
|
||||
]),
|
||||
Payment: [0].concat(base, [
|
||||
[ 'Destination' , REQUIRED ],
|
||||
[ 'Amount' , REQUIRED ],
|
||||
[ 'SendMax' , OPTIONAL ],
|
||||
[ 'Paths' , DEFAULT ],
|
||||
[ 'InvoiceID' , OPTIONAL ],
|
||||
[ 'DestinationTag' , OPTIONAL ]
|
||||
['Destination' , REQUIRED],
|
||||
['Amount' , REQUIRED],
|
||||
['SendMax' , OPTIONAL],
|
||||
['Paths' , DEFAULT],
|
||||
['InvoiceID' , OPTIONAL],
|
||||
['DestinationTag' , OPTIONAL]
|
||||
]),
|
||||
Contract: [9].concat(base, [
|
||||
[ 'Expiration' , REQUIRED ],
|
||||
[ 'BondAmount' , REQUIRED ],
|
||||
[ 'StampEscrow' , REQUIRED ],
|
||||
[ 'RippleEscrow' , REQUIRED ],
|
||||
[ 'CreateCode' , OPTIONAL ],
|
||||
[ 'FundCode' , OPTIONAL ],
|
||||
[ 'RemoveCode' , OPTIONAL ],
|
||||
[ 'ExpireCode' , OPTIONAL ]
|
||||
['Expiration' , REQUIRED],
|
||||
['BondAmount' , REQUIRED],
|
||||
['StampEscrow' , REQUIRED],
|
||||
['RippleEscrow' , REQUIRED],
|
||||
['CreateCode' , OPTIONAL],
|
||||
['FundCode' , OPTIONAL],
|
||||
['RemoveCode' , OPTIONAL],
|
||||
['ExpireCode' , OPTIONAL]
|
||||
]),
|
||||
RemoveContract: [10].concat(base, [
|
||||
[ 'Target' , REQUIRED ]
|
||||
['Target' , REQUIRED]
|
||||
]),
|
||||
EnableFeature: [100].concat(base, [
|
||||
[ 'Feature' , REQUIRED ]
|
||||
['Feature' , REQUIRED]
|
||||
]),
|
||||
EnableAmendment: [100].concat(base, [
|
||||
[ 'Amendment' , REQUIRED ]
|
||||
['Amendment' , REQUIRED]
|
||||
]),
|
||||
SetFee: [101].concat(base, [
|
||||
[ 'BaseFee' , REQUIRED ],
|
||||
[ 'ReferenceFeeUnits' , REQUIRED ],
|
||||
[ 'ReserveBase' , REQUIRED ],
|
||||
[ 'ReserveIncrement' , REQUIRED ]
|
||||
['BaseFee' , REQUIRED],
|
||||
['ReferenceFeeUnits' , REQUIRED],
|
||||
['ReserveBase' , REQUIRED],
|
||||
['ReserveIncrement' , REQUIRED]
|
||||
]),
|
||||
TicketCreate: [10].concat(base, [
|
||||
[ 'Target' , OPTIONAL ],
|
||||
[ 'Expiration' , OPTIONAL ]
|
||||
['Target' , OPTIONAL],
|
||||
['Expiration' , OPTIONAL]
|
||||
]),
|
||||
TicketCancel: [11].concat(base, [
|
||||
[ 'TicketID' , REQUIRED ]
|
||||
['TicketID' , REQUIRED]
|
||||
]),
|
||||
SignerListSet: [12].concat(base, [
|
||||
['SignerQuorum', REQUIRED],
|
||||
['SignerEntries', OPTIONAL]
|
||||
]),
|
||||
SuspendedPaymentCreate: [1].concat(base, [
|
||||
['Destination' , REQUIRED],
|
||||
['Amount' , REQUIRED],
|
||||
['Digest' , OPTIONAL],
|
||||
['CancelAfter' , OPTIONAL],
|
||||
['FinishAfter' , OPTIONAL],
|
||||
['DestinationTag' , OPTIONAL]
|
||||
]),
|
||||
SuspendedPaymentFinish: [2].concat(base, [
|
||||
['Owner' , REQUIRED],
|
||||
['OfferSequence' , REQUIRED],
|
||||
['Method' , OPTIONAL],
|
||||
['Digest' , OPTIONAL],
|
||||
['Proof' , OPTIONAL]
|
||||
]),
|
||||
SuspendedPaymentCancel: [4].concat(base, [
|
||||
['Owner' , REQUIRED],
|
||||
['OfferSequence' , REQUIRED]
|
||||
])
|
||||
};
|
||||
|
||||
@@ -396,14 +431,22 @@ exports.ledger = {
|
||||
['LedgerIndex', OPTIONAL],
|
||||
['Balance', REQUIRED],
|
||||
['LowLimit', REQUIRED],
|
||||
['HighLimit', REQUIRED]])
|
||||
['HighLimit', REQUIRED]]),
|
||||
SignerList: [83].concat(sleBase,[
|
||||
['OwnerNode', REQUIRED],
|
||||
['SignerQuorum', REQUIRED],
|
||||
['SignerEntries', REQUIRED],
|
||||
['SignerListID', REQUIRED],
|
||||
['PreviousTxnID', REQUIRED],
|
||||
['PreviousTxnLgrSeq', REQUIRED]
|
||||
])
|
||||
};
|
||||
|
||||
exports.metadata = [
|
||||
[ 'DeliveredAmount' , OPTIONAL ],
|
||||
[ 'TransactionIndex' , REQUIRED ],
|
||||
[ 'TransactionResult' , REQUIRED ],
|
||||
[ 'AffectedNodes' , REQUIRED ]
|
||||
['DeliveredAmount' , OPTIONAL],
|
||||
['TransactionIndex' , REQUIRED],
|
||||
['TransactionResult' , REQUIRED],
|
||||
['AffectedNodes' , REQUIRED]
|
||||
];
|
||||
|
||||
exports.ter = {
|
||||
@@ -423,7 +466,7 @@ exports.ter = {
|
||||
tecNO_LINE_REDUNDANT : 127,
|
||||
tecPATH_DRY : 128,
|
||||
tecUNFUNDED : 129, // Deprecated, old ambiguous unfunded.
|
||||
tecMASTER_DISABLED : 130,
|
||||
tecNO_ALTERNATIVE_KEY : 130,
|
||||
tecNO_REGULAR_KEY : 131,
|
||||
tecOWNERS : 132,
|
||||
tecNO_ISSUER : 133,
|
||||
@@ -437,5 +480,6 @@ exports.ter = {
|
||||
tecINSUFFICIENT_RESERVE : 141,
|
||||
tecNEED_MASTER_KEY : 142,
|
||||
tecDST_TAG_NEEDED : 143,
|
||||
tecINTERNAL : 144
|
||||
tecINTERNAL : 144,
|
||||
tecOVERSIZE : 145
|
||||
};
|
||||
|
||||
6
src/core/constants.js
Normal file
6
src/core/constants.js
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
ACCOUNT_ZERO: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
|
||||
ACCOUNT_ONE: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
};
|
||||
@@ -1,15 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
var extend = require('extend');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var utils = require('./utils');
|
||||
var Float = require('./ieee754').Float;
|
||||
const extend = require('extend');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const utils = require('./utils');
|
||||
const Float = require('./ieee754').Float;
|
||||
|
||||
//
|
||||
// Currency support
|
||||
//
|
||||
|
||||
var Currency = extend(function() {
|
||||
const Currency = extend(function() {
|
||||
// Internal form: 0 = XRP. 3 letter-code.
|
||||
// XXX Internal should be 0 or hex with three letter annotation when valid.
|
||||
|
||||
@@ -22,7 +22,7 @@ var Currency = extend(function() {
|
||||
this._update();
|
||||
}, UInt160);
|
||||
|
||||
Currency.prototype = extend({}, UInt160.prototype);
|
||||
Currency.prototype = Object.create(extend({}, UInt160.prototype));
|
||||
Currency.prototype.constructor = Currency;
|
||||
|
||||
Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
|
||||
@@ -61,12 +61,12 @@ Currency.HEX_CURRENCY_BAD = '0000000000000000000000005852500000000000';
|
||||
*
|
||||
*/
|
||||
|
||||
/*eslint-disable max-len*/
|
||||
/* eslint-disable max-len*/
|
||||
Currency.prototype.human_RE = /^\s*([a-zA-Z0-9\<\>\(\)\{\}\[\]\|\?\!\@\#\$\%\^\&]{3})(\s*-\s*[- \w]+)?(\s*\(-?\d+\.?\d*%pa\))?\s*$/;
|
||||
/*eslint-enable max-len*/
|
||||
/* eslint-enable max-len*/
|
||||
|
||||
Currency.from_json = function(j, shouldInterpretXrpAsIou) {
|
||||
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
|
||||
return (new Currency()).parse_json(j, shouldInterpretXrpAsIou);
|
||||
};
|
||||
|
||||
Currency.from_human = function(j, opts) {
|
||||
@@ -78,7 +78,7 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
this._value = NaN;
|
||||
|
||||
if (j instanceof Currency) {
|
||||
this._value = j.copyTo({})._value;
|
||||
this._value = j._value;
|
||||
this._update();
|
||||
return this;
|
||||
}
|
||||
@@ -111,10 +111,10 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
}
|
||||
|
||||
// match the given string to see if it's in an allowed format
|
||||
var matches = j.match(this.human_RE);
|
||||
const matches = j.match(this.human_RE);
|
||||
|
||||
if (matches) {
|
||||
var currencyCode = matches[1];
|
||||
let currencyCode = matches[1];
|
||||
|
||||
// for the currency 'XRP' case
|
||||
// we drop everything else that could have been provided
|
||||
@@ -131,14 +131,14 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
// the full currency is matched as it is part of the valid currency
|
||||
// format, but not stored
|
||||
// var full_currency = matches[2] || '';
|
||||
var interest = matches[3] || '';
|
||||
const interest = matches[3] || '';
|
||||
|
||||
// interest is defined as interest per year, per annum (pa)
|
||||
var percentage = interest.match(/(-?\d+\.?\d+)/);
|
||||
let percentage = interest.match(/(-?\d+\.?\d+)/);
|
||||
|
||||
currencyCode = currencyCode.toUpperCase();
|
||||
|
||||
var currencyData = utils.arraySet(20, 0);
|
||||
const currencyData = utils.arraySet(20, 0);
|
||||
|
||||
if (percentage) {
|
||||
/*
|
||||
@@ -164,15 +164,15 @@ Currency.prototype.parse_json = function(j, shouldInterpretXrpAsIou) {
|
||||
|
||||
// the interest or demurrage is expressed as a yearly (per annum)
|
||||
// value
|
||||
var secondsPerYear = 31536000; // 60 * 60 * 24 * 365
|
||||
const secondsPerYear = 31536000; // 60 * 60 * 24 * 365
|
||||
|
||||
// Calculating the interest e-fold
|
||||
// 0.5% demurrage is expressed 0.995, 0.005 less than 1
|
||||
// 0.5% interest is expressed as 1.005, 0.005 more than 1
|
||||
var interestEfold = secondsPerYear / Math.log(1 + percentage / 100);
|
||||
var bytes = Float.toIEEE754Double(interestEfold);
|
||||
const interestEfold = secondsPerYear / Math.log(1 + percentage / 100);
|
||||
const bytes = Float.toIEEE754Double(interestEfold);
|
||||
|
||||
for (var i = 0; i <= bytes.length; i++) {
|
||||
for (let i = 0; i <= bytes.length; i++) {
|
||||
currencyData[8 + i] = bytes[i] & 0xff;
|
||||
}
|
||||
|
||||
@@ -204,10 +204,10 @@ Currency.prototype.parse_human = function(j) {
|
||||
*/
|
||||
|
||||
Currency.prototype._update = function() {
|
||||
var bytes = this.to_bytes();
|
||||
const bytes = this.to_bytes();
|
||||
|
||||
// is it 0 everywhere except 12, 13, 14?
|
||||
var isZeroExceptInStandardPositions = true;
|
||||
let isZeroExceptInStandardPositions = true;
|
||||
|
||||
if (!bytes) {
|
||||
return;
|
||||
@@ -219,7 +219,7 @@ Currency.prototype._update = function() {
|
||||
this._interest_period = NaN;
|
||||
this._iso_code = '';
|
||||
|
||||
for (var i = 0; i < 20; i++) {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
isZeroExceptInStandardPositions = isZeroExceptInStandardPositions
|
||||
&& (i === 12 || i === 13 || i === 14 || bytes[i] === 0);
|
||||
}
|
||||
@@ -249,6 +249,34 @@ Currency.prototype._update = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns copy.
|
||||
*
|
||||
* This copies code from UInt.copyTo so we do not call _update,
|
||||
* bvecause to_bytes is very expensive.
|
||||
*/
|
||||
|
||||
Currency.prototype.copyTo = function(d) {
|
||||
d._value = this._value;
|
||||
|
||||
if (this._version_byte !== undefined) {
|
||||
d._version_byte = this._version_byte;
|
||||
}
|
||||
|
||||
if (!d.is_valid()) {
|
||||
return d;
|
||||
}
|
||||
|
||||
d._native = this._native;
|
||||
d._type = this._type;
|
||||
d._interest_start = this._interest_start;
|
||||
d._interest_period = this._interest_period;
|
||||
d._iso_code = this._iso_code;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
// XXX Probably not needed anymore?
|
||||
/*
|
||||
Currency.prototype.parse_bytes = function(byte_array) {
|
||||
@@ -300,18 +328,20 @@ Currency.prototype.has_interest = function() {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} referenceDate number of seconds since the Ripple Epoch
|
||||
* @param {number} referenceDate_ number of seconds since the Ripple Epoch
|
||||
* (0:00 on January 1, 2000 UTC) used to calculate the
|
||||
* interest over provided interval pass in one years
|
||||
* worth of seconds to ge the yearly interest
|
||||
* @returns {number} interest for provided interval, can be negative for
|
||||
* demurred currencies
|
||||
*/
|
||||
Currency.prototype.get_interest_at = function(referenceDate) {
|
||||
Currency.prototype.get_interest_at = function(referenceDate_) {
|
||||
if (!this.has_interest()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let referenceDate = referenceDate_;
|
||||
|
||||
// use one year as a default period
|
||||
if (!referenceDate) {
|
||||
referenceDate = this._interest_start + 3600 * 24 * 365;
|
||||
@@ -326,13 +356,14 @@ Currency.prototype.get_interest_at = function(referenceDate) {
|
||||
/ this._interest_period);
|
||||
};
|
||||
|
||||
Currency.prototype.get_interest_percentage_at
|
||||
= function(referenceDate, decimals) {
|
||||
var interest = this.get_interest_at(referenceDate, decimals);
|
||||
Currency.prototype.get_interest_percentage_at = function(referenceDate,
|
||||
decimals
|
||||
) {
|
||||
let interest = this.get_interest_at(referenceDate, decimals);
|
||||
|
||||
// convert to percentage
|
||||
interest = (interest * 100) - 100;
|
||||
var decimalMultiplier = decimals ? Math.pow(10, decimals) : 100;
|
||||
const decimalMultiplier = decimals ? Math.pow(10, decimals) : 100;
|
||||
|
||||
// round to two decimals behind the dot
|
||||
return Math.round(interest * decimalMultiplier) / decimalMultiplier;
|
||||
@@ -347,18 +378,14 @@ Currency.prototype.get_interest_percentage_at
|
||||
// return UInt.prototype.is_valid() && ...;
|
||||
// };
|
||||
|
||||
Currency.prototype.to_json = function(opts) {
|
||||
Currency.prototype.to_json = function(opts = {}) {
|
||||
if (!this.is_valid()) {
|
||||
// XXX This is backwards compatible behavior, but probably not very good.
|
||||
return 'XRP';
|
||||
}
|
||||
|
||||
if (!opts) {
|
||||
opts = {};
|
||||
}
|
||||
|
||||
var currency;
|
||||
var fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
|
||||
let currency;
|
||||
const fullName = opts && opts.full_name ? ' - ' + opts.full_name : '';
|
||||
opts.show_interest = opts.show_interest !== undefined
|
||||
? opts.show_interest
|
||||
: this.has_interest();
|
||||
@@ -366,8 +393,8 @@ Currency.prototype.to_json = function(opts) {
|
||||
if (!opts.force_hex && /^[A-Z0-9]{3}$/.test(this._iso_code)) {
|
||||
currency = this._iso_code + fullName;
|
||||
if (opts.show_interest) {
|
||||
var decimals = !isNaN(opts.decimals) ? opts.decimals : undefined;
|
||||
var interestPercentage = this.has_interest()
|
||||
const decimals = !isNaN(opts.decimals) ? opts.decimals : undefined;
|
||||
const interestPercentage = this.has_interest()
|
||||
? this.get_interest_percentage_at(
|
||||
this._interest_start + 3600 * 24 * 365, decimals
|
||||
)
|
||||
|
||||
@@ -30,6 +30,8 @@ exports.HASH_LEAF_NODE = 0x4D4C4E00; // 'MLN'
|
||||
exports.HASH_TX_SIGN = 0x53545800; // 'STX'
|
||||
// inner transaction to sign (TESTNET)
|
||||
exports.HASH_TX_SIGN_TESTNET = 0x73747800; // 'stx'
|
||||
// inner transaction to multisign
|
||||
exports.HASH_TX_MULTISIGN = 0x534D5400; // 'SMT'
|
||||
|
||||
Object.keys(exports).forEach(function(k) {
|
||||
exports[k + '_BYTES'] = toBytes(exports[k]);
|
||||
|
||||
@@ -9,7 +9,6 @@ exports.Base = require('./base').Base;
|
||||
exports.UInt128 = require('./uint128').UInt128;
|
||||
exports.UInt160 = require('./uint160').UInt160;
|
||||
exports.UInt256 = require('./uint256').UInt256;
|
||||
exports.Seed = require('./seed').Seed;
|
||||
exports.Meta = require('./meta').Meta;
|
||||
exports.SerializedObject = require('./serializedobject').SerializedObject;
|
||||
exports.RippleError = require('./rippleerror').RippleError;
|
||||
@@ -24,10 +23,8 @@ exports._test = {
|
||||
Log: require('./log'),
|
||||
PathFind: require('./pathfind').PathFind,
|
||||
TransactionManager: require('./transactionmanager').TransactionManager,
|
||||
RangeSet: require('./rangeset').RangeSet
|
||||
RangeSet: require('./rangeset').RangeSet,
|
||||
HashPrefixes: require('./hashprefixes')
|
||||
};
|
||||
|
||||
exports.types = require('./serializedtypes');
|
||||
|
||||
// This patches remote with legacy support for positional arguments
|
||||
require('./legacy-support.js')(exports);
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Value = require('./value').Value;
|
||||
const XRPValue = require('./xrpvalue').XRPValue;
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
const BigNumber = GlobalBigNumber.another({
|
||||
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
|
||||
DECIMAL_PLACES: 40
|
||||
});
|
||||
const rippleUnits = new BigNumber(1e6);
|
||||
|
||||
class IOUValue extends Value {
|
||||
|
||||
constructor(value: string | BigNumber, roundingMode: ?number = null,
|
||||
base: ?number = null) {
|
||||
|
||||
super(new BigNumber(value, base).toDigits(16, roundingMode));
|
||||
}
|
||||
|
||||
multiply(multiplicand: Value) {
|
||||
if (multiplicand instanceof XRPValue) {
|
||||
return super.multiply(
|
||||
new IOUValue(
|
||||
multiplicand._value.times(rippleUnits)));
|
||||
}
|
||||
return super.multiply(multiplicand);
|
||||
}
|
||||
|
||||
divide(divisor: Value) {
|
||||
if (divisor instanceof XRPValue) {
|
||||
return super.divide(
|
||||
new IOUValue(divisor._value.times(rippleUnits)));
|
||||
}
|
||||
return super.divide(divisor);
|
||||
}
|
||||
|
||||
negate() {
|
||||
return new IOUValue(this._value.neg());
|
||||
}
|
||||
|
||||
_canonicalize(value) {
|
||||
if (value.isNaN()) {
|
||||
throw new Error('Invalid result');
|
||||
}
|
||||
return new IOUValue(value.toPrecision(16));
|
||||
}
|
||||
|
||||
equals(comparator) {
|
||||
return (comparator instanceof IOUValue)
|
||||
&& this._value.equals(comparator._value);
|
||||
}
|
||||
}
|
||||
|
||||
exports.IOUValue = IOUValue;
|
||||
@@ -1,201 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const log = require('./log');
|
||||
|
||||
function wrapRemote(Remote) {
|
||||
|
||||
/**
|
||||
* Create options object from positional function arguments
|
||||
*
|
||||
* @param {Array} params function parameters
|
||||
* @param {Array} args function arguments
|
||||
* @return {Object} keyed options
|
||||
*/
|
||||
|
||||
function makeOptions(command, params, args) {
|
||||
const result = {};
|
||||
|
||||
log.warn(
|
||||
'DEPRECATED: First argument to ' + command
|
||||
+ ' request constructor must be an object containing'
|
||||
+ ' request properties: '
|
||||
+ params.join(', ')
|
||||
);
|
||||
|
||||
if (_.isFunction(_.last(args))) {
|
||||
result.callback = args.pop();
|
||||
}
|
||||
|
||||
return _.merge(result, _.zipObject(params, args));
|
||||
}
|
||||
|
||||
function shiftFront(list, e) {
|
||||
let ix = 0;
|
||||
while (list[0] === e) {
|
||||
list.shift();
|
||||
ix++;
|
||||
}
|
||||
return ix;
|
||||
}
|
||||
|
||||
function addBackwardsCompatibility(compatParams) {
|
||||
const {method,
|
||||
command,
|
||||
staticMethod = false,
|
||||
positionals = [],
|
||||
mappings = {},
|
||||
hasCallback = true,
|
||||
aliases = []} = compatParams;
|
||||
|
||||
const positionalsStartIx = shiftFront(positionals, '*');
|
||||
const needsWrapping = positionals.length ||
|
||||
Object.keys(mappings).length;
|
||||
|
||||
function wrapFunction(func) {
|
||||
return function() {
|
||||
const optionsArg = arguments[positionalsStartIx];
|
||||
const options = {};
|
||||
|
||||
if (hasCallback) {
|
||||
options.callback = arguments[positionalsStartIx + 1];
|
||||
}
|
||||
|
||||
if (_.isPlainObject(optionsArg)) {
|
||||
const mapped = _.transform(optionsArg, (result, v, k) => {
|
||||
const to = mappings[k];
|
||||
result[to !== undefined ? to : k] = v;
|
||||
});
|
||||
_.merge(options, mapped);
|
||||
} else {
|
||||
// This hack handles accountRequest type helper helpers
|
||||
const commandName = positionalsStartIx ? arguments[0] : command;
|
||||
const args = _.slice(arguments, positionalsStartIx);
|
||||
const positionalOptions = makeOptions(commandName, positionals, args);
|
||||
_.merge(options, positionalOptions);
|
||||
}
|
||||
// Only some `positionals` get remapped to options
|
||||
const alwaysPositional = _.slice(arguments, 0, positionalsStartIx);
|
||||
const args = alwaysPositional.concat([options, options.callback]);
|
||||
return func.apply(this, args);
|
||||
};
|
||||
}
|
||||
|
||||
const obj = staticMethod ? Remote : Remote.prototype;
|
||||
// Wrap the function and set the aliases
|
||||
const wrapped = needsWrapping ? wrapFunction(obj[method]) : obj[method];
|
||||
aliases.concat(method).forEach((name) => {
|
||||
obj[name] = wrapped;
|
||||
});
|
||||
}
|
||||
|
||||
const remoteMethods = [
|
||||
{
|
||||
method: 'requestPathFindCreate',
|
||||
command: 'path_find',
|
||||
positionals: ['source_account',
|
||||
'destination_account',
|
||||
'destination_amount',
|
||||
'source_currencies'],
|
||||
mappings: {
|
||||
src_currencies: 'source_currencies',
|
||||
src_account: 'source_account',
|
||||
dst_amount: 'destination_amount',
|
||||
dst_account: 'destination_account'
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'requestRipplePathFind',
|
||||
command: 'ripple_path_find',
|
||||
positionals: ['source_account',
|
||||
'destination_account',
|
||||
'destination_amount',
|
||||
'source_currencies'],
|
||||
mappings: {
|
||||
src_currencies: 'source_currencies',
|
||||
src_account: 'source_account',
|
||||
dst_amount: 'destination_amount',
|
||||
dst_account: 'destination_account'
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'createPathFind',
|
||||
aliases: ['pathFind'],
|
||||
command: 'pathfind',
|
||||
positionals: ['src_account',
|
||||
'dst_account',
|
||||
'dst_amount',
|
||||
'src_currencies']
|
||||
},
|
||||
{
|
||||
method: 'requestTransactionEntry',
|
||||
command: 'transaction_entry',
|
||||
positionals: ['hash', 'ledger'],
|
||||
mappings: {ledger_index: 'ledger', ledger_hash: 'ledger'}
|
||||
},
|
||||
{
|
||||
method: 'requestTransaction',
|
||||
command: 'tx',
|
||||
positionals: ['hash', 'ledger'],
|
||||
mappings: {ledger_index: 'ledger', ledger_hash: 'ledger'},
|
||||
aliases: ['requestTx']
|
||||
},
|
||||
{
|
||||
method: 'requestBookOffers',
|
||||
command: 'book_offers',
|
||||
positionals: ['gets', 'pays', 'taker', 'ledger', 'limit'],
|
||||
mappings: {taker_pays: 'pays', taker_gets: 'gets'}
|
||||
},
|
||||
{
|
||||
method: 'createOrderBook',
|
||||
hasCallback: false,
|
||||
command: 'orderbook',
|
||||
positionals: ['currency_gets', 'issuer_gets',
|
||||
'currency_pays', 'issuer_pays']
|
||||
},
|
||||
{
|
||||
method: 'requestTransactionHistory',
|
||||
command: 'tx_history',
|
||||
positionals: ['start'],
|
||||
aliases: ['requestTxHistory']
|
||||
},
|
||||
{
|
||||
method: 'requestWalletAccounts',
|
||||
command: 'wallet_accounts',
|
||||
positionals: ['seed']
|
||||
},
|
||||
{
|
||||
method: 'requestSign',
|
||||
command: 'sign',
|
||||
positionals: ['secret', 'tx_json']
|
||||
},
|
||||
{
|
||||
method: 'accountSeqCache',
|
||||
command: 'accountseqcache',
|
||||
positionals: ['account', 'ledger']
|
||||
},
|
||||
{
|
||||
method: 'requestRippleBalance',
|
||||
command: 'ripplebalance',
|
||||
positionals: ['account', 'issuer', 'currency', 'ledger']
|
||||
},
|
||||
{
|
||||
staticMethod: true,
|
||||
method: 'accountRequest',
|
||||
command: 'accountrequest(*)',
|
||||
positionals: ['*', 'account', 'ledger', 'peer', 'limit', 'marker']
|
||||
},
|
||||
{
|
||||
staticMethod: true,
|
||||
method: 'accountRootRequest',
|
||||
command: 'accountRootRequest(*)',
|
||||
positionals: ['*', '*', 'account', 'ledger']
|
||||
}
|
||||
];
|
||||
|
||||
remoteMethods.forEach(addBackwardsCompatibility);
|
||||
}
|
||||
|
||||
module.exports = function wrapAPI(index) {
|
||||
wrapRemote(index.Remote);
|
||||
};
|
||||
107
src/core/meta.js
107
src/core/meta.js
@@ -1,7 +1,10 @@
|
||||
var extend = require('extend');
|
||||
var utils = require('./utils');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Amount = require('./amount').Amount;
|
||||
'use strict';
|
||||
const extend = require('extend');
|
||||
const utils = require('./utils');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Amount = require('./amount').Amount;
|
||||
const ACCOUNT_ZERO = require('./constants').ACCOUNT_ZERO;
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
|
||||
/**
|
||||
* Meta data processing facility
|
||||
@@ -11,8 +14,6 @@ var Amount = require('./amount').Amount;
|
||||
*/
|
||||
|
||||
function Meta(data) {
|
||||
var self = this;
|
||||
|
||||
this.nodes = [ ];
|
||||
|
||||
if (typeof data !== 'object') {
|
||||
@@ -24,7 +25,7 @@ function Meta(data) {
|
||||
}
|
||||
|
||||
data.AffectedNodes.forEach(this.addNode, this);
|
||||
};
|
||||
}
|
||||
|
||||
Meta.NODE_TYPES = [
|
||||
'CreatedNode',
|
||||
@@ -53,10 +54,10 @@ Meta.ACCOUNT_FIELDS = [
|
||||
*/
|
||||
|
||||
Meta.prototype.getNodeType = function(node) {
|
||||
var result = null;
|
||||
let result = null;
|
||||
|
||||
for (var i=0; i<Meta.NODE_TYPES.length; i++) {
|
||||
var type = Meta.NODE_TYPES[i];
|
||||
for (let i = 0; i < Meta.NODE_TYPES.length; i++) {
|
||||
const type = Meta.NODE_TYPES[i];
|
||||
if (node.hasOwnProperty(type)) {
|
||||
result = type;
|
||||
break;
|
||||
@@ -83,20 +84,22 @@ Meta.prototype.isAccountField = function(field) {
|
||||
*/
|
||||
|
||||
Meta.prototype.addNode = function(node) {
|
||||
this._affectedAccounts = void(0);
|
||||
this._affectedBooks = void(0);
|
||||
this._affectedAccounts = undefined;
|
||||
this._affectedBooks = undefined;
|
||||
|
||||
var result = { };
|
||||
const result = { };
|
||||
|
||||
if ((result.nodeType = this.getNodeType(node))) {
|
||||
node = node[result.nodeType];
|
||||
result.diffType = result.nodeType;
|
||||
result.entryType = node.LedgerEntryType;
|
||||
result.ledgerIndex = node.LedgerIndex;
|
||||
result.fields = extend({ }, node.PreviousFields, node.NewFields, node.FinalFields);
|
||||
result.fieldsPrev = node.PreviousFields || { };
|
||||
result.fieldsNew = node.NewFields || { };
|
||||
result.fieldsFinal = node.FinalFields || { };
|
||||
result.nodeType = this.getNodeType(node);
|
||||
if (result.nodeType) {
|
||||
const _node = node[result.nodeType];
|
||||
result.diffType = result.nodeType;
|
||||
result.entryType = _node.LedgerEntryType;
|
||||
result.ledgerIndex = _node.LedgerIndex;
|
||||
result.fields = extend({ }, _node.PreviousFields,
|
||||
_node.NewFields, _node.FinalFields);
|
||||
result.fieldsPrev = _node.PreviousFields || { };
|
||||
result.fieldsNew = _node.NewFields || { };
|
||||
result.fieldsFinal = _node.FinalFields || { };
|
||||
|
||||
// getAffectedBooks will set this
|
||||
// result.bookKey = undefined;
|
||||
@@ -126,36 +129,36 @@ Meta.prototype.getNodes = function(options) {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
return this.nodes;
|
||||
}
|
||||
return this.nodes;
|
||||
};
|
||||
|
||||
Meta.prototype.getAffectedAccounts = function(from) {
|
||||
Meta.prototype.getAffectedAccounts = function() {
|
||||
if (this._affectedAccounts) {
|
||||
return this._affectedAccounts;
|
||||
}
|
||||
|
||||
var accounts = [ ];
|
||||
const accounts = [ ];
|
||||
|
||||
// This code should match the behavior of the C++ method:
|
||||
// TransactionMetaSet::getAffectedAccounts
|
||||
for (var i=0; i<this.nodes.length; i++) {
|
||||
var node = this.nodes[i];
|
||||
var fields = (node.nodeType === 'CreatedNode')
|
||||
for (let i = 0; i < this.nodes.length; i++) {
|
||||
const node = this.nodes[i];
|
||||
const fields = (node.nodeType === 'CreatedNode')
|
||||
? node.fieldsNew
|
||||
: node.fieldsFinal;
|
||||
|
||||
for (var fieldName in fields) {
|
||||
var field = fields[fieldName];
|
||||
for (const fieldName in fields) {
|
||||
const field = fields[fieldName];
|
||||
|
||||
if (this.isAccountField(fieldName) && UInt160.is_valid(field)) {
|
||||
accounts.push(field);
|
||||
} else if (~Meta.AMOUNT_FIELDS_AFFECTING_ISSUER.indexOf(fieldName)) {
|
||||
var amount = Amount.from_json(field);
|
||||
var issuer = amount.issuer();
|
||||
if (issuer.is_valid() && !issuer.is_zero()) {
|
||||
accounts.push(issuer.to_json());
|
||||
} else if (
|
||||
Meta.AMOUNT_FIELDS_AFFECTING_ISSUER.indexOf(fieldName) !== -1) {
|
||||
const amount = Amount.from_json(field);
|
||||
const issuer = amount.issuer();
|
||||
if (isValidAddress(issuer) && issuer !== ACCOUNT_ZERO) {
|
||||
accounts.push(issuer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,29 +174,29 @@ Meta.prototype.getAffectedBooks = function() {
|
||||
return this._affectedBooks;
|
||||
}
|
||||
|
||||
var books = [ ];
|
||||
const books = [ ];
|
||||
|
||||
for (var i=0; i<this.nodes.length; i++) {
|
||||
var node = this.nodes[i];
|
||||
for (let i = 0; i < this.nodes.length; i++) {
|
||||
const node = this.nodes[i];
|
||||
|
||||
if (node.entryType !== 'Offer') {
|
||||
continue;
|
||||
}
|
||||
|
||||
var gets = Amount.from_json(node.fields.TakerGets);
|
||||
var pays = Amount.from_json(node.fields.TakerPays);
|
||||
var getsKey = gets.currency().to_json();
|
||||
var paysKey = pays.currency().to_json();
|
||||
const gets = Amount.from_json(node.fields.TakerGets);
|
||||
const pays = Amount.from_json(node.fields.TakerPays);
|
||||
let getsKey = gets.currency().to_json();
|
||||
let paysKey = pays.currency().to_json();
|
||||
|
||||
if (getsKey !== 'XRP') {
|
||||
getsKey += '/' + gets.issuer().to_json();
|
||||
getsKey += '/' + gets.issuer();
|
||||
}
|
||||
|
||||
if (paysKey !== 'XRP') {
|
||||
paysKey += '/' + pays.issuer().to_json();
|
||||
paysKey += '/' + pays.issuer();
|
||||
}
|
||||
|
||||
var key = getsKey + ':' + paysKey;
|
||||
const key = getsKey + ':' + paysKey;
|
||||
|
||||
// Hell of a lot of work, so we are going to cache this. We can use this
|
||||
// later to good effect in OrderBook.notify to make sure we only process
|
||||
@@ -243,12 +246,12 @@ Meta.prototype.getAffectedBooks = function() {
|
||||
*/
|
||||
|
||||
[
|
||||
'forEach',
|
||||
'map',
|
||||
'filter',
|
||||
'every',
|
||||
'some',
|
||||
'reduce'
|
||||
'forEach',
|
||||
'map',
|
||||
'filter',
|
||||
'every',
|
||||
'some',
|
||||
'reduce'
|
||||
].forEach(function(fn) {
|
||||
Meta.prototype[fn] = function() {
|
||||
return Array.prototype[fn].apply(this.nodes, arguments);
|
||||
|
||||
@@ -22,10 +22,17 @@ const Currency = require('./currency').Currency;
|
||||
const AutobridgeCalculator = require('./autobridgecalculator');
|
||||
const OrderBookUtils = require('./orderbookutils');
|
||||
const log = require('./log').internal.sub('orderbook');
|
||||
const IOUValue = require('./iouvalue').IOUValue;
|
||||
const {IOUValue} = require('ripple-lib-value');
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
assert(!_.isNull(number) && !isNaN(number), message);
|
||||
function _sortOffers(a, b) {
|
||||
const aQuality = OrderBookUtils.getOfferQuality(a, this._currencyGets);
|
||||
const bQuality = OrderBookUtils.getOfferQuality(b, this._currencyGets);
|
||||
|
||||
return aQuality._value.comparedTo(bQuality._value);
|
||||
}
|
||||
|
||||
function _sortOffersQuick(a, b) {
|
||||
return a.qualityHex.localeCompare(b.qualityHex);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,11 +43,13 @@ function assertValidNumber(number, message) {
|
||||
* @param {String} bid currency
|
||||
* @param {String} bid issuer
|
||||
* @param {String} orderbook key
|
||||
* @param {Boolean} fire 'model' event after receiving transaction
|
||||
only once in 10 seconds
|
||||
*/
|
||||
|
||||
function OrderBook(remote,
|
||||
currencyGets, issuerGets, currencyPays, issuerPays,
|
||||
key) {
|
||||
currencyGets, issuerGets, currencyPays, issuerPays, key
|
||||
) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
const self = this;
|
||||
@@ -61,6 +70,8 @@ function OrderBook(remote,
|
||||
this._ownerFundsUnadjusted = {};
|
||||
this._ownerFunds = {};
|
||||
this._ownerOffersTotal = {};
|
||||
this._validAccounts = {};
|
||||
this._validAccountsCount = 0;
|
||||
|
||||
// We consider ourselves synced if we have a current
|
||||
// copy of the offers, we are online and subscribed to updates
|
||||
@@ -73,13 +84,45 @@ function OrderBook(remote,
|
||||
// books that we must keep track of to compute autobridged offers
|
||||
this._legOneBook = null;
|
||||
this._legTwoBook = null;
|
||||
this._gotOffersFromLegOne = false;
|
||||
this._gotOffersFromLegTwo = false;
|
||||
|
||||
this._waitingForOffers = false;
|
||||
this._lastUpdateLedgerSequence = 0;
|
||||
this._transactionsLeft = 0;
|
||||
this._calculatorRunning = false;
|
||||
|
||||
|
||||
this.sortOffers = this._currencyGets.has_interest() ?
|
||||
_sortOffers.bind(this) : _sortOffersQuick;
|
||||
|
||||
this._isAutobridgeable = !this._currencyGets.is_native()
|
||||
&& !this._currencyPays.is_native();
|
||||
|
||||
function computeAutobridgedOffersWrapper() {
|
||||
self.computeAutobridgedOffers();
|
||||
self.mergeDirectAndAutobridgedBooks();
|
||||
function computeAutobridgedOffersWrapperOne() {
|
||||
if (!self._gotOffersFromLegOne) {
|
||||
self._gotOffersFromLegOne = true;
|
||||
self.computeAutobridgedOffersWrapper();
|
||||
}
|
||||
}
|
||||
|
||||
function computeAutobridgedOffersWrapperTwo() {
|
||||
if (!self._gotOffersFromLegTwo) {
|
||||
self._gotOffersFromLegTwo = true;
|
||||
self.computeAutobridgedOffersWrapper();
|
||||
}
|
||||
}
|
||||
|
||||
function onDisconnect() {
|
||||
self.resetCache();
|
||||
self._gotOffersFromLegOne = false;
|
||||
self._gotOffersFromLegTwo = false;
|
||||
if (!self._destroyed) {
|
||||
self._remote.once('disconnect', onDisconnect);
|
||||
self._remote.once('connect', function() {
|
||||
self.subscribe();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this._isAutobridgeable) {
|
||||
@@ -89,15 +132,19 @@ function OrderBook(remote,
|
||||
issuer_pays: issuerPays
|
||||
});
|
||||
|
||||
this._legOneBook.on('model', computeAutobridgedOffersWrapper);
|
||||
|
||||
this._legTwoBook = remote.createOrderBook({
|
||||
currency_gets: currencyGets,
|
||||
issuer_gets: issuerGets,
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
}
|
||||
|
||||
this._legTwoBook.on('model', computeAutobridgedOffersWrapper);
|
||||
function onTransactionWrapper(transaction) {
|
||||
self.onTransaction(transaction);
|
||||
}
|
||||
|
||||
function onLedgerClosedWrapper(message) {
|
||||
self.onLedgerClosed(message);
|
||||
}
|
||||
|
||||
function listenersModified(action, event) {
|
||||
@@ -107,7 +154,17 @@ function OrderBook(remote,
|
||||
switch (action) {
|
||||
case 'add':
|
||||
if (++self._listeners === 1) {
|
||||
self._shouldSubscribe = true;
|
||||
self.subscribe();
|
||||
|
||||
self._remote.on('transaction', onTransactionWrapper);
|
||||
self._remote.on('ledger_closed', onLedgerClosedWrapper);
|
||||
self._remote.once('disconnect', onDisconnect);
|
||||
|
||||
if (self._isAutobridgeable) {
|
||||
self._legOneBook.on('model', computeAutobridgedOffersWrapperOne);
|
||||
self._legTwoBook.on('model', computeAutobridgedOffersWrapperTwo);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'remove':
|
||||
@@ -119,10 +176,6 @@ function OrderBook(remote,
|
||||
}
|
||||
}
|
||||
|
||||
function updateFundedAmountsWrapper(transaction) {
|
||||
self.updateFundedAmounts(transaction);
|
||||
}
|
||||
|
||||
this.on('newListener', function(event) {
|
||||
listenersModified('add', event);
|
||||
});
|
||||
@@ -131,23 +184,22 @@ function OrderBook(remote,
|
||||
listenersModified('remove', event);
|
||||
});
|
||||
|
||||
this._remote.on('transaction', updateFundedAmountsWrapper);
|
||||
|
||||
this.on('unsubscribe', function() {
|
||||
self.resetCache();
|
||||
|
||||
self._remote.removeListener('transaction', updateFundedAmountsWrapper);
|
||||
});
|
||||
self._remote.removeListener('transaction', onTransactionWrapper);
|
||||
self._remote.removeListener('ledger_closed', onLedgerClosedWrapper);
|
||||
self._remote.removeListener('disconnect', onDisconnect);
|
||||
|
||||
this._remote.once('prepare_subscribe', function() {
|
||||
self.subscribe();
|
||||
});
|
||||
self._gotOffersFromLegOne = false;
|
||||
self._gotOffersFromLegTwo = false;
|
||||
|
||||
this._remote.on('disconnect', function() {
|
||||
self.resetCache();
|
||||
self._remote.once('prepare_subscribe', function() {
|
||||
self.subscribe();
|
||||
});
|
||||
if (self._isAutobridgeable) {
|
||||
self._legOneBook.removeListener('model',
|
||||
computeAutobridgedOffersWrapperOne);
|
||||
self._legTwoBook.removeListener('model',
|
||||
computeAutobridgedOffersWrapperTwo);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
@@ -165,7 +217,11 @@ OrderBook.EVENTS = [
|
||||
'offer_changed', 'offer_funds_changed'
|
||||
];
|
||||
|
||||
OrderBook.DEFAULT_TRANSFER_RATE = 1000000000;
|
||||
OrderBook.DEFAULT_TRANSFER_RATE = new IOUValue(1000000000);
|
||||
|
||||
OrderBook.ZERO_NATIVE_AMOUNT = Amount.from_json('0');
|
||||
|
||||
OrderBook.ZERO_NORMALIZED_AMOUNT = OrderBookUtils.normalizeAmount('0');
|
||||
|
||||
/**
|
||||
* Normalize offers from book_offers and transaction stream
|
||||
@@ -192,18 +248,20 @@ OrderBook.offerRewrite = function(offer) {
|
||||
result.Flags = result.Flags || 0;
|
||||
result.OwnerNode = result.OwnerNode || new Array(16 + 1).join('0');
|
||||
result.BookNode = result.BookNode || new Array(16 + 1).join('0');
|
||||
result.qualityHex = result.BookDirectory.slice(-16);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize orderbook. Get orderbook offers and subscribe to transactions
|
||||
* @api private
|
||||
*/
|
||||
|
||||
OrderBook.prototype.subscribe = function() {
|
||||
const self = this;
|
||||
|
||||
if (!this._shouldSubscribe) {
|
||||
if (!this._shouldSubscribe || this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -216,7 +274,7 @@ OrderBook.prototype.subscribe = function() {
|
||||
self.requestTransferRate(callback);
|
||||
},
|
||||
function(callback) {
|
||||
self.requestOffers(callback);
|
||||
self.requestOffers(callback, true);
|
||||
},
|
||||
function(callback) {
|
||||
self.subscribeTransactions(callback);
|
||||
@@ -229,6 +287,7 @@ OrderBook.prototype.subscribe = function() {
|
||||
/**
|
||||
* Unhook event listeners and prevent ripple-lib from further work on this
|
||||
* orderbook. There is no more orderbook stream, so "unsubscribe" is nominal
|
||||
* @api private
|
||||
*/
|
||||
|
||||
OrderBook.prototype.unsubscribe = function() {
|
||||
@@ -250,35 +309,87 @@ OrderBook.prototype.unsubscribe = function() {
|
||||
this.emit('unsubscribe');
|
||||
};
|
||||
|
||||
/**
|
||||
* After that you can't use this object.
|
||||
*/
|
||||
|
||||
OrderBook.prototype.destroy = function() {
|
||||
this._destroyed = true;
|
||||
if (this._subscribed) {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
if (this._remote._books.hasOwnProperty(this._key)) {
|
||||
delete this._remote._books[this._key];
|
||||
}
|
||||
|
||||
if (this._isAutobridgeable) {
|
||||
this._legOneBook.destroy();
|
||||
this._legTwoBook.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Request orderbook entries from server
|
||||
*
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
OrderBook.prototype.requestOffers = function(callback=function() {}) {
|
||||
OrderBook.prototype.requestOffers = function(callback = function() {},
|
||||
internal = false) {
|
||||
const self = this;
|
||||
|
||||
if (!this._remote.isConnected()) {
|
||||
// do not make request if not online.
|
||||
// that requests will be queued and
|
||||
// eventually all of them will fire back
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this._shouldSubscribe) {
|
||||
return callback(new Error('Should not request offers'));
|
||||
callback(new Error('Should not request offers'));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this._remote.trace) {
|
||||
log.info('requesting offers', this._key);
|
||||
}
|
||||
|
||||
this._synchronized = false;
|
||||
|
||||
if (this._isAutobridgeable && !internal) {
|
||||
this._gotOffersFromLegOne = false;
|
||||
this._gotOffersFromLegTwo = false;
|
||||
|
||||
this._legOneBook.requestOffers();
|
||||
this._legTwoBook.requestOffers();
|
||||
}
|
||||
|
||||
|
||||
function handleOffers(res) {
|
||||
if (self._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._waitingForOffers = false;
|
||||
|
||||
if (!Array.isArray(res.offers)) {
|
||||
// XXX What now?
|
||||
return callback(new Error('Invalid response'));
|
||||
callback(new Error('Invalid response'));
|
||||
self.emit('model', []);
|
||||
return;
|
||||
}
|
||||
|
||||
if (self._remote.trace) {
|
||||
log.info('requested offers', self._key, 'offers: ' + res.offers.length);
|
||||
}
|
||||
|
||||
self.setOffers(res.offers);
|
||||
self.notifyDirectOffersChanged();
|
||||
|
||||
if (self._isAutobridgeable) {
|
||||
self.computeAutobridgedOffersWrapper();
|
||||
} else {
|
||||
self.emit('model', self._offers);
|
||||
}
|
||||
|
||||
callback(null, self._offers);
|
||||
}
|
||||
@@ -289,9 +400,12 @@ OrderBook.prototype.requestOffers = function(callback=function() {}) {
|
||||
log.info('failed to request offers', self._key, err);
|
||||
}
|
||||
|
||||
self._waitingForOffers = false;
|
||||
callback(err);
|
||||
}
|
||||
|
||||
this._waitingForOffers = true;
|
||||
|
||||
const requestOptions = _.merge({}, this.toJSON(), {ledger: 'validated'});
|
||||
const request = this._remote.requestBookOffers(requestOptions);
|
||||
request.once('success', handleOffers);
|
||||
@@ -331,8 +445,10 @@ OrderBook.prototype.requestTransferRate = function(callback) {
|
||||
|
||||
// When transfer rate is not explicitly set on account, it implies the
|
||||
// default transfer rate
|
||||
self._issuerTransferRate = info.account_data.TransferRate ||
|
||||
OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
self._issuerTransferRate =
|
||||
info.account_data.TransferRate ?
|
||||
new IOUValue(info.account_data.TransferRate) :
|
||||
OrderBook.DEFAULT_TRANSFER_RATE;
|
||||
|
||||
callback(null, self._issuerTransferRate);
|
||||
}
|
||||
@@ -387,18 +503,6 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles notifying listeners that direct offers have changed. For autobridged
|
||||
* books, an additional merge step is also performed
|
||||
*/
|
||||
|
||||
OrderBook.prototype.notifyDirectOffersChanged = function() {
|
||||
if (this._isAutobridgeable) {
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
} else {
|
||||
this.emit('model', this._offers);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset cached owner's funds, offer counts, and offer sums
|
||||
@@ -409,6 +513,12 @@ OrderBook.prototype.resetCache = function() {
|
||||
this._ownerOffersTotal = {};
|
||||
this._offerCounts = {};
|
||||
this._synced = false;
|
||||
this._offers = [];
|
||||
|
||||
if (this._validAccountsCount > 3000) {
|
||||
this._validAccounts = {};
|
||||
this._validAccountsCount = 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -418,7 +528,6 @@ OrderBook.prototype.resetCache = function() {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.hasOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
return this._ownerFunds[account] !== undefined;
|
||||
};
|
||||
|
||||
@@ -430,7 +539,6 @@ OrderBook.prototype.hasOwnerFunds = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.setOwnerFunds = function(account, fundedAmount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
assert(!isNaN(fundedAmount), 'Funded amount is invalid');
|
||||
|
||||
this._ownerFundsUnadjusted[account] = fundedAmount;
|
||||
@@ -447,11 +555,10 @@ OrderBook.prototype.setOwnerFunds = function(account, fundedAmount) {
|
||||
|
||||
OrderBook.prototype.applyTransferRate = function(balance) {
|
||||
assert(!isNaN(balance), 'Balance is invalid');
|
||||
assertValidNumber(this._issuerTransferRate, 'Transfer rate is invalid');
|
||||
|
||||
const adjustedBalance = (new IOUValue(balance))
|
||||
.divide(new IOUValue(this._issuerTransferRate))
|
||||
.multiply(new IOUValue(OrderBook.DEFAULT_TRANSFER_RATE)).toString();
|
||||
.divide(this._issuerTransferRate)
|
||||
.multiply(OrderBook.DEFAULT_TRANSFER_RATE).toString();
|
||||
|
||||
return adjustedBalance;
|
||||
};
|
||||
@@ -464,7 +571,6 @@ OrderBook.prototype.applyTransferRate = function(balance) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
if (this.hasOwnerFunds(account)) {
|
||||
if (this._currencyGets.is_native()) {
|
||||
return Amount.from_json(this._ownerFunds[account]);
|
||||
@@ -481,7 +587,6 @@ OrderBook.prototype.getOwnerFunds = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getUnadjustedOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
return this._ownerFundsUnadjusted[account];
|
||||
};
|
||||
|
||||
@@ -492,7 +597,6 @@ OrderBook.prototype.getUnadjustedOwnerFunds = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.deleteOwnerFunds = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
this._ownerFunds[account] = undefined;
|
||||
};
|
||||
|
||||
@@ -504,7 +608,6 @@ OrderBook.prototype.deleteOwnerFunds = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOwnerOfferCount = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
return this._offerCounts[account] || 0;
|
||||
};
|
||||
|
||||
@@ -516,7 +619,6 @@ OrderBook.prototype.getOwnerOfferCount = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.incrementOwnerOfferCount = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
const result = (this._offerCounts[account] || 0) + 1;
|
||||
this._offerCounts[account] = result;
|
||||
return result;
|
||||
@@ -531,7 +633,6 @@ OrderBook.prototype.incrementOwnerOfferCount = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.decrementOwnerOfferCount = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
const result = (this._offerCounts[account] || 1) - 1;
|
||||
this._offerCounts[account] = result;
|
||||
|
||||
@@ -552,8 +653,6 @@ OrderBook.prototype.decrementOwnerOfferCount = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.addOwnerOfferTotal = function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
const previousAmount = this.getOwnerOfferTotal(account);
|
||||
const currentAmount = previousAmount.add(Amount.from_json(amount));
|
||||
|
||||
@@ -572,14 +671,12 @@ OrderBook.prototype.addOwnerOfferTotal = function(account, amount) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.subtractOwnerOfferTotal = function(account, amount) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
const previousAmount = this.getOwnerOfferTotal(account);
|
||||
const newAmount = previousAmount.subtract(Amount.from_json(amount));
|
||||
|
||||
this._ownerOffersTotal[account] = newAmount;
|
||||
|
||||
assert(!newAmount.is_negative(), 'Offer total cannot be negative');
|
||||
|
||||
return newAmount;
|
||||
};
|
||||
|
||||
@@ -591,15 +688,14 @@ OrderBook.prototype.subtractOwnerOfferTotal = function(account, amount) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.getOwnerOfferTotal = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
const amount = this._ownerOffersTotal[account];
|
||||
if (amount) {
|
||||
return amount;
|
||||
}
|
||||
if (this._currencyGets.is_native()) {
|
||||
return Amount.from_json('0');
|
||||
return OrderBook.ZERO_NATIVE_AMOUNT.clone();
|
||||
}
|
||||
return OrderBookUtils.normalizeAmount('0');
|
||||
return OrderBook.ZERO_NORMALIZED_AMOUNT.clone();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -610,11 +706,10 @@ OrderBook.prototype.getOwnerOfferTotal = function(account) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.resetOwnerOfferTotal = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
if (this._currencyGets.is_native()) {
|
||||
this._ownerOffersTotal[account] = Amount.from_json('0');
|
||||
this._ownerOffersTotal[account] = OrderBook.ZERO_NATIVE_AMOUNT.clone();
|
||||
} else {
|
||||
this._ownerOffersTotal[account] = OrderBookUtils.normalizeAmount('0');
|
||||
this._ownerOffersTotal[account] = OrderBook.ZERO_NORMALIZED_AMOUNT.clone();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -632,17 +727,18 @@ OrderBook.prototype.resetOwnerOfferTotal = function(account) {
|
||||
OrderBook.prototype.setOfferFundedAmount = function(offer) {
|
||||
assert.strictEqual(typeof offer, 'object', 'Offer is invalid');
|
||||
|
||||
const takerGets = Amount.from_json(offer.TakerGets);
|
||||
const fundedAmount = this.getOwnerFunds(offer.Account);
|
||||
const previousOfferSum = this.getOwnerOfferTotal(offer.Account);
|
||||
const currentOfferSum = previousOfferSum.add(
|
||||
Amount.from_json(offer.TakerGets));
|
||||
const currentOfferSum = previousOfferSum.add(takerGets);
|
||||
|
||||
offer.owner_funds = this.getUnadjustedOwnerFunds(offer.Account);
|
||||
|
||||
offer.is_fully_funded = fundedAmount.compareTo(currentOfferSum) >= 0;
|
||||
offer.is_fully_funded = fundedAmount.is_comparable(currentOfferSum) &&
|
||||
fundedAmount.compareTo(currentOfferSum) >= 0;
|
||||
|
||||
if (offer.is_fully_funded) {
|
||||
offer.taker_gets_funded = Amount.from_json(offer.TakerGets).to_text();
|
||||
offer.taker_gets_funded = takerGets.to_text();
|
||||
offer.taker_pays_funded = Amount.from_json(offer.TakerPays).to_text();
|
||||
} else if (previousOfferSum.compareTo(fundedAmount) < 0) {
|
||||
offer.taker_gets_funded = fundedAmount.subtract(previousOfferSum).to_text();
|
||||
@@ -698,7 +794,11 @@ OrderBook.prototype.parseAccountBalanceFromNode = function(node) {
|
||||
}
|
||||
|
||||
assert(!isNaN(result.balance), 'node has an invalid balance');
|
||||
assert(UInt160.is_valid(result.account), 'node has an invalid account');
|
||||
if (this._validAccounts[result.Account] === undefined) {
|
||||
assert(UInt160.is_valid(result.account), 'node has an invalid account');
|
||||
this._validAccounts[result.Account] = true;
|
||||
this._validAccountsCount++;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
@@ -737,6 +837,32 @@ OrderBook.prototype.isBalanceChangeNode = function(node) {
|
||||
return true;
|
||||
};
|
||||
|
||||
OrderBook.prototype._canRunAutobridgeCalc = function(): boolean {
|
||||
return !this._calculatorRunning;
|
||||
};
|
||||
|
||||
OrderBook.prototype.onTransaction = function(transaction) {
|
||||
this.updateFundedAmounts(transaction);
|
||||
|
||||
|
||||
if (--this._transactionsLeft === 0 && !this._waitingForOffers) {
|
||||
const lastClosedLedger = this._remote.getLedgerSequenceSync();
|
||||
if (this._isAutobridgeable) {
|
||||
if (this._canRunAutobridgeCalc()) {
|
||||
if (this._legOneBook._lastUpdateLedgerSequence === lastClosedLedger ||
|
||||
this._legTwoBook._lastUpdateLedgerSequence === lastClosedLedger
|
||||
) {
|
||||
this.computeAutobridgedOffersWrapper();
|
||||
} else if (this._lastUpdateLedgerSequence === lastClosedLedger) {
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
}
|
||||
}
|
||||
} else if (this._lastUpdateLedgerSequence === lastClosedLedger) {
|
||||
this.emit('model', this._offers);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates funded amounts/balances using modified balance nodes
|
||||
*
|
||||
@@ -786,6 +912,7 @@ OrderBook.prototype.updateFundedAmounts = function(transaction) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Update offers' funded amount with their owner's funds
|
||||
*
|
||||
@@ -793,10 +920,15 @@ OrderBook.prototype.updateFundedAmounts = function(transaction) {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
// assert(UInt160.is_valid(account), 'Account is invalid');
|
||||
|
||||
const self = this;
|
||||
|
||||
if (!this.hasOwnerFunds(account)) {
|
||||
// We are only updating owner funds that are already cached
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._remote.trace) {
|
||||
const ownerFunds = this.getOwnerFunds(account);
|
||||
log.info('updating offer funds', this._key, account,
|
||||
@@ -838,6 +970,16 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
OrderBook.prototype.onLedgerClosed = function(message) {
|
||||
if (!message || (message && !_.isNumber(message.txn_count)) ||
|
||||
!this._subscribed || this._destroyed || this._waitingForOffers
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this._transactionsLeft = message.txn_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify orderbook of a relevant transaction
|
||||
*
|
||||
@@ -848,7 +990,7 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
OrderBook.prototype.notify = function(transaction) {
|
||||
const self = this;
|
||||
|
||||
if (!(this._subscribed && this._synced)) {
|
||||
if (!(this._subscribed && this._synced) || this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -884,6 +1026,12 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
function handleNode(node) {
|
||||
switch (node.nodeType) {
|
||||
case 'DeletedNode':
|
||||
if (self._validAccounts[node.fields.Account] === undefined) {
|
||||
assert(UInt160.is_valid(node.fields.Account),
|
||||
'node has an invalid account');
|
||||
self._validAccounts[node.fields.Account] = true;
|
||||
self._validAccountsCount++;
|
||||
}
|
||||
self.deleteOffer(node, isOfferCancel);
|
||||
|
||||
// We don't want to count an OfferCancel as a trade
|
||||
@@ -894,6 +1042,12 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
break;
|
||||
|
||||
case 'ModifiedNode':
|
||||
if (self._validAccounts[node.fields.Account] === undefined) {
|
||||
assert(UInt160.is_valid(node.fields.Account),
|
||||
'node has an invalid account');
|
||||
self._validAccounts[node.fields.Account] = true;
|
||||
self._validAccountsCount++;
|
||||
}
|
||||
self.modifyOffer(node);
|
||||
|
||||
takerGetsTotal = takerGetsTotal
|
||||
@@ -906,6 +1060,12 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
break;
|
||||
|
||||
case 'CreatedNode':
|
||||
if (self._validAccounts[node.fields.Account] === undefined) {
|
||||
assert(UInt160.is_valid(node.fields.Account),
|
||||
'node has an invalid account');
|
||||
self._validAccounts[node.fields.Account] = true;
|
||||
self._validAccountsCount++;
|
||||
}
|
||||
// rippled does not set owner_funds if the order maker is the issuer
|
||||
// because the value would be infinite
|
||||
const fundedAmount = transactionOwnerFunds !== undefined ?
|
||||
@@ -919,7 +1079,9 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
_.each(affectedNodes, handleNode);
|
||||
|
||||
this.emit('transaction', transaction);
|
||||
this.notifyDirectOffersChanged();
|
||||
|
||||
this._lastUpdateLedgerSequence = this._remote.getLedgerSequenceSync();
|
||||
|
||||
if (!takerGetsTotal.is_zero()) {
|
||||
this.emit('trade', takerPaysTotal, takerGetsTotal);
|
||||
}
|
||||
@@ -951,17 +1113,27 @@ OrderBook.prototype.insertOffer = function(node) {
|
||||
|
||||
const originalLength = this._offers.length;
|
||||
|
||||
for (let i = 0; i < originalLength; i++) {
|
||||
const quality = OrderBookUtils.getOfferQuality(offer, this._currencyGets);
|
||||
const existingOfferQuality = OrderBookUtils.getOfferQuality(
|
||||
this._offers[i],
|
||||
this._currencyGets
|
||||
);
|
||||
if (!this._currencyGets.has_interest()) {
|
||||
// use fast path
|
||||
for (let i = 0; i < originalLength; i++) {
|
||||
if (offer.qualityHex <= this._offers[i].qualityHex) {
|
||||
this._offers.splice(i, 0, offer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < originalLength; i++) {
|
||||
const quality = OrderBookUtils.getOfferQuality(offer, this._currencyGets);
|
||||
const existingOfferQuality = OrderBookUtils.getOfferQuality(
|
||||
this._offers[i],
|
||||
this._currencyGets
|
||||
);
|
||||
|
||||
if (quality.compareTo(existingOfferQuality) <= 0) {
|
||||
this._offers.splice(i, 0, offer);
|
||||
if (quality.compareTo(existingOfferQuality) <= 0) {
|
||||
this._offers.splice(i, 0, offer);
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1067,28 +1239,34 @@ OrderBook.prototype.deleteOffer = function(node, isOfferCancel) {
|
||||
OrderBook.prototype.setOffers = function(offers) {
|
||||
assert(Array.isArray(offers), 'Offers is not an array');
|
||||
|
||||
const self = this;
|
||||
|
||||
this.resetCache();
|
||||
|
||||
const newOffers = _.map(offers, function(rawOffer) {
|
||||
const offer = OrderBook.offerRewrite(rawOffer);
|
||||
let i = -1;
|
||||
let offer;
|
||||
const l = offers.length;
|
||||
|
||||
if (offer.hasOwnProperty('owner_funds')) {
|
||||
while (++i < l) {
|
||||
offer = OrderBook.offerRewrite(offers[i]);
|
||||
|
||||
if (this._validAccounts[offer.Account] === undefined) {
|
||||
assert(UInt160.is_valid(offer.Account), 'Account is invalid');
|
||||
this._validAccounts[offer.Account] = true;
|
||||
this._validAccountsCount++;
|
||||
}
|
||||
if (offer.owner_funds !== undefined) {
|
||||
// The first offer of each owner from book_offers contains owner balance
|
||||
// of offer's output
|
||||
self.setOwnerFunds(offer.Account, offer.owner_funds);
|
||||
this.setOwnerFunds(offer.Account, offer.owner_funds);
|
||||
}
|
||||
|
||||
self.incrementOwnerOfferCount(offer.Account);
|
||||
this.incrementOwnerOfferCount(offer.Account);
|
||||
|
||||
self.setOfferFundedAmount(offer);
|
||||
self.addOwnerOfferTotal(offer.Account, offer.TakerGets);
|
||||
this.setOfferFundedAmount(offer);
|
||||
this.addOwnerOfferTotal(offer.Account, offer.TakerGets);
|
||||
offers[i] = offer;
|
||||
}
|
||||
|
||||
return offer;
|
||||
});
|
||||
|
||||
this._offers = newOffers;
|
||||
this._offers = offers;
|
||||
this._synced = true;
|
||||
};
|
||||
|
||||
@@ -1187,10 +1365,16 @@ OrderBook.prototype.is_valid = function() {
|
||||
* IOU:XRP and XRP:IOU books
|
||||
*/
|
||||
|
||||
OrderBook.prototype.computeAutobridgedOffers = function() {
|
||||
OrderBook.prototype.computeAutobridgedOffers = function(callback = function() {}
|
||||
) {
|
||||
assert(!this._currencyGets.is_native() && !this._currencyPays.is_native(),
|
||||
'Autobridging is only for IOU:IOU orderbooks');
|
||||
|
||||
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const autobridgeCalculator = new AutobridgeCalculator(
|
||||
this._currencyGets,
|
||||
this._currencyPays,
|
||||
@@ -1200,7 +1384,24 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
|
||||
this._issuerPays
|
||||
);
|
||||
|
||||
this._offersAutobridged = autobridgeCalculator.calculate();
|
||||
autobridgeCalculator.calculate((autobridgedOffers) => {
|
||||
this._offersAutobridged = autobridgedOffers;
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
OrderBook.prototype.computeAutobridgedOffersWrapper = function() {
|
||||
if (!this._gotOffersFromLegOne || !this._gotOffersFromLegTwo ||
|
||||
!this._synchronized || this._destroyed || this._calculatorRunning
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._calculatorRunning = true;
|
||||
this.computeAutobridgedOffers(() => {
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
this._calculatorRunning = false;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1210,22 +1411,24 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
|
||||
*/
|
||||
|
||||
OrderBook.prototype.mergeDirectAndAutobridgedBooks = function() {
|
||||
const self = this;
|
||||
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isEmpty(this._offers) && _.isEmpty(this._offersAutobridged)) {
|
||||
// still emit empty offers list to indicate that load is completed
|
||||
this.emit('model', []);
|
||||
if (this._synced && this._gotOffersFromLegOne &&
|
||||
this._gotOffersFromLegTwo) {
|
||||
// emit empty model to indicate to listeners that we've got offers,
|
||||
// just there was no one
|
||||
this.emit('model', []);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._mergedOffers = this._offers
|
||||
.concat(this._offersAutobridged)
|
||||
.sort(function(a, b) {
|
||||
const aQuality = OrderBookUtils.getOfferQuality(a, self._currencyGets);
|
||||
const bQuality = OrderBookUtils.getOfferQuality(b, self._currencyGets);
|
||||
|
||||
return aQuality.compareTo(bQuality);
|
||||
});
|
||||
.sort(this.sortOffers);
|
||||
|
||||
this.emit('model', this._mergedOffers);
|
||||
};
|
||||
|
||||
@@ -5,6 +5,9 @@ const assert = require('assert');
|
||||
const SerializedObject = require('./serializedobject').SerializedObject;
|
||||
const Types = require('./serializedtypes');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./currency').Currency;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const {IOUValue} = require('ripple-lib-value');
|
||||
const OrderBookUtils = {};
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
@@ -19,10 +22,17 @@ function assertValidNumber(number, message) {
|
||||
* @return JSON amount object
|
||||
*/
|
||||
|
||||
function createAmount(value, currency, counterparty) {
|
||||
const newJSON =
|
||||
{'value': value, 'currency': currency, 'issuer': counterparty};
|
||||
return Amount.from_json(newJSON);
|
||||
function createAmount(value, currency_, counterparty_) {
|
||||
|
||||
const currency = currency_ instanceof Currency ?
|
||||
currency_ :
|
||||
Currency.from_json(currency_);
|
||||
|
||||
const counterparty = counterparty_ instanceof UInt160 ?
|
||||
counterparty_.to_json() : counterparty_;
|
||||
|
||||
return Amount.from_components_unsafe(new IOUValue(value),
|
||||
currency, counterparty, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,11 +62,11 @@ function getIssuerFromOffer(offer) {
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferTakerGetsFunded = function(offer) {
|
||||
OrderBookUtils.getOfferTakerGetsFunded = function(offer, currency_, issuer_) {
|
||||
assertValidNumber(offer.taker_gets_funded, 'Taker gets funded is invalid');
|
||||
|
||||
const currency = getCurrencyFromOffer(offer);
|
||||
const issuer = getIssuerFromOffer(offer);
|
||||
const currency = currency_ || getCurrencyFromOffer(offer);
|
||||
const issuer = issuer_ || getIssuerFromOffer(offer);
|
||||
|
||||
return createAmount(offer.taker_gets_funded, currency, issuer);
|
||||
};
|
||||
@@ -68,11 +78,11 @@ OrderBookUtils.getOfferTakerGetsFunded = function(offer) {
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferTakerPaysFunded = function(offer) {
|
||||
OrderBookUtils.getOfferTakerPaysFunded = function(offer, currency_, issuer_) {
|
||||
assertValidNumber(offer.taker_pays_funded, 'Taker gets funded is invalid');
|
||||
|
||||
const currency = getCurrencyFromOffer(offer);
|
||||
const issuer = getIssuerFromOffer(offer);
|
||||
const currency = currency_ || getCurrencyFromOffer(offer);
|
||||
const issuer = issuer_ || getIssuerFromOffer(offer);
|
||||
|
||||
return createAmount(offer.taker_pays_funded, currency, issuer);
|
||||
};
|
||||
@@ -85,11 +95,11 @@ OrderBookUtils.getOfferTakerPaysFunded = function(offer) {
|
||||
* @return {Amount}
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferTakerGets = function(offer) {
|
||||
OrderBookUtils.getOfferTakerGets = function(offer, currency_, issuer_) {
|
||||
assert(typeof offer, 'object', 'Offer is invalid');
|
||||
|
||||
const currency = offer.TakerPays.currency;
|
||||
const issuer = offer.TakerPays.issuer;
|
||||
const currency = currency_ || offer.TakerPays.currency;
|
||||
const issuer = issuer_ || offer.TakerPays.issuer;
|
||||
|
||||
return createAmount(offer.TakerGets, currency, issuer);
|
||||
};
|
||||
@@ -101,7 +111,9 @@ OrderBookUtils.getOfferTakerGets = function(offer) {
|
||||
* @param {Currency} currencyGets
|
||||
*/
|
||||
|
||||
OrderBookUtils.getOfferQuality = function(offer, currencyGets) {
|
||||
OrderBookUtils.getOfferQuality = function(offer, currencyGets, currency_,
|
||||
issuer_
|
||||
) {
|
||||
let amount;
|
||||
|
||||
if (currencyGets.has_interest()) {
|
||||
@@ -113,8 +125,8 @@ OrderBookUtils.getOfferQuality = function(offer, currencyGets) {
|
||||
});
|
||||
} else {
|
||||
|
||||
const currency = getCurrencyFromOffer(offer);
|
||||
const issuer = getIssuerFromOffer(offer);
|
||||
const currency = currency_ || getCurrencyFromOffer(offer);
|
||||
const issuer = issuer_ || getIssuerFromOffer(offer);
|
||||
|
||||
amount = createAmount(offer.quality, currency, issuer);
|
||||
}
|
||||
@@ -140,13 +152,35 @@ OrderBookUtils.convertOfferQualityToHex = function(quality) {
|
||||
return so.to_hex();
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats an offer quality amount to a hex that can be parsed by
|
||||
* Amount.parse_quality
|
||||
*
|
||||
* @param {String} quality
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
|
||||
OrderBookUtils.convertOfferQualityToHexFromText = function(quality) {
|
||||
|
||||
const so = new SerializedObject();
|
||||
Types.Quality.serialize(so, quality);
|
||||
|
||||
return so.to_hex();
|
||||
};
|
||||
|
||||
|
||||
OrderBookUtils.CURRENCY_ONE = Currency.from_json(1);
|
||||
|
||||
OrderBookUtils.ISSUER_ONE = UInt160.from_json(1);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
OrderBookUtils.normalizeAmount = function(value) {
|
||||
|
||||
return Amount.from_number(value);
|
||||
return Amount.from_components_unsafe(new IOUValue(value),
|
||||
OrderBookUtils.CURRENCY_ONE, OrderBookUtils.ISSUER_ONE, false);
|
||||
};
|
||||
|
||||
module.exports = OrderBookUtils;
|
||||
|
||||
@@ -11,7 +11,8 @@ const Amount = require('./amount').Amount;
|
||||
* the 'end' and 'superceded' events.
|
||||
*/
|
||||
|
||||
function PathFind(remote, src_account, dst_account, dst_amount, src_currencies
|
||||
function PathFind(remote, src_account, dst_account, dst_amount,
|
||||
src_currencies, src_amount
|
||||
) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
@@ -21,6 +22,7 @@ function PathFind(remote, src_account, dst_account, dst_amount, src_currencies
|
||||
this.dst_account = dst_account;
|
||||
this.dst_amount = dst_amount;
|
||||
this.src_currencies = src_currencies;
|
||||
this.src_amount = src_amount;
|
||||
}
|
||||
|
||||
util.inherits(PathFind, EventEmitter);
|
||||
@@ -42,7 +44,8 @@ PathFind.prototype.create = function() {
|
||||
source_account: this.src_account,
|
||||
destination_account: this.dst_account,
|
||||
destination_amount: this.dst_amount,
|
||||
source_currencies: this.src_currencies
|
||||
source_currencies: this.src_currencies,
|
||||
send_max: this.src_amount
|
||||
});
|
||||
|
||||
req.once('error', function(err) {
|
||||
|
||||
@@ -36,6 +36,8 @@ const utils = require('./utils');
|
||||
const hashprefixes = require('./hashprefixes');
|
||||
const log = require('./log').internal.sub('remote');
|
||||
|
||||
export type GetLedgerSequenceCallback = (err?: ?Error, index?: number) => void;
|
||||
|
||||
/**
|
||||
* Interface to manage connections to rippled servers
|
||||
*
|
||||
@@ -224,14 +226,16 @@ Remote.flags = {
|
||||
Passive: 0x00010000,
|
||||
Sell: 0x00020000 // offer was placed as a sell
|
||||
},
|
||||
// Ripple tate
|
||||
// Ripple state
|
||||
state: {
|
||||
LowReserve: 0x00010000, // entry counts toward reserve
|
||||
HighReserve: 0x00020000,
|
||||
LowAuth: 0x00040000,
|
||||
HighAuth: 0x00080000,
|
||||
LowNoRipple: 0x00100000,
|
||||
HighNoRipple: 0x00200000
|
||||
HighNoRipple: 0x00200000,
|
||||
LowFreeze: 0x00400000,
|
||||
HighFreeze: 0x00800000
|
||||
}
|
||||
};
|
||||
|
||||
@@ -419,22 +423,23 @@ Remote.prototype.reconnect = function() {
|
||||
/**
|
||||
* Connect to the Ripple network
|
||||
*
|
||||
* @param {Function} callback
|
||||
* @param [Function] [callback]
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Remote.prototype.connect = function(callback) {
|
||||
if (!this._servers.length) {
|
||||
Remote.prototype.connect = function(callback = function() {}) {
|
||||
if (_.isEmpty(this._servers)) {
|
||||
throw new Error('No servers available.');
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this.once('connect', callback);
|
||||
if (this.isConnected()) {
|
||||
callback();
|
||||
return this;
|
||||
}
|
||||
|
||||
this.once('connect', callback);
|
||||
this._should_connect = true;
|
||||
|
||||
this._servers.forEach(function(server) {
|
||||
this._servers.forEach(server => {
|
||||
server.connect();
|
||||
});
|
||||
|
||||
@@ -444,29 +449,23 @@ Remote.prototype.connect = function(callback) {
|
||||
/**
|
||||
* Disconnect from the Ripple network.
|
||||
*
|
||||
* @param {Function} callback
|
||||
* @param {Function} [callback]
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Remote.prototype.disconnect = function(callback_) {
|
||||
if (!this._servers.length) {
|
||||
Remote.prototype.disconnect = function(callback = function() {}) {
|
||||
if (_.isEmpty(this._servers)) {
|
||||
throw new Error('No servers available, not disconnecting');
|
||||
}
|
||||
|
||||
const callback = _.isFunction(callback_)
|
||||
? callback_
|
||||
: function() {};
|
||||
|
||||
this._should_connect = false;
|
||||
|
||||
if (!this.isConnected()) {
|
||||
callback();
|
||||
return this;
|
||||
}
|
||||
|
||||
this._should_connect = false;
|
||||
this.once('disconnect', callback);
|
||||
|
||||
this._servers.forEach(function(server) {
|
||||
this._servers.forEach(server => {
|
||||
server.disconnect();
|
||||
});
|
||||
|
||||
@@ -521,7 +520,34 @@ Remote.prototype._handleMessage = function(message, server) {
|
||||
}
|
||||
};
|
||||
|
||||
Remote.prototype.getLedgerSequence = function() {
|
||||
/**
|
||||
*
|
||||
* @param {Function} [callback]
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Remote.prototype.getLedgerSequence = function(callback = function() {}) {
|
||||
if (!this._servers.length) {
|
||||
callback(new Error('No servers available.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isFinite(this._ledger_current_index)) {
|
||||
// the "current" ledger is the one after the most recently closed ledger
|
||||
callback(null, this._ledger_current_index - 1);
|
||||
} else {
|
||||
this.once('ledger_closed', () => {
|
||||
callback(null, this._ledger_current_index - 1);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Remote.prototype.getLedgerSequenceSync = function(): number {
|
||||
if (!this._ledger_current_index) {
|
||||
throw new Error('Ledger sequence has not yet been initialized');
|
||||
}
|
||||
@@ -784,8 +810,8 @@ Remote.prototype.request = function(request) {
|
||||
/**
|
||||
* Request ping
|
||||
*
|
||||
* @param [String] server host
|
||||
* @param [Function] callback
|
||||
* @param {String} [server] host
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -817,7 +843,7 @@ Remote.prototype.requestPing = function(host, callback_) {
|
||||
/**
|
||||
* Request server_info
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -887,7 +913,7 @@ Remote.prototype.requestLedger = function(options, callback_) {
|
||||
/**
|
||||
* Request ledger_closed
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -900,7 +926,7 @@ Remote.prototype.requestLedgerHash = function(callback) {
|
||||
/**
|
||||
* Request ledger_header
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -916,7 +942,7 @@ Remote.prototype.requestLedgerHeader = function(callback) {
|
||||
*
|
||||
* Only for unit testing.
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -930,13 +956,13 @@ Remote.prototype.requestLedgerCurrent = function(callback) {
|
||||
* Get the contents of a specified ledger
|
||||
*
|
||||
* @param {Object} options
|
||||
* @property {Boolean} [options.binary]- Flag which determines if rippled
|
||||
* returns binary or parsed JSON
|
||||
* @property {String|Number} [options.ledger] - Hash or sequence of a ledger
|
||||
* to get contents for
|
||||
* @property {Number} [options.limit] - Number of contents to retrieve
|
||||
* from the ledger
|
||||
* @property {Function} callback
|
||||
* @param {Boolean} [options.binary]- Flag which determines if rippled
|
||||
* returns binary or parsed JSON
|
||||
* @param {String|Number} [options.ledger] - Hash or sequence of a ledger
|
||||
* to get contents for
|
||||
* @param {Number} [options.limit] - Number of contents to retrieve
|
||||
* from the ledger
|
||||
* @param {Function} callback
|
||||
*
|
||||
* @callback
|
||||
* @param {Error} error
|
||||
@@ -984,8 +1010,8 @@ Remote.prototype.requestLedgerData = function(options, callback) {
|
||||
/**
|
||||
* Request ledger_entry
|
||||
*
|
||||
* @param [String] type
|
||||
* @param [Function] callback
|
||||
* @param {String} [type]
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -1056,7 +1082,7 @@ Remote.prototype.requestLedgerEntry = function(type, callback_) {
|
||||
* Request subscribe
|
||||
*
|
||||
* @param {Array} streams
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -1076,7 +1102,7 @@ Remote.prototype.requestSubscribe = function(streams, callback) {
|
||||
* Request usubscribe
|
||||
*
|
||||
* @param {Array} streams
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -1098,7 +1124,7 @@ Remote.prototype.requestUnsubscribe = function(streams, callback) {
|
||||
* @param {Object} options -
|
||||
* @param {String} [options.transaction] - hash
|
||||
* @param {String|Number} [options.ledger='validated'] - hash or sequence
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
@@ -1113,14 +1139,15 @@ Remote.prototype.requestTransactionEntry = function(options, callback) {
|
||||
/**
|
||||
* Request tx
|
||||
*
|
||||
* @param {Object|String} hash
|
||||
* @property {String} hash.hash - Transaction hash
|
||||
* @property {Boolean} [hash.binary=true] - Flag which determines if rippled
|
||||
* returns binary or parsed JSON
|
||||
* @param [Function] callback
|
||||
* @param {Object} options -
|
||||
* @property {String} [options.hash] - Transaction hash
|
||||
* @property {Boolean} [options.binary=true] - Flag which determines if rippled
|
||||
* returns binary or parsed JSON
|
||||
* @param {Function} [callback]
|
||||
* @return {Request} request
|
||||
*/
|
||||
|
||||
Remote.prototype.requestTx =
|
||||
Remote.prototype.requestTransaction = function(options, callback) {
|
||||
const request = new Request(this, 'tx');
|
||||
request.message.binary = options.binary !== false;
|
||||
@@ -1151,17 +1178,17 @@ Remote.prototype.requestTransaction = function(options, callback) {
|
||||
*
|
||||
* @param {String} command - request command, e.g. 'account_lines'
|
||||
* @param {Object} options - all optional
|
||||
* @param {String} account - ripple address
|
||||
* @param {String} peer - ripple address
|
||||
* @param [String|Number] ledger identifier
|
||||
* @param [Number] limit - max results per response
|
||||
* @param {String} marker - start position in response paging
|
||||
* @param [Function] callback
|
||||
* @param {String} options.account - ripple address
|
||||
* @param {String} [options.peer] - ripple address
|
||||
* @param {String|Number} [options.ledger] - identifier
|
||||
* @param {Number} [options.limit] - max results per response
|
||||
* @param {String} [options.marker] - start position in response paging
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
* @throws {Error} if a marker is provided, but no ledger_index or ledger_hash
|
||||
*/
|
||||
|
||||
Remote.accountRequest = function(command, options, callback) {
|
||||
Remote.prototype._accountRequest = function(command, options, callback) {
|
||||
if (options.marker) {
|
||||
if (!(Number(options.ledger) > 0) && !UInt256.is_valid(options.ledger)) {
|
||||
throw new Error(
|
||||
@@ -1207,33 +1234,31 @@ Remote.accountRequest = function(command, options, callback) {
|
||||
/**
|
||||
* Request account_info
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {String} account - ripple address
|
||||
* @param {String} peer - ripple address
|
||||
* @param [String|Number] ledger identifier
|
||||
* @param [Function] callback
|
||||
* @param {Object} options -
|
||||
* @param {String} options.account - ripple address
|
||||
* @param {String} [options.peer] - ripple address
|
||||
* @param {String|Number} [options.ledger] - identifier
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestAccountInfo = function(...args) {
|
||||
const options = ['account_info', ...args];
|
||||
return Remote.accountRequest.apply(this, options);
|
||||
Remote.prototype.requestAccountInfo = function(options, callback) {
|
||||
return this._accountRequest('account_info', options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request account_currencies
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {String} account - ripple address
|
||||
* @param {String} peer - ripple address
|
||||
* @param [String|Number] ledger identifier
|
||||
* @param [Function] callback
|
||||
* @param {String} options.account - ripple address
|
||||
* @param {String} [options.peer] - ripple address
|
||||
* @param {String|Number} [options.ledger] - identifier
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestAccountCurrencies = function(...args) {
|
||||
const options = ['account_currencies', ...args];
|
||||
return Remote.accountRequest.apply(this, options);
|
||||
Remote.prototype.requestAccountCurrencies = function(options, callback) {
|
||||
return this._accountRequest('account_currencies', options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1247,33 +1272,19 @@ Remote.prototype.requestAccountCurrencies = function(...args) {
|
||||
* when paging to ensure a complete response
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {String} account - ripple address
|
||||
* @param {String} peer - ripple address
|
||||
* @param [String|Number] ledger identifier
|
||||
* @param [Number] limit - max results per response
|
||||
* @param {String} marker - start position in response paging
|
||||
* @param [Function] callback
|
||||
* @param {String} options.account - ripple address
|
||||
* @param {String} [options.peer] - ripple address
|
||||
* @param {String|Number} [options.ledger] identifier
|
||||
* @param {Number} [options.limit] - max results per response
|
||||
* @param {String} [options.marker] - start position in response paging
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestAccountLines = function(...args) {
|
||||
Remote.prototype.requestAccountLines = function(options, callback) {
|
||||
// XXX Does this require the server to be trusted?
|
||||
// utils.assert(this.trusted);
|
||||
let options = ['account_lines'];
|
||||
|
||||
if (_.isPlainObject(args[0])) {
|
||||
options = options.concat(args);
|
||||
} else {
|
||||
const [account, peer, ledger] = args;
|
||||
options = options.concat([
|
||||
account,
|
||||
ledger,
|
||||
peer,
|
||||
...args.slice(3)
|
||||
]);
|
||||
}
|
||||
|
||||
return Remote.accountRequest.apply(this, options);
|
||||
return this._accountRequest('account_lines', options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1287,17 +1298,16 @@ Remote.prototype.requestAccountLines = function(...args) {
|
||||
* when paging to ensure a complete response
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {String} account - ripple address
|
||||
* @param [String|Number] ledger identifier
|
||||
* @param [Number] limit - max results per response
|
||||
* @param {String} marker - start position in response paging
|
||||
* @param [Function] callback
|
||||
* @param {String} options.account - ripple address
|
||||
* @param {String|Number} [options.ledger] identifier
|
||||
* @param {Number} [options.limit] - max results per response
|
||||
* @param {String} [options.marker] - start position in response paging
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestAccountOffers = function(...args) {
|
||||
const options = ['account_offers', ...args];
|
||||
return Remote.accountRequest.apply(this, options);
|
||||
Remote.prototype.requestAccountOffers = function(options, callback) {
|
||||
return this._accountRequest('account_offers', options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1305,17 +1315,17 @@ Remote.prototype.requestAccountOffers = function(...args) {
|
||||
*
|
||||
* @param {Object} options
|
||||
*
|
||||
* @param {String} account
|
||||
* @param [Number] ledger_index_min defaults to -1
|
||||
* @param [Number] ledger_index_max defaults to -1
|
||||
* @param [Boolean] binary, defaults to true
|
||||
* @param [Boolean] parseBinary, defaults to true
|
||||
* @param [Boolean] count, defaults to false
|
||||
* @param [Boolean] descending, defaults to false
|
||||
* @param [Number] offset, defaults to 0
|
||||
* @param [Number] limit
|
||||
* @param {String} options.account
|
||||
* @param {Number} [options.ledger_index_min=-1]
|
||||
* @param {Number} [options.ledger_index_max=-1]
|
||||
* @param {Boolean} [options.binary=true]
|
||||
* @param {Boolean} [options.parseBinary=true]
|
||||
* @param {Boolean} [options.count=false]
|
||||
* @param {Boolean} [options.descending=false]
|
||||
* @param {Number} [options.offset=0]
|
||||
* @param {Number} [options.limit]
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -1474,8 +1484,8 @@ Remote.parseBinaryLedgerData = function(ledgerData) {
|
||||
* Returns a list of transactions that happened recently on the network. The
|
||||
* default number of transactions to be returned is 20.
|
||||
*
|
||||
* @param [Number] start
|
||||
* @param [Function] callback
|
||||
* @param {Number} [start]
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -1493,33 +1503,42 @@ Remote.prototype.requestTransactionHistory = function(options, callback) {
|
||||
* Request book_offers
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Object} options.gets - taker_gets with issuer and currency
|
||||
* @param {Object} options.pays - taker_pays with issuer and currency
|
||||
* @param {Object} options.taker_gets - taker_gets with issuer and currency
|
||||
* @param {Object} options.taker_pays - taker_pays with issuer and currency
|
||||
* @param {String} [options.taker]
|
||||
* @param {String} [options.ledger]
|
||||
* @param {String|Number} [options.limit]
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestBookOffers = function(options, callback) {
|
||||
const {gets, pays, taker, ledger, limit} = options;
|
||||
const {taker, ledger, limit} = options;
|
||||
let {taker_gets, taker_pays} = options;
|
||||
|
||||
if (taker_gets === undefined) {
|
||||
taker_gets = options.gets;
|
||||
}
|
||||
if (taker_pays === undefined) {
|
||||
taker_pays = options.pays;
|
||||
}
|
||||
|
||||
const request = new Request(this, 'book_offers');
|
||||
|
||||
request.message.taker_gets = {
|
||||
currency: Currency.json_rewrite(gets.currency, {force_hex: true})
|
||||
currency: Currency.json_rewrite(taker_gets.currency, {force_hex: true})
|
||||
};
|
||||
|
||||
if (!Currency.from_json(request.message.taker_gets.currency).is_native()) {
|
||||
request.message.taker_gets.issuer = UInt160.json_rewrite(gets.issuer);
|
||||
request.message.taker_gets.issuer = UInt160.json_rewrite(taker_gets.issuer);
|
||||
}
|
||||
|
||||
request.message.taker_pays = {
|
||||
currency: Currency.json_rewrite(pays.currency, {force_hex: true})
|
||||
currency: Currency.json_rewrite(taker_pays.currency, {force_hex: true})
|
||||
};
|
||||
|
||||
if (!Currency.from_json(request.message.taker_pays.currency).is_native()) {
|
||||
request.message.taker_pays.issuer = UInt160.json_rewrite(pays.issuer);
|
||||
request.message.taker_pays.issuer = UInt160.json_rewrite(taker_pays.issuer);
|
||||
}
|
||||
|
||||
request.message.taker = taker ? taker : UInt160.ACCOUNT_ONE;
|
||||
@@ -1543,6 +1562,7 @@ Remote.prototype.requestBookOffers = function(options, callback) {
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
@@ -1550,7 +1570,7 @@ Remote.prototype.requestBookOffers = function(options, callback) {
|
||||
* Request wallet_accounts
|
||||
*
|
||||
* @param {String} seed
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -1568,7 +1588,7 @@ Remote.prototype.requestWalletAccounts = function(options, callback) {
|
||||
*
|
||||
* @param {String} secret
|
||||
* @param {Object} tx_json
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -1586,7 +1606,7 @@ Remote.prototype.requestSign = function(options, callback) {
|
||||
/**
|
||||
* Request submit
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -1602,7 +1622,7 @@ Remote.prototype.requestSubmit = function(callback) {
|
||||
*
|
||||
* This function will create and return the request, but not submit it.
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @api private
|
||||
*/
|
||||
|
||||
@@ -1645,7 +1665,7 @@ Remote.prototype._serverPrepareSubscribe = function(server, callback_) {
|
||||
* to 'ledger_hash' events. A good way to be notified of the result of this is:
|
||||
* remote.on('ledger_closed', function(ledger_closed, ledger_index) { ... } );
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
*/
|
||||
|
||||
Remote.prototype.ledgerAccept =
|
||||
@@ -1677,7 +1697,8 @@ Remote.prototype.requestLedgerAccept = function(callback) {
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Remote.accountRootRequest = function(command, filter, options, callback) {
|
||||
Remote.prototype._accountRootRequest = function(command, filter,
|
||||
options, callback) {
|
||||
const request = this.requestLedgerEntry('account_root');
|
||||
request.accountRoot(options.account);
|
||||
request.selectLedger(options.ledger);
|
||||
@@ -1694,55 +1715,55 @@ Remote.accountRootRequest = function(command, filter, options, callback) {
|
||||
/**
|
||||
* Request account balance
|
||||
*
|
||||
* @param {String} account
|
||||
* @param [String|Number] ledger
|
||||
* @param [Function] callback
|
||||
* @param {Object} options
|
||||
* @param {String} options.account -
|
||||
* @param {String|Number} [options.ledger] -
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestAccountBalance = function(...args) {
|
||||
Remote.prototype.requestAccountBalance = function(options, callback) {
|
||||
function responseFilter(message) {
|
||||
return Amount.from_json(message.node.Balance);
|
||||
}
|
||||
|
||||
const options = ['account_balance', responseFilter, ...args];
|
||||
return Remote.accountRootRequest.apply(this, options);
|
||||
return this._accountRootRequest(
|
||||
'account_balance', responseFilter, options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request account flags
|
||||
*
|
||||
* @param {String} account
|
||||
* @param [String|Number] ledger
|
||||
* @param [Function] callback
|
||||
* @param {Object} options
|
||||
* @param {String} options.account -
|
||||
* @param {String|Number} [options.ledger] -
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestAccountFlags = function(...args) {
|
||||
Remote.prototype.requestAccountFlags = function(options, callback) {
|
||||
function responseFilter(message) {
|
||||
return message.node.Flags;
|
||||
}
|
||||
|
||||
const options = ['account_flags', responseFilter, ...args];
|
||||
return Remote.accountRootRequest.apply(this, options);
|
||||
return this._accountRootRequest(
|
||||
'account_flags', responseFilter, options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Request owner count
|
||||
*
|
||||
* @param {String} account
|
||||
* @param [String|Number] ledger
|
||||
* @param [Function] callback
|
||||
* @param {Object} options
|
||||
* @param {String} options.account
|
||||
* @param {String|Number} [options.ledger]
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestOwnerCount = function(...args) {
|
||||
Remote.prototype.requestOwnerCount = function(options, callback) {
|
||||
function responseFilter(message) {
|
||||
return message.node.OwnerCount;
|
||||
}
|
||||
|
||||
const options = ['owner_count', responseFilter, ...args];
|
||||
return Remote.accountRootRequest.apply(this, options);
|
||||
return this._accountRootRequest(
|
||||
'owner_count', responseFilter, options, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1792,7 +1813,7 @@ Remote.prototype.findAccount = function(accountID) {
|
||||
* Create a pathfind
|
||||
*
|
||||
* @param {Object} options -
|
||||
* @param {Function} callback -
|
||||
* @param {Function} [callback] -
|
||||
* @return {PathFind} -
|
||||
*/
|
||||
Remote.prototype.createPathFind = function(options, callback) {
|
||||
@@ -1803,7 +1824,7 @@ Remote.prototype.createPathFind = function(options, callback) {
|
||||
|
||||
const pathFind = new PathFind(this,
|
||||
options.src_account, options.dst_account,
|
||||
options.dst_amount, options.src_currencies);
|
||||
options.dst_amount, options.src_currencies, options.src_amount);
|
||||
|
||||
if (this._cur_path_find) {
|
||||
this._cur_path_find.notify_superceded();
|
||||
@@ -1811,9 +1832,20 @@ Remote.prototype.createPathFind = function(options, callback) {
|
||||
|
||||
if (callback) {
|
||||
pathFind.on('update', (data) => {
|
||||
if (data.full_reply) {
|
||||
pathFind.close();
|
||||
if (data.full_reply && !data.closed) {
|
||||
this._cur_path_find = null;
|
||||
callback(null, data);
|
||||
// "A client can only have one pathfinding request open at a time.
|
||||
// If another pathfinding request is already open on the same
|
||||
// connection, the old request is automatically closed and replaced
|
||||
// with the new request."
|
||||
// - ripple.com/build/rippled-apis/#path-find-create
|
||||
if (this._queued_path_finds.length > 0) {
|
||||
const pathfind = this._queued_path_finds.shift();
|
||||
this.createPathFind(pathfind.options, pathfind.callback);
|
||||
} else {
|
||||
pathFind.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
pathFind.on('error', callback);
|
||||
@@ -1904,9 +1936,10 @@ Remote.prototype.setAccountSeq = function(account_, sequence) {
|
||||
/**
|
||||
* Refresh an account's sequence from server
|
||||
*
|
||||
* @param {String} account
|
||||
* @param [String|Number] ledger
|
||||
* @param [Function] callback
|
||||
* @param {Object} options
|
||||
* @param {String} options.account
|
||||
* @param {String|Number} [options.ledger]
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2004,11 +2037,12 @@ Remote.prototype.requestOffer = function(options, callback) {
|
||||
/**
|
||||
* Get an account's balance
|
||||
*
|
||||
* @param {String} account
|
||||
* @param [String] issuer
|
||||
* @param [String] currency
|
||||
* @param [String|Number] ledger
|
||||
* @param [Function] callback
|
||||
* @param {Object} options
|
||||
* @param {String} options.account
|
||||
* @param {String} [options.issuer]
|
||||
* @param {String} [options.currency]
|
||||
* @param {String|Number} [options.ledger]
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2088,7 +2122,7 @@ Remote.prepareCurrencies = function(currency) {
|
||||
* Request ripple_path_find
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2117,7 +2151,7 @@ Remote.prototype.requestRipplePathFind = function(options, callback) {
|
||||
* Request path_find/create
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2138,6 +2172,10 @@ Remote.prototype.requestPathFindCreate = function(options, callback) {
|
||||
options.source_currencies.map(Remote.prepareCurrency);
|
||||
}
|
||||
|
||||
if (options.send_max) {
|
||||
request.message.send_max = Amount.json_rewrite(options.send_max);
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
return request;
|
||||
};
|
||||
@@ -2145,28 +2183,21 @@ Remote.prototype.requestPathFindCreate = function(options, callback) {
|
||||
/**
|
||||
* Request path_find/close
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
Remote.prototype.requestPathFindClose = function(callback) {
|
||||
const request = new Request(this, 'path_find');
|
||||
|
||||
request.message.subcommand = 'close';
|
||||
request.callback(callback);
|
||||
this._cur_path_find = null;
|
||||
if (this._queued_path_finds.length > 0) {
|
||||
const pathfind = this._queued_path_finds.shift();
|
||||
this.createPathFind(pathfind.options, pathfind.callback);
|
||||
}
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Request unl_list
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2179,7 +2210,7 @@ Remote.prototype.requestUnlList = function(callback) {
|
||||
*
|
||||
* @param {String} address
|
||||
* @param {String} comment
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2202,7 +2233,7 @@ Remote.prototype.requestUnlAdd = function(address, comment, callback) {
|
||||
* Request unl_delete
|
||||
*
|
||||
* @param {String} node
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2218,7 +2249,7 @@ Remote.prototype.requestUnlDelete = function(node, callback) {
|
||||
/**
|
||||
* Request peers
|
||||
*
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2231,7 +2262,7 @@ Remote.prototype.requestPeers = function(callback) {
|
||||
*
|
||||
* @param {String} ip
|
||||
* @param {Number} port
|
||||
* @param [Function] callback
|
||||
* @param {Function} [callback]
|
||||
* @return {Request}
|
||||
*/
|
||||
|
||||
@@ -2249,6 +2280,29 @@ Remote.prototype.requestConnect = function(ip, port, callback) {
|
||||
return request;
|
||||
};
|
||||
|
||||
Remote.prototype.requestGatewayBalances = function(options, callback) {
|
||||
assert(_.isObject(options), 'Options missing');
|
||||
assert(options.account, 'Account missing');
|
||||
|
||||
const request = new Request(this, 'gateway_balances');
|
||||
|
||||
request.message.account = UInt160.json_rewrite(options.account);
|
||||
|
||||
if (!_.isUndefined(options.hotwallet)) {
|
||||
request.message.hotwallet = options.hotwallet;
|
||||
}
|
||||
if (!_.isUndefined(options.strict)) {
|
||||
request.message.strict = options.strict;
|
||||
}
|
||||
if (!_.isUndefined(options.ledger)) {
|
||||
request.selectLedger(options.ledger);
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a Transaction
|
||||
*
|
||||
@@ -2274,7 +2328,11 @@ Remote.prototype.createTransaction = function(type, options = {}) {
|
||||
TrustSet: transaction.trustSet,
|
||||
OfferCreate: transaction.offerCreate,
|
||||
OfferCancel: transaction.offerCancel,
|
||||
SetRegularKey: transaction.setRegularKey
|
||||
SetRegularKey: transaction.setRegularKey,
|
||||
SignerListSet: transaction.setSignerList,
|
||||
SuspendedPaymentCreate: transaction.suspendedPaymentCreate,
|
||||
SuspendedPaymentFinish: transaction.suspendedPaymentFinish,
|
||||
SuspendedPaymentCancel: transaction.suspendedPaymentCancel
|
||||
};
|
||||
|
||||
const transactionConstructor = constructorMap[type];
|
||||
@@ -2305,6 +2363,32 @@ Remote.prototype.feeTx = function(units) {
|
||||
return server._feeTx(units);
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as feeTx, but will wait to connect to server if currently
|
||||
* disconnected.
|
||||
*
|
||||
* @param {Number} fee units
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Remote.prototype.feeTxAsync = function(units, callback) {
|
||||
if (!this._servers.length) {
|
||||
callback(new Error('No servers available.'));
|
||||
return;
|
||||
}
|
||||
|
||||
let server = this.getServer();
|
||||
|
||||
if (!server) {
|
||||
this.once('connected', () => {
|
||||
server = this.getServer();
|
||||
callback(null, server._feeTx(units));
|
||||
});
|
||||
} else {
|
||||
callback(null, server._feeTx(units));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current recommended transaction fee unit.
|
||||
*
|
||||
|
||||
@@ -34,32 +34,66 @@ function Request(remote, command) {
|
||||
command: command,
|
||||
id: undefined
|
||||
};
|
||||
this._timeout = this.remote.submission_timeout;
|
||||
}
|
||||
|
||||
util.inherits(Request, EventEmitter);
|
||||
|
||||
// Send the request to a remote.
|
||||
Request.prototype.request = function(servers, callback) {
|
||||
this.emit('before');
|
||||
this.callback(callback);
|
||||
Request.prototype.request = function(servers, callback_) {
|
||||
const callback = typeof servers === 'function' ? servers : callback_;
|
||||
const self = this;
|
||||
|
||||
if (this.requested) {
|
||||
throw new Error('Already requested');
|
||||
}
|
||||
|
||||
this.emit('before');
|
||||
// emit handler can set requested flag
|
||||
if (this.requested) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.requested = true;
|
||||
this.callback(callback);
|
||||
|
||||
this.on('error', function() {});
|
||||
this.emit('request', this.remote);
|
||||
|
||||
if (Array.isArray(servers)) {
|
||||
servers.forEach(function(server) {
|
||||
this.setServer(server);
|
||||
this.remote.request(this);
|
||||
}, this);
|
||||
} else {
|
||||
this.remote.request(this);
|
||||
function doRequest() {
|
||||
if (Array.isArray(servers)) {
|
||||
servers.forEach(function(server) {
|
||||
self.setServer(server);
|
||||
self.remote.request(self);
|
||||
}, self);
|
||||
} else {
|
||||
self.remote.request(self);
|
||||
}
|
||||
}
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(new RippleError('tejTimeout'));
|
||||
}
|
||||
|
||||
this.emit('timeout');
|
||||
// just in case
|
||||
this.emit = _.noop;
|
||||
this.cancel();
|
||||
}, this._timeout);
|
||||
|
||||
function onResponse() {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
if (this.remote.isConnected()) {
|
||||
this.remote.on('connected', doRequest);
|
||||
}
|
||||
|
||||
this.once('response', onResponse);
|
||||
|
||||
doRequest();
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -207,14 +241,8 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
|
||||
|
||||
let called = false;
|
||||
|
||||
function requestSuccess(message) {
|
||||
if (!called) {
|
||||
called = true;
|
||||
callback.call(self, null, message);
|
||||
}
|
||||
}
|
||||
|
||||
function requestError(error) {
|
||||
self.remote.removeListener('error', requestError);
|
||||
if (!called) {
|
||||
called = true;
|
||||
|
||||
@@ -226,45 +254,30 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
function requestSuccess(message) {
|
||||
self.remote.removeListener('error', requestError);
|
||||
if (!called) {
|
||||
called = true;
|
||||
callback.call(self, null, message);
|
||||
}
|
||||
}
|
||||
|
||||
this.remote.once('error', requestError); // e.g. rate-limiting slowDown error
|
||||
this.once(this.successEvent, requestSuccess);
|
||||
this.once(this.errorEvent, requestError);
|
||||
this.request();
|
||||
|
||||
if (!this.requested) {
|
||||
this.request();
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Request.prototype.timeout = function(duration, callback) {
|
||||
const self = this;
|
||||
|
||||
function requested() {
|
||||
self.timeout(duration, callback);
|
||||
Request.prototype.setTimeout = function(delay) {
|
||||
if (!_.isFinite(delay)) {
|
||||
throw new Error('delay must be number');
|
||||
}
|
||||
|
||||
if (!this.requested) {
|
||||
// Defer until requested
|
||||
return this.once('request', requested);
|
||||
}
|
||||
|
||||
const emit = this.emit;
|
||||
let timed_out = false;
|
||||
|
||||
const timeout = setTimeout(function() {
|
||||
timed_out = true;
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
|
||||
emit.call(self, 'timeout');
|
||||
self.cancel();
|
||||
}, duration);
|
||||
|
||||
this.emit = function() {
|
||||
if (!timed_out) {
|
||||
clearTimeout(timeout);
|
||||
emit.apply(self, arguments);
|
||||
}
|
||||
};
|
||||
this._timeout = delay;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -1,30 +1,42 @@
|
||||
var util = require('util');
|
||||
var extend = require('extend');
|
||||
'use strict';
|
||||
|
||||
function RippleError(code, message) {
|
||||
switch (typeof code) {
|
||||
case 'object':
|
||||
extend(this, code);
|
||||
break;
|
||||
const util = require('util');
|
||||
const _ = require('lodash');
|
||||
|
||||
case 'string':
|
||||
this.result = code;
|
||||
this.result_message = message;
|
||||
break;
|
||||
function RippleError(code?: any, message?: string) {
|
||||
if (code instanceof Error) {
|
||||
this.result = code;
|
||||
this.result_message = code.message;
|
||||
} else {
|
||||
switch (typeof code) {
|
||||
case 'object':
|
||||
_.extend(this, code);
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
this.result = code;
|
||||
this.result_message = message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.engine_result = this.result = (this.result || this.engine_result || this.error || 'Error');
|
||||
this.engine_result_message = this.result_message = (this.result_message || this.engine_result_message || this.error_message || 'Error');
|
||||
this.result_message = this.message = (this.result_message);
|
||||
this.engine_result = this.result = (this.result || this.engine_result ||
|
||||
this.error || 'Error');
|
||||
this.engine_result_message = this.result_message = (this.result_message ||
|
||||
this.engine_result_message || this.error_message || 'Error');
|
||||
this.message = this.result_message;
|
||||
|
||||
var stack;
|
||||
let stack;
|
||||
|
||||
if (!!Error.captureStackTrace) {
|
||||
if (Boolean(Error.captureStackTrace)) {
|
||||
Error.captureStackTrace(this, code || this);
|
||||
} else if ((stack = new Error().stack)) {
|
||||
this.stack = stack;
|
||||
} else {
|
||||
stack = new Error().stack;
|
||||
if (Boolean(stack)) {
|
||||
this.stack = stack;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(RippleError, Error);
|
||||
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// Seed support
|
||||
//
|
||||
|
||||
const {KeyPair, KeyType} = require('ripple-keypairs');
|
||||
const {decodeSeed, encodeSeed} = require('ripple-address-codec');
|
||||
const extend = require('extend');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const BN = require('bn.js');
|
||||
const hashjs = require('hash.js');
|
||||
|
||||
const UInt = require('./uint').UInt;
|
||||
|
||||
const Seed = extend(function() {
|
||||
this._value = NaN;
|
||||
this._type = KeyType.secp256k1;
|
||||
}, UInt);
|
||||
|
||||
Seed.width = 16;
|
||||
Seed.prototype = extend({}, UInt.prototype);
|
||||
Seed.prototype.constructor = Seed;
|
||||
|
||||
// value = NaN on error.
|
||||
// One day this will support rfc1751 too.
|
||||
Seed.prototype.parse_json = function(j) {
|
||||
if (typeof j === 'string') {
|
||||
if (!j.length) {
|
||||
this._value = NaN;
|
||||
} else {
|
||||
this.parse_base58(j);
|
||||
if (!this.is_valid()) {
|
||||
this.parse_hex(j);
|
||||
// XXX Should also try 1751
|
||||
}
|
||||
if (!this.is_valid() && j[0] !== 's') {
|
||||
this.parse_passphrase(j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.parse_base58 = function(j) {
|
||||
if (typeof j !== 'string') {
|
||||
throw new Error('Value must be a string');
|
||||
}
|
||||
if (!j.length || j[0] !== 's') {
|
||||
this._value = NaN;
|
||||
} else {
|
||||
try {
|
||||
const {bytes, type} = decodeSeed(j);
|
||||
this._value = new BN(bytes);
|
||||
this._type = type;
|
||||
} catch (e) {
|
||||
this._value = NaN;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.set_ed25519 = function() {
|
||||
this._type = KeyType.ed25519;
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.parse_passphrase = function(j) {
|
||||
if (typeof j !== 'string') {
|
||||
throw new Error('Passphrase must be a string');
|
||||
}
|
||||
|
||||
const phraseBytes = sjclcodec.bytes.fromBits(sjclcodec.utf8String.toBits(j));
|
||||
const hash = hashjs.sha512().update(phraseBytes).digest();
|
||||
this.parse_bytes(hash.slice(0, 16));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.to_json = function() {
|
||||
if (!(this.is_valid())) {
|
||||
return NaN;
|
||||
}
|
||||
return encodeSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
Seed.prototype.get_key = function() {
|
||||
if (!this.is_valid()) {
|
||||
throw new Error('Cannot generate keys from invalid seed!');
|
||||
}
|
||||
return KeyPair.fromSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
exports.Seed = Seed;
|
||||
@@ -8,6 +8,7 @@
|
||||
* SerializedObject.parse() or SerializedObject.serialize().
|
||||
*/
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const extend = require('extend');
|
||||
const BN = require('bn.js');
|
||||
@@ -99,7 +100,8 @@ SerializedType.serialize_varint = function(so, val) {
|
||||
|
||||
SerializedType.prototype.parse_varint = function(so) {
|
||||
const b1 = so.read(1)[0];
|
||||
let b2, b3;
|
||||
let b2;
|
||||
let b3;
|
||||
let result;
|
||||
|
||||
if (b1 > 254) {
|
||||
@@ -404,7 +406,7 @@ exports.Quality = new SerializedType({
|
||||
serialize: function(so, val) {
|
||||
let value;
|
||||
// if in format: amount/currency/issuer
|
||||
if (val.includes('/')) {
|
||||
if (_.includes(val, '/')) {
|
||||
const amount = Amount.from_json(val);
|
||||
|
||||
if (!amount.is_valid()) {
|
||||
@@ -415,7 +417,8 @@ exports.Quality = new SerializedType({
|
||||
value = new BigNumber(val);
|
||||
}
|
||||
|
||||
let hi = 0, lo = 0;
|
||||
let hi = 0;
|
||||
let lo = 0;
|
||||
|
||||
const offset = value.e - 15;
|
||||
if (val !== 0) {
|
||||
@@ -482,7 +485,8 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
valueBytes[0] |= 0x40;
|
||||
}
|
||||
} else {
|
||||
let hi = 0, lo = 0;
|
||||
let hi = 0;
|
||||
let lo = 0;
|
||||
|
||||
// First bit: non-native
|
||||
hi |= 1 << 31;
|
||||
@@ -517,7 +521,7 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
STCurrency.serialize(so, currency, true);
|
||||
|
||||
// Issuer (160-bit hash)
|
||||
so.append(amount.issuer().to_bytes());
|
||||
so.append(UInt160.from_json(amount.issuer()).to_bytes());
|
||||
}
|
||||
},
|
||||
parse: function(so) {
|
||||
@@ -833,7 +837,7 @@ exports.STMemo = new SerializedType({
|
||||
output.parsed_memo_data = convertHexToString(output.MemoData);
|
||||
}
|
||||
/* eslint-disable no-empty */
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we'll fail in case the content does not match what the MemoFormat
|
||||
// described
|
||||
|
||||
@@ -4,7 +4,6 @@ const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
const url = require('url');
|
||||
const HttpsProxyAgent = require('https-proxy-agent');
|
||||
const LRU = require('lru-cache');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const RippleError = require('./rippleerror').RippleError;
|
||||
@@ -447,6 +446,12 @@ Server.prototype.connect = function() {
|
||||
const parsed = url.parse(this._opts.url);
|
||||
const opts = url.parse(this._remote.proxy);
|
||||
opts.secureEndpoint = parsed.protocol === 'wss:';
|
||||
let HttpsProxyAgent;
|
||||
try {
|
||||
HttpsProxyAgent = require('https-proxy-agent');
|
||||
} catch (error) {
|
||||
throw new Error('"proxy" option is not supported in the browser');
|
||||
}
|
||||
const agent = new HttpsProxyAgent(opts);
|
||||
|
||||
this._ws = new WebSocket(this._opts.url, {agent: agent});
|
||||
@@ -687,6 +692,7 @@ Server.prototype._handleResponse = function(message) {
|
||||
remote: message
|
||||
});
|
||||
}
|
||||
request.emit('response', message);
|
||||
};
|
||||
|
||||
Server.prototype._handlePathFind = function(message) {
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
const lodash = require('lodash');
|
||||
const _ = require('lodash');
|
||||
const {deriveKeypair, sign} = require('ripple-keypairs');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const utils = require('./utils');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./amount').Currency;
|
||||
const UInt160 = require('./amount').UInt160;
|
||||
const Seed = require('./seed').Seed;
|
||||
const Currency = require('./currency').Currency;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const SerializedObject = require('./serializedobject').SerializedObject;
|
||||
const RippleError = require('./rippleerror').RippleError;
|
||||
const hashprefixes = require('./hashprefixes');
|
||||
const log = require('./log').internal.sub('transaction');
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
|
||||
/**
|
||||
* @constructor Transaction
|
||||
@@ -43,6 +45,7 @@ function Transaction(remote) {
|
||||
? this.remote.automatic_resubmission
|
||||
: true;
|
||||
this._maxFee = remoteExists ? this.remote.max_fee : undefined;
|
||||
this._lastLedgerOffset = remoteExists ? this.remote.last_ledger_offset : 3;
|
||||
this.state = 'unsubmitted';
|
||||
this.finalized = false;
|
||||
this.previousSigningHash = undefined;
|
||||
@@ -199,7 +202,7 @@ Transaction.from_json = function(j) {
|
||||
|
||||
Transaction.prototype.setJson =
|
||||
Transaction.prototype.parseJson = function(v) {
|
||||
this.tx_json = v;
|
||||
this.tx_json = _.merge({}, v);
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -358,7 +361,6 @@ Transaction.prototype._computeFee = function() {
|
||||
});
|
||||
|
||||
const midInd = Math.floor(fees.length / 2);
|
||||
|
||||
const median = fees.length % 2 === 0
|
||||
? Math.floor(0.5 + (fees[midInd] + fees[midInd - 1]) / 2)
|
||||
: fees[midInd];
|
||||
@@ -377,56 +379,67 @@ Transaction.prototype._computeFee = function() {
|
||||
* return `false`
|
||||
*/
|
||||
|
||||
Transaction.prototype.err = function(error, errorMessage) {
|
||||
this.emit('error', new RippleError(error, errorMessage));
|
||||
return false;
|
||||
};
|
||||
|
||||
Transaction.prototype.complete = function() {
|
||||
if (this.remote) {
|
||||
if (!this.remote.trusted && !this.remote.local_signing) {
|
||||
this.emit('error', new RippleError(
|
||||
'tejServerUntrusted', 'Attempt to give secret to untrusted server'));
|
||||
return false;
|
||||
}
|
||||
// Auto-fill the secret
|
||||
this._secret = this._secret || this.getSecret();
|
||||
|
||||
if (_.isUndefined(this._secret)) {
|
||||
return this.err('tejSecretUnknown', 'Missing secret');
|
||||
}
|
||||
|
||||
if (!this._secret) {
|
||||
this._secret = this.getSecret();
|
||||
if (this.remote && !(this.remote.local_signing || this.remote.trusted)) {
|
||||
return this.err(
|
||||
'tejServerUntrusted',
|
||||
'Attempt to give secret to untrusted server');
|
||||
}
|
||||
|
||||
// Try to auto-fill the secret
|
||||
if (!this._secret) {
|
||||
this.emit('error', new RippleError('tejSecretUnknown', 'Missing secret'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof this.tx_json.SigningPubKey === 'undefined') {
|
||||
if (_.isUndefined(this.tx_json.SigningPubKey)) {
|
||||
try {
|
||||
const seed = Seed.from_json(this._secret);
|
||||
const key = seed.get_key();
|
||||
this.tx_json.SigningPubKey = key.pubKeyHex();
|
||||
} catch(e) {
|
||||
this.emit('error', new RippleError(
|
||||
'tejSecretInvalid', 'Invalid secret'));
|
||||
return false;
|
||||
this.setSigningPubKey(this.getSigningPubKey());
|
||||
} catch (e) {
|
||||
return this.err('tejSecretInvalid', 'Invalid secret');
|
||||
}
|
||||
}
|
||||
|
||||
// If the Fee hasn't been set, one needs to be computed by
|
||||
// an assigned server
|
||||
if (this.remote && typeof this.tx_json.Fee === 'undefined') {
|
||||
if (this.remote.local_fee || !this.remote.trusted) {
|
||||
this.tx_json.Fee = this._computeFee();
|
||||
if (!this.tx_json.Fee) {
|
||||
this.emit('error', new RippleError('tejUnconnected'));
|
||||
return false;
|
||||
// Auto-fill transaction Fee
|
||||
if (_.isUndefined(this.tx_json.Fee)) {
|
||||
if (this.remote && (this.remote.local_fee || !this.remote.trusted)) {
|
||||
const computedFee = this._computeFee();
|
||||
|
||||
if (!computedFee) {
|
||||
// Unable to compute fee due to no connected servers
|
||||
return this.err('tejUnconnected');
|
||||
}
|
||||
|
||||
this.tx_json.Fee = computedFee;
|
||||
}
|
||||
}
|
||||
|
||||
if (Number(this.tx_json.Fee) > this._maxFee) {
|
||||
this.emit('error', new RippleError(
|
||||
'tejMaxFeeExceeded', 'Max fee exceeded'));
|
||||
return false;
|
||||
return this.err('tejMaxFeeExceeded', 'Max fee exceeded');
|
||||
}
|
||||
|
||||
// Set canonical flag - this enables canonicalized signature checking
|
||||
this.setCanonicalFlag();
|
||||
|
||||
return this.tx_json;
|
||||
};
|
||||
|
||||
Transaction.prototype.getSigningPubKey = function(secret) {
|
||||
return deriveKeypair(secret || this._secret).publicKey;
|
||||
};
|
||||
|
||||
Transaction.prototype.setSigningPubKey = function(key) {
|
||||
this.tx_json.SigningPubKey = key;
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.setCanonicalFlag = function() {
|
||||
if (this.remote && this.remote.local_signing && this.canonical) {
|
||||
this.tx_json.Flags |= Transaction.flags.Universal.FullyCanonicalSig;
|
||||
|
||||
@@ -435,7 +448,7 @@ Transaction.prototype.complete = function() {
|
||||
this.tx_json.Flags = this.tx_json.Flags >>> 0;
|
||||
}
|
||||
|
||||
return this.tx_json;
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.serialize = function() {
|
||||
@@ -453,6 +466,14 @@ Transaction.prototype.signingData = function() {
|
||||
return so;
|
||||
};
|
||||
|
||||
Transaction.prototype.multiSigningData = function(account) {
|
||||
const so = new SerializedObject();
|
||||
so.append(hashprefixes.HASH_TX_MULTISIGN_BYTES);
|
||||
so.parse_json(this.tx_json);
|
||||
so.append(UInt160.from_json(account).to_bytes());
|
||||
return so;
|
||||
};
|
||||
|
||||
Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
|
||||
let prefix;
|
||||
|
||||
@@ -469,12 +490,13 @@ Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
|
||||
return asUINT256 ? hash : hash.to_hex();
|
||||
};
|
||||
|
||||
Transaction.prototype.sign = function() {
|
||||
const seed = Seed.from_json(this._secret);
|
||||
Transaction.prototype.sign = function(secret) {
|
||||
if (this.hasMultiSigners()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const prev_sig = this.tx_json.TxnSignature;
|
||||
|
||||
delete this.tx_json.TxnSignature;
|
||||
|
||||
const hash = this.signingHash();
|
||||
|
||||
// If the hash is the same, we can re-use the previous signature
|
||||
@@ -483,9 +505,9 @@ Transaction.prototype.sign = function() {
|
||||
return this;
|
||||
}
|
||||
|
||||
const key = seed.get_key();
|
||||
const hex = key.signHex(this.signingData().buffer);
|
||||
this.tx_json.TxnSignature = hex;
|
||||
const keypair = deriveKeypair(secret || this._secret);
|
||||
this.tx_json.TxnSignature = sign(this.signingData().buffer,
|
||||
keypair.privateKey);
|
||||
this.previousSigningHash = hash;
|
||||
|
||||
return this;
|
||||
@@ -499,7 +521,7 @@ Transaction.prototype.sign = function() {
|
||||
*/
|
||||
|
||||
Transaction.prototype.addId = function(id) {
|
||||
if (!lodash.contains(this.submittedIDs, id)) {
|
||||
if (!_.contains(this.submittedIDs, id)) {
|
||||
this.submittedIDs.unshift(id);
|
||||
}
|
||||
};
|
||||
@@ -515,7 +537,7 @@ Transaction.prototype.addId = function(id) {
|
||||
*/
|
||||
|
||||
Transaction.prototype.findId = function(cache) {
|
||||
const cachedTransactionID = lodash.detect(this.submittedIDs, function(id) {
|
||||
const cachedTransactionID = _.detect(this.submittedIDs, function(id) {
|
||||
return cache.hasOwnProperty(id);
|
||||
});
|
||||
return cache[cachedTransactionID];
|
||||
@@ -541,19 +563,30 @@ Transaction.prototype.clientID = function(id) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set LastLedgerSequence as the absolute last ledger sequence the transaction
|
||||
* is valid for. LastLedgerSequence is set automatically if not set using this
|
||||
* method
|
||||
*
|
||||
* @param {Number} ledger index
|
||||
*/
|
||||
Transaction.prototype.setLastLedgerSequenceOffset = function(offset) {
|
||||
this._lastLedgerOffset = offset;
|
||||
};
|
||||
|
||||
Transaction.prototype.setLastLedgerSequence =
|
||||
Transaction.prototype.getLastLedgerSequenceOffset = function() {
|
||||
return this._lastLedgerOffset;
|
||||
};
|
||||
|
||||
Transaction.prototype.lastLedger =
|
||||
Transaction.prototype.setLastLedger =
|
||||
Transaction.prototype.lastLedger = function(sequence) {
|
||||
this._setUInt32('LastLedgerSequence', sequence);
|
||||
Transaction.prototype.setLastLedgerSequence = function(sequence) {
|
||||
if (!_.isUndefined(sequence)) {
|
||||
this._setUInt32('LastLedgerSequence', sequence);
|
||||
} else {
|
||||
// Autofill LastLedgerSequence
|
||||
assert(this.remote, 'Unable to set LastLedgerSequence, missing Remote');
|
||||
|
||||
this._setUInt32('LastLedgerSequence',
|
||||
this.remote.getLedgerSequenceSync() + 1
|
||||
+ this.getLastLedgerSequenceOffset());
|
||||
}
|
||||
|
||||
this._setLastLedger = true;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -582,9 +615,24 @@ Transaction.prototype.maxFee = function(fee) {
|
||||
* @returns {Transaction} calling instance for chaining
|
||||
*/
|
||||
Transaction.prototype.setFixedFee = function(fee) {
|
||||
if (typeof fee === 'number' && fee >= 0) {
|
||||
this._setFixedFee = true;
|
||||
return this.setFee(fee, {fixed: true});
|
||||
};
|
||||
|
||||
Transaction.prototype.setFee = function(fee, options = {}) {
|
||||
if (_.isNumber(fee) && fee >= 0) {
|
||||
this.tx_json.Fee = String(fee);
|
||||
if (options.fixed) {
|
||||
this._setFixedFee = true;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.setSequence = function(sequence) {
|
||||
if (_.isNumber(sequence)) {
|
||||
this._setUInt32('Sequence', sequence);
|
||||
this._setSequence = true;
|
||||
}
|
||||
|
||||
return this;
|
||||
@@ -606,7 +654,7 @@ Transaction.prototype.secret = function(secret) {
|
||||
};
|
||||
|
||||
Transaction.prototype.setType = function(type) {
|
||||
if (lodash.isUndefined(Transaction.formats, type)) {
|
||||
if (_.isUndefined(Transaction.formats, type)) {
|
||||
throw new Error('TransactionType must be a valid transaction type');
|
||||
}
|
||||
|
||||
@@ -616,14 +664,14 @@ Transaction.prototype.setType = function(type) {
|
||||
};
|
||||
|
||||
Transaction.prototype._setUInt32 = function(name, value, options_) {
|
||||
const options = lodash.merge({}, options_);
|
||||
const options = _.merge({}, options_);
|
||||
const isValidUInt32 = typeof value === 'number'
|
||||
&& value >= 0 && value < Math.pow(256, 4);
|
||||
|
||||
if (!isValidUInt32) {
|
||||
throw new Error(name + ' must be a valid UInt32');
|
||||
}
|
||||
if (!lodash.isUndefined(options.min_value) && value < options.min_value) {
|
||||
if (!_.isUndefined(options.min_value) && value < options.min_value) {
|
||||
throw new Error(name + ' must be >= ' + options.min_value);
|
||||
}
|
||||
|
||||
@@ -660,7 +708,7 @@ Transaction.prototype.setAccount = function(account) {
|
||||
};
|
||||
|
||||
Transaction.prototype._setAmount = function(name, amount, options_) {
|
||||
const options = lodash.merge({no_native: false}, options_);
|
||||
const options = _.merge({no_native: false}, options_);
|
||||
const parsedAmount = Amount.from_json(amount);
|
||||
|
||||
if (parsedAmount.is_negative()) {
|
||||
@@ -675,7 +723,7 @@ Transaction.prototype._setAmount = function(name, amount, options_) {
|
||||
if (!(isNative || parsedAmount.currency().is_valid())) {
|
||||
throw new Error(name + ' must have a valid currency');
|
||||
}
|
||||
if (!(isNative || parsedAmount.issuer().is_valid())) {
|
||||
if (!(isNative || isValidAddress(parsedAmount.issuer()))) {
|
||||
throw new Error(name + ' must have a valid issuer');
|
||||
}
|
||||
|
||||
@@ -689,7 +737,7 @@ Transaction.prototype._setHash256 = function(name, value, options_) {
|
||||
throw new Error(name + ' must be a valid Hash256');
|
||||
}
|
||||
|
||||
const options = lodash.merge({pad: false}, options_);
|
||||
const options = _.merge({pad: false}, options_);
|
||||
let hash256 = value;
|
||||
|
||||
if (options.pad) {
|
||||
@@ -752,6 +800,11 @@ Transaction.prototype.setFlags = function(flags) {
|
||||
return this;
|
||||
};
|
||||
|
||||
function convertStringToHex(string) {
|
||||
const utf8String = sjclcodec.utf8String.toBits(string);
|
||||
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a Memo to transaction.
|
||||
*
|
||||
@@ -769,7 +822,7 @@ Transaction.prototype.addMemo = function(options_) {
|
||||
let options;
|
||||
|
||||
if (typeof options_ === 'object') {
|
||||
options = lodash.merge({}, options_);
|
||||
options = _.merge({}, options_);
|
||||
} else {
|
||||
options = {
|
||||
memoType: arguments[0],
|
||||
@@ -778,11 +831,6 @@ Transaction.prototype.addMemo = function(options_) {
|
||||
};
|
||||
}
|
||||
|
||||
function convertStringToHex(string) {
|
||||
const utf8String = sjclcodec.utf8String.toBits(string);
|
||||
return sjclcodec.hex.fromBits(utf8String).toUpperCase();
|
||||
}
|
||||
|
||||
const memo = {};
|
||||
const memoRegex = Transaction.MEMO_REGEX;
|
||||
let memoType = options.memoType;
|
||||
@@ -790,7 +838,7 @@ Transaction.prototype.addMemo = function(options_) {
|
||||
let memoData = options.memoData;
|
||||
|
||||
if (memoType) {
|
||||
if (!(lodash.isString(memoType) && memoRegex.test(memoType))) {
|
||||
if (!(_.isString(memoType) && memoRegex.test(memoType))) {
|
||||
throw new Error(
|
||||
'MemoType must be a string containing only valid URL characters');
|
||||
}
|
||||
@@ -803,7 +851,7 @@ Transaction.prototype.addMemo = function(options_) {
|
||||
}
|
||||
|
||||
if (memoFormat) {
|
||||
if (!(lodash.isString(memoFormat) && memoRegex.test(memoFormat))) {
|
||||
if (!(_.isString(memoFormat) && memoRegex.test(memoFormat))) {
|
||||
throw new Error(
|
||||
'MemoFormat must be a string containing only valid URL characters');
|
||||
}
|
||||
@@ -857,15 +905,15 @@ Transaction.prototype.accountSet = function(options_) {
|
||||
let options;
|
||||
|
||||
if (typeof options_ === 'object') {
|
||||
options = lodash.merge({}, options_);
|
||||
options = _.merge({}, options_);
|
||||
|
||||
if (lodash.isUndefined(options.account)) {
|
||||
if (_.isUndefined(options.account)) {
|
||||
options.account = options.src;
|
||||
}
|
||||
if (lodash.isUndefined(options.set_flag)) {
|
||||
if (_.isUndefined(options.set_flag)) {
|
||||
options.set_flag = options.set;
|
||||
}
|
||||
if (lodash.isUndefined(options.clear_flag)) {
|
||||
if (_.isUndefined(options.clear_flag)) {
|
||||
options.clear_flag = options.clear;
|
||||
}
|
||||
} else {
|
||||
@@ -879,10 +927,10 @@ Transaction.prototype.accountSet = function(options_) {
|
||||
this.setType('AccountSet');
|
||||
this.setAccount(options.account);
|
||||
|
||||
if (!lodash.isUndefined(options.set_flag)) {
|
||||
if (!_.isUndefined(options.set_flag)) {
|
||||
this.setSetFlag(options.set_flag);
|
||||
}
|
||||
if (!lodash.isUndefined(options.clear_flag)) {
|
||||
if (!_.isUndefined(options.clear_flag)) {
|
||||
this.setClearFlag(options.clear_flag);
|
||||
}
|
||||
|
||||
@@ -899,7 +947,7 @@ Transaction.prototype.setAccountSetFlag = function(name, value) {
|
||||
: accountSetFlags['asf' + flagValue];
|
||||
}
|
||||
|
||||
if (!lodash.contains(lodash.values(accountSetFlags), flagValue)) {
|
||||
if (!_.contains(_.values(accountSetFlags), flagValue)) {
|
||||
throw new Error(name + ' must be a valid AccountSet flag');
|
||||
}
|
||||
|
||||
@@ -956,9 +1004,9 @@ Transaction.prototype.setRegularKey = function(options_) {
|
||||
let options;
|
||||
|
||||
if (typeof options_ === 'object') {
|
||||
options = lodash.merge({}, options_);
|
||||
options = _.merge({}, options_);
|
||||
|
||||
if (lodash.isUndefined(options.account)) {
|
||||
if (_.isUndefined(options.account)) {
|
||||
options.account = options.src;
|
||||
}
|
||||
} else {
|
||||
@@ -971,7 +1019,7 @@ Transaction.prototype.setRegularKey = function(options_) {
|
||||
this.setType('SetRegularKey');
|
||||
this.setAccount(options.account);
|
||||
|
||||
if (!lodash.isUndefined(options.regular_key)) {
|
||||
if (!_.isUndefined(options.regular_key)) {
|
||||
this._setAccount('RegularKey', options.regular_key);
|
||||
}
|
||||
|
||||
@@ -992,9 +1040,9 @@ Transaction.prototype.rippleLineSet = function(options_) {
|
||||
let options;
|
||||
|
||||
if (typeof options_ === 'object') {
|
||||
options = lodash.merge({}, options_);
|
||||
options = _.merge({}, options_);
|
||||
|
||||
if (lodash.isUndefined(options.account)) {
|
||||
if (_.isUndefined(options.account)) {
|
||||
options.account = options.src;
|
||||
}
|
||||
} else {
|
||||
@@ -1009,13 +1057,13 @@ Transaction.prototype.rippleLineSet = function(options_) {
|
||||
this.setType('TrustSet');
|
||||
this.setAccount(options.account);
|
||||
|
||||
if (!lodash.isUndefined(options.limit)) {
|
||||
if (!_.isUndefined(options.limit)) {
|
||||
this.setLimit(options.limit);
|
||||
}
|
||||
if (!lodash.isUndefined(options.quality_in)) {
|
||||
if (!_.isUndefined(options.quality_in)) {
|
||||
this.setQualityIn(options.quality_in);
|
||||
}
|
||||
if (!lodash.isUndefined(options.quality_out)) {
|
||||
if (!_.isUndefined(options.quality_out)) {
|
||||
this.setQualityOut(options.quality_out);
|
||||
}
|
||||
|
||||
@@ -1057,12 +1105,12 @@ Transaction.prototype.payment = function(options_) {
|
||||
let options;
|
||||
|
||||
if (typeof options_ === 'object') {
|
||||
options = lodash.merge({}, options_);
|
||||
options = _.merge({}, options_);
|
||||
|
||||
if (lodash.isUndefined(options.account)) {
|
||||
if (_.isUndefined(options.account)) {
|
||||
options.account = options.src || options.from;
|
||||
}
|
||||
if (lodash.isUndefined(options.destination)) {
|
||||
if (_.isUndefined(options.destination)) {
|
||||
options.destination = options.dst || options.to;
|
||||
}
|
||||
} else {
|
||||
@@ -1241,18 +1289,18 @@ Transaction.prototype.offerCreate = function(options_) {
|
||||
let options;
|
||||
|
||||
if (typeof options_ === 'object') {
|
||||
options = lodash.merge({}, options_);
|
||||
options = _.merge({}, options_);
|
||||
|
||||
if (lodash.isUndefined(options.account)) {
|
||||
if (_.isUndefined(options.account)) {
|
||||
options.account = options.src;
|
||||
}
|
||||
if (lodash.isUndefined(options.taker_pays)) {
|
||||
if (_.isUndefined(options.taker_pays)) {
|
||||
options.taker_pays = options.buy;
|
||||
}
|
||||
if (lodash.isUndefined(options.taker_gets)) {
|
||||
if (_.isUndefined(options.taker_gets)) {
|
||||
options.taker_gets = options.sell;
|
||||
}
|
||||
if (lodash.isUndefined(options.offer_sequence)) {
|
||||
if (_.isUndefined(options.offer_sequence)) {
|
||||
options.offer_sequence = options.cancel_sequence || options.sequence;
|
||||
}
|
||||
} else {
|
||||
@@ -1270,10 +1318,10 @@ Transaction.prototype.offerCreate = function(options_) {
|
||||
this.setTakerGets(options.taker_gets);
|
||||
this.setTakerPays(options.taker_pays);
|
||||
|
||||
if (!lodash.isUndefined(options.expiration)) {
|
||||
if (!_.isUndefined(options.expiration)) {
|
||||
this.setExpiration(options.expiration);
|
||||
}
|
||||
if (!lodash.isUndefined(options.offer_sequence)) {
|
||||
if (!_.isUndefined(options.offer_sequence)) {
|
||||
this.setOfferSequence(options.offer_sequence);
|
||||
}
|
||||
|
||||
@@ -1311,12 +1359,12 @@ Transaction.prototype.offerCancel = function(options_) {
|
||||
let options;
|
||||
|
||||
if (typeof options_ === 'object') {
|
||||
options = lodash.merge({}, options_);
|
||||
options = _.merge({}, options_);
|
||||
|
||||
if (lodash.isUndefined(options.account)) {
|
||||
if (_.isUndefined(options.account)) {
|
||||
options.account = options.src;
|
||||
}
|
||||
if (lodash.isUndefined(options.offer_sequence)) {
|
||||
if (_.isUndefined(options.offer_sequence)) {
|
||||
options.offer_sequence = options.sequence || options.cancel_sequence;
|
||||
}
|
||||
} else {
|
||||
@@ -1333,16 +1381,48 @@ Transaction.prototype.offerCancel = function(options_) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction._prepareSignerEntry = function(signer) {
|
||||
const {account, weight} = signer;
|
||||
|
||||
assert(UInt160.is_valid(account), 'Signer account invalid');
|
||||
assert(_.isNumber(weight), 'Signer weight missing');
|
||||
assert(weight > 0 && weight <= 65535, 'Signer weight must be 1-65535');
|
||||
|
||||
return {
|
||||
SignerEntry: {
|
||||
Account: account,
|
||||
SignerWeight: weight
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Transaction.prototype.setSignerList = function(options = {}) {
|
||||
this.setType('SignerListSet');
|
||||
this.setAccount(options.account);
|
||||
this.setSignerQuorum(options.signerQuorum);
|
||||
|
||||
if (!_.isEmpty(options.signers)) {
|
||||
this.tx_json.SignerEntries =
|
||||
options.signers.map(Transaction._prepareSignerEntry);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.setSignerQuorum = function(quorum) {
|
||||
this._setUInt32('SignerQuorum', quorum);
|
||||
};
|
||||
|
||||
/**
|
||||
* Submit transaction to the network
|
||||
*
|
||||
* @param [Function] callback
|
||||
*/
|
||||
|
||||
Transaction.prototype.submit = function(callback) {
|
||||
Transaction.prototype.submit = function(callback = function() {}) {
|
||||
const self = this;
|
||||
|
||||
this.callback = (typeof callback === 'function') ? callback : function() {};
|
||||
this.callback = callback;
|
||||
|
||||
this._errorHandler = function transactionError(error_, message) {
|
||||
let error = error_;
|
||||
@@ -1391,7 +1471,7 @@ Transaction.prototype.summary = function() {
|
||||
submissionAttempts: this.attempts,
|
||||
submitIndex: this.submitIndex,
|
||||
initialSubmitIndex: this.initialSubmitIndex,
|
||||
lastLedgerSequence: this.lastLedgerSequence,
|
||||
lastLedgerSequence: this.tx_json.LastLedgerSequence,
|
||||
state: this.state,
|
||||
finalized: this.finalized
|
||||
};
|
||||
@@ -1413,6 +1493,177 @@ Transaction.prototype.summary = function() {
|
||||
return txSummary;
|
||||
};
|
||||
|
||||
exports.Transaction = Transaction;
|
||||
/**
|
||||
* Construct a 'SuspendedPaymentCreate' transaction
|
||||
*
|
||||
* Relevant setters:
|
||||
* - setSourceTag()
|
||||
* - setFlags()
|
||||
* - setDigest()
|
||||
* - setAllowCancelAfter()
|
||||
* - setAllowExecuteAfter()
|
||||
*
|
||||
* @param {String} options.account source account
|
||||
* @param {String} options.destination account
|
||||
* @param {Amount} options.amount payment amount
|
||||
*/
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
Transaction.prototype.suspendedPaymentCreate = function(options) {
|
||||
this.setType('SuspendedPaymentCreate');
|
||||
this.setAccount(options.account);
|
||||
this.setDestination(options.destination);
|
||||
this.setAmount(options.amount);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a 'SuspendedPaymentFinish' transaction
|
||||
*
|
||||
* Relevant setters:
|
||||
* - setSourceTag()
|
||||
* - setFlags()
|
||||
* - setOwner()
|
||||
* - setOfferSequence()
|
||||
* - setMethod()
|
||||
* - setDigest()
|
||||
* - setProof()
|
||||
*
|
||||
* @param {String} options.account source account
|
||||
* @param {String} options.owner SuspendedPaymentCreate's Account
|
||||
* @param {Integer} options.paymentSequence SuspendedPaymentCreate's Sequence
|
||||
*/
|
||||
|
||||
Transaction.prototype.suspendedPaymentFinish = function(options) {
|
||||
this.setType('SuspendedPaymentFinish');
|
||||
this.setAccount(options.account);
|
||||
this.setOwner(options.owner);
|
||||
this.setOfferSequence(options.paymentSequence);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a 'SuspendedPaymentCancel' transaction
|
||||
*
|
||||
* Relevant setters:
|
||||
* - setSourceTag()
|
||||
* - setFlags()
|
||||
* - setOwner()
|
||||
* - setOfferSequence()
|
||||
*
|
||||
* @param {String} options.account source account
|
||||
* @param {String} options.owner SuspendedPaymentCreate's Account
|
||||
* @param {Integer} options.paymentSequence SuspendedPaymentCreate's Sequence
|
||||
*/
|
||||
|
||||
Transaction.prototype.suspendedPaymentCancel = function(options) {
|
||||
this.setType('SuspendedPaymentCancel');
|
||||
this.setAccount(options.account);
|
||||
this.setOwner(options.owner);
|
||||
this.setOfferSequence(options.paymentSequence);
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.setDigest = function(digest) {
|
||||
return this._setHash256('Digest', digest);
|
||||
};
|
||||
|
||||
Transaction.prototype.setAllowCancelAfter = function(after) {
|
||||
return this._setUInt32('CancelAfter', utils.time.toRipple(after));
|
||||
};
|
||||
|
||||
Transaction.prototype.setAllowExecuteAfter = function(after) {
|
||||
return this._setUInt32('FinishAfter', utils.time.toRipple(after));
|
||||
};
|
||||
|
||||
Transaction.prototype.setOwner = function(owner) {
|
||||
return this._setAccount('Owner', owner);
|
||||
};
|
||||
|
||||
Transaction.prototype.setMethod = function(method) {
|
||||
return this._setUInt8('Method', method);
|
||||
};
|
||||
|
||||
Transaction.prototype.setProof = function(proof) {
|
||||
this.tx_json.Proof = convertStringToHex(proof);
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype._setUInt8 = function(name, value) {
|
||||
const isValidUInt8 = typeof value === 'number' && value >= 0 && value < 256;
|
||||
if (!isValidUInt8) {
|
||||
throw new Error(name + ' must be a valid UInt8');
|
||||
}
|
||||
this.tx_json[name] = value;
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.setSigners = function(signers) {
|
||||
if (_.isArray(signers)) {
|
||||
this.tx_json.Signers = signers;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.addMultiSigner = function(signer) {
|
||||
assert(UInt160.is_valid(signer.Account), 'Signer must have a valid Account');
|
||||
|
||||
if (_.isUndefined(this.tx_json.Signers)) {
|
||||
this.tx_json.Signers = [];
|
||||
}
|
||||
|
||||
this.tx_json.Signers.push({Signer: signer});
|
||||
|
||||
this.tx_json.Signers.sort((a, b) => {
|
||||
return UInt160.from_json(a.Signer.Account)
|
||||
.cmp(UInt160.from_json(b.Signer.Account));
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.hasMultiSigners = function() {
|
||||
return !_.isEmpty(this.tx_json.Signers);
|
||||
};
|
||||
|
||||
Transaction.prototype.getMultiSigners = function() {
|
||||
return this.tx_json.Signers;
|
||||
};
|
||||
|
||||
Transaction.prototype.getMultiSigningJson = function() {
|
||||
assert(this.tx_json.Sequence, 'Sequence must be set before multi-signing');
|
||||
assert(this.tx_json.Fee, 'Fee must be set before multi-signing');
|
||||
|
||||
if (_.isUndefined(this.tx_json.LastLedgerSequence)) {
|
||||
// Auto-fill LastLedgerSequence
|
||||
this.setLastLedgerSequence();
|
||||
}
|
||||
|
||||
const cleanedJson = _.omit(this.tx_json, [
|
||||
'SigningPubKey',
|
||||
'Signers',
|
||||
'TxnSignature'
|
||||
]);
|
||||
|
||||
const signingTx = Transaction.from_json(cleanedJson);
|
||||
signingTx.remote = this.remote;
|
||||
signingTx.setSigningPubKey('');
|
||||
signingTx.setCanonicalFlag();
|
||||
|
||||
return signingTx.tx_json;
|
||||
};
|
||||
|
||||
Transaction.prototype.multiSign = function(account, secret) {
|
||||
const signingData = this.multiSigningData(account);
|
||||
const keypair = deriveKeypair(secret);
|
||||
|
||||
const signer = {
|
||||
Account: account,
|
||||
TxnSignature: sign(signingData.buffer, keypair.privateKey),
|
||||
SigningPubKey: keypair.publicKey
|
||||
};
|
||||
|
||||
return signer;
|
||||
};
|
||||
|
||||
exports.Transaction = Transaction;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const util = require('util');
|
||||
const assert = require('assert');
|
||||
const async = require('async');
|
||||
@@ -20,13 +21,12 @@ function TransactionManager(account) {
|
||||
const self = this;
|
||||
|
||||
this._account = account;
|
||||
this._accountID = account._account_id;
|
||||
this._accountID = account._address;
|
||||
this._remote = account._remote;
|
||||
this._nextSequence = undefined;
|
||||
this._maxFee = this._remote.max_fee;
|
||||
this._maxAttempts = this._remote.max_attempts;
|
||||
this._submissionTimeout = this._remote.submission_timeout;
|
||||
this._lastLedgerOffset = this._remote.last_ledger_offset;
|
||||
this._pending = new PendingQueue();
|
||||
|
||||
this._account.on('transaction-outbound', function(res) {
|
||||
@@ -529,6 +529,10 @@ TransactionManager.prototype._prepareRequest = function(tx) {
|
||||
const hash = tx.hash(null, null, serialized);
|
||||
tx.addId(hash);
|
||||
} else {
|
||||
if (tx.hasMultiSigners()) {
|
||||
submitRequest.message.command = 'submit_multisigned';
|
||||
}
|
||||
|
||||
// ND: `build_path` is completely ignored when doing local signing as
|
||||
// `Paths` is a component of the signed blob, the `tx_blob` is signed,
|
||||
// sealed and delivered, and the txn unmodified.
|
||||
@@ -568,6 +572,11 @@ TransactionManager.prototype._request = function(tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number(tx.tx_json.Fee) > tx._maxFee) {
|
||||
tx.emit('error', new RippleError('tejMaxFeeExceeded'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (remote.trace) {
|
||||
log.info('submit transaction:', tx.tx_json);
|
||||
}
|
||||
@@ -672,24 +681,12 @@ TransactionManager.prototype._request = function(tx) {
|
||||
}
|
||||
}
|
||||
|
||||
tx.submitIndex = this._remote._ledger_current_index;
|
||||
tx.submitIndex = this._remote.getLedgerSequenceSync() + 1;
|
||||
|
||||
if (tx.attempts === 0) {
|
||||
tx.initialSubmitIndex = tx.submitIndex;
|
||||
}
|
||||
|
||||
if (!tx._setLastLedger) {
|
||||
// Honor LastLedgerSequence set with tx.lastLedger()
|
||||
tx.tx_json.LastLedgerSequence = tx.initialSubmitIndex
|
||||
+ this._lastLedgerOffset;
|
||||
}
|
||||
|
||||
tx.lastLedgerSequence = tx.tx_json.LastLedgerSequence;
|
||||
|
||||
if (remote.local_signing) {
|
||||
tx.sign();
|
||||
}
|
||||
|
||||
const submitRequest = this._prepareRequest(tx);
|
||||
submitRequest.once('error', submitted);
|
||||
submitRequest.once('success', submitted);
|
||||
@@ -701,7 +698,8 @@ TransactionManager.prototype._request = function(tx) {
|
||||
|
||||
tx.emit('postsubmit');
|
||||
|
||||
submitRequest.timeout(self._submissionTimeout, requestTimeout);
|
||||
submitRequest.setTimeout(self._submissionTimeout);
|
||||
submitRequest.once('timeout', requestTimeout);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -726,9 +724,18 @@ TransactionManager.prototype.submit = function(tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof tx.tx_json.Sequence !== 'number') {
|
||||
if (!_.isNumber(tx.tx_json.Sequence)) {
|
||||
// Honor manually-set sequences
|
||||
tx.tx_json.Sequence = this._nextSequence++;
|
||||
tx.setSequence(this._nextSequence++);
|
||||
}
|
||||
|
||||
if (_.isUndefined(tx.tx_json.LastLedgerSequence)) {
|
||||
tx.setLastLedgerSequence();
|
||||
}
|
||||
|
||||
if (tx.hasMultiSigners()) {
|
||||
tx.setResubmittable(false);
|
||||
tx.setSigningPubKey('');
|
||||
}
|
||||
|
||||
tx.once('cleanup', function() {
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
var extend = require('extend');
|
||||
var UInt = require('./uint').UInt;
|
||||
const utils = require('./utils');
|
||||
const extend = require('extend');
|
||||
const UInt = require('./uint').UInt;
|
||||
|
||||
//
|
||||
// UInt128 support
|
||||
//
|
||||
|
||||
var UInt128 = extend(function() {
|
||||
const UInt128 = extend(function() {
|
||||
this._value = NaN;
|
||||
}, UInt);
|
||||
|
||||
UInt128.width = 16;
|
||||
UInt128.prototype = extend({}, UInt.prototype);
|
||||
UInt128.prototype = Object.create(extend({}, UInt.prototype));
|
||||
UInt128.prototype.constructor = UInt128;
|
||||
|
||||
var HEX_ZERO = UInt128.HEX_ZERO = '00000000000000000000000000000000';
|
||||
var HEX_ONE = UInt128.HEX_ONE = '00000000000000000000000000000000';
|
||||
const HEX_ZERO = UInt128.HEX_ZERO = '00000000000000000000000000000000';
|
||||
const HEX_ONE = UInt128.HEX_ONE = '00000000000000000000000000000000';
|
||||
|
||||
UInt128.STR_ZERO = utils.hexToString(HEX_ZERO);
|
||||
UInt128.STR_ONE = utils.hexToString(HEX_ONE);
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
var extend = require('extend');
|
||||
const utils = require('./utils');
|
||||
const extend = require('extend');
|
||||
|
||||
var UInt = require('./uint').UInt;
|
||||
var Base = require('./base').Base;
|
||||
const UInt = require('./uint').UInt;
|
||||
const Base = require('./base').Base;
|
||||
|
||||
//
|
||||
// UInt160 support
|
||||
//
|
||||
|
||||
var UInt160 = extend(function() {
|
||||
const UInt160 = extend(function() {
|
||||
this._value = NaN;
|
||||
this._version_byte = undefined;
|
||||
this._update();
|
||||
}, UInt);
|
||||
|
||||
UInt160.width = 20;
|
||||
UInt160.prototype = extend({}, UInt.prototype);
|
||||
UInt160.prototype = Object.create(extend({}, UInt.prototype));
|
||||
UInt160.prototype.constructor = UInt160;
|
||||
|
||||
var HEX_ZERO = UInt160.HEX_ZERO = '0000000000000000000000000000000000000000';
|
||||
var HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000001';
|
||||
const HEX_ZERO = UInt160.HEX_ZERO = '0000000000000000000000000000000000000000';
|
||||
const HEX_ONE = UInt160.HEX_ONE = '0000000000000000000000000000000000000001';
|
||||
|
||||
UInt160.ACCOUNT_ZERO = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp';
|
||||
UInt160.ACCOUNT_ONE = 'rrrrrrrrrrrrrrrrrrrrBZbvji';
|
||||
@@ -74,13 +74,12 @@ UInt160.prototype.parse_generic = function(j) {
|
||||
};
|
||||
|
||||
// XXX Json form should allow 0 and 1, C++ doesn't currently allow it.
|
||||
UInt160.prototype.to_json = function(opts) {
|
||||
opts = opts || {};
|
||||
UInt160.prototype.to_json = function(opts = {}) {
|
||||
|
||||
if (this.is_valid()) {
|
||||
// If this value has a type, return a Base58 encoded string.
|
||||
if (typeof this._version_byte === 'number') {
|
||||
var output = Base.encode_check(this._version_byte, this.to_bytes());
|
||||
let output = Base.encode_check(this._version_byte, this.to_bytes());
|
||||
|
||||
if (opts.gateways && output in opts.gateways) {
|
||||
output = opts.gateways[output];
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
var utils = require('./utils');
|
||||
var extend = require('extend');
|
||||
var UInt = require('./uint').UInt;
|
||||
const utils = require('./utils');
|
||||
const extend = require('extend');
|
||||
const UInt = require('./uint').UInt;
|
||||
|
||||
//
|
||||
// UInt256 support
|
||||
//
|
||||
|
||||
var UInt256 = extend(function() {
|
||||
const UInt256 = extend(function() {
|
||||
this._value = NaN;
|
||||
}, UInt);
|
||||
|
||||
UInt256.width = 32;
|
||||
UInt256.prototype = extend({}, UInt.prototype);
|
||||
UInt256.prototype = Object.create(extend({}, UInt.prototype));
|
||||
UInt256.prototype.constructor = UInt256;
|
||||
|
||||
var HEX_ZERO = UInt256.HEX_ZERO = '00000000000000000000000000000000' +
|
||||
const HEX_ZERO = UInt256.HEX_ZERO = '00000000000000000000000000000000' +
|
||||
'00000000000000000000000000000000';
|
||||
|
||||
var HEX_ONE = UInt256.HEX_ONE = '00000000000000000000000000000000' +
|
||||
const HEX_ONE = UInt256.HEX_ONE = '00000000000000000000000000000000' +
|
||||
'00000000000000000000000000000001';
|
||||
|
||||
UInt256.STR_ZERO = utils.hexToString(HEX_ZERO);
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
|
||||
const BigNumber = GlobalBigNumber.another({
|
||||
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
|
||||
DECIMAL_PLACES: 40
|
||||
});
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
class Value {
|
||||
|
||||
constructor(value: string | BigNumber) {
|
||||
if (this.constructor === 'Value') {
|
||||
throw new Error(
|
||||
'Cannot instantiate Value directly, it is an abstract base class');
|
||||
}
|
||||
this._value = new BigNumber(value);
|
||||
}
|
||||
|
||||
static getBNRoundDown() {
|
||||
return BigNumber.ROUND_DOWN;
|
||||
}
|
||||
|
||||
abs() {
|
||||
const result = this._value.abs();
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
add(addend: Value) {
|
||||
assert(this.constructor === addend.constructor);
|
||||
const result = this._value.plus(addend._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
subtract(subtrahend: Value) {
|
||||
assert(this.constructor === subtrahend.constructor);
|
||||
const result = this._value.minus(subtrahend._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
multiply(multiplicand: Value) {
|
||||
const result = this._value.times(multiplicand._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
divide(divisor: Value) {
|
||||
if (divisor.isZero()) {
|
||||
throw new Error('divide by zero');
|
||||
}
|
||||
const result = this._value.dividedBy(divisor._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
invert() {
|
||||
const result = (new BigNumber(this._value)).toPower(-1);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
round(decimalPlaces: number, roundingMode: number) {
|
||||
const result = this._value.round(decimalPlaces, roundingMode);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
toFixed(decimalPlaces: number, roundingMode: number) {
|
||||
return this._value.toFixed(decimalPlaces, roundingMode);
|
||||
}
|
||||
|
||||
getExponent() {
|
||||
return this._value.e;
|
||||
}
|
||||
|
||||
isNaN() {
|
||||
return this._value.isNaN();
|
||||
}
|
||||
|
||||
isZero() {
|
||||
return this._value.isZero();
|
||||
}
|
||||
|
||||
isNegative() {
|
||||
return this._value.isNegative();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this._value.toString();
|
||||
}
|
||||
|
||||
greaterThan(comparator: Value) {
|
||||
assert(this.constructor === comparator.constructor);
|
||||
return this._value.greaterThan(comparator._value);
|
||||
}
|
||||
|
||||
lessThan(comparator: Value) {
|
||||
assert(this.constructor === comparator.constructor);
|
||||
return this._value.lessThan(comparator._value);
|
||||
}
|
||||
|
||||
comparedTo(comparator: Value) {
|
||||
assert(this.constructor === comparator.constructor);
|
||||
return this._value.comparedTo(comparator._value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.Value = Value;
|
||||
@@ -1,59 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
const BigNumber = GlobalBigNumber.another({
|
||||
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
|
||||
DECIMAL_PLACES: 40
|
||||
});
|
||||
|
||||
const Value = require('./value').Value;
|
||||
const rippleUnits = new BigNumber(1e6);
|
||||
|
||||
class XRPValue extends Value {
|
||||
|
||||
constructor(value: string | BigNumber) {
|
||||
super(value);
|
||||
if (this._value.dp() > 6) {
|
||||
throw new Error(
|
||||
'Value has more than 6 digits of precision past the decimal point, '
|
||||
+ 'an IOUValue may be being cast to an XRPValue'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
multiply(multiplicand: Value) {
|
||||
if (multiplicand instanceof XRPValue) {
|
||||
return super.multiply(
|
||||
new XRPValue(multiplicand._value.times(rippleUnits)));
|
||||
}
|
||||
return super.multiply(multiplicand);
|
||||
}
|
||||
|
||||
divide(divisor: Value) {
|
||||
if (divisor instanceof XRPValue) {
|
||||
return super.divide(
|
||||
new XRPValue(divisor._value.times(rippleUnits)));
|
||||
}
|
||||
return super.divide(divisor);
|
||||
}
|
||||
|
||||
negate() {
|
||||
return new XRPValue(this._value.neg());
|
||||
}
|
||||
|
||||
_canonicalize(value) {
|
||||
if (value.isNaN()) {
|
||||
throw new Error('Invalid result');
|
||||
}
|
||||
return new XRPValue(value.round(6, BigNumber.ROUND_DOWN));
|
||||
}
|
||||
|
||||
equals(comparator) {
|
||||
return (comparator instanceof XRPValue)
|
||||
&& this._value.equals(comparator._value);
|
||||
}
|
||||
}
|
||||
|
||||
exports.XRPValue = XRPValue;
|
||||
46
test/amount-test-error.js
Normal file
46
test/amount-test-error.js
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const Amount = require('ripple-lib').Amount;
|
||||
const Remote = require('ripple-lib').Remote;
|
||||
|
||||
const data = require('./fixtures/negative-error');
|
||||
|
||||
|
||||
describe.skip('Amount ', function() {
|
||||
it('Show "Offer total cannot be negative" error', function() {
|
||||
const a1 = {
|
||||
currency: 'JPY',
|
||||
issuer: 'r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN',
|
||||
value: '66436.33517689175'
|
||||
};
|
||||
const a2 = {
|
||||
currency: 'JPY',
|
||||
issuer: 'r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN',
|
||||
value: '66435.49665972557'
|
||||
};
|
||||
const a1a = Amount.from_json(a1);
|
||||
const res = a1a.add(a2).subtract(a2).subtract(a1);
|
||||
|
||||
console.log(res.to_human());
|
||||
assert(!res.is_negative(), 'Offer total cannot be negative');
|
||||
});
|
||||
|
||||
it('Show Details of "Offer total cannot be negative" error', function() {
|
||||
const book = new Remote().createOrderBook({
|
||||
currency_gets: 'JPY',
|
||||
issuer_gets: 'r94s8px6kSw1uZ1MV98dhSRTvc6VMPoPcN',
|
||||
currency_pays: 'XRP'
|
||||
});
|
||||
book._subscribed = true;
|
||||
book._synced = true;
|
||||
book._offers = data._offers;
|
||||
book._offerCounts = data._offerCounts;
|
||||
book._ownerFundsUnadjusted = data._ownerFundsUnadjusted;
|
||||
book._ownerFunds = data._ownerFunds;
|
||||
book._ownerOffersTotal = data._ownerOffersTotal;
|
||||
book._issuerTransferRate = 1000000000;
|
||||
book._remote._handleTransaction(data.message1);
|
||||
book._remote._handleTransaction(data.lastMessage);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1048,6 +1048,17 @@ describe('Amount', function() {
|
||||
});
|
||||
|
||||
describe('ratio_human', function() {
|
||||
it('Divide USD by XRP', function() {
|
||||
const a = Amount.from_json({
|
||||
value: '0.08161672093323858',
|
||||
currency: 'USD',
|
||||
issuer: 'rLFPPebckMYZf3urdomLsaqRGmQ6zHVrrK'
|
||||
});
|
||||
const b = Amount.from_json('15000000');
|
||||
const c = a.ratio_human(b);
|
||||
assert.deepEqual(c.to_json(), {value: '0.005441114728882572',
|
||||
currency: 'USD', issuer: 'rLFPPebckMYZf3urdomLsaqRGmQ6zHVrrK'});
|
||||
});
|
||||
it('Divide USD by XAU (dem)', function() {
|
||||
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').ratio_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_text_full(), '201.0049931765529/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
@@ -1223,29 +1234,52 @@ describe('Amount', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('from_json minimum IOU', function() {
|
||||
const amt = Amount.from_json('-1e-81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '-1000000000000000e-96');
|
||||
it('from_json minimum positive IOU', function() {
|
||||
const amt = Amount.from_json('1e-81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '1000000000000000e-96');
|
||||
assert.strictEqual(amt.to_text(), Amount.min_value);
|
||||
});
|
||||
|
||||
it('from_json exceed minimum IOU', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('-1e-82/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
}, 'Exceeding min value of ' + Amount.min_value);
|
||||
it('from_json smallest-absolute-value negative IOU', function() {
|
||||
const amt = Amount.from_json('-1e-81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '-1000000000000000e-96');
|
||||
assert.strictEqual(amt.negate().to_text(), Amount.min_value);
|
||||
});
|
||||
|
||||
it('from_json maximum IOU', function() {
|
||||
it('from_json exceeding minimum positive IOU', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('1e-82/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
}, 'Exceeding min absolute value of ' + Amount.min_value);
|
||||
});
|
||||
|
||||
it('from_json exceeding minimum-absolute-value negative IOU', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('-1e-82/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
}, 'Exceeding min absolute value of ' + Amount.min_value);
|
||||
});
|
||||
|
||||
it('from_json maximum positive IOU', function() {
|
||||
const amt = Amount.from_json('9999999999999999e80/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '9999999999999999e80');
|
||||
});
|
||||
|
||||
it('from_json exceed maximum IOU', function() {
|
||||
it('from_json exceed maximum positive IOU', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('9999999999999999e81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
}, 'Exceeding max value of ' + Amount.max_value);
|
||||
});
|
||||
|
||||
it('from_json largest-absolute-value negative IOU', function() {
|
||||
const amt = Amount.from_json('-9999999999999999e80/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '-9999999999999999e80');
|
||||
});
|
||||
|
||||
it('from_json exceed largest-absolute-value negative IOU', function() {
|
||||
assert.throws(function() {
|
||||
Amount.from_json('-9999999999999999e81/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
}, 'Exceeding max value of ' + Amount.max_value);
|
||||
});
|
||||
|
||||
it('from_json normalize mantissa to valid max range, lost significant digits', function() {
|
||||
const amt = Amount.from_json('99999999999999999999999999999999/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
assert.strictEqual(amt.to_text(), '9999999999999999e16');
|
||||
|
||||
475
test/api-test.js
475
test/api-test.js
@@ -2,7 +2,6 @@
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert-diff');
|
||||
const path = require('path');
|
||||
const setupAPI = require('./setup-api');
|
||||
const RippleAPI = require('ripple-api').RippleAPI;
|
||||
const common = RippleAPI._PRIVATE.common;
|
||||
@@ -16,6 +15,7 @@ const validate = common.validate;
|
||||
const utils = RippleAPI._PRIVATE.ledgerUtils;
|
||||
const ledgerClosed = require('./fixtures/api/rippled/ledger-close-newer');
|
||||
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
|
||||
const ledgerHashSchema = require('./fixtures/schemas/ledgerhash.json');
|
||||
|
||||
const orderbook = {
|
||||
base: {
|
||||
@@ -29,7 +29,6 @@ const orderbook = {
|
||||
};
|
||||
|
||||
function checkResult(expected, schemaName, response) {
|
||||
// console.log(JSON.stringify(response, null, 2));
|
||||
assert.deepEqual(response, expected);
|
||||
if (schemaName) {
|
||||
schemaValidator.schemaValidate(schemaName, response);
|
||||
@@ -48,82 +47,91 @@ describe('RippleAPI', function() {
|
||||
}, instructions);
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePayment, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePayment, 'tx'));
|
||||
_.partial(checkResult, responses.preparePayment.normal, 'prepare'));
|
||||
});
|
||||
|
||||
it('preparePayment with all options specified', function() {
|
||||
const localInstructions = {
|
||||
maxLedgerVersion: this.api.getLedgerVersion() + 100,
|
||||
fee: '0.000012'
|
||||
};
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentAllOptions, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePaymentAllOptions, 'tx'));
|
||||
return this.api.getLedgerVersion().then((ver) => {
|
||||
const localInstructions = {
|
||||
maxLedgerVersion: ver + 100,
|
||||
fee: '0.000012'
|
||||
};
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentAllOptions, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePayment.allOptions, 'prepare'));
|
||||
});
|
||||
});
|
||||
|
||||
it('preparePayment without counterparty set', function() {
|
||||
const localInstructions = _.defaults({sequence: 23}, instructions);
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentNoCounterparty, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePaymentNoCounterparty, 'tx'));
|
||||
_.partial(checkResult, responses.preparePayment.noCounterparty,
|
||||
'prepare'));
|
||||
});
|
||||
|
||||
it('preparePayment - destination.minAmount', function() {
|
||||
return this.api.preparePayment(address, responses.getPaths.sendAll[0],
|
||||
instructions).then(_.partial(checkResult,
|
||||
responses.preparePayment.minAmount, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareOrder - buy order', function() {
|
||||
return this.api.prepareOrder(address, requests.prepareOrder, instructions)
|
||||
.then(_.partial(checkResult, responses.prepareOrder, 'tx'));
|
||||
.then(_.partial(checkResult, responses.prepareOrder, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareOrder - sell order', function() {
|
||||
return this.api.prepareOrder(
|
||||
address, requests.prepareOrderSell, instructions).then(
|
||||
_.partial(checkResult, responses.prepareOrderSell, 'tx'));
|
||||
_.partial(checkResult, responses.prepareOrderSell, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareOrderCancellation', function() {
|
||||
return this.api.prepareOrderCancellation(address, 23, instructions).then(
|
||||
_.partial(checkResult, responses.prepareOrderCancellation, 'tx'));
|
||||
_.partial(checkResult, responses.prepareOrderCancellation, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareTrustline - simple', function() {
|
||||
return this.api.prepareTrustline(
|
||||
address, requests.prepareTrustline.simple, instructions).then(
|
||||
_.partial(checkResult, responses.prepareTrustline.simple, 'tx'));
|
||||
_.partial(checkResult, responses.prepareTrustline.simple, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareTrustline - complex', function() {
|
||||
return this.api.prepareTrustline(
|
||||
address, requests.prepareTrustline.complex, instructions).then(
|
||||
_.partial(checkResult, responses.prepareTrustline.complex, 'tx'));
|
||||
_.partial(checkResult, responses.prepareTrustline.complex, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSettings', function() {
|
||||
return this.api.prepareSettings(
|
||||
address, requests.prepareSettings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.flags, 'tx'));
|
||||
_.partial(checkResult, responses.prepareSettings.flags, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSettings - regularKey', function() {
|
||||
const regularKey = {regularKey: 'rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD'};
|
||||
return this.api.prepareSettings(address, regularKey, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.regularKey, 'tx'));
|
||||
_.partial(checkResult, responses.prepareSettings.regularKey, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSettings - flag set', function() {
|
||||
const settings = {requireDestinationTag: true};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.flagSet, 'tx'));
|
||||
_.partial(checkResult, responses.prepareSettings.flagSet, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSettings - flag clear', function() {
|
||||
const settings = {requireDestinationTag: false};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.flagClear, 'tx'));
|
||||
_.partial(checkResult, responses.prepareSettings.flagClear, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSettings - string field clear', function() {
|
||||
const settings = {walletLocator: null};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.fieldClear, 'tx'));
|
||||
_.partial(checkResult, responses.prepareSettings.fieldClear, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSettings - integer field clear', function() {
|
||||
@@ -131,19 +139,45 @@ describe('RippleAPI', function() {
|
||||
return this.api.prepareSettings(address, settings, instructions)
|
||||
.then(data => {
|
||||
assert(data);
|
||||
assert.strictEqual(data.WalletSize, 0);
|
||||
assert.strictEqual(JSON.parse(data.txJSON).WalletSize, 0);
|
||||
});
|
||||
});
|
||||
|
||||
it('prepareSettings - set transferRate', function() {
|
||||
const settings = {transferRate: 1};
|
||||
return this.api.prepareSettings(address, settings, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSettings.setTransferRate, 'tx'));
|
||||
_.partial(checkResult, responses.prepareSettings.setTransferRate,
|
||||
'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSuspendedPaymentCreation', function() {
|
||||
const localInstructions = _.defaults({
|
||||
maxFee: '0.000012'
|
||||
}, instructions);
|
||||
return this.api.prepareSuspendedPaymentCreation(
|
||||
address, requests.prepareSuspendedPaymentCreation,
|
||||
localInstructions).then(
|
||||
_.partial(checkResult, responses.prepareSuspendedPaymentCreation,
|
||||
'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSuspendedPaymentExecution', function() {
|
||||
return this.api.prepareSuspendedPaymentExecution(
|
||||
address, requests.prepareSuspendedPaymentExecution, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSuspendedPaymentExecution,
|
||||
'prepare'));
|
||||
});
|
||||
|
||||
it('prepareSuspendedPaymentCancellation', function() {
|
||||
return this.api.prepareSuspendedPaymentCancellation(
|
||||
address, requests.prepareSuspendedPaymentCancellation, instructions).then(
|
||||
_.partial(checkResult, responses.prepareSuspendedPaymentCancellation,
|
||||
'prepare'));
|
||||
});
|
||||
|
||||
it('sign', function() {
|
||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||
const result = this.api.sign(requests.sign, secret);
|
||||
const result = this.api.sign(requests.sign.txJSON, secret);
|
||||
assert.deepEqual(result, responses.sign);
|
||||
schemaValidator.schemaValidate('sign', result);
|
||||
});
|
||||
@@ -153,160 +187,203 @@ describe('RippleAPI', function() {
|
||||
_.partial(checkResult, responses.submit, 'submit'));
|
||||
});
|
||||
|
||||
it('submit - failure', function() {
|
||||
return this.api.submit('BAD').then(() => {
|
||||
assert(false, 'Should throw RippleError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.RippleError);
|
||||
assert(error.data);
|
||||
});
|
||||
});
|
||||
|
||||
it('getBalances', function() {
|
||||
return this.api.getBalances(address).then(
|
||||
_.partial(checkResult, responses.getBalances, 'getBalances'));
|
||||
});
|
||||
|
||||
it('getTransaction - payment', function() {
|
||||
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
|
||||
_.partial(checkResult, responses.getTransaction.payment,
|
||||
'getTransaction'));
|
||||
it('getBalanceSheet', function() {
|
||||
return this.api.getBalanceSheet(address).then(
|
||||
_.partial(checkResult, responses.getBalanceSheet, 'getBalanceSheet'));
|
||||
});
|
||||
|
||||
it('getTransaction - settings', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.settings,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - order', function() {
|
||||
const hash =
|
||||
'10A6FB4A66EE80BED46AAE4815D7DC43B97E944984CCD5B93BCF3F8538CABC51';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.order,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - order cancellation', function() {
|
||||
const hash =
|
||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.orderCancellation,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline set', function() {
|
||||
const hash =
|
||||
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustline,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline frozen off', function() {
|
||||
const hash =
|
||||
'FE72FAD0FA7CA904FB6C633A1666EDF0B9C73B2F5A4555D37EEF2739A78A531B';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustlineFrozenOff,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline no quality', function() {
|
||||
const hash =
|
||||
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustlineNoQuality,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - not validated', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.notValidated,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - tracking on', function() {
|
||||
const hash =
|
||||
'8925FC8844A1E930E2CC76AD0A15E7665AFCC5425376D548BB1413F484C31B8C';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trackingOn,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - tracking off', function() {
|
||||
const hash =
|
||||
'C8C5E20DFB1BF533D0D81A2ED23F0A3CBD1EF2EE8A902A1D760500473CC9C582';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trackingOff,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - set regular key', function() {
|
||||
const hash =
|
||||
'278E6687C1C60C6873996210A6523564B63F2844FB1019576C157353B1813E60';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.setRegularKey,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - not found in range', function() {
|
||||
const hash =
|
||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||
const options = {
|
||||
minLedgerVersion: 32570,
|
||||
maxLedgerVersion: 32571
|
||||
};
|
||||
return this.api.getTransaction(hash, options).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
describe('getTransaction', () => {
|
||||
it('getTransaction - payment', function() {
|
||||
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
|
||||
_.partial(checkResult, responses.getTransaction.payment,
|
||||
'getTransaction'));
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - not found by hash', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
it('getTransaction - settings', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.settings,
|
||||
'getTransaction'));
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - missing ledger history', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
// make gaps in history
|
||||
this.api.remote.getServer().emit('message', ledgerClosed);
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw MissingLedgerHistoryError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
|
||||
it('getTransaction - order', function() {
|
||||
const hash =
|
||||
'10A6FB4A66EE80BED46AAE4815D7DC43B97E944984CCD5B93BCF3F8538CABC51';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.order,
|
||||
'getTransaction'));
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - ledger_index not found', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
assert(error.message.indexOf('ledger_index') !== -1);
|
||||
it('getTransaction - order cancellation', function() {
|
||||
const hash =
|
||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.orderCancellation,
|
||||
'getTransaction'));
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - transaction ledger not found', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA12';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
assert(error.message.indexOf('ledger not found') !== -1);
|
||||
it('getTransaction - trustline set', function() {
|
||||
const hash =
|
||||
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustline,
|
||||
'getTransaction'));
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - ledger missing close time', function() {
|
||||
const hash =
|
||||
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A04';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw ApiError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.ApiError);
|
||||
it('getTransaction - trustline frozen off', function() {
|
||||
const hash =
|
||||
'FE72FAD0FA7CA904FB6C633A1666EDF0B9C73B2F5A4555D37EEF2739A78A531B';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustlineFrozenOff,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - trustline no quality', function() {
|
||||
const hash =
|
||||
'BAF1C678323C37CCB7735550C379287667D8288C30F83148AD3C1CB019FC9002';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trustlineNoQuality,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - not validated', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA10';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - tracking on', function() {
|
||||
const hash =
|
||||
'8925FC8844A1E930E2CC76AD0A15E7665AFCC5425376D548BB1413F484C31B8C';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trackingOn,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - tracking off', function() {
|
||||
const hash =
|
||||
'C8C5E20DFB1BF533D0D81A2ED23F0A3CBD1EF2EE8A902A1D760500473CC9C582';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.trackingOff,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - set regular key', function() {
|
||||
const hash =
|
||||
'278E6687C1C60C6873996210A6523564B63F2844FB1019576C157353B1813E60';
|
||||
return this.api.getTransaction(hash).then(
|
||||
_.partial(checkResult, responses.getTransaction.setRegularKey,
|
||||
'getTransaction'));
|
||||
});
|
||||
|
||||
it('getTransaction - not found in range', function() {
|
||||
const hash =
|
||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||
const options = {
|
||||
minLedgerVersion: 32570,
|
||||
maxLedgerVersion: 32571
|
||||
};
|
||||
return this.api.getTransaction(hash, options).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - not found by hash', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - missing ledger history', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
// make gaps in history
|
||||
this.api.remote.getServer().emit('message', ledgerClosed);
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw MissingLedgerHistoryError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - missing ledger history with ledger range', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
const options = {
|
||||
minLedgerVersion: 32569,
|
||||
maxLedgerVersion: 32571
|
||||
};
|
||||
return this.api.getTransaction(hash, options).then(() => {
|
||||
assert(false, 'Should throw MissingLedgerHistoryError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.MissingLedgerHistoryError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - not found - future maxLedgerVersion', function() {
|
||||
const hash = hashes.NOTFOUND_TRANSACTION_HASH;
|
||||
const options = {
|
||||
maxLedgerVersion: 99999999999
|
||||
};
|
||||
return this.api.getTransaction(hash, options).then(() => {
|
||||
assert(false, 'Should throw PendingLedgerVersionError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.PendingLedgerVersionError);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - ledger_index not found', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
assert(error.message.indexOf('ledger_index') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - transaction ledger not found', function() {
|
||||
const hash =
|
||||
'4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA12';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw NotFoundError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.NotFoundError);
|
||||
assert(error.message.indexOf('ledger not found') !== -1);
|
||||
});
|
||||
});
|
||||
|
||||
it('getTransaction - ledger missing close time', function() {
|
||||
const hash =
|
||||
'0F7ED9F40742D8A513AE86029462B7A6768325583DF8EE21B7EC663019DD6A04';
|
||||
return this.api.getTransaction(hash).then(() => {
|
||||
assert(false, 'Should throw ApiError');
|
||||
}).catch(error => {
|
||||
assert(error instanceof this.api.errors.ApiError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -416,7 +493,7 @@ describe('RippleAPI', function() {
|
||||
function random() {
|
||||
return _.fill(Array(16), 0);
|
||||
}
|
||||
assert.deepEqual(this.api.generateAddress({random}),
|
||||
assert.deepEqual(this.api.generateAddress({entropy: random()}),
|
||||
responses.generateAddress);
|
||||
});
|
||||
|
||||
@@ -505,6 +582,18 @@ describe('RippleAPI', function() {
|
||||
_.partial(checkResult, responses.getPaths.XrpToUsd, 'getPaths'));
|
||||
});
|
||||
|
||||
it('getPaths - queuing', function() {
|
||||
return Promise.all([
|
||||
this.api.getPaths(requests.getPaths.normal),
|
||||
this.api.getPaths(requests.getPaths.UsdToUsd),
|
||||
this.api.getPaths(requests.getPaths.XrpToXrp)
|
||||
]).then(results => {
|
||||
checkResult(responses.getPaths.XrpToUsd, 'getPaths', results[0]);
|
||||
checkResult(responses.getPaths.UsdToUsd, 'getPaths', results[1]);
|
||||
checkResult(responses.getPaths.XrpToXrp, 'getPaths', results[2]);
|
||||
});
|
||||
});
|
||||
|
||||
// @TODO
|
||||
// need decide what to do with currencies/XRP:
|
||||
// if add 'XRP' in currencies, then there will be exception in
|
||||
@@ -560,8 +649,16 @@ describe('RippleAPI', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getLedgerVersion', function() {
|
||||
assert.strictEqual(this.api.getLedgerVersion(), 8819951);
|
||||
it('getPaths - send all', function() {
|
||||
return this.api.getPaths(requests.getPaths.sendAll).then(
|
||||
_.partial(checkResult, responses.getPaths.sendAll, 'getPaths'));
|
||||
});
|
||||
|
||||
it('getLedgerVersion', function(done) {
|
||||
this.api.getLedgerVersion().then((ver) => {
|
||||
assert.strictEqual(ver, 8819951);
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('getLedger', function() {
|
||||
@@ -581,7 +678,7 @@ describe('RippleAPI', function() {
|
||||
.then(response => {
|
||||
const ledger = _.assign({}, response,
|
||||
{parentCloseTime: response.closeTime});
|
||||
const hash = this.api.computeLedgerHash(ledger);
|
||||
const hash = RippleAPI._PRIVATE.computeLedgerHash(ledger);
|
||||
assert.strictEqual(hash,
|
||||
'E6DB7365949BF9814D76BCC730B01818EB9136A89DB224F3F9F5AAE4569D758E');
|
||||
});
|
||||
@@ -626,9 +723,7 @@ describe('RippleAPI', function() {
|
||||
|
||||
describe('schema-validator', function() {
|
||||
beforeEach(function() {
|
||||
const schema = schemaValidator.loadSchema(path.join(__dirname,
|
||||
'./fixtures/schemas/ledgerhash.json'));
|
||||
schemaValidator.SCHEMAS.ledgerhash = schema;
|
||||
schemaValidator.SCHEMAS.ledgerhash = ledgerHashSchema;
|
||||
});
|
||||
|
||||
it('valid', function() {
|
||||
@@ -650,12 +745,6 @@ describe('RippleAPI', function() {
|
||||
}, this.api.errors.ValidationError);
|
||||
});
|
||||
|
||||
it('load schema error', function() {
|
||||
assert.throws(function() {
|
||||
schemaValidator.loadSchema('/bad/file/name');
|
||||
}, Error);
|
||||
});
|
||||
|
||||
it('schema not found error', function() {
|
||||
assert.throws(function() {
|
||||
schemaValidator.schemaValidate('unexisting', 'anything');
|
||||
@@ -763,6 +852,13 @@ describe('RippleAPI', function() {
|
||||
|
||||
});
|
||||
|
||||
it('ledgerClosed', function(done) {
|
||||
this.api.on('ledgerClosed', message => {
|
||||
checkResult(responses.ledgerClosed, 'ledgerClosed', message);
|
||||
done();
|
||||
});
|
||||
this.api.remote.getServer().emit('message', ledgerClosed);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RippleAPI - offline', function() {
|
||||
@@ -775,55 +871,36 @@ describe('RippleAPI - offline', function() {
|
||||
maxLedgerVersion: 8820051,
|
||||
fee: '0.000012'
|
||||
};
|
||||
return api.prepareSettings(address, settings, instructions).then(txJSON => {
|
||||
assert.deepEqual(txJSON, responses.prepareSettings.flags);
|
||||
assert.deepEqual(api.sign(txJSON, secret), responses.sign);
|
||||
return api.prepareSettings(address, settings, instructions).then(data => {
|
||||
assert.deepEqual(data, responses.prepareSettings.flags);
|
||||
assert.deepEqual(api.sign(data.txJSON, secret), responses.sign);
|
||||
});
|
||||
});
|
||||
|
||||
it('computeLedgerHash', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = requests.computeLedgerHash.header;
|
||||
const ledgerHash = api.computeLedgerHash(header);
|
||||
const ledgerHash = RippleAPI._PRIVATE.computeLedgerHash(header);
|
||||
assert.strictEqual(ledgerHash,
|
||||
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
|
||||
});
|
||||
|
||||
it('computeLedgerHash - with transactions', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = _.omit(requests.computeLedgerHash.header,
|
||||
'transactionHash');
|
||||
header.rawTransactions = JSON.stringify(
|
||||
requests.computeLedgerHash.transactions);
|
||||
const ledgerHash = api.computeLedgerHash(header);
|
||||
const ledgerHash = RippleAPI._PRIVATE.computeLedgerHash(header);
|
||||
assert.strictEqual(ledgerHash,
|
||||
'F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349');
|
||||
});
|
||||
|
||||
it('computeLedgerHash - incorrent transaction_hash', function() {
|
||||
const api = new RippleAPI();
|
||||
const header = _.assign({}, requests.computeLedgerHash.header,
|
||||
{transactionHash:
|
||||
'325EACC5271322539EEEC2D6A5292471EF1B3E72AE7180533EFC3B8F0AD435C9'});
|
||||
header.rawTransactions = JSON.stringify(
|
||||
requests.computeLedgerHash.transactions);
|
||||
assert.throws(() => api.computeLedgerHash(header));
|
||||
});
|
||||
|
||||
it('isValidAddress - valid', function() {
|
||||
const api = new RippleAPI();
|
||||
assert(api.isValidAddress(address));
|
||||
});
|
||||
|
||||
it('isValidAddress - invalid', function() {
|
||||
const api = new RippleAPI();
|
||||
assert(!api.isValidAddress(address.slice(0, -1) + 'a'));
|
||||
});
|
||||
|
||||
it('isValidAddress - invalid - hex representation', function() {
|
||||
const api = new RippleAPI();
|
||||
const hex = '6e3efa86a5eb0a3c5dc9beb3a204783bb00e1913';
|
||||
assert(!api.isValidAddress(hex));
|
||||
assert.throws(() => RippleAPI._PRIVATE.computeLedgerHash(header));
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
15
test/fixtures/api/requests/getpaths/send-all.json
vendored
Normal file
15
test/fixtures/api/requests/getpaths/send-all.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"amount": {
|
||||
"currency": "USD"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
test/fixtures/api/requests/index.js
vendored
9
test/fixtures/api/requests/index.js
vendored
@@ -7,6 +7,12 @@ module.exports = {
|
||||
preparePaymentAllOptions: require('./prepare-payment-all-options'),
|
||||
preparePaymentNoCounterparty: require('./prepare-payment-no-counterparty'),
|
||||
prepareSettings: require('./prepare-settings'),
|
||||
prepareSuspendedPaymentCreation:
|
||||
require('./prepare-suspended-payment-creation'),
|
||||
prepareSuspendedPaymentExecution:
|
||||
require('./prepare-suspended-payment-execution'),
|
||||
prepareSuspendedPaymentCancellation:
|
||||
require('./prepare-suspended-payment-cancellation'),
|
||||
prepareTrustline: {
|
||||
simple: require('./prepare-trustline-simple'),
|
||||
complex: require('./prepare-trustline')
|
||||
@@ -19,7 +25,8 @@ module.exports = {
|
||||
XrpToXrpNotEnough: require('./getpaths/xrp2xrp-not-enough'),
|
||||
NotAcceptCurrency: require('./getpaths/not-accept-currency'),
|
||||
NoPaths: require('./getpaths/no-paths'),
|
||||
NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies')
|
||||
NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies'),
|
||||
sendAll: require('./getpaths/send-all')
|
||||
},
|
||||
computeLedgerHash: {
|
||||
header: require('./compute-ledger-hash'),
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
}
|
||||
],
|
||||
"invoiceID": "A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A",
|
||||
"allowPartialPayment": true,
|
||||
"noDirectRipple": true,
|
||||
"limitQuality": true,
|
||||
"paths": "[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"type\":49,\"type_hex\":\"0000000000000031\"},{\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":48,\"type_hex\":\"0000000000000030\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":49,\"type_hex\":\"0000000000000031\"}]]"
|
||||
|
||||
4
test/fixtures/api/requests/prepare-suspended-payment-cancellation.json
vendored
Normal file
4
test/fixtures/api/requests/prepare-suspended-payment-cancellation.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"paymentSequence": 1234
|
||||
}
|
||||
19
test/fixtures/api/requests/prepare-suspended-payment-creation.json
vendored
Normal file
19
test/fixtures/api/requests/prepare-suspended-payment-creation.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"maxAmount": {
|
||||
"value": "0.01",
|
||||
"currency": "USD",
|
||||
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||
"amount": {
|
||||
"value": "0.01",
|
||||
"currency": "USD",
|
||||
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM"
|
||||
}
|
||||
},
|
||||
"allowCancelAfter": 1440694329002
|
||||
}
|
||||
7
test/fixtures/api/requests/prepare-suspended-payment-execution.json
vendored
Normal file
7
test/fixtures/api/requests/prepare-suspended-payment-execution.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"owner": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"paymentSequence": 1234,
|
||||
"method": 1,
|
||||
"digest": "8F434346648F6B96DF89DDA901C5176B10A6D83961DD3C1AC88B59B2DC327AA4",
|
||||
"proof": "whatever"
|
||||
}
|
||||
13
test/fixtures/api/requests/sign.json
vendored
13
test/fixtures/api/requests/sign.json
vendored
@@ -1,9 +1,8 @@
|
||||
{
|
||||
"Flags": 0,
|
||||
"TransactionType": "AccountSet",
|
||||
"Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"Domain": "726970706C652E636F6D",
|
||||
"LastLedgerSequence": 8820051,
|
||||
"Fee": "12",
|
||||
"Sequence": 23
|
||||
"txJSON": "{\"Flags\":0,\"TransactionType\":\"AccountSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Domain\":\"726970706C652E636F6D\",\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
||||
"instructions": {
|
||||
"fee": "12",
|
||||
"sequence": 23,
|
||||
"maxLedgerVersion": 8820051
|
||||
}
|
||||
}
|
||||
|
||||
54
test/fixtures/api/responses/get-balance-sheet.json
vendored
Normal file
54
test/fixtures/api/responses/get-balance-sheet.json
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"balances": [
|
||||
{
|
||||
"counterparty": "rKm4uWpg9tfwbVSeATv4KxDe6mpE9yPkgJ",
|
||||
"currency": "EUR",
|
||||
"value": "29826.1965999999"
|
||||
},
|
||||
{
|
||||
"counterparty": "rKm4uWpg9tfwbVSeATv4KxDe6mpE9yPkgJ",
|
||||
"currency": "USD",
|
||||
"value": "10.0"
|
||||
},
|
||||
{
|
||||
"counterparty": "ra7JkEzrgeKHdzKgo4EUUVBnxggY4z37kt",
|
||||
"currency": "USD",
|
||||
"value": "13857.70416"
|
||||
}
|
||||
],
|
||||
"assets": [
|
||||
{
|
||||
"counterparty": "r9F6wk8HkXrgYWoJ7fsv4VrUBVoqDVtzkH",
|
||||
"currency": "BTC",
|
||||
"value": "5444166510000000e-26"
|
||||
},
|
||||
{
|
||||
"counterparty": "r9F6wk8HkXrgYWoJ7fsv4VrUBVoqDVtzkH",
|
||||
"currency": "USD",
|
||||
"value": "100.0"
|
||||
},
|
||||
{
|
||||
"counterparty": "rwmUaXsWtXU4Z843xSYwgt1is97bgY8yj6",
|
||||
"currency": "BTC",
|
||||
"value": "8700000000000000e-30"
|
||||
}
|
||||
],
|
||||
"obligations": [
|
||||
{
|
||||
"currency": "BTC",
|
||||
"value": "5908.324927635318"
|
||||
},
|
||||
{
|
||||
"currency": "EUR",
|
||||
"value": "992471.7419793958"
|
||||
},
|
||||
{
|
||||
"currency": "GBP",
|
||||
"value": "4991.38706013193"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"value": "1997134.20229482"
|
||||
}
|
||||
]
|
||||
}
|
||||
70
test/fixtures/api/responses/get-paths-send-all.json
vendored
Normal file
70
test/fixtures/api/responses/get-paths-send-all.json
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
[
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.93463759481038"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.93463759481038"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"EUR\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}],[{\"account\":\"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun\"},{\"currency\":\"USD\",\"issuer\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"}]]"
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.93463759481038"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"JPY\",\"issuer\":\"rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.990019960079841"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
|
||||
}
|
||||
]
|
||||
@@ -2,10 +2,9 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||
"amount": {
|
||||
"maxAmount": {
|
||||
"currency": "USD",
|
||||
"value": "0.000001002",
|
||||
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo"
|
||||
"value": "0.000001002"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user