mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-15 18:15:49 +00:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e86ba0aa0 | ||
|
|
cabd4cc88d | ||
|
|
28d2fc2a3a | ||
|
|
42db44fb59 | ||
|
|
ad0165cf22 | ||
|
|
708d8c0bb5 | ||
|
|
5074441b3b | ||
|
|
a1edc0b2d5 | ||
|
|
3afdc1fca0 | ||
|
|
6b7cdfc413 | ||
|
|
95e1314eb9 | ||
|
|
ad0cc209af | ||
|
|
dd11ce480d | ||
|
|
10ace18d27 | ||
|
|
e8ca25f792 | ||
|
|
95d626f69e | ||
|
|
3fdc56ab90 | ||
|
|
bb44598e32 | ||
|
|
3c86b04fee | ||
|
|
7ee87f2625 | ||
|
|
2f03347dda | ||
|
|
bbb19dce9f | ||
|
|
c8b0035897 | ||
|
|
67d1b5278d | ||
|
|
43e444b47d | ||
|
|
f3fd468566 | ||
|
|
97d4f1f2ce | ||
|
|
fbecaf2ddc | ||
|
|
978a5bcd99 | ||
|
|
650d722609 | ||
|
|
ef1f8752d9 | ||
|
|
6fd0b3a5f1 | ||
|
|
362bb13da1 | ||
|
|
d76a5fc87a | ||
|
|
824f87a692 | ||
|
|
fa2385ee1c | ||
|
|
2be7a8fa94 | ||
|
|
7d6a3a7518 | ||
|
|
61692a5e34 | ||
|
|
e82c585955 | ||
|
|
bc50603111 | ||
|
|
debb9cb3a5 | ||
|
|
4fd74b3671 | ||
|
|
eb4ac74ce6 | ||
|
|
cf4d2b2c1a | ||
|
|
c79b044aaa | ||
|
|
32718d583b | ||
|
|
c3965f325b | ||
|
|
7b77177962 | ||
|
|
3781b798a1 | ||
|
|
31314212a5 | ||
|
|
7d36cfa068 | ||
|
|
848c179fd4 | ||
|
|
e2cecd07e4 | ||
|
|
08b76fba22 | ||
|
|
2642589ea4 | ||
|
|
965dc4bd87 | ||
|
|
c9689ec2a8 | ||
|
|
9a3ef216f9 | ||
|
|
e8817d9e0b | ||
|
|
abb1d23ee2 | ||
|
|
9636849c63 | ||
|
|
4aa76b38f9 | ||
|
|
03655b4ed2 | ||
|
|
f1ec45769b | ||
|
|
5db1f5668c | ||
|
|
dd4c6a0353 | ||
|
|
95a2655501 | ||
|
|
2ba31c5b75 | ||
|
|
33fb35138f | ||
|
|
ed26f9a763 | ||
|
|
415a61f06a | ||
|
|
4eb64b5e72 | ||
|
|
9e96fa3473 | ||
|
|
1c1a3fa583 | ||
|
|
c985838cdd | ||
|
|
81dfd99642 | ||
|
|
9f6fa6a4fd | ||
|
|
6b4fa159ea | ||
|
|
782787a5b9 | ||
|
|
fd0f64fe54 | ||
|
|
c7e08378ac | ||
|
|
382cf4cb1f | ||
|
|
be71af5c55 |
13
.github/dependabot.yml
vendored
Normal file
13
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "15:00"
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: jsonschema
|
||||
versions:
|
||||
- "> 1.2.2"
|
||||
- "< 2"
|
||||
67
.github/workflows/codeql-analysis.yml
vendored
Normal file
67
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ develop, master ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ develop ]
|
||||
schedule:
|
||||
- cron: '44 5 * * 6'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
@@ -7,4 +7,4 @@
|
||||
"trailingComma": "none",
|
||||
"quoteProps": "consistent",
|
||||
"bracketSpacing": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ Warning: Use at your own risk.
|
||||
|
||||
Exchange allowing clients to buy and sell XRP, based in Mexico.
|
||||
|
||||
## Data and visualizations
|
||||
## Explorers
|
||||
|
||||
- **[xrpintel - XRP Intelligence](https://xrpintel.com/)**
|
||||
|
||||
@@ -42,6 +42,10 @@ Warning: Use at your own risk.
|
||||
|
||||
Real-time XRP price, trades, and orderbook data from the XRP Ledger.
|
||||
|
||||
- **[Bithomp - XRP Explorer](https://bithomp.com/explorer/)**
|
||||
|
||||
Look up information by entering an address, transaction hash, username, or PayID.
|
||||
|
||||
- **[Bithomp - XRPL validators](https://bithomp.com/validators)**
|
||||
|
||||
List of XRPL validators, nodes, and testnet validators.
|
||||
@@ -54,10 +58,20 @@ Warning: Use at your own risk.
|
||||
|
||||
XRP Ledger explorer, API, metrics, and analytics using a graph database that is synchronized live with the XRPL.
|
||||
|
||||
## Data monitoring
|
||||
|
||||
- **[zerptracker](https://zerptracker.com)**
|
||||
|
||||
Monitor the XRPL using powerful JSONPath expressions, and receive notifications via email, SMS, webhooks, and more.
|
||||
|
||||
- **[Utility-Scan](https://utility-scan.com)**
|
||||
|
||||
Attempts to detect RippleNet on-demand liquidity (ODL) transactions through known fiat corridors and report these transactions in real time.
|
||||
|
||||
- **[XRPL Rosetta](https://xrpl-rosetta-oepox.ondigitalocean.app)**
|
||||
|
||||
3D Globe written in three.js connected to a Node.js websocket server that is listening to exchanges and the XRPL. The visualization aims to show trading, ODL, and liquidity at exchanges, intra-exchange volume, and flows.
|
||||
|
||||
## Wallets and wallet tools
|
||||
|
||||
- **[XUMM](https://xumm.app/)**
|
||||
@@ -147,7 +161,3 @@ Warning: Use at your own risk.
|
||||
- **[XRP Stats](https://ledger.exposed/)** (ledger.exposed)
|
||||
|
||||
Rich list, live ledger stats and XRP distribution. Visualize escrows and flow of funds.
|
||||
|
||||
- **[XRP Vanity](https://xrpvanity.com/)** (xrpvanity.com)
|
||||
|
||||
Custom XRP addresses for sale, delivered by SetRegularKey.
|
||||
|
||||
171
HISTORY.md
171
HISTORY.md
@@ -2,6 +2,177 @@
|
||||
|
||||
Subscribe to [the **ripple-lib-announce** mailing list](https://groups.google.com/forum/#!forum/ripple-lib-announce) for release announcements. We recommend that ripple-lib users stay up-to-date with the latest stable release.
|
||||
|
||||
## 1.9.6 (2021-07-01)
|
||||
|
||||
* Bug fixes
|
||||
* Use 'current' ledger when preparing txs (#1429) (#999)
|
||||
* Allow multiple settings at once (#1435)
|
||||
* Dependencies
|
||||
* ripple-address-codec, prettier, mocha
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
cac7f6f3be93efbd61dc5fd527c40f0d1baec06f2f9faa64e9eeb191cc85a710 build/ripple-latest-min.js
|
||||
fc17a5572001d814ea6b81aa701fcb66882ec031c68afb769a8ea8b71c6529a6 build/ripple-latest-min.js.LICENSE.txt
|
||||
5737483e940dca8b73768d8a1de8217c7e921a9cebaadef02d2b16867658f331 build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.9.5 (2021-06-01)
|
||||
|
||||
* Bug fixes
|
||||
* Prevent getFee from returning NaN from Reporting Mode (#1401) (#1398)
|
||||
* Return promise inside catch block of reconnect to propagate promise (#1418) (#1113) (thanks @camposfyi)
|
||||
* Internal
|
||||
* Update mocha to use RC file config (#1417) (#1210) (thanks @camposfyi)
|
||||
* Dependencies
|
||||
* @types/ws, ws, browserslist, @types/lodash
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
fcdc4aa1e1df7cb788b68f3d036e168aa64f9e818f441b99fef62d4571c0387d build/ripple-latest-min.js
|
||||
fc17a5572001d814ea6b81aa701fcb66882ec031c68afb769a8ea8b71c6529a6 build/ripple-latest-min.js.LICENSE.txt
|
||||
dfb7a92c4156fb3ee367254b5ea0935cda741cd3b5c36cdca695e7d89f88605e build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.9.4 (2021-04-18)
|
||||
|
||||
* Add memos support for all transaction types for getTransactions (#1353, #1397)
|
||||
* Add Deno and React instructions (#1387)
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
daa2b892a18037e89fea6fcf7de67624a782971956cb8df17cd765a4b0201ee9 build/ripple-latest-min.js
|
||||
fc17a5572001d814ea6b81aa701fcb66882ec031c68afb769a8ea8b71c6529a6 build/ripple-latest-min.js.LICENSE.txt
|
||||
b1d0bab54c6dbc76091610ede54a4269e73dea8cc6a9c25738d62bd7671920e4 build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.9.3 (2021-03-16)
|
||||
|
||||
* Expose ripple-address-codec methods. These are static methods on RippleAPI, so you do not need to create a RippleAPI instance.
|
||||
* `classicAddressToXAddress` / `xAddressToClassicAddress`
|
||||
* `isValidXAddress` / `isValidClassicAddress`
|
||||
* `encodeSeed` / `decodeSeed`
|
||||
* `encodeAccountID` / `decodeAccountID`
|
||||
* `encodeNodePublic` / `decodeNodePublic`
|
||||
* `encodeAccountPublic` / `decodeAccountPublic`
|
||||
* `encodeXAddress` / `decodeXAddress`
|
||||
|
||||
Example 1. Encode an X-address with tag 4294967295:
|
||||
```js
|
||||
const RippleAPI = require('ripple-lib').RippleAPI
|
||||
const xAddress = RippleAPI.classicAddressToXAddress('rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf', 4294967295)
|
||||
console.log(xAddress)
|
||||
```
|
||||
|
||||
Output for Example 1:
|
||||
```
|
||||
XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi
|
||||
```
|
||||
|
||||
Example 2. Encode a test address for use with Testnet or Devnet:
|
||||
```js
|
||||
const RippleAPI = require('ripple-lib').RippleAPI
|
||||
const address = RippleAPI.classicAddressToXAddress('r3SVzk8ApofDJuVBPKdmbbLjWGCCXpBQ2g', 123, true)
|
||||
console.log(address)
|
||||
```
|
||||
|
||||
Output for Example 2:
|
||||
```
|
||||
T7oKJ3q7s94kDH6tpkBowhetT1JKfcfdSCmAXbS75iATyLD
|
||||
```
|
||||
|
||||
Example 3. Decode an X-address:
|
||||
```js
|
||||
const RippleAPI = require('ripple-lib').RippleAPI
|
||||
const address = RippleAPI.xAddressToClassicAddress('XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi')
|
||||
console.log(address)
|
||||
```
|
||||
|
||||
Output for Example 3:
|
||||
```js
|
||||
{
|
||||
classicAddress: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
tag: 4294967295,
|
||||
test: false
|
||||
}
|
||||
```
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
bcc8db4e5464197151a267d9f240693794bf1eb4d26a4e0b3637f82a1d66e440 build/ripple-latest-min.js
|
||||
fc17a5572001d814ea6b81aa701fcb66882ec031c68afb769a8ea8b71c6529a6 build/ripple-latest-min.js.LICENSE.txt
|
||||
99c2825685d249c074abe7b59abaf197afce67ece7ad08ded6db67185e916dd2 build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.9.2 (2021-03-12)
|
||||
|
||||
* Docs
|
||||
* Add missing transaction type links (#1378)
|
||||
* Bug fixes
|
||||
* Deserialization and verification of payment paths (#1382) (#1347) (#1376)
|
||||
* Dependencies
|
||||
* Bump ripple-binary-codec to 1.1.2
|
||||
* Fix edge case when constructing a value from "0", which can occur when using rippled v1.7.0
|
||||
* Bump lodash, ripple-address-codec
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
a1fd24b65d81ea5dbc36d74da7a6317267a048bba084effff5380d47299c3c63 build/ripple-latest-min.js
|
||||
fc17a5572001d814ea6b81aa701fcb66882ec031c68afb769a8ea8b71c6529a6 build/ripple-latest-min.js.LICENSE.txt
|
||||
410f78105c4f23c13671ec94f963ef47179393bfcad65ff610bc838c5a3c6a65 build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.9.1 (2021-02-25)
|
||||
|
||||
* Docs
|
||||
* Add transaction specifications: (#1352)
|
||||
* Ticket Create
|
||||
* Account Delete
|
||||
* Deposit Preauth
|
||||
* Update link to subscribe page (#1354)
|
||||
* Bug fixes
|
||||
* Allow connectionTimeout option to be customized (#1355)
|
||||
* Dependencies
|
||||
* Bump ripple-keypairs to 1.0.3
|
||||
* Bump elliptic to 6.5.4 (this patches a potential security issue, although we do not believe that the issue affects ripple-lib: [details](https://github.com/ripple/ripple-keypairs/security/advisories/GHSA-w6x3-9ph2-7x54))
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
f59e0221a7218460eea59b0441a0ee2d2a14484dd473ed5373283852798516c7 build/ripple-latest-min.js
|
||||
fc17a5572001d814ea6b81aa701fcb66882ec031c68afb769a8ea8b71c6529a6 build/ripple-latest-min.js.LICENSE.txt
|
||||
731ed44cbff8db26bcf256e0e3f3ac3fe90a10b6c227701d67918a5d643c5b29 build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.9.0 (2020-12-07)
|
||||
|
||||
* New features
|
||||
* Support for tickets (TicketBatch amendment required - not yet activated on live/main network)
|
||||
* `prepareTicketCreate`
|
||||
* Types: Add LedgerClosedEvent and export more types
|
||||
* Docs
|
||||
* Improve descriptions of get-ledger response time fields
|
||||
* Applications
|
||||
* Add Bithomp explorer
|
||||
* Add example of reliable transaction submission
|
||||
* Node.js
|
||||
* Require Node.js version 10.13.0+
|
||||
* Internal
|
||||
* Update webpack, webpack-cli, mocha, nyc, ripple-binary-codec
|
||||
* Run prettier to format code
|
||||
|
||||
The SHA-256 checksums for the browser version of this release can be found below.
|
||||
```
|
||||
% shasum -a 256 build/*
|
||||
2d3ae057ad637df272f98cfe940ea9e1317588e5bbf4fee47c8b16d6e6e71d85 build/ripple-latest-min.js
|
||||
8cbbc7bb482f68bcc8d411bae2e42effdb14ddfa562fcbc329a373910b85cf8c build/ripple-latest.js
|
||||
```
|
||||
|
||||
## 1.8.2 (2020-10-23)
|
||||
|
||||
* Bug fixes
|
||||
|
||||
68
README.md
68
README.md
@@ -24,16 +24,80 @@ What is ripple-lib used for? The applications on the list linked above use `ripp
|
||||
|
||||
### Requirements
|
||||
|
||||
+ **[Node v10](https://nodejs.org/)** is recommended. Other versions may work but are not frequently tested.
|
||||
+ **[Node.js v14](https://nodejs.org/)** is recommended. Other versions may work but are not frequently tested.
|
||||
+ **[Yarn](https://yarnpkg.com/)** is recommended. `npm` may work but we use `yarn.lock`.
|
||||
|
||||
### Install
|
||||
## Getting Started
|
||||
|
||||
See also: [RippleAPI Beginners Guide](https://xrpl.org/get-started-with-rippleapi-for-javascript.html)
|
||||
|
||||
In an existing project (with `package.json`), install `ripple-lib`:
|
||||
```
|
||||
$ yarn add ripple-lib
|
||||
```
|
||||
|
||||
Then see the [documentation](#documentation).
|
||||
|
||||
### Using ripple-lib with React Native
|
||||
|
||||
If you want to use `ripple-lib` with React Native you will need to have some of the NodeJS modules available. To help with this you can use a module like [rn-nodeify](https://github.com/tradle/rn-nodeify).
|
||||
|
||||
1. Install dependencies (you can use `npm` as well):
|
||||
|
||||
```shell
|
||||
yarn add react-native-crypto
|
||||
yarn add ripple-lib
|
||||
# install peer deps
|
||||
yarn add react-native-randombytes
|
||||
# install latest rn-nodeify
|
||||
yarn add rn-nodeify@latest --dev
|
||||
```
|
||||
|
||||
2. After that, run the following command:
|
||||
|
||||
```shell
|
||||
# install node core shims and recursively hack package.json files
|
||||
# in ./node_modules to add/update the "browser"/"react-native" field with relevant mappings
|
||||
./node_modules/.bin/rn-nodeify --hack --install
|
||||
```
|
||||
|
||||
3. Enable `crypto`:
|
||||
|
||||
`rn-nodeify` will create a `shim.js` file in the project root directory.
|
||||
Open it and uncomment the line that requires the crypto module:
|
||||
|
||||
```javascript
|
||||
// If using the crypto shim, uncomment the following line to ensure
|
||||
// crypto is loaded first, so it can populate global.crypto
|
||||
require('crypto')
|
||||
```
|
||||
|
||||
4. Import `shim` in your project (it must be the first line):
|
||||
|
||||
```javascript
|
||||
import './shim'
|
||||
...
|
||||
```
|
||||
|
||||
### Using ripple-lib with Deno
|
||||
|
||||
Until official support for [Deno](https://deno.land) is added, you can use the following work-around to use `ripple-lib` with Deno:
|
||||
|
||||
```javascript
|
||||
import ripple from 'https://dev.jspm.io/npm:ripple-lib';
|
||||
|
||||
(async () => {
|
||||
const api = new (ripple as any).RippleAPI({ server: 'wss://s.altnet.rippletest.net:51233' });
|
||||
const address = 'rH8NxV12EuV...khfJ5uw9kT';
|
||||
|
||||
api.connect().then(() => {
|
||||
api.getBalances(address).then((balances: any) => {
|
||||
console.log(JSON.stringify(balances, null, 2));
|
||||
});
|
||||
});
|
||||
})();
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
+ [RippleAPI Beginners Guide](https://xrpl.org/get-started-with-rippleapi-for-javascript.html)
|
||||
|
||||
796
docs/index.md
796
docs/index.md
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
||||
Usage:
|
||||
babel-node balances.js
|
||||
babel-node cancelall.js (requires setting address and secret in source file first)
|
||||
babel-node payment.js (requires setting address and secret in source file first)
|
||||
babel-node ticket.js (requires setting address and secret in source file first)
|
||||
|
||||
31
docs/samples/ticket.js
Normal file
31
docs/samples/ticket.js
Normal file
@@ -0,0 +1,31 @@
|
||||
'use strict';
|
||||
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
|
||||
|
||||
const address = 'INSERT ADDRESS HERE';
|
||||
const secret = 'INSERT SECRET HERE';
|
||||
|
||||
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
|
||||
const instructions = {
|
||||
maxLedgerVersionOffset: 5
|
||||
};
|
||||
const numberOfTickets = 1;
|
||||
|
||||
function quit(message) {
|
||||
console.log(message);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function fail(message) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
api.connect().then(() => {
|
||||
console.log('Connected...');
|
||||
return api.prepareTicketCreate(address, numberOfTickets, instructions).then(prepared => {
|
||||
console.log('Ticket transaction prepared...');
|
||||
const {signedTransaction} = api.sign(prepared.txJSON, secret);
|
||||
console.log('Ticket transaction signed...');
|
||||
api.submit(signedTransaction).then(quit, fail);
|
||||
});
|
||||
}).catch(fail);
|
||||
@@ -22,7 +22,7 @@ An *X-address* encodes a hash of the account's public key, a tag, and a checksum
|
||||
|
||||
## Account Sequence Number
|
||||
|
||||
Every XRP Ledger account has a *sequence number* that is used to keep transactions in order. Every transaction must have a sequence number. A transaction can only be executed if it has the next sequence number in order, of the account sending it. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
|
||||
Every XRP Ledger account has a *sequence number* that is used to keep transactions in order. Every transaction must have a sequence or a ticketSequence number. A transaction can only be executed if it has the next sequence number in order, of the account sending it, or uses a previously generated ticketSequence number. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
|
||||
|
||||
## Currency
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<%- include('prepareCheckCreate.md.ejs') %>
|
||||
<%- include('prepareCheckCancel.md.ejs') %>
|
||||
<%- include('prepareCheckCash.md.ejs') %>
|
||||
<%- include('prepareTicketCreate.md.ejs') %>
|
||||
<%- include('sign.md.ejs') %>
|
||||
<%- include('combine.md.ejs') %>
|
||||
<%- include('submit.md.ejs') %>
|
||||
|
||||
34
docs/src/prepareTicketCreate.md.ejs
Normal file
34
docs/src/prepareTicketCreate.md.ejs
Normal file
@@ -0,0 +1,34 @@
|
||||
## prepareTicketCreate
|
||||
|
||||
`prepareTicketCreate(address: string, ticketCount: number, instructions: object): Promise<object>`
|
||||
|
||||
Prepare a ticket transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
|
||||
|
||||
Ticket functionality requires the [TicketBatch amendment](https://github.com/ripple/xrpl-dev-portal/issues/898). As of 2020-11-24, this amendment is not activated on the Mainnet, Testnet, or Devnet.
|
||||
|
||||
### Parameters
|
||||
|
||||
<%- renderSchema("input/prepare-ticket-create.json") %>
|
||||
|
||||
### Return Value
|
||||
|
||||
This method returns a promise that resolves with an object with the following structure:
|
||||
|
||||
<aside class="notice">
|
||||
All "prepare*" methods have the same return type.
|
||||
</aside>
|
||||
|
||||
<%- renderSchema("output/prepare.json") %>
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
|
||||
return api.prepareTicketCreate(address, 2).then(prepared => {
|
||||
/* ... */
|
||||
}).catch(error => {
|
||||
/* ... as with all prepare* methods, use a Promise catch block to handle errors ... */
|
||||
})
|
||||
```
|
||||
|
||||
<%- renderFixture("responses/prepare-ticket-create.json") %>
|
||||
@@ -14,7 +14,8 @@ When using rippled APIs:
|
||||
|
||||
## Listening to streams
|
||||
|
||||
The `rippled` server can push updates to your client when various events happen. Refer to [Subscriptions in the `rippled` API docs](https://developers.ripple.com/subscription-methods.html) for details.
|
||||
The `rippled` server can push updates to your client when various events happen.
|
||||
Refer to [Subscriptions in the `rippled` API docs](https://xrpl.org/subscribe.html) for details.
|
||||
|
||||
Note that the `streams` parameter for generic streams takes an array. For example, to subscribe to the `validations` stream, use `{ streams: [ 'validations' ] }`.
|
||||
|
||||
|
||||
@@ -1,145 +1,185 @@
|
||||
# Transaction Specifications
|
||||
|
||||
A *transaction specification* specifies what a transaction should do. Each [Transaction Type](#transaction-types) has its own type of specification.
|
||||
A *transaction specification* specifies what a transaction should do. Each [Transaction Type](#transaction-types) has its own type of specification, which corresponds to the [native XRP Ledger transaction types](https://xrpl.org/transaction-types.html).
|
||||
|
||||
## Payment
|
||||
## Account Delete
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
Delete your account and send the remaining XRP elsewhere. (Native transaction type: [AccountDelete](https://xrpl.org/accountdelete.html))
|
||||
|
||||
<%- renderSchema('specifications/payment.json') %>
|
||||
<%- renderSchema('specifications/account-delete.json') %>
|
||||
|
||||
### Example
|
||||
> **Note:** To prepare an Account Delete transaction, use [`prepareTransaction()`](#preparetransaction) with the [native transaction format](https://xrpl.org/accountdelete.html).
|
||||
|
||||
<%- renderFixture('requests/prepare-payment.json') %>
|
||||
## Check Cancel
|
||||
|
||||
## Trustline
|
||||
Cancel a Check that has not been redeemed. (Native transaction type: [CheckCancel](https://xrpl.org/checkcancel.html))
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
<%- renderSchema('specifications/check-cancel.json') %>
|
||||
|
||||
<%- renderSchema('specifications/trustline.json') %>
|
||||
#### Example
|
||||
|
||||
### Example
|
||||
<%- renderFixture('requests/prepare-check-cancel.json') %>
|
||||
|
||||
|
||||
## Check Cash
|
||||
|
||||
Redeem a Check for up to its stated value. (Native transaction type: [CheckCash](https://xrpl.org/checkcash.html))
|
||||
|
||||
<%- renderSchema('specifications/check-cash.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-check-cash-amount.json') %>
|
||||
|
||||
|
||||
## Check Create
|
||||
|
||||
Create a Check, a deferred payment that can be redeemed by the destination. (Native transaction type: [CheckCreate](https://xrpl.org/checkcreate.html))
|
||||
|
||||
<%- renderSchema('specifications/check-create.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-check-create.json') %>
|
||||
|
||||
|
||||
## Deposit Preauth
|
||||
|
||||
Preauthorize an sender to deposit money at an account using [Deposit Authorization](https://xrpl.org/depositauth.html). (Native transaction type: [DepositPreauth](https://xrpl.org/depositpreauth.html))
|
||||
|
||||
<%- renderSchema('specifications/deposit-preauth.json') %>
|
||||
|
||||
> **Note:** To prepare a Deposit Preauth transaction, use [`prepareTransaction()`](#preparetransaction) with the [native transaction format](https://xrpl.org/depositpreauth.html).
|
||||
|
||||
|
||||
## Escrow Cancellation
|
||||
|
||||
Cancel an Escrow that has passed its expiration. (Native transaction type: [EscrowCancel](https://xrpl.org/escrowcancel.html))
|
||||
|
||||
<%- renderSchema('specifications/escrow-cancellation.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-escrow-cancellation.json') %>
|
||||
|
||||
|
||||
## Escrow Creation
|
||||
|
||||
Create an Escrow that locks up XRP until a given time or condition is met. (Native transaction type: [EscrowCreate](https://xrpl.org/escrowcreate.html))
|
||||
|
||||
<%- renderSchema('specifications/escrow-creation.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-escrow-creation.json') %>
|
||||
|
||||
|
||||
## Escrow Execution
|
||||
|
||||
Deliver XRP from an Escrow after its conditions have been met. (Native transaction type: [EscrowFinish](https://xrpl.org/escrowfinish.html))
|
||||
|
||||
<%- renderSchema('specifications/escrow-execution.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-escrow-execution.json') %>
|
||||
|
||||
<%- renderFixture('requests/prepare-trustline.json') %>
|
||||
|
||||
## Order
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
Create and execute a limit order in the decentralized exchange. (Native transaction type: [OfferCreate](https://xrpl.org/offercreate.html))
|
||||
|
||||
<%- renderSchema('specifications/order.json') %>
|
||||
|
||||
The following invalid flag combination causes a `ValidationError`: `immediateOrCancel` and `fillOrKill`. These fields are mutually exclusive, and cannot both be set at the same time.
|
||||
|
||||
### Example
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-order.json') %>
|
||||
|
||||
|
||||
## Order Cancellation
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
Cancel an order in the decentralized exchange. (Native transaction type: [OfferCancel](https://xrpl.org/offercancel.html))
|
||||
|
||||
<%- renderSchema('specifications/order-cancellation.json') %>
|
||||
|
||||
### Example
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-order-cancellation.json') %>
|
||||
|
||||
## Settings
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
## Payment
|
||||
|
||||
<%- renderSchema('output/get-settings.json') %>
|
||||
Send value from one account to another. (Native transaction type: [Payment](https://xrpl.org/payment.html))
|
||||
|
||||
### Example
|
||||
<%- renderSchema('specifications/payment.json') %>
|
||||
|
||||
<%- renderFixture('requests/prepare-settings.json') %>
|
||||
#### Example
|
||||
|
||||
## Escrow Creation
|
||||
<%- renderFixture('requests/prepare-payment.json') %>
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/escrow-creation.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-escrow-creation.json') %>
|
||||
|
||||
## Escrow Cancellation
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/escrow-cancellation.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-escrow-cancellation.json') %>
|
||||
|
||||
## Escrow Execution
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/escrow-execution.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-escrow-execution.json') %>
|
||||
|
||||
## Check Create
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/check-create.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-check-create.json') %>
|
||||
|
||||
## Check Cancel
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/check-cancel.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-check-cancel.json') %>
|
||||
|
||||
## Check Cash
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/check-cash.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-check-cash-amount.json') %>
|
||||
|
||||
## Payment Channel Create
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/payment-channel-create.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-payment-channel-create.json') %>
|
||||
|
||||
## Payment Channel Fund
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
|
||||
<%- renderSchema('specifications/payment-channel-fund.json') %>
|
||||
|
||||
### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-payment-channel-fund.json') %>
|
||||
|
||||
## Payment Channel Claim
|
||||
|
||||
See [Transaction Types](#transaction-types) for a description.
|
||||
Redeem XRP from a Payment Channel. (Native transaction type: [PaymentChannelClaim](https://xrpl.org/paymentchannelclaim.html))
|
||||
|
||||
<%- renderSchema('specifications/payment-channel-claim.json') %>
|
||||
|
||||
### Example
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-payment-channel-claim.json') %>
|
||||
|
||||
|
||||
## Payment Channel Create
|
||||
|
||||
Create a Payment Channel with XRP set aside for asynchronous payments. (Native transaction type: [PaymentChannelCreate](https://xrpl.org/paymentchannelcreate.html))
|
||||
|
||||
<%- renderSchema('specifications/payment-channel-create.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-payment-channel-create.json') %>
|
||||
|
||||
|
||||
## Payment Channel Fund
|
||||
|
||||
Add XRP to a Payment Channel. (Native transaction type: [PaymentChannelFund](https://xrpl.org/paymentchannelfund.html))
|
||||
|
||||
<%- renderSchema('specifications/payment-channel-fund.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-payment-channel-fund.json') %>
|
||||
|
||||
|
||||
|
||||
## Settings
|
||||
|
||||
Change account settings. (Native transaction types: [AccountSet](https://xrpl.org/accountset.html), [SetRegularKey](https://xrpl.org/setregularkey.html), [SignerListSet](https://xrpl.org/signerlistset.html))
|
||||
|
||||
<%- renderSchema('output/get-settings.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-settings.json') %>
|
||||
|
||||
|
||||
## Ticket Create
|
||||
|
||||
Set aside account Sequence numbers as Tickets to be used by later transactions.
|
||||
|
||||
> **Caution:** As of 2021-01-22, Tickets are not yet available on the XRP Ledger.
|
||||
|
||||
> **Note:** To prepare a Ticket Create transaction, use [`prepareTransaction()`](#preparetransaction) with the native transaction format. <!-- Future link: https://xrpl.org/ticketcreate.html -->
|
||||
|
||||
|
||||
## Trustline
|
||||
|
||||
Create or modify a trust line between two accounts, for an issued currency. (Native transaction type: [TrustSet](https://xrpl.org/trustset.html))
|
||||
|
||||
<%- renderSchema('specifications/trustline.json') %>
|
||||
|
||||
#### Example
|
||||
|
||||
<%- renderFixture('requests/prepare-trustline.json') %>
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Static Methods
|
||||
|
||||
ripple-lib features a number of static methods that you can access directly on the `RippleAPI` object. The most commonly-used one is `computeBinaryTransactionHash`, described below. For the full list, see the [XRP Ledger Hashes README](https://github.com/ripple/ripple-lib/blob/develop/src/common/hashes/README.md).
|
||||
You can access static methods directly on the `RippleAPI` class. For example, `RippleAPI.computeBinaryTransactionHash(...)`.
|
||||
|
||||
A few of the most commonly-used methods are documented below.
|
||||
|
||||
For a full list, refer to these docs:
|
||||
|
||||
- [XRP Ledger Hashes](https://github.com/ripple/ripple-lib/blob/develop/src/common/hashes/README.md)
|
||||
- [ripple-address-codec API](https://github.com/ripple/ripple-address-codec/blob/master/README.md#api)
|
||||
|
||||
## computeBinaryTransactionHash
|
||||
|
||||
@@ -33,3 +40,61 @@ Transaction hash: 80C5E11E1A21A626759D6CB944B33DBAAC66BD704A289C86E330B847904F5C
|
||||
```
|
||||
|
||||
[RunKit Example: computeBinaryTransactionHash](https://runkit.com/intelliot/computebinarytransactionhash-example)
|
||||
|
||||
## classicAddressToXAddress
|
||||
|
||||
`classicAddressToXAddress(classicAddress: string, tag: number | false[, test: boolean]): string`
|
||||
|
||||
Convert a classic address and tag to an X-address.
|
||||
|
||||
If `test` is `true`, the address with start with `T` and readers of the address will know that the address is intended for use on a test network.
|
||||
|
||||
### Example: Encode an X-address with tag 4294967295
|
||||
|
||||
```javascript
|
||||
const RippleAPI = require('ripple-lib').RippleAPI
|
||||
const xAddress = RippleAPI.classicAddressToXAddress('rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf', 4294967295)
|
||||
console.log(xAddress)
|
||||
```
|
||||
|
||||
```
|
||||
XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi
|
||||
```
|
||||
|
||||
### Example: Encode a test address for use with Testnet or Devnet
|
||||
|
||||
```javascript
|
||||
const RippleAPI = require('ripple-lib').RippleAPI
|
||||
const address = RippleAPI.classicAddressToXAddress('r3SVzk8ApofDJuVBPKdmbbLjWGCCXpBQ2g', 123, true)
|
||||
console.log(address)
|
||||
```
|
||||
|
||||
```
|
||||
T7oKJ3q7s94kDH6tpkBowhetT1JKfcfdSCmAXbS75iATyLD
|
||||
```
|
||||
|
||||
## xAddressToClassicAddress
|
||||
|
||||
`xAddressToClassicAddress(xAddress: string): {classicAddress: string, tag: number | false, test: boolean}`
|
||||
|
||||
Convert an X-address to a classic address and tag.
|
||||
|
||||
Since `0` is a valid tag, use `if (tag !== false)` to you want to check for a tag.
|
||||
|
||||
If the address is intended for use on a test network, `test === true`. Otherwise, `test === false`.
|
||||
|
||||
### Example
|
||||
|
||||
```javascript
|
||||
const RippleAPI = require('ripple-lib').RippleAPI
|
||||
const address = RippleAPI.xAddressToClassicAddress('XVLhHMPHU98es4dbozjVtdWzVrDjtV18pX8yuPT7y4xaEHi')
|
||||
console.log(address)
|
||||
```
|
||||
|
||||
```
|
||||
{
|
||||
classicAddress: 'rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf',
|
||||
tag: 4294967295,
|
||||
test: false
|
||||
}
|
||||
```
|
||||
|
||||
@@ -9,7 +9,7 @@ Type | Description
|
||||
[payment](#payment) | A `payment` transaction represents a transfer of value from one account to another. Depending on the [path](https://ripple.com/build/paths/) taken, additional exchanges of value may occur atomically to facilitate the payment.
|
||||
[order](#order) | An `order` transaction creates a limit order. It defines an intent to exchange currencies, and creates an order in the XRP Ledger's order book if not completely fulfilled when placed. Orders can be partially fulfilled.
|
||||
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the XRP Ledger's order book.
|
||||
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
|
||||
[trustline](#trustline) | A `trustline` transaction creates or modifies a trust line between two accounts.
|
||||
[settings](#settings) | A `settings` transaction modifies the settings of an account in the XRP Ledger.
|
||||
[escrowCreation](#escrow-creation) | An `escrowCreation` transaction creates an escrow on the ledger, which locks XRP until a cryptographic condition is met or it expires. It is like an escrow service where the XRP Ledger acts as the escrow agent.
|
||||
[escrowCancellation](#escrow-cancellation) | An `escrowCancellation` transaction unlocks the funds in an escrow and sends them back to the creator of the escrow, but it will only work after the escrow expires.
|
||||
@@ -20,6 +20,7 @@ Type | Description
|
||||
[paymentChannelCreate](#payment-channel-create) | A `paymentChannelCreate` transaction opens a payment channel between two addresses with XRP set aside for asynchronous payments.
|
||||
[paymentChannelFund](#payment-channel-fund) | A `paymentChannelFund` transaction adds XRP to a payment channel and optionally sets a new expiration for the channel.
|
||||
[paymentChannelClaim](#payment-channel-claim) | A `paymentChannelClaim` transaction withdraws XRP from a channel and optionally requests to close it.
|
||||
[ticketCreate](#ticket-create) | A successful `ticketCreate` transaction adds a Ticket in the directory of the owning account.
|
||||
|
||||
## Transaction Flow
|
||||
|
||||
@@ -37,6 +38,7 @@ Executing a transaction with `RippleAPI` requires the following four steps:
|
||||
* [prepareCheckCreate](#preparecheckcreate)
|
||||
* [prepareCheckCancel](#preparecheckcancel)
|
||||
* [prepareCheckCash](#preparecheckcash)
|
||||
* [prepareTicketCreate](#prepareticketcreate)
|
||||
2. [Sign](#sign) - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place. For multisignature transactions, the `signedTransaction` fields returned by `sign` must be collected and passed to the [combine](#combine) method.
|
||||
3. [Submit](#submit) - Submit the transaction to the connected server.
|
||||
4. Verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted.
|
||||
|
||||
41
package.json
41
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "1.8.2",
|
||||
"version": "1.9.6",
|
||||
"license": "ISC",
|
||||
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
|
||||
"files": [
|
||||
@@ -28,30 +28,39 @@
|
||||
"lodash": "^4.17.4",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"ripple-address-codec": "^4.1.1",
|
||||
"ripple-binary-codec": "^0.2.7",
|
||||
"ripple-keypairs": "^1.0.0",
|
||||
"ripple-binary-codec": "^1.1.2",
|
||||
"ripple-keypairs": "^1.0.3",
|
||||
"ripple-lib-transactionparser": "0.8.2",
|
||||
"ws": "^7.2.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"elliptic": "^6.5.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^7.0.1",
|
||||
"@types/node": "^14.0.1",
|
||||
"@types/mocha": "^8.2.1",
|
||||
"@types/node": "^15.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.3.3",
|
||||
"@typescript-eslint/parser": "^2.27.0",
|
||||
"assert": "^2.0.0",
|
||||
"assert-diff": "^3.0.0",
|
||||
"doctoc": "^1.4.0",
|
||||
"buffer": "^6.0.2",
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"doctoc": "^2.0.0",
|
||||
"ejs": "^3.0.1",
|
||||
"eslint": "^6.5.1",
|
||||
"eventemitter2": "^6.0.0",
|
||||
"json-schema-to-markdown-table": "^0.4.0",
|
||||
"mocha": "^7.1.1",
|
||||
"nyc": "^15.0.0",
|
||||
"mocha": "^8",
|
||||
"nyc": "^15",
|
||||
"prettier": "^2.0.5",
|
||||
"ts-node": "^8.4.1",
|
||||
"process": "^0.11.10",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^3.7.5",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-bundle-analyzer": "^3.6.0",
|
||||
"webpack-cli": "^3.3.9"
|
||||
"url": "^0.11.0",
|
||||
"webpack": "^5.6.0",
|
||||
"webpack-bundle-analyzer": "^4.1.0",
|
||||
"webpack-cli": "^4.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build:schemas": "mkdir -p dist/npm/common && cp -r src/common/schemas dist/npm/common/",
|
||||
@@ -64,12 +73,14 @@
|
||||
"doctoc": "doctoc docs/index.md --title '# RippleAPI Reference' --github --maxlevel 2",
|
||||
"docgen": "node --harmony scripts/build_docs.js",
|
||||
"prepublish": "yarn clean && yarn build",
|
||||
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --exit",
|
||||
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --config=test/.mocharc.json --exit",
|
||||
"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"
|
||||
"compile:snippets": "tsc -p snippets/tsconfig.json",
|
||||
"start:snippet": "npm run compile:snippets && node ./snippets/dist/start.js",
|
||||
"inspect:snippet": "npm run compile:snippets && node inspect ./snippets/dist/start.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -77,7 +88,7 @@
|
||||
},
|
||||
"readmeFilename": "README.md",
|
||||
"engines": {
|
||||
"node": ">=8",
|
||||
"node": ">=10.13.0",
|
||||
"yarn": "^1.15.2"
|
||||
}
|
||||
}
|
||||
|
||||
9
snippets/src/decoder.ts
Normal file
9
snippets/src/decoder.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import * as codec from 'ripple-binary-codec'
|
||||
|
||||
const original = codec.decode('12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402200A693FB5CA6B21250EBDFD8CFF526EE0DF7C9E4E31EB0660692E75E6A93BF5F802203CC39463DDA21386898CA31E18AD1A6828647D65741DD637BAD71BC83E29DB9481145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900')
|
||||
|
||||
const test = codec.decode('12000022800200002400000017201B008694F261EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100D8B57E8E06EAE27B1343AF8CAD3F501E18260CCF8BCED08066074106F0F191A3022058FEA6CE9E7FA69D1244C3A70F18983CC2DAF0B10CBB86A6677CF2A5D2B8A68081145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900')
|
||||
|
||||
console.log('original:', JSON.stringify(original))
|
||||
|
||||
console.log('test:', JSON.stringify(test))
|
||||
47
snippets/src/paths.ts
Normal file
47
snippets/src/paths.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import {RippleAPI} from '../../dist/npm'
|
||||
|
||||
const api = new RippleAPI({
|
||||
// server: 'wss://s.altnet.rippletest.net:51233'
|
||||
// server: 'ws://35.158.96.209:51233'
|
||||
server: 'ws://34.210.87.206:51233'
|
||||
})
|
||||
|
||||
sign()
|
||||
|
||||
async function sign() {
|
||||
await api.connect()
|
||||
const pathfind: any = {
|
||||
source: {
|
||||
address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
amount: {
|
||||
currency: 'drops',
|
||||
value: '100'
|
||||
}
|
||||
},
|
||||
destination: {
|
||||
address: 'rKT4JX4cCof6LcDYRz8o3rGRu7qxzZ2Zwj',
|
||||
amount: {
|
||||
currency: 'USD',
|
||||
counterparty: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await api.getPaths(pathfind).then(async (data) => {
|
||||
console.log('paths:', JSON.stringify(data))
|
||||
const fakeSecret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
||||
|
||||
pathfind.paths = data[0].paths
|
||||
pathfind.destination = data[0].destination
|
||||
await api.preparePayment('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', pathfind).then(ret => {
|
||||
const signed = api.sign(ret.txJSON, fakeSecret)
|
||||
console.log('signed:', signed)
|
||||
}).catch(err => {
|
||||
console.log('ERR 1:', JSON.stringify(err))
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log('ERR 2:', err)
|
||||
})
|
||||
|
||||
api.disconnect()
|
||||
}
|
||||
200
snippets/src/reliableTransactionSubmission.ts
Normal file
200
snippets/src/reliableTransactionSubmission.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
import {
|
||||
RippleAPI,
|
||||
AccountInfoResponse,
|
||||
LedgerClosedEvent
|
||||
} from '../../dist/npm'
|
||||
import https = require('https')
|
||||
|
||||
/**
|
||||
* When implementing Reliable Transaction Submission, there are many potential solutions, each with different trade-offs. The main decision points are:
|
||||
* 1) Transaction preparation:
|
||||
* - How do we decide which account sequence and LastLedgerSequence numbers to use?
|
||||
* (To prevent unintentional duplicate transactions, an {account, account_sequence} pair can be used as a transaction's idempotency key)
|
||||
* - How do we decide how much to pay for the transaction fee? (If our transactions have been failing due to low fee, we should consider increasing this value)
|
||||
* 2) Transaction status retrieval. Options include:
|
||||
* - Poll for transaction status:
|
||||
* - On a regular interval (e.g. every 3-5 seconds), or
|
||||
* - When a new validated ledger is detected
|
||||
* + (To accommodate an edge case in transaction retrieval, check the sending account's Sequence number to confirm that it has the expected value;
|
||||
* alternatively, wait until a few additional ledgers have been validated before deciding that a transaction has definitively not been included in a validated ledger)
|
||||
* - Listen for transaction status: scan all validated transactions to see if our transactions are among them
|
||||
* 3) What do we do when a transaction fails? It is possible to implement retry logic, but caution is advised. Note that there are a few ways for a transaction to fail:
|
||||
* A) `tec`: The transaction was included in a ledger but only claimed the transaction fee
|
||||
* B) `tesSUCCESS` but unexpected result: The transaction was successful but did not have the expected result. This generally does not occur for XRP-to-XRP payments
|
||||
* C) The transaction was not, and never will be, included in a validated ledger [3C]
|
||||
*
|
||||
* References:
|
||||
* - https://xrpl.org/reliable-transaction-submission.html
|
||||
* - https://xrpl.org/send-xrp.html
|
||||
* - https://xrpl.org/look-up-transaction-results.html
|
||||
* - https://xrpl.org/get-started-with-rippleapi-for-javascript.html
|
||||
* - https://xrpl.org/monitor-incoming-payments-with-websocket.html
|
||||
*
|
||||
* For the implementation in this example, we have made the following decisions:
|
||||
* 1) The script will choose the account sequence and LastLedgerSequence numbers automatically. We allow ripple-lib to choose the fee.
|
||||
* Payments are defined upfront, and idempotency is not needed. If the script is run a second time, duplicate payments will result.
|
||||
* 2) We will listen for notification that a new validated ledger has been found, and poll for transaction status at that time.
|
||||
* Futhermore, as a precaution, we will wait until the server is 3 ledgers past the transaction's LastLedgerSequence
|
||||
* (with the transaction nowhere to be seen) before deciding that it has definitively failed per [3C]
|
||||
* 3) Transactions will not be automatically retried. Transactions are limited to XRP-to-XRP payments and cannot "succeed" in an unexpected way.
|
||||
*/
|
||||
reliableTransactionSubmissionExample()
|
||||
|
||||
async function reliableTransactionSubmissionExample() {
|
||||
/**
|
||||
* Array of payments to execute.
|
||||
*
|
||||
* For brevity, these are XRP-to-XRP payments, taking a source, destination, and an amount in drops.
|
||||
*
|
||||
* The script will attempt to make all of these payments as quickly as possible, and report the final status of each. Transactions that fail are NOT retried.
|
||||
*/
|
||||
const payments = []
|
||||
|
||||
const sourceAccount = (await generateTestnetAccount()).account
|
||||
console.log(`Generated new Testnet account: ${sourceAccount.classicAddress}/${sourceAccount.secret}`)
|
||||
// Send amounts from 1 drop to 10 drops
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
payments.push({
|
||||
source: sourceAccount,
|
||||
destination: 'rhsoCozhUxwcyQgzFi1FVRoMVQgk7cZd4L', // Random Testnet destination
|
||||
amount_drops: i.toString(),
|
||||
})
|
||||
}
|
||||
const results = await performPayments(payments)
|
||||
console.log(JSON.stringify(results, null, 2))
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
async function performPayments(payments) {
|
||||
const finalResults = []
|
||||
const txFinalizedPromises = []
|
||||
const api = new RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
|
||||
await api.connect()
|
||||
|
||||
for (let i = 0; i < payments.length; i++) {
|
||||
const payment = payments[i]
|
||||
const account_info: AccountInfoResponse = await api.request('account_info', {
|
||||
account: payment.source.classicAddress,
|
||||
ledger_index: 'current'})
|
||||
const sequence = account_info.account_data.Sequence
|
||||
const preparedPayment = await api.preparePayment(payment.source.classicAddress, {
|
||||
source: {
|
||||
address: payment.source.classicAddress,
|
||||
amount: {
|
||||
value: payment.amount_drops,
|
||||
currency: 'drops'
|
||||
}
|
||||
},
|
||||
destination: {
|
||||
address: payment.destination,
|
||||
minAmount: {
|
||||
value: payment.amount_drops,
|
||||
currency: 'drops'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
sequence
|
||||
})
|
||||
const signed = api.sign(preparedPayment.txJSON, payment.source.secret)
|
||||
finalResults.push({
|
||||
id: signed.id
|
||||
})
|
||||
const result = await api.submit(signed.signedTransaction)
|
||||
|
||||
// Most of the time we'll get 'tesSUCCESS' or (after many submissions) 'terQUEUED'
|
||||
console.log(`tx ${i} - tentative: ${result.resultCode}`)
|
||||
|
||||
const txFinalizedPromise = new Promise((resolve) => {
|
||||
const ledgerClosedCallback = async (event: LedgerClosedEvent) => {
|
||||
let status
|
||||
try {
|
||||
status = await api.getTransaction(signed.id)
|
||||
} catch (e) {
|
||||
// Typical error when the tx hasn't been validated yet:
|
||||
if (e.name !== 'MissingLedgerHistoryError') {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
if (event.ledger_index > preparedPayment.instructions.maxLedgerVersion + 3) {
|
||||
// Assumptions:
|
||||
// - We are still connected to the same rippled server
|
||||
// - No ledger gaps occurred
|
||||
// - All ledgers between the time we submitted the tx and now have been checked for the tx
|
||||
status = {
|
||||
finalResult: 'Transaction was not, and never will be, included in a validated ledger'
|
||||
}
|
||||
} else {
|
||||
// Check again later:
|
||||
api.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (let j = 0; j < finalResults.length; j++) {
|
||||
if (finalResults[j].id === signed.id) {
|
||||
finalResults[j].result = status.address ? {
|
||||
source: status.address,
|
||||
destination: status.specification.destination.address,
|
||||
deliveredAmount: status.outcome.deliveredAmount,
|
||||
result: status.outcome.result,
|
||||
timestamp: status.outcome.timestamp,
|
||||
ledgerVersion: status.outcome.ledgerVersion
|
||||
} : status
|
||||
process.stdout.write('.')
|
||||
return resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
api.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
})
|
||||
txFinalizedPromises.push(txFinalizedPromise)
|
||||
}
|
||||
await Promise.all(txFinalizedPromises)
|
||||
return finalResults
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new Testnet account by requesting one from the faucet
|
||||
*/
|
||||
async function generateTestnetAccount(): Promise<{
|
||||
account: {
|
||||
xAddress: string,
|
||||
classicAddress, string,
|
||||
secret: string
|
||||
},
|
||||
balance: number
|
||||
}> {
|
||||
const options = {
|
||||
hostname: 'faucet.altnet.rippletest.net',
|
||||
port: 443,
|
||||
path: '/accounts',
|
||||
method: 'POST'
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = https.request(options, response => {
|
||||
const chunks = []
|
||||
response.on('data', d => {
|
||||
chunks.push(d)
|
||||
})
|
||||
response.on('end', () => {
|
||||
const body = Buffer.concat(chunks).toString()
|
||||
|
||||
// "application/json; charset=utf-8"
|
||||
if (response.headers['content-type'].startsWith('application/json')) {
|
||||
resolve(JSON.parse(body))
|
||||
} else {
|
||||
reject({
|
||||
statusCode: response.statusCode,
|
||||
contentType: response.headers['content-type'],
|
||||
body
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
request.on('error', error => {
|
||||
console.error(error)
|
||||
reject(error)
|
||||
})
|
||||
request.end()
|
||||
})
|
||||
}
|
||||
54
src/api.ts
54
src/api.ts
@@ -43,6 +43,7 @@ import prepareCheckCreate from './transaction/check-create'
|
||||
import prepareCheckCancel from './transaction/check-cancel'
|
||||
import prepareCheckCash from './transaction/check-cash'
|
||||
import prepareSettings from './transaction/settings'
|
||||
import prepareTicketCreate from './transaction/ticket'
|
||||
import sign from './transaction/sign'
|
||||
import combine from './transaction/combine'
|
||||
import submit from './transaction/submit'
|
||||
@@ -88,7 +89,7 @@ import {getServerInfo, getFee} from './common/serverinfo'
|
||||
import {clamp, renameCounterpartyToIssuer} from './ledger/utils'
|
||||
import {TransactionJSON, Instructions, Prepare} from './transaction/types'
|
||||
import {ConnectionUserOptions} from './common/connection'
|
||||
import {isValidXAddress, isValidClassicAddress} from 'ripple-address-codec'
|
||||
import {classicAddressToXAddress, xAddressToClassicAddress, isValidXAddress, isValidClassicAddress, encodeSeed, decodeSeed, encodeAccountID, decodeAccountID, encodeNodePublic, decodeNodePublic, encodeAccountPublic, decodeAccountPublic, encodeXAddress, decodeXAddress} from 'ripple-address-codec'
|
||||
import {
|
||||
computeBinaryTransactionHash,
|
||||
computeTransactionHash,
|
||||
@@ -155,7 +156,7 @@ class RippleAPI extends EventEmitter {
|
||||
const serverURL = options.server
|
||||
if (serverURL !== undefined) {
|
||||
this.connection = new Connection(serverURL, options)
|
||||
this.connection.on('ledgerClosed', message => {
|
||||
this.connection.on('ledgerClosed', (message) => {
|
||||
this.emit('ledger', formatLedgerClose(message))
|
||||
})
|
||||
this.connection.on('error', (errorCode, errorMessage, data) => {
|
||||
@@ -164,7 +165,7 @@ class RippleAPI extends EventEmitter {
|
||||
this.connection.on('connected', () => {
|
||||
this.emit('connected')
|
||||
})
|
||||
this.connection.on('disconnected', code => {
|
||||
this.connection.on('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
|
||||
@@ -395,6 +396,7 @@ class RippleAPI extends EventEmitter {
|
||||
prepareCheckCreate = prepareCheckCreate
|
||||
prepareCheckCash = prepareCheckCash
|
||||
prepareCheckCancel = prepareCheckCancel
|
||||
prepareTicketCreate = prepareTicketCreate
|
||||
prepareSettings = prepareSettings
|
||||
sign = sign
|
||||
combine = combine
|
||||
@@ -413,8 +415,23 @@ class RippleAPI extends EventEmitter {
|
||||
// RippleAPI.deriveClassicAddress (static) is a new name for api.deriveAddress
|
||||
static deriveClassicAddress = deriveAddress
|
||||
|
||||
static isValidXAddress = isValidXAddress
|
||||
static isValidClassicAddress = isValidClassicAddress
|
||||
/**
|
||||
* Static methods to expose ripple-address-codec methods
|
||||
*/
|
||||
static classicAddressToXAddress = classicAddressToXAddress
|
||||
static xAddressToClassicAddress = xAddressToClassicAddress
|
||||
static isValidXAddress = isValidXAddress
|
||||
static isValidClassicAddress = isValidClassicAddress
|
||||
static encodeSeed = encodeSeed
|
||||
static decodeSeed = decodeSeed
|
||||
static encodeAccountID = encodeAccountID
|
||||
static decodeAccountID = decodeAccountID
|
||||
static encodeNodePublic = encodeNodePublic
|
||||
static decodeNodePublic = decodeNodePublic
|
||||
static encodeAccountPublic = encodeAccountPublic
|
||||
static decodeAccountPublic = decodeAccountPublic
|
||||
static encodeXAddress = encodeXAddress
|
||||
static decodeXAddress = decodeXAddress
|
||||
|
||||
/**
|
||||
* Static methods that replace functionality from the now-deprecated ripple-hashes library
|
||||
@@ -451,4 +468,29 @@ class RippleAPI extends EventEmitter {
|
||||
isValidSecret = schemaValidator.isValidSecret
|
||||
}
|
||||
|
||||
export {RippleAPI}
|
||||
export {
|
||||
RippleAPI
|
||||
}
|
||||
|
||||
export type {
|
||||
AccountObjectsRequest,
|
||||
AccountObjectsResponse,
|
||||
AccountOffersRequest,
|
||||
AccountOffersResponse,
|
||||
AccountInfoRequest,
|
||||
AccountInfoResponse,
|
||||
AccountLinesRequest,
|
||||
AccountLinesResponse,
|
||||
BookOffersRequest,
|
||||
BookOffersResponse,
|
||||
GatewayBalancesRequest,
|
||||
GatewayBalancesResponse,
|
||||
LedgerRequest,
|
||||
LedgerResponse,
|
||||
LedgerDataRequest,
|
||||
LedgerDataResponse,
|
||||
LedgerEntryRequest,
|
||||
LedgerEntryResponse,
|
||||
ServerInfoRequest,
|
||||
ServerInfoResponse
|
||||
}
|
||||
|
||||
@@ -9,38 +9,38 @@ class RippleAPIBroadcast extends RippleAPI {
|
||||
super(options)
|
||||
|
||||
const apis: RippleAPI[] = servers.map(
|
||||
server => new RippleAPI(_.assign({}, options, {server}))
|
||||
(server) => new RippleAPI(_.assign({}, options, {server}))
|
||||
)
|
||||
|
||||
// exposed for testing
|
||||
this._apis = apis
|
||||
|
||||
this.getMethodNames().forEach(name => {
|
||||
this[name] = function() {
|
||||
this.getMethodNames().forEach((name) => {
|
||||
this[name] = function () {
|
||||
// eslint-disable-line no-loop-func
|
||||
return Promise.race(apis.map(api => api[name](...arguments)))
|
||||
return Promise.race(apis.map((api) => api[name](...arguments)))
|
||||
}
|
||||
})
|
||||
|
||||
// connection methods must be overridden to apply to all api instances
|
||||
this.connect = async function() {
|
||||
await Promise.all(apis.map(api => api.connect()))
|
||||
this.connect = async function () {
|
||||
await Promise.all(apis.map((api) => api.connect()))
|
||||
}
|
||||
this.disconnect = async function() {
|
||||
await Promise.all(apis.map(api => api.disconnect()))
|
||||
this.disconnect = async function () {
|
||||
await Promise.all(apis.map((api) => api.disconnect()))
|
||||
}
|
||||
this.isConnected = function() {
|
||||
return apis.map(api => api.isConnected()).every(Boolean)
|
||||
this.isConnected = function () {
|
||||
return apis.map((api) => api.isConnected()).every(Boolean)
|
||||
}
|
||||
|
||||
// synchronous methods are all passed directly to the first api instance
|
||||
const defaultAPI = apis[0]
|
||||
const syncMethods = ['sign', 'generateAddress', 'computeLedgerHash']
|
||||
syncMethods.forEach(name => {
|
||||
syncMethods.forEach((name) => {
|
||||
this[name] = defaultAPI[name].bind(defaultAPI)
|
||||
})
|
||||
|
||||
apis.forEach(api => {
|
||||
apis.forEach((api) => {
|
||||
api.on('ledger', this.onLedgerEvent.bind(this))
|
||||
api.on('error', (errorCode, errorMessage, data) =>
|
||||
this.emit('error', errorCode, errorMessage, data)
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface ConnectionOptions {
|
||||
key?: string
|
||||
passphrase?: string
|
||||
certificate?: string
|
||||
timeout: number
|
||||
timeout: number // request timeout
|
||||
connectionTimeout: number
|
||||
}
|
||||
|
||||
@@ -119,8 +119,8 @@ function createWebSocket(url: string, config: ConnectionOptions): WebSocket {
|
||||
* ws.send(), but promisified.
|
||||
*/
|
||||
function websocketSendAsync(ws: WebSocket, message: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ws.send(message, undefined, error => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
ws.send(message, undefined, (error) => {
|
||||
if (error) {
|
||||
reject(new DisconnectedError(error.message, error))
|
||||
} else {
|
||||
@@ -326,7 +326,7 @@ export class Connection extends EventEmitter {
|
||||
this._url = url
|
||||
this._config = {
|
||||
timeout: 20 * 1000,
|
||||
connectionTimeout: 2 * 1000,
|
||||
connectionTimeout: 5 * 1000,
|
||||
...options
|
||||
}
|
||||
if (typeof options.trace === 'function') {
|
||||
@@ -390,7 +390,7 @@ export class Connection extends EventEmitter {
|
||||
*/
|
||||
private _heartbeat = () => {
|
||||
return this.request({command: 'ping'}).catch(() => {
|
||||
this.reconnect().catch(error => {
|
||||
return this.reconnect().catch((error) => {
|
||||
this.emit('error', 'reconnect', error.message, error)
|
||||
})
|
||||
})
|
||||
@@ -488,7 +488,8 @@ export class Connection extends EventEmitter {
|
||||
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.`
|
||||
`If your internet connection is working, the rippled server may be blocked or inaccessible. ` +
|
||||
`You can also try setting the 'connectionTimeout' option in the RippleAPI constructor.`
|
||||
)
|
||||
)
|
||||
}, this._config.connectionTimeout)
|
||||
@@ -504,11 +505,11 @@ export class Connection extends EventEmitter {
|
||||
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._ws.on('error', (error) =>
|
||||
this.emit('error', 'websocket', error.message, error)
|
||||
)
|
||||
// Handle a closed connection: reconnect if it was unexpected
|
||||
this._ws.once('close', code => {
|
||||
this._ws.once('close', (code) => {
|
||||
this._clearHeartbeatInterval()
|
||||
this._requestManager.rejectAll(
|
||||
new DisconnectedError('websocket was closed')
|
||||
@@ -524,7 +525,7 @@ export class Connection extends EventEmitter {
|
||||
// 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.reconnect().catch((error) => {
|
||||
this.emit('error', 'reconnect', error.message, error)
|
||||
})
|
||||
}, retryTimeout)
|
||||
@@ -558,8 +559,8 @@ export class Connection extends EventEmitter {
|
||||
if (this._state === WebSocket.CLOSED || !this._ws) {
|
||||
return Promise.resolve(undefined)
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
this._ws.once('close', code => resolve(code))
|
||||
return new Promise((resolve) => {
|
||||
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.
|
||||
@@ -635,7 +636,7 @@ export class Connection extends EventEmitter {
|
||||
timeout || this._config.timeout
|
||||
)
|
||||
this._trace('send', message)
|
||||
websocketSendAsync(this._ws, message).catch(error => {
|
||||
websocketSendAsync(this._ws, message).catch((error) => {
|
||||
this._requestManager.reject(id, error)
|
||||
})
|
||||
|
||||
|
||||
@@ -36,10 +36,7 @@ const addressToHex = (address: string): string => {
|
||||
|
||||
const currencyToHex = (currency: string): string => {
|
||||
if (currency.length === 3) {
|
||||
const bytes = new Array(20 + 1)
|
||||
.join('0')
|
||||
.split('')
|
||||
.map(parseFloat)
|
||||
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
|
||||
@@ -163,7 +160,7 @@ export const computeTrustlineHash = (
|
||||
export const computeTransactionTreeHash = (transactions: any[]): string => {
|
||||
const shamap = new SHAMap()
|
||||
|
||||
transactions.forEach(txJSON => {
|
||||
transactions.forEach((txJSON) => {
|
||||
const txBlobHex = encode(txJSON)
|
||||
const metaHex = encode(txJSON.metaData)
|
||||
const txHash = computeBinaryTransactionHash(txBlobHex)
|
||||
@@ -177,7 +174,7 @@ export const computeTransactionTreeHash = (transactions: any[]): string => {
|
||||
export const computeStateTreeHash = (entries: any[]): string => {
|
||||
const shamap = new SHAMap()
|
||||
|
||||
entries.forEach(ledgerEntry => {
|
||||
entries.forEach((ledgerEntry) => {
|
||||
const data = encode(ledgerEntry)
|
||||
shamap.addItem(ledgerEntry.index, data, NodeType.ACCOUNT_STATE)
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@ type Interval = [number, number]
|
||||
|
||||
function mergeIntervals(intervals: Interval[]): Interval[] {
|
||||
const stack: Interval[] = [[-Infinity, -Infinity]]
|
||||
_.sortBy(intervals, x => x[0]).forEach(interval => {
|
||||
_.sortBy(intervals, (x) => x[0]).forEach((interval) => {
|
||||
const lastInterval: Interval = stack.pop()!
|
||||
if (interval[0] <= lastInterval[1] + 1) {
|
||||
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])])
|
||||
@@ -30,7 +30,7 @@ class RangeSet {
|
||||
|
||||
serialize() {
|
||||
return this.ranges
|
||||
.map(range => range[0].toString() + '-' + range[1].toString())
|
||||
.map((range) => range[0].toString() + '-' + range[1].toString())
|
||||
.join(',')
|
||||
}
|
||||
|
||||
@@ -45,14 +45,14 @@ class RangeSet {
|
||||
|
||||
parseAndAddRanges(rangesString: string) {
|
||||
const rangeStrings = rangesString.split(',')
|
||||
_.forEach(rangeStrings, rangeString => {
|
||||
_.forEach(rangeStrings, (rangeString) => {
|
||||
const range = rangeString.split('-').map(Number)
|
||||
this.addRange(range[0], range.length === 1 ? range[0] : range[1])
|
||||
})
|
||||
}
|
||||
|
||||
containsRange(start: number, end: number) {
|
||||
return _.some(this.ranges, range => range[0] <= start && range[1] >= end)
|
||||
return _.some(this.ranges, (range) => range[0] <= start && range[1] >= end)
|
||||
}
|
||||
|
||||
containsValue(value: number) {
|
||||
|
||||
@@ -13,6 +13,7 @@ function loadSchemas() {
|
||||
require('./schemas/objects/hash128.json'),
|
||||
require('./schemas/objects/hash256.json'),
|
||||
require('./schemas/objects/sequence.json'),
|
||||
require('./schemas/objects/ticket-sequence.json'),
|
||||
require('./schemas/objects/signature.json'),
|
||||
require('./schemas/objects/issue.json'),
|
||||
require('./schemas/objects/ledger-version.json'),
|
||||
@@ -115,6 +116,7 @@ function loadSchemas() {
|
||||
require('./schemas/input/prepare-check-create.json'),
|
||||
require('./schemas/input/prepare-check-cash.json'),
|
||||
require('./schemas/input/prepare-check-cancel.json'),
|
||||
require('./schemas/input/prepare-ticket-create.json'),
|
||||
require('./schemas/input/compute-ledger-hash.json'),
|
||||
require('./schemas/input/sign.json'),
|
||||
require('./schemas/input/submit.json'),
|
||||
@@ -123,8 +125,8 @@ function loadSchemas() {
|
||||
require('./schemas/input/verify-payment-channel-claim.json'),
|
||||
require('./schemas/input/combine.json')
|
||||
]
|
||||
const titles = schemas.map(schema => schema.title)
|
||||
const duplicates = _.keys(_.pickBy(_.countBy(titles), count => count > 1))
|
||||
const titles = schemas.map((schema) => schema.title)
|
||||
const duplicates = _.keys(_.pickBy(_.countBy(titles), (count) => count > 1))
|
||||
assert.ok(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates)
|
||||
const validator = new Validator()
|
||||
// Register custom format validators that ignore undefined instances
|
||||
@@ -132,7 +134,7 @@ function loadSchemas() {
|
||||
// (optional) property
|
||||
|
||||
// This relies on "format": "xAddress" in `x-address.json`!
|
||||
validator.customFormats.xAddress = function(instance) {
|
||||
validator.customFormats.xAddress = function (instance) {
|
||||
if (instance === undefined) {
|
||||
return true
|
||||
}
|
||||
@@ -140,14 +142,14 @@ function loadSchemas() {
|
||||
}
|
||||
|
||||
// This relies on "format": "classicAddress" in `classic-address.json`!
|
||||
validator.customFormats.classicAddress = function(instance) {
|
||||
validator.customFormats.classicAddress = function (instance) {
|
||||
if (instance === undefined) {
|
||||
return true
|
||||
}
|
||||
return isValidAddress(instance)
|
||||
}
|
||||
|
||||
validator.customFormats.secret = function(instance) {
|
||||
validator.customFormats.secret = function (instance) {
|
||||
if (instance === undefined) {
|
||||
return true
|
||||
}
|
||||
@@ -155,7 +157,9 @@ function loadSchemas() {
|
||||
}
|
||||
|
||||
// Register under the root URI '/'
|
||||
_.forEach(schemas, schema => validator.addSchema(schema, '/' + schema.title))
|
||||
_.forEach(schemas, (schema) =>
|
||||
validator.addSchema(schema, '/' + schema.title)
|
||||
)
|
||||
return validator
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,12 @@
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer",
|
||||
"description": "Timeout in milliseconds before considering a request to have failed.",
|
||||
"description": "Request timeout in milliseconds before considering a request to have failed. See also: connectionTimeout.",
|
||||
"minimum": 1
|
||||
},
|
||||
"connectionTimeout": {
|
||||
"type": "integer",
|
||||
"description": "Connection timeout, in milliseconds, before considering connect() to have failed.",
|
||||
"minimum": 1
|
||||
},
|
||||
"proxyAuthorization": {
|
||||
|
||||
18
src/common/schemas/input/prepare-ticket-create.json
Normal file
18
src/common/schemas/input/prepare-ticket-create.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "prepareTicketParameters",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"$ref": "address",
|
||||
"description": "The address of the account that is creating the transaction."
|
||||
},
|
||||
"ticketCount": {
|
||||
"type": "number",
|
||||
"description": "The number of tickets to be created."
|
||||
},
|
||||
"instructions": {"$ref": "instructions"}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["address", "ticketCount"]
|
||||
}
|
||||
@@ -6,9 +6,13 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sequence": {
|
||||
"description": "The initiating account's sequence number for this transaction.",
|
||||
"description": "The initiating account's sequence number for this transaction. `sequence` and `ticketSequence` are mutually exclusive, only one of them can be set.",
|
||||
"$ref": "sequence"
|
||||
},
|
||||
"ticketSequence": {
|
||||
"description": "The ticket sequence to be used for this transaction. `sequence` and `ticketSequence` are mutually exclusive, only one of them can be set.",
|
||||
"$ref": "ticket-sequence"
|
||||
},
|
||||
"fee": {
|
||||
"description": "An exact fee to pay for the transaction, before multiplying for multi-signed transactions. See [Transaction Fees](#transaction-fees) for more information.",
|
||||
"$ref": "value"
|
||||
@@ -45,6 +49,10 @@
|
||||
{
|
||||
"description": "maxLedgerVersion and maxLedgerVersionOffset are mutually exclusive",
|
||||
"required": ["maxLedgerVersion", "maxLedgerVersionOffset"]
|
||||
},
|
||||
{
|
||||
"description": "sequence and ticketSequence are mutually exclusive",
|
||||
"required": ["sequence", "ticketSequence"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
"link": "account-sequence-number",
|
||||
"description": "An account transaction sequence number",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
"minimum": 0
|
||||
}
|
||||
|
||||
8
src/common/schemas/objects/ticket-sequence.json
Normal file
8
src/common/schemas/objects/ticket-sequence.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "ticket-sequence",
|
||||
"link": "account-sequence-number",
|
||||
"description": "An account transaction tickt sequence number",
|
||||
"type": "integer",
|
||||
"minimum": 1
|
||||
}
|
||||
@@ -10,12 +10,12 @@
|
||||
"closeTime": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The time at which this ledger was closed."
|
||||
"description": "The approximate time when this ledger was closed. This number is rounded based on the `closeTimeResolution`. If it would have been rounded to the same time as a previous ledger, the close time is recorded as 1 second later instead."
|
||||
},
|
||||
"closeTimeResolution": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"description": "Approximate number of seconds between closing one ledger version and closing the next one."
|
||||
"description": "A number of seconds, indicating how much the `closeTime` could be rounded. Ledger close times are approximate so that small differences in servers clocks don't hinder consensus."
|
||||
},
|
||||
"closeFlags": {
|
||||
"type": "integer",
|
||||
@@ -37,7 +37,7 @@
|
||||
"parentCloseTime": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The time at which the previous ledger was closed."
|
||||
"description": "The previous ledger's recorded close time."
|
||||
},
|
||||
"totalDrops": {
|
||||
"$ref": "value",
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
},
|
||||
"sequence": {
|
||||
"$ref": "sequence",
|
||||
"description": "The initiating account's sequence number for this transaction."
|
||||
"description": "The initiating account's sequence number for this transaction. `sequence` and `ticketSequence` are mutually exclusive, only one of them can be set."
|
||||
},
|
||||
"ticketSequence": {
|
||||
"$ref": "ticket-sequence",
|
||||
"description": "The initiating account's ticket sequence number for this transaction. `sequence` and `ticketSequence` are mutually exclusive, only one of them can be set."
|
||||
},
|
||||
"maxLedgerVersion": {
|
||||
"oneOf": [
|
||||
@@ -29,8 +33,14 @@
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["fee", "sequence", "maxLedgerVersion"]
|
||||
}
|
||||
"required": ["fee", "maxLedgerVersion"],
|
||||
"anyOf": [
|
||||
{ "required":
|
||||
[ "sequence" ] },
|
||||
{ "required":
|
||||
[ "ticketSequence" ] }
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["txJSON", "instructions"]
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
"checkID": {
|
||||
"$ref": "hash256",
|
||||
"description": "The ID of the Check ledger object to cancel, as a 64-character hexadecimal string."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"required": ["checkID"],
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"deliverMin": {
|
||||
"$ref": "laxAmount",
|
||||
"description": "Redeem the Check for at least this amount and for as much as possible. The currency must match that of the sendMax of the corresponding CheckCreate transaction. You must provide either this field or amount."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"required": ["checkID"],
|
||||
"oneOf": [
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
"invoiceID": {
|
||||
"$ref": "hash256",
|
||||
"description": "256-bit hash, as a 64-character hexadecimal string, representing a specific reason or identifier for this check."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"required": ["destination", "sendMax"],
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"unauthorize": {
|
||||
"$ref": "address",
|
||||
"description": "Address of the account that can cash the check."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"oneOf": [
|
||||
{"required": ["authorize"]},
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
"close": {
|
||||
"type": "boolean",
|
||||
"description": "Request to close the channel. If the channel has no XRP remaining or the destination address requests it, closes the channel immediately (returning unclaimed XRP to the source address). Otherwise, sets the channel to expire after settleDelay seconds have passed."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"required": ["channel"],
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
"destinationTag": {
|
||||
"$ref": "tag",
|
||||
"description": "Destination tag."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"required": ["amount", "destination", "settleDelay", "publicKey"],
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "New expiration for this channel. (This does not change the cancelAfter expiration, if the channel has one.) Cannot move the expiration sooner than settleDelay seconds from time of the request."
|
||||
}
|
||||
},
|
||||
"memos": {"$ref": "memos"}
|
||||
},
|
||||
"required": ["amount", "channel"],
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -41,7 +41,7 @@ function renameKeys(object, mapping) {
|
||||
}
|
||||
|
||||
function getServerInfo(this: RippleAPI): Promise<GetServerInfoResponse> {
|
||||
return this.request('server_info').then(response => {
|
||||
return this.request('server_info').then((response) => {
|
||||
const info = convertKeysFromSnakeCaseToCamelCase(response.info)
|
||||
renameKeys(info, {hostid: 'hostID'})
|
||||
if (info.validatedLedger) {
|
||||
@@ -71,6 +71,10 @@ async function getFee(this: RippleAPI, cushion?: number): Promise<string> {
|
||||
|
||||
const serverInfo = (await this.request('server_info')).info
|
||||
const baseFeeXrp = new BigNumber(serverInfo.validated_ledger.base_fee_xrp)
|
||||
if (serverInfo.load_factor === undefined) {
|
||||
// https://github.com/ripple/rippled/issues/3812#issuecomment-816871100
|
||||
serverInfo.load_factor = 1
|
||||
}
|
||||
let fee = baseFeeXrp.times(serverInfo.load_factor).times(cushion)
|
||||
|
||||
// Cap fee to `this._maxFeeXRP`
|
||||
|
||||
@@ -21,3 +21,17 @@ export interface Ledger {
|
||||
accountState?: any[]
|
||||
validated?: boolean
|
||||
}
|
||||
|
||||
// https://xrpl.org/subscribe.html#ledger-stream
|
||||
export type LedgerClosedEvent = {
|
||||
type: 'ledgerClosed'
|
||||
fee_base: number
|
||||
fee_ref: number
|
||||
ledger_hash: string
|
||||
ledger_index: number
|
||||
ledger_time: number
|
||||
reserve_base: number
|
||||
reserve_inc: number
|
||||
txn_count: number
|
||||
validated_ledgers: string
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
|
||||
// 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())
|
||||
newKey = key.replace(FINDSNAKE, (r) => r[0] + r[2].toUpperCase())
|
||||
}
|
||||
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
|
||||
return result
|
||||
|
||||
@@ -132,6 +132,11 @@ export const prepareCheckCancel = _.partial(
|
||||
'prepareCheckCancelParameters'
|
||||
)
|
||||
|
||||
export const prepareTicketCreate = _.partial(
|
||||
schemaValidate,
|
||||
'prepareTicketParameters'
|
||||
)
|
||||
|
||||
export const sign = _.partial(schemaValidate, 'signParameters')
|
||||
|
||||
export const combine = _.partial(schemaValidate, 'combineParameters')
|
||||
|
||||
@@ -37,11 +37,11 @@ class WSWrapper extends EventEmitter {
|
||||
this.emit('open')
|
||||
}
|
||||
|
||||
this._ws.onerror = error => {
|
||||
this._ws.onerror = (error) => {
|
||||
this.emit('error', error)
|
||||
}
|
||||
|
||||
this._ws.onmessage = message => {
|
||||
this._ws.onmessage = (message) => {
|
||||
this.emit('message', message.data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export {RippleAPI} from './api'
|
||||
export * from './api'
|
||||
|
||||
export {FormattedTransactionType} from './transaction/types'
|
||||
export * from './transaction/types'
|
||||
|
||||
export * from './common/types/objects/ledger'
|
||||
|
||||
// Broadcast api is experimental
|
||||
export {RippleAPIBroadcast} from './broadcast'
|
||||
|
||||
@@ -24,7 +24,7 @@ function formatBalanceSheet(balanceSheet): GetBalanceSheet {
|
||||
if (!_.isUndefined(balanceSheet.balances)) {
|
||||
result.balances = []
|
||||
_.forEach(balanceSheet.balances, (balances, counterparty) => {
|
||||
_.forEach(balances, balance => {
|
||||
_.forEach(balances, (balance) => {
|
||||
result.balances.push(Object.assign({counterparty}, balance))
|
||||
})
|
||||
})
|
||||
@@ -32,7 +32,7 @@ function formatBalanceSheet(balanceSheet): GetBalanceSheet {
|
||||
if (!_.isUndefined(balanceSheet.assets)) {
|
||||
result.assets = []
|
||||
_.forEach(balanceSheet.assets, (assets, counterparty) => {
|
||||
_.forEach(assets, balance => {
|
||||
_.forEach(assets, (balance) => {
|
||||
result.assets.push(Object.assign({counterparty}, balance))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -67,11 +67,11 @@ function getBalances(
|
||||
getLedgerVersionHelper(
|
||||
this.connection,
|
||||
options.ledgerVersion
|
||||
).then(ledgerVersion =>
|
||||
).then((ledgerVersion) =>
|
||||
utils.getXRPBalance(this.connection, address, ledgerVersion)
|
||||
),
|
||||
this.getTrustlines(address, options)
|
||||
]).then(results =>
|
||||
]).then((results) =>
|
||||
formatBalances(options, {xrp: results[0], trustlines: results[1]})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -116,11 +116,11 @@ export async function getOrderbook(
|
||||
// 3. Return Formatted Response
|
||||
const directOffers = _.flatMap(
|
||||
directOfferResults,
|
||||
directOfferResult => directOfferResult.offers
|
||||
(directOfferResult) => directOfferResult.offers
|
||||
)
|
||||
const reverseOffers = _.flatMap(
|
||||
reverseOfferResults,
|
||||
reverseOfferResult => reverseOfferResult.offers
|
||||
(reverseOfferResult) => reverseOfferResult.offers
|
||||
)
|
||||
return formatBidsAndAsks(orderbook, [...directOffers, ...reverseOffers])
|
||||
}
|
||||
|
||||
@@ -15,12 +15,12 @@ function formatResponse(
|
||||
): FormattedAccountOrder[] {
|
||||
let orders: FormattedAccountOrder[] = []
|
||||
for (const response of responses) {
|
||||
const offers = response.offers.map(offer => {
|
||||
const offers = response.offers.map((offer) => {
|
||||
return parseAccountOrder(address, offer)
|
||||
})
|
||||
orders = orders.concat(offers)
|
||||
}
|
||||
return _.sortBy(orders, order => order.properties.sequence)
|
||||
return _.sortBy(orders, (order) => order.properties.sequence)
|
||||
}
|
||||
|
||||
export default async function getOrders(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
import {classicAddressToXAddress} from 'ripple-address-codec'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
export type FormattedAccountDelete = {
|
||||
// account (address) of an account to receive any leftover XRP after deleting the sending account.
|
||||
@@ -21,6 +22,7 @@ function parseAccountDelete(tx: any): FormattedAccountDelete {
|
||||
assert.ok(tx.TransactionType === 'AccountDelete')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
destination: tx.Destination,
|
||||
destinationTag: tx.DestinationTag,
|
||||
destinationXAddress: classicAddressToXAddress(
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
function parseOrderCancellation(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'OfferCancel')
|
||||
return {
|
||||
memos: parseMemos(tx),
|
||||
orderSequence: tx.OfferSequence
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
export type FormattedCheckCancel = {
|
||||
// ID of the Check ledger object to cancel.
|
||||
@@ -10,6 +11,7 @@ function parseCheckCancel(tx: any): FormattedCheckCancel {
|
||||
assert.ok(tx.TransactionType === 'CheckCancel')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
checkID: tx.CheckID
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
import parseAmount from './amount'
|
||||
import {Amount} from '../../common/types/objects'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
export type FormattedCheckCash = {
|
||||
// ID of the Check ledger object to cash.
|
||||
@@ -25,6 +26,7 @@ function parseCheckCash(tx: any): FormattedCheckCash {
|
||||
assert.ok(tx.TransactionType === 'CheckCash')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
checkID: tx.CheckID,
|
||||
amount: tx.Amount && parseAmount(tx.Amount),
|
||||
deliverMin: tx.DeliverMin && parseAmount(tx.DeliverMin)
|
||||
|
||||
@@ -3,6 +3,7 @@ import {parseTimestamp} from './utils'
|
||||
import {removeUndefined} from '../../common'
|
||||
import parseAmount from './amount'
|
||||
import {Amount} from '../../common/types/objects'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
export type FormattedCheckCreate = {
|
||||
// account that can cash the check.
|
||||
@@ -26,6 +27,7 @@ function parseCheckCreate(tx: any): FormattedCheckCreate {
|
||||
assert.ok(tx.TransactionType === 'CheckCreate')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
destination: tx.Destination,
|
||||
sendMax: parseAmount(tx.SendMax),
|
||||
destinationTag: tx.DestinationTag,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
export type FormattedDepositPreauth = {
|
||||
// account (address) of the sender to preauthorize
|
||||
@@ -13,6 +14,7 @@ function parseDepositPreauth(tx: any): FormattedDepositPreauth {
|
||||
assert.ok(tx.TransactionType === 'DepositPreauth')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
authorize: tx.Authorize,
|
||||
unauthorize: tx.Unauthorize
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {dropsToXrp} from '../../common'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
function parseFeeUpdate(tx: any) {
|
||||
const baseFeeDrops = new BigNumber(tx.BaseFee, 16).toString()
|
||||
return {
|
||||
memos: parseMemos(tx),
|
||||
baseFeeXRP: dropsToXrp(baseFeeDrops),
|
||||
referenceFeeUnits: tx.ReferenceFeeUnits,
|
||||
reserveBaseXRP: dropsToXrp(tx.ReserveBase),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseTimestamp} from './utils'
|
||||
import {parseMemos} from './utils'
|
||||
import parseAmount from './amount'
|
||||
import {removeUndefined, txFlags} from '../../common'
|
||||
import {
|
||||
@@ -19,6 +20,7 @@ function parseOrder(tx: OfferCreateTransaction): FormattedOrderSpecification {
|
||||
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
direction: direction,
|
||||
quantity: quantity,
|
||||
totalPrice: totalPrice,
|
||||
|
||||
@@ -4,8 +4,8 @@ import {Amount, RippledAmount} from '../../common/types/objects'
|
||||
import {Path, GetPaths, RippledPathsResponse} from '../pathfind-types'
|
||||
|
||||
function parsePaths(paths) {
|
||||
return paths.map(steps =>
|
||||
steps.map(step => _.omit(step, ['type', 'type_hex']))
|
||||
return paths.map((steps) =>
|
||||
steps.map((step) => _.omit(step, ['type', 'type_hex']))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ function parsePathfind(pathfindResult: RippledPathsResponse): GetPaths {
|
||||
const sourceAddress = pathfindResult.source_account
|
||||
const destinationAddress = pathfindResult.destination_account
|
||||
const destinationAmount = pathfindResult.destination_amount
|
||||
return pathfindResult.alternatives.map(alt =>
|
||||
return pathfindResult.alternatives.map((alt) =>
|
||||
parseAlternative(sourceAddress, destinationAddress, destinationAmount, alt)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined, txFlags} from '../../common'
|
||||
import parseAmount from './amount'
|
||||
import {parseMemos} from './utils'
|
||||
const claimFlags = txFlags.PaymentChannelClaim
|
||||
|
||||
function parsePaymentChannelClaim(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelClaim')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
channel: tx.Channel,
|
||||
balance: tx.Balance && parseAmount(tx.Balance).value,
|
||||
amount: tx.Amount && parseAmount(tx.Amount).value,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseTimestamp} from './utils'
|
||||
import {parseTimestamp,parseMemos} from './utils'
|
||||
import {removeUndefined} from '../../common'
|
||||
import parseAmount from './amount'
|
||||
|
||||
@@ -7,6 +7,7 @@ function parsePaymentChannelCreate(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelCreate')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
amount: parseAmount(tx.Amount).value,
|
||||
destination: tx.Destination,
|
||||
settleDelay: tx.SettleDelay,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseTimestamp} from './utils'
|
||||
import {parseTimestamp,parseMemos} from './utils'
|
||||
import {removeUndefined} from '../../common'
|
||||
import parseAmount from './amount'
|
||||
|
||||
@@ -7,6 +7,7 @@ function parsePaymentChannelFund(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelFund')
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
channel: tx.Channel,
|
||||
amount: parseAmount(tx.Amount).value,
|
||||
expiration: tx.Expiration && parseTimestamp(tx.Expiration)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {parseTimestamp} from './utils'
|
||||
import {parseTimestamp, parseMemos} from './utils'
|
||||
import {removeUndefined, dropsToXrp} from '../../common'
|
||||
import {PayChannelLedgerEntry} from '../../common/types/objects'
|
||||
|
||||
@@ -21,6 +21,7 @@ export function parsePaymentChannel(
|
||||
data: PayChannelLedgerEntry
|
||||
): FormattedPaymentChannel {
|
||||
return removeUndefined({
|
||||
memos: parseMemos(data),
|
||||
account: data.Account,
|
||||
amount: dropsToXrp(data.Amount),
|
||||
balance: dropsToXrp(data.Balance),
|
||||
|
||||
@@ -6,7 +6,7 @@ import parseFields from './fields'
|
||||
|
||||
function getAccountRootModifiedNode(tx: any) {
|
||||
const modifiedNodes = tx.meta.AffectedNodes.filter(
|
||||
node => node.ModifiedNode.LedgerEntryType === 'AccountRoot'
|
||||
(node) => node.ModifiedNode.LedgerEntryType === 'AccountRoot'
|
||||
)
|
||||
assert.ok(modifiedNodes.length === 1)
|
||||
return modifiedNodes[0].ModifiedNode
|
||||
|
||||
13
src/ledger/parse/ticket-create.ts
Normal file
13
src/ledger/parse/ticket-create.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../common'
|
||||
import {parseMemos} from './utils'
|
||||
|
||||
function parseTicketCreate(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'TicketCreate')
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
ticketCount: tx.TicketCount
|
||||
})
|
||||
}
|
||||
|
||||
export default parseTicketCreate
|
||||
@@ -16,6 +16,7 @@ import parsePayment from './payment'
|
||||
import parsePaymentChannelClaim from './payment-channel-claim'
|
||||
import parsePaymentChannelCreate from './payment-channel-create'
|
||||
import parsePaymentChannelFund from './payment-channel-fund'
|
||||
import parseTicketCreate from './ticket-create'
|
||||
import parseTrustline from './trustline'
|
||||
|
||||
import parseAmendment from './amendment' // pseudo-transaction
|
||||
@@ -41,6 +42,7 @@ function parseTransactionType(type) {
|
||||
PaymentChannelFund: 'paymentChannelFund',
|
||||
SetRegularKey: 'settings',
|
||||
SignerListSet: 'settings',
|
||||
TicketCreate: 'ticketCreate',
|
||||
TrustSet: 'trustline',
|
||||
|
||||
EnableAmendment: 'amendment', // pseudo-transaction
|
||||
@@ -68,6 +70,7 @@ function parseTransaction(tx: any, includeRawTransaction: boolean): any {
|
||||
paymentChannelClaim: parsePaymentChannelClaim,
|
||||
paymentChannelCreate: parsePaymentChannelCreate,
|
||||
paymentChannelFund: parsePaymentChannelFund,
|
||||
ticketCreate: parseTicketCreate,
|
||||
trustline: parseTrustline,
|
||||
|
||||
amendment: parseAmendment, // pseudo-transaction
|
||||
|
||||
@@ -42,14 +42,14 @@ function removeEmptyCounterparty(amount) {
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInBalanceChanges(balanceChanges) {
|
||||
_.forEach(balanceChanges, changes => {
|
||||
_.forEach(balanceChanges, (changes) => {
|
||||
_.forEach(changes, removeEmptyCounterparty)
|
||||
})
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInOrderbookChanges(orderbookChanges) {
|
||||
_.forEach(orderbookChanges, changes => {
|
||||
_.forEach(changes, change => {
|
||||
_.forEach(orderbookChanges, (changes) => {
|
||||
_.forEach(changes, (change) => {
|
||||
_.forEach(change, removeEmptyCounterparty)
|
||||
})
|
||||
})
|
||||
@@ -132,7 +132,7 @@ function parseMemos(tx: any): Array<Memo> | undefined {
|
||||
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
return tx.Memos.map(m => {
|
||||
return tx.Memos.map((m) => {
|
||||
return common.removeUndefined({
|
||||
type: m.Memo.parsed_memo_type || hexToString(m.Memo.MemoType),
|
||||
format: m.Memo.parsed_memo_format || hexToString(m.Memo.MemoFormat),
|
||||
|
||||
@@ -62,7 +62,7 @@ function requestPathFind(
|
||||
request.destination_amount.issuer = request.destination_account
|
||||
}
|
||||
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
|
||||
request.source_currencies = pathfind.source.currencies.map(amount =>
|
||||
request.source_currencies = pathfind.source.currencies.map((amount) =>
|
||||
renameCounterpartyToIssuer(amount)
|
||||
)
|
||||
}
|
||||
@@ -79,7 +79,7 @@ function requestPathFind(
|
||||
}
|
||||
}
|
||||
|
||||
return connection.request(request).then(paths => addParams(request, paths))
|
||||
return connection.request(request).then((paths) => addParams(request, paths))
|
||||
}
|
||||
|
||||
function addDirectXrpPath(
|
||||
@@ -116,7 +116,7 @@ function conditionallyAddDirectXRPPath(
|
||||
) {
|
||||
return Promise.resolve(paths)
|
||||
}
|
||||
return getXRPBalance(connection, address, undefined).then(xrpBalance =>
|
||||
return getXRPBalance(connection, address, undefined).then((xrpBalance) =>
|
||||
addDirectXrpPath(paths, xrpBalance)
|
||||
)
|
||||
}
|
||||
@@ -130,7 +130,7 @@ function filterSourceFundsLowPaths(
|
||||
pathfind.destination.amount.value === undefined &&
|
||||
paths.alternatives
|
||||
) {
|
||||
paths.alternatives = _.filter(paths.alternatives, alt => {
|
||||
paths.alternatives = _.filter(paths.alternatives, (alt) => {
|
||||
if (!alt.source_amount) {
|
||||
return false
|
||||
}
|
||||
@@ -191,11 +191,11 @@ function getPaths(this: RippleAPI, pathfind: PathFind): Promise<GetPaths> {
|
||||
|
||||
const address = pathfind.source.address
|
||||
return requestPathFind(this.connection, pathfind)
|
||||
.then(paths =>
|
||||
.then((paths) =>
|
||||
conditionallyAddDirectXRPPath(this.connection, address, paths)
|
||||
)
|
||||
.then(paths => filterSourceFundsLowPaths(pathfind, paths))
|
||||
.then(paths => formatResponse(pathfind, paths))
|
||||
.then((paths) => filterSourceFundsLowPaths(pathfind, paths))
|
||||
.then((paths) => formatResponse(pathfind, paths))
|
||||
}
|
||||
|
||||
export default getPaths
|
||||
|
||||
@@ -48,13 +48,13 @@ function attachTransactionDate(
|
||||
|
||||
return connection
|
||||
.request(request)
|
||||
.then(data => {
|
||||
.then((data) => {
|
||||
if (typeof data.ledger.close_time === 'number') {
|
||||
return _.assign({date: data.ledger.close_time}, tx)
|
||||
}
|
||||
throw new errors.UnexpectedError('Ledger missing close_time')
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
if (error instanceof errors.UnexpectedError) {
|
||||
throw error
|
||||
}
|
||||
@@ -95,11 +95,11 @@ function convertError(
|
||||
options.minLedgerVersion,
|
||||
options.maxLedgerVersion
|
||||
)
|
||||
.then(hasCompleteLedgerRange => {
|
||||
.then((hasCompleteLedgerRange) => {
|
||||
if (!hasCompleteLedgerRange) {
|
||||
return utils
|
||||
.isPendingLedgerVersion(connection, options.maxLedgerVersion)
|
||||
.then(isPendingLedgerVersion => {
|
||||
.then((isPendingLedgerVersion) => {
|
||||
return isPendingLedgerVersion
|
||||
? new errors.PendingLedgerVersionError()
|
||||
: new errors.MissingLedgerHistoryError()
|
||||
|
||||
@@ -101,12 +101,12 @@ function formatPartialResponse(
|
||||
options: TransactionsOptions,
|
||||
data
|
||||
) {
|
||||
const parse = tx =>
|
||||
const parse = (tx) =>
|
||||
parseAccountTxTransaction(tx, options.includeRawTransactions)
|
||||
return {
|
||||
marker: data.marker,
|
||||
results: data.transactions
|
||||
.filter(tx => tx.validated)
|
||||
.filter((tx) => tx.validated)
|
||||
.map(parse)
|
||||
.filter(_.partial(transactionFilter, address, options))
|
||||
.filter(_.partial(orderFilter, options))
|
||||
@@ -135,7 +135,7 @@ function getAccountTx(
|
||||
|
||||
return connection
|
||||
.request(request)
|
||||
.then(response => formatPartialResponse(address, options, response))
|
||||
.then((response) => formatPartialResponse(address, options, response))
|
||||
}
|
||||
|
||||
function checkForLedgerGaps(
|
||||
@@ -158,7 +158,7 @@ function checkForLedgerGaps(
|
||||
|
||||
return utils
|
||||
.hasCompleteLedgerRange(connection, minLedgerVersion, maxLedgerVersion)
|
||||
.then(hasCompleteLedgerRange => {
|
||||
.then((hasCompleteLedgerRange) => {
|
||||
if (!hasCompleteLedgerRange) {
|
||||
throw new errors.MissingLedgerHistoryError()
|
||||
}
|
||||
@@ -204,7 +204,7 @@ function getTransactions(
|
||||
|
||||
const defaults = {maxLedgerVersion: -1}
|
||||
if (options.start) {
|
||||
return getTransaction.call(this, options.start).then(tx => {
|
||||
return getTransaction.call(this, options.start).then((tx) => {
|
||||
const ledgerVersion = tx.outcome.ledgerVersion
|
||||
const bound = options.earliestFirst
|
||||
? {minLedgerVersion: ledgerVersion}
|
||||
|
||||
@@ -36,8 +36,8 @@ async function getTrustlines(
|
||||
peer: options.counterparty
|
||||
})
|
||||
// 3. Return Formatted Response
|
||||
const trustlines = _.flatMap(responses, response => response.lines)
|
||||
return trustlines.map(parseAccountTrustline).filter(trustline => {
|
||||
const trustlines = _.flatMap(responses, (response) => response.lines)
|
||||
return trustlines.map(parseAccountTrustline).filter((trustline) => {
|
||||
return currencyFilter(options.currency || null, trustline)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ function getXRPBalance(
|
||||
}
|
||||
return connection
|
||||
.request(request)
|
||||
.then(data => common.dropsToXrp(data.account_data.Balance))
|
||||
.then((data) => common.dropsToXrp(data.account_data.Balance))
|
||||
}
|
||||
|
||||
// If the marker is omitted from a response, you have reached the end
|
||||
@@ -39,10 +39,10 @@ function getRecursiveRecur(
|
||||
marker: string | undefined,
|
||||
limit: number
|
||||
): Promise<Array<any>> {
|
||||
return getter(marker, limit).then(data => {
|
||||
return getter(marker, limit).then((data) => {
|
||||
const remaining = limit - data.results.length
|
||||
if (remaining > 0 && data.marker !== undefined) {
|
||||
return getRecursiveRecur(getter, data.marker, remaining).then(results =>
|
||||
return getRecursiveRecur(getter, data.marker, remaining).then((results) =>
|
||||
data.results.concat(results)
|
||||
)
|
||||
}
|
||||
@@ -118,7 +118,7 @@ function isPendingLedgerVersion(
|
||||
): Promise<boolean> {
|
||||
return connection
|
||||
.getLedgerVersion()
|
||||
.then(ledgerVersion => ledgerVersion < (maxLedgerVersion || 0))
|
||||
.then((ledgerVersion) => ledgerVersion < (maxLedgerVersion || 0))
|
||||
}
|
||||
|
||||
function ensureLedgerVersion(this: RippleAPI, options: any): Promise<object> {
|
||||
@@ -129,7 +129,7 @@ function ensureLedgerVersion(this: RippleAPI, options: any): Promise<object> {
|
||||
) {
|
||||
return Promise.resolve(options)
|
||||
}
|
||||
return this.getLedgerVersion().then(ledgerVersion =>
|
||||
return this.getLedgerVersion().then((ledgerVersion) =>
|
||||
_.assign({}, options, {ledgerVersion})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ function computeTransactionHash(
|
||||
transactions = JSON.parse(ledger.rawTransactions)
|
||||
} else if (ledger.transactions) {
|
||||
try {
|
||||
transactions = ledger.transactions.map(tx =>
|
||||
transactions = ledger.transactions.map((tx) =>
|
||||
JSON.parse(tx.rawTransaction)
|
||||
)
|
||||
} catch (e) {
|
||||
@@ -60,7 +60,7 @@ function computeTransactionHash(
|
||||
}
|
||||
return ledger.transactionHash
|
||||
}
|
||||
const txs = _.map(transactions, tx => {
|
||||
const txs = _.map(transactions, (tx) => {
|
||||
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {})
|
||||
// rename `meta` back to `metaData`
|
||||
const renameMeta = _.assign(
|
||||
|
||||
@@ -24,7 +24,7 @@ function combine(signedTransactions: Array<string>): object {
|
||||
// tests and this code handle it as an array of objects. Fix!
|
||||
const txs: any[] = _.map(signedTransactions, binary.decode)
|
||||
const tx = _.omit(txs[0], 'Signers')
|
||||
if (!_.every(txs, _tx => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
|
||||
if (!_.every(txs, (_tx) => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
|
||||
throw new utils.common.errors.ValidationError(
|
||||
'txJSON is not the same for all signedTransactions'
|
||||
)
|
||||
|
||||
@@ -76,8 +76,8 @@ function applyAnyCounterpartyEncoding(payment: Payment): void {
|
||||
// Convert blank counterparty to sender or receiver's address
|
||||
// (Ripple convention for 'any counterparty')
|
||||
// https://developers.ripple.com/payment.html#special-issuer-values-for-sendmax-and-amount
|
||||
_.forEach([payment.source, payment.destination], adjustment => {
|
||||
_.forEach(['amount', 'minAmount', 'maxAmount'], key => {
|
||||
_.forEach([payment.source, payment.destination], (adjustment) => {
|
||||
_.forEach(['amount', 'minAmount', 'maxAmount'], (key) => {
|
||||
if (isIOUWithoutCounterparty(adjustment[key])) {
|
||||
adjustment[key].counterparty = adjustment.address
|
||||
}
|
||||
@@ -91,7 +91,8 @@ function createMaximalAmount(amount: Amount): Amount {
|
||||
// Equivalent to '9999999999999999e80' but we cannot use that because sign()
|
||||
// now checks that the encoded representation exactly matches the transaction
|
||||
// as it was originally provided.
|
||||
const maxIOUValue = '999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
const maxIOUValue =
|
||||
'999999999999999900000000000000000000000000000000000000000000000000000000000000000000000000000000'
|
||||
|
||||
let maxValue
|
||||
if (amount.currency === 'XRP') {
|
||||
|
||||
@@ -17,9 +17,9 @@ function setTransactionFlags(
|
||||
txJSON: TransactionJSON,
|
||||
values: FormattedSettings
|
||||
) {
|
||||
const keys = Object.keys(values)
|
||||
const keys = Object.keys(values).filter((key) => AccountFlagIndices[key] !== undefined)
|
||||
assert.ok(
|
||||
keys.length === 1,
|
||||
keys.length <= 1,
|
||||
'ERROR: can only set one setting per transaction'
|
||||
)
|
||||
const flagName = keys[0]
|
||||
@@ -55,9 +55,7 @@ function setTransactionFields(
|
||||
|
||||
if (field.encoding === 'hex' && !field.length) {
|
||||
// This is currently only used for Domain field
|
||||
value = Buffer.from(value, 'ascii')
|
||||
.toString('hex')
|
||||
.toUpperCase()
|
||||
value = Buffer.from(value, 'ascii').toString('hex').toUpperCase()
|
||||
}
|
||||
|
||||
txJSON[fieldName] = value
|
||||
|
||||
@@ -56,11 +56,8 @@ function signWithKeypair(
|
||||
keypair.privateKey
|
||||
)
|
||||
}
|
||||
|
||||
const serialized = binaryCodec.encode(txToSignAndEncode)
|
||||
|
||||
checkTxSerialization(serialized, tx)
|
||||
|
||||
return {
|
||||
signedTransaction: serialized,
|
||||
id: computeBinaryTransactionHash(serialized)
|
||||
@@ -79,7 +76,7 @@ function objectDiff(a: object, b: object): object {
|
||||
const diffs = {}
|
||||
|
||||
// Compare two items and push non-matches to object
|
||||
const compare = function(i1: any, i2: any, k: string): void {
|
||||
const compare = function (i1: any, i2: any, k: string): void {
|
||||
const type1 = Object.prototype.toString.call(i1)
|
||||
const type2 = Object.prototype.toString.call(i2)
|
||||
if (type2 === '[object Undefined]') {
|
||||
|
||||
45
src/transaction/ticket.ts
Normal file
45
src/transaction/ticket.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import * as _ from 'lodash'
|
||||
import * as utils from './utils'
|
||||
import {Prepare, TransactionJSON, Instructions} from './types'
|
||||
import {RippleAPI} from '..'
|
||||
|
||||
const validate = utils.common.validate
|
||||
const ValidationError = utils.common.errors.ValidationError
|
||||
|
||||
export interface Ticket {
|
||||
account: string
|
||||
sequence: number
|
||||
}
|
||||
|
||||
function createTicketTransaction(
|
||||
account: string,
|
||||
ticketCount: number
|
||||
): TransactionJSON {
|
||||
if (!ticketCount || ticketCount === 0)
|
||||
throw new ValidationError('Ticket count must be greater than 0.')
|
||||
|
||||
const txJSON: any = {
|
||||
TransactionType: 'TicketCreate',
|
||||
Account: account,
|
||||
TicketCount: ticketCount
|
||||
}
|
||||
|
||||
return txJSON
|
||||
}
|
||||
|
||||
function prepareTicketCreate(
|
||||
this: RippleAPI,
|
||||
address: string,
|
||||
ticketCount: number,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
try {
|
||||
validate.prepareTicketCreate({address, ticketCount, instructions})
|
||||
const txJSON = createTicketTransaction(address, ticketCount)
|
||||
return utils.prepareTransaction(txJSON, this, instructions)
|
||||
} catch (e) {
|
||||
return Promise.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
export default prepareTicketCreate
|
||||
@@ -19,6 +19,7 @@ export type TransactionJSON = {
|
||||
|
||||
export type Instructions = {
|
||||
sequence?: number
|
||||
ticketSequence?: number
|
||||
fee?: string
|
||||
// @deprecated
|
||||
maxFee?: string
|
||||
@@ -31,7 +32,8 @@ export type Prepare = {
|
||||
txJSON: string
|
||||
instructions: {
|
||||
fee: string
|
||||
sequence: number
|
||||
sequence?: number
|
||||
ticketSequence?: number
|
||||
maxLedgerVersion?: number
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,14 @@ export type ApiMemo = {
|
||||
function formatPrepareResponse(txJSON: any): Prepare {
|
||||
const instructions = {
|
||||
fee: common.dropsToXrp(txJSON.Fee),
|
||||
sequence: txJSON.Sequence,
|
||||
maxLedgerVersion:
|
||||
txJSON.LastLedgerSequence === undefined ? null : txJSON.LastLedgerSequence
|
||||
}
|
||||
if (txJSON.TicketSequence !== undefined) {
|
||||
instructions['ticketSequence'] = txJSON.TicketSequence
|
||||
} else {
|
||||
instructions['sequence'] = txJSON.Sequence
|
||||
}
|
||||
return {
|
||||
txJSON: JSON.stringify(txJSON),
|
||||
instructions
|
||||
@@ -52,10 +56,7 @@ function setCanonicalFlag(txJSON: TransactionJSON): void {
|
||||
}
|
||||
|
||||
function scaleValue(value, multiplier, extra = 0) {
|
||||
return new BigNumber(value)
|
||||
.times(multiplier)
|
||||
.plus(extra)
|
||||
.toString()
|
||||
return new BigNumber(value).times(multiplier).plus(extra).toString()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,13 +114,22 @@ function prepareTransaction(
|
||||
): Promise<Prepare> {
|
||||
common.validate.instructions(instructions)
|
||||
common.validate.tx_json(txJSON)
|
||||
|
||||
// We allow 0 values in the Sequence schema to support the Tickets feature
|
||||
// When a ticketSequence is used, sequence has to be 0
|
||||
// We validate that a sequence with value 0 is not passed even if the json schema allows it
|
||||
if (instructions.sequence !== undefined && instructions.sequence === 0) {
|
||||
return Promise.reject(new ValidationError('`sequence` cannot be 0'))
|
||||
}
|
||||
|
||||
const disallowedFieldsInTxJSON = [
|
||||
'maxLedgerVersion',
|
||||
'maxLedgerVersionOffset',
|
||||
'fee',
|
||||
'sequence'
|
||||
'sequence',
|
||||
'ticketSequence'
|
||||
]
|
||||
const badFields = disallowedFieldsInTxJSON.filter(field => txJSON[field])
|
||||
const badFields = disallowedFieldsInTxJSON.filter((field) => txJSON[field])
|
||||
if (badFields.length) {
|
||||
return Promise.reject(
|
||||
new ValidationError(
|
||||
@@ -236,7 +246,7 @@ function prepareTransaction(
|
||||
instructions.maxLedgerVersionOffset !== undefined
|
||||
? instructions.maxLedgerVersionOffset
|
||||
: 3
|
||||
return api.connection.getLedgerVersion().then(ledgerVersion => {
|
||||
return api.connection.getLedgerVersion().then((ledgerVersion) => {
|
||||
newTxJSON.LastLedgerSequence = ledgerVersion + offset
|
||||
return
|
||||
})
|
||||
@@ -281,8 +291,8 @@ function prepareTransaction(
|
||||
return Promise.resolve()
|
||||
}
|
||||
const cushion = api._feeCushion
|
||||
return api.getFee(cushion).then(fee => {
|
||||
return api.connection.getFeeRef().then(feeRef => {
|
||||
return api.getFee(cushion).then((fee) => {
|
||||
return api.connection.getFeeRef().then((feeRef) => {
|
||||
const extraFee =
|
||||
newTxJSON.TransactionType !== 'EscrowFinish' ||
|
||||
newTxJSON.Fulfillment === undefined
|
||||
@@ -323,14 +333,22 @@ function prepareTransaction(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (newTxJSON.Sequence !== undefined) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
// Ticket Sequence
|
||||
if (instructions.ticketSequence !== undefined) {
|
||||
newTxJSON.Sequence = 0
|
||||
newTxJSON.TicketSequence = instructions.ticketSequence
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
try {
|
||||
// Consider requesting from the 'current' ledger (instead of 'validated').
|
||||
const response = await api.request('account_info', {
|
||||
account: classicAccount
|
||||
account: classicAccount,
|
||||
ledger_index: 'current' // Fix #999
|
||||
})
|
||||
newTxJSON.Sequence = response.account_data.Sequence
|
||||
return Promise.resolve()
|
||||
@@ -347,9 +365,7 @@ function prepareTransaction(
|
||||
}
|
||||
|
||||
function convertStringToHex(string: string): string {
|
||||
return Buffer.from(string, 'utf8')
|
||||
.toString('hex')
|
||||
.toUpperCase()
|
||||
return Buffer.from(string, 'utf8').toString('hex').toUpperCase()
|
||||
}
|
||||
|
||||
function convertMemo(memo: Memo): {Memo: ApiMemo} {
|
||||
|
||||
21
test/.mocharc.json
Normal file
21
test/.mocharc.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"diff": true,
|
||||
"spec": ["./test/*.ts"],
|
||||
"extension": ["ts"],
|
||||
"package": "../package.json",
|
||||
"require": "ts-node/register",
|
||||
"reporter": "spec",
|
||||
"slow": 500,
|
||||
"timeout": 5000,
|
||||
"recursive": true,
|
||||
"file": [],
|
||||
"ui": "bdd",
|
||||
"watch-files": [
|
||||
"src/**/*.ts",
|
||||
"test/**/*.ts"
|
||||
],
|
||||
"watch-ignore": [
|
||||
"test/node_modules/**/*",
|
||||
"test/vendor/**/*"
|
||||
]
|
||||
}
|
||||
@@ -8,11 +8,11 @@ import BigNumber from 'bignumber.js'
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'works with a typical amount': async api => {
|
||||
'works with a typical amount': async (api) => {
|
||||
const xrp = api.dropsToXrp('2000000')
|
||||
assert.strictEqual(xrp, '2', '2 million drops equals 2 XRP')
|
||||
},
|
||||
'works with fractions': async api => {
|
||||
'works with fractions': async (api) => {
|
||||
let xrp = api.dropsToXrp('3456789')
|
||||
assert.strictEqual(xrp, '3.456789', '3,456,789 drops equals 3.456789 XRP')
|
||||
|
||||
@@ -28,7 +28,7 @@ export default <TestSuite>{
|
||||
xrp = api.dropsToXrp('1.00')
|
||||
assert.strictEqual(xrp, '0.000001', '1.00 drops equals 0.000001 XRP')
|
||||
},
|
||||
'works with zero': async api => {
|
||||
'works with zero': async (api) => {
|
||||
let xrp = api.dropsToXrp('0')
|
||||
assert.strictEqual(xrp, '0', '0 drops equals 0 XRP')
|
||||
|
||||
@@ -42,18 +42,18 @@ export default <TestSuite>{
|
||||
xrp = api.dropsToXrp('000000000')
|
||||
assert.strictEqual(xrp, '0', '000000000 drops equals 0 XRP')
|
||||
},
|
||||
'works with a negative value': async api => {
|
||||
'works with a negative value': async (api) => {
|
||||
const xrp = api.dropsToXrp('-2000000')
|
||||
assert.strictEqual(xrp, '-2', '-2 million drops equals -2 XRP')
|
||||
},
|
||||
'works with a value ending with a decimal point': async api => {
|
||||
'works with a value ending with a decimal point': async (api) => {
|
||||
let xrp = api.dropsToXrp('2000000.')
|
||||
assert.strictEqual(xrp, '2', '2000000. drops equals 2 XRP')
|
||||
|
||||
xrp = api.dropsToXrp('-2000000.')
|
||||
assert.strictEqual(xrp, '-2', '-2000000. drops equals -2 XRP')
|
||||
},
|
||||
'works with BigNumber objects': async api => {
|
||||
'works with BigNumber objects': async (api) => {
|
||||
let xrp = api.dropsToXrp(new BigNumber(2000000))
|
||||
assert.strictEqual(xrp, '2', '(BigNumber) 2 million drops equals 2 XRP')
|
||||
|
||||
@@ -74,14 +74,14 @@ export default <TestSuite>{
|
||||
'(BigNumber) -2,345,678 drops equals -2.345678 XRP'
|
||||
)
|
||||
},
|
||||
'works with a number': async api => {
|
||||
'works with a number': async (api) => {
|
||||
// This is not recommended. Use strings or BigNumber objects to avoid precision errors.
|
||||
let xrp = api.dropsToXrp(2000000)
|
||||
assert.strictEqual(xrp, '2', '(number) 2 million drops equals 2 XRP')
|
||||
xrp = api.dropsToXrp(-2000000)
|
||||
assert.strictEqual(xrp, '-2', '(number) -2 million drops equals -2 XRP')
|
||||
},
|
||||
'throws with an amount with too many decimal places': async api => {
|
||||
'throws with an amount with too many decimal places': async (api) => {
|
||||
assert.throws(() => {
|
||||
api.dropsToXrp('1.2')
|
||||
}, /has too many decimal places/)
|
||||
@@ -90,7 +90,7 @@ export default <TestSuite>{
|
||||
api.dropsToXrp('0.10')
|
||||
}, /has too many decimal places/)
|
||||
},
|
||||
'throws with an invalid value': async api => {
|
||||
'throws with an invalid value': async (api) => {
|
||||
assert.throws(() => {
|
||||
api.dropsToXrp('FOO')
|
||||
}, /invalid value/)
|
||||
@@ -107,7 +107,7 @@ export default <TestSuite>{
|
||||
api.dropsToXrp('.')
|
||||
}, /dropsToXrp: invalid value '\.', should be a BigNumber or string-encoded number\./)
|
||||
},
|
||||
'throws with an amount more than one decimal point': async api => {
|
||||
'throws with an amount more than one decimal point': async (api) => {
|
||||
assert.throws(() => {
|
||||
api.dropsToXrp('1.0.0')
|
||||
}, /dropsToXrp: invalid value '1\.0\.0'/)
|
||||
|
||||
@@ -267,16 +267,16 @@ export default <TestSuite>{
|
||||
])
|
||||
|
||||
const bidRates = orderbook.bids.map(
|
||||
bid => bid.properties.makerExchangeRate
|
||||
(bid) => bid.properties.makerExchangeRate
|
||||
)
|
||||
const askRates = orderbook.asks.map(
|
||||
ask => ask.properties.makerExchangeRate
|
||||
(ask) => ask.properties.makerExchangeRate
|
||||
)
|
||||
// makerExchangeRate = quality = takerPays.value/takerGets.value
|
||||
// so the best deal for the taker is the lowest makerExchangeRate
|
||||
// bids and asks should be sorted so that the best deals come first
|
||||
assert.deepEqual(bidRates.map(x => Number(x)).sort(), bidRates)
|
||||
assert.deepEqual(askRates.map(x => Number(x)).sort(), askRates)
|
||||
assert.deepEqual(bidRates.map((x) => Number(x)).sort(), bidRates)
|
||||
assert.deepEqual(askRates.map((x) => Number(x)).sort(), askRates)
|
||||
})
|
||||
},
|
||||
|
||||
@@ -322,7 +322,7 @@ export default <TestSuite>{
|
||||
])
|
||||
|
||||
const orders = [...orderbook.bids, ...orderbook.asks]
|
||||
orders.forEach(order => {
|
||||
orders.forEach((order) => {
|
||||
const quantity = order.specification.quantity
|
||||
const totalPrice = order.specification.totalPrice
|
||||
const {base, counter} = requests.getOrderbook.normal
|
||||
@@ -375,9 +375,11 @@ export default <TestSuite>{
|
||||
...reverseOffers
|
||||
])
|
||||
|
||||
assert(orderbook.bids.every(bid => bid.specification.direction === 'buy'))
|
||||
assert(
|
||||
orderbook.asks.every(ask => ask.specification.direction === 'sell')
|
||||
orderbook.bids.every((bid) => bid.specification.direction === 'buy')
|
||||
)
|
||||
assert(
|
||||
orderbook.asks.every((ask) => ask.specification.direction === 'sell')
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ const {generateAddress: RESPONSE_FIXTURES} = responses
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'generateAddress': async api => {
|
||||
'generateAddress': async (api) => {
|
||||
// GIVEN entropy of all zeros
|
||||
function random() {
|
||||
return new Array(16).fill(0)
|
||||
@@ -25,7 +25,7 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'generateAddress invalid entropy': async api => {
|
||||
'generateAddress invalid entropy': async (api) => {
|
||||
assert.throws(() => {
|
||||
// GIVEN entropy of 1 byte
|
||||
function random() {
|
||||
@@ -40,7 +40,7 @@ export default <TestSuite>{
|
||||
}, api.errors.UnexpectedError)
|
||||
},
|
||||
|
||||
'generateAddress with no options object': async api => {
|
||||
'generateAddress with no options object': async (api) => {
|
||||
// GIVEN no options
|
||||
|
||||
// WHEN generating an address
|
||||
@@ -51,7 +51,7 @@ export default <TestSuite>{
|
||||
assert(account.secret.startsWith('s'), 'Secret must start with `s`')
|
||||
},
|
||||
|
||||
'generateAddress with empty options object': async api => {
|
||||
'generateAddress with empty options object': async (api) => {
|
||||
// GIVEN an empty options object
|
||||
const options = {}
|
||||
|
||||
@@ -63,7 +63,7 @@ export default <TestSuite>{
|
||||
assert(account.secret.startsWith('s'), 'Secret must start with `s`')
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1`': async api => {
|
||||
'generateAddress with algorithm `ecdsa-secp256k1`': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1'}
|
||||
|
||||
@@ -84,7 +84,7 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519`': async api => {
|
||||
'generateAddress with algorithm `ed25519`': async (api) => {
|
||||
// GIVEN we want to use 'ed25519'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519'}
|
||||
|
||||
@@ -100,7 +100,9 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy': async api => {
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ecdsa-secp256k1',
|
||||
@@ -114,7 +116,7 @@ export default <TestSuite>{
|
||||
assert.deepEqual(account, responses.generateAddress)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519` and given entropy': async api => {
|
||||
'generateAddress with algorithm `ed25519` and given entropy': async (api) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ed25519',
|
||||
@@ -135,7 +137,9 @@ export default <TestSuite>{
|
||||
})
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address': async api => {
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ecdsa-secp256k1',
|
||||
@@ -150,7 +154,9 @@ export default <TestSuite>{
|
||||
assert.deepEqual(account, responses.generateAddress)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519` and given entropy; include classic address': async api => {
|
||||
'generateAddress with algorithm `ed25519` and given entropy; include classic address': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ed25519',
|
||||
@@ -172,7 +178,9 @@ export default <TestSuite>{
|
||||
})
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address; for test network use': async api => {
|
||||
'generateAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address; for test network use': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ecdsa-secp256k1',
|
||||
@@ -192,7 +200,9 @@ export default <TestSuite>{
|
||||
assert.deepEqual(account, response)
|
||||
},
|
||||
|
||||
'generateAddress with algorithm `ed25519` and given entropy; include classic address; for test network use': async api => {
|
||||
'generateAddress with algorithm `ed25519` and given entropy; include classic address; for test network use': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ed25519',
|
||||
@@ -215,7 +225,7 @@ export default <TestSuite>{
|
||||
})
|
||||
},
|
||||
|
||||
'generateAddress for test network use': async api => {
|
||||
'generateAddress for test network use': async (api) => {
|
||||
// GIVEN we want an address for test network use
|
||||
const options: GenerateAddressOptions = {test: true}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {GenerateAddressOptions} from '../../../src/offline/generate-address'
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'generateXAddress': async api => {
|
||||
'generateXAddress': async (api) => {
|
||||
// GIVEN entropy of all zeros
|
||||
function random() {
|
||||
return new Array(16).fill(0)
|
||||
@@ -24,7 +24,7 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'generateXAddress invalid entropy': async api => {
|
||||
'generateXAddress invalid entropy': async (api) => {
|
||||
assert.throws(() => {
|
||||
// GIVEN entropy of 1 byte
|
||||
function random() {
|
||||
@@ -39,7 +39,7 @@ export default <TestSuite>{
|
||||
}, api.errors.UnexpectedError)
|
||||
},
|
||||
|
||||
'generateXAddress with no options object': async api => {
|
||||
'generateXAddress with no options object': async (api) => {
|
||||
// GIVEN no options
|
||||
|
||||
// WHEN generating an X-address
|
||||
@@ -53,7 +53,7 @@ export default <TestSuite>{
|
||||
assert(account.secret.startsWith('s'), 'Secrets start with s')
|
||||
},
|
||||
|
||||
'generateXAddress with empty options object': async api => {
|
||||
'generateXAddress with empty options object': async (api) => {
|
||||
// GIVEN an empty options object
|
||||
const options = {}
|
||||
|
||||
@@ -68,7 +68,7 @@ export default <TestSuite>{
|
||||
assert(account.secret.startsWith('s'), 'Secrets start with s')
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1`': async api => {
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1`': async (api) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ecdsa-secp256k1'}
|
||||
|
||||
@@ -92,7 +92,7 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519`': async api => {
|
||||
'generateXAddress with algorithm `ed25519`': async (api) => {
|
||||
// GIVEN we want to use 'ed25519'
|
||||
const options: GenerateAddressOptions = {algorithm: 'ed25519'}
|
||||
|
||||
@@ -111,7 +111,9 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy': async api => {
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ecdsa-secp256k1',
|
||||
@@ -125,7 +127,9 @@ export default <TestSuite>{
|
||||
assert.deepEqual(account, responses.generateXAddress)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519` and given entropy': async api => {
|
||||
'generateXAddress with algorithm `ed25519` and given entropy': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ed25519',
|
||||
@@ -142,7 +146,9 @@ export default <TestSuite>{
|
||||
})
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address': async api => {
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ecdsa-secp256k1',
|
||||
@@ -157,7 +163,9 @@ export default <TestSuite>{
|
||||
assert.deepEqual(account, responses.generateAddress)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519` and given entropy; include classic address': async api => {
|
||||
'generateXAddress with algorithm `ed25519` and given entropy; include classic address': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ed25519',
|
||||
@@ -177,7 +185,9 @@ export default <TestSuite>{
|
||||
})
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address; for test network use': async api => {
|
||||
'generateXAddress with algorithm `ecdsa-secp256k1` and given entropy; include classic address; for test network use': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ecdsa-secp256k1' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ecdsa-secp256k1',
|
||||
@@ -196,7 +206,9 @@ export default <TestSuite>{
|
||||
assert.deepEqual(account, response)
|
||||
},
|
||||
|
||||
'generateXAddress with algorithm `ed25519` and given entropy; include classic address; for test network use': async api => {
|
||||
'generateXAddress with algorithm `ed25519` and given entropy; include classic address; for test network use': async (
|
||||
api
|
||||
) => {
|
||||
// GIVEN we want to use 'ed25519' with entropy of zero
|
||||
const options: GenerateAddressOptions = {
|
||||
algorithm: 'ed25519',
|
||||
@@ -217,7 +229,7 @@ export default <TestSuite>{
|
||||
})
|
||||
},
|
||||
|
||||
'generateXAddress for test network use': async api => {
|
||||
'generateXAddress for test network use': async (api) => {
|
||||
// GIVEN we want an X-address for test network use
|
||||
const options: GenerateAddressOptions = {test: true}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export default <TestSuite>{
|
||||
'getBalances - limit & currency': async (api, address) => {
|
||||
const options = {currency: 'USD', limit: 3}
|
||||
const expectedResponse = responses.getBalances
|
||||
.filter(item => item.currency === 'USD')
|
||||
.filter((item) => item.currency === 'USD')
|
||||
.slice(0, 3)
|
||||
const result = await api.getBalances(address, options)
|
||||
assertResultMatch(result, expectedResponse, 'getBalances')
|
||||
@@ -36,7 +36,7 @@ export default <TestSuite>{
|
||||
}
|
||||
const expectedResponse = responses.getBalances
|
||||
.filter(
|
||||
item =>
|
||||
(item) =>
|
||||
item.currency === 'USD' &&
|
||||
item.counterparty === 'rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B'
|
||||
)
|
||||
|
||||
@@ -51,5 +51,14 @@ export default <TestSuite>{
|
||||
api._feeCushion = 0.9
|
||||
const fee = await api.getFee()
|
||||
assert.strictEqual(fee, '0.000009')
|
||||
},
|
||||
|
||||
'getFee reporting': async (api, address) => {
|
||||
api.connection.request({
|
||||
command: 'config',
|
||||
data: {reporting: true}
|
||||
})
|
||||
const fee = await api.getFee()
|
||||
assert.strictEqual(fee, '0.000012')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,22 +9,22 @@ const {getLedger: RESPONSE_FIXTURES} = responses
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'simple test': async api => {
|
||||
'simple test': async (api) => {
|
||||
const response = await api.getLedger()
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.header, 'getLedger')
|
||||
},
|
||||
'by hash': async api => {
|
||||
'by hash': async (api) => {
|
||||
const response = await api.getLedger({
|
||||
ledgerHash:
|
||||
'15F20E5FA6EA9770BBFFDBD62787400960B04BE32803B20C41F117F41C13830D'
|
||||
})
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.headerByHash, 'getLedger')
|
||||
},
|
||||
'future ledger version': async api => {
|
||||
'future ledger version': async (api) => {
|
||||
const response = await api.getLedger({ledgerVersion: 14661789})
|
||||
assert(!!response)
|
||||
},
|
||||
'with state as hashes': async api => {
|
||||
'with state as hashes': async (api) => {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeAllData: false,
|
||||
@@ -38,7 +38,7 @@ export default <TestSuite>{
|
||||
'getLedger'
|
||||
)
|
||||
},
|
||||
'with settings transaction': async api => {
|
||||
'with settings transaction': async (api) => {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeAllData: true,
|
||||
@@ -47,7 +47,7 @@ export default <TestSuite>{
|
||||
const response = await api.getLedger(request)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.withSettingsTx, 'getLedger')
|
||||
},
|
||||
'with partial payment': async api => {
|
||||
'with partial payment': async (api) => {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeAllData: true,
|
||||
@@ -56,7 +56,7 @@ export default <TestSuite>{
|
||||
const response = await api.getLedger(request)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.withPartial, 'getLedger')
|
||||
},
|
||||
'pre 2014 with partial payment': async api => {
|
||||
'pre 2014 with partial payment': async (api) => {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeAllData: true,
|
||||
@@ -69,7 +69,7 @@ export default <TestSuite>{
|
||||
'getLedger'
|
||||
)
|
||||
},
|
||||
'full, then computeLedgerHash': async api => {
|
||||
'full, then computeLedgerHash': async (api) => {
|
||||
const request = {
|
||||
includeTransactions: true,
|
||||
includeState: true,
|
||||
|
||||
@@ -108,17 +108,21 @@ export default <TestSuite>{
|
||||
address,
|
||||
requests.getOrderbook.normal
|
||||
)
|
||||
const bidRates = response.bids.map(bid => bid.properties.makerExchangeRate)
|
||||
const askRates = response.asks.map(ask => ask.properties.makerExchangeRate)
|
||||
const bidRates = response.bids.map(
|
||||
(bid) => bid.properties.makerExchangeRate
|
||||
)
|
||||
const askRates = response.asks.map(
|
||||
(ask) => ask.properties.makerExchangeRate
|
||||
)
|
||||
// makerExchangeRate = quality = takerPays.value/takerGets.value
|
||||
// so the best deal for the taker is the lowest makerExchangeRate
|
||||
// bids and asks should be sorted so that the best deals come first
|
||||
assert.deepEqual(
|
||||
bidRates.sort(x => Number(x)),
|
||||
bidRates.sort((x) => Number(x)),
|
||||
bidRates
|
||||
)
|
||||
assert.deepEqual(
|
||||
askRates.sort(x => Number(x)),
|
||||
askRates.sort((x) => Number(x)),
|
||||
askRates
|
||||
)
|
||||
},
|
||||
@@ -128,7 +132,7 @@ export default <TestSuite>{
|
||||
address,
|
||||
requests.getOrderbook.normal
|
||||
)
|
||||
;[...response.bids, ...response.asks].forEach(order => {
|
||||
;[...response.bids, ...response.asks].forEach((order) => {
|
||||
const quantity = order.specification.quantity
|
||||
const totalPrice = order.specification.totalPrice
|
||||
const {base, counter} = requests.getOrderbook.normal
|
||||
@@ -144,7 +148,7 @@ export default <TestSuite>{
|
||||
address,
|
||||
requests.getOrderbook.normal
|
||||
)
|
||||
assert(response.bids.every(bid => bid.specification.direction === 'buy'))
|
||||
assert(response.asks.every(ask => ask.specification.direction === 'sell'))
|
||||
assert(response.bids.every((bid) => bid.specification.direction === 'buy'))
|
||||
assert(response.asks.every((ask) => ask.specification.direction === 'sell'))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,11 @@ const {getPaths: RESPONSE_FIXTURES} = responses
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
'simple test': async api => {
|
||||
'simple test': async (api) => {
|
||||
const response = await api.getPaths(REQUEST_FIXTURES.normal)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.XrpToUsd, 'getPaths')
|
||||
},
|
||||
'queuing': async api => {
|
||||
'queuing': async (api) => {
|
||||
const [normalResult, usdOnlyResult, xrpOnlyResult] = await Promise.all([
|
||||
api.getPaths(REQUEST_FIXTURES.normal),
|
||||
api.getPaths(REQUEST_FIXTURES.UsdToUsd),
|
||||
@@ -30,56 +30,56 @@ export default <TestSuite>{
|
||||
// need decide what to do with currencies/XRP:
|
||||
// if add 'XRP' in currencies, then there will be exception in
|
||||
// xrpToDrops function (called from toRippledAmount)
|
||||
'getPaths USD 2 USD': async api => {
|
||||
'getPaths USD 2 USD': async (api) => {
|
||||
const response = await api.getPaths(REQUEST_FIXTURES.UsdToUsd)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.UsdToUsd, 'getPaths')
|
||||
},
|
||||
'getPaths XRP 2 XRP': async api => {
|
||||
'getPaths XRP 2 XRP': async (api) => {
|
||||
const response = await api.getPaths(REQUEST_FIXTURES.XrpToXrp)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.XrpToXrp, 'getPaths')
|
||||
},
|
||||
'source with issuer': async api => {
|
||||
'source with issuer': async (api) => {
|
||||
return assertRejects(
|
||||
api.getPaths(REQUEST_FIXTURES.issuer),
|
||||
api.errors.NotFoundError
|
||||
)
|
||||
},
|
||||
'XRP 2 XRP - not enough': async api => {
|
||||
'XRP 2 XRP - not enough': async (api) => {
|
||||
return assertRejects(
|
||||
api.getPaths(REQUEST_FIXTURES.XrpToXrpNotEnough),
|
||||
api.errors.NotFoundError
|
||||
)
|
||||
},
|
||||
'invalid PathFind': async api => {
|
||||
'invalid PathFind': async (api) => {
|
||||
assert.throws(() => {
|
||||
api.getPaths(REQUEST_FIXTURES.invalid)
|
||||
}, /Cannot specify both source.amount/)
|
||||
},
|
||||
'does not accept currency': async api => {
|
||||
'does not accept currency': async (api) => {
|
||||
return assertRejects(
|
||||
api.getPaths(REQUEST_FIXTURES.NotAcceptCurrency),
|
||||
api.errors.NotFoundError
|
||||
)
|
||||
},
|
||||
'no paths': async api => {
|
||||
'no paths': async (api) => {
|
||||
return assertRejects(
|
||||
api.getPaths(REQUEST_FIXTURES.NoPaths),
|
||||
api.errors.NotFoundError
|
||||
)
|
||||
},
|
||||
'no paths source amount': async api => {
|
||||
'no paths source amount': async (api) => {
|
||||
return assertRejects(
|
||||
api.getPaths(REQUEST_FIXTURES.NoPathsSource),
|
||||
api.errors.NotFoundError
|
||||
)
|
||||
},
|
||||
'no paths with source currencies': async api => {
|
||||
'no paths with source currencies': async (api) => {
|
||||
return assertRejects(
|
||||
api.getPaths(REQUEST_FIXTURES.NoPathsWithCurrencies),
|
||||
api.errors.NotFoundError
|
||||
)
|
||||
},
|
||||
'error: srcActNotFound': async api => {
|
||||
'error: srcActNotFound': async (api) => {
|
||||
return assertRejects(
|
||||
api.getPaths({
|
||||
...REQUEST_FIXTURES.normal,
|
||||
@@ -88,7 +88,7 @@ export default <TestSuite>{
|
||||
api.errors.RippleError
|
||||
)
|
||||
},
|
||||
'send all': async api => {
|
||||
'send all': async (api) => {
|
||||
const response = await api.getPaths(REQUEST_FIXTURES.sendAll)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.sendAll, 'getPaths')
|
||||
}
|
||||
|
||||
@@ -69,6 +69,13 @@ export default <TestSuite>{
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.order, 'getTransaction')
|
||||
},
|
||||
|
||||
'order with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_OFFER_CREATE_TRANSACTION_HASH
|
||||
closeLedger(api.connection)
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.orderWithMemo, 'getTransaction')
|
||||
},
|
||||
|
||||
'sell order': async (api, address) => {
|
||||
const hash =
|
||||
'458101D51051230B1D56E9ACAFAA34451BF65FA000F95DF6F0FF5B3A62D83FC2'
|
||||
@@ -100,6 +107,17 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'order cancellation with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_ORDER_CANCELLATION_TRANSACTION_HASH
|
||||
closeLedger(api.connection)
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(
|
||||
response,
|
||||
RESPONSE_FIXTURES.orderCancellationWithMemo,
|
||||
'getTransaction'
|
||||
)
|
||||
},
|
||||
|
||||
'trustline set': async (api, address) => {
|
||||
const hash =
|
||||
'635A0769BD94710A1F6A76CDE65A3BC661B20B798807D1BBBDADCEA26420538D'
|
||||
@@ -258,6 +276,12 @@ export default <TestSuite>{
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.checkCreate, 'getTransaction')
|
||||
},
|
||||
|
||||
'CheckCreate with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_CHECK_CREATE_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.checkCreateWithMemo, 'getTransaction')
|
||||
},
|
||||
|
||||
'CheckCancel': async (api, address) => {
|
||||
const hash =
|
||||
'B4105D1B2D83819647E4692B7C5843D674283F669524BD50C9614182E3A12CD4'
|
||||
@@ -265,6 +289,12 @@ export default <TestSuite>{
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.checkCancel, 'getTransaction')
|
||||
},
|
||||
|
||||
'CheckCancel with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_CHECK_CANCEL_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.checkCancelWithMemo, 'getTransaction')
|
||||
},
|
||||
|
||||
'CheckCash': async (api, address) => {
|
||||
const hash =
|
||||
'8321208465F70BA52C28BCC4F646BAF3B012BA13B57576C0336F42D77E3E0749'
|
||||
@@ -272,6 +302,12 @@ export default <TestSuite>{
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.checkCash, 'getTransaction')
|
||||
},
|
||||
|
||||
'CheckCash with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_CHECK_CASH_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.checkCashWithMemo, 'getTransaction')
|
||||
},
|
||||
|
||||
// Escrows
|
||||
'EscrowCreation': async (api, address) => {
|
||||
const hash =
|
||||
@@ -332,6 +368,16 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'PaymentChannelCreate with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_PAYMENT_CHANNEL_CREATE_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(
|
||||
response,
|
||||
RESPONSE_FIXTURES.paymentChannelCreateWithMemo,
|
||||
'getTransaction'
|
||||
)
|
||||
},
|
||||
|
||||
'PaymentChannelFund': async (api, address) => {
|
||||
const hash =
|
||||
'CD053D8867007A6A4ACB7A432605FE476D088DCB515AFFC886CF2B4EB6D2AE8B'
|
||||
@@ -343,6 +389,16 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'PaymentChannelFund with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_PAYMENT_CHANNEL_FUND_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(
|
||||
response,
|
||||
RESPONSE_FIXTURES.paymentChannelFundWithMemo,
|
||||
'getTransaction'
|
||||
)
|
||||
},
|
||||
|
||||
'PaymentChannelClaim': async (api, address) => {
|
||||
const hash =
|
||||
'81B9ECAE7195EB6E8034AEDF44D8415A7A803E14513FDBB34FA984AB37D59563'
|
||||
@@ -354,6 +410,16 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'PaymentChannelClaim with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_PAYMENT_CHANNEL_CLAIM_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(
|
||||
response,
|
||||
RESPONSE_FIXTURES.paymentChannelClaimWithMemo,
|
||||
'getTransaction'
|
||||
)
|
||||
},
|
||||
|
||||
'AccountDelete': async (api, address) => {
|
||||
const hash =
|
||||
'EC2AB14028DC84DE525470AB4DAAA46358B50A8662C63804BFF38244731C0CB9'
|
||||
@@ -365,6 +431,16 @@ export default <TestSuite>{
|
||||
)
|
||||
},
|
||||
|
||||
'AccountDelete with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_ACCOUNT_DELETE_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(
|
||||
response,
|
||||
RESPONSE_FIXTURES.accountDeleteWithMemo,
|
||||
'getTransaction'
|
||||
)
|
||||
},
|
||||
|
||||
'no Meta': async (api, address) => {
|
||||
const hash =
|
||||
'AFB3ADF22F3C605E23FAEFAA185F3BD763C4692CAC490D9819D117CD33BFAA1B'
|
||||
@@ -396,5 +472,37 @@ export default <TestSuite>{
|
||||
'C6A40F56127436DCD830B1B35FF939FD05B5747D30D6542572B7A835239817AF'
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.feeUpdate)
|
||||
},
|
||||
|
||||
'feeUpdate with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_FEE_UPDATE_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.feeUpdateWithMemo)
|
||||
},
|
||||
|
||||
'order with one memo': async (api, address) => {
|
||||
const hash =
|
||||
'995570FE1E40F42DF56BFC80503BA9E3C1229619C61A1C279A76BC0805036D74'
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.withMemo)
|
||||
},
|
||||
|
||||
'order with more than one memo': async (api, address) => {
|
||||
const hash =
|
||||
'995570FE1E40F42DF56BFC80503BA9E3C1229619C61A1C279A76BC0805036D73'
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.withMemos)
|
||||
},
|
||||
|
||||
'ticketCreate with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_TICKET_CREATE_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.ticketCreateWithMemo)
|
||||
},
|
||||
|
||||
'depositPreauth with memo': async (api, address) => {
|
||||
const hash = hashes.WITH_MEMOS_DEPOSIT_PREAUTH_TRANSACTION_HASH
|
||||
const response = await api.getTransaction(hash)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.depositPreauthWithMemo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ export default <TestSuite>{
|
||||
const response = await api.getTransactions(address, options)
|
||||
hack(response)
|
||||
assert.strictEqual(response.length, 10)
|
||||
response.forEach(t => assert(t.type === 'payment' || t.type === 'order'))
|
||||
response.forEach(t => assert(t.outcome.result === 'tesSUCCESS'))
|
||||
response.forEach((t) => assert(t.type === 'payment' || t.type === 'order'))
|
||||
response.forEach((t) => assert(t.outcome.result === 'tesSUCCESS'))
|
||||
},
|
||||
|
||||
'filters for incoming': async (api, address) => {
|
||||
@@ -115,8 +115,8 @@ export default <TestSuite>{
|
||||
const response = await api.getTransactions(address, options)
|
||||
hack(response)
|
||||
assert.strictEqual(response.length, 10)
|
||||
response.forEach(t => assert(t.type === 'payment' || t.type === 'order'))
|
||||
response.forEach(t => assert(t.outcome.result === 'tesSUCCESS'))
|
||||
response.forEach((t) => assert(t.type === 'payment' || t.type === 'order'))
|
||||
response.forEach((t) => assert(t.outcome.result === 'tesSUCCESS'))
|
||||
},
|
||||
|
||||
// this is the case where core.RippleError just falls
|
||||
@@ -155,6 +155,7 @@ export default <TestSuite>{
|
||||
const response = await api.getTransactions(addresses.OTHER_ACCOUNT)
|
||||
assertResultMatch(response, RESPONSE_FIXTURES.one, 'getTransactions')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// This test relies on the binary (hex string) format, but computed fields like `date`
|
||||
@@ -162,7 +163,7 @@ export default <TestSuite>{
|
||||
// the expected response. Long term, a better approach would be to use/test the json
|
||||
// format responses, instead of the binary.
|
||||
function hack(response) {
|
||||
response.forEach(element => {
|
||||
response.forEach((element) => {
|
||||
element.outcome.timestamp = '2019-04-01T07:39:01.000Z'
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import requests from '../../fixtures/requests'
|
||||
import responses from '../../fixtures/responses'
|
||||
import {assertResultMatch, TestSuite} from '../../utils'
|
||||
const instructionsWithMaxLedgerVersionOffset = {maxLedgerVersionOffset: 100}
|
||||
|
||||
/**
|
||||
* Every test suite exports their tests in the default object.
|
||||
@@ -8,11 +9,25 @@ import {assertResultMatch, TestSuite} from '../../utils'
|
||||
* - Check out "test/api/index.ts" for more information about the test runner.
|
||||
*/
|
||||
export default <TestSuite>{
|
||||
prepareCheckCancel: async (api, address) => {
|
||||
'prepareCheckCancel': async (api, address) => {
|
||||
const result = await api.prepareCheckCancel(
|
||||
address,
|
||||
requests.prepareCheckCancel.normal
|
||||
)
|
||||
assertResultMatch(result, responses.prepareCheckCancel.normal, 'prepare')
|
||||
},
|
||||
|
||||
'with ticket': async (api, address) => {
|
||||
const localInstructions = {
|
||||
...instructionsWithMaxLedgerVersionOffset,
|
||||
maxFee: '0.000012',
|
||||
ticketSequence: 23
|
||||
}
|
||||
const result = await api.prepareCheckCancel(
|
||||
address,
|
||||
requests.prepareCheckCancel.normal,
|
||||
localInstructions
|
||||
)
|
||||
assertResultMatch(result, responses.prepareCheckCancel.ticket, 'prepare')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import requests from '../../fixtures/requests'
|
||||
import responses from '../../fixtures/responses'
|
||||
import {assertResultMatch, TestSuite} from '../../utils'
|
||||
const instructionsWithMaxLedgerVersionOffset = {maxLedgerVersionOffset: 100}
|
||||
|
||||
/**
|
||||
* Every test suite exports their tests in the default object.
|
||||
@@ -22,5 +23,19 @@ export default <TestSuite>{
|
||||
requests.prepareCheckCash.deliverMin
|
||||
)
|
||||
assertResultMatch(result, responses.prepareCheckCash.deliverMin, 'prepare')
|
||||
},
|
||||
|
||||
'with ticket': async (api, address) => {
|
||||
const localInstructions = {
|
||||
...instructionsWithMaxLedgerVersionOffset,
|
||||
maxFee: '0.000012',
|
||||
ticketSequence: 23
|
||||
}
|
||||
const result = await api.prepareCheckCash(
|
||||
address,
|
||||
requests.prepareCheckCash.amount,
|
||||
localInstructions
|
||||
)
|
||||
assertResultMatch(result, responses.prepareCheckCash.ticket, 'prepare')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,5 +28,19 @@ export default <TestSuite>{
|
||||
requests.prepareCheckCreate.full
|
||||
)
|
||||
assertResultMatch(result, responses.prepareCheckCreate.full, 'prepare')
|
||||
},
|
||||
|
||||
'prepareCheckCreate with ticket': async (api, address) => {
|
||||
const localInstructions = {
|
||||
...instructionsWithMaxLedgerVersionOffset,
|
||||
maxFee: '0.000012',
|
||||
ticketSequence: 23
|
||||
}
|
||||
const result = await api.prepareCheckCreate(
|
||||
address,
|
||||
requests.prepareCheckCreate.normal,
|
||||
localInstructions
|
||||
)
|
||||
assertResultMatch(result, responses.prepareCheckCreate.ticket, 'prepare')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,5 +32,23 @@ export default <TestSuite>{
|
||||
responses.prepareEscrowCancellation.memos,
|
||||
'prepare'
|
||||
)
|
||||
},
|
||||
|
||||
'with ticket': async (api, address) => {
|
||||
const localInstructions = {
|
||||
...instructionsWithMaxLedgerVersionOffset,
|
||||
maxFee: '0.000012',
|
||||
ticketSequence: 23
|
||||
}
|
||||
const result = await api.prepareEscrowCancellation(
|
||||
address,
|
||||
requests.prepareEscrowCancellation.normal,
|
||||
localInstructions
|
||||
)
|
||||
assertResultMatch(
|
||||
result,
|
||||
responses.prepareEscrowCancellation.ticket,
|
||||
'prepare'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,5 +45,19 @@ export default <TestSuite>{
|
||||
api.errors.ValidationError,
|
||||
'instance.escrowCreation requires property "amount"'
|
||||
)
|
||||
},
|
||||
|
||||
'with ticket': async (api, address) => {
|
||||
const localInstructions = {
|
||||
...instructionsWithMaxLedgerVersionOffset,
|
||||
maxFee: '0.000396',
|
||||
ticketSequence: 23
|
||||
}
|
||||
const result = await api.prepareEscrowCreation(
|
||||
address,
|
||||
requests.prepareEscrowCreation.normal,
|
||||
localInstructions
|
||||
)
|
||||
assertResultMatch(result, responses.prepareEscrowCreation.ticket, 'prepare')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,5 +56,23 @@ export default <TestSuite>{
|
||||
api.errors.ValidationError,
|
||||
'"condition" and "fulfillment" fields on EscrowFinish must only be specified together.'
|
||||
)
|
||||
},
|
||||
|
||||
'with ticket': async (api, address) => {
|
||||
const localInstructions = {
|
||||
...instructionsWithMaxLedgerVersionOffset,
|
||||
maxFee: '0.000396',
|
||||
ticketSequence: 23
|
||||
}
|
||||
const result = await api.prepareEscrowExecution(
|
||||
address,
|
||||
requests.prepareEscrowExecution.normal,
|
||||
localInstructions
|
||||
)
|
||||
assertResultMatch(
|
||||
result,
|
||||
responses.prepareEscrowExecution.ticket,
|
||||
'prepare'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user