mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-08 14:55:49 +00:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00713d8ec1 | ||
|
|
d949881e9f | ||
|
|
5075441a69 | ||
|
|
94a852cb8b | ||
|
|
11ed6b124f | ||
|
|
8767fc0068 | ||
|
|
66b07623b0 | ||
|
|
4f3635eef0 | ||
|
|
f638833759 | ||
|
|
ab9d1936d9 | ||
|
|
0fefb2bd2c | ||
|
|
6740eee495 | ||
|
|
aa6020e00d | ||
|
|
7bfe4a6cd8 | ||
|
|
aa467681e4 | ||
|
|
6b8cd6151d | ||
|
|
0d6aaee12a | ||
|
|
dc03c6e0ac | ||
|
|
0f4d957d14 | ||
|
|
71a13224a1 | ||
|
|
8097ed60ba | ||
|
|
408bb74214 | ||
|
|
9433b43873 | ||
|
|
896bf48c79 | ||
|
|
3dd21a7e11 | ||
|
|
ed79a04018 | ||
|
|
ebfe20defb | ||
|
|
28b148348d | ||
|
|
fe099f2c8b | ||
|
|
107c8c9f0f | ||
|
|
39e818b3e5 | ||
|
|
588ffa3d5c | ||
|
|
691e4dd114 | ||
|
|
55bc42725f | ||
|
|
cce55b9361 | ||
|
|
c23c6e4fc9 | ||
|
|
afdd60efe8 | ||
|
|
8f6ea573ff | ||
|
|
3271b544ef | ||
|
|
6e83130754 | ||
|
|
f6ebe32519 | ||
|
|
3caed3c761 | ||
|
|
ce1c55427a | ||
|
|
9cd72595f0 | ||
|
|
ad1d3e135f | ||
|
|
76866ab901 | ||
|
|
20b647dfbf | ||
|
|
99d08065e4 | ||
|
|
261fba3d21 | ||
|
|
e1d9de7b1f | ||
|
|
391b2f3622 | ||
|
|
86ff315ef2 | ||
|
|
8d8a850864 | ||
|
|
7bf2da6014 | ||
|
|
7eae3ce709 | ||
|
|
5f5e48e414 | ||
|
|
4f6a37f7b1 | ||
|
|
82613e7e8b | ||
|
|
588aa382a1 | ||
|
|
809d981987 | ||
|
|
cfc21fde8c | ||
|
|
5c06ef547b | ||
|
|
0990ad4a6f | ||
|
|
d8f967d2b8 | ||
|
|
fe1c3e7130 | ||
|
|
062148674c | ||
|
|
11320693fd | ||
|
|
7af7eaccb4 | ||
|
|
5d5cf868a2 | ||
|
|
ddf8fe5b1a | ||
|
|
dc24f6afe0 | ||
|
|
f7dac6ab25 | ||
|
|
4b4fc36ebd | ||
|
|
7626ea5ed8 | ||
|
|
7061e9afe4 | ||
|
|
a124635c2c | ||
|
|
c9704137b7 | ||
|
|
ab8d75d3cc | ||
|
|
5e720891f5 | ||
|
|
27ed1aadd5 | ||
|
|
001f203983 | ||
|
|
515047d2dc | ||
|
|
995606b1e6 | ||
|
|
b5081344da | ||
|
|
4f86691fb8 | ||
|
|
45aca016d4 | ||
|
|
7f33d8a71e | ||
|
|
af620755c5 | ||
|
|
a76b554cad | ||
|
|
ef1e9e1b70 | ||
|
|
fdbac63f46 | ||
|
|
377f1dbfa1 | ||
|
|
14b840f3fe | ||
|
|
beb1cc0cde | ||
|
|
ef2515507d | ||
|
|
a271b9e816 | ||
|
|
8a3d4a64db |
4
.babelrc
Normal file
4
.babelrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"presets": ["es2015", "stage-1"],
|
||||||
|
"plugins": ["syntax-flow", "transform-flow-strip-types"]
|
||||||
|
}
|
||||||
@@ -4,6 +4,11 @@
|
|||||||
.*/ripple-lib/test/fixtures/.*
|
.*/ripple-lib/test/fixtures/.*
|
||||||
.*/node_modules/flow-bin/.*
|
.*/node_modules/flow-bin/.*
|
||||||
.*/node_modules/webpack/.*
|
.*/node_modules/webpack/.*
|
||||||
|
.*/node_modules/babel-core/.*
|
||||||
|
.*/node_modules/babel-eslint/.*
|
||||||
|
.*/node_modules/babel-preset-es2015/.*
|
||||||
|
.*/node_modules/babel-preset-stage-1/.*
|
||||||
|
.*/node_modules/babel-register/.*
|
||||||
|
|
||||||
[include]
|
[include]
|
||||||
./node_modules/
|
./node_modules/
|
||||||
|
|||||||
90
Gulpfile.js
90
Gulpfile.js
@@ -8,24 +8,49 @@ var rename = require('gulp-rename');
|
|||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var bump = require('gulp-bump');
|
var bump = require('gulp-bump');
|
||||||
var argv = require('yargs').argv;
|
var argv = require('yargs').argv;
|
||||||
|
var assert = require('assert');
|
||||||
|
var fs = require('fs');
|
||||||
|
|
||||||
var pkg = require('./package.json');
|
var pkg = require('./package.json');
|
||||||
|
|
||||||
|
var uglifyOptions = {
|
||||||
|
mangle: {
|
||||||
|
except: ['_', 'RippleError', 'RippledError', 'UnexpectedError',
|
||||||
|
'LedgerVersionError', 'ConnectionError', 'NotConnectedError',
|
||||||
|
'DisconnectedError', 'TimeoutError', 'ResponseFormatError',
|
||||||
|
'ValidationError', 'NotFoundError', 'MissingLedgerHistoryError',
|
||||||
|
'PendingLedgerVersionError'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function webpackConfig(extension, overrides) {
|
function webpackConfig(extension, overrides) {
|
||||||
overrides = overrides || {};
|
overrides = overrides || {};
|
||||||
var defaults = {
|
var defaults = {
|
||||||
cache: true,
|
cache: true,
|
||||||
|
externals: [{
|
||||||
|
'lodash': '_'
|
||||||
|
}],
|
||||||
entry: './src/index.js',
|
entry: './src/index.js',
|
||||||
output: {
|
output: {
|
||||||
library: 'ripple',
|
library: 'ripple',
|
||||||
path: './build/',
|
path: './build/',
|
||||||
filename: ['ripple-', extension].join(pkg.version)
|
filename: ['ripple-', extension].join(pkg.version)
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.NormalModuleReplacementPlugin(/^ws$/, './wswrapper'),
|
||||||
|
new webpack.NormalModuleReplacementPlugin(/^\.\/wallet$/, './wallet-web'),
|
||||||
|
new webpack.NormalModuleReplacementPlugin(/^.*setup-api$/,
|
||||||
|
'./setup-api-web')
|
||||||
|
],
|
||||||
module: {
|
module: {
|
||||||
loaders: [{
|
loaders: [{
|
||||||
|
test: /jayson/,
|
||||||
|
loader: 'null'
|
||||||
|
}, {
|
||||||
test: /\.js$/,
|
test: /\.js$/,
|
||||||
exclude: /node_modules/,
|
exclude: [/node_modules/],
|
||||||
loader: 'babel-loader?optional=runtime'
|
loader: 'babel-loader'
|
||||||
}, {
|
}, {
|
||||||
test: /\.json/,
|
test: /\.json/,
|
||||||
loader: 'json-loader'
|
loader: 'json-loader'
|
||||||
@@ -35,15 +60,70 @@ function webpackConfig(extension, overrides) {
|
|||||||
return _.assign({}, defaults, overrides);
|
return _.assign({}, defaults, overrides);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function webpackConfigForWebTest(testFileName, path) {
|
||||||
|
var match = testFileName.match(/\/?([^\/]*)-test.js$/);
|
||||||
|
if (!match) {
|
||||||
|
assert(false, 'wrong filename:' + testFileName);
|
||||||
|
}
|
||||||
|
var configOverrides = {
|
||||||
|
externals: [{
|
||||||
|
'lodash': '_',
|
||||||
|
'ripple-api': 'ripple',
|
||||||
|
'net': 'null'
|
||||||
|
}],
|
||||||
|
entry: testFileName,
|
||||||
|
output: {
|
||||||
|
library: match[1].replace(/-/g, '_'),
|
||||||
|
path: './test-compiled-for-web/' + (path ? path : ''),
|
||||||
|
filename: match[1] + '-test.js'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return webpackConfig('.js', configOverrides);
|
||||||
|
}
|
||||||
|
|
||||||
|
gulp.task('build-tests', function(callback) {
|
||||||
|
var times = 0;
|
||||||
|
function done() {
|
||||||
|
if (++times >= 5) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
webpack(webpackConfigForWebTest('./test/rangeset-test.js'), done);
|
||||||
|
webpack(webpackConfigForWebTest('./test/connection-test.js'), done);
|
||||||
|
webpack(webpackConfigForWebTest('./test/api-test.js'), done);
|
||||||
|
webpack(webpackConfigForWebTest('./test/broadcast-api-test.js'), done);
|
||||||
|
webpack(webpackConfigForWebTest('./test/integration/integration-test.js',
|
||||||
|
'integration/'), done);
|
||||||
|
});
|
||||||
|
|
||||||
|
function createLink(from, to) {
|
||||||
|
if (fs.existsSync(to)) {
|
||||||
|
fs.unlinkSync(to);
|
||||||
|
}
|
||||||
|
fs.linkSync(from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBuildLink(callback) {
|
||||||
|
return function(err, res) {
|
||||||
|
createLink('./build/ripple-' + pkg.version + '.js',
|
||||||
|
'./build/ripple-latest.js');
|
||||||
|
callback(err, res);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
gulp.task('build', function(callback) {
|
gulp.task('build', function(callback) {
|
||||||
webpack(webpackConfig('.js'), callback);
|
webpack(webpackConfig('.js'), createBuildLink(callback));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build-min', ['build'], function() {
|
gulp.task('build-min', ['build'], function() {
|
||||||
return gulp.src(['./build/ripple-', '.js'].join(pkg.version))
|
return gulp.src(['./build/ripple-', '.js'].join(pkg.version))
|
||||||
.pipe(uglify())
|
.pipe(uglify(uglifyOptions))
|
||||||
.pipe(rename(['ripple-', '-min.js'].join(pkg.version)))
|
.pipe(rename(['ripple-', '-min.js'].join(pkg.version)))
|
||||||
.pipe(gulp.dest('./build/'));
|
.pipe(gulp.dest('./build/'))
|
||||||
|
.on('end', function() {
|
||||||
|
createLink('./build/ripple-' + pkg.version + '-min.js',
|
||||||
|
'./build/ripple-latest-min.js');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('build-debug', function(callback) {
|
gulp.task('build-debug', function(callback) {
|
||||||
|
|||||||
64
HISTORY.md
64
HISTORY.md
@@ -1,3 +1,67 @@
|
|||||||
|
##0.16.5
|
||||||
|
**Changes**
|
||||||
|
+ [Filter insufficient source funds paths from pathfind results](https://github.com/ripple/ripple-lib/pull/688)
|
||||||
|
|
||||||
|
##0.16.4
|
||||||
|
**Changes**
|
||||||
|
+ [Update ws to 1.0.1](https://github.com/ripple/ripple-lib/pull/682)
|
||||||
|
|
||||||
|
##0.16.2
|
||||||
|
**Changes**
|
||||||
|
+ [Bump ripple-binary-codec dependency version to 0.1.1 to fix issue with computeLedgerHash for transactions with DeliverMin]
|
||||||
|
|
||||||
|
##0.16.1
|
||||||
|
**Changes**
|
||||||
|
+ [FIX: Use assert not assert-diff](https://github.com/ripple/ripple-lib/commit/f6ebe325193e7208c5ee8d8e84a7504714f0009e)
|
||||||
|
|
||||||
|
##0.16.0
|
||||||
|
**Breaking Changes**
|
||||||
|
+ [Fix types of XRP values in getServerInfo response](https://github.com/ripple/ripple-lib/commit/99d08065e4bda3dda6ae1f183adbd11abc70a9b7)
|
||||||
|
+ [Change error event format and fix crash due to error event on webscocket](https://github.com/ripple/ripple-lib/commit/9cd72595f0efc062d77b9d625695d6030c524cc6)
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
+ [Fix generateAddress docs and add error event listener to boilerplate](https://github.com/ripple/ripple-lib/commit/809d981987a2890fac3a73a40a05c598b9040334)
|
||||||
|
+ [Allow setting maxLedgerVersion to null to specify no maximum](https://github.com/ripple/ripple-lib/commit/82613e7e8b360d1ae1552eab4559ab4763c06d7e)
|
||||||
|
+ [Add support for client certificates](https://github.com/ripple/ripple-lib/commit/5f5e48e4140345d166b8c1a3ee0847b0d9e2d893)
|
||||||
|
+ [getFee returns a string not float](https://github.com/ripple/ripple-lib/commit/7bf2da6014c87e164542e69356efeaabb575a157)
|
||||||
|
+ [Fix parsing of quality for getTrustlines](https://github.com/ripple/ripple-lib/commit/86ff315ef2a39dfdc2ce97e0e1c4aa73f04e363b)
|
||||||
|
+ [Fix DeliverMin value when specifying minAmount](Fix DeliverMin value when specifying minAmount)
|
||||||
|
+ [http server example](https://github.com/ripple/ripple-lib/commit/76866ab901ea46a2dd73181605e0f7f4220043d4)
|
||||||
|
|
||||||
|
|
||||||
|
##0.15.2
|
||||||
|
**Changes**
|
||||||
|
+ [Fix support for proxy credentials in proxy URL and fix error when there are more than 10 outstanding requests](https://github.com/ripple/ripple-lib/commit/0990ad4a6f1d59ca9d2cb859b4e2d71693f3fc4b)
|
||||||
|
|
||||||
|
##0.15.1
|
||||||
|
**Changes**
|
||||||
|
+ [Fix babel-polyfill require](https://github.com/ripple/ripple-lib/commit/062148674c3b1293ab82c28e25615ddd530339fa)
|
||||||
|
+ [Fix samples](https://github.com/ripple/ripple-lib/commit/5d5cf868a2ddb1b1cd40e4a4f0a782d0066c2055)
|
||||||
|
+ [add unit tests for RippleAPIBroadcast](https://github.com/ripple/ripple-lib/commit/ddf8fe5b1a9c750490dca98fb9ffaaf8017f87e0)
|
||||||
|
|
||||||
|
##0.15.0
|
||||||
|
**Breaking Changes**
|
||||||
|
+ ["servers" parameter changed to single "server"](https://github.com/ripple/ripple-lib/commit/7061e9afe46f0682254d098adeff3dd7157521a1)
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
+ [fix handling memos in prepareSettings](https://github.com/ripple/ripple-lib/commit/c9704137b7b538e8dbf31c483bcdcf2dcfd7cd75)
|
||||||
|
+ [Docs: SusPay warnings, offline mode, and other tweaks](https://github.com/ripple/ripple-lib/commit/4b4fc36ebd93f1360781a65f2869bd2c4f0a5093)
|
||||||
|
+ [Fix prepareOrderCancellation documentation](https://github.com/ripple/ripple-lib/commit/5e720891f579fd73d43c64e5ec519d9121023c10)
|
||||||
|
|
||||||
|
##0.14.0
|
||||||
|
**Breaking Changes**
|
||||||
|
+ [prepareOrderCancellation now takes orderCancellation specification](https://github.com/ripple/ripple-lib/commit/7f33d8a71e56289e5a5e0ead1c74f75ebcde72bc)
|
||||||
|
+ [Rename "ledgerClosed" event to "ledger" and change format](https://github.com/ripple/ripple-lib/commit/8a3d4a64db5fbf560ebf87dc62e0212513c5e18a)
|
||||||
|
|
||||||
|
**Changes**
|
||||||
|
+ [Fix proxy support and add support for proxy authorization](https://github.com/ripple/ripple-lib/commit/14b840f3feca758e0384b746c94e36d8bf59b8c2)
|
||||||
|
+ [Fix trace option](https://github.com/ripple/ripple-lib/commit/af620755c53556c55eed12de4b0013ef5a349ce2)
|
||||||
|
+ [Allow memos on all transaction types](https://github.com/ripple/ripple-lib/commit/b5081344da8e66fbd3a5113cc3313325ef72a494)
|
||||||
|
+ [Add documentation for RippleAPI options](https://github.com/ripple/ripple-lib/commit/a76b554cadb9f9f918b06f8386bc29355682a1a4)
|
||||||
|
+ [Docs: more on basic types, tx types](https://github.com/ripple/ripple-lib/commit/fdbac63f466b4fd3be701d4878800d856692e26e)
|
||||||
|
+ [Docs: revised introductory material](https://github.com/ripple/ripple-lib/commit/ef2515507dbd3c6a426ab5b31332a1bdf72d5b2d)
|
||||||
|
+ [boost coverage to almost 100%](https://github.com/ripple/ripple-lib/commit/995606b1e6f3643af34d9fd442ccd31f320b03eb)
|
||||||
|
|
||||||
##0.13.2
|
##0.13.2
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#ripple-lib
|
#ripple-lib
|
||||||
|
|
||||||
A JavaScript API for interacting with Ripple in Node.js and the browser
|
A JavaScript API for interacting with Ripple in Node.js
|
||||||
|
|
||||||
[](https://circleci.com/gh/ripple/ripple-lib/tree/develop) [](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
|
[](https://circleci.com/gh/ripple/ripple-lib/tree/develop) [](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
|
||||||
|
|
||||||
@@ -8,7 +8,7 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
|
|||||||
|
|
||||||
###Features
|
###Features
|
||||||
|
|
||||||
+ Connect to a rippled server in JavaScript (Node.js or browser)
|
+ Connect to a rippled server in Node.js
|
||||||
+ Issue [rippled API](https://ripple.com/build/rippled-apis/) requests
|
+ Issue [rippled API](https://ripple.com/build/rippled-apis/) requests
|
||||||
+ Listen to events on the Ripple network (transaction, ledger, etc.)
|
+ Listen to events on the Ripple network (transaction, ledger, etc.)
|
||||||
+ Sign and submit transactions to the Ripple network
|
+ Sign and submit transactions to the Ripple network
|
||||||
|
|||||||
11
circle.yml
11
circle.yml
@@ -1,7 +1,18 @@
|
|||||||
machine:
|
machine:
|
||||||
node:
|
node:
|
||||||
version: 0.12.0
|
version: 0.12.0
|
||||||
|
hosts:
|
||||||
|
testripple.circleci.com: 127.0.0.1
|
||||||
|
dependencies:
|
||||||
|
pre:
|
||||||
|
- wget https://s3-us-west-2.amazonaws.com/ripple-debs/rippled_0.30.1-b11-1.deb
|
||||||
|
- sudo dpkg -i rippled_0.30.1-b11-1.deb
|
||||||
test:
|
test:
|
||||||
|
pre:
|
||||||
|
- rippled -a --start --conf "$HOME/$CIRCLE_PROJECT_REPONAME/test/integration/rippled.cfg":
|
||||||
|
background: true
|
||||||
override:
|
override:
|
||||||
- scripts/ci.sh "$CIRCLE_NODE_INDEX" "$CIRCLE_NODE_TOTAL":
|
- scripts/ci.sh "$CIRCLE_NODE_INDEX" "$CIRCLE_NODE_TOTAL":
|
||||||
parallel: true
|
parallel: true
|
||||||
|
post:
|
||||||
|
- killall /usr/bin/rippled
|
||||||
|
|||||||
448
docs/index.md
448
docs/index.md
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
- [Introduction](#introduction)
|
- [Introduction](#introduction)
|
||||||
- [Boilerplate](#boilerplate)
|
- [Boilerplate](#boilerplate)
|
||||||
|
- [Offline functionality](#offline-functionality)
|
||||||
- [Basic Types](#basic-types)
|
- [Basic Types](#basic-types)
|
||||||
- [Ripple Address](#ripple-address)
|
- [Ripple Address](#ripple-address)
|
||||||
- [Account Sequence Number](#account-sequence-number)
|
- [Account Sequence Number](#account-sequence-number)
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
- [Transaction Fees](#transaction-fees)
|
- [Transaction Fees](#transaction-fees)
|
||||||
- [Transaction Instructions](#transaction-instructions)
|
- [Transaction Instructions](#transaction-instructions)
|
||||||
- [Transaction ID](#transaction-id)
|
- [Transaction ID](#transaction-id)
|
||||||
|
- [Transaction Memos](#transaction-memos)
|
||||||
- [Transaction Specifications](#transaction-specifications)
|
- [Transaction Specifications](#transaction-specifications)
|
||||||
- [Payment](#payment)
|
- [Payment](#payment)
|
||||||
- [Trustline](#trustline)
|
- [Trustline](#trustline)
|
||||||
@@ -52,28 +54,41 @@
|
|||||||
- [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
|
- [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
|
||||||
- [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
|
- [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
|
||||||
- [sign](#sign)
|
- [sign](#sign)
|
||||||
|
- [combine](#combine)
|
||||||
- [submit](#submit)
|
- [submit](#submit)
|
||||||
- [generateAddress](#generateaddress)
|
- [generateAddress](#generateaddress)
|
||||||
- [computeLedgerHash](#computeledgerhash)
|
- [computeLedgerHash](#computeledgerhash)
|
||||||
- [API Events](#api-events)
|
- [API Events](#api-events)
|
||||||
- [ledgerClosed](#ledgerclosed)
|
- [ledger](#ledger)
|
||||||
- [error](#error)
|
- [error](#error)
|
||||||
|
|
||||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
RippleAPI allows you to query and submit transactions to a node on the Ripple network.
|
RippleAPI is the official client library to the Ripple Consensus Ledger. Currently, RippleAPI is only available in JavaScript.
|
||||||
|
Using RippleAPI, you can:
|
||||||
|
|
||||||
|
* [Query transactions from the network](#gettransaction)
|
||||||
|
* [Sign](#sign) transactions securely without connecting to any server
|
||||||
|
* [Submit](#submit) transactions to the Ripple Consensus Ledger, including [Payments](#payment), [Orders](#order), [Settings changes](#settings), and [other types](#transaction-types)
|
||||||
|
* [Generate a new Ripple Address](#generateaddress)
|
||||||
|
* ... and [much more](#api-methods).
|
||||||
|
|
||||||
RippleAPI only provides access to *validated*, *immutable* transaction data.
|
RippleAPI only provides access to *validated*, *immutable* transaction data.
|
||||||
|
|
||||||
## Boilerplate
|
## Boilerplate
|
||||||
|
|
||||||
|
Use the following [boilerplate code](https://en.wikipedia.org/wiki/Boilerplate_code) to wrap your custom code using RippleAPI.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const {RippleAPI} = require('ripple-lib');
|
const {RippleAPI} = require('ripple-lib');
|
||||||
|
|
||||||
const api = new RippleAPI({
|
const api = new RippleAPI({
|
||||||
servers: ['wss://s1.ripple.com']
|
server: 'wss://s1.ripple.com' // Public rippled server hosted by Ripple, Inc.
|
||||||
|
});
|
||||||
|
api.on('error', (errorCode, errorMessage) => {
|
||||||
|
console.log(errorCode + ': ' + errorMessage);
|
||||||
});
|
});
|
||||||
api.connect().then(() => {
|
api.connect().then(() => {
|
||||||
/* insert code here */
|
/* insert code here */
|
||||||
@@ -82,26 +97,87 @@ api.connect().then(() => {
|
|||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
```
|
```
|
||||||
|
|
||||||
To get started, first install [nodejs](https://nodejs.org) version `0.12.0` or greater, then:
|
RippleAPI is designed to work in [NodeJS](https://nodejs.org) (version `0.12.0` or greater) using [Babel](https://babeljs.io/) for [ECMAScript 6](https://babeljs.io/docs/learn-es2015/) support.
|
||||||
|
|
||||||
`npm install -g babel`
|
The code samples in this documentation are written in ES6, but `RippleAPI` will work with ES5 also. Regardless of whether you use ES5 or ES6, the methods that return promises will return [ES6-style promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||||
|
|
||||||
`npm install ripple-lib`
|
|
||||||
|
|
||||||
Then create a script based on the boilerplate shown here and run with:
|
|
||||||
|
|
||||||
`babel-node script.js`
|
|
||||||
|
|
||||||
The code samples in this documentation are written in ES6, but `RippleAPI` will work with ES5 also. Regardless of whether you use ES5 or ES6, the methods that return promises will return ES6-style promises.
|
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
All the code snippets in this documentation assume that you have surrounded them with this boilerplate.
|
All the code snippets in this documentation assume that you have surrounded them with this boilerplate.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
Dont forget the "catch" or errors may not be visible.
|
If you omit the "catch" section, errors may not be visible.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
The "error" event is emitted whenever an error occurs that cannot be associated with a specific request. If the listener is not registered, an exception will be thrown whenever the event is emitted.
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
The RippleAPI constructor optionally takes one argument, an object with the following options:
|
||||||
|
|
||||||
|
Name | Type | Description
|
||||||
|
---- | ---- | -----------
|
||||||
|
authorization | string | *Optional* Username and password for HTTP basic authentication to the rippled server in the format **username:password**.
|
||||||
|
certificate | string | *Optional* A string containing the certificate key of the client in PEM format. (Can be an array of certificates).
|
||||||
|
feeCushion | number | *Optional* Factor to multiply estimated fee by to provide a cushion in case the required fee rises during submission of a transaction. Defaults to `1.2`.
|
||||||
|
key | string | *Optional* A string containing the private key of the client in PEM format. (Can be an array of keys).
|
||||||
|
passphrase | string | *Optional* The passphrase for the private key of the client.
|
||||||
|
proxy | uri string | *Optional* URI for HTTP/HTTPS proxy to use to connect to the rippled server.
|
||||||
|
proxyAuthorization | string | *Optional* Username and password for HTTP basic authentication to the proxy in the format **username:password**.
|
||||||
|
server | uri string | *Optional* URI for rippled websocket port to connect to. Must start with `wss://` or `ws://`.
|
||||||
|
timeout | integer | *Optional* Timeout in milliseconds before considering a request to have failed.
|
||||||
|
trace | boolean | *Optional* If true, log rippled requests and responses to stdout.
|
||||||
|
trustedCertificates | array\<string\> | *Optional* Array of PEM-formatted SSL certificates to trust when connecting to a proxy. This is useful if you want to use a self-signed certificate on the proxy server. Note: Each element must contain a single certificate; concatenated certificates are not valid.
|
||||||
|
|
||||||
|
If you omit the `server` parameter, RippleAPI operates [offline](#offline-functionality).
|
||||||
|
|
||||||
|
|
||||||
|
### Installation ###
|
||||||
|
|
||||||
|
1. Install [NodeJS](https://nodejs.org) and the Node Package Manager (npm). Most Linux distros have a package for NodeJS, but make sure you have version `0.12.0` or higher.
|
||||||
|
2. Use npm to install [Babel](https://babeljs.io/) globally:
|
||||||
|
`npm install -g babel-cli`
|
||||||
|
3. Use npm to install RippleAPI:
|
||||||
|
`npm install ripple-lib`
|
||||||
|
|
||||||
|
After you have installed ripple-lib, you can create scripts using the [boilerplate](#boilerplate) and run them using babel-node:
|
||||||
|
`babel-node script.js`
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
Instead of using babel-node in production, we recommend using Babel to transpile to ECMAScript 5 first.
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
|
||||||
|
## Offline functionality
|
||||||
|
|
||||||
|
RippleAPI can also function without internet connectivity. This can be useful in order to generate secrets and sign transactions from a secure, isolated machine.
|
||||||
|
|
||||||
|
To instantiate RippleAPI in offline mode, use the following boilerplate code:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const {RippleAPI} = require('ripple-lib');
|
||||||
|
|
||||||
|
const api = new RippleAPI();
|
||||||
|
/* insert code here */
|
||||||
|
```
|
||||||
|
|
||||||
|
Methods that depend on the state of the Ripple Consensus Ledger are unavailable in offline mode. To prepare transactions offline, you **must** specify the `fee`, `sequence`, and `maxLedgerVersion` parameters in the [transaction instructions](#transaction-instructions). The following methods should work offline:
|
||||||
|
|
||||||
|
* [preparePayment](#preparepayment)
|
||||||
|
* [prepareTrustline](#preparetrustline)
|
||||||
|
* [prepareOrder](#prepareorder)
|
||||||
|
* [prepareOrderCancellation](#prepareordercancellation)
|
||||||
|
* [prepareSettings](#preparesettings)
|
||||||
|
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
|
||||||
|
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
|
||||||
|
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
|
||||||
|
* [sign](#sign)
|
||||||
|
* [generateAddress](#generateaddress)
|
||||||
|
* [computeLedgerHash](#computeledgerhash)
|
||||||
|
|
||||||
|
|
||||||
# Basic Types
|
# Basic Types
|
||||||
|
|
||||||
## Ripple Address
|
## Ripple Address
|
||||||
@@ -110,23 +186,28 @@ Dont forget the "catch" or errors may not be visible.
|
|||||||
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||||
```
|
```
|
||||||
|
|
||||||
Every Ripple account has an *address*, which is a base58-encoding of a hash of the account's public key.
|
Every Ripple account has an *address*, which is a base58-encoding of a hash of the account's public key. Ripple addresses always start with the lowercase letter `r`.
|
||||||
|
|
||||||
## Account Sequence Number
|
## Account Sequence Number
|
||||||
|
|
||||||
Every Ripple account has a *sequence number* that is used to order transactions. Every transaction must have a sequence number and transaction can only be executed in order by sequence number. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
|
Every Ripple account has a *sequence number* that is used to keep transactions in order. Every transaction must have a sequence number. A transaction can only be executed if it has the next sequence number in order, of the account sending it. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
|
||||||
|
|
||||||
## Currency
|
## Currency
|
||||||
|
|
||||||
Currencies are represented as either 3-character currency codes or 40-character uppercase hexadecimal strings. We recommend using uppercase [ISO 4217 Currency Codes](http://www.xe.com/iso4217.php) only. The string "XRP" is disallowed on trustlines because it is reserved for the Ripple native currency. The following characters are permitted: all uppercase and lowercase letters, digits, as well as the symbols `?`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `<`, `>`, `(`, `)`, `{`, `}`, `[`, `]`, and `|`.
|
Currencies are represented as either 3-character currency codes or 40-character uppercase hexadecimal strings. We recommend using uppercase [ISO 4217 Currency Codes](http://www.xe.com/iso4217.php) only. The string "XRP" is disallowed on trustlines because it is reserved for the Ripple native currency. The following characters are permitted: all uppercase and lowercase letters, digits, as well as the symbols `?`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `<`, `>`, `(`, `)`, `{`, `}`, `[`, `]`, and `|`.
|
||||||
|
|
||||||
## Value
|
## Value
|
||||||
A *value* is a quantity of a currency represented as a decimal string (string encoding is used because javascript numbers do not have sufficient precision).
|
A *value* is a quantity of a currency represented as a decimal string. Be careful: JavaScript's native number format does not have sufficient precision to represent all values. XRP has different precision from other currencies.
|
||||||
|
|
||||||
|
**XRP** has 6 significant digits past the decimal point. In other words, XRP cannot be divided into positive values smaller than `0.000001` (1e-6). XRP has a maximum value of `100000000000` (1e11).
|
||||||
|
|
||||||
|
**Non-XRP values** have 15 decimal digits of precision, with a maximum value of `9999999999999999e80`. The smallest positive non-XRP value is `1e-81`.
|
||||||
|
|
||||||
An XRP value has 6 significant digits past the decimal point. A non-XRP value has 16 total digits of precision.
|
|
||||||
|
|
||||||
## Amount
|
## Amount
|
||||||
|
|
||||||
|
Example amount:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
@@ -135,6 +216,7 @@ An XRP value has 6 significant digits past the decimal point. A non-XRP value ha
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Example XRP amount:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"currency": "XRP",
|
"currency": "XRP",
|
||||||
@@ -142,7 +224,7 @@ An XRP value has 6 significant digits past the decimal point. A non-XRP value ha
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value (for all currencies besides "XRP").
|
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value. For XRP, there is no counterparty.
|
||||||
|
|
||||||
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
||||||
|
|
||||||
@@ -164,7 +246,7 @@ A transaction type is specified by the strings in the first column in the table
|
|||||||
|
|
||||||
Type | Description
|
Type | Description
|
||||||
---- | -----------
|
---- | -----------
|
||||||
[payment](#payment) | A `payment` transaction represents a transfer of value from one account to another. Depending on the path taken, additional exchanges of value may occur atomically to facilitate the payment.
|
[payment](#payment) | A `payment` transaction represents a transfer of value from one account to another. Depending on the [path](https://ripple.com/build/paths/) taken, additional exchanges of value may occur atomically to facilitate the payment.
|
||||||
[order](#order) | An `order` transaction creates a limit order. It defines an intent to exchange currencies, and creates an order in the Ripple Consensus Ledger's order book if not completely fulfilled when placed. Orders can be partially fulfilled.
|
[order](#order) | An `order` transaction creates a limit order. It defines an intent to exchange currencies, and creates an order in the Ripple Consensus Ledger's order book if not completely fulfilled when placed. Orders can be partially fulfilled.
|
||||||
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the Ripple Consensus Ledger's order book.
|
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the Ripple Consensus Ledger's order book.
|
||||||
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
|
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
|
||||||
@@ -173,32 +255,45 @@ Type | Description
|
|||||||
[suspendedPaymentCancellation](#suspended-payment-cancellation) | A `suspendedPaymentCancellation` transaction unlocks the funds in a suspended payment and sends them back to the creator of the suspended payment, but it will only work after the suspended payment expires.
|
[suspendedPaymentCancellation](#suspended-payment-cancellation) | A `suspendedPaymentCancellation` transaction unlocks the funds in a suspended payment and sends them back to the creator of the suspended payment, but it will only work after the suspended payment expires.
|
||||||
[suspendedPaymentExecution](#suspended-payment-execution) | A `suspendedPaymentExecution` transaction unlocks the funds in a suspended payment and sends them to the destination of the suspended payment, but it will only work if the cryptographic condition is provided.
|
[suspendedPaymentExecution](#suspended-payment-execution) | A `suspendedPaymentExecution` transaction unlocks the funds in a suspended payment and sends them to the destination of the suspended payment, but it will only work if the cryptographic condition is provided.
|
||||||
|
|
||||||
|
The three "suspended payment" transaction types are not supported by the production Ripple peer-to-peer network at this time. They are available for testing purposes if you [configure RippleAPI](#boilerplate) to connect to the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) instead.
|
||||||
|
|
||||||
## Transaction Flow
|
## Transaction Flow
|
||||||
|
|
||||||
Executing a transaction with `RippleAPI` requires the following four steps:
|
Executing a transaction with `RippleAPI` requires the following four steps:
|
||||||
|
|
||||||
1. prepare - Create an unsigned transaction based on a [specification](#transaction-specifications) and [instructions](#transaction-instructions).
|
1. Prepare - Create an unsigned transaction based on a [specification](#transaction-specifications) and [instructions](#transaction-instructions). There is a method to prepare each type of transaction:
|
||||||
2. sign - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place.
|
* [preparePayment](#preparepayment)
|
||||||
3. submit - Submit the transaction to the connected server.
|
* [prepareTrustline](#preparetrustline)
|
||||||
4. verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted. It is recommended that you specify a `maxLedgerVersion` in the instructions when preparing a transaction because without it there is no way to know that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the network ledger version exceeds the `maxLedgerVersion` provided in the transaction instructions.
|
* [prepareOrder](#prepareorder)
|
||||||
|
* [prepareOrderCancellation](#prepareordercancellation)
|
||||||
|
* [prepareSettings](#preparesettings)
|
||||||
|
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
|
||||||
|
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
|
||||||
|
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
|
||||||
|
2. [Sign](#sign) - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place. For multisignature transactions, the `signedTransaction` fields returned by `sign` must be collected and passed to the [combine](#combine) method.
|
||||||
|
3. [Submit](#submit) - Submit the transaction to the connected server.
|
||||||
|
4. Verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted.
|
||||||
|
|
||||||
## Transaction Fees
|
## Transaction Fees
|
||||||
|
|
||||||
Every transaction requires a *fee* to be paid in XRP. The fee is destroyed; it is not sent to any other party. The purpose of the fee is to prevent denial of service attacks on the Ripple network.
|
Every transaction must destroy a small amount of XRP as a cost to send the transaction. This is also called a *transaction fee*. The transaction cost is designed to increase along with the load on the Ripple network, making it very expensive to deliberately or inadvertently overload the network.
|
||||||
|
|
||||||
You can choose the size of the fee you want to pay or let a default be used. The fee is like a bid in an auction for slots in the next ledger closing. If the fee you choose is too low, your transaction will not be included in the next ledger closing. You can get an estimate of the fee required to be included in the next ledger closing with the [getFee](#getfee) method.
|
You can choose the size of the fee you want to pay or let a default be used. You can get an estimate of the fee required to be included in the next ledger closing with the [getFee](#getfee) method.
|
||||||
|
|
||||||
## Transaction Instructions
|
## Transaction Instructions
|
||||||
|
|
||||||
Transactions instructions indicates how to execute a transaction, complementary with the [transaction specification](#transaction-specifications).
|
Transaction instructions indicate how to execute a transaction, complementary with the [transaction specification](#transaction-specifications).
|
||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
---- | ---- | -----------
|
---- | ---- | -----------
|
||||||
fee | [value](#value) | *Optional* An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
fee | [value](#value) | *Optional* An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
maxFee | [value](#value) | *Optional* The maximum fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
maxFee | [value](#value) | *Optional* The maximum fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
maxLedgerVersion | integer,null | *Optional* The highest ledger version that the transaction can be included in. If this option and `maxLedgerVersionOffset` are both omitted, the `maxLedgerVersion` option will default to 3 greater than the current validated ledger version (equivalent to `maxLedgerVersionOffset=3`). Use `null` to not set a maximum ledger version.
|
||||||
maxLedgerVersionOffset | integer | *Optional* Offset from current legder version to highest ledger version that the transaction can be included in.
|
maxLedgerVersionOffset | integer | *Optional* Offset from current validated legder version to highest ledger version that the transaction can be included in.
|
||||||
sequence | [sequence](#account-sequence-number) | *Optional* The initiating account's sequence number for this transaction.
|
sequence | [sequence](#account-sequence-number) | *Optional* The initiating account's sequence number for this transaction.
|
||||||
|
signersCount | integer | *Optional* Number of signers that will be signing this transaction.
|
||||||
|
|
||||||
|
We recommended that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the network ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare*" method.
|
||||||
|
|
||||||
## Transaction ID
|
## Transaction ID
|
||||||
|
|
||||||
@@ -206,7 +301,19 @@ sequence | [sequence](#account-sequence-number) | *Optional* The initiating acco
|
|||||||
"F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF"
|
"F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF"
|
||||||
```
|
```
|
||||||
|
|
||||||
A hash of the transaction that can be used to identify it. A transaction can be looked up by its ID using the [getTransaction](#gettransaction) method.
|
A transaction ID is a 64-bit hexadecimal string that uniquely identifies the transaction. The transaction ID is derived from the transaction instruction and specifications, using a strong hash function.
|
||||||
|
|
||||||
|
You can look up a transaction by ID using the [getTransaction](#gettransaction) method.
|
||||||
|
|
||||||
|
## Transaction Memos
|
||||||
|
|
||||||
|
Every transaction can optionally have an array of memos for user applications. The `memos` field in each [transaction specification](#transaction-specifications) is an array of objects with the following structure:
|
||||||
|
|
||||||
|
Name | Type | Description
|
||||||
|
---- | ---- | -----------
|
||||||
|
data | string | *Optional* Arbitrary string, conventionally containing the content of the memo.
|
||||||
|
format | string | *Optional* Conventionally containing information on how the memo is encoded, for example as a [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml). Only characters allowed in URLs are permitted.
|
||||||
|
type | string | *Optional* Conventionally, a unique relation (according to [RFC 5988](http://tools.ietf.org/html/rfc5988#section-4)) that defines the format of this memo. Only characters allowed in URLs are permitted.
|
||||||
|
|
||||||
# Transaction Specifications
|
# Transaction Specifications
|
||||||
|
|
||||||
@@ -221,22 +328,18 @@ Name | Type | Description
|
|||||||
source | object | The source of the funds to be sent.
|
source | object | The source of the funds to be sent.
|
||||||
*source.* address | [address](#ripple-address) | The address to send from.
|
*source.* address | [address](#ripple-address) | The address to send from.
|
||||||
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with source.maxAmount)
|
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with source.maxAmount)
|
||||||
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.
|
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||||
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field is exclusive with source.amount)
|
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field is exclusive with source.amount)
|
||||||
destination | object | The destination of the funds to be sent.
|
destination | object | The destination of the funds to be sent.
|
||||||
*destination.* address | [address](#ripple-address) | The address to receive at.
|
*destination.* address | [address](#ripple-address) | The address to receive at.
|
||||||
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with destination.minAmount).
|
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with destination.minAmount).
|
||||||
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.
|
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||||
*destination.* address | [address](#ripple-address) | The address to send to.
|
*destination.* address | [address](#ripple-address) | The address to send to.
|
||||||
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field is exclusive with destination.amount)
|
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field is exclusive with destination.amount)
|
||||||
allowPartialPayment | boolean | *Optional* A boolean that, if set to true, indicates that this payment should go through even if the whole amount cannot be delivered because of a lack of liquidity or funds in the source account account
|
allowPartialPayment | boolean | *Optional* A boolean that, if set to true, indicates that this payment should go through even if the whole amount cannot be delivered because of a lack of liquidity or funds in the source account account
|
||||||
invoiceID | string | *Optional* A 256-bit hash that can be used to identify a particular payment.
|
invoiceID | string | *Optional* A 256-bit hash that can be used to identify a particular payment.
|
||||||
limitQuality | boolean | *Optional* Only take paths where all the conversions have an input:output ratio that is equal or better than the ratio of destination.amount:source.maxAmount.
|
limitQuality | boolean | *Optional* Only take paths where all the conversions have an input:output ratio that is equal or better than the ratio of destination.amount:source.maxAmount.
|
||||||
memos | array | *Optional* Array of memos to attach to the transaction.
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
memos[] | object | Memo objects represent arbitrary data that can be included in a transaction
|
|
||||||
*memos[].* data | string | *Optional* Arbitrary string, conventionally containing the content of the memo.
|
|
||||||
*memos[].* format | string | *Optional* Conventionally containing information on how the memo is encoded, for example as a [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml). Only characters allowed in URLs are permitted.
|
|
||||||
*memos[].* type | string | *Optional* Conventionally, a unique relation (according to [RFC 5988](http://tools.ietf.org/html/rfc5988#section-4)) that defines the format of this memo. Only characters allowed in URLs are permitted.
|
|
||||||
noDirectRipple | boolean | *Optional* A boolean that can be set to true if paths are specified and the sender would like the Ripple Network to disregard any direct paths from the source account to the destination account. This may be used to take advantage of an arbitrage opportunity or by gateways wishing to issue balances from a hot wallet to a user who has mistakenly set a trustline directly to the hot wallet
|
noDirectRipple | boolean | *Optional* A boolean that can be set to true if paths are specified and the sender would like the Ripple Network to disregard any direct paths from the source account to the destination account. This may be used to take advantage of an arbitrage opportunity or by gateways wishing to issue balances from a hot wallet to a user who has mistakenly set a trustline directly to the hot wallet
|
||||||
paths | string | *Optional* The paths of trustlines and orders to use in executing the payment.
|
paths | string | *Optional* The paths of trustlines and orders to use in executing the payment.
|
||||||
|
|
||||||
@@ -276,6 +379,7 @@ counterparty | [address](#ripple-address) | The address of the account this trus
|
|||||||
limit | [value](#value) | The maximum amount that the owner of the trustline can be owed through the trustline.
|
limit | [value](#value) | The maximum amount that the owner of the trustline can be owed through the trustline.
|
||||||
authorized | boolean | *Optional* If true, authorize the counterparty to hold issuances from this account.
|
authorized | boolean | *Optional* If true, authorize the counterparty to hold issuances from this account.
|
||||||
frozen | boolean | *Optional* If true, the trustline is frozen, which means that funds can only be sent to the owner.
|
frozen | boolean | *Optional* If true, the trustline is frozen, which means that funds can only be sent to the owner.
|
||||||
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
qualityIn | number | *Optional* Incoming balances on this trustline are valued at this ratio.
|
qualityIn | number | *Optional* Incoming balances on this trustline are valued at this ratio.
|
||||||
qualityOut | number | *Optional* Outgoing balances on this trustline are valued at this ratio.
|
qualityOut | number | *Optional* Outgoing balances on this trustline are valued at this ratio.
|
||||||
ripplingDisabled | boolean | *Optional* If true, payments cannot ripple through this trustline.
|
ripplingDisabled | boolean | *Optional* If true, payments cannot ripple through this trustline.
|
||||||
@@ -291,7 +395,14 @@ ripplingDisabled | boolean | *Optional* If true, payments cannot ripple through
|
|||||||
"qualityIn": 0.91,
|
"qualityIn": 0.91,
|
||||||
"qualityOut": 0.87,
|
"qualityOut": 0.87,
|
||||||
"ripplingDisabled": true,
|
"ripplingDisabled": true,
|
||||||
"frozen": false
|
"frozen": false,
|
||||||
|
"memos": [
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"format": "plain/text",
|
||||||
|
"data": "texted data"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -308,6 +419,7 @@ totalPrice | [amount](#amount) | The total price to be paid for the `quantity` t
|
|||||||
expirationTime | date-time string | *Optional* Time after which the offer is no longer active, as an [ISO 8601 date-time](https://en.wikipedia.org/wiki/ISO_8601).
|
expirationTime | date-time string | *Optional* Time after which the offer is no longer active, as an [ISO 8601 date-time](https://en.wikipedia.org/wiki/ISO_8601).
|
||||||
fillOrKill | boolean | *Optional* Treat the offer as a [Fill or Kill order](http://en.wikipedia.org/wiki/Fill_or_kill). Only attempt to match existing offers in the ledger, and only do so if the entire quantity can be exchanged.
|
fillOrKill | boolean | *Optional* Treat the offer as a [Fill or Kill order](http://en.wikipedia.org/wiki/Fill_or_kill). Only attempt to match existing offers in the ledger, and only do so if the entire quantity can be exchanged.
|
||||||
immediateOrCancel | boolean | *Optional* Treat the offer as an [Immediate or Cancel order](http://en.wikipedia.org/wiki/Immediate_or_cancel). If enabled, the offer will never become a ledger node: it only attempts to match existing offers in the ledger.
|
immediateOrCancel | boolean | *Optional* Treat the offer as an [Immediate or Cancel order](http://en.wikipedia.org/wiki/Immediate_or_cancel). If enabled, the offer will never become a ledger node: it only attempts to match existing offers in the ledger.
|
||||||
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
passive | boolean | *Optional* If enabled, the offer will not consume offers that exactly match it, and instead becomes an Offer node in the ledger. It will still consume offers that cross it.
|
passive | boolean | *Optional* If enabled, the offer will not consume offers that exactly match it, and instead becomes an Offer node in the ledger. It will still consume offers that cross it.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
@@ -325,7 +437,8 @@ passive | boolean | *Optional* If enabled, the offer will not consume offers tha
|
|||||||
"currency": "XRP",
|
"currency": "XRP",
|
||||||
"value": "2"
|
"value": "2"
|
||||||
},
|
},
|
||||||
"immediateOrCancel": true
|
"passive": true,
|
||||||
|
"fillOrKill": true
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -337,12 +450,15 @@ See [Transaction Types](#transaction-types) for a description.
|
|||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
---- | ---- | -----------
|
---- | ---- | -----------
|
||||||
orderSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the order to cancel.
|
orderSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the order to cancel.
|
||||||
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
|
|
||||||
```json
|
```json
|
||||||
23
|
{
|
||||||
|
"orderSequence": 23
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -359,12 +475,19 @@ domain | string | *Optional* The domain that owns this account, as a hexadecima
|
|||||||
emailHash | string,null | *Optional* Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.
|
emailHash | string,null | *Optional* Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.
|
||||||
enableTransactionIDTracking | boolean | *Optional* Track the ID of this account’s most recent transaction.
|
enableTransactionIDTracking | boolean | *Optional* Track the ID of this account’s most recent transaction.
|
||||||
globalFreeze | boolean | *Optional* Freeze all assets issued by this account.
|
globalFreeze | boolean | *Optional* Freeze all assets issued by this account.
|
||||||
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
messageKey | string | *Optional* Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple.
|
messageKey | string | *Optional* Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple.
|
||||||
noFreeze | boolean | *Optional* Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled.
|
noFreeze | boolean | *Optional* Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled.
|
||||||
passwordSpent | boolean | *Optional* Indicates that the account has used its free SetRegularKey transaction.
|
passwordSpent | boolean | *Optional* Indicates that the account has used its free SetRegularKey transaction.
|
||||||
regularKey | [address](#ripple-address),null | *Optional* The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key.
|
regularKey | [address](#ripple-address),null | *Optional* The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key.
|
||||||
requireAuthorization | boolean | *Optional* If set, this account must individually approve other users in order for those users to hold this account’s issuances.
|
requireAuthorization | boolean | *Optional* If set, this account must individually approve other users in order for those users to hold this account’s issuances.
|
||||||
requireDestinationTag | boolean | *Optional* Requires incoming payments to specify a destination tag.
|
requireDestinationTag | boolean | *Optional* Requires incoming payments to specify a destination tag.
|
||||||
|
signers | object | *Optional* Settings that determine what sets of accounts can be used to sign a transaction on behalf of this account using multisigning.
|
||||||
|
*signers.* threshold | integer | *Optional* A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`.
|
||||||
|
*signers.* weights | array | *Optional* Weights of signatures for each signer.
|
||||||
|
*signers.* weights[] | object | An association of an address and a weight.
|
||||||
|
*signers.weights[].* address | [address](#ripple-address) | A Ripple account address
|
||||||
|
*signers.weights[].* weight | integer | The weight that the signature of this account counts as towards the threshold.
|
||||||
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, represented as billionths of a unit. Use `null` to set no fee.
|
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, represented as billionths of a unit. Use `null` to set no fee.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
@@ -372,7 +495,14 @@ transferRate | number,null | *Optional* The fee to charge when users transfer t
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"domain": "ripple.com"
|
"domain": "ripple.com",
|
||||||
|
"memos": [
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"format": "plain/text",
|
||||||
|
"data": "texted data"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -386,19 +516,15 @@ Name | Type | Description
|
|||||||
source | object | Fields pertaining to the source of the payment.
|
source | object | Fields pertaining to the source of the payment.
|
||||||
*source.* address | [address](#ripple-address) | The address to send from.
|
*source.* address | [address](#ripple-address) | The address to send from.
|
||||||
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field is exclusive with source.amount)
|
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field is exclusive with source.amount)
|
||||||
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.
|
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||||
destination | object | Fields pertaining to the destination of the payment.
|
destination | object | Fields pertaining to the destination of the payment.
|
||||||
*destination.* address | [address](#ripple-address) | The address to receive at.
|
*destination.* address | [address](#ripple-address) | The address to receive at.
|
||||||
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with destination.minAmount).
|
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with destination.minAmount).
|
||||||
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.
|
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||||
allowCancelAfter | date-time string | *Optional* If present, the suspended payment may be cancelled after this time.
|
allowCancelAfter | date-time string | *Optional* If present, the suspended payment may be cancelled after this time.
|
||||||
allowExecuteAfter | date-time string | *Optional* If present, the suspended payment can not be executed before this time.
|
allowExecuteAfter | date-time string | *Optional* If present, the suspended payment can not be executed before this time.
|
||||||
digest | string | *Optional* If present, proof is required upon execution.
|
digest | string | *Optional* If present, proof is required upon execution.
|
||||||
memos | array | *Optional* Array of memos to attach to the transaction.
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
memos[] | object | Memo objects represent arbitrary data that can be included in a transaction
|
|
||||||
*memos[].* data | string | *Optional* Arbitrary string, conventionally containing the content of the memo.
|
|
||||||
*memos[].* format | string | *Optional* Conventionally containing information on how the memo is encoded, for example as a [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml). Only characters allowed in URLs are permitted.
|
|
||||||
*memos[].* type | string | *Optional* Conventionally, a unique relation (according to [RFC 5988](http://tools.ietf.org/html/rfc5988#section-4)) that defines the format of this memo. Only characters allowed in URLs are permitted.
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -434,11 +560,7 @@ Name | Type | Description
|
|||||||
---- | ---- | -----------
|
---- | ---- | -----------
|
||||||
owner | [address](#ripple-address) | The address of the owner of the suspended payment to cancel.
|
owner | [address](#ripple-address) | The address of the owner of the suspended payment to cancel.
|
||||||
suspensionSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to cancel.
|
suspensionSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to cancel.
|
||||||
memos | array | *Optional* Array of memos to attach to the transaction.
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
memos[] | object | Memo objects represent arbitrary data that can be included in a transaction
|
|
||||||
*memos[].* data | string | *Optional* Arbitrary string, conventionally containing the content of the memo.
|
|
||||||
*memos[].* format | string | *Optional* Conventionally containing information on how the memo is encoded, for example as a [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml). Only characters allowed in URLs are permitted.
|
|
||||||
*memos[].* type | string | *Optional* Conventionally, a unique relation (according to [RFC 5988](http://tools.ietf.org/html/rfc5988#section-4)) that defines the format of this memo. Only characters allowed in URLs are permitted.
|
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -460,11 +582,7 @@ Name | Type | Description
|
|||||||
owner | [address](#ripple-address) | The address of the owner of the suspended payment to execute.
|
owner | [address](#ripple-address) | The address of the owner of the suspended payment to execute.
|
||||||
suspensionSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to execute.
|
suspensionSequence | [sequence](#account-sequence-number) | The [account sequence number](#account-sequence-number) of the [Suspended Payment Creation](#suspended-payment-creation) transaction for the suspended payment to execute.
|
||||||
digest | string | *Optional* The original `digest` from the suspended payment creation transaction. This is sha256 hash of `proof` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old digest with the new digest.
|
digest | string | *Optional* The original `digest` from the suspended payment creation transaction. This is sha256 hash of `proof` string. It is replicated here so that the relatively expensive hashing operation can be delegated to a server without ledger history and the server with ledger history only has to do a quick comparison of the old digest with the new digest.
|
||||||
memos | array | *Optional* Array of memos to attach to the transaction.
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
memos[] | object | Memo objects represent arbitrary data that can be included in a transaction
|
|
||||||
*memos[].* data | string | *Optional* Arbitrary string, conventionally containing the content of the memo.
|
|
||||||
*memos[].* format | string | *Optional* Conventionally containing information on how the memo is encoded, for example as a [MIME type](http://www.iana.org/assignments/media-types/media-types.xhtml). Only characters allowed in URLs are permitted.
|
|
||||||
*memos[].* type | string | *Optional* Conventionally, a unique relation (according to [RFC 5988](http://tools.ietf.org/html/rfc5988#section-4)) that defines the format of this memo. Only characters allowed in URLs are permitted.
|
|
||||||
method | integer | *Optional* The method for verifying the proof; only method `1` is supported.
|
method | integer | *Optional* The method for verifying the proof; only method `1` is supported.
|
||||||
proof | string | *Optional* A value that produces the digest when hashed. It must be 32 charaters long and contain only 8-bit characters.
|
proof | string | *Optional* A value that produces the digest when hashed. It must be 32 charaters long and contain only 8-bit characters.
|
||||||
|
|
||||||
@@ -488,7 +606,7 @@ proof | string | *Optional* A value that produces the digest when hashed. It mus
|
|||||||
|
|
||||||
`connect(): Promise<void>`
|
`connect(): Promise<void>`
|
||||||
|
|
||||||
Tells the RippleAPI instance to connect to its server(s).
|
Tells the RippleAPI instance to connect to its rippled server.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
@@ -506,7 +624,7 @@ See [Boilerplate](#boilerplate) for code sample.
|
|||||||
|
|
||||||
`disconnect(): Promise<void>`
|
`disconnect(): Promise<void>`
|
||||||
|
|
||||||
Tells the RippleAPI instance to disconnect from its server(s).
|
Tells the RippleAPI instance to disconnect from its rippled server.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
@@ -524,7 +642,7 @@ See [Boilerplate](#boilerplate) for code sample
|
|||||||
|
|
||||||
`isConnected(): boolean`
|
`isConnected(): boolean`
|
||||||
|
|
||||||
Checks if the RippleAPI instance is connected to its server(s).
|
Checks if the RippleAPI instance is connected to its rippled server.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
@@ -573,10 +691,10 @@ pubkeyNode | string | Public key used to verify this node for internal communica
|
|||||||
serverState | string | A string indicating to what extent the server is participating in the network. See [Possible Server States](https://ripple.com/build/rippled-apis/#possible-server-states) for more details.
|
serverState | string | A string indicating to what extent the server is participating in the network. See [Possible Server States](https://ripple.com/build/rippled-apis/#possible-server-states) for more details.
|
||||||
validatedLedger | object | Information about the fully-validated ledger with the highest sequence number (the most recent).
|
validatedLedger | object | Information about the fully-validated ledger with the highest sequence number (the most recent).
|
||||||
*validatedLedger.* age | integer | The time since the ledger was closed, in seconds.
|
*validatedLedger.* age | integer | The time since the ledger was closed, in seconds.
|
||||||
*validatedLedger.* baseFeeXRP | number | Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005.
|
*validatedLedger.* baseFeeXRP | [value](#value) | Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005.
|
||||||
*validatedLedger.* hash | string | Unique hash for the ledger, as an uppercase hexadecimal string.
|
*validatedLedger.* hash | string | Unique hash for the ledger, as an uppercase hexadecimal string.
|
||||||
*validatedLedger.* reserveBaseXRP | integer | Minimum amount of XRP (not drops) necessary for every account to keep in reserve.
|
*validatedLedger.* reserveBaseXRP | [value](#value) | Minimum amount of XRP necessary for every account to keep in reserve.
|
||||||
*validatedLedger.* reserveIncrementXRP | integer | Amount of XRP (not drops) added to the account reserve for each object an account is responsible for in the ledger.
|
*validatedLedger.* reserveIncrementXRP | [value](#value) | Amount of XRP added to the account reserve for each object an account is responsible for in the ledger.
|
||||||
*validatedLedger.* ledgerVersion | integer | Identifying sequence number of this ledger version.
|
*validatedLedger.* ledgerVersion | integer | Identifying sequence number of this ledger version.
|
||||||
validationQuorum | number | Minimum number of trusted validations required in order to validate a ledger version. Some circumstances may cause the server to require more validations.
|
validationQuorum | number | Minimum number of trusted validations required in order to validate a ledger version. Some circumstances may cause the server to require more validations.
|
||||||
load | object | *Optional* *(Admin only)* Detailed information about the current load state of the server.
|
load | object | *Optional* *(Admin only)* Detailed information about the current load state of the server.
|
||||||
@@ -607,10 +725,10 @@ return api.getServerInfo().then(info => {/* ... */});
|
|||||||
"serverState": "full",
|
"serverState": "full",
|
||||||
"validatedLedger": {
|
"validatedLedger": {
|
||||||
"age": 5,
|
"age": 5,
|
||||||
"baseFeeXRP": 0.00001,
|
"baseFeeXRP": "0.00001",
|
||||||
"hash": "4482DEE5362332F54A4036ED57EE1767C9F33CF7CE5A6670355C16CECE381D46",
|
"hash": "4482DEE5362332F54A4036ED57EE1767C9F33CF7CE5A6670355C16CECE381D46",
|
||||||
"reserveBaseXRP": 20,
|
"reserveBaseXRP": "20",
|
||||||
"reserveIncrementXRP": 5,
|
"reserveIncrementXRP": "5",
|
||||||
"ledgerVersion": 6595042
|
"ledgerVersion": 6595042
|
||||||
},
|
},
|
||||||
"validationQuorum": 3
|
"validationQuorum": 3
|
||||||
@@ -622,7 +740,7 @@ return api.getServerInfo().then(info => {/* ... */});
|
|||||||
|
|
||||||
`getFee(): Promise<number>`
|
`getFee(): Promise<number>`
|
||||||
|
|
||||||
Returns the estimated transaction fee for the server(s) the RippleAPI instance is connected to.
|
Returns the estimated transaction fee for the rippled server the RippleAPI instance is connected to.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
@@ -630,7 +748,7 @@ This method has no parameters.
|
|||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
This method returns a promise that resolves with a floating point value representing the estimated fee to submit a transaction, expressed in XRP.
|
This method returns a promise that resolves with a string encoded floating point value representing the estimated fee to submit a transaction, expressed in XRP.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -639,7 +757,7 @@ return api.getFee().then(fee => {/* ... */});
|
|||||||
```
|
```
|
||||||
|
|
||||||
```json
|
```json
|
||||||
0.012
|
"0.012"
|
||||||
```
|
```
|
||||||
|
|
||||||
## getLedgerVersion
|
## getLedgerVersion
|
||||||
@@ -1070,7 +1188,7 @@ counterparty | object | Properties of the trustline from the perspective of the
|
|||||||
*counterparty.* frozen | boolean | *Optional* If true, the trustline is frozen, which means that funds can only be sent to the counterparty.
|
*counterparty.* frozen | boolean | *Optional* If true, the trustline is frozen, which means that funds can only be sent to the counterparty.
|
||||||
*counterparty.* ripplingDisabled | boolean | *Optional* If true, payments cannot ripple through this trustline.
|
*counterparty.* ripplingDisabled | boolean | *Optional* If true, payments cannot ripple through this trustline.
|
||||||
state | object | Properties of the trustline regarding it's current state that are not part of the specification.
|
state | object | Properties of the trustline regarding it's current state that are not part of the specification.
|
||||||
*state.* balance | [value](#value) | The balance on the trustline, representing which party owes the other and by how much.
|
*state.* balance | [signedValue](#value) | The balance on the trustline, representing which party owes the other and by how much.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -1476,12 +1594,12 @@ Name | Type | Description
|
|||||||
source | object | Properties of the source of the payment.
|
source | object | Properties of the source of the payment.
|
||||||
*source.* address | [address](#ripple-address) | The address to send from.
|
*source.* address | [address](#ripple-address) | The address to send from.
|
||||||
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with source.maxAmount)
|
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with source.maxAmount)
|
||||||
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.
|
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||||
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field is exclusive with source.amount)
|
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field is exclusive with source.amount)
|
||||||
destination | object | Properties of the destination of the payment.
|
destination | object | Properties of the destination of the payment.
|
||||||
*destination.* address | [address](#ripple-address) | The address to receive at.
|
*destination.* address | [address](#ripple-address) | The address to receive at.
|
||||||
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with destination.minAmount).
|
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field is exclusive with destination.minAmount).
|
||||||
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.
|
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||||
*destination.* address | [address](#ripple-address) | The address to send to.
|
*destination.* address | [address](#ripple-address) | The address to send to.
|
||||||
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field is exclusive with destination.amount)
|
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field is exclusive with destination.amount)
|
||||||
paths | string | The paths of trustlines and orders to use in executing the payment.
|
paths | string | The paths of trustlines and orders to use in executing the payment.
|
||||||
@@ -1489,18 +1607,18 @@ paths | string | The paths of trustlines and orders to use in executing the paym
|
|||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const pathfind = {
|
const pathfind = {
|
||||||
"source": {
|
"source": {
|
||||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||||
},
|
},
|
||||||
"destination": {
|
"destination": {
|
||||||
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||||
"amount": {
|
"amount": {
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
|
"counterparty": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM",
|
||||||
"value": "100"
|
"value": "100"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return api.getPaths(pathfind)
|
return api.getPaths(pathfind)
|
||||||
.then(paths => {/* ... */});
|
.then(paths => {/* ... */});
|
||||||
@@ -2527,12 +2645,19 @@ domain | string | *Optional* The domain that owns this account, as a hexadecima
|
|||||||
emailHash | string,null | *Optional* Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.
|
emailHash | string,null | *Optional* Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.
|
||||||
enableTransactionIDTracking | boolean | *Optional* Track the ID of this account’s most recent transaction.
|
enableTransactionIDTracking | boolean | *Optional* Track the ID of this account’s most recent transaction.
|
||||||
globalFreeze | boolean | *Optional* Freeze all assets issued by this account.
|
globalFreeze | boolean | *Optional* Freeze all assets issued by this account.
|
||||||
|
memos | [memos](#transaction-memos) | *Optional* Array of memos to attach to the transaction.
|
||||||
messageKey | string | *Optional* Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple.
|
messageKey | string | *Optional* Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple.
|
||||||
noFreeze | boolean | *Optional* Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled.
|
noFreeze | boolean | *Optional* Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled.
|
||||||
passwordSpent | boolean | *Optional* Indicates that the account has used its free SetRegularKey transaction.
|
passwordSpent | boolean | *Optional* Indicates that the account has used its free SetRegularKey transaction.
|
||||||
regularKey | [address](#ripple-address),null | *Optional* The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key.
|
regularKey | [address](#ripple-address),null | *Optional* The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key.
|
||||||
requireAuthorization | boolean | *Optional* If set, this account must individually approve other users in order for those users to hold this account’s issuances.
|
requireAuthorization | boolean | *Optional* If set, this account must individually approve other users in order for those users to hold this account’s issuances.
|
||||||
requireDestinationTag | boolean | *Optional* Requires incoming payments to specify a destination tag.
|
requireDestinationTag | boolean | *Optional* Requires incoming payments to specify a destination tag.
|
||||||
|
signers | object | *Optional* Settings that determine what sets of accounts can be used to sign a transaction on behalf of this account using multisigning.
|
||||||
|
*signers.* threshold | integer | *Optional* A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`.
|
||||||
|
*signers.* weights | array | *Optional* Weights of signatures for each signer.
|
||||||
|
*signers.* weights[] | object | An association of an address and a weight.
|
||||||
|
*signers.weights[].* address | [address](#ripple-address) | A Ripple account address
|
||||||
|
*signers.weights[].* weight | integer | The weight that the signature of this account counts as towards the threshold.
|
||||||
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, represented as billionths of a unit. Use `null` to set no fee.
|
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, represented as billionths of a unit. Use `null` to set no fee.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
@@ -2692,7 +2817,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -2761,7 +2886,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -2774,7 +2899,14 @@ const trustline = {
|
|||||||
"qualityIn": 0.91,
|
"qualityIn": 0.91,
|
||||||
"qualityOut": 0.87,
|
"qualityOut": 0.87,
|
||||||
"ripplingDisabled": true,
|
"ripplingDisabled": true,
|
||||||
"frozen": false
|
"frozen": false,
|
||||||
|
"memos": [
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"format": "plain/text",
|
||||||
|
"data": "texted data"
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
return api.preparePayment(address, trustline).then(prepared =>
|
return api.preparePayment(address, trustline).then(prepared =>
|
||||||
{/* ... */});
|
{/* ... */});
|
||||||
@@ -2783,7 +2915,7 @@ return api.preparePayment(address, trustline).then(prepared =>
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"txJSON": "{\"Flags\":2149711872,\"TransactionType\":\"TrustSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"LimitAmount\":{\"value\":\"10000\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"QualityIn\":910000000,\"QualityOut\":870000000,\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
"txJSON": "{\"TransactionType\":\"TrustSet\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"LimitAmount\":{\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\",\"value\":\"10000\"},\"Flags\":2149711872,\"QualityIn\":910000000,\"QualityOut\":870000000,\"Memos\":[{\"Memo\":{\"MemoData\":\"7465787465642064617461\",\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"fee": "0.000012",
|
"fee": "0.000012",
|
||||||
"sequence": 23,
|
"sequence": 23,
|
||||||
@@ -2812,7 +2944,7 @@ instructions | [instructions](#transaction-instructions) | *Optional* Instructio
|
|||||||
This method returns a promise that resolves with an object with the following structure:
|
This method returns a promise that resolves with an object with the following structure:
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
All "prepare" methods have the same return type.
|
All "prepare*" methods have the same return type.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
@@ -2821,7 +2953,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -2838,7 +2970,8 @@ const order = {
|
|||||||
"currency": "XRP",
|
"currency": "XRP",
|
||||||
"value": "2"
|
"value": "2"
|
||||||
},
|
},
|
||||||
"immediateOrCancel": true
|
"passive": true,
|
||||||
|
"fillOrKill": true
|
||||||
};
|
};
|
||||||
return api.prepareOrder(address, order)
|
return api.prepareOrder(address, order)
|
||||||
.then(prepared => {/* ... */});
|
.then(prepared => {/* ... */});
|
||||||
@@ -2847,11 +2980,11 @@ return api.prepareOrder(address, order)
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"txJSON": "{\"Flags\":2147614720,\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":\"2000000\",\"TakerPays\":{\"value\":\"10.1\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
"txJSON": "{\"Flags\":2147811328,\"TransactionType\":\"OfferCreate\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"TakerGets\":\"2000000\",\"TakerPays\":{\"value\":\"10.1\",\"currency\":\"USD\",\"issuer\":\"rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM\"},\"LastLedgerSequence\":8819954,\"Fee\":\"12\",\"Sequence\":23}",
|
||||||
"instructions": {
|
"instructions": {
|
||||||
"fee": "0.000012",
|
"fee": "0.000012",
|
||||||
"sequence": 23,
|
"sequence": 23,
|
||||||
"maxLedgerVersion": 8820051
|
"maxLedgerVersion": 8819954
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -2859,7 +2992,7 @@ return api.prepareOrder(address, order)
|
|||||||
|
|
||||||
## prepareOrderCancellation
|
## prepareOrderCancellation
|
||||||
|
|
||||||
`prepareOrderCancellation(address: string, sequence: number, instructions: Object): Promise<Object>`
|
`prepareOrderCancellation(address: string, orderCancellation: Object, instructions: Object): Promise<Object>`
|
||||||
|
|
||||||
Prepare an order cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare an order cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
@@ -2868,7 +3001,7 @@ Prepare an order cancellation transaction. The prepared transaction must subsequ
|
|||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
---- | ---- | -----------
|
---- | ---- | -----------
|
||||||
address | [address](#ripple-address) | The address of the account that is creating the transaction.
|
address | [address](#ripple-address) | The address of the account that is creating the transaction.
|
||||||
sequence | [sequence](#account-sequence-number) | The account sequence number of the transaction that created the order to cancel.
|
orderCancellation | [orderCancellation](#order-cancellation) | The specification of the order cancellation to prepare.
|
||||||
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
|
instructions | [instructions](#transaction-instructions) | *Optional* Instructions for executing the transaction
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
@@ -2876,7 +3009,7 @@ instructions | [instructions](#transaction-instructions) | *Optional* Instructio
|
|||||||
This method returns a promise that resolves with an object with the following structure:
|
This method returns a promise that resolves with an object with the following structure:
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
All "prepare" methods have the same return type.
|
All "prepare*" methods have the same return type.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
@@ -2885,13 +3018,13 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||||
const sequence = 123;
|
const orderCancellation = {orderSequence: 123};
|
||||||
return api.prepareOrderCancellation(address, sequence)
|
return api.prepareOrderCancellation(address, sequence)
|
||||||
.then(prepared => {/* ... */});
|
.then(prepared => {/* ... */});
|
||||||
```
|
```
|
||||||
@@ -2937,14 +3070,21 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||||
const settings = {
|
const settings = {
|
||||||
"domain": "ripple.com"
|
"domain": "ripple.com",
|
||||||
|
"memos": [
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"format": "plain/text",
|
||||||
|
"data": "texted data"
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
return api.prepareSettings(address, settings)
|
return api.prepareSettings(address, settings)
|
||||||
.then(prepared => {/* ... */});
|
.then(prepared => {/* ... */});
|
||||||
@@ -2953,7 +3093,14 @@ return api.prepareSettings(address, settings)
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"domain": "ripple.com"
|
"domain": "ripple.com",
|
||||||
|
"memos": [
|
||||||
|
{
|
||||||
|
"type": "test",
|
||||||
|
"format": "plain/text",
|
||||||
|
"data": "texted data"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -2964,6 +3111,8 @@ return api.prepareSettings(address, settings)
|
|||||||
|
|
||||||
Prepare a suspended payment creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare a suspended payment creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
|
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
@@ -2986,7 +3135,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -3034,6 +3183,8 @@ return api.prepareSuspendedPaymentCreation(address, suspendedPaymentCreation).th
|
|||||||
|
|
||||||
Prepare a suspended payment cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare a suspended payment cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
|
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
@@ -3056,7 +3207,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -3089,6 +3240,8 @@ return api.prepareSuspendedPaymentCancellation(address, suspendedPaymentCancella
|
|||||||
|
|
||||||
Prepare a suspended payment execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare a suspended payment execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
|
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
@@ -3111,7 +3264,7 @@ txJSON | string | The prepared transaction in rippled JSON format.
|
|||||||
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
instructions | object | The instructions for how to execute the transaction after adding automatic defaults.
|
||||||
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
*instructions.* fee | [value](#value) | An exact fee to pay for the transaction. See [Transaction Fees](#transaction-fees) for more information.
|
||||||
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
*instructions.* sequence | [sequence](#account-sequence-number) | The initiating account's sequence number for this transaction.
|
||||||
*instructions.* maxLedgerVersion | integer | *Optional* The highest ledger version that the transaction can be included in.
|
*instructions.* maxLedgerVersion | integer,null | The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -3143,7 +3296,7 @@ return api.prepareSuspendedPaymentExecution(address, suspendedPaymentExecution).
|
|||||||
|
|
||||||
## sign
|
## sign
|
||||||
|
|
||||||
`sign(txJSON: string, secret: string): {signedTransaction: string, id: string}`
|
`sign(txJSON: string, secret: string, options: Object): {signedTransaction: string, id: string}`
|
||||||
|
|
||||||
Sign a prepared transaction. The signed transaction must subsequently be [submitted](#submit).
|
Sign a prepared transaction. The signed transaction must subsequently be [submitted](#submit).
|
||||||
|
|
||||||
@@ -3153,6 +3306,8 @@ Name | Type | Description
|
|||||||
---- | ---- | -----------
|
---- | ---- | -----------
|
||||||
txJSON | string | Transaction represented as a JSON string in rippled format.
|
txJSON | string | Transaction represented as a JSON string in rippled format.
|
||||||
secret | secret string | The secret of the account that is initiating the transaction.
|
secret | secret string | The secret of the account that is initiating the transaction.
|
||||||
|
options | object | *Optional* Options that control the type of signature that will be generated.
|
||||||
|
*options.* signAs | [address](#ripple-address) | *Optional* The account that the signature should count for in multisigning.
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
@@ -3180,6 +3335,44 @@ return api.sign(txJSON, secret);
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## combine
|
||||||
|
|
||||||
|
`combine(signedTransactions: Array<string>): {signedTransaction: string, id: string}`
|
||||||
|
|
||||||
|
Combines signed transactions from multiple accounts for a multisignature transaction. The signed transaction must subsequently be [submitted](#submit).
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
Name | Type | Description
|
||||||
|
---- | ---- | -----------
|
||||||
|
signedTransactions | array\<string\> | An array of signed transactions (from the output of [sign](#sign)) to combine.
|
||||||
|
|
||||||
|
### Return Value
|
||||||
|
|
||||||
|
This method returns an object with the following structure:
|
||||||
|
|
||||||
|
Name | Type | Description
|
||||||
|
---- | ---- | -----------
|
||||||
|
signedTransaction | string | The signed transaction represented as an uppercase hexadecimal string.
|
||||||
|
id | [id](#transaction-id) | The [Transaction ID](#transaction-id) of the signed transaction.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const signedTransactions = [ "12000322800000002400000004201B000000116840000000000F42407300770B6578616D706C652E636F6D811407C532442A675C881BA1235354D4AB9D023243A6F3E0107321026C784C1987F83BACBF02CD3E484AFC84ADE5CA6B36ED4DCA06D5BA233B9D382774473045022100E484F54FF909469FA2033E22EFF3DF8EDFE62217062680BB2F3EDF2F185074FE0220350DB29001C710F0450DAF466C5D819DC6D6A3340602DE9B6CB7DA8E17C90F798114FE9337B0574213FA5BCC0A319DBB4A7AC0CCA894E1F1",
|
||||||
|
"12000322800000002400000004201B000000116840000000000F42407300770B6578616D706C652E636F6D811407C532442A675C881BA1235354D4AB9D023243A6F3E01073210287AAAB8FBE8C4C4A47F6F1228C6E5123A7ED844BFE88A9B22C2F7CC34279EEAA74473045022100B09DDF23144595B5A9523B20E605E138DC6549F5CA7B5984D7C32B0E3469DF6B022018845CA6C203D4B6288C87DDA439134C83E7ADF8358BD41A8A9141A9B631419F8114517D9B9609229E0CDFE2428B586738C5B2E84D45E1F1" ];
|
||||||
|
return api.combine(signedTransactions);
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"signedTransaction": "12000322800000002400000004201B000000116840000000000F42407300770B6578616D706C652E636F6D811407C532442A675C881BA1235354D4AB9D023243A6F3E01073210287AAAB8FBE8C4C4A47F6F1228C6E5123A7ED844BFE88A9B22C2F7CC34279EEAA74473045022100B09DDF23144595B5A9523B20E605E138DC6549F5CA7B5984D7C32B0E3469DF6B022018845CA6C203D4B6288C87DDA439134C83E7ADF8358BD41A8A9141A9B631419F8114517D9B9609229E0CDFE2428B586738C5B2E84D45E1E0107321026C784C1987F83BACBF02CD3E484AFC84ADE5CA6B36ED4DCA06D5BA233B9D382774473045022100E484F54FF909469FA2033E22EFF3DF8EDFE62217062680BB2F3EDF2F185074FE0220350DB29001C710F0450DAF466C5D819DC6D6A3340602DE9B6CB7DA8E17C90F798114FE9337B0574213FA5BCC0A319DBB4A7AC0CCA894E1F1",
|
||||||
|
"id": "8A3BFD2214B4C8271ED62648FCE9ADE4EE82EF01827CF7D1F7ED497549A368CC"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## submit
|
## submit
|
||||||
|
|
||||||
`submit(signedTransaction: string): Promise<Object>`
|
`submit(signedTransaction: string): Promise<Object>`
|
||||||
@@ -3226,7 +3419,11 @@ Generate a new Ripple address and corresponding secret.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
This method has no parameters.
|
Name | Type | Description
|
||||||
|
---- | ---- | -----------
|
||||||
|
options | object | *Optional* Options to control how the address and secret are generated.
|
||||||
|
*options.* algorithm | string | *Optional* The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
|
||||||
|
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed.
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
@@ -3240,8 +3437,7 @@ secret | secret string | The secret corresponding to the `address`.
|
|||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
return api.generateAddress()
|
return api.generateAddress();
|
||||||
.then(result => {/* ... */});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -3311,7 +3507,7 @@ return api.computeLedgerHash(ledger);
|
|||||||
|
|
||||||
# API Events
|
# API Events
|
||||||
|
|
||||||
## ledgerClosed
|
## ledger
|
||||||
|
|
||||||
This event is emitted whenever a new ledger version is validated on the connected server.
|
This event is emitted whenever a new ledger version is validated on the connected server.
|
||||||
|
|
||||||
@@ -3319,12 +3515,11 @@ This event is emitted whenever a new ledger version is validated on the connecte
|
|||||||
|
|
||||||
Name | Type | Description
|
Name | Type | Description
|
||||||
---- | ---- | -----------
|
---- | ---- | -----------
|
||||||
feeBase | integer | Base fee, in drops.
|
baseFeeXRP | [value](#value) | Base fee, in XRP.
|
||||||
feeReference | integer | Cost of the 'reference transaction' in 'fee units'.
|
|
||||||
ledgerHash | string | Unique hash of the ledger that was closed, as hex.
|
ledgerHash | string | Unique hash of the ledger that was closed, as hex.
|
||||||
ledgerTimestamp | date-time string | The time at which this ledger closed.
|
ledgerTimestamp | date-time string | The time at which this ledger closed.
|
||||||
reserveBase | integer | The minimum reserve, in drops of XRP, that is required for an account.
|
reserveBaseXRP | [value](#value) | The minimum reserve, in XRP, that is required for an account.
|
||||||
reserveIncrement | integer | The increase in account reserve that is added for each item the account owns, such as offers or trust lines.
|
reserveIncrementXRP | [value](#value) | The increase in account reserve that is added for each item the account owns, such as offers or trust lines.
|
||||||
transactionCount | integer | Number of new transactions included in this ledger.
|
transactionCount | integer | Number of new transactions included in this ledger.
|
||||||
ledgerVersion | integer | Ledger version of the ledger that closed.
|
ledgerVersion | integer | Ledger version of the ledger that closed.
|
||||||
validatedLedgerVersions | string | Range of ledgers that the server has available. This may be discontiguous.
|
validatedLedgerVersions | string | Range of ledgers that the server has available. This may be discontiguous.
|
||||||
@@ -3332,7 +3527,7 @@ validatedLedgerVersions | string | Range of ledgers that the server has availabl
|
|||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
api.on('ledgerClosed', ledger => {
|
api.on('ledger', ledger => {
|
||||||
console.log(JSON.stringify(ledger, null, 2));
|
console.log(JSON.stringify(ledger, null, 2));
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -3340,13 +3535,12 @@ api.on('ledgerClosed', ledger => {
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"feeBase": 10,
|
"baseFeeXRP": "0.00001",
|
||||||
"feeReference": 10,
|
|
||||||
"ledgerVersion": 14804627,
|
"ledgerVersion": 14804627,
|
||||||
"ledgerHash": "9141FA171F2C0CE63E609466AF728FF66C12F7ACD4B4B50B0947A7F3409D593A",
|
"ledgerHash": "9141FA171F2C0CE63E609466AF728FF66C12F7ACD4B4B50B0947A7F3409D593A",
|
||||||
"ledgerTimestamp": "2015-07-23T05:50:40.000Z",
|
"ledgerTimestamp": "2015-07-23T05:50:40.000Z",
|
||||||
"reserveBase": 20000000,
|
"reserveBaseXRP": "20",
|
||||||
"reserveIncrement": 5000000,
|
"reserveIncrementXRP": "5",
|
||||||
"transactionCount": 19,
|
"transactionCount": 19,
|
||||||
"validatedLedgerVersions": "13983423-14804627"
|
"validatedLedgerVersions": "13983423-14804627"
|
||||||
}
|
}
|
||||||
@@ -3355,16 +3549,26 @@ api.on('ledgerClosed', ledger => {
|
|||||||
|
|
||||||
## error
|
## error
|
||||||
|
|
||||||
This event is emitted when there is an error on the connection to the server.
|
This event is emitted when there is an error on the connection to the server that cannot be associated to a specific request.
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
The first parameter is a string indicating the error type, which may be `badMessage` (meaning that rippled returned a malformed message), or one of the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors). The second parameter is a message explaining the error, or the message that caused the error in the case of `badMessage`.
|
The first parameter is a string indicating the error type:
|
||||||
|
* `badMessage` - rippled returned a malformed message
|
||||||
|
* `websocket` - the websocket library emitted an error
|
||||||
|
* one of the error codes found in the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors).
|
||||||
|
|
||||||
|
The second parameter is a message explaining the error.
|
||||||
|
|
||||||
|
The third parameter is:
|
||||||
|
* the message that caused the error for `badMessage`
|
||||||
|
* the error object emitted for `websocket`
|
||||||
|
* the parsed response for rippled errors
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
api.on('error', (errorCode, errorMessage) => {
|
api.on('error', (errorCode, errorMessage, data) => {
|
||||||
console.log(errorCode + ': ' + errorMessage);
|
console.log(errorCode + ': ' + errorMessage);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
|
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
|
||||||
|
|
||||||
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
|
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
|
||||||
const address = 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV';
|
const address = 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV';
|
||||||
|
|
||||||
api.connect().then(() => {
|
api.connect().then(() => {
|
||||||
|
|||||||
38
docs/samples/cancelall.js
Normal file
38
docs/samples/cancelall.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
'use strict';
|
||||||
|
const RippleAPI = require('../../dist/npm').RippleAPI; // require('ripple-lib')
|
||||||
|
|
||||||
|
const address = 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K';
|
||||||
|
const secret = '';
|
||||||
|
|
||||||
|
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
|
||||||
|
const instructions = {maxLedgerVersionOffset: 5};
|
||||||
|
|
||||||
|
function fail(message) {
|
||||||
|
console.error(message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelOrder(orderSequence) {
|
||||||
|
console.log('Cancelling order: ' + orderSequence.toString());
|
||||||
|
return api.prepareOrderCancellation(address, {orderSequence}, instructions)
|
||||||
|
.then(prepared => {
|
||||||
|
const signing = api.sign(prepared.txJSON, secret);
|
||||||
|
return api.submit(signing.signedTransaction);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelAllOrders(orderSequences) {
|
||||||
|
if (orderSequences.length === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
const orderSequence = orderSequences.pop();
|
||||||
|
return cancelOrder(orderSequence).then(() => cancelAllOrders(orderSequences));
|
||||||
|
}
|
||||||
|
|
||||||
|
api.connect().then(() => {
|
||||||
|
console.log('Connected...');
|
||||||
|
return api.getOrders(address).then(orders => {
|
||||||
|
const orderSequences = orders.map(order => order.properties.sequence);
|
||||||
|
return cancelAllOrders(orderSequences);
|
||||||
|
}).then(() => process.exit(0));
|
||||||
|
}).catch(fail);
|
||||||
@@ -4,13 +4,13 @@ const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
|
|||||||
const address = 'INSERT ADDRESS HERE';
|
const address = 'INSERT ADDRESS HERE';
|
||||||
const secret = 'INSERT SECRET HERE';
|
const secret = 'INSERT SECRET HERE';
|
||||||
|
|
||||||
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
|
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
|
||||||
const instructions = {maxLedgerVersionOffset: 5};
|
const instructions = {maxLedgerVersionOffset: 5};
|
||||||
|
|
||||||
const payment = {
|
const payment = {
|
||||||
source: {
|
source: {
|
||||||
address: address,
|
address: address,
|
||||||
amount: {
|
maxAmount: {
|
||||||
value: '0.01',
|
value: '0.01',
|
||||||
currency: 'XRP'
|
currency: 'XRP'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,23 +6,28 @@
|
|||||||
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||||
```
|
```
|
||||||
|
|
||||||
Every Ripple account has an *address*, which is a base58-encoding of a hash of the account's public key.
|
Every Ripple account has an *address*, which is a base58-encoding of a hash of the account's public key. Ripple addresses always start with the lowercase letter `r`.
|
||||||
|
|
||||||
## Account Sequence Number
|
## Account Sequence Number
|
||||||
|
|
||||||
Every Ripple account has a *sequence number* that is used to order transactions. Every transaction must have a sequence number and transaction can only be executed in order by sequence number. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
|
Every Ripple account has a *sequence number* that is used to keep transactions in order. Every transaction must have a sequence number. A transaction can only be executed if it has the next sequence number in order, of the account sending it. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
|
||||||
|
|
||||||
## Currency
|
## Currency
|
||||||
|
|
||||||
Currencies are represented as either 3-character currency codes or 40-character uppercase hexadecimal strings. We recommend using uppercase [ISO 4217 Currency Codes](http://www.xe.com/iso4217.php) only. The string "XRP" is disallowed on trustlines because it is reserved for the Ripple native currency. The following characters are permitted: all uppercase and lowercase letters, digits, as well as the symbols `?`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `<`, `>`, `(`, `)`, `{`, `}`, `[`, `]`, and `|`.
|
Currencies are represented as either 3-character currency codes or 40-character uppercase hexadecimal strings. We recommend using uppercase [ISO 4217 Currency Codes](http://www.xe.com/iso4217.php) only. The string "XRP" is disallowed on trustlines because it is reserved for the Ripple native currency. The following characters are permitted: all uppercase and lowercase letters, digits, as well as the symbols `?`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `<`, `>`, `(`, `)`, `{`, `}`, `[`, `]`, and `|`.
|
||||||
|
|
||||||
## Value
|
## Value
|
||||||
A *value* is a quantity of a currency represented as a decimal string (string encoding is used because javascript numbers do not have sufficient precision).
|
A *value* is a quantity of a currency represented as a decimal string. Be careful: JavaScript's native number format does not have sufficient precision to represent all values. XRP has different precision from other currencies.
|
||||||
|
|
||||||
|
**XRP** has 6 significant digits past the decimal point. In other words, XRP cannot be divided into positive values smaller than `0.000001` (1e-6). XRP has a maximum value of `100000000000` (1e11).
|
||||||
|
|
||||||
|
**Non-XRP values** have 15 decimal digits of precision, with a maximum value of `9999999999999999e80`. The smallest positive non-XRP value is `1e-81`.
|
||||||
|
|
||||||
An XRP value has 6 significant digits past the decimal point. A non-XRP value has 16 total digits of precision.
|
|
||||||
|
|
||||||
## Amount
|
## Amount
|
||||||
|
|
||||||
|
Example amount:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"currency": "USD",
|
"currency": "USD",
|
||||||
@@ -31,6 +36,7 @@ An XRP value has 6 significant digits past the decimal point. A non-XRP value ha
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Example XRP amount:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"currency": "XRP",
|
"currency": "XRP",
|
||||||
@@ -38,7 +44,7 @@ An XRP value has 6 significant digits past the decimal point. A non-XRP value ha
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value (for all currencies besides "XRP").
|
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value. For XRP, there is no counterparty.
|
||||||
|
|
||||||
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,15 @@
|
|||||||
## Boilerplate
|
## Boilerplate
|
||||||
|
|
||||||
|
Use the following [boilerplate code](https://en.wikipedia.org/wiki/Boilerplate_code) to wrap your custom code using RippleAPI.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const {RippleAPI} = require('ripple-lib');
|
const {RippleAPI} = require('ripple-lib');
|
||||||
|
|
||||||
const api = new RippleAPI({
|
const api = new RippleAPI({
|
||||||
servers: ['wss://s1.ripple.com']
|
server: 'wss://s1.ripple.com' // Public rippled server hosted by Ripple, Inc.
|
||||||
|
});
|
||||||
|
api.on('error', (errorCode, errorMessage) => {
|
||||||
|
console.log(errorCode + ': ' + errorMessage);
|
||||||
});
|
});
|
||||||
api.connect().then(() => {
|
api.connect().then(() => {
|
||||||
/* insert code here */
|
/* insert code here */
|
||||||
@@ -13,22 +18,43 @@ api.connect().then(() => {
|
|||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
```
|
```
|
||||||
|
|
||||||
To get started, first install [nodejs](https://nodejs.org) version `0.12.0` or greater, then:
|
RippleAPI is designed to work in [NodeJS](https://nodejs.org) (version `0.12.0` or greater) using [Babel](https://babeljs.io/) for [ECMAScript 6](https://babeljs.io/docs/learn-es2015/) support.
|
||||||
|
|
||||||
`npm install -g babel`
|
The code samples in this documentation are written in ES6, but `RippleAPI` will work with ES5 also. Regardless of whether you use ES5 or ES6, the methods that return promises will return [ES6-style promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||||
|
|
||||||
`npm install ripple-lib`
|
|
||||||
|
|
||||||
Then create a script based on the boilerplate shown here and run with:
|
|
||||||
|
|
||||||
`babel-node script.js`
|
|
||||||
|
|
||||||
The code samples in this documentation are written in ES6, but `RippleAPI` will work with ES5 also. Regardless of whether you use ES5 or ES6, the methods that return promises will return ES6-style promises.
|
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
All the code snippets in this documentation assume that you have surrounded them with this boilerplate.
|
All the code snippets in this documentation assume that you have surrounded them with this boilerplate.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
Dont forget the "catch" or errors may not be visible.
|
If you omit the "catch" section, errors may not be visible.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
The "error" event is emitted whenever an error occurs that cannot be associated with a specific request. If the listener is not registered, an exception will be thrown whenever the event is emitted.
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
The RippleAPI constructor optionally takes one argument, an object with the following options:
|
||||||
|
|
||||||
|
<%- renderSchema('input/api-options.json') %>
|
||||||
|
|
||||||
|
If you omit the `server` parameter, RippleAPI operates [offline](#offline-functionality).
|
||||||
|
|
||||||
|
|
||||||
|
### Installation ###
|
||||||
|
|
||||||
|
1. Install [NodeJS](https://nodejs.org) and the Node Package Manager (npm). Most Linux distros have a package for NodeJS, but make sure you have version `0.12.0` or higher.
|
||||||
|
2. Use npm to install [Babel](https://babeljs.io/) globally:
|
||||||
|
`npm install -g babel-cli`
|
||||||
|
3. Use npm to install RippleAPI:
|
||||||
|
`npm install ripple-lib`
|
||||||
|
|
||||||
|
After you have installed ripple-lib, you can create scripts using the [boilerplate](#boilerplate) and run them using babel-node:
|
||||||
|
`babel-node script.js`
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
Instead of using babel-node in production, we recommend using Babel to transpile to ECMAScript 5 first.
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
|||||||
24
docs/src/combine.md.ejs
Normal file
24
docs/src/combine.md.ejs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
## combine
|
||||||
|
|
||||||
|
`combine(signedTransactions: Array<string>): {signedTransaction: string, id: string}`
|
||||||
|
|
||||||
|
Combines signed transactions from multiple accounts for a multisignature transaction. The signed transaction must subsequently be [submitted](#submit).
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
<%- renderSchema("input/combine.json") %>
|
||||||
|
|
||||||
|
### Return Value
|
||||||
|
|
||||||
|
This method returns an object with the following structure:
|
||||||
|
|
||||||
|
<%- renderSchema("output/sign.json") %>
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const signedTransactions = <%- importFile('test/fixtures/requests/combine.json') %>;
|
||||||
|
return api.combine(signedTransactions);
|
||||||
|
```
|
||||||
|
|
||||||
|
<%- renderFixture("responses/combine.json") %>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
`connect(): Promise<void>`
|
`connect(): Promise<void>`
|
||||||
|
|
||||||
Tells the RippleAPI instance to connect to its server(s).
|
Tells the RippleAPI instance to connect to its rippled server.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
`disconnect(): Promise<void>`
|
`disconnect(): Promise<void>`
|
||||||
|
|
||||||
Tells the RippleAPI instance to disconnect from its server(s).
|
Tells the RippleAPI instance to disconnect from its rippled server.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,45 @@
|
|||||||
# API Events
|
# API Events
|
||||||
|
|
||||||
## ledgerClosed
|
## ledger
|
||||||
|
|
||||||
This event is emitted whenever a new ledger version is validated on the connected server.
|
This event is emitted whenever a new ledger version is validated on the connected server.
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
<%- renderSchema('output/ledger-closed.json') %>
|
<%- renderSchema('output/ledger-event.json') %>
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
api.on('ledgerClosed', ledger => {
|
api.on('ledger', ledger => {
|
||||||
console.log(JSON.stringify(ledger, null, 2));
|
console.log(JSON.stringify(ledger, null, 2));
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
<%- renderFixture('responses/ledger-closed.json') %>
|
<%- renderFixture('responses/ledger-event.json') %>
|
||||||
|
|
||||||
## error
|
## error
|
||||||
|
|
||||||
This event is emitted when there is an error on the connection to the server.
|
This event is emitted when there is an error on the connection to the server that cannot be associated to a specific request.
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
The first parameter is a string indicating the error type, which may be `badMessage` (meaning that rippled returned a malformed message), or one of the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors). The second parameter is a message explaining the error, or the message that caused the error in the case of `badMessage`.
|
The first parameter is a string indicating the error type:
|
||||||
|
* `badMessage` - rippled returned a malformed message
|
||||||
|
* `websocket` - the websocket library emitted an error
|
||||||
|
* one of the error codes found in the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors).
|
||||||
|
|
||||||
|
The second parameter is a message explaining the error.
|
||||||
|
|
||||||
|
The third parameter is:
|
||||||
|
* the message that caused the error for `badMessage`
|
||||||
|
* the error object emitted for `websocket`
|
||||||
|
* the parsed response for rippled errors
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
api.on('error', (errorCode, errorMessage) => {
|
api.on('error', (errorCode, errorMessage, data) => {
|
||||||
console.log(errorCode + ': ' + errorMessage);
|
console.log(errorCode + ': ' + errorMessage);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ Generate a new Ripple address and corresponding secret.
|
|||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
This method has no parameters.
|
<%- renderSchema('input/generate-address.json') %>
|
||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
@@ -17,8 +17,7 @@ This method returns an object with the following structure:
|
|||||||
### Example
|
### Example
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
return api.generateAddress()
|
return api.generateAddress();
|
||||||
.then(result => {/* ... */});
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<%- renderFixture('responses/generate-address.json') %>
|
<%- renderFixture('responses/generate-address.json') %>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
`getFee(): Promise<number>`
|
`getFee(): Promise<number>`
|
||||||
|
|
||||||
Returns the estimated transaction fee for the server(s) the RippleAPI instance is connected to.
|
Returns the estimated transaction fee for the rippled server the RippleAPI instance is connected to.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ This method has no parameters.
|
|||||||
|
|
||||||
### Return Value
|
### Return Value
|
||||||
|
|
||||||
This method returns a promise that resolves with a floating point value representing the estimated fee to submit a transaction, expressed in XRP.
|
This method returns a promise that resolves with a string encoded floating point value representing the estimated fee to submit a transaction, expressed in XRP.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
@@ -19,5 +19,5 @@ return api.getFee().then(fee => {/* ... */});
|
|||||||
```
|
```
|
||||||
|
|
||||||
```json
|
```json
|
||||||
0.012
|
"0.012"
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<% include introduction.md.ejs %>
|
<% include introduction.md.ejs %>
|
||||||
<% include boilerplate.md.ejs %>
|
<% include boilerplate.md.ejs %>
|
||||||
|
<% include offline.md.ejs %>
|
||||||
<% include basictypes.md.ejs %>
|
<% include basictypes.md.ejs %>
|
||||||
<% include transactions.md.ejs %>
|
<% include transactions.md.ejs %>
|
||||||
<% include specifications.md.ejs %>
|
<% include specifications.md.ejs %>
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
<% include prepareSuspendedPaymentCancellation.md.ejs %>
|
<% include prepareSuspendedPaymentCancellation.md.ejs %>
|
||||||
<% include prepareSuspendedPaymentExecution.md.ejs %>
|
<% include prepareSuspendedPaymentExecution.md.ejs %>
|
||||||
<% include sign.md.ejs %>
|
<% include sign.md.ejs %>
|
||||||
|
<% include combine.md.ejs %>
|
||||||
<% include submit.md.ejs %>
|
<% include submit.md.ejs %>
|
||||||
<% include generateAddress.md.ejs %>
|
<% include generateAddress.md.ejs %>
|
||||||
<% include computeLedgerHash.md.ejs %>
|
<% include computeLedgerHash.md.ejs %>
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
RippleAPI allows you to query and submit transactions to a node on the Ripple network.
|
RippleAPI is the official client library to the Ripple Consensus Ledger. Currently, RippleAPI is only available in JavaScript.
|
||||||
|
Using RippleAPI, you can:
|
||||||
|
|
||||||
|
* [Query transactions from the network](#gettransaction)
|
||||||
|
* [Sign](#sign) transactions securely without connecting to any server
|
||||||
|
* [Submit](#submit) transactions to the Ripple Consensus Ledger, including [Payments](#payment), [Orders](#order), [Settings changes](#settings), and [other types](#transaction-types)
|
||||||
|
* [Generate a new Ripple Address](#generateaddress)
|
||||||
|
* ... and [much more](#api-methods).
|
||||||
|
|
||||||
RippleAPI only provides access to *validated*, *immutable* transaction data.
|
RippleAPI only provides access to *validated*, *immutable* transaction data.
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
`isConnected(): boolean`
|
`isConnected(): boolean`
|
||||||
|
|
||||||
Checks if the RippleAPI instance is connected to its server(s).
|
Checks if the RippleAPI instance is connected to its rippled server.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
|
|||||||
27
docs/src/offline.md.ejs
Normal file
27
docs/src/offline.md.ejs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
## Offline functionality
|
||||||
|
|
||||||
|
RippleAPI can also function without internet connectivity. This can be useful in order to generate secrets and sign transactions from a secure, isolated machine.
|
||||||
|
|
||||||
|
To instantiate RippleAPI in offline mode, use the following boilerplate code:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const {RippleAPI} = require('ripple-lib');
|
||||||
|
|
||||||
|
const api = new RippleAPI();
|
||||||
|
/* insert code here */
|
||||||
|
```
|
||||||
|
|
||||||
|
Methods that depend on the state of the Ripple Consensus Ledger are unavailable in offline mode. To prepare transactions offline, you **must** specify the `fee`, `sequence`, and `maxLedgerVersion` parameters in the [transaction instructions](#transaction-instructions). The following methods should work offline:
|
||||||
|
|
||||||
|
* [preparePayment](#preparepayment)
|
||||||
|
* [prepareTrustline](#preparetrustline)
|
||||||
|
* [prepareOrder](#prepareorder)
|
||||||
|
* [prepareOrderCancellation](#prepareordercancellation)
|
||||||
|
* [prepareSettings](#preparesettings)
|
||||||
|
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
|
||||||
|
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
|
||||||
|
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
|
||||||
|
* [sign](#sign)
|
||||||
|
* [generateAddress](#generateaddress)
|
||||||
|
* [computeLedgerHash](#computeledgerhash)
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ Prepare an order transaction. The prepared transaction must subsequently be [sig
|
|||||||
This method returns a promise that resolves with an object with the following structure:
|
This method returns a promise that resolves with an object with the following structure:
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
All "prepare" methods have the same return type.
|
All "prepare*" methods have the same return type.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<%- renderSchema('output/prepare.json') %>
|
<%- renderSchema('output/prepare.json') %>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## prepareOrderCancellation
|
## prepareOrderCancellation
|
||||||
|
|
||||||
`prepareOrderCancellation(address: string, sequence: number, instructions: Object): Promise<Object>`
|
`prepareOrderCancellation(address: string, orderCancellation: Object, instructions: Object): Promise<Object>`
|
||||||
|
|
||||||
Prepare an order cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare an order cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ Prepare an order cancellation transaction. The prepared transaction must subsequ
|
|||||||
This method returns a promise that resolves with an object with the following structure:
|
This method returns a promise that resolves with an object with the following structure:
|
||||||
|
|
||||||
<aside class="notice">
|
<aside class="notice">
|
||||||
All "prepare" methods have the same return type.
|
All "prepare*" methods have the same return type.
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<%- renderSchema("output/prepare.json") %>
|
<%- renderSchema("output/prepare.json") %>
|
||||||
@@ -22,7 +22,7 @@ All "prepare" methods have the same return type.
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||||
const sequence = 123;
|
const orderCancellation = {orderSequence: 123};
|
||||||
return api.prepareOrderCancellation(address, sequence)
|
return api.prepareOrderCancellation(address, sequence)
|
||||||
.then(prepared => {/* ... */});
|
.then(prepared => {/* ... */});
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Prepare a suspended payment cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare a suspended payment cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
|
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
<%- renderSchema('input/prepare-suspended-payment-cancellation.json') %>
|
<%- renderSchema('input/prepare-suspended-payment-cancellation.json') %>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Prepare a suspended payment creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare a suspended payment creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
|
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
<%- renderSchema('input/prepare-suspended-payment-creation.json') %>
|
<%- renderSchema('input/prepare-suspended-payment-creation.json') %>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Prepare a suspended payment execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
Prepare a suspended payment execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||||
|
|
||||||
|
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
|
||||||
|
|
||||||
### Parameters
|
### Parameters
|
||||||
|
|
||||||
<%- renderSchema('input/prepare-suspended-payment-execution.json') %>
|
<%- renderSchema('input/prepare-suspended-payment-execution.json') %>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
## sign
|
## sign
|
||||||
|
|
||||||
`sign(txJSON: string, secret: string): {signedTransaction: string, id: string}`
|
`sign(txJSON: string, secret: string, options: Object): {signedTransaction: string, id: string}`
|
||||||
|
|
||||||
Sign a prepared transaction. The signed transaction must subsequently be [submitted](#submit).
|
Sign a prepared transaction. The signed transaction must subsequently be [submitted](#submit).
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ A transaction type is specified by the strings in the first column in the table
|
|||||||
|
|
||||||
Type | Description
|
Type | Description
|
||||||
---- | -----------
|
---- | -----------
|
||||||
[payment](#payment) | A `payment` transaction represents a transfer of value from one account to another. Depending on the path taken, additional exchanges of value may occur atomically to facilitate the payment.
|
[payment](#payment) | A `payment` transaction represents a transfer of value from one account to another. Depending on the [path](https://ripple.com/build/paths/) taken, additional exchanges of value may occur atomically to facilitate the payment.
|
||||||
[order](#order) | An `order` transaction creates a limit order. It defines an intent to exchange currencies, and creates an order in the Ripple Consensus Ledger's order book if not completely fulfilled when placed. Orders can be partially fulfilled.
|
[order](#order) | An `order` transaction creates a limit order. It defines an intent to exchange currencies, and creates an order in the Ripple Consensus Ledger's order book if not completely fulfilled when placed. Orders can be partially fulfilled.
|
||||||
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the Ripple Consensus Ledger's order book.
|
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the Ripple Consensus Ledger's order book.
|
||||||
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
|
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
|
||||||
@@ -15,31 +15,51 @@ Type | Description
|
|||||||
[suspendedPaymentCancellation](#suspended-payment-cancellation) | A `suspendedPaymentCancellation` transaction unlocks the funds in a suspended payment and sends them back to the creator of the suspended payment, but it will only work after the suspended payment expires.
|
[suspendedPaymentCancellation](#suspended-payment-cancellation) | A `suspendedPaymentCancellation` transaction unlocks the funds in a suspended payment and sends them back to the creator of the suspended payment, but it will only work after the suspended payment expires.
|
||||||
[suspendedPaymentExecution](#suspended-payment-execution) | A `suspendedPaymentExecution` transaction unlocks the funds in a suspended payment and sends them to the destination of the suspended payment, but it will only work if the cryptographic condition is provided.
|
[suspendedPaymentExecution](#suspended-payment-execution) | A `suspendedPaymentExecution` transaction unlocks the funds in a suspended payment and sends them to the destination of the suspended payment, but it will only work if the cryptographic condition is provided.
|
||||||
|
|
||||||
|
The three "suspended payment" transaction types are not supported by the production Ripple peer-to-peer network at this time. They are available for testing purposes if you [configure RippleAPI](#boilerplate) to connect to the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) instead.
|
||||||
|
|
||||||
## Transaction Flow
|
## Transaction Flow
|
||||||
|
|
||||||
Executing a transaction with `RippleAPI` requires the following four steps:
|
Executing a transaction with `RippleAPI` requires the following four steps:
|
||||||
|
|
||||||
1. prepare - Create an unsigned transaction based on a [specification](#transaction-specifications) and [instructions](#transaction-instructions).
|
1. Prepare - Create an unsigned transaction based on a [specification](#transaction-specifications) and [instructions](#transaction-instructions). There is a method to prepare each type of transaction:
|
||||||
2. sign - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place.
|
* [preparePayment](#preparepayment)
|
||||||
3. submit - Submit the transaction to the connected server.
|
* [prepareTrustline](#preparetrustline)
|
||||||
4. verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted. It is recommended that you specify a `maxLedgerVersion` in the instructions when preparing a transaction because without it there is no way to know that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the network ledger version exceeds the `maxLedgerVersion` provided in the transaction instructions.
|
* [prepareOrder](#prepareorder)
|
||||||
|
* [prepareOrderCancellation](#prepareordercancellation)
|
||||||
|
* [prepareSettings](#preparesettings)
|
||||||
|
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
|
||||||
|
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
|
||||||
|
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
|
||||||
|
2. [Sign](#sign) - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place. For multisignature transactions, the `signedTransaction` fields returned by `sign` must be collected and passed to the [combine](#combine) method.
|
||||||
|
3. [Submit](#submit) - Submit the transaction to the connected server.
|
||||||
|
4. Verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted.
|
||||||
|
|
||||||
## Transaction Fees
|
## Transaction Fees
|
||||||
|
|
||||||
Every transaction requires a *fee* to be paid in XRP. The fee is destroyed; it is not sent to any other party. The purpose of the fee is to prevent denial of service attacks on the Ripple network.
|
Every transaction must destroy a small amount of XRP as a cost to send the transaction. This is also called a *transaction fee*. The transaction cost is designed to increase along with the load on the Ripple network, making it very expensive to deliberately or inadvertently overload the network.
|
||||||
|
|
||||||
You can choose the size of the fee you want to pay or let a default be used. The fee is like a bid in an auction for slots in the next ledger closing. If the fee you choose is too low, your transaction will not be included in the next ledger closing. You can get an estimate of the fee required to be included in the next ledger closing with the [getFee](#getfee) method.
|
You can choose the size of the fee you want to pay or let a default be used. You can get an estimate of the fee required to be included in the next ledger closing with the [getFee](#getfee) method.
|
||||||
|
|
||||||
## Transaction Instructions
|
## Transaction Instructions
|
||||||
|
|
||||||
Transactions instructions indicates how to execute a transaction, complementary with the [transaction specification](#transaction-specifications).
|
Transaction instructions indicate how to execute a transaction, complementary with the [transaction specification](#transaction-specifications).
|
||||||
|
|
||||||
<%- renderSchema("objects/instructions.json") %>
|
<%- renderSchema("objects/instructions.json") %>
|
||||||
|
|
||||||
|
We recommended that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the network ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare*" method.
|
||||||
|
|
||||||
## Transaction ID
|
## Transaction ID
|
||||||
|
|
||||||
```json
|
```json
|
||||||
"F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF"
|
"F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF"
|
||||||
```
|
```
|
||||||
|
|
||||||
A hash of the transaction that can be used to identify it. A transaction can be looked up by its ID using the [getTransaction](#gettransaction) method.
|
A transaction ID is a 64-bit hexadecimal string that uniquely identifies the transaction. The transaction ID is derived from the transaction instruction and specifications, using a strong hash function.
|
||||||
|
|
||||||
|
You can look up a transaction by ID using the [getTransaction](#gettransaction) method.
|
||||||
|
|
||||||
|
## Transaction Memos
|
||||||
|
|
||||||
|
Every transaction can optionally have an array of memos for user applications. The `memos` field in each [transaction specification](#transaction-specifications) is an array of objects with the following structure:
|
||||||
|
|
||||||
|
<%- renderSchema('objects/memos.json') %>
|
||||||
|
|||||||
236
npm-shrinkwrap.json
generated
236
npm-shrinkwrap.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "ripple-lib",
|
"name": "ripple-lib",
|
||||||
"version": "0.13.2",
|
"version": "0.16.6",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.10",
|
||||||
"from": "https://registry.npmjs.org/ajv/-/ajv-1.4.8.tgz",
|
"from": "https://registry.npmjs.org/ajv/-/ajv-1.4.10.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-1.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-1.4.10.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"json-stable-stringify": {
|
"json-stable-stringify": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -21,106 +21,162 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ajv-i18n": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"from": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-0.1.1.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv-i18n/-/ajv-i18n-0.1.1.tgz"
|
||||||
|
},
|
||||||
"babel-polyfill": {
|
"babel-polyfill": {
|
||||||
"version": "6.2.0",
|
"version": "6.3.14",
|
||||||
"from": "babel-polyfill@*",
|
"from": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.3.14.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.3.14.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.6",
|
||||||
"from": "core-js@>=1.0.1 <2.0.0",
|
"from": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz"
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz"
|
||||||
},
|
},
|
||||||
"babel-regenerator-runtime": {
|
"babel-regenerator-runtime": {
|
||||||
"version": "6.2.0",
|
"version": "6.3.13",
|
||||||
"from": "babel-regenerator-runtime@>=6.2.0 <7.0.0",
|
"from": "https://registry.npmjs.org/babel-regenerator-runtime/-/babel-regenerator-runtime-6.3.13.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/babel-regenerator-runtime/-/babel-regenerator-runtime-6.2.0.tgz"
|
"resolved": "https://registry.npmjs.org/babel-regenerator-runtime/-/babel-regenerator-runtime-6.3.13.tgz"
|
||||||
|
},
|
||||||
|
"babel-runtime": {
|
||||||
|
"version": "5.8.34",
|
||||||
|
"from": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.34.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.34.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"babel-runtime": {
|
"babel-runtime": {
|
||||||
"version": "5.8.29",
|
"version": "6.3.19",
|
||||||
"from": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.29.tgz",
|
"from": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.3.19.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.29.tgz",
|
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.3.19.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.6",
|
||||||
"from": "https://registry.npmjs.org/core-js/-/core-js-1.2.3.tgz",
|
"from": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.3.tgz"
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bignumber.js": {
|
"bignumber.js": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.2",
|
||||||
"from": "bignumber.js@>=2.0.3 <3.0.0",
|
"from": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.1.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.1.0.tgz"
|
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.1.2.tgz"
|
||||||
},
|
},
|
||||||
"https-proxy-agent": {
|
"https-proxy-agent": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"from": "https-proxy-agent@>=1.0.0 <2.0.0",
|
"from": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"agent-base": {
|
"agent-base": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "agent-base@>=2.0.0 <3.0.0",
|
"from": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.1.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.0.3",
|
"version": "5.0.3",
|
||||||
"from": "semver@>=5.0.1 <5.1.0",
|
"from": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz"
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.0.3.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"from": "debug@>=2.0.0 <3.0.0",
|
"from": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "0.7.1",
|
"version": "0.7.1",
|
||||||
"from": "ms@0.7.1",
|
"from": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
|
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"extend": {
|
"extend": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"from": "extend@>=3.0.0 <4.0.0",
|
"from": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jayson": {
|
||||||
|
"version": "1.2.2",
|
||||||
|
"from": "https://registry.npmjs.org/jayson/-/jayson-1.2.2.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/jayson/-/jayson-1.2.2.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"JSONStream": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"from": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.0.3.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.0.3.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"jsonparse": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"from": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.0.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.0.0.tgz"
|
||||||
|
},
|
||||||
|
"through": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"from": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commander": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"from": "https://registry.npmjs.org/commander/-/commander-1.3.2.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-1.3.2.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"keypress": {
|
||||||
|
"version": "0.1.0",
|
||||||
|
"from": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/keypress/-/keypress-0.1.0.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"eyes": {
|
||||||
|
"version": "0.1.8",
|
||||||
|
"from": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz"
|
||||||
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"from": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.6.0.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"lodash": {
|
"lodash": {
|
||||||
"version": "3.10.1",
|
"version": "3.10.1",
|
||||||
"from": "lodash@>=3.1.0 <4.0.0",
|
"from": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
|
||||||
},
|
},
|
||||||
"ripple-address-codec": {
|
"ripple-address-codec": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "ripple-address-codec@>=2.0.1 <3.0.0",
|
"from": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hash.js": {
|
"hash.js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"from": "hash.js@>=1.0.3 <2.0.0",
|
"from": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "inherits@>=2.0.1 <3.0.0",
|
"from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"x-address-codec": {
|
"x-address-codec": {
|
||||||
"version": "0.7.2",
|
"version": "0.7.2",
|
||||||
"from": "x-address-codec@>=0.7.0 <0.8.0",
|
"from": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.2.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"base-x": {
|
"base-x": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"from": "base-x@>=1.0.1 <2.0.0",
|
"from": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,77 +184,89 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ripple-binary-codec": {
|
"ripple-binary-codec": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.2",
|
||||||
"from": "ripple-binary-codec@0.1.0",
|
"from": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.1.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-0.1.2.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel-runtime": {
|
||||||
|
"version": "5.8.34",
|
||||||
|
"from": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.34.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.34.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"from": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"from": "bn.js@>=3.2.0 <4.0.0",
|
"from": "https://registry.npmjs.org/bn.js/-/bn.js-3.3.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.3.0.tgz"
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.3.0.tgz"
|
||||||
},
|
},
|
||||||
"create-hash": {
|
"create-hash": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"from": "create-hash@>=1.1.2 <2.0.0",
|
"from": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cipher-base": {
|
"cipher-base": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"from": "cipher-base@>=1.0.1 <2.0.0",
|
"from": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
|
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
|
||||||
},
|
},
|
||||||
"ripemd160": {
|
"ripemd160": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"from": "ripemd160@>=1.0.0 <2.0.0",
|
"from": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz"
|
||||||
},
|
},
|
||||||
"sha.js": {
|
"sha.js": {
|
||||||
"version": "2.4.4",
|
"version": "2.4.4",
|
||||||
"from": "sha.js@>=2.3.6 <3.0.0",
|
"from": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.4.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.4.tgz"
|
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.4.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"decimal.js": {
|
"decimal.js": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"from": "decimal.js@>=4.0.2 <5.0.0",
|
"from": "https://registry.npmjs.org/decimal.js/-/decimal.js-4.0.3.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-4.0.3.tgz"
|
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-4.0.3.tgz"
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "inherits@>=2.0.1 <3.0.0",
|
"from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ripple-hashes": {
|
"ripple-hashes": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"from": "ripple-hashes@0.1.0",
|
"from": "https://registry.npmjs.org/ripple-hashes/-/ripple-hashes-0.1.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ripple-hashes/-/ripple-hashes-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/ripple-hashes/-/ripple-hashes-0.1.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"create-hash": {
|
"create-hash": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"from": "create-hash@>=1.1.2 <2.0.0",
|
"from": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.2.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cipher-base": {
|
"cipher-base": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"from": "cipher-base@>=1.0.1 <2.0.0",
|
"from": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
|
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.2.tgz"
|
||||||
},
|
},
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "inherits@>=2.0.1 <3.0.0",
|
"from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
},
|
},
|
||||||
"ripemd160": {
|
"ripemd160": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"from": "ripemd160@>=1.0.0 <2.0.0",
|
"from": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-1.0.1.tgz"
|
||||||
},
|
},
|
||||||
"sha.js": {
|
"sha.js": {
|
||||||
"version": "2.4.4",
|
"version": "2.4.4",
|
||||||
"from": "sha.js@>=2.3.6 <3.0.0",
|
"from": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.4.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.4.tgz"
|
"resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.4.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,39 +275,51 @@
|
|||||||
},
|
},
|
||||||
"ripple-keypairs": {
|
"ripple-keypairs": {
|
||||||
"version": "0.10.0",
|
"version": "0.10.0",
|
||||||
"from": "ripple-keypairs@>=0.10.0 <0.11.0",
|
"from": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.10.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.10.0.tgz",
|
"resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.10.0.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel-runtime": {
|
||||||
|
"version": "5.8.34",
|
||||||
|
"from": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.34.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.34.tgz",
|
||||||
|
"dependencies": {
|
||||||
|
"core-js": {
|
||||||
|
"version": "1.2.6",
|
||||||
|
"from": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.6.tgz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"bn.js": {
|
"bn.js": {
|
||||||
"version": "3.3.0",
|
"version": "3.3.0",
|
||||||
"from": "bn.js@>=3.1.1 <4.0.0",
|
"from": "https://registry.npmjs.org/bn.js/-/bn.js-3.3.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.3.0.tgz"
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-3.3.0.tgz"
|
||||||
},
|
},
|
||||||
"brorand": {
|
"brorand": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"from": "brorand@>=1.0.5 <2.0.0",
|
"from": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
|
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.0.5.tgz"
|
||||||
},
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"from": "elliptic@>=5.1.0 <6.0.0",
|
"from": "https://registry.npmjs.org/elliptic/-/elliptic-5.2.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-5.2.1.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "inherits@>=2.0.1 <3.0.0",
|
"from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hash.js": {
|
"hash.js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"from": "hash.js@>=1.0.3 <2.0.0",
|
"from": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"inherits": {
|
"inherits": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"from": "inherits@>=2.0.1 <3.0.0",
|
"from": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,57 +328,23 @@
|
|||||||
},
|
},
|
||||||
"ripple-lib-transactionparser": {
|
"ripple-lib-transactionparser": {
|
||||||
"version": "0.6.0",
|
"version": "0.6.0",
|
||||||
"from": "ripple-lib-transactionparser@>=0.6.0 <0.7.0",
|
"from": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.6.0.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.6.0.tgz"
|
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.6.0.tgz"
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "0.7.2",
|
"version": "1.0.1",
|
||||||
"from": "ws@>=0.7.1 <0.8.0",
|
"from": "https://registry.npmjs.org/ws/-/ws-1.0.1.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-0.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-1.0.1.tgz",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"options": {
|
"options": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"from": "options@>=0.0.5",
|
"from": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
|
"resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz"
|
||||||
},
|
},
|
||||||
"ultron": {
|
"ultron": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"from": "ultron@>=1.0.0 <1.1.0",
|
"from": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
|
||||||
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz"
|
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz"
|
||||||
},
|
|
||||||
"bufferutil": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"from": "bufferutil@>=1.1.0 <1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-1.1.0.tgz",
|
|
||||||
"dependencies": {
|
|
||||||
"bindings": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"from": "bindings@>=1.2.0 <1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
|
|
||||||
},
|
|
||||||
"nan": {
|
|
||||||
"version": "1.8.4",
|
|
||||||
"from": "nan@>=1.8.0 <1.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"utf-8-validate": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"from": "utf-8-validate@>=1.1.0 <1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-1.1.0.tgz",
|
|
||||||
"dependencies": {
|
|
||||||
"bindings": {
|
|
||||||
"version": "1.2.1",
|
|
||||||
"from": "bindings@>=1.2.0 <1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
|
|
||||||
},
|
|
||||||
"nan": {
|
|
||||||
"version": "1.8.4",
|
|
||||||
"from": "nan@>=1.8.0 <1.9.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/nan/-/nan-1.8.4.tgz"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
package.json
38
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ripple-lib",
|
"name": "ripple-lib",
|
||||||
"version": "0.13.2",
|
"version": "0.16.6",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -16,39 +16,49 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "^1.4.8",
|
"ajv": "^1.4.8",
|
||||||
"babel-polyfill": "^6.2.0",
|
"babel-polyfill": "^6.3.14",
|
||||||
"babel-runtime": "^5.5.4",
|
"babel-runtime": "^6.3.19",
|
||||||
"bignumber.js": "^2.0.3",
|
"bignumber.js": "^2.0.3",
|
||||||
"https-proxy-agent": "^1.0.0",
|
"https-proxy-agent": "^1.0.0",
|
||||||
|
"jayson": "^1.2.2",
|
||||||
"lodash": "^3.1.0",
|
"lodash": "^3.1.0",
|
||||||
"ripple-address-codec": "^2.0.1",
|
"ripple-address-codec": "^2.0.1",
|
||||||
"ripple-binary-codec": "^0.1.0",
|
"ripple-binary-codec": "^0.1.2",
|
||||||
"ripple-hashes": "^0.1.0",
|
"ripple-hashes": "^0.1.0",
|
||||||
"ripple-keypairs": "^0.10.0",
|
"ripple-keypairs": "^0.10.0",
|
||||||
"ripple-lib-transactionparser": "^0.6.0",
|
"ripple-lib-transactionparser": "^0.6.0",
|
||||||
"ws": "^0.7.1"
|
"ws": "^1.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"assert-diff": "^1.0.1",
|
"assert-diff": "^1.0.1",
|
||||||
"babel": "^5.8.21",
|
"babel-cli": "^6.4.0",
|
||||||
"babel-core": "^5.8.22",
|
"babel-core": "^6.4.0",
|
||||||
"babel-eslint": "^4.1.3",
|
"babel-eslint": "^4.1.6",
|
||||||
"babel-loader": "^5.3.2",
|
"babel-loader": "^6.2.1",
|
||||||
|
"babel-plugin-syntax-flow": "^6.3.13",
|
||||||
|
"babel-plugin-transform-flow-strip-types": "^6.4.0",
|
||||||
|
"babel-preset-es2015": "^6.3.13",
|
||||||
|
"babel-preset-stage-1": "^6.3.13",
|
||||||
|
"babel-register": "^6.3.13",
|
||||||
"coveralls": "^2.10.0",
|
"coveralls": "^2.10.0",
|
||||||
"doctoc": "^0.15.0",
|
"doctoc": "^0.15.0",
|
||||||
"ejs": "^2.3.4",
|
"ejs": "^2.3.4",
|
||||||
"eslint": "^1.3.0",
|
"eslint": "^1.3.0",
|
||||||
"eslint-plugin-flowtype": "^1.0.0",
|
|
||||||
"eventemitter2": "^0.4.14",
|
"eventemitter2": "^0.4.14",
|
||||||
"flow-bin": "^0.14",
|
"flow-bin": "^0.14",
|
||||||
"gulp": "^3.8.10",
|
"gulp": "^3.8.10",
|
||||||
"gulp-bump": "^0.1.13",
|
"gulp-bump": "^0.1.13",
|
||||||
"gulp-rename": "^1.2.0",
|
"gulp-rename": "^1.2.0",
|
||||||
"gulp-uglify": "^1.1.0",
|
"gulp-uglify": "^1.1.0",
|
||||||
"istanbul": "^0.3.5",
|
"http-server": "^0.8.5",
|
||||||
|
"isparta": "^4.0.0",
|
||||||
"json-loader": "^0.5.2",
|
"json-loader": "^0.5.2",
|
||||||
"json-schema-to-markdown-table": "^0.4.0",
|
"json-schema-to-markdown-table": "^0.4.0",
|
||||||
"mocha": "^2.1.0",
|
"mocha": "^2.1.0",
|
||||||
|
"mocha-junit-reporter": "^1.9.1",
|
||||||
|
"mocha-phantomjs": "^4.0.1",
|
||||||
|
"mocha-in-sauce": "^0.0.1",
|
||||||
|
"null-loader": "^0.1.1",
|
||||||
"webpack": "^1.5.3",
|
"webpack": "^1.5.3",
|
||||||
"yargs": "^1.3.1"
|
"yargs": "^1.3.1"
|
||||||
},
|
},
|
||||||
@@ -62,10 +72,12 @@
|
|||||||
"watch": "babel -w -D --optional runtime -d dist/npm/ src/",
|
"watch": "babel -w -D --optional runtime -d dist/npm/ src/",
|
||||||
"compile-with-source-maps": "babel -D --optional runtime -s -t -d dist/npm/ src/",
|
"compile-with-source-maps": "babel -D --optional runtime -s -t -d dist/npm/ src/",
|
||||||
"prepublish": "npm run clean && npm run compile",
|
"prepublish": "npm run clean && npm run compile",
|
||||||
"test": "istanbul test _mocha",
|
"test": "babel-node node_modules/isparta/lib/cli cover ./node_modules/mocha/bin/_mocha",
|
||||||
"coveralls": "cat ./coverage/lcov.info | coveralls",
|
"coveralls": "cat ./coverage/lcov.info | coveralls",
|
||||||
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/",
|
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/",
|
||||||
"perf": "./scripts/perf_test.sh"
|
"perf": "./scripts/perf_test.sh",
|
||||||
|
"start": "babel-node scripts/http.js",
|
||||||
|
"sauce": "babel-node scripts/sauce-runner.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
18
scripts/checkeol.sh
Executable file
18
scripts/checkeol.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
function checkEOL {
|
||||||
|
local changedFiles=$(git --no-pager diff --name-only -M100% --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD)
|
||||||
|
local result=0
|
||||||
|
for name in $changedFiles; do
|
||||||
|
grep -c -U -q $'\r' $name
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "windows eol found in $name" >&2
|
||||||
|
result=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ $result -eq 1 ]; then
|
||||||
|
false
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
checkEOL
|
||||||
@@ -3,6 +3,10 @@
|
|||||||
NODE_INDEX="$1"
|
NODE_INDEX="$1"
|
||||||
TOTAL_NODES="$2"
|
TOTAL_NODES="$2"
|
||||||
|
|
||||||
|
function checkEOL {
|
||||||
|
./scripts/checkeol.sh
|
||||||
|
}
|
||||||
|
|
||||||
typecheck() {
|
typecheck() {
|
||||||
npm install -g flow-bin
|
npm install -g flow-bin
|
||||||
flow --version
|
flow --version
|
||||||
@@ -11,7 +15,7 @@ typecheck() {
|
|||||||
|
|
||||||
lint() {
|
lint() {
|
||||||
echo "eslint $(node_modules/.bin/eslint --version)"
|
echo "eslint $(node_modules/.bin/eslint --version)"
|
||||||
npm list babel-eslint | grep babel-eslint
|
npm list babel-eslint
|
||||||
REPO_URL="https://raw.githubusercontent.com/ripple/javascript-style-guide"
|
REPO_URL="https://raw.githubusercontent.com/ripple/javascript-style-guide"
|
||||||
curl "$REPO_URL/es6/eslintrc" > ./eslintrc
|
curl "$REPO_URL/es6/eslintrc" > ./eslintrc
|
||||||
echo "parser: babel-eslint" >> ./eslintrc
|
echo "parser: babel-eslint" >> ./eslintrc
|
||||||
@@ -20,6 +24,7 @@ lint() {
|
|||||||
|
|
||||||
unittest() {
|
unittest() {
|
||||||
# test "src"
|
# test "src"
|
||||||
|
mocha test --reporter mocha-junit-reporter --reporter-options mochaFile=$CIRCLE_TEST_REPORTS/test-results.xml
|
||||||
npm test --coverage
|
npm test --coverage
|
||||||
npm run coveralls
|
npm run coveralls
|
||||||
|
|
||||||
@@ -29,11 +34,33 @@ unittest() {
|
|||||||
mkdir -p test-compiled/node_modules
|
mkdir -p test-compiled/node_modules
|
||||||
ln -nfs ../../dist/npm test-compiled/node_modules/ripple-api
|
ln -nfs ../../dist/npm test-compiled/node_modules/ripple-api
|
||||||
mocha --opts test-compiled/mocha.opts test-compiled
|
mocha --opts test-compiled/mocha.opts test-compiled
|
||||||
|
|
||||||
|
# compile tests for browser testing
|
||||||
|
gulp build-min build-tests
|
||||||
|
node --harmony test-compiled/mocked-server.js > /dev/null &
|
||||||
|
|
||||||
|
echo "Running tests in PhantomJS"
|
||||||
|
mocha-phantomjs test/localrunner.html
|
||||||
|
echo "Running tests using minified version in PhantomJS"
|
||||||
|
mocha-phantomjs test/localrunnermin.html
|
||||||
|
|
||||||
|
echo "Running tests in SauceLabs"
|
||||||
|
http-server &
|
||||||
|
npm run sauce
|
||||||
|
|
||||||
|
pkill -f mocked-server.js
|
||||||
|
pkill -f http-server
|
||||||
rm -rf test-compiled
|
rm -rf test-compiled
|
||||||
}
|
}
|
||||||
|
|
||||||
integrationtest() {
|
integrationtest() {
|
||||||
mocha test/integration/integration-test.js
|
mocha test/integration/integration-test.js
|
||||||
|
mocha test/integration/http-integration-test.js
|
||||||
|
|
||||||
|
# run integration tests in PhantomJS
|
||||||
|
gulp build-tests build-min
|
||||||
|
echo "Running integragtion tests in PhantomJS"
|
||||||
|
mocha-phantomjs test/localintegrationrunner.html
|
||||||
}
|
}
|
||||||
|
|
||||||
doctest() {
|
doctest() {
|
||||||
@@ -46,6 +73,7 @@ doctest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
oneNode() {
|
oneNode() {
|
||||||
|
checkEOL
|
||||||
doctest
|
doctest
|
||||||
lint
|
lint
|
||||||
typecheck
|
typecheck
|
||||||
@@ -56,7 +84,7 @@ oneNode() {
|
|||||||
twoNodes() {
|
twoNodes() {
|
||||||
case "$NODE_INDEX" in
|
case "$NODE_INDEX" in
|
||||||
0) doctest; lint; integrationtest;;
|
0) doctest; lint; integrationtest;;
|
||||||
1) typecheck; unittest;;
|
1) checkEOL; typecheck; unittest;;
|
||||||
*) echo "ERROR: invalid usage"; exit 2;;
|
*) echo "ERROR: invalid usage"; exit 2;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
@@ -64,7 +92,7 @@ twoNodes() {
|
|||||||
threeNodes() {
|
threeNodes() {
|
||||||
case "$NODE_INDEX" in
|
case "$NODE_INDEX" in
|
||||||
0) doctest; lint; integrationtest;;
|
0) doctest; lint; integrationtest;;
|
||||||
1) typecheck;;
|
1) checkEOL; typecheck;;
|
||||||
2) unittest;;
|
2) unittest;;
|
||||||
*) echo "ERROR: invalid usage"; exit 2;;
|
*) echo "ERROR: invalid usage"; exit 2;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
16
scripts/http.js
Normal file
16
scripts/http.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const createHTTPServer = require('../src/index').createHTTPServer;
|
||||||
|
const port = 5990;
|
||||||
|
const serverUrl = 'wss://s1.ripple.com';
|
||||||
|
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
const server = createHTTPServer({server: serverUrl}, port);
|
||||||
|
server.start().then(() => {
|
||||||
|
console.log('Server started on port ' + String(port));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
@@ -16,28 +16,3 @@ echo ""
|
|||||||
echo "publish to npm"
|
echo "publish to npm"
|
||||||
npm publish
|
npm publish
|
||||||
exit_on_error
|
exit_on_error
|
||||||
|
|
||||||
rm -rf dist/bower
|
|
||||||
echo ""
|
|
||||||
echo "publish to bower"
|
|
||||||
|
|
||||||
git clone git@github.com:ripple/bower-ripple.git dist/bower
|
|
||||||
gulp bower
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
cd dist/bower
|
|
||||||
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc([0-9])+)?')
|
|
||||||
echo "version: $version"
|
|
||||||
git add ripple.js ripple-debug.js ripple-min.js bower.json
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
git commit -m "[TASK] add v$version"
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
git tag "v$version"
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
git push origin master
|
|
||||||
git push --tags origin master
|
|
||||||
|
|
||||||
cd ../..
|
|
||||||
|
|||||||
@@ -16,28 +16,3 @@ echo ""
|
|||||||
echo "publish rc to npm"
|
echo "publish rc to npm"
|
||||||
npm publish --tag beta
|
npm publish --tag beta
|
||||||
exit_on_error
|
exit_on_error
|
||||||
|
|
||||||
rm -rf dist/bower
|
|
||||||
echo ""
|
|
||||||
echo "publish to bower"
|
|
||||||
|
|
||||||
git clone git@github.com:ripple/bower-ripple.git dist/bower
|
|
||||||
gulp bower
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
cd dist/bower
|
|
||||||
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc([0-9])+)?')
|
|
||||||
echo "version: $version"
|
|
||||||
git add ripple.js ripple-debug.js ripple-min.js bower.json
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
git commit -m "[TASK] add v$version"
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
git tag "v$version"
|
|
||||||
exit_on_error
|
|
||||||
|
|
||||||
git push origin master
|
|
||||||
git push --tags origin master
|
|
||||||
|
|
||||||
cd ../..
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
rm -rf dist/bower
|
|
||||||
git clone git@github.com:ripple/bower-ripple.git dist/bower
|
|
||||||
gulp bower
|
|
||||||
cd dist/bower
|
|
||||||
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc[0-9])?')
|
|
||||||
echo "version: $version"
|
|
||||||
git add ripple.js ripple-debug.js ripple-min.js bower.json
|
|
||||||
git commit -m "[TASK] add v$version"
|
|
||||||
git tag "v$version"
|
|
||||||
git push origin master
|
|
||||||
git push --tags origin master
|
|
||||||
cd ..
|
|
||||||
98
scripts/sauce-runner.js
Normal file
98
scripts/sauce-runner.js
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
const MochaSauce = require('mocha-in-sauce');
|
||||||
|
|
||||||
|
const testUrl = 'http://testripple.circleci.com:8080/test/saucerunner.html';
|
||||||
|
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
// uncomment for more debug info
|
||||||
|
// process.env.DEBUG = '*';
|
||||||
|
|
||||||
|
// configure
|
||||||
|
const config = {
|
||||||
|
name: 'RippleAPI',
|
||||||
|
host: 'localhost',
|
||||||
|
port: 4445,
|
||||||
|
maxDuration: 180000,
|
||||||
|
// the current build name (optional)
|
||||||
|
build: Date.now(),
|
||||||
|
seleniumVersion: '2.50.1',
|
||||||
|
url: testUrl,
|
||||||
|
runSauceConnect: true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.CIRCLE_BUILD_NUM) {
|
||||||
|
config.build = process.env.CIRCLE_BUILD_NUM;
|
||||||
|
config.tags = [process.env.CIRCLE_BRANCH, process.env.CIRCLE_SHA1];
|
||||||
|
config.tunnelIdentifier = process.env.CIRCLE_BUILD_NUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sauce = new MochaSauce(config);
|
||||||
|
|
||||||
|
sauce.concurrency(5);
|
||||||
|
|
||||||
|
// setup what browsers to test with
|
||||||
|
sauce.browser({browserName: 'firefox', platform: 'Linux',
|
||||||
|
version: '43'});
|
||||||
|
sauce.browser({browserName: 'firefox', platform: 'Windows 8.1',
|
||||||
|
version: '43'});
|
||||||
|
sauce.browser({browserName: 'firefox', platform: 'OS X 10.11',
|
||||||
|
version: '43'});
|
||||||
|
sauce.browser({browserName: 'safari', platform: 'OS X 10.11',
|
||||||
|
version: '9'});
|
||||||
|
sauce.browser({browserName: 'chrome', platform: 'OS X 10.11',
|
||||||
|
version: '47'});
|
||||||
|
sauce.browser({browserName: 'chrome', platform: 'Linux',
|
||||||
|
version: '47'});
|
||||||
|
sauce.browser({browserName: 'chrome', platform: 'Windows 8.1',
|
||||||
|
version: '47'});
|
||||||
|
sauce.browser({browserName: 'internet explorer', platform: 'Windows 10',
|
||||||
|
version: '11'});
|
||||||
|
sauce.browser({browserName: 'MicrosoftEdge', platform: 'Windows 10',
|
||||||
|
version: '20'});
|
||||||
|
|
||||||
|
sauce.on('init', function(browser) {
|
||||||
|
console.log(' init : %s %s', browser.browserName, browser.platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
sauce.on('start', function(browser) {
|
||||||
|
console.log(' start : %s %s', browser.browserName, browser.platform);
|
||||||
|
});
|
||||||
|
|
||||||
|
sauce.on('end', function(browser, res) {
|
||||||
|
console.log(' end : %s %s : %d failures', browser.browserName,
|
||||||
|
browser.platform, res && res.failures);
|
||||||
|
});
|
||||||
|
|
||||||
|
sauce.on('connected', sauceConnectProcess => {
|
||||||
|
sauceConnectProcess.on('exit', function(code, /* signal */) {
|
||||||
|
if (code > 0) {
|
||||||
|
console.log('something wrong - exiting');
|
||||||
|
process.exit();
|
||||||
|
} else {
|
||||||
|
console.log('normal tunnel exit');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
sauce.start(function(err, res) {
|
||||||
|
let failure = false;
|
||||||
|
if (err) {
|
||||||
|
console.log('Error starting Sauce');
|
||||||
|
console.error(err);
|
||||||
|
process.exitCode = 2;
|
||||||
|
} else {
|
||||||
|
console.log('-------------- done --------------');
|
||||||
|
failure = _.some(res, 'failures');
|
||||||
|
console.log('Tests are failed:', failure);
|
||||||
|
if (failure) {
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
147
src/api.js
Normal file
147
src/api.js
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/* @flow */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/* eslint-disable max-len */
|
||||||
|
// Enable core-js polyfills. This allows use of ES6/7 extensions listed here:
|
||||||
|
// https://github.com/zloirock/core-js/blob/fb0890f32dabe8d4d88a4350d1b268446127132e/shim.js#L1-L103
|
||||||
|
/* eslint-enable max-len */
|
||||||
|
|
||||||
|
// In node.js env, polyfill might be already loaded (from any npm package),
|
||||||
|
// that's why we do this check.
|
||||||
|
if (!global._babelPolyfill) {
|
||||||
|
require('babel-polyfill');
|
||||||
|
}
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
const EventEmitter = require('events').EventEmitter;
|
||||||
|
const common = require('./common');
|
||||||
|
const server = require('./server/server');
|
||||||
|
const connect = server.connect;
|
||||||
|
const disconnect = server.disconnect;
|
||||||
|
const getServerInfo = server.getServerInfo;
|
||||||
|
const getFee = server.getFee;
|
||||||
|
const isConnected = server.isConnected;
|
||||||
|
const getLedgerVersion = server.getLedgerVersion;
|
||||||
|
const getTransaction = require('./ledger/transaction');
|
||||||
|
const getTransactions = require('./ledger/transactions');
|
||||||
|
const getTrustlines = require('./ledger/trustlines');
|
||||||
|
const getBalances = require('./ledger/balances');
|
||||||
|
const getBalanceSheet = require('./ledger/balance-sheet');
|
||||||
|
const getPaths = require('./ledger/pathfind');
|
||||||
|
const getOrders = require('./ledger/orders');
|
||||||
|
const getOrderbook = require('./ledger/orderbook');
|
||||||
|
const getSettings = require('./ledger/settings');
|
||||||
|
const getAccountInfo = require('./ledger/accountinfo');
|
||||||
|
const preparePayment = require('./transaction/payment');
|
||||||
|
const prepareTrustline = require('./transaction/trustline');
|
||||||
|
const prepareOrder = require('./transaction/order');
|
||||||
|
const prepareOrderCancellation = require('./transaction/ordercancellation');
|
||||||
|
const prepareSuspendedPaymentCreation =
|
||||||
|
require('./transaction/suspended-payment-creation');
|
||||||
|
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 combine = require('./transaction/combine');
|
||||||
|
const submit = require('./transaction/submit');
|
||||||
|
const errors = require('./common').errors;
|
||||||
|
const generateAddress =
|
||||||
|
require('./offline/generate-address').generateAddressAPI;
|
||||||
|
const computeLedgerHash = require('./offline/ledgerhash');
|
||||||
|
const getLedger = require('./ledger/ledger');
|
||||||
|
|
||||||
|
type APIOptions = {
|
||||||
|
server?: string,
|
||||||
|
feeCushion?: number,
|
||||||
|
trace?: boolean,
|
||||||
|
proxy?: string,
|
||||||
|
timeout?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent access to non-validated ledger versions
|
||||||
|
class RestrictedConnection extends common.Connection {
|
||||||
|
request(request, timeout) {
|
||||||
|
const ledger_index = request.ledger_index;
|
||||||
|
if (ledger_index !== undefined && ledger_index !== 'validated') {
|
||||||
|
if (!_.isNumber(ledger_index) || ledger_index > this._ledgerVersion) {
|
||||||
|
return Promise.reject(new errors.LedgerVersionError(
|
||||||
|
`ledgerVersion ${ledger_index} is greater than server\'s ` +
|
||||||
|
`most recent validated ledger: ${this._ledgerVersion}`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.request(request, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RippleAPI extends EventEmitter {
|
||||||
|
constructor(options: APIOptions = {}) {
|
||||||
|
common.validate.apiOptions(options);
|
||||||
|
super();
|
||||||
|
this._feeCushion = options.feeCushion || 1.2;
|
||||||
|
const serverURL = options.server;
|
||||||
|
if (serverURL !== undefined) {
|
||||||
|
this.connection = new RestrictedConnection(serverURL, options);
|
||||||
|
this.connection.on('ledgerClosed', message => {
|
||||||
|
this.emit('ledger', server.formatLedgerClose(message));
|
||||||
|
});
|
||||||
|
this.connection.on('error', (errorCode, errorMessage, data) => {
|
||||||
|
this.emit('error', errorCode, errorMessage, data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// use null object pattern to provide better error message if user
|
||||||
|
// tries to call a method that requires a connection
|
||||||
|
this.connection = new RestrictedConnection(null, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_.assign(RippleAPI.prototype, {
|
||||||
|
connect,
|
||||||
|
disconnect,
|
||||||
|
isConnected,
|
||||||
|
getServerInfo,
|
||||||
|
getFee,
|
||||||
|
getLedgerVersion,
|
||||||
|
|
||||||
|
getTransaction,
|
||||||
|
getTransactions,
|
||||||
|
getTrustlines,
|
||||||
|
getBalances,
|
||||||
|
getBalanceSheet,
|
||||||
|
getPaths,
|
||||||
|
getOrders,
|
||||||
|
getOrderbook,
|
||||||
|
getSettings,
|
||||||
|
getAccountInfo,
|
||||||
|
getLedger,
|
||||||
|
|
||||||
|
preparePayment,
|
||||||
|
prepareTrustline,
|
||||||
|
prepareOrder,
|
||||||
|
prepareOrderCancellation,
|
||||||
|
prepareSuspendedPaymentCreation,
|
||||||
|
prepareSuspendedPaymentExecution,
|
||||||
|
prepareSuspendedPaymentCancellation,
|
||||||
|
prepareSettings,
|
||||||
|
sign,
|
||||||
|
combine,
|
||||||
|
submit,
|
||||||
|
|
||||||
|
generateAddress,
|
||||||
|
computeLedgerHash,
|
||||||
|
errors
|
||||||
|
});
|
||||||
|
|
||||||
|
// these are exposed only for use by unit tests; they are not part of the API
|
||||||
|
RippleAPI._PRIVATE = {
|
||||||
|
validate: common.validate,
|
||||||
|
RangeSet: require('./common/rangeset').RangeSet,
|
||||||
|
ledgerUtils: require('./ledger/utils'),
|
||||||
|
schemaValidator: require('./common/schema-validator')
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
RippleAPI
|
||||||
|
};
|
||||||
70
src/broadcast.js
Normal file
70
src/broadcast.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
|
const RippleAPI = require('./api').RippleAPI;
|
||||||
|
|
||||||
|
class RippleAPIBroadcast extends RippleAPI {
|
||||||
|
constructor(servers, options) {
|
||||||
|
super(options);
|
||||||
|
this.ledgerVersion = 0;
|
||||||
|
|
||||||
|
const apis = servers.map(server => new RippleAPI(
|
||||||
|
_.assign({}, options, {server})
|
||||||
|
));
|
||||||
|
|
||||||
|
// exposed for testing
|
||||||
|
this._apis = apis;
|
||||||
|
|
||||||
|
this.getMethodNames().forEach(name => {
|
||||||
|
this[name] = function() { // eslint-disable-line no-loop-func
|
||||||
|
return Promise.race(apis.map(api => api[name].apply(api, arguments)));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// connection methods must be overridden to apply to all api instances
|
||||||
|
this.connect = function() {
|
||||||
|
return Promise.all(apis.map(api => api.connect()));
|
||||||
|
};
|
||||||
|
this.disconnect = function() {
|
||||||
|
return Promise.all(apis.map(api => api.disconnect()));
|
||||||
|
};
|
||||||
|
this.isConnected = function() {
|
||||||
|
return _.every(apis.map(api => api.isConnected()));
|
||||||
|
};
|
||||||
|
|
||||||
|
// synchronous methods are all passed directly to the first api instance
|
||||||
|
const defaultAPI = apis[0];
|
||||||
|
const syncMethods = ['sign', 'generateAddress', 'computeLedgerHash'];
|
||||||
|
syncMethods.forEach(name => {
|
||||||
|
this[name] = defaultAPI[name].bind(defaultAPI);
|
||||||
|
});
|
||||||
|
|
||||||
|
apis.forEach(api => {
|
||||||
|
api.on('ledger', this.onLedgerEvent.bind(this));
|
||||||
|
api.on('error', (errorCode, errorMessage, data) =>
|
||||||
|
this.emit('error', errorCode, errorMessage, data));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onLedgerEvent(ledger) {
|
||||||
|
if (ledger.ledgerVersion > this.ledgerVersion) {
|
||||||
|
this.ledgerVersion = ledger.ledgerVersion;
|
||||||
|
this.emit('ledger', ledger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getMethodNames() {
|
||||||
|
const methodNames = [];
|
||||||
|
for (const name in RippleAPI.prototype) {
|
||||||
|
if (RippleAPI.prototype.hasOwnProperty(name)) {
|
||||||
|
if (typeof RippleAPI.prototype[name] === 'function') {
|
||||||
|
methodNames.push(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return methodNames;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
RippleAPIBroadcast
|
||||||
|
};
|
||||||
21
src/common/browser-hacks.js
Normal file
21
src/common/browser-hacks.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
function setPrototypeOf(object, prototype) {
|
||||||
|
// Object.setPrototypeOf not supported on Internet Explorer 9
|
||||||
|
/* eslint-disable */
|
||||||
|
Object.setPrototypeOf ? Object.setPrototypeOf(object, prototype) :
|
||||||
|
object.__proto__ = prototype;
|
||||||
|
/* eslint-enable */
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConstructorName(object) {
|
||||||
|
// hack for internet explorer
|
||||||
|
return process.browser ?
|
||||||
|
object.constructor.toString().match(/^function\s+([^(]*)/)[1] :
|
||||||
|
object.constructor.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getConstructorName,
|
||||||
|
setPrototypeOf
|
||||||
|
};
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
const {EventEmitter} = require('events');
|
const {EventEmitter} = require('events');
|
||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
const parseURL = require('url').parse;
|
const parseURL = require('url').parse;
|
||||||
@@ -15,9 +16,20 @@ function isStreamMessageType(type) {
|
|||||||
class Connection extends EventEmitter {
|
class Connection extends EventEmitter {
|
||||||
constructor(url, options = {}) {
|
constructor(url, options = {}) {
|
||||||
super();
|
super();
|
||||||
|
this.setMaxListeners(Infinity);
|
||||||
this._url = url;
|
this._url = url;
|
||||||
this._proxyURL = options.proxyURL;
|
this._trace = options.trace;
|
||||||
|
if (this._trace) {
|
||||||
|
// for easier unit testing
|
||||||
|
this._console = console;
|
||||||
|
}
|
||||||
|
this._proxyURL = options.proxy;
|
||||||
|
this._proxyAuthorization = options.proxyAuthorization;
|
||||||
this._authorization = options.authorization;
|
this._authorization = options.authorization;
|
||||||
|
this._trustedCertificates = options.trustedCertificates;
|
||||||
|
this._key = options.key;
|
||||||
|
this._passphrase = options.passphrase;
|
||||||
|
this._certificate = options.certificate;
|
||||||
this._timeout = options.timeout || (20 * 1000);
|
this._timeout = options.timeout || (20 * 1000);
|
||||||
this._isReady = false;
|
this._isReady = false;
|
||||||
this._ws = null;
|
this._ws = null;
|
||||||
@@ -26,6 +38,17 @@ class Connection extends EventEmitter {
|
|||||||
this._nextRequestID = 1;
|
this._nextRequestID = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_updateLedgerVersions(data) {
|
||||||
|
this._ledgerVersion = Number(data.ledger_index);
|
||||||
|
if (data.validated_ledgers) {
|
||||||
|
this._availableLedgerVersions.reset();
|
||||||
|
this._availableLedgerVersions.parseAndAddRanges(
|
||||||
|
data.validated_ledgers);
|
||||||
|
} else {
|
||||||
|
this._availableLedgerVersions.addValue(this._ledgerVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// return value is array of arguments to Connection.emit
|
// return value is array of arguments to Connection.emit
|
||||||
_parseMessage(message) {
|
_parseMessage(message) {
|
||||||
const data = JSON.parse(message);
|
const data = JSON.parse(message);
|
||||||
@@ -36,29 +59,29 @@ class Connection extends EventEmitter {
|
|||||||
return [data.id.toString(), data];
|
return [data.id.toString(), data];
|
||||||
} else if (isStreamMessageType(data.type)) {
|
} else if (isStreamMessageType(data.type)) {
|
||||||
if (data.type === 'ledgerClosed') {
|
if (data.type === 'ledgerClosed') {
|
||||||
this._ledgerVersion = Number(data.ledger_index);
|
this._updateLedgerVersions(data);
|
||||||
this._availableLedgerVersions.reset();
|
|
||||||
this._availableLedgerVersions.parseAndAddRanges(
|
|
||||||
data.validated_ledgers);
|
|
||||||
}
|
}
|
||||||
return [data.type, data];
|
return [data.type, data];
|
||||||
} else if (data.type === undefined && data.error) {
|
} else if (data.type === undefined && data.error) {
|
||||||
return ['error', data.error, data.error_message]; // e.g. slowDown
|
return ['error', data.error, data.error_message, data]; // e.g. slowDown
|
||||||
}
|
}
|
||||||
throw new ResponseFormatError('unrecognized message type: ' + data.type);
|
throw new ResponseFormatError('unrecognized message type: ' + data.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onMessage(message) {
|
_onMessage(message) {
|
||||||
let parameters;
|
let parameters;
|
||||||
|
if (this._trace) {
|
||||||
|
this._console.log(message);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
parameters = this._parseMessage(message);
|
parameters = this._parseMessage(message);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.emit('error', 'badMessage', message);
|
this.emit('error', 'badMessage', error.message, message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// we don't want this inside the try/catch or exceptions in listener
|
// we don't want this inside the try/catch or exceptions in listener
|
||||||
// will be caught
|
// will be caught
|
||||||
this.emit.apply(this, parameters);
|
this.emit(...parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
get _state() {
|
get _state() {
|
||||||
@@ -73,31 +96,43 @@ class Connection extends EventEmitter {
|
|||||||
return this._state === WebSocket.OPEN && this._isReady;
|
return this._state === WebSocket.OPEN && this._isReady;
|
||||||
}
|
}
|
||||||
|
|
||||||
_onUnexpectedClose() {
|
_onUnexpectedClose(resolve = function() {}, reject = function() {}) {
|
||||||
|
this._ws = null;
|
||||||
this._isReady = false;
|
this._isReady = false;
|
||||||
this.connect().then();
|
this.connect().then(resolve, reject);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onOpen() {
|
_onOpen() {
|
||||||
|
this._ws.removeListener('close', this._onUnexpectedCloseBound);
|
||||||
|
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this);
|
||||||
|
this._ws.once('close', this._onUnexpectedCloseBound);
|
||||||
|
|
||||||
const request = {
|
const request = {
|
||||||
command: 'subscribe',
|
command: 'subscribe',
|
||||||
streams: ['ledger']
|
streams: ['ledger']
|
||||||
};
|
};
|
||||||
return this.request(request).then(response => {
|
return this.request(request).then(data => {
|
||||||
this._ledgerVersion = Number(response.ledger_index);
|
this._updateLedgerVersions(data);
|
||||||
this._availableLedgerVersions.parseAndAddRanges(
|
|
||||||
response.validated_ledgers);
|
|
||||||
this._isReady = true;
|
this._isReady = true;
|
||||||
this.emit('connected');
|
this.emit('connected');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_createWebSocket(url, proxyURL, authorization) {
|
_createWebSocket() {
|
||||||
const options = {};
|
const options = {};
|
||||||
if (proxyURL !== undefined) {
|
if (this._proxyURL !== undefined) {
|
||||||
const parsedURL = parseURL(url);
|
const parsedURL = parseURL(this._url);
|
||||||
const proxyOptions = parseURL(proxyURL);
|
const parsedProxyURL = parseURL(this._proxyURL);
|
||||||
proxyOptions.secureEndpoint = (parsedURL.protocol === 'wss:');
|
const proxyOverrides = _.omit({
|
||||||
|
secureEndpoint: (parsedURL.protocol === 'wss:'),
|
||||||
|
secureProxy: (parsedProxyURL.protocol === 'https:'),
|
||||||
|
auth: this._proxyAuthorization,
|
||||||
|
ca: this._trustedCertificates,
|
||||||
|
key: this._key,
|
||||||
|
passphrase: this._passphrase,
|
||||||
|
cert: this._certificate
|
||||||
|
}, _.isUndefined);
|
||||||
|
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides);
|
||||||
let HttpsProxyAgent;
|
let HttpsProxyAgent;
|
||||||
try {
|
try {
|
||||||
HttpsProxyAgent = require('https-proxy-agent');
|
HttpsProxyAgent = require('https-proxy-agent');
|
||||||
@@ -106,11 +141,24 @@ class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
options.agent = new HttpsProxyAgent(proxyOptions);
|
options.agent = new HttpsProxyAgent(proxyOptions);
|
||||||
}
|
}
|
||||||
if (authorization !== undefined) {
|
if (this._authorization !== undefined) {
|
||||||
const base64 = new Buffer(authorization).toString('base64');
|
const base64 = new Buffer(this._authorization).toString('base64');
|
||||||
options.headers = {Authorization: `Basic ${base64}`};
|
options.headers = {Authorization: `Basic ${base64}`};
|
||||||
}
|
}
|
||||||
return new WebSocket(url, options);
|
const optionsOverrides = _.omit({
|
||||||
|
ca: this._trustedCertificates,
|
||||||
|
key: this._key,
|
||||||
|
passphrase: this._passphrase,
|
||||||
|
cert: this._certificate
|
||||||
|
}, _.isUndefined);
|
||||||
|
const websocketOptions = _.assign({}, options, optionsOverrides);
|
||||||
|
const websocket = new WebSocket(this._url, null, websocketOptions);
|
||||||
|
// we will have a listener for each outstanding request,
|
||||||
|
// so we have to raise the limit (the default is 10)
|
||||||
|
if (typeof websocket.setMaxListeners === 'function') {
|
||||||
|
websocket.setMaxListeners(Infinity);
|
||||||
|
}
|
||||||
|
return websocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
@@ -124,10 +172,21 @@ class Connection extends EventEmitter {
|
|||||||
} else if (this._state === WebSocket.CONNECTING) {
|
} else if (this._state === WebSocket.CONNECTING) {
|
||||||
this._ws.once('open', resolve);
|
this._ws.once('open', resolve);
|
||||||
} else {
|
} else {
|
||||||
this._ws = this._createWebSocket(this._url, this._proxyURL,
|
this._ws = this._createWebSocket();
|
||||||
this._authorization);
|
// when an error causes the connection to close, the close event
|
||||||
|
// should still be emitted; the "ws" documentation says: "The close
|
||||||
|
// event is also emitted when then underlying net.Socket closes the
|
||||||
|
// connection (end or close)."
|
||||||
|
this._ws.on('error', error =>
|
||||||
|
this.emit('error', 'websocket', error.message, error));
|
||||||
this._ws.on('message', this._onMessage.bind(this));
|
this._ws.on('message', this._onMessage.bind(this));
|
||||||
this._ws.once('close', () => this._onUnexpectedClose);
|
// in browser close event can came before open event, so we must
|
||||||
|
// resolve connect's promise after reconnect in that case.
|
||||||
|
// after open event we will rebound _onUnexpectedCloseBound
|
||||||
|
// without resolve and reject functions
|
||||||
|
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this,
|
||||||
|
resolve, reject);
|
||||||
|
this._ws.once('close', this._onUnexpectedCloseBound);
|
||||||
this._ws.once('open', () => this._onOpen().then(resolve, reject));
|
this._ws.once('open', () => this._onOpen().then(resolve, reject));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -140,7 +199,7 @@ class Connection extends EventEmitter {
|
|||||||
} else if (this._state === WebSocket.CLOSING) {
|
} else if (this._state === WebSocket.CLOSING) {
|
||||||
this._ws.once('close', resolve);
|
this._ws.once('close', resolve);
|
||||||
} else {
|
} else {
|
||||||
this._ws.removeListener('close', this._onUnexpectedClose);
|
this._ws.removeListener('close', this._onUnexpectedCloseBound);
|
||||||
this._ws.once('close', () => {
|
this._ws.once('close', () => {
|
||||||
this._ws = null;
|
this._ws = null;
|
||||||
this._isReady = false;
|
this._isReady = false;
|
||||||
@@ -182,6 +241,9 @@ class Connection extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_send(message) {
|
_send(message) {
|
||||||
|
if (this._trace) {
|
||||||
|
this._console.log(message);
|
||||||
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this._ws.send(message, undefined, (error, result) => {
|
this._ws.send(message, undefined, (error, result) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
|
|||||||
@@ -1,13 +1,27 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
const browserHacks = require('./browser-hacks');
|
||||||
|
|
||||||
class RippleError extends Error {
|
// this is needed because extending builtins doesn't work in babel 6.x
|
||||||
|
function extendableBuiltin(cls) {
|
||||||
|
function ExtendableBuiltin() {
|
||||||
|
cls.apply(this, arguments);
|
||||||
|
}
|
||||||
|
ExtendableBuiltin.prototype = Object.create(cls.prototype);
|
||||||
|
browserHacks.setPrototypeOf(ExtendableBuiltin, cls);
|
||||||
|
return ExtendableBuiltin;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RippleError extends extendableBuiltin(Error) {
|
||||||
constructor(message, data) {
|
constructor(message, data) {
|
||||||
super(message);
|
super(message);
|
||||||
this.name = this.constructor.name;
|
|
||||||
|
this.name = browserHacks.getConstructorName(this);
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
Error.captureStackTrace(this, this.constructor.name);
|
if (Error.captureStackTrace) {
|
||||||
|
Error.captureStackTrace(this, this.constructor.name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ function loadSchemas() {
|
|||||||
require('./schemas/objects/signed-value.json'),
|
require('./schemas/objects/signed-value.json'),
|
||||||
require('./schemas/objects/orderbook.json'),
|
require('./schemas/objects/orderbook.json'),
|
||||||
require('./schemas/objects/instructions.json'),
|
require('./schemas/objects/instructions.json'),
|
||||||
|
require('./schemas/objects/settings.json'),
|
||||||
require('./schemas/specifications/settings.json'),
|
require('./schemas/specifications/settings.json'),
|
||||||
require('./schemas/specifications/payment.json'),
|
require('./schemas/specifications/payment.json'),
|
||||||
require('./schemas/specifications/suspended-payment-cancellation.json'),
|
require('./schemas/specifications/suspended-payment-cancellation.json'),
|
||||||
@@ -61,7 +62,7 @@ function loadSchemas() {
|
|||||||
require('./schemas/output/get-orders.json'),
|
require('./schemas/output/get-orders.json'),
|
||||||
require('./schemas/output/order-change.json'),
|
require('./schemas/output/order-change.json'),
|
||||||
require('./schemas/output/prepare.json'),
|
require('./schemas/output/prepare.json'),
|
||||||
require('./schemas/output/ledger-closed.json'),
|
require('./schemas/output/ledger-event.json'),
|
||||||
require('./schemas/output/get-paths.json'),
|
require('./schemas/output/get-paths.json'),
|
||||||
require('./schemas/output/get-server-info.json'),
|
require('./schemas/output/get-server-info.json'),
|
||||||
require('./schemas/output/get-settings.json'),
|
require('./schemas/output/get-settings.json'),
|
||||||
@@ -91,8 +92,10 @@ function loadSchemas() {
|
|||||||
require('./schemas/input/prepare-suspended-payment-cancellation.json'),
|
require('./schemas/input/prepare-suspended-payment-cancellation.json'),
|
||||||
require('./schemas/input/prepare-suspended-payment-execution.json'),
|
require('./schemas/input/prepare-suspended-payment-execution.json'),
|
||||||
require('./schemas/input/compute-ledger-hash'),
|
require('./schemas/input/compute-ledger-hash'),
|
||||||
require('./schemas/input/sign'),
|
require('./schemas/input/sign.json'),
|
||||||
require('./schemas/input/submit')
|
require('./schemas/input/submit.json'),
|
||||||
|
require('./schemas/input/generate-address.json'),
|
||||||
|
require('./schemas/input/combine.json')
|
||||||
];
|
];
|
||||||
const titles = _.map(schemas, schema => schema.title);
|
const titles = _.map(schemas, schema => schema.title);
|
||||||
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
|
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
|
||||||
|
|||||||
@@ -3,21 +3,57 @@
|
|||||||
"title": "api-options",
|
"title": "api-options",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"trace": {"type": "boolean"},
|
"trace": {
|
||||||
"feeCushion": {"$ref": "value"},
|
"type": "boolean",
|
||||||
"servers": {
|
"description": "If true, log rippled requests and responses to stdout."
|
||||||
"type": "array",
|
},
|
||||||
"items": {
|
"feeCushion": {
|
||||||
"type": "string",
|
"type": "number",
|
||||||
"format": "uri",
|
"minimum": 1,
|
||||||
"pattern": "^wss?://"
|
"description": "Factor to multiply estimated fee by to provide a cushion in case the required fee rises during submission of a transaction. Defaults to `1.2`."
|
||||||
}
|
},
|
||||||
|
"server": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "URI for rippled websocket port to connect to. Must start with `wss://` or `ws://`.",
|
||||||
|
"format": "uri",
|
||||||
|
"pattern": "^wss?://"
|
||||||
},
|
},
|
||||||
"proxy": {
|
"proxy": {
|
||||||
"format": "uri"
|
"format": "uri",
|
||||||
|
"description": "URI for HTTP/HTTPS proxy to use to connect to the rippled server."
|
||||||
|
},
|
||||||
|
"timeout": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Timeout in milliseconds before considering a request to have failed.",
|
||||||
|
"minimum": 1
|
||||||
|
},
|
||||||
|
"proxyAuthorization": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Username and password for HTTP basic authentication to the proxy in the format **username:password**."
|
||||||
},
|
},
|
||||||
"authorization": {
|
"authorization": {
|
||||||
"type": "string"
|
"type": "string",
|
||||||
|
"description": "Username and password for HTTP basic authentication to the rippled server in the format **username:password**."
|
||||||
|
},
|
||||||
|
"trustedCertificates": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Array of PEM-formatted SSL certificates to trust when connecting to a proxy. This is useful if you want to use a self-signed certificate on the proxy server. Note: Each element must contain a single certificate; concatenated certificates are not valid.",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A PEM-formatted SSL certificate to trust when connecting to a proxy."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"key": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A string containing the private key of the client in PEM format. (Can be an array of keys)."
|
||||||
|
},
|
||||||
|
"passphrase": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The passphrase for the private key of the client."
|
||||||
|
},
|
||||||
|
"certificate": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A string containing the certificate key of the client in PEM format. (Can be an array of certificates)."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
19
src/common/schemas/input/combine.json
Normal file
19
src/common/schemas/input/combine.json
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"title": "combineParameters",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"signedTransactions": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "An array of signed transactions (from the output of [sign](#sign)) to combine.",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[A-F0-9]+$",
|
||||||
|
"description": "A single-signed transaction represented as an uppercase hexadecimal string (from the output of [sign](#sign))"
|
||||||
|
},
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": ["signedTransactions"]
|
||||||
|
}
|
||||||
29
src/common/schemas/input/generate-address.json
Normal file
29
src/common/schemas/input/generate-address.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"title": "generateAddressParameters",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"options": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Options to control how the address and secret are generated.",
|
||||||
|
"properties": {
|
||||||
|
"entropy": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 255
|
||||||
|
},
|
||||||
|
"description": "The entropy to use to generate the seed."
|
||||||
|
},
|
||||||
|
"algorithm": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["ecdsa-secp256k1", "ed25519"],
|
||||||
|
"description": "The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
@@ -7,12 +7,12 @@
|
|||||||
"$ref": "address",
|
"$ref": "address",
|
||||||
"description": "The address of the account that is creating the transaction."
|
"description": "The address of the account that is creating the transaction."
|
||||||
},
|
},
|
||||||
"sequence": {
|
"orderCancellation": {
|
||||||
"$ref": "sequence",
|
"$ref": "orderCancellation",
|
||||||
"description": "The account sequence number of the transaction that created the order to cancel."
|
"description": "The specification of the order cancellation to prepare."
|
||||||
},
|
},
|
||||||
"instructions": {"$ref": "instructions"}
|
"instructions": {"$ref": "instructions"}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": ["address", "sequence"]
|
"required": ["address", "orderCancellation"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,17 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "secret",
|
"format": "secret",
|
||||||
"description": "The secret of the account that is initiating the transaction."
|
"description": "The secret of the account that is initiating the transaction."
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Options that control the type of signature that will be generated.",
|
||||||
|
"properties": {
|
||||||
|
"signAs": {
|
||||||
|
"$ref": "address",
|
||||||
|
"description": "The account that the signature should count for in multisigning."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@@ -18,13 +18,21 @@
|
|||||||
"$ref": "value"
|
"$ref": "value"
|
||||||
},
|
},
|
||||||
"maxLedgerVersion": {
|
"maxLedgerVersion": {
|
||||||
"description": "The highest ledger version that the transaction can be included in.",
|
"description": "The highest ledger version that the transaction can be included in. If this option and `maxLedgerVersionOffset` are both omitted, the `maxLedgerVersion` option will default to 3 greater than the current validated ledger version (equivalent to `maxLedgerVersionOffset=3`). Use `null` to not set a maximum ledger version.",
|
||||||
"$ref": "ledgerVersion"
|
"oneOf": [
|
||||||
|
{"$ref": "ledgerVersion"},
|
||||||
|
{"type": "null"}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"maxLedgerVersionOffset": {
|
"maxLedgerVersionOffset": {
|
||||||
"description": "Offset from current legder version to highest ledger version that the transaction can be included in.",
|
"description": "Offset from current validated legder version to highest ledger version that the transaction can be included in.",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 0
|
"minimum": 0
|
||||||
|
},
|
||||||
|
"signersCount": {
|
||||||
|
"description": "Number of signers that will be signing this transaction.",
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
"title": "memos",
|
"title": "memos",
|
||||||
|
"link": "transaction-memos",
|
||||||
"description": "Array of memos to attach to the transaction.",
|
"description": "Array of memos to attach to the transaction.",
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
|||||||
103
src/common/schemas/objects/settings.json
Normal file
103
src/common/schemas/objects/settings.json
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"title": "settingsPlusMemos",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"passwordSpent": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Indicates that the account has used its free SetRegularKey transaction."
|
||||||
|
},
|
||||||
|
"requireDestinationTag": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Requires incoming payments to specify a destination tag."
|
||||||
|
},
|
||||||
|
"requireAuthorization": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "If set, this account must individually approve other users in order for those users to hold this account’s issuances."
|
||||||
|
},
|
||||||
|
"disallowIncomingXRP": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Indicates that client applications should not send XRP to this account. Not enforced by rippled."
|
||||||
|
},
|
||||||
|
"disableMasterKey": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Disallows use of the master key to sign transactions for this account."
|
||||||
|
},
|
||||||
|
"enableTransactionIDTracking": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Track the ID of this account’s most recent transaction."
|
||||||
|
},
|
||||||
|
"noFreeze": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled."
|
||||||
|
},
|
||||||
|
"globalFreeze": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Freeze all assets issued by this account."
|
||||||
|
},
|
||||||
|
"defaultRipple": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Enable [rippling](https://ripple.com/knowledge_center/understanding-the-noripple-flag/) on this account’s trust lines by default. (New in [rippled 0.27.3](https://github.com/ripple/rippled/releases/tag/0.27.3))"
|
||||||
|
},
|
||||||
|
"emailHash": {
|
||||||
|
"description": "Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.",
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "null"},
|
||||||
|
{"$ref": "hash128"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"messageKey": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple."
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"type": "string",
|
||||||
|
"description": " The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase."
|
||||||
|
},
|
||||||
|
"transferRate": {
|
||||||
|
"description": " The fee to charge when users transfer this account’s issuances, represented as billionths of a unit. Use `null` to set no fee.",
|
||||||
|
"oneOf": [
|
||||||
|
{"type": "null"},
|
||||||
|
{"type": "number", "minimum": 1, "maximum": 4.294967295}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"regularKey": {
|
||||||
|
"oneOf": [
|
||||||
|
{"$ref": "address"},
|
||||||
|
{"type": "null"}
|
||||||
|
],
|
||||||
|
"description": "The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key."
|
||||||
|
},
|
||||||
|
"signers": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Settings that determine what sets of accounts can be used to sign a transaction on behalf of this account using multisigning.",
|
||||||
|
"properties": {
|
||||||
|
"threshold": {
|
||||||
|
"$ref": "uint32",
|
||||||
|
"description": "A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`."
|
||||||
|
},
|
||||||
|
"weights": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Weights of signatures for each signer.",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "An association of an address and a weight.",
|
||||||
|
"properties": {
|
||||||
|
"address": {"$ref": "address"},
|
||||||
|
"weight": {
|
||||||
|
"$ref": "uint32",
|
||||||
|
"description": "The weight that the signature of this account counts as towards the threshold."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["address", "weight"],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"minItems": 1,
|
||||||
|
"maxItems": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"memos": {"$ref": "memos"}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
"title": "tag",
|
"title": "tag",
|
||||||
"description": "An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.",
|
"description": "An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.",
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"$ref": "uint32"
|
"$ref": "uint32"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@
|
|||||||
"description": "The time since the ledger was closed, in seconds."
|
"description": "The time since the ledger was closed, in seconds."
|
||||||
},
|
},
|
||||||
"baseFeeXRP": {
|
"baseFeeXRP": {
|
||||||
"type": "number",
|
"$ref": "value",
|
||||||
"description": "Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005."
|
"description": "Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005."
|
||||||
},
|
},
|
||||||
"hash": {
|
"hash": {
|
||||||
@@ -91,14 +91,12 @@
|
|||||||
"description": "Unique hash for the ledger, as an uppercase hexadecimal string."
|
"description": "Unique hash for the ledger, as an uppercase hexadecimal string."
|
||||||
},
|
},
|
||||||
"reserveBaseXRP": {
|
"reserveBaseXRP": {
|
||||||
"type": "integer",
|
"$ref": "value",
|
||||||
"minimum": 0,
|
"description": "Minimum amount of XRP necessary for every account to keep in reserve."
|
||||||
"description": "Minimum amount of XRP (not drops) necessary for every account to keep in reserve."
|
|
||||||
},
|
},
|
||||||
"reserveIncrementXRP": {
|
"reserveIncrementXRP": {
|
||||||
"type": "integer",
|
"$ref": "value",
|
||||||
"minimum": 0,
|
"description": "Amount of XRP added to the account reserve for each object an account is responsible for in the ledger."
|
||||||
"description": "Amount of XRP (not drops) added to the account reserve for each object an account is responsible for in the ledger."
|
|
||||||
},
|
},
|
||||||
"ledgerVersion": {
|
"ledgerVersion": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
|
|||||||
@@ -1,73 +1,8 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
"title": "getSettings",
|
"title": "getSettings",
|
||||||
"type": "object",
|
"$ref": "settingsPlusMemos",
|
||||||
"properties": {
|
"not": {
|
||||||
"passwordSpent": {
|
"required": ["memos"]
|
||||||
"type": "boolean",
|
}
|
||||||
"description": "Indicates that the account has used its free SetRegularKey transaction."
|
|
||||||
},
|
|
||||||
"requireDestinationTag": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Requires incoming payments to specify a destination tag."
|
|
||||||
},
|
|
||||||
"requireAuthorization": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "If set, this account must individually approve other users in order for those users to hold this account’s issuances."
|
|
||||||
},
|
|
||||||
"disallowIncomingXRP": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Indicates that client applications should not send XRP to this account. Not enforced by rippled."
|
|
||||||
},
|
|
||||||
"disableMasterKey": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Disallows use of the master key to sign transactions for this account."
|
|
||||||
},
|
|
||||||
"enableTransactionIDTracking": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Track the ID of this account’s most recent transaction."
|
|
||||||
},
|
|
||||||
"noFreeze": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled."
|
|
||||||
},
|
|
||||||
"globalFreeze": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Freeze all assets issued by this account."
|
|
||||||
},
|
|
||||||
"defaultRipple": {
|
|
||||||
"type": "boolean",
|
|
||||||
"description": "Enable [rippling](https://ripple.com/knowledge_center/understanding-the-noripple-flag/) on this account’s trust lines by default. (New in [rippled 0.27.3](https://github.com/ripple/rippled/releases/tag/0.27.3))"
|
|
||||||
},
|
|
||||||
"emailHash": {
|
|
||||||
"description": "Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.",
|
|
||||||
"oneOf": [
|
|
||||||
{"type": "null"},
|
|
||||||
{"$ref": "hash128"}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"messageKey": {
|
|
||||||
"type": "string",
|
|
||||||
"description": "Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple."
|
|
||||||
},
|
|
||||||
"domain": {
|
|
||||||
"type": "string",
|
|
||||||
"description": " The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase."
|
|
||||||
},
|
|
||||||
"transferRate": {
|
|
||||||
"description": " The fee to charge when users transfer this account’s issuances, represented as billionths of a unit. Use `null` to set no fee.",
|
|
||||||
"oneOf": [
|
|
||||||
{"type": "null"},
|
|
||||||
{"type": "number", "minimum": 1, "maximum": 4.294967295}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"regularKey": {
|
|
||||||
"oneOf": [
|
|
||||||
{"$ref": "address"},
|
|
||||||
{"type": "null"}
|
|
||||||
],
|
|
||||||
"description": "The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"additionalProperties": false
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"state": {
|
"state": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"balance": {
|
"balance": {
|
||||||
"$ref": "value",
|
"$ref": "signedValue",
|
||||||
"description": "The balance on the trustline, representing which party owes the other and by how much."
|
"description": "The balance on the trustline, representing which party owes the other and by how much."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
{
|
{
|
||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
"title": "ledgerClosed",
|
"title": "ledgerEvent",
|
||||||
"description": "A ledgerClosed event message",
|
"description": "A ledger event message",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"feeBase": {
|
"baseFeeXRP": {
|
||||||
"type": "integer",
|
"$ref": "value",
|
||||||
"minimum": 0,
|
"description": "Base fee, in XRP."
|
||||||
"description": "Base fee, in drops."
|
|
||||||
},
|
|
||||||
"feeReference": {
|
|
||||||
"type": "integer",
|
|
||||||
"minimum": 0,
|
|
||||||
"description": "Cost of the 'reference transaction' in 'fee units'."
|
|
||||||
},
|
},
|
||||||
"ledgerHash": {
|
"ledgerHash": {
|
||||||
"$ref": "hash256",
|
"$ref": "hash256",
|
||||||
@@ -27,14 +21,12 @@
|
|||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"description": "The time at which this ledger closed."
|
"description": "The time at which this ledger closed."
|
||||||
},
|
},
|
||||||
"reserveBase": {
|
"reserveBaseXRP": {
|
||||||
"type": "integer",
|
"$ref": "value",
|
||||||
"minimum": 0,
|
"description": "The minimum reserve, in XRP, that is required for an account."
|
||||||
"description": "The minimum reserve, in drops of XRP, that is required for an account."
|
|
||||||
},
|
},
|
||||||
"reserveIncrement": {
|
"reserveIncrementXRP": {
|
||||||
"type": "integer",
|
"$ref": "value",
|
||||||
"minimum": 0,
|
|
||||||
"description": "The increase in account reserve that is added for each item the account owns, such as offers or trust lines."
|
"description": "The increase in account reserve that is added for each item the account owns, such as offers or trust lines."
|
||||||
},
|
},
|
||||||
"transactionCount": {
|
"transactionCount": {
|
||||||
@@ -48,7 +40,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"addtionalProperties": false,
|
"addtionalProperties": false,
|
||||||
"required": ["feeBase", "feeReference", "ledgerHash", "ledgerTimestamp",
|
"required": ["baseFeeXRP", "ledgerHash", "ledgerTimestamp",
|
||||||
"reserveBase", "reserveIncrement", "transactionCount",
|
"reserveBaseXRP", "reserveIncrementXRP", "transactionCount",
|
||||||
"ledgerVersion", "validatedLedgerVersions"]
|
"ledgerVersion", "validatedLedgerVersions"]
|
||||||
}
|
}
|
||||||
@@ -21,12 +21,15 @@
|
|||||||
"description": "The initiating account's sequence number for this transaction."
|
"description": "The initiating account's sequence number for this transaction."
|
||||||
},
|
},
|
||||||
"maxLedgerVersion": {
|
"maxLedgerVersion": {
|
||||||
"$ref": "ledgerVersion",
|
"oneOf": [
|
||||||
"description": "The highest ledger version that the transaction can be included in."
|
{"$ref": "ledgerVersion"},
|
||||||
|
{"type": "null"}
|
||||||
|
],
|
||||||
|
"description": "The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": ["fee", "sequence"]
|
"required": ["fee", "sequence", "maxLedgerVersion"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
"orderSequence": {
|
"orderSequence": {
|
||||||
"$ref": "sequence",
|
"$ref": "sequence",
|
||||||
"description": "The [account sequence number](#account-sequence-number) of the order to cancel."
|
"description": "The [account sequence number](#account-sequence-number) of the order to cancel."
|
||||||
}
|
},
|
||||||
|
"memos": {"$ref": "memos"}
|
||||||
},
|
},
|
||||||
"required": ["orderSequence"],
|
"required": ["orderSequence"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
@@ -33,7 +33,8 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"description": "Time after which the offer is no longer active, as an [ISO 8601 date-time](https://en.wikipedia.org/wiki/ISO_8601)."
|
"description": "Time after which the offer is no longer active, as an [ISO 8601 date-time](https://en.wikipedia.org/wiki/ISO_8601)."
|
||||||
}
|
},
|
||||||
|
"memos": {"$ref": "memos"}
|
||||||
},
|
},
|
||||||
"required": ["direction", "quantity", "totalPrice"],
|
"required": ["direction", "quantity", "totalPrice"],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
|
|||||||
@@ -2,11 +2,17 @@
|
|||||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
"title": "settings",
|
"title": "settings",
|
||||||
"link": "settings",
|
"link": "settings",
|
||||||
"allOf": [
|
"$ref": "settingsPlusMemos",
|
||||||
|
"oneOf": [
|
||||||
{
|
{
|
||||||
"$ref": "getSettings"
|
"required": ["memos"],
|
||||||
|
"minProperties": 2,
|
||||||
|
"maxProperties": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"not": {
|
||||||
|
"required": ["memos"]
|
||||||
|
},
|
||||||
"minProperties": 1,
|
"minProperties": 1,
|
||||||
"maxProperties": 1
|
"maxProperties": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
"frozen": {
|
"frozen": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "If true, the trustline is frozen, which means that funds can only be sent to the owner."
|
"description": "If true, the trustline is frozen, which means that funds can only be sent to the owner."
|
||||||
}
|
},
|
||||||
|
"memos": {"$ref": "memos"}
|
||||||
},
|
},
|
||||||
"required": ["currency", "counterparty", "limit"],
|
"required": ["currency", "counterparty", "limit"],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type {Connection} from './connection';
|
|||||||
export type GetServerInfoResponse = {
|
export type GetServerInfoResponse = {
|
||||||
buildVersion: string,
|
buildVersion: string,
|
||||||
completeLedgers: string,
|
completeLedgers: string,
|
||||||
hostid: string,
|
hostID: string,
|
||||||
ioLatencyMs: number,
|
ioLatencyMs: number,
|
||||||
load?: {
|
load?: {
|
||||||
jobTypes: Array<Object>,
|
jobTypes: Array<Object>,
|
||||||
@@ -23,11 +23,11 @@ export type GetServerInfoResponse = {
|
|||||||
serverState: string,
|
serverState: string,
|
||||||
validatedLedger: {
|
validatedLedger: {
|
||||||
age: number,
|
age: number,
|
||||||
baseFeeXrp: number,
|
baseFeeXRP: string,
|
||||||
hash: string,
|
hash: string,
|
||||||
reserveBaseXrp: number,
|
reserveBaseXRP: string,
|
||||||
reserveIncXrp: number,
|
reserveIncrementXRP: string,
|
||||||
seq: number
|
ledgerVersion: number
|
||||||
},
|
},
|
||||||
validationQuorum: number
|
validationQuorum: number
|
||||||
}
|
}
|
||||||
@@ -49,6 +49,12 @@ function getServerInfo(connection: Connection): Promise<GetServerInfoResponse> {
|
|||||||
reserveIncXrp: 'reserveIncrementXRP',
|
reserveIncXrp: 'reserveIncrementXRP',
|
||||||
seq: 'ledgerVersion'
|
seq: 'ledgerVersion'
|
||||||
});
|
});
|
||||||
|
info.validatedLedger.baseFeeXRP =
|
||||||
|
info.validatedLedger.baseFeeXRP.toString();
|
||||||
|
info.validatedLedger.reserveBaseXRP =
|
||||||
|
info.validatedLedger.reserveBaseXRP.toString();
|
||||||
|
info.validatedLedger.reserveIncrementXRP =
|
||||||
|
info.validatedLedger.reserveIncrementXRP.toString();
|
||||||
return info;
|
return info;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
export type RippledAmountIOU = {
|
export type RippledAmountIOU = {
|
||||||
currency: string,
|
currency: string,
|
||||||
value: string,
|
value: string,
|
||||||
issuer?: string
|
issuer?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RippledAmount = string | RippledAmountIOU
|
export type RippledAmount = string | RippledAmountIOU
|
||||||
|
|
||||||
|
|
||||||
export type Amount = {
|
export type Amount = {
|
||||||
value: string,
|
value: string,
|
||||||
currency: string,
|
currency: string,
|
||||||
counterparty?: string
|
counterparty?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Amount where counterparty and value are optional
|
// Amount where counterparty and value are optional
|
||||||
export type LaxLaxAmount = {
|
export type LaxLaxAmount = {
|
||||||
currency: string,
|
currency: string,
|
||||||
value?: string,
|
value?: string,
|
||||||
counterparty?: string
|
counterparty?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// A currency-counterparty pair, or just currency if it's XRP
|
// A currency-counterparty pair, or just currency if it's XRP
|
||||||
export type Issue = {
|
export type Issue = {
|
||||||
currency: string,
|
currency: string,
|
||||||
counterparty?: string
|
counterparty?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Adjustment = {
|
export type Adjustment = {
|
||||||
address: string,
|
address: string,
|
||||||
amount: Amount,
|
amount: Amount,
|
||||||
tag?: number
|
tag?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MaxAdjustment = {
|
export type MaxAdjustment = {
|
||||||
address: string,
|
address: string,
|
||||||
maxAmount: Amount,
|
maxAmount: Amount,
|
||||||
tag?: number
|
tag?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MinAdjustment = {
|
export type MinAdjustment = {
|
||||||
address: string,
|
address: string,
|
||||||
minAmount: Amount,
|
minAmount: Amount,
|
||||||
tag?: number
|
tag?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Memo = {
|
export type Memo = {
|
||||||
type?: string,
|
type?: string,
|
||||||
format?: string,
|
format?: string,
|
||||||
data?: string
|
data?: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const BigNumber = require('bignumber.js');
|
const BigNumber = require('bignumber.js');
|
||||||
const errors = require('./errors');
|
|
||||||
const keypairs = require('ripple-keypairs');
|
|
||||||
const {deriveKeypair} = require('ripple-keypairs');
|
const {deriveKeypair} = require('ripple-keypairs');
|
||||||
|
|
||||||
import type {Amount, RippledAmount} from './types.js';
|
import type {Amount, RippledAmount} from './types.js';
|
||||||
@@ -37,27 +35,13 @@ function toRippledAmount(amount: Amount): RippledAmount {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateAddress(options?: Object): Object {
|
|
||||||
const secret = keypairs.generateSeed(options);
|
|
||||||
const keypair = keypairs.deriveKeypair(secret);
|
|
||||||
const address = keypairs.deriveAddress(keypair.publicKey);
|
|
||||||
return {secret, address};
|
|
||||||
}
|
|
||||||
|
|
||||||
function generateAddressAPI(options?: Object): Object {
|
|
||||||
try {
|
|
||||||
return generateAddress(options);
|
|
||||||
} catch (error) {
|
|
||||||
throw new errors.UnexpectedError(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
|
|
||||||
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
|
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
|
||||||
if (typeof obj === 'object') {
|
if (typeof obj === 'object') {
|
||||||
let newKey;
|
let newKey;
|
||||||
return _.reduce(obj, (result, value, key) => {
|
return _.reduce(obj, (result, value, key) => {
|
||||||
newKey = key;
|
newKey = key;
|
||||||
|
// taking this out of function leads to error in PhantomJS
|
||||||
|
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
|
||||||
if (FINDSNAKE.test(key)) {
|
if (FINDSNAKE.test(key)) {
|
||||||
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase());
|
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase());
|
||||||
}
|
}
|
||||||
@@ -101,8 +85,6 @@ module.exports = {
|
|||||||
dropsToXrp,
|
dropsToXrp,
|
||||||
xrpToDrops,
|
xrpToDrops,
|
||||||
toRippledAmount,
|
toRippledAmount,
|
||||||
generateAddress,
|
|
||||||
generateAddressAPI,
|
|
||||||
convertKeysFromSnakeCaseToCamelCase,
|
convertKeysFromSnakeCaseToCamelCase,
|
||||||
removeUndefined,
|
removeUndefined,
|
||||||
rippleTimeToISO8601,
|
rippleTimeToISO8601,
|
||||||
|
|||||||
@@ -47,8 +47,10 @@ module.exports = {
|
|||||||
prepareSuspendedPaymentExecution: _.partial(schemaValidate,
|
prepareSuspendedPaymentExecution: _.partial(schemaValidate,
|
||||||
'prepareSuspendedPaymentExecutionParameters'),
|
'prepareSuspendedPaymentExecutionParameters'),
|
||||||
sign: _.partial(schemaValidate, 'signParameters'),
|
sign: _.partial(schemaValidate, 'signParameters'),
|
||||||
|
combine: _.partial(schemaValidate, 'combineParameters'),
|
||||||
submit: _.partial(schemaValidate, 'submitParameters'),
|
submit: _.partial(schemaValidate, 'submitParameters'),
|
||||||
computeLedgerHash: _.partial(schemaValidate, 'computeLedgerHashParameters'),
|
computeLedgerHash: _.partial(schemaValidate, 'computeLedgerHashParameters'),
|
||||||
|
generateAddress: _.partial(schemaValidate, 'generateAddressParameters'),
|
||||||
apiOptions: _.partial(schemaValidate, 'api-options'),
|
apiOptions: _.partial(schemaValidate, 'api-options'),
|
||||||
instructions: _.partial(schemaValidate, 'instructions')
|
instructions: _.partial(schemaValidate, 'instructions')
|
||||||
};
|
};
|
||||||
|
|||||||
59
src/common/wswrapper.js
Normal file
59
src/common/wswrapper.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const {EventEmitter} = require('events');
|
||||||
|
|
||||||
|
function unsused() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides `EventEmitter` interface for native browser `WebSocket`,
|
||||||
|
* same, as `ws` package provides.
|
||||||
|
*/
|
||||||
|
class WSWrapper extends EventEmitter {
|
||||||
|
constructor(url, protocols = null, websocketOptions = {}) {
|
||||||
|
super();
|
||||||
|
unsused(protocols);
|
||||||
|
unsused(websocketOptions);
|
||||||
|
this.setMaxListeners(Infinity);
|
||||||
|
|
||||||
|
this._ws = new WebSocket(url);
|
||||||
|
|
||||||
|
this._ws.onclose = () => {
|
||||||
|
this.emit('close');
|
||||||
|
};
|
||||||
|
|
||||||
|
this._ws.onopen = () => {
|
||||||
|
this.emit('open');
|
||||||
|
};
|
||||||
|
|
||||||
|
this._ws.onerror = error => {
|
||||||
|
this.emit('error', error);
|
||||||
|
};
|
||||||
|
|
||||||
|
this._ws.onmessage = message => {
|
||||||
|
this.emit('message', message.data);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
if (this.readyState === 1) {
|
||||||
|
this._ws.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
send(message) {
|
||||||
|
this._ws.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
get readyState() {
|
||||||
|
return this._ws.readyState;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
WSWrapper.CONNECTING = 0;
|
||||||
|
WSWrapper.OPEN = 1;
|
||||||
|
WSWrapper.CLOSING = 2;
|
||||||
|
WSWrapper.CLOSED = 3;
|
||||||
|
|
||||||
|
module.exports = WSWrapper;
|
||||||
|
|
||||||
86
src/http.js
Normal file
86
src/http.js
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
/* eslint-disable new-cap */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const jayson = require('jayson');
|
||||||
|
|
||||||
|
const RippleAPI = require('./api').RippleAPI;
|
||||||
|
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
function createHTTPServer(options, httpPort) {
|
||||||
|
const rippleAPI = new RippleAPI(options);
|
||||||
|
|
||||||
|
const methodNames = _.filter(_.keys(RippleAPI.prototype), k => {
|
||||||
|
return typeof RippleAPI.prototype[k] === 'function'
|
||||||
|
&& k !== 'connect'
|
||||||
|
&& k !== 'disconnect'
|
||||||
|
&& k !== 'constructor'
|
||||||
|
&& k !== 'RippleAPI';
|
||||||
|
});
|
||||||
|
|
||||||
|
function applyPromiseWithCallback(fnName, callback, args_) {
|
||||||
|
try {
|
||||||
|
let args = args_;
|
||||||
|
if (!_.isArray(args_)) {
|
||||||
|
const fnParameters = jayson.Utils.getParameterNames(rippleAPI[fnName]);
|
||||||
|
args = fnParameters.map(name => args_[name]);
|
||||||
|
const defaultArgs = _.omit(args_, fnParameters);
|
||||||
|
assert(_.size(defaultArgs) <= 1,
|
||||||
|
'Function must have no more than one default argument');
|
||||||
|
if (_.size(defaultArgs) > 0) {
|
||||||
|
args.push(defaultArgs[_.keys(defaultArgs)[0]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Promise.resolve(rippleAPI[fnName].apply(rippleAPI, args))
|
||||||
|
.then(res => callback(null, res))
|
||||||
|
.catch(err => {
|
||||||
|
callback({code: 99, message: err.message, data: {name: err.name}});
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
callback({code: 99, message: err.message, data: {name: err.name}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const methods = {};
|
||||||
|
_.forEach(methodNames, fn => {
|
||||||
|
methods[fn] = jayson.Method((args, cb) => {
|
||||||
|
applyPromiseWithCallback(fn, cb, args);
|
||||||
|
}, {collect: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = jayson.server(methods);
|
||||||
|
let httpServer = null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
server: server,
|
||||||
|
start: function() {
|
||||||
|
if (httpServer !== null) {
|
||||||
|
return Promise.reject('Already started');
|
||||||
|
}
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
rippleAPI.connect().then(() => {
|
||||||
|
httpServer = server.http();
|
||||||
|
httpServer.listen(httpPort, resolve);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
stop: function() {
|
||||||
|
if (httpServer === null) {
|
||||||
|
return Promise.reject('Not started');
|
||||||
|
}
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
rippleAPI.disconnect();
|
||||||
|
httpServer.close(() => {
|
||||||
|
httpServer = null;
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
createHTTPServer
|
||||||
|
};
|
||||||
148
src/index.js
148
src/index.js
@@ -1,145 +1,9 @@
|
|||||||
/* @flow */
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/* eslint-disable max-len */
|
module.exports = {
|
||||||
// Enable core-js polyfills. This allows use of ES6/7 extensions listed here:
|
RippleAPI: require('./api').RippleAPI,
|
||||||
// https://github.com/zloirock/core-js/blob/fb0890f32dabe8d4d88a4350d1b268446127132e/shim.js#L1-L103
|
// Broadcast api is experimental
|
||||||
/* eslint-enable max-len */
|
RippleAPIBroadcast: require('./broadcast').RippleAPIBroadcast,
|
||||||
|
// HTTP server is experimental
|
||||||
// In node.js env, polyfill might be already loaded (from any npm package),
|
createHTTPServer: require('./http').createHTTPServer
|
||||||
// that's why we do this check.
|
|
||||||
if (!global._babelPolyfill) {
|
|
||||||
require('babel-core/polyfill');
|
|
||||||
}
|
|
||||||
|
|
||||||
const _ = require('lodash');
|
|
||||||
const EventEmitter = require('events').EventEmitter;
|
|
||||||
const common = require('./common');
|
|
||||||
const server = require('./server/server');
|
|
||||||
const connect = server.connect;
|
|
||||||
const disconnect = server.disconnect;
|
|
||||||
const getServerInfo = server.getServerInfo;
|
|
||||||
const getFee = server.getFee;
|
|
||||||
const isConnected = server.isConnected;
|
|
||||||
const getLedgerVersion = server.getLedgerVersion;
|
|
||||||
const getTransaction = require('./ledger/transaction');
|
|
||||||
const getTransactions = require('./ledger/transactions');
|
|
||||||
const getTrustlines = require('./ledger/trustlines');
|
|
||||||
const getBalances = require('./ledger/balances');
|
|
||||||
const getBalanceSheet = require('./ledger/balance-sheet');
|
|
||||||
const getPaths = require('./ledger/pathfind');
|
|
||||||
const getOrders = require('./ledger/orders');
|
|
||||||
const getOrderbook = require('./ledger/orderbook');
|
|
||||||
const getSettings = require('./ledger/settings');
|
|
||||||
const getAccountInfo = require('./ledger/accountinfo');
|
|
||||||
const preparePayment = require('./transaction/payment');
|
|
||||||
const prepareTrustline = require('./transaction/trustline');
|
|
||||||
const prepareOrder = require('./transaction/order');
|
|
||||||
const prepareOrderCancellation = require('./transaction/ordercancellation');
|
|
||||||
const 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');
|
|
||||||
const errors = require('./common').errors;
|
|
||||||
const generateAddress = common.generateAddressAPI;
|
|
||||||
const computeLedgerHash = require('./offline/ledgerhash');
|
|
||||||
const getLedger = require('./ledger/ledger');
|
|
||||||
|
|
||||||
type APIOptions = {
|
|
||||||
servers?: Array<string>,
|
|
||||||
feeCushion?: number,
|
|
||||||
trace?: boolean,
|
|
||||||
proxy?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent access to non-validated ledger versions
|
|
||||||
class RestrictedConnection extends common.Connection {
|
|
||||||
request(request, timeout) {
|
|
||||||
const ledger_index = request.ledger_index;
|
|
||||||
if (ledger_index !== undefined && ledger_index !== 'validated') {
|
|
||||||
if (!_.isNumber(ledger_index) || ledger_index > this._ledgerVersion) {
|
|
||||||
return Promise.reject(new errors.LedgerVersionError(
|
|
||||||
`ledgerVersion ${ledger_index} is greater than server\'s ` +
|
|
||||||
`most recent validated ledger: ${this._ledgerVersion}`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.request(request, timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class RippleAPI extends EventEmitter {
|
|
||||||
constructor(options: APIOptions = {}) {
|
|
||||||
common.validate.apiOptions(options);
|
|
||||||
super();
|
|
||||||
this._feeCushion = options.feeCushion || 1.2;
|
|
||||||
if (options.servers !== undefined) {
|
|
||||||
const servers: Array<string> = options.servers;
|
|
||||||
if (servers.length === 1) {
|
|
||||||
this.connection = new RestrictedConnection(servers[0], options);
|
|
||||||
this.connection.on('ledgerClosed', message => {
|
|
||||||
this.emit('ledgerClosed', server.formatLedgerClose(message));
|
|
||||||
});
|
|
||||||
this.connection.on('error', (type, info) => {
|
|
||||||
this.emit('error', type, info);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new errors.RippleError('Multi-server not implemented');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// use null object pattern to provide better error message if user
|
|
||||||
// tries to call a method that requires a connection
|
|
||||||
this.connection = new RestrictedConnection(null, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_.assign(RippleAPI.prototype, {
|
|
||||||
connect,
|
|
||||||
disconnect,
|
|
||||||
isConnected,
|
|
||||||
getServerInfo,
|
|
||||||
getFee,
|
|
||||||
getLedgerVersion,
|
|
||||||
|
|
||||||
getTransaction,
|
|
||||||
getTransactions,
|
|
||||||
getTrustlines,
|
|
||||||
getBalances,
|
|
||||||
getBalanceSheet,
|
|
||||||
getPaths,
|
|
||||||
getOrders,
|
|
||||||
getOrderbook,
|
|
||||||
getSettings,
|
|
||||||
getAccountInfo,
|
|
||||||
getLedger,
|
|
||||||
|
|
||||||
preparePayment,
|
|
||||||
prepareTrustline,
|
|
||||||
prepareOrder,
|
|
||||||
prepareOrderCancellation,
|
|
||||||
prepareSuspendedPaymentCreation,
|
|
||||||
prepareSuspendedPaymentExecution,
|
|
||||||
prepareSuspendedPaymentCancellation,
|
|
||||||
prepareSettings,
|
|
||||||
sign,
|
|
||||||
submit,
|
|
||||||
|
|
||||||
generateAddress,
|
|
||||||
computeLedgerHash,
|
|
||||||
errors
|
|
||||||
});
|
|
||||||
|
|
||||||
// these are exposed only for use by unit tests; they are not part of the API
|
|
||||||
RippleAPI._PRIVATE = {
|
|
||||||
validate: common.validate,
|
|
||||||
RangeSet: require('./common/rangeset').RangeSet,
|
|
||||||
ledgerUtils: require('./ledger/utils'),
|
|
||||||
schemaValidator: require('./common/schema-validator')
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.RippleAPI = RippleAPI;
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ function parseAccountTrustline(trustline: Trustline): AccountTrustline {
|
|||||||
limit: trustline.limit,
|
limit: trustline.limit,
|
||||||
currency: trustline.currency,
|
currency: trustline.currency,
|
||||||
counterparty: trustline.account,
|
counterparty: trustline.account,
|
||||||
qualityIn: trustline.quality_in || undefined,
|
qualityIn: utils.parseQuality(trustline.quality_in) || undefined,
|
||||||
qualityOut: trustline.quality_out || undefined,
|
qualityOut: utils.parseQuality(trustline.quality_out) || undefined,
|
||||||
ripplingDisabled: trustline.no_ripple || undefined,
|
ripplingDisabled: trustline.no_ripple || undefined,
|
||||||
frozen: trustline.freeze || undefined,
|
frozen: trustline.freeze || undefined,
|
||||||
authorized: trustline.authorized || undefined
|
authorized: trustline.authorized || undefined
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
const BigNumber = require('bignumber.js');
|
const BigNumber = require('bignumber.js');
|
||||||
const AccountFields = require('./utils').constants.AccountFields;
|
const AccountFields = require('./utils').constants.AccountFields;
|
||||||
|
|
||||||
@@ -22,6 +23,26 @@ function parseFields(data: Object): Object {
|
|||||||
settings[info.name] = parseField(info, fieldValue);
|
settings[info.name] = parseField(info, fieldValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.RegularKey) {
|
||||||
|
settings.regularKey = data.RegularKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this isn't implemented in rippled yet, may have to change this later
|
||||||
|
if (data.SignerQuorum || data.SignerEntries) {
|
||||||
|
settings.signers = {};
|
||||||
|
if (data.SignerQuorum) {
|
||||||
|
settings.signers.threshold = data.SignerQuorum;
|
||||||
|
}
|
||||||
|
if (data.SignerEntries) {
|
||||||
|
settings.signers.weights = _.map(data.SignerEntries, entry => {
|
||||||
|
return {
|
||||||
|
address: entry.SignerEntry.Account,
|
||||||
|
weight: entry.SignerEntry.SignerWeight
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ function parseFlags(tx: Object) {
|
|||||||
|
|
||||||
function parseSettings(tx: Object) {
|
function parseSettings(tx: Object) {
|
||||||
const txType = tx.TransactionType;
|
const txType = tx.TransactionType;
|
||||||
assert(txType === 'AccountSet' || txType === 'SetRegularKey');
|
assert(txType === 'AccountSet' || txType === 'SetRegularKey' ||
|
||||||
|
txType === 'SignerListSet');
|
||||||
|
|
||||||
const regularKey = tx.RegularKey ? {regularKey: tx.RegularKey} : {};
|
return _.assign({}, parseFlags(tx), parseFields(tx));
|
||||||
return _.assign(regularKey, parseFlags(tx), parseFields(tx));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = parseSettings;
|
module.exports = parseSettings;
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ function parseTransactionType(type) {
|
|||||||
SetRegularKey: 'settings',
|
SetRegularKey: 'settings',
|
||||||
SuspendedPaymentCreate: 'suspendedPaymentCreation',
|
SuspendedPaymentCreate: 'suspendedPaymentCreation',
|
||||||
SuspendedPaymentFinish: 'suspendedPaymentExecution',
|
SuspendedPaymentFinish: 'suspendedPaymentExecution',
|
||||||
SuspendedPaymentCancel: 'suspendedPaymentCancellation'
|
SuspendedPaymentCancel: 'suspendedPaymentCancellation',
|
||||||
|
SignerListSet: 'settings'
|
||||||
};
|
};
|
||||||
return mapping[type] || null;
|
return mapping[type] || null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const flags = utils.txFlags.TrustSet;
|
const flags = utils.txFlags.TrustSet;
|
||||||
const BigNumber = require('bignumber.js');
|
|
||||||
|
|
||||||
function parseFlag(flagsValue, trueValue, falseValue) {
|
function parseFlag(flagsValue, trueValue, falseValue) {
|
||||||
if (flagsValue & trueValue) {
|
if (flagsValue & trueValue) {
|
||||||
@@ -15,13 +14,6 @@ function parseFlag(flagsValue, trueValue, falseValue) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseQuality(quality?: number) {
|
|
||||||
if (typeof quality === 'number') {
|
|
||||||
return (new BigNumber(quality)).shift(-9).toNumber();
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseTrustline(tx: Object): Object {
|
function parseTrustline(tx: Object): Object {
|
||||||
assert(tx.TransactionType === 'TrustSet');
|
assert(tx.TransactionType === 'TrustSet');
|
||||||
|
|
||||||
@@ -29,8 +21,8 @@ function parseTrustline(tx: Object): Object {
|
|||||||
limit: tx.LimitAmount.value,
|
limit: tx.LimitAmount.value,
|
||||||
currency: tx.LimitAmount.currency,
|
currency: tx.LimitAmount.currency,
|
||||||
counterparty: tx.LimitAmount.issuer,
|
counterparty: tx.LimitAmount.issuer,
|
||||||
qualityIn: parseQuality(tx.QualityIn),
|
qualityIn: utils.parseQuality(tx.QualityIn),
|
||||||
qualityOut: parseQuality(tx.QualityOut),
|
qualityOut: utils.parseQuality(tx.QualityOut),
|
||||||
ripplingDisabled: parseFlag(
|
ripplingDisabled: parseFlag(
|
||||||
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
|
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
|
||||||
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
|
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
|
||||||
|
|||||||
@@ -17,6 +17,13 @@ function adjustQualityForXRP(
|
|||||||
(new BigNumber(quality)).shift(shift).toString();
|
(new BigNumber(quality)).shift(shift).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseQuality(quality: ?number) {
|
||||||
|
if (typeof quality === 'number') {
|
||||||
|
return (new BigNumber(quality)).shift(-9).toNumber();
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function parseTimestamp(rippleTime: number): string | void {
|
function parseTimestamp(rippleTime: number): string | void {
|
||||||
return rippleTime ? utils.common.rippleTimeToISO8601(rippleTime) : undefined;
|
return rippleTime ? utils.common.rippleTimeToISO8601(rippleTime) : undefined;
|
||||||
}
|
}
|
||||||
@@ -80,6 +87,7 @@ function parseMemos(tx: Object): ?Array<Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
parseQuality,
|
||||||
parseOutcome,
|
parseOutcome,
|
||||||
parseMemos,
|
parseMemos,
|
||||||
hexToString,
|
hexToString,
|
||||||
|
|||||||
@@ -85,6 +85,20 @@ function conditionallyAddDirectXRPPath(connection: Connection, address: string,
|
|||||||
xrpBalance => addDirectXrpPath(paths, xrpBalance));
|
xrpBalance => addDirectXrpPath(paths, xrpBalance));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function filterSourceFundsLowPaths(pathfind: PathFind,
|
||||||
|
paths: RippledPathsResponse
|
||||||
|
): RippledPathsResponse {
|
||||||
|
if (pathfind.source.amount &&
|
||||||
|
pathfind.destination.amount.value === undefined && paths.alternatives) {
|
||||||
|
paths.alternatives = _.filter(paths.alternatives, alt => {
|
||||||
|
return alt.source_amount &&
|
||||||
|
pathfind.source.amount &&
|
||||||
|
alt.source_amount.value === pathfind.source.amount.value;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
|
function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
|
||||||
if (paths.alternatives && paths.alternatives.length > 0) {
|
if (paths.alternatives && paths.alternatives.length > 0) {
|
||||||
return parsePathfind(paths);
|
return parsePathfind(paths);
|
||||||
@@ -116,7 +130,9 @@ function getPaths(pathfind: PathFind): Promise<GetPaths> {
|
|||||||
const address = pathfind.source.address;
|
const address = pathfind.source.address;
|
||||||
return requestPathFind(this.connection, pathfind).then(paths =>
|
return requestPathFind(this.connection, pathfind).then(paths =>
|
||||||
conditionallyAddDirectXRPPath(this.connection, address, paths)
|
conditionallyAddDirectXRPPath(this.connection, address, paths)
|
||||||
).then(paths => formatResponse(pathfind, paths));
|
)
|
||||||
|
.then(paths => filterSourceFundsLowPaths(pathfind, paths))
|
||||||
|
.then(paths => formatResponse(pathfind, paths));
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = getPaths;
|
module.exports = getPaths;
|
||||||
|
|||||||
@@ -1,135 +1,135 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import type {Amount, Memo} from '../common/types.js';
|
import type {Amount, Memo} from '../common/types.js';
|
||||||
|
|
||||||
type Outcome = {
|
type Outcome = {
|
||||||
result: string,
|
result: string,
|
||||||
ledgerVersion: number,
|
ledgerVersion: number,
|
||||||
indexInLedger: number,
|
indexInLedger: number,
|
||||||
fee: string,
|
fee: string,
|
||||||
balanceChanges: {
|
balanceChanges: {
|
||||||
[key: string]: [{
|
[key: string]: [{
|
||||||
currency: string,
|
currency: string,
|
||||||
counterparty?: string,
|
counterparty?: string,
|
||||||
value: string
|
value: string
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
orderbookChanges: Object,
|
orderbookChanges: Object,
|
||||||
timestamp?: string
|
timestamp?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Adjustment = {
|
type Adjustment = {
|
||||||
address: string,
|
address: string,
|
||||||
amount: {
|
amount: {
|
||||||
currency: string,
|
currency: string,
|
||||||
counterparty?: string,
|
counterparty?: string,
|
||||||
value: string
|
value: string
|
||||||
},
|
},
|
||||||
tag?: number
|
tag?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type Trustline = {
|
type Trustline = {
|
||||||
currency: string,
|
currency: string,
|
||||||
counterparty: string,
|
counterparty: string,
|
||||||
limit: string,
|
limit: string,
|
||||||
qualityIn?: number,
|
qualityIn?: number,
|
||||||
qualityOut?: number,
|
qualityOut?: number,
|
||||||
ripplingDisabled?: boolean,
|
ripplingDisabled?: boolean,
|
||||||
authorized?: boolean,
|
authorized?: boolean,
|
||||||
frozen?: boolean
|
frozen?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type Settings = {
|
type Settings = {
|
||||||
passwordSpent?: boolean,
|
passwordSpent?: boolean,
|
||||||
requireDestinationTag?: boolean,
|
requireDestinationTag?: boolean,
|
||||||
requireAuthorization?: boolean,
|
requireAuthorization?: boolean,
|
||||||
disallowIncomingXRP?: boolean,
|
disallowIncomingXRP?: boolean,
|
||||||
disableMasterKey?: boolean,
|
disableMasterKey?: boolean,
|
||||||
enableTransactionIDTracking?: boolean,
|
enableTransactionIDTracking?: boolean,
|
||||||
noFreeze?: boolean,
|
noFreeze?: boolean,
|
||||||
globalFreeze?: boolean,
|
globalFreeze?: boolean,
|
||||||
defaultRipple?: boolean,
|
defaultRipple?: boolean,
|
||||||
emailHash?: string,
|
emailHash?: string,
|
||||||
messageKey?: string,
|
messageKey?: string,
|
||||||
domain?: string,
|
domain?: string,
|
||||||
transferRate?: number,
|
transferRate?: number,
|
||||||
regularKey?: string
|
regularKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderCancellation = {
|
type OrderCancellation = {
|
||||||
orderSequence: number
|
orderSequence: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type Payment = {
|
type Payment = {
|
||||||
source: Adjustment,
|
source: Adjustment,
|
||||||
destination: Adjustment,
|
destination: Adjustment,
|
||||||
paths?: string,
|
paths?: string,
|
||||||
memos?: Array<Memo>,
|
memos?: Array<Memo>,
|
||||||
invoiceID?: string,
|
invoiceID?: string,
|
||||||
allowPartialPayment?: boolean,
|
allowPartialPayment?: boolean,
|
||||||
noDirectRipple?: boolean,
|
noDirectRipple?: boolean,
|
||||||
limitQuality?: boolean
|
limitQuality?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type PaymentTransaction = {
|
type PaymentTransaction = {
|
||||||
type: string,
|
type: string,
|
||||||
specification: Payment,
|
specification: Payment,
|
||||||
outcome: Outcome,
|
outcome: Outcome,
|
||||||
id: string,
|
id: string,
|
||||||
address: string,
|
address: string,
|
||||||
sequence: number
|
sequence: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Order = {
|
export type Order = {
|
||||||
direction: string,
|
direction: string,
|
||||||
quantity: Amount,
|
quantity: Amount,
|
||||||
totalPrice: Amount,
|
totalPrice: Amount,
|
||||||
immediateOrCancel?: boolean,
|
immediateOrCancel?: boolean,
|
||||||
fillOrKill?: boolean,
|
fillOrKill?: boolean,
|
||||||
passive?: boolean
|
passive?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderTransaction = {
|
type OrderTransaction = {
|
||||||
type: string,
|
type: string,
|
||||||
specification: Order,
|
specification: Order,
|
||||||
outcome: Outcome,
|
outcome: Outcome,
|
||||||
id: string,
|
id: string,
|
||||||
address: string,
|
address: string,
|
||||||
sequence: number
|
sequence: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderCancellationTransaction = {
|
type OrderCancellationTransaction = {
|
||||||
type: string,
|
type: string,
|
||||||
specification: OrderCancellation,
|
specification: OrderCancellation,
|
||||||
outcome: Outcome,
|
outcome: Outcome,
|
||||||
id: string,
|
id: string,
|
||||||
address: string,
|
address: string,
|
||||||
sequence: number
|
sequence: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type TrustlineTransaction = {
|
type TrustlineTransaction = {
|
||||||
type: string,
|
type: string,
|
||||||
specification: Trustline,
|
specification: Trustline,
|
||||||
outcome: Outcome,
|
outcome: Outcome,
|
||||||
id: string,
|
id: string,
|
||||||
address: string,
|
address: string,
|
||||||
sequence: number
|
sequence: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type SettingsTransaction = {
|
type SettingsTransaction = {
|
||||||
type: string,
|
type: string,
|
||||||
specification: Settings,
|
specification: Settings,
|
||||||
outcome: Outcome,
|
outcome: Outcome,
|
||||||
id: string,
|
id: string,
|
||||||
address: string,
|
address: string,
|
||||||
sequence: number
|
sequence: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TransactionOptions = {
|
export type TransactionOptions = {
|
||||||
minLedgerVersion?: number,
|
minLedgerVersion?: number,
|
||||||
maxLedgerVersion?: number
|
maxLedgerVersion?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TransactionType = PaymentTransaction | OrderTransaction |
|
export type TransactionType = PaymentTransaction | OrderTransaction |
|
||||||
OrderCancellationTransaction | TrustlineTransaction | SettingsTransaction
|
OrderCancellationTransaction | TrustlineTransaction | SettingsTransaction
|
||||||
|
|||||||
@@ -47,9 +47,6 @@ function parseAccountTxTransaction(tx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function counterpartyFilter(filters, tx: TransactionType) {
|
function counterpartyFilter(filters, tx: TransactionType) {
|
||||||
if (!filters.counterparty) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (tx.address === filters.counterparty || (
|
if (tx.address === filters.counterparty || (
|
||||||
tx.specification && (
|
tx.specification && (
|
||||||
(tx.specification.destination &&
|
(tx.specification.destination &&
|
||||||
@@ -173,11 +170,11 @@ function getTransactions(address: string, options: TransactionsOptions = {}
|
|||||||
const ledgerVersion = tx.outcome.ledgerVersion;
|
const ledgerVersion = tx.outcome.ledgerVersion;
|
||||||
const bound = options.earliestFirst ?
|
const bound = options.earliestFirst ?
|
||||||
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
|
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
|
||||||
const newOptions = _.assign(defaults, options, {startTx: tx}, bound);
|
const newOptions = _.assign({}, defaults, options, {startTx: tx}, bound);
|
||||||
return getTransactionsInternal(this.connection, address, newOptions);
|
return getTransactionsInternal(this.connection, address, newOptions);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const newOptions = _.assign(defaults, options);
|
const newOptions = _.assign({}, defaults, options);
|
||||||
return getTransactionsInternal(this.connection, address, newOptions);
|
return getTransactionsInternal(this.connection, address, newOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
export type TrustLineSpecification = {
|
export type TrustLineSpecification = {
|
||||||
currency: string,
|
currency: string,
|
||||||
counterparty: string,
|
counterparty: string,
|
||||||
limit: string,
|
limit: string,
|
||||||
qualityIn?: number,
|
qualityIn?: number,
|
||||||
qualityOut?: number,
|
qualityOut?: number,
|
||||||
ripplingDisabled?: boolean,
|
ripplingDisabled?: boolean,
|
||||||
authorized?: boolean,
|
authorized?: boolean,
|
||||||
frozen?: boolean
|
frozen?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Trustline = {
|
export type Trustline = {
|
||||||
specification: TrustLineSpecification,
|
specification: TrustLineSpecification,
|
||||||
counterparty: {
|
counterparty: {
|
||||||
limit: string,
|
limit: string,
|
||||||
ripplingDisabled?: boolean,
|
ripplingDisabled?: boolean,
|
||||||
frozen?: boolean,
|
frozen?: boolean,
|
||||||
authorized?: boolean
|
authorized?: boolean
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
balance: string
|
balance: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TrustlinesOptions = {
|
export type TrustlinesOptions = {
|
||||||
counterparty?: string,
|
counterparty?: string,
|
||||||
currency?: string,
|
currency?: string,
|
||||||
limit?: number,
|
limit?: number,
|
||||||
ledgerVersion?: number
|
ledgerVersion?: number
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import type {Amount} from '../common/types.js';
|
import type {Amount} from '../common/types.js';
|
||||||
|
|
||||||
export type OrdersOptions = {
|
export type OrdersOptions = {
|
||||||
limit?: number,
|
limit?: number,
|
||||||
ledgerVersion?: number
|
ledgerVersion?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OrderSpecification = {
|
export type OrderSpecification = {
|
||||||
direction: string,
|
direction: string,
|
||||||
quantity: Amount,
|
quantity: Amount,
|
||||||
totalPrice: Amount,
|
totalPrice: Amount,
|
||||||
immediateOrCancel?: boolean,
|
immediateOrCancel?: boolean,
|
||||||
fillOrKill?: boolean,
|
fillOrKill?: boolean,
|
||||||
// If enabled, the offer will not consume offers that exactly match it, and
|
// If enabled, the offer will not consume offers that exactly match it, and
|
||||||
// instead becomes an Offer node in the ledger. It will still consume offers
|
// instead becomes an Offer node in the ledger. It will still consume offers
|
||||||
// that cross it.
|
// that cross it.
|
||||||
passive?: boolean
|
passive?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Order = {
|
export type Order = {
|
||||||
specification: OrderSpecification,
|
specification: OrderSpecification,
|
||||||
properties: {
|
properties: {
|
||||||
maker: string,
|
maker: string,
|
||||||
sequence: number,
|
sequence: number,
|
||||||
makerExchangeRate: string
|
makerExchangeRate: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetLedger = {
|
export type GetLedger = {
|
||||||
accepted: boolean,
|
accepted: boolean,
|
||||||
closed: boolean,
|
closed: boolean,
|
||||||
stateHash: string,
|
stateHash: string,
|
||||||
closeTime: number,
|
closeTime: number,
|
||||||
closeTimeResolution: number,
|
closeTimeResolution: number,
|
||||||
closeFlags: number,
|
closeFlags: number,
|
||||||
ledgerHash: string,
|
ledgerHash: string,
|
||||||
ledgerVersion: number,
|
ledgerVersion: number,
|
||||||
parentLedgerHash: string,
|
parentLedgerHash: string,
|
||||||
parentCloseTime: number,
|
parentCloseTime: number,
|
||||||
totalDrops: string,
|
totalDrops: string,
|
||||||
transactionHash: string,
|
transactionHash: string,
|
||||||
transactions?: Array<Object>,
|
transactions?: Array<Object>,
|
||||||
rawTransactions?: string,
|
rawTransactions?: string,
|
||||||
transactionHashes?: Array<string>,
|
transactionHashes?: Array<string>,
|
||||||
rawState?: string,
|
rawState?: string,
|
||||||
stateHashes?: Array<string>
|
stateHashes?: Array<string>
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/offline/generate-address.js
Normal file
24
src/offline/generate-address.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
const keypairs = require('ripple-keypairs');
|
||||||
|
const common = require('../common');
|
||||||
|
const {errors, validate} = common;
|
||||||
|
|
||||||
|
function generateAddress(options?: Object): Object {
|
||||||
|
const secret = keypairs.generateSeed(options);
|
||||||
|
const keypair = keypairs.deriveKeypair(secret);
|
||||||
|
const address = keypairs.deriveAddress(keypair.publicKey);
|
||||||
|
return {secret, address};
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateAddressAPI(options?: Object): Object {
|
||||||
|
validate.generateAddress({options});
|
||||||
|
try {
|
||||||
|
return generateAddress(options);
|
||||||
|
} catch (error) {
|
||||||
|
throw new errors.UnexpectedError(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
generateAddressAPI
|
||||||
|
};
|
||||||
@@ -30,13 +30,12 @@ function getFee(): Promise<number> {
|
|||||||
|
|
||||||
function formatLedgerClose(ledgerClose: Object): Object {
|
function formatLedgerClose(ledgerClose: Object): Object {
|
||||||
return {
|
return {
|
||||||
feeBase: ledgerClose.fee_base,
|
baseFeeXRP: common.dropsToXrp(ledgerClose.fee_base),
|
||||||
feeReference: ledgerClose.fee_ref,
|
|
||||||
ledgerHash: ledgerClose.ledger_hash,
|
ledgerHash: ledgerClose.ledger_hash,
|
||||||
ledgerVersion: ledgerClose.ledger_index,
|
ledgerVersion: ledgerClose.ledger_index,
|
||||||
ledgerTimestamp: common.rippleTimeToISO8601(ledgerClose.ledger_time),
|
ledgerTimestamp: common.rippleTimeToISO8601(ledgerClose.ledger_time),
|
||||||
reserveBase: ledgerClose.reserve_base,
|
reserveBaseXRP: common.dropsToXrp(ledgerClose.reserve_base),
|
||||||
reserveIncrement: ledgerClose.reserve_inc,
|
reserveIncrementXRP: common.dropsToXrp(ledgerClose.reserve_inc),
|
||||||
transactionCount: ledgerClose.txn_count,
|
transactionCount: ledgerClose.txn_count,
|
||||||
validatedLedgerVersions: ledgerClose.validated_ledgers
|
validatedLedgerVersions: ledgerClose.validated_ledgers
|
||||||
};
|
};
|
||||||
|
|||||||
39
src/transaction/combine.js
Normal file
39
src/transaction/combine.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/* @flow */
|
||||||
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
|
const binary = require('ripple-binary-codec');
|
||||||
|
const utils = require('./utils');
|
||||||
|
const BigNumber = require('bignumber.js');
|
||||||
|
const {decodeAddress} = require('ripple-address-codec');
|
||||||
|
const {validate} = utils.common;
|
||||||
|
const {computeBinaryTransactionHash} = require('ripple-hashes');
|
||||||
|
|
||||||
|
function addressToBigNumber(address) {
|
||||||
|
const hex = (new Buffer(decodeAddress(address))).toString('hex');
|
||||||
|
return new BigNumber(hex, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareSigners(a, b) {
|
||||||
|
return addressToBigNumber(a.Signer.Account)
|
||||||
|
.comparedTo(addressToBigNumber(b.Signer.Account));
|
||||||
|
}
|
||||||
|
|
||||||
|
function combine(signedTransactions: Array<string>): Object {
|
||||||
|
validate.combine({signedTransactions});
|
||||||
|
|
||||||
|
const txs = _.map(signedTransactions, binary.decode);
|
||||||
|
const tx = _.omit(txs[0], 'Signers');
|
||||||
|
if (!_.every(txs, _tx => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
|
||||||
|
throw new utils.common.errors.ValidationError(
|
||||||
|
'txJSON is not the same for all signedTransactions');
|
||||||
|
}
|
||||||
|
const unsortedSigners = _.reduce(txs, (accumulator, _tx) =>
|
||||||
|
accumulator.concat(_tx.Signers || []), []);
|
||||||
|
const signers = unsortedSigners.sort(compareSigners);
|
||||||
|
const signedTx = _.assign({}, tx, {Signers: signers});
|
||||||
|
const signedTransaction = binary.encode(signedTx);
|
||||||
|
const id = computeBinaryTransactionHash(signedTransaction);
|
||||||
|
return {signedTransaction, id};
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = combine;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const offerFlags = utils.common.txFlags.OfferCreate;
|
const offerFlags = utils.common.txFlags.OfferCreate;
|
||||||
const {validate, iso8601ToRippleTime} = utils.common;
|
const {validate, iso8601ToRippleTime} = utils.common;
|
||||||
@@ -34,6 +35,9 @@ function createOrderTransaction(account: string, order: Order): Object {
|
|||||||
if (order.expirationTime !== undefined) {
|
if (order.expirationTime !== undefined) {
|
||||||
txJSON.Expiration = iso8601ToRippleTime(order.expirationTime);
|
txJSON.Expiration = iso8601ToRippleTime(order.expirationTime);
|
||||||
}
|
}
|
||||||
|
if (order.memos !== undefined) {
|
||||||
|
txJSON.Memos = _.map(order.memos, utils.convertMemo);
|
||||||
|
}
|
||||||
return txJSON;
|
return txJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const validate = utils.common.validate;
|
const validate = utils.common.validate;
|
||||||
import type {Instructions, Prepare} from './types.js';
|
import type {Instructions, Prepare} from './types.js';
|
||||||
|
|
||||||
function createOrderCancellationTransaction(account: string,
|
function createOrderCancellationTransaction(account: string,
|
||||||
sequence: number
|
orderCancellation: Object
|
||||||
): Object {
|
): Object {
|
||||||
return {
|
const txJSON: Object = {
|
||||||
TransactionType: 'OfferCancel',
|
TransactionType: 'OfferCancel',
|
||||||
Account: account,
|
Account: account,
|
||||||
OfferSequence: sequence
|
OfferSequence: orderCancellation.orderSequence
|
||||||
};
|
};
|
||||||
|
if (orderCancellation.memos !== undefined) {
|
||||||
|
txJSON.Memos = _.map(orderCancellation.memos, utils.convertMemo);
|
||||||
|
}
|
||||||
|
return txJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareOrderCancellation(address: string, sequence: number,
|
function prepareOrderCancellation(address: string, orderCancellation: Object,
|
||||||
instructions: Instructions = {}
|
instructions: Instructions = {}
|
||||||
): Promise<Prepare> {
|
): Promise<Prepare> {
|
||||||
validate.prepareOrderCancellation({address, sequence, instructions});
|
validate.prepareOrderCancellation({address, orderCancellation, instructions});
|
||||||
const txJSON = createOrderCancellationTransaction(address, sequence);
|
const txJSON = createOrderCancellationTransaction(address, orderCancellation);
|
||||||
return utils.prepareTransaction(txJSON, this, instructions);
|
return utils.prepareTransaction(txJSON, this, instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ type Payment = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isXRPToXRPPayment(payment: Payment): boolean {
|
function isXRPToXRPPayment(payment: Payment): boolean {
|
||||||
const sourceCurrency = _.get(payment, 'source.maxAmount.currency');
|
const sourceCurrency = _.get(payment, 'source.maxAmount.currency',
|
||||||
const destinationCurrency = _.get(payment, 'destination.amount.currency');
|
_.get(payment, 'source.amount.currency'));
|
||||||
|
const destinationCurrency = _.get(payment, 'destination.amount.currency',
|
||||||
|
_.get(payment, 'destination.minAmount.currency'));
|
||||||
return sourceCurrency === 'XRP' && destinationCurrency === 'XRP';
|
return sourceCurrency === 'XRP' && destinationCurrency === 'XRP';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +64,7 @@ function createMaximalAmount(amount: Amount): Amount {
|
|||||||
const maxXRPValue = '100000000000';
|
const maxXRPValue = '100000000000';
|
||||||
const maxIOUValue = '9999999999999999e80';
|
const maxIOUValue = '9999999999999999e80';
|
||||||
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue;
|
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue;
|
||||||
return _.assign(amount, {value: maxValue});
|
return _.assign({}, amount, {value: maxValue});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPaymentTransaction(address: string, paymentArgument: Payment
|
function createPaymentTransaction(address: string, paymentArgument: Payment
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
type SettingPasswordSpent = {
|
type SettingPasswordSpent = {
|
||||||
passwordSpent?: boolean,
|
passwordSpent?: boolean,
|
||||||
}
|
}
|
||||||
type SettingRequireDestinationTag = {
|
type SettingRequireDestinationTag = {
|
||||||
requireDestinationTag?: boolean,
|
requireDestinationTag?: boolean,
|
||||||
}
|
}
|
||||||
type SettingRequireAuthorization = {
|
type SettingRequireAuthorization = {
|
||||||
requireAuthorization?: boolean,
|
requireAuthorization?: boolean,
|
||||||
}
|
}
|
||||||
type SettingDisallowIncomingXRP = {
|
type SettingDisallowIncomingXRP = {
|
||||||
disallowIncomingXRP?: boolean,
|
disallowIncomingXRP?: boolean,
|
||||||
}
|
}
|
||||||
type SettingDisableMasterKey = {
|
type SettingDisableMasterKey = {
|
||||||
disableMasterKey?: boolean,
|
disableMasterKey?: boolean,
|
||||||
}
|
}
|
||||||
type SettingEnableTransactionIDTracking = {
|
type SettingEnableTransactionIDTracking = {
|
||||||
enableTransactionIDTracking?: boolean,
|
enableTransactionIDTracking?: boolean,
|
||||||
}
|
}
|
||||||
type SettingNoFreeze = {
|
type SettingNoFreeze = {
|
||||||
noFreeze?: boolean,
|
noFreeze?: boolean,
|
||||||
}
|
}
|
||||||
type SettingGlobalFreeze = {
|
type SettingGlobalFreeze = {
|
||||||
globalFreeze?: boolean,
|
globalFreeze?: boolean,
|
||||||
}
|
}
|
||||||
type SettingDefaultRipple = {
|
type SettingDefaultRipple = {
|
||||||
defaultRipple?: boolean,
|
defaultRipple?: boolean,
|
||||||
}
|
}
|
||||||
type SettingEmailHash = {
|
type SettingEmailHash = {
|
||||||
emailHash?: ?string,
|
emailHash?: ?string,
|
||||||
}
|
}
|
||||||
type SettingMessageKey = {
|
type SettingMessageKey = {
|
||||||
messageKey?: string,
|
messageKey?: string,
|
||||||
}
|
}
|
||||||
type SettingDomain = {
|
type SettingDomain = {
|
||||||
domain?: string,
|
domain?: string,
|
||||||
}
|
}
|
||||||
type SettingTransferRate = {
|
type SettingTransferRate = {
|
||||||
transferRate?: ?number,
|
transferRate?: ?number,
|
||||||
}
|
}
|
||||||
type SettingRegularKey = {
|
type SettingRegularKey = {
|
||||||
regularKey?: string
|
regularKey?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Settings = SettingRegularKey |
|
export type Settings = SettingRegularKey |
|
||||||
SettingTransferRate | SettingDomain | SettingMessageKey |
|
SettingTransferRate | SettingDomain | SettingMessageKey |
|
||||||
SettingEmailHash | SettingDefaultRipple |
|
SettingEmailHash | SettingDefaultRipple |
|
||||||
SettingGlobalFreeze | SettingNoFreeze | SettingEnableTransactionIDTracking |
|
SettingGlobalFreeze | SettingNoFreeze | SettingEnableTransactionIDTracking |
|
||||||
SettingDisableMasterKey | SettingDisallowIncomingXRP |
|
SettingDisableMasterKey | SettingDisallowIncomingXRP |
|
||||||
SettingRequireAuthorization | SettingRequireDestinationTag |
|
SettingRequireAuthorization | SettingRequireDestinationTag |
|
||||||
SettingPasswordSpent
|
SettingPasswordSpent
|
||||||
|
|||||||
@@ -70,7 +70,17 @@ function convertTransferRate(transferRate: number | string): number | string {
|
|||||||
return (new BigNumber(transferRate)).shift(9).toNumber();
|
return (new BigNumber(transferRate)).shift(9).toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSettingsTransaction(account: string, settings: Settings
|
function formatSignerEntry(signer: Object): Object {
|
||||||
|
return {
|
||||||
|
SignerEntry: {
|
||||||
|
Account: signer.address,
|
||||||
|
SignerWeight: signer.weight
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSettingsTransactionWithoutMemos(
|
||||||
|
account: string, settings: Settings
|
||||||
): Object {
|
): Object {
|
||||||
if (settings.regularKey !== undefined) {
|
if (settings.regularKey !== undefined) {
|
||||||
const removeRegularKey = {
|
const removeRegularKey = {
|
||||||
@@ -83,11 +93,21 @@ function createSettingsTransaction(account: string, settings: Settings
|
|||||||
return _.assign({}, removeRegularKey, {RegularKey: settings.regularKey});
|
return _.assign({}, removeRegularKey, {RegularKey: settings.regularKey});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (settings.signers !== undefined) {
|
||||||
|
return {
|
||||||
|
TransactionType: 'SignerListSet',
|
||||||
|
Account: account,
|
||||||
|
SignerQuorum: settings.signers.threshold,
|
||||||
|
SignerEntries: _.map(settings.signers.weights, formatSignerEntry)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const txJSON: Object = {
|
const txJSON: Object = {
|
||||||
TransactionType: 'AccountSet',
|
TransactionType: 'AccountSet',
|
||||||
Account: account
|
Account: account
|
||||||
};
|
};
|
||||||
setTransactionFlags(txJSON, settings);
|
|
||||||
|
setTransactionFlags(txJSON, _.omit(settings, 'memos'));
|
||||||
setTransactionFields(txJSON, settings);
|
setTransactionFields(txJSON, settings);
|
||||||
|
|
||||||
if (txJSON.TransferRate !== undefined) {
|
if (txJSON.TransferRate !== undefined) {
|
||||||
@@ -96,6 +116,15 @@ function createSettingsTransaction(account: string, settings: Settings
|
|||||||
return txJSON;
|
return txJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createSettingsTransaction(account: string, settings: Settings
|
||||||
|
): Object {
|
||||||
|
const txJSON = createSettingsTransactionWithoutMemos(account, settings);
|
||||||
|
if (settings.memos !== undefined) {
|
||||||
|
txJSON.Memos = _.map(settings.memos, utils.convertMemo);
|
||||||
|
}
|
||||||
|
return txJSON;
|
||||||
|
}
|
||||||
|
|
||||||
function prepareSettings(address: string, settings: Settings,
|
function prepareSettings(address: string, settings: Settings,
|
||||||
instructions: Instructions = {}
|
instructions: Instructions = {}
|
||||||
): Promise<Prepare> {
|
): Promise<Prepare> {
|
||||||
|
|||||||
@@ -6,23 +6,38 @@ const binary = require('ripple-binary-codec');
|
|||||||
const {computeBinaryTransactionHash} = require('ripple-hashes');
|
const {computeBinaryTransactionHash} = require('ripple-hashes');
|
||||||
const validate = utils.common.validate;
|
const validate = utils.common.validate;
|
||||||
|
|
||||||
function computeSignature(txJSON, privateKey) {
|
function computeSignature(tx: Object, privateKey: string, signAs: ?string) {
|
||||||
const signingData = binary.encodeForSigning(txJSON);
|
const signingData = signAs ?
|
||||||
|
binary.encodeForMultisigning(tx, signAs) : binary.encodeForSigning(tx);
|
||||||
return keypairs.sign(signingData, privateKey);
|
return keypairs.sign(signingData, privateKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sign(txJSON: string, secret: string
|
function sign(txJSON: string, secret: string, options: Object = {}
|
||||||
): {signedTransaction: string; id: string} {
|
): {signedTransaction: string; id: string} {
|
||||||
validate.sign({txJSON, secret});
|
validate.sign({txJSON, secret});
|
||||||
// we can't validate that the secret matches the account because
|
// we can't validate that the secret matches the account because
|
||||||
// the secret could correspond to the regular key
|
// the secret could correspond to the regular key
|
||||||
|
|
||||||
const tx = JSON.parse(txJSON);
|
const tx = JSON.parse(txJSON);
|
||||||
const keypair = keypairs.deriveKeypair(secret);
|
if (tx.TxnSignature || tx.Signers) {
|
||||||
if (tx.SigningPubKey === undefined) {
|
throw new utils.common.errors.ValidationError(
|
||||||
tx.SigningPubKey = keypair.publicKey;
|
'txJSON must not contain "TxnSignature" or "Signers" properties');
|
||||||
}
|
}
|
||||||
tx.TxnSignature = computeSignature(tx, keypair.privateKey);
|
|
||||||
|
const keypair = keypairs.deriveKeypair(secret);
|
||||||
|
tx.SigningPubKey = options.signAs ? '' : keypair.publicKey;
|
||||||
|
|
||||||
|
if (options.signAs) {
|
||||||
|
const signer = {
|
||||||
|
Account: options.signAs,
|
||||||
|
SigningPubKey: keypair.publicKey,
|
||||||
|
TxnSignature: computeSignature(tx, keypair.privateKey, options.signAs)
|
||||||
|
};
|
||||||
|
tx.Signers = [{Signer: signer}];
|
||||||
|
} else {
|
||||||
|
tx.TxnSignature = computeSignature(tx, keypair.privateKey);
|
||||||
|
}
|
||||||
|
|
||||||
const serialized = binary.encode(tx);
|
const serialized = binary.encode(tx);
|
||||||
return {
|
return {
|
||||||
signedTransaction: serialized,
|
signedTransaction: serialized,
|
||||||
|
|||||||
@@ -3,15 +3,7 @@
|
|||||||
const _ = require('lodash');
|
const _ = require('lodash');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const {validate} = utils.common;
|
const {validate} = utils.common;
|
||||||
|
import type {Submit} from './types.js';
|
||||||
type Submit = {
|
|
||||||
success: boolean,
|
|
||||||
engineResult: string,
|
|
||||||
engineResultCode: number,
|
|
||||||
engineResultMessage?: string,
|
|
||||||
txBlob?: string,
|
|
||||||
txJson?: Object
|
|
||||||
}
|
|
||||||
|
|
||||||
function isImmediateRejection(engineResult: string): boolean {
|
function isImmediateRejection(engineResult: string): boolean {
|
||||||
// note: "tel" errors mean the local server refused to process the
|
// note: "tel" errors mean the local server refused to process the
|
||||||
@@ -23,7 +15,7 @@ function isImmediateRejection(engineResult: string): boolean {
|
|||||||
return _.startsWith(engineResult, 'tem') || _.startsWith(engineResult, 'tej');
|
return _.startsWith(engineResult, 'tem') || _.startsWith(engineResult, 'tej');
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatResponse(response) {
|
function formatSubmitResponse(response) {
|
||||||
const data = {
|
const data = {
|
||||||
resultCode: response.engine_result,
|
resultCode: response.engine_result,
|
||||||
resultMessage: response.engine_result_message
|
resultMessage: response.engine_result_message
|
||||||
@@ -36,11 +28,12 @@ function formatResponse(response) {
|
|||||||
|
|
||||||
function submit(signedTransaction: string): Promise<Submit> {
|
function submit(signedTransaction: string): Promise<Submit> {
|
||||||
validate.submit({signedTransaction});
|
validate.submit({signedTransaction});
|
||||||
|
|
||||||
const request = {
|
const request = {
|
||||||
command: 'submit',
|
command: 'submit',
|
||||||
tx_blob: signedTransaction
|
tx_blob: signedTransaction
|
||||||
};
|
};
|
||||||
return this.connection.request(request).then(formatResponse);
|
return this.connection.request(request).then(formatSubmitResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = submit;
|
module.exports = submit;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
const validate = utils.common.validate;
|
const validate = utils.common.validate;
|
||||||
const trustlineFlags = utils.common.txFlags.TrustSet;
|
const trustlineFlags = utils.common.txFlags.TrustSet;
|
||||||
@@ -8,8 +9,7 @@ import type {Instructions, Prepare} from './types.js';
|
|||||||
import type {TrustLineSpecification} from '../ledger/trustlines-types.js';
|
import type {TrustLineSpecification} from '../ledger/trustlines-types.js';
|
||||||
|
|
||||||
function convertQuality(quality) {
|
function convertQuality(quality) {
|
||||||
return quality === undefined ? undefined :
|
return (new BigNumber(quality)).shift(9).truncated().toNumber();
|
||||||
(new BigNumber(quality)).shift(9).truncated().toNumber();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createTrustlineTransaction(account: string,
|
function createTrustlineTransaction(account: string,
|
||||||
@@ -44,6 +44,9 @@ function createTrustlineTransaction(account: string,
|
|||||||
txJSON.Flags |= trustline.frozen ?
|
txJSON.Flags |= trustline.frozen ?
|
||||||
trustlineFlags.SetFreeze : trustlineFlags.ClearFreeze;
|
trustlineFlags.SetFreeze : trustlineFlags.ClearFreeze;
|
||||||
}
|
}
|
||||||
|
if (trustline.memos !== undefined) {
|
||||||
|
txJSON.Memos = _.map(trustline.memos, utils.convertMemo);
|
||||||
|
}
|
||||||
return txJSON;
|
return txJSON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
/* @flow */
|
/* @flow */
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
export type Instructions = {
|
export type Instructions = {
|
||||||
sequence?: number,
|
sequence?: number,
|
||||||
fee?: string,
|
fee?: string,
|
||||||
maxFee?: string,
|
maxFee?: string,
|
||||||
maxLedgerVersion?: number,
|
maxLedgerVersion?: number,
|
||||||
maxLedgerVersionOffset?: number
|
maxLedgerVersionOffset?: number,
|
||||||
}
|
signersCount?: number
|
||||||
|
}
|
||||||
export type Prepare = {
|
|
||||||
txJSON: string,
|
export type Prepare = {
|
||||||
instructions: {
|
txJSON: string,
|
||||||
fee: string,
|
instructions: {
|
||||||
sequence: number,
|
fee: string,
|
||||||
maxLedgerVersion?: number
|
sequence: number,
|
||||||
}
|
maxLedgerVersion?: number
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Submit = {
|
||||||
|
success: boolean,
|
||||||
|
engineResult: string,
|
||||||
|
engineResultCode: number,
|
||||||
|
engineResultMessage?: string,
|
||||||
|
txBlob?: string,
|
||||||
|
txJson?: Object
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ function formatPrepareResponse(txJSON: Object): Object {
|
|||||||
const instructions = {
|
const instructions = {
|
||||||
fee: common.dropsToXrp(txJSON.Fee),
|
fee: common.dropsToXrp(txJSON.Fee),
|
||||||
sequence: txJSON.Sequence,
|
sequence: txJSON.Sequence,
|
||||||
maxLedgerVersion: txJSON.LastLedgerSequence
|
maxLedgerVersion: txJSON.LastLedgerSequence === undefined ?
|
||||||
|
null : txJSON.LastLedgerSequence
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
txJSON: JSON.stringify(txJSON),
|
txJSON: JSON.stringify(txJSON),
|
||||||
@@ -26,6 +27,10 @@ function setCanonicalFlag(txJSON) {
|
|||||||
txJSON.Flags = txJSON.Flags >>> 0;
|
txJSON.Flags = txJSON.Flags >>> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scaleValue(value, multiplier) {
|
||||||
|
return (new BigNumber(value)).times(multiplier).toString();
|
||||||
|
}
|
||||||
|
|
||||||
function prepareTransaction(txJSON: Object, api: Object,
|
function prepareTransaction(txJSON: Object, api: Object,
|
||||||
instructions: Instructions
|
instructions: Instructions
|
||||||
): Promise<Prepare> {
|
): Promise<Prepare> {
|
||||||
@@ -36,7 +41,9 @@ function prepareTransaction(txJSON: Object, api: Object,
|
|||||||
|
|
||||||
function prepareMaxLedgerVersion(): Promise<Object> {
|
function prepareMaxLedgerVersion(): Promise<Object> {
|
||||||
if (instructions.maxLedgerVersion !== undefined) {
|
if (instructions.maxLedgerVersion !== undefined) {
|
||||||
txJSON.LastLedgerSequence = instructions.maxLedgerVersion;
|
if (instructions.maxLedgerVersion !== null) {
|
||||||
|
txJSON.LastLedgerSequence = instructions.maxLedgerVersion;
|
||||||
|
}
|
||||||
return Promise.resolve(txJSON);
|
return Promise.resolve(txJSON);
|
||||||
}
|
}
|
||||||
const offset = instructions.maxLedgerVersionOffset !== undefined ?
|
const offset = instructions.maxLedgerVersionOffset !== undefined ?
|
||||||
@@ -48,8 +55,10 @@ function prepareTransaction(txJSON: Object, api: Object,
|
|||||||
}
|
}
|
||||||
|
|
||||||
function prepareFee(): Promise<Object> {
|
function prepareFee(): Promise<Object> {
|
||||||
|
const multiplier = instructions.signersCount === undefined ? 1 :
|
||||||
|
instructions.signersCount + 1;
|
||||||
if (instructions.fee !== undefined) {
|
if (instructions.fee !== undefined) {
|
||||||
txJSON.Fee = common.xrpToDrops(instructions.fee);
|
txJSON.Fee = scaleValue(common.xrpToDrops(instructions.fee), multiplier);
|
||||||
return Promise.resolve(txJSON);
|
return Promise.resolve(txJSON);
|
||||||
}
|
}
|
||||||
const cushion = api._feeCushion;
|
const cushion = api._feeCushion;
|
||||||
@@ -57,9 +66,10 @@ function prepareTransaction(txJSON: Object, api: Object,
|
|||||||
const feeDrops = common.xrpToDrops(fee);
|
const feeDrops = common.xrpToDrops(fee);
|
||||||
if (instructions.maxFee !== undefined) {
|
if (instructions.maxFee !== undefined) {
|
||||||
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
|
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
|
||||||
txJSON.Fee = BigNumber.min(feeDrops, maxFeeDrops).toString();
|
const normalFee = BigNumber.min(feeDrops, maxFeeDrops).toString();
|
||||||
|
txJSON.Fee = scaleValue(normalFee, multiplier);
|
||||||
} else {
|
} else {
|
||||||
txJSON.Fee = feeDrops;
|
txJSON.Fee = scaleValue(feeDrops, multiplier);
|
||||||
}
|
}
|
||||||
return txJSON;
|
return txJSON;
|
||||||
});
|
});
|
||||||
|
|||||||
600
test/api-test.js
600
test/api-test.js
@@ -14,6 +14,14 @@ const address = addresses.ACCOUNT;
|
|||||||
const utils = RippleAPI._PRIVATE.ledgerUtils;
|
const utils = RippleAPI._PRIVATE.ledgerUtils;
|
||||||
const ledgerClosed = require('./fixtures/rippled/ledger-close-newer');
|
const ledgerClosed = require('./fixtures/rippled/ledger-close-newer');
|
||||||
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
|
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
|
||||||
|
const binary = require('ripple-binary-codec');
|
||||||
|
assert.options.strict = true;
|
||||||
|
|
||||||
|
// how long before each test case times out
|
||||||
|
const TIMEOUT = process.browser ? 25000 : 10000;
|
||||||
|
|
||||||
|
function unused() {
|
||||||
|
}
|
||||||
|
|
||||||
function closeLedger(connection) {
|
function closeLedger(connection) {
|
||||||
connection._ws.emit('message', JSON.stringify(ledgerClosed));
|
connection._ws.emit('message', JSON.stringify(ledgerClosed));
|
||||||
@@ -31,49 +39,96 @@ function checkResult(expected, schemaName, response) {
|
|||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
describe('RippleAPI', function() {
|
describe('RippleAPI', function() {
|
||||||
|
this.timeout(TIMEOUT);
|
||||||
const instructions = {maxLedgerVersionOffset: 100};
|
const instructions = {maxLedgerVersionOffset: 100};
|
||||||
beforeEach(setupAPI.setup);
|
beforeEach(setupAPI.setup);
|
||||||
afterEach(setupAPI.teardown);
|
afterEach(setupAPI.teardown);
|
||||||
|
|
||||||
it('preparePayment', function() {
|
it('error inspect', function() {
|
||||||
const localInstructions = _.defaults({
|
const error = new this.api.errors.RippleError('mess', {data: 1});
|
||||||
maxFee: '0.000012'
|
assert.strictEqual(error.inspect(), '[RippleError(mess, { data: 1 })]');
|
||||||
}, instructions);
|
|
||||||
return this.api.preparePayment(
|
|
||||||
address, requests.preparePayment, localInstructions).then(
|
|
||||||
_.partial(checkResult, responses.preparePayment.normal, 'prepare'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('preparePayment with all options specified', function() {
|
describe('preparePayment', function() {
|
||||||
return this.api.getLedgerVersion().then((ver) => {
|
|
||||||
const localInstructions = {
|
it('normal', function() {
|
||||||
maxLedgerVersion: ver + 100,
|
const localInstructions = _.defaults({
|
||||||
fee: '0.000012'
|
maxFee: '0.000012'
|
||||||
};
|
}, instructions);
|
||||||
return this.api.preparePayment(
|
return this.api.preparePayment(
|
||||||
address, requests.preparePaymentAllOptions, localInstructions).then(
|
address, requests.preparePayment.normal, localInstructions).then(
|
||||||
_.partial(checkResult, responses.preparePayment.allOptions, 'prepare'));
|
_.partial(checkResult, responses.preparePayment.normal, 'prepare'));
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('preparePayment without counterparty set', function() {
|
it('preparePayment - min amount xrp', function() {
|
||||||
const localInstructions = _.defaults({sequence: 23}, instructions);
|
const localInstructions = _.defaults({
|
||||||
return this.api.preparePayment(
|
maxFee: '0.000012'
|
||||||
address, requests.preparePaymentNoCounterparty, localInstructions).then(
|
}, instructions);
|
||||||
_.partial(checkResult, responses.preparePayment.noCounterparty,
|
return this.api.preparePayment(
|
||||||
'prepare'));
|
address, requests.preparePayment.minAmountXRP, localInstructions).then(
|
||||||
});
|
_.partial(checkResult,
|
||||||
|
responses.preparePayment.minAmountXRP, 'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
it('preparePayment - destination.minAmount', function() {
|
it('preparePayment - min amount xrp2xrp', function() {
|
||||||
return this.api.preparePayment(address, responses.getPaths.sendAll[0],
|
return this.api.preparePayment(
|
||||||
instructions).then(_.partial(checkResult,
|
address, requests.preparePayment.minAmount, instructions).then(
|
||||||
responses.preparePayment.minAmount, 'prepare'));
|
_.partial(checkResult,
|
||||||
|
responses.preparePayment.minAmountXRPXRP, 'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preparePayment - XRP to XRP no partial', function() {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.preparePayment(address, requests.preparePayment.wrongPartial);
|
||||||
|
}, /XRP to XRP payments cannot be partial payments/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preparePayment - address must match payment.source.address', function(
|
||||||
|
) {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.preparePayment(address, requests.preparePayment.wrongAddress);
|
||||||
|
}, /address must match payment.source.address/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preparePayment - wrong amount', function() {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.preparePayment(address, requests.preparePayment.wrongAmount);
|
||||||
|
}, this.api.errors.ValidationError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preparePayment with all options specified', function() {
|
||||||
|
return this.api.getLedgerVersion().then(ver => {
|
||||||
|
const localInstructions = {
|
||||||
|
maxLedgerVersion: ver + 100,
|
||||||
|
fee: '0.000012'
|
||||||
|
};
|
||||||
|
return this.api.preparePayment(
|
||||||
|
address, requests.preparePayment.allOptions, 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.preparePayment.noCounterparty, localInstructions)
|
||||||
|
.then(_.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() {
|
it('prepareOrder - buy order', function() {
|
||||||
const request = requests.prepareOrder.buy;
|
const request = requests.prepareOrder.buy;
|
||||||
return this.api.prepareOrder(address, request, instructions)
|
return this.api.prepareOrder(address, request)
|
||||||
.then(_.partial(checkResult, responses.prepareOrder.buy, 'prepare'));
|
.then(_.partial(checkResult, responses.prepareOrder.buy, 'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -91,9 +146,25 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('prepareOrderCancellation', function() {
|
it('prepareOrderCancellation', function() {
|
||||||
const request = requests.prepareOrderCancellation;
|
const request = requests.prepareOrderCancellation.simple;
|
||||||
return this.api.prepareOrderCancellation(address, request, instructions)
|
return this.api.prepareOrderCancellation(address, request, instructions)
|
||||||
.then(_.partial(checkResult, responses.prepareOrderCancellation,
|
.then(_.partial(checkResult, responses.prepareOrderCancellation.normal,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepareOrderCancellation - no instructions', function() {
|
||||||
|
const request = requests.prepareOrderCancellation.simple;
|
||||||
|
return this.api.prepareOrderCancellation(address, request)
|
||||||
|
.then(_.partial(checkResult,
|
||||||
|
responses.prepareOrderCancellation.noInstructions,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepareOrderCancellation - with memos', function() {
|
||||||
|
const request = requests.prepareOrderCancellation.withMemos;
|
||||||
|
return this.api.prepareOrderCancellation(address, request)
|
||||||
|
.then(_.partial(checkResult,
|
||||||
|
responses.prepareOrderCancellation.withMemos,
|
||||||
'prepare'));
|
'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,6 +174,12 @@ describe('RippleAPI', function() {
|
|||||||
_.partial(checkResult, responses.prepareTrustline.simple, 'prepare'));
|
_.partial(checkResult, responses.prepareTrustline.simple, 'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('prepareTrustline - frozen', function() {
|
||||||
|
return this.api.prepareTrustline(
|
||||||
|
address, requests.prepareTrustline.frozen).then(
|
||||||
|
_.partial(checkResult, responses.prepareTrustline.frozen, 'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
it('prepareTrustline - complex', function() {
|
it('prepareTrustline - complex', function() {
|
||||||
return this.api.prepareTrustline(
|
return this.api.prepareTrustline(
|
||||||
address, requests.prepareTrustline.complex, instructions).then(
|
address, requests.prepareTrustline.complex, instructions).then(
|
||||||
@@ -111,10 +188,26 @@ describe('RippleAPI', function() {
|
|||||||
|
|
||||||
it('prepareSettings', function() {
|
it('prepareSettings', function() {
|
||||||
return this.api.prepareSettings(
|
return this.api.prepareSettings(
|
||||||
address, requests.prepareSettings, instructions).then(
|
address, requests.prepareSettings.domain, instructions).then(
|
||||||
_.partial(checkResult, responses.prepareSettings.flags, 'prepare'));
|
_.partial(checkResult, responses.prepareSettings.flags, 'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('prepareSettings - no maxLedgerVersion', function() {
|
||||||
|
return this.api.prepareSettings(
|
||||||
|
address, requests.prepareSettings.domain, {maxLedgerVersion: null}).then(
|
||||||
|
_.partial(checkResult, responses.prepareSettings.noMaxLedgerVersion,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepareSettings - no instructions', function() {
|
||||||
|
return this.api.prepareSettings(
|
||||||
|
address, requests.prepareSettings.domain).then(
|
||||||
|
_.partial(
|
||||||
|
checkResult,
|
||||||
|
responses.prepareSettings.noInstructions,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
it('prepareSettings - regularKey', function() {
|
it('prepareSettings - regularKey', function() {
|
||||||
const regularKey = {regularKey: 'rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD'};
|
const regularKey = {regularKey: 'rAR8rR8sUkBoCZFawhkWzY4Y5YoyuznwD'};
|
||||||
return this.api.prepareSettings(address, regularKey, instructions).then(
|
return this.api.prepareSettings(address, regularKey, instructions).then(
|
||||||
@@ -156,47 +249,110 @@ describe('RippleAPI', function() {
|
|||||||
'prepare'));
|
'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('prepareSettings - set signers', function() {
|
||||||
|
const settings = requests.prepareSettings.signers;
|
||||||
|
return this.api.prepareSettings(address, settings, instructions).then(
|
||||||
|
_.partial(checkResult, responses.prepareSettings.signers,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepareSettings - fee for multisign', function() {
|
||||||
|
const localInstructions = _.defaults({
|
||||||
|
signersCount: 4
|
||||||
|
}, instructions);
|
||||||
|
return this.api.prepareSettings(
|
||||||
|
address, requests.prepareSettings.domain, localInstructions).then(
|
||||||
|
_.partial(checkResult, responses.prepareSettings.flagsMultisign,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
it('prepareSuspendedPaymentCreation', function() {
|
it('prepareSuspendedPaymentCreation', function() {
|
||||||
const localInstructions = _.defaults({
|
const localInstructions = _.defaults({
|
||||||
maxFee: '0.000012'
|
maxFee: '0.000012'
|
||||||
}, instructions);
|
}, instructions);
|
||||||
return this.api.prepareSuspendedPaymentCreation(
|
return this.api.prepareSuspendedPaymentCreation(
|
||||||
address, requests.prepareSuspendedPaymentCreation,
|
address, requests.prepareSuspendedPaymentCreation.normal,
|
||||||
localInstructions).then(
|
localInstructions).then(
|
||||||
_.partial(checkResult, responses.prepareSuspendedPaymentCreation,
|
_.partial(checkResult, responses.prepareSuspendedPaymentCreation.normal,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepareSuspendedPaymentCreation full', function() {
|
||||||
|
return this.api.prepareSuspendedPaymentCreation(
|
||||||
|
address, requests.prepareSuspendedPaymentCreation.full).then(
|
||||||
|
_.partial(checkResult, responses.prepareSuspendedPaymentCreation.full,
|
||||||
'prepare'));
|
'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prepareSuspendedPaymentExecution', function() {
|
it('prepareSuspendedPaymentExecution', function() {
|
||||||
return this.api.prepareSuspendedPaymentExecution(
|
return this.api.prepareSuspendedPaymentExecution(
|
||||||
address, requests.prepareSuspendedPaymentExecution, instructions).then(
|
address,
|
||||||
_.partial(checkResult, responses.prepareSuspendedPaymentExecution,
|
requests.prepareSuspendedPaymentExecution.normal, instructions).then(
|
||||||
'prepare'));
|
_.partial(checkResult,
|
||||||
|
responses.prepareSuspendedPaymentExecution.normal,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepareSuspendedPaymentExecution - simple', function() {
|
||||||
|
return this.api.prepareSuspendedPaymentExecution(
|
||||||
|
address,
|
||||||
|
requests.prepareSuspendedPaymentExecution.simple).then(
|
||||||
|
_.partial(checkResult,
|
||||||
|
responses.prepareSuspendedPaymentExecution.simple,
|
||||||
|
'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prepareSuspendedPaymentCancellation', function() {
|
it('prepareSuspendedPaymentCancellation', function() {
|
||||||
return this.api.prepareSuspendedPaymentCancellation(
|
return this.api.prepareSuspendedPaymentCancellation(
|
||||||
address, requests.prepareSuspendedPaymentCancellation, instructions).then(
|
address,
|
||||||
_.partial(checkResult, responses.prepareSuspendedPaymentCancellation,
|
requests.prepareSuspendedPaymentCancellation.normal, instructions).then(
|
||||||
'prepare'));
|
_.partial(checkResult,
|
||||||
|
responses.prepareSuspendedPaymentCancellation.normal,
|
||||||
|
'prepare'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepareSuspendedPaymentCancellation with memos', function() {
|
||||||
|
return this.api.prepareSuspendedPaymentCancellation(
|
||||||
|
address,
|
||||||
|
requests.prepareSuspendedPaymentCancellation.memos).then(
|
||||||
|
_.partial(checkResult,
|
||||||
|
responses.prepareSuspendedPaymentCancellation.memos,
|
||||||
|
'prepare'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sign', function() {
|
it('sign', function() {
|
||||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||||
const result = this.api.sign(requests.sign.txJSON, secret);
|
const result = this.api.sign(requests.sign.normal.txJSON, secret);
|
||||||
assert.deepEqual(result, responses.sign);
|
assert.deepEqual(result, responses.sign.normal);
|
||||||
schemaValidator.schemaValidate('sign', result);
|
schemaValidator.schemaValidate('sign', result);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sign - already signed', function() {
|
||||||
|
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||||
|
const result = this.api.sign(requests.sign.normal.txJSON, secret);
|
||||||
|
assert.throws(() => {
|
||||||
|
const tx = JSON.stringify(binary.decode(result.signedTransaction));
|
||||||
|
this.api.sign(tx, secret);
|
||||||
|
}, /txJSON must not contain "TxnSignature" or "Signers" properties/);
|
||||||
|
});
|
||||||
|
|
||||||
it('sign - SuspendedPaymentExecution', function() {
|
it('sign - SuspendedPaymentExecution', function() {
|
||||||
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
|
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
|
||||||
const result = this.api.sign(requests.signSuspended.txJSON, secret);
|
const result = this.api.sign(requests.sign.suspended.txJSON, secret);
|
||||||
assert.deepEqual(result, responses.signSuspended);
|
assert.deepEqual(result, responses.sign.suspended);
|
||||||
schemaValidator.schemaValidate('sign', result);
|
schemaValidator.schemaValidate('sign', result);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('sign - signAs', function() {
|
||||||
|
const txJSON = requests.sign.signAs;
|
||||||
|
const secret = 'snoPBrXtMeMyMHUVTgbuqAfg1SUTb';
|
||||||
|
const signature = this.api.sign(JSON.stringify(txJSON), secret,
|
||||||
|
{signAs: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'});
|
||||||
|
assert.deepEqual(signature, responses.sign.signAs);
|
||||||
|
});
|
||||||
|
|
||||||
it('submit', function() {
|
it('submit', function() {
|
||||||
return this.api.submit(responses.sign.signedTransaction).then(
|
return this.api.submit(responses.sign.normal.signedTransaction).then(
|
||||||
_.partial(checkResult, responses.submit, 'submit'));
|
_.partial(checkResult, responses.submit, 'submit'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -209,6 +365,21 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('combine', function() {
|
||||||
|
const combined = this.api.combine(requests.combine.setDomain);
|
||||||
|
checkResult(responses.combine.single, 'sign', combined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('combine - different transactions', function() {
|
||||||
|
const request = [requests.combine.setDomain[0]];
|
||||||
|
const tx = binary.decode(requests.combine.setDomain[0]);
|
||||||
|
tx.Flags = 0;
|
||||||
|
request.push(binary.encode(tx));
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.combine(request);
|
||||||
|
}, /txJSON is not the same for all signedTransactions/);
|
||||||
|
});
|
||||||
|
|
||||||
describe('RippleAPI', function() {
|
describe('RippleAPI', function() {
|
||||||
|
|
||||||
it('getBalances', function() {
|
it('getBalances', function() {
|
||||||
@@ -218,7 +389,8 @@ describe('RippleAPI', function() {
|
|||||||
|
|
||||||
it('getBalances - limit', function() {
|
it('getBalances - limit', function() {
|
||||||
const options = {
|
const options = {
|
||||||
limit: 3
|
limit: 3,
|
||||||
|
ledgerVersion: 123456
|
||||||
};
|
};
|
||||||
const expectedResponse = responses.getBalances.slice(0, 3);
|
const expectedResponse = responses.getBalances.slice(0, 3);
|
||||||
return this.api.getBalances(address, options).then(
|
return this.api.getBalances(address, options).then(
|
||||||
@@ -255,6 +427,18 @@ describe('RippleAPI', function() {
|
|||||||
_.partial(checkResult, responses.getBalanceSheet, 'getBalanceSheet'));
|
_.partial(checkResult, responses.getBalanceSheet, 'getBalanceSheet'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getBalanceSheet - invalid options', function() {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.getBalanceSheet(address, {invalid: 'options'});
|
||||||
|
}, this.api.errors.ValidationError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getBalanceSheet - empty', function() {
|
||||||
|
const options = {ledgerVersion: 123456};
|
||||||
|
return this.api.getBalanceSheet(address, options).then(
|
||||||
|
_.partial(checkResult, {}, 'getBalanceSheet'));
|
||||||
|
});
|
||||||
|
|
||||||
describe('getTransaction', () => {
|
describe('getTransaction', () => {
|
||||||
it('getTransaction - payment', function() {
|
it('getTransaction - payment', function() {
|
||||||
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
|
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
|
||||||
@@ -279,6 +463,15 @@ describe('RippleAPI', function() {
|
|||||||
'getTransaction'));
|
'getTransaction'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getTransaction - sell order', function() {
|
||||||
|
const hash =
|
||||||
|
'458101D51051230B1D56E9ACAFAA34451BF65FA000F95DF6F0FF5B3A62D83FC2';
|
||||||
|
closeLedger(this.api.connection);
|
||||||
|
return this.api.getTransaction(hash).then(
|
||||||
|
_.partial(checkResult, responses.getTransaction.orderSell,
|
||||||
|
'getTransaction'));
|
||||||
|
});
|
||||||
|
|
||||||
it('getTransaction - order cancellation', function() {
|
it('getTransaction - order cancellation', function() {
|
||||||
const hash =
|
const hash =
|
||||||
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
'809335DD3B0B333865096217AA2F55A4DF168E0198080B3A090D12D88880FF0E';
|
||||||
@@ -456,6 +649,15 @@ describe('RippleAPI', function() {
|
|||||||
'getTransaction'));
|
'getTransaction'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getTransaction - SuspendedPaymentCreation iou', function() {
|
||||||
|
const hash =
|
||||||
|
'144F272380BDB4F1BD92329A2178BABB70C20F59042C495E10BF72EBFB408EE2';
|
||||||
|
return this.api.getTransaction(hash).then(
|
||||||
|
_.partial(checkResult,
|
||||||
|
responses.getTransaction.SuspendedPaymentCreationIOU,
|
||||||
|
'getTransaction'));
|
||||||
|
});
|
||||||
|
|
||||||
it('getTransaction - SuspendedPaymentCancellation', function() {
|
it('getTransaction - SuspendedPaymentCancellation', function() {
|
||||||
const hash =
|
const hash =
|
||||||
'F346E542FFB7A8398C30A87B952668DAB48B7D421094F8B71776DA19775A3B22';
|
'F346E542FFB7A8398C30A87B952668DAB48B7D421094F8B71776DA19775A3B22';
|
||||||
@@ -466,20 +668,52 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('getTransaction - SuspendedPaymentExecution', function() {
|
it('getTransaction - SuspendedPaymentExecution', function() {
|
||||||
|
const options = {
|
||||||
|
minLedgerVersion: 10,
|
||||||
|
maxLedgerVersion: 15
|
||||||
|
};
|
||||||
const hash =
|
const hash =
|
||||||
'CC5277137B3F25EE8B86259C83CB0EAADE818505E4E9BCBF19B1AC6FD136993B';
|
'CC5277137B3F25EE8B86259C83CB0EAADE818505E4E9BCBF19B1AC6FD136993B';
|
||||||
return this.api.getTransaction(hash).then(
|
return this.api.getTransaction(hash, options).then(
|
||||||
_.partial(checkResult,
|
_.partial(checkResult,
|
||||||
responses.getTransaction.suspendedPaymentExecution,
|
responses.getTransaction.suspendedPaymentExecution,
|
||||||
'getTransaction'));
|
'getTransaction'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getTransaction - SuspendedPaymentExecution simple', function() {
|
||||||
|
const hash =
|
||||||
|
'CC5277137B3F25EE8B86259C83CB0EAADE818505E4E9BCBF19B1AC6FD1369931';
|
||||||
|
return this.api.getTransaction(hash).then(
|
||||||
|
_.partial(checkResult,
|
||||||
|
responses.getTransaction.suspendedPaymentExecutionSimple,
|
||||||
|
'getTransaction'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTransaction - no Meta', function() {
|
||||||
|
const hash =
|
||||||
|
'AFB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B';
|
||||||
|
return this.api.getTransaction(hash).then(result => {
|
||||||
|
assert.deepEqual(result, responses.getTransaction.noMeta);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTransaction - Unrecognized transaction type', function() {
|
||||||
|
const hash =
|
||||||
|
'AFB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA11';
|
||||||
|
closeLedger(this.api.connection);
|
||||||
|
return this.api.getTransaction(hash).then(() => {
|
||||||
|
assert(false, 'Unrecognized transaction type');
|
||||||
|
}).catch(error => {
|
||||||
|
assert.strictEqual(error.message, 'Unrecognized transaction type');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getTransactions', function() {
|
it('getTransactions', function() {
|
||||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2};
|
const options = {types: ['payment', 'order'], initiated: true, limit: 2};
|
||||||
return this.api.getTransactions(address, options).then(
|
return this.api.getTransactions(address, options).then(
|
||||||
_.partial(checkResult, responses.getTransactions,
|
_.partial(checkResult, responses.getTransactions.normal,
|
||||||
'getTransactions'));
|
'getTransactions'));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -487,12 +721,13 @@ describe('RippleAPI', function() {
|
|||||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
||||||
earliestFirst: true
|
earliestFirst: true
|
||||||
};
|
};
|
||||||
const expected = _.cloneDeep(responses.getTransactions)
|
const expected = _.cloneDeep(responses.getTransactions.normal)
|
||||||
.sort(utils.compareTransactions);
|
.sort(utils.compareTransactions);
|
||||||
return this.api.getTransactions(address, options).then(
|
return this.api.getTransactions(address, options).then(
|
||||||
_.partial(checkResult, expected, 'getTransactions'));
|
_.partial(checkResult, expected, 'getTransactions'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('getTransactions - earliest first with start option', function() {
|
it('getTransactions - earliest first with start option', function() {
|
||||||
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
const options = {types: ['payment', 'order'], initiated: true, limit: 2,
|
||||||
start: hashes.VALID_TRANSACTION_HASH,
|
start: hashes.VALID_TRANSACTION_HASH,
|
||||||
@@ -569,13 +804,35 @@ describe('RippleAPI', function() {
|
|||||||
limit: 2
|
limit: 2
|
||||||
};
|
};
|
||||||
return this.api.getTransactions(address, options).then(
|
return this.api.getTransactions(address, options).then(
|
||||||
_.partial(checkResult, responses.getTransactions, 'getTransactions'));
|
_.partial(checkResult, responses.getTransactions.normal,
|
||||||
|
'getTransactions'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getTrustlines', function() {
|
it('getTransactions - start transaction with zero ledger version', function(
|
||||||
|
) {
|
||||||
|
const options = {
|
||||||
|
start: '4FB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA13',
|
||||||
|
limit: 1
|
||||||
|
};
|
||||||
|
return this.api.getTransactions(address, options).then(
|
||||||
|
_.partial(checkResult, [], 'getTransactions'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTransactions - no options', function() {
|
||||||
|
return this.api.getTransactions(addresses.OTHER_ACCOUNT).then(
|
||||||
|
_.partial(checkResult, responses.getTransactions.one, 'getTransactions'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTrustlines - filtered', function() {
|
||||||
const options = {currency: 'USD'};
|
const options = {currency: 'USD'};
|
||||||
return this.api.getTrustlines(address, options).then(
|
return this.api.getTrustlines(address, options).then(
|
||||||
_.partial(checkResult, responses.getTrustlines, 'getTrustlines'));
|
_.partial(checkResult,
|
||||||
|
responses.getTrustlines.filtered, 'getTrustlines'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getTrustlines - no options', function() {
|
||||||
|
return this.api.getTrustlines(address).then(
|
||||||
|
_.partial(checkResult, responses.getTrustlines.all, 'getTrustlines'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('generateAddress', function() {
|
it('generateAddress', function() {
|
||||||
@@ -586,61 +843,123 @@ describe('RippleAPI', function() {
|
|||||||
responses.generateAddress);
|
responses.generateAddress);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('generateAddress invalid', function() {
|
||||||
|
assert.throws(() => {
|
||||||
|
function random() {
|
||||||
|
return _.fill(Array(1), 0);
|
||||||
|
}
|
||||||
|
this.api.generateAddress({entropy: random()});
|
||||||
|
}, this.api.errors.UnexpectedError);
|
||||||
|
});
|
||||||
|
|
||||||
it('getSettings', function() {
|
it('getSettings', function() {
|
||||||
return this.api.getSettings(address).then(
|
return this.api.getSettings(address).then(
|
||||||
_.partial(checkResult, responses.getSettings, 'getSettings'));
|
_.partial(checkResult, responses.getSettings, 'getSettings'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getSettings - options undefined', function() {
|
||||||
|
return this.api.getSettings(address, undefined).then(
|
||||||
|
_.partial(checkResult, responses.getSettings, 'getSettings'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSettings - invalid options', function() {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.getSettings(address, {invalid: 'options'});
|
||||||
|
}, this.api.errors.ValidationError);
|
||||||
|
});
|
||||||
|
|
||||||
it('getAccountInfo', function() {
|
it('getAccountInfo', function() {
|
||||||
return this.api.getAccountInfo(address).then(
|
return this.api.getAccountInfo(address).then(
|
||||||
_.partial(checkResult, responses.getAccountInfo, 'getAccountInfo'));
|
_.partial(checkResult, responses.getAccountInfo, 'getAccountInfo'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getAccountInfo - options undefined', function() {
|
||||||
|
return this.api.getAccountInfo(address, undefined).then(
|
||||||
|
_.partial(checkResult, responses.getAccountInfo, 'getAccountInfo'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getAccountInfo - invalid options', function() {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.getAccountInfo(address, {invalid: 'options'});
|
||||||
|
}, this.api.errors.ValidationError);
|
||||||
|
});
|
||||||
|
|
||||||
it('getOrders', function() {
|
it('getOrders', function() {
|
||||||
return this.api.getOrders(address).then(
|
return this.api.getOrders(address).then(
|
||||||
_.partial(checkResult, responses.getOrders, 'getOrders'));
|
_.partial(checkResult, responses.getOrders, 'getOrders'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getOrderbook', function() {
|
it('getOrders', function() {
|
||||||
return this.api.getOrderbook(address, requests.getOrderbook).then(
|
return this.api.getOrders(address, undefined).then(
|
||||||
_.partial(checkResult, responses.getOrderbook, 'getOrderbook'));
|
_.partial(checkResult, responses.getOrders, 'getOrders'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getOrderbook - sorted so that best deals come first', function() {
|
it('getOrders - invalid options', function() {
|
||||||
return this.api.getOrderbook(address, requests.getOrderbook)
|
assert.throws(() => {
|
||||||
.then(data => {
|
this.api.getOrders(address, {invalid: 'options'});
|
||||||
const bidRates = data.bids.map(bid => bid.properties.makerExchangeRate);
|
}, this.api.errors.ValidationError);
|
||||||
const askRates = data.asks.map(ask => ask.properties.makerExchangeRate);
|
});
|
||||||
// makerExchangeRate = quality = takerPays.value/takerGets.value
|
|
||||||
// so the best deal for the taker is the lowest makerExchangeRate
|
describe('getOrderbook', function() {
|
||||||
// bids and asks should be sorted so that the best deals come first
|
|
||||||
assert.deepEqual(_.sortBy(bidRates, x => Number(x)), bidRates);
|
it('normal', function() {
|
||||||
assert.deepEqual(_.sortBy(askRates, x => Number(x)), askRates);
|
return this.api.getOrderbook(address,
|
||||||
|
requests.getOrderbook.normal, undefined).then(
|
||||||
|
_.partial(checkResult,
|
||||||
|
responses.getOrderbook.normal, 'getOrderbook'));
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('getOrderbook - currency & counterparty are correct', function() {
|
it('invalid options', function() {
|
||||||
return this.api.getOrderbook(address, requests.getOrderbook)
|
assert.throws(() => {
|
||||||
.then(data => {
|
this.api.getOrderbook(address, requests.getOrderbook.normal,
|
||||||
const orders = _.flatten([data.bids, data.asks]);
|
{invalid: 'options'});
|
||||||
_.forEach(orders, order => {
|
}, this.api.errors.ValidationError);
|
||||||
const quantity = order.specification.quantity;
|
});
|
||||||
const totalPrice = order.specification.totalPrice;
|
|
||||||
const {base, counter} = requests.getOrderbook;
|
it('with XRP', function() {
|
||||||
assert.strictEqual(quantity.currency, base.currency);
|
return this.api.getOrderbook(address, requests.getOrderbook.withXRP).then(
|
||||||
assert.strictEqual(quantity.counterparty, base.counterparty);
|
_.partial(checkResult, responses.getOrderbook.withXRP, 'getOrderbook'));
|
||||||
assert.strictEqual(totalPrice.currency, counter.currency);
|
});
|
||||||
assert.strictEqual(totalPrice.counterparty, counter.counterparty);
|
|
||||||
|
it('sorted so that best deals come first', function() {
|
||||||
|
return this.api.getOrderbook(address, requests.getOrderbook.normal)
|
||||||
|
.then(data => {
|
||||||
|
const bidRates = data.bids.map(bid => bid.properties.makerExchangeRate);
|
||||||
|
const askRates = data.asks.map(ask => ask.properties.makerExchangeRate);
|
||||||
|
// makerExchangeRate = quality = takerPays.value/takerGets.value
|
||||||
|
// so the best deal for the taker is the lowest makerExchangeRate
|
||||||
|
// bids and asks should be sorted so that the best deals come first
|
||||||
|
assert.deepEqual(_.sortBy(bidRates, x => Number(x)), bidRates);
|
||||||
|
assert.deepEqual(_.sortBy(askRates, x => Number(x)), askRates);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('getOrderbook - direction is correct for bids and asks', function() {
|
it('currency & counterparty are correct', function() {
|
||||||
return this.api.getOrderbook(address, requests.getOrderbook)
|
return this.api.getOrderbook(address, requests.getOrderbook.normal)
|
||||||
.then(data => {
|
.then(data => {
|
||||||
assert(_.every(data.bids, bid => bid.specification.direction === 'buy'));
|
const orders = _.flatten([data.bids, data.asks]);
|
||||||
assert(_.every(data.asks, ask => ask.specification.direction === 'sell'));
|
_.forEach(orders, order => {
|
||||||
|
const quantity = order.specification.quantity;
|
||||||
|
const totalPrice = order.specification.totalPrice;
|
||||||
|
const {base, counter} = requests.getOrderbook.normal;
|
||||||
|
assert.strictEqual(quantity.currency, base.currency);
|
||||||
|
assert.strictEqual(quantity.counterparty, base.counterparty);
|
||||||
|
assert.strictEqual(totalPrice.currency, counter.currency);
|
||||||
|
assert.strictEqual(totalPrice.counterparty, counter.counterparty);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('direction is correct for bids and asks', function() {
|
||||||
|
return this.api.getOrderbook(address, requests.getOrderbook.normal)
|
||||||
|
.then(data => {
|
||||||
|
assert(
|
||||||
|
_.every(data.bids, bid => bid.specification.direction === 'buy'));
|
||||||
|
assert(
|
||||||
|
_.every(data.asks, ask => ask.specification.direction === 'sell'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getServerInfo', function() {
|
it('getServerInfo', function() {
|
||||||
@@ -649,7 +968,11 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('getServerInfo - error', function() {
|
it('getServerInfo - error', function() {
|
||||||
this.mockRippled.returnErrorOnServerInfo = true;
|
this.api.connection._send(JSON.stringify({
|
||||||
|
command: 'config',
|
||||||
|
data: {returnErrorOnServerInfo: true}
|
||||||
|
}));
|
||||||
|
|
||||||
return this.api.getServerInfo().then(() => {
|
return this.api.getServerInfo().then(() => {
|
||||||
assert(false, 'Should throw NetworkError');
|
assert(false, 'Should throw NetworkError');
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
@@ -664,6 +987,13 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getFee default', function() {
|
||||||
|
this.api._feeCushion = undefined;
|
||||||
|
return this.api.getFee().then(fee => {
|
||||||
|
assert.strictEqual(fee, '0.000012');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('disconnect & isConnected', function() {
|
it('disconnect & isConnected', function() {
|
||||||
assert.strictEqual(this.api.isConnected(), true);
|
assert.strictEqual(this.api.isConnected(), true);
|
||||||
return this.api.disconnect().then(() => {
|
return this.api.disconnect().then(() => {
|
||||||
@@ -702,6 +1032,14 @@ describe('RippleAPI', function() {
|
|||||||
_.partial(checkResult, responses.getPaths.XrpToXrp, 'getPaths'));
|
_.partial(checkResult, responses.getPaths.XrpToXrp, 'getPaths'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getPaths - source with issuer', function() {
|
||||||
|
return this.api.getPaths(requests.getPaths.issuer).then(() => {
|
||||||
|
assert(false, 'Should throw NotFoundError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.NotFoundError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('getPaths - XRP 2 XRP - not enough', function() {
|
it('getPaths - XRP 2 XRP - not enough', function() {
|
||||||
return this.api.getPaths(requests.getPaths.XrpToXrpNotEnough).then(() => {
|
return this.api.getPaths(requests.getPaths.XrpToXrpNotEnough).then(() => {
|
||||||
assert(false, 'Should throw NotFoundError');
|
assert(false, 'Should throw NotFoundError');
|
||||||
@@ -710,6 +1048,12 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getPaths - invalid PathFind', function() {
|
||||||
|
assert.throws(() => {
|
||||||
|
this.api.getPaths(requests.getPaths.invalid);
|
||||||
|
}, /Cannot specify both source.amount/);
|
||||||
|
});
|
||||||
|
|
||||||
it('getPaths - does not accept currency', function() {
|
it('getPaths - does not accept currency', function() {
|
||||||
return this.api.getPaths(requests.getPaths.NotAcceptCurrency).then(() => {
|
return this.api.getPaths(requests.getPaths.NotAcceptCurrency).then(() => {
|
||||||
assert(false, 'Should throw NotFoundError');
|
assert(false, 'Should throw NotFoundError');
|
||||||
@@ -726,6 +1070,15 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getPaths - no paths source amount', function() {
|
||||||
|
return this.api.getPaths(requests.getPaths.NoPathsSource).then(() => {
|
||||||
|
assert(false, 'Should throw NotFoundError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.NotFoundError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it('getPaths - no paths with source currencies', function() {
|
it('getPaths - no paths with source currencies', function() {
|
||||||
const pathfind = requests.getPaths.NoPathsWithCurrencies;
|
const pathfind = requests.getPaths.NoPathsWithCurrencies;
|
||||||
return this.api.getPaths(pathfind).then(() => {
|
return this.api.getPaths(pathfind).then(() => {
|
||||||
@@ -749,7 +1102,7 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('getLedgerVersion', function(done) {
|
it('getLedgerVersion', function(done) {
|
||||||
this.api.getLedgerVersion().then((ver) => {
|
this.api.getLedgerVersion().then(ver => {
|
||||||
assert.strictEqual(ver, 8819951);
|
assert.strictEqual(ver, 8819951);
|
||||||
done();
|
done();
|
||||||
}, done);
|
}, done);
|
||||||
@@ -768,6 +1121,18 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getLedger - with state as hashes', function() {
|
||||||
|
const request = {
|
||||||
|
includeTransactions: true,
|
||||||
|
includeAllData: false,
|
||||||
|
includeState: true,
|
||||||
|
ledgerVersion: 6
|
||||||
|
};
|
||||||
|
return this.api.getLedger(request).then(
|
||||||
|
_.partial(checkResult, responses.getLedger.withStateAsHashes,
|
||||||
|
'getLedger'));
|
||||||
|
});
|
||||||
|
|
||||||
it('getLedger - with settings transaction', function() {
|
it('getLedger - with settings transaction', function() {
|
||||||
const request = {
|
const request = {
|
||||||
includeTransactions: true,
|
includeTransactions: true,
|
||||||
@@ -796,7 +1161,55 @@ describe('RippleAPI', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('computeLedgerHash - wrong hash', function() {
|
||||||
|
const request = {
|
||||||
|
includeTransactions: true,
|
||||||
|
includeState: true,
|
||||||
|
includeAllData: true,
|
||||||
|
ledgerVersion: 38129
|
||||||
|
};
|
||||||
|
return this.api.getLedger(request).then(
|
||||||
|
_.partial(checkResult, responses.getLedger.full, 'getLedger'))
|
||||||
|
.then(response => {
|
||||||
|
const ledger = _.assign({}, response, {
|
||||||
|
parentCloseTime: response.closeTime, stateHash:
|
||||||
|
'D9ABF622DA26EEEE48203085D4BC23B0F77DC6F8724AC33D975DA3CA492D2E44'});
|
||||||
|
assert.throws(() => {
|
||||||
|
const hash = this.api.computeLedgerHash(ledger);
|
||||||
|
unused(hash);
|
||||||
|
}, /does not match computed hash of state/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('RippleError with data', function() {
|
||||||
|
const error = new this.api.errors.RippleError('_message_', '_data_');
|
||||||
|
assert.strictEqual(error.toString(),
|
||||||
|
'[RippleError(_message_, \'_data_\')]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('NotFoundError default message', function() {
|
||||||
|
const error = new this.api.errors.NotFoundError();
|
||||||
|
assert.strictEqual(error.toString(),
|
||||||
|
'[NotFoundError(Not found)]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('common utils - toRippledAmount', function() {
|
||||||
|
const amount = {issuer: 'is', currency: 'c', value: 'v'};
|
||||||
|
|
||||||
|
assert.deepEqual(utils.common.toRippledAmount(amount), {
|
||||||
|
issuer: 'is', currency: 'c', value: 'v'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ledger utils - renameCounterpartyToIssuerInOrder', function() {
|
||||||
|
const order = {taker_gets: {issuer: '1'}};
|
||||||
|
const expected = {taker_gets: {issuer: '1'}};
|
||||||
|
|
||||||
|
assert.deepEqual(utils.renameCounterpartyToIssuerInOrder(order), expected);
|
||||||
|
});
|
||||||
|
|
||||||
it('ledger utils - compareTransactions', function() {
|
it('ledger utils - compareTransactions', function() {
|
||||||
|
assert.strictEqual(utils.compareTransactions({}, {}), 0);
|
||||||
let first = {outcome: {ledgerVersion: 1, indexInLedger: 100}};
|
let first = {outcome: {ledgerVersion: 1, indexInLedger: 100}};
|
||||||
let second = {outcome: {ledgerVersion: 1, indexInLedger: 200}};
|
let second = {outcome: {ledgerVersion: 1, indexInLedger: 200}};
|
||||||
|
|
||||||
@@ -896,9 +1309,9 @@ describe('RippleAPI', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('ledgerClosed', function(done) {
|
it('ledger event', function(done) {
|
||||||
this.api.on('ledgerClosed', message => {
|
this.api.on('ledger', message => {
|
||||||
checkResult(responses.ledgerClosed, 'ledgerClosed', message);
|
checkResult(responses.ledgerEvent, 'ledgerEvent', message);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
closeLedger(this.api.connection);
|
closeLedger(this.api.connection);
|
||||||
@@ -909,7 +1322,7 @@ describe('RippleAPI - offline', function() {
|
|||||||
it('prepareSettings and sign', function() {
|
it('prepareSettings and sign', function() {
|
||||||
const api = new RippleAPI();
|
const api = new RippleAPI();
|
||||||
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
const secret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV';
|
||||||
const settings = requests.prepareSettings;
|
const settings = requests.prepareSettings.domain;
|
||||||
const instructions = {
|
const instructions = {
|
||||||
sequence: 23,
|
sequence: 23,
|
||||||
maxLedgerVersion: 8820051,
|
maxLedgerVersion: 8820051,
|
||||||
@@ -917,7 +1330,8 @@ describe('RippleAPI - offline', function() {
|
|||||||
};
|
};
|
||||||
return api.prepareSettings(address, settings, instructions).then(data => {
|
return api.prepareSettings(address, settings, instructions).then(data => {
|
||||||
checkResult(responses.prepareSettings.flags, 'prepare', data);
|
checkResult(responses.prepareSettings.flags, 'prepare', data);
|
||||||
assert.deepEqual(api.sign(data.txJSON, secret), responses.sign);
|
assert.deepEqual(api.sign(data.txJSON, secret),
|
||||||
|
responses.prepareSettings.signed);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -961,7 +1375,7 @@ describe('RippleAPI - offline', function() {
|
|||||||
|
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
it('RippleAPI - implicit server port', function() {
|
it('RippleAPI - implicit server port', function() {
|
||||||
const api = new RippleAPI({servers: ['wss://s1.ripple.com']});
|
const api = new RippleAPI({server: 'wss://s1.ripple.com'});
|
||||||
});
|
});
|
||||||
/* eslint-enable no-unused-vars */
|
/* eslint-enable no-unused-vars */
|
||||||
it('RippleAPI invalid options', function() {
|
it('RippleAPI invalid options', function() {
|
||||||
@@ -969,12 +1383,12 @@ describe('RippleAPI - offline', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('RippleAPI valid options', function() {
|
it('RippleAPI valid options', function() {
|
||||||
const api = new RippleAPI({servers: ['wss://s:1']});
|
const api = new RippleAPI({server: 'wss://s:1'});
|
||||||
assert.deepEqual(api.connection._url, 'wss://s:1');
|
assert.deepEqual(api.connection._url, 'wss://s:1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('RippleAPI invalid server uri', function() {
|
it('RippleAPI invalid server uri', function() {
|
||||||
assert.throws(() => new RippleAPI({servers: ['wss//s:1']}));
|
assert.throws(() => new RippleAPI({server: 'wss//s:1'}));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
71
test/broadcast-api-test.js
Normal file
71
test/broadcast-api-test.js
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
/* eslint-disable max-nested-callbacks */
|
||||||
|
'use strict';
|
||||||
|
const _ = require('lodash');
|
||||||
|
const assert = require('assert-diff');
|
||||||
|
const setupAPI = require('./setup-api');
|
||||||
|
const responses = require('./fixtures').responses;
|
||||||
|
const ledgerClosed = require('./fixtures/rippled/ledger-close');
|
||||||
|
const RippleAPI = require('ripple-api').RippleAPI;
|
||||||
|
const schemaValidator = RippleAPI._PRIVATE.schemaValidator;
|
||||||
|
|
||||||
|
const TIMEOUT = process.browser ? 25000 : 10000;
|
||||||
|
|
||||||
|
function checkResult(expected, schemaName, response) {
|
||||||
|
if (expected.txJSON) {
|
||||||
|
assert(response.txJSON);
|
||||||
|
assert.deepEqual(JSON.parse(response.txJSON), JSON.parse(expected.txJSON));
|
||||||
|
}
|
||||||
|
assert.deepEqual(_.omit(response, 'txJSON'), _.omit(expected, 'txJSON'));
|
||||||
|
if (schemaName) {
|
||||||
|
schemaValidator.schemaValidate(schemaName, response);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('RippleAPIBroadcast', function() {
|
||||||
|
this.timeout(TIMEOUT);
|
||||||
|
beforeEach(setupAPI.setupBroadcast);
|
||||||
|
afterEach(setupAPI.teardown);
|
||||||
|
|
||||||
|
it('base', function() {
|
||||||
|
const expected = {request_server_info: 1};
|
||||||
|
if (!process.browser) {
|
||||||
|
this.mocks.forEach(mock => mock.expect(_.assign({}, expected)));
|
||||||
|
}
|
||||||
|
assert(this.api.isConnected());
|
||||||
|
return this.api.getServerInfo().then(
|
||||||
|
_.partial(checkResult, responses.getServerInfo, 'getServerInfo'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ledger', function(done) {
|
||||||
|
let gotLedger = 0;
|
||||||
|
this.api.on('ledger', () => {
|
||||||
|
gotLedger++;
|
||||||
|
});
|
||||||
|
const ledgerNext = _.assign({}, ledgerClosed);
|
||||||
|
ledgerNext.ledger_index++;
|
||||||
|
|
||||||
|
this.api._apis.forEach(api => api.connection._send(JSON.stringify({
|
||||||
|
command: 'echo',
|
||||||
|
data: ledgerNext
|
||||||
|
})));
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
assert.strictEqual(gotLedger, 1);
|
||||||
|
done();
|
||||||
|
}, 1250);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('error propagation', function(done) {
|
||||||
|
this.api.once('error', (type, info) => {
|
||||||
|
assert.strictEqual(type, 'type');
|
||||||
|
assert.strictEqual(info, 'info');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
this.api._apis[1].connection._send(JSON.stringify({
|
||||||
|
command: 'echo',
|
||||||
|
data: {error: 'type', error_message: 'info'}
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
285
test/connection-test.js
Normal file
285
test/connection-test.js
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
/* eslint-disable max-nested-callbacks */
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const _ = require('lodash');
|
||||||
|
const net = require('net');
|
||||||
|
const assert = require('assert-diff');
|
||||||
|
const setupAPI = require('./setup-api');
|
||||||
|
const RippleAPI = require('ripple-api').RippleAPI;
|
||||||
|
const utils = RippleAPI._PRIVATE.ledgerUtils;
|
||||||
|
const ledgerClose = require('./fixtures/rippled/ledger-close.json');
|
||||||
|
|
||||||
|
|
||||||
|
const TIMEOUT = 200000; // how long before each test case times out
|
||||||
|
|
||||||
|
function unused() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function createServer() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const server = net.createServer();
|
||||||
|
server.on('listening', function() {
|
||||||
|
resolve(server);
|
||||||
|
});
|
||||||
|
server.on('error', function(error) {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
server.listen(0, '0.0.0.0');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Connection', function() {
|
||||||
|
this.timeout(TIMEOUT);
|
||||||
|
beforeEach(setupAPI.setup);
|
||||||
|
afterEach(setupAPI.teardown);
|
||||||
|
|
||||||
|
it('default options', function() {
|
||||||
|
const connection = new utils.common.Connection('url');
|
||||||
|
assert.strictEqual(connection._url, 'url');
|
||||||
|
assert(_.isUndefined(connection._proxyURL));
|
||||||
|
assert(_.isUndefined(connection._authorization));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('trace', function() {
|
||||||
|
const connection = new utils.common.Connection('url', {trace: true});
|
||||||
|
const message1 = '{"type": "transaction"}';
|
||||||
|
const message2 = '{"type": "path_find"}';
|
||||||
|
const messages = [];
|
||||||
|
connection._console = {
|
||||||
|
log: function(message) {
|
||||||
|
messages.push(message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
connection._ws = {
|
||||||
|
send: function() {}
|
||||||
|
};
|
||||||
|
connection._onMessage(message1);
|
||||||
|
connection._send(message2);
|
||||||
|
|
||||||
|
assert.deepEqual(messages, [message1, message2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('with proxy', function(done) {
|
||||||
|
if (process.browser) {
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createServer().then(server => {
|
||||||
|
const port = server.address().port;
|
||||||
|
const expect = 'CONNECT localhost';
|
||||||
|
server.on('connection', socket => {
|
||||||
|
socket.on('data', data => {
|
||||||
|
const got = data.toString('ascii', 0, expect.length);
|
||||||
|
assert.strictEqual(got, expect);
|
||||||
|
server.close();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
proxy: 'ws://localhost:' + port,
|
||||||
|
authorization: 'authorization',
|
||||||
|
trustedCertificates: ['path/to/pem']
|
||||||
|
};
|
||||||
|
const connection =
|
||||||
|
new utils.common.Connection(this.api.connection._url, options);
|
||||||
|
connection.connect().catch(done);
|
||||||
|
connection.connect().catch(done);
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Multiply disconnect calls', function() {
|
||||||
|
this.api.disconnect();
|
||||||
|
return this.api.disconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reconnect', function() {
|
||||||
|
return this.api.connection.reconnect();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('NotConnectedError', function() {
|
||||||
|
const connection = new utils.common.Connection('url');
|
||||||
|
return connection.getLedgerVersion().then(() => {
|
||||||
|
assert(false, 'Should throw NotConnectedError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.NotConnectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DisconnectedError', function() {
|
||||||
|
this.api.connection._send(JSON.stringify({
|
||||||
|
command: 'config',
|
||||||
|
data: {disconnectOnServerInfo: true}
|
||||||
|
}));
|
||||||
|
return this.api.getServerInfo().then(() => {
|
||||||
|
assert(false, 'Should throw DisconnectedError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.DisconnectedError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('TimeoutError', function() {
|
||||||
|
this.api.connection._send = function() {
|
||||||
|
return Promise.resolve({});
|
||||||
|
};
|
||||||
|
const request = {command: 'server_info'};
|
||||||
|
return this.api.connection.request(request, 1).then(() => {
|
||||||
|
assert(false, 'Should throw TimeoutError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.TimeoutError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('DisconnectedError on send', function() {
|
||||||
|
this.api.connection._ws.send = function(message, options, callback) {
|
||||||
|
unused(message, options);
|
||||||
|
callback({message: 'not connected'});
|
||||||
|
};
|
||||||
|
return this.api.getServerInfo().then(() => {
|
||||||
|
assert(false, 'Should throw DisconnectedError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.DisconnectedError);
|
||||||
|
assert.strictEqual(error.message, 'not connected');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ResponseFormatError', function() {
|
||||||
|
this.api.connection._send = function(message) {
|
||||||
|
const parsed = JSON.parse(message);
|
||||||
|
setTimeout(() => {
|
||||||
|
this._ws.emit('message', JSON.stringify({
|
||||||
|
id: parsed.id,
|
||||||
|
type: 'response',
|
||||||
|
status: 'unrecognized'
|
||||||
|
}));
|
||||||
|
}, 2);
|
||||||
|
return new Promise(() => {});
|
||||||
|
};
|
||||||
|
return this.api.getServerInfo().then(() => {
|
||||||
|
assert(false, 'Should throw ResponseFormatError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.ResponseFormatError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('reconnect on unexpected close ', function(done) {
|
||||||
|
this.api.connection.on('connected', () => {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.api.connection._ws.close();
|
||||||
|
}, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Multiply connect calls', function() {
|
||||||
|
return this.api.connect().then(() => {
|
||||||
|
return this.api.connect();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('hasLedgerVersion', function() {
|
||||||
|
return this.api.connection.hasLedgerVersion(8819951).then(result => {
|
||||||
|
assert(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Cannot connect because no server', function() {
|
||||||
|
const connection = new utils.common.Connection();
|
||||||
|
return connection.connect().then(() => {
|
||||||
|
assert(false, 'Should throw ConnectionError');
|
||||||
|
}).catch(error => {
|
||||||
|
assert(error instanceof this.api.errors.ConnectionError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('connect multiserver error', function() {
|
||||||
|
const options = {
|
||||||
|
servers: ['wss://server1.com', 'wss://server2.com']
|
||||||
|
};
|
||||||
|
assert.throws(function() {
|
||||||
|
const api = new RippleAPI(options);
|
||||||
|
unused(api);
|
||||||
|
}, this.api.errors.RippleError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('connect throws error', function(done) {
|
||||||
|
this.api.once('error', (type, info) => {
|
||||||
|
assert.strictEqual(type, 'type');
|
||||||
|
assert.strictEqual(info, 'info');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
this.api.connection.emit('error', 'type', 'info');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('emit stream messages', function(done) {
|
||||||
|
let transactionCount = 0;
|
||||||
|
let pathFindCount = 0;
|
||||||
|
this.api.connection.on('transaction', () => {
|
||||||
|
transactionCount++;
|
||||||
|
});
|
||||||
|
this.api.connection.on('path_find', () => {
|
||||||
|
pathFindCount++;
|
||||||
|
});
|
||||||
|
this.api.connection.on('1', () => {
|
||||||
|
assert.strictEqual(transactionCount, 1);
|
||||||
|
assert.strictEqual(pathFindCount, 1);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.api.connection._onMessage(JSON.stringify({
|
||||||
|
type: 'transaction'
|
||||||
|
}));
|
||||||
|
this.api.connection._onMessage(JSON.stringify({
|
||||||
|
type: 'path_find'
|
||||||
|
}));
|
||||||
|
this.api.connection._onMessage(JSON.stringify({
|
||||||
|
type: 'response', id: 1
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('invalid message id', function(done) {
|
||||||
|
this.api.on('error', (errorCode, errorMessage, message) => {
|
||||||
|
assert.strictEqual(errorCode, 'badMessage');
|
||||||
|
assert.strictEqual(errorMessage, 'valid id not found in response');
|
||||||
|
assert.strictEqual(message,
|
||||||
|
'{"type":"response","id":"must be integer"}');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
this.api.connection._onMessage(JSON.stringify({
|
||||||
|
type: 'response', id: 'must be integer'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('propagate error message', function(done) {
|
||||||
|
this.api.on('error', (errorCode, errorMessage, data) => {
|
||||||
|
assert.strictEqual(errorCode, 'slowDown');
|
||||||
|
assert.strictEqual(errorMessage, 'slow down');
|
||||||
|
assert.deepEqual(data, {error: 'slowDown', error_message: 'slow down'});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
this.api.connection._onMessage(JSON.stringify({
|
||||||
|
error: 'slowDown', error_message: 'slow down'
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unrecognized message type', function(done) {
|
||||||
|
this.api.on('error', (errorCode, errorMessage, message) => {
|
||||||
|
assert.strictEqual(errorCode, 'badMessage');
|
||||||
|
assert.strictEqual(errorMessage, 'unrecognized message type: unknown');
|
||||||
|
assert.strictEqual(message, '{"type":"unknown"}');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.api.connection._onMessage(JSON.stringify({type: 'unknown'}));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ledger close without validated_ledgers', function(done) {
|
||||||
|
const message = _.omit(ledgerClose, 'validated_ledgers');
|
||||||
|
this.api.on('ledger', function(ledger) {
|
||||||
|
assert.strictEqual(ledger.ledgerVersion, 8819951);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
this.api.connection._ws.emit('message', JSON.stringify(message));
|
||||||
|
});
|
||||||
|
});
|
||||||
3
test/fixtures/addresses.js
vendored
3
test/fixtures/addresses.js
vendored
@@ -6,5 +6,6 @@ module.exports = {
|
|||||||
FOURTH_ACCOUNT: 'rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr',
|
FOURTH_ACCOUNT: 'rJnZ4YHCUsHvQu7R6mZohevKJDHFzVD6Zr',
|
||||||
ISSUER: 'rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM',
|
ISSUER: 'rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM',
|
||||||
NOTFOUND: 'rajTAg3hon5Lcu1RxQQPxTgHvqfhc1EaUS',
|
NOTFOUND: 'rajTAg3hon5Lcu1RxQQPxTgHvqfhc1EaUS',
|
||||||
SECRET: 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
SECRET: 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV',
|
||||||
|
SOURCE_LOW_FUNDS: 'rhVgDEfS1r1fLyRUZCpab4TdowZcAJwHy2'
|
||||||
};
|
};
|
||||||
|
|||||||
2
test/fixtures/requests/combine.json
vendored
Normal file
2
test/fixtures/requests/combine.json
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[ "12000322800000002400000004201B000000116840000000000F42407300770B6578616D706C652E636F6D811407C532442A675C881BA1235354D4AB9D023243A6F3E0107321026C784C1987F83BACBF02CD3E484AFC84ADE5CA6B36ED4DCA06D5BA233B9D382774473045022100E484F54FF909469FA2033E22EFF3DF8EDFE62217062680BB2F3EDF2F185074FE0220350DB29001C710F0450DAF466C5D819DC6D6A3340602DE9B6CB7DA8E17C90F798114FE9337B0574213FA5BCC0A319DBB4A7AC0CCA894E1F1",
|
||||||
|
"12000322800000002400000004201B000000116840000000000F42407300770B6578616D706C652E636F6D811407C532442A675C881BA1235354D4AB9D023243A6F3E01073210287AAAB8FBE8C4C4A47F6F1228C6E5123A7ED844BFE88A9B22C2F7CC34279EEAA74473045022100B09DDF23144595B5A9523B20E605E138DC6549F5CA7B5984D7C32B0E3469DF6B022018845CA6C203D4B6288C87DDA439134C83E7ADF8358BD41A8A9141A9B631419F8114517D9B9609229E0CDFE2428B586738C5B2E84D45E1F1" ]
|
||||||
9
test/fixtures/requests/get-orderbook-with-xrp.json
vendored
Normal file
9
test/fixtures/requests/get-orderbook-with-xrp.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"base": {
|
||||||
|
"currency": "USD",
|
||||||
|
"counterparty": "rp8rJYTpodf8qbSCHVTNacf8nSW8mRakFw"
|
||||||
|
},
|
||||||
|
"counter": {
|
||||||
|
"currency": "XRP"
|
||||||
|
}
|
||||||
|
}
|
||||||
16
test/fixtures/requests/getpaths/invalid.json
vendored
Normal file
16
test/fixtures/requests/getpaths/invalid.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"source": {
|
||||||
|
"address": "rwBYyfufTzk77zUSKEu4MvixfarC35av1J",
|
||||||
|
"amount": {
|
||||||
|
"value": "1000002",
|
||||||
|
"currency": "USD"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"destination": {
|
||||||
|
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||||
|
"amount": {
|
||||||
|
"value": "1000002",
|
||||||
|
"currency": "USD"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user