mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-04 21:15:47 +00:00
Compare commits
199 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c234be0a8c | ||
|
|
7c6b8398cf | ||
|
|
7de677c953 | ||
|
|
aa23f44555 | ||
|
|
901d75a1eb | ||
|
|
aa95286810 | ||
|
|
f6b3f661d6 | ||
|
|
0850d85791 | ||
|
|
c564400ac4 | ||
|
|
94ab545ffe | ||
|
|
e10df203b7 | ||
|
|
eea20a6eab | ||
|
|
5f208801ee | ||
|
|
0a22697e5d | ||
|
|
30cf4f0b00 | ||
|
|
e4bb88a725 | ||
|
|
e3822e6bc3 | ||
|
|
20d3be0d1d | ||
|
|
1785863686 | ||
|
|
ea4ced3cc1 | ||
|
|
149008d18b | ||
|
|
55a21d2eec | ||
|
|
c7491e631a | ||
|
|
468a205e36 | ||
|
|
bebe951a57 | ||
|
|
85a8ab32ef | ||
|
|
34ddbe170c | ||
|
|
e9846eb249 | ||
|
|
7cc418ac93 | ||
|
|
69532a4f23 | ||
|
|
f59419d96f | ||
|
|
7f288d0555 | ||
|
|
53afa8c276 | ||
|
|
22f4dd2f75 | ||
|
|
0989152024 | ||
|
|
f74809d361 | ||
|
|
9724cf7776 | ||
|
|
909e5438a8 | ||
|
|
3c534d87c0 | ||
|
|
4022a59705 | ||
|
|
138e7942da | ||
|
|
d7d26a3ae1 | ||
|
|
23504821cf | ||
|
|
b09da3e8f1 | ||
|
|
f3dd2fec99 | ||
|
|
462e375800 | ||
|
|
ca8c881375 | ||
|
|
96605a57d4 | ||
|
|
491ce40081 | ||
|
|
f33eb07bdd | ||
|
|
8bb1dc9b47 | ||
|
|
78b50472da | ||
|
|
e0259b37ed | ||
|
|
bf863a2594 | ||
|
|
edd174881e | ||
|
|
0bf747f6fc | ||
|
|
ab4d2b5d58 | ||
|
|
1b81280358 | ||
|
|
32f4eea3b8 | ||
|
|
1a3a49decb | ||
|
|
416717aff6 | ||
|
|
769d955a40 | ||
|
|
6de85b841d | ||
|
|
66db127245 | ||
|
|
b4c6af29e4 | ||
|
|
7192606e21 | ||
|
|
932be02e9e | ||
|
|
fc524894c6 | ||
|
|
f5196389e8 | ||
|
|
27be06c5c9 | ||
|
|
1d3ddb5e85 | ||
|
|
2145c104fd | ||
|
|
64e0d098e7 | ||
|
|
50d8cbb0ee | ||
|
|
9580397558 | ||
|
|
cf544b74f5 | ||
|
|
312f831efb | ||
|
|
f5bad5d28e | ||
|
|
9b7d255200 | ||
|
|
7a027bdd93 | ||
|
|
439a611a9e | ||
|
|
5f92b230aa | ||
|
|
29bc5303ae | ||
|
|
5314e5e7e9 | ||
|
|
c88462c99b | ||
|
|
0b552f1a7e | ||
|
|
552635a3c7 | ||
|
|
ca769bee39 | ||
|
|
b7ca0a0a14 | ||
|
|
84097a3179 | ||
|
|
8ba36b2588 | ||
|
|
e1d4ebc5f6 | ||
|
|
9e712d6089 | ||
|
|
90bea3dc6b | ||
|
|
bf480bb971 | ||
|
|
a94b48be50 | ||
|
|
abed42d848 | ||
|
|
3d6e795ca5 | ||
|
|
a3cbe8e9d4 | ||
|
|
0a2000098a | ||
|
|
a2348b5133 | ||
|
|
20d2f9d894 | ||
|
|
321f908e76 | ||
|
|
49875cb0e5 | ||
|
|
fa6a2c5bbb | ||
|
|
1074c00b60 | ||
|
|
8a8b10541e | ||
|
|
46cf4d677c | ||
|
|
94587d7515 | ||
|
|
14ec58ef9a | ||
|
|
8f4f6f3de0 | ||
|
|
f8c0ac3ce0 | ||
|
|
8ebad98912 | ||
|
|
08429b6110 | ||
|
|
0e128e15f1 | ||
|
|
b77a12fd0d | ||
|
|
a98526b398 | ||
|
|
5639bf9d48 | ||
|
|
c626685103 | ||
|
|
e233d15fbb | ||
|
|
a5d83900d9 | ||
|
|
d8dbeedcc2 | ||
|
|
56b67d62a3 | ||
|
|
dc084b4bd9 | ||
|
|
1bc0eab7ae | ||
|
|
ca14d1b108 | ||
|
|
d6757aced2 | ||
|
|
5c84eed292 | ||
|
|
b648387a57 | ||
|
|
5232f95c3f | ||
|
|
0742960ec4 | ||
|
|
4c41b7f8df | ||
|
|
c5d0c24237 | ||
|
|
8b5c51ceaa | ||
|
|
c09bceb66a | ||
|
|
e7afd3ec76 | ||
|
|
3d30be3472 | ||
|
|
e08367365f | ||
|
|
6692fbeed4 | ||
|
|
36a9e7a7cf | ||
|
|
49b5ff5fd9 | ||
|
|
1bde56a11a | ||
|
|
f47d7b6935 | ||
|
|
2dbad40a34 | ||
|
|
39f6a51794 | ||
|
|
7ec128c2e4 | ||
|
|
b55f0e849e | ||
|
|
f158390ba1 | ||
|
|
e4b245104a | ||
|
|
789497b07e | ||
|
|
5cf01ba099 | ||
|
|
e8669891f8 | ||
|
|
ac0f265a5b | ||
|
|
fcd6b430e1 | ||
|
|
f3ad8a9b80 | ||
|
|
43ff824da1 | ||
|
|
b8022610ca | ||
|
|
03defe203a | ||
|
|
aedcbe56b3 | ||
|
|
c365db460a | ||
|
|
cfdc4752d0 | ||
|
|
e1964ac5ed | ||
|
|
e17ab9cd8f | ||
|
|
edc15b8727 | ||
|
|
034f8d41fc | ||
|
|
0fa70db1e1 | ||
|
|
fa7ba9b72b | ||
|
|
3a20123e0f | ||
|
|
03510d1bc4 | ||
|
|
8b116f637a | ||
|
|
9c49de6552 | ||
|
|
842347bcab | ||
|
|
628b9c4853 | ||
|
|
d60b6ee33f | ||
|
|
4f4fcbbc70 | ||
|
|
cc896670dc | ||
|
|
eb2a497dee | ||
|
|
0cf5ce1416 | ||
|
|
988381d584 | ||
|
|
0b163eae23 | ||
|
|
3a3ff8a65e | ||
|
|
9f183a6dfc | ||
|
|
fadfd4e06c | ||
|
|
eb521faa8d | ||
|
|
6b572ca862 | ||
|
|
d075ec6716 | ||
|
|
14e6bf5ef9 | ||
|
|
b6bddd3b0e | ||
|
|
d5ed9b6cf5 | ||
|
|
7a9912d4e0 | ||
|
|
4d2ddceb4e | ||
|
|
ae2aed675a | ||
|
|
1d7cb41218 | ||
|
|
76db002eb5 | ||
|
|
ebb64ba177 | ||
|
|
1a685e2b68 | ||
|
|
9c561885a1 | ||
|
|
612e98b198 | ||
|
|
0c98082b25 |
26
.eslintrc.json
Normal file
26
.eslintrc.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"no-useless-constructor": 0,
|
||||
"no-unused-vars": 0,
|
||||
"no-prototype-builtins": 0,
|
||||
"require-atomic-updates": 0,
|
||||
"no-dupe-class-members": 0
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
lib-cov
|
||||
coverage.html
|
||||
src
|
||||
dist/bower
|
||||
10
.prettierrc
Normal file
10
.prettierrc
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"parser": "typescript",
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"quoteProps": "consistent",
|
||||
"bracketSpacing": false
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 6
|
||||
- 8
|
||||
- 10
|
||||
- 11
|
||||
- 12
|
||||
- 13
|
||||
script:
|
||||
- yarn compile
|
||||
- yarn test
|
||||
- yarn build
|
||||
- yarn lint
|
||||
|
||||
@@ -12,7 +12,7 @@ Warning: Use at your own risk.
|
||||
|
||||
## Data and visualizations
|
||||
|
||||
- **[Wipple - XRP Intelligence](https://wipple.devnull.network/)**
|
||||
- **[xrp1ntel - XRP Intelligence](https://xrp1ntel.com/)**
|
||||
|
||||
Monitor the XRP Network in real time and explore historical statistics.
|
||||
|
||||
@@ -36,7 +36,7 @@ Warning: Use at your own risk.
|
||||
|
||||
List of XRPL validators, nodes, and testnet validators.
|
||||
|
||||
- **[XRP Scan - XRP Ledger explorer](https://http://xrpscan.com)**
|
||||
- **[XRP Scan - XRP Ledger explorer](https://xrpscan.com)**
|
||||
|
||||
XRP Ledger explorer, metrics and analytics.
|
||||
|
||||
@@ -60,6 +60,10 @@ Warning: Use at your own risk.
|
||||
|
||||
## Wallets and wallet tools
|
||||
|
||||
- **[XRP Toolkit](https://www.xrptoolkit.com)**
|
||||
|
||||
A web interface to the XRP Ledger, supporting both hardware and software wallets.
|
||||
|
||||
- **[Toast Wallet](https://toastwallet.com/)**
|
||||
|
||||
A free, open source XRP Wallet for iOS, Android, Windows, Mac and Linux.
|
||||
|
||||
151
Gulpfile.js
151
Gulpfile.js
@@ -1,151 +0,0 @@
|
||||
/* eslint-disable no-var, no-param-reassign */
|
||||
/* these eslint rules are disabled because gulp does not support babel yet */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const assert = require('assert');
|
||||
const gulp = require('gulp');
|
||||
const webpack = require('webpack');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
|
||||
const pkg = require('./package.json');
|
||||
|
||||
const uglifyOptions = {
|
||||
mangle: {
|
||||
reserved: ['_', 'RippleError', 'RippledError', 'UnexpectedError',
|
||||
'LedgerVersionError', 'ConnectionError', 'NotConnectedError',
|
||||
'DisconnectedError', 'TimeoutError', 'ResponseFormatError',
|
||||
'ValidationError', 'NotFoundError', 'MissingLedgerHistoryError',
|
||||
'PendingLedgerVersionError'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
function getWebpackConfig(extension, overrides) {
|
||||
overrides = overrides || {};
|
||||
let defaults = {
|
||||
cache: true,
|
||||
externals: [{
|
||||
'lodash': '_'
|
||||
}],
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
library: 'ripple',
|
||||
path: path.join(__dirname, 'build/'),
|
||||
filename: `ripple-${pkg.version}${extension}`
|
||||
},
|
||||
plugins: [
|
||||
new webpack.NormalModuleReplacementPlugin(/^ws$/, './wswrapper'),
|
||||
new webpack.NormalModuleReplacementPlugin(/^\.\/wallet$/, './wallet-web'),
|
||||
new webpack.NormalModuleReplacementPlugin(/^.*setup-api$/,
|
||||
'./setup-api-web')
|
||||
],
|
||||
module: {
|
||||
rules: [{
|
||||
test: /jayson/,
|
||||
use: 'null',
|
||||
}, {
|
||||
test: /\.ts$/,
|
||||
use: [{
|
||||
loader: 'ts-loader',
|
||||
options: {
|
||||
compilerOptions: {
|
||||
composite: false,
|
||||
declaration: false,
|
||||
declarationMap: false
|
||||
}
|
||||
},
|
||||
}],
|
||||
}]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [ '.ts', '.js' ]
|
||||
},
|
||||
};
|
||||
return _.assign({}, defaults, overrides);
|
||||
}
|
||||
|
||||
function webpackConfigForWebTest(testFileName) {
|
||||
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: path.join(__dirname, 'test-compiled-for-web/'),
|
||||
filename: match[1] + '-test.js'
|
||||
}
|
||||
};
|
||||
return getWebpackConfig('.js', configOverrides);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
||||
function watch(callback) {
|
||||
gulp.watch('src/*', gulp.series(buildDebug));
|
||||
callback();
|
||||
}
|
||||
|
||||
function build(callback) {
|
||||
webpack(getWebpackConfig('.js'), createBuildLink(callback));
|
||||
}
|
||||
|
||||
function buildDebug(callback) {
|
||||
const webpackConfig = getWebpackConfig('-debug.js', {devtool: 'eval'});
|
||||
webpackConfig.plugins.unshift(new webpack.LoaderOptionsPlugin({debug: true}));
|
||||
webpack(webpackConfig, callback);
|
||||
}
|
||||
|
||||
function buildMin(callback) {
|
||||
const webpackConfig = getWebpackConfig('-min.js');
|
||||
webpackConfig.plugins.push(new UglifyJsPlugin({uglifyOptions}));
|
||||
webpack(webpackConfig, function() {
|
||||
createLink('./build/ripple-' + pkg.version + '-min.js',
|
||||
'./build/ripple-latest-min.js');
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function buildTests(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);
|
||||
}
|
||||
|
||||
exports.watch = watch;
|
||||
exports.build = build;
|
||||
exports.buildDebug = buildDebug;
|
||||
exports.buildMin = buildMin;
|
||||
exports.buildTests = buildTests;
|
||||
|
||||
exports.default = gulp.parallel(build, buildDebug, buildMin);
|
||||
130
HISTORY.md
130
HISTORY.md
@@ -1,5 +1,133 @@
|
||||
# ripple-lib Release History
|
||||
|
||||
## 1.6.1 (2020-01-14)
|
||||
|
||||
* Documentation
|
||||
* Document message type 'path_find' (#1121) (thanks @r0bertz)
|
||||
* Improve documentation for address generation; functions that can be called offline; generateXAddress() (#1158, #1159, #1169) (thanks @hbergren)
|
||||
* Add [Security Policy](https://github.com/ripple/ripple-lib/blob/develop/SECURITY.md)
|
||||
* Bug fixes
|
||||
* Types: Fix AccountObjectsResponse structure (#1127) (thanks @you21979)
|
||||
* In some cases, ripple-lib would fail to wait for the connection to be ready (#1119)
|
||||
* Dependencies
|
||||
* Update docs dependencies, ejs and doctoc (#1153)
|
||||
* Update eslint, ripple-lib-transactionparser, typescript, nyc, ws, @types/node, ripple-binary-codec, mocha, mocha-junit-reporter
|
||||
* Internal
|
||||
* Add LedgerHistory to Connection (#1119): Moved ledger logic into its own Ledger class
|
||||
|
||||
## 1.6.0 (2020-01-06)
|
||||
|
||||
* Add support for AccountDelete (#1120)
|
||||
* Improve error type given on rejected message _send to be DisconnectedError (#1098)
|
||||
* Internal
|
||||
* Add unit test for unhandled promise rejection warning on message _send (#1098)
|
||||
* Dependencies
|
||||
* Update @types/node, @typescript-eslint/parser
|
||||
|
||||
## 1.5.1 (2019-12-28)
|
||||
|
||||
* Fix support for CDNs (#1142)
|
||||
* Internal
|
||||
* Clean up connection trace logic (#1114)
|
||||
* Clean up the connection config (#1115)
|
||||
* Run prettier format (#1116)
|
||||
* Update eslint command (#1118)
|
||||
* Dependencies
|
||||
* Update webpack-cli, webpack, ts-node, @types/lodash, @types/ws, @types/node, @typescript-eslint/parser, @typescript-eslint/eslint-plugin, https-proxy-agent, mocha, eventemitter2
|
||||
|
||||
## 1.5.0 (2019-12-14)
|
||||
|
||||
* Add support for `WalletLocator` (#1083)
|
||||
* Types: Move and de-dupe `TransactionJSON` type (#1096)
|
||||
* This resolves an error surfaced by [TypeScript 3.7](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#local-and-imported-type-declarations-now-conflict)
|
||||
* Add a heartbeat to detect hung connections (#1101)
|
||||
* Dependencies
|
||||
* Update TypeScript version (#1096)
|
||||
* Update ripple-lib-transactionparser to 0.8.1 (#1097)
|
||||
* Update ripple-binary-codec to 0.2.5
|
||||
* Update webpack (#1112)
|
||||
* Require node 8 and yarn (#1107)
|
||||
* Testing: Refactor and add unit tests
|
||||
* Fix some errors caught by the improved tests
|
||||
|
||||
## 1.4.2 (2019-11-14)
|
||||
|
||||
* Add support for tick size (#1090) (thanks @RareData)
|
||||
* Update email hash default to allow proper clearing (#1089) (thanks @RareData)
|
||||
* Fix Unhandled Promise Rejection Warning on message `_send`
|
||||
* Add an immediate catch to the `_send` promise passed to `_whenReady` in case there is rejection before async handlers are added (#1092) (thanks @nickewansmith)
|
||||
* Docs improvements
|
||||
* Add XRP Toolkit reference (#1088)
|
||||
* Internal improvements
|
||||
* Add a prettier config
|
||||
* Update Node.js Testing Versions (#1085)
|
||||
* Testing matrix based on: https://nodejs.org/en/about/releases/
|
||||
- Node 11 is no longer supported (not LTS)
|
||||
- Node 12 added (active LTS)
|
||||
- Node 13 added ("current" release)
|
||||
|
||||
## 1.4.1 (2019-11-06)
|
||||
|
||||
* Compatibility: Change TypeScript compile target back to `es6` (#1071)
|
||||
* WARNING: This allows for the use of Node v6, which is no longer supported by Node.js, as it was end-of-life'd in April 2019
|
||||
* We recommend updating to Node v8/v10 ASAP in order to get security updates and fixes from the Node.js team
|
||||
* We are not actively running tests against Node v6 (ref #1076)
|
||||
* Docs: `getAccountObjects` doc fix
|
||||
* Dependencies:
|
||||
* Update `bignumber.js`
|
||||
* Update `ripple-keypairs`
|
||||
* Update `ws`
|
||||
* Build process: Update `webpack` flow
|
||||
|
||||
## 1.4.0 (2019-10-28)
|
||||
|
||||
* Unref timer so it does not hang the Node.js process
|
||||
* Add a 2-second timeout for connect()
|
||||
* Improve getTransaction() error when tx has not been validated yet
|
||||
* Add support for the new X-address format
|
||||
* Fix error in Safari, Chrome 78, Firefox 70
|
||||
* Some error messages have changed slightly. For example:
|
||||
* `-instance.Account is not of a type(s) string,instance.Account does not conform to the "address" format`
|
||||
* `+instance.Account is not of a type(s) string,instance.Account is not exactly one from <xAddress>,<classicAddress>`
|
||||
|
||||
### Internal improvements
|
||||
|
||||
* Reduce dependency size
|
||||
* Move tests to TypeScript
|
||||
* Replace tslint with eslint
|
||||
* Update https-proxy-agent
|
||||
* Add tests
|
||||
|
||||
## 1.3.4 (2019-10-18)
|
||||
|
||||
* Update ripple-lib-transactionparser
|
||||
* Improve error message when signing fails (e.g. due to trailing zeros)
|
||||
* Integrate ripple-hashes (in TypeScript with improved naming and docs)
|
||||
* Add multi-signing example to sign() method docs
|
||||
* Update TypeScript
|
||||
|
||||
## 1.3.3 (2019-09-10)
|
||||
|
||||
* Expand node version compatibility to support Node.js 12 ([ripple-binary-codec#32](https://github.com/ripple/ripple-binary-codec/issues/32))
|
||||
|
||||
## 1.3.2 (2019-09-03)
|
||||
|
||||
* Export and document `rippleTimeToISO8601()` method
|
||||
* Add type for isValidAddress (fixes error TS7016, #1032)
|
||||
* Docs: update recommended Node.js version (#1031)
|
||||
|
||||
When using this release with [rippled](https://github.com/ripple/rippled), we recommend using [rippled version 1.3.1](https://github.com/ripple/rippled/releases/tag/1.3.1) or later.
|
||||
|
||||
## 1.3.1 (2019-08-26)
|
||||
|
||||
* Upgrade to gulp 4 (#1030)
|
||||
* Remove http server (unused)
|
||||
* Update dependencies
|
||||
|
||||
There are no changes in the browser version with this release. The npm package for Node.js should be slightly smaller.
|
||||
|
||||
When using this release with [rippled](https://github.com/ripple/rippled), we recommend using [rippled version 1.3.1](https://github.com/ripple/rippled/releases/tag/1.3.1).
|
||||
|
||||
## 1.3.0 (2019-08-16)
|
||||
|
||||
### Bug fixes:
|
||||
@@ -334,7 +462,7 @@ f28921f57a133678dcb3cb54c497626bd76b1f953d22d61f3ddca31c8947d552 ripple-1.1.0-m
|
||||
The SHA-256 checksums for the browser version of this release can be found
|
||||
below.
|
||||
```
|
||||
% shasum -a 256 *
|
||||
% shasum -a 256 *
|
||||
2556fe17296e127ed44e7066e90a6175e2b164f00ca3c1aa7b1c554f31c688dd ripple-1.0.2-debug.js
|
||||
e0342ea21eac32a1024c62034fba09c6f26dd3e7371b23ea1e153e03135cd590 ripple-1.0.2-min.js
|
||||
c7286c517497d018d02d09257e81172b61d36c8b9885a077af68e8133c3b3b9b ripple-1.0.2.js
|
||||
|
||||
50
README.md
50
README.md
@@ -1,9 +1,13 @@
|
||||
# ripple-lib
|
||||
# ripple-lib (RippleAPI)
|
||||
|
||||
A JavaScript API for interacting with the XRP Ledger
|
||||
A JavaScript/TypeScript API for interacting with the XRP Ledger
|
||||
|
||||
[](https://www.npmjs.org/package/ripple-lib)
|
||||
|
||||
This is the recommended library for integrating a JavaScript/TypeScript app with the XRP Ledger, especially if you intend to use advanced functionality such as IOUs, payment paths, the decentralized exchange, account settings, payment channels, escrows, multi-signing, and more.
|
||||
|
||||
**What is ripple-lib used for?** Here's a [list of applications](APPLICATIONS.md) that use `ripple-lib`. Open a PR to add your app or project to the list!
|
||||
|
||||
### Features
|
||||
|
||||
+ Connect to a `rippled` server from Node.js or a web browser
|
||||
@@ -12,10 +16,6 @@ A JavaScript API for interacting with the XRP Ledger
|
||||
+ Sign and submit transactions to the XRP Ledger
|
||||
+ Type definitions for TypeScript
|
||||
|
||||
## Getting Started
|
||||
|
||||
See also: [RippleAPI Beginners Guide](https://xrpl.org/get-started-with-rippleapi-for-javascript.html)
|
||||
|
||||
### Requirements
|
||||
|
||||
+ **[Node v10](https://nodejs.org/)** is recommended. Other versions may work but are not frequently tested.
|
||||
@@ -28,9 +28,11 @@ In an existing project (with `package.json`), install `ripple-lib`:
|
||||
$ yarn add ripple-lib
|
||||
```
|
||||
|
||||
Then see the [documentation](https://github.com/ripple/ripple-lib/blob/develop/docs/index.md) and [code samples](https://github.com/ripple/ripple-lib/tree/develop/docs/samples).
|
||||
## Documentation
|
||||
|
||||
**What is ripple-lib used for?** Here's a [list of applications](APPLICATIONS.md) that use `ripple-lib`. Open a PR to add your app or project to the list!
|
||||
+ [RippleAPI Beginners Guide](https://xrpl.org/get-started-with-rippleapi-for-javascript.html)
|
||||
+ [RippleAPI Full Reference Documentation](https://xrpl.org/rippleapi-reference.html) ([in this repo](https://github.com/ripple/ripple-lib/blob/develop/docs/index.md))
|
||||
+ [Code Samples](https://github.com/ripple/ripple-lib/tree/develop/docs/samples)
|
||||
|
||||
### Mailing Lists
|
||||
|
||||
@@ -44,35 +46,41 @@ If you're using the XRP Ledger in production, you should run a [rippled server](
|
||||
|
||||
## Development
|
||||
|
||||
To build the library for Node.js:
|
||||
```
|
||||
$ yarn compile
|
||||
```
|
||||
|
||||
The TypeScript compiler will [output](./tsconfig.json#L7) the resulting JS files in `./dist/npm/`.
|
||||
|
||||
To build the library for the browser:
|
||||
To build the library for Node.js and the browser:
|
||||
```
|
||||
$ yarn build
|
||||
```
|
||||
|
||||
Gulp will [output](./Gulpfile.js) the resulting JS files in `./build/`.
|
||||
The TypeScript compiler will [output](./tsconfig.json#L7) the resulting JS files in `./dist/npm/`.
|
||||
|
||||
webpack will output the resulting JS files in `./build/`.
|
||||
|
||||
For details, see the `scripts` in `package.json`.
|
||||
|
||||
## Running Tests
|
||||
|
||||
### Unit Tests
|
||||
|
||||
1. Clone the repository
|
||||
2. `cd` into the repository and install dependencies with `yarn install`
|
||||
3. `yarn test`
|
||||
|
||||
Also, run `yarn lint` to lint the code with `tslint`.
|
||||
### Linting
|
||||
|
||||
Run `yarn lint` to lint the code with `tslint`.
|
||||
|
||||
## Generating Documentation
|
||||
|
||||
The continuous integration tests require that the documentation stays up-to-date. If you make changes to the JSON schemas, fixtures, or documentation sources, you must update the documentation by running `yarn run docgen`.
|
||||
Do not edit `./docs/index.md` directly because it is a generated file.
|
||||
|
||||
Instead, edit the appropriate `.md.ejs` files in `./docs/src/`.
|
||||
|
||||
If you make changes to the JSON schemas, fixtures, or documentation sources, update the documentation by running `yarn run docgen`.
|
||||
|
||||
## More Information
|
||||
|
||||
+ [RippleAPI Reference](https://developers.ripple.com/rippleapi-reference.html) - XRP Ledger Dev Portal
|
||||
+ [XRP Ledger Dev Portal](https://developers.ripple.com/)
|
||||
+ [ripple-lib-announce mailing list](https://groups.google.com/forum/#!forum/ripple-lib-announce) - subscribe for release announcements
|
||||
+ [RippleAPI Reference](https://xrpl.org/rippleapi-reference.html) - XRP Ledger Dev Portal
|
||||
+ [XRP Ledger Dev Portal](https://xrpl.org/)
|
||||
|
||||
[](https://travis-ci.org/ripple/ripple-lib)
|
||||
|
||||
29
SECURITY.md
Normal file
29
SECURITY.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
This table shows which versions of ripple-lib are currently supported with security updates:
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ---------------------- |
|
||||
| 1.x | :white_check_mark: Yes |
|
||||
| 0.x | :x: No |
|
||||
|
||||
## Responsible disclosure security policy
|
||||
|
||||
The responsible disclosure of vulnerabilities helps to protect users of the project. Vulnerabilities are first triaged in a private manner, and only publicly disclosed after a reasonable time period that allows patching the vulnerability and provides an upgrade path for users.
|
||||
|
||||
When contacting us directly via email, we will do our best to respond in a reasonable time to resolve the issue. Do not disclose the vulnerability until it has been patched and users have been given time to upgrade.
|
||||
|
||||
We kindly ask you to refrain from malicious acts that put our users, the project, or any of the project’s team members at risk.
|
||||
|
||||
## Reporting a security issue
|
||||
|
||||
Security is a top priority. But no matter how much effort we put into security, there can still be vulnerabilities present.
|
||||
|
||||
If you discover a security vulnerability, please use the following means of communications to report it to us:
|
||||
|
||||
- Report the security issue to bugs@ripple.com
|
||||
- [Ripple Bug Bounty](https://ripple.com/bug-bounty/)
|
||||
|
||||
Your efforts to responsibly disclose your findings are sincerely appreciated and will be taken into account to acknowledge your contributions.
|
||||
313
docs/index.md
313
docs/index.md
@@ -80,6 +80,7 @@
|
||||
- [sign](#sign)
|
||||
- [combine](#combine)
|
||||
- [submit](#submit)
|
||||
- [generateXAddress](#generatexaddress)
|
||||
- [generateAddress](#generateaddress)
|
||||
- [isValidAddress](#isvalidaddress)
|
||||
- [isValidSecret](#isvalidsecret)
|
||||
@@ -91,6 +92,7 @@
|
||||
- [xrpToDrops](#xrptodrops)
|
||||
- [dropsToXrp](#dropstoxrp)
|
||||
- [iso8601ToRippleTime](#iso8601torippletime)
|
||||
- [rippleTimeToISO8601](#rippletimetoiso8601)
|
||||
- [txFlags](#txflags)
|
||||
- [schemaValidator](#schemavalidator)
|
||||
- [schemaValidate](#schemavalidate)
|
||||
@@ -104,7 +106,8 @@
|
||||
|
||||
# Introduction
|
||||
|
||||
RippleAPI (ripple-lib) is the official client library to the XRP Ledger. Currently, RippleAPI is only available in JavaScript.
|
||||
RippleAPI (ripple-lib) is the official client library to the XRP Ledger. Currently, RippleAPI is only available in JavaScript/TypeScript.
|
||||
|
||||
Using RippleAPI, you can:
|
||||
|
||||
* [Query transactions from the XRP Ledger history](#gettransaction)
|
||||
@@ -113,6 +116,9 @@ Using RippleAPI, you can:
|
||||
* [Generate a new XRP Ledger Address](#generateaddress)
|
||||
* ... and [much more](#api-methods).
|
||||
|
||||
This page contains documentation for ripple-lib. To use ripple-lib with npm/yarn, begin with the [Getting Started](https://github.com/ripple/ripple-lib#getting-started) steps.
|
||||
|
||||
**What is ripple-lib used for?** Here's a [list of applications that use `ripple-lib`](https://github.com/ripple/ripple-lib/blob/develop/APPLICATIONS.md). Open a PR to add your app or project to the list!
|
||||
## Boilerplate
|
||||
|
||||
Use the following [boilerplate code](https://en.wikipedia.org/wiki/Boilerplate_code) to wrap your custom code using RippleAPI.
|
||||
@@ -141,7 +147,7 @@ api.connect().then(() => {
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
RippleAPI is designed to work in [Node.js](https://nodejs.org) version **6.11.3**. RippleAPI may work on older Node.js versions if you use [Babel](https://babeljs.io/) for [ECMAScript 6](https://babeljs.io/docs/learn-es2015/) support.
|
||||
RippleAPI is designed to work in [Node.js](https://nodejs.org) version 6 or higher. Ripple recommends Node.js v10 LTS.
|
||||
|
||||
The code samples in this documentation are written with ECMAScript 6 (ES6) features, but `RippleAPI` also works with ECMAScript 5 (ES5). Regardless of whether you use ES5 or ES6, the methods that return Promises return [ES6-style promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
|
||||
@@ -188,7 +194,6 @@ If you omit the `server` parameter, RippleAPI operates [offline](#offline-functi
|
||||
After you have installed ripple-lib, you can create scripts using the [boilerplate](#boilerplate) and run them using the Node.js executable, typically named `node`:
|
||||
|
||||
`node script.js`
|
||||
|
||||
## 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.
|
||||
@@ -214,8 +219,8 @@ Methods that depend on the state of the XRP Ledger are unavailable in offline mo
|
||||
* [prepareEscrowExecution](#prepareescrowexecution)
|
||||
* [sign](#sign)
|
||||
* [generateAddress](#generateaddress)
|
||||
* [generateXAddress](#generatexaddress)
|
||||
* [computeLedgerHash](#computeledgerhash)
|
||||
|
||||
# Basic Types
|
||||
|
||||
## Address
|
||||
@@ -224,7 +229,19 @@ Methods that depend on the state of the XRP Ledger are unavailable in offline mo
|
||||
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||
```
|
||||
|
||||
Every XRP Ledger account has an *address*, which is a base58-encoding of a hash of the account's public key. XRP Ledger addresses always start with the lowercase letter `r`.
|
||||
```json
|
||||
"X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ"
|
||||
```
|
||||
|
||||
An *address* refers to a specific XRP Ledger account. It is a base-58 encoding of a hash of the account's public key. There are two kinds of addresses in common use:
|
||||
|
||||
### Classic Address
|
||||
|
||||
A *classic address* encodes a hash of the account's public key and a checksum. It has no other data. This kind of address always starts with the lowercase letter `r`.
|
||||
|
||||
### X-address
|
||||
|
||||
An *X-address* encodes a hash of the account's public key, a tag, and a checksum. This kind of address starts with the uppercase letter `X` if it is intended for use on the production XRP Ledger (mainnet). It starts with the uppercase letter `T` if it is intended for use on a test network such as Testnet or Devnet.
|
||||
|
||||
## Account Sequence Number
|
||||
|
||||
@@ -275,7 +292,6 @@ Name | Type | Description
|
||||
currency | [currency](#currency) | The three-character code or hexadecimal string used to denote currencies, or "drops" for the smallest unit of XRP.
|
||||
counterparty | [address](#address) | *Optional* The XRP Ledger address of the account that owes or is owed the funds (omitted if `currency` is "XRP" or "drops")
|
||||
value | [value](#value) | *Optional* The quantity of the currency, denoted as a string to retain floating point precision
|
||||
|
||||
# Transaction Overview
|
||||
|
||||
## Transaction Types
|
||||
@@ -362,7 +378,6 @@ 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
|
||||
|
||||
A *transaction specification* specifies what a transaction should do. Each [Transaction Type](#transaction-types) has its own type of specification.
|
||||
@@ -376,12 +391,12 @@ Name | Type | Description
|
||||
source | object | The source of the funds to be sent.
|
||||
*source.* address | [address](#address) | The address to send from.
|
||||
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with source.maxAmount)
|
||||
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||
*source.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
|
||||
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field cannot be used with source.amount)
|
||||
destination | object | The destination of the funds to be sent.
|
||||
*destination.* address | [address](#address) | An address representing the destination of the transaction.
|
||||
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with `destination.minAmount`.)
|
||||
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||
*destination.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
|
||||
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field cannot be used with destination.amount)
|
||||
allowPartialPayment | boolean | *Optional* If true, this payment should proceed even if the whole amount cannot be delivered due to a lack of liquidity or a lack of funds in the source account.
|
||||
invoiceID | string | *Optional* A 256-bit hash that can be used to identify a particular payment.
|
||||
@@ -522,7 +537,7 @@ defaultRipple | boolean | *Optional* Enable [rippling](https://ripple.com/build/
|
||||
depositAuth | boolean | *Optional* Enable [Deposit Authorization](https://ripple.com/build/deposit-authorization/) on this account. If set, transactions cannot send value of any kind to this account unless the sender of those transactions is the account itself. (Requires the [DepositAuth amendment](https://ripple.com/build/known-amendments/#depositauth))
|
||||
disableMasterKey | boolean | *Optional* Disallows use of the master key to sign transactions for this account. To disable the master key, you must authorize the transaction by signing it with the master key pair. You cannot use a regular key pair or a multi-signature. You can re-enable the master key pair using a regular key pair or multi-signature. See [AccountSet](https://developers.ripple.com/accountset.html).
|
||||
disallowIncomingXRP | boolean | *Optional* Indicates that client applications should not send XRP to this account. Not enforced by rippled.
|
||||
domain | string | *Optional* The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase.
|
||||
domain | string | *Optional* The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase.
|
||||
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.
|
||||
globalFreeze | boolean | *Optional* Freeze all assets issued by this account.
|
||||
@@ -537,9 +552,11 @@ signers | object | *Optional* Settings that determine what sets of accounts can
|
||||
*signers.* threshold | integer | A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`.
|
||||
*signers.* weights | array | *Optional* Weights of signatures for each signer.
|
||||
*signers.* weights[] | object | An association of an address and a weight.
|
||||
*signers.weights[].* address | [address](#address) | A Ripple account address
|
||||
*signers.weights[].* address | [address](#address) | An account address on the XRP Ledger
|
||||
*signers.weights[].* weight | integer | The weight that the signature of this account counts as towards the threshold.
|
||||
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.
|
||||
tickSize | string | *Optional* Tick size to use for offers involving a currency issued by this address. The exchange rates of those offers is rounded to this many significant digits. Valid values are 3 to 15 inclusive, or 0 to disable.
|
||||
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.
|
||||
walletLocator | string,null | *Optional* Transaction hash or any other 64 character hexadecimal string, that may or may not represent the result of a hash operation. Use `null` to clear.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -771,15 +788,16 @@ signature | string | *Optional* Signed claim authorizing withdrawal of XRP from
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# rippled APIs
|
||||
|
||||
ripple-lib relies on [rippled APIs](https://ripple.com/build/rippled-apis/) for all online functionality. With ripple-lib version 1.0.0 and higher, you can easily access rippled APIs through ripple-lib. Use the `request()`, `hasNextPage()`, and `requestNextPage()` methods:
|
||||
* Use `request()` to issue any `rippled` command, including `account_currencies`, `subscribe`, and `unsubscribe`. [Full list of API Methods](https://ripple.com/build/rippled-apis/#api-methods).
|
||||
ripple-lib relies on [rippled APIs](https://ripple.com/build/rippled-apis/) for online functionality. In addition to ripple-lib's own methods, you can also access rippled APIs through ripple-lib. Use the `request()`, `hasNextPage()`, and `requestNextPage()` methods:
|
||||
|
||||
* Use `request()` to issue any `rippled` command, including `account_currencies`, `subscribe`, and `unsubscribe`. [Full list of API Methods](https://ripple.com/build/rippled-apis/#api-methods).
|
||||
* Use `hasNextPage()` to determine whether a response has more pages. This is true when the response includes a [`marker` field](https://ripple.com/build/rippled-apis/#markers-and-pagination).
|
||||
* Use `requestNextPage()` to request the next page of data.
|
||||
|
||||
When using rippled APIs:
|
||||
|
||||
* [Specify XRP amounts in drops](https://developers.ripple.com/basic-data-types.html#specifying-currency-amounts).
|
||||
* [Specify timestamps as the number of seconds since the "Ripple Epoch"](https://developers.ripple.com/basic-data-types.html#specifying-time).
|
||||
* Instead of `counterparty`, use `issuer`.
|
||||
@@ -811,6 +829,7 @@ Type | Description
|
||||
`manifestReceived` | Sent by the `manifests` stream when the server receives a manifest.
|
||||
`transaction` | Sent by many subscriptions including `transactions`, `transactions_proposed`, `accounts`, `accounts_proposed`, and `book` (Order Book). See [Transaction Streams](https://ripple.com/build/rippled-apis/#transaction-streams) for details.
|
||||
`peerStatusChange` | (Admin-only) Reports a large amount of information on the activities of other `rippled` servers to which the server is connected.
|
||||
`path_find` | Asynchronous follow-up response to the currently open path\_find request. See [rippled path\_find method](https://xrpl.org/path_find.html) for details.
|
||||
|
||||
To register your listener function, use `connection.on(type, handler)`.
|
||||
|
||||
@@ -841,7 +860,6 @@ api.connect().then(() => { // Omit this if you are already connected
|
||||
The subscription ends when you unsubscribe or the WebSocket connection is closed.
|
||||
|
||||
For full details, see [rippled Subscriptions](https://ripple.com/build/rippled-apis/#subscriptions).
|
||||
|
||||
## request
|
||||
|
||||
`request(command: string, options: object): Promise<object>`
|
||||
@@ -850,7 +868,7 @@ Returns the response from invoking the specified command, with the specified opt
|
||||
|
||||
Refer to [rippled APIs](https://ripple.com/build/rippled-apis/) for commands and options. All XRP amounts must be specified in drops. One drop is equal to 0.000001 XRP. See [Specifying Currency Amounts](https://ripple.com/build/rippled-apis/#specifying-currency-amounts).
|
||||
|
||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://ripple.com/build/rippled-apis/#specifying-ledgers).
|
||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://xrpl.org/basic-data-types.html#specifying-ledgers).
|
||||
|
||||
### Return Value
|
||||
|
||||
@@ -895,7 +913,6 @@ return api.request('ledger', {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## hasNextPage
|
||||
|
||||
`hasNextPage(currentResponse): boolean`
|
||||
@@ -923,7 +940,6 @@ return api.request('ledger_data', {
|
||||
}
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
## requestNextPage
|
||||
|
||||
`requestNextPage(command: string, params: object = {}, currentResponse: object): Promise<object>`
|
||||
@@ -954,9 +970,7 @@ return api.request(command, params).then(response => {
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
|
||||
# Static Methods
|
||||
|
||||
## renameCounterpartyToIssuer
|
||||
|
||||
`renameCounterpartyToIssuer(issue: {currency: string, counterparty: address}): {currency: string, issuer: address}`
|
||||
@@ -994,7 +1008,6 @@ console.log(RippleAPI.renameCounterpartyToIssuer(orderbookInfo.counter))
|
||||
{ currency: 'USD', issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' }
|
||||
{ currency: 'BTC', issuer: 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B' }
|
||||
```
|
||||
|
||||
## formatBidsAndAsks
|
||||
|
||||
`formatBidsAndAsks(orderbookInfo: {base: Issue, counter: Issue}, offers: BookOffer[]): orderbook`
|
||||
@@ -1273,9 +1286,7 @@ return Promise.all(
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
# API Methods
|
||||
|
||||
## connect
|
||||
|
||||
`connect(): Promise<void>`
|
||||
@@ -1293,7 +1304,6 @@ This method returns a promise that resolves with a void value when a connection
|
||||
### Example
|
||||
|
||||
See [Boilerplate](#boilerplate) for code sample.
|
||||
|
||||
## disconnect
|
||||
|
||||
`disconnect(): Promise<void>`
|
||||
@@ -1311,7 +1321,6 @@ This method returns a promise that resolves with a void value when a connection
|
||||
### Example
|
||||
|
||||
See [Boilerplate](#boilerplate) for code sample
|
||||
|
||||
## isConnected
|
||||
|
||||
`isConnected(): boolean`
|
||||
@@ -1335,7 +1344,6 @@ return api.isConnected();
|
||||
```json
|
||||
true
|
||||
```
|
||||
|
||||
## getServerInfo
|
||||
|
||||
`getServerInfo(): Promise<object>`
|
||||
@@ -1409,7 +1417,6 @@ return api.getServerInfo().then(info => {/* ... */});
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getFee
|
||||
|
||||
`getFee(): Promise<string>`
|
||||
@@ -1437,7 +1444,6 @@ return api.getFee().then(fee => {/* ... */});
|
||||
```json
|
||||
"0.000012"
|
||||
```
|
||||
|
||||
## getLedgerVersion
|
||||
|
||||
`getLedgerVersion(): Promise<number>`
|
||||
@@ -1464,7 +1470,6 @@ return api.getLedgerVersion().then(ledgerVersion => {
|
||||
16869039
|
||||
```
|
||||
|
||||
|
||||
## getTransaction
|
||||
|
||||
`getTransaction(id: string, options: object): Promise<object>`
|
||||
@@ -1616,7 +1621,6 @@ return api.getTransaction(id).then(transaction => {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getTransactions
|
||||
|
||||
`getTransactions(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -1685,6 +1689,7 @@ return api.getTransactions(address).then(transaction => {
|
||||
},
|
||||
"outcome": {
|
||||
"result": "tesSUCCESS",
|
||||
"timestamp": "2019-04-01T07:39:01.000Z",
|
||||
"fee": "0.00001",
|
||||
"deliveredAmount": {
|
||||
"currency": "USD",
|
||||
@@ -1778,6 +1783,7 @@ return api.getTransactions(address).then(transaction => {
|
||||
},
|
||||
"outcome": {
|
||||
"result": "tesSUCCESS",
|
||||
"timestamp": "2019-04-01T07:39:01.000Z",
|
||||
"fee": "0.00001",
|
||||
"deliveredAmount": {
|
||||
"currency": "USD",
|
||||
@@ -1848,7 +1854,6 @@ return api.getTransactions(address).then(transaction => {
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getTrustlines
|
||||
|
||||
`getTrustlines(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -1993,7 +1998,6 @@ return api.getTrustlines(address).then(trustlines =>
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getBalances
|
||||
|
||||
`getBalances(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -2160,7 +2164,6 @@ return api.getBalances(address).then(balances =>
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getBalanceSheet
|
||||
|
||||
`getBalanceSheet(address: string, options: object): Promise<object>`
|
||||
@@ -2256,7 +2259,6 @@ return api.getBalanceSheet(address).then(balanceSheet =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getPaths
|
||||
|
||||
`getPaths(pathfind: object): Promise<Array<object>>`
|
||||
@@ -2288,12 +2290,12 @@ Name | Type | Description
|
||||
source | object | Properties of the source of the payment.
|
||||
*source.* address | [address](#address) | The address to send from.
|
||||
*source.* amount | [laxAmount](#amount) | An exact amount to send. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with source.maxAmount)
|
||||
*source.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||
*source.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
|
||||
*source.* maxAmount | [laxAmount](#amount) | The maximum amount to send. (This field cannot be used with source.amount)
|
||||
destination | object | Properties of the destination of the payment.
|
||||
*destination.* address | [address](#address) | An address representing the destination of the transaction.
|
||||
*destination.* amount | [laxAmount](#amount) | An exact amount to deliver to the recipient. If the counterparty is not specified, amounts with any counterparty may be used. (This field cannot be used with `destination.minAmount`.)
|
||||
*destination.* tag | integer | *Optional* An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.
|
||||
*destination.* tag | integer | *Optional* An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.
|
||||
*destination.* minAmount | [laxAmount](#amount) | The minimum amount to be delivered. (This field cannot be used with destination.amount)
|
||||
paths | string | The paths of trustlines and orders to use in executing the payment.
|
||||
|
||||
@@ -2377,7 +2379,6 @@ return api.getPaths(pathfind)
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getOrders
|
||||
|
||||
`getOrders(address: string, options: object): Promise<Array<object>>`
|
||||
@@ -2758,7 +2759,6 @@ return api.getOrders(address).then(orders =>
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## getOrderbook
|
||||
|
||||
`getOrderbook(address: string, orderbook: object, options: object): Promise<object>`
|
||||
@@ -3864,7 +3864,6 @@ return api.getOrderbook(address, orderbook)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getSettings
|
||||
|
||||
`getSettings(address: string, options: object): Promise<object>`
|
||||
@@ -3890,7 +3889,7 @@ defaultRipple | boolean | *Optional* Enable [rippling](https://ripple.com/build/
|
||||
depositAuth | boolean | *Optional* Enable [Deposit Authorization](https://ripple.com/build/deposit-authorization/) on this account. If set, transactions cannot send value of any kind to this account unless the sender of those transactions is the account itself. (Requires the [DepositAuth amendment](https://ripple.com/build/known-amendments/#depositauth))
|
||||
disableMasterKey | boolean | *Optional* Disallows use of the master key to sign transactions for this account. To disable the master key, you must authorize the transaction by signing it with the master key pair. You cannot use a regular key pair or a multi-signature. You can re-enable the master key pair using a regular key pair or multi-signature. See [AccountSet](https://developers.ripple.com/accountset.html).
|
||||
disallowIncomingXRP | boolean | *Optional* Indicates that client applications should not send XRP to this account. Not enforced by rippled.
|
||||
domain | string | *Optional* The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase.
|
||||
domain | string | *Optional* The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase.
|
||||
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.
|
||||
globalFreeze | boolean | *Optional* Freeze all assets issued by this account.
|
||||
@@ -3905,9 +3904,11 @@ signers | object | *Optional* Settings that determine what sets of accounts can
|
||||
*signers.* threshold | integer | A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`.
|
||||
*signers.* weights | array | *Optional* Weights of signatures for each signer.
|
||||
*signers.* weights[] | object | An association of an address and a weight.
|
||||
*signers.weights[].* address | [address](#address) | A Ripple account address
|
||||
*signers.weights[].* address | [address](#address) | An account address on the XRP Ledger
|
||||
*signers.weights[].* weight | integer | The weight that the signature of this account counts as towards the threshold.
|
||||
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.
|
||||
tickSize | string | *Optional* Tick size to use for offers involving a currency issued by this address. The exchange rates of those offers is rounded to this many significant digits. Valid values are 3 to 15 inclusive, or 0 to disable.
|
||||
transferRate | number,null | *Optional* The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.
|
||||
walletLocator | string,null | *Optional* Transaction hash or any other 64 character hexadecimal string, that may or may not represent the result of a hash operation. Use `null` to clear.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -3923,8 +3924,10 @@ return api.getSettings(address).then(settings =>
|
||||
"requireDestinationTag": true,
|
||||
"disallowIncomingXRP": true,
|
||||
"emailHash": "23463B99B62A72F26ED677CC556C44E8",
|
||||
"walletLocator": "00000000000000000000000000000000000000000000000000000000DEADBEEF",
|
||||
"domain": "example.com",
|
||||
"transferRate": 1.002,
|
||||
"tickSize": 5,
|
||||
"signers": {
|
||||
"threshold": 3,
|
||||
"weights": [
|
||||
@@ -3943,7 +3946,6 @@ return api.getSettings(address).then(settings =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getAccountInfo
|
||||
|
||||
`getAccountInfo(address: string, options: object): Promise<object>`
|
||||
@@ -3992,7 +3994,6 @@ return api.getAccountInfo(address).then(info =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getAccountObjects
|
||||
|
||||
`getAccountObjects(address: string, options: object): Promise<AccountObjectsResponse>`
|
||||
@@ -4028,12 +4029,13 @@ limit | integer | *Optional* (May be omitted) The limit that was used in this re
|
||||
validated | boolean | *Optional* If included and set to true, the information in this request comes from a validated ledger version. Otherwise, the information is subject to change.
|
||||
|
||||
The types of objects that may be returned include:
|
||||
* Offer objects for orders that are currently live, unfunded, or expired but not yet removed.
|
||||
* RippleState objects for trust lines where this account's side is not in the default state.
|
||||
* A SignerList object if the account has multi-signing enabled.
|
||||
* Escrow objects for held payments that have not yet been executed or canceled.
|
||||
* PayChannel objects for open payment channels.
|
||||
* Check objects for pending checks.
|
||||
|
||||
* `Offer` objects for orders that are currently live, unfunded, or expired but not yet removed.
|
||||
* `RippleState` objects for trust lines where this account's side is not in the default state.
|
||||
* A `SignerList` object if the account has multi-signing enabled.
|
||||
* `Escrow` objects for held payments that have not yet been executed or canceled.
|
||||
* `PayChannel` objects for open payment channels.
|
||||
* `Check` objects for pending checks.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -4316,7 +4318,6 @@ return api.getAccountObjects(address: address).then(objects =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getPaymentChannel
|
||||
|
||||
`getPaymentChannel(id: string): Promise<object>`
|
||||
@@ -4372,7 +4373,6 @@ return api.getPaymentChannel(channelId).then(channel =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## getLedger
|
||||
|
||||
`getLedger(options: object): Promise<object>`
|
||||
@@ -4436,7 +4436,6 @@ return api.getLedger()
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## parseAccountFlags
|
||||
|
||||
`parseAccountFlags(Flags: number): object`
|
||||
@@ -4472,7 +4471,6 @@ console.log(JSON.stringify(flags, null, 2))
|
||||
"defaultRipple": false
|
||||
}
|
||||
```
|
||||
|
||||
## prepareTransaction
|
||||
|
||||
`prepareTransaction(transaction: object, instructions: object): Promise<object>`
|
||||
@@ -4481,7 +4479,7 @@ Prepare a transaction. The prepared transaction must subsequently be [signed](#s
|
||||
|
||||
This method works with any of [the transaction types supported by rippled](https://developers.ripple.com/transaction-types.html).
|
||||
|
||||
Notably, this is the preferred method for preparing a `DepositPreauth` transaction (added in rippled 1.1.0).
|
||||
Notably, this is the preferred method for preparing `DepositPreauth` or `AccountDelete` transactions.
|
||||
|
||||
### Parameters
|
||||
|
||||
@@ -4530,7 +4528,6 @@ async function preparedPreauth() {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## preparePayment
|
||||
|
||||
`preparePayment(address: string, payment: object, instructions: object): Promise<object>`
|
||||
@@ -4603,7 +4600,6 @@ return api.preparePayment(address, payment).then(prepared => {
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareTrustline
|
||||
|
||||
`prepareTrustline(address: string, trustline: object, instructions: object): Promise<object>`
|
||||
@@ -4671,7 +4667,6 @@ return api.prepareTrustline(address, trustline).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareOrder
|
||||
|
||||
`prepareOrder(address: string, order: object, instructions: object): Promise<object>`
|
||||
@@ -4739,7 +4734,6 @@ return api.prepareOrder(address, order)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareOrderCancellation
|
||||
|
||||
`prepareOrderCancellation(address: string, orderCancellation: object, instructions: object): Promise<object>`
|
||||
@@ -4792,7 +4786,6 @@ return api.prepareOrderCancellation(address, orderCancellation)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareSettings
|
||||
|
||||
`prepareSettings(address: string, settings: object, instructions: object): Promise<object>`
|
||||
@@ -4856,7 +4849,6 @@ return api.prepareSettings(address, settings)
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareEscrowCreation
|
||||
|
||||
`prepareEscrowCreation(address: string, escrowCreation: object, instructions: object): Promise<object>`
|
||||
@@ -4920,7 +4912,6 @@ return api.prepareEscrowCreation(address, escrowCreation).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareEscrowCancellation
|
||||
|
||||
`prepareEscrowCancellation(address: string, escrowCancellation: object, instructions: object): Promise<object>`
|
||||
@@ -4976,7 +4967,6 @@ return api.prepareEscrowCancellation(address, escrowCancellation).then(prepared
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareEscrowExecution
|
||||
|
||||
`prepareEscrowExecution(address: string, escrowExecution: object, instructions: object): Promise<object>`
|
||||
@@ -5034,7 +5024,6 @@ return api.prepareEscrowExecution(address, escrowExecution).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## preparePaymentChannelCreate
|
||||
|
||||
`preparePaymentChannelCreate(address: string, paymentChannelCreate: object, instructions: object): Promise<object>`
|
||||
@@ -5092,7 +5081,6 @@ return api.preparePaymentChannelCreate(address, paymentChannelCreate).then(prepa
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## preparePaymentChannelClaim
|
||||
|
||||
`preparePaymentChannelClaim(address: string, paymentChannelClaim: object, instructions: object): Promise<object>`
|
||||
@@ -5147,7 +5135,6 @@ return api.preparePaymentChannelClaim(address, paymentChannelClaim).then(prepare
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## preparePaymentChannelFund
|
||||
|
||||
`preparePaymentChannelFund(address: string, paymentChannelFund: object, instructions: object): Promise<object>`
|
||||
@@ -5203,7 +5190,6 @@ return api.preparePaymentChannelFund(address, paymentChannelFund).then(prepared
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareCheckCreate
|
||||
|
||||
`prepareCheckCreate(address: string, checkCreate: object, instructions: object): Promise<object>`
|
||||
@@ -5262,7 +5248,6 @@ return api.prepareCheckCreate(address, checkCreate).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareCheckCancel
|
||||
|
||||
`prepareCheckCancel(address: string, checkCancel: object, instructions: object): Promise<object>`
|
||||
@@ -5317,7 +5302,6 @@ return api.prepareCheckCancel(address, checkCancel).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## prepareCheckCash
|
||||
|
||||
`prepareCheckCash(address: string, checkCash: object, instructions: object): Promise<object>`
|
||||
@@ -5376,7 +5360,6 @@ return api.prepareCheckCash(address, checkCash).then(prepared =>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## sign
|
||||
|
||||
```
|
||||
@@ -5402,6 +5385,8 @@ options | object | *Optional* Options that control the type of signature that wi
|
||||
*options.* signAs | [address](#address) | *Optional* The account that the signature should count for in multisigning.
|
||||
secret | secret string | *Optional* The secret of the account that is initiating the transaction. (This field cannot be used with keypair).
|
||||
|
||||
When this method is used for multisigning, the `options` parameter is required. See the multisigning example in this section for more details.
|
||||
|
||||
### Return Value
|
||||
|
||||
This method returns an object with the following structure:
|
||||
@@ -5429,6 +5414,93 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
|
||||
```
|
||||
|
||||
|
||||
### Example (multisigning)
|
||||
|
||||
```javascript
|
||||
const RippleAPI = require('ripple-lib').RippleAPI;
|
||||
|
||||
// jon's address will have a multi-signing setup with a quorum of 2
|
||||
const jon = {
|
||||
account: 'rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj',
|
||||
secret: 'sh4Va7b1wQof8knHFV2sxwX12fSgK'
|
||||
};
|
||||
const aya = {
|
||||
account: 'rnrPdBjs98fFFfmRpL6hM7exT788SWQPFN',
|
||||
secret: 'snaMuMrXeVc2Vd4NYvHofeGNjgYoe'
|
||||
};
|
||||
const bran = {
|
||||
account: 'rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH',
|
||||
secret: 'shQtQ8Um5MS218yvEU3Ehy1eZQKqH'
|
||||
};
|
||||
|
||||
// Setup the signers list with a quorum of 2
|
||||
const multiSignSetupTransaction = {
|
||||
"Flags": 0,
|
||||
"TransactionType": "SignerListSet",
|
||||
"Account": "rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj",
|
||||
"Fee": "120",
|
||||
"SignerQuorum": 2,
|
||||
"SignerEntries": [
|
||||
{
|
||||
"SignerEntry": {
|
||||
"Account": "rnrPdBjs98fFFfmRpL6hM7exT788SWQPFN",
|
||||
"SignerWeight": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"SignerEntry": {
|
||||
"Account": "rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH",
|
||||
"SignerWeight": 1
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
// a transaction which requires multi signing
|
||||
const multiSignPaymentTransaction = {
|
||||
TransactionType: 'Payment',
|
||||
Account: 'rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj',
|
||||
Destination: 'rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH',
|
||||
Amount: '88000000'
|
||||
};
|
||||
|
||||
const api = new RippleAPI({
|
||||
server: 'wss://s.altnet.rippletest.net:51233'
|
||||
});
|
||||
|
||||
api.connect().then(() => {
|
||||
// adding the multi signing feature to jon's account
|
||||
api.prepareTransaction(multiSignSetupTransaction).then((prepared) => {
|
||||
console.log(prepared);
|
||||
jonSign = api.sign(prepared.txJSON, jon.secret).signedTransaction;
|
||||
api.submit(jonSign).then( response => {
|
||||
console.log(response.resultCode, response.resultMessage);
|
||||
|
||||
// multi sign a transaction
|
||||
api.prepareTransaction(multiSignPaymentTransaction).then(prepared => {
|
||||
console.log(prepared);
|
||||
|
||||
// Aya and Bran sign it too but with 'signAs' set to their own account
|
||||
let ayaSign = api.sign(prepared.txJSON, aya.secret, {'signAs': aya.account}).signedTransaction;
|
||||
let branSign = api.sign(prepared.txJSON, bran.secret, {'signAs': bran.account}).signedTransaction;
|
||||
|
||||
// signatures are combined and submitted
|
||||
let combinedTx = api.combine([ayaSign, branSign]);
|
||||
api.submit(combinedTx.signedTransaction).then(response => {
|
||||
console.log(response.tx_json.hash);
|
||||
return api.disconnect();
|
||||
}).catch(console.error);
|
||||
}).catch(console.error);
|
||||
}).catch(console.error)
|
||||
}).catch(console.error);
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
Assuming the multisigning account was setup properly, the above example will respond with `resultCode: 'tesSUCCESS'` and the hash for the transaction.
|
||||
If any of `{signAs: some_address}` options were missing the code will return a validation error as follow:
|
||||
```
|
||||
[ValidationError(txJSON is not the same for all signedTransactions)]
|
||||
```
|
||||
## combine
|
||||
|
||||
`combine(signedTransactions: Array<string>): {signedTransaction: string, id: string}`
|
||||
@@ -5466,7 +5538,6 @@ return api.combine(signedTransactions);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## submit
|
||||
|
||||
`submit(signedTransaction: string): Promise<object>`
|
||||
@@ -5529,10 +5600,9 @@ return api.submit(signedTransaction)
|
||||
}
|
||||
```
|
||||
|
||||
## generateXAddress
|
||||
|
||||
## generateAddress
|
||||
|
||||
`generateAddress(): {address: string, secret: string}`
|
||||
`generateXAddress(options?: object): {address: string, secret: string}`
|
||||
|
||||
Generate a new XRP Ledger address and corresponding secret.
|
||||
|
||||
@@ -5542,7 +5612,9 @@ Name | Type | Description
|
||||
---- | ---- | -----------
|
||||
options | object | *Optional* Options to control how the address and secret are generated.
|
||||
*options.* algorithm | string | *Optional* The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
|
||||
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed.
|
||||
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)
|
||||
*options.* includeClassicAddress | boolean | *Optional* Specifies whether the classic address should also be included in the returned payload.
|
||||
*options.* test | boolean | *Optional* Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`.
|
||||
|
||||
### Return Value
|
||||
|
||||
@@ -5550,8 +5622,8 @@ This method returns an object with the following structure:
|
||||
|
||||
Name | Type | Description
|
||||
---- | ---- | -----------
|
||||
address | [address](#address) | A randomly generated Ripple account address.
|
||||
secret | secret string | The secret corresponding to the `address`.
|
||||
xAddress | [xAddress](#x-address) | A randomly generated XRP Ledger address in X-address format.
|
||||
secret | secret string | The secret corresponding to the address.
|
||||
|
||||
### Example
|
||||
|
||||
@@ -5562,17 +5634,61 @@ return api.generateAddress();
|
||||
|
||||
```json
|
||||
{
|
||||
"xAddress": "XVLcsWWNiFdUEqoDmSwgxh1abfddG1LtbGFk7omPgYpbyE8",
|
||||
"secret": "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
|
||||
}
|
||||
```
|
||||
|
||||
## generateAddress
|
||||
|
||||
`generateAddress(options?: object): {address: string, secret: string}`
|
||||
|
||||
Deprecated: This method returns a classic address. If you do not need the classic address, use `generateXAddress` instead.
|
||||
|
||||
Generate a new XRP Ledger address and corresponding secret.
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description
|
||||
---- | ---- | -----------
|
||||
options | object | *Optional* Options to control how the address and secret are generated.
|
||||
*options.* algorithm | string | *Optional* The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
|
||||
*options.* entropy | array\<integer\> | *Optional* The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)
|
||||
*options.* includeClassicAddress | boolean | *Optional* If `true`, return the classic address, in addition to the X-address.
|
||||
*options.* test | boolean | *Optional* Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`.
|
||||
|
||||
### Return Value
|
||||
|
||||
This method returns an object with the following structure:
|
||||
|
||||
Name | Type | Description
|
||||
---- | ---- | -----------
|
||||
xAddress | [xAddress](#x-address) | A randomly generated XRP Ledger address in X-address format.
|
||||
classicAddress | [classicAddress](#classic-address) | A randomly generated XRP Ledger Account ID (classic address).
|
||||
address | [classicAddress](#classic-address) | Deprecated: Use `classicAddress` instead.
|
||||
secret | secret string | The secret corresponding to the address.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
return api.generateAddress();
|
||||
```
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"xAddress": "XVLcsWWNiFdUEqoDmSwgxh1abfddG1LtbGFk7omPgYpbyE8",
|
||||
"classicAddress": "rGCkuB7PBr5tNy68tPEABEtcdno4hE6Y7f",
|
||||
"address": "rGCkuB7PBr5tNy68tPEABEtcdno4hE6Y7f",
|
||||
"secret": "sp6JS7f14BuwFY8Mw6bTtLKWauoUs"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## isValidAddress
|
||||
|
||||
`isValidAddress(address: string): boolean`
|
||||
|
||||
Checks if the specified string contains a valid address.
|
||||
Checks if the specified string contains a valid address. X-addresses are considered valid with ripple-lib v1.4.0 and higher.
|
||||
|
||||
### Parameters
|
||||
|
||||
@@ -5587,7 +5703,6 @@ This method returns `true` if the address is valid and `false` if it is not.
|
||||
```javascript
|
||||
return api.isValidAddress("address")
|
||||
```
|
||||
|
||||
## isValidSecret
|
||||
|
||||
`isValidSecret(secret: string): boolean`
|
||||
@@ -5607,7 +5722,6 @@ This method returns `true` if the secret is valid and `false` if it is not.
|
||||
```javascript
|
||||
return api.isValidSecret("secret")
|
||||
```
|
||||
|
||||
## deriveKeypair
|
||||
|
||||
`deriveKeypair(seed: string): {privateKey: string, publicKey: string}`
|
||||
@@ -5629,7 +5743,6 @@ var keypair = api.deriveKeypair(seed)
|
||||
var public_key = keypair.publicKey;
|
||||
var private_key = keypair.privateKey;
|
||||
```
|
||||
|
||||
## deriveAddress
|
||||
|
||||
`deriveAddress(publicKey: string): string`
|
||||
@@ -5649,7 +5762,6 @@ This method returns a string corresponding to the address derived from the publi
|
||||
```javascript
|
||||
var address = api.deriveAddress(public_key);
|
||||
```
|
||||
|
||||
## signPaymentChannelClaim
|
||||
|
||||
`signPaymentChannelClaim(channel: string, amount: string, privateKey: string): string`
|
||||
@@ -5688,7 +5800,6 @@ return api.signPaymentChannelClaim(channel, amount, privateKey);
|
||||
"3045022100B5C54654221F154347679B97AE7791CBEF5E6772A3F894F9C781B8F1B400F89F022021E466D29DC5AEB5DFAFC76E8A88D2E388EBD25A84143B6AC3B647F479CB89B7"
|
||||
```
|
||||
|
||||
|
||||
## verifyPaymentChannelClaim
|
||||
|
||||
`verifyPaymentChannelClaim(channel: string, amount: string, signature: string, publicKey: string): boolean`
|
||||
@@ -5727,7 +5838,6 @@ return api.verifyPaymentChannelClaim(channel, amount, signature, publicKey);
|
||||
```json
|
||||
true
|
||||
```
|
||||
|
||||
## computeLedgerHash
|
||||
|
||||
`computeLedgerHash(ledger: object): string`
|
||||
@@ -5783,7 +5893,6 @@ return api.computeLedgerHash(ledger);
|
||||
```json
|
||||
"F4D865D83EB88C1A1911B9E90641919A1314F36E1B099F8E95FE3B7C77BE3349"
|
||||
```
|
||||
|
||||
## xrpToDrops
|
||||
|
||||
`xrpToDrops(xrp: string | BigNumber): string`
|
||||
@@ -5831,7 +5940,6 @@ return api.dropsToXrp('1');
|
||||
```json
|
||||
'0.000001'
|
||||
```
|
||||
|
||||
## iso8601ToRippleTime
|
||||
|
||||
`iso8601ToRippleTime(iso8601: string): number`
|
||||
@@ -5859,7 +5967,33 @@ api.iso8601ToRippleTime('2017-02-17T15:04:57Z');
|
||||
```json
|
||||
540659097
|
||||
```
|
||||
## rippleTimeToISO8601
|
||||
|
||||
`rippleTimeToISO8601(rippleTime: number): string`
|
||||
|
||||
This method takes the number of seconds since the "Ripple Epoch" of January 1, 2000 (00:00 UTC) and returns a string representation of a date.
|
||||
|
||||
The Ripple Epoch is 946684800 seconds after the Unix Epoch.
|
||||
|
||||
This method is useful for interpreting timestamps returned by the rippled APIs. The rippled APIs represent time as an unsigned integer of the number of seconds since the Ripple Epoch.
|
||||
|
||||
### Parameters
|
||||
|
||||
`rippleTime`: A number of seconds since the Ripple Epoch.
|
||||
|
||||
### Return Value
|
||||
|
||||
A string representing a date and time, created by calling a `Date` object's `toISOString()` method.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
api.rippleTimeToISO8601(540659097);
|
||||
```
|
||||
|
||||
```json
|
||||
'2017-02-17T15:04:57.000Z'
|
||||
```
|
||||
## txFlags
|
||||
|
||||
`txFlags.TRANSACTION_TYPE.FLAG`
|
||||
@@ -5941,7 +6075,6 @@ The remaining transaction types do not have any flags at this time.
|
||||
* EscrowCancel
|
||||
* PaymentChannelCreate
|
||||
* PaymentChannelFund
|
||||
|
||||
## schemaValidator
|
||||
|
||||
Unlike the rest of the ripple-lib API, schemaValidator is a static object on RippleAPI. It provides utility methods that do not use a server.
|
||||
@@ -5977,7 +6110,6 @@ RippleAPI.schemaValidator.schemaValidate('sign', {
|
||||
```
|
||||
[ValidationError(instance.id does not match pattern "^[A-F0-9]{64}$")]
|
||||
```
|
||||
|
||||
# API Events
|
||||
|
||||
## ledger
|
||||
@@ -6082,4 +6214,3 @@ api.on('disconnected', (code) => {
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,19 @@
|
||||
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||
```
|
||||
|
||||
Every XRP Ledger account has an *address*, which is a base58-encoding of a hash of the account's public key. XRP Ledger addresses always start with the lowercase letter `r`.
|
||||
```json
|
||||
"X7AcgcsBL6XDcUb289X4mJ8djcdyKaB5hJDWMArnXr61cqZ"
|
||||
```
|
||||
|
||||
An *address* refers to a specific XRP Ledger account. It is a base-58 encoding of a hash of the account's public key. There are two kinds of addresses in common use:
|
||||
|
||||
### Classic Address
|
||||
|
||||
A *classic address* encodes a hash of the account's public key and a checksum. It has no other data. This kind of address always starts with the lowercase letter `r`.
|
||||
|
||||
### X-address
|
||||
|
||||
An *X-address* encodes a hash of the account's public key, a tag, and a checksum. This kind of address starts with the uppercase letter `X` if it is intended for use on the production XRP Ledger (mainnet). It starts with the uppercase letter `T` if it is intended for use on a test network such as Testnet or Devnet.
|
||||
|
||||
## Account Sequence Number
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ api.connect().then(() => {
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
RippleAPI is designed to work in [Node.js](https://nodejs.org) version **6.11.3**. RippleAPI may work on older Node.js versions if you use [Babel](https://babeljs.io/) for [ECMAScript 6](https://babeljs.io/docs/learn-es2015/) support.
|
||||
RippleAPI is designed to work in [Node.js](https://nodejs.org) version 6 or higher. Ripple recommends Node.js v10 LTS.
|
||||
|
||||
The code samples in this documentation are written with ECMAScript 6 (ES6) features, but `RippleAPI` also works with ECMAScript 5 (ES5). Regardless of whether you use ES5 or ES6, the methods that return Promises return [ES6-style promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
## generateAddress
|
||||
|
||||
`generateAddress(): {address: string, secret: string}`
|
||||
`generateAddress(options?: object): {address: string, secret: string}`
|
||||
|
||||
Deprecated: This method returns a classic address. If you do not need the classic address, use `generateXAddress` instead.
|
||||
|
||||
Generate a new XRP Ledger address and corresponding secret.
|
||||
|
||||
|
||||
23
docs/src/generateXAddress.md.ejs
Normal file
23
docs/src/generateXAddress.md.ejs
Normal file
@@ -0,0 +1,23 @@
|
||||
## generateXAddress
|
||||
|
||||
`generateXAddress(options?: object): {address: string, secret: string}`
|
||||
|
||||
Generate a new XRP Ledger address and corresponding secret.
|
||||
|
||||
### Parameters
|
||||
|
||||
<%- renderSchema('input/generate-x-address.json') %>
|
||||
|
||||
### Return Value
|
||||
|
||||
This method returns an object with the following structure:
|
||||
|
||||
<%- renderSchema('output/generate-x-address.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
return api.generateAddress();
|
||||
```
|
||||
|
||||
<%- renderFixture('responses/generate-x-address.json') %>
|
||||
@@ -15,12 +15,13 @@ This method returns a promise that resolves with an object with the following st
|
||||
<%- renderSchema('output/get-account-objects.json') %>
|
||||
|
||||
The types of objects that may be returned include:
|
||||
* Offer objects for orders that are currently live, unfunded, or expired but not yet removed.
|
||||
* RippleState objects for trust lines where this account's side is not in the default state.
|
||||
* A SignerList object if the account has multi-signing enabled.
|
||||
* Escrow objects for held payments that have not yet been executed or canceled.
|
||||
* PayChannel objects for open payment channels.
|
||||
* Check objects for pending checks.
|
||||
|
||||
* `Offer` objects for orders that are currently live, unfunded, or expired but not yet removed.
|
||||
* `RippleState` objects for trust lines where this account's side is not in the default state.
|
||||
* A `SignerList` object if the account has multi-signing enabled.
|
||||
* `Escrow` objects for held payments that have not yet been executed or canceled.
|
||||
* `PayChannel` objects for open payment channels.
|
||||
* `Check` objects for pending checks.
|
||||
|
||||
### Example
|
||||
|
||||
|
||||
@@ -1,67 +1,69 @@
|
||||
<% include introduction.md.ejs %>
|
||||
<% include boilerplate.md.ejs %>
|
||||
<% include offline.md.ejs %>
|
||||
<% include basictypes.md.ejs %>
|
||||
<% include transactions.md.ejs %>
|
||||
<% include specifications.md.ejs %>
|
||||
<% include rippledAPIs.md.ejs %>
|
||||
<% include request.md.ejs %>
|
||||
<% include hasNextPage.md.ejs %>
|
||||
<% include requestNextPage.md.ejs %>
|
||||
<%- include('introduction.md.ejs') -%>
|
||||
<%- include('boilerplate.md.ejs') -%>
|
||||
<%- include('offline.md.ejs') -%>
|
||||
<%- include('basictypes.md.ejs') -%>
|
||||
<%- include('transactions.md.ejs') -%>
|
||||
<%- include('specifications.md.ejs') -%>
|
||||
<%- include('rippledAPIs.md.ejs') -%>
|
||||
<%- include('request.md.ejs') -%>
|
||||
<%- include('hasNextPage.md.ejs') -%>
|
||||
<%- include('requestNextPage.md.ejs') -%>
|
||||
|
||||
<% include staticMethods.md.ejs %>
|
||||
<% include renameCounterpartyToIssuer.md.ejs %>
|
||||
<% include formatBidsAndAsks.md.ejs %>
|
||||
<%- include('staticMethods.md.ejs') -%>
|
||||
<%- include('renameCounterpartyToIssuer.md.ejs') -%>
|
||||
<%- include('formatBidsAndAsks.md.ejs') -%>
|
||||
|
||||
<% include methods.md.ejs %>
|
||||
<% include connect.md.ejs %>
|
||||
<% include disconnect.md.ejs %>
|
||||
<% include isConnected.md.ejs %>
|
||||
<% include getServerInfo.md.ejs %>
|
||||
<% include getFee.md.ejs %>
|
||||
<% include getLedgerVersion.md.ejs %>
|
||||
<% include getTransaction.md.ejs %>
|
||||
<% include getTransactions.md.ejs %>
|
||||
<% include getTrustlines.md.ejs %>
|
||||
<% include getBalances.md.ejs %>
|
||||
<% include getBalanceSheet.md.ejs %>
|
||||
<% include getPaths.md.ejs %>
|
||||
<% include getOrders.md.ejs %>
|
||||
<% include getOrderbook.md.ejs %>
|
||||
<% include getSettings.md.ejs %>
|
||||
<% include getAccountInfo.md.ejs %>
|
||||
<% include getAccountObjects.md.ejs %>
|
||||
<% include getPaymentChannel.md.ejs %>
|
||||
<% include getLedger.md.ejs %>
|
||||
<% include parseAccountFlags.md.ejs %>
|
||||
<% include prepareTransaction.md.ejs %>
|
||||
<% include preparePayment.md.ejs %>
|
||||
<% include prepareTrustline.md.ejs %>
|
||||
<% include prepareOrder.md.ejs %>
|
||||
<% include prepareOrderCancellation.md.ejs %>
|
||||
<% include prepareSettings.md.ejs %>
|
||||
<% include prepareEscrowCreation.md.ejs %>
|
||||
<% include prepareEscrowCancellation.md.ejs %>
|
||||
<% include prepareEscrowExecution.md.ejs %>
|
||||
<% include preparePaymentChannelCreate.md.ejs %>
|
||||
<% include preparePaymentChannelClaim.md.ejs %>
|
||||
<% include preparePaymentChannelFund.md.ejs %>
|
||||
<% include prepareCheckCreate.md.ejs %>
|
||||
<% include prepareCheckCancel.md.ejs %>
|
||||
<% include prepareCheckCash.md.ejs %>
|
||||
<% include sign.md.ejs %>
|
||||
<% include combine.md.ejs %>
|
||||
<% include submit.md.ejs %>
|
||||
<% include generateAddress.md.ejs %>
|
||||
<% include isValidAddress.md.ejs %>
|
||||
<% include isValidSecret.md.ejs %>
|
||||
<% include deriveKeypair.md.ejs %>
|
||||
<% include deriveAddress.md.ejs %>
|
||||
<% include signPaymentChannelClaim.md.ejs %>
|
||||
<% include verifyPaymentChannelClaim.md.ejs %>
|
||||
<% include computeLedgerHash.md.ejs %>
|
||||
<% include xrpToDropsAndDropsToXrp.md.ejs %>
|
||||
<% include iso8601ToRippleTime.md.ejs %>
|
||||
<% include txFlags.md.ejs %>
|
||||
<% include schemaValidator.md.ejs %>
|
||||
<% include events.md.ejs %>
|
||||
<%- include('methods.md.ejs') -%>
|
||||
<%- include('connect.md.ejs') -%>
|
||||
<%- include('disconnect.md.ejs') -%>
|
||||
<%- include('isConnected.md.ejs') -%>
|
||||
<%- include('getServerInfo.md.ejs') -%>
|
||||
<%- include('getFee.md.ejs') -%>
|
||||
<%- include('getLedgerVersion.md.ejs') -%>
|
||||
<%- include('getTransaction.md.ejs') -%>
|
||||
<%- include('getTransactions.md.ejs') -%>
|
||||
<%- include('getTrustlines.md.ejs') -%>
|
||||
<%- include('getBalances.md.ejs') -%>
|
||||
<%- include('getBalanceSheet.md.ejs') -%>
|
||||
<%- include('getPaths.md.ejs') -%>
|
||||
<%- include('getOrders.md.ejs') -%>
|
||||
<%- include('getOrderbook.md.ejs') -%>
|
||||
<%- include('getSettings.md.ejs') -%>
|
||||
<%- include('getAccountInfo.md.ejs') -%>
|
||||
<%- include('getAccountObjects.md.ejs') -%>
|
||||
<%- include('getPaymentChannel.md.ejs') -%>
|
||||
<%- include('getLedger.md.ejs') -%>
|
||||
<%- include('parseAccountFlags.md.ejs') -%>
|
||||
<%- include('prepareTransaction.md.ejs') -%>
|
||||
<%- include('preparePayment.md.ejs') -%>
|
||||
<%- include('prepareTrustline.md.ejs') -%>
|
||||
<%- include('prepareOrder.md.ejs') -%>
|
||||
<%- include('prepareOrderCancellation.md.ejs') -%>
|
||||
<%- include('prepareSettings.md.ejs') -%>
|
||||
<%- include('prepareEscrowCreation.md.ejs') -%>
|
||||
<%- include('prepareEscrowCancellation.md.ejs') -%>
|
||||
<%- include('prepareEscrowExecution.md.ejs') -%>
|
||||
<%- include('preparePaymentChannelCreate.md.ejs') -%>
|
||||
<%- include('preparePaymentChannelClaim.md.ejs') -%>
|
||||
<%- include('preparePaymentChannelFund.md.ejs') -%>
|
||||
<%- include('prepareCheckCreate.md.ejs') -%>
|
||||
<%- include('prepareCheckCancel.md.ejs') -%>
|
||||
<%- include('prepareCheckCash.md.ejs') -%>
|
||||
<%- include('sign.md.ejs') -%>
|
||||
<%- include('combine.md.ejs') -%>
|
||||
<%- include('submit.md.ejs') -%>
|
||||
<%- include('generateXAddress.md.ejs') -%>
|
||||
<%- include('generateAddress.md.ejs') -%>
|
||||
<%- include('isValidAddress.md.ejs') -%>
|
||||
<%- include('isValidSecret.md.ejs') -%>
|
||||
<%- include('deriveKeypair.md.ejs') -%>
|
||||
<%- include('deriveAddress.md.ejs') -%>
|
||||
<%- include('signPaymentChannelClaim.md.ejs') -%>
|
||||
<%- include('verifyPaymentChannelClaim.md.ejs') -%>
|
||||
<%- include('computeLedgerHash.md.ejs') -%>
|
||||
<%- include('xrpToDropsAndDropsToXrp.md.ejs') -%>
|
||||
<%- include('iso8601ToRippleTime.md.ejs') -%>
|
||||
<%- include('rippleTimeToISO8601.md.ejs') -%>
|
||||
<%- include('txFlags.md.ejs') -%>
|
||||
<%- include('schemaValidator.md.ejs') -%>
|
||||
<%- include('events.md.ejs') -%>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Introduction
|
||||
|
||||
RippleAPI (ripple-lib) is the official client library to the XRP Ledger. Currently, RippleAPI is only available in JavaScript.
|
||||
RippleAPI (ripple-lib) is the official client library to the XRP Ledger. Currently, RippleAPI is only available in JavaScript/TypeScript.
|
||||
|
||||
Using RippleAPI, you can:
|
||||
|
||||
* [Query transactions from the XRP Ledger history](#gettransaction)
|
||||
@@ -8,3 +9,7 @@ Using RippleAPI, you can:
|
||||
* [Submit](#submit) transactions to the XRP Ledger, including [Payments](#payment), [Orders](#order), [Settings changes](#settings), and [other types](#transaction-types)
|
||||
* [Generate a new XRP Ledger Address](#generateaddress)
|
||||
* ... and [much more](#api-methods).
|
||||
|
||||
This page contains documentation for ripple-lib. To use ripple-lib with npm/yarn, begin with the [Getting Started](https://github.com/ripple/ripple-lib#getting-started) steps.
|
||||
|
||||
**What is ripple-lib used for?** Here's a [list of applications that use `ripple-lib`](https://github.com/ripple/ripple-lib/blob/develop/APPLICATIONS.md). Open a PR to add your app or project to the list!
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
`isValidAddress(address: string): boolean`
|
||||
|
||||
Checks if the specified string contains a valid address.
|
||||
Checks if the specified string contains a valid address. X-addresses are considered valid with ripple-lib v1.4.0 and higher.
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
@@ -23,4 +23,5 @@ Methods that depend on the state of the XRP Ledger are unavailable in offline mo
|
||||
* [prepareEscrowExecution](#prepareescrowexecution)
|
||||
* [sign](#sign)
|
||||
* [generateAddress](#generateaddress)
|
||||
* [generateXAddress](#generatexaddress)
|
||||
* [computeLedgerHash](#computeledgerhash)
|
||||
|
||||
@@ -6,7 +6,7 @@ Prepare a transaction. The prepared transaction must subsequently be [signed](#s
|
||||
|
||||
This method works with any of [the transaction types supported by rippled](https://developers.ripple.com/transaction-types.html).
|
||||
|
||||
Notably, this is the preferred method for preparing a `DepositPreauth` transaction (added in rippled 1.1.0).
|
||||
Notably, this is the preferred method for preparing `DepositPreauth` or `AccountDelete` transactions.
|
||||
|
||||
### Parameters
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Returns the response from invoking the specified command, with the specified opt
|
||||
|
||||
Refer to [rippled APIs](https://ripple.com/build/rippled-apis/) for commands and options. All XRP amounts must be specified in drops. One drop is equal to 0.000001 XRP. See [Specifying Currency Amounts](https://ripple.com/build/rippled-apis/#specifying-currency-amounts).
|
||||
|
||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://ripple.com/build/rippled-apis/#specifying-ledgers).
|
||||
Most commands return data for the `current` (in-progress, open) ledger by default. Do not rely on this. Always specify a ledger version in your request. In the example below, the 'validated' ledger is requested, which is the most recent ledger that has been validated by the whole network. See [Specifying Ledgers](https://xrpl.org/basic-data-types.html#specifying-ledgers).
|
||||
|
||||
### Return Value
|
||||
|
||||
|
||||
27
docs/src/rippleTimeToISO8601.md.ejs
Normal file
27
docs/src/rippleTimeToISO8601.md.ejs
Normal file
@@ -0,0 +1,27 @@
|
||||
## rippleTimeToISO8601
|
||||
|
||||
`rippleTimeToISO8601(rippleTime: number): string`
|
||||
|
||||
This method takes the number of seconds since the "Ripple Epoch" of January 1, 2000 (00:00 UTC) and returns a string representation of a date.
|
||||
|
||||
The Ripple Epoch is 946684800 seconds after the Unix Epoch.
|
||||
|
||||
This method is useful for interpreting timestamps returned by the rippled APIs. The rippled APIs represent time as an unsigned integer of the number of seconds since the Ripple Epoch.
|
||||
|
||||
### Parameters
|
||||
|
||||
`rippleTime`: A number of seconds since the Ripple Epoch.
|
||||
|
||||
### Return Value
|
||||
|
||||
A string representing a date and time, created by calling a `Date` object's `toISOString()` method.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
api.rippleTimeToISO8601(540659097);
|
||||
```
|
||||
|
||||
```json
|
||||
'2017-02-17T15:04:57.000Z'
|
||||
```
|
||||
@@ -1,11 +1,13 @@
|
||||
# rippled APIs
|
||||
|
||||
ripple-lib relies on [rippled APIs](https://ripple.com/build/rippled-apis/) for all online functionality. With ripple-lib version 1.0.0 and higher, you can easily access rippled APIs through ripple-lib. Use the `request()`, `hasNextPage()`, and `requestNextPage()` methods:
|
||||
* Use `request()` to issue any `rippled` command, including `account_currencies`, `subscribe`, and `unsubscribe`. [Full list of API Methods](https://ripple.com/build/rippled-apis/#api-methods).
|
||||
ripple-lib relies on [rippled APIs](https://ripple.com/build/rippled-apis/) for online functionality. In addition to ripple-lib's own methods, you can also access rippled APIs through ripple-lib. Use the `request()`, `hasNextPage()`, and `requestNextPage()` methods:
|
||||
|
||||
* Use `request()` to issue any `rippled` command, including `account_currencies`, `subscribe`, and `unsubscribe`. [Full list of API Methods](https://ripple.com/build/rippled-apis/#api-methods).
|
||||
* Use `hasNextPage()` to determine whether a response has more pages. This is true when the response includes a [`marker` field](https://ripple.com/build/rippled-apis/#markers-and-pagination).
|
||||
* Use `requestNextPage()` to request the next page of data.
|
||||
|
||||
When using rippled APIs:
|
||||
|
||||
* [Specify XRP amounts in drops](https://developers.ripple.com/basic-data-types.html#specifying-currency-amounts).
|
||||
* [Specify timestamps as the number of seconds since the "Ripple Epoch"](https://developers.ripple.com/basic-data-types.html#specifying-time).
|
||||
* Instead of `counterparty`, use `issuer`.
|
||||
@@ -37,6 +39,7 @@ Type | Description
|
||||
`manifestReceived` | Sent by the `manifests` stream when the server receives a manifest.
|
||||
`transaction` | Sent by many subscriptions including `transactions`, `transactions_proposed`, `accounts`, `accounts_proposed`, and `book` (Order Book). See [Transaction Streams](https://ripple.com/build/rippled-apis/#transaction-streams) for details.
|
||||
`peerStatusChange` | (Admin-only) Reports a large amount of information on the activities of other `rippled` servers to which the server is connected.
|
||||
`path_find` | Asynchronous follow-up response to the currently open path\_find request. See [rippled path\_find method](https://xrpl.org/path_find.html) for details.
|
||||
|
||||
To register your listener function, use `connection.on(type, handler)`.
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ This method can sign any of [the transaction types supported by ripple-binary-co
|
||||
|
||||
<%- renderSchema("input/sign.json") %>
|
||||
|
||||
When this method is used for multisigning, the `options` parameter is required. See the multisigning example in this section for more details.
|
||||
|
||||
### Return Value
|
||||
|
||||
This method returns an object with the following structure:
|
||||
@@ -31,3 +33,91 @@ return api.sign(txJSON, secret); // or: api.sign(txJSON, keypair);
|
||||
```
|
||||
|
||||
<%- renderFixture("responses/sign.json") %>
|
||||
|
||||
### Example (multisigning)
|
||||
|
||||
```javascript
|
||||
const RippleAPI = require('ripple-lib').RippleAPI;
|
||||
|
||||
// jon's address will have a multi-signing setup with a quorum of 2
|
||||
const jon = {
|
||||
account: 'rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj',
|
||||
secret: 'sh4Va7b1wQof8knHFV2sxwX12fSgK'
|
||||
};
|
||||
const aya = {
|
||||
account: 'rnrPdBjs98fFFfmRpL6hM7exT788SWQPFN',
|
||||
secret: 'snaMuMrXeVc2Vd4NYvHofeGNjgYoe'
|
||||
};
|
||||
const bran = {
|
||||
account: 'rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH',
|
||||
secret: 'shQtQ8Um5MS218yvEU3Ehy1eZQKqH'
|
||||
};
|
||||
|
||||
// Setup the signers list with a quorum of 2
|
||||
const multiSignSetupTransaction = {
|
||||
"Flags": 0,
|
||||
"TransactionType": "SignerListSet",
|
||||
"Account": "rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj",
|
||||
"Fee": "120",
|
||||
"SignerQuorum": 2,
|
||||
"SignerEntries": [
|
||||
{
|
||||
"SignerEntry": {
|
||||
"Account": "rnrPdBjs98fFFfmRpL6hM7exT788SWQPFN",
|
||||
"SignerWeight": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"SignerEntry": {
|
||||
"Account": "rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH",
|
||||
"SignerWeight": 1
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
// a transaction which requires multi signing
|
||||
const multiSignPaymentTransaction = {
|
||||
TransactionType: 'Payment',
|
||||
Account: 'rJKpme4m2zBQceBuU89d7vLMzgoUw2Ptj',
|
||||
Destination: 'rJ93RLnT1t5A8fCr7HTScw7WtfKJMRXodH',
|
||||
Amount: '88000000'
|
||||
};
|
||||
|
||||
const api = new RippleAPI({
|
||||
server: 'wss://s.altnet.rippletest.net:51233'
|
||||
});
|
||||
|
||||
api.connect().then(() => {
|
||||
// adding the multi signing feature to jon's account
|
||||
api.prepareTransaction(multiSignSetupTransaction).then((prepared) => {
|
||||
console.log(prepared);
|
||||
jonSign = api.sign(prepared.txJSON, jon.secret).signedTransaction;
|
||||
api.submit(jonSign).then( response => {
|
||||
console.log(response.resultCode, response.resultMessage);
|
||||
|
||||
// multi sign a transaction
|
||||
api.prepareTransaction(multiSignPaymentTransaction).then(prepared => {
|
||||
console.log(prepared);
|
||||
|
||||
// Aya and Bran sign it too but with 'signAs' set to their own account
|
||||
let ayaSign = api.sign(prepared.txJSON, aya.secret, {'signAs': aya.account}).signedTransaction;
|
||||
let branSign = api.sign(prepared.txJSON, bran.secret, {'signAs': bran.account}).signedTransaction;
|
||||
|
||||
// signatures are combined and submitted
|
||||
let combinedTx = api.combine([ayaSign, branSign]);
|
||||
api.submit(combinedTx.signedTransaction).then(response => {
|
||||
console.log(response.tx_json.hash);
|
||||
return api.disconnect();
|
||||
}).catch(console.error);
|
||||
}).catch(console.error);
|
||||
}).catch(console.error)
|
||||
}).catch(console.error);
|
||||
}).catch(console.error);
|
||||
```
|
||||
|
||||
Assuming the multisigning account was setup properly, the above example will respond with `resultCode: 'tesSUCCESS'` and the hash for the transaction.
|
||||
If any of `{signAs: some_address}` options were missing the code will return a validation error as follow:
|
||||
```
|
||||
[ValidationError(txJSON is not the same for all signedTransactions)]
|
||||
```
|
||||
|
||||
82
package.json
82
package.json
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "1.3.0",
|
||||
"version": "1.6.1",
|
||||
"license": "ISC",
|
||||
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
||||
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
|
||||
"files": [
|
||||
"dist/npm/*",
|
||||
"build/*"
|
||||
"build/ripple-latest-min.js",
|
||||
"build/ripple-latest.js"
|
||||
],
|
||||
"main": "dist/npm/",
|
||||
"unpkg": "build/ripple-latest-min.js",
|
||||
"jsdelivr": "build/ripple-latest-min.js",
|
||||
"types": "dist/npm/index.d.ts",
|
||||
"browser": {
|
||||
"ws": "./dist/npm/common/wswrapper.js",
|
||||
@@ -18,50 +21,54 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/lodash": "^4.14.136",
|
||||
"@types/ws": "^3.2.0",
|
||||
"bignumber.js": "^4.1.0",
|
||||
"https-proxy-agent": "2.2.1",
|
||||
"@types/ws": "^6.0.3",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"https-proxy-agent": "^4.0.0",
|
||||
"jsonschema": "1.2.2",
|
||||
"lodash": "^4.17.4",
|
||||
"ripple-address-codec": "^2.0.1",
|
||||
"ripple-binary-codec": "0.2.2",
|
||||
"ripple-hashes": "0.3.2",
|
||||
"ripple-keypairs": "^0.10.1",
|
||||
"ripple-lib-transactionparser": "0.7.1",
|
||||
"ws": "^3.3.1"
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"ripple-address-codec": "^4.0.0",
|
||||
"ripple-binary-codec": "^0.2.5",
|
||||
"ripple-keypairs": "^0.11.0",
|
||||
"ripple-lib-transactionparser": "0.8.2",
|
||||
"ws": "^7.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "11.13.0",
|
||||
"assert-diff": "^1.0.1",
|
||||
"doctoc": "^0.15.0",
|
||||
"ejs": "^2.3.4",
|
||||
"eventemitter2": "^0.4.14",
|
||||
"gulp": "^4.0.2",
|
||||
"json-loader": "^0.5.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^13.1.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.3.3",
|
||||
"@typescript-eslint/parser": "^2.3.3",
|
||||
"assert-diff": "^2.0.3",
|
||||
"doctoc": "^1.4.0",
|
||||
"ejs": "^3.0.1",
|
||||
"eslint": "^6.5.1",
|
||||
"eventemitter2": "^6.0.0",
|
||||
"json-schema-to-markdown-table": "^0.4.0",
|
||||
"mocha": "6.1.3",
|
||||
"mocha": "7.0.0",
|
||||
"mocha-junit-reporter": "^1.9.1",
|
||||
"null-loader": "^0.1.1",
|
||||
"nyc": "^14.1.1",
|
||||
"source-map-support": "0.5.12",
|
||||
"ts-loader": "^3.2.0",
|
||||
"ts-node": "8.0.3",
|
||||
"tslint": "^5.8.0",
|
||||
"tslint-eslint-rules": "^4.1.1",
|
||||
"typescript": "3.4.2",
|
||||
"uglifyjs-webpack-plugin": "^1.1.4",
|
||||
"webpack": "3.12.0"
|
||||
"nyc": "^15.0.0",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-node": "^8.4.1",
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-bundle-analyzer": "^3.6.0",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"build:schemas": "mkdir -p dist/npm/common && cp -r src/common/schemas dist/npm/common/",
|
||||
"build:lib": "tsc --build",
|
||||
"build:web": "webpack",
|
||||
"build": "yarn build:schemas && yarn build:lib && yarn build:web",
|
||||
"analyze": "yarn build:web --analyze",
|
||||
"watch": "yarn build:lib --watch",
|
||||
"clean": "rm -rf dist/npm",
|
||||
"doctoc": "doctoc docs/index.md --title '# RippleAPI Reference' --github --maxlevel 2",
|
||||
"docgen": "node --harmony scripts/build_docs.js",
|
||||
"clean": "rm -rf dist/npm",
|
||||
"compile": "mkdir -p dist/npm/common/js && cp -r src/common/js/ dist/npm/common/js/ && mkdir -p dist/npm/common && cp -r src/common/schemas dist/npm/common/ && tsc --build",
|
||||
"watch": "tsc -w",
|
||||
"prepublish": "npm run clean && npm run compile && npm run build",
|
||||
"prepublish": "yarn clean && yarn build",
|
||||
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --exit",
|
||||
"lint": "tslint -p ./",
|
||||
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --watch --reporter dot",
|
||||
"format": "prettier --write '{src,test}/**/*.ts'",
|
||||
"lint": "eslint 'src/**/*.ts' 'test/*-test.{ts,js}'",
|
||||
"perf": "./scripts/perf_test.sh",
|
||||
"start": "node scripts/http.js"
|
||||
},
|
||||
@@ -71,6 +78,7 @@
|
||||
},
|
||||
"readmeFilename": "README.md",
|
||||
"engines": {
|
||||
"node": ">=6.12.3"
|
||||
"node": ">=8",
|
||||
"yarn": "^1.15.2"
|
||||
}
|
||||
}
|
||||
|
||||
192
src/api.ts
192
src/api.ts
@@ -5,8 +5,10 @@ import {
|
||||
validate,
|
||||
xrpToDrops,
|
||||
dropsToXrp,
|
||||
rippleTimeToISO8601,
|
||||
iso8601ToRippleTime,
|
||||
txFlags
|
||||
txFlags,
|
||||
ensureClassicAddress
|
||||
} from './common'
|
||||
import {
|
||||
connect,
|
||||
@@ -22,8 +24,7 @@ import getBalances from './ledger/balances'
|
||||
import getBalanceSheet from './ledger/balance-sheet'
|
||||
import getPaths from './ledger/pathfind'
|
||||
import getOrders from './ledger/orders'
|
||||
import {getOrderbook,
|
||||
formatBidsAndAsks} from './ledger/orderbook'
|
||||
import {getOrderbook, formatBidsAndAsks} from './ledger/orderbook'
|
||||
import {getSettings, parseAccountFlags} from './ledger/settings'
|
||||
import getAccountInfo from './ledger/accountinfo'
|
||||
import getAccountObjects from './ledger/accountobjects'
|
||||
@@ -45,26 +46,40 @@ import prepareSettings from './transaction/settings'
|
||||
import sign from './transaction/sign'
|
||||
import combine from './transaction/combine'
|
||||
import submit from './transaction/submit'
|
||||
import {generateAddressAPI} from './offline/generate-address'
|
||||
import {deriveKeypair, deriveAddress} from './offline/derive'
|
||||
import {
|
||||
generateAddressAPI,
|
||||
GenerateAddressOptions,
|
||||
GeneratedAddress
|
||||
} from './offline/generate-address'
|
||||
import {deriveKeypair, deriveAddress, deriveXAddress} from './offline/derive'
|
||||
import computeLedgerHash from './offline/ledgerhash'
|
||||
import signPaymentChannelClaim from './offline/sign-payment-channel-claim'
|
||||
import verifyPaymentChannelClaim from './offline/verify-payment-channel-claim'
|
||||
import getLedger from './ledger/ledger'
|
||||
|
||||
import {
|
||||
AccountObjectsRequest, AccountObjectsResponse,
|
||||
AccountOffersRequest, AccountOffersResponse,
|
||||
AccountInfoRequest, AccountInfoResponse,
|
||||
AccountLinesRequest, AccountLinesResponse,
|
||||
BookOffersRequest, BookOffersResponse,
|
||||
GatewayBalancesRequest, GatewayBalancesResponse,
|
||||
LedgerRequest, LedgerResponse,
|
||||
LedgerEntryRequest, LedgerEntryResponse,
|
||||
ServerInfoRequest, ServerInfoResponse
|
||||
AccountObjectsRequest,
|
||||
AccountObjectsResponse,
|
||||
AccountOffersRequest,
|
||||
AccountOffersResponse,
|
||||
AccountInfoRequest,
|
||||
AccountInfoResponse,
|
||||
AccountLinesRequest,
|
||||
AccountLinesResponse,
|
||||
BookOffersRequest,
|
||||
BookOffersResponse,
|
||||
GatewayBalancesRequest,
|
||||
GatewayBalancesResponse,
|
||||
LedgerRequest,
|
||||
LedgerResponse,
|
||||
LedgerDataRequest,
|
||||
LedgerDataResponse,
|
||||
LedgerEntryRequest,
|
||||
LedgerEntryResponse,
|
||||
ServerInfoRequest,
|
||||
ServerInfoResponse
|
||||
} from './common/types/commands'
|
||||
|
||||
|
||||
import RangeSet from './common/rangeset'
|
||||
import * as ledgerUtils from './ledger/utils'
|
||||
import * as transactionUtils from './transaction/utils'
|
||||
@@ -72,14 +87,14 @@ import * as schemaValidator from './common/schema-validator'
|
||||
import {getServerInfo, getFee} from './common/serverinfo'
|
||||
import {clamp, renameCounterpartyToIssuer} from './ledger/utils'
|
||||
import {TransactionJSON, Instructions, Prepare} from './transaction/types'
|
||||
import {ConnectionOptions} from './common/connection'
|
||||
import {ConnectionUserOptions} from './common/connection'
|
||||
import {isValidXAddress, isValidClassicAddress} from 'ripple-address-codec'
|
||||
|
||||
export interface APIOptions extends ConnectionOptions {
|
||||
server?: string,
|
||||
feeCushion?: number,
|
||||
maxFeeXRP?: string,
|
||||
trace?: boolean,
|
||||
proxy?: string,
|
||||
export interface APIOptions extends ConnectionUserOptions {
|
||||
server?: string
|
||||
feeCushion?: number
|
||||
maxFeeXRP?: string
|
||||
proxy?: string
|
||||
timeout?: number
|
||||
}
|
||||
|
||||
@@ -88,7 +103,7 @@ export interface APIOptions extends ConnectionOptions {
|
||||
* command. This varies from command to command, but we need to know it to
|
||||
* properly count across many requests.
|
||||
*/
|
||||
function getCollectKeyFromCommand(command: string): string|undefined {
|
||||
function getCollectKeyFromCommand(command: string): string | undefined {
|
||||
switch (command) {
|
||||
case 'account_offers':
|
||||
case 'book_offers':
|
||||
@@ -137,7 +152,14 @@ class RippleAPI extends EventEmitter {
|
||||
this.emit('connected')
|
||||
})
|
||||
this.connection.on('disconnected', code => {
|
||||
this.emit('disconnected', code)
|
||||
let finalCode = code
|
||||
// 1005: This is a backwards-compatible fix for this change in the ws library: https://github.com/websockets/ws/issues/1257
|
||||
// 4000: Connection uses a 4000 code internally to indicate a manual disconnect/close
|
||||
// TODO: Remove in next major, breaking version
|
||||
if (finalCode === 1005 || finalCode === 4000) {
|
||||
finalCode = 1000
|
||||
}
|
||||
this.emit('disconnected', finalCode)
|
||||
})
|
||||
} else {
|
||||
// use null object pattern to provide better error message if user
|
||||
@@ -146,35 +168,56 @@ class RippleAPI extends EventEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes a request to the API with the given command and
|
||||
* additional request body parameters.
|
||||
*/
|
||||
async request(command: 'account_info', params: AccountInfoRequest):
|
||||
Promise<AccountInfoResponse>
|
||||
async request(command: 'account_lines', params: AccountLinesRequest):
|
||||
Promise<AccountLinesResponse>
|
||||
async request(command: 'account_objects', params: AccountObjectsRequest):
|
||||
Promise<AccountObjectsResponse>
|
||||
async request(command: 'account_offers', params: AccountOffersRequest):
|
||||
Promise<AccountOffersResponse>
|
||||
async request(command: 'book_offers', params: BookOffersRequest):
|
||||
Promise<BookOffersResponse>
|
||||
async request(command: 'gateway_balances', params: GatewayBalancesRequest):
|
||||
Promise<GatewayBalancesResponse>
|
||||
async request(command: 'ledger', params: LedgerRequest):
|
||||
Promise<LedgerResponse>
|
||||
async request(command: 'ledger_entry', params: LedgerEntryRequest):
|
||||
Promise<LedgerEntryResponse>
|
||||
async request(command: 'server_info', params?: ServerInfoRequest):
|
||||
Promise<ServerInfoResponse>
|
||||
async request(command: string, params: any):
|
||||
Promise<any>
|
||||
async request(
|
||||
command: 'account_info',
|
||||
params: AccountInfoRequest
|
||||
): Promise<AccountInfoResponse>
|
||||
async request(
|
||||
command: 'account_lines',
|
||||
params: AccountLinesRequest
|
||||
): Promise<AccountLinesResponse>
|
||||
async request(
|
||||
command: 'account_objects',
|
||||
params: AccountObjectsRequest
|
||||
): Promise<AccountObjectsResponse>
|
||||
async request(
|
||||
command: 'account_offers',
|
||||
params: AccountOffersRequest
|
||||
): Promise<AccountOffersResponse>
|
||||
async request(
|
||||
command: 'book_offers',
|
||||
params: BookOffersRequest
|
||||
): Promise<BookOffersResponse>
|
||||
async request(
|
||||
command: 'gateway_balances',
|
||||
params: GatewayBalancesRequest
|
||||
): Promise<GatewayBalancesResponse>
|
||||
async request(
|
||||
command: 'ledger',
|
||||
params: LedgerRequest
|
||||
): Promise<LedgerResponse>
|
||||
async request(
|
||||
command: 'ledger_data',
|
||||
params?: LedgerDataRequest
|
||||
): Promise<LedgerDataResponse>
|
||||
async request(
|
||||
command: 'ledger_entry',
|
||||
params: LedgerEntryRequest
|
||||
): Promise<LedgerEntryResponse>
|
||||
async request(
|
||||
command: 'server_info',
|
||||
params?: ServerInfoRequest
|
||||
): Promise<ServerInfoResponse>
|
||||
async request(command: string, params: any): Promise<any>
|
||||
async request(command: string, params: any = {}): Promise<any> {
|
||||
return this.connection.request({
|
||||
...params,
|
||||
command
|
||||
command,
|
||||
account: params.account ? ensureClassicAddress(params.account) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
@@ -194,7 +237,7 @@ class RippleAPI extends EventEmitter {
|
||||
command: string,
|
||||
params: object = {},
|
||||
currentResponse: T
|
||||
): Promise<object> {
|
||||
): Promise<T> {
|
||||
if (!currentResponse.marker) {
|
||||
return Promise.reject(
|
||||
new errors.NotFoundError('response does not have a next page')
|
||||
@@ -211,8 +254,10 @@ class RippleAPI extends EventEmitter {
|
||||
*
|
||||
* You can later submit the transaction with `submit()`.
|
||||
*/
|
||||
async prepareTransaction(txJSON: TransactionJSON, instructions: Instructions = {}):
|
||||
Promise<Prepare> {
|
||||
async prepareTransaction(
|
||||
txJSON: TransactionJSON,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
return transactionUtils.prepareTransaction(txJSON, this, instructions)
|
||||
}
|
||||
|
||||
@@ -240,16 +285,23 @@ class RippleAPI extends EventEmitter {
|
||||
* general use. Instead, use rippled's built-in pagination and make multiple
|
||||
* requests as needed.
|
||||
*/
|
||||
async _requestAll(command: 'account_offers', params: AccountOffersRequest):
|
||||
Promise<AccountOffersResponse[]>
|
||||
async _requestAll(command: 'book_offers', params: BookOffersRequest):
|
||||
Promise<BookOffersResponse[]>
|
||||
async _requestAll(command: 'account_lines', params: AccountLinesRequest):
|
||||
Promise<AccountLinesResponse[]>
|
||||
async _requestAll(
|
||||
command: 'account_offers',
|
||||
params: AccountOffersRequest
|
||||
): Promise<AccountOffersResponse[]>
|
||||
async _requestAll(
|
||||
command: 'book_offers',
|
||||
params: BookOffersRequest
|
||||
): Promise<BookOffersResponse[]>
|
||||
async _requestAll(
|
||||
command: 'account_lines',
|
||||
params: AccountLinesRequest
|
||||
): Promise<AccountLinesResponse[]>
|
||||
async _requestAll(
|
||||
command: string,
|
||||
params: any = {},
|
||||
options: {collect?: string} = {}): Promise<any[]> {
|
||||
options: {collect?: string} = {}
|
||||
): Promise<any[]> {
|
||||
// The data under collection is keyed based on the command. Fail if command
|
||||
// not recognized and collection key not provided.
|
||||
const collectKey = options.collect || getCollectKeyFromCommand(command)
|
||||
@@ -258,8 +310,7 @@ class RippleAPI extends EventEmitter {
|
||||
}
|
||||
// If limit is not provided, fetches all data over multiple requests.
|
||||
// NOTE: This may return much more than needed. Set limit when possible.
|
||||
const countTo: number =
|
||||
(params.limit !== undefined) ? params.limit : Infinity
|
||||
const countTo: number = params.limit !== undefined ? params.limit : Infinity
|
||||
let count: number = 0
|
||||
let marker: string = params.marker
|
||||
let lastBatchLength: number
|
||||
@@ -283,10 +334,19 @@ class RippleAPI extends EventEmitter {
|
||||
} else {
|
||||
lastBatchLength = 0
|
||||
}
|
||||
} while(!!marker && count < countTo && lastBatchLength !== 0)
|
||||
} while (!!marker && count < countTo && lastBatchLength !== 0)
|
||||
return results
|
||||
}
|
||||
|
||||
// @deprecated Use X-addresses instead
|
||||
generateAddress(options: GenerateAddressOptions = {}): GeneratedAddress {
|
||||
return generateAddressAPI({...options, includeClassicAddress: true})
|
||||
}
|
||||
|
||||
generateXAddress(options: GenerateAddressOptions = {}): GeneratedAddress {
|
||||
return generateAddressAPI(options)
|
||||
}
|
||||
|
||||
connect = connect
|
||||
disconnect = disconnect
|
||||
isConnected = isConnected
|
||||
@@ -327,7 +387,6 @@ class RippleAPI extends EventEmitter {
|
||||
combine = combine
|
||||
submit = submit
|
||||
|
||||
generateAddress = generateAddressAPI
|
||||
deriveKeypair = deriveKeypair
|
||||
deriveAddress = deriveAddress
|
||||
computeLedgerHash = computeLedgerHash
|
||||
@@ -335,8 +394,17 @@ class RippleAPI extends EventEmitter {
|
||||
verifyPaymentChannelClaim = verifyPaymentChannelClaim
|
||||
errors = errors
|
||||
|
||||
static deriveXAddress = deriveXAddress
|
||||
|
||||
// RippleAPI.deriveClassicAddress (static) is a new name for api.deriveAddress
|
||||
static deriveClassicAddress = deriveAddress
|
||||
|
||||
static isValidXAddress = isValidXAddress
|
||||
static isValidClassicAddress = isValidClassicAddress
|
||||
|
||||
xrpToDrops = xrpToDrops
|
||||
dropsToXrp = dropsToXrp
|
||||
rippleTimeToISO8601 = rippleTimeToISO8601
|
||||
iso8601ToRippleTime = iso8601ToRippleTime
|
||||
txFlags = txFlags
|
||||
|
||||
@@ -344,6 +412,4 @@ class RippleAPI extends EventEmitter {
|
||||
isValidSecret = schemaValidator.isValidSecret
|
||||
}
|
||||
|
||||
export {
|
||||
RippleAPI
|
||||
}
|
||||
export {RippleAPI}
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
|
||||
import * as _ from 'lodash'
|
||||
import {RippleAPI} from './api'
|
||||
import {RippleAPI, APIOptions} from './api'
|
||||
|
||||
class RippleAPIBroadcast extends RippleAPI {
|
||||
|
||||
ledgerVersion: number | undefined = undefined
|
||||
private _apis: RippleAPI[]
|
||||
|
||||
constructor(servers, options) {
|
||||
constructor(servers, options: APIOptions = {}) {
|
||||
super(options)
|
||||
|
||||
const apis: RippleAPI[] = servers.map(server => new RippleAPI(
|
||||
_.assign({}, options, {server})
|
||||
))
|
||||
const apis: RippleAPI[] = 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
|
||||
this[name] = function() {
|
||||
// eslint-disable-line no-loop-func
|
||||
return Promise.race(apis.map(api => api[name](...arguments)))
|
||||
}
|
||||
})
|
||||
@@ -44,13 +43,16 @@ class RippleAPIBroadcast extends RippleAPI {
|
||||
apis.forEach(api => {
|
||||
api.on('ledger', this.onLedgerEvent.bind(this))
|
||||
api.on('error', (errorCode, errorMessage, data) =>
|
||||
this.emit('error', errorCode, errorMessage, data))
|
||||
this.emit('error', errorCode, errorMessage, data)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
onLedgerEvent(ledger) {
|
||||
if (ledger.ledgerVersion > this.ledgerVersion ||
|
||||
this.ledgerVersion === undefined) {
|
||||
if (
|
||||
ledger.ledgerVersion > this.ledgerVersion ||
|
||||
this.ledgerVersion === undefined
|
||||
) {
|
||||
this.ledgerVersion = ledger.ledgerVersion
|
||||
this.emit('ledger', ledger)
|
||||
}
|
||||
@@ -68,6 +70,4 @@ class RippleAPIBroadcast extends RippleAPI {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
RippleAPIBroadcast
|
||||
}
|
||||
export {RippleAPIBroadcast}
|
||||
|
||||
44
src/common/backoff.ts
Normal file
44
src/common/backoff.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Original code based on "backo" - https://github.com/segmentio/backo
|
||||
* MIT License - Copyright 2014 Segment.io
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A Back off strategy that increases exponentionally. Useful with repeated
|
||||
* setTimeout calls over a network (where the destination may be down).
|
||||
*/
|
||||
export class ExponentialBackoff {
|
||||
private readonly ms: number
|
||||
private readonly max: number
|
||||
private readonly factor: number = 2
|
||||
private readonly jitter: number = 0
|
||||
attempts: number = 0
|
||||
|
||||
constructor(opts: {min?: number; max?: number} = {}) {
|
||||
this.ms = opts.min || 100
|
||||
this.max = opts.max || 10000
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the backoff duration.
|
||||
*/
|
||||
duration() {
|
||||
var ms = this.ms * Math.pow(this.factor, this.attempts++)
|
||||
if (this.jitter) {
|
||||
var rand = Math.random()
|
||||
var deviation = Math.floor(rand * this.jitter * ms)
|
||||
ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation
|
||||
}
|
||||
return Math.min(ms, this.max) | 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the number of attempts.
|
||||
*/
|
||||
reset() {
|
||||
this.attempts = 0
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
|
||||
function setPrototypeOf(object, prototype) {
|
||||
// Object.setPrototypeOf not supported on Internet Explorer 9
|
||||
Object.setPrototypeOf ? Object.setPrototypeOf(object, prototype) :
|
||||
// @ts-ignore: Specifically a fallback for IE9
|
||||
object.__proto__ = prototype
|
||||
Object.setPrototypeOf
|
||||
? Object.setPrototypeOf(object, prototype)
|
||||
: // @ts-ignore: Specifically a fallback for IE9
|
||||
(object.__proto__ = prototype)
|
||||
}
|
||||
|
||||
function getConstructorName(object: object): string {
|
||||
// hack for internet explorer
|
||||
if (!object.constructor.name) {
|
||||
return object.constructor.toString().match(/^function\s+([^(]*)/)![1]
|
||||
if (object.constructor.name) {
|
||||
return object.constructor.name
|
||||
}
|
||||
return object.constructor.name
|
||||
// try to guess it on legacy browsers (ie)
|
||||
const constructorString = object.constructor.toString()
|
||||
const functionConstructor = constructorString.match(/^function\s+([^(]*)/)
|
||||
const classConstructor = constructorString.match(/^class\s([^\s]*)/)
|
||||
return functionConstructor ? functionConstructor[1] : classConstructor[1]
|
||||
}
|
||||
|
||||
export {
|
||||
getConstructorName,
|
||||
setPrototypeOf
|
||||
}
|
||||
export {getConstructorName, setPrototypeOf}
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
import * as _ from 'lodash'
|
||||
import {EventEmitter} from 'events'
|
||||
import {parse as parseUrl} from 'url'
|
||||
import * as WebSocket from 'ws'
|
||||
import WebSocket from 'ws'
|
||||
import RangeSet from './rangeset'
|
||||
import {RippledError, DisconnectedError, NotConnectedError,
|
||||
TimeoutError, ResponseFormatError, ConnectionError,
|
||||
RippledNotInitializedError} from './errors'
|
||||
import {
|
||||
RippledError,
|
||||
DisconnectedError,
|
||||
NotConnectedError,
|
||||
TimeoutError,
|
||||
ResponseFormatError,
|
||||
ConnectionError,
|
||||
RippledNotInitializedError,
|
||||
RippleError
|
||||
} from './errors'
|
||||
import {ExponentialBackoff} from './backoff';
|
||||
|
||||
/**
|
||||
* ConnectionOptions is the configuration for the Connection class.
|
||||
*/
|
||||
export interface ConnectionOptions {
|
||||
trace?: boolean,
|
||||
trace?: boolean | ((id: string, message: string) => void)
|
||||
proxy?: string
|
||||
proxyAuthorization?: string
|
||||
authorization?: string
|
||||
@@ -16,449 +27,592 @@ export interface ConnectionOptions {
|
||||
key?: string
|
||||
passphrase?: string
|
||||
certificate?: string
|
||||
timeout?: number
|
||||
timeout: number
|
||||
connectionTimeout: number
|
||||
}
|
||||
|
||||
class Connection extends EventEmitter {
|
||||
/**
|
||||
* ConnectionUserOptions is the user-provided configuration object. All configuration
|
||||
* is optional, so any ConnectionOptions configuration that has a default value is
|
||||
* still optional at the point that the user provides it.
|
||||
*/
|
||||
export type ConnectionUserOptions = Partial<ConnectionOptions>
|
||||
|
||||
/**
|
||||
* Represents an intentionally triggered web-socket disconnect code.
|
||||
* WebSocket spec allows 4xxx codes for app/library specific codes.
|
||||
* See: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||
**/
|
||||
const INTENTIONAL_DISCONNECT_CODE = 4000
|
||||
|
||||
/**
|
||||
* Create a new websocket given your URL and optional proxy/certificate
|
||||
* configuration.
|
||||
*/
|
||||
function createWebSocket(url: string, config: ConnectionOptions): WebSocket {
|
||||
const options: WebSocket.ClientOptions = {}
|
||||
if (config.proxy !== undefined) {
|
||||
const parsedURL = parseUrl(url)
|
||||
const parsedProxyURL = parseUrl(config.proxy)
|
||||
const proxyOverrides = _.omitBy(
|
||||
{
|
||||
secureEndpoint: parsedURL.protocol === 'wss:',
|
||||
secureProxy: parsedProxyURL.protocol === 'https:',
|
||||
auth: config.proxyAuthorization,
|
||||
ca: config.trustedCertificates,
|
||||
key: config.key,
|
||||
passphrase: config.passphrase,
|
||||
cert: config.certificate
|
||||
},
|
||||
_.isUndefined
|
||||
)
|
||||
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides)
|
||||
let HttpsProxyAgent
|
||||
try {
|
||||
HttpsProxyAgent = require('https-proxy-agent')
|
||||
} catch (error) {
|
||||
throw new Error('"proxy" option is not supported in the browser')
|
||||
}
|
||||
options.agent = new HttpsProxyAgent(proxyOptions)
|
||||
}
|
||||
if (config.authorization !== undefined) {
|
||||
const base64 = Buffer.from(config.authorization).toString('base64')
|
||||
options.headers = {Authorization: `Basic ${base64}`}
|
||||
}
|
||||
const optionsOverrides = _.omitBy(
|
||||
{
|
||||
ca: config.trustedCertificates,
|
||||
key: config.key,
|
||||
passphrase: config.passphrase,
|
||||
cert: config.certificate
|
||||
},
|
||||
_.isUndefined
|
||||
)
|
||||
const websocketOptions = _.assign({}, options, optionsOverrides)
|
||||
const websocket = new WebSocket(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
|
||||
}
|
||||
|
||||
/**
|
||||
* ws.send(), but promisified.
|
||||
*/
|
||||
function websocketSendAsync(ws: WebSocket, message: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ws.send(message, undefined, error => {
|
||||
if (error) {
|
||||
reject(new DisconnectedError(error.message, error))
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* LedgerHistory is used to store and reference ledger information that has been
|
||||
* captured by the Connection class over time.
|
||||
*/
|
||||
class LedgerHistory {
|
||||
private availableVersions = new RangeSet()
|
||||
latestVersion: null | number = null
|
||||
feeBase: null | number = null
|
||||
feeRef: null | number = null
|
||||
|
||||
/**
|
||||
* Returns true if the given version exists.
|
||||
*/
|
||||
hasVersion(version: number): boolean {
|
||||
return this.availableVersions.containsValue(version)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given range of versions exist (inclusive).
|
||||
*/
|
||||
hasVersions(lowVersion: number, highVersion: number): boolean {
|
||||
return this.availableVersions.containsRange(lowVersion, highVersion)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update LedgerHistory with a new ledger response object. The "responseData"
|
||||
* format lets you pass in any valid rippled ledger response data, regardless
|
||||
* of whether ledger history data exists or not. If relevant ledger data
|
||||
* is found, we'll update our history (ex: from a "ledgerClosed" event).
|
||||
*/
|
||||
update(responseData: {
|
||||
ledger_index?: string
|
||||
validated_ledgers?: string
|
||||
fee_base?: string
|
||||
fee_ref?: string
|
||||
}) {
|
||||
this.latestVersion = Number(responseData.ledger_index)
|
||||
if (responseData.validated_ledgers) {
|
||||
this.availableVersions.reset()
|
||||
this.availableVersions.parseAndAddRanges(responseData.validated_ledgers)
|
||||
} else {
|
||||
this.availableVersions.addValue(this.latestVersion)
|
||||
}
|
||||
if (responseData.fee_base) {
|
||||
this.feeBase = Number(responseData.fee_base)
|
||||
}
|
||||
if (responseData.fee_ref) {
|
||||
this.feeRef = Number(responseData.fee_ref)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage all the requests made to the websocket, and their async responses
|
||||
* that come in from the WebSocket. Because they come in over the WS connection
|
||||
* after-the-fact.
|
||||
*/
|
||||
class ConnectionManager {
|
||||
private promisesAwaitingConnection: {
|
||||
resolve: Function
|
||||
reject: Function
|
||||
}[] = []
|
||||
|
||||
resolveAllAwaiting() {
|
||||
this.promisesAwaitingConnection.map(({resolve}) => resolve())
|
||||
this.promisesAwaitingConnection = []
|
||||
}
|
||||
|
||||
rejectAllAwaiting(error: Error) {
|
||||
this.promisesAwaitingConnection.map(({reject}) => reject(error))
|
||||
this.promisesAwaitingConnection = []
|
||||
}
|
||||
|
||||
awaitConnection(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.promisesAwaitingConnection.push({resolve, reject})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage all the requests made to the websocket, and their async responses
|
||||
* that come in from the WebSocket. Responses come in over the WS connection
|
||||
* after-the-fact, so this manager will tie that response to resolve the
|
||||
* original request.
|
||||
*/
|
||||
class RequestManager {
|
||||
private nextId = 0
|
||||
private promisesAwaitingResponse: {
|
||||
resolve: Function
|
||||
reject: Function
|
||||
timer: NodeJS.Timeout
|
||||
}[] = []
|
||||
|
||||
cancel(id: number) {
|
||||
const {timer} = this.promisesAwaitingResponse[id]
|
||||
clearTimeout(timer)
|
||||
}
|
||||
|
||||
resolve(id: number, data: any) {
|
||||
const {timer, resolve} = this.promisesAwaitingResponse[id]
|
||||
clearTimeout(timer)
|
||||
resolve(data)
|
||||
}
|
||||
|
||||
reject(id: number, error: Error) {
|
||||
const {timer, reject} = this.promisesAwaitingResponse[id]
|
||||
clearTimeout(timer)
|
||||
reject(error)
|
||||
}
|
||||
|
||||
rejectAll(error: Error) {
|
||||
this.promisesAwaitingResponse.forEach((_, id) => {
|
||||
this.reject(id, error)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WebSocket request. This sets up a timeout timer to catch
|
||||
* hung responses, and a promise that will resolve with the response once
|
||||
* the response is seen & handled.
|
||||
*/
|
||||
createRequest(data: any, timeout: number): [number, string, Promise<any>] {
|
||||
const newId = this.nextId++
|
||||
const newData = JSON.stringify({...data, id: newId})
|
||||
const timer = setTimeout(
|
||||
() => this.reject(newId, new TimeoutError()),
|
||||
timeout
|
||||
)
|
||||
// Node.js won't exit if a timer is still running, so we tell Node to ignore.
|
||||
// (Node will still wait for the request to complete).
|
||||
if (timer.unref) {
|
||||
timer.unref()
|
||||
}
|
||||
const newPromise = new Promise((resolve, reject) => {
|
||||
this.promisesAwaitingResponse[newId] = {resolve, reject, timer}
|
||||
})
|
||||
return [newId, newData, newPromise]
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a "response" (any message with `{type: "response"}`). Responses
|
||||
* match to the earlier request handlers, and resolve/reject based on the
|
||||
* data received.
|
||||
*/
|
||||
handleResponse(data: any) {
|
||||
if (!Number.isInteger(data.id) || data.id < 0) {
|
||||
throw new ResponseFormatError('valid id not found in response', data)
|
||||
}
|
||||
if (!this.promisesAwaitingResponse[data.id]) {
|
||||
throw new ResponseFormatError('response handler not found', data)
|
||||
}
|
||||
if (data.status === 'error') {
|
||||
const error = new RippledError(data.error_message || data.error, data)
|
||||
this.reject(data.id, error)
|
||||
return
|
||||
}
|
||||
if (data.status !== 'success') {
|
||||
const error = new ResponseFormatError(
|
||||
`unrecognized status: ${data.status}`,
|
||||
data
|
||||
)
|
||||
this.reject(data.id, error)
|
||||
return
|
||||
}
|
||||
this.resolve(data.id, data.result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main Connection class. Responsible for connecting to & managing
|
||||
* an active WebSocket connection to a XRPL node.
|
||||
*/
|
||||
export class Connection extends EventEmitter {
|
||||
private _url: string
|
||||
private _trace: boolean
|
||||
private _console?: Console
|
||||
private _proxyURL?: string
|
||||
private _proxyAuthorization?: string
|
||||
private _authorization?: string
|
||||
private _trustedCertificates?: string[]
|
||||
private _key?: string
|
||||
private _passphrase?: string
|
||||
private _certificate?: string
|
||||
private _timeout: number
|
||||
private _isReady: boolean = false
|
||||
private _ws: null|WebSocket = null
|
||||
protected _ledgerVersion: null|number = null
|
||||
private _availableLedgerVersions = new RangeSet()
|
||||
private _nextRequestID: number = 1
|
||||
private _retry: number = 0
|
||||
private _retryTimer: null|NodeJS.Timer = null
|
||||
private _onOpenErrorBound: null| null|((...args: any[]) => void) = null
|
||||
private _onUnexpectedCloseBound: null|((...args: any[]) => void) = null
|
||||
private _fee_base: null|number = null
|
||||
private _fee_ref: null|number = null
|
||||
private _ws: null | WebSocket = null
|
||||
private _reconnectTimeoutID: null | NodeJS.Timeout = null
|
||||
private _heartbeatIntervalID: null | NodeJS.Timeout = null
|
||||
private _retryConnectionBackoff = new ExponentialBackoff({
|
||||
min: 100,
|
||||
max: 60 * 1000
|
||||
})
|
||||
|
||||
constructor(url, options: ConnectionOptions = {}) {
|
||||
private _trace: (id: string, message: string) => void = () => {}
|
||||
private _config: ConnectionOptions
|
||||
private _ledger: LedgerHistory = new LedgerHistory()
|
||||
private _requestManager = new RequestManager()
|
||||
private _connectionManager = new ConnectionManager()
|
||||
|
||||
constructor(url?: string, options: ConnectionUserOptions = {}) {
|
||||
super()
|
||||
this.setMaxListeners(Infinity)
|
||||
this._url = url
|
||||
this._trace = options.trace || false
|
||||
if (this._trace) {
|
||||
// for easier unit testing
|
||||
this._console = console
|
||||
this._config = {
|
||||
timeout: 20 * 1000,
|
||||
connectionTimeout: 2 * 1000,
|
||||
...options
|
||||
}
|
||||
this._proxyURL = options.proxy
|
||||
this._proxyAuthorization = options.proxyAuthorization
|
||||
this._authorization = options.authorization
|
||||
this._trustedCertificates = options.trustedCertificates
|
||||
this._key = options.key
|
||||
this._passphrase = options.passphrase
|
||||
this._certificate = options.certificate
|
||||
this._timeout = options.timeout || (20 * 1000)
|
||||
}
|
||||
|
||||
_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)
|
||||
if (typeof options.trace === 'function') {
|
||||
this._trace = options.trace
|
||||
} else if (options.trace === true) {
|
||||
this._trace = console.log
|
||||
}
|
||||
}
|
||||
|
||||
_updateFees(data) {
|
||||
this._fee_base = Number(data.fee_base)
|
||||
this._fee_ref = Number(data.fee_ref)
|
||||
}
|
||||
|
||||
// return value is array of arguments to Connection.emit
|
||||
_parseMessage(message): [string, Object] | ['error', string, string, Object] {
|
||||
const data = JSON.parse(message)
|
||||
if (data.type === 'response') {
|
||||
if (!(Number.isInteger(data.id) && data.id >= 0)) {
|
||||
throw new ResponseFormatError('valid id not found in response', data)
|
||||
}
|
||||
return [data.id.toString(), data]
|
||||
} else if (data.type === undefined && data.error) {
|
||||
return ['error', data.error, data.error_message, data] // e.g. slowDown
|
||||
}
|
||||
|
||||
// Possible `data.type` values include 'ledgerClosed',
|
||||
// 'transaction', 'path_find', and many others.
|
||||
if (data.type === 'ledgerClosed') {
|
||||
this._updateLedgerVersions(data)
|
||||
this._updateFees(data)
|
||||
}
|
||||
return [data.type, data]
|
||||
}
|
||||
|
||||
_onMessage(message) {
|
||||
if (this._trace) {
|
||||
this._console!.log(message)
|
||||
}
|
||||
let parameters
|
||||
private _onMessage(message) {
|
||||
this._trace('receive', message)
|
||||
let data: any
|
||||
try {
|
||||
parameters = this._parseMessage(message)
|
||||
data = JSON.parse(message)
|
||||
} catch (error) {
|
||||
this.emit('error', 'badMessage', error.message, message)
|
||||
return
|
||||
}
|
||||
// we don't want this inside the try/catch or exceptions in listener
|
||||
// will be caught
|
||||
this.emit.apply(this, parameters)
|
||||
if (data.type === undefined && data.error) {
|
||||
this.emit('error', data.error, data.error_message, data) // e.g. slowDown
|
||||
return
|
||||
}
|
||||
if (data.type) {
|
||||
this.emit(data.type, data)
|
||||
}
|
||||
if (data.type === 'ledgerClosed') {
|
||||
this._ledger.update(data)
|
||||
}
|
||||
if (data.type === 'response') {
|
||||
try {
|
||||
this._requestManager.handleResponse(data)
|
||||
} catch (error) {
|
||||
this.emit('error', 'badMessage', error.message, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get _state() {
|
||||
private get _state() {
|
||||
return this._ws ? this._ws.readyState : WebSocket.CLOSED
|
||||
}
|
||||
|
||||
get _shouldBeConnected() {
|
||||
private get _shouldBeConnected() {
|
||||
return this._ws !== null
|
||||
}
|
||||
|
||||
isConnected() {
|
||||
return this._state === WebSocket.OPEN && this._isReady
|
||||
private _clearHeartbeatInterval = () => {
|
||||
clearInterval(this._heartbeatIntervalID)
|
||||
}
|
||||
|
||||
_onUnexpectedClose(beforeOpen, resolve, reject, code) {
|
||||
if (this._onOpenErrorBound) {
|
||||
this._ws!.removeListener('error', this._onOpenErrorBound)
|
||||
this._onOpenErrorBound = null
|
||||
}
|
||||
// just in case
|
||||
this._ws!.removeAllListeners('open')
|
||||
this._ws = null
|
||||
this._isReady = false
|
||||
if (beforeOpen) {
|
||||
// connection was closed before it was properly opened, so we must return
|
||||
// error to connect's caller
|
||||
this.connect().then(resolve, reject)
|
||||
} else {
|
||||
// if first parameter ws lib sends close code,
|
||||
// but sometimes it forgots about it, so default to 1006 - CLOSE_ABNORMAL
|
||||
this.emit('disconnected', code || 1006)
|
||||
this._retryConnect()
|
||||
}
|
||||
private _startHeartbeatInterval = () => {
|
||||
this._clearHeartbeatInterval()
|
||||
this._heartbeatIntervalID = setInterval(
|
||||
() => this._heartbeat(),
|
||||
this._config.timeout
|
||||
)
|
||||
}
|
||||
|
||||
_calculateTimeout(retriesCount) {
|
||||
return (retriesCount < 40)
|
||||
// First, for 2 seconds: 20 times per second
|
||||
? (1000 / 20)
|
||||
: (retriesCount < 40 + 60)
|
||||
// Then, for 1 minute: once per second
|
||||
? (1000)
|
||||
: (retriesCount < 40 + 60 + 60)
|
||||
// Then, for 10 minutes: once every 10 seconds
|
||||
? (10 * 1000)
|
||||
// Then: once every 30 seconds
|
||||
: (30 * 1000)
|
||||
/**
|
||||
* A heartbeat is just a "ping" command, sent on an interval.
|
||||
* If this succeeds, we're good. If it fails, disconnect so that the consumer can reconnect, if desired.
|
||||
*/
|
||||
private _heartbeat = () => {
|
||||
return this.request({command: 'ping'}).catch(() => this.reconnect())
|
||||
}
|
||||
|
||||
_retryConnect() {
|
||||
this._retry += 1
|
||||
const retryTimeout = this._calculateTimeout(this._retry)
|
||||
this._retryTimer = setTimeout(() => {
|
||||
this.emit('reconnecting', this._retry)
|
||||
this.connect().catch(this._retryConnect.bind(this))
|
||||
}, retryTimeout)
|
||||
}
|
||||
|
||||
_clearReconnectTimer() {
|
||||
if (this._retryTimer !== null) {
|
||||
clearTimeout(this._retryTimer)
|
||||
this._retryTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
_onOpen() {
|
||||
if (!this._ws) {
|
||||
return Promise.reject(new DisconnectedError())
|
||||
}
|
||||
if (this._onOpenErrorBound) {
|
||||
this._ws.removeListener('error', this._onOpenErrorBound)
|
||||
this._onOpenErrorBound = null
|
||||
}
|
||||
|
||||
const request = {
|
||||
command: 'subscribe',
|
||||
streams: ['ledger']
|
||||
}
|
||||
return this.request(request).then((data: any) => {
|
||||
if (_.isEmpty(data) || !data.ledger_index) {
|
||||
// rippled instance doesn't have validated ledgers
|
||||
return this._disconnect(false).then(() => {
|
||||
throw new RippledNotInitializedError('Rippled not initialized')
|
||||
})
|
||||
/**
|
||||
* Wait for a valid connection before resolving. Useful for deferring methods
|
||||
* until a connection has been established.
|
||||
*/
|
||||
private _waitForReady(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._shouldBeConnected) {
|
||||
reject(new NotConnectedError())
|
||||
} else if (this._state === WebSocket.OPEN) {
|
||||
resolve()
|
||||
} else {
|
||||
this.once('connected', () => resolve())
|
||||
}
|
||||
|
||||
this._updateLedgerVersions(data)
|
||||
this._updateFees(data)
|
||||
this._rebindOnUnexpectedClose()
|
||||
|
||||
this._retry = 0
|
||||
this._ws.on('error', error => {
|
||||
this.emit('error', 'websocket', error.message, error)
|
||||
})
|
||||
|
||||
this._isReady = true
|
||||
this.emit('connected')
|
||||
|
||||
return undefined
|
||||
})
|
||||
}
|
||||
|
||||
_rebindOnUnexpectedClose() {
|
||||
if (this._onUnexpectedCloseBound) {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound)
|
||||
}
|
||||
this._onUnexpectedCloseBound =
|
||||
this._onUnexpectedClose.bind(this, false, null, null)
|
||||
this._ws.once('close', this._onUnexpectedCloseBound)
|
||||
}
|
||||
|
||||
_unbindOnUnexpectedClose() {
|
||||
if (this._onUnexpectedCloseBound) {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound)
|
||||
}
|
||||
this._onUnexpectedCloseBound = null
|
||||
}
|
||||
|
||||
_onOpenError(reject, error) {
|
||||
this._onOpenErrorBound = null
|
||||
this._unbindOnUnexpectedClose()
|
||||
reject(new NotConnectedError(error.message, error))
|
||||
}
|
||||
|
||||
_createWebSocket(): WebSocket {
|
||||
const options: WebSocket.ClientOptions = {}
|
||||
if (this._proxyURL !== undefined) {
|
||||
const parsedURL = parseUrl(this._url)
|
||||
const parsedProxyURL = parseUrl(this._proxyURL)
|
||||
const proxyOverrides = _.omitBy({
|
||||
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
|
||||
private async _subscribeToLedger() {
|
||||
const data = await this.request({
|
||||
command: 'subscribe',
|
||||
streams: ['ledger']
|
||||
})
|
||||
// If rippled instance doesn't have validated ledgers, disconnect and then reject.
|
||||
if (_.isEmpty(data) || !data.ledger_index) {
|
||||
try {
|
||||
HttpsProxyAgent = require('https-proxy-agent')
|
||||
await this.disconnect()
|
||||
} catch (error) {
|
||||
throw new Error('"proxy" option is not supported in the browser')
|
||||
// Ignore this error, propagate the root cause.
|
||||
} finally {
|
||||
// Throw the root error (takes precendence over try/catch).
|
||||
// eslint-disable-next-line no-unsafe-finally
|
||||
throw new RippledNotInitializedError('Rippled not initialized')
|
||||
}
|
||||
options.agent = new HttpsProxyAgent(proxyOptions)
|
||||
}
|
||||
if (this._authorization !== undefined) {
|
||||
const base64 = Buffer.from(this._authorization).toString('base64')
|
||||
options.headers = {Authorization: `Basic ${base64}`}
|
||||
this._ledger.update(data)
|
||||
}
|
||||
|
||||
private _onConnectionFailed = (errorOrCode: Error | number | undefined) => {
|
||||
if (this._ws) {
|
||||
this._ws.removeAllListeners()
|
||||
this._ws.on('error', () => {
|
||||
// Correctly listen for -- but ignore -- any future errors: If you
|
||||
// don't have a listener on "error" node would log a warning on error.
|
||||
})
|
||||
this._ws.close()
|
||||
this._ws = null
|
||||
}
|
||||
const optionsOverrides = _.omitBy({
|
||||
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)
|
||||
if (typeof errorOrCode === 'number') {
|
||||
this._connectionManager.rejectAllAwaiting(
|
||||
new NotConnectedError(`Connection failed with code ${errorOrCode}.`, {
|
||||
code: errorOrCode
|
||||
})
|
||||
)
|
||||
} else if (errorOrCode && errorOrCode.message) {
|
||||
this._connectionManager.rejectAllAwaiting(
|
||||
new NotConnectedError(errorOrCode.message, errorOrCode)
|
||||
)
|
||||
} else {
|
||||
this._connectionManager.rejectAllAwaiting(
|
||||
new NotConnectedError('Connection failed.')
|
||||
)
|
||||
}
|
||||
return websocket
|
||||
}
|
||||
|
||||
isConnected() {
|
||||
return this._state === WebSocket.OPEN
|
||||
}
|
||||
|
||||
connect(): Promise<void> {
|
||||
this._clearReconnectTimer()
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._url) {
|
||||
reject(new ConnectionError(
|
||||
'Cannot connect because no server was specified'))
|
||||
}
|
||||
if (this._state === WebSocket.OPEN) {
|
||||
resolve()
|
||||
} else if (this._state === WebSocket.CONNECTING) {
|
||||
this._ws.once('open', resolve)
|
||||
} else {
|
||||
this._ws = this._createWebSocket()
|
||||
// when an error causes the connection to close, the close event
|
||||
// should still be emitted; the "ws" documentation says: "The close
|
||||
// event is also emitted when then underlying net.Socket closes the
|
||||
// connection (end or close)."
|
||||
// In case if there is connection error (say, server is not responding)
|
||||
// we must return this error to connection's caller. After successful
|
||||
// opening, we will forward all errors to main api object.
|
||||
this._onOpenErrorBound = this._onOpenError.bind(this, reject)
|
||||
this._ws.once('error', this._onOpenErrorBound)
|
||||
this._ws.on('message', this._onMessage.bind(this))
|
||||
// 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, true,
|
||||
resolve, reject)
|
||||
this._ws.once('close', this._onUnexpectedCloseBound)
|
||||
this._ws.once('open', () => this._onOpen().then(resolve, reject))
|
||||
if (this.isConnected()) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
if (this._state === WebSocket.CONNECTING) {
|
||||
return this._connectionManager.awaitConnection()
|
||||
}
|
||||
if (!this._url) {
|
||||
return Promise.reject(
|
||||
new ConnectionError('Cannot connect because no server was specified')
|
||||
)
|
||||
}
|
||||
if (this._ws) {
|
||||
return Promise.reject(
|
||||
new RippleError('Websocket connection never cleaned up.', {
|
||||
state: this._state
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// Create the connection timeout, in case the connection hangs longer than expected.
|
||||
const connectionTimeoutID = setTimeout(() => {
|
||||
this._onConnectionFailed(
|
||||
new ConnectionError(
|
||||
`Error: connect() timed out after ${this._config.connectionTimeout} ms. ` +
|
||||
`If your internet connection is working, the rippled server may be blocked or inaccessible.`
|
||||
)
|
||||
)
|
||||
}, this._config.connectionTimeout)
|
||||
// Connection listeners: these stay attached only until a connection is done/open.
|
||||
this._ws = createWebSocket(this._url, this._config)
|
||||
this._ws.on('error', this._onConnectionFailed)
|
||||
this._ws.on('error', () => clearTimeout(connectionTimeoutID))
|
||||
this._ws.on('close', this._onConnectionFailed)
|
||||
this._ws.on('close', () => clearTimeout(connectionTimeoutID))
|
||||
this._ws.once('open', async () => {
|
||||
// Once the connection completes successfully, remove all old listeners
|
||||
this._ws.removeAllListeners()
|
||||
clearTimeout(connectionTimeoutID)
|
||||
// Add new, long-term connected listeners for messages and errors
|
||||
this._ws.on('message', (message: string) => this._onMessage(message))
|
||||
this._ws.on('error', error =>
|
||||
this.emit('error', 'websocket', error.message, error)
|
||||
)
|
||||
// Finalize the connection and resolve all awaiting connect() requests
|
||||
try {
|
||||
this._retryConnectionBackoff.reset()
|
||||
await this._subscribeToLedger()
|
||||
this._startHeartbeatInterval()
|
||||
this._connectionManager.resolveAllAwaiting()
|
||||
this.emit('connected')
|
||||
} catch (error) {
|
||||
this._connectionManager.rejectAllAwaiting(error)
|
||||
this.disconnect()
|
||||
return
|
||||
}
|
||||
// Handle a closed connection: reconnect if it was unexpected
|
||||
this._ws.once('close', code => {
|
||||
this._clearHeartbeatInterval()
|
||||
this._requestManager.rejectAll(
|
||||
new DisconnectedError('websocket was closed')
|
||||
)
|
||||
this._ws.removeAllListeners()
|
||||
this._ws = null
|
||||
this.emit('disconnected', code)
|
||||
// If this wasn't a manual disconnect, then lets reconnect ASAP.
|
||||
if (code !== INTENTIONAL_DISCONNECT_CODE) {
|
||||
const retryTimeout = this._retryConnectionBackoff.duration()
|
||||
this._trace('reconnect', `Retrying connection in ${retryTimeout}ms.`)
|
||||
this.emit('reconnecting', this._retryConnectionBackoff.attempts)
|
||||
// Start the reconnect timeout, but set it to `this._reconnectTimeoutID`
|
||||
// so that we can cancel one in-progress on disconnect.
|
||||
this._reconnectTimeoutID = setTimeout(() => {
|
||||
this.reconnect().catch(error => {
|
||||
this.emit('error', 'reconnect', error.message, error)
|
||||
})
|
||||
}, retryTimeout)
|
||||
}
|
||||
})
|
||||
})
|
||||
return this._connectionManager.awaitConnection()
|
||||
}
|
||||
|
||||
disconnect(): Promise<void> {
|
||||
return this._disconnect(true)
|
||||
}
|
||||
|
||||
_disconnect(calledByUser): Promise<void> {
|
||||
if (calledByUser) {
|
||||
this._clearReconnectTimer()
|
||||
this._retry = 0
|
||||
/**
|
||||
* Disconnect the websocket connection.
|
||||
* We never expect this method to reject. Even on "bad" disconnects, the websocket
|
||||
* should still successfully close with the relevant error code returned.
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent for the full list.
|
||||
* If no open websocket connection exists, resolve with no code (`undefined`).
|
||||
*/
|
||||
disconnect(): Promise<number | undefined> {
|
||||
clearTimeout(this._reconnectTimeoutID);
|
||||
this._reconnectTimeoutID = null;
|
||||
if (this._state === WebSocket.CLOSED || !this._ws) {
|
||||
return Promise.resolve(undefined)
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
if (this._state === WebSocket.CLOSED) {
|
||||
resolve()
|
||||
} else if (this._state === WebSocket.CLOSING) {
|
||||
this._ws.once('close', resolve)
|
||||
} else {
|
||||
if (this._onUnexpectedCloseBound) {
|
||||
this._ws.removeListener('close', this._onUnexpectedCloseBound)
|
||||
this._onUnexpectedCloseBound = null
|
||||
}
|
||||
this._ws.once('close', code => {
|
||||
this._ws = null
|
||||
this._isReady = false
|
||||
if (calledByUser) {
|
||||
this.emit('disconnected', code || 1000) // 1000 - CLOSE_NORMAL
|
||||
}
|
||||
resolve()
|
||||
})
|
||||
this._ws.close()
|
||||
this._ws.once('close', code => resolve(code))
|
||||
// Connection already has a disconnect handler for the disconnect logic.
|
||||
// Just close the websocket manually (with our "intentional" code) to
|
||||
// trigger that.
|
||||
if (this._state !== WebSocket.CLOSING) {
|
||||
this._ws.close(INTENTIONAL_DISCONNECT_CODE)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
reconnect() {
|
||||
return this.disconnect().then(() => this.connect())
|
||||
/**
|
||||
* Disconnect the websocket, then connect again.
|
||||
*/
|
||||
async reconnect() {
|
||||
// NOTE: We currently have a "reconnecting" event, but that only triggers
|
||||
// through an unexpected connection retry logic.
|
||||
// See: https://github.com/ripple/ripple-lib/pull/1101#issuecomment-565360423
|
||||
this.emit('reconnect')
|
||||
await this.disconnect()
|
||||
await this.connect()
|
||||
}
|
||||
|
||||
_whenReady<T>(promise: Promise<T>): Promise<T> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._shouldBeConnected) {
|
||||
reject(new NotConnectedError())
|
||||
} else if (this._state === WebSocket.OPEN && this._isReady) {
|
||||
promise.then(resolve, reject)
|
||||
} else {
|
||||
this.once('connected', () => promise.then(resolve, reject))
|
||||
}
|
||||
})
|
||||
async getLedgerVersion(): Promise<number> {
|
||||
await this._waitForReady()
|
||||
return this._ledger.latestVersion!
|
||||
}
|
||||
|
||||
getLedgerVersion(): Promise<number> {
|
||||
return this._whenReady(Promise.resolve(this._ledgerVersion!))
|
||||
async getFeeBase(): Promise<number> {
|
||||
await this._waitForReady()
|
||||
return this._ledger.feeBase!
|
||||
}
|
||||
|
||||
hasLedgerVersions(lowLedgerVersion, highLedgerVersion): Promise<boolean> {
|
||||
return this._whenReady(Promise.resolve(
|
||||
this._availableLedgerVersions.containsRange(
|
||||
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)))
|
||||
async getFeeRef(): Promise<number> {
|
||||
await this._waitForReady()
|
||||
return this._ledger.feeRef!
|
||||
}
|
||||
|
||||
hasLedgerVersion(ledgerVersion): Promise<boolean> {
|
||||
return this.hasLedgerVersions(ledgerVersion, ledgerVersion)
|
||||
}
|
||||
|
||||
getFeeBase(): Promise<number> {
|
||||
return this._whenReady(Promise.resolve(Number(this._fee_base)))
|
||||
}
|
||||
|
||||
getFeeRef(): Promise<number> {
|
||||
return this._whenReady(Promise.resolve(Number(this._fee_ref)))
|
||||
}
|
||||
|
||||
_send(message: string): Promise<void> {
|
||||
if (this._trace) {
|
||||
this._console.log(message)
|
||||
/**
|
||||
* Returns true if the given range of ledger versions exist in history
|
||||
* (inclusive).
|
||||
*/
|
||||
async hasLedgerVersions(
|
||||
lowLedgerVersion: number,
|
||||
highLedgerVersion: number | undefined
|
||||
): Promise<boolean> {
|
||||
// You can call hasVersions with a potentially unknown upper limit, which
|
||||
// will just act as a check on the lower limit.
|
||||
if (!highLedgerVersion) {
|
||||
return this.hasLedgerVersion(lowLedgerVersion)
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._ws.send(message, undefined, error => {
|
||||
if (error) {
|
||||
reject(new DisconnectedError(error.message, error))
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
await this._waitForReady()
|
||||
return this._ledger.hasVersions(lowLedgerVersion, highLedgerVersion)
|
||||
}
|
||||
|
||||
request(request, timeout?: number): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this._shouldBeConnected) {
|
||||
reject(new NotConnectedError())
|
||||
}
|
||||
/**
|
||||
* Returns true if the given ledger version exists in history.
|
||||
*/
|
||||
async hasLedgerVersion(ledgerVersion: number): Promise<boolean> {
|
||||
await this._waitForReady()
|
||||
return this._ledger.hasVersion(ledgerVersion)
|
||||
}
|
||||
|
||||
let timer = null
|
||||
const self = this
|
||||
const id = this._nextRequestID
|
||||
this._nextRequestID += 1
|
||||
const eventName = id.toString()
|
||||
|
||||
function onDisconnect() {
|
||||
clearTimeout(timer)
|
||||
self.removeAllListeners(eventName)
|
||||
reject(new DisconnectedError('websocket was closed'))
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
clearTimeout(timer)
|
||||
self.removeAllListeners(eventName)
|
||||
if (self._ws !== null) {
|
||||
self._ws.removeListener('close', onDisconnect)
|
||||
}
|
||||
}
|
||||
|
||||
function _resolve(response) {
|
||||
cleanup()
|
||||
resolve(response)
|
||||
}
|
||||
|
||||
function _reject(error) {
|
||||
cleanup()
|
||||
reject(error)
|
||||
}
|
||||
|
||||
this.once(eventName, response => {
|
||||
if (response.status === 'error') {
|
||||
_reject(new RippledError(response.error_message || response.error, response))
|
||||
} else if (response.status === 'success') {
|
||||
_resolve(response.result)
|
||||
} else {
|
||||
_reject(new ResponseFormatError(
|
||||
'unrecognized status: ' + response.status, response))
|
||||
}
|
||||
})
|
||||
|
||||
this._ws.once('close', onDisconnect)
|
||||
|
||||
// JSON.stringify automatically removes keys with value of 'undefined'
|
||||
const message = JSON.stringify(Object.assign({}, request, {id}))
|
||||
|
||||
this._whenReady(this._send(message)).then(() => {
|
||||
const delay = timeout || this._timeout
|
||||
timer = setTimeout(() => _reject(new TimeoutError()), delay)
|
||||
}).catch(_reject)
|
||||
async request(request, timeout?: number): Promise<any> {
|
||||
if (!this._shouldBeConnected) {
|
||||
throw new NotConnectedError()
|
||||
}
|
||||
const [id, message, responsePromise] = this._requestManager.createRequest(
|
||||
request,
|
||||
timeout || this._config.timeout
|
||||
)
|
||||
this._trace('send', message)
|
||||
websocketSendAsync(this._ws, message).catch(error => {
|
||||
this._requestManager.reject(id, error)
|
||||
})
|
||||
|
||||
return responsePromise
|
||||
}
|
||||
}
|
||||
|
||||
export default Connection
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
|
||||
import {txFlagIndices} from './txflags'
|
||||
|
||||
// Ordering from https://developers.ripple.com/accountroot.html
|
||||
const accountRootFlags = {
|
||||
|
||||
// lsfDefaultRipple:
|
||||
// Enable rippling on trust lines by default.
|
||||
// Required for issuing addresses; discouraged for others.
|
||||
@@ -73,15 +71,17 @@ const AccountFlagIndices = {
|
||||
}
|
||||
|
||||
const AccountFields = {
|
||||
EmailHash: {name: 'emailHash', encoding: 'hex',
|
||||
length: 32, defaults: '0'},
|
||||
EmailHash: {
|
||||
name: 'emailHash',
|
||||
encoding: 'hex',
|
||||
length: 32,
|
||||
defaults: '00000000000000000000000000000000'
|
||||
},
|
||||
WalletLocator: {name: 'walletLocator'},
|
||||
MessageKey: {name: 'messageKey'},
|
||||
Domain: {name: 'domain', encoding: 'hex'},
|
||||
TransferRate: {name: 'transferRate', defaults: 0, shift: 9}
|
||||
TransferRate: {name: 'transferRate', defaults: 0, shift: 9},
|
||||
TickSize: {name: 'tickSize', defaults: 0}
|
||||
}
|
||||
|
||||
export {
|
||||
AccountFields,
|
||||
AccountFlagIndices,
|
||||
AccountFlags
|
||||
}
|
||||
export {AccountFields, AccountFlagIndices, AccountFlags}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
|
||||
import {inspect} from 'util'
|
||||
import * as browserHacks from './browser-hacks'
|
||||
|
||||
class RippleError extends Error {
|
||||
|
||||
name: string
|
||||
message: string
|
||||
data?: any
|
||||
@@ -70,8 +68,11 @@ class MissingLedgerHistoryError extends RippleError {
|
||||
|
||||
class PendingLedgerVersionError extends RippleError {
|
||||
constructor(message?: string) {
|
||||
super(message || 'maxLedgerVersion is greater than server\'s most recent' +
|
||||
' validated ledger')
|
||||
super(
|
||||
message ||
|
||||
"maxLedgerVersion is greater than server's most recent" +
|
||||
' validated ledger'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
49
src/common/hashes/README.md
Normal file
49
src/common/hashes/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# XRP Ledger Hashes
|
||||
|
||||
Methods to hash XRP Ledger objects
|
||||
|
||||
## Methods
|
||||
|
||||
### computeBinaryTransactionHash = (txBlobHex: string): string
|
||||
|
||||
Compute the hash of a binary transaction blob.
|
||||
|
||||
### computeTransactionHash = (txJSON: any): string
|
||||
|
||||
Compute the hash of a transaction in txJSON format.
|
||||
|
||||
### computeBinaryTransactionSigningHash = (txBlobHex: string): string
|
||||
|
||||
### computeTransactionSigningHash = (txJSON: any): string
|
||||
|
||||
### computeAccountHash = (address: string): string
|
||||
|
||||
Compute the hash of an account, given the account's classic address (starting with `r`).
|
||||
|
||||
### computeSignerListHash = (address: string): string
|
||||
|
||||
Compute the hash of an account's SignerList.
|
||||
|
||||
### computeOrderHash = (address: string, sequence: number): string
|
||||
|
||||
Compute the hash of an order, given the owner's classic address (starting with `r`) and the account sequence number of the `OfferCreate` order transaction.
|
||||
|
||||
### computeTrustlineHash = (address1: string, address2: string, currency: string): string
|
||||
|
||||
Compute the hash of a trustline, given the two parties' classic addresses (starting with `r`) and the currency code.
|
||||
|
||||
### computeTransactionTreeHash = (transactions: any[]): string
|
||||
|
||||
### computeStateTreeHash = (entries: any[]): string
|
||||
|
||||
### computeLedgerHash = (ledgerHeader): string
|
||||
|
||||
Compute the hash of a ledger.
|
||||
|
||||
### computeEscrowHash = (address, sequence): string
|
||||
|
||||
Compute the hash of an escrow, given the owner's classic address (starting with `r`) and the account sequence number of the `EscrowCreate` escrow transaction.
|
||||
|
||||
### computePaymentChannelHash = (address, dstAddress, sequence): string
|
||||
|
||||
Compute the hash of a payment channel, given the owner's classic address (starting with `r`), the classic address of the destination, and the account sequence number of the `PaymentChannelCreate` payment channel transaction.
|
||||
40
src/common/hashes/hash-prefix.ts
Normal file
40
src/common/hashes/hash-prefix.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Prefix for hashing functions.
|
||||
*
|
||||
* These prefixes are inserted before the source material used to
|
||||
* generate various hashes. This is done to put each hash in its own
|
||||
* "space." This way, two different types of objects with the
|
||||
* same binary data will produce different hashes.
|
||||
*
|
||||
* Each prefix is a 4-byte value with the last byte set to zero
|
||||
* and the first three bytes formed from the ASCII equivalent of
|
||||
* some arbitrary string. For example "TXN".
|
||||
*/
|
||||
|
||||
enum HashPrefix {
|
||||
// transaction plus signature to give transaction ID
|
||||
TRANSACTION_ID = 0x54584e00, // 'TXN'
|
||||
|
||||
// transaction plus metadata
|
||||
TRANSACTION_NODE = 0x534e4400, // 'TND'
|
||||
|
||||
// inner node in tree
|
||||
INNER_NODE = 0x4d494e00, // 'MIN'
|
||||
|
||||
// leaf node in tree
|
||||
LEAF_NODE = 0x4d4c4e00, // 'MLN'
|
||||
|
||||
// inner transaction to sign
|
||||
TRANSACTION_SIGN = 0x53545800, // 'STX'
|
||||
|
||||
// inner transaction to sign (TESTNET)
|
||||
TRANSACTION_SIGN_TESTNET = 0x73747800, // 'stx'
|
||||
|
||||
// inner transaction to multisign
|
||||
TRANSACTION_MULTISIGN = 0x534d5400, // 'SMT'
|
||||
|
||||
// ledger
|
||||
LEDGER = 0x4c575200 // 'LWR'
|
||||
}
|
||||
|
||||
export default HashPrefix
|
||||
179
src/common/hashes/index.ts
Normal file
179
src/common/hashes/index.ts
Normal file
@@ -0,0 +1,179 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {decodeAccountID} from 'ripple-address-codec'
|
||||
import sha512Half from './sha512Half'
|
||||
import HashPrefix from './hash-prefix'
|
||||
import {SHAMap, NodeType} from './shamap'
|
||||
import {encode} from 'ripple-binary-codec'
|
||||
import ledgerspaces from './ledgerspaces'
|
||||
|
||||
const padLeftZero = (string: string, length: number): string => {
|
||||
return Array(length - string.length + 1).join('0') + string
|
||||
}
|
||||
|
||||
const intToHex = (integer: number, byteLength: number): string => {
|
||||
return padLeftZero(Number(integer).toString(16), byteLength * 2)
|
||||
}
|
||||
|
||||
const bytesToHex = (bytes: number[]): string => {
|
||||
return Buffer.from(bytes).toString('hex')
|
||||
}
|
||||
|
||||
const bigintToHex = (
|
||||
integerString: string | number | BigNumber,
|
||||
byteLength: number
|
||||
): string => {
|
||||
const hex = new BigNumber(integerString).toString(16)
|
||||
return padLeftZero(hex, byteLength * 2)
|
||||
}
|
||||
|
||||
const ledgerSpaceHex = (name: string): string => {
|
||||
return intToHex(ledgerspaces[name].charCodeAt(0), 2)
|
||||
}
|
||||
|
||||
const addressToHex = (address: string): string => {
|
||||
return Buffer.from(decodeAccountID(address)).toString('hex')
|
||||
}
|
||||
|
||||
const currencyToHex = (currency: string): string => {
|
||||
if (currency.length === 3) {
|
||||
const bytes = new Array(20 + 1)
|
||||
.join('0')
|
||||
.split('')
|
||||
.map(parseFloat)
|
||||
bytes[12] = currency.charCodeAt(0) & 0xff
|
||||
bytes[13] = currency.charCodeAt(1) & 0xff
|
||||
bytes[14] = currency.charCodeAt(2) & 0xff
|
||||
return bytesToHex(bytes)
|
||||
}
|
||||
return currency
|
||||
}
|
||||
|
||||
const addLengthPrefix = (hex: string): string => {
|
||||
const length = hex.length / 2
|
||||
if (length <= 192) {
|
||||
return bytesToHex([length]) + hex
|
||||
} else if (length <= 12480) {
|
||||
const x = length - 193
|
||||
return bytesToHex([193 + (x >>> 8), x & 0xff]) + hex
|
||||
} else if (length <= 918744) {
|
||||
const x = length - 12481
|
||||
return bytesToHex([241 + (x >>> 16), (x >>> 8) & 0xff, x & 0xff]) + hex
|
||||
}
|
||||
throw new Error('Variable integer overflow.')
|
||||
}
|
||||
|
||||
export const computeBinaryTransactionHash = (txBlobHex: string): string => {
|
||||
const prefix = HashPrefix.TRANSACTION_ID.toString(16).toUpperCase()
|
||||
return sha512Half(prefix + txBlobHex)
|
||||
}
|
||||
|
||||
export const computeTransactionHash = (txJSON: any): string => {
|
||||
return computeBinaryTransactionHash(encode(txJSON))
|
||||
}
|
||||
|
||||
export const computeBinaryTransactionSigningHash = (
|
||||
txBlobHex: string
|
||||
): string => {
|
||||
const prefix = HashPrefix.TRANSACTION_SIGN.toString(16).toUpperCase()
|
||||
return sha512Half(prefix + txBlobHex)
|
||||
}
|
||||
|
||||
export const computeTransactionSigningHash = (txJSON: any): string => {
|
||||
return computeBinaryTransactionSigningHash(encode(txJSON))
|
||||
}
|
||||
|
||||
export const computeAccountHash = (address: string): string => {
|
||||
return sha512Half(ledgerSpaceHex('account') + addressToHex(address))
|
||||
}
|
||||
|
||||
export const computeSignerListHash = (address: string): string => {
|
||||
return sha512Half(
|
||||
ledgerSpaceHex('signerList') + addressToHex(address) + '00000000'
|
||||
) // uint32(0) signer list index
|
||||
}
|
||||
|
||||
export const computeOrderHash = (address: string, sequence: number): string => {
|
||||
const prefix = '00' + intToHex(ledgerspaces.offer.charCodeAt(0), 1)
|
||||
return sha512Half(prefix + addressToHex(address) + intToHex(sequence, 4))
|
||||
}
|
||||
|
||||
export const computeTrustlineHash = (
|
||||
address1: string,
|
||||
address2: string,
|
||||
currency: string
|
||||
): string => {
|
||||
const address1Hex = addressToHex(address1)
|
||||
const address2Hex = addressToHex(address2)
|
||||
|
||||
const swap = new BigNumber(address1Hex, 16).isGreaterThan(
|
||||
new BigNumber(address2Hex, 16)
|
||||
)
|
||||
const lowAddressHex = swap ? address2Hex : address1Hex
|
||||
const highAddressHex = swap ? address1Hex : address2Hex
|
||||
|
||||
const prefix = ledgerSpaceHex('rippleState')
|
||||
return sha512Half(
|
||||
prefix + lowAddressHex + highAddressHex + currencyToHex(currency)
|
||||
)
|
||||
}
|
||||
|
||||
export const computeTransactionTreeHash = (transactions: any[]): string => {
|
||||
const shamap = new SHAMap()
|
||||
|
||||
transactions.forEach(txJSON => {
|
||||
const txBlobHex = encode(txJSON)
|
||||
const metaHex = encode(txJSON.metaData)
|
||||
const txHash = computeBinaryTransactionHash(txBlobHex)
|
||||
const data = addLengthPrefix(txBlobHex) + addLengthPrefix(metaHex)
|
||||
shamap.addItem(txHash, data, NodeType.TRANSACTION_METADATA)
|
||||
})
|
||||
|
||||
return shamap.hash
|
||||
}
|
||||
|
||||
export const computeStateTreeHash = (entries: any[]): string => {
|
||||
const shamap = new SHAMap()
|
||||
|
||||
entries.forEach(ledgerEntry => {
|
||||
const data = encode(ledgerEntry)
|
||||
shamap.addItem(ledgerEntry.index, data, NodeType.ACCOUNT_STATE)
|
||||
})
|
||||
|
||||
return shamap.hash
|
||||
}
|
||||
|
||||
// see rippled Ledger::updateHash()
|
||||
export const computeLedgerHash = (ledgerHeader): string => {
|
||||
const prefix = HashPrefix.LEDGER.toString(16).toUpperCase()
|
||||
return sha512Half(
|
||||
prefix +
|
||||
intToHex(ledgerHeader.ledger_index, 4) +
|
||||
bigintToHex(ledgerHeader.total_coins, 8) +
|
||||
ledgerHeader.parent_hash +
|
||||
ledgerHeader.transaction_hash +
|
||||
ledgerHeader.account_hash +
|
||||
intToHex(ledgerHeader.parent_close_time, 4) +
|
||||
intToHex(ledgerHeader.close_time, 4) +
|
||||
intToHex(ledgerHeader.close_time_resolution, 1) +
|
||||
intToHex(ledgerHeader.close_flags, 1)
|
||||
)
|
||||
}
|
||||
|
||||
export const computeEscrowHash = (address, sequence): string => {
|
||||
return sha512Half(
|
||||
ledgerSpaceHex('escrow') + addressToHex(address) + intToHex(sequence, 4)
|
||||
)
|
||||
}
|
||||
|
||||
export const computePaymentChannelHash = (
|
||||
address,
|
||||
dstAddress,
|
||||
sequence
|
||||
): string => {
|
||||
return sha512Half(
|
||||
ledgerSpaceHex('paychan') +
|
||||
addressToHex(address) +
|
||||
addressToHex(dstAddress) +
|
||||
intToHex(sequence, 4)
|
||||
)
|
||||
}
|
||||
24
src/common/hashes/ledgerspaces.ts
Normal file
24
src/common/hashes/ledgerspaces.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Ripple ledger namespace prefixes.
|
||||
*
|
||||
* The Ripple ledger is a key-value store. In order to avoid name collisions,
|
||||
* names are partitioned into namespaces.
|
||||
*
|
||||
* Each namespace is just a single character prefix.
|
||||
*/
|
||||
export default {
|
||||
account: 'a',
|
||||
dirNode: 'd',
|
||||
generatorMap: 'g',
|
||||
rippleState: 'r',
|
||||
offer: 'o', // Entry for an offer.
|
||||
ownerDir: 'O', // Directory of things owned by an account.
|
||||
bookDir: 'B', // Directory of order books.
|
||||
contract: 'c',
|
||||
skipList: 's',
|
||||
amendment: 'f',
|
||||
feeSettings: 'e',
|
||||
signerList: 'S',
|
||||
escrow: 'u',
|
||||
paychan: 'x'
|
||||
}
|
||||
11
src/common/hashes/sha512Half.ts
Normal file
11
src/common/hashes/sha512Half.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import {createHash} from 'crypto'
|
||||
|
||||
const sha512Half = (hex: string): string => {
|
||||
return createHash('sha512')
|
||||
.update(Buffer.from(hex, 'hex'))
|
||||
.digest('hex')
|
||||
.toUpperCase()
|
||||
.slice(0, 64)
|
||||
}
|
||||
|
||||
export default sha512Half
|
||||
181
src/common/hashes/shamap.ts
Normal file
181
src/common/hashes/shamap.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import hashPrefix from './hash-prefix'
|
||||
import sha512Half from './sha512Half'
|
||||
const HEX_ZERO =
|
||||
'0000000000000000000000000000000000000000000000000000000000000000'
|
||||
|
||||
export enum NodeType {
|
||||
INNER = 1,
|
||||
TRANSACTION_NO_METADATA = 2,
|
||||
TRANSACTION_METADATA = 3,
|
||||
ACCOUNT_STATE = 4
|
||||
}
|
||||
|
||||
export abstract class Node {
|
||||
/**
|
||||
* Abstract constructor representing a node in a SHAMap tree.
|
||||
* Can be either InnerNode or Leaf.
|
||||
* @constructor
|
||||
*/
|
||||
public constructor() {}
|
||||
|
||||
public addItem(_tag: string, _node: Node): void {
|
||||
throw new Error(
|
||||
'Called unimplemented virtual method SHAMapTreeNode#addItem.'
|
||||
)
|
||||
}
|
||||
public get hash(): string | void {
|
||||
throw new Error('Called unimplemented virtual method SHAMapTreeNode#hash.')
|
||||
}
|
||||
}
|
||||
|
||||
export class InnerNode extends Node {
|
||||
public leaves: {[slot: number]: Node}
|
||||
public type: NodeType
|
||||
public depth: number
|
||||
public empty: boolean
|
||||
|
||||
/**
|
||||
* Define an Inner (non-leaf) node in a SHAMap tree.
|
||||
* @param {number} depth i.e. how many parent inner nodes
|
||||
* @constructor
|
||||
*/
|
||||
public constructor(depth: number = 0) {
|
||||
super()
|
||||
this.leaves = {}
|
||||
this.type = NodeType.INNER
|
||||
this.depth = depth
|
||||
this.empty = true
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} tag equates to a ledger entry `index`
|
||||
* @param {Node} node to add
|
||||
* @return {void}
|
||||
*/
|
||||
|
||||
public addItem(tag: string, node: Node): void {
|
||||
const existingNode = this.getNode(parseInt(tag[this.depth], 16))
|
||||
if (existingNode) {
|
||||
// A node already exists in this slot
|
||||
if (existingNode instanceof InnerNode) {
|
||||
// There is an inner node, so we need to go deeper
|
||||
existingNode.addItem(tag, node)
|
||||
} else if (existingNode instanceof Leaf) {
|
||||
if (existingNode.tag === tag) {
|
||||
// Collision
|
||||
throw new Error(
|
||||
'Tried to add a node to a SHAMap that was already in there.'
|
||||
)
|
||||
} else {
|
||||
// Turn it into an inner node
|
||||
const newInnerNode = new InnerNode(this.depth + 1)
|
||||
|
||||
// Parent new and existing node
|
||||
newInnerNode.addItem(existingNode.tag, existingNode)
|
||||
newInnerNode.addItem(tag, node)
|
||||
|
||||
// And place the newly created inner node in the slot
|
||||
this.setNode(parseInt(tag[this.depth], 16), newInnerNode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Neat, we have a nice open spot for the new node
|
||||
this.setNode(parseInt(tag[this.depth], 16), node)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite the node that is currently in a given slot.
|
||||
* @param {number} slot a number 0-15
|
||||
* @param {Node} node to place
|
||||
* @return {void}
|
||||
*/
|
||||
public setNode(slot: number, node: Node): void {
|
||||
if (slot < 0 || slot > 15) {
|
||||
throw new Error('Invalid slot: slot must be between 0-15.')
|
||||
}
|
||||
this.leaves[slot] = node
|
||||
this.empty = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node that is currently in a given slot.
|
||||
* @param {number} slot a number 0-15
|
||||
* @return {Node}
|
||||
*/
|
||||
public getNode(slot: number): Node {
|
||||
if (slot < 0 || slot > 15) {
|
||||
throw new Error('Invalid slot: slot must be between 0-15.')
|
||||
}
|
||||
return this.leaves[slot]
|
||||
}
|
||||
|
||||
public get hash(): string {
|
||||
if (this.empty) return HEX_ZERO
|
||||
let hex = ''
|
||||
for (let i = 0; i < 16; i++) {
|
||||
hex += this.leaves[i] ? this.leaves[i].hash : HEX_ZERO
|
||||
}
|
||||
const prefix = hashPrefix.INNER_NODE.toString(16)
|
||||
return sha512Half(prefix + hex)
|
||||
}
|
||||
}
|
||||
|
||||
export class Leaf extends Node {
|
||||
public tag: string
|
||||
public type: NodeType
|
||||
public data: string
|
||||
|
||||
/**
|
||||
* Leaf node in a SHAMap tree.
|
||||
* @param {string} tag equates to a ledger entry `index`
|
||||
* @param {string} data hex of account state, transaction etc
|
||||
* @param {number} type one of TYPE_ACCOUNT_STATE, TYPE_TRANSACTION_MD etc
|
||||
* @constructor
|
||||
*/
|
||||
public constructor(tag: string, data: string, type: NodeType) {
|
||||
super()
|
||||
this.tag = tag
|
||||
this.type = type
|
||||
this.data = data
|
||||
}
|
||||
|
||||
public get hash(): string | void {
|
||||
switch (this.type) {
|
||||
case NodeType.ACCOUNT_STATE: {
|
||||
const leafPrefix = hashPrefix.LEAF_NODE.toString(16)
|
||||
return sha512Half(leafPrefix + this.data + this.tag)
|
||||
}
|
||||
case NodeType.TRANSACTION_NO_METADATA: {
|
||||
const txIDPrefix = hashPrefix.TRANSACTION_ID.toString(16)
|
||||
return sha512Half(txIDPrefix + this.data)
|
||||
}
|
||||
case NodeType.TRANSACTION_METADATA: {
|
||||
const txNodePrefix = hashPrefix.TRANSACTION_NODE.toString(16)
|
||||
return sha512Half(txNodePrefix + this.data + this.tag)
|
||||
}
|
||||
default:
|
||||
throw new Error('Tried to hash a SHAMap node of unknown type.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SHAMap {
|
||||
public root: InnerNode
|
||||
|
||||
/**
|
||||
* SHAMap tree.
|
||||
* @constructor
|
||||
*/
|
||||
public constructor() {
|
||||
this.root = new InnerNode(0)
|
||||
}
|
||||
|
||||
public addItem(tag: string, data: string, type: NodeType): void {
|
||||
this.root.addItem(tag, new Leaf(tag, data, type))
|
||||
}
|
||||
|
||||
public get hash(): string {
|
||||
return this.root.hash
|
||||
}
|
||||
}
|
||||
@@ -2,13 +2,29 @@ import * as constants from './constants'
|
||||
import * as errors from './errors'
|
||||
import * as validate from './validate'
|
||||
import * as serverInfo from './serverinfo'
|
||||
export {
|
||||
constants,
|
||||
errors,
|
||||
validate,
|
||||
serverInfo
|
||||
import {xAddressToClassicAddress, isValidXAddress} from 'ripple-address-codec'
|
||||
|
||||
export function ensureClassicAddress(account: string): string {
|
||||
if (isValidXAddress(account)) {
|
||||
const {classicAddress, tag} = xAddressToClassicAddress(account)
|
||||
|
||||
// Except for special cases, X-addresses used for requests
|
||||
// must not have an embedded tag. In other words,
|
||||
// `tag` should be `false`.
|
||||
if (tag !== false) {
|
||||
throw new Error(
|
||||
'This command does not support the use of a tag. Use an address without a tag.'
|
||||
)
|
||||
}
|
||||
|
||||
// For rippled requests that use an account, always use a classic address.
|
||||
return classicAddress
|
||||
} else {
|
||||
return account
|
||||
}
|
||||
}
|
||||
|
||||
export {constants, errors, validate, serverInfo}
|
||||
export {
|
||||
dropsToXrp,
|
||||
xrpToDrops,
|
||||
@@ -18,6 +34,5 @@ export {
|
||||
iso8601ToRippleTime,
|
||||
rippleTimeToISO8601
|
||||
} from './utils'
|
||||
export {default as Connection} from './connection'
|
||||
export {Connection} from './connection'
|
||||
export {txFlags} from './txflags'
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -18,7 +18,6 @@ function mergeIntervals(intervals: Interval[]): Interval[] {
|
||||
}
|
||||
|
||||
class RangeSet {
|
||||
|
||||
ranges: Array<[number, number]>
|
||||
|
||||
constructor() {
|
||||
@@ -30,12 +29,13 @@ class RangeSet {
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return this.ranges.map(range =>
|
||||
range[0].toString() + '-' + range[1].toString()).join(',')
|
||||
return this.ranges
|
||||
.map(range => range[0].toString() + '-' + range[1].toString())
|
||||
.join(',')
|
||||
}
|
||||
|
||||
addRange(start: number, end: number) {
|
||||
assert(start <= end, `invalid range ${start} <= ${end}`)
|
||||
assert.ok(start <= end, `invalid range ${start} <= ${end}`)
|
||||
this.ranges = mergeIntervals(this.ranges.concat([[start, end]]))
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as _ from 'lodash'
|
||||
import * as assert from 'assert'
|
||||
const {Validator} = require('jsonschema')
|
||||
import {ValidationError} from './errors'
|
||||
import {isValidAddress} from 'ripple-address-codec'
|
||||
import {isValidClassicAddress, isValidXAddress} from 'ripple-address-codec'
|
||||
import {isValidSecret} from './utils'
|
||||
|
||||
function loadSchemas() {
|
||||
@@ -34,6 +34,8 @@ function loadSchemas() {
|
||||
require('./schemas/objects/destination-address-tag.json'),
|
||||
require('./schemas/objects/transaction-hash.json'),
|
||||
require('./schemas/objects/address.json'),
|
||||
require('./schemas/objects/x-address.json'),
|
||||
require('./schemas/objects/classic-address.json'),
|
||||
require('./schemas/objects/adjustment.json'),
|
||||
require('./schemas/objects/quality.json'),
|
||||
require('./schemas/objects/amount.json'),
|
||||
@@ -60,6 +62,8 @@ function loadSchemas() {
|
||||
require('./schemas/specifications/check-cash.json'),
|
||||
require('./schemas/specifications/check-cancel.json'),
|
||||
require('./schemas/specifications/trustline.json'),
|
||||
require('./schemas/specifications/deposit-preauth.json'),
|
||||
require('./schemas/specifications/account-delete.json'),
|
||||
require('./schemas/output/sign.json'),
|
||||
require('./schemas/output/submit.json'),
|
||||
require('./schemas/output/get-account-info.json'),
|
||||
@@ -121,17 +125,28 @@ function loadSchemas() {
|
||||
]
|
||||
const titles = schemas.map(schema => schema.title)
|
||||
const duplicates = _.keys(_.pickBy(_.countBy(titles), count => count > 1))
|
||||
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates)
|
||||
assert.ok(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates)
|
||||
const validator = new Validator()
|
||||
// Register custom format validators that ignore undefined instances
|
||||
// since jsonschema will still call the format validator on a missing
|
||||
// (optional) property
|
||||
validator.customFormats.address = function(instance) {
|
||||
|
||||
// This relies on "format": "xAddress" in `x-address.json`!
|
||||
validator.customFormats.xAddress = function(instance) {
|
||||
if (instance === undefined) {
|
||||
return true
|
||||
}
|
||||
return isValidXAddress(instance)
|
||||
}
|
||||
|
||||
// This relies on "format": "classicAddress" in `classic-address.json`!
|
||||
validator.customFormats.classicAddress = function(instance) {
|
||||
if (instance === undefined) {
|
||||
return true
|
||||
}
|
||||
return isValidAddress(instance)
|
||||
}
|
||||
|
||||
validator.customFormats.secret = function(instance) {
|
||||
if (instance === undefined) {
|
||||
return true
|
||||
@@ -158,8 +173,8 @@ function schemaValidate(schemaName: string, object: any): void {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
schemaValidate,
|
||||
isValidSecret,
|
||||
isValidAddress
|
||||
function isValidAddress(address: string): boolean {
|
||||
return isValidXAddress(address) || isValidClassicAddress(address)
|
||||
}
|
||||
|
||||
export {schemaValidate, isValidSecret, isValidAddress}
|
||||
|
||||
@@ -14,12 +14,20 @@
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
"description": "The entropy to use to generate the seed."
|
||||
"description": "The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)"
|
||||
},
|
||||
"algorithm": {
|
||||
"type": "string",
|
||||
"enum": ["ecdsa-secp256k1", "ed25519"],
|
||||
"description": "The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`."
|
||||
},
|
||||
"test": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`."
|
||||
},
|
||||
"includeClassicAddress": {
|
||||
"type": "boolean",
|
||||
"description": "If `true`, return the classic address, in addition to the X-address."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
37
src/common/schemas/input/generate-x-address.json
Normal file
37
src/common/schemas/input/generate-x-address.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "generateXAddressParameters",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"options": {
|
||||
"type": "object",
|
||||
"description": "Options to control how the address and secret are generated.",
|
||||
"properties": {
|
||||
"entropy": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
"description": "The entropy to use to generate the seed. Must be an array of length 16 with values from 0-255 (16 bytes of entropy)"
|
||||
},
|
||||
"algorithm": {
|
||||
"type": "string",
|
||||
"enum": ["ecdsa-secp256k1", "ed25519"],
|
||||
"description": "The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`."
|
||||
},
|
||||
"test": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether the address is intended for use on a test network such as Testnet or Devnet. If `true`, the address should only be used for testing, and will start with `T`. If `false`, the address should only be used on mainnet, and will start with `X`."
|
||||
},
|
||||
"includeClassicAddress": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether the classic address should also be included in the returned payload."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "address",
|
||||
"description": "A Ripple account address",
|
||||
"description": "An account address on the XRP Ledger",
|
||||
"type": "string",
|
||||
"format": "address",
|
||||
"link": "address",
|
||||
"pattern": "^r[1-9A-HJ-NP-Za-km-z]{25,34}$"
|
||||
"oneOf": [
|
||||
{"$ref": "xAddress"},
|
||||
{"$ref": "classicAddress"}
|
||||
]
|
||||
}
|
||||
|
||||
9
src/common/schemas/objects/classic-address.json
Normal file
9
src/common/schemas/objects/classic-address.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "classicAddress",
|
||||
"description": "A classic address (Account ID) for the XRP Ledger",
|
||||
"type": "string",
|
||||
"format": "classicAddress",
|
||||
"link": "classic-address",
|
||||
"pattern": "^r[1-9A-HJ-NP-Za-km-z]{24,34}$"
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
},
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"description": " The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase."
|
||||
"description": "The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase."
|
||||
},
|
||||
"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.",
|
||||
@@ -30,6 +30,13 @@
|
||||
{"$ref": "hash128"}
|
||||
]
|
||||
},
|
||||
"walletLocator": {
|
||||
"description": "Transaction hash or any other 64 character hexadecimal string, that may or may not represent the result of a hash operation. Use `null` to clear.",
|
||||
"oneOf": [
|
||||
{"type": "null"},
|
||||
{"$ref": "hash256"}
|
||||
]
|
||||
},
|
||||
"enableTransactionIDTracking": {
|
||||
"type": "boolean",
|
||||
"description": "Track the ID of this account’s most recent transaction."
|
||||
@@ -98,11 +105,15 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"transferRate": {
|
||||
"description": " The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.",
|
||||
"description": "The fee to charge when users transfer this account’s issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.",
|
||||
"oneOf": [
|
||||
{"type": "null"},
|
||||
{"type": "number", "minimum": 1, "maximum": 4.294967295}
|
||||
]
|
||||
},
|
||||
"tickSize": {
|
||||
"description": "Tick size to use for offers involving a currency issued by this address. The exchange rates of those offers is rounded to this many significant digits. Valid values are 3 to 15 inclusive, or 0 to disable.",
|
||||
"enum": [0, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "tag",
|
||||
"description": "An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.",
|
||||
"description": "An arbitrary 32-bit unsigned integer. It typically maps to an off-ledger account; for example, a hosted wallet or exchange account.",
|
||||
"type": "integer",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
"paymentChannelClaim",
|
||||
"checkCreate",
|
||||
"checkCancel",
|
||||
"checkCash"
|
||||
"checkCash",
|
||||
"depositPreauth",
|
||||
"accountDelete"
|
||||
]
|
||||
}
|
||||
|
||||
9
src/common/schemas/objects/x-address.json
Normal file
9
src/common/schemas/objects/x-address.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "xAddress",
|
||||
"description": "An XRP Ledger address in X-address format",
|
||||
"type": "string",
|
||||
"format": "xAddress",
|
||||
"link": "x-address",
|
||||
"pattern": "^[XT][1-9A-HJ-NP-Za-km-z]{46}$"
|
||||
}
|
||||
@@ -3,16 +3,24 @@
|
||||
"title": "generateAddress",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"xAddress": {
|
||||
"$ref": "xAddress",
|
||||
"description": "A randomly generated XRP Ledger address in X-address format."
|
||||
},
|
||||
"classicAddress": {
|
||||
"$ref": "classicAddress",
|
||||
"description": "A randomly generated XRP Ledger Account ID (classic address)."
|
||||
},
|
||||
"address": {
|
||||
"$ref": "address",
|
||||
"description": "A randomly generated Ripple account address."
|
||||
"$ref": "classicAddress",
|
||||
"description": "Deprecated: Use `classicAddress` instead."
|
||||
},
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"format": "secret",
|
||||
"description": "The secret corresponding to the `address`."
|
||||
"description": "The secret corresponding to the address."
|
||||
}
|
||||
},
|
||||
"required": ["address", "secret"],
|
||||
"required": ["xAddress", "classicAddress", "address", "secret"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
|
||||
18
src/common/schemas/output/generate-x-address.json
Normal file
18
src/common/schemas/output/generate-x-address.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "generateXAddress",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"xAddress": {
|
||||
"$ref": "xAddress",
|
||||
"description": "A randomly generated XRP Ledger address in X-address format."
|
||||
},
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"format": "secret",
|
||||
"description": "The secret corresponding to the address."
|
||||
}
|
||||
},
|
||||
"required": ["xAddress", "secret"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -208,6 +208,30 @@
|
||||
"$ref": "paymentChannelClaim"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"depositPreauth"
|
||||
]
|
||||
},
|
||||
"specification": {
|
||||
"$ref": "depositPreauth"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"type": {
|
||||
"enum": [
|
||||
"accountDelete"
|
||||
]
|
||||
},
|
||||
"specification": {
|
||||
"$ref": "accountDelete"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
29
src/common/schemas/specifications/account-delete.json
Normal file
29
src/common/schemas/specifications/account-delete.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "accountDelete",
|
||||
"link": "account-delete",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"destination": {
|
||||
"$ref": "address",
|
||||
"description": "Address of an account to receive any leftover XRP after deleting the sending account. Must be a funded account in the ledger, and must not be the sending account."
|
||||
},
|
||||
"destinationTag": {
|
||||
"$ref": "tag",
|
||||
"description": "(Optional) Arbitrary destination tag that identifies a hosted recipient or other information for the recipient of the deleted account's leftover XRP."
|
||||
},
|
||||
"destinationXAddress": {
|
||||
"$ref": "address",
|
||||
"description": "X-address of an account to receive any leftover XRP after deleting the sending account. Must be a funded account in the ledger, and must not be the sending account."
|
||||
}
|
||||
},
|
||||
"anyOf": [
|
||||
{
|
||||
"required": ["destination"]
|
||||
},
|
||||
{
|
||||
"required": ["destinationXAddress"]
|
||||
}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
21
src/common/schemas/specifications/deposit-preauth.json
Normal file
21
src/common/schemas/specifications/deposit-preauth.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "depositPreauth",
|
||||
"link": "deposit-preauth",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"authorize": {
|
||||
"$ref": "address",
|
||||
"description": "Address of the account that can cash the check."
|
||||
},
|
||||
"unauthorize": {
|
||||
"$ref": "address",
|
||||
"description": "Address of the account that can cash the check."
|
||||
}
|
||||
},
|
||||
"oneOf": [
|
||||
{"required": ["authorize"]},
|
||||
{"required": ["unauthorize"]}
|
||||
],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -4,32 +4,33 @@ import BigNumber from 'bignumber.js'
|
||||
import {RippleAPI} from '..'
|
||||
|
||||
export type GetServerInfoResponse = {
|
||||
buildVersion: string,
|
||||
completeLedgers: string,
|
||||
hostID: string,
|
||||
ioLatencyMs: number,
|
||||
buildVersion: string
|
||||
completeLedgers: string
|
||||
hostID: string
|
||||
ioLatencyMs: number
|
||||
load?: {
|
||||
jobTypes: Array<object>,
|
||||
jobTypes: Array<object>
|
||||
threads: number
|
||||
},
|
||||
}
|
||||
lastClose: {
|
||||
convergeTimeS: number,
|
||||
convergeTimeS: number
|
||||
proposers: number
|
||||
},
|
||||
loadFactor: number,
|
||||
peers: number,
|
||||
pubkeyNode: string,
|
||||
pubkeyValidator?: string,
|
||||
serverState: string,
|
||||
}
|
||||
loadFactor: number
|
||||
peers: number
|
||||
pubkeyNode: string
|
||||
pubkeyValidator?: string
|
||||
serverState: string
|
||||
validatedLedger: {
|
||||
age: number,
|
||||
baseFeeXRP: string,
|
||||
hash: string,
|
||||
reserveBaseXRP: string,
|
||||
reserveIncrementXRP: string,
|
||||
age: number
|
||||
baseFeeXRP: string
|
||||
hash: string
|
||||
reserveBaseXRP: string
|
||||
reserveIncrementXRP: string
|
||||
ledgerVersion: number
|
||||
},
|
||||
}
|
||||
validationQuorum: number
|
||||
networkLedger?: string
|
||||
}
|
||||
|
||||
function renameKeys(object, mapping) {
|
||||
@@ -50,12 +51,9 @@ function getServerInfo(this: RippleAPI): Promise<GetServerInfoResponse> {
|
||||
reserveIncXrp: 'reserveIncrementXRP',
|
||||
seq: 'ledgerVersion'
|
||||
})
|
||||
info.validatedLedger.baseFeeXRP =
|
||||
info.validatedLedger.baseFeeXRP.toString()
|
||||
info.validatedLedger.reserveBaseXRP =
|
||||
info.validatedLedger.reserveBaseXRP.toString()
|
||||
info.validatedLedger.reserveIncrementXRP =
|
||||
info.validatedLedger.reserveIncrementXRP.toString()
|
||||
info.validatedLedger.baseFeeXRP = info.validatedLedger.baseFeeXRP.toString()
|
||||
info.validatedLedger.reserveBaseXRP = info.validatedLedger.reserveBaseXRP.toString()
|
||||
info.validatedLedger.reserveIncrementXRP = info.validatedLedger.reserveIncrementXRP.toString()
|
||||
}
|
||||
return info
|
||||
})
|
||||
@@ -63,10 +61,7 @@ function getServerInfo(this: RippleAPI): Promise<GetServerInfoResponse> {
|
||||
|
||||
// This is a public API that can be called directly.
|
||||
// This is not used by the `prepare*` methods. See `src/transaction/utils.ts`
|
||||
async function getFee(
|
||||
this: RippleAPI,
|
||||
cushion?: number
|
||||
): Promise<string> {
|
||||
async function getFee(this: RippleAPI, cushion?: number): Promise<string> {
|
||||
if (cushion === undefined) {
|
||||
cushion = this._feeCushion
|
||||
}
|
||||
@@ -81,10 +76,7 @@ async function getFee(
|
||||
// Cap fee to `this._maxFeeXRP`
|
||||
fee = BigNumber.min(fee, this._maxFeeXRP)
|
||||
// Round fee to 6 decimal places
|
||||
return (new BigNumber(fee.toFixed(6))).toString(10)
|
||||
return new BigNumber(fee.toFixed(6)).toString(10)
|
||||
}
|
||||
|
||||
export {
|
||||
getServerInfo,
|
||||
getFee
|
||||
}
|
||||
export {getServerInfo, getFee}
|
||||
|
||||
@@ -58,7 +58,4 @@ const txFlagIndices = {
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
txFlags,
|
||||
txFlagIndices
|
||||
}
|
||||
export {txFlags, txFlagIndices}
|
||||
|
||||
@@ -5,19 +5,19 @@ import {
|
||||
} from '../objects'
|
||||
|
||||
export interface AccountInfoRequest {
|
||||
account: string,
|
||||
strict?: boolean,
|
||||
queue?: boolean,
|
||||
ledger_hash?: string,
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current'),
|
||||
account: string
|
||||
strict?: boolean
|
||||
queue?: boolean
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
signer_lists?: boolean
|
||||
}
|
||||
|
||||
export interface AccountInfoResponse {
|
||||
account_data: AccountRootLedgerEntry,
|
||||
signer_lists?: SignerListLedgerEntry[],
|
||||
ledger_current_index?: number,
|
||||
ledger_index?: number,
|
||||
queue_data?: QueueData,
|
||||
account_data: AccountRootLedgerEntry
|
||||
signer_lists?: SignerListLedgerEntry[]
|
||||
ledger_current_index?: number
|
||||
ledger_index?: number
|
||||
queue_data?: QueueData
|
||||
validated?: boolean
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {Trustline} from '../objects'
|
||||
|
||||
export interface AccountLinesRequest {
|
||||
account: string,
|
||||
ledger_hash?: string,
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current'),
|
||||
peer?: string,
|
||||
limit?: number,
|
||||
marker?: any,
|
||||
account: string
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
peer?: string
|
||||
limit?: number
|
||||
marker?: any
|
||||
}
|
||||
|
||||
export interface AccountLinesResponse {
|
||||
account: string,
|
||||
lines: Trustline[],
|
||||
ledger_current_index?: number,
|
||||
ledger_index?: number,
|
||||
ledger_hash?: string,
|
||||
marker?: any,
|
||||
account: string
|
||||
lines: Trustline[]
|
||||
ledger_current_index?: number
|
||||
ledger_index?: number
|
||||
ledger_hash?: string
|
||||
marker?: any
|
||||
}
|
||||
|
||||
@@ -1,84 +1,91 @@
|
||||
import {
|
||||
CheckLedgerEntry, RippleStateLedgerEntry,
|
||||
OfferLedgerEntry, SignerListLedgerEntry,
|
||||
EscrowLedgerEntry, PayChannelLedgerEntry,
|
||||
CheckLedgerEntry,
|
||||
RippleStateLedgerEntry,
|
||||
OfferLedgerEntry,
|
||||
SignerListLedgerEntry,
|
||||
EscrowLedgerEntry,
|
||||
PayChannelLedgerEntry,
|
||||
DepositPreauthLedgerEntry
|
||||
} from '../objects'
|
||||
|
||||
export interface GetAccountObjectsOptions {
|
||||
type?: string | (
|
||||
'check' |
|
||||
'escrow' |
|
||||
'offer' |
|
||||
'payment_channel' |
|
||||
'signer_list' |
|
||||
'state'
|
||||
),
|
||||
ledgerHash?: string,
|
||||
ledgerIndex?: number | ('validated' | 'closed' | 'current'),
|
||||
limit?: number,
|
||||
type?:
|
||||
| string
|
||||
| (
|
||||
| 'check'
|
||||
| 'escrow'
|
||||
| 'offer'
|
||||
| 'payment_channel'
|
||||
| 'signer_list'
|
||||
| 'state'
|
||||
)
|
||||
ledgerHash?: string
|
||||
ledgerIndex?: number | ('validated' | 'closed' | 'current')
|
||||
limit?: number
|
||||
marker?: string
|
||||
}
|
||||
|
||||
export interface AccountObjectsRequest {
|
||||
account: string,
|
||||
account: string
|
||||
|
||||
// (Optional) Filter results to include only this type of ledger object.
|
||||
type?: string | (
|
||||
'check' |
|
||||
'escrow' |
|
||||
'offer' |
|
||||
'payment_channel' |
|
||||
'signer_list' |
|
||||
'state'
|
||||
),
|
||||
type?:
|
||||
| string
|
||||
| (
|
||||
| 'check'
|
||||
| 'escrow'
|
||||
| 'offer'
|
||||
| 'payment_channel'
|
||||
| 'signer_list'
|
||||
| 'state'
|
||||
)
|
||||
|
||||
// (Optional) A 20-byte hex string for the ledger version to use.
|
||||
ledger_hash?: string,
|
||||
ledger_hash?: string
|
||||
|
||||
// (Optional) The sequence number of the ledger to use,
|
||||
// or a shortcut string to choose a ledger automatically.
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current'),
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
|
||||
limit?: number,
|
||||
limit?: number
|
||||
|
||||
marker?: string
|
||||
}
|
||||
|
||||
export interface AccountObjectsResponse {
|
||||
account: string,
|
||||
account: string
|
||||
|
||||
// Array of objects owned by this account.
|
||||
// from the getAccountObjects section of the dev center
|
||||
account_objects: Array<
|
||||
CheckLedgerEntry |
|
||||
RippleStateLedgerEntry |
|
||||
OfferLedgerEntry |
|
||||
SignerListLedgerEntry |
|
||||
EscrowLedgerEntry |
|
||||
PayChannelLedgerEntry |
|
||||
DepositPreauthLedgerEntry
|
||||
>,
|
||||
| CheckLedgerEntry
|
||||
| RippleStateLedgerEntry
|
||||
| OfferLedgerEntry
|
||||
| SignerListLedgerEntry
|
||||
| EscrowLedgerEntry
|
||||
| PayChannelLedgerEntry
|
||||
| DepositPreauthLedgerEntry
|
||||
>
|
||||
|
||||
// (May be omitted) The identifying hash of the ledger
|
||||
// that was used to generate this response.
|
||||
ledger_hash?: string,
|
||||
ledger_hash?: string
|
||||
|
||||
// (May be omitted) The sequence number of the ledger version
|
||||
// that was used to generate this response.
|
||||
ledger_index?: number,
|
||||
ledger_index?: number
|
||||
|
||||
// (May be omitted) The sequence number of the current in-progress ledger
|
||||
// version that was used to generate this response.
|
||||
ledger_current_index?: number,
|
||||
ledger_current_index?: number
|
||||
|
||||
// The limit that was used in this request, if any.
|
||||
limit?: number,
|
||||
limit?: number
|
||||
|
||||
// Server-defined value indicating the response is paginated. Pass this
|
||||
// to the next call to resume where this call left off. Omitted when there
|
||||
// are no additional pages after this one.
|
||||
marker?: string,
|
||||
marker?: string
|
||||
|
||||
// If true, this information comes from a ledger version
|
||||
// that has been validated by consensus.
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import {RippledAmount} from '../objects'
|
||||
|
||||
export interface AccountOffersRequest {
|
||||
account: string,
|
||||
ledger_hash?: string,
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current'),
|
||||
limit?: number,
|
||||
marker?: any,
|
||||
account: string
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
limit?: number
|
||||
marker?: any
|
||||
}
|
||||
|
||||
export interface AccountOffersResponse {
|
||||
account: string,
|
||||
ledger_hash?: string,
|
||||
ledger_current_index?: number,
|
||||
ledger_index?: number,
|
||||
marker?: any,
|
||||
offers?: AccountOffer[],
|
||||
account: string
|
||||
ledger_hash?: string
|
||||
ledger_current_index?: number
|
||||
ledger_index?: number
|
||||
marker?: any
|
||||
offers?: AccountOffer[]
|
||||
}
|
||||
|
||||
export interface AccountOffer {
|
||||
seq: number,
|
||||
flags: number,
|
||||
taker_gets: RippledAmount,
|
||||
taker_pays: RippledAmount,
|
||||
quality: string,
|
||||
seq: number
|
||||
flags: number
|
||||
taker_gets: RippledAmount
|
||||
taker_pays: RippledAmount
|
||||
quality: string
|
||||
expiration?: number
|
||||
}
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
import {
|
||||
TakerRequestAmount,
|
||||
RippledAmount,
|
||||
OfferLedgerEntry
|
||||
} from '../objects'
|
||||
import {TakerRequestAmount, RippledAmount, OfferLedgerEntry} from '../objects'
|
||||
|
||||
export interface BookOffersRequest {
|
||||
taker?: string,
|
||||
taker_gets: TakerRequestAmount,
|
||||
taker_pays: TakerRequestAmount,
|
||||
ledger_hash?: string,
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current'),
|
||||
limit?: number,
|
||||
taker?: string
|
||||
taker_gets: TakerRequestAmount
|
||||
taker_pays: TakerRequestAmount
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
limit?: number
|
||||
marker?: any
|
||||
}
|
||||
|
||||
export interface BookOffersResponse {
|
||||
offers: BookOffer[],
|
||||
ledger_hash?: string,
|
||||
ledger_current_index?: number,
|
||||
ledger_index?: number,
|
||||
offers: BookOffer[]
|
||||
ledger_hash?: string
|
||||
ledger_current_index?: number
|
||||
ledger_index?: number
|
||||
marker?: any
|
||||
}
|
||||
|
||||
export interface BookOffer extends OfferLedgerEntry {
|
||||
quality?: string
|
||||
owner_funds?: string,
|
||||
taker_gets_funded?: RippledAmount,
|
||||
owner_funds?: string
|
||||
taker_gets_funded?: RippledAmount
|
||||
taker_pays_funded?: RippledAmount
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {Amount} from '../objects'
|
||||
|
||||
export interface GatewayBalancesRequest {
|
||||
account: string,
|
||||
strict?: boolean,
|
||||
hotwallet: string|Array<string>,
|
||||
ledger_hash?: string,
|
||||
account: string
|
||||
strict?: boolean
|
||||
hotwallet: string | Array<string>
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
}
|
||||
|
||||
export interface GatewayBalancesResponse {
|
||||
account: string,
|
||||
obligations?: {[currency: string]: string},
|
||||
balances?: {[address: string]: Amount[]},
|
||||
assets?: {[address: string]: Amount[]},
|
||||
ledger_hash?: string,
|
||||
ledger_current_index?: number,
|
||||
account: string
|
||||
obligations?: {[currency: string]: string}
|
||||
balances?: {[address: string]: Amount[]}
|
||||
assets?: {[address: string]: Amount[]}
|
||||
ledger_hash?: string
|
||||
ledger_current_index?: number
|
||||
ledger_index?: number
|
||||
}
|
||||
|
||||
@@ -5,5 +5,6 @@ export * from './account_offers'
|
||||
export * from './book_offers'
|
||||
export * from './gateway_balances'
|
||||
export * from './ledger'
|
||||
export * from './ledger_data'
|
||||
export * from './ledger_entry'
|
||||
export * from './server_info'
|
||||
|
||||
12
src/common/types/commands/ledger_data.ts
Normal file
12
src/common/types/commands/ledger_data.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import {LedgerData} from '../objects'
|
||||
|
||||
export interface LedgerDataRequest {
|
||||
id?: any
|
||||
ledger_hash?: string
|
||||
ledger_index?: string
|
||||
binary?: boolean
|
||||
limit?: number
|
||||
marker?: string
|
||||
}
|
||||
|
||||
export type LedgerDataResponse = LedgerData
|
||||
@@ -3,29 +3,34 @@ import {LedgerEntry} from '../objects'
|
||||
export interface LedgerEntryRequest {
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
index?: string,
|
||||
account_root?: string,
|
||||
directory?: string | {
|
||||
sub_index?: number,
|
||||
dir_root: string
|
||||
} | {
|
||||
sub_index?: number,
|
||||
owner: string
|
||||
},
|
||||
offer?: string | {
|
||||
account: string,
|
||||
seq: number
|
||||
},
|
||||
index?: string
|
||||
account_root?: string
|
||||
directory?:
|
||||
| string
|
||||
| {
|
||||
sub_index?: number
|
||||
dir_root: string
|
||||
}
|
||||
| {
|
||||
sub_index?: number
|
||||
owner: string
|
||||
}
|
||||
offer?:
|
||||
| string
|
||||
| {
|
||||
account: string
|
||||
seq: number
|
||||
}
|
||||
ripple_state?: {
|
||||
accounts: [string, string],
|
||||
accounts: [string, string]
|
||||
currency: string
|
||||
},
|
||||
}
|
||||
binary?: boolean
|
||||
}
|
||||
|
||||
export interface LedgerEntryResponse {
|
||||
index: string,
|
||||
ledger_index: number,
|
||||
node_binary?: string,
|
||||
node?: LedgerEntry,
|
||||
index: string
|
||||
ledger_index: number
|
||||
node_binary?: string
|
||||
node?: LedgerEntry
|
||||
}
|
||||
|
||||
@@ -4,48 +4,48 @@ export interface ServerInfoRequest {
|
||||
|
||||
export interface ServerInfoResponse {
|
||||
info: {
|
||||
amendment_blocked?: boolean,
|
||||
build_version: string,
|
||||
closed_ledger?: LedgerInfo,
|
||||
complete_ledgers: string,
|
||||
hostid: string,
|
||||
io_latency_ms: number,
|
||||
amendment_blocked?: boolean
|
||||
build_version: string
|
||||
closed_ledger?: LedgerInfo
|
||||
complete_ledgers: string
|
||||
hostid: string
|
||||
io_latency_ms: number
|
||||
last_close: {
|
||||
converge_time_s: number,
|
||||
converge_time_s: number
|
||||
proposers: number
|
||||
},
|
||||
}
|
||||
load?: {
|
||||
job_types: {
|
||||
job_type: string,
|
||||
per_second: number,
|
||||
job_type: string
|
||||
per_second: number
|
||||
in_progress: number
|
||||
}[],
|
||||
}[]
|
||||
threads: number
|
||||
},
|
||||
load_factor: number,
|
||||
load_factor_local?: number,
|
||||
load_factor_net?: number,
|
||||
load_factor_cluster?: number,
|
||||
load_factor_fee_escalation?: number,
|
||||
load_factor_fee_queue?: number,
|
||||
load_factor_server?: number,
|
||||
peers: number,
|
||||
pubkey_node: string,
|
||||
pubkey_validator: string,
|
||||
server_state: string,
|
||||
state_accounting: any,
|
||||
uptime: number,
|
||||
validated_ledger?: LedgerInfo,
|
||||
validation_quorum: number,
|
||||
}
|
||||
load_factor: number
|
||||
load_factor_local?: number
|
||||
load_factor_net?: number
|
||||
load_factor_cluster?: number
|
||||
load_factor_fee_escalation?: number
|
||||
load_factor_fee_queue?: number
|
||||
load_factor_server?: number
|
||||
peers: number
|
||||
pubkey_node: string
|
||||
pubkey_validator: string
|
||||
server_state: string
|
||||
state_accounting: any
|
||||
uptime: number
|
||||
validated_ledger?: LedgerInfo
|
||||
validation_quorum: number
|
||||
validator_list_expires: string
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export interface LedgerInfo {
|
||||
age: number,
|
||||
base_fee_xrp: number,
|
||||
hash: string,
|
||||
reserve_base_xrp: number,
|
||||
reserve_inc_xrp: number,
|
||||
seq: number,
|
||||
age: number
|
||||
base_fee_xrp: number
|
||||
hash: string
|
||||
reserve_base_xrp: number
|
||||
reserve_inc_xrp: number
|
||||
seq: number
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {Amount} from './amounts'
|
||||
|
||||
export type Adjustment = {
|
||||
address: string,
|
||||
amount: Amount,
|
||||
address: string
|
||||
amount: Amount
|
||||
tag?: number
|
||||
}
|
||||
|
||||
export type MaxAdjustment = {
|
||||
address: string,
|
||||
maxAmount: Amount,
|
||||
address: string
|
||||
maxAmount: Amount
|
||||
tag?: number
|
||||
}
|
||||
|
||||
export type MinAdjustment = {
|
||||
address: string,
|
||||
minAmount: Amount,
|
||||
address: string
|
||||
minAmount: Amount
|
||||
tag?: number
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ export interface Amount extends Issue {
|
||||
value: string
|
||||
}
|
||||
|
||||
|
||||
export type RippledAmount = string | Amount
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export * from './adjustments'
|
||||
export * from './amounts'
|
||||
export * from './ledger'
|
||||
export * from './ledger_data'
|
||||
export * from './ledger_entries'
|
||||
export * from './memos'
|
||||
export * from './orders'
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
export interface Ledger {
|
||||
account_hash: string,
|
||||
close_time: number,
|
||||
close_time_human: string,
|
||||
close_time_resolution: number,
|
||||
closed: boolean,
|
||||
ledger_hash: string,
|
||||
ledger_index: string,
|
||||
parent_hash: string,
|
||||
total_coins: string,
|
||||
transaction_hash: string,
|
||||
transactions: string[] | object[],
|
||||
account_hash: string
|
||||
close_time: number
|
||||
close_time_human: string
|
||||
close_time_resolution: number
|
||||
closed: boolean
|
||||
ledger_hash: string
|
||||
ledger_index: string
|
||||
parent_hash: string
|
||||
total_coins: string
|
||||
transaction_hash: string
|
||||
transactions: string[] | object[]
|
||||
// @deprecated
|
||||
seqNum?: string,
|
||||
seqNum?: string
|
||||
// @deprecated
|
||||
totalCoins?: string,
|
||||
totalCoins?: string
|
||||
// @deprecated
|
||||
hash?: string,
|
||||
close_flags?: number,
|
||||
parent_close_time?: number,
|
||||
accountState?: any[],
|
||||
hash?: string
|
||||
close_flags?: number
|
||||
parent_close_time?: number
|
||||
accountState?: any[]
|
||||
validated?: boolean
|
||||
}
|
||||
|
||||
6
src/common/types/objects/ledger_data.ts
Normal file
6
src/common/types/objects/ledger_data.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface LedgerData {
|
||||
ledger_index: string
|
||||
ledger_hash: string
|
||||
marker: string
|
||||
state: ({data?: string; LedgerEntryType?: string; index: string} & any)[]
|
||||
}
|
||||
@@ -2,188 +2,189 @@ import {SignerEntry} from './index'
|
||||
import {Amount, RippledAmount} from './amounts'
|
||||
|
||||
export interface AccountRootLedgerEntry {
|
||||
LedgerEntryType: 'AccountRoot',
|
||||
Account: string,
|
||||
Balance: string,
|
||||
Flags: number,
|
||||
OwnerCount: number,
|
||||
PreviousTxnID: string,
|
||||
PreviousTxnLgrSeq: number,
|
||||
Sequence: number,
|
||||
AccountTxnID?: string,
|
||||
Domain?: string,
|
||||
EmailHash?: string,
|
||||
LedgerEntryType: 'AccountRoot'
|
||||
Account: string
|
||||
Balance: string
|
||||
Flags: number
|
||||
OwnerCount: number
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
Sequence: number
|
||||
AccountTxnID?: string
|
||||
Domain?: string
|
||||
EmailHash?: string
|
||||
MessageKey?: string
|
||||
RegularKey?: string,
|
||||
TickSize?: number,
|
||||
TransferRate?: number,
|
||||
WalletLocator?: string, // DEPRECATED
|
||||
RegularKey?: string
|
||||
TickSize?: number
|
||||
TransferRate?: number
|
||||
WalletLocator?: string
|
||||
WalletSize?: number // DEPRECATED
|
||||
}
|
||||
|
||||
export interface AmendmentsLedgerEntry {
|
||||
LedgerEntryType: 'Amendments',
|
||||
Amendments?: string[],
|
||||
Majorities?: any[],
|
||||
LedgerEntryType: 'Amendments'
|
||||
Amendments?: string[]
|
||||
Majorities?: any[]
|
||||
Flags: 0
|
||||
}
|
||||
|
||||
export interface CheckLedgerEntry {
|
||||
LedgerEntryType: 'Check',
|
||||
Account: string,
|
||||
Destination, string,
|
||||
Flags: 0,
|
||||
OwnerNode: string,
|
||||
PreviousTxnID: string,
|
||||
PreviousTxnLgrSeq: number,
|
||||
SendMax: string | object,
|
||||
Sequence: number,
|
||||
DestinationNode: string,
|
||||
DestinationTag: number,
|
||||
Expiration: number,
|
||||
InvoiceID: string,
|
||||
LedgerEntryType: 'Check'
|
||||
Account: string
|
||||
Destination
|
||||
string
|
||||
Flags: 0
|
||||
OwnerNode: string
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
SendMax: string | object
|
||||
Sequence: number
|
||||
DestinationNode: string
|
||||
DestinationTag: number
|
||||
Expiration: number
|
||||
InvoiceID: string
|
||||
SourceTag: number
|
||||
}
|
||||
|
||||
export interface DepositPreauthLedgerEntry {
|
||||
LedgerEntryType: 'DepositPreauth',
|
||||
Account: string,
|
||||
Authorize: string,
|
||||
OwnerNode: string,
|
||||
PreviousTxnID: string,
|
||||
LedgerEntryType: 'DepositPreauth'
|
||||
Account: string
|
||||
Authorize: string
|
||||
OwnerNode: string
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
}
|
||||
|
||||
export interface DirectoryNodeLedgerEntry {
|
||||
LedgerEntryType: 'DirectoryNode',
|
||||
Flags: number,
|
||||
RootIndex: string,
|
||||
Indexes: string[],
|
||||
IndexNext?: number,
|
||||
LedgerEntryType: 'DirectoryNode'
|
||||
Flags: number
|
||||
RootIndex: string
|
||||
Indexes: string[]
|
||||
IndexNext?: number
|
||||
IndexPrevious?: number
|
||||
}
|
||||
|
||||
export interface OfferDirectoryNodeLedgerEntry
|
||||
extends DirectoryNodeLedgerEntry {
|
||||
TakerPaysCurrency: string,
|
||||
TakerPaysIssuer: string,
|
||||
TakerGetsCurrency: string,
|
||||
TakerGetsIssuer: string,
|
||||
extends DirectoryNodeLedgerEntry {
|
||||
TakerPaysCurrency: string
|
||||
TakerPaysIssuer: string
|
||||
TakerGetsCurrency: string
|
||||
TakerGetsIssuer: string
|
||||
ExchangeRate?: number // DEPRECATED
|
||||
}
|
||||
|
||||
export interface OwnerDirectoryNodeLedgerEntry
|
||||
extends DirectoryNodeLedgerEntry {
|
||||
Owner: string,
|
||||
Owner: string
|
||||
}
|
||||
|
||||
export interface EscrowLedgerEntry {
|
||||
LedgerEntryType: 'Escrow',
|
||||
Account: string,
|
||||
Destination: string,
|
||||
Amount: string,
|
||||
Condition?: string,
|
||||
CancelAfter?: number,
|
||||
FinishAfter?: number,
|
||||
Flags: number,
|
||||
SourceTag?: number,
|
||||
DestinationTag?: number,
|
||||
OwnerNode: string,
|
||||
DestinationNode?: string,
|
||||
PreviousTxnID: string,
|
||||
LedgerEntryType: 'Escrow'
|
||||
Account: string
|
||||
Destination: string
|
||||
Amount: string
|
||||
Condition?: string
|
||||
CancelAfter?: number
|
||||
FinishAfter?: number
|
||||
Flags: number
|
||||
SourceTag?: number
|
||||
DestinationTag?: number
|
||||
OwnerNode: string
|
||||
DestinationNode?: string
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
}
|
||||
|
||||
export interface FeeSettingsLedgerEntry {
|
||||
LedgerEntryType: 'FeeSettings',
|
||||
BaseFee: string,
|
||||
ReferenceFeeUnits: number,
|
||||
ReserveBase: number,
|
||||
ReserveIncrement: number,
|
||||
LedgerEntryType: 'FeeSettings'
|
||||
BaseFee: string
|
||||
ReferenceFeeUnits: number
|
||||
ReserveBase: number
|
||||
ReserveIncrement: number
|
||||
Flags: number
|
||||
}
|
||||
|
||||
export interface LedgerHashesLedgerEntry {
|
||||
LedgerEntryType: 'LedgerHashes',
|
||||
Hashes: string[],
|
||||
Flags: number,
|
||||
FirstLedgerSequence?: number, // DEPRECATED
|
||||
LedgerEntryType: 'LedgerHashes'
|
||||
Hashes: string[]
|
||||
Flags: number
|
||||
FirstLedgerSequence?: number // DEPRECATED
|
||||
LastLedgerSequence?: number
|
||||
}
|
||||
|
||||
export interface OfferLedgerEntry {
|
||||
LedgerEntryType: 'Offer',
|
||||
Flags: number,
|
||||
Account: string,
|
||||
Sequence: number,
|
||||
TakerPays: RippledAmount,
|
||||
TakerGets: RippledAmount,
|
||||
BookDirectory: string,
|
||||
BookNode: string,
|
||||
OwnerNode: string,
|
||||
PreviousTxnID: string,
|
||||
PreviousTxnLgrSeq: number,
|
||||
LedgerEntryType: 'Offer'
|
||||
Flags: number
|
||||
Account: string
|
||||
Sequence: number
|
||||
TakerPays: RippledAmount
|
||||
TakerGets: RippledAmount
|
||||
BookDirectory: string
|
||||
BookNode: string
|
||||
OwnerNode: string
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
Expiration?: number
|
||||
}
|
||||
|
||||
export interface PayChannelLedgerEntry {
|
||||
LedgerEntryType: 'PayChannel',
|
||||
Sequence: number,
|
||||
Account: string,
|
||||
Amount: string,
|
||||
Balance: string,
|
||||
PublicKey: string,
|
||||
Destination: string,
|
||||
SettleDelay: number,
|
||||
Expiration?: number,
|
||||
CancelAfter?: number,
|
||||
SourceTag?: number,
|
||||
DestinationTag?: number,
|
||||
OwnerNode: string,
|
||||
PreviousTxnID: string,
|
||||
PreviousTxnLgrSeq: number,
|
||||
LedgerEntryType: 'PayChannel'
|
||||
Sequence: number
|
||||
Account: string
|
||||
Amount: string
|
||||
Balance: string
|
||||
PublicKey: string
|
||||
Destination: string
|
||||
SettleDelay: number
|
||||
Expiration?: number
|
||||
CancelAfter?: number
|
||||
SourceTag?: number
|
||||
DestinationTag?: number
|
||||
OwnerNode: string
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
index: string
|
||||
}
|
||||
|
||||
export interface RippleStateLedgerEntry {
|
||||
LedgerEntryType: 'RippleState',
|
||||
Flags: number,
|
||||
Balance: Amount,
|
||||
LowLimit: Amount,
|
||||
HighLimit: Amount,
|
||||
PreviousTxnID: string,
|
||||
PreviousTxnLgrSeq: number,
|
||||
LowNode?: string,
|
||||
HighNode?: string,
|
||||
LowQualityIn?: number,
|
||||
LowQualityOut?: number,
|
||||
HighQualityIn?: number,
|
||||
LedgerEntryType: 'RippleState'
|
||||
Flags: number
|
||||
Balance: Amount
|
||||
LowLimit: Amount
|
||||
HighLimit: Amount
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
LowNode?: string
|
||||
HighNode?: string
|
||||
LowQualityIn?: number
|
||||
LowQualityOut?: number
|
||||
HighQualityIn?: number
|
||||
HighQualityOut?: number
|
||||
}
|
||||
|
||||
export interface SignerListLedgerEntry {
|
||||
LedgerEntryType: 'SignerList',
|
||||
OwnerNode: string,
|
||||
SignerQuorum: number,
|
||||
SignerEntries: SignerEntry[],
|
||||
SignerListID: number,
|
||||
PreviousTxnID: string,
|
||||
LedgerEntryType: 'SignerList'
|
||||
OwnerNode: string
|
||||
SignerQuorum: number
|
||||
SignerEntries: SignerEntry[]
|
||||
SignerListID: number
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
}
|
||||
|
||||
// see https://ripple.com/build/ledger-format/#ledger-object-types
|
||||
export type LedgerEntry =
|
||||
AccountRootLedgerEntry |
|
||||
AmendmentsLedgerEntry |
|
||||
CheckLedgerEntry |
|
||||
DepositPreauthLedgerEntry |
|
||||
DirectoryNodeLedgerEntry |
|
||||
OfferDirectoryNodeLedgerEntry |
|
||||
OwnerDirectoryNodeLedgerEntry |
|
||||
EscrowLedgerEntry |
|
||||
FeeSettingsLedgerEntry |
|
||||
LedgerHashesLedgerEntry |
|
||||
OfferLedgerEntry |
|
||||
PayChannelLedgerEntry |
|
||||
RippleStateLedgerEntry |
|
||||
SignerListLedgerEntry
|
||||
| AccountRootLedgerEntry
|
||||
| AmendmentsLedgerEntry
|
||||
| CheckLedgerEntry
|
||||
| DepositPreauthLedgerEntry
|
||||
| DirectoryNodeLedgerEntry
|
||||
| OfferDirectoryNodeLedgerEntry
|
||||
| OwnerDirectoryNodeLedgerEntry
|
||||
| EscrowLedgerEntry
|
||||
| FeeSettingsLedgerEntry
|
||||
| LedgerHashesLedgerEntry
|
||||
| OfferLedgerEntry
|
||||
| PayChannelLedgerEntry
|
||||
| RippleStateLedgerEntry
|
||||
| SignerListLedgerEntry
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
|
||||
export type Memo = {
|
||||
type?: string,
|
||||
format?: string,
|
||||
type?: string
|
||||
format?: string
|
||||
data?: string
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ import {Amount} from './amounts'
|
||||
import {Memo} from './memos'
|
||||
|
||||
export type FormattedOrderSpecification = {
|
||||
direction: string,
|
||||
quantity: Amount,
|
||||
totalPrice: Amount,
|
||||
immediateOrCancel?: boolean,
|
||||
fillOrKill?: boolean,
|
||||
expirationTime?: string,
|
||||
orderToReplace?: number,
|
||||
memos?: Memo[],
|
||||
direction: string
|
||||
quantity: Amount
|
||||
totalPrice: Amount
|
||||
immediateOrCancel?: boolean
|
||||
fillOrKill?: boolean
|
||||
expirationTime?: string
|
||||
orderToReplace?: number
|
||||
memos?: Memo[]
|
||||
// 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.
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
export interface QueueTransaction {
|
||||
auth_change: boolean,
|
||||
fee: string,
|
||||
fee_level: string,
|
||||
max_spend_drops: string,
|
||||
auth_change: boolean
|
||||
fee: string
|
||||
fee_level: string
|
||||
max_spend_drops: string
|
||||
seq: number
|
||||
}
|
||||
|
||||
export interface QueueData {
|
||||
txn_count: number,
|
||||
auth_change_queued?: boolean,
|
||||
lowest_sequence?: number,
|
||||
highest_sequence?: number,
|
||||
max_spend_drops_total?: string,
|
||||
txn_count: number
|
||||
auth_change_queued?: boolean
|
||||
lowest_sequence?: number
|
||||
highest_sequence?: number
|
||||
max_spend_drops_total?: string
|
||||
transactions?: QueueTransaction[]
|
||||
}
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
import {Memo} from './memos'
|
||||
|
||||
export type WeightedSigner = {
|
||||
address: string,
|
||||
address: string
|
||||
weight: number
|
||||
}
|
||||
|
||||
export type Signers = {
|
||||
threshold?: number,
|
||||
threshold?: number
|
||||
weights: WeightedSigner[]
|
||||
}
|
||||
|
||||
export type FormattedSettings = {
|
||||
defaultRipple?: boolean,
|
||||
depositAuth?: boolean,
|
||||
disableMasterKey?: boolean,
|
||||
disallowIncomingXRP?: boolean,
|
||||
domain?: string,
|
||||
emailHash?: string|null,
|
||||
enableTransactionIDTracking?: boolean,
|
||||
globalFreeze?: boolean,
|
||||
memos?: Memo[],
|
||||
messageKey?: string,
|
||||
noFreeze?: boolean,
|
||||
passwordSpent?: boolean,
|
||||
regularKey?: string,
|
||||
requireAuthorization?: boolean,
|
||||
requireDestinationTag?: boolean,
|
||||
signers?: Signers,
|
||||
transferRate?: number|null
|
||||
defaultRipple?: boolean
|
||||
depositAuth?: boolean
|
||||
disableMasterKey?: boolean
|
||||
disallowIncomingXRP?: boolean
|
||||
domain?: string
|
||||
emailHash?: string | null
|
||||
walletLocator?: string | null
|
||||
enableTransactionIDTracking?: boolean
|
||||
globalFreeze?: boolean
|
||||
memos?: Memo[]
|
||||
messageKey?: string
|
||||
noFreeze?: boolean
|
||||
passwordSpent?: boolean
|
||||
regularKey?: string
|
||||
requireAuthorization?: boolean
|
||||
requireDestinationTag?: boolean
|
||||
signers?: Signers
|
||||
transferRate?: number | null
|
||||
tickSize?: number
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export interface SignerEntry {
|
||||
Account: string,
|
||||
SignerWeight: number
|
||||
SignerEntry: {
|
||||
Account: string
|
||||
SignerWeight: number
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,21 @@ import {RippledAmount} from './amounts'
|
||||
import {Memo} from './memos'
|
||||
|
||||
export interface OfferCreateTransaction {
|
||||
TransactionType: 'OfferCreate',
|
||||
Account: string,
|
||||
AccountTxnID?: string,
|
||||
Fee: string,
|
||||
Field: any,
|
||||
Flags: number,
|
||||
LastLedgerSequence?: number,
|
||||
Sequence: number,
|
||||
Signers: any[],
|
||||
SigningPubKey: string,
|
||||
SourceTag?: number,
|
||||
TakerGets: RippledAmount,
|
||||
TakerPays: RippledAmount,
|
||||
TxnSignature: string,
|
||||
Expiration?: number,
|
||||
Memos?: Memo[],
|
||||
OfferSequence?: number,
|
||||
TransactionType: 'OfferCreate'
|
||||
Account: string
|
||||
AccountTxnID?: string
|
||||
Fee: string
|
||||
Field: any
|
||||
Flags: number
|
||||
LastLedgerSequence?: number
|
||||
Sequence: number
|
||||
Signers: any[]
|
||||
SigningPubKey: string
|
||||
SourceTag?: number
|
||||
TakerGets: RippledAmount
|
||||
TakerPays: RippledAmount
|
||||
TxnSignature: string
|
||||
Expiration?: number
|
||||
Memos?: Memo[]
|
||||
OfferSequence?: number
|
||||
}
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import {Memo} from './memos'
|
||||
|
||||
export interface Trustline {
|
||||
account: string,
|
||||
balance: string,
|
||||
currency: string,
|
||||
limit: string,
|
||||
limit_peer: string,
|
||||
quality_in: number,
|
||||
quality_out: number,
|
||||
no_ripple?: boolean,
|
||||
no_ripple_peer?: boolean,
|
||||
freeze?: boolean,
|
||||
freeze_peer?: boolean,
|
||||
authorized?: boolean,
|
||||
peer_authorized?: boolean,
|
||||
account: string
|
||||
balance: string
|
||||
currency: string
|
||||
limit: string
|
||||
limit_peer: string
|
||||
quality_in: number
|
||||
quality_out: number
|
||||
no_ripple?: boolean
|
||||
no_ripple_peer?: boolean
|
||||
freeze?: boolean
|
||||
freeze_peer?: boolean
|
||||
authorized?: boolean
|
||||
peer_authorized?: boolean
|
||||
}
|
||||
|
||||
export type FormattedTrustlineSpecification = {
|
||||
currency: string,
|
||||
counterparty: string,
|
||||
limit: string,
|
||||
qualityIn?: number,
|
||||
qualityOut?: number,
|
||||
ripplingDisabled?: boolean,
|
||||
authorized?: boolean,
|
||||
frozen?: boolean,
|
||||
currency: string
|
||||
counterparty: string
|
||||
limit: string
|
||||
qualityIn?: number
|
||||
qualityOut?: number
|
||||
ripplingDisabled?: boolean
|
||||
authorized?: boolean
|
||||
frozen?: boolean
|
||||
memos?: Memo[]
|
||||
}
|
||||
|
||||
export type FormattedTrustline = {
|
||||
specification: FormattedTrustlineSpecification,
|
||||
specification: FormattedTrustlineSpecification
|
||||
counterparty: {
|
||||
limit: string,
|
||||
ripplingDisabled?: boolean,
|
||||
frozen?: boolean,
|
||||
limit: string
|
||||
ripplingDisabled?: boolean
|
||||
frozen?: boolean
|
||||
authorized?: boolean
|
||||
},
|
||||
}
|
||||
state: {
|
||||
balance: string
|
||||
}
|
||||
|
||||
@@ -13,77 +13,96 @@ function isValidSecret(secret: string): boolean {
|
||||
}
|
||||
}
|
||||
|
||||
function dropsToXrp(drops: string | BigNumber): string {
|
||||
function dropsToXrp(drops: BigNumber.Value): string {
|
||||
if (typeof drops === 'string') {
|
||||
if (!drops.match(/^-?[0-9]*\.?[0-9]*$/)) {
|
||||
throw new ValidationError(`dropsToXrp: invalid value '${drops}',` +
|
||||
` should be a number matching (^-?[0-9]*\.?[0-9]*$).`)
|
||||
throw new ValidationError(
|
||||
`dropsToXrp: invalid value '${drops}',` +
|
||||
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`
|
||||
)
|
||||
} else if (drops === '.') {
|
||||
throw new ValidationError(`dropsToXrp: invalid value '${drops}',` +
|
||||
` should be a BigNumber or string-encoded number.`)
|
||||
throw new ValidationError(
|
||||
`dropsToXrp: invalid value '${drops}',` +
|
||||
` should be a BigNumber or string-encoded number.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Converting to BigNumber and then back to string should remove any
|
||||
// decimal point followed by zeros, e.g. '1.00'.
|
||||
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
|
||||
drops = (new BigNumber(drops)).toString(10)
|
||||
drops = new BigNumber(drops).toString(10)
|
||||
|
||||
// drops are only whole units
|
||||
if (drops.includes('.')) {
|
||||
throw new ValidationError(`dropsToXrp: value '${drops}' has` +
|
||||
` too many decimal places.`)
|
||||
throw new ValidationError(
|
||||
`dropsToXrp: value '${drops}' has` + ` too many decimal places.`
|
||||
)
|
||||
}
|
||||
|
||||
// This should never happen; the value has already been
|
||||
// validated above. This just ensures BigNumber did not do
|
||||
// something unexpected.
|
||||
if (!drops.match(/^-?[0-9]+$/)) {
|
||||
throw new ValidationError(`dropsToXrp: failed sanity check -` +
|
||||
` value '${drops}',` +
|
||||
` does not match (^-?[0-9]+$).`)
|
||||
throw new ValidationError(
|
||||
`dropsToXrp: failed sanity check -` +
|
||||
` value '${drops}',` +
|
||||
` does not match (^-?[0-9]+$).`
|
||||
)
|
||||
}
|
||||
|
||||
return (new BigNumber(drops)).dividedBy(1000000.0).toString(10)
|
||||
return new BigNumber(drops).dividedBy(1000000.0).toString(10)
|
||||
}
|
||||
|
||||
function xrpToDrops(xrp: string | BigNumber): string {
|
||||
function xrpToDrops(xrp: BigNumber.Value): string {
|
||||
if (typeof xrp === 'string') {
|
||||
if (!xrp.match(/^-?[0-9]*\.?[0-9]*$/)) {
|
||||
throw new ValidationError(`xrpToDrops: invalid value '${xrp}',` +
|
||||
` should be a number matching (^-?[0-9]*\.?[0-9]*$).`)
|
||||
throw new ValidationError(
|
||||
`xrpToDrops: invalid value '${xrp}',` +
|
||||
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`
|
||||
)
|
||||
} else if (xrp === '.') {
|
||||
throw new ValidationError(`xrpToDrops: invalid value '${xrp}',` +
|
||||
` should be a BigNumber or string-encoded number.`)
|
||||
throw new ValidationError(
|
||||
`xrpToDrops: invalid value '${xrp}',` +
|
||||
` should be a BigNumber or string-encoded number.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
|
||||
xrp = (new BigNumber(xrp)).toString(10)
|
||||
xrp = new BigNumber(xrp).toString(10)
|
||||
|
||||
// This should never happen; the value has already been
|
||||
// validated above. This just ensures BigNumber did not do
|
||||
// something unexpected.
|
||||
if (!xrp.match(/^-?[0-9.]+$/)) {
|
||||
throw new ValidationError(`xrpToDrops: failed sanity check -` +
|
||||
` value '${xrp}',` +
|
||||
` does not match (^-?[0-9.]+$).`)
|
||||
throw new ValidationError(
|
||||
`xrpToDrops: failed sanity check -` +
|
||||
` value '${xrp}',` +
|
||||
` does not match (^-?[0-9.]+$).`
|
||||
)
|
||||
}
|
||||
|
||||
const components = xrp.split('.')
|
||||
if (components.length > 2) {
|
||||
throw new ValidationError(`xrpToDrops: failed sanity check -` +
|
||||
` value '${xrp}' has` +
|
||||
` too many decimal points.`)
|
||||
throw new ValidationError(
|
||||
`xrpToDrops: failed sanity check -` +
|
||||
` value '${xrp}' has` +
|
||||
` too many decimal points.`
|
||||
)
|
||||
}
|
||||
|
||||
const fraction = components[1] || '0'
|
||||
if (fraction.length > 6) {
|
||||
throw new ValidationError(`xrpToDrops: value '${xrp}' has` +
|
||||
` too many decimal places.`)
|
||||
throw new ValidationError(
|
||||
`xrpToDrops: value '${xrp}' has` + ` too many decimal places.`
|
||||
)
|
||||
}
|
||||
|
||||
return (new BigNumber(xrp)).times(1000000.0).floor().toString(10)
|
||||
return new BigNumber(xrp)
|
||||
.times(1000000.0)
|
||||
.integerValue(BigNumber.ROUND_FLOOR)
|
||||
.toString(10)
|
||||
}
|
||||
|
||||
function toRippledAmount(amount: Amount): RippledAmount {
|
||||
@@ -95,8 +114,11 @@ function toRippledAmount(amount: Amount): RippledAmount {
|
||||
}
|
||||
return {
|
||||
currency: amount.currency,
|
||||
issuer: amount.counterparty ? amount.counterparty :
|
||||
(amount.issuer ? amount.issuer : undefined),
|
||||
issuer: amount.counterparty
|
||||
? amount.counterparty
|
||||
: amount.issuer
|
||||
? amount.issuer
|
||||
: undefined,
|
||||
value: amount.value
|
||||
}
|
||||
}
|
||||
@@ -105,16 +127,20 @@ function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
|
||||
if (typeof obj === 'object') {
|
||||
const accumulator = Array.isArray(obj) ? [] : {}
|
||||
let newKey
|
||||
return _.reduce(obj, (result, value, 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)) {
|
||||
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
|
||||
}
|
||||
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
|
||||
return result
|
||||
}, accumulator)
|
||||
return _.reduce(
|
||||
obj,
|
||||
(result, value, 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)) {
|
||||
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
|
||||
}
|
||||
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
|
||||
return result
|
||||
},
|
||||
accumulator
|
||||
)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
@@ -128,7 +154,7 @@ function removeUndefined<T extends object>(obj: T): T {
|
||||
* @return {Number} ms since unix epoch
|
||||
*/
|
||||
function rippleToUnixTimestamp(rpepoch: number): number {
|
||||
return (rpepoch + 0x386D4380) * 1000
|
||||
return (rpepoch + 0x386d4380) * 1000
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,7 +162,7 @@ function rippleToUnixTimestamp(rpepoch: number): number {
|
||||
* @return {Number} seconds since ripple epoch (1/1/2000 GMT)
|
||||
*/
|
||||
function unixToRippleTimestamp(timestamp: number): number {
|
||||
return Math.round(timestamp / 1000) - 0x386D4380
|
||||
return Math.round(timestamp / 1000) - 0x386d4380
|
||||
}
|
||||
|
||||
function rippleTimeToISO8601(rippleTime: number): string {
|
||||
@@ -161,4 +187,3 @@ export {
|
||||
iso8601ToRippleTime,
|
||||
isValidSecret
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,11 @@ function error(text) {
|
||||
}
|
||||
|
||||
function validateLedgerRange(options) {
|
||||
if (!_.isUndefined(options) && !_.isUndefined(options.minLedgerVersion)
|
||||
&& !_.isUndefined(options.maxLedgerVersion)) {
|
||||
if (
|
||||
!_.isUndefined(options) &&
|
||||
!_.isUndefined(options.minLedgerVersion) &&
|
||||
!_.isUndefined(options.maxLedgerVersion)
|
||||
) {
|
||||
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
|
||||
throw error('minLedgerVersion must not be greater than maxLedgerVersion')
|
||||
}
|
||||
@@ -20,110 +23,143 @@ function validateOptions(schema, instance) {
|
||||
validateLedgerRange(instance.options)
|
||||
}
|
||||
|
||||
export const getPaths =
|
||||
_.partial(schemaValidate, 'getPathsParameters')
|
||||
export const getPaths = _.partial(schemaValidate, 'getPathsParameters')
|
||||
|
||||
export const getTransactions =
|
||||
_.partial(validateOptions, 'getTransactionsParameters')
|
||||
export const getTransactions = _.partial(
|
||||
validateOptions,
|
||||
'getTransactionsParameters'
|
||||
)
|
||||
|
||||
export const getSettings =
|
||||
_.partial(validateOptions, 'getSettingsParameters')
|
||||
export const getSettings = _.partial(validateOptions, 'getSettingsParameters')
|
||||
|
||||
export const getAccountInfo =
|
||||
_.partial(validateOptions, 'getAccountInfoParameters')
|
||||
export const getAccountInfo = _.partial(
|
||||
validateOptions,
|
||||
'getAccountInfoParameters'
|
||||
)
|
||||
|
||||
export const getTrustlines =
|
||||
_.partial(validateOptions, 'getTrustlinesParameters')
|
||||
export const getTrustlines = _.partial(
|
||||
validateOptions,
|
||||
'getTrustlinesParameters'
|
||||
)
|
||||
|
||||
export const getBalances =
|
||||
_.partial(validateOptions, 'getBalancesParameters')
|
||||
export const getBalances = _.partial(validateOptions, 'getBalancesParameters')
|
||||
|
||||
export const getBalanceSheet =
|
||||
_.partial(validateOptions, 'getBalanceSheetParameters')
|
||||
export const getBalanceSheet = _.partial(
|
||||
validateOptions,
|
||||
'getBalanceSheetParameters'
|
||||
)
|
||||
|
||||
export const getOrders =
|
||||
_.partial(validateOptions, 'getOrdersParameters')
|
||||
export const getOrders = _.partial(validateOptions, 'getOrdersParameters')
|
||||
|
||||
export const getOrderbook =
|
||||
_.partial(validateOptions, 'getOrderbookParameters')
|
||||
export const getOrderbook = _.partial(validateOptions, 'getOrderbookParameters')
|
||||
|
||||
export const getTransaction =
|
||||
_.partial(validateOptions, 'getTransactionParameters')
|
||||
export const getTransaction = _.partial(
|
||||
validateOptions,
|
||||
'getTransactionParameters'
|
||||
)
|
||||
|
||||
export const getPaymentChannel =
|
||||
_.partial(validateOptions, 'getPaymentChannelParameters')
|
||||
export const getPaymentChannel = _.partial(
|
||||
validateOptions,
|
||||
'getPaymentChannelParameters'
|
||||
)
|
||||
|
||||
export const getLedger =
|
||||
_.partial(validateOptions, 'getLedgerParameters')
|
||||
export const getLedger = _.partial(validateOptions, 'getLedgerParameters')
|
||||
|
||||
export const preparePayment =
|
||||
_.partial(schemaValidate, 'preparePaymentParameters')
|
||||
export const preparePayment = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentParameters'
|
||||
)
|
||||
|
||||
export const prepareOrder =
|
||||
_.partial(schemaValidate, 'prepareOrderParameters')
|
||||
export const prepareOrder = _.partial(schemaValidate, 'prepareOrderParameters')
|
||||
|
||||
export const prepareOrderCancellation =
|
||||
_.partial(schemaValidate, 'prepareOrderCancellationParameters')
|
||||
export const prepareOrderCancellation = _.partial(
|
||||
schemaValidate,
|
||||
'prepareOrderCancellationParameters'
|
||||
)
|
||||
|
||||
export const prepareTrustline =
|
||||
_.partial(schemaValidate, 'prepareTrustlineParameters')
|
||||
export const prepareTrustline = _.partial(
|
||||
schemaValidate,
|
||||
'prepareTrustlineParameters'
|
||||
)
|
||||
|
||||
export const prepareSettings =
|
||||
_.partial(schemaValidate, 'prepareSettingsParameters')
|
||||
export const prepareSettings = _.partial(
|
||||
schemaValidate,
|
||||
'prepareSettingsParameters'
|
||||
)
|
||||
|
||||
export const prepareEscrowCreation =
|
||||
_.partial(schemaValidate, 'prepareEscrowCreationParameters')
|
||||
export const prepareEscrowCreation = _.partial(
|
||||
schemaValidate,
|
||||
'prepareEscrowCreationParameters'
|
||||
)
|
||||
|
||||
export const prepareEscrowCancellation =
|
||||
_.partial(schemaValidate, 'prepareEscrowCancellationParameters')
|
||||
export const prepareEscrowCancellation = _.partial(
|
||||
schemaValidate,
|
||||
'prepareEscrowCancellationParameters'
|
||||
)
|
||||
|
||||
export const prepareEscrowExecution =
|
||||
_.partial(schemaValidate, 'prepareEscrowExecutionParameters')
|
||||
export const prepareEscrowExecution = _.partial(
|
||||
schemaValidate,
|
||||
'prepareEscrowExecutionParameters'
|
||||
)
|
||||
|
||||
export const preparePaymentChannelCreate =
|
||||
_.partial(schemaValidate, 'preparePaymentChannelCreateParameters')
|
||||
export const preparePaymentChannelCreate = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentChannelCreateParameters'
|
||||
)
|
||||
|
||||
export const preparePaymentChannelFund =
|
||||
_.partial(schemaValidate, 'preparePaymentChannelFundParameters')
|
||||
export const preparePaymentChannelFund = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentChannelFundParameters'
|
||||
)
|
||||
|
||||
export const preparePaymentChannelClaim =
|
||||
_.partial(schemaValidate, 'preparePaymentChannelClaimParameters')
|
||||
export const preparePaymentChannelClaim = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentChannelClaimParameters'
|
||||
)
|
||||
|
||||
export const prepareCheckCreate =
|
||||
_.partial(schemaValidate, 'prepareCheckCreateParameters')
|
||||
export const prepareCheckCreate = _.partial(
|
||||
schemaValidate,
|
||||
'prepareCheckCreateParameters'
|
||||
)
|
||||
|
||||
export const prepareCheckCash =
|
||||
_.partial(schemaValidate, 'prepareCheckCashParameters')
|
||||
export const prepareCheckCash = _.partial(
|
||||
schemaValidate,
|
||||
'prepareCheckCashParameters'
|
||||
)
|
||||
|
||||
export const prepareCheckCancel =
|
||||
_.partial(schemaValidate, 'prepareCheckCancelParameters')
|
||||
export const prepareCheckCancel = _.partial(
|
||||
schemaValidate,
|
||||
'prepareCheckCancelParameters'
|
||||
)
|
||||
|
||||
export const sign =
|
||||
_.partial(schemaValidate, 'signParameters')
|
||||
export const sign = _.partial(schemaValidate, 'signParameters')
|
||||
|
||||
export const combine =
|
||||
_.partial(schemaValidate, 'combineParameters')
|
||||
export const combine = _.partial(schemaValidate, 'combineParameters')
|
||||
|
||||
export const submit =
|
||||
_.partial(schemaValidate, 'submitParameters')
|
||||
export const submit = _.partial(schemaValidate, 'submitParameters')
|
||||
|
||||
export const computeLedgerHash =
|
||||
_.partial(schemaValidate, 'computeLedgerHashParameters')
|
||||
export const computeLedgerHash = _.partial(
|
||||
schemaValidate,
|
||||
'computeLedgerHashParameters'
|
||||
)
|
||||
|
||||
export const generateAddress =
|
||||
_.partial(schemaValidate, 'generateAddressParameters')
|
||||
export const generateAddress = _.partial(
|
||||
schemaValidate,
|
||||
'generateAddressParameters'
|
||||
)
|
||||
|
||||
export const signPaymentChannelClaim =
|
||||
_.partial(schemaValidate, 'signPaymentChannelClaimParameters')
|
||||
export const signPaymentChannelClaim = _.partial(
|
||||
schemaValidate,
|
||||
'signPaymentChannelClaimParameters'
|
||||
)
|
||||
|
||||
export const verifyPaymentChannelClaim =
|
||||
_.partial(schemaValidate, 'verifyPaymentChannelClaimParameters')
|
||||
export const verifyPaymentChannelClaim = _.partial(
|
||||
schemaValidate,
|
||||
'verifyPaymentChannelClaimParameters'
|
||||
)
|
||||
|
||||
export const apiOptions =
|
||||
_.partial(schemaValidate, 'api-options')
|
||||
export const apiOptions = _.partial(schemaValidate, 'api-options')
|
||||
|
||||
export const instructions =
|
||||
_.partial(schemaValidate, 'instructions')
|
||||
export const instructions = _.partial(schemaValidate, 'instructions')
|
||||
|
||||
export const tx_json =
|
||||
_.partial(schemaValidate, 'tx-json')
|
||||
export const tx_json = _.partial(schemaValidate, 'tx-json')
|
||||
|
||||
@@ -17,7 +17,6 @@ declare class WebSocket {
|
||||
* same, as `ws` package provides.
|
||||
*/
|
||||
class WSWrapper extends EventEmitter {
|
||||
|
||||
private _ws: WebSocket
|
||||
static CONNECTING = 0
|
||||
static OPEN = 1
|
||||
@@ -60,8 +59,6 @@ class WSWrapper extends EventEmitter {
|
||||
get readyState() {
|
||||
return this._ws.readyState
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export = WSWrapper
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
export {RippleAPI} from './api'
|
||||
|
||||
export {
|
||||
FormattedTransactionType
|
||||
} from './transaction/types'
|
||||
export {FormattedTransactionType} from './transaction/types'
|
||||
|
||||
// Broadcast api is experimental
|
||||
export {RippleAPIBroadcast} from './broadcast'
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import {validate, removeUndefined, dropsToXrp} from '../common'
|
||||
import {
|
||||
validate,
|
||||
removeUndefined,
|
||||
dropsToXrp,
|
||||
ensureClassicAddress
|
||||
} from '../common'
|
||||
import {RippleAPI} from '..'
|
||||
import {AccountInfoResponse} from '../common/types/commands/account_info'
|
||||
|
||||
@@ -7,11 +12,11 @@ export type GetAccountInfoOptions = {
|
||||
}
|
||||
|
||||
export type FormattedGetAccountInfoResponse = {
|
||||
sequence: number,
|
||||
xrpBalance: string,
|
||||
ownerCount: number,
|
||||
previousInitiatedTransactionID: string,
|
||||
previousAffectingTransactionID: string,
|
||||
sequence: number
|
||||
xrpBalance: string
|
||||
ownerCount: number
|
||||
previousInitiatedTransactionID: string
|
||||
previousAffectingTransactionID: string
|
||||
previousAffectingTransactionLedgerVersion: number
|
||||
}
|
||||
|
||||
@@ -30,10 +35,17 @@ function formatAccountInfo(
|
||||
}
|
||||
|
||||
export default async function getAccountInfo(
|
||||
this: RippleAPI, address: string, options: GetAccountInfoOptions = {}
|
||||
this: RippleAPI,
|
||||
address: string,
|
||||
options: GetAccountInfoOptions = {}
|
||||
): Promise<FormattedGetAccountInfoResponse> {
|
||||
// 1. Validate
|
||||
validate.getAccountInfo({address, options})
|
||||
|
||||
// Only support retrieving account info without a tag,
|
||||
// since account info is not distinguished by tag.
|
||||
address = ensureClassicAddress(address)
|
||||
|
||||
// 2. Make Request
|
||||
const response = await this.request('account_info', {
|
||||
account: address,
|
||||
|
||||
@@ -14,14 +14,17 @@ export default async function getAccountObjects(
|
||||
// through to rippled. rippled validates requests.
|
||||
|
||||
// Make Request
|
||||
const response = await this.request('account_objects', removeUndefined({
|
||||
account: address,
|
||||
type: options.type,
|
||||
ledger_hash: options.ledgerHash,
|
||||
ledger_index: options.ledgerIndex,
|
||||
limit: options.limit,
|
||||
marker: options.marker
|
||||
}))
|
||||
const response = await this.request(
|
||||
'account_objects',
|
||||
removeUndefined({
|
||||
account: address,
|
||||
type: options.type,
|
||||
ledger_hash: options.ledgerHash,
|
||||
ledger_index: options.ledgerIndex,
|
||||
limit: options.limit,
|
||||
marker: options.marker
|
||||
})
|
||||
)
|
||||
// Return Response
|
||||
return response
|
||||
}
|
||||
|
||||
@@ -5,17 +5,17 @@ import {ensureLedgerVersion} from './utils'
|
||||
import {RippleAPI} from '..'
|
||||
|
||||
export type BalanceSheetOptions = {
|
||||
excludeAddresses?: Array<string>,
|
||||
excludeAddresses?: Array<string>
|
||||
ledgerVersion?: number
|
||||
}
|
||||
|
||||
export type GetBalanceSheet = {
|
||||
balances?: Array<Amount>,
|
||||
assets?: Array<Amount>,
|
||||
balances?: Array<Amount>
|
||||
assets?: Array<Amount>
|
||||
obligations?: Array<{
|
||||
currency: string,
|
||||
value: string
|
||||
}>
|
||||
currency: string
|
||||
value: string
|
||||
}>
|
||||
}
|
||||
|
||||
function formatBalanceSheet(balanceSheet): GetBalanceSheet {
|
||||
@@ -48,7 +48,9 @@ function formatBalanceSheet(balanceSheet): GetBalanceSheet {
|
||||
}
|
||||
|
||||
async function getBalanceSheet(
|
||||
this: RippleAPI, address: string, options: BalanceSheetOptions = {}
|
||||
this: RippleAPI,
|
||||
address: string,
|
||||
options: BalanceSheetOptions = {}
|
||||
): Promise<GetBalanceSheet> {
|
||||
// 1. Validate
|
||||
validate.getBalanceSheet({address, options})
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import * as utils from './utils'
|
||||
import {validate} from '../common'
|
||||
import {validate, ensureClassicAddress} from '../common'
|
||||
import {Connection} from '../common'
|
||||
import {GetTrustlinesOptions} from './trustlines'
|
||||
import {FormattedTrustline} from '../common/types/objects/trustlines'
|
||||
import {RippleAPI} from '..'
|
||||
|
||||
|
||||
export type Balance = {
|
||||
value: string,
|
||||
currency: string,
|
||||
value: string
|
||||
currency: string
|
||||
counterparty?: string
|
||||
}
|
||||
|
||||
@@ -24,9 +23,9 @@ function getTrustlineBalanceAmount(trustline: FormattedTrustline) {
|
||||
|
||||
function formatBalances(options, balances) {
|
||||
const result = balances.trustlines.map(getTrustlineBalanceAmount)
|
||||
if (!(options.counterparty ||
|
||||
(options.currency && options.currency !== 'XRP')
|
||||
)) {
|
||||
if (
|
||||
!(options.counterparty || (options.currency && options.currency !== 'XRP'))
|
||||
) {
|
||||
const xrpBalance = {
|
||||
currency: 'XRP',
|
||||
value: balances.xrp
|
||||
@@ -40,7 +39,9 @@ function formatBalances(options, balances) {
|
||||
return result
|
||||
}
|
||||
|
||||
function getLedgerVersionHelper(connection: Connection, optionValue?: number
|
||||
function getLedgerVersionHelper(
|
||||
connection: Connection,
|
||||
optionValue?: number
|
||||
): Promise<number> {
|
||||
if (optionValue !== undefined && optionValue !== null) {
|
||||
return Promise.resolve(optionValue)
|
||||
@@ -48,17 +49,31 @@ function getLedgerVersionHelper(connection: Connection, optionValue?: number
|
||||
return connection.getLedgerVersion()
|
||||
}
|
||||
|
||||
function getBalances(this: RippleAPI, address: string, options: GetTrustlinesOptions = {}
|
||||
function getBalances(
|
||||
this: RippleAPI,
|
||||
address: string,
|
||||
options: GetTrustlinesOptions = {}
|
||||
): Promise<GetBalances> {
|
||||
validate.getTrustlines({address, options})
|
||||
|
||||
// Only support retrieving balances without a tag,
|
||||
// since we currently do not calculate balances
|
||||
// on a per-tag basis. Apps must interpret and
|
||||
// use tags independent of the XRP Ledger, comparing
|
||||
// with the XRP Ledger's balance as an accounting check.
|
||||
address = ensureClassicAddress(address)
|
||||
|
||||
return Promise.all([
|
||||
getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
|
||||
ledgerVersion =>
|
||||
utils.getXRPBalance(this.connection, address, ledgerVersion)),
|
||||
getLedgerVersionHelper(
|
||||
this.connection,
|
||||
options.ledgerVersion
|
||||
).then(ledgerVersion =>
|
||||
utils.getXRPBalance(this.connection, address, ledgerVersion)
|
||||
),
|
||||
this.getTrustlines(address, options)
|
||||
]).then(results =>
|
||||
formatBalances(options, {xrp: results[0], trustlines: results[1]}))
|
||||
formatBalances(options, {xrp: results[0], trustlines: results[1]})
|
||||
)
|
||||
}
|
||||
|
||||
export default getBalances
|
||||
|
||||
@@ -3,15 +3,16 @@ import {FormattedLedger, parseLedger} from './parse/ledger'
|
||||
import {RippleAPI} from '..'
|
||||
|
||||
export type GetLedgerOptions = {
|
||||
ledgerHash?: string,
|
||||
ledgerVersion?: number,
|
||||
includeAllData?: boolean,
|
||||
includeTransactions?: boolean,
|
||||
ledgerHash?: string
|
||||
ledgerVersion?: number
|
||||
includeAllData?: boolean
|
||||
includeTransactions?: boolean
|
||||
includeState?: boolean
|
||||
}
|
||||
|
||||
async function getLedger(
|
||||
this: RippleAPI, options: GetLedgerOptions = {}
|
||||
this: RippleAPI,
|
||||
options: GetLedgerOptions = {}
|
||||
): Promise<FormattedLedger> {
|
||||
// 1. Validate
|
||||
validate.getLedger({options})
|
||||
|
||||
@@ -11,7 +11,7 @@ import {RippleAPI} from '..'
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
export type FormattedOrderbook = {
|
||||
bids: FormattedOrderbookOrder[],
|
||||
bids: FormattedOrderbookOrder[]
|
||||
asks: FormattedOrderbookOrder[]
|
||||
}
|
||||
|
||||
@@ -34,13 +34,18 @@ function flipOrder(order: FormattedOrderbookOrder) {
|
||||
return _.merge({}, order, {specification: newSpecification})
|
||||
}
|
||||
|
||||
function alignOrder(base: Issue, order: FormattedOrderbookOrder): FormattedOrderbookOrder {
|
||||
function alignOrder(
|
||||
base: Issue,
|
||||
order: FormattedOrderbookOrder
|
||||
): FormattedOrderbookOrder {
|
||||
const quantity = order.specification.quantity
|
||||
return isSameIssue(quantity, base) ? order : flipOrder(order)
|
||||
}
|
||||
|
||||
export function formatBidsAndAsks(
|
||||
orderbook: OrderbookInfo, offers: BookOffer[]) {
|
||||
orderbook: OrderbookInfo,
|
||||
offers: BookOffer[]
|
||||
) {
|
||||
// the "base" currency is the currency that you are buying or selling
|
||||
// the "counter" is the currency that the "base" is priced in
|
||||
// a "bid"/"ask" is an order to buy/sell the base, respectively
|
||||
@@ -51,9 +56,11 @@ export function formatBidsAndAsks(
|
||||
// for asks: lowest quality => lowest totalPrice/quantity => lowest price
|
||||
// for both bids and asks, lowest quality is closest to mid-market
|
||||
// we sort the orders so that earlier orders are closer to mid-market
|
||||
const orders = offers.sort((a, b) => {
|
||||
return (new BigNumber(a.quality)).comparedTo(b.quality)
|
||||
}).map(parseOrderbookOrder)
|
||||
const orders = offers
|
||||
.sort((a, b) => {
|
||||
return new BigNumber(a.quality).comparedTo(b.quality)
|
||||
})
|
||||
.map(parseOrderbookOrder)
|
||||
|
||||
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base))
|
||||
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'))
|
||||
@@ -64,8 +71,11 @@ export function formatBidsAndAsks(
|
||||
// account is to specify a "perspective", which affects which unfunded offers
|
||||
// are returned
|
||||
async function makeRequest(
|
||||
api: RippleAPI, taker: string, options: GetOrderbookOptions,
|
||||
takerGets: Issue, takerPays: Issue
|
||||
api: RippleAPI,
|
||||
taker: string,
|
||||
options: GetOrderbookOptions,
|
||||
takerGets: Issue,
|
||||
takerPays: Issue
|
||||
) {
|
||||
const orderData = utils.renameCounterpartyToIssuerInOrder({
|
||||
taker_gets: takerGets,
|
||||
@@ -80,14 +90,13 @@ async function makeRequest(
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export type GetOrderbookOptions = {
|
||||
limit?: number,
|
||||
limit?: number
|
||||
ledgerVersion?: number
|
||||
}
|
||||
|
||||
export type OrderbookInfo = {
|
||||
base: Issue,
|
||||
base: Issue
|
||||
counter: Issue
|
||||
}
|
||||
|
||||
@@ -105,10 +114,13 @@ export async function getOrderbook(
|
||||
makeRequest(this, address, options, orderbook.counter, orderbook.base)
|
||||
])
|
||||
// 3. Return Formatted Response
|
||||
const directOffers = _.flatMap(directOfferResults,
|
||||
directOfferResult => directOfferResult.offers)
|
||||
const reverseOffers = _.flatMap(reverseOfferResults,
|
||||
reverseOfferResult => reverseOfferResult.offers)
|
||||
return formatBidsAndAsks(orderbook,
|
||||
[...directOffers, ...reverseOffers])
|
||||
const directOffers = _.flatMap(
|
||||
directOfferResults,
|
||||
directOfferResult => directOfferResult.offers
|
||||
)
|
||||
const reverseOffers = _.flatMap(
|
||||
reverseOfferResults,
|
||||
reverseOfferResult => reverseOfferResult.offers
|
||||
)
|
||||
return formatBidsAndAsks(orderbook, [...directOffers, ...reverseOffers])
|
||||
}
|
||||
|
||||
@@ -5,12 +5,13 @@ import {RippleAPI} from '..'
|
||||
import {AccountOffersResponse} from '../common/types/commands'
|
||||
|
||||
export type GetOrdersOptions = {
|
||||
limit?: number,
|
||||
limit?: number
|
||||
ledgerVersion?: number
|
||||
}
|
||||
|
||||
function formatResponse(
|
||||
address: string, responses: AccountOffersResponse[]
|
||||
address: string,
|
||||
responses: AccountOffersResponse[]
|
||||
): FormattedAccountOrder[] {
|
||||
let orders: FormattedAccountOrder[] = []
|
||||
for (const response of responses) {
|
||||
@@ -23,16 +24,18 @@ function formatResponse(
|
||||
}
|
||||
|
||||
export default async function getOrders(
|
||||
this: RippleAPI, address: string, options: GetOrdersOptions = {}
|
||||
this: RippleAPI,
|
||||
address: string,
|
||||
options: GetOrdersOptions = {}
|
||||
): Promise<FormattedAccountOrder[]> {
|
||||
// 1. Validate
|
||||
validate.getOrders({address, options})
|
||||
// 2. Make Request
|
||||
const responses = await this._requestAll('account_offers', {
|
||||
account: address,
|
||||
ledger_index: options.ledgerVersion || await this.getLedgerVersion(),
|
||||
ledger_index: options.ledgerVersion || (await this.getLedgerVersion()),
|
||||
limit: options.limit
|
||||
})
|
||||
// 3. Return Formatted Response
|
||||
// 3. Return Formatted Response, from the perspective of `address`
|
||||
return formatResponse(address, responses)
|
||||
}
|
||||
|
||||
34
src/ledger/parse/account-delete.ts
Normal file
34
src/ledger/parse/account-delete.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
import {classicAddressToXAddress} from 'ripple-address-codec'
|
||||
|
||||
export type FormattedAccountDelete = {
|
||||
// account (address) of an account to receive any leftover XRP after deleting the sending account.
|
||||
// Must be a funded account in the ledger, and must not be the sending account.
|
||||
destination: string
|
||||
|
||||
// (Optional) Arbitrary destination tag that identifies a hosted recipient or other information
|
||||
// for the recipient of the deleted account's leftover XRP. NB: Ensure that the hosted recipient is
|
||||
// able to account for AccountDelete transactions; if not, your balance may not be properly credited.
|
||||
destinationTag?: number
|
||||
|
||||
// X-address of an account to receive any leftover XRP after deleting the sending account.
|
||||
// Must be a funded account in the ledger, and must not be the sending account.
|
||||
destinationXAddress: string
|
||||
}
|
||||
|
||||
function parseAccountDelete(tx: any): FormattedAccountDelete {
|
||||
assert.ok(tx.TransactionType === 'AccountDelete')
|
||||
|
||||
return removeUndefined({
|
||||
destination: tx.Destination,
|
||||
destinationTag: tx.DestinationTag,
|
||||
destinationXAddress: classicAddressToXAddress(
|
||||
tx.Destination,
|
||||
tx.DestinationTag === undefined ? false : tx.DestinationTag,
|
||||
false
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
export default parseAccountDelete
|
||||
@@ -6,10 +6,10 @@ import {orderFlags} from './flags'
|
||||
import {FormattedOrderSpecification} from '../../common/types/objects'
|
||||
|
||||
export type FormattedAccountOrder = {
|
||||
specification: FormattedOrderSpecification,
|
||||
properties: {
|
||||
maker: string,
|
||||
sequence: number,
|
||||
specification: FormattedOrderSpecification
|
||||
properties: {
|
||||
maker: string
|
||||
sequence: number
|
||||
makerExchangeRate: string
|
||||
}
|
||||
}
|
||||
@@ -17,19 +17,20 @@ export type FormattedAccountOrder = {
|
||||
// TODO: remove this function once rippled provides quality directly
|
||||
function computeQuality(takerGets, takerPays) {
|
||||
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value)
|
||||
return quotient.toDigits(16, BigNumber.ROUND_HALF_UP).toString()
|
||||
return quotient.precision(16, BigNumber.ROUND_HALF_UP).toString()
|
||||
}
|
||||
|
||||
// rippled 'account_offers' returns a different format for orders than 'tx'
|
||||
// the flags are also different
|
||||
export function parseAccountOrder(
|
||||
address: string, order: any
|
||||
address: string,
|
||||
order: any
|
||||
): FormattedAccountOrder {
|
||||
const direction = (order.flags & orderFlags.Sell) === 0 ? 'buy' : 'sell'
|
||||
const takerGetsAmount = parseAmount(order.taker_gets)
|
||||
const takerPaysAmount = parseAmount(order.taker_pays)
|
||||
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
|
||||
const quantity = direction === 'buy' ? takerPaysAmount : takerGetsAmount
|
||||
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
|
||||
// so we can omit those flags here
|
||||
@@ -37,15 +38,18 @@ export function parseAccountOrder(
|
||||
direction: direction,
|
||||
quantity: quantity,
|
||||
totalPrice: totalPrice,
|
||||
passive: ((order.flags & orderFlags.Passive) !== 0) || undefined,
|
||||
passive: (order.flags & orderFlags.Passive) !== 0 || undefined,
|
||||
// rippled currently does not provide "expiration" in account_offers
|
||||
expirationTime: parseTimestamp(order.expiration)
|
||||
})
|
||||
|
||||
const makerExchangeRate = order.quality ?
|
||||
adjustQualityForXRP(order.quality.toString(),
|
||||
takerGetsAmount.currency, takerPaysAmount.currency) :
|
||||
computeQuality(takerGetsAmount, takerPaysAmount)
|
||||
const makerExchangeRate = order.quality
|
||||
? adjustQualityForXRP(
|
||||
order.quality.toString(),
|
||||
takerGetsAmount.currency,
|
||||
takerPaysAmount.currency
|
||||
)
|
||||
: computeQuality(takerGetsAmount, takerPaysAmount)
|
||||
const properties = {
|
||||
maker: address,
|
||||
sequence: order.seq,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
function parseAmendment(tx: any) {
|
||||
return {
|
||||
amendment: tx.Amendment
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import * as common from '../../common'
|
||||
import {Amount, RippledAmount} from '../../common/types/objects'
|
||||
|
||||
|
||||
function parseAmount(amount: RippledAmount): Amount {
|
||||
if (typeof amount === 'string') {
|
||||
return {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as assert from 'assert'
|
||||
|
||||
function parseOrderCancellation(tx: any): object {
|
||||
assert(tx.TransactionType === 'OfferCancel')
|
||||
assert.ok(tx.TransactionType === 'OfferCancel')
|
||||
return {
|
||||
orderSequence: tx.OfferSequence
|
||||
}
|
||||
|
||||
@@ -2,13 +2,12 @@ import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
|
||||
export type FormattedCheckCancel = {
|
||||
|
||||
// ID of the Check ledger object to cancel.
|
||||
checkID: string
|
||||
}
|
||||
|
||||
function parseCheckCancel(tx: any): FormattedCheckCancel {
|
||||
assert(tx.TransactionType === 'CheckCancel')
|
||||
assert.ok(tx.TransactionType === 'CheckCancel')
|
||||
|
||||
return removeUndefined({
|
||||
checkID: tx.CheckID
|
||||
|
||||
@@ -4,14 +4,13 @@ import parseAmount from './amount'
|
||||
import {Amount} from '../../common/types/objects'
|
||||
|
||||
export type FormattedCheckCash = {
|
||||
|
||||
// ID of the Check ledger object to cash.
|
||||
checkID: string,
|
||||
checkID: string
|
||||
|
||||
// (Optional) redeem the Check for exactly this amount, if possible.
|
||||
// The currency must match that of the `SendMax` of the corresponding
|
||||
// `CheckCreate` transaction.
|
||||
amount: Amount,
|
||||
amount: Amount
|
||||
|
||||
// (Optional) redeem the Check for at least this amount and
|
||||
// for as much as possible.
|
||||
@@ -23,7 +22,7 @@ export type FormattedCheckCash = {
|
||||
}
|
||||
|
||||
function parseCheckCash(tx: any): FormattedCheckCash {
|
||||
assert(tx.TransactionType === 'CheckCash')
|
||||
assert.ok(tx.TransactionType === 'CheckCash')
|
||||
|
||||
return removeUndefined({
|
||||
checkID: tx.CheckID,
|
||||
|
||||
@@ -5,26 +5,25 @@ import parseAmount from './amount'
|
||||
import {Amount} from '../../common/types/objects'
|
||||
|
||||
export type FormattedCheckCreate = {
|
||||
|
||||
// account that can cash the check.
|
||||
destination: string,
|
||||
destination: string
|
||||
|
||||
// amount the check is allowed to debit the sender,
|
||||
// including transfer fees on non-XRP currencies.
|
||||
sendMax: Amount,
|
||||
sendMax: Amount
|
||||
|
||||
// (Optional) identifies the reason for the check, or a hosted recipient.
|
||||
destinationTag?: string,
|
||||
destinationTag?: string
|
||||
|
||||
// (Optional) time in seconds since the Ripple Epoch.
|
||||
expiration?: string,
|
||||
expiration?: string
|
||||
|
||||
// (Optional) 256-bit hash representing a specific reason or identifier.
|
||||
invoiceID?: string
|
||||
}
|
||||
|
||||
function parseCheckCreate(tx: any): FormattedCheckCreate {
|
||||
assert(tx.TransactionType === 'CheckCreate')
|
||||
assert.ok(tx.TransactionType === 'CheckCreate')
|
||||
|
||||
return removeUndefined({
|
||||
destination: tx.Destination,
|
||||
|
||||
@@ -3,14 +3,14 @@ import {removeUndefined} from '../../common'
|
||||
|
||||
export type FormattedDepositPreauth = {
|
||||
// account (address) of the sender to preauthorize
|
||||
authorize: string,
|
||||
authorize: string
|
||||
|
||||
// account (address) of the sender whose preauthorization should be revoked
|
||||
unauthorize: string
|
||||
}
|
||||
|
||||
function parseDepositPreauth(tx: any): FormattedDepositPreauth {
|
||||
assert(tx.TransactionType === 'DepositPreauth')
|
||||
assert.ok(tx.TransactionType === 'DepositPreauth')
|
||||
|
||||
return removeUndefined({
|
||||
authorize: tx.Authorize,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user