Compare commits

...

35 Commits
1.5.0 ... 1.6.0

Author SHA1 Message Date
Hans Bergren
3c534d87c0 Release 1.6.0 2020-01-06 16:47:44 -05:00
Elliot Lee
138e7942da Add support for AccountDelete (#1120)
https://xrpl.org/accountdelete.html
2020-01-06 04:01:10 -08:00
dependabot-preview[bot]
23504821cf Bump @typescript-eslint/parser from 2.13.0 to 2.14.0 (#1147)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 2.13.0 to 2.14.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.14.0/packages/parser)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-05 19:54:52 -08:00
dependabot-preview[bot]
b09da3e8f1 Bump @types/node from 13.1.1 to 13.1.2 (#1148)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 13.1.1 to 13.1.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-01-05 19:54:37 -08:00
Elliot Lee
f3dd2fec99 Merge pull request #1098 from nickewansmith/fix-possible-unhandled-throw-on-send
Adds unit test for ripple#1092, fixes unhandled throw on upgraded ws send
2020-01-05 19:51:58 -08:00
Elliot Lee
462e375800 Add ripple-latest.js to npm 2019-12-28 11:51:00 -08:00
Elliot Lee
ca8c881375 Release 1.5.1 2019-12-28 11:47:57 -08:00
Elliot Lee
96605a57d4 Fix CDNs (#1142)
* Rename browser build output file to ripple-latest-min.js (the name prior to #1061)
* Add unpkg and jsdelivr
2019-12-28 11:46:52 -08:00
FKSRipple
491ce40081 Merge pull request #1136 from ripple/dependabot/npm_and_yarn/typescript-eslint/eslint-plugin-2.13.0
Bump @typescript-eslint/eslint-plugin from 2.5.0 to 2.13.0
2019-12-27 12:46:00 -08:00
FKSRipple
f33eb07bdd Merge pull request #1135 from ripple/dependabot/npm_and_yarn/https-proxy-agent-4.0.0
Bump https-proxy-agent from 3.0.1 to 4.0.0
2019-12-27 10:16:58 -08:00
dependabot-preview[bot]
8bb1dc9b47 Bump @typescript-eslint/eslint-plugin from 2.5.0 to 2.13.0
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 2.5.0 to 2.13.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.13.0/packages/eslint-plugin)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-27 18:03:54 +00:00
FKSRipple
78b50472da Merge pull request #1137 from ripple/dependabot/npm_and_yarn/types/node-13.1.1
Bump @types/node from 12.12.5 to 13.1.1
2019-12-27 10:02:11 -08:00
FKSRipple
e0259b37ed Merge pull request #1138 from ripple/dependabot/npm_and_yarn/eventemitter2-6.0.0
Bump eventemitter2 from 5.0.1 to 6.0.0
2019-12-27 10:01:27 -08:00
FKSRipple
bf863a2594 Merge pull request #1139 from ripple/dependabot/npm_and_yarn/typescript-eslint/parser-2.13.0
Bump @typescript-eslint/parser from 2.5.0 to 2.13.0
2019-12-27 10:01:10 -08:00
FKSRipple
edd174881e Merge pull request #1134 from ripple/dependabot/npm_and_yarn/types/lodash-4.14.149
Bump @types/lodash from 4.14.144 to 4.14.149
2019-12-27 09:57:50 -08:00
dependabot-preview[bot]
0bf747f6fc Bump @typescript-eslint/parser from 2.5.0 to 2.13.0
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 2.5.0 to 2.13.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v2.13.0/packages/parser)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-27 13:40:22 +00:00
dependabot-preview[bot]
ab4d2b5d58 Bump eventemitter2 from 5.0.1 to 6.0.0
Bumps [eventemitter2](https://github.com/hij1nx/EventEmitter2) from 5.0.1 to 6.0.0.
- [Release notes](https://github.com/hij1nx/EventEmitter2/releases)
- [Changelog](https://github.com/EventEmitter2/EventEmitter2/blob/master/CHANGELOG.md)
- [Commits](https://github.com/hij1nx/EventEmitter2/commits/v6.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-27 13:39:54 +00:00
dependabot-preview[bot]
1b81280358 Bump mocha from 6.2.0 to 6.2.2 (#1126)
Bumps [mocha](https://github.com/mochajs/mocha) from 6.2.0 to 6.2.2.
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v6.2.0...v6.2.2)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-26 12:49:31 -08:00
dependabot-preview[bot]
32f4eea3b8 Bump @types/node from 12.12.5 to 13.1.1
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 12.12.5 to 13.1.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-26 20:21:14 +00:00
dependabot-preview[bot]
1a3a49decb Bump @types/ws from 6.0.3 to 6.0.4 (#1125)
Bumps [@types/ws](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/ws) from 6.0.3 to 6.0.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/ws)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-26 12:19:32 -08:00
dependabot-preview[bot]
416717aff6 Bump https-proxy-agent from 3.0.1 to 4.0.0
Bumps [https-proxy-agent](https://github.com/TooTallNate/node-https-proxy-agent) from 3.0.1 to 4.0.0.
- [Release notes](https://github.com/TooTallNate/node-https-proxy-agent/releases)
- [Commits](https://github.com/TooTallNate/node-https-proxy-agent/compare/3.0.1...4.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-24 13:53:06 +00:00
dependabot-preview[bot]
769d955a40 Bump @types/lodash from 4.14.144 to 4.14.149
Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.14.144 to 4.14.149.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-24 13:52:42 +00:00
dependabot-preview[bot]
6de85b841d Bump ts-node from 8.4.1 to 8.5.4 (#1124)
Bumps [ts-node](https://github.com/TypeStrong/ts-node) from 8.4.1 to 8.5.4.
- [Release notes](https://github.com/TypeStrong/ts-node/releases)
- [Commits](https://github.com/TypeStrong/ts-node/compare/v8.4.1...v8.5.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-23 11:40:59 -08:00
dependabot-preview[bot]
b4c6af29e4 Bump webpack from 4.41.2 to 4.41.4 (#1123)
Bumps [webpack](https://github.com/webpack/webpack) from 4.41.2 to 4.41.4.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v4.41.2...v4.41.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-22 15:53:29 -08:00
dependabot-preview[bot]
7192606e21 Bump webpack-cli from 3.3.9 to 3.3.10 (#1122)
Bumps [webpack-cli](https://github.com/webpack/webpack-cli) from 3.3.9 to 3.3.10.
- [Release notes](https://github.com/webpack/webpack-cli/releases)
- [Changelog](https://github.com/webpack/webpack-cli/blob/v3.3.10/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-cli/compare/v3.3.9...v3.3.10)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-12-22 15:52:01 -08:00
FKSRipple
f5196389e8 Merge pull request #1116 from ripple/prettier-format
run prettier format
2019-12-18 11:25:14 -08:00
FKSRipple
27be06c5c9 Merge pull request #1118 from ripple/eslint-fix
Fix incorrect eslint matching glob
2019-12-18 11:24:52 -08:00
Fred K. Schott
1d3ddb5e85 update eslint command 2019-12-17 10:36:32 -08:00
Fred K. Schott
2145c104fd run prettier format 2019-12-17 10:35:59 -08:00
FKSRipple
64e0d098e7 Merge pull request #1115 from ripple/connection-cleanup-config
Cleanup the connection config state
2019-12-17 10:31:04 -08:00
FKSRipple
50d8cbb0ee Merge pull request #1114 from ripple/connection-cleanup-trace
Cleanup the connection trace logic
2019-12-17 08:58:44 -08:00
Fred K. Schott
9580397558 cleanup the connection config 2019-12-16 17:26:00 -08:00
Fred K. Schott
cf544b74f5 fix msg abbreviation 2019-12-16 17:25:21 -08:00
Fred K. Schott
312f831efb cleanup the connection trace logic 2019-12-16 11:57:51 -08:00
Nicholas Smith
fa6a2c5bbb Adds unit test for ripple#1092, fixes unhandled throw when not connected on send due to upgraded ws module 2019-11-17 01:28:19 -05:00
188 changed files with 4656 additions and 3521 deletions

View File

@@ -20,6 +20,7 @@
"no-useless-constructor": 0,
"no-unused-vars": 0,
"no-prototype-builtins": 0,
"require-atomic-updates": 0
"require-atomic-updates": 0,
"no-dupe-class-members": 0
}
}

View File

@@ -5,5 +5,6 @@
"semi": false,
"singleQuote": true,
"trailingComma": "none",
"quoteProps": "consistent"
"quoteProps": "consistent",
"bracketSpacing": false
}

View File

@@ -1,5 +1,25 @@
# ripple-lib Release History
## 1.6.0 (2020-01-06)
* Add support for AccountDelete (#1120)
* Improve error type given on rejected message _send to be DisconnectedError (#1098)
* Internal
* Add unit test for unhandled promise rejection warning on message _send (#1098)
* Dependencies
* Update @types/node, @typescript-eslint/parser
## 1.5.1 (2019-12-28)
* Fix support for CDNs (#1142)
* Internal
* Clean up connection trace logic (#1114)
* Clean up the connection config (#1115)
* Run prettier format (#1116)
* Update eslint command (#1118)
* Dependencies
* Update webpack-cli, webpack, ts-node, @types/lodash, @types/ws, @types/node, @typescript-eslint/parser, @typescript-eslint/eslint-plugin, https-proxy-agent, mocha, eventemitter2
## 1.5.0 (2019-12-14)
* Add support for `WalletLocator` (#1083)

View File

@@ -4511,7 +4511,7 @@ Prepare a transaction. The prepared transaction must subsequently be [signed](#s
This method works with any of [the transaction types supported by rippled](https://developers.ripple.com/transaction-types.html).
Notably, this is the preferred method for preparing a `DepositPreauth` transaction (added in rippled 1.1.0).
Notably, this is the preferred method for preparing `DepositPreauth` or `AccountDelete` transactions.
### Parameters

View File

@@ -6,7 +6,7 @@ Prepare a transaction. The prepared transaction must subsequently be [signed](#s
This method works with any of [the transaction types supported by rippled](https://developers.ripple.com/transaction-types.html).
Notably, this is the preferred method for preparing a `DepositPreauth` transaction (added in rippled 1.1.0).
Notably, this is the preferred method for preparing `DepositPreauth` or `AccountDelete` transactions.
### Parameters

View File

@@ -1,13 +1,16 @@
{
"name": "ripple-lib",
"version": "1.5.0",
"version": "1.6.0",
"license": "ISC",
"description": "A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser",
"files": [
"dist/npm/*",
"build/ripple-latest-min.js"
"build/ripple-latest-min.js",
"build/ripple-latest.js"
],
"main": "dist/npm/",
"unpkg": "build/ripple-latest-min.js",
"jsdelivr": "build/ripple-latest-min.js",
"types": "dist/npm/index.d.ts",
"browser": {
"ws": "./dist/npm/common/wswrapper.js",
@@ -20,7 +23,7 @@
"@types/lodash": "^4.14.136",
"@types/ws": "^6.0.3",
"bignumber.js": "^9.0.0",
"https-proxy-agent": "^3.0.0",
"https-proxy-agent": "^4.0.0",
"jsonschema": "1.2.2",
"lodash": "^4.17.4",
"lodash.isequal": "^4.5.0",
@@ -32,18 +35,19 @@
},
"devDependencies": {
"@types/mocha": "^5.2.7",
"@types/node": "^12.12.5",
"@types/node": "^13.1.1",
"@typescript-eslint/eslint-plugin": "^2.3.3",
"@typescript-eslint/parser": "^2.3.3",
"assert-diff": "^2.0.3",
"doctoc": "^0.15.0",
"ejs": "^2.3.4",
"eslint": "^6.5.1",
"eventemitter2": "^5.0.1",
"eventemitter2": "^6.0.0",
"json-schema-to-markdown-table": "^0.4.0",
"mocha": "6.2.0",
"mocha": "6.2.2",
"mocha-junit-reporter": "^1.9.1",
"nyc": "^14.1.1",
"prettier": "^1.19.1",
"ts-node": "^8.4.1",
"typescript": "^3.6.4",
"webpack": "^4.41.2",
@@ -63,7 +67,8 @@
"prepublish": "yarn clean && yarn build",
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --exit",
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --watch --reporter dot",
"lint": "eslint src/**/*.ts 'test/*-test.{ts,js}'",
"format": "prettier --write '{src,test}/**/*.ts'",
"lint": "eslint 'src/**/*.ts' 'test/*-test.{ts,js}'",
"perf": "./scripts/perf_test.sh",
"start": "node scripts/http.js"
},

View File

@@ -24,8 +24,7 @@ import getBalances from './ledger/balances'
import getBalanceSheet from './ledger/balance-sheet'
import getPaths from './ledger/pathfind'
import getOrders from './ledger/orders'
import {getOrderbook,
formatBidsAndAsks} from './ledger/orderbook'
import {getOrderbook, formatBidsAndAsks} from './ledger/orderbook'
import {getSettings, parseAccountFlags} from './ledger/settings'
import getAccountInfo from './ledger/accountinfo'
import getAccountObjects from './ledger/accountobjects'
@@ -47,7 +46,11 @@ import prepareSettings from './transaction/settings'
import sign from './transaction/sign'
import combine from './transaction/combine'
import submit from './transaction/submit'
import {generateAddressAPI, GenerateAddressOptions, GeneratedAddress} from './offline/generate-address'
import {
generateAddressAPI,
GenerateAddressOptions,
GeneratedAddress
} from './offline/generate-address'
import {deriveKeypair, deriveAddress, deriveXAddress} from './offline/derive'
import computeLedgerHash from './offline/ledgerhash'
import signPaymentChannelClaim from './offline/sign-payment-channel-claim'
@@ -55,19 +58,28 @@ import verifyPaymentChannelClaim from './offline/verify-payment-channel-claim'
import getLedger from './ledger/ledger'
import {
AccountObjectsRequest, AccountObjectsResponse,
AccountOffersRequest, AccountOffersResponse,
AccountInfoRequest, AccountInfoResponse,
AccountLinesRequest, AccountLinesResponse,
BookOffersRequest, BookOffersResponse,
GatewayBalancesRequest, GatewayBalancesResponse,
LedgerRequest, LedgerResponse,
LedgerDataRequest, LedgerDataResponse,
LedgerEntryRequest, LedgerEntryResponse,
ServerInfoRequest, ServerInfoResponse,
AccountObjectsRequest,
AccountObjectsResponse,
AccountOffersRequest,
AccountOffersResponse,
AccountInfoRequest,
AccountInfoResponse,
AccountLinesRequest,
AccountLinesResponse,
BookOffersRequest,
BookOffersResponse,
GatewayBalancesRequest,
GatewayBalancesResponse,
LedgerRequest,
LedgerResponse,
LedgerDataRequest,
LedgerDataResponse,
LedgerEntryRequest,
LedgerEntryResponse,
ServerInfoRequest,
ServerInfoResponse
} from './common/types/commands'
import RangeSet from './common/rangeset'
import * as ledgerUtils from './ledger/utils'
import * as transactionUtils from './transaction/utils'
@@ -75,15 +87,14 @@ import * as schemaValidator from './common/schema-validator'
import {getServerInfo, getFee} from './common/serverinfo'
import {clamp, renameCounterpartyToIssuer} from './ledger/utils'
import {TransactionJSON, Instructions, Prepare} from './transaction/types'
import {ConnectionOptions} from './common/connection'
import {ConnectionUserOptions} from './common/connection'
import {isValidXAddress, isValidClassicAddress} from 'ripple-address-codec'
export interface APIOptions extends ConnectionOptions {
server?: string,
feeCushion?: number,
maxFeeXRP?: string,
trace?: boolean,
proxy?: string,
export interface APIOptions extends ConnectionUserOptions {
server?: string
feeCushion?: number
maxFeeXRP?: string
proxy?: string
timeout?: number
}
@@ -92,7 +103,7 @@ export interface APIOptions extends ConnectionOptions {
* command. This varies from command to command, but we need to know it to
* properly count across many requests.
*/
function getCollectKeyFromCommand(command: string): string|undefined {
function getCollectKeyFromCommand(command: string): string | undefined {
switch (command) {
case 'account_offers':
case 'book_offers':
@@ -141,12 +152,12 @@ class RippleAPI extends EventEmitter {
this.emit('connected')
})
this.connection.on('disconnected', code => {
let finalCode = code;
// This is a backwards-compatible fix for this change in the ws library:
let finalCode = code
// This is a backwards-compatible fix for this change in the ws library:
// https://github.com/websockets/ws/issues/1257
// TODO: Remove in next major, breaking version
if (finalCode === 1005) {
finalCode = 1000;
finalCode = 1000
}
this.emit('disconnected', finalCode)
})
@@ -157,33 +168,51 @@ class RippleAPI extends EventEmitter {
}
}
/**
* Makes a request to the API with the given command and
* additional request body parameters.
*/
async request(command: 'account_info', params: AccountInfoRequest):
Promise<AccountInfoResponse>
async request(command: 'account_lines', params: AccountLinesRequest):
Promise<AccountLinesResponse>
async request(command: 'account_objects', params: AccountObjectsRequest):
Promise<AccountObjectsResponse>
async request(command: 'account_offers', params: AccountOffersRequest):
Promise<AccountOffersResponse>
async request(command: 'book_offers', params: BookOffersRequest):
Promise<BookOffersResponse>
async request(command: 'gateway_balances', params: GatewayBalancesRequest):
Promise<GatewayBalancesResponse>
async request(command: 'ledger', params: LedgerRequest):
Promise<LedgerResponse>
async request(command: 'ledger_data', params?: LedgerDataRequest):
Promise<LedgerDataResponse>
async request(command: 'ledger_entry', params: LedgerEntryRequest):
Promise<LedgerEntryResponse>
async request(command: 'server_info', params?: ServerInfoRequest):
Promise<ServerInfoResponse>
async request(command: string, params: any):
Promise<any>
async request(
command: 'account_info',
params: AccountInfoRequest
): Promise<AccountInfoResponse>
async request(
command: 'account_lines',
params: AccountLinesRequest
): Promise<AccountLinesResponse>
async request(
command: 'account_objects',
params: AccountObjectsRequest
): Promise<AccountObjectsResponse>
async request(
command: 'account_offers',
params: AccountOffersRequest
): Promise<AccountOffersResponse>
async request(
command: 'book_offers',
params: BookOffersRequest
): Promise<BookOffersResponse>
async request(
command: 'gateway_balances',
params: GatewayBalancesRequest
): Promise<GatewayBalancesResponse>
async request(
command: 'ledger',
params: LedgerRequest
): Promise<LedgerResponse>
async request(
command: 'ledger_data',
params?: LedgerDataRequest
): Promise<LedgerDataResponse>
async request(
command: 'ledger_entry',
params: LedgerEntryRequest
): Promise<LedgerEntryResponse>
async request(
command: 'server_info',
params?: ServerInfoRequest
): Promise<ServerInfoResponse>
async request(command: string, params: any): Promise<any>
async request(command: string, params: any = {}): Promise<any> {
return this.connection.request({
...params,
@@ -225,8 +254,10 @@ class RippleAPI extends EventEmitter {
*
* You can later submit the transaction with `submit()`.
*/
async prepareTransaction(txJSON: TransactionJSON, instructions: Instructions = {}):
Promise<Prepare> {
async prepareTransaction(
txJSON: TransactionJSON,
instructions: Instructions = {}
): Promise<Prepare> {
return transactionUtils.prepareTransaction(txJSON, this, instructions)
}
@@ -254,16 +285,23 @@ class RippleAPI extends EventEmitter {
* general use. Instead, use rippled's built-in pagination and make multiple
* requests as needed.
*/
async _requestAll(command: 'account_offers', params: AccountOffersRequest):
Promise<AccountOffersResponse[]>
async _requestAll(command: 'book_offers', params: BookOffersRequest):
Promise<BookOffersResponse[]>
async _requestAll(command: 'account_lines', params: AccountLinesRequest):
Promise<AccountLinesResponse[]>
async _requestAll(
command: 'account_offers',
params: AccountOffersRequest
): Promise<AccountOffersResponse[]>
async _requestAll(
command: 'book_offers',
params: BookOffersRequest
): Promise<BookOffersResponse[]>
async _requestAll(
command: 'account_lines',
params: AccountLinesRequest
): Promise<AccountLinesResponse[]>
async _requestAll(
command: string,
params: any = {},
options: {collect?: string} = {}): Promise<any[]> {
options: {collect?: string} = {}
): Promise<any[]> {
// The data under collection is keyed based on the command. Fail if command
// not recognized and collection key not provided.
const collectKey = options.collect || getCollectKeyFromCommand(command)
@@ -272,8 +310,7 @@ class RippleAPI extends EventEmitter {
}
// If limit is not provided, fetches all data over multiple requests.
// NOTE: This may return much more than needed. Set limit when possible.
const countTo: number =
(params.limit !== undefined) ? params.limit : Infinity
const countTo: number = params.limit !== undefined ? params.limit : Infinity
let count: number = 0
let marker: string = params.marker
let lastBatchLength: number
@@ -297,7 +334,7 @@ class RippleAPI extends EventEmitter {
} else {
lastBatchLength = 0
}
} while(!!marker && count < countTo && lastBatchLength !== 0)
} while (!!marker && count < countTo && lastBatchLength !== 0)
return results
}
@@ -375,6 +412,4 @@ class RippleAPI extends EventEmitter {
isValidSecret = schemaValidator.isValidSecret
}
export {
RippleAPI
}
export {RippleAPI}

View File

@@ -1,24 +1,23 @@
import * as _ from 'lodash'
import {RippleAPI, APIOptions} from './api'
class RippleAPIBroadcast extends RippleAPI {
ledgerVersion: number | undefined = undefined
private _apis: RippleAPI[]
constructor(servers, options: APIOptions = {}) {
super(options)
const apis: RippleAPI[] = servers.map(server => new RippleAPI(
_.assign({}, options, {server})
))
const apis: RippleAPI[] = servers.map(
server => new RippleAPI(_.assign({}, options, {server}))
)
// exposed for testing
this._apis = apis
this.getMethodNames().forEach(name => {
this[name] = function() { // eslint-disable-line no-loop-func
this[name] = function() {
// eslint-disable-line no-loop-func
return Promise.race(apis.map(api => api[name](...arguments)))
}
})
@@ -44,13 +43,16 @@ class RippleAPIBroadcast extends RippleAPI {
apis.forEach(api => {
api.on('ledger', this.onLedgerEvent.bind(this))
api.on('error', (errorCode, errorMessage, data) =>
this.emit('error', errorCode, errorMessage, data))
this.emit('error', errorCode, errorMessage, data)
)
})
}
onLedgerEvent(ledger) {
if (ledger.ledgerVersion > this.ledgerVersion ||
this.ledgerVersion === undefined) {
if (
ledger.ledgerVersion > this.ledgerVersion ||
this.ledgerVersion === undefined
) {
this.ledgerVersion = ledger.ledgerVersion
this.emit('ledger', ledger)
}
@@ -68,6 +70,4 @@ class RippleAPIBroadcast extends RippleAPI {
}
}
export {
RippleAPIBroadcast
}
export {RippleAPIBroadcast}

View File

@@ -1,9 +1,9 @@
function setPrototypeOf(object, prototype) {
// Object.setPrototypeOf not supported on Internet Explorer 9
Object.setPrototypeOf ? Object.setPrototypeOf(object, prototype) :
// @ts-ignore: Specifically a fallback for IE9
object.__proto__ = prototype
Object.setPrototypeOf
? Object.setPrototypeOf(object, prototype)
: // @ts-ignore: Specifically a fallback for IE9
(object.__proto__ = prototype)
}
function getConstructorName(object: object): string {
@@ -17,7 +17,4 @@ function getConstructorName(object: object): string {
return functionConstructor ? functionConstructor[1] : classConstructor[1]
}
export {
getConstructorName,
setPrototypeOf
}
export {getConstructorName, setPrototypeOf}

View File

@@ -3,12 +3,21 @@ import {EventEmitter} from 'events'
import {parse as parseUrl} from 'url'
import WebSocket from 'ws'
import RangeSet from './rangeset'
import {RippledError, DisconnectedError, NotConnectedError,
TimeoutError, ResponseFormatError, ConnectionError,
RippledNotInitializedError} from './errors'
import {
RippledError,
DisconnectedError,
NotConnectedError,
TimeoutError,
ResponseFormatError,
ConnectionError,
RippledNotInitializedError
} from './errors'
/**
* ConnectionOptions is the configuration for the configuration object.
*/
export interface ConnectionOptions {
trace?: boolean
trace?: boolean | ((id: string, message: string) => void)
proxy?: string
proxyAuthorization?: string
authorization?: string
@@ -16,64 +25,57 @@ export interface ConnectionOptions {
key?: string
passphrase?: string
certificate?: string
timeout?: number,
connectionTimeout?: number
timeout: number
connectionTimeout: number
}
class Connection extends EventEmitter {
/**
* ConnectionUserOptions is the user-provided configuration object. All configuration
* is optional, so any ConnectionOptions configuration that has a default value is
* still optional for the user to provide.
*/
export type ConnectionUserOptions = Partial<ConnectionOptions>
class Connection extends EventEmitter {
private _url: string
private _trace: boolean
private _console?: Console
private _proxyURL?: string
private _proxyAuthorization?: string
private _authorization?: string
private _trustedCertificates?: string[]
private _key?: string
private _passphrase?: string
private _certificate?: string
private _timeout: number
private _isReady: boolean = false
private _ws: null|WebSocket = null
protected _ledgerVersion: null|number = null
private _ws: null | WebSocket = null
protected _ledgerVersion: null | number = null
private _availableLedgerVersions = new RangeSet()
private _nextRequestID: number = 1
private _retry: number = 0
private _connectTimer: null|NodeJS.Timeout = null
private _retryTimer: null|NodeJS.Timeout = null
private _heartbeatInterval: null|NodeJS.Timeout = null;
private _onOpenErrorBound: null| null|((...args: any[]) => void) = null
private _onUnexpectedCloseBound: null|((...args: any[]) => void) = null
private _fee_base: null|number = null
private _fee_ref: null|number = null
private _connectionTimeout: number
private _connectTimer: null | NodeJS.Timeout = null
private _retryTimer: null | NodeJS.Timeout = null
private _heartbeatInterval: null | NodeJS.Timeout = null
private _onOpenErrorBound: null | null | ((...args: any[]) => void) = null
private _onUnexpectedCloseBound: null | ((...args: any[]) => void) = null
private _fee_base: null | number = null
private _fee_ref: null | number = null
constructor(url, options: ConnectionOptions = {}) {
private _trace: (id: string, message: string) => void = () => {}
private _config: ConnectionOptions
constructor(url?: string, options: ConnectionUserOptions = {}) {
super()
this.setMaxListeners(Infinity)
this._url = url
this._trace = options.trace || false
if (this._trace) {
// for easier unit testing
this._console = console
this._config = {
timeout: 20 * 1000,
connectionTimeout: 2 * 1000,
...options
}
if (typeof options.trace === 'function') {
this._trace = options.trace
} else if (options.trace === true) {
this._trace = console.log
}
this._proxyURL = options.proxy
this._proxyAuthorization = options.proxyAuthorization
this._authorization = options.authorization
this._trustedCertificates = options.trustedCertificates
this._key = options.key
this._passphrase = options.passphrase
this._certificate = options.certificate
this._timeout = options.timeout || (20 * 1000)
this._connectionTimeout = options.connectionTimeout || 2000
}
_updateLedgerVersions(data) {
this._ledgerVersion = Number(data.ledger_index)
if (data.validated_ledgers) {
this._availableLedgerVersions.reset()
this._availableLedgerVersions.parseAndAddRanges(
data.validated_ledgers)
this._availableLedgerVersions.parseAndAddRanges(data.validated_ledgers)
} else {
this._availableLedgerVersions.addValue(this._ledgerVersion)
}
@@ -106,9 +108,7 @@ class Connection extends EventEmitter {
}
_onMessage(message) {
if (this._trace) {
this._console!.log(message)
}
this._trace('receive', message)
let parameters
try {
parameters = this._parseMessage(message)
@@ -155,17 +155,17 @@ class Connection extends EventEmitter {
}
_calculateTimeout(retriesCount) {
return (retriesCount < 40)
// First, for 2 seconds: 20 times per second
? (1000 / 20)
: (retriesCount < 40 + 60)
// Then, for 1 minute: once per second
? (1000)
: (retriesCount < 40 + 60 + 60)
// Then, for 10 minutes: once every 10 seconds
? (10 * 1000)
// Then: once every 30 seconds
: (30 * 1000)
return retriesCount < 40
? // First, for 2 seconds: 20 times per second
1000 / 20
: retriesCount < 40 + 60
? // Then, for 1 minute: once per second
1000
: retriesCount < 40 + 60 + 60
? // Then, for 10 minutes: once every 10 seconds
10 * 1000
: // Then: once every 30 seconds
30 * 1000
}
_retryConnect() {
@@ -232,8 +232,12 @@ class Connection extends EventEmitter {
if (this._onUnexpectedCloseBound) {
this._ws.removeListener('close', this._onUnexpectedCloseBound)
}
this._onUnexpectedCloseBound =
this._onUnexpectedClose.bind(this, false, null, null)
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(
this,
false,
null,
null
)
this._ws.once('close', this._onUnexpectedCloseBound)
}
@@ -252,18 +256,21 @@ class Connection extends EventEmitter {
_createWebSocket(): WebSocket {
const options: WebSocket.ClientOptions = {}
if (this._proxyURL !== undefined) {
if (this._config.proxy !== undefined) {
const parsedURL = parseUrl(this._url)
const parsedProxyURL = parseUrl(this._proxyURL)
const proxyOverrides = _.omitBy({
secureEndpoint: (parsedURL.protocol === 'wss:'),
secureProxy: (parsedProxyURL.protocol === 'https:'),
auth: this._proxyAuthorization,
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined)
const parsedProxyURL = parseUrl(this._config.proxy)
const proxyOverrides = _.omitBy(
{
secureEndpoint: parsedURL.protocol === 'wss:',
secureProxy: parsedProxyURL.protocol === 'https:',
auth: this._config.proxyAuthorization,
ca: this._config.trustedCertificates,
key: this._config.key,
passphrase: this._config.passphrase,
cert: this._config.certificate
},
_.isUndefined
)
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides)
let HttpsProxyAgent
try {
@@ -273,16 +280,19 @@ class Connection extends EventEmitter {
}
options.agent = new HttpsProxyAgent(proxyOptions)
}
if (this._authorization !== undefined) {
const base64 = Buffer.from(this._authorization).toString('base64')
if (this._config.authorization !== undefined) {
const base64 = Buffer.from(this._config.authorization).toString('base64')
options.headers = {Authorization: `Basic ${base64}`}
}
const optionsOverrides = _.omitBy({
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined)
const optionsOverrides = _.omitBy(
{
ca: this._config.trustedCertificates,
key: this._config.key,
passphrase: this._config.passphrase,
cert: this._config.certificate
},
_.isUndefined
)
const websocketOptions = _.assign({}, options, optionsOverrides)
const websocket = new WebSocket(this._url, null, websocketOptions)
// we will have a listener for each outstanding request,
@@ -297,56 +307,69 @@ class Connection extends EventEmitter {
this._clearConnectTimer()
this._clearReconnectTimer()
this._clearHeartbeatInterval()
return new Promise<void>((_resolve, reject) => {
this._connectTimer = setTimeout(() => {
reject(new ConnectionError(`Error: connect() timed out after ${this._connectionTimeout} ms. ` +
`If your internet connection is working, the rippled server may be blocked or inaccessible.`))
}, this._connectionTimeout)
if (!this._url) {
reject(new ConnectionError(
'Cannot connect because no server was specified'))
}
const resolve = () => {
this._startHeartbeatInterval();
_resolve();
}
if (this._state === WebSocket.OPEN) {
resolve()
} else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', () => resolve)
} else {
this._ws = this._createWebSocket()
// when an error causes the connection to close, the close event
// should still be emitted; the "ws" documentation says: "The close
// event is also emitted when then underlying net.Socket closes the
// connection (end or close)."
// In case if there is connection error (say, server is not responding)
// we must return this error to connection's caller. After successful
// opening, we will forward all errors to main api object.
this._onOpenErrorBound = this._onOpenError.bind(this, reject)
this._ws.once('error', this._onOpenErrorBound)
this._ws.on('message', this._onMessage.bind(this))
// in browser close event can came before open event, so we must
// resolve connect's promise after reconnect in that case.
// after open event we will rebound _onUnexpectedCloseBound
// without resolve and reject functions
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this, true,
resolve, reject)
this._ws.once('close', this._onUnexpectedCloseBound)
this._ws.once('open', () => {
return this._onOpen().then(resolve, reject)
return (
new Promise<void>((_resolve, reject) => {
this._connectTimer = setTimeout(() => {
reject(
new ConnectionError(
`Error: connect() timed out after ${this._config.connectionTimeout} ms. ` +
`If your internet connection is working, the rippled server may be blocked or inaccessible.`
)
)
}, this._config.connectionTimeout)
if (!this._url) {
reject(
new ConnectionError(
'Cannot connect because no server was specified'
)
)
}
const resolve = () => {
this._startHeartbeatInterval()
_resolve()
}
if (this._state === WebSocket.OPEN) {
resolve()
} else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', () => resolve)
} else {
this._ws = this._createWebSocket()
// when an error causes the connection to close, the close event
// should still be emitted; the "ws" documentation says: "The close
// event is also emitted when then underlying net.Socket closes the
// connection (end or close)."
// In case if there is connection error (say, server is not responding)
// we must return this error to connection's caller. After successful
// opening, we will forward all errors to main api object.
this._onOpenErrorBound = this._onOpenError.bind(this, reject)
this._ws.once('error', this._onOpenErrorBound)
this._ws.on('message', this._onMessage.bind(this))
// in browser close event can came before open event, so we must
// resolve connect's promise after reconnect in that case.
// after open event we will rebound _onUnexpectedCloseBound
// without resolve and reject functions
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(
this,
true,
resolve,
reject
)
this._ws.once('close', this._onUnexpectedCloseBound)
this._ws.once('open', () => {
return this._onOpen().then(resolve, reject)
})
}
})
// Once we have a resolution or rejection, clear the timeout timer as no
// longer needed.
.then(() => {
this._clearConnectTimer()
})
}
})
// Once we have a resolution or rejection, clear the timeout timer as no
// longer needed.
.then(() => {
this._clearConnectTimer()
})
.catch((err) => {
this._clearConnectTimer()
throw err;
})
.catch(err => {
this._clearConnectTimer()
throw err
})
)
}
disconnect(): Promise<void> {
@@ -385,20 +408,20 @@ class Connection extends EventEmitter {
reconnect() {
// NOTE: We currently have a "reconnecting" event, but that only triggers through
// _retryConnect, which was written in a way that is required to run as an internal
// _retryConnect, which was written in a way that is required to run as an internal
// part of the post-disconnect connect() flow.
// See: https://github.com/ripple/ripple-lib/pull/1101#issuecomment-565360423
this.emit('reconnect');
this.emit('reconnect')
return this.disconnect().then(() => this.connect())
}
private _clearHeartbeatInterval = () => {
clearInterval(this._heartbeatInterval);
clearInterval(this._heartbeatInterval)
}
private _startHeartbeatInterval = () => {
this._clearHeartbeatInterval()
this._heartbeatInterval = setInterval(() => this._heartbeat(), 1000 * 60);
this._heartbeatInterval = setInterval(() => this._heartbeat(), 1000 * 60)
}
/**
@@ -406,12 +429,12 @@ class Connection extends EventEmitter {
* If this succeeds, we're good. If it fails, disconnect so that the consumer can reconnect, if desired.
*/
private _heartbeat = () => {
return this.request({command: "ping"}).catch(() => this.reconnect());
return this.request({command: 'ping'}).catch(() => this.reconnect())
}
_whenReady<T>(promise: Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
promise.catch(reject);
promise.catch(reject)
if (!this._shouldBeConnected) {
reject(new NotConnectedError())
} else if (this._state === WebSocket.OPEN && this._isReady) {
@@ -427,9 +450,14 @@ class Connection extends EventEmitter {
}
hasLedgerVersions(lowLedgerVersion, highLedgerVersion): Promise<boolean> {
return this._whenReady(Promise.resolve(
this._availableLedgerVersions.containsRange(
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)))
return this._whenReady(
Promise.resolve(
this._availableLedgerVersions.containsRange(
lowLedgerVersion,
highLedgerVersion || this._ledgerVersion
)
)
)
}
hasLedgerVersion(ledgerVersion): Promise<boolean> {
@@ -445,17 +473,19 @@ class Connection extends EventEmitter {
}
_send(message: string): Promise<void> {
if (this._trace) {
this._console.log(message)
}
this._trace('send', message)
return new Promise((resolve, reject) => {
this._ws.send(message, undefined, error => {
if (error) {
reject(new DisconnectedError(error.message, error))
} else {
resolve()
}
})
try {
this._ws.send(message, undefined, error => {
if (error) {
reject(new DisconnectedError(error.message, error))
} else {
resolve()
}
})
} catch (error) {
reject(new DisconnectedError(error.message, error))
}
})
}
@@ -497,12 +527,18 @@ class Connection extends EventEmitter {
this.once(eventName, response => {
if (response.status === 'error') {
_reject(new RippledError(response.error_message || response.error, response))
_reject(
new RippledError(response.error_message || response.error, response)
)
} else if (response.status === 'success') {
_resolve(response.result)
} else {
_reject(new ResponseFormatError(
'unrecognized status: ' + response.status, response))
_reject(
new ResponseFormatError(
'unrecognized status: ' + response.status,
response
)
)
}
})
@@ -511,14 +547,16 @@ class Connection extends EventEmitter {
// JSON.stringify automatically removes keys with value of 'undefined'
const message = JSON.stringify(Object.assign({}, request, {id}))
this._whenReady(this._send(message)).then(() => {
const delay = timeout || this._timeout
timer = setTimeout(() => _reject(new TimeoutError()), delay)
// Node.js won't exit if a timer is still running, so we tell Node to ignore (Node will still wait for the request to complete)
if (timer.unref) {
timer.unref()
}
}).catch(_reject)
this._whenReady(this._send(message))
.then(() => {
const delay = timeout || this._config.timeout
timer = setTimeout(() => _reject(new TimeoutError()), delay)
// Node.js won't exit if a timer is still running, so we tell Node to ignore (Node will still wait for the request to complete)
if (timer.unref) {
timer.unref()
}
})
.catch(_reject)
})
}
}

View File

@@ -1,9 +1,7 @@
import {txFlagIndices} from './txflags'
// Ordering from https://developers.ripple.com/accountroot.html
const accountRootFlags = {
// lsfDefaultRipple:
// Enable rippling on trust lines by default.
// Required for issuing addresses; discouraged for others.
@@ -73,8 +71,12 @@ const AccountFlagIndices = {
}
const AccountFields = {
EmailHash: {name: 'emailHash', encoding: 'hex',
length: 32, defaults: '00000000000000000000000000000000'},
EmailHash: {
name: 'emailHash',
encoding: 'hex',
length: 32,
defaults: '00000000000000000000000000000000'
},
WalletLocator: {name: 'walletLocator'},
MessageKey: {name: 'messageKey'},
Domain: {name: 'domain', encoding: 'hex'},
@@ -82,8 +84,4 @@ const AccountFields = {
TickSize: {name: 'tickSize', defaults: 0}
}
export {
AccountFields,
AccountFlagIndices,
AccountFlags
}
export {AccountFields, AccountFlagIndices, AccountFlags}

View File

@@ -1,9 +1,7 @@
import {inspect} from 'util'
import * as browserHacks from './browser-hacks'
class RippleError extends Error {
name: string
message: string
data?: any
@@ -70,8 +68,11 @@ class MissingLedgerHistoryError extends RippleError {
class PendingLedgerVersionError extends RippleError {
constructor(message?: string) {
super(message || 'maxLedgerVersion is greater than server\'s most recent' +
' validated ledger')
super(
message ||
"maxLedgerVersion is greater than server's most recent" +
' validated ledger'
)
}
}

View File

@@ -11,18 +11,18 @@
* some arbitrary string. For example "TXN".
*/
enum HashPrefix {
enum HashPrefix {
// transaction plus signature to give transaction ID
TRANSACTION_ID = 0x54584E00, // 'TXN'
TRANSACTION_ID = 0x54584e00, // 'TXN'
// transaction plus metadata
TRANSACTION_NODE = 0x534E4400, // 'TND'
TRANSACTION_NODE = 0x534e4400, // 'TND'
// inner node in tree
INNER_NODE = 0x4D494E00, // 'MIN'
INNER_NODE = 0x4d494e00, // 'MIN'
// leaf node in tree
LEAF_NODE = 0x4D4C4E00, // 'MLN'
LEAF_NODE = 0x4d4c4e00, // 'MLN'
// inner transaction to sign
TRANSACTION_SIGN = 0x53545800, // 'STX'
@@ -31,10 +31,10 @@
TRANSACTION_SIGN_TESTNET = 0x73747800, // 'stx'
// inner transaction to multisign
TRANSACTION_MULTISIGN = 0x534D5400, // 'SMT'
TRANSACTION_MULTISIGN = 0x534d5400, // 'SMT'
// ledger
LEDGER = 0x4C575200 // 'LWR'
LEDGER = 0x4c575200 // 'LWR'
}
export default HashPrefix

View File

@@ -18,8 +18,11 @@ const bytesToHex = (bytes: number[]): string => {
return Buffer.from(bytes).toString('hex')
}
const bigintToHex = (integerString: string | number | BigNumber, byteLength: number): string => {
const hex = (new BigNumber(integerString)).toString(16)
const bigintToHex = (
integerString: string | number | BigNumber,
byteLength: number
): string => {
const hex = new BigNumber(integerString).toString(16)
return padLeftZero(hex, byteLength * 2)
}
@@ -28,12 +31,15 @@ const ledgerSpaceHex = (name: string): string => {
}
const addressToHex = (address: string): string => {
return (Buffer.from(decodeAccountID(address))).toString('hex')
return Buffer.from(decodeAccountID(address)).toString('hex')
}
const currencyToHex = (currency: string): string => {
if (currency.length === 3) {
const bytes = new Array(20 + 1).join('0').split('').map(parseFloat)
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
@@ -51,7 +57,7 @@ const addLengthPrefix = (hex: string): string => {
return bytesToHex([193 + (x >>> 8), x & 0xff]) + hex
} else if (length <= 918744) {
const x = length - 12481
return bytesToHex([241 + (x >>> 16), x >>> 8 & 0xff, x & 0xff]) + hex
return bytesToHex([241 + (x >>> 16), (x >>> 8) & 0xff, x & 0xff]) + hex
}
throw new Error('Variable integer overflow.')
}
@@ -65,7 +71,9 @@ export const computeTransactionHash = (txJSON: any): string => {
return computeBinaryTransactionHash(encode(txJSON))
}
export const computeBinaryTransactionSigningHash = (txBlobHex: string): string => {
export const computeBinaryTransactionSigningHash = (
txBlobHex: string
): string => {
const prefix = HashPrefix.TRANSACTION_SIGN.toString(16).toUpperCase()
return sha512Half(prefix + txBlobHex)
}
@@ -79,9 +87,9 @@ export const computeAccountHash = (address: string): string => {
}
export const computeSignerListHash = (address: string): string => {
return sha512Half(ledgerSpaceHex('signerList') +
addressToHex(address) +
'00000000') // uint32(0) signer list index
return sha512Half(
ledgerSpaceHex('signerList') + addressToHex(address) + '00000000'
) // uint32(0) signer list index
}
export const computeOrderHash = (address: string, sequence: number): string => {
@@ -89,18 +97,24 @@ export const computeOrderHash = (address: string, sequence: number): string => {
return sha512Half(prefix + addressToHex(address) + intToHex(sequence, 4))
}
export const computeTrustlineHash = (address1: string, address2: string, currency: string): string => {
export const computeTrustlineHash = (
address1: string,
address2: string,
currency: string
): string => {
const address1Hex = addressToHex(address1)
const address2Hex = addressToHex(address2)
const swap = (new BigNumber(address1Hex, 16)).isGreaterThan(
new BigNumber(address2Hex, 16))
const swap = new BigNumber(address1Hex, 16).isGreaterThan(
new BigNumber(address2Hex, 16)
)
const lowAddressHex = swap ? address2Hex : address1Hex
const highAddressHex = swap ? address1Hex : address2Hex
const prefix = ledgerSpaceHex('rippleState')
return sha512Half(prefix + lowAddressHex + highAddressHex +
currencyToHex(currency))
return sha512Half(
prefix + lowAddressHex + highAddressHex + currencyToHex(currency)
)
}
export const computeTransactionTreeHash = (transactions: any[]): string => {
@@ -131,25 +145,35 @@ export const computeStateTreeHash = (entries: any[]): string => {
// see rippled Ledger::updateHash()
export const computeLedgerHash = (ledgerHeader): string => {
const prefix = HashPrefix.LEDGER.toString(16).toUpperCase()
return sha512Half(prefix +
intToHex(ledgerHeader.ledger_index, 4) +
bigintToHex(ledgerHeader.total_coins, 8) +
ledgerHeader.parent_hash +
ledgerHeader.transaction_hash +
ledgerHeader.account_hash +
intToHex(ledgerHeader.parent_close_time, 4) +
intToHex(ledgerHeader.close_time, 4) +
intToHex(ledgerHeader.close_time_resolution, 1) +
intToHex(ledgerHeader.close_flags, 1)
return sha512Half(
prefix +
intToHex(ledgerHeader.ledger_index, 4) +
bigintToHex(ledgerHeader.total_coins, 8) +
ledgerHeader.parent_hash +
ledgerHeader.transaction_hash +
ledgerHeader.account_hash +
intToHex(ledgerHeader.parent_close_time, 4) +
intToHex(ledgerHeader.close_time, 4) +
intToHex(ledgerHeader.close_time_resolution, 1) +
intToHex(ledgerHeader.close_flags, 1)
)
}
export const computeEscrowHash = (address, sequence): string => {
return sha512Half(ledgerSpaceHex('escrow') + addressToHex(address) +
intToHex(sequence, 4))
return sha512Half(
ledgerSpaceHex('escrow') + addressToHex(address) + intToHex(sequence, 4)
)
}
export const computePaymentChannelHash = (address, dstAddress, sequence): string => {
return sha512Half(ledgerSpaceHex('paychan') + addressToHex(address) +
addressToHex(dstAddress) + intToHex(sequence, 4))
export const computePaymentChannelHash = (
address,
dstAddress,
sequence
): string => {
return sha512Half(
ledgerSpaceHex('paychan') +
addressToHex(address) +
addressToHex(dstAddress) +
intToHex(sequence, 4)
)
}

View File

@@ -1,4 +1,3 @@
/**
* Ripple ledger namespace prefixes.
*
@@ -8,18 +7,18 @@
* Each namespace is just a single character prefix.
*/
export default {
account : 'a',
dirNode : 'd',
generatorMap : 'g',
rippleState : 'r',
offer : 'o', // Entry for an offer.
ownerDir : 'O', // Directory of things owned by an account.
bookDir : 'B', // Directory of order books.
contract : 'c',
skipList : 's',
amendment : 'f',
feeSettings : 'e',
signerList : 'S',
escrow : 'u',
paychan : 'x'
account: 'a',
dirNode: 'd',
generatorMap: 'g',
rippleState: 'r',
offer: 'o', // Entry for an offer.
ownerDir: 'O', // Directory of things owned by an account.
bookDir: 'B', // Directory of order books.
contract: 'c',
skipList: 's',
amendment: 'f',
feeSettings: 'e',
signerList: 'S',
escrow: 'u',
paychan: 'x'
}

View File

@@ -1,7 +1,11 @@
import {createHash} from 'crypto'
const sha512Half = (hex: string): string => {
return createHash('sha512').update(Buffer.from(hex, 'hex')).digest('hex').toUpperCase().slice(0, 64)
return createHash('sha512')
.update(Buffer.from(hex, 'hex'))
.digest('hex')
.toUpperCase()
.slice(0, 64)
}
export default sha512Half

View File

@@ -1,6 +1,7 @@
import hashPrefix from './hash-prefix'
import sha512Half from './sha512Half'
const HEX_ZERO = '0000000000000000000000000000000000000000000000000000000000000000'
const HEX_ZERO =
'0000000000000000000000000000000000000000000000000000000000000000'
export enum NodeType {
INNER = 1,
@@ -18,15 +19,17 @@ export abstract class Node {
public constructor() {}
public addItem(_tag: string, _node: Node): void {
throw new Error('Called unimplemented virtual method SHAMapTreeNode#addItem.')
throw new Error(
'Called unimplemented virtual method SHAMapTreeNode#addItem.'
)
}
public get hash(): string|void {
throw new Error('Called unimplemented virtual method SHAMapTreeNode#hash.');
public get hash(): string | void {
throw new Error('Called unimplemented virtual method SHAMapTreeNode#hash.')
}
}
export class InnerNode extends Node {
public leaves: { [slot: number]: Node }
public leaves: {[slot: number]: Node}
public type: NodeType
public depth: number
public empty: boolean
@@ -48,9 +51,10 @@ export class InnerNode extends Node {
* @param {string} tag equates to a ledger entry `index`
* @param {Node} node to add
* @return {void}
*/
*/
public addItem(tag: string, node: Node): void {
const existingNode = this.getNode(parseInt(tag[this.depth],16))
const existingNode = this.getNode(parseInt(tag[this.depth], 16))
if (existingNode) {
// A node already exists in this slot
if (existingNode instanceof InnerNode) {
@@ -60,15 +64,16 @@ export class InnerNode extends Node {
if (existingNode.tag === tag) {
// Collision
throw new Error(
'Tried to add a node to a SHAMap that was already in there.')
'Tried to add a node to a SHAMap that was already in there.'
)
} else {
// Turn it into an inner node
const newInnerNode = new InnerNode(this.depth + 1)
// Parent new and existing node
newInnerNode.addItem(existingNode.tag, existingNode)
newInnerNode.addItem(tag, node)
// And place the newly created inner node in the slot
this.setNode(parseInt(tag[this.depth], 16), newInnerNode)
}
@@ -87,7 +92,7 @@ export class InnerNode extends Node {
*/
public setNode(slot: number, node: Node): void {
if (slot < 0 || slot > 15) {
throw new Error ('Invalid slot: slot must be between 0-15.')
throw new Error('Invalid slot: slot must be between 0-15.')
}
this.leaves[slot] = node
this.empty = false
@@ -100,8 +105,8 @@ export class InnerNode extends Node {
*/
public getNode(slot: number): Node {
if (slot < 0 || slot > 15) {
throw new Error ('Invalid slot: slot must be between 0-15.')
}
throw new Error('Invalid slot: slot must be between 0-15.')
}
return this.leaves[slot]
}
@@ -113,7 +118,7 @@ export class InnerNode extends Node {
}
const prefix = hashPrefix.INNER_NODE.toString(16)
return sha512Half(prefix + hex)
}
}
}
export class Leaf extends Node {
@@ -135,21 +140,24 @@ export class Leaf extends Node {
this.data = data
}
public get hash(): string|void {
public get hash(): string | void {
switch (this.type) {
case NodeType.ACCOUNT_STATE:
case NodeType.ACCOUNT_STATE: {
const leafPrefix = hashPrefix.LEAF_NODE.toString(16)
return sha512Half(leafPrefix + this.data + this.tag)
case NodeType.TRANSACTION_NO_METADATA:
}
case NodeType.TRANSACTION_NO_METADATA: {
const txIDPrefix = hashPrefix.TRANSACTION_ID.toString(16)
return sha512Half(txIDPrefix + this.data)
case NodeType.TRANSACTION_METADATA:
}
case NodeType.TRANSACTION_METADATA: {
const txNodePrefix = hashPrefix.TRANSACTION_NODE.toString(16)
return sha512Half(txNodePrefix + this.data + this.tag)
}
default:
throw new Error('Tried to hash a SHAMap node of unknown type.')
}
}
}
}
export class SHAMap {

View File

@@ -6,16 +6,15 @@ import {xAddressToClassicAddress, isValidXAddress} from 'ripple-address-codec'
export function ensureClassicAddress(account: string): string {
if (isValidXAddress(account)) {
const {
classicAddress,
tag
} = xAddressToClassicAddress(account)
const {classicAddress, tag} = xAddressToClassicAddress(account)
// Except for special cases, X-addresses used for requests
// must not have an embedded tag. In other words,
// `tag` should be `false`.
if (tag !== false) {
throw new Error('This command does not support the use of a tag. Use an address without a tag.')
throw new Error(
'This command does not support the use of a tag. Use an address without a tag.'
)
}
// For rippled requests that use an account, always use a classic address.
@@ -25,12 +24,7 @@ export function ensureClassicAddress(account: string): string {
}
}
export {
constants,
errors,
validate,
serverInfo
}
export {constants, errors, validate, serverInfo}
export {
dropsToXrp,
xrpToDrops,

View File

@@ -18,7 +18,6 @@ function mergeIntervals(intervals: Interval[]): Interval[] {
}
class RangeSet {
ranges: Array<[number, number]>
constructor() {
@@ -30,8 +29,9 @@ class RangeSet {
}
serialize() {
return this.ranges.map(range =>
range[0].toString() + '-' + range[1].toString()).join(',')
return this.ranges
.map(range => range[0].toString() + '-' + range[1].toString())
.join(',')
}
addRange(start: number, end: number) {

View File

@@ -62,6 +62,8 @@ function loadSchemas() {
require('./schemas/specifications/check-cash.json'),
require('./schemas/specifications/check-cancel.json'),
require('./schemas/specifications/trustline.json'),
require('./schemas/specifications/deposit-preauth.json'),
require('./schemas/specifications/account-delete.json'),
require('./schemas/output/sign.json'),
require('./schemas/output/submit.json'),
require('./schemas/output/get-account-info.json'),
@@ -175,8 +177,4 @@ function isValidAddress(address: string): boolean {
return isValidXAddress(address) || isValidClassicAddress(address)
}
export {
schemaValidate,
isValidSecret,
isValidAddress
}
export {schemaValidate, isValidSecret, isValidAddress}

View File

@@ -18,6 +18,8 @@
"paymentChannelClaim",
"checkCreate",
"checkCancel",
"checkCash"
"checkCash",
"depositPreauth",
"accountDelete"
]
}

View File

@@ -208,6 +208,30 @@
"$ref": "paymentChannelClaim"
}
}
},
{
"properties": {
"type": {
"enum": [
"depositPreauth"
]
},
"specification": {
"$ref": "depositPreauth"
}
}
},
{
"properties": {
"type": {
"enum": [
"accountDelete"
]
},
"specification": {
"$ref": "accountDelete"
}
}
}
]
}

View File

@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "accountDelete",
"link": "account-delete",
"type": "object",
"properties": {
"destination": {
"$ref": "address",
"description": "Address of an account to receive any leftover XRP after deleting the sending account. Must be a funded account in the ledger, and must not be the sending account."
},
"destinationTag": {
"$ref": "tag",
"description": "(Optional) Arbitrary destination tag that identifies a hosted recipient or other information for the recipient of the deleted account's leftover XRP."
},
"destinationXAddress": {
"$ref": "address",
"description": "X-address of an account to receive any leftover XRP after deleting the sending account. Must be a funded account in the ledger, and must not be the sending account."
}
},
"anyOf": [
{
"required": ["destination"]
},
{
"required": ["destinationXAddress"]
}
],
"additionalProperties": false
}

View File

@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "depositPreauth",
"link": "deposit-preauth",
"type": "object",
"properties": {
"authorize": {
"$ref": "address",
"description": "Address of the account that can cash the check."
},
"unauthorize": {
"$ref": "address",
"description": "Address of the account that can cash the check."
}
},
"oneOf": [
{"required": ["authorize"]},
{"required": ["unauthorize"]}
],
"additionalProperties": false
}

View File

@@ -4,32 +4,32 @@ import BigNumber from 'bignumber.js'
import {RippleAPI} from '..'
export type GetServerInfoResponse = {
buildVersion: string,
completeLedgers: string,
hostID: string,
ioLatencyMs: number,
buildVersion: string
completeLedgers: string
hostID: string
ioLatencyMs: number
load?: {
jobTypes: Array<object>,
jobTypes: Array<object>
threads: number
},
}
lastClose: {
convergeTimeS: number,
convergeTimeS: number
proposers: number
},
loadFactor: number,
peers: number,
pubkeyNode: string,
pubkeyValidator?: string,
serverState: string,
}
loadFactor: number
peers: number
pubkeyNode: string
pubkeyValidator?: string
serverState: string
validatedLedger: {
age: number,
baseFeeXRP: string,
hash: string,
reserveBaseXRP: string,
reserveIncrementXRP: string,
age: number
baseFeeXRP: string
hash: string
reserveBaseXRP: string
reserveIncrementXRP: string
ledgerVersion: number
},
validationQuorum: number,
}
validationQuorum: number
networkLedger?: string
}
@@ -51,12 +51,9 @@ function getServerInfo(this: RippleAPI): Promise<GetServerInfoResponse> {
reserveIncXrp: 'reserveIncrementXRP',
seq: 'ledgerVersion'
})
info.validatedLedger.baseFeeXRP =
info.validatedLedger.baseFeeXRP.toString()
info.validatedLedger.reserveBaseXRP =
info.validatedLedger.reserveBaseXRP.toString()
info.validatedLedger.reserveIncrementXRP =
info.validatedLedger.reserveIncrementXRP.toString()
info.validatedLedger.baseFeeXRP = info.validatedLedger.baseFeeXRP.toString()
info.validatedLedger.reserveBaseXRP = info.validatedLedger.reserveBaseXRP.toString()
info.validatedLedger.reserveIncrementXRP = info.validatedLedger.reserveIncrementXRP.toString()
}
return info
})
@@ -64,10 +61,7 @@ function getServerInfo(this: RippleAPI): Promise<GetServerInfoResponse> {
// This is a public API that can be called directly.
// This is not used by the `prepare*` methods. See `src/transaction/utils.ts`
async function getFee(
this: RippleAPI,
cushion?: number
): Promise<string> {
async function getFee(this: RippleAPI, cushion?: number): Promise<string> {
if (cushion === undefined) {
cushion = this._feeCushion
}
@@ -82,10 +76,7 @@ async function getFee(
// Cap fee to `this._maxFeeXRP`
fee = BigNumber.min(fee, this._maxFeeXRP)
// Round fee to 6 decimal places
return (new BigNumber(fee.toFixed(6))).toString(10)
return new BigNumber(fee.toFixed(6)).toString(10)
}
export {
getServerInfo,
getFee
}
export {getServerInfo, getFee}

View File

@@ -58,7 +58,4 @@ const txFlagIndices = {
}
}
export {
txFlags,
txFlagIndices
}
export {txFlags, txFlagIndices}

View File

@@ -5,19 +5,19 @@ import {
} from '../objects'
export interface AccountInfoRequest {
account: string,
strict?: boolean,
queue?: boolean,
ledger_hash?: string,
ledger_index?: number | ('validated' | 'closed' | 'current'),
account: string
strict?: boolean
queue?: boolean
ledger_hash?: string
ledger_index?: number | ('validated' | 'closed' | 'current')
signer_lists?: boolean
}
export interface AccountInfoResponse {
account_data: AccountRootLedgerEntry,
signer_lists?: SignerListLedgerEntry[],
ledger_current_index?: number,
ledger_index?: number,
queue_data?: QueueData,
account_data: AccountRootLedgerEntry
signer_lists?: SignerListLedgerEntry[]
ledger_current_index?: number
ledger_index?: number
queue_data?: QueueData
validated?: boolean
}

View File

@@ -1,19 +1,19 @@
import {Trustline} from '../objects'
export interface AccountLinesRequest {
account: string,
ledger_hash?: string,
ledger_index?: number | ('validated' | 'closed' | 'current'),
peer?: string,
limit?: number,
marker?: any,
account: string
ledger_hash?: string
ledger_index?: number | ('validated' | 'closed' | 'current')
peer?: string
limit?: number
marker?: any
}
export interface AccountLinesResponse {
account: string,
lines: Trustline[],
ledger_current_index?: number,
ledger_index?: number,
ledger_hash?: string,
marker?: any,
account: string
lines: Trustline[]
ledger_current_index?: number
ledger_index?: number
ledger_hash?: string
marker?: any
}

View File

@@ -1,84 +1,91 @@
import {
CheckLedgerEntry, RippleStateLedgerEntry,
OfferLedgerEntry, SignerListLedgerEntry,
EscrowLedgerEntry, PayChannelLedgerEntry,
CheckLedgerEntry,
RippleStateLedgerEntry,
OfferLedgerEntry,
SignerListLedgerEntry,
EscrowLedgerEntry,
PayChannelLedgerEntry,
DepositPreauthLedgerEntry
} from '../objects'
export interface GetAccountObjectsOptions {
type?: string | (
'check' |
'escrow' |
'offer' |
'payment_channel' |
'signer_list' |
'state'
),
ledgerHash?: string,
ledgerIndex?: number | ('validated' | 'closed' | 'current'),
limit?: number,
type?:
| string
| (
| 'check'
| 'escrow'
| 'offer'
| 'payment_channel'
| 'signer_list'
| 'state'
)
ledgerHash?: string
ledgerIndex?: number | ('validated' | 'closed' | 'current')
limit?: number
marker?: string
}
export interface AccountObjectsRequest {
account: string,
account: string
// (Optional) Filter results to include only this type of ledger object.
type?: string | (
'check' |
'escrow' |
'offer' |
'payment_channel' |
'signer_list' |
'state'
),
type?:
| string
| (
| 'check'
| 'escrow'
| 'offer'
| 'payment_channel'
| 'signer_list'
| 'state'
)
// (Optional) A 20-byte hex string for the ledger version to use.
ledger_hash?: string,
ledger_hash?: string
// (Optional) The sequence number of the ledger to use,
// or a shortcut string to choose a ledger automatically.
ledger_index?: number | ('validated' | 'closed' | 'current'),
ledger_index?: number | ('validated' | 'closed' | 'current')
limit?: number,
limit?: number
marker?: string
}
export interface AccountObjectsResponse {
account: string,
account: string
// Array of objects owned by this account.
// from the getAccountObjects section of the dev center
account_objects: Array<
CheckLedgerEntry |
RippleStateLedgerEntry |
OfferLedgerEntry |
SignerListLedgerEntry |
EscrowLedgerEntry |
PayChannelLedgerEntry |
DepositPreauthLedgerEntry
>,
| CheckLedgerEntry
| RippleStateLedgerEntry
| OfferLedgerEntry
| SignerListLedgerEntry
| EscrowLedgerEntry
| PayChannelLedgerEntry
| DepositPreauthLedgerEntry
>
// (May be omitted) The identifying hash of the ledger
// that was used to generate this response.
ledger_hash?: string,
ledger_hash?: string
// (May be omitted) The sequence number of the ledger version
// that was used to generate this response.
ledger_index?: number,
ledger_index?: number
// (May be omitted) The sequence number of the current in-progress ledger
// version that was used to generate this response.
ledger_current_index?: number,
ledger_current_index?: number
// The limit that was used in this request, if any.
limit?: number,
limit?: number
// Server-defined value indicating the response is paginated. Pass this
// to the next call to resume where this call left off. Omitted when there
// are no additional pages after this one.
marker?: string,
marker?: string
// If true, this information comes from a ledger version
// that has been validated by consensus.

View File

@@ -1,27 +1,27 @@
import {RippledAmount} from '../objects'
export interface AccountOffersRequest {
account: string,
ledger_hash?: string,
ledger_index?: number | ('validated' | 'closed' | 'current'),
limit?: number,
marker?: any,
account: string
ledger_hash?: string
ledger_index?: number | ('validated' | 'closed' | 'current')
limit?: number
marker?: any
}
export interface AccountOffersResponse {
account: string,
ledger_hash?: string,
ledger_current_index?: number,
ledger_index?: number,
marker?: any,
offers?: AccountOffer[],
account: string
ledger_hash?: string
ledger_current_index?: number
ledger_index?: number
marker?: any
offers?: AccountOffer[]
}
export interface AccountOffer {
seq: number,
flags: number,
taker_gets: RippledAmount,
taker_pays: RippledAmount,
quality: string,
seq: number
flags: number
taker_gets: RippledAmount
taker_pays: RippledAmount
quality: string
expiration?: number
}

View File

@@ -1,30 +1,26 @@
import {
TakerRequestAmount,
RippledAmount,
OfferLedgerEntry
} from '../objects'
import {TakerRequestAmount, RippledAmount, OfferLedgerEntry} from '../objects'
export interface BookOffersRequest {
taker?: string,
taker_gets: TakerRequestAmount,
taker_pays: TakerRequestAmount,
ledger_hash?: string,
ledger_index?: number | ('validated' | 'closed' | 'current'),
limit?: number,
taker?: string
taker_gets: TakerRequestAmount
taker_pays: TakerRequestAmount
ledger_hash?: string
ledger_index?: number | ('validated' | 'closed' | 'current')
limit?: number
marker?: any
}
export interface BookOffersResponse {
offers: BookOffer[],
ledger_hash?: string,
ledger_current_index?: number,
ledger_index?: number,
offers: BookOffer[]
ledger_hash?: string
ledger_current_index?: number
ledger_index?: number
marker?: any
}
export interface BookOffer extends OfferLedgerEntry {
quality?: string
owner_funds?: string,
taker_gets_funded?: RippledAmount,
owner_funds?: string
taker_gets_funded?: RippledAmount
taker_pays_funded?: RippledAmount
}

View File

@@ -1,19 +1,19 @@
import {Amount} from '../objects'
export interface GatewayBalancesRequest {
account: string,
strict?: boolean,
hotwallet: string|Array<string>,
ledger_hash?: string,
account: string
strict?: boolean
hotwallet: string | Array<string>
ledger_hash?: string
ledger_index?: number | ('validated' | 'closed' | 'current')
}
export interface GatewayBalancesResponse {
account: string,
obligations?: {[currency: string]: string},
balances?: {[address: string]: Amount[]},
assets?: {[address: string]: Amount[]},
ledger_hash?: string,
ledger_current_index?: number,
account: string
obligations?: {[currency: string]: string}
balances?: {[address: string]: Amount[]}
assets?: {[address: string]: Amount[]}
ledger_hash?: string
ledger_current_index?: number
ledger_index?: number
}

View File

@@ -1,4 +1,4 @@
import { LedgerData } from '../objects'
import {LedgerData} from '../objects'
export interface LedgerDataRequest {
id?: any
@@ -9,4 +9,4 @@ export interface LedgerDataRequest {
marker?: string
}
export type LedgerDataResponse = LedgerData;
export type LedgerDataResponse = LedgerData

View File

@@ -3,29 +3,34 @@ import {LedgerEntry} from '../objects'
export interface LedgerEntryRequest {
ledger_hash?: string
ledger_index?: number | ('validated' | 'closed' | 'current')
index?: string,
account_root?: string,
directory?: string | {
sub_index?: number,
dir_root: string
} | {
sub_index?: number,
owner: string
},
offer?: string | {
account: string,
seq: number
},
index?: string
account_root?: string
directory?:
| string
| {
sub_index?: number
dir_root: string
}
| {
sub_index?: number
owner: string
}
offer?:
| string
| {
account: string
seq: number
}
ripple_state?: {
accounts: [string, string],
accounts: [string, string]
currency: string
},
}
binary?: boolean
}
export interface LedgerEntryResponse {
index: string,
ledger_index: number,
node_binary?: string,
node?: LedgerEntry,
index: string
ledger_index: number
node_binary?: string
node?: LedgerEntry
}

View File

@@ -4,48 +4,48 @@ export interface ServerInfoRequest {
export interface ServerInfoResponse {
info: {
amendment_blocked?: boolean,
build_version: string,
closed_ledger?: LedgerInfo,
complete_ledgers: string,
hostid: string,
io_latency_ms: number,
amendment_blocked?: boolean
build_version: string
closed_ledger?: LedgerInfo
complete_ledgers: string
hostid: string
io_latency_ms: number
last_close: {
converge_time_s: number,
converge_time_s: number
proposers: number
},
}
load?: {
job_types: {
job_type: string,
per_second: number,
job_type: string
per_second: number
in_progress: number
}[],
}[]
threads: number
},
load_factor: number,
load_factor_local?: number,
load_factor_net?: number,
load_factor_cluster?: number,
load_factor_fee_escalation?: number,
load_factor_fee_queue?: number,
load_factor_server?: number,
peers: number,
pubkey_node: string,
pubkey_validator: string,
server_state: string,
state_accounting: any,
uptime: number,
validated_ledger?: LedgerInfo,
validation_quorum: number,
}
load_factor: number
load_factor_local?: number
load_factor_net?: number
load_factor_cluster?: number
load_factor_fee_escalation?: number
load_factor_fee_queue?: number
load_factor_server?: number
peers: number
pubkey_node: string
pubkey_validator: string
server_state: string
state_accounting: any
uptime: number
validated_ledger?: LedgerInfo
validation_quorum: number
validator_list_expires: string
},
}
}
export interface LedgerInfo {
age: number,
base_fee_xrp: number,
hash: string,
reserve_base_xrp: number,
reserve_inc_xrp: number,
seq: number,
age: number
base_fee_xrp: number
hash: string
reserve_base_xrp: number
reserve_inc_xrp: number
seq: number
}

View File

@@ -1,19 +1,19 @@
import {Amount} from './amounts'
export type Adjustment = {
address: string,
amount: Amount,
address: string
amount: Amount
tag?: number
}
export type MaxAdjustment = {
address: string,
maxAmount: Amount,
address: string
maxAmount: Amount
tag?: number
}
export type MinAdjustment = {
address: string,
minAmount: Amount,
address: string
minAmount: Amount
tag?: number
}

View File

@@ -2,7 +2,6 @@ export interface Amount extends Issue {
value: string
}
export type RippledAmount = string | Amount
/**

View File

@@ -1,23 +1,23 @@
export interface Ledger {
account_hash: string,
close_time: number,
close_time_human: string,
close_time_resolution: number,
closed: boolean,
ledger_hash: string,
ledger_index: string,
parent_hash: string,
total_coins: string,
transaction_hash: string,
transactions: string[] | object[],
account_hash: string
close_time: number
close_time_human: string
close_time_resolution: number
closed: boolean
ledger_hash: string
ledger_index: string
parent_hash: string
total_coins: string
transaction_hash: string
transactions: string[] | object[]
// @deprecated
seqNum?: string,
seqNum?: string
// @deprecated
totalCoins?: string,
totalCoins?: string
// @deprecated
hash?: string,
close_flags?: number,
parent_close_time?: number,
accountState?: any[],
hash?: string
close_flags?: number
parent_close_time?: number
accountState?: any[]
validated?: boolean
}

View File

@@ -2,5 +2,5 @@ export interface LedgerData {
ledger_index: string
ledger_hash: string
marker: string
state: ({ data?: string; LedgerEntryType?: string; index: string } & any)[]
}
state: ({data?: string; LedgerEntryType?: string; index: string} & any)[]
}

View File

@@ -2,188 +2,189 @@ import {SignerEntry} from './index'
import {Amount, RippledAmount} from './amounts'
export interface AccountRootLedgerEntry {
LedgerEntryType: 'AccountRoot',
Account: string,
Balance: string,
Flags: number,
OwnerCount: number,
PreviousTxnID: string,
PreviousTxnLgrSeq: number,
Sequence: number,
AccountTxnID?: string,
Domain?: string,
EmailHash?: string,
LedgerEntryType: 'AccountRoot'
Account: string
Balance: string
Flags: number
OwnerCount: number
PreviousTxnID: string
PreviousTxnLgrSeq: number
Sequence: number
AccountTxnID?: string
Domain?: string
EmailHash?: string
MessageKey?: string
RegularKey?: string,
TickSize?: number,
TransferRate?: number,
WalletLocator?: string,
RegularKey?: string
TickSize?: number
TransferRate?: number
WalletLocator?: string
WalletSize?: number // DEPRECATED
}
export interface AmendmentsLedgerEntry {
LedgerEntryType: 'Amendments',
Amendments?: string[],
Majorities?: any[],
LedgerEntryType: 'Amendments'
Amendments?: string[]
Majorities?: any[]
Flags: 0
}
export interface CheckLedgerEntry {
LedgerEntryType: 'Check',
Account: string,
Destination, string,
Flags: 0,
OwnerNode: string,
PreviousTxnID: string,
PreviousTxnLgrSeq: number,
SendMax: string | object,
Sequence: number,
DestinationNode: string,
DestinationTag: number,
Expiration: number,
InvoiceID: string,
LedgerEntryType: 'Check'
Account: string
Destination
string
Flags: 0
OwnerNode: string
PreviousTxnID: string
PreviousTxnLgrSeq: number
SendMax: string | object
Sequence: number
DestinationNode: string
DestinationTag: number
Expiration: number
InvoiceID: string
SourceTag: number
}
export interface DepositPreauthLedgerEntry {
LedgerEntryType: 'DepositPreauth',
Account: string,
Authorize: string,
OwnerNode: string,
PreviousTxnID: string,
LedgerEntryType: 'DepositPreauth'
Account: string
Authorize: string
OwnerNode: string
PreviousTxnID: string
PreviousTxnLgrSeq: number
}
export interface DirectoryNodeLedgerEntry {
LedgerEntryType: 'DirectoryNode',
Flags: number,
RootIndex: string,
Indexes: string[],
IndexNext?: number,
LedgerEntryType: 'DirectoryNode'
Flags: number
RootIndex: string
Indexes: string[]
IndexNext?: number
IndexPrevious?: number
}
export interface OfferDirectoryNodeLedgerEntry
extends DirectoryNodeLedgerEntry {
TakerPaysCurrency: string,
TakerPaysIssuer: string,
TakerGetsCurrency: string,
TakerGetsIssuer: string,
extends DirectoryNodeLedgerEntry {
TakerPaysCurrency: string
TakerPaysIssuer: string
TakerGetsCurrency: string
TakerGetsIssuer: string
ExchangeRate?: number // DEPRECATED
}
export interface OwnerDirectoryNodeLedgerEntry
extends DirectoryNodeLedgerEntry {
Owner: string,
Owner: string
}
export interface EscrowLedgerEntry {
LedgerEntryType: 'Escrow',
Account: string,
Destination: string,
Amount: string,
Condition?: string,
CancelAfter?: number,
FinishAfter?: number,
Flags: number,
SourceTag?: number,
DestinationTag?: number,
OwnerNode: string,
DestinationNode?: string,
PreviousTxnID: string,
LedgerEntryType: 'Escrow'
Account: string
Destination: string
Amount: string
Condition?: string
CancelAfter?: number
FinishAfter?: number
Flags: number
SourceTag?: number
DestinationTag?: number
OwnerNode: string
DestinationNode?: string
PreviousTxnID: string
PreviousTxnLgrSeq: number
}
export interface FeeSettingsLedgerEntry {
LedgerEntryType: 'FeeSettings',
BaseFee: string,
ReferenceFeeUnits: number,
ReserveBase: number,
ReserveIncrement: number,
LedgerEntryType: 'FeeSettings'
BaseFee: string
ReferenceFeeUnits: number
ReserveBase: number
ReserveIncrement: number
Flags: number
}
export interface LedgerHashesLedgerEntry {
LedgerEntryType: 'LedgerHashes',
Hashes: string[],
Flags: number,
FirstLedgerSequence?: number, // DEPRECATED
LedgerEntryType: 'LedgerHashes'
Hashes: string[]
Flags: number
FirstLedgerSequence?: number // DEPRECATED
LastLedgerSequence?: number
}
export interface OfferLedgerEntry {
LedgerEntryType: 'Offer',
Flags: number,
Account: string,
Sequence: number,
TakerPays: RippledAmount,
TakerGets: RippledAmount,
BookDirectory: string,
BookNode: string,
OwnerNode: string,
PreviousTxnID: string,
PreviousTxnLgrSeq: number,
LedgerEntryType: 'Offer'
Flags: number
Account: string
Sequence: number
TakerPays: RippledAmount
TakerGets: RippledAmount
BookDirectory: string
BookNode: string
OwnerNode: string
PreviousTxnID: string
PreviousTxnLgrSeq: number
Expiration?: number
}
export interface PayChannelLedgerEntry {
LedgerEntryType: 'PayChannel',
Sequence: number,
Account: string,
Amount: string,
Balance: string,
PublicKey: string,
Destination: string,
SettleDelay: number,
Expiration?: number,
CancelAfter?: number,
SourceTag?: number,
DestinationTag?: number,
OwnerNode: string,
PreviousTxnID: string,
PreviousTxnLgrSeq: number,
LedgerEntryType: 'PayChannel'
Sequence: number
Account: string
Amount: string
Balance: string
PublicKey: string
Destination: string
SettleDelay: number
Expiration?: number
CancelAfter?: number
SourceTag?: number
DestinationTag?: number
OwnerNode: string
PreviousTxnID: string
PreviousTxnLgrSeq: number
index: string
}
export interface RippleStateLedgerEntry {
LedgerEntryType: 'RippleState',
Flags: number,
Balance: Amount,
LowLimit: Amount,
HighLimit: Amount,
PreviousTxnID: string,
PreviousTxnLgrSeq: number,
LowNode?: string,
HighNode?: string,
LowQualityIn?: number,
LowQualityOut?: number,
HighQualityIn?: number,
LedgerEntryType: 'RippleState'
Flags: number
Balance: Amount
LowLimit: Amount
HighLimit: Amount
PreviousTxnID: string
PreviousTxnLgrSeq: number
LowNode?: string
HighNode?: string
LowQualityIn?: number
LowQualityOut?: number
HighQualityIn?: number
HighQualityOut?: number
}
export interface SignerListLedgerEntry {
LedgerEntryType: 'SignerList',
OwnerNode: string,
SignerQuorum: number,
SignerEntries: SignerEntry[],
SignerListID: number,
PreviousTxnID: string,
LedgerEntryType: 'SignerList'
OwnerNode: string
SignerQuorum: number
SignerEntries: SignerEntry[]
SignerListID: number
PreviousTxnID: string
PreviousTxnLgrSeq: number
}
// see https://ripple.com/build/ledger-format/#ledger-object-types
export type LedgerEntry =
AccountRootLedgerEntry |
AmendmentsLedgerEntry |
CheckLedgerEntry |
DepositPreauthLedgerEntry |
DirectoryNodeLedgerEntry |
OfferDirectoryNodeLedgerEntry |
OwnerDirectoryNodeLedgerEntry |
EscrowLedgerEntry |
FeeSettingsLedgerEntry |
LedgerHashesLedgerEntry |
OfferLedgerEntry |
PayChannelLedgerEntry |
RippleStateLedgerEntry |
SignerListLedgerEntry
| AccountRootLedgerEntry
| AmendmentsLedgerEntry
| CheckLedgerEntry
| DepositPreauthLedgerEntry
| DirectoryNodeLedgerEntry
| OfferDirectoryNodeLedgerEntry
| OwnerDirectoryNodeLedgerEntry
| EscrowLedgerEntry
| FeeSettingsLedgerEntry
| LedgerHashesLedgerEntry
| OfferLedgerEntry
| PayChannelLedgerEntry
| RippleStateLedgerEntry
| SignerListLedgerEntry

View File

@@ -1,6 +1,5 @@
export type Memo = {
type?: string,
format?: string,
type?: string
format?: string
data?: string
}

View File

@@ -2,14 +2,14 @@ import {Amount} from './amounts'
import {Memo} from './memos'
export type FormattedOrderSpecification = {
direction: string,
quantity: Amount,
totalPrice: Amount,
immediateOrCancel?: boolean,
fillOrKill?: boolean,
expirationTime?: string,
orderToReplace?: number,
memos?: Memo[],
direction: string
quantity: Amount
totalPrice: Amount
immediateOrCancel?: boolean
fillOrKill?: boolean
expirationTime?: string
orderToReplace?: number
memos?: Memo[]
// If enabled, the offer will not consume offers that exactly match it, and
// instead becomes an Offer node in the ledger. It will still consume offers
// that cross it.

View File

@@ -1,16 +1,16 @@
export interface QueueTransaction {
auth_change: boolean,
fee: string,
fee_level: string,
max_spend_drops: string,
auth_change: boolean
fee: string
fee_level: string
max_spend_drops: string
seq: number
}
export interface QueueData {
txn_count: number,
auth_change_queued?: boolean,
lowest_sequence?: number,
highest_sequence?: number,
max_spend_drops_total?: string,
txn_count: number
auth_change_queued?: boolean
lowest_sequence?: number
highest_sequence?: number
max_spend_drops_total?: string
transactions?: QueueTransaction[]
}

View File

@@ -1,33 +1,33 @@
import {Memo} from './memos'
export type WeightedSigner = {
address: string,
address: string
weight: number
}
export type Signers = {
threshold?: number,
threshold?: number
weights: WeightedSigner[]
}
export type FormattedSettings = {
defaultRipple?: boolean,
depositAuth?: boolean,
disableMasterKey?: boolean,
disallowIncomingXRP?: boolean,
domain?: string,
emailHash?: string|null,
walletLocator?: string|null,
enableTransactionIDTracking?: boolean,
globalFreeze?: boolean,
memos?: Memo[],
messageKey?: string,
noFreeze?: boolean,
passwordSpent?: boolean,
regularKey?: string,
requireAuthorization?: boolean,
requireDestinationTag?: boolean,
signers?: Signers,
transferRate?: number|null,
defaultRipple?: boolean
depositAuth?: boolean
disableMasterKey?: boolean
disallowIncomingXRP?: boolean
domain?: string
emailHash?: string | null
walletLocator?: string | null
enableTransactionIDTracking?: boolean
globalFreeze?: boolean
memos?: Memo[]
messageKey?: string
noFreeze?: boolean
passwordSpent?: boolean
regularKey?: string
requireAuthorization?: boolean
requireDestinationTag?: boolean
signers?: Signers
transferRate?: number | null
tickSize?: number
}

View File

@@ -1,4 +1,4 @@
export interface SignerEntry {
Account: string,
Account: string
SignerWeight: number
}

View File

@@ -2,21 +2,21 @@ import {RippledAmount} from './amounts'
import {Memo} from './memos'
export interface OfferCreateTransaction {
TransactionType: 'OfferCreate',
Account: string,
AccountTxnID?: string,
Fee: string,
Field: any,
Flags: number,
LastLedgerSequence?: number,
Sequence: number,
Signers: any[],
SigningPubKey: string,
SourceTag?: number,
TakerGets: RippledAmount,
TakerPays: RippledAmount,
TxnSignature: string,
Expiration?: number,
Memos?: Memo[],
OfferSequence?: number,
TransactionType: 'OfferCreate'
Account: string
AccountTxnID?: string
Fee: string
Field: any
Flags: number
LastLedgerSequence?: number
Sequence: number
Signers: any[]
SigningPubKey: string
SourceTag?: number
TakerGets: RippledAmount
TakerPays: RippledAmount
TxnSignature: string
Expiration?: number
Memos?: Memo[]
OfferSequence?: number
}

View File

@@ -1,41 +1,41 @@
import {Memo} from './memos'
export interface Trustline {
account: string,
balance: string,
currency: string,
limit: string,
limit_peer: string,
quality_in: number,
quality_out: number,
no_ripple?: boolean,
no_ripple_peer?: boolean,
freeze?: boolean,
freeze_peer?: boolean,
authorized?: boolean,
peer_authorized?: boolean,
account: string
balance: string
currency: string
limit: string
limit_peer: string
quality_in: number
quality_out: number
no_ripple?: boolean
no_ripple_peer?: boolean
freeze?: boolean
freeze_peer?: boolean
authorized?: boolean
peer_authorized?: boolean
}
export type FormattedTrustlineSpecification = {
currency: string,
counterparty: string,
limit: string,
qualityIn?: number,
qualityOut?: number,
ripplingDisabled?: boolean,
authorized?: boolean,
frozen?: boolean,
currency: string
counterparty: string
limit: string
qualityIn?: number
qualityOut?: number
ripplingDisabled?: boolean
authorized?: boolean
frozen?: boolean
memos?: Memo[]
}
export type FormattedTrustline = {
specification: FormattedTrustlineSpecification,
specification: FormattedTrustlineSpecification
counterparty: {
limit: string,
ripplingDisabled?: boolean,
frozen?: boolean,
limit: string
ripplingDisabled?: boolean
frozen?: boolean
authorized?: boolean
},
}
state: {
balance: string
}

View File

@@ -16,74 +16,93 @@ function isValidSecret(secret: string): boolean {
function dropsToXrp(drops: BigNumber.Value): string {
if (typeof drops === 'string') {
if (!drops.match(/^-?[0-9]*\.?[0-9]*$/)) {
throw new ValidationError(`dropsToXrp: invalid value '${drops}',` +
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`)
throw new ValidationError(
`dropsToXrp: invalid value '${drops}',` +
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`
)
} else if (drops === '.') {
throw new ValidationError(`dropsToXrp: invalid value '${drops}',` +
` should be a BigNumber or string-encoded number.`)
throw new ValidationError(
`dropsToXrp: invalid value '${drops}',` +
` should be a BigNumber or string-encoded number.`
)
}
}
// Converting to BigNumber and then back to string should remove any
// decimal point followed by zeros, e.g. '1.00'.
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
drops = (new BigNumber(drops)).toString(10)
drops = new BigNumber(drops).toString(10)
// drops are only whole units
if (drops.includes('.')) {
throw new ValidationError(`dropsToXrp: value '${drops}' has` +
` too many decimal places.`)
throw new ValidationError(
`dropsToXrp: value '${drops}' has` + ` too many decimal places.`
)
}
// This should never happen; the value has already been
// validated above. This just ensures BigNumber did not do
// something unexpected.
if (!drops.match(/^-?[0-9]+$/)) {
throw new ValidationError(`dropsToXrp: failed sanity check -` +
` value '${drops}',` +
` does not match (^-?[0-9]+$).`)
throw new ValidationError(
`dropsToXrp: failed sanity check -` +
` value '${drops}',` +
` does not match (^-?[0-9]+$).`
)
}
return (new BigNumber(drops)).dividedBy(1000000.0).toString(10)
return new BigNumber(drops).dividedBy(1000000.0).toString(10)
}
function xrpToDrops(xrp: BigNumber.Value): string {
if (typeof xrp === 'string') {
if (!xrp.match(/^-?[0-9]*\.?[0-9]*$/)) {
throw new ValidationError(`xrpToDrops: invalid value '${xrp}',` +
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`)
throw new ValidationError(
`xrpToDrops: invalid value '${xrp}',` +
` should be a number matching (^-?[0-9]*\\.?[0-9]*$).`
)
} else if (xrp === '.') {
throw new ValidationError(`xrpToDrops: invalid value '${xrp}',` +
` should be a BigNumber or string-encoded number.`)
throw new ValidationError(
`xrpToDrops: invalid value '${xrp}',` +
` should be a BigNumber or string-encoded number.`
)
}
}
// Important: specify base 10 to avoid exponential notation, e.g. '1e-7'.
xrp = (new BigNumber(xrp)).toString(10)
xrp = new BigNumber(xrp).toString(10)
// This should never happen; the value has already been
// validated above. This just ensures BigNumber did not do
// something unexpected.
if (!xrp.match(/^-?[0-9.]+$/)) {
throw new ValidationError(`xrpToDrops: failed sanity check -` +
` value '${xrp}',` +
` does not match (^-?[0-9.]+$).`)
throw new ValidationError(
`xrpToDrops: failed sanity check -` +
` value '${xrp}',` +
` does not match (^-?[0-9.]+$).`
)
}
const components = xrp.split('.')
if (components.length > 2) {
throw new ValidationError(`xrpToDrops: failed sanity check -` +
` value '${xrp}' has` +
` too many decimal points.`)
throw new ValidationError(
`xrpToDrops: failed sanity check -` +
` value '${xrp}' has` +
` too many decimal points.`
)
}
const fraction = components[1] || '0'
if (fraction.length > 6) {
throw new ValidationError(`xrpToDrops: value '${xrp}' has` +
` too many decimal places.`)
throw new ValidationError(
`xrpToDrops: value '${xrp}' has` + ` too many decimal places.`
)
}
return (new BigNumber(xrp)).times(1000000.0).integerValue(BigNumber.ROUND_FLOOR).toString(10)
return new BigNumber(xrp)
.times(1000000.0)
.integerValue(BigNumber.ROUND_FLOOR)
.toString(10)
}
function toRippledAmount(amount: Amount): RippledAmount {
@@ -95,8 +114,11 @@ function toRippledAmount(amount: Amount): RippledAmount {
}
return {
currency: amount.currency,
issuer: amount.counterparty ? amount.counterparty :
(amount.issuer ? amount.issuer : undefined),
issuer: amount.counterparty
? amount.counterparty
: amount.issuer
? amount.issuer
: undefined,
value: amount.value
}
}
@@ -105,16 +127,20 @@ function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
if (typeof obj === 'object') {
const accumulator = Array.isArray(obj) ? [] : {}
let newKey
return _.reduce(obj, (result, value, key) => {
newKey = key
// taking this out of function leads to error in PhantomJS
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g
if (FINDSNAKE.test(key)) {
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
}
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
return result
}, accumulator)
return _.reduce(
obj,
(result, value, key) => {
newKey = key
// taking this out of function leads to error in PhantomJS
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g
if (FINDSNAKE.test(key)) {
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
}
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
return result
},
accumulator
)
}
return obj
}
@@ -128,7 +154,7 @@ function removeUndefined<T extends object>(obj: T): T {
* @return {Number} ms since unix epoch
*/
function rippleToUnixTimestamp(rpepoch: number): number {
return (rpepoch + 0x386D4380) * 1000
return (rpepoch + 0x386d4380) * 1000
}
/**
@@ -136,7 +162,7 @@ function rippleToUnixTimestamp(rpepoch: number): number {
* @return {Number} seconds since ripple epoch (1/1/2000 GMT)
*/
function unixToRippleTimestamp(timestamp: number): number {
return Math.round(timestamp / 1000) - 0x386D4380
return Math.round(timestamp / 1000) - 0x386d4380
}
function rippleTimeToISO8601(rippleTime: number): string {
@@ -161,4 +187,3 @@ export {
iso8601ToRippleTime,
isValidSecret
}

View File

@@ -7,8 +7,11 @@ function error(text) {
}
function validateLedgerRange(options) {
if (!_.isUndefined(options) && !_.isUndefined(options.minLedgerVersion)
&& !_.isUndefined(options.maxLedgerVersion)) {
if (
!_.isUndefined(options) &&
!_.isUndefined(options.minLedgerVersion) &&
!_.isUndefined(options.maxLedgerVersion)
) {
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
throw error('minLedgerVersion must not be greater than maxLedgerVersion')
}
@@ -20,110 +23,143 @@ function validateOptions(schema, instance) {
validateLedgerRange(instance.options)
}
export const getPaths =
_.partial(schemaValidate, 'getPathsParameters')
export const getPaths = _.partial(schemaValidate, 'getPathsParameters')
export const getTransactions =
_.partial(validateOptions, 'getTransactionsParameters')
export const getTransactions = _.partial(
validateOptions,
'getTransactionsParameters'
)
export const getSettings =
_.partial(validateOptions, 'getSettingsParameters')
export const getSettings = _.partial(validateOptions, 'getSettingsParameters')
export const getAccountInfo =
_.partial(validateOptions, 'getAccountInfoParameters')
export const getAccountInfo = _.partial(
validateOptions,
'getAccountInfoParameters'
)
export const getTrustlines =
_.partial(validateOptions, 'getTrustlinesParameters')
export const getTrustlines = _.partial(
validateOptions,
'getTrustlinesParameters'
)
export const getBalances =
_.partial(validateOptions, 'getBalancesParameters')
export const getBalances = _.partial(validateOptions, 'getBalancesParameters')
export const getBalanceSheet =
_.partial(validateOptions, 'getBalanceSheetParameters')
export const getBalanceSheet = _.partial(
validateOptions,
'getBalanceSheetParameters'
)
export const getOrders =
_.partial(validateOptions, 'getOrdersParameters')
export const getOrders = _.partial(validateOptions, 'getOrdersParameters')
export const getOrderbook =
_.partial(validateOptions, 'getOrderbookParameters')
export const getOrderbook = _.partial(validateOptions, 'getOrderbookParameters')
export const getTransaction =
_.partial(validateOptions, 'getTransactionParameters')
export const getTransaction = _.partial(
validateOptions,
'getTransactionParameters'
)
export const getPaymentChannel =
_.partial(validateOptions, 'getPaymentChannelParameters')
export const getPaymentChannel = _.partial(
validateOptions,
'getPaymentChannelParameters'
)
export const getLedger =
_.partial(validateOptions, 'getLedgerParameters')
export const getLedger = _.partial(validateOptions, 'getLedgerParameters')
export const preparePayment =
_.partial(schemaValidate, 'preparePaymentParameters')
export const preparePayment = _.partial(
schemaValidate,
'preparePaymentParameters'
)
export const prepareOrder =
_.partial(schemaValidate, 'prepareOrderParameters')
export const prepareOrder = _.partial(schemaValidate, 'prepareOrderParameters')
export const prepareOrderCancellation =
_.partial(schemaValidate, 'prepareOrderCancellationParameters')
export const prepareOrderCancellation = _.partial(
schemaValidate,
'prepareOrderCancellationParameters'
)
export const prepareTrustline =
_.partial(schemaValidate, 'prepareTrustlineParameters')
export const prepareTrustline = _.partial(
schemaValidate,
'prepareTrustlineParameters'
)
export const prepareSettings =
_.partial(schemaValidate, 'prepareSettingsParameters')
export const prepareSettings = _.partial(
schemaValidate,
'prepareSettingsParameters'
)
export const prepareEscrowCreation =
_.partial(schemaValidate, 'prepareEscrowCreationParameters')
export const prepareEscrowCreation = _.partial(
schemaValidate,
'prepareEscrowCreationParameters'
)
export const prepareEscrowCancellation =
_.partial(schemaValidate, 'prepareEscrowCancellationParameters')
export const prepareEscrowCancellation = _.partial(
schemaValidate,
'prepareEscrowCancellationParameters'
)
export const prepareEscrowExecution =
_.partial(schemaValidate, 'prepareEscrowExecutionParameters')
export const prepareEscrowExecution = _.partial(
schemaValidate,
'prepareEscrowExecutionParameters'
)
export const preparePaymentChannelCreate =
_.partial(schemaValidate, 'preparePaymentChannelCreateParameters')
export const preparePaymentChannelCreate = _.partial(
schemaValidate,
'preparePaymentChannelCreateParameters'
)
export const preparePaymentChannelFund =
_.partial(schemaValidate, 'preparePaymentChannelFundParameters')
export const preparePaymentChannelFund = _.partial(
schemaValidate,
'preparePaymentChannelFundParameters'
)
export const preparePaymentChannelClaim =
_.partial(schemaValidate, 'preparePaymentChannelClaimParameters')
export const preparePaymentChannelClaim = _.partial(
schemaValidate,
'preparePaymentChannelClaimParameters'
)
export const prepareCheckCreate =
_.partial(schemaValidate, 'prepareCheckCreateParameters')
export const prepareCheckCreate = _.partial(
schemaValidate,
'prepareCheckCreateParameters'
)
export const prepareCheckCash =
_.partial(schemaValidate, 'prepareCheckCashParameters')
export const prepareCheckCash = _.partial(
schemaValidate,
'prepareCheckCashParameters'
)
export const prepareCheckCancel =
_.partial(schemaValidate, 'prepareCheckCancelParameters')
export const prepareCheckCancel = _.partial(
schemaValidate,
'prepareCheckCancelParameters'
)
export const sign =
_.partial(schemaValidate, 'signParameters')
export const sign = _.partial(schemaValidate, 'signParameters')
export const combine =
_.partial(schemaValidate, 'combineParameters')
export const combine = _.partial(schemaValidate, 'combineParameters')
export const submit =
_.partial(schemaValidate, 'submitParameters')
export const submit = _.partial(schemaValidate, 'submitParameters')
export const computeLedgerHash =
_.partial(schemaValidate, 'computeLedgerHashParameters')
export const computeLedgerHash = _.partial(
schemaValidate,
'computeLedgerHashParameters'
)
export const generateAddress =
_.partial(schemaValidate, 'generateAddressParameters')
export const generateAddress = _.partial(
schemaValidate,
'generateAddressParameters'
)
export const signPaymentChannelClaim =
_.partial(schemaValidate, 'signPaymentChannelClaimParameters')
export const signPaymentChannelClaim = _.partial(
schemaValidate,
'signPaymentChannelClaimParameters'
)
export const verifyPaymentChannelClaim =
_.partial(schemaValidate, 'verifyPaymentChannelClaimParameters')
export const verifyPaymentChannelClaim = _.partial(
schemaValidate,
'verifyPaymentChannelClaimParameters'
)
export const apiOptions =
_.partial(schemaValidate, 'api-options')
export const apiOptions = _.partial(schemaValidate, 'api-options')
export const instructions =
_.partial(schemaValidate, 'instructions')
export const instructions = _.partial(schemaValidate, 'instructions')
export const tx_json =
_.partial(schemaValidate, 'tx-json')
export const tx_json = _.partial(schemaValidate, 'tx-json')

View File

@@ -17,7 +17,6 @@ declare class WebSocket {
* same, as `ws` package provides.
*/
class WSWrapper extends EventEmitter {
private _ws: WebSocket
static CONNECTING = 0
static OPEN = 1
@@ -60,8 +59,6 @@ class WSWrapper extends EventEmitter {
get readyState() {
return this._ws.readyState
}
}
export = WSWrapper

View File

@@ -1,8 +1,6 @@
export {RippleAPI} from './api'
export {
FormattedTransactionType
} from './transaction/types'
export {FormattedTransactionType} from './transaction/types'
// Broadcast api is experimental
export {RippleAPIBroadcast} from './broadcast'

View File

@@ -1,4 +1,9 @@
import {validate, removeUndefined, dropsToXrp, ensureClassicAddress} from '../common'
import {
validate,
removeUndefined,
dropsToXrp,
ensureClassicAddress
} from '../common'
import {RippleAPI} from '..'
import {AccountInfoResponse} from '../common/types/commands/account_info'
@@ -7,11 +12,11 @@ export type GetAccountInfoOptions = {
}
export type FormattedGetAccountInfoResponse = {
sequence: number,
xrpBalance: string,
ownerCount: number,
previousInitiatedTransactionID: string,
previousAffectingTransactionID: string,
sequence: number
xrpBalance: string
ownerCount: number
previousInitiatedTransactionID: string
previousAffectingTransactionID: string
previousAffectingTransactionLedgerVersion: number
}
@@ -30,7 +35,9 @@ function formatAccountInfo(
}
export default async function getAccountInfo(
this: RippleAPI, address: string, options: GetAccountInfoOptions = {}
this: RippleAPI,
address: string,
options: GetAccountInfoOptions = {}
): Promise<FormattedGetAccountInfoResponse> {
// 1. Validate
validate.getAccountInfo({address, options})

View File

@@ -14,14 +14,17 @@ export default async function getAccountObjects(
// through to rippled. rippled validates requests.
// Make Request
const response = await this.request('account_objects', removeUndefined({
account: address,
type: options.type,
ledger_hash: options.ledgerHash,
ledger_index: options.ledgerIndex,
limit: options.limit,
marker: options.marker
}))
const response = await this.request(
'account_objects',
removeUndefined({
account: address,
type: options.type,
ledger_hash: options.ledgerHash,
ledger_index: options.ledgerIndex,
limit: options.limit,
marker: options.marker
})
)
// Return Response
return response
}

View File

@@ -5,17 +5,17 @@ import {ensureLedgerVersion} from './utils'
import {RippleAPI} from '..'
export type BalanceSheetOptions = {
excludeAddresses?: Array<string>,
excludeAddresses?: Array<string>
ledgerVersion?: number
}
export type GetBalanceSheet = {
balances?: Array<Amount>,
assets?: Array<Amount>,
balances?: Array<Amount>
assets?: Array<Amount>
obligations?: Array<{
currency: string,
value: string
}>
currency: string
value: string
}>
}
function formatBalanceSheet(balanceSheet): GetBalanceSheet {
@@ -48,7 +48,9 @@ function formatBalanceSheet(balanceSheet): GetBalanceSheet {
}
async function getBalanceSheet(
this: RippleAPI, address: string, options: BalanceSheetOptions = {}
this: RippleAPI,
address: string,
options: BalanceSheetOptions = {}
): Promise<GetBalanceSheet> {
// 1. Validate
validate.getBalanceSheet({address, options})

View File

@@ -6,8 +6,8 @@ import {FormattedTrustline} from '../common/types/objects/trustlines'
import {RippleAPI} from '..'
export type Balance = {
value: string,
currency: string,
value: string
currency: string
counterparty?: string
}
@@ -23,9 +23,9 @@ function getTrustlineBalanceAmount(trustline: FormattedTrustline) {
function formatBalances(options, balances) {
const result = balances.trustlines.map(getTrustlineBalanceAmount)
if (!(options.counterparty ||
(options.currency && options.currency !== 'XRP')
)) {
if (
!(options.counterparty || (options.currency && options.currency !== 'XRP'))
) {
const xrpBalance = {
currency: 'XRP',
value: balances.xrp
@@ -39,7 +39,9 @@ function formatBalances(options, balances) {
return result
}
function getLedgerVersionHelper(connection: Connection, optionValue?: number
function getLedgerVersionHelper(
connection: Connection,
optionValue?: number
): Promise<number> {
if (optionValue !== undefined && optionValue !== null) {
return Promise.resolve(optionValue)
@@ -47,7 +49,10 @@ function getLedgerVersionHelper(connection: Connection, optionValue?: number
return connection.getLedgerVersion()
}
function getBalances(this: RippleAPI, address: string, options: GetTrustlinesOptions = {}
function getBalances(
this: RippleAPI,
address: string,
options: GetTrustlinesOptions = {}
): Promise<GetBalances> {
validate.getTrustlines({address, options})
@@ -59,12 +64,16 @@ function getBalances(this: RippleAPI, address: string, options: GetTrustlinesOpt
address = ensureClassicAddress(address)
return Promise.all([
getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
ledgerVersion =>
utils.getXRPBalance(this.connection, address, ledgerVersion)),
getLedgerVersionHelper(
this.connection,
options.ledgerVersion
).then(ledgerVersion =>
utils.getXRPBalance(this.connection, address, ledgerVersion)
),
this.getTrustlines(address, options)
]).then(results =>
formatBalances(options, {xrp: results[0], trustlines: results[1]}))
formatBalances(options, {xrp: results[0], trustlines: results[1]})
)
}
export default getBalances

View File

@@ -3,15 +3,16 @@ import {FormattedLedger, parseLedger} from './parse/ledger'
import {RippleAPI} from '..'
export type GetLedgerOptions = {
ledgerHash?: string,
ledgerVersion?: number,
includeAllData?: boolean,
includeTransactions?: boolean,
ledgerHash?: string
ledgerVersion?: number
includeAllData?: boolean
includeTransactions?: boolean
includeState?: boolean
}
async function getLedger(
this: RippleAPI, options: GetLedgerOptions = {}
this: RippleAPI,
options: GetLedgerOptions = {}
): Promise<FormattedLedger> {
// 1. Validate
validate.getLedger({options})

View File

@@ -11,7 +11,7 @@ import {RippleAPI} from '..'
import BigNumber from 'bignumber.js'
export type FormattedOrderbook = {
bids: FormattedOrderbookOrder[],
bids: FormattedOrderbookOrder[]
asks: FormattedOrderbookOrder[]
}
@@ -34,13 +34,18 @@ function flipOrder(order: FormattedOrderbookOrder) {
return _.merge({}, order, {specification: newSpecification})
}
function alignOrder(base: Issue, order: FormattedOrderbookOrder): FormattedOrderbookOrder {
function alignOrder(
base: Issue,
order: FormattedOrderbookOrder
): FormattedOrderbookOrder {
const quantity = order.specification.quantity
return isSameIssue(quantity, base) ? order : flipOrder(order)
}
export function formatBidsAndAsks(
orderbook: OrderbookInfo, offers: BookOffer[]) {
orderbook: OrderbookInfo,
offers: BookOffer[]
) {
// the "base" currency is the currency that you are buying or selling
// the "counter" is the currency that the "base" is priced in
// a "bid"/"ask" is an order to buy/sell the base, respectively
@@ -51,9 +56,11 @@ export function formatBidsAndAsks(
// for asks: lowest quality => lowest totalPrice/quantity => lowest price
// for both bids and asks, lowest quality is closest to mid-market
// we sort the orders so that earlier orders are closer to mid-market
const orders = offers.sort((a, b) => {
return (new BigNumber(a.quality)).comparedTo(b.quality)
}).map(parseOrderbookOrder)
const orders = offers
.sort((a, b) => {
return new BigNumber(a.quality).comparedTo(b.quality)
})
.map(parseOrderbookOrder)
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base))
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'))
@@ -64,8 +71,11 @@ export function formatBidsAndAsks(
// account is to specify a "perspective", which affects which unfunded offers
// are returned
async function makeRequest(
api: RippleAPI, taker: string, options: GetOrderbookOptions,
takerGets: Issue, takerPays: Issue
api: RippleAPI,
taker: string,
options: GetOrderbookOptions,
takerGets: Issue,
takerPays: Issue
) {
const orderData = utils.renameCounterpartyToIssuerInOrder({
taker_gets: takerGets,
@@ -80,14 +90,13 @@ async function makeRequest(
})
}
export type GetOrderbookOptions = {
limit?: number,
limit?: number
ledgerVersion?: number
}
export type OrderbookInfo = {
base: Issue,
base: Issue
counter: Issue
}
@@ -105,10 +114,13 @@ export async function getOrderbook(
makeRequest(this, address, options, orderbook.counter, orderbook.base)
])
// 3. Return Formatted Response
const directOffers = _.flatMap(directOfferResults,
directOfferResult => directOfferResult.offers)
const reverseOffers = _.flatMap(reverseOfferResults,
reverseOfferResult => reverseOfferResult.offers)
return formatBidsAndAsks(orderbook,
[...directOffers, ...reverseOffers])
const directOffers = _.flatMap(
directOfferResults,
directOfferResult => directOfferResult.offers
)
const reverseOffers = _.flatMap(
reverseOfferResults,
reverseOfferResult => reverseOfferResult.offers
)
return formatBidsAndAsks(orderbook, [...directOffers, ...reverseOffers])
}

View File

@@ -5,12 +5,13 @@ import {RippleAPI} from '..'
import {AccountOffersResponse} from '../common/types/commands'
export type GetOrdersOptions = {
limit?: number,
limit?: number
ledgerVersion?: number
}
function formatResponse(
address: string, responses: AccountOffersResponse[]
address: string,
responses: AccountOffersResponse[]
): FormattedAccountOrder[] {
let orders: FormattedAccountOrder[] = []
for (const response of responses) {
@@ -23,14 +24,16 @@ function formatResponse(
}
export default async function getOrders(
this: RippleAPI, address: string, options: GetOrdersOptions = {}
this: RippleAPI,
address: string,
options: GetOrdersOptions = {}
): Promise<FormattedAccountOrder[]> {
// 1. Validate
validate.getOrders({address, options})
// 2. Make Request
const responses = await this._requestAll('account_offers', {
account: address,
ledger_index: options.ledgerVersion || await this.getLedgerVersion(),
ledger_index: options.ledgerVersion || (await this.getLedgerVersion()),
limit: options.limit
})
// 3. Return Formatted Response, from the perspective of `address`

View File

@@ -0,0 +1,34 @@
import * as assert from 'assert'
import {removeUndefined} from '../../common'
import {classicAddressToXAddress} from 'ripple-address-codec'
export type FormattedAccountDelete = {
// account (address) of an account to receive any leftover XRP after deleting the sending account.
// Must be a funded account in the ledger, and must not be the sending account.
destination: string
// (Optional) Arbitrary destination tag that identifies a hosted recipient or other information
// for the recipient of the deleted account's leftover XRP. NB: Ensure that the hosted recipient is
// able to account for AccountDelete transactions; if not, your balance may not be properly credited.
destinationTag?: number
// X-address of an account to receive any leftover XRP after deleting the sending account.
// Must be a funded account in the ledger, and must not be the sending account.
destinationXAddress: string
}
function parseAccountDelete(tx: any): FormattedAccountDelete {
assert.ok(tx.TransactionType === 'AccountDelete')
return removeUndefined({
destination: tx.Destination,
destinationTag: tx.DestinationTag,
destinationXAddress: classicAddressToXAddress(
tx.Destination,
tx.DestinationTag === undefined ? false : tx.DestinationTag,
false
)
})
}
export default parseAccountDelete

View File

@@ -6,10 +6,10 @@ import {orderFlags} from './flags'
import {FormattedOrderSpecification} from '../../common/types/objects'
export type FormattedAccountOrder = {
specification: FormattedOrderSpecification,
properties: {
maker: string,
sequence: number,
specification: FormattedOrderSpecification
properties: {
maker: string
sequence: number
makerExchangeRate: string
}
}
@@ -23,13 +23,14 @@ function computeQuality(takerGets, takerPays) {
// rippled 'account_offers' returns a different format for orders than 'tx'
// the flags are also different
export function parseAccountOrder(
address: string, order: any
address: string,
order: any
): FormattedAccountOrder {
const direction = (order.flags & orderFlags.Sell) === 0 ? 'buy' : 'sell'
const takerGetsAmount = parseAmount(order.taker_gets)
const takerPaysAmount = parseAmount(order.taker_pays)
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
const quantity = direction === 'buy' ? takerPaysAmount : takerGetsAmount
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
@@ -37,15 +38,18 @@ export function parseAccountOrder(
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((order.flags & orderFlags.Passive) !== 0) || undefined,
passive: (order.flags & orderFlags.Passive) !== 0 || undefined,
// rippled currently does not provide "expiration" in account_offers
expirationTime: parseTimestamp(order.expiration)
})
const makerExchangeRate = order.quality ?
adjustQualityForXRP(order.quality.toString(),
takerGetsAmount.currency, takerPaysAmount.currency) :
computeQuality(takerGetsAmount, takerPaysAmount)
const makerExchangeRate = order.quality
? adjustQualityForXRP(
order.quality.toString(),
takerGetsAmount.currency,
takerPaysAmount.currency
)
: computeQuality(takerGetsAmount, takerPaysAmount)
const properties = {
maker: address,
sequence: order.seq,

View File

@@ -1,5 +1,3 @@
function parseAmendment(tx: any) {
return {
amendment: tx.Amendment

View File

@@ -1,7 +1,6 @@
import * as common from '../../common'
import {Amount, RippledAmount} from '../../common/types/objects'
function parseAmount(amount: RippledAmount): Amount {
if (typeof amount === 'string') {
return {

View File

@@ -2,7 +2,6 @@ import * as assert from 'assert'
import {removeUndefined} from '../../common'
export type FormattedCheckCancel = {
// ID of the Check ledger object to cancel.
checkID: string
}

View File

@@ -4,14 +4,13 @@ import parseAmount from './amount'
import {Amount} from '../../common/types/objects'
export type FormattedCheckCash = {
// ID of the Check ledger object to cash.
checkID: string,
checkID: string
// (Optional) redeem the Check for exactly this amount, if possible.
// The currency must match that of the `SendMax` of the corresponding
// `CheckCreate` transaction.
amount: Amount,
amount: Amount
// (Optional) redeem the Check for at least this amount and
// for as much as possible.

View File

@@ -5,19 +5,18 @@ import parseAmount from './amount'
import {Amount} from '../../common/types/objects'
export type FormattedCheckCreate = {
// account that can cash the check.
destination: string,
destination: string
// amount the check is allowed to debit the sender,
// including transfer fees on non-XRP currencies.
sendMax: Amount,
sendMax: Amount
// (Optional) identifies the reason for the check, or a hosted recipient.
destinationTag?: string,
destinationTag?: string
// (Optional) time in seconds since the Ripple Epoch.
expiration?: string,
expiration?: string
// (Optional) 256-bit hash representing a specific reason or identifier.
invoiceID?: string

View File

@@ -3,7 +3,7 @@ import {removeUndefined} from '../../common'
export type FormattedDepositPreauth = {
// account (address) of the sender to preauthorize
authorize: string,
authorize: string
// account (address) of the sender whose preauthorization should be revoked
unauthorize: string

View File

@@ -1,9 +1,8 @@
import BigNumber from 'bignumber.js'
import {dropsToXrp} from '../../common'
function parseFeeUpdate(tx: any) {
const baseFeeDrops = (new BigNumber(tx.BaseFee, 16)).toString()
const baseFeeDrops = new BigNumber(tx.BaseFee, 16).toString()
return {
baseFeeXRP: dropsToXrp(baseFeeDrops),
referenceFeeUnits: tx.ReferenceFeeUnits,

View File

@@ -4,11 +4,12 @@ import {constants} from '../../common'
const AccountFields = constants.AccountFields
function parseField(info, value) {
if (info.encoding === 'hex' && !info.length) { // e.g. "domain"
if (info.encoding === 'hex' && !info.length) {
// e.g. "domain"
return Buffer.from(value, 'hex').toString('ascii')
}
if (info.shift) {
return (new BigNumber(value)).shiftedBy(-info.shift).toNumber()
return new BigNumber(value).shiftedBy(-info.shift).toNumber()
}
return value
}
@@ -42,7 +43,8 @@ function parseFields(data: any): object {
address: entry.SignerEntry.Account,
weight: entry.SignerEntry.SignerWeight
}
})
}
)
}
}
return settings

View File

@@ -1,5 +1,3 @@
const orderFlags = {
Passive: 0x00010000,
Sell: 0x00020000 // offer was placed as a sell
@@ -16,7 +14,4 @@ const trustlineFlags = {
HighFreeze: 0x00800000
}
export {
orderFlags,
trustlineFlags
}
export {orderFlags, trustlineFlags}

View File

@@ -7,19 +7,19 @@ export type FormattedLedger = {
// TODO: properties in type don't match response object. Fix!
// accepted: boolean,
// closed: boolean,
stateHash: string,
closeTime: string,
closeTimeResolution: number,
closeFlags: number,
ledgerHash: string,
ledgerVersion: number,
parentLedgerHash: string,
parentCloseTime: string,
totalDrops: string,
transactionHash: string,
transactions?: Array<object>,
transactionHashes?: Array<string>,
rawState?: string,
stateHash: string
closeTime: string
closeTimeResolution: number
closeFlags: number
ledgerHash: string
ledgerVersion: number
parentLedgerHash: string
parentCloseTime: string
totalDrops: string
transactionHash: string
transactions?: Array<object>
transactionHashes?: Array<string>
rawState?: string
stateHashes?: Array<string>
}
@@ -44,8 +44,10 @@ function parseTransactions(transactions, ledgerVersion) {
return {transactionHashes: transactions}
}
return {
transactions: _.map(transactions,
_.partial(parseTransactionWrapper, ledgerVersion))
transactions: _.map(
transactions,
_.partial(parseTransactionWrapper, ledgerVersion)
)
}
}
@@ -66,20 +68,22 @@ function parseState(state) {
*/
export function parseLedger(ledger: Ledger): FormattedLedger {
const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10)
return removeUndefined(Object.assign(
{
stateHash: ledger.account_hash,
closeTime: rippleTimeToISO8601(ledger.close_time),
closeTimeResolution: ledger.close_time_resolution,
closeFlags: ledger.close_flags,
ledgerHash: ledger.hash || ledger.ledger_hash,
ledgerVersion: ledgerVersion,
parentLedgerHash: ledger.parent_hash,
parentCloseTime: rippleTimeToISO8601(ledger.parent_close_time),
totalDrops: ledger.total_coins || ledger.totalCoins,
transactionHash: ledger.transaction_hash
},
parseTransactions(ledger.transactions, ledgerVersion),
parseState(ledger.accountState)
))
return removeUndefined(
Object.assign(
{
stateHash: ledger.account_hash,
closeTime: rippleTimeToISO8601(ledger.close_time),
closeTimeResolution: ledger.close_time_resolution,
closeFlags: ledger.close_flags,
ledgerHash: ledger.hash || ledger.ledger_hash,
ledgerVersion: ledgerVersion,
parentLedgerHash: ledger.parent_hash,
parentCloseTime: rippleTimeToISO8601(ledger.parent_close_time),
totalDrops: ledger.total_coins || ledger.totalCoins,
transactionHash: ledger.transaction_hash
},
parseTransactions(ledger.transactions, ledgerVersion),
parseState(ledger.accountState)
)
)
}

View File

@@ -15,17 +15,16 @@ function parseOrder(tx: OfferCreateTransaction): FormattedOrderSpecification {
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell'
const takerGetsAmount = parseAmount(tx.TakerGets)
const takerPaysAmount = parseAmount(tx.TakerPays)
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
const quantity = direction === 'buy' ? takerPaysAmount : takerGetsAmount
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
return removeUndefined({
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((tx.Flags & flags.Passive) !== 0) || undefined,
immediateOrCancel: ((tx.Flags & flags.ImmediateOrCancel) !== 0)
|| undefined,
fillOrKill: ((tx.Flags & flags.FillOrKill) !== 0) || undefined,
passive: (tx.Flags & flags.Passive) !== 0 || undefined,
immediateOrCancel: (tx.Flags & flags.ImmediateOrCancel) !== 0 || undefined,
fillOrKill: (tx.Flags & flags.FillOrKill) !== 0 || undefined,
expirationTime: parseTimestamp(tx.Expiration)
})
}

View File

@@ -8,27 +8,25 @@ import {BookOffer} from '../../common/types/commands'
import {Amount, FormattedOrderSpecification} from '../../common/types/objects'
export type FormattedOrderbookOrder = {
specification: FormattedOrderSpecification,
specification: FormattedOrderSpecification
properties: {
maker: string,
sequence: number,
maker: string
sequence: number
makerExchangeRate: string
},
}
state?: {
fundedAmount: Amount,
fundedAmount: Amount
priceOfFundedAmount: Amount
},
}
data: BookOffer
}
export function parseOrderbookOrder(
data: BookOffer
): FormattedOrderbookOrder {
export function parseOrderbookOrder(data: BookOffer): FormattedOrderbookOrder {
const direction = (data.Flags & orderFlags.Sell) === 0 ? 'buy' : 'sell'
const takerGetsAmount = parseAmount(data.TakerGets)
const takerPaysAmount = parseAmount(data.TakerPays)
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount
const quantity = direction === 'buy' ? takerPaysAmount : takerGetsAmount
const totalPrice = direction === 'buy' ? takerGetsAmount : takerPaysAmount
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
@@ -36,21 +34,26 @@ export function parseOrderbookOrder(
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((data.Flags & orderFlags.Passive) !== 0) || undefined,
passive: (data.Flags & orderFlags.Passive) !== 0 || undefined,
expirationTime: parseTimestamp(data.Expiration)
})
const properties = {
maker: data.Account,
sequence: data.Sequence,
makerExchangeRate: adjustQualityForXRP(data.quality,
takerGetsAmount.currency, takerPaysAmount.currency)
makerExchangeRate: adjustQualityForXRP(
data.quality,
takerGetsAmount.currency,
takerPaysAmount.currency
)
}
const takerGetsFunded = data.taker_gets_funded ?
parseAmount(data.taker_gets_funded) : undefined
const takerPaysFunded = data.taker_pays_funded ?
parseAmount(data.taker_pays_funded) : undefined
const takerGetsFunded = data.taker_gets_funded
? parseAmount(data.taker_gets_funded)
: undefined
const takerPaysFunded = data.taker_pays_funded
? parseAmount(data.taker_pays_funded)
: undefined
const available = removeUndefined({
fundedAmount: takerGetsFunded,
priceOfFundedAmount: takerPaysFunded

View File

@@ -4,33 +4,48 @@ 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']))
)
}
function removeAnyCounterpartyEncoding(address: string, amount: Amount) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount
return amount.counterparty === address
? _.omit(amount, 'counterparty')
: amount
}
function createAdjustment(
address: string, adjustmentWithoutAddress: object): any {
address: string,
adjustmentWithoutAddress: object
): any {
const amountKey = _.keys(adjustmentWithoutAddress)[0]
const amount = adjustmentWithoutAddress[amountKey]
return _.set({address: address}, amountKey,
removeAnyCounterpartyEncoding(address, amount))
return _.set(
{address: address},
amountKey,
removeAnyCounterpartyEncoding(address, amount)
)
}
function parseAlternative(sourceAddress: string, destinationAddress: string,
destinationAmount: RippledAmount, alternative: any
function parseAlternative(
sourceAddress: string,
destinationAddress: string,
destinationAmount: RippledAmount,
alternative: any
): Path {
// we use "maxAmount"/"minAmount" here so that the result can be passed
// directly to preparePayment
const amounts = (alternative.destination_amount !== undefined) ?
{source: {amount: parseAmount(alternative.source_amount)},
destination: {minAmount: parseAmount(alternative.destination_amount)}} :
{source: {maxAmount: parseAmount(alternative.source_amount)},
destination: {amount: parseAmount(destinationAmount)}}
const amounts =
alternative.destination_amount !== undefined
? {
source: {amount: parseAmount(alternative.source_amount)},
destination: {minAmount: parseAmount(alternative.destination_amount)}
}
: {
source: {maxAmount: parseAmount(alternative.source_amount)},
destination: {amount: parseAmount(destinationAmount)}
}
return {
source: createAdjustment(sourceAddress, amounts.source),
@@ -44,7 +59,8 @@ function parsePathfind(pathfindResult: RippledPathsResponse): GetPaths {
const destinationAddress = pathfindResult.destination_account
const destinationAmount = pathfindResult.destination_amount
return pathfindResult.alternatives.map(alt =>
parseAlternative(sourceAddress, destinationAddress, destinationAmount, alt))
parseAlternative(sourceAddress, destinationAddress, destinationAmount, alt)
)
}
export default parsePathfind

View File

@@ -3,17 +3,17 @@ import {removeUndefined, dropsToXrp} from '../../common'
import {PayChannelLedgerEntry} from '../../common/types/objects'
export type FormattedPaymentChannel = {
account: string,
amount: string,
balance: string,
publicKey: string,
destination: string,
settleDelay: number,
expiration?: string,
cancelAfter?: string,
sourceTag?: number,
destinationTag?: number,
previousAffectingTransactionID: string,
account: string
amount: string
balance: string
publicKey: string
destination: string
settleDelay: number
expiration?: string
cancelAfter?: string
sourceTag?: number
destinationTag?: number
previousAffectingTransactionID: string
previousAffectingTransactionLedgerVersion: number
}

View File

@@ -13,8 +13,9 @@ function isQualityLimited(tx) {
}
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount
return amount.counterparty === address
? _.omit(amount, 'counterparty')
: amount
}
// Payment specification
@@ -24,12 +25,14 @@ function parsePayment(tx: any): object {
const source = {
address: tx.Account,
maxAmount: removeGenericCounterparty(
parseAmount(tx.SendMax || tx.Amount), tx.Account),
parseAmount(tx.SendMax || tx.Amount),
tx.Account
),
tag: tx.SourceTag
}
const destination: {
address: string,
address: string
tag: number | undefined
} = {
address: tx.Destination,

View File

@@ -5,8 +5,9 @@ const AccountFlags = constants.AccountFlags
import parseFields from './fields'
function getAccountRootModifiedNode(tx: any) {
const modifiedNodes = tx.meta.AffectedNodes.filter(node =>
node.ModifiedNode.LedgerEntryType === 'AccountRoot')
const modifiedNodes = tx.meta.AffectedNodes.filter(
node => node.ModifiedNode.LedgerEntryType === 'AccountRoot'
)
assert.ok(modifiedNodes.length === 1)
return modifiedNodes[0].ModifiedNode
}
@@ -51,8 +52,11 @@ function parseFlags(tx: any): any {
function parseSettings(tx: any) {
const txType = tx.TransactionType
assert.ok(txType === 'AccountSet' || txType === 'SetRegularKey' ||
txType === 'SignerListSet')
assert.ok(
txType === 'AccountSet' ||
txType === 'SetRegularKey' ||
txType === 'SignerListSet'
)
return _.assign({}, parseFlags(tx), parseFields(tx))
}

View File

@@ -1,27 +1,31 @@
import {parseOutcome} from './utils'
import {removeUndefined} from '../../common'
import parsePayment from './payment'
import parseTrustline from './trustline'
import parseOrder from './order'
import parseOrderCancellation from './cancellation'
import parseSettings from './settings'
import parseAccountDelete from './account-delete'
import parseCheckCancel from './check-cancel'
import parseCheckCash from './check-cash'
import parseCheckCreate from './check-create'
import parseDepositPreauth from './deposit-preauth'
import parseEscrowCancellation from './escrow-cancellation'
import parseEscrowCreation from './escrow-creation'
import parseEscrowExecution from './escrow-execution'
import parseEscrowCancellation from './escrow-cancellation'
import parseCheckCreate from './check-create'
import parseCheckCash from './check-cash'
import parseCheckCancel from './check-cancel'
import parseDepositPreauth from './deposit-preauth'
import parseOrderCancellation from './cancellation'
import parseOrder from './order'
import parsePayment from './payment'
import parsePaymentChannelClaim from './payment-channel-claim'
import parsePaymentChannelCreate from './payment-channel-create'
import parsePaymentChannelFund from './payment-channel-fund'
import parsePaymentChannelClaim from './payment-channel-claim'
import parseFeeUpdate from './fee-update'
import parseAmendment from './amendment'
import parseTrustline from './trustline'
import parseAmendment from './amendment' // pseudo-transaction
import parseFeeUpdate from './fee-update' // pseudo-transaction
function parseTransactionType(type) {
// Ordering matches https://developers.ripple.com/transaction-types.html
const mapping = {
AccountSet: 'settings',
AccountDelete: 'accountDelete',
CheckCancel: 'checkCancel',
CheckCash: 'checkCash',
CheckCreate: 'checkCreate',
@@ -49,30 +53,35 @@ function parseTransactionType(type) {
function parseTransaction(tx: any, includeRawTransaction: boolean): any {
const type = parseTransactionType(tx.TransactionType)
const mapping = {
'payment': parsePayment,
'trustline': parseTrustline,
'order': parseOrder,
'orderCancellation': parseOrderCancellation,
'settings': parseSettings,
'escrowCreation': parseEscrowCreation,
'escrowExecution': parseEscrowExecution,
'escrowCancellation': parseEscrowCancellation,
'checkCreate': parseCheckCreate,
'checkCash': parseCheckCash,
'checkCancel': parseCheckCancel,
'depositPreauth': parseDepositPreauth,
'paymentChannelCreate': parsePaymentChannelCreate,
'paymentChannelFund': parsePaymentChannelFund,
'paymentChannelClaim': parsePaymentChannelClaim,
'feeUpdate': parseFeeUpdate,
'amendment': parseAmendment
settings: parseSettings,
accountDelete: parseAccountDelete,
checkCancel: parseCheckCancel,
checkCash: parseCheckCash,
checkCreate: parseCheckCreate,
depositPreauth: parseDepositPreauth,
escrowCancellation: parseEscrowCancellation,
escrowCreation: parseEscrowCreation,
escrowExecution: parseEscrowExecution,
orderCancellation: parseOrderCancellation,
order: parseOrder,
payment: parsePayment,
paymentChannelClaim: parsePaymentChannelClaim,
paymentChannelCreate: parsePaymentChannelCreate,
paymentChannelFund: parsePaymentChannelFund,
trustline: parseTrustline,
amendment: parseAmendment, // pseudo-transaction
feeUpdate: parseFeeUpdate // pseudo-transaction
}
const parser: Function = mapping[type]
const specification = parser ? parser(tx) : {
UNAVAILABLE: 'Unrecognized transaction type.',
SEE_RAW_TRANSACTION: 'Since this type is unrecognized, `rawTransaction` is included in this response.'
}
const specification = parser
? parser(tx)
: {
UNAVAILABLE: 'Unrecognized transaction type.',
SEE_RAW_TRANSACTION:
'Since this type is unrecognized, `rawTransaction` is included in this response.'
}
if (!parser) {
includeRawTransaction = true
}

View File

@@ -24,7 +24,10 @@ function parseTrustline(tx: any): object {
qualityIn: parseQuality(tx.QualityIn),
qualityOut: parseQuality(tx.QualityOut),
ripplingDisabled: parseFlag(
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
tx.Flags,
flags.SetNoRipple,
flags.ClearNoRipple
),
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
authorized: parseFlag(tx.Flags, flags.SetAuth, 0)
})

View File

@@ -7,25 +7,28 @@ import parseAmount from './amount'
import {Amount, Memo} from '../../common/types/objects'
function adjustQualityForXRP(
quality: string, takerGetsCurrency: string, takerPaysCurrency: string
quality: string,
takerGetsCurrency: string,
takerPaysCurrency: string
) {
// quality = takerPays.value/takerGets.value
// using drops (1e-6 XRP) for XRP values
const numeratorShift = (takerPaysCurrency === 'XRP' ? -6 : 0)
const denominatorShift = (takerGetsCurrency === 'XRP' ? -6 : 0)
const numeratorShift = takerPaysCurrency === 'XRP' ? -6 : 0
const denominatorShift = takerGetsCurrency === 'XRP' ? -6 : 0
const shift = numeratorShift - denominatorShift
return shift === 0 ? quality :
(new BigNumber(quality)).shiftedBy(shift).toString()
return shift === 0
? quality
: new BigNumber(quality).shiftedBy(shift).toString()
}
function parseQuality(quality?: number|null): number|undefined {
function parseQuality(quality?: number | null): number | undefined {
if (typeof quality !== 'number') {
return undefined
}
return (new BigNumber(quality)).shiftedBy(-9).toNumber()
return new BigNumber(quality).shiftedBy(-9).toNumber()
}
function parseTimestamp(rippleTime?: number|null): string|undefined {
function parseTimestamp(rippleTime?: number | null): string | undefined {
if (typeof rippleTime !== 'number') {
return undefined
}
@@ -57,14 +60,14 @@ function isPartialPayment(tx: any) {
}
function parseDeliveredAmount(tx: any): Amount | void {
if (tx.TransactionType !== 'Payment' ||
tx.meta.TransactionResult !== 'tesSUCCESS') {
if (
tx.TransactionType !== 'Payment' ||
tx.meta.TransactionResult !== 'tesSUCCESS'
) {
return undefined
}
if (tx.meta.delivered_amount &&
tx.meta.delivered_amount === 'unavailable') {
if (tx.meta.delivered_amount && tx.meta.delivered_amount === 'unavailable') {
return undefined
}
@@ -96,7 +99,7 @@ function parseDeliveredAmount(tx: any): Amount | void {
return undefined
}
function parseOutcome(tx: any): any|undefined {
function parseOutcome(tx: any): any | undefined {
const metadata = tx.meta || tx.metaData
if (!metadata) {
return undefined
@@ -121,11 +124,11 @@ function parseOutcome(tx: any): any|undefined {
})
}
function hexToString(hex: string): string|undefined {
function hexToString(hex: string): string | undefined {
return hex ? Buffer.from(hex, 'hex').toString('utf-8') : undefined
}
function parseMemos(tx: any): Array<Memo>|undefined {
function parseMemos(tx: any): Array<Memo> | undefined {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined
}

View File

@@ -1,18 +1,22 @@
import {Amount, RippledAmount, Adjustment, MaxAdjustment,
MinAdjustment} from '../common/types/objects'
import {
Amount,
RippledAmount,
Adjustment,
MaxAdjustment,
MinAdjustment
} from '../common/types/objects'
// Amount where counterparty and value are optional
export type LaxLaxAmount = {
currency: string,
value?: string,
issuer?: string,
currency: string
value?: string
issuer?: string
counterparty?: string
}
export type Path = {
source: Adjustment | MaxAdjustment,
destination: Adjustment | MinAdjustment,
source: Adjustment | MaxAdjustment
destination: Adjustment | MinAdjustment
paths: string
}
@@ -20,41 +24,43 @@ export type GetPaths = Array<Path>
export type PathFind = {
source: {
address: string,
amount?: Amount,
currencies?: Array<{currency: string, counterparty?:string}>
},
address: string
amount?: Amount
currencies?: Array<{currency: string; counterparty?: string}>
}
destination: {
address: string,
address: string
amount: LaxLaxAmount
}
}
export type PathFindRequest = {
command: string,
source_account: string,
destination_amount: RippledAmount,
destination_account: string,
source_currencies?: {currency: string, issuer?: string}[],
command: string
source_account: string
destination_amount: RippledAmount
destination_account: string
source_currencies?: {currency: string; issuer?: string}[]
send_max?: RippledAmount
}
export type RippledPathsResponse = {
alternatives: Array<{
paths_computed: Array<Array<{
type: number,
type_hex: string,
account?: string,
issuer?: string,
currency?: string
}>>,
paths_computed: Array<
Array<{
type: number
type_hex: string
account?: string
issuer?: string
currency?: string
}>
>
source_amount: RippledAmount
}>,
type: string,
destination_account: string,
destination_amount: RippledAmount,
destination_currencies?: Array<string>,
source_account: string,
source_currencies?: Array<{currency: string}>,
}>
type: string
destination_account: string
destination_amount: RippledAmount
destination_currencies?: Array<string>
source_account: string
source_currencies?: Array<{currency: string}>
full_reply?: boolean
}

View File

@@ -12,28 +12,37 @@ import {Connection} from '../common'
import parsePathfind from './parse/pathfind'
import {RippledAmount, Amount} from '../common/types/objects'
import {
GetPaths, PathFind, RippledPathsResponse, PathFindRequest
GetPaths,
PathFind,
RippledPathsResponse,
PathFindRequest
} from './pathfind-types'
import {RippleAPI} from '..'
const NotFoundError = errors.NotFoundError
const ValidationError = errors.ValidationError
function addParams(request: PathFindRequest, result: RippledPathsResponse
function addParams(
request: PathFindRequest,
result: RippledPathsResponse
): RippledPathsResponse {
return _.defaults(_.assign({}, result, {
source_account: request.source_account,
source_currencies: request.source_currencies
}), {destination_amount: request.destination_amount})
return _.defaults(
_.assign({}, result, {
source_account: request.source_account,
source_currencies: request.source_currencies
}),
{destination_amount: request.destination_amount}
)
}
function requestPathFind(connection: Connection, pathfind: PathFind
function requestPathFind(
connection: Connection,
pathfind: PathFind
): Promise<RippledPathsResponse> {
const destinationAmount: Amount = _.assign(
{
// This is converted back to drops by toRippledAmount()
value: pathfind.destination.amount.currency === 'XRP' ?
dropsToXrp('-1') : '-1'
value:
pathfind.destination.amount.currency === 'XRP' ? dropsToXrp('-1') : '-1'
},
pathfind.destination.amount
)
@@ -43,21 +52,26 @@ function requestPathFind(connection: Connection, pathfind: PathFind
destination_account: pathfind.destination.address,
destination_amount: toRippledAmount(destinationAmount)
}
if (typeof request.destination_amount === 'object'
&& !request.destination_amount.issuer) {
if (
typeof request.destination_amount === 'object' &&
!request.destination_amount.issuer
) {
// Convert blank issuer to sender's address
// (Ripple convention for 'any issuer')
// https://developers.ripple.com/payment.html#special-issuer-values-for-sendmax-and-amount
request.destination_amount.issuer = request.destination_account
}
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
request.source_currencies = pathfind.source.currencies.map(
amount => renameCounterpartyToIssuer(amount))
request.source_currencies = pathfind.source.currencies.map(amount =>
renameCounterpartyToIssuer(amount)
)
}
if (pathfind.source.amount) {
if (pathfind.destination.amount.value !== undefined) {
throw new ValidationError('Cannot specify both source.amount'
+ ' and destination.amount.value in getPaths')
throw new ValidationError(
'Cannot specify both source.amount' +
' and destination.amount.value in getPaths'
)
}
request.send_max = toRippledAmount(pathfind.source.amount)
if (typeof request.send_max !== 'string' && !request.send_max.issuer) {
@@ -68,12 +82,14 @@ function requestPathFind(connection: Connection, pathfind: PathFind
return connection.request(request).then(paths => addParams(request, paths))
}
function addDirectXrpPath(paths: RippledPathsResponse, xrpBalance: string
function addDirectXrpPath(
paths: RippledPathsResponse,
xrpBalance: string
): RippledPathsResponse {
// Add XRP "path" only if the source acct has enough XRP to make the payment
const destinationAmount = paths.destination_amount
// @ts-ignore: destinationAmount can be a currency amount object! Fix!
if ((new BigNumber(xrpBalance)).isGreaterThanOrEqualTo(destinationAmount)) {
if (new BigNumber(xrpBalance).isGreaterThanOrEqualTo(destinationAmount)) {
paths.alternatives.unshift({
paths_computed: [],
source_amount: paths.destination_amount
@@ -84,38 +100,49 @@ function addDirectXrpPath(paths: RippledPathsResponse, xrpBalance: string
function isRippledIOUAmount(amount: RippledAmount) {
// rippled XRP amounts are specified as decimal strings
return (typeof amount === 'object') &&
amount.currency && (amount.currency !== 'XRP')
return (
typeof amount === 'object' && amount.currency && amount.currency !== 'XRP'
)
}
function conditionallyAddDirectXRPPath(connection: Connection, address: string,
function conditionallyAddDirectXRPPath(
connection: Connection,
address: string,
paths: RippledPathsResponse
): Promise<RippledPathsResponse> {
if (isRippledIOUAmount(paths.destination_amount)
|| !_.includes(paths.destination_currencies, 'XRP')) {
if (
isRippledIOUAmount(paths.destination_amount) ||
!_.includes(paths.destination_currencies, 'XRP')
) {
return Promise.resolve(paths)
}
return getXRPBalance(connection, address, undefined).then(
xrpBalance => addDirectXrpPath(paths, xrpBalance))
return getXRPBalance(connection, address, undefined).then(xrpBalance =>
addDirectXrpPath(paths, xrpBalance)
)
}
function filterSourceFundsLowPaths(pathfind: PathFind,
function filterSourceFundsLowPaths(
pathfind: PathFind,
paths: RippledPathsResponse
): RippledPathsResponse {
if (pathfind.source.amount &&
pathfind.destination.amount.value === undefined && paths.alternatives) {
if (
pathfind.source.amount &&
pathfind.destination.amount.value === undefined &&
paths.alternatives
) {
paths.alternatives = _.filter(paths.alternatives, alt => {
if (!alt.source_amount) {
return false
}
const pathfindSourceAmountValue = new BigNumber(
pathfind.source.amount.currency === 'XRP' ?
xrpToDrops(pathfind.source.amount.value) :
pathfind.source.amount.value)
pathfind.source.amount.currency === 'XRP'
? xrpToDrops(pathfind.source.amount.value)
: pathfind.source.amount.value
)
const altSourceAmountValue = new BigNumber(
typeof alt.source_amount === 'string' ?
alt.source_amount :
alt.source_amount.value
typeof alt.source_amount === 'string'
? alt.source_amount
: alt.source_amount.value
)
return altSourceAmountValue.eq(pathfindSourceAmountValue)
})
@@ -127,24 +154,35 @@ function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
if (paths.alternatives && paths.alternatives.length > 0) {
return parsePathfind(paths)
}
if (paths.destination_currencies !== undefined &&
!_.includes(paths.destination_currencies,
pathfind.destination.amount.currency)) {
throw new NotFoundError('No paths found. ' +
'The destination_account does not accept ' +
pathfind.destination.amount.currency + ', they only accept: ' +
paths.destination_currencies.join(', '))
if (
paths.destination_currencies !== undefined &&
!_.includes(
paths.destination_currencies,
pathfind.destination.amount.currency
)
) {
throw new NotFoundError(
'No paths found. ' +
'The destination_account does not accept ' +
pathfind.destination.amount.currency +
', they only accept: ' +
paths.destination_currencies.join(', ')
)
} else if (paths.source_currencies && paths.source_currencies.length > 0) {
throw new NotFoundError('No paths found. Please ensure' +
' that the source_account has sufficient funds to execute' +
' the payment in one of the specified source_currencies. If it does' +
' there may be insufficient liquidity in the network to execute' +
' this payment right now')
throw new NotFoundError(
'No paths found. Please ensure' +
' that the source_account has sufficient funds to execute' +
' the payment in one of the specified source_currencies. If it does' +
' there may be insufficient liquidity in the network to execute' +
' this payment right now'
)
} else {
throw new NotFoundError('No paths found.' +
' Please ensure that the source_account has sufficient funds to' +
' execute the payment. If it does there may be insufficient liquidity' +
' in the network to execute this payment right now')
throw new NotFoundError(
'No paths found.' +
' Please ensure that the source_account has sufficient funds to' +
' execute the payment. If it does there may be insufficient liquidity' +
' in the network to execute this payment right now'
)
}
}
@@ -152,9 +190,10 @@ function getPaths(this: RippleAPI, pathfind: PathFind): Promise<GetPaths> {
validate.getPaths({pathfind})
const address = pathfind.source.address
return requestPathFind(this.connection, pathfind).then(paths =>
conditionallyAddDirectXRPPath(this.connection, address, paths)
)
return requestPathFind(this.connection, pathfind)
.then(paths =>
conditionallyAddDirectXRPPath(this.connection, address, paths)
)
.then(paths => filterSourceFundsLowPaths(pathfind, paths))
.then(paths => formatResponse(pathfind, paths))
}

View File

@@ -10,15 +10,18 @@ const NotFoundError = errors.NotFoundError
function formatResponse(
response: LedgerEntryResponse
): FormattedPaymentChannel {
if (response.node === undefined ||
response.node.LedgerEntryType !== 'PayChannel') {
if (
response.node === undefined ||
response.node.LedgerEntryType !== 'PayChannel'
) {
throw new NotFoundError('Payment channel ledger entry not found')
}
return parsePaymentChannel(response.node)
}
async function getPaymentChannel(
this: RippleAPI, id: string
this: RippleAPI,
id: string
): Promise<FormattedPaymentChannel> {
// 1. Validate
validate.getPaymentChannel({id})

View File

@@ -12,8 +12,9 @@ export type SettingsOptions = {
}
export function parseAccountFlags(
value: number,
options: {excludeFalse?: boolean} = {}) {
value: number,
options: {excludeFalse?: boolean} = {}
) {
const settings = {}
for (const flagName in AccountFlags) {
if (value & AccountFlags[flagName]) {
@@ -35,7 +36,9 @@ function formatSettings(response: AccountInfoResponse) {
}
export async function getSettings(
this: RippleAPI, address: string, options: SettingsOptions = {}
this: RippleAPI,
address: string,
options: SettingsOptions = {}
): Promise<FormattedSettings> {
// 1. Validate
validate.getSettings({address, options})

View File

@@ -8,19 +8,20 @@ import {RippledError} from '../common/errors'
import {RippleAPI} from '..'
export type TransactionOptions = {
minLedgerVersion?: number,
maxLedgerVersion?: number,
minLedgerVersion?: number
maxLedgerVersion?: number
includeRawTransaction?: boolean
}
type TransactionResponse = FormattedTransactionType & {
hash: string,
ledger_index: number,
meta: any,
validated?: boolean
hash: string
ledger_index: number
meta: any
validated?: boolean
}
function attachTransactionDate(connection: Connection, tx: any
function attachTransactionDate(
connection: Connection,
tx: any
): Promise<TransactionResponse> {
if (tx.date) {
return Promise.resolve(tx)
@@ -31,7 +32,8 @@ function attachTransactionDate(connection: Connection, tx: any
if (!ledgerVersion) {
return new Promise(() => {
const error = new errors.NotFoundError(
'Transaction has not been validated yet; try again later')
'Transaction has not been validated yet; try again later'
)
error.data = {
details: '(ledger_index and LedgerSequence not found in tx)'
}
@@ -44,56 +46,74 @@ function attachTransactionDate(connection: Connection, tx: any
ledger_index: ledgerVersion
}
return connection.request(request).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 => {
if (error instanceof errors.UnexpectedError) {
throw error
}
throw new errors.NotFoundError('Transaction ledger not found')
})
return connection
.request(request)
.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 => {
if (error instanceof errors.UnexpectedError) {
throw error
}
throw new errors.NotFoundError('Transaction ledger not found')
})
}
function isTransactionInRange(tx: any, options: TransactionOptions) {
return (!options.minLedgerVersion
|| tx.ledger_index >= options.minLedgerVersion)
&& (!options.maxLedgerVersion
|| tx.ledger_index <= options.maxLedgerVersion)
return (
(!options.minLedgerVersion ||
tx.ledger_index >= options.minLedgerVersion) &&
(!options.maxLedgerVersion || tx.ledger_index <= options.maxLedgerVersion)
)
}
function convertError(connection: Connection, options: TransactionOptions,
function convertError(
connection: Connection,
options: TransactionOptions,
error: RippledError
): Promise<Error> {
let shouldUseNotFoundError = false
if ((error.data && error.data.error === 'txnNotFound') || error.message === 'txnNotFound') {
if (
(error.data && error.data.error === 'txnNotFound') ||
error.message === 'txnNotFound'
) {
shouldUseNotFoundError = true
}
// In the future, we should deprecate this error, instead passing through the one from rippled.
const _error = shouldUseNotFoundError ? new errors.NotFoundError('Transaction not found') : error
const _error = shouldUseNotFoundError
? new errors.NotFoundError('Transaction not found')
: error
if (_error instanceof errors.NotFoundError) {
return utils.hasCompleteLedgerRange(connection, options.minLedgerVersion,
options.maxLedgerVersion).then(hasCompleteLedgerRange => {
if (!hasCompleteLedgerRange) {
return utils.isPendingLedgerVersion(
connection, options.maxLedgerVersion)
.then(isPendingLedgerVersion => {
return isPendingLedgerVersion ?
new errors.PendingLedgerVersionError() :
new errors.MissingLedgerHistoryError()
})
}
return _error
})
return utils
.hasCompleteLedgerRange(
connection,
options.minLedgerVersion,
options.maxLedgerVersion
)
.then(hasCompleteLedgerRange => {
if (!hasCompleteLedgerRange) {
return utils
.isPendingLedgerVersion(connection, options.maxLedgerVersion)
.then(isPendingLedgerVersion => {
return isPendingLedgerVersion
? new errors.PendingLedgerVersionError()
: new errors.MissingLedgerHistoryError()
})
}
return _error
})
}
return Promise.resolve(_error)
}
function formatResponse(options: TransactionOptions, tx: TransactionResponse
function formatResponse(
options: TransactionOptions,
tx: TransactionResponse
): FormattedTransactionType {
if (tx.validated !== true || !isTransactionInRange(tx, options)) {
throw new errors.NotFoundError('Transaction not found')
@@ -101,7 +121,10 @@ function formatResponse(options: TransactionOptions, tx: TransactionResponse
return parseTransaction(tx, options.includeRawTransaction)
}
async function getTransaction(this: RippleAPI, id: string, options: TransactionOptions = {}
async function getTransaction(
this: RippleAPI,
id: string,
options: TransactionOptions = {}
): Promise<FormattedTransactionType> {
validate.getTransaction({id, options})
const _options = await utils.ensureLedgerVersion.call(this, options)
@@ -113,7 +136,7 @@ async function getTransaction(this: RippleAPI, id: string, options: TransactionO
const txWithDate = await attachTransactionDate(this.connection, tx)
return formatResponse(_options, txWithDate)
} catch (error) {
throw (await convertError(this.connection, _options, error))
throw await convertError(this.connection, _options, error)
}
}

View File

@@ -1,5 +1,5 @@
import * as _ from 'lodash'
import binary from 'ripple-binary-codec';
import binary from 'ripple-binary-codec'
import {computeTransactionHash} from '../common/hashes'
import * as utils from './utils'
import parseTransaction from './parse/transaction'
@@ -9,17 +9,17 @@ import {FormattedTransactionType} from '../transaction/types'
import {RippleAPI} from '..'
export type TransactionsOptions = {
start?: string,
limit?: number,
minLedgerVersion?: number,
maxLedgerVersion?: number,
earliestFirst?: boolean,
excludeFailures?: boolean,
initiated?: boolean,
counterparty?: string,
types?: Array<string>,
includeRawTransactions?: boolean,
binary?: boolean,
start?: string
limit?: number
minLedgerVersion?: number
maxLedgerVersion?: number
earliestFirst?: boolean
excludeFailures?: boolean
initiated?: boolean
counterparty?: string
types?: Array<string>
includeRawTransactions?: boolean
binary?: boolean
startTx?: FormattedTransactionType
}
@@ -39,8 +39,10 @@ function parseBinaryTransaction(transaction) {
function parseAccountTxTransaction(tx, includeRawTransaction: boolean) {
const _tx = tx.tx_blob ? parseBinaryTransaction(tx) : tx
// rippled uses a different response format for 'account_tx' than 'tx'
return parseTransaction(_.assign({}, _tx.tx,
{meta: _tx.meta, validated: _tx.validated}), includeRawTransaction)
return parseTransaction(
_.assign({}, _tx.tx, {meta: _tx.meta, validated: _tx.validated}),
includeRawTransaction
)
}
function counterpartyFilter(filters, tx: FormattedTransactionType) {
@@ -48,15 +50,20 @@ function counterpartyFilter(filters, tx: FormattedTransactionType) {
return true
}
const specification: any = tx.specification
if (specification && ((specification.destination &&
specification.destination.address === filters.counterparty) ||
(specification.counterparty === filters.counterparty))) {
return true
if (
specification &&
((specification.destination &&
specification.destination.address === filters.counterparty) ||
specification.counterparty === filters.counterparty)
) {
return true
}
return false
}
function transactionFilter(address: string, filters: TransactionsOptions,
function transactionFilter(
address: string,
filters: TransactionsOptions,
tx: FormattedTransactionType
) {
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
@@ -78,15 +85,21 @@ function transactionFilter(address: string, filters: TransactionsOptions,
}
function orderFilter(
options: TransactionsOptions, tx: FormattedTransactionType
options: TransactionsOptions,
tx: FormattedTransactionType
) {
return !options.startTx || (options.earliestFirst ?
utils.compareTransactions(tx, options.startTx) > 0 :
utils.compareTransactions(tx, options.startTx) < 0)
return (
!options.startTx ||
(options.earliestFirst
? utils.compareTransactions(tx, options.startTx) > 0
: utils.compareTransactions(tx, options.startTx) < 0)
)
}
function formatPartialResponse(address: string,
options: TransactionsOptions, data
function formatPartialResponse(
address: string,
options: TransactionsOptions,
data
) {
const parse = tx =>
parseAccountTxTransaction(tx, options.includeRawTransactions)
@@ -100,8 +113,12 @@ function formatPartialResponse(address: string,
}
}
function getAccountTx(connection: Connection, address: string,
options: TransactionsOptions, marker: string, limit: number
function getAccountTx(
connection: Connection,
address: string,
options: TransactionsOptions,
marker: string,
limit: number
) {
const request = {
command: 'account_tx',
@@ -116,12 +133,15 @@ function getAccountTx(connection: Connection, address: string,
marker: marker
}
return connection.request(request).then(response =>
formatPartialResponse(address, options, response))
return connection
.request(request)
.then(response => formatPartialResponse(address, options, response))
}
function checkForLedgerGaps(connection: Connection,
options: TransactionsOptions, transactions: GetTransactionsResponse
function checkForLedgerGaps(
connection: Connection,
options: TransactionsOptions,
transactions: GetTransactionsResponse
) {
let {minLedgerVersion, maxLedgerVersion} = options
@@ -136,25 +156,32 @@ function checkForLedgerGaps(connection: Connection,
}
}
return utils.hasCompleteLedgerRange(connection, minLedgerVersion,
maxLedgerVersion).then(hasCompleteLedgerRange => {
if (!hasCompleteLedgerRange) {
throw new errors.MissingLedgerHistoryError()
}
})
return utils
.hasCompleteLedgerRange(connection, minLedgerVersion, maxLedgerVersion)
.then(hasCompleteLedgerRange => {
if (!hasCompleteLedgerRange) {
throw new errors.MissingLedgerHistoryError()
}
})
}
function formatResponse(connection: Connection, options: TransactionsOptions,
function formatResponse(
connection: Connection,
options: TransactionsOptions,
transactions: GetTransactionsResponse
) {
const compare = options.earliestFirst ? utils.compareTransactions :
_.rearg(utils.compareTransactions, 1, 0)
const compare = options.earliestFirst
? utils.compareTransactions
: _.rearg(utils.compareTransactions, 1, 0)
const sortedTransactions = transactions.sort(compare)
return checkForLedgerGaps(connection, options, sortedTransactions).then(
() => sortedTransactions)
() => sortedTransactions
)
}
function getTransactionsInternal(connection: Connection, address: string,
function getTransactionsInternal(
connection: Connection,
address: string,
options: TransactionsOptions
): Promise<GetTransactionsResponse> {
const getter = _.partial(getAccountTx, connection, address, options)
@@ -162,7 +189,10 @@ function getTransactionsInternal(connection: Connection, address: string,
return utils.getRecursive(getter, options.limit).then(format)
}
function getTransactions(this: RippleAPI, address: string, options: TransactionsOptions = {}
function getTransactions(
this: RippleAPI,
address: string,
options: TransactionsOptions = {}
): Promise<GetTransactionsResponse> {
validate.getTransactions({address, options})
@@ -176,8 +206,9 @@ function getTransactions(this: RippleAPI, address: string, options: Transactions
if (options.start) {
return getTransaction.call(this, options.start).then(tx => {
const ledgerVersion = tx.outcome.ledgerVersion
const bound = options.earliestFirst ?
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion}
const bound = options.earliestFirst
? {minLedgerVersion: ledgerVersion}
: {maxLedgerVersion: ledgerVersion}
const startOptions = _.assign({}, defaults, options, {startTx: tx}, bound)
return getTransactionsInternal(this.connection, address, startOptions)
})

View File

@@ -5,9 +5,9 @@ import {RippleAPI} from '..'
import {FormattedTrustline} from '../common/types/objects/trustlines'
export type GetTrustlinesOptions = {
counterparty?: string,
currency?: string,
limit?: number,
counterparty?: string
currency?: string
limit?: number
ledgerVersion?: number
}
@@ -16,7 +16,9 @@ function currencyFilter(currency: string, trustline: FormattedTrustline) {
}
async function getTrustlines(
this: RippleAPI, address: string, options: GetTrustlinesOptions = {}
this: RippleAPI,
address: string,
options: GetTrustlinesOptions = {}
): Promise<FormattedTrustline[]> {
// 1. Validate
validate.getTrustlines({address, options})

View File

@@ -4,10 +4,10 @@ import * as common from '../common'
import {Connection} from '../common'
import {FormattedTransactionType} from '../transaction/types'
import {Issue} from '../common/types/objects'
import {RippleAPI} from '..'
import {RippleAPI} from '..'
export type RecursiveData = {
marker: string,
marker: string
results: Array<any>
}
@@ -18,7 +18,9 @@ function clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max)
}
function getXRPBalance(connection: Connection, address: string,
function getXRPBalance(
connection: Connection,
address: string,
ledgerVersion?: number
): Promise<string> {
const request = {
@@ -26,13 +28,16 @@ function getXRPBalance(connection: Connection, address: string,
account: address,
ledger_index: ledgerVersion
}
return connection.request(request).then(data =>
common.dropsToXrp(data.account_data.Balance))
return connection
.request(request)
.then(data => common.dropsToXrp(data.account_data.Balance))
}
// If the marker is omitted from a response, you have reached the end
function getRecursiveRecur(
getter: Getter, marker: string | undefined, limit: number
getter: Getter,
marker: string | undefined,
limit: number
): Promise<Array<any>> {
return getter(marker, limit).then(data => {
const remaining = limit - data.results.length
@@ -50,17 +55,20 @@ function getRecursive(getter: Getter, limit?: number): Promise<Array<any>> {
}
function renameCounterpartyToIssuer<T>(
obj: T & {counterparty?: string, issuer?: string}
): (T & {issuer?: string}) {
const issuer = (obj.counterparty !== undefined) ?
obj.counterparty :
((obj.issuer !== undefined) ? obj.issuer : undefined)
obj: T & {counterparty?: string; issuer?: string}
): T & {issuer?: string} {
const issuer =
obj.counterparty !== undefined
? obj.counterparty
: obj.issuer !== undefined
? obj.issuer
: undefined
const withIssuer = Object.assign({}, obj, {issuer})
delete withIssuer.counterparty
return withIssuer
}
export type RequestBookOffersArgs = {taker_gets: Issue, taker_pays: Issue}
export type RequestBookOffersArgs = {taker_gets: Issue; taker_pays: Issue}
function renameCounterpartyToIssuerInOrder(order: RequestBookOffersArgs) {
const taker_gets = renameCounterpartyToIssuer(order.taker_gets)
@@ -70,7 +78,7 @@ function renameCounterpartyToIssuerInOrder(order: RequestBookOffersArgs) {
}
function signum(num) {
return (num === 0) ? 0 : (num > 0 ? 1 : -1)
return num === 0 ? 0 : num > 0 ? 1 : -1
}
/**
@@ -80,7 +88,8 @@ function signum(num) {
* See: https://developers.ripple.com/transaction-metadata.html
*/
function compareTransactions(
first: FormattedTransactionType, second: FormattedTransactionType
first: FormattedTransactionType,
second: FormattedTransactionType
): number {
if (!first.outcome || !second.outcome) {
return 0
@@ -91,30 +100,38 @@ function compareTransactions(
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1
}
function hasCompleteLedgerRange(connection: Connection,
minLedgerVersion?: number, maxLedgerVersion?: number
function hasCompleteLedgerRange(
connection: Connection,
minLedgerVersion?: number,
maxLedgerVersion?: number
): Promise<boolean> {
const firstLedgerVersion = 32570 // earlier versions have been lost
return connection.hasLedgerVersions(
minLedgerVersion || firstLedgerVersion, maxLedgerVersion)
minLedgerVersion || firstLedgerVersion,
maxLedgerVersion
)
}
function isPendingLedgerVersion(connection: Connection,
function isPendingLedgerVersion(
connection: Connection,
maxLedgerVersion?: number
): Promise<boolean> {
return connection.getLedgerVersion().then(ledgerVersion =>
ledgerVersion < (maxLedgerVersion || 0))
return connection
.getLedgerVersion()
.then(ledgerVersion => ledgerVersion < (maxLedgerVersion || 0))
}
function ensureLedgerVersion(this: RippleAPI, options: any
): Promise<object> {
if (Boolean(options) && options.ledgerVersion !== undefined &&
function ensureLedgerVersion(this: RippleAPI, options: any): Promise<object> {
if (
Boolean(options) &&
options.ledgerVersion !== undefined &&
options.ledgerVersion !== null
) {
return Promise.resolve(options)
}
return this.getLedgerVersion().then(ledgerVersion =>
_.assign({}, options, {ledgerVersion}))
_.assign({}, options, {ledgerVersion})
)
}
export {

View File

@@ -1,13 +1,13 @@
import {deriveKeypair, deriveAddress} from 'ripple-keypairs'
import {classicAddressToXAddress} from 'ripple-address-codec'
function deriveXAddress(options: {publicKey: string, tag: number | false, test: boolean}): string {
function deriveXAddress(options: {
publicKey: string
tag: number | false
test: boolean
}): string {
const classicAddress = deriveAddress(options.publicKey)
return classicAddressToXAddress(classicAddress, options.tag, options.test)
}
export {
deriveKeypair,
deriveAddress,
deriveXAddress
}
export {deriveKeypair, deriveAddress, deriveXAddress}

View File

@@ -3,23 +3,23 @@ import keypairs from 'ripple-keypairs'
import {errors, validate} from '../common'
export type GeneratedAddress = {
xAddress: string,
classicAddress?: string,
address?: string, // @deprecated Use `classicAddress` instead.
xAddress: string
classicAddress?: string
address?: string // @deprecated Use `classicAddress` instead.
secret: string
}
export interface GenerateAddressOptions {
// The entropy to use to generate the seed.
entropy?: Uint8Array | number[],
entropy?: Uint8Array | number[]
// The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`.
algorithm?: 'ecdsa-secp256k1' | 'ed25519',
algorithm?: 'ecdsa-secp256k1' | 'ed25519'
// Specifies whether the address is intended for use on a test network such as Testnet or Devnet.
// If `true`, the address should only be used for testing, and will start with `T`.
// If `false` (default), the address should only be used on mainnet, and will start with `X`.
test?: boolean,
test?: boolean
// If `true`, return the classic address, in addition to the X-address.
includeClassicAddress?: boolean
@@ -32,7 +32,11 @@ function generateAddressAPI(options: GenerateAddressOptions): GeneratedAddress {
const keypair = keypairs.deriveKeypair(secret)
const classicAddress = keypairs.deriveAddress(keypair.publicKey)
const returnValue: any = {
xAddress: classicAddressToXAddress(classicAddress, false, options && options.test),
xAddress: classicAddressToXAddress(
classicAddress,
false,
options && options.test
),
secret
}
if (options.includeClassicAddress) {
@@ -45,6 +49,4 @@ function generateAddressAPI(options: GenerateAddressOptions): GeneratedAddress {
}
}
export {
generateAddressAPI
}
export {generateAddressAPI}

View File

@@ -1,5 +1,9 @@
import * as _ from 'lodash'
import {computeLedgerHash, computeTransactionTreeHash, computeStateTreeHash} from '../common/hashes'
import {
computeLedgerHash,
computeTransactionTreeHash,
computeStateTreeHash
} from '../common/hashes'
import * as common from '../common'
function convertLedgerHeader(header): any {
@@ -25,63 +29,79 @@ function hashLedgerHeader(ledgerHeader) {
return computeLedgerHash(header)
}
function computeTransactionHash(ledger,
options: ComputeLedgerHeaderHashOptions) {
function computeTransactionHash(
ledger,
options: ComputeLedgerHeaderHashOptions
) {
let transactions: any[]
if (ledger.rawTransactions) {
transactions = JSON.parse(ledger.rawTransactions)
} else if (ledger.transactions) {
try {
transactions = ledger.transactions.map(tx =>
JSON.parse(tx.rawTransaction))
JSON.parse(tx.rawTransaction)
)
} catch (e) {
if (e.toString() === 'SyntaxError: Unexpected' +
' token u in JSON at position 0') {
if (
e.toString() ===
'SyntaxError: Unexpected' + ' token u in JSON at position 0'
) {
// one or more of the `tx.rawTransaction`s is undefined
throw new common.errors.ValidationError('ledger'
+ ' is missing raw transactions')
throw new common.errors.ValidationError(
'ledger' + ' is missing raw transactions'
)
}
}
} else {
if (options.computeTreeHashes) {
throw new common.errors.ValidationError('transactions'
+ ' property is missing from the ledger')
throw new common.errors.ValidationError(
'transactions' + ' property is missing from the ledger'
)
}
return ledger.transactionHash
}
const txs = _.map(transactions, tx => {
const mergeTx = _.assign({}, _.omit(tx, 'tx'), tx.tx || {})
// rename `meta` back to `metaData`
const renameMeta = _.assign({}, _.omit(mergeTx, 'meta'),
tx.meta ? {metaData: tx.meta} : {})
const renameMeta = _.assign(
{},
_.omit(mergeTx, 'meta'),
tx.meta ? {metaData: tx.meta} : {}
)
return renameMeta
})
const transactionHash = computeTransactionTreeHash(txs)
if (ledger.transactionHash !== undefined
&& ledger.transactionHash !== transactionHash) {
throw new common.errors.ValidationError('transactionHash in header'
+ ' does not match computed hash of transactions', {
if (
ledger.transactionHash !== undefined &&
ledger.transactionHash !== transactionHash
) {
throw new common.errors.ValidationError(
'transactionHash in header' +
' does not match computed hash of transactions',
{
transactionHashInHeader: ledger.transactionHash,
computedHashOfTransactions: transactionHash
})
}
)
}
return transactionHash
}
function computeStateHash(ledger,
options: ComputeLedgerHeaderHashOptions) {
function computeStateHash(ledger, options: ComputeLedgerHeaderHashOptions) {
if (ledger.rawState === undefined) {
if (options.computeTreeHashes) {
throw new common.errors.ValidationError('rawState'
+ ' property is missing from the ledger')
throw new common.errors.ValidationError(
'rawState' + ' property is missing from the ledger'
)
}
return ledger.stateHash
}
const state = JSON.parse(ledger.rawState)
const stateHash = computeStateTreeHash(state)
if (ledger.stateHash !== undefined && ledger.stateHash !== stateHash) {
throw new common.errors.ValidationError('stateHash in header'
+ ' does not match computed hash of state')
throw new common.errors.ValidationError(
'stateHash in header' + ' does not match computed hash of state'
)
}
return stateHash
}
@@ -90,8 +110,10 @@ export type ComputeLedgerHeaderHashOptions = {
computeTreeHashes?: boolean
}
function computeLedgerHeaderHash(ledger: any,
options: ComputeLedgerHeaderHashOptions = {}): string {
function computeLedgerHeaderHash(
ledger: any,
options: ComputeLedgerHeaderHashOptions = {}
): string {
const subhashes = {
transactionHash: computeTransactionHash(ledger, options),
stateHash: computeStateHash(ledger, options)

View File

@@ -3,7 +3,9 @@ import keypairs from 'ripple-keypairs'
import binary from 'ripple-binary-codec'
const {validate, xrpToDrops} = common
function signPaymentChannelClaim(channel: string, amount: string,
function signPaymentChannelClaim(
channel: string,
amount: string,
privateKey: string
): string {
validate.signPaymentChannelClaim({channel, amount, privateKey})

View File

@@ -2,8 +2,11 @@ import keypairs from 'ripple-keypairs'
import binary from 'ripple-binary-codec'
import {validate, xrpToDrops} from '../common'
function verifyPaymentChannelClaim(channel: string, amount: string,
signature: string, publicKey: string
function verifyPaymentChannelClaim(
channel: string,
amount: string,
signature: string,
publicKey: string
): string {
validate.verifyPaymentChannelClaim({channel, amount, signature, publicKey})

View File

@@ -30,10 +30,4 @@ function formatLedgerClose(ledgerClose: any): object {
}
}
export {
connect,
disconnect,
isConnected,
getLedgerVersion,
formatLedgerClose
}
export {connect, disconnect, isConnected, getLedgerVersion, formatLedgerClose}

View File

@@ -7,7 +7,8 @@ export type CheckCancelParameters = {
checkID: string
}
function createCheckCancelTransaction(account: string,
function createCheckCancelTransaction(
account: string,
cancel: CheckCancelParameters
): TransactionJSON {
const txJSON = {
@@ -19,15 +20,15 @@ function createCheckCancelTransaction(account: string,
return txJSON
}
function prepareCheckCancel(this: RippleAPI, address: string,
function prepareCheckCancel(
this: RippleAPI,
address: string,
checkCancel: CheckCancelParameters,
instructions: Instructions = {}
): Promise<Prepare> {
try {
validate.prepareCheckCancel(
{address, checkCancel, instructions})
const txJSON = createCheckCancelTransaction(
address, checkCancel)
validate.prepareCheckCancel({address, checkCancel, instructions})
const txJSON = createCheckCancelTransaction(address, checkCancel)
return prepareTransaction(txJSON, this, instructions)
} catch (e) {
return Promise.reject(e)

View File

@@ -7,17 +7,20 @@ import {Amount} from '../common/types/objects'
import {RippleAPI} from '..'
export type CheckCashParameters = {
checkID: string,
amount?: Amount,
checkID: string
amount?: Amount
deliverMin?: Amount
}
function createCheckCashTransaction(account: string,
function createCheckCashTransaction(
account: string,
checkCash: CheckCashParameters
): TransactionJSON {
if (checkCash.amount && checkCash.deliverMin) {
throw new ValidationError('"amount" and "deliverMin" properties on '
+ 'CheckCash are mutually exclusive')
throw new ValidationError(
'"amount" and "deliverMin" properties on ' +
'CheckCash are mutually exclusive'
)
}
const txJSON: any = {
@@ -37,15 +40,15 @@ function createCheckCashTransaction(account: string,
return txJSON
}
function prepareCheckCash(this: RippleAPI, address: string,
function prepareCheckCash(
this: RippleAPI,
address: string,
checkCash: CheckCashParameters,
instructions: Instructions = {}
): Promise<Prepare> {
try {
validate.prepareCheckCash(
{address, checkCash, instructions})
const txJSON = createCheckCashTransaction(
address, checkCash)
validate.prepareCheckCash({address, checkCash, instructions})
const txJSON = createCheckCashTransaction(address, checkCash)
return utils.prepareTransaction(txJSON, this, instructions)
} catch (e) {
return Promise.reject(e)

View File

@@ -6,14 +6,15 @@ import {Amount} from '../common/types/objects'
import {RippleAPI} from '..'
export type CheckCreateParameters = {
destination: string,
sendMax: Amount,
destinationTag?: number,
expiration?: string,
destination: string
sendMax: Amount
destinationTag?: number
expiration?: string
invoiceID?: string
}
function createCheckCreateTransaction(account: string,
function createCheckCreateTransaction(
account: string,
check: CheckCreateParameters
): TransactionJSON {
const txJSON: any = {
@@ -38,15 +39,15 @@ function createCheckCreateTransaction(account: string,
return txJSON
}
function prepareCheckCreate(this: RippleAPI, address: string,
function prepareCheckCreate(
this: RippleAPI,
address: string,
checkCreate: CheckCreateParameters,
instructions: Instructions = {}
): Promise<Prepare> {
try {
validate.prepareCheckCreate(
{address, checkCreate, instructions})
const txJSON = createCheckCreateTransaction(
address, checkCreate)
validate.prepareCheckCreate({address, checkCreate, instructions})
const txJSON = createCheckCreateTransaction(address, checkCreate)
return utils.prepareTransaction(txJSON, this, instructions)
} catch (e) {
return Promise.reject(e)

View File

@@ -7,13 +7,14 @@ import {validate} from '../common'
import {computeBinaryTransactionHash} from '../common/hashes'
function addressToBigNumber(address) {
const hex = (Buffer.from(decodeAccountID(address))).toString('hex')
const hex = Buffer.from(decodeAccountID(address)).toString('hex')
return new BigNumber(hex, 16)
}
function compareSigners(a, b) {
return addressToBigNumber(a.Signer.Account)
.comparedTo(addressToBigNumber(b.Signer.Account))
return addressToBigNumber(a.Signer.Account).comparedTo(
addressToBigNumber(b.Signer.Account)
)
}
function combine(signedTransactions: Array<string>): object {
@@ -25,10 +26,14 @@ function combine(signedTransactions: Array<string>): object {
const tx = _.omit(txs[0], 'Signers')
if (!_.every(txs, _tx => _.isEqual(tx, _.omit(_tx, 'Signers')))) {
throw new utils.common.errors.ValidationError(
'txJSON is not the same for all signedTransactions')
'txJSON is not the same for all signedTransactions'
)
}
const unsortedSigners = _.reduce(txs, (accumulator, _tx) =>
accumulator.concat(_tx.Signers || []), [])
const unsortedSigners = _.reduce(
txs,
(accumulator, _tx) => accumulator.concat(_tx.Signers || []),
[]
)
const signers = unsortedSigners.sort(compareSigners)
const signedTx = _.assign({}, tx, {Signers: signers})
const signedTransaction = binary.encode(signedTx)

View File

@@ -5,15 +5,16 @@ import {Memo} from '../common/types/objects'
import {RippleAPI} from '..'
export type EscrowCancellation = {
owner: string,
escrowSequence: number,
owner: string
escrowSequence: number
// TODO: This ripple-lib memo format should be deprecated in favor of rippled's format.
// If necessary, expose a public method for converting between the two formats.
memos?: Array<Memo>
}
function createEscrowCancellationTransaction(account: string,
function createEscrowCancellationTransaction(
account: string,
payment: EscrowCancellation
): TransactionJSON {
const txJSON: any = {
@@ -28,14 +29,21 @@ function createEscrowCancellationTransaction(account: string,
return txJSON
}
function prepareEscrowCancellation(this: RippleAPI, address: string,
function prepareEscrowCancellation(
this: RippleAPI,
address: string,
escrowCancellation: EscrowCancellation,
instructions: Instructions = {}
): Promise<Prepare> {
validate.prepareEscrowCancellation(
{address, escrowCancellation, instructions})
validate.prepareEscrowCancellation({
address,
escrowCancellation,
instructions
})
const txJSON = createEscrowCancellationTransaction(
address, escrowCancellation)
address,
escrowCancellation
)
return utils.prepareTransaction(txJSON, this, instructions)
}

View File

@@ -6,17 +6,18 @@ import {Memo} from '../common/types/objects'
import {RippleAPI} from '..'
export type EscrowCreation = {
amount: string,
destination: string,
memos?: Array<Memo>,
condition?: string,
allowCancelAfter?: string,
allowExecuteAfter?: string,
sourceTag?: number,
amount: string
destination: string
memos?: Array<Memo>
condition?: string
allowCancelAfter?: string
allowExecuteAfter?: string
sourceTag?: number
destinationTag?: number
}
function createEscrowCreationTransaction(account: string,
function createEscrowCreationTransaction(
account: string,
payment: EscrowCreation
): TransactionJSON {
const txJSON: any = {
@@ -44,23 +45,28 @@ function createEscrowCreationTransaction(account: string,
if (payment.memos !== undefined) {
txJSON.Memos = payment.memos.map(utils.convertMemo)
}
if (Boolean(payment.allowCancelAfter) && Boolean(payment.allowExecuteAfter) &&
txJSON.CancelAfter <= txJSON.FinishAfter) {
throw new ValidationError('prepareEscrowCreation: ' +
'"allowCancelAfter" must be after "allowExecuteAfter"')
if (
Boolean(payment.allowCancelAfter) &&
Boolean(payment.allowExecuteAfter) &&
txJSON.CancelAfter <= txJSON.FinishAfter
) {
throw new ValidationError(
'prepareEscrowCreation: ' +
'"allowCancelAfter" must be after "allowExecuteAfter"'
)
}
return txJSON
}
function prepareEscrowCreation(this: RippleAPI, address: string,
function prepareEscrowCreation(
this: RippleAPI,
address: string,
escrowCreation: EscrowCreation,
instructions: Instructions = {}
): Promise<Prepare> {
try {
validate.prepareEscrowCreation(
{address, escrowCreation, instructions})
const txJSON = createEscrowCreationTransaction(
address, escrowCreation)
validate.prepareEscrowCreation({address, escrowCreation, instructions})
const txJSON = createEscrowCreationTransaction(address, escrowCreation)
return utils.prepareTransaction(txJSON, this, instructions)
} catch (e) {
return Promise.reject(e)

View File

@@ -6,14 +6,15 @@ import {Memo} from '../common/types/objects'
import {RippleAPI} from '..'
export type EscrowExecution = {
owner: string,
escrowSequence: number,
memos?: Array<Memo>,
condition?: string,
owner: string
escrowSequence: number
memos?: Array<Memo>
condition?: string
fulfillment?: string
}
function createEscrowExecutionTransaction(account: string,
function createEscrowExecutionTransaction(
account: string,
payment: EscrowExecution
): TransactionJSON {
const txJSON: any = {
@@ -24,8 +25,10 @@ function createEscrowExecutionTransaction(account: string,
}
if (Boolean(payment.condition) !== Boolean(payment.fulfillment)) {
throw new ValidationError('"condition" and "fulfillment" fields on'
+ ' EscrowFinish must only be specified together.')
throw new ValidationError(
'"condition" and "fulfillment" fields on' +
' EscrowFinish must only be specified together.'
)
}
if (payment.condition !== undefined) {
@@ -40,15 +43,15 @@ function createEscrowExecutionTransaction(account: string,
return txJSON
}
function prepareEscrowExecution(this: RippleAPI, address: string,
function prepareEscrowExecution(
this: RippleAPI,
address: string,
escrowExecution: EscrowExecution,
instructions: Instructions = {}
): Promise<Prepare> {
try {
validate.prepareEscrowExecution(
{address, escrowExecution, instructions})
const txJSON = createEscrowExecutionTransaction(
address, escrowExecution)
validate.prepareEscrowExecution({address, escrowExecution, instructions})
const txJSON = createEscrowExecutionTransaction(address, escrowExecution)
return utils.prepareTransaction(txJSON, this, instructions)
} catch (e) {
return Promise.reject(e)

Some files were not shown because too many files have changed in this diff Show More