mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-20 12:15:51 +00:00
build: Initial linting setup (#1560)
* sets up linting config and runs `yarn lint --fix` once, so that all changes will show up correctly in future PRs. * Note that there are still a lot of linter errors.
This commit is contained in:
committed by
Mayukha Vadari
parent
cfa014c44b
commit
6742e2048a
2
.eslintignore
Normal file
2
.eslintignore
Normal file
@@ -0,0 +1,2 @@
|
||||
dist
|
||||
node_modules
|
||||
99
.eslintrc.js
Normal file
99
.eslintrc.js
Normal file
@@ -0,0 +1,99 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
|
||||
// Make ESLint compatible with TypeScript
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
// Enable linting rules with type information from our tsconfig
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.eslint.json'],
|
||||
|
||||
// Allow the use of imports / ES modules
|
||||
sourceType: 'module',
|
||||
|
||||
ecmaFeatures: {
|
||||
// Enable global strict mode
|
||||
impliedStrict: true
|
||||
}
|
||||
},
|
||||
|
||||
// Specify global variables that are predefined
|
||||
env: {
|
||||
node: true, // Enable node global variables & Node.js scoping
|
||||
es2020: true // Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
|
||||
},
|
||||
|
||||
plugins: [],
|
||||
extends: ['@xrplf/eslint-config/base', 'plugin:mocha/recommended'],
|
||||
rules: {
|
||||
// Certain rippled APIs require snake_case naming
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
{
|
||||
selector: 'interface',
|
||||
format: ['PascalCase']
|
||||
},
|
||||
{
|
||||
selector: 'interface',
|
||||
format: ['snake_case']
|
||||
}
|
||||
],
|
||||
|
||||
// Ignore type imports when counting dependencies.
|
||||
'import/max-dependencies': [
|
||||
'error',
|
||||
{
|
||||
max: 5,
|
||||
ignoreTypeImports: true
|
||||
}
|
||||
],
|
||||
// Removes comments and blank lines from the max-line rules
|
||||
'max-lines-per-function': [
|
||||
'warn',
|
||||
{
|
||||
max: 50,
|
||||
skipBlankLines: true,
|
||||
skipComments: true
|
||||
}
|
||||
],
|
||||
'max-lines': [
|
||||
'warn',
|
||||
{
|
||||
max: 250,
|
||||
skipBlankLines: true,
|
||||
skipComments: true
|
||||
}
|
||||
]
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['test/**/*.ts'],
|
||||
rules: {
|
||||
// Removed the max for test files and test helper files, since tests usually need to import more things
|
||||
'import/max-dependencies': 'off',
|
||||
|
||||
// describe blocks count as a function in Mocha tests, and can be insanely long
|
||||
'max-lines-per-function': 'off',
|
||||
|
||||
// Tests can be very long turns off max-line count
|
||||
'max-lines': 'off',
|
||||
|
||||
// We have lots of statements in tests
|
||||
'max-statements': 'off',
|
||||
|
||||
// We have lots of magic numbers in tests
|
||||
'no-magic-number': 'off'
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['.eslintrc.js', 'jest.config.js'],
|
||||
rules: {
|
||||
// Removed no-commonjs requirement as eslint must be in common js format
|
||||
'import/no-commonjs': 'off',
|
||||
|
||||
// Removed this as eslint prevents us from doing this differently
|
||||
'import/unambiguous': 'off'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"globals": {
|
||||
"NodeJS": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2018,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"no-useless-constructor": 0,
|
||||
"no-unused-vars": 0,
|
||||
"no-prototype-builtins": 0,
|
||||
"require-atomic-updates": 0,
|
||||
"no-dupe-class-members": 0
|
||||
}
|
||||
}
|
||||
29
.github/workflows/nodejs.yml
vendored
29
.github/workflows/nodejs.yml
vendored
@@ -10,10 +10,27 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
unit:
|
||||
# build-and-lint:
|
||||
# runs-on: ubuntu-latest
|
||||
|
||||
# strategy:
|
||||
# matrix:
|
||||
# node-version: [14.x]
|
||||
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: Use Node.js ${{ matrix.node-version }}
|
||||
# uses: actions/setup-node@v1
|
||||
# with:
|
||||
# node-version: ${{ matrix.node-version }}
|
||||
# - run: yarn install
|
||||
# - run: yarn lint
|
||||
# - run: yarn build
|
||||
|
||||
unit:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [12.x, 14.x, 16.x]
|
||||
@@ -24,10 +41,8 @@ jobs:
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: yarn install
|
||||
- run: yarn install --ignore-engines
|
||||
- run: yarn test
|
||||
- run: yarn lint
|
||||
- run: yarn build
|
||||
|
||||
integration:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -42,7 +57,7 @@ jobs:
|
||||
ports:
|
||||
- 6006:6006
|
||||
options:
|
||||
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
|
||||
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -50,7 +65,7 @@ jobs:
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: yarn install
|
||||
- run: yarn install --ignore-engines
|
||||
- run: yarn test:integration
|
||||
env:
|
||||
HOST: localhost
|
||||
@@ -69,7 +84,7 @@ jobs:
|
||||
ports:
|
||||
- 6006:6006
|
||||
options:
|
||||
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
|
||||
--health-cmd="wget localhost:6006 || exit 1" --health-interval=5s --health-retries=10 --health-timeout=2s
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
10
.prettierrc
10
.prettierrc
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"parser": "typescript",
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"quoteProps": "consistent",
|
||||
"bracketSpacing": false
|
||||
}
|
||||
6
.vscode/extenstions.json
vendored
Normal file
6
.vscode/extenstions.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
35
.vscode/settings.json
vendored
Normal file
35
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true
|
||||
},
|
||||
"eslint.alwaysShowStatus": true,
|
||||
"eslint.lintTask.enable": true,
|
||||
"eslint.codeAction.showDocumentation": {
|
||||
"enable": true
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/.hg/store/**": true
|
||||
},
|
||||
}
|
||||
39
package.json
39
package.json
@@ -12,10 +12,6 @@
|
||||
"unpkg": "build/ripple-latest-min.js",
|
||||
"jsdelivr": "build/ripple-latest-min.js",
|
||||
"types": "dist/npm/index.d.ts",
|
||||
"browser": {
|
||||
"ws": "./dist/npm/client/wsWrapper.js",
|
||||
"https-proxy-agent": false
|
||||
},
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
@@ -41,8 +37,10 @@
|
||||
"@types/chai": "^4.2.21",
|
||||
"@types/mocha": "^9.0.0",
|
||||
"@types/node": "^16.4.3",
|
||||
"@typescript-eslint/eslint-plugin": "^2.3.3",
|
||||
"@typescript-eslint/parser": "^2.27.0",
|
||||
"@types/puppeteer": "5.4.4",
|
||||
"@typescript-eslint/eslint-plugin": "^3.7.0",
|
||||
"@typescript-eslint/parser": "^3.7.0",
|
||||
"@xrplf/eslint-config": "^1.1.0",
|
||||
"assert": "^2.0.0",
|
||||
"assert-diff": "^3.0.0",
|
||||
"buffer": "^6.0.2",
|
||||
@@ -50,21 +48,29 @@
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"doctoc": "^2.0.0",
|
||||
"ejs": "^3.0.1",
|
||||
"eslint": "^6.5.1",
|
||||
"eslint": "^7.5.0",
|
||||
"eslint-plugin-array-func": "^3.1.7",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.24.1",
|
||||
"eslint-plugin-jsdoc": "^29.0.0",
|
||||
"eslint-plugin-mocha": "^9.0.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-tsdoc": "^0.2.14",
|
||||
"eventemitter2": "^6.0.0",
|
||||
"https-browserify": "^1.0.0",
|
||||
"json-schema-to-markdown-table": "^0.4.0",
|
||||
"mocha": "^9",
|
||||
"nyc": "^15",
|
||||
"path-browserify": "1.0.1",
|
||||
"prettier": "^2.0.5",
|
||||
"prettier": "^2.3.2",
|
||||
"process": "^0.11.10",
|
||||
"puppeteer": "10.2.0",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"stream-http": "3.2.0",
|
||||
"ts-loader": "^9.2.5",
|
||||
"ts-node": "^10.1.0",
|
||||
"typescript": "^3.9.9",
|
||||
"typescript": "^3.9.10",
|
||||
"url": "^0.11.0",
|
||||
"webpack": "^5.6.0",
|
||||
"webpack-bundle-analyzer": "^4.1.0",
|
||||
@@ -72,21 +78,22 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build:schemas": "mkdir -p dist/npm/common && cp -r src/common/schemas dist/npm/common/",
|
||||
"build:lib": "tsc --build",
|
||||
"build:snippets": "tsc --build ./snippets/tsconfig.json",
|
||||
"build:lib": "tsc --build tsconfig.build.json",
|
||||
"build:web": "webpack",
|
||||
"build": "yarn build:schemas && yarn build:lib && yarn build:web",
|
||||
"build": "yarn build:schemas && yarn build:lib && yarn build:snippets && yarn build:web",
|
||||
"analyze": "yarn build:web --analyze",
|
||||
"watch": "yarn build:lib --watch",
|
||||
"clean": "rm -rf dist/npm",
|
||||
"clean": "rm -rf dist",
|
||||
"doctoc": "doctoc docs/index.md --title '# RippleAPI Reference' --github --maxlevel 2",
|
||||
"docgen": "node --harmony scripts/build_docs.js",
|
||||
"prepublish": "yarn clean && yarn build",
|
||||
"test": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha --config=test/.mocharc.json --exit",
|
||||
"test:integration": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha ./test/integration/*.ts",
|
||||
"test:browser": "TS_NODE_PROJECT=src/tsconfig.json nyc mocha ./test/browser/*.ts",
|
||||
"test": "nyc mocha --config=test/.mocharc.json --exit",
|
||||
"test:integration": "TS_NODE_PROJECT=tsconfig.build.json nyc mocha ./test/integration/*.ts",
|
||||
"test:browser": "TS_NODE_PROJECT=tsconfig.build.json nyc mocha ./test/browser/*.ts",
|
||||
"test:watch": "TS_NODE_PROJECT=src/tsconfig.json mocha --config=test/.mocharc.json --watch --reporter dot",
|
||||
"format": "prettier --write '{src,test}/**/*.ts'",
|
||||
"lint": "eslint 'src/**/*.ts' 'test/*.{ts,js}'",
|
||||
"lint": "eslint . --ext .ts --max-warnings 0",
|
||||
"perf": "./scripts/perf_test.sh",
|
||||
"compile:snippets": "tsc -p snippets/tsconfig.json",
|
||||
"start:snippet": "npm run compile:snippets && node ./snippets/dist/start.js",
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import * as codec from 'ripple-binary-codec'
|
||||
// import * as codec from 'ripple-binary-codec'
|
||||
|
||||
const original = codec.decode('12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402200A693FB5CA6B21250EBDFD8CFF526EE0DF7C9E4E31EB0660692E75E6A93BF5F802203CC39463DDA21386898CA31E18AD1A6828647D65741DD637BAD71BC83E29DB9481145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900')
|
||||
// const original = codec.decode(
|
||||
// '12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402200A693FB5CA6B21250EBDFD8CFF526EE0DF7C9E4E31EB0660692E75E6A93BF5F802203CC39463DDA21386898CA31E18AD1A6828647D65741DD637BAD71BC83E29DB9481145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900'
|
||||
// )
|
||||
|
||||
const test = codec.decode('12000022800200002400000017201B008694F261EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100D8B57E8E06EAE27B1343AF8CAD3F501E18260CCF8BCED08066074106F0F191A3022058FEA6CE9E7FA69D1244C3A70F18983CC2DAF0B10CBB86A6677CF2A5D2B8A68081145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900')
|
||||
// const test = codec.decode(
|
||||
// '12000022800200002400000017201B008694F261EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100D8B57E8E06EAE27B1343AF8CAD3F501E18260CCF8BCED08066074106F0F191A3022058FEA6CE9E7FA69D1244C3A70F18983CC2DAF0B10CBB86A6677CF2A5D2B8A68081145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900'
|
||||
// )
|
||||
|
||||
console.log('original:', JSON.stringify(original))
|
||||
// console.log('original:', JSON.stringify(original))
|
||||
|
||||
console.log('test:', JSON.stringify(test))
|
||||
// console.log('test:', JSON.stringify(test))
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
import {Client} from '../../dist/npm'
|
||||
import { TransactionMetadata } from '../../src/models/common/transaction'
|
||||
// import {Client} from '../../dist/npm'
|
||||
// import {TransactionMetadata} from '../../src/models/common/transaction'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
// const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
getTransaction()
|
||||
// getTransaction()
|
||||
|
||||
async function getTransaction() {
|
||||
await client.connect()
|
||||
const ledger = await client.request({command: 'ledger', transactions: true})
|
||||
console.log(ledger)
|
||||
const tx = await client.request({
|
||||
command: 'tx',
|
||||
transaction: ledger.result.ledger.transactions[0] as string
|
||||
})
|
||||
console.log(tx)
|
||||
console.log('deliveredAmount:', (tx.result.meta as TransactionMetadata).DeliveredAmount)
|
||||
process.exit(0)
|
||||
}
|
||||
// async function getTransaction() {
|
||||
// await client.connect()
|
||||
// const ledger = await client.request({command: 'ledger', transactions: true})
|
||||
// console.log(ledger)
|
||||
// const tx = await client.request({
|
||||
// command: 'tx',
|
||||
// transaction: ledger.result.ledger.transactions[0] as string
|
||||
// })
|
||||
// console.log(tx)
|
||||
// console.log(
|
||||
// 'deliveredAmount:',
|
||||
// (tx.result.meta as TransactionMetadata).DeliveredAmount
|
||||
// )
|
||||
// process.exit(0)
|
||||
// }
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import {Client} from '../../dist/npm'
|
||||
import { AccountFlags } from '../../dist/npm/common/constants'
|
||||
// import {Client} from '../../dist/npm'
|
||||
// import {AccountFlags} from '../../dist/npm/common/constants'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
// const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
parseAccountFlags()
|
||||
// parseAccountFlags()
|
||||
|
||||
async function parseAccountFlags() {
|
||||
await client.connect()
|
||||
const account_info = await client.request({command: 'account_info', account: 'rKsdkGhyZH6b2Zzd5hNnEqSv2wpznn4n6N'})
|
||||
const flags = account_info.result.account_data.Flags
|
||||
for (const flagName in AccountFlags) {
|
||||
if (flags & AccountFlags[flagName]) {
|
||||
console.log(`${flagName} enabled`)
|
||||
}
|
||||
}
|
||||
process.exit(0)
|
||||
}
|
||||
// async function parseAccountFlags() {
|
||||
// await client.connect()
|
||||
// const account_info = await client.request({
|
||||
// command: 'account_info',
|
||||
// account: 'rKsdkGhyZH6b2Zzd5hNnEqSv2wpznn4n6N'
|
||||
// })
|
||||
// const flags = account_info.result.account_data.Flags
|
||||
// for (const flagName in AccountFlags) {
|
||||
// if (flags & AccountFlags[flagName]) {
|
||||
// console.log(`${flagName} enabled`)
|
||||
// }
|
||||
// }
|
||||
// process.exit(0)
|
||||
// }
|
||||
|
||||
@@ -1,47 +1,53 @@
|
||||
import {Client} from '../../dist/npm'
|
||||
// import {Client} from '../../dist/npm'
|
||||
|
||||
const client = new Client(
|
||||
// 'wss://s.altnet.rippletest.net:51233'
|
||||
// 'ws://35.158.96.209:51233'
|
||||
'ws://34.210.87.206:51233'
|
||||
)
|
||||
// const client = new Client(
|
||||
// // 'wss://s.altnet.rippletest.net:51233'
|
||||
// // 'ws://35.158.96.209:51233'
|
||||
// 'ws://34.210.87.206:51233'
|
||||
// )
|
||||
|
||||
sign()
|
||||
// sign()
|
||||
|
||||
async function sign() {
|
||||
await client.connect()
|
||||
const pathfind: any = {
|
||||
source: {
|
||||
address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
amount: {
|
||||
currency: 'drops',
|
||||
value: '100'
|
||||
}
|
||||
},
|
||||
destination: {
|
||||
address: 'rKT4JX4cCof6LcDYRz8o3rGRu7qxzZ2Zwj',
|
||||
amount: {
|
||||
currency: 'USD',
|
||||
counterparty: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc'
|
||||
}
|
||||
}
|
||||
}
|
||||
// async function sign() {
|
||||
// await client.connect()
|
||||
// const pathfind: any = {
|
||||
// source: {
|
||||
// address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
// amount: {
|
||||
// currency: 'drops',
|
||||
// value: '100'
|
||||
// }
|
||||
// },
|
||||
// destination: {
|
||||
// address: 'rKT4JX4cCof6LcDYRz8o3rGRu7qxzZ2Zwj',
|
||||
// amount: {
|
||||
// currency: 'USD',
|
||||
// counterparty: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
await client.getPaths(pathfind).then(async (data) => {
|
||||
console.log('paths:', JSON.stringify(data))
|
||||
const fakeSecret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
||||
// await client
|
||||
// .getPaths(pathfind)
|
||||
// .then(async (data) => {
|
||||
// console.log('paths:', JSON.stringify(data))
|
||||
// const fakeSecret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
||||
|
||||
pathfind.paths = data[0].paths
|
||||
pathfind.destination = data[0].destination
|
||||
await client.preparePayment('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', pathfind).then(ret => {
|
||||
const signed = client.sign(ret.txJSON, fakeSecret)
|
||||
console.log('signed:', signed)
|
||||
}).catch(err => {
|
||||
console.log('ERR 1:', JSON.stringify(err))
|
||||
})
|
||||
}).catch(err => {
|
||||
console.log('ERR 2:', err)
|
||||
})
|
||||
// pathfind.paths = data[0].paths
|
||||
// pathfind.destination = data[0].destination
|
||||
// await client
|
||||
// .preparePayment('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', pathfind)
|
||||
// .then((ret) => {
|
||||
// const signed = client.sign(ret.txJSON, fakeSecret)
|
||||
// console.log('signed:', signed)
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.log('ERR 1:', JSON.stringify(err))
|
||||
// })
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.log('ERR 2:', err)
|
||||
// })
|
||||
|
||||
client.disconnect()
|
||||
}
|
||||
// client.disconnect()
|
||||
// }
|
||||
|
||||
@@ -1,201 +1,215 @@
|
||||
import {
|
||||
Client,
|
||||
AccountInfoResponse,
|
||||
LedgerClosedEvent
|
||||
} from '../../dist/npm'
|
||||
import https = require('https')
|
||||
// import https = require('https')
|
||||
|
||||
/**
|
||||
* When implementing Reliable Transaction Submission, there are many potential solutions, each with different trade-offs. The main decision points are:
|
||||
* 1) Transaction preparation:
|
||||
* - How do we decide which account sequence and LastLedgerSequence numbers to use?
|
||||
* (To prevent unintentional duplicate transactions, an {account, account_sequence} pair can be used as a transaction's idempotency key)
|
||||
* - How do we decide how much to pay for the transaction fee? (If our transactions have been failing due to low fee, we should consider increasing this value)
|
||||
* 2) Transaction status retrieval. Options include:
|
||||
* - Poll for transaction status:
|
||||
* - On a regular interval (e.g. every 3-5 seconds), or
|
||||
* - When a new validated ledger is detected
|
||||
* + (To accommodate an edge case in transaction retrieval, check the sending account's Sequence number to confirm that it has the expected value;
|
||||
* alternatively, wait until a few additional ledgers have been validated before deciding that a transaction has definitively not been included in a validated ledger)
|
||||
* - Listen for transaction status: scan all validated transactions to see if our transactions are among them
|
||||
* 3) What do we do when a transaction fails? It is possible to implement retry logic, but caution is advised. Note that there are a few ways for a transaction to fail:
|
||||
* A) `tec`: The transaction was included in a ledger but only claimed the transaction fee
|
||||
* B) `tesSUCCESS` but unexpected result: The transaction was successful but did not have the expected result. This generally does not occur for XRP-to-XRP payments
|
||||
* C) The transaction was not, and never will be, included in a validated ledger [3C]
|
||||
*
|
||||
* References:
|
||||
* - https://xrpl.org/reliable-transaction-submission.html
|
||||
* - https://xrpl.org/send-xrp.html
|
||||
* - https://xrpl.org/look-up-transaction-results.html
|
||||
* - https://xrpl.org/get-started-with-rippleapi-for-javascript.html
|
||||
* - https://xrpl.org/monitor-incoming-payments-with-websocket.html
|
||||
*
|
||||
* For the implementation in this example, we have made the following decisions:
|
||||
* 1) The script will choose the account sequence and LastLedgerSequence numbers automatically. We allow ripple-lib to choose the fee.
|
||||
* Payments are defined upfront, and idempotency is not needed. If the script is run a second time, duplicate payments will result.
|
||||
* 2) We will listen for notification that a new validated ledger has been found, and poll for transaction status at that time.
|
||||
* Futhermore, as a precaution, we will wait until the server is 3 ledgers past the transaction's LastLedgerSequence
|
||||
* (with the transaction nowhere to be seen) before deciding that it has definitively failed per [3C]
|
||||
* 3) Transactions will not be automatically retried. Transactions are limited to XRP-to-XRP payments and cannot "succeed" in an unexpected way.
|
||||
*/
|
||||
reliableTransactionSubmissionExample()
|
||||
// import {Client, AccountInfoResponse, LedgerClosedEvent} from '../../dist/npm'
|
||||
|
||||
async function reliableTransactionSubmissionExample() {
|
||||
/**
|
||||
* Array of payments to execute.
|
||||
*
|
||||
* For brevity, these are XRP-to-XRP payments, taking a source, destination, and an amount in drops.
|
||||
*
|
||||
* The script will attempt to make all of these payments as quickly as possible, and report the final status of each. Transactions that fail are NOT retried.
|
||||
*/
|
||||
const payments = []
|
||||
// /**
|
||||
// * When implementing Reliable Transaction Submission, there are many potential solutions, each with different trade-offs. The main decision points are:
|
||||
// * 1) Transaction preparation:
|
||||
// * - How do we decide which account sequence and LastLedgerSequence numbers to use?
|
||||
// * (To prevent unintentional duplicate transactions, an {account, account_sequence} pair can be used as a transaction's idempotency key)
|
||||
// * - How do we decide how much to pay for the transaction fee? (If our transactions have been failing due to low fee, we should consider increasing this value)
|
||||
// * 2) Transaction status retrieval. Options include:
|
||||
// * - Poll for transaction status:
|
||||
// * - On a regular interval (e.g. Every 3-5 seconds), or
|
||||
// * - When a new validated ledger is detected
|
||||
// * + (To accommodate an edge case in transaction retrieval, check the sending account's Sequence number to confirm that it has the expected value;
|
||||
// * alternatively, wait until a few additional ledgers have been validated before deciding that a transaction has definitively not been included in a validated ledger)
|
||||
// * - Listen for transaction status: scan all validated transactions to see if our transactions are among them
|
||||
// * 3) What do we do when a transaction fails? It is possible to implement retry logic, but caution is advised. Note that there are a few ways for a transaction to fail:
|
||||
// * A) `tec`: The transaction was included in a ledger but only claimed the transaction fee
|
||||
// * B) `tesSUCCESS` but unexpected result: The transaction was successful but did not have the expected result. This generally does not occur for XRP-to-XRP payments
|
||||
// * C) The transaction was not, and never will be, included in a validated ledger [3C].
|
||||
// *
|
||||
// * References:
|
||||
// * - https://xrpl.org/reliable-transaction-submission.html
|
||||
// * - https://xrpl.org/send-xrp.html
|
||||
// * - https://xrpl.org/look-up-transaction-results.html
|
||||
// * - https://xrpl.org/get-started-with-rippleapi-for-javascript.html
|
||||
// * - https://xrpl.org/monitor-incoming-payments-with-websocket.html.
|
||||
// *
|
||||
// * For the implementation in this example, we have made the following decisions:
|
||||
// * 1) The script will choose the account sequence and LastLedgerSequence numbers automatically. We allow ripple-lib to choose the fee.
|
||||
// * Payments are defined upfront, and idempotency is not needed. If the script is run a second time, duplicate payments will result.
|
||||
// * 2) We will listen for notification that a new validated ledger has been found, and poll for transaction status at that time.
|
||||
// * Futhermore, as a precaution, we will wait until the server is 3 ledgers past the transaction's LastLedgerSequence
|
||||
// * (with the transaction nowhere to be seen) before deciding that it has definitively failed per [3C]
|
||||
// * 3) Transactions will not be automatically retried. Transactions are limited to XRP-to-XRP payments and cannot "succeed" in an unexpected way.
|
||||
// */
|
||||
// reliableTransactionSubmissionExample()
|
||||
|
||||
const sourceAccount = (await generateTestnetAccount()).account
|
||||
console.log(`Generated new Testnet account: ${sourceAccount.classicAddress}/${sourceAccount.secret}`)
|
||||
// Send amounts from 1 drop to 10 drops
|
||||
for (let i = 1; i <= 10; i++) {
|
||||
payments.push({
|
||||
source: sourceAccount,
|
||||
destination: 'rhsoCozhUxwcyQgzFi1FVRoMVQgk7cZd4L', // Random Testnet destination
|
||||
amount_drops: i.toString(),
|
||||
})
|
||||
}
|
||||
const results = await performPayments(payments)
|
||||
console.log(JSON.stringify(results, null, 2))
|
||||
process.exit(0)
|
||||
}
|
||||
// async function reliableTransactionSubmissionExample() {
|
||||
// /**
|
||||
// * Array of payments to execute.
|
||||
// *
|
||||
// * For brevity, these are XRP-to-XRP payments, taking a source, destination, and an amount in drops.
|
||||
// *
|
||||
// * The script will attempt to make all of these payments as quickly as possible, and report the final status of each. Transactions that fail are NOT retried.
|
||||
// */
|
||||
// const payments = []
|
||||
|
||||
async function performPayments(payments) {
|
||||
const finalResults = []
|
||||
const txFinalizedPromises = []
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
await client.connect()
|
||||
// const sourceAccount = (await generateTestnetAccount()).account
|
||||
// console.log(
|
||||
// `Generated new Testnet account: ${sourceAccount.classicAddress}/${sourceAccount.secret}`
|
||||
// )
|
||||
// // Send amounts from 1 drop to 10 drops
|
||||
// for (let i = 1; i <= 10; i++) {
|
||||
// payments.push({
|
||||
// source: sourceAccount,
|
||||
// destination: 'rhsoCozhUxwcyQgzFi1FVRoMVQgk7cZd4L', // Random Testnet destination
|
||||
// amount_drops: i.toString()
|
||||
// })
|
||||
// }
|
||||
// const results = await performPayments(payments)
|
||||
// console.log(JSON.stringify(results, null, 2))
|
||||
// process.exit(0)
|
||||
// }
|
||||
|
||||
for (let i = 0; i < payments.length; i++) {
|
||||
const payment = payments[i]
|
||||
const account_info: AccountInfoResponse = await client.request({
|
||||
command: 'account_info',
|
||||
account: payment.source.classicAddress,
|
||||
ledger_index: 'current'})
|
||||
const sequence = account_info.result.account_data.Sequence
|
||||
const preparedPayment = await client.preparePayment(payment.source.classicAddress, {
|
||||
source: {
|
||||
address: payment.source.classicAddress,
|
||||
amount: {
|
||||
value: payment.amount_drops,
|
||||
currency: 'drops'
|
||||
}
|
||||
},
|
||||
destination: {
|
||||
address: payment.destination,
|
||||
minAmount: {
|
||||
value: payment.amount_drops,
|
||||
currency: 'drops'
|
||||
}
|
||||
}
|
||||
}, {
|
||||
sequence
|
||||
})
|
||||
const signed = client.sign(preparedPayment.txJSON, payment.source.secret)
|
||||
finalResults.push({
|
||||
id: signed.id
|
||||
})
|
||||
const response = await client.request({command: 'submit', tx_blob: signed.signedTransaction})
|
||||
// async function performPayments(payments) {
|
||||
// const finalResults = []
|
||||
// const txFinalizedPromises = []
|
||||
// const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
// await client.connect()
|
||||
|
||||
// Most of the time we'll get 'tesSUCCESS' or (after many submissions) 'terQUEUED'
|
||||
console.log(`tx ${i} - tentative: ${response.result.engine_result}`)
|
||||
// for (let i = 0; i < payments.length; i++) {
|
||||
// const payment = payments[i]
|
||||
// const account_info: AccountInfoResponse = await client.request({
|
||||
// command: 'account_info',
|
||||
// account: payment.source.classicAddress,
|
||||
// ledger_index: 'current'
|
||||
// })
|
||||
// const sequence = account_info.result.account_data.Sequence
|
||||
// const preparedPayment = await client.preparePayment(
|
||||
// payment.source.classicAddress,
|
||||
// {
|
||||
// source: {
|
||||
// address: payment.source.classicAddress,
|
||||
// amount: {
|
||||
// value: payment.amount_drops,
|
||||
// currency: 'drops'
|
||||
// }
|
||||
// },
|
||||
// destination: {
|
||||
// address: payment.destination,
|
||||
// minAmount: {
|
||||
// value: payment.amount_drops,
|
||||
// currency: 'drops'
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// sequence
|
||||
// }
|
||||
// )
|
||||
// const signed = client.sign(preparedPayment.txJSON, payment.source.secret)
|
||||
// finalResults.push({
|
||||
// id: signed.id
|
||||
// })
|
||||
// const response = await client.request({
|
||||
// command: 'submit',
|
||||
// tx_blob: signed.signedTransaction
|
||||
// })
|
||||
|
||||
const txFinalizedPromise = new Promise<void>((resolve) => {
|
||||
const ledgerClosedCallback = async (event: LedgerClosedEvent) => {
|
||||
let status
|
||||
try {
|
||||
status = await client.request({command: 'tx', transaction: signed.id})
|
||||
} catch (e) {
|
||||
// Typical error when the tx hasn't been validated yet:
|
||||
if (e.name !== 'MissingLedgerHistoryError') {
|
||||
console.log(e)
|
||||
}
|
||||
// // Most of the time we'll get 'tesSUCCESS' or (after many submissions) 'terQUEUED'
|
||||
// console.log(`tx ${i} - tentative: ${response.result.engine_result}`)
|
||||
|
||||
if (event.ledger_index > preparedPayment.instructions.maxLedgerVersion + 3) {
|
||||
// Assumptions:
|
||||
// - We are still connected to the same rippled server
|
||||
// - No ledger gaps occurred
|
||||
// - All ledgers between the time we submitted the tx and now have been checked for the tx
|
||||
status = {
|
||||
finalResult: 'Transaction was not, and never will be, included in a validated ledger'
|
||||
}
|
||||
} else {
|
||||
// Check again later:
|
||||
client.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
return
|
||||
}
|
||||
}
|
||||
// const txFinalizedPromise = new Promise<void>((resolve) => {
|
||||
// const ledgerClosedCallback = async (event: LedgerClosedEvent) => {
|
||||
// let status
|
||||
// try {
|
||||
// status = await client.request({command: 'tx', transaction: signed.id})
|
||||
// } catch (e) {
|
||||
// // Typical error when the tx hasn't been validated yet:
|
||||
// if (e.name !== 'MissingLedgerHistoryError') {
|
||||
// console.log(e)
|
||||
// }
|
||||
|
||||
for (let j = 0; j < finalResults.length; j++) {
|
||||
if (finalResults[j].id === signed.id) {
|
||||
finalResults[j].result = status.address ? {
|
||||
source: status.address,
|
||||
destination: status.specification.destination.address,
|
||||
deliveredAmount: status.outcome.deliveredAmount,
|
||||
result: status.outcome.result,
|
||||
timestamp: status.outcome.timestamp,
|
||||
ledgerVersion: status.outcome.ledgerVersion
|
||||
} : status
|
||||
process.stdout.write('.')
|
||||
return resolve()
|
||||
}
|
||||
}
|
||||
}
|
||||
client.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
})
|
||||
txFinalizedPromises.push(txFinalizedPromise)
|
||||
}
|
||||
await Promise.all(txFinalizedPromises)
|
||||
return finalResults
|
||||
}
|
||||
// if (
|
||||
// event.ledger_index >
|
||||
// preparedPayment.instructions.maxLedgerVersion + 3
|
||||
// ) {
|
||||
// // Assumptions:
|
||||
// // - We are still connected to the same rippled server
|
||||
// // - No ledger gaps occurred
|
||||
// // - All ledgers between the time we submitted the tx and now have been checked for the tx
|
||||
// status = {
|
||||
// finalResult:
|
||||
// 'Transaction was not, and never will be, included in a validated ledger'
|
||||
// }
|
||||
// } else {
|
||||
// // Check again later:
|
||||
// client.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Generate a new Testnet account by requesting one from the faucet
|
||||
*/
|
||||
async function generateTestnetAccount(): Promise<{
|
||||
account: {
|
||||
xAddress: string,
|
||||
classicAddress, string,
|
||||
secret: string
|
||||
},
|
||||
balance: number
|
||||
}> {
|
||||
const options = {
|
||||
hostname: 'faucet.altnet.rippletest.net',
|
||||
port: 443,
|
||||
path: '/accounts',
|
||||
method: 'POST'
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = https.request(options, response => {
|
||||
const chunks = []
|
||||
response.on('data', d => {
|
||||
chunks.push(d)
|
||||
})
|
||||
response.on('end', () => {
|
||||
const body = Buffer.concat(chunks).toString()
|
||||
// for (let j = 0; j < finalResults.length; j++) {
|
||||
// if (finalResults[j].id === signed.id) {
|
||||
// finalResults[j].result = status.address
|
||||
// ? {
|
||||
// source: status.address,
|
||||
// destination: status.specification.destination.address,
|
||||
// deliveredAmount: status.outcome.deliveredAmount,
|
||||
// result: status.outcome.result,
|
||||
// timestamp: status.outcome.timestamp,
|
||||
// ledgerVersion: status.outcome.ledgerVersion
|
||||
// }
|
||||
// : status
|
||||
// process.stdout.write('.')
|
||||
// return resolve()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// client.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
// })
|
||||
// txFinalizedPromises.push(txFinalizedPromise)
|
||||
// }
|
||||
// await Promise.all(txFinalizedPromises)
|
||||
// return finalResults
|
||||
// }
|
||||
|
||||
// "application/json; charset=utf-8"
|
||||
if (response.headers['content-type'].startsWith('application/json')) {
|
||||
resolve(JSON.parse(body))
|
||||
} else {
|
||||
reject({
|
||||
statusCode: response.statusCode,
|
||||
contentType: response.headers['content-type'],
|
||||
body
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
request.on('error', error => {
|
||||
console.error(error)
|
||||
reject(error)
|
||||
})
|
||||
request.end()
|
||||
})
|
||||
}
|
||||
// /**
|
||||
// * Generate a new Testnet account by requesting one from the faucet.
|
||||
// */
|
||||
// async function generateTestnetAccount(): Promise<{
|
||||
// account: {
|
||||
// xAddress: string
|
||||
// classicAddress
|
||||
// string
|
||||
// secret: string
|
||||
// }
|
||||
// balance: number
|
||||
// }> {
|
||||
// const options = {
|
||||
// hostname: 'faucet.altnet.rippletest.net',
|
||||
// port: 443,
|
||||
// path: '/accounts',
|
||||
// method: 'POST'
|
||||
// }
|
||||
// return new Promise((resolve, reject) => {
|
||||
// const request = https.request(options, (response) => {
|
||||
// const chunks = []
|
||||
// response.on('data', (d) => {
|
||||
// chunks.push(d)
|
||||
// })
|
||||
// response.on('end', () => {
|
||||
// const body = Buffer.concat(chunks).toString()
|
||||
|
||||
// // "application/json; charset=utf-8"
|
||||
// if (response.headers['content-type'].startsWith('application/json')) {
|
||||
// resolve(JSON.parse(body))
|
||||
// } else {
|
||||
// reject({
|
||||
// statusCode: response.statusCode,
|
||||
// contentType: response.headers['content-type'],
|
||||
// body
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
// request.on('error', (error) => {
|
||||
// console.error(error)
|
||||
// reject(error)
|
||||
// })
|
||||
// request.end()
|
||||
// })
|
||||
// }
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
{
|
||||
"extends": "../tsconfig-base",
|
||||
"extends": "../tsconfig.build.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"references": [
|
||||
{ "path": "../src" }
|
||||
],
|
||||
"include": [
|
||||
"./src/**/*.ts"
|
||||
]
|
||||
|
||||
120
src/Wallet.ts
120
src/Wallet.ts
@@ -1,12 +1,13 @@
|
||||
import {fromSeed} from 'bip32'
|
||||
import {mnemonicToSeedSync} from 'bip39'
|
||||
import {decode, encodeForSigning} from 'ripple-binary-codec'
|
||||
import {deriveKeypair, generateSeed, verify} from 'ripple-keypairs'
|
||||
import ECDSA from './common/ecdsa'
|
||||
import {SignedTransaction} from './common/types/objects'
|
||||
import {signOffline} from './transaction/sign'
|
||||
import {SignOptions} from './transaction/types'
|
||||
import {ValidationError} from './common/errors'
|
||||
import { fromSeed } from "bip32";
|
||||
import { mnemonicToSeedSync } from "bip39";
|
||||
import { decode, encodeForSigning } from "ripple-binary-codec";
|
||||
import { deriveKeypair, generateSeed, verify } from "ripple-keypairs";
|
||||
|
||||
import ECDSA from "./common/ecdsa";
|
||||
import { ValidationError } from "./common/errors";
|
||||
import { SignedTransaction } from "./common/types/objects";
|
||||
import { signOffline } from "./transaction/sign";
|
||||
import { SignOptions } from "./transaction/types";
|
||||
|
||||
/**
|
||||
* A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
|
||||
@@ -14,53 +15,61 @@ import {ValidationError} from './common/errors'
|
||||
* It provides functionality to sign/verify transactions offline.
|
||||
*/
|
||||
class Wallet {
|
||||
readonly publicKey: string
|
||||
readonly privateKey: string
|
||||
private static readonly defaultAlgorithm: ECDSA = ECDSA.ed25519
|
||||
private static readonly defaultDerivationPath: string = "m/44'/144'/0'/0/0"
|
||||
readonly publicKey: string;
|
||||
readonly privateKey: string;
|
||||
private static readonly defaultAlgorithm: ECDSA = ECDSA.ed25519;
|
||||
private static readonly defaultDerivationPath: string = "m/44'/144'/0'/0/0";
|
||||
|
||||
constructor(publicKey: string, privateKey: string) {
|
||||
this.publicKey = publicKey
|
||||
this.privateKey = privateKey
|
||||
this.publicKey = publicKey;
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a wallet from a seed.
|
||||
* @param {string} seed A string used to generate a keypair (publicKey/privateKey) to derive a wallet.
|
||||
* @param {ECDSA} algorithm The digital signature algorithm to generate an address for.
|
||||
* @returns {Wallet} A Wallet derived from a seed.
|
||||
*
|
||||
* @param seed - A string used to generate a keypair (publicKey/privateKey) to derive a wallet.
|
||||
* @param algorithm - The digital signature algorithm to generate an address for.
|
||||
* @returns A Wallet derived from a seed.
|
||||
*/
|
||||
static fromSeed(seed: string, algorithm: ECDSA = Wallet.defaultAlgorithm): Wallet {
|
||||
return Wallet.deriveWallet(seed, algorithm)
|
||||
static fromSeed(
|
||||
seed: string,
|
||||
algorithm: ECDSA = Wallet.defaultAlgorithm
|
||||
): Wallet {
|
||||
return Wallet.deriveWallet(seed, algorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a wallet from a mnemonic.
|
||||
* @param {string} mnemonic A string consisting of words (whitespace delimited) used to derive a wallet.
|
||||
* @param {string} derivationPath The path to derive a keypair (publicKey/privateKey) from a seed (that was converted from a mnemonic).
|
||||
* @returns {Wallet} A Wallet derived from a mnemonic.
|
||||
*
|
||||
* @param mnemonic - A string consisting of words (whitespace delimited) used to derive a wallet.
|
||||
* @param derivationPath - The path to derive a keypair (publicKey/privateKey) from a seed (that was converted from a mnemonic).
|
||||
* @returns A Wallet derived from a mnemonic.
|
||||
*/
|
||||
static fromMnemonic(
|
||||
mnemonic: string,
|
||||
derivationPath: string = Wallet.defaultDerivationPath
|
||||
): Wallet {
|
||||
const seed = mnemonicToSeedSync(mnemonic)
|
||||
const masterNode = fromSeed(seed)
|
||||
const node = masterNode.derivePath(derivationPath)
|
||||
const seed = mnemonicToSeedSync(mnemonic);
|
||||
const masterNode = fromSeed(seed);
|
||||
const node = masterNode.derivePath(derivationPath);
|
||||
if (node.privateKey === undefined) {
|
||||
throw new ValidationError('Unable to derive privateKey from mnemonic input')
|
||||
throw new ValidationError(
|
||||
"Unable to derive privateKey from mnemonic input"
|
||||
);
|
||||
}
|
||||
|
||||
const publicKey = Wallet.hexFromBuffer(node.publicKey)
|
||||
const privateKey = Wallet.hexFromBuffer(node.privateKey)
|
||||
return new Wallet(publicKey, `00${privateKey}`)
|
||||
const publicKey = Wallet.hexFromBuffer(node.publicKey);
|
||||
const privateKey = Wallet.hexFromBuffer(node.privateKey);
|
||||
return new Wallet(publicKey, `00${privateKey}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives a wallet from an entropy (array of random numbers).
|
||||
* @param {Uint8Array | number[]} entropy An array of random numbers to generate a seed used to derive a wallet.
|
||||
* @param {ECDSA} algorithm The digital signature algorithm to generate an address for.
|
||||
* @returns {Wallet} A Wallet derived from an entropy.
|
||||
*
|
||||
* @param entropy - An array of random numbers to generate a seed used to derive a wallet.
|
||||
* @param algorithm - The digital signature algorithm to generate an address for.
|
||||
* @returns A Wallet derived from an entropy.
|
||||
*/
|
||||
static fromEntropy(
|
||||
entropy: Uint8Array | number[],
|
||||
@@ -68,45 +77,50 @@ class Wallet {
|
||||
): Wallet {
|
||||
const options = {
|
||||
entropy: Uint8Array.from(entropy),
|
||||
algorithm
|
||||
}
|
||||
const seed = generateSeed(options)
|
||||
return Wallet.deriveWallet(seed, algorithm)
|
||||
algorithm,
|
||||
};
|
||||
const seed = generateSeed(options);
|
||||
return Wallet.deriveWallet(seed, algorithm);
|
||||
}
|
||||
|
||||
private static hexFromBuffer(buffer: Buffer): string {
|
||||
return buffer.toString('hex').toUpperCase()
|
||||
return buffer.toString("hex").toUpperCase();
|
||||
}
|
||||
|
||||
private static deriveWallet(seed: string, algorithm: ECDSA = Wallet.defaultAlgorithm): Wallet {
|
||||
const {publicKey, privateKey} = deriveKeypair(seed, {algorithm})
|
||||
return new Wallet(publicKey, privateKey)
|
||||
private static deriveWallet(
|
||||
seed: string,
|
||||
algorithm: ECDSA = Wallet.defaultAlgorithm
|
||||
): Wallet {
|
||||
const { publicKey, privateKey } = deriveKeypair(seed, { algorithm });
|
||||
return new Wallet(publicKey, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs a transaction offline.
|
||||
* @param {object} transaction A transaction to be signed offline.
|
||||
* @param {SignOptions} options Options to include for signing.
|
||||
* @returns {SignedTransaction} A signed transaction.
|
||||
*
|
||||
* @param transaction - A transaction to be signed offline.
|
||||
* @param options - Options to include for signing.
|
||||
* @returns A signed transaction.
|
||||
*/
|
||||
signTransaction(
|
||||
transaction: any, // TODO: transaction should be typed with Transaction type.
|
||||
options: SignOptions = {signAs: ''}
|
||||
options: SignOptions = { signAs: "" }
|
||||
): SignedTransaction {
|
||||
return signOffline(this, JSON.stringify(transaction), options)
|
||||
return signOffline(this, JSON.stringify(transaction), options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a signed transaction offline.
|
||||
* @param {string} signedTransaction A signed transaction (hex string of signTransaction result) to be verified offline.
|
||||
* @returns {boolean} Returns true if a signedTransaction is valid.
|
||||
*
|
||||
* @param signedTransaction - A signed transaction (hex string of signTransaction result) to be verified offline.
|
||||
* @returns Returns true if a signedTransaction is valid.
|
||||
*/
|
||||
verifyTransaction(signedTransaction: string): boolean {
|
||||
const tx = decode(signedTransaction)
|
||||
const messageHex: string = encodeForSigning(tx)
|
||||
const signature = tx.TxnSignature
|
||||
return verify(messageHex, signature, this.publicKey)
|
||||
const tx = decode(signedTransaction);
|
||||
const messageHex: string = encodeForSigning(tx);
|
||||
const signature = tx.TxnSignature;
|
||||
return verify(messageHex, signature, this.publicKey);
|
||||
}
|
||||
}
|
||||
|
||||
export default Wallet
|
||||
export default Wallet;
|
||||
|
||||
@@ -1,44 +1,42 @@
|
||||
/*
|
||||
* Original code based on "backo" - https://github.com/segmentio/backo
|
||||
* MIT License - Copyright 2014 Segment.io
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
// Original code based on "backo" - https://github.com/segmentio/backo
|
||||
// MIT License - Copyright 2014 Segment.io
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
/**
|
||||
* A Back off strategy that increases exponentially. Useful with repeated
|
||||
* setTimeout calls over a network (where the destination may be down).
|
||||
*/
|
||||
export class ExponentialBackoff {
|
||||
private readonly ms: number
|
||||
private readonly max: number
|
||||
private readonly factor: number = 2
|
||||
private readonly jitter: number = 0
|
||||
attempts: number = 0
|
||||
private readonly ms: number;
|
||||
private readonly max: number;
|
||||
private readonly factor: number = 2;
|
||||
private readonly jitter: number = 0;
|
||||
attempts = 0;
|
||||
|
||||
constructor(opts: {min?: number; max?: number} = {}) {
|
||||
this.ms = opts.min || 100
|
||||
this.max = opts.max || 10000
|
||||
constructor(opts: { min?: number; max?: number } = {}) {
|
||||
this.ms = opts.min || 100;
|
||||
this.max = opts.max || 10000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the backoff duration.
|
||||
*/
|
||||
duration() {
|
||||
var ms = this.ms * Math.pow(this.factor, this.attempts++)
|
||||
let ms = this.ms * this.factor ** this.attempts++;
|
||||
if (this.jitter) {
|
||||
var rand = Math.random()
|
||||
var deviation = Math.floor(rand * this.jitter * ms)
|
||||
ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation
|
||||
const rand = Math.random();
|
||||
const deviation = Math.floor(rand * this.jitter * ms);
|
||||
ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation;
|
||||
}
|
||||
return Math.min(ms, this.max) | 0
|
||||
return Math.min(ms, this.max) | 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the number of attempts.
|
||||
*/
|
||||
reset() {
|
||||
this.attempts = 0
|
||||
this.attempts = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,62 +1,66 @@
|
||||
import {Client, ClientOptions} from './'
|
||||
import { Client, ClientOptions } from ".";
|
||||
|
||||
class BroadcastClient extends Client {
|
||||
ledgerVersion: number | undefined = undefined
|
||||
private _clients: Client[]
|
||||
ledgerVersion: number | undefined = undefined;
|
||||
private readonly _clients: Client[];
|
||||
|
||||
constructor(servers, options: ClientOptions = {}) {
|
||||
super(servers[0], options)
|
||||
super(servers[0], options);
|
||||
|
||||
const clients: Client[] = servers.map(
|
||||
(server) => new Client(server, options)
|
||||
)
|
||||
);
|
||||
|
||||
// exposed for testing
|
||||
this._clients = clients
|
||||
this._clients = clients;
|
||||
this.getMethodNames().forEach((name) => {
|
||||
this[name] = function () {
|
||||
// eslint-disable-line no-loop-func
|
||||
return Promise.race(clients.map((client) => client[name](...arguments)))
|
||||
}
|
||||
})
|
||||
return Promise.race(
|
||||
clients.map((client) => client[name](...arguments))
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
// connection methods must be overridden to apply to all client instances
|
||||
this.connect = async function () {
|
||||
await Promise.all(clients.map((client) => client.connect()))
|
||||
}
|
||||
await Promise.all(clients.map((client) => client.connect()));
|
||||
};
|
||||
this.disconnect = async function () {
|
||||
await Promise.all(clients.map((client) => client.disconnect()))
|
||||
}
|
||||
await Promise.all(clients.map((client) => client.disconnect()));
|
||||
};
|
||||
this.isConnected = function () {
|
||||
return clients.map((client) => client.isConnected()).every(Boolean)
|
||||
}
|
||||
return clients.map((client) => client.isConnected()).every(Boolean);
|
||||
};
|
||||
|
||||
// synchronous methods are all passed directly to the first client instance
|
||||
const defaultClient = clients[0]
|
||||
const syncMethods = ['sign']
|
||||
const defaultClient = clients[0];
|
||||
const syncMethods = ["sign"];
|
||||
syncMethods.forEach((name) => {
|
||||
this[name] = defaultClient[name].bind(defaultClient)
|
||||
})
|
||||
this[name] = defaultClient[name].bind(defaultClient);
|
||||
});
|
||||
|
||||
clients.forEach((client) => {
|
||||
client.on('error', (errorCode, errorMessage, data) =>
|
||||
this.emit('error', errorCode, errorMessage, data)
|
||||
)
|
||||
})
|
||||
client.on("error", (errorCode, errorMessage, data) =>
|
||||
this.emit("error", errorCode, errorMessage, data)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
getMethodNames() {
|
||||
const methodNames: string[] = []
|
||||
const firstClient = this._clients[0]
|
||||
const methods = Object.getOwnPropertyNames(firstClient)
|
||||
methods.push(...Object.getOwnPropertyNames(Object.getPrototypeOf(firstClient)))
|
||||
const methodNames: string[] = [];
|
||||
const firstClient = this._clients[0];
|
||||
const methods = Object.getOwnPropertyNames(firstClient);
|
||||
methods.push(
|
||||
...Object.getOwnPropertyNames(Object.getPrototypeOf(firstClient))
|
||||
);
|
||||
for (const name of methods) {
|
||||
if (typeof firstClient[name] === 'function' && name !== 'constructor') {
|
||||
methodNames.push(name)
|
||||
if (typeof firstClient[name] === "function" && name !== "constructor") {
|
||||
methodNames.push(name);
|
||||
}
|
||||
}
|
||||
return methodNames
|
||||
return methodNames;
|
||||
}
|
||||
}
|
||||
|
||||
export {BroadcastClient}
|
||||
export { BroadcastClient };
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import _ from 'lodash'
|
||||
import {EventEmitter} from 'events'
|
||||
import {parse as parseURL} from 'url'
|
||||
import WebSocket from 'ws'
|
||||
import { EventEmitter } from "events";
|
||||
import { parse as parseURL } from "url";
|
||||
|
||||
import _ from "lodash";
|
||||
import WebSocket from "ws";
|
||||
|
||||
import {
|
||||
RippledError,
|
||||
DisconnectedError,
|
||||
@@ -9,25 +11,26 @@ import {
|
||||
TimeoutError,
|
||||
ResponseFormatError,
|
||||
ConnectionError,
|
||||
RippleError
|
||||
} from '../common/errors'
|
||||
import {ExponentialBackoff} from './backoff'
|
||||
import { Response } from '../models/methods'
|
||||
RippleError,
|
||||
} from "../common/errors";
|
||||
import { Response } from "../models/methods";
|
||||
|
||||
import { ExponentialBackoff } from "./backoff";
|
||||
|
||||
/**
|
||||
* ConnectionOptions is the configuration for the Connection class.
|
||||
*/
|
||||
export interface ConnectionOptions {
|
||||
trace?: boolean | ((id: string, message: string) => void)
|
||||
proxy?: string
|
||||
proxyAuthorization?: string
|
||||
authorization?: string
|
||||
trustedCertificates?: string[]
|
||||
key?: string
|
||||
passphrase?: string
|
||||
certificate?: string
|
||||
timeout: number // request timeout
|
||||
connectionTimeout: number
|
||||
trace?: boolean | ((id: string, message: string) => void);
|
||||
proxy?: string;
|
||||
proxyAuthorization?: string;
|
||||
authorization?: string;
|
||||
trustedCertificates?: string[];
|
||||
key?: string;
|
||||
passphrase?: string;
|
||||
certificate?: string;
|
||||
timeout: number; // request timeout
|
||||
connectionTimeout: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,82 +38,88 @@ export interface ConnectionOptions {
|
||||
* is optional, so any ConnectionOptions configuration that has a default value is
|
||||
* still optional at the point that the user provides it.
|
||||
*/
|
||||
export type ConnectionUserOptions = Partial<ConnectionOptions>
|
||||
export type ConnectionUserOptions = Partial<ConnectionOptions>;
|
||||
|
||||
/**
|
||||
* Represents an intentionally triggered web-socket disconnect code.
|
||||
* WebSocket spec allows 4xxx codes for app/library specific codes.
|
||||
* See: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||
**/
|
||||
const INTENTIONAL_DISCONNECT_CODE = 4000
|
||||
//
|
||||
// Represents an intentionally triggered web-socket disconnect code.
|
||||
// WebSocket spec allows 4xxx codes for app/library specific codes.
|
||||
// See: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
|
||||
//
|
||||
const INTENTIONAL_DISCONNECT_CODE = 4000;
|
||||
|
||||
/**
|
||||
* Create a new websocket given your URL and optional proxy/certificate
|
||||
* configuration.
|
||||
*
|
||||
* @param url
|
||||
* @param config
|
||||
*/
|
||||
function createWebSocket(url: string, config: ConnectionOptions): WebSocket {
|
||||
const options: WebSocket.ClientOptions = {}
|
||||
const options: WebSocket.ClientOptions = {};
|
||||
if (config.proxy != null) {
|
||||
// TODO: replace deprecated method
|
||||
const parsedURL = parseURL(url)
|
||||
const parsedProxyURL = parseURL(config.proxy)
|
||||
const parsedURL = parseURL(url);
|
||||
const parsedProxyURL = parseURL(config.proxy);
|
||||
const proxyOverrides = _.omitBy(
|
||||
{
|
||||
secureEndpoint: parsedURL.protocol === 'wss:',
|
||||
secureProxy: parsedProxyURL.protocol === 'https:',
|
||||
secureEndpoint: parsedURL.protocol === "wss:",
|
||||
secureProxy: parsedProxyURL.protocol === "https:",
|
||||
auth: config.proxyAuthorization,
|
||||
ca: config.trustedCertificates,
|
||||
key: config.key,
|
||||
passphrase: config.passphrase,
|
||||
cert: config.certificate
|
||||
cert: config.certificate,
|
||||
},
|
||||
(value) => value == null
|
||||
)
|
||||
const proxyOptions = {...parsedProxyURL, ...proxyOverrides}
|
||||
let HttpsProxyAgent
|
||||
);
|
||||
const proxyOptions = { ...parsedProxyURL, ...proxyOverrides };
|
||||
let HttpsProxyAgent;
|
||||
try {
|
||||
HttpsProxyAgent = require('https-proxy-agent')
|
||||
HttpsProxyAgent = require("https-proxy-agent");
|
||||
} catch (error) {
|
||||
throw new Error('"proxy" option is not supported in the browser')
|
||||
throw new Error('"proxy" option is not supported in the browser');
|
||||
}
|
||||
options.agent = new HttpsProxyAgent(proxyOptions)
|
||||
options.agent = new HttpsProxyAgent(proxyOptions);
|
||||
}
|
||||
if (config.authorization != null) {
|
||||
const base64 = Buffer.from(config.authorization).toString('base64')
|
||||
options.headers = {Authorization: `Basic ${base64}`}
|
||||
const base64 = Buffer.from(config.authorization).toString("base64");
|
||||
options.headers = { Authorization: `Basic ${base64}` };
|
||||
}
|
||||
const optionsOverrides = _.omitBy(
|
||||
{
|
||||
ca: config.trustedCertificates,
|
||||
key: config.key,
|
||||
passphrase: config.passphrase,
|
||||
cert: config.certificate
|
||||
cert: config.certificate,
|
||||
},
|
||||
(value) => value == null
|
||||
)
|
||||
const websocketOptions = {...options, ...optionsOverrides}
|
||||
const websocket = new WebSocket(url, null, websocketOptions)
|
||||
);
|
||||
const websocketOptions = { ...options, ...optionsOverrides };
|
||||
const websocket = new WebSocket(url, websocketOptions);
|
||||
// we will have a listener for each outstanding request,
|
||||
// so we have to raise the limit (the default is 10)
|
||||
if (typeof websocket.setMaxListeners === 'function') {
|
||||
websocket.setMaxListeners(Infinity)
|
||||
if (typeof websocket.setMaxListeners === "function") {
|
||||
websocket.setMaxListeners(Infinity);
|
||||
}
|
||||
return websocket
|
||||
return websocket;
|
||||
}
|
||||
|
||||
/**
|
||||
* ws.send(), but promisified.
|
||||
* Ws.send(), but promisified.
|
||||
*
|
||||
* @param ws
|
||||
* @param message
|
||||
*/
|
||||
function websocketSendAsync(ws: WebSocket, message: string) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
ws.send(message, undefined, (error) => {
|
||||
ws.send(message, (error) => {
|
||||
if (error) {
|
||||
reject(new DisconnectedError(error.message, error))
|
||||
reject(new DisconnectedError(error.message, error));
|
||||
} else {
|
||||
resolve()
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,25 +128,25 @@ function websocketSendAsync(ws: WebSocket, message: string) {
|
||||
* after-the-fact.
|
||||
*/
|
||||
class ConnectionManager {
|
||||
private promisesAwaitingConnection: {
|
||||
resolve: Function
|
||||
reject: Function
|
||||
}[] = []
|
||||
private promisesAwaitingConnection: Array<{
|
||||
resolve: Function;
|
||||
reject: Function;
|
||||
}> = [];
|
||||
|
||||
resolveAllAwaiting() {
|
||||
this.promisesAwaitingConnection.map(({resolve}) => resolve())
|
||||
this.promisesAwaitingConnection = []
|
||||
this.promisesAwaitingConnection.map(({ resolve }) => resolve());
|
||||
this.promisesAwaitingConnection = [];
|
||||
}
|
||||
|
||||
rejectAllAwaiting(error: Error) {
|
||||
this.promisesAwaitingConnection.map(({reject}) => reject(error))
|
||||
this.promisesAwaitingConnection = []
|
||||
this.promisesAwaitingConnection.map(({ reject }) => reject(error));
|
||||
this.promisesAwaitingConnection = [];
|
||||
}
|
||||
|
||||
awaitConnection(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.promisesAwaitingConnection.push({resolve, reject})
|
||||
})
|
||||
this.promisesAwaitingConnection.push({ resolve, reject });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,231 +157,247 @@ class ConnectionManager {
|
||||
* original request.
|
||||
*/
|
||||
class RequestManager {
|
||||
private nextId = 0
|
||||
private promisesAwaitingResponse: {
|
||||
resolve: Function
|
||||
reject: Function
|
||||
timer: NodeJS.Timeout
|
||||
}[] = []
|
||||
private nextId = 0;
|
||||
private promisesAwaitingResponse: Array<{
|
||||
resolve: Function;
|
||||
reject: Function;
|
||||
timer: NodeJS.Timeout;
|
||||
}> = [];
|
||||
|
||||
cancel(id: number) {
|
||||
const {timer} = this.promisesAwaitingResponse[id]
|
||||
clearTimeout(timer)
|
||||
delete this.promisesAwaitingResponse[id]
|
||||
const { timer } = this.promisesAwaitingResponse[id];
|
||||
clearTimeout(timer);
|
||||
delete this.promisesAwaitingResponse[id];
|
||||
}
|
||||
|
||||
resolve(id: string | number, data: Response) {
|
||||
const {timer, resolve} = this.promisesAwaitingResponse[id]
|
||||
clearTimeout(timer)
|
||||
resolve(data)
|
||||
delete this.promisesAwaitingResponse[id]
|
||||
const { timer, resolve } = this.promisesAwaitingResponse[id];
|
||||
clearTimeout(timer);
|
||||
resolve(data);
|
||||
delete this.promisesAwaitingResponse[id];
|
||||
}
|
||||
|
||||
reject(id: string | number, error: Error) {
|
||||
const {timer, reject} = this.promisesAwaitingResponse[id]
|
||||
clearTimeout(timer)
|
||||
reject(error)
|
||||
delete this.promisesAwaitingResponse[id]
|
||||
const { timer, reject } = this.promisesAwaitingResponse[id];
|
||||
clearTimeout(timer);
|
||||
reject(error);
|
||||
delete this.promisesAwaitingResponse[id];
|
||||
}
|
||||
|
||||
rejectAll(error: Error) {
|
||||
this.promisesAwaitingResponse.forEach((_, id) => {
|
||||
this.reject(id, error)
|
||||
})
|
||||
this.reject(id, error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new WebSocket request. This sets up a timeout timer to catch
|
||||
* hung responses, and a promise that will resolve with the response once
|
||||
* the response is seen & handled.
|
||||
*
|
||||
* @param data
|
||||
* @param timeout
|
||||
*/
|
||||
createRequest(data: any, timeout: number): [string | number, string, Promise<any>] {
|
||||
const newId = data.id ? data.id : this.nextId++
|
||||
const newData = JSON.stringify({...data, id: newId})
|
||||
createRequest(
|
||||
data: any,
|
||||
timeout: number
|
||||
): [string | number, string, Promise<any>] {
|
||||
const newId = data.id ? data.id : this.nextId++;
|
||||
const newData = JSON.stringify({ ...data, id: newId });
|
||||
const timer = setTimeout(
|
||||
() => this.reject(newId, new TimeoutError()),
|
||||
timeout
|
||||
)
|
||||
);
|
||||
// Node.js won't exit if a timer is still running, so we tell Node to ignore.
|
||||
// (Node will still wait for the request to complete).
|
||||
if (timer.unref) {
|
||||
timer.unref()
|
||||
timer.unref();
|
||||
}
|
||||
const newPromise = new Promise((resolve: (data: Response) => void, reject) => {
|
||||
this.promisesAwaitingResponse[newId] = {resolve, reject, timer}
|
||||
})
|
||||
return [newId, newData, newPromise]
|
||||
const newPromise = new Promise(
|
||||
(resolve: (data: Response) => void, reject) => {
|
||||
this.promisesAwaitingResponse[newId] = { resolve, reject, timer };
|
||||
}
|
||||
);
|
||||
return [newId, newData, newPromise];
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a "response". Responses match to the earlier request handlers,
|
||||
* Handle a "response". Responses match to the earlier request handlers,
|
||||
* and resolve/reject based on the data received.
|
||||
*
|
||||
* @param data
|
||||
*/
|
||||
handleResponse(data: Response) {
|
||||
if (!Number.isInteger(data.id) || data.id < 0) {
|
||||
throw new ResponseFormatError('valid id not found in response', data)
|
||||
throw new ResponseFormatError("valid id not found in response", data);
|
||||
}
|
||||
if (!this.promisesAwaitingResponse[data.id]) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (data.status === 'error') {
|
||||
const error = new RippledError(data.error_message || data.error, data)
|
||||
this.reject(data.id, error)
|
||||
return
|
||||
if (data.status === "error") {
|
||||
const error = new RippledError(data.error_message || data.error, data);
|
||||
this.reject(data.id, error);
|
||||
return;
|
||||
}
|
||||
if (data.status !== 'success') {
|
||||
if (data.status !== "success") {
|
||||
const error = new ResponseFormatError(
|
||||
`unrecognized status: ${data.status}`,
|
||||
data
|
||||
)
|
||||
this.reject(data.id, error)
|
||||
return
|
||||
);
|
||||
this.reject(data.id, error);
|
||||
return;
|
||||
}
|
||||
this.resolve(data.id, data)
|
||||
this.resolve(data.id, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main Connection class. Responsible for connecting to & managing
|
||||
* an active WebSocket connection to a XRPL node.
|
||||
*
|
||||
* @param errorOrCode
|
||||
*/
|
||||
export class Connection extends EventEmitter {
|
||||
private _url: string
|
||||
private _ws: null | WebSocket = null
|
||||
private _reconnectTimeoutID: null | NodeJS.Timeout = null
|
||||
private _heartbeatIntervalID: null | NodeJS.Timeout = null
|
||||
private _retryConnectionBackoff = new ExponentialBackoff({
|
||||
private readonly _url: string | undefined;
|
||||
private _ws: null | WebSocket = null;
|
||||
private _reconnectTimeoutID: null | NodeJS.Timeout = null;
|
||||
private _heartbeatIntervalID: null | NodeJS.Timeout = null;
|
||||
private readonly _retryConnectionBackoff = new ExponentialBackoff({
|
||||
min: 100,
|
||||
max: 60 * 1000
|
||||
})
|
||||
max: 60 * 1000,
|
||||
});
|
||||
|
||||
private _trace: (id: string, message: string) => void = () => {}
|
||||
private _config: ConnectionOptions
|
||||
private _requestManager = new RequestManager()
|
||||
private _connectionManager = new ConnectionManager()
|
||||
private readonly _trace: (id: string, message: string) => void = () => {};
|
||||
private readonly _config: ConnectionOptions;
|
||||
private readonly _requestManager = new RequestManager();
|
||||
private readonly _connectionManager = new ConnectionManager();
|
||||
|
||||
constructor(url?: string, options: ConnectionUserOptions = {}) {
|
||||
super()
|
||||
this.setMaxListeners(Infinity)
|
||||
this._url = url
|
||||
super();
|
||||
this.setMaxListeners(Infinity);
|
||||
this._url = url;
|
||||
this._config = {
|
||||
timeout: 20 * 1000,
|
||||
connectionTimeout: 5 * 1000,
|
||||
...options
|
||||
}
|
||||
if (typeof options.trace === 'function') {
|
||||
this._trace = options.trace
|
||||
} else if (options.trace === true) {
|
||||
this._trace = console.log
|
||||
...options,
|
||||
};
|
||||
if (typeof options.trace === "function") {
|
||||
this._trace = options.trace;
|
||||
} else if (options.trace) {
|
||||
this._trace = console.log;
|
||||
}
|
||||
}
|
||||
|
||||
private _onMessage(message) {
|
||||
this._trace('receive', message)
|
||||
let data: any
|
||||
this._trace("receive", message);
|
||||
let data: any;
|
||||
try {
|
||||
data = JSON.parse(message)
|
||||
data = JSON.parse(message);
|
||||
} catch (error) {
|
||||
this.emit('error', 'badMessage', error.message, message)
|
||||
return
|
||||
this.emit("error", "badMessage", error.message, message);
|
||||
return;
|
||||
}
|
||||
if (data.type == null && data.error) {
|
||||
this.emit('error', data.error, data.error_message, data) // e.g. slowDown
|
||||
return
|
||||
this.emit("error", data.error, data.error_message, data); // e.g. slowDown
|
||||
return;
|
||||
}
|
||||
if (data.type) {
|
||||
this.emit(data.type, data)
|
||||
this.emit(data.type, data);
|
||||
}
|
||||
if (data.type === 'response') {
|
||||
if (data.type === "response") {
|
||||
try {
|
||||
this._requestManager.handleResponse(data)
|
||||
this._requestManager.handleResponse(data);
|
||||
} catch (error) {
|
||||
this.emit('error', 'badMessage', error.message, message)
|
||||
this.emit("error", "badMessage", error.message, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private get _state() {
|
||||
return this._ws ? this._ws.readyState : WebSocket.CLOSED
|
||||
return this._ws ? this._ws.readyState : WebSocket.CLOSED;
|
||||
}
|
||||
|
||||
private get _shouldBeConnected() {
|
||||
return this._ws !== null
|
||||
return this._ws !== null;
|
||||
}
|
||||
|
||||
private _clearHeartbeatInterval = () => {
|
||||
clearInterval(this._heartbeatIntervalID)
|
||||
}
|
||||
private readonly _clearHeartbeatInterval = () => {
|
||||
if (this._heartbeatIntervalID) {
|
||||
clearInterval(this._heartbeatIntervalID);
|
||||
}
|
||||
};
|
||||
|
||||
private _startHeartbeatInterval = () => {
|
||||
this._clearHeartbeatInterval()
|
||||
private readonly _startHeartbeatInterval = () => {
|
||||
this._clearHeartbeatInterval();
|
||||
this._heartbeatIntervalID = setInterval(
|
||||
() => this._heartbeat(),
|
||||
this._config.timeout
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* A heartbeat is just a "ping" command, sent on an interval.
|
||||
* If this succeeds, we're good. If it fails, disconnect so that the consumer can reconnect, if desired.
|
||||
*/
|
||||
private _heartbeat = () => {
|
||||
return this.request({command: 'ping'}).catch(() => {
|
||||
private readonly _heartbeat = () => {
|
||||
return this.request({ command: "ping" }).catch(() => {
|
||||
return this.reconnect().catch((error) => {
|
||||
this.emit('error', 'reconnect', error.message, error)
|
||||
})
|
||||
})
|
||||
}
|
||||
this.emit("error", "reconnect", error.message, error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
private _onConnectionFailed = (errorOrCode: Error | number | null) => {
|
||||
private readonly _onConnectionFailed = (
|
||||
errorOrCode: Error | number | null
|
||||
) => {
|
||||
if (this._ws) {
|
||||
this._ws.removeAllListeners()
|
||||
this._ws.on('error', () => {
|
||||
this._ws.removeAllListeners();
|
||||
this._ws.on("error", () => {
|
||||
// Correctly listen for -- but ignore -- any future errors: If you
|
||||
// don't have a listener on "error" node would log a warning on error.
|
||||
})
|
||||
this._ws.close()
|
||||
this._ws = null
|
||||
});
|
||||
this._ws.close();
|
||||
this._ws = null;
|
||||
}
|
||||
if (typeof errorOrCode === 'number') {
|
||||
if (typeof errorOrCode === "number") {
|
||||
this._connectionManager.rejectAllAwaiting(
|
||||
new NotConnectedError(`Connection failed with code ${errorOrCode}.`, {
|
||||
code: errorOrCode
|
||||
code: errorOrCode,
|
||||
})
|
||||
)
|
||||
);
|
||||
} else if (errorOrCode && errorOrCode.message) {
|
||||
this._connectionManager.rejectAllAwaiting(
|
||||
new NotConnectedError(errorOrCode.message, errorOrCode)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
this._connectionManager.rejectAllAwaiting(
|
||||
new NotConnectedError('Connection failed.')
|
||||
)
|
||||
new NotConnectedError("Connection failed.")
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
isConnected() {
|
||||
return this._state === WebSocket.OPEN
|
||||
return this._state === WebSocket.OPEN;
|
||||
}
|
||||
|
||||
connect(): Promise<void> {
|
||||
if (this.isConnected()) {
|
||||
return Promise.resolve()
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (this._state === WebSocket.CONNECTING) {
|
||||
return this._connectionManager.awaitConnection()
|
||||
return this._connectionManager.awaitConnection();
|
||||
}
|
||||
if (!this._url) {
|
||||
return Promise.reject(
|
||||
new ConnectionError('Cannot connect because no server was specified')
|
||||
)
|
||||
new ConnectionError("Cannot connect because no server was specified")
|
||||
);
|
||||
}
|
||||
if (this._ws) {
|
||||
return Promise.reject(
|
||||
new RippleError('Websocket connection never cleaned up.', {
|
||||
state: this._state
|
||||
new RippleError("Websocket connection never cleaned up.", {
|
||||
state: this._state,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Create the connection timeout, in case the connection hangs longer than expected.
|
||||
@@ -383,58 +408,71 @@ export class Connection extends EventEmitter {
|
||||
`If your internet connection is working, the rippled server may be blocked or inaccessible. ` +
|
||||
`You can also try setting the 'connectionTimeout' option in the Client constructor.`
|
||||
)
|
||||
)
|
||||
}, this._config.connectionTimeout)
|
||||
);
|
||||
}, this._config.connectionTimeout);
|
||||
// Connection listeners: these stay attached only until a connection is done/open.
|
||||
this._ws = createWebSocket(this._url, this._config)
|
||||
this._ws.on('error', this._onConnectionFailed)
|
||||
this._ws.on('error', () => clearTimeout(connectionTimeoutID))
|
||||
this._ws.on('close', this._onConnectionFailed)
|
||||
this._ws.on('close', () => clearTimeout(connectionTimeoutID))
|
||||
this._ws.once('open', async () => {
|
||||
this._ws = createWebSocket(this._url, this._config);
|
||||
|
||||
if (this._ws == null) {
|
||||
throw new Error("Connect: created null websocket");
|
||||
}
|
||||
|
||||
this._ws.on("error", this._onConnectionFailed);
|
||||
this._ws.on("error", () => clearTimeout(connectionTimeoutID));
|
||||
this._ws.on("close", this._onConnectionFailed);
|
||||
this._ws.on("close", () => clearTimeout(connectionTimeoutID));
|
||||
this._ws.once("open", async () => {
|
||||
if (this._ws == null) {
|
||||
throw new Error("onceOpen: ws is null");
|
||||
}
|
||||
|
||||
// Once the connection completes successfully, remove all old listeners
|
||||
this._ws.removeAllListeners()
|
||||
clearTimeout(connectionTimeoutID)
|
||||
this._ws.removeAllListeners();
|
||||
clearTimeout(connectionTimeoutID);
|
||||
// Add new, long-term connected listeners for messages and errors
|
||||
this._ws.on('message', (message: string) => this._onMessage(message))
|
||||
this._ws.on('error', (error) =>
|
||||
this.emit('error', 'websocket', error.message, error)
|
||||
)
|
||||
this._ws.on("message", (message: string) => this._onMessage(message));
|
||||
this._ws.on("error", (error) =>
|
||||
this.emit("error", "websocket", error.message, error)
|
||||
);
|
||||
// Handle a closed connection: reconnect if it was unexpected
|
||||
this._ws.once('close', (code, reason) => {
|
||||
this._clearHeartbeatInterval()
|
||||
this._ws.once("close", (code, reason) => {
|
||||
if (this._ws == null) {
|
||||
throw new Error("onceClose: ws is null");
|
||||
}
|
||||
|
||||
this._clearHeartbeatInterval();
|
||||
this._requestManager.rejectAll(
|
||||
new DisconnectedError(`websocket was closed, ${reason}`)
|
||||
)
|
||||
this._ws.removeAllListeners()
|
||||
this._ws = null
|
||||
this.emit('disconnected', code)
|
||||
);
|
||||
this._ws.removeAllListeners();
|
||||
this._ws = null;
|
||||
this.emit("disconnected", code);
|
||||
// If this wasn't a manual disconnect, then lets reconnect ASAP.
|
||||
if (code !== INTENTIONAL_DISCONNECT_CODE) {
|
||||
const retryTimeout = this._retryConnectionBackoff.duration()
|
||||
this._trace('reconnect', `Retrying connection in ${retryTimeout}ms.`)
|
||||
this.emit('reconnecting', this._retryConnectionBackoff.attempts)
|
||||
const retryTimeout = this._retryConnectionBackoff.duration();
|
||||
this._trace("reconnect", `Retrying connection in ${retryTimeout}ms.`);
|
||||
this.emit("reconnecting", this._retryConnectionBackoff.attempts);
|
||||
// Start the reconnect timeout, but set it to `this._reconnectTimeoutID`
|
||||
// so that we can cancel one in-progress on disconnect.
|
||||
this._reconnectTimeoutID = setTimeout(() => {
|
||||
this.reconnect().catch((error) => {
|
||||
this.emit('error', 'reconnect', error.message, error)
|
||||
})
|
||||
}, retryTimeout)
|
||||
this.emit("error", "reconnect", error.message, error);
|
||||
});
|
||||
}, retryTimeout);
|
||||
}
|
||||
})
|
||||
});
|
||||
// Finalize the connection and resolve all awaiting connect() requests
|
||||
try {
|
||||
this._retryConnectionBackoff.reset()
|
||||
this._startHeartbeatInterval()
|
||||
this._connectionManager.resolveAllAwaiting()
|
||||
this.emit('connected')
|
||||
this._retryConnectionBackoff.reset();
|
||||
this._startHeartbeatInterval();
|
||||
this._connectionManager.resolveAllAwaiting();
|
||||
this.emit("connected");
|
||||
} catch (error) {
|
||||
this._connectionManager.rejectAllAwaiting(error)
|
||||
await this.disconnect().catch(() => {}) // Ignore this error, propagate the root cause.
|
||||
this._connectionManager.rejectAllAwaiting(error);
|
||||
await this.disconnect().catch(() => {}); // Ignore this error, propagate the root cause.
|
||||
}
|
||||
})
|
||||
return this._connectionManager.awaitConnection()
|
||||
});
|
||||
return this._connectionManager.awaitConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -445,20 +483,30 @@ export class Connection extends EventEmitter {
|
||||
* If no open websocket connection exists, resolve with no code (`undefined`).
|
||||
*/
|
||||
disconnect(): Promise<number | undefined> {
|
||||
clearTimeout(this._reconnectTimeoutID)
|
||||
this._reconnectTimeoutID = null
|
||||
if (this._state === WebSocket.CLOSED || !this._ws) {
|
||||
return Promise.resolve(undefined)
|
||||
if (this._reconnectTimeoutID !== null) {
|
||||
clearTimeout(this._reconnectTimeoutID);
|
||||
this._reconnectTimeoutID = null;
|
||||
}
|
||||
if (this._state === WebSocket.CLOSED) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
if (this._ws === null) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this._ws.once('close', (code) => resolve(code))
|
||||
if (this._ws === null) {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
this._ws.once("close", (code) => resolve(code));
|
||||
// Connection already has a disconnect handler for the disconnect logic.
|
||||
// Just close the websocket manually (with our "intentional" code) to
|
||||
// trigger that.
|
||||
if (this._state !== WebSocket.CLOSING) {
|
||||
this._ws.close(INTENTIONAL_DISCONNECT_CODE)
|
||||
if (this._ws != null && this._state !== WebSocket.CLOSING) {
|
||||
this._ws.close(INTENTIONAL_DISCONNECT_CODE);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,33 +516,36 @@ export class Connection extends EventEmitter {
|
||||
// NOTE: We currently have a "reconnecting" event, but that only triggers
|
||||
// through an unexpected connection retry logic.
|
||||
// See: https://github.com/ripple/ripple-lib/pull/1101#issuecomment-565360423
|
||||
this.emit('reconnect')
|
||||
await this.disconnect()
|
||||
await this.connect()
|
||||
this.emit("reconnect");
|
||||
await this.disconnect();
|
||||
await this.connect();
|
||||
}
|
||||
|
||||
async request<T extends {command: string}>(request: T, timeout?: number): Promise<any> {
|
||||
if (!this._shouldBeConnected) {
|
||||
throw new NotConnectedError()
|
||||
async request<T extends { command: string }>(
|
||||
request: T,
|
||||
timeout?: number
|
||||
): Promise<any> {
|
||||
if (!this._shouldBeConnected || this._ws == null) {
|
||||
throw new NotConnectedError();
|
||||
}
|
||||
const [id, message, responsePromise] = this._requestManager.createRequest(
|
||||
request,
|
||||
timeout || this._config.timeout
|
||||
)
|
||||
this._trace('send', message)
|
||||
);
|
||||
this._trace("send", message);
|
||||
websocketSendAsync(this._ws, message).catch((error) => {
|
||||
this._requestManager.reject(id, error)
|
||||
})
|
||||
this._requestManager.reject(id, error);
|
||||
});
|
||||
|
||||
return responsePromise
|
||||
return responsePromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Websocket connection URL
|
||||
* Get the Websocket connection URL.
|
||||
*
|
||||
* @returns The Websocket connection URL
|
||||
* @returns The Websocket connection URL.
|
||||
*/
|
||||
getUrl(): string {
|
||||
return this._url
|
||||
return this._url ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
import {EventEmitter} from 'events'
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
import {
|
||||
constants,
|
||||
errors,
|
||||
txFlags,
|
||||
} from '../common'
|
||||
import { Connection, ConnectionUserOptions } from './connection'
|
||||
import getTrustlines from '../ledger/trustlines'
|
||||
import getBalances from '../ledger/balances'
|
||||
import getPaths from '../ledger/pathfind'
|
||||
import {getOrderbook, formatBidsAndAsks} from '../ledger/orderbook'
|
||||
import preparePayment from '../transaction/payment'
|
||||
import prepareTrustline from '../transaction/trustline'
|
||||
import prepareOrder from '../transaction/order'
|
||||
import prepareOrderCancellation from '../transaction/ordercancellation'
|
||||
import prepareEscrowCreation from '../transaction/escrow-creation'
|
||||
import prepareEscrowExecution from '../transaction/escrow-execution'
|
||||
import prepareEscrowCancellation from '../transaction/escrow-cancellation'
|
||||
import preparePaymentChannelCreate from '../transaction/payment-channel-create'
|
||||
import preparePaymentChannelFund from '../transaction/payment-channel-fund'
|
||||
import preparePaymentChannelClaim from '../transaction/payment-channel-claim'
|
||||
import prepareCheckCreate from '../transaction/check-create'
|
||||
import prepareCheckCancel from '../transaction/check-cancel'
|
||||
import prepareCheckCash from '../transaction/check-cash'
|
||||
import prepareSettings from '../transaction/settings'
|
||||
import prepareTicketCreate from '../transaction/ticket'
|
||||
import {sign} from '../transaction/sign'
|
||||
import combine from '../transaction/combine'
|
||||
import {deriveAddress, deriveXAddress} from '../utils/derive'
|
||||
classicAddressToXAddress,
|
||||
xAddressToClassicAddress,
|
||||
isValidXAddress,
|
||||
isValidClassicAddress,
|
||||
encodeSeed,
|
||||
decodeSeed,
|
||||
encodeAccountID,
|
||||
decodeAccountID,
|
||||
encodeNodePublic,
|
||||
decodeNodePublic,
|
||||
encodeAccountPublic,
|
||||
decodeAccountPublic,
|
||||
encodeXAddress,
|
||||
decodeXAddress,
|
||||
} from "ripple-address-codec";
|
||||
|
||||
import { constants, errors, txFlags, ensureClassicAddress } from "../common";
|
||||
import { ValidationError } from "../common/errors";
|
||||
import { getFee } from "../common/fee";
|
||||
import * as schemaValidator from "../common/schema-validator";
|
||||
import getBalances from "../ledger/balances";
|
||||
import { getOrderbook, formatBidsAndAsks } from "../ledger/orderbook";
|
||||
import getPaths from "../ledger/pathfind";
|
||||
import getTrustlines from "../ledger/trustlines";
|
||||
import { clamp } from "../ledger/utils";
|
||||
import {
|
||||
Request,
|
||||
Response,
|
||||
@@ -94,162 +93,172 @@ import {
|
||||
PingRequest,
|
||||
PingResponse,
|
||||
RandomRequest,
|
||||
RandomResponse
|
||||
} from '../models/methods'
|
||||
RandomResponse,
|
||||
} from "../models/methods";
|
||||
import prepareCheckCancel from "../transaction/check-cancel";
|
||||
import prepareCheckCash from "../transaction/check-cash";
|
||||
import prepareCheckCreate from "../transaction/check-create";
|
||||
import combine from "../transaction/combine";
|
||||
import prepareEscrowCancellation from "../transaction/escrow-cancellation";
|
||||
import prepareEscrowCreation from "../transaction/escrow-creation";
|
||||
import prepareEscrowExecution from "../transaction/escrow-execution";
|
||||
import prepareOrder from "../transaction/order";
|
||||
import prepareOrderCancellation from "../transaction/ordercancellation";
|
||||
import preparePayment from "../transaction/payment";
|
||||
import preparePaymentChannelClaim from "../transaction/payment-channel-claim";
|
||||
import preparePaymentChannelCreate from "../transaction/payment-channel-create";
|
||||
import preparePaymentChannelFund from "../transaction/payment-channel-fund";
|
||||
import prepareSettings from "../transaction/settings";
|
||||
import { sign } from "../transaction/sign";
|
||||
import prepareTicketCreate from "../transaction/ticket";
|
||||
import prepareTrustline from "../transaction/trustline";
|
||||
import { TransactionJSON, Instructions, Prepare } from "../transaction/types";
|
||||
import * as transactionUtils from "../transaction/utils";
|
||||
import { deriveAddress, deriveXAddress } from "../utils/derive";
|
||||
import generateFaucetWallet from "../wallet/wallet-generation";
|
||||
|
||||
import * as transactionUtils from '../transaction/utils'
|
||||
import * as schemaValidator from '../common/schema-validator'
|
||||
import {getFee} from '../common/fee'
|
||||
import {ensureClassicAddress} from '../common'
|
||||
import {clamp} from '../ledger/utils'
|
||||
import {TransactionJSON, Instructions, Prepare} from '../transaction/types'
|
||||
import {
|
||||
classicAddressToXAddress,
|
||||
xAddressToClassicAddress,
|
||||
isValidXAddress,
|
||||
isValidClassicAddress,
|
||||
encodeSeed,
|
||||
decodeSeed,
|
||||
encodeAccountID,
|
||||
decodeAccountID,
|
||||
encodeNodePublic,
|
||||
decodeNodePublic,
|
||||
encodeAccountPublic,
|
||||
decodeAccountPublic,
|
||||
encodeXAddress,
|
||||
decodeXAddress
|
||||
} from 'ripple-address-codec'
|
||||
import generateFaucetWallet from '../wallet/wallet-generation'
|
||||
import { ValidationError } from '../common/errors'
|
||||
import { Connection, ConnectionUserOptions } from "./connection";
|
||||
|
||||
export interface ClientOptions extends ConnectionUserOptions {
|
||||
feeCushion?: number
|
||||
maxFeeXRP?: string
|
||||
proxy?: string
|
||||
timeout?: number
|
||||
feeCushion?: number;
|
||||
maxFeeXRP?: string;
|
||||
proxy?: string;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response key / property name that contains the listed data for a
|
||||
* command. This varies from command to command, but we need to know it to
|
||||
* properly count across many requests.
|
||||
*
|
||||
* @param command
|
||||
*/
|
||||
function getCollectKeyFromCommand(command: string): string | null {
|
||||
switch (command) {
|
||||
case 'account_channels':
|
||||
return 'channels'
|
||||
case 'account_lines':
|
||||
return 'lines'
|
||||
case 'account_objects':
|
||||
return 'account_objects'
|
||||
case 'account_tx':
|
||||
return 'transactions'
|
||||
case 'account_offers':
|
||||
case 'book_offers':
|
||||
return 'offers'
|
||||
case 'ledger_data':
|
||||
return 'state'
|
||||
case "account_channels":
|
||||
return "channels";
|
||||
case "account_lines":
|
||||
return "lines";
|
||||
case "account_objects":
|
||||
return "account_objects";
|
||||
case "account_tx":
|
||||
return "transactions";
|
||||
case "account_offers":
|
||||
case "book_offers":
|
||||
return "offers";
|
||||
case "ledger_data":
|
||||
return "state";
|
||||
default:
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
type MarkerRequest = AccountChannelsRequest
|
||||
| AccountLinesRequest
|
||||
| AccountObjectsRequest
|
||||
| AccountOffersRequest
|
||||
| AccountTxRequest
|
||||
| LedgerDataRequest
|
||||
type MarkerRequest =
|
||||
| AccountChannelsRequest
|
||||
| AccountLinesRequest
|
||||
| AccountObjectsRequest
|
||||
| AccountOffersRequest
|
||||
| AccountTxRequest
|
||||
| LedgerDataRequest;
|
||||
|
||||
type MarkerResponse = AccountChannelsResponse
|
||||
| AccountLinesResponse
|
||||
| AccountObjectsResponse
|
||||
| AccountOffersResponse
|
||||
| AccountTxResponse
|
||||
| LedgerDataResponse
|
||||
type MarkerResponse =
|
||||
| AccountChannelsResponse
|
||||
| AccountLinesResponse
|
||||
| AccountObjectsResponse
|
||||
| AccountOffersResponse
|
||||
| AccountTxResponse
|
||||
| LedgerDataResponse;
|
||||
|
||||
class Client extends EventEmitter {
|
||||
// Factor to multiply estimated fee by to provide a cushion in case the
|
||||
// required fee rises during submission of a transaction. Defaults to 1.2.
|
||||
_feeCushion: number
|
||||
_feeCushion: number;
|
||||
// Maximum fee to use with transactions, in XRP. Must be a string-encoded
|
||||
// number. Defaults to '2'.
|
||||
_maxFeeXRP: string
|
||||
_maxFeeXRP: string;
|
||||
|
||||
// New in > 0.21.0
|
||||
// non-validated ledger versions are allowed, and passed to rippled as-is.
|
||||
connection: Connection
|
||||
connection: Connection;
|
||||
|
||||
constructor(server: string, options: ClientOptions = {}) {
|
||||
super()
|
||||
if (typeof server !== 'string' || !server.match("^(wss?|wss?\\+unix)://")) {
|
||||
throw new ValidationError("server URI must start with `wss://`, `ws://`, `wss+unix://`, or `ws+unix://`.")
|
||||
super();
|
||||
if (typeof server !== "string" || !server.match("^(wss?|wss?\\+unix)://")) {
|
||||
throw new ValidationError(
|
||||
"server URI must start with `wss://`, `ws://`, `wss+unix://`, or `ws+unix://`."
|
||||
);
|
||||
}
|
||||
|
||||
this._feeCushion = options.feeCushion || 1.2
|
||||
this._maxFeeXRP = options.maxFeeXRP || '2'
|
||||
this._feeCushion = options.feeCushion || 1.2;
|
||||
this._maxFeeXRP = options.maxFeeXRP || "2";
|
||||
|
||||
this.connection = new Connection(server, options)
|
||||
this.connection = new Connection(server, options);
|
||||
|
||||
this.connection.on('error', (errorCode, errorMessage, data) => {
|
||||
this.emit('error', errorCode, errorMessage, data)
|
||||
})
|
||||
this.connection.on("error", (errorCode, errorMessage, data) => {
|
||||
this.emit("error", errorCode, errorMessage, data);
|
||||
});
|
||||
|
||||
this.connection.on('connected', () => {
|
||||
this.emit('connected')
|
||||
})
|
||||
|
||||
this.connection.on('disconnected', (code) => {
|
||||
let finalCode = code
|
||||
this.connection.on("connected", () => {
|
||||
this.emit("connected");
|
||||
});
|
||||
|
||||
this.connection.on("disconnected", (code) => {
|
||||
let finalCode = code;
|
||||
// 4000: Connection uses a 4000 code internally to indicate a manual disconnect/close
|
||||
// Since 4000 is a normal disconnect reason, we convert this to the standard exit code 1000
|
||||
if (finalCode === 4000) {
|
||||
finalCode = 1000
|
||||
finalCode = 1000;
|
||||
}
|
||||
this.emit('disconnected', finalCode)
|
||||
})
|
||||
this.emit("disconnected", finalCode);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a request to the client with the given command and
|
||||
* additional request body parameters.
|
||||
*/
|
||||
public request(r: AccountChannelsRequest): Promise<AccountChannelsResponse>
|
||||
public request(r: AccountCurrenciesRequest): Promise<AccountCurrenciesResponse>
|
||||
public request(r: AccountInfoRequest): Promise<AccountInfoResponse>
|
||||
public request(r: AccountLinesRequest): Promise<AccountLinesResponse>
|
||||
public request(r: AccountObjectsRequest): Promise<AccountObjectsResponse>
|
||||
public request(r: AccountOffersRequest): Promise<AccountOffersResponse>
|
||||
public request(r: AccountTxRequest): Promise<AccountTxResponse>
|
||||
public request(r: BookOffersRequest): Promise<BookOffersResponse>
|
||||
public request(r: ChannelVerifyRequest): Promise<ChannelVerifyResponse>
|
||||
public request(r: DepositAuthorizedRequest): Promise<DepositAuthorizedResponse>
|
||||
public request(r: FeeRequest): Promise<FeeResponse>
|
||||
public request(r: GatewayBalancesRequest): Promise<GatewayBalancesResponse>
|
||||
public request(r: LedgerRequest): Promise<LedgerResponse>
|
||||
public request(r: LedgerClosedRequest): Promise<LedgerClosedResponse>
|
||||
public request(r: LedgerCurrentRequest): Promise<LedgerCurrentResponse>
|
||||
public request(r: LedgerDataRequest): Promise<LedgerDataResponse>
|
||||
public request(r: LedgerEntryRequest): Promise<LedgerEntryResponse>
|
||||
public request(r: ManifestRequest): Promise<ManifestResponse>
|
||||
public request(r: NoRippleCheckRequest): Promise<NoRippleCheckResponse>
|
||||
public request(r: PathFindRequest): Promise<PathFindResponse>
|
||||
public request(r: PingRequest): Promise<PingResponse>
|
||||
public request(r: RandomRequest): Promise<RandomResponse>
|
||||
public request(r: RipplePathFindRequest): Promise<RipplePathFindResponse>
|
||||
public request(r: ServerInfoRequest): Promise<ServerInfoResponse>
|
||||
public request(r: ServerStateRequest): Promise<ServerStateResponse>
|
||||
public request(r: SubmitRequest): Promise<SubmitResponse>
|
||||
public request(r: SubmitMultisignedRequest): Promise<SubmitMultisignedResponse>
|
||||
public request(r: TransactionEntryRequest): Promise<TransactionEntryResponse>
|
||||
public request(r: TxRequest): Promise<TxResponse>
|
||||
public request(r: AccountChannelsRequest): Promise<AccountChannelsResponse>;
|
||||
public request(
|
||||
r: AccountCurrenciesRequest
|
||||
): Promise<AccountCurrenciesResponse>;
|
||||
public request(r: AccountInfoRequest): Promise<AccountInfoResponse>;
|
||||
public request(r: AccountLinesRequest): Promise<AccountLinesResponse>;
|
||||
public request(r: AccountObjectsRequest): Promise<AccountObjectsResponse>;
|
||||
public request(r: AccountOffersRequest): Promise<AccountOffersResponse>;
|
||||
public request(r: AccountTxRequest): Promise<AccountTxResponse>;
|
||||
public request(r: BookOffersRequest): Promise<BookOffersResponse>;
|
||||
public request(r: ChannelVerifyRequest): Promise<ChannelVerifyResponse>;
|
||||
public request(
|
||||
r: DepositAuthorizedRequest
|
||||
): Promise<DepositAuthorizedResponse>;
|
||||
public request(r: FeeRequest): Promise<FeeResponse>;
|
||||
public request(r: GatewayBalancesRequest): Promise<GatewayBalancesResponse>;
|
||||
public request(r: LedgerRequest): Promise<LedgerResponse>;
|
||||
public request(r: LedgerClosedRequest): Promise<LedgerClosedResponse>;
|
||||
public request(r: LedgerCurrentRequest): Promise<LedgerCurrentResponse>;
|
||||
public request(r: LedgerDataRequest): Promise<LedgerDataResponse>;
|
||||
public request(r: LedgerEntryRequest): Promise<LedgerEntryResponse>;
|
||||
public request(r: ManifestRequest): Promise<ManifestResponse>;
|
||||
public request(r: NoRippleCheckRequest): Promise<NoRippleCheckResponse>;
|
||||
public request(r: PathFindRequest): Promise<PathFindResponse>;
|
||||
public request(r: PingRequest): Promise<PingResponse>;
|
||||
public request(r: RandomRequest): Promise<RandomResponse>;
|
||||
public request(r: RipplePathFindRequest): Promise<RipplePathFindResponse>;
|
||||
public request(r: ServerInfoRequest): Promise<ServerInfoResponse>;
|
||||
public request(r: ServerStateRequest): Promise<ServerStateResponse>;
|
||||
public request(r: SubmitRequest): Promise<SubmitResponse>;
|
||||
public request(
|
||||
r: SubmitMultisignedRequest
|
||||
): Promise<SubmitMultisignedResponse>;
|
||||
public request(r: TransactionEntryRequest): Promise<TransactionEntryResponse>;
|
||||
public request(r: TxRequest): Promise<TxResponse>;
|
||||
public request<R extends Request, T extends Response>(r: R): Promise<T> {
|
||||
// TODO: should this be typed with `extends BaseRequest/BaseResponse`?
|
||||
return this.connection.request({
|
||||
...r,
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
account: r.account ? ensureClassicAddress(r.account) : undefined,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -258,38 +267,64 @@ class Client extends EventEmitter {
|
||||
* When there are more results than contained in the response, the response
|
||||
* includes a `marker` field.
|
||||
*
|
||||
* See https://ripple.com/build/rippled-apis/#markers-and-pagination
|
||||
* See https://ripple.com/build/rippled-apis/#markers-and-pagination.
|
||||
*
|
||||
* @param response
|
||||
*/
|
||||
hasNextPage(response: MarkerResponse): boolean {
|
||||
return !!response.result.marker
|
||||
return Boolean(response.result.marker);
|
||||
}
|
||||
|
||||
async requestNextPage(req: AccountChannelsRequest, resp: AccountChannelsResponse): Promise<AccountChannelsResponse>
|
||||
async requestNextPage(req: AccountLinesRequest, resp: AccountLinesResponse): Promise<AccountLinesResponse>
|
||||
async requestNextPage(req: AccountObjectsRequest, resp: AccountObjectsResponse): Promise<AccountObjectsResponse>
|
||||
async requestNextPage(req: AccountOffersRequest, resp: AccountOffersResponse): Promise<AccountOffersResponse>
|
||||
async requestNextPage(req: AccountTxRequest, resp: AccountTxResponse): Promise<AccountTxResponse>
|
||||
async requestNextPage(req: LedgerDataRequest, resp: LedgerDataResponse): Promise<LedgerDataResponse>
|
||||
async requestNextPage<T extends MarkerRequest, U extends MarkerResponse>(req: T, resp: U): Promise<U> {
|
||||
async requestNextPage(
|
||||
req: AccountChannelsRequest,
|
||||
resp: AccountChannelsResponse
|
||||
): Promise<AccountChannelsResponse>;
|
||||
async requestNextPage(
|
||||
req: AccountLinesRequest,
|
||||
resp: AccountLinesResponse
|
||||
): Promise<AccountLinesResponse>;
|
||||
async requestNextPage(
|
||||
req: AccountObjectsRequest,
|
||||
resp: AccountObjectsResponse
|
||||
): Promise<AccountObjectsResponse>;
|
||||
async requestNextPage(
|
||||
req: AccountOffersRequest,
|
||||
resp: AccountOffersResponse
|
||||
): Promise<AccountOffersResponse>;
|
||||
async requestNextPage(
|
||||
req: AccountTxRequest,
|
||||
resp: AccountTxResponse
|
||||
): Promise<AccountTxResponse>;
|
||||
async requestNextPage(
|
||||
req: LedgerDataRequest,
|
||||
resp: LedgerDataResponse
|
||||
): Promise<LedgerDataResponse>;
|
||||
async requestNextPage<T extends MarkerRequest, U extends MarkerResponse>(
|
||||
req: T,
|
||||
resp: U
|
||||
): Promise<U> {
|
||||
if (!resp.result.marker) {
|
||||
return Promise.reject(
|
||||
new errors.NotFoundError('response does not have a next page')
|
||||
)
|
||||
new errors.NotFoundError("response does not have a next page")
|
||||
);
|
||||
}
|
||||
const nextPageRequest = {...req, marker: resp.result.marker}
|
||||
return this.connection.request(nextPageRequest)
|
||||
const nextPageRequest = { ...req, marker: resp.result.marker };
|
||||
return this.connection.request(nextPageRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a transaction.
|
||||
*
|
||||
* You can later submit the transaction with a `submit` request.
|
||||
*
|
||||
* @param txJSON
|
||||
* @param instructions
|
||||
*/
|
||||
async prepareTransaction(
|
||||
txJSON: TransactionJSON,
|
||||
instructions: Instructions = {}
|
||||
): Promise<Prepare> {
|
||||
return transactionUtils.prepareTransaction(txJSON, this, instructions)
|
||||
return transactionUtils.prepareTransaction(txJSON, this, instructions);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,15 +332,15 @@ class Client extends EventEmitter {
|
||||
*
|
||||
* This can be used to generate `MemoData`, `MemoType`, and `MemoFormat`.
|
||||
*
|
||||
* @param string string to convert to hex
|
||||
* @param string - String to convert to hex.
|
||||
*/
|
||||
convertStringToHex(string: string): string {
|
||||
return transactionUtils.convertStringToHex(string)
|
||||
return transactionUtils.convertStringToHex(string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes multiple paged requests to the client to return a given number of
|
||||
* resources. requestAll() will make multiple requests until the `limit`
|
||||
* resources. Multiple paged requests will be made until the `limit`
|
||||
* number of resources is reached (if no `limit` is provided, a single request
|
||||
* will be made).
|
||||
*
|
||||
@@ -316,129 +351,136 @@ class Client extends EventEmitter {
|
||||
* general use. Instead, use rippled's built-in pagination and make multiple
|
||||
* requests as needed.
|
||||
*/
|
||||
async requestAll(req: AccountChannelsRequest): Promise<AccountChannelsResponse[]>
|
||||
async requestAll(req: AccountLinesRequest): Promise<AccountLinesResponse[]>
|
||||
async requestAll(req: AccountObjectsRequest): Promise<AccountObjectsResponse[]>
|
||||
async requestAll(req: AccountOffersRequest): Promise<AccountOffersResponse[]>
|
||||
async requestAll(req: AccountTxRequest): Promise<AccountTxResponse[]>
|
||||
async requestAll(req: BookOffersRequest): Promise<BookOffersResponse[]>
|
||||
async requestAll(req: LedgerDataRequest): Promise<LedgerDataResponse[]>
|
||||
async requestAll<T extends MarkerRequest, U extends MarkerResponse>(request: T, options: {collect?: string} = {}): Promise<U[]> {
|
||||
async requestAll(
|
||||
req: AccountChannelsRequest
|
||||
): Promise<AccountChannelsResponse[]>;
|
||||
async requestAll(req: AccountLinesRequest): Promise<AccountLinesResponse[]>;
|
||||
async requestAll(
|
||||
req: AccountObjectsRequest
|
||||
): Promise<AccountObjectsResponse[]>;
|
||||
async requestAll(req: AccountOffersRequest): Promise<AccountOffersResponse[]>;
|
||||
async requestAll(req: AccountTxRequest): Promise<AccountTxResponse[]>;
|
||||
async requestAll(req: BookOffersRequest): Promise<BookOffersResponse[]>;
|
||||
async requestAll(req: LedgerDataRequest): Promise<LedgerDataResponse[]>;
|
||||
async requestAll<T extends MarkerRequest, U extends MarkerResponse>(
|
||||
request: T,
|
||||
options: { collect?: string } = {}
|
||||
): Promise<U[]> {
|
||||
// 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(request.command)
|
||||
const collectKey =
|
||||
options.collect || getCollectKeyFromCommand(request.command);
|
||||
if (!collectKey) {
|
||||
throw new errors.ValidationError(`no collect key for command ${request.command}`)
|
||||
throw new errors.ValidationError(
|
||||
`no collect key for command ${request.command}`
|
||||
);
|
||||
}
|
||||
// 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 = request.limit != null ? request.limit : Infinity
|
||||
let count: number = 0
|
||||
let marker: string = request.marker
|
||||
let lastBatchLength: number
|
||||
const results = []
|
||||
const countTo: number = request.limit != null ? request.limit : Infinity;
|
||||
let count = 0;
|
||||
let marker: string = request.marker;
|
||||
let lastBatchLength: number;
|
||||
const results: any[] = [];
|
||||
do {
|
||||
const countRemaining = clamp(countTo - count, 10, 400)
|
||||
const countRemaining = clamp(countTo - count, 10, 400);
|
||||
const repeatProps = {
|
||||
...request,
|
||||
limit: countRemaining,
|
||||
marker
|
||||
}
|
||||
const singleResponse = await this.connection.request(repeatProps)
|
||||
const singleResult = singleResponse.result
|
||||
const collectedData = singleResult[collectKey]
|
||||
marker = singleResult['marker']
|
||||
results.push(singleResponse)
|
||||
marker,
|
||||
};
|
||||
const singleResponse = await this.connection.request(repeatProps);
|
||||
const singleResult = singleResponse.result;
|
||||
const collectedData = singleResult[collectKey];
|
||||
marker = singleResult.marker;
|
||||
results.push(singleResponse);
|
||||
// Make sure we handle when no data (not even an empty array) is returned.
|
||||
const isExpectedFormat = Array.isArray(collectedData)
|
||||
const isExpectedFormat = Array.isArray(collectedData);
|
||||
if (isExpectedFormat) {
|
||||
count += collectedData.length
|
||||
lastBatchLength = collectedData.length
|
||||
count += collectedData.length;
|
||||
lastBatchLength = collectedData.length;
|
||||
} else {
|
||||
lastBatchLength = 0
|
||||
lastBatchLength = 0;
|
||||
}
|
||||
} while (!!marker && count < countTo && lastBatchLength !== 0)
|
||||
return results
|
||||
} while (Boolean(marker) && count < countTo && lastBatchLength !== 0);
|
||||
return results;
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
return this.connection.isConnected()
|
||||
return this.connection.isConnected();
|
||||
}
|
||||
|
||||
|
||||
async connect(): Promise<void> {
|
||||
return this.connection.connect()
|
||||
return this.connection.connect();
|
||||
}
|
||||
|
||||
|
||||
async disconnect(): Promise<void> {
|
||||
// backwards compatibility: connection.disconnect() can return a number, but
|
||||
// this method returns nothing. SO we await but don't return any result.
|
||||
await this.connection.disconnect()
|
||||
await this.connection.disconnect();
|
||||
}
|
||||
|
||||
getFee = getFee
|
||||
getFee = getFee;
|
||||
|
||||
getTrustlines = getTrustlines
|
||||
getBalances = getBalances
|
||||
getPaths = getPaths
|
||||
getOrderbook = getOrderbook
|
||||
getTrustlines = getTrustlines;
|
||||
getBalances = getBalances;
|
||||
getPaths = getPaths;
|
||||
getOrderbook = getOrderbook;
|
||||
|
||||
preparePayment = preparePayment
|
||||
prepareTrustline = prepareTrustline
|
||||
prepareOrder = prepareOrder
|
||||
prepareOrderCancellation = prepareOrderCancellation
|
||||
prepareEscrowCreation = prepareEscrowCreation
|
||||
prepareEscrowExecution = prepareEscrowExecution
|
||||
prepareEscrowCancellation = prepareEscrowCancellation
|
||||
preparePaymentChannelCreate = preparePaymentChannelCreate
|
||||
preparePaymentChannelFund = preparePaymentChannelFund
|
||||
preparePaymentChannelClaim = preparePaymentChannelClaim
|
||||
prepareCheckCreate = prepareCheckCreate
|
||||
prepareCheckCash = prepareCheckCash
|
||||
prepareCheckCancel = prepareCheckCancel
|
||||
prepareTicketCreate = prepareTicketCreate
|
||||
prepareSettings = prepareSettings
|
||||
sign = sign
|
||||
combine = combine
|
||||
preparePayment = preparePayment;
|
||||
prepareTrustline = prepareTrustline;
|
||||
prepareOrder = prepareOrder;
|
||||
prepareOrderCancellation = prepareOrderCancellation;
|
||||
prepareEscrowCreation = prepareEscrowCreation;
|
||||
prepareEscrowExecution = prepareEscrowExecution;
|
||||
prepareEscrowCancellation = prepareEscrowCancellation;
|
||||
preparePaymentChannelCreate = preparePaymentChannelCreate;
|
||||
preparePaymentChannelFund = preparePaymentChannelFund;
|
||||
preparePaymentChannelClaim = preparePaymentChannelClaim;
|
||||
prepareCheckCreate = prepareCheckCreate;
|
||||
prepareCheckCash = prepareCheckCash;
|
||||
prepareCheckCancel = prepareCheckCancel;
|
||||
prepareTicketCreate = prepareTicketCreate;
|
||||
prepareSettings = prepareSettings;
|
||||
sign = sign;
|
||||
combine = combine;
|
||||
|
||||
generateFaucetWallet = generateFaucetWallet
|
||||
generateFaucetWallet = generateFaucetWallet;
|
||||
|
||||
errors = errors
|
||||
errors = errors;
|
||||
|
||||
static deriveXAddress = deriveXAddress
|
||||
static deriveXAddress = deriveXAddress;
|
||||
|
||||
// Client.deriveClassicAddress (static) is a new name for client.deriveAddress
|
||||
static deriveClassicAddress = deriveAddress
|
||||
static deriveClassicAddress = deriveAddress;
|
||||
|
||||
static formatBidsAndAsks = formatBidsAndAsks
|
||||
static formatBidsAndAsks = formatBidsAndAsks;
|
||||
|
||||
/**
|
||||
* Static methods to expose ripple-address-codec methods
|
||||
* Static methods to expose ripple-address-codec methods.
|
||||
*/
|
||||
static classicAddressToXAddress = classicAddressToXAddress
|
||||
static xAddressToClassicAddress = xAddressToClassicAddress
|
||||
static isValidXAddress = isValidXAddress
|
||||
static isValidClassicAddress = isValidClassicAddress
|
||||
static encodeSeed = encodeSeed
|
||||
static decodeSeed = decodeSeed
|
||||
static encodeAccountID = encodeAccountID
|
||||
static decodeAccountID = decodeAccountID
|
||||
static encodeNodePublic = encodeNodePublic
|
||||
static decodeNodePublic = decodeNodePublic
|
||||
static encodeAccountPublic = encodeAccountPublic
|
||||
static decodeAccountPublic = decodeAccountPublic
|
||||
static encodeXAddress = encodeXAddress
|
||||
static decodeXAddress = decodeXAddress
|
||||
static classicAddressToXAddress = classicAddressToXAddress;
|
||||
static xAddressToClassicAddress = xAddressToClassicAddress;
|
||||
static isValidXAddress = isValidXAddress;
|
||||
static isValidClassicAddress = isValidClassicAddress;
|
||||
static encodeSeed = encodeSeed;
|
||||
static decodeSeed = decodeSeed;
|
||||
static encodeAccountID = encodeAccountID;
|
||||
static decodeAccountID = decodeAccountID;
|
||||
static encodeNodePublic = encodeNodePublic;
|
||||
static decodeNodePublic = decodeNodePublic;
|
||||
static encodeAccountPublic = encodeAccountPublic;
|
||||
static decodeAccountPublic = decodeAccountPublic;
|
||||
static encodeXAddress = encodeXAddress;
|
||||
static decodeXAddress = decodeXAddress;
|
||||
|
||||
txFlags = txFlags
|
||||
static txFlags = txFlags
|
||||
accountSetFlags = constants.AccountSetFlags
|
||||
static accountSetFlags = constants.AccountSetFlags
|
||||
txFlags = txFlags;
|
||||
static txFlags = txFlags;
|
||||
accountSetFlags = constants.AccountSetFlags;
|
||||
static accountSetFlags = constants.AccountSetFlags;
|
||||
|
||||
isValidAddress = schemaValidator.isValidAddress
|
||||
isValidSecret = schemaValidator.isValidSecret
|
||||
isValidAddress = schemaValidator.isValidAddress;
|
||||
isValidSecret = schemaValidator.isValidSecret;
|
||||
}
|
||||
|
||||
export {
|
||||
Client,
|
||||
Connection
|
||||
}
|
||||
export { Client, Connection };
|
||||
|
||||
@@ -1,63 +1,64 @@
|
||||
import * as _ from 'lodash'
|
||||
import * as assert from 'assert'
|
||||
import * as assert from "assert";
|
||||
|
||||
type Interval = [number, number]
|
||||
import * as _ from "lodash";
|
||||
|
||||
type Interval = [number, number];
|
||||
|
||||
function mergeIntervals(intervals: Interval[]): Interval[] {
|
||||
const stack: Interval[] = [[-Infinity, -Infinity]]
|
||||
const stack: Interval[] = [[-Infinity, -Infinity]];
|
||||
_.sortBy(intervals, (x) => x[0]).forEach((interval) => {
|
||||
const lastInterval: Interval = stack.pop()!
|
||||
const lastInterval: Interval = stack.pop()!;
|
||||
if (interval[0] <= lastInterval[1] + 1) {
|
||||
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])])
|
||||
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])]);
|
||||
} else {
|
||||
stack.push(lastInterval)
|
||||
stack.push(interval)
|
||||
stack.push(lastInterval);
|
||||
stack.push(interval);
|
||||
}
|
||||
})
|
||||
return stack.slice(1)
|
||||
});
|
||||
return stack.slice(1);
|
||||
}
|
||||
|
||||
class RangeSet {
|
||||
ranges: Array<[number, number]>
|
||||
ranges: Array<[number, number]> = [];
|
||||
|
||||
constructor() {
|
||||
this.reset()
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.ranges = []
|
||||
this.ranges = [];
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return this.ranges
|
||||
.map((range) => range[0].toString() + '-' + range[1].toString())
|
||||
.join(',')
|
||||
.map((range) => `${range[0].toString()}-${range[1].toString()}`)
|
||||
.join(",");
|
||||
}
|
||||
|
||||
addRange(start: number, end: number) {
|
||||
assert.ok(start <= end, `invalid range ${start} <= ${end}`)
|
||||
this.ranges = mergeIntervals(this.ranges.concat([[start, end]]))
|
||||
assert.ok(start <= end, `invalid range ${start} <= ${end}`);
|
||||
this.ranges = mergeIntervals(this.ranges.concat([[start, end]]));
|
||||
}
|
||||
|
||||
addValue(value: number) {
|
||||
this.addRange(value, value)
|
||||
this.addRange(value, value);
|
||||
}
|
||||
|
||||
parseAndAddRanges(rangesString: string) {
|
||||
const rangeStrings = rangesString.split(',')
|
||||
const rangeStrings = rangesString.split(",");
|
||||
rangeStrings.forEach((rangeString) => {
|
||||
const range = rangeString.split('-').map(Number)
|
||||
this.addRange(range[0], range.length === 1 ? range[0] : range[1])
|
||||
})
|
||||
const range = rangeString.split("-").map(Number);
|
||||
this.addRange(range[0], range.length === 1 ? range[0] : range[1]);
|
||||
});
|
||||
}
|
||||
|
||||
containsRange(start: number, end: number) {
|
||||
return this.ranges.some((range) => range[0] <= start && range[1] >= end)
|
||||
return this.ranges.some((range) => range[0] <= start && range[1] >= end);
|
||||
}
|
||||
|
||||
containsValue(value: number) {
|
||||
return this.containsRange(value, value)
|
||||
return this.containsRange(value, value);
|
||||
}
|
||||
}
|
||||
|
||||
export default RangeSet
|
||||
export default RangeSet;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import {EventEmitter} from 'events'
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
// Define the global WebSocket class found on the native browser
|
||||
declare class WebSocket {
|
||||
onclose?: Function
|
||||
onopen?: Function
|
||||
onerror?: Function
|
||||
onmessage?: Function
|
||||
readyState: number
|
||||
constructor(url: string)
|
||||
close()
|
||||
send(message: string)
|
||||
onclose?: Function;
|
||||
onopen?: Function;
|
||||
onerror?: Function;
|
||||
onmessage?: Function;
|
||||
readyState: number;
|
||||
constructor(url: string);
|
||||
close();
|
||||
send(message: string);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -17,46 +17,46 @@ declare class WebSocket {
|
||||
* same, as `ws` package provides.
|
||||
*/
|
||||
export default class WSWrapper extends EventEmitter {
|
||||
private _ws: WebSocket
|
||||
static CONNECTING = 0
|
||||
static OPEN = 1
|
||||
static CLOSING = 2
|
||||
static CLOSED = 3
|
||||
private readonly _ws: WebSocket;
|
||||
static CONNECTING = 0;
|
||||
static OPEN = 1;
|
||||
static CLOSING = 2;
|
||||
static CLOSED = 3;
|
||||
|
||||
constructor(url, _protocols: any, _websocketOptions: any) {
|
||||
super()
|
||||
this.setMaxListeners(Infinity)
|
||||
super();
|
||||
this.setMaxListeners(Infinity);
|
||||
|
||||
this._ws = new WebSocket(url)
|
||||
this._ws = new WebSocket(url);
|
||||
|
||||
this._ws.onclose = () => {
|
||||
this.emit('close')
|
||||
}
|
||||
this.emit("close");
|
||||
};
|
||||
|
||||
this._ws.onopen = () => {
|
||||
this.emit('open')
|
||||
}
|
||||
this.emit("open");
|
||||
};
|
||||
|
||||
this._ws.onerror = (error) => {
|
||||
this.emit('error', error)
|
||||
}
|
||||
this.emit("error", error);
|
||||
};
|
||||
|
||||
this._ws.onmessage = (message) => {
|
||||
this.emit('message', message.data)
|
||||
}
|
||||
this.emit("message", message.data);
|
||||
};
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.readyState === 1) {
|
||||
this._ws.close()
|
||||
this._ws.close();
|
||||
}
|
||||
}
|
||||
|
||||
send(message) {
|
||||
this._ws.send(message)
|
||||
this._ws.send(message);
|
||||
}
|
||||
|
||||
get readyState() {
|
||||
return this._ws.readyState
|
||||
return this._ws.readyState;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {txFlagIndices} from './txflags'
|
||||
import { txFlagIndices } from "./txflags";
|
||||
|
||||
// Ordering from https://developers.ripple.com/accountroot.html
|
||||
const accountRootFlags = {
|
||||
@@ -43,8 +43,8 @@ const accountRootFlags = {
|
||||
|
||||
// lsfRequireDestTag:
|
||||
// Require a DestinationTag for incoming payments.
|
||||
RequireDestTag: 0x00020000
|
||||
}
|
||||
RequireDestTag: 0x00020000,
|
||||
};
|
||||
|
||||
const AccountFlags = {
|
||||
passwordSpent: accountRootFlags.PasswordSpent,
|
||||
@@ -55,19 +55,19 @@ const AccountFlags = {
|
||||
disableMasterKey: accountRootFlags.DisableMaster,
|
||||
noFreeze: accountRootFlags.NoFreeze,
|
||||
globalFreeze: accountRootFlags.GlobalFreeze,
|
||||
defaultRipple: accountRootFlags.DefaultRipple
|
||||
}
|
||||
defaultRipple: accountRootFlags.DefaultRipple,
|
||||
};
|
||||
|
||||
export interface Settings {
|
||||
passwordSpent?: boolean
|
||||
requireDestinationTag?: boolean
|
||||
requireAuthorization?: boolean
|
||||
depositAuth?: boolean
|
||||
disallowIncomingXRP?: boolean
|
||||
disableMasterKey?: boolean
|
||||
noFreeze?: boolean
|
||||
globalFreeze?: boolean
|
||||
defaultRipple?: boolean
|
||||
passwordSpent?: boolean;
|
||||
requireDestinationTag?: boolean;
|
||||
requireAuthorization?: boolean;
|
||||
depositAuth?: boolean;
|
||||
disallowIncomingXRP?: boolean;
|
||||
disableMasterKey?: boolean;
|
||||
noFreeze?: boolean;
|
||||
globalFreeze?: boolean;
|
||||
defaultRipple?: boolean;
|
||||
}
|
||||
|
||||
const AccountSetFlags = {
|
||||
@@ -79,21 +79,21 @@ const AccountSetFlags = {
|
||||
enableTransactionIDTracking: txFlagIndices.AccountSet.asfAccountTxnID,
|
||||
noFreeze: txFlagIndices.AccountSet.asfNoFreeze,
|
||||
globalFreeze: txFlagIndices.AccountSet.asfGlobalFreeze,
|
||||
defaultRipple: txFlagIndices.AccountSet.asfDefaultRipple
|
||||
}
|
||||
defaultRipple: txFlagIndices.AccountSet.asfDefaultRipple,
|
||||
};
|
||||
|
||||
const AccountFields = {
|
||||
EmailHash: {
|
||||
name: 'emailHash',
|
||||
encoding: 'hex',
|
||||
name: "emailHash",
|
||||
encoding: "hex",
|
||||
length: 32,
|
||||
defaults: '00000000000000000000000000000000'
|
||||
defaults: "00000000000000000000000000000000",
|
||||
},
|
||||
WalletLocator: {name: 'walletLocator'},
|
||||
MessageKey: {name: 'messageKey'},
|
||||
Domain: {name: 'domain', encoding: 'hex'},
|
||||
TransferRate: {name: 'transferRate', defaults: 0, shift: 9},
|
||||
TickSize: {name: 'tickSize', defaults: 0}
|
||||
}
|
||||
WalletLocator: { name: "walletLocator" },
|
||||
MessageKey: { name: "messageKey" },
|
||||
Domain: { name: "domain", encoding: "hex" },
|
||||
TransferRate: { name: "transferRate", defaults: 0, shift: 9 },
|
||||
TickSize: { name: "tickSize", defaults: 0 },
|
||||
};
|
||||
|
||||
export {AccountFields, AccountSetFlags, AccountFlags}
|
||||
export { AccountFields, AccountSetFlags, AccountFlags };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
enum ECDSA {
|
||||
ed25519 = 'ed25519',
|
||||
secp256k1 = 'ecdsa-secp256k1',
|
||||
ed25519 = "ed25519",
|
||||
secp256k1 = "ecdsa-secp256k1",
|
||||
}
|
||||
|
||||
export default ECDSA
|
||||
export default ECDSA;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import {inspect} from 'util'
|
||||
import { inspect } from "util";
|
||||
|
||||
class RippleError extends Error {
|
||||
name: string
|
||||
message: string
|
||||
data?: any
|
||||
name: string;
|
||||
message: string;
|
||||
data?: any;
|
||||
|
||||
constructor(message = '', data?: any) {
|
||||
super(message)
|
||||
constructor(message = "", data?: any) {
|
||||
super(message);
|
||||
|
||||
this.name = this.constructor.name
|
||||
this.message = message
|
||||
this.data = data
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
let result = '[' + this.name + '(' + this.message
|
||||
let result = `[${this.name}(${this.message}`;
|
||||
if (this.data) {
|
||||
result += ', ' + inspect(this.data)
|
||||
result += `, ${inspect(this.data)}`;
|
||||
}
|
||||
result += ')]'
|
||||
return result
|
||||
result += ")]";
|
||||
return result;
|
||||
}
|
||||
|
||||
/* console.log in node uses util.inspect on object, and util.inspect allows
|
||||
us to customize its output:
|
||||
https://nodejs.org/api/util.html#util_custom_inspect_function_on_objects */
|
||||
// console.log in node uses util.inspect on object, and util.inspect allows
|
||||
// us to customize its output:
|
||||
// https://nodejs.org/api/util.html#util_custom_inspect_function_on_objects
|
||||
inspect() {
|
||||
return this.toString()
|
||||
return this.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,14 +56,14 @@ class ValidationError extends RippleError {}
|
||||
class XRPLFaucetError extends RippleError {}
|
||||
|
||||
class NotFoundError extends RippleError {
|
||||
constructor(message = 'Not found') {
|
||||
super(message)
|
||||
constructor(message = "Not found") {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
class MissingLedgerHistoryError extends RippleError {
|
||||
constructor(message?: string) {
|
||||
super(message || 'Server is missing ledger history in the specified range')
|
||||
super(message || "Server is missing ledger history in the specified range");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ class PendingLedgerVersionError extends RippleError {
|
||||
super(
|
||||
message ||
|
||||
"maxLedgerVersion is greater than server's most recent" +
|
||||
' validated ledger'
|
||||
)
|
||||
" validated ledger"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,5 +92,5 @@ export {
|
||||
PendingLedgerVersionError,
|
||||
MissingLedgerHistoryError,
|
||||
LedgerVersionError,
|
||||
XRPLFaucetError
|
||||
}
|
||||
XRPLFaucetError,
|
||||
};
|
||||
|
||||
@@ -1,29 +1,38 @@
|
||||
import _ from 'lodash'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {Client} from '..'
|
||||
import BigNumber from "bignumber.js";
|
||||
import _ from "lodash";
|
||||
|
||||
import { Client } from "..";
|
||||
|
||||
// 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: Client, cushion?: number): Promise<string> {
|
||||
if (cushion == null) {
|
||||
cushion = this._feeCushion
|
||||
cushion = this._feeCushion;
|
||||
}
|
||||
if (cushion == null) {
|
||||
cushion = 1.2
|
||||
cushion = 1.2;
|
||||
}
|
||||
|
||||
const serverInfo = (await this.request({command: "server_info"})).result.info
|
||||
const baseFeeXrp = new BigNumber(serverInfo.validated_ledger.base_fee_xrp)
|
||||
const serverInfo = (await this.request({ command: "server_info" })).result
|
||||
.info;
|
||||
|
||||
const baseFee = serverInfo.validated_ledger?.base_fee_xrp;
|
||||
|
||||
if (baseFee == null) {
|
||||
throw new Error("getFee: Could not get base_fee_xrp from server_info");
|
||||
}
|
||||
|
||||
const baseFeeXrp = new BigNumber(baseFee);
|
||||
if (serverInfo.load_factor == null) {
|
||||
// https://github.com/ripple/rippled/issues/3812#issuecomment-816871100
|
||||
serverInfo.load_factor = 1
|
||||
serverInfo.load_factor = 1;
|
||||
}
|
||||
let fee = baseFeeXrp.times(serverInfo.load_factor).times(cushion)
|
||||
let fee = baseFeeXrp.times(serverInfo.load_factor).times(cushion);
|
||||
|
||||
// Cap fee to `this._maxFeeXRP`
|
||||
fee = BigNumber.min(fee, 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 {getFee}
|
||||
export { getFee };
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
import * as constants from './constants'
|
||||
import * as errors from './errors'
|
||||
import * as validate from './validate'
|
||||
import {xAddressToClassicAddress, isValidXAddress} from 'ripple-address-codec'
|
||||
import {
|
||||
xAddressToClassicAddress,
|
||||
isValidXAddress,
|
||||
} from "ripple-address-codec";
|
||||
|
||||
import * as constants from "./constants";
|
||||
import * as errors from "./errors";
|
||||
import * as validate from "./validate";
|
||||
|
||||
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.'
|
||||
)
|
||||
"This command does not support the use of a tag. Use an address without a tag."
|
||||
);
|
||||
}
|
||||
|
||||
// For rippled requests that use an account, always use a classic address.
|
||||
return classicAddress
|
||||
} else {
|
||||
return account
|
||||
return classicAddress;
|
||||
}
|
||||
return account;
|
||||
}
|
||||
|
||||
export {constants, errors, validate}
|
||||
export {txFlags} from './txflags'
|
||||
export { constants, errors, validate };
|
||||
export { txFlags } from "./txflags";
|
||||
|
||||
@@ -1,134 +1,140 @@
|
||||
import _ from 'lodash'
|
||||
import * as assert from 'assert'
|
||||
const {Validator} = require('jsonschema')
|
||||
import {ValidationError} from './errors'
|
||||
import {isValidClassicAddress, isValidXAddress} from 'ripple-address-codec'
|
||||
import {isValidSecret} from '../utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
import _ from "lodash";
|
||||
import { isValidClassicAddress, isValidXAddress } from "ripple-address-codec";
|
||||
|
||||
import { isValidSecret } from "../utils";
|
||||
|
||||
import { ValidationError } from "./errors";
|
||||
|
||||
const { Validator } = require("jsonschema");
|
||||
|
||||
function loadSchemas() {
|
||||
// listed explicitly for webpack (instead of scanning schemas directory)
|
||||
const schemas = [
|
||||
require('./schemas/objects/tx-json.json'),
|
||||
require('./schemas/objects/transaction-type.json'),
|
||||
require('./schemas/objects/hash128.json'),
|
||||
require('./schemas/objects/hash256.json'),
|
||||
require('./schemas/objects/sequence.json'),
|
||||
require('./schemas/objects/ticket-sequence.json'),
|
||||
require('./schemas/objects/signature.json'),
|
||||
require('./schemas/objects/issue.json'),
|
||||
require('./schemas/objects/ledger-version.json'),
|
||||
require('./schemas/objects/max-adjustment.json'),
|
||||
require('./schemas/objects/memo.json'),
|
||||
require('./schemas/objects/memos.json'),
|
||||
require('./schemas/objects/public-key.json'),
|
||||
require('./schemas/objects/private-key.json'),
|
||||
require('./schemas/objects/uint32.json'),
|
||||
require('./schemas/objects/value.json'),
|
||||
require('./schemas/objects/source-adjustment.json'),
|
||||
require('./schemas/objects/destination-adjustment.json'),
|
||||
require('./schemas/objects/tag.json'),
|
||||
require('./schemas/objects/lax-amount.json'),
|
||||
require('./schemas/objects/lax-lax-amount.json'),
|
||||
require('./schemas/objects/min-adjustment.json'),
|
||||
require('./schemas/objects/source-exact-adjustment.json'),
|
||||
require('./schemas/objects/destination-exact-adjustment.json'),
|
||||
require('./schemas/objects/destination-address-tag.json'),
|
||||
require('./schemas/objects/transaction-hash.json'),
|
||||
require('./schemas/objects/address.json'),
|
||||
require('./schemas/objects/x-address.json'),
|
||||
require('./schemas/objects/classic-address.json'),
|
||||
require('./schemas/objects/adjustment.json'),
|
||||
require('./schemas/objects/quality.json'),
|
||||
require('./schemas/objects/amount.json'),
|
||||
require('./schemas/objects/amountbase.json'),
|
||||
require('./schemas/objects/balance.json'),
|
||||
require('./schemas/objects/blob.json'),
|
||||
require('./schemas/objects/currency.json'),
|
||||
require('./schemas/objects/signed-value.json'),
|
||||
require('./schemas/objects/orderbook.json'),
|
||||
require('./schemas/objects/instructions.json'),
|
||||
require('./schemas/objects/settings-plus-memos.json'),
|
||||
require('./schemas/specifications/settings.json'),
|
||||
require('./schemas/specifications/payment.json'),
|
||||
require('./schemas/specifications/get-payment.json'),
|
||||
require('./schemas/specifications/escrow-cancellation.json'),
|
||||
require('./schemas/specifications/order-cancellation.json'),
|
||||
require('./schemas/specifications/order.json'),
|
||||
require('./schemas/specifications/escrow-execution.json'),
|
||||
require('./schemas/specifications/escrow-creation.json'),
|
||||
require('./schemas/specifications/payment-channel-create.json'),
|
||||
require('./schemas/specifications/payment-channel-fund.json'),
|
||||
require('./schemas/specifications/payment-channel-claim.json'),
|
||||
require('./schemas/specifications/check-create.json'),
|
||||
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'),
|
||||
require('./schemas/output/get-account-objects.json'),
|
||||
require('./schemas/output/get-balances.json'),
|
||||
require('./schemas/output/get-balance-sheet.json'),
|
||||
require('./schemas/output/get-ledger.json'),
|
||||
require('./schemas/output/get-orderbook.json'),
|
||||
require('./schemas/output/get-orders.json'),
|
||||
require('./schemas/output/order-change.json'),
|
||||
require('./schemas/output/get-payment-channel.json'),
|
||||
require('./schemas/output/prepare.json'),
|
||||
require('./schemas/output/ledger-event.json'),
|
||||
require('./schemas/output/get-paths.json'),
|
||||
require('./schemas/output/get-server-info.json'),
|
||||
require('./schemas/output/get-settings.json'),
|
||||
require('./schemas/output/orderbook-orders.json'),
|
||||
require('./schemas/output/outcome.json'),
|
||||
require('./schemas/output/get-transaction.json'),
|
||||
require('./schemas/output/get-transactions.json'),
|
||||
require('./schemas/output/get-trustlines.json'),
|
||||
require('./schemas/output/sign-payment-channel-claim.json'),
|
||||
require('./schemas/output/verify-payment-channel-claim.json'),
|
||||
require('./schemas/input/get-balances.json'),
|
||||
require('./schemas/input/get-balance-sheet.json'),
|
||||
require('./schemas/input/get-ledger.json'),
|
||||
require('./schemas/input/get-orders.json'),
|
||||
require('./schemas/input/get-orderbook.json'),
|
||||
require('./schemas/input/get-paths.json'),
|
||||
require('./schemas/input/get-payment-channel.json'),
|
||||
require('./schemas/input/api-options.json'),
|
||||
require('./schemas/input/get-settings.json'),
|
||||
require('./schemas/input/get-account-info.json'),
|
||||
require('./schemas/input/get-account-objects.json'),
|
||||
require('./schemas/input/get-transaction.json'),
|
||||
require('./schemas/input/get-transactions.json'),
|
||||
require('./schemas/input/get-trustlines.json'),
|
||||
require('./schemas/input/prepare-payment.json'),
|
||||
require('./schemas/input/prepare-order.json'),
|
||||
require('./schemas/input/prepare-trustline.json'),
|
||||
require('./schemas/input/prepare-order-cancellation.json'),
|
||||
require('./schemas/input/prepare-settings.json'),
|
||||
require('./schemas/input/prepare-escrow-creation.json'),
|
||||
require('./schemas/input/prepare-escrow-cancellation.json'),
|
||||
require('./schemas/input/prepare-escrow-execution.json'),
|
||||
require('./schemas/input/prepare-payment-channel-create.json'),
|
||||
require('./schemas/input/prepare-payment-channel-fund.json'),
|
||||
require('./schemas/input/prepare-payment-channel-claim.json'),
|
||||
require('./schemas/input/prepare-check-create.json'),
|
||||
require('./schemas/input/prepare-check-cash.json'),
|
||||
require('./schemas/input/prepare-check-cancel.json'),
|
||||
require('./schemas/input/prepare-ticket-create.json'),
|
||||
require('./schemas/input/compute-ledger-hash.json'),
|
||||
require('./schemas/input/sign.json'),
|
||||
require('./schemas/input/submit.json'),
|
||||
require('./schemas/input/generate-address.json'),
|
||||
require('./schemas/input/sign-payment-channel-claim.json'),
|
||||
require('./schemas/input/verify-payment-channel-claim.json'),
|
||||
require('./schemas/input/combine.json')
|
||||
]
|
||||
const titles = schemas.map((schema) => schema.title)
|
||||
const duplicates = Object.keys(_.pickBy(_.countBy(titles), (count) => count > 1))
|
||||
assert.ok(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates)
|
||||
const validator = new Validator()
|
||||
require("./schemas/objects/tx-json.json"),
|
||||
require("./schemas/objects/transaction-type.json"),
|
||||
require("./schemas/objects/hash128.json"),
|
||||
require("./schemas/objects/hash256.json"),
|
||||
require("./schemas/objects/sequence.json"),
|
||||
require("./schemas/objects/ticket-sequence.json"),
|
||||
require("./schemas/objects/signature.json"),
|
||||
require("./schemas/objects/issue.json"),
|
||||
require("./schemas/objects/ledger-version.json"),
|
||||
require("./schemas/objects/max-adjustment.json"),
|
||||
require("./schemas/objects/memo.json"),
|
||||
require("./schemas/objects/memos.json"),
|
||||
require("./schemas/objects/public-key.json"),
|
||||
require("./schemas/objects/private-key.json"),
|
||||
require("./schemas/objects/uint32.json"),
|
||||
require("./schemas/objects/value.json"),
|
||||
require("./schemas/objects/source-adjustment.json"),
|
||||
require("./schemas/objects/destination-adjustment.json"),
|
||||
require("./schemas/objects/tag.json"),
|
||||
require("./schemas/objects/lax-amount.json"),
|
||||
require("./schemas/objects/lax-lax-amount.json"),
|
||||
require("./schemas/objects/min-adjustment.json"),
|
||||
require("./schemas/objects/source-exact-adjustment.json"),
|
||||
require("./schemas/objects/destination-exact-adjustment.json"),
|
||||
require("./schemas/objects/destination-address-tag.json"),
|
||||
require("./schemas/objects/transaction-hash.json"),
|
||||
require("./schemas/objects/address.json"),
|
||||
require("./schemas/objects/x-address.json"),
|
||||
require("./schemas/objects/classic-address.json"),
|
||||
require("./schemas/objects/adjustment.json"),
|
||||
require("./schemas/objects/quality.json"),
|
||||
require("./schemas/objects/amount.json"),
|
||||
require("./schemas/objects/amountbase.json"),
|
||||
require("./schemas/objects/balance.json"),
|
||||
require("./schemas/objects/blob.json"),
|
||||
require("./schemas/objects/currency.json"),
|
||||
require("./schemas/objects/signed-value.json"),
|
||||
require("./schemas/objects/orderbook.json"),
|
||||
require("./schemas/objects/instructions.json"),
|
||||
require("./schemas/objects/settings-plus-memos.json"),
|
||||
require("./schemas/specifications/settings.json"),
|
||||
require("./schemas/specifications/payment.json"),
|
||||
require("./schemas/specifications/get-payment.json"),
|
||||
require("./schemas/specifications/escrow-cancellation.json"),
|
||||
require("./schemas/specifications/order-cancellation.json"),
|
||||
require("./schemas/specifications/order.json"),
|
||||
require("./schemas/specifications/escrow-execution.json"),
|
||||
require("./schemas/specifications/escrow-creation.json"),
|
||||
require("./schemas/specifications/payment-channel-create.json"),
|
||||
require("./schemas/specifications/payment-channel-fund.json"),
|
||||
require("./schemas/specifications/payment-channel-claim.json"),
|
||||
require("./schemas/specifications/check-create.json"),
|
||||
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"),
|
||||
require("./schemas/output/get-account-objects.json"),
|
||||
require("./schemas/output/get-balances.json"),
|
||||
require("./schemas/output/get-balance-sheet.json"),
|
||||
require("./schemas/output/get-ledger.json"),
|
||||
require("./schemas/output/get-orderbook.json"),
|
||||
require("./schemas/output/get-orders.json"),
|
||||
require("./schemas/output/order-change.json"),
|
||||
require("./schemas/output/get-payment-channel.json"),
|
||||
require("./schemas/output/prepare.json"),
|
||||
require("./schemas/output/ledger-event.json"),
|
||||
require("./schemas/output/get-paths.json"),
|
||||
require("./schemas/output/get-server-info.json"),
|
||||
require("./schemas/output/get-settings.json"),
|
||||
require("./schemas/output/orderbook-orders.json"),
|
||||
require("./schemas/output/outcome.json"),
|
||||
require("./schemas/output/get-transaction.json"),
|
||||
require("./schemas/output/get-transactions.json"),
|
||||
require("./schemas/output/get-trustlines.json"),
|
||||
require("./schemas/output/sign-payment-channel-claim.json"),
|
||||
require("./schemas/output/verify-payment-channel-claim.json"),
|
||||
require("./schemas/input/get-balances.json"),
|
||||
require("./schemas/input/get-balance-sheet.json"),
|
||||
require("./schemas/input/get-ledger.json"),
|
||||
require("./schemas/input/get-orders.json"),
|
||||
require("./schemas/input/get-orderbook.json"),
|
||||
require("./schemas/input/get-paths.json"),
|
||||
require("./schemas/input/get-payment-channel.json"),
|
||||
require("./schemas/input/api-options.json"),
|
||||
require("./schemas/input/get-settings.json"),
|
||||
require("./schemas/input/get-account-info.json"),
|
||||
require("./schemas/input/get-account-objects.json"),
|
||||
require("./schemas/input/get-transaction.json"),
|
||||
require("./schemas/input/get-transactions.json"),
|
||||
require("./schemas/input/get-trustlines.json"),
|
||||
require("./schemas/input/prepare-payment.json"),
|
||||
require("./schemas/input/prepare-order.json"),
|
||||
require("./schemas/input/prepare-trustline.json"),
|
||||
require("./schemas/input/prepare-order-cancellation.json"),
|
||||
require("./schemas/input/prepare-settings.json"),
|
||||
require("./schemas/input/prepare-escrow-creation.json"),
|
||||
require("./schemas/input/prepare-escrow-cancellation.json"),
|
||||
require("./schemas/input/prepare-escrow-execution.json"),
|
||||
require("./schemas/input/prepare-payment-channel-create.json"),
|
||||
require("./schemas/input/prepare-payment-channel-fund.json"),
|
||||
require("./schemas/input/prepare-payment-channel-claim.json"),
|
||||
require("./schemas/input/prepare-check-create.json"),
|
||||
require("./schemas/input/prepare-check-cash.json"),
|
||||
require("./schemas/input/prepare-check-cancel.json"),
|
||||
require("./schemas/input/prepare-ticket-create.json"),
|
||||
require("./schemas/input/compute-ledger-hash.json"),
|
||||
require("./schemas/input/sign.json"),
|
||||
require("./schemas/input/submit.json"),
|
||||
require("./schemas/input/generate-address.json"),
|
||||
require("./schemas/input/sign-payment-channel-claim.json"),
|
||||
require("./schemas/input/verify-payment-channel-claim.json"),
|
||||
require("./schemas/input/combine.json"),
|
||||
];
|
||||
const titles = schemas.map((schema) => schema.title);
|
||||
const duplicates = Object.keys(
|
||||
_.pickBy(_.countBy(titles), (count) => count > 1)
|
||||
);
|
||||
assert.ok(duplicates.length === 0, `Duplicate schemas for: ${duplicates}`);
|
||||
const validator = new Validator();
|
||||
// Register custom format validators that ignore undefined instances
|
||||
// since jsonschema will still call the format validator on a missing
|
||||
// (optional) property
|
||||
@@ -136,49 +142,47 @@ function loadSchemas() {
|
||||
// This relies on "format": "xAddress" in `x-address.json`!
|
||||
validator.customFormats.xAddress = function (instance) {
|
||||
if (instance == null) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return isValidXAddress(instance)
|
||||
}
|
||||
return isValidXAddress(instance);
|
||||
};
|
||||
|
||||
// This relies on "format": "classicAddress" in `classic-address.json`!
|
||||
validator.customFormats.classicAddress = function (instance) {
|
||||
if (instance == null) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return isValidAddress(instance)
|
||||
}
|
||||
return isValidAddress(instance);
|
||||
};
|
||||
|
||||
validator.customFormats.secret = function (instance) {
|
||||
if (instance == null) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return isValidSecret(instance)
|
||||
}
|
||||
return isValidSecret(instance);
|
||||
};
|
||||
|
||||
// Register under the root URI '/'
|
||||
schemas.forEach((schema) =>
|
||||
validator.addSchema(schema, '/' + schema.title)
|
||||
)
|
||||
return validator
|
||||
schemas.forEach((schema) => validator.addSchema(schema, `/${schema.title}`));
|
||||
return validator;
|
||||
}
|
||||
|
||||
const schemaValidator = loadSchemas()
|
||||
const schemaValidator = loadSchemas();
|
||||
|
||||
function schemaValidate(schemaName: string, object: any): void {
|
||||
// Lookup under the root URI '/'
|
||||
const schema = schemaValidator.getSchema('/' + schemaName)
|
||||
const schema = schemaValidator.getSchema(`/${schemaName}`);
|
||||
if (schema == null) {
|
||||
throw new ValidationError('no schema for ' + schemaName)
|
||||
throw new ValidationError(`no schema for ${schemaName}`);
|
||||
}
|
||||
const result = schemaValidator.validate(object, schema)
|
||||
const result = schemaValidator.validate(object, schema);
|
||||
if (!result.valid) {
|
||||
throw new ValidationError(result.errors.join())
|
||||
throw new ValidationError(result.errors.join());
|
||||
}
|
||||
}
|
||||
|
||||
function isValidAddress(address: string): boolean {
|
||||
return isValidXAddress(address) || isValidClassicAddress(address)
|
||||
return isValidXAddress(address) || isValidClassicAddress(address);
|
||||
}
|
||||
|
||||
export {schemaValidate, isValidSecret, isValidAddress}
|
||||
export { schemaValidate, isValidSecret, isValidAddress };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const txFlags = {
|
||||
// Universal flags can apply to any transaction type
|
||||
Universal: {
|
||||
FullyCanonicalSig: 0x80000000
|
||||
FullyCanonicalSig: 0x80000000,
|
||||
},
|
||||
|
||||
AccountSet: {
|
||||
@@ -10,7 +10,7 @@ const txFlags = {
|
||||
RequireAuth: 0x00040000,
|
||||
OptionalAuth: 0x00080000,
|
||||
DisallowXRP: 0x00100000,
|
||||
AllowXRP: 0x00200000
|
||||
AllowXRP: 0x00200000,
|
||||
},
|
||||
|
||||
TrustSet: {
|
||||
@@ -19,27 +19,27 @@ const txFlags = {
|
||||
SetNoRipple: 0x00020000,
|
||||
ClearNoRipple: 0x00040000,
|
||||
SetFreeze: 0x00100000,
|
||||
ClearFreeze: 0x00200000
|
||||
ClearFreeze: 0x00200000,
|
||||
},
|
||||
|
||||
OfferCreate: {
|
||||
Passive: 0x00010000,
|
||||
ImmediateOrCancel: 0x00020000,
|
||||
FillOrKill: 0x00040000,
|
||||
Sell: 0x00080000
|
||||
Sell: 0x00080000,
|
||||
},
|
||||
|
||||
Payment: {
|
||||
NoRippleDirect: 0x00010000,
|
||||
PartialPayment: 0x00020000,
|
||||
LimitQuality: 0x00040000
|
||||
LimitQuality: 0x00040000,
|
||||
},
|
||||
|
||||
PaymentChannelClaim: {
|
||||
Renew: 0x00010000,
|
||||
Close: 0x00020000
|
||||
}
|
||||
}
|
||||
Close: 0x00020000,
|
||||
},
|
||||
};
|
||||
|
||||
// The following are integer (as opposed to bit) flags
|
||||
// that can be set for particular transactions in the
|
||||
@@ -54,8 +54,8 @@ const txFlagIndices = {
|
||||
asfNoFreeze: 6,
|
||||
asfGlobalFreeze: 7,
|
||||
asfDefaultRipple: 8,
|
||||
asfDepositAuth: 9
|
||||
}
|
||||
}
|
||||
asfDepositAuth: 9,
|
||||
},
|
||||
};
|
||||
|
||||
export {txFlags, txFlagIndices}
|
||||
export { txFlags, txFlagIndices };
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import {
|
||||
AccountRootLedgerEntry,
|
||||
SignerListLedgerEntry,
|
||||
QueueData
|
||||
} from '../objects'
|
||||
QueueData,
|
||||
} from "../objects";
|
||||
|
||||
export interface AccountInfoRequest {
|
||||
account: string
|
||||
strict?: boolean
|
||||
queue?: boolean
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
signer_lists?: boolean
|
||||
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
|
||||
validated?: boolean
|
||||
account_data: AccountRootLedgerEntry;
|
||||
signer_lists?: SignerListLedgerEntry[];
|
||||
ledger_current_index?: number;
|
||||
ledger_index?: number;
|
||||
queue_data?: QueueData;
|
||||
validated?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {Trustline} from '../objects'
|
||||
import { Trustline } from "../objects";
|
||||
|
||||
export interface AccountLinesRequest {
|
||||
account: string
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
peer?: string
|
||||
limit?: number
|
||||
marker?: any
|
||||
account: string;
|
||||
ledger_hash?: string;
|
||||
ledger_index?: number | ("validated" | "closed" | "current");
|
||||
peer?: string;
|
||||
limit?: number;
|
||||
marker?: any;
|
||||
}
|
||||
|
||||
export interface AccountLinesResponse {
|
||||
account: string
|
||||
lines: Trustline[]
|
||||
ledger_current_index?: number
|
||||
ledger_index?: number
|
||||
ledger_hash?: string
|
||||
marker?: any
|
||||
account: string;
|
||||
lines: Trustline[];
|
||||
ledger_current_index?: number;
|
||||
ledger_index?: number;
|
||||
ledger_hash?: string;
|
||||
marker?: any;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AccountObjectType } from '../../../models/common';
|
||||
import { AccountObjectType } from "../../../models/common";
|
||||
import {
|
||||
CheckLedgerEntry,
|
||||
RippleStateLedgerEntry,
|
||||
@@ -6,46 +6,46 @@ import {
|
||||
SignerListLedgerEntry,
|
||||
EscrowLedgerEntry,
|
||||
PayChannelLedgerEntry,
|
||||
DepositPreauthLedgerEntry
|
||||
} from '../objects'
|
||||
DepositPreauthLedgerEntry,
|
||||
} from "../objects";
|
||||
|
||||
export interface GetAccountObjectsOptions {
|
||||
type?: AccountObjectType
|
||||
ledgerHash?: string
|
||||
ledgerIndex?: number | ('validated' | 'closed' | 'current')
|
||||
limit?: number
|
||||
marker?: string
|
||||
type?: AccountObjectType;
|
||||
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'
|
||||
)
|
||||
| "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
|
||||
marker?: string;
|
||||
}
|
||||
|
||||
export interface AccountObjectsResponse {
|
||||
account: string
|
||||
account: string;
|
||||
|
||||
// Array of objects owned by this account.
|
||||
// from the getAccountObjects section of the dev center
|
||||
@@ -57,29 +57,29 @@ export interface AccountObjectsResponse {
|
||||
| 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.
|
||||
validated?: boolean
|
||||
validated?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import {RippledAmount} from '../objects'
|
||||
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
|
||||
expiration?: number
|
||||
seq: number;
|
||||
flags: number;
|
||||
taker_gets: RippledAmount;
|
||||
taker_pays: RippledAmount;
|
||||
quality: string;
|
||||
expiration?: number;
|
||||
}
|
||||
|
||||
@@ -1,26 +1,30 @@
|
||||
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
|
||||
marker?: any
|
||||
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
|
||||
marker?: any
|
||||
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
|
||||
taker_pays_funded?: RippledAmount
|
||||
quality?: string;
|
||||
owner_funds?: string;
|
||||
taker_gets_funded?: RippledAmount;
|
||||
taker_pays_funded?: RippledAmount;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {Amount} from '../objects'
|
||||
import { Amount } from "../objects";
|
||||
|
||||
export interface GatewayBalancesRequest {
|
||||
account: string
|
||||
strict?: boolean
|
||||
hotwallet: string | Array<string>
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
account: string;
|
||||
strict?: boolean;
|
||||
hotwallet: string | 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
|
||||
ledger_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;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export * from './account_info'
|
||||
export * from './account_lines'
|
||||
export * from './account_objects'
|
||||
export * from './account_offers'
|
||||
export * from './book_offers'
|
||||
export * from './gateway_balances'
|
||||
export * from './ledger'
|
||||
export * from './ledger_data'
|
||||
export * from './ledger_entry'
|
||||
export * from './server_info'
|
||||
export * from "./account_info";
|
||||
export * from "./account_lines";
|
||||
export * from "./account_objects";
|
||||
export * from "./account_offers";
|
||||
export * from "./book_offers";
|
||||
export * from "./gateway_balances";
|
||||
export * from "./ledger";
|
||||
export * from "./ledger_data";
|
||||
export * from "./ledger_entry";
|
||||
export * from "./server_info";
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import {Ledger, QueueData} from '../objects'
|
||||
import { Ledger, QueueData } from "../objects";
|
||||
|
||||
export interface LedgerRequest {
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
full?: boolean
|
||||
accounts?: boolean
|
||||
transactions?: boolean
|
||||
expand?: boolean
|
||||
owner_funds?: boolean
|
||||
binary?: boolean
|
||||
queue?: boolean
|
||||
ledger_hash?: string;
|
||||
ledger_index?: number | ("validated" | "closed" | "current");
|
||||
full?: boolean;
|
||||
accounts?: boolean;
|
||||
transactions?: boolean;
|
||||
expand?: boolean;
|
||||
owner_funds?: boolean;
|
||||
binary?: boolean;
|
||||
queue?: boolean;
|
||||
}
|
||||
|
||||
export interface LedgerResponse {
|
||||
ledger_index: number
|
||||
ledger_hash: string
|
||||
ledger: Ledger
|
||||
queue_data?: QueueData
|
||||
ledger_index: number;
|
||||
ledger_hash: string;
|
||||
ledger: Ledger;
|
||||
queue_data?: QueueData;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {LedgerData} from '../objects'
|
||||
import { LedgerData } from "../objects";
|
||||
|
||||
export interface LedgerDataRequest {
|
||||
id?: any
|
||||
ledger_hash?: string
|
||||
ledger_index?: string
|
||||
binary?: boolean
|
||||
limit?: number
|
||||
marker?: string
|
||||
id?: any;
|
||||
ledger_hash?: string;
|
||||
ledger_index?: string;
|
||||
binary?: boolean;
|
||||
limit?: number;
|
||||
marker?: string;
|
||||
}
|
||||
|
||||
export type LedgerDataResponse = LedgerData
|
||||
export type LedgerDataResponse = LedgerData;
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
import {LedgerEntry} from '../objects'
|
||||
import { LedgerEntry } from "../objects";
|
||||
|
||||
export interface LedgerEntryRequest {
|
||||
ledger_hash?: string
|
||||
ledger_index?: number | ('validated' | 'closed' | 'current')
|
||||
index?: string
|
||||
account_root?: string
|
||||
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;
|
||||
dir_root: string;
|
||||
}
|
||||
| {
|
||||
sub_index?: number
|
||||
owner: string
|
||||
}
|
||||
sub_index?: number;
|
||||
owner: string;
|
||||
};
|
||||
offer?:
|
||||
| string
|
||||
| {
|
||||
account: string
|
||||
seq: number
|
||||
}
|
||||
account: string;
|
||||
seq: number;
|
||||
};
|
||||
ripple_state?: {
|
||||
accounts: [string, string]
|
||||
currency: string
|
||||
}
|
||||
binary?: boolean
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
export interface ServerInfoRequest {
|
||||
id?: number
|
||||
id?: number;
|
||||
}
|
||||
|
||||
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
|
||||
proposers: number
|
||||
}
|
||||
converge_time_s: number;
|
||||
proposers: number;
|
||||
};
|
||||
load?: {
|
||||
job_types: {
|
||||
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
|
||||
validator_list_expires: string
|
||||
}
|
||||
job_types: Array<{
|
||||
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;
|
||||
validator_list_expires: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface LedgerInfo {
|
||||
age: number
|
||||
base_fee_xrp: number
|
||||
hash: string
|
||||
reserve_base_xrp: number
|
||||
reserve_inc_xrp: number
|
||||
seq: number
|
||||
age: number;
|
||||
base_fee_xrp: number;
|
||||
hash: string;
|
||||
reserve_base_xrp: number;
|
||||
reserve_inc_xrp: number;
|
||||
seq: number;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import {Amount} from './amounts'
|
||||
import { Amount } from "./amounts";
|
||||
|
||||
export type Adjustment = {
|
||||
address: string
|
||||
amount: Amount
|
||||
tag?: number
|
||||
export interface Adjustment {
|
||||
address: string;
|
||||
amount: Amount;
|
||||
tag?: number;
|
||||
}
|
||||
|
||||
export type MaxAdjustment = {
|
||||
address: string
|
||||
maxAmount: Amount
|
||||
tag?: number
|
||||
export interface MaxAdjustment {
|
||||
address: string;
|
||||
maxAmount: Amount;
|
||||
tag?: number;
|
||||
}
|
||||
|
||||
export type MinAdjustment = {
|
||||
address: string
|
||||
minAmount: Amount
|
||||
tag?: number
|
||||
export interface MinAdjustment {
|
||||
address: string;
|
||||
minAmount: Amount;
|
||||
tag?: number;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export interface Amount extends Issue {
|
||||
value: string
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type RippledAmount = string | Amount
|
||||
export type RippledAmount = string | Amount;
|
||||
|
||||
/**
|
||||
* Specification of which currency the account taking the offer would pay/
|
||||
@@ -10,15 +10,15 @@ export type RippledAmount = string | Amount
|
||||
* Similar to currency amounts.
|
||||
*/
|
||||
export interface TakerRequestAmount {
|
||||
currency: string
|
||||
issuer?: string
|
||||
currency: string;
|
||||
issuer?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A currency-counterparty pair, or just currency if it's XRP.
|
||||
*/
|
||||
export interface Issue {
|
||||
currency: string
|
||||
issuer?: string
|
||||
counterparty?: string
|
||||
currency: string;
|
||||
issuer?: string;
|
||||
counterparty?: string;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
export * from './adjustments'
|
||||
export * from './amounts'
|
||||
export * from './ledger'
|
||||
export * from './ledger_data'
|
||||
export * from './ledger_entries'
|
||||
export * from './memos'
|
||||
export * from './orders'
|
||||
export * from './queue_data'
|
||||
export * from './settings'
|
||||
export * from './signers'
|
||||
export * from './transactions'
|
||||
export * from './trustlines'
|
||||
export * from "./adjustments";
|
||||
export * from "./amounts";
|
||||
export * from "./ledger";
|
||||
export * from "./ledger_data";
|
||||
export * from "./ledger_entries";
|
||||
export * from "./memos";
|
||||
export * from "./orders";
|
||||
export * from "./queue_data";
|
||||
export * from "./settings";
|
||||
export * from "./signers";
|
||||
export * from "./transactions";
|
||||
export * from "./trustlines";
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
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[]
|
||||
close_flags?: number
|
||||
parent_close_time?: number
|
||||
accountState?: any[]
|
||||
validated?: boolean
|
||||
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[];
|
||||
close_flags?: number;
|
||||
parent_close_time?: number;
|
||||
accountState?: any[];
|
||||
validated?: boolean;
|
||||
}
|
||||
|
||||
// https://xrpl.org/subscribe.html#ledger-stream
|
||||
export type LedgerClosedEvent = {
|
||||
type: 'ledgerClosed'
|
||||
fee_base: number
|
||||
fee_ref: number
|
||||
ledger_hash: string
|
||||
ledger_index: number
|
||||
ledger_time: number
|
||||
reserve_base: number
|
||||
reserve_inc: number
|
||||
txn_count: number
|
||||
validated_ledgers: string
|
||||
export interface LedgerClosedEvent {
|
||||
type: "ledgerClosed";
|
||||
fee_base: number;
|
||||
fee_ref: number;
|
||||
ledger_hash: string;
|
||||
ledger_index: number;
|
||||
ledger_time: number;
|
||||
reserve_base: number;
|
||||
reserve_inc: number;
|
||||
txn_count: number;
|
||||
validated_ledgers: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export interface LedgerData {
|
||||
ledger_index: string
|
||||
ledger_hash: string
|
||||
marker: string
|
||||
state: ({data?: string; LedgerEntryType?: string; index: string} & any)[]
|
||||
ledger_index: string;
|
||||
ledger_hash: string;
|
||||
marker: string;
|
||||
state: Array<
|
||||
{ data?: string; LedgerEntryType?: string; index: string } & any
|
||||
>;
|
||||
}
|
||||
|
||||
@@ -1,175 +1,176 @@
|
||||
import {SignerEntry} from './index'
|
||||
import {Amount, RippledAmount} from './amounts'
|
||||
import { Amount, RippledAmount } from "./amounts";
|
||||
|
||||
import { SignerEntry } from "./index";
|
||||
|
||||
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
|
||||
MessageKey?: string
|
||||
RegularKey?: string
|
||||
TickSize?: number
|
||||
TransferRate?: number
|
||||
WalletLocator?: string
|
||||
WalletSize?: number // DEPRECATED
|
||||
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;
|
||||
WalletSize?: number; // DEPRECATED
|
||||
}
|
||||
|
||||
export interface AmendmentsLedgerEntry {
|
||||
LedgerEntryType: 'Amendments'
|
||||
Amendments?: string[]
|
||||
Majorities?: any[]
|
||||
Flags: 0
|
||||
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
|
||||
SourceTag: number
|
||||
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
|
||||
PreviousTxnLgrSeq: number
|
||||
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
|
||||
IndexPrevious?: 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
|
||||
ExchangeRate?: number // DEPRECATED
|
||||
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
|
||||
PreviousTxnLgrSeq: number
|
||||
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
|
||||
Flags: 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
|
||||
LastLedgerSequence?: number
|
||||
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
|
||||
Expiration?: 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
|
||||
index: string
|
||||
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
|
||||
HighQualityOut?: 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
|
||||
PreviousTxnLgrSeq: number
|
||||
LedgerEntryType: "SignerList";
|
||||
OwnerNode: string;
|
||||
SignerQuorum: number;
|
||||
SignerEntries: SignerEntry[];
|
||||
SignerListID: number;
|
||||
PreviousTxnID: string;
|
||||
PreviousTxnLgrSeq: number;
|
||||
}
|
||||
|
||||
// see https://ripple.com/build/ledger-format/#ledger-object-types
|
||||
@@ -187,4 +188,4 @@ export type LedgerEntry =
|
||||
| OfferLedgerEntry
|
||||
| PayChannelLedgerEntry
|
||||
| RippleStateLedgerEntry
|
||||
| SignerListLedgerEntry
|
||||
| SignerListLedgerEntry;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export type Memo = {
|
||||
type?: string
|
||||
format?: string
|
||||
data?: string
|
||||
export interface Memo {
|
||||
type?: string;
|
||||
format?: string;
|
||||
data?: string;
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import {Amount} from './amounts'
|
||||
import {Memo} from './memos'
|
||||
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[]
|
||||
export interface FormattedOrderSpecification {
|
||||
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.
|
||||
passive?: boolean
|
||||
passive?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
export interface QueueTransaction {
|
||||
auth_change: boolean
|
||||
fee: string
|
||||
fee_level: string
|
||||
max_spend_drops: string
|
||||
seq: number
|
||||
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
|
||||
transactions?: QueueTransaction[]
|
||||
txn_count: number;
|
||||
auth_change_queued?: boolean;
|
||||
lowest_sequence?: number;
|
||||
highest_sequence?: number;
|
||||
max_spend_drops_total?: string;
|
||||
transactions?: QueueTransaction[];
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import {Memo} from './memos'
|
||||
import { Memo } from "./memos";
|
||||
|
||||
export type WeightedSigner = {
|
||||
address: string
|
||||
weight: number
|
||||
export interface WeightedSigner {
|
||||
address: string;
|
||||
weight: number;
|
||||
}
|
||||
|
||||
export type Signers = {
|
||||
threshold?: number
|
||||
weights: WeightedSigner[]
|
||||
export interface Signers {
|
||||
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
|
||||
tickSize?: number
|
||||
export interface 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;
|
||||
tickSize?: number;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export interface SignerEntry {
|
||||
SignerEntry: {
|
||||
Account: string
|
||||
SignerWeight: number
|
||||
}
|
||||
Account: string;
|
||||
SignerWeight: number;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
import {RippledAmount} from './amounts'
|
||||
import {Memo} from './memos'
|
||||
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;
|
||||
}
|
||||
|
||||
export interface SignedTransaction {
|
||||
signedTransaction: string
|
||||
id: string
|
||||
signedTransaction: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
@@ -1,42 +1,42 @@
|
||||
import {Memo} from './memos'
|
||||
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
|
||||
memos?: Memo[]
|
||||
export interface FormattedTrustlineSpecification {
|
||||
currency: string;
|
||||
counterparty: string;
|
||||
limit: string;
|
||||
qualityIn?: number;
|
||||
qualityOut?: number;
|
||||
ripplingDisabled?: boolean;
|
||||
authorized?: boolean;
|
||||
frozen?: boolean;
|
||||
memos?: Memo[];
|
||||
}
|
||||
|
||||
export type FormattedTrustline = {
|
||||
specification: FormattedTrustlineSpecification
|
||||
export interface FormattedTrustline {
|
||||
specification: FormattedTrustlineSpecification;
|
||||
counterparty: {
|
||||
limit: string
|
||||
ripplingDisabled?: boolean
|
||||
frozen?: boolean
|
||||
authorized?: boolean
|
||||
}
|
||||
limit: string;
|
||||
ripplingDisabled?: boolean;
|
||||
frozen?: boolean;
|
||||
authorized?: boolean;
|
||||
};
|
||||
state: {
|
||||
balance: string
|
||||
}
|
||||
balance: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import _ from 'lodash'
|
||||
import {ValidationError} from './errors'
|
||||
import {schemaValidate} from './schema-validator'
|
||||
import _ from "lodash";
|
||||
|
||||
import { ValidationError } from "./errors";
|
||||
import { schemaValidate } from "./schema-validator";
|
||||
|
||||
function error(text) {
|
||||
return new ValidationError(text)
|
||||
return new ValidationError(text);
|
||||
}
|
||||
|
||||
function validateLedgerRange(options) {
|
||||
@@ -13,158 +14,161 @@ function validateLedgerRange(options) {
|
||||
options.maxLedgerVersion != null
|
||||
) {
|
||||
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
|
||||
throw error('minLedgerVersion must not be greater than maxLedgerVersion')
|
||||
throw error("minLedgerVersion must not be greater than maxLedgerVersion");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateOptions(schema, instance) {
|
||||
schemaValidate(schema, instance)
|
||||
validateLedgerRange(instance.options)
|
||||
schemaValidate(schema, instance);
|
||||
validateLedgerRange(instance.options);
|
||||
}
|
||||
|
||||
export const getPaths = _.partial(schemaValidate, 'getPathsParameters')
|
||||
export const getPaths = _.partial(schemaValidate, "getPathsParameters");
|
||||
|
||||
export const getTransactions = _.partial(
|
||||
validateOptions,
|
||||
'getTransactionsParameters'
|
||||
)
|
||||
"getTransactionsParameters"
|
||||
);
|
||||
|
||||
export const getSettings = _.partial(validateOptions, 'getSettingsParameters')
|
||||
export const getSettings = _.partial(validateOptions, "getSettingsParameters");
|
||||
|
||||
export const getAccountInfo = _.partial(
|
||||
validateOptions,
|
||||
'getAccountInfoParameters'
|
||||
)
|
||||
"getAccountInfoParameters"
|
||||
);
|
||||
|
||||
export const getTrustlines = _.partial(
|
||||
validateOptions,
|
||||
'getTrustlinesParameters'
|
||||
)
|
||||
"getTrustlinesParameters"
|
||||
);
|
||||
|
||||
export const getBalances = _.partial(validateOptions, 'getBalancesParameters')
|
||||
export const getBalances = _.partial(validateOptions, "getBalancesParameters");
|
||||
|
||||
export const getBalanceSheet = _.partial(
|
||||
validateOptions,
|
||||
'getBalanceSheetParameters'
|
||||
)
|
||||
"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'
|
||||
)
|
||||
"getTransactionParameters"
|
||||
);
|
||||
|
||||
export const getPaymentChannel = _.partial(
|
||||
validateOptions,
|
||||
'getPaymentChannelParameters'
|
||||
)
|
||||
"getPaymentChannelParameters"
|
||||
);
|
||||
|
||||
export const getLedger = _.partial(validateOptions, 'getLedgerParameters')
|
||||
export const getLedger = _.partial(validateOptions, "getLedgerParameters");
|
||||
|
||||
export const preparePayment = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentParameters'
|
||||
)
|
||||
"preparePaymentParameters"
|
||||
);
|
||||
|
||||
export const prepareOrder = _.partial(schemaValidate, 'prepareOrderParameters')
|
||||
export const prepareOrder = _.partial(schemaValidate, "prepareOrderParameters");
|
||||
|
||||
export const prepareOrderCancellation = _.partial(
|
||||
schemaValidate,
|
||||
'prepareOrderCancellationParameters'
|
||||
)
|
||||
"prepareOrderCancellationParameters"
|
||||
);
|
||||
|
||||
export const prepareTrustline = _.partial(
|
||||
schemaValidate,
|
||||
'prepareTrustlineParameters'
|
||||
)
|
||||
"prepareTrustlineParameters"
|
||||
);
|
||||
|
||||
export const prepareSettings = _.partial(
|
||||
schemaValidate,
|
||||
'prepareSettingsParameters'
|
||||
)
|
||||
"prepareSettingsParameters"
|
||||
);
|
||||
|
||||
export const prepareEscrowCreation = _.partial(
|
||||
schemaValidate,
|
||||
'prepareEscrowCreationParameters'
|
||||
)
|
||||
"prepareEscrowCreationParameters"
|
||||
);
|
||||
|
||||
export const prepareEscrowCancellation = _.partial(
|
||||
schemaValidate,
|
||||
'prepareEscrowCancellationParameters'
|
||||
)
|
||||
"prepareEscrowCancellationParameters"
|
||||
);
|
||||
|
||||
export const prepareEscrowExecution = _.partial(
|
||||
schemaValidate,
|
||||
'prepareEscrowExecutionParameters'
|
||||
)
|
||||
"prepareEscrowExecutionParameters"
|
||||
);
|
||||
|
||||
export const preparePaymentChannelCreate = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentChannelCreateParameters'
|
||||
)
|
||||
"preparePaymentChannelCreateParameters"
|
||||
);
|
||||
|
||||
export const preparePaymentChannelFund = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentChannelFundParameters'
|
||||
)
|
||||
"preparePaymentChannelFundParameters"
|
||||
);
|
||||
|
||||
export const preparePaymentChannelClaim = _.partial(
|
||||
schemaValidate,
|
||||
'preparePaymentChannelClaimParameters'
|
||||
)
|
||||
"preparePaymentChannelClaimParameters"
|
||||
);
|
||||
|
||||
export const prepareCheckCreate = _.partial(
|
||||
schemaValidate,
|
||||
'prepareCheckCreateParameters'
|
||||
)
|
||||
"prepareCheckCreateParameters"
|
||||
);
|
||||
|
||||
export const prepareCheckCash = _.partial(
|
||||
schemaValidate,
|
||||
'prepareCheckCashParameters'
|
||||
)
|
||||
"prepareCheckCashParameters"
|
||||
);
|
||||
|
||||
export const prepareCheckCancel = _.partial(
|
||||
schemaValidate,
|
||||
'prepareCheckCancelParameters'
|
||||
)
|
||||
"prepareCheckCancelParameters"
|
||||
);
|
||||
|
||||
export const prepareTicketCreate = _.partial(
|
||||
schemaValidate,
|
||||
'prepareTicketParameters'
|
||||
)
|
||||
"prepareTicketParameters"
|
||||
);
|
||||
|
||||
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'
|
||||
)
|
||||
"computeLedgerHashParameters"
|
||||
);
|
||||
|
||||
export const generateAddress = _.partial(
|
||||
schemaValidate,
|
||||
'generateAddressParameters'
|
||||
)
|
||||
"generateAddressParameters"
|
||||
);
|
||||
|
||||
export const signPaymentChannelClaim = _.partial(
|
||||
schemaValidate,
|
||||
'signPaymentChannelClaimParameters'
|
||||
)
|
||||
"signPaymentChannelClaimParameters"
|
||||
);
|
||||
|
||||
export const verifyPaymentChannelClaim = _.partial(
|
||||
schemaValidate,
|
||||
'verifyPaymentChannelClaimParameters'
|
||||
)
|
||||
"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");
|
||||
|
||||
14
src/index.ts
14
src/index.ts
@@ -1,14 +1,14 @@
|
||||
export {Client} from './client'
|
||||
export { Client } from "./client";
|
||||
|
||||
export * from './transaction/types'
|
||||
export * from "./transaction/types";
|
||||
|
||||
export * from './common/types/objects/ledger'
|
||||
export * from "./common/types/objects/ledger";
|
||||
|
||||
export * from './models/methods'
|
||||
export * from "./models/methods";
|
||||
|
||||
export * from './utils'
|
||||
export * from "./utils";
|
||||
|
||||
// Broadcast client is experimental
|
||||
export {BroadcastClient} from './client/broadcastClient'
|
||||
export { BroadcastClient } from "./client/broadcastClient";
|
||||
|
||||
export * from './Wallet'
|
||||
export * from "./Wallet";
|
||||
|
||||
@@ -1,42 +1,46 @@
|
||||
import * as utils from './utils'
|
||||
import {validate, ensureClassicAddress} from '../common'
|
||||
import {Connection} from '../client'
|
||||
import {GetTrustlinesOptions} from './trustlines'
|
||||
import {FormattedTrustline} from '../common/types/objects/trustlines'
|
||||
import {Client} from '..'
|
||||
import { Client } from "..";
|
||||
import { Connection } from "../client";
|
||||
import { validate, ensureClassicAddress } from "../common";
|
||||
import { FormattedTrustline } from "../common/types/objects/trustlines";
|
||||
|
||||
export type Balance = {
|
||||
value: string
|
||||
currency: string
|
||||
counterparty?: string
|
||||
import { GetTrustlinesOptions } from "./trustlines";
|
||||
import * as utils from "./utils";
|
||||
|
||||
export interface Balance {
|
||||
value: string;
|
||||
currency: string;
|
||||
counterparty?: string;
|
||||
}
|
||||
|
||||
export type GetBalances = Array<Balance>
|
||||
export type GetBalances = Balance[];
|
||||
|
||||
function getTrustlineBalanceAmount(trustline: FormattedTrustline): Balance {
|
||||
return {
|
||||
currency: trustline.specification.currency,
|
||||
counterparty: trustline.specification.counterparty,
|
||||
value: trustline.state.balance
|
||||
}
|
||||
value: trustline.state.balance,
|
||||
};
|
||||
}
|
||||
|
||||
function formatBalances(options: GetTrustlinesOptions, balances: {xrp: string, trustlines: FormattedTrustline[]}) {
|
||||
const result = balances.trustlines.map(getTrustlineBalanceAmount)
|
||||
function formatBalances(
|
||||
options: GetTrustlinesOptions,
|
||||
balances: { xrp: string; trustlines: FormattedTrustline[] }
|
||||
) {
|
||||
const result = balances.trustlines.map(getTrustlineBalanceAmount);
|
||||
if (
|
||||
!(options.counterparty || (options.currency && options.currency !== 'XRP'))
|
||||
!(options.counterparty || (options.currency && options.currency !== "XRP"))
|
||||
) {
|
||||
const xrpBalance = {
|
||||
currency: 'XRP',
|
||||
value: balances.xrp
|
||||
}
|
||||
result.unshift(xrpBalance)
|
||||
currency: "XRP",
|
||||
value: balances.xrp,
|
||||
};
|
||||
result.unshift(xrpBalance);
|
||||
}
|
||||
if (options.limit && result.length > options.limit) {
|
||||
const toRemove = result.length - options.limit
|
||||
result.splice(-toRemove, toRemove)
|
||||
const toRemove = result.length - options.limit;
|
||||
result.splice(-toRemove, toRemove);
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
function getLedgerVersionHelper(
|
||||
@@ -44,12 +48,14 @@ function getLedgerVersionHelper(
|
||||
optionValue?: number
|
||||
): Promise<number> {
|
||||
if (optionValue != null && optionValue !== null) {
|
||||
return Promise.resolve(optionValue)
|
||||
return Promise.resolve(optionValue);
|
||||
}
|
||||
return connection.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated'
|
||||
}).then(response => response.result.ledger_index);
|
||||
return connection
|
||||
.request({
|
||||
command: "ledger",
|
||||
ledger_index: "validated",
|
||||
})
|
||||
.then((response) => response.result.ledger_index);
|
||||
}
|
||||
|
||||
function getBalances(
|
||||
@@ -57,26 +63,23 @@ function getBalances(
|
||||
address: string,
|
||||
options: GetTrustlinesOptions = {}
|
||||
): Promise<GetBalances> {
|
||||
validate.getTrustlines({address, options})
|
||||
validate.getTrustlines({ address, options });
|
||||
|
||||
// Only support retrieving balances without a tag,
|
||||
// since we currently do not calculate balances
|
||||
// on a per-tag basis. Apps must interpret and
|
||||
// use tags independent of the XRP Ledger, comparing
|
||||
// with the XRP Ledger's balance as an accounting check.
|
||||
address = ensureClassicAddress(address)
|
||||
address = ensureClassicAddress(address);
|
||||
|
||||
return Promise.all([
|
||||
getLedgerVersionHelper(
|
||||
this.connection,
|
||||
options.ledgerVersion
|
||||
).then((ledgerVersion) =>
|
||||
utils.getXRPBalance(this, address, ledgerVersion)
|
||||
getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
|
||||
(ledgerVersion) => utils.getXRPBalance(this, address, ledgerVersion)
|
||||
),
|
||||
this.getTrustlines(address, options)
|
||||
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
|
||||
export default getBalances;
|
||||
|
||||
@@ -1,45 +1,46 @@
|
||||
import _ from 'lodash'
|
||||
import * as utils from './utils'
|
||||
import BigNumber from "bignumber.js";
|
||||
import _ from "lodash";
|
||||
|
||||
import type { Client } from "../client";
|
||||
import type { BookOffer } from "../common/types/commands";
|
||||
import type { Issue } from "../common/types/objects";
|
||||
|
||||
import {
|
||||
parseOrderbookOrder,
|
||||
FormattedOrderbookOrder
|
||||
} from './parse/orderbook-order'
|
||||
import {validate} from '../common'
|
||||
import {Issue} from '../common/types/objects'
|
||||
import {BookOffer} from '../common/types/commands'
|
||||
import {Client} from '..'
|
||||
import BigNumber from 'bignumber.js'
|
||||
FormattedOrderbookOrder,
|
||||
} from "./parse/orderbook-order";
|
||||
import * as utils from "./utils";
|
||||
|
||||
export type FormattedOrderbook = {
|
||||
bids: FormattedOrderbookOrder[]
|
||||
asks: FormattedOrderbookOrder[]
|
||||
export interface FormattedOrderbook {
|
||||
bids: FormattedOrderbookOrder[];
|
||||
asks: FormattedOrderbookOrder[];
|
||||
}
|
||||
|
||||
function isSameIssue(a: Issue, b: Issue) {
|
||||
return a.currency === b.currency && a.counterparty === b.counterparty
|
||||
return a.currency === b.currency && a.counterparty === b.counterparty;
|
||||
}
|
||||
|
||||
function directionFilter(direction: string, order: FormattedOrderbookOrder) {
|
||||
return order.specification.direction === direction
|
||||
return order.specification.direction === direction;
|
||||
}
|
||||
|
||||
function flipOrder(order: FormattedOrderbookOrder) {
|
||||
const specification = order.specification
|
||||
const specification = order.specification;
|
||||
const flippedSpecification = {
|
||||
quantity: specification.totalPrice,
|
||||
totalPrice: specification.quantity,
|
||||
direction: specification.direction === 'buy' ? 'sell' : 'buy'
|
||||
}
|
||||
const newSpecification = _.merge({}, specification, flippedSpecification)
|
||||
return _.merge({}, order, {specification: newSpecification})
|
||||
direction: specification.direction === "buy" ? "sell" : "buy",
|
||||
};
|
||||
const newSpecification = _.merge({}, specification, flippedSpecification);
|
||||
return _.merge({}, order, { specification: newSpecification });
|
||||
}
|
||||
|
||||
function alignOrder(
|
||||
base: Issue,
|
||||
order: FormattedOrderbookOrder
|
||||
): FormattedOrderbookOrder {
|
||||
const quantity = order.specification.quantity
|
||||
return isSameIssue(quantity, base) ? order : flipOrder(order)
|
||||
const quantity = order.specification.quantity;
|
||||
return isSameIssue(quantity, base) ? order : flipOrder(order);
|
||||
}
|
||||
|
||||
export function formatBidsAndAsks(
|
||||
@@ -58,14 +59,17 @@ export function formatBidsAndAsks(
|
||||
// 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 qualityA = a.quality ?? 0;
|
||||
const qualityB = b.quality ?? 0;
|
||||
|
||||
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base))
|
||||
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'))
|
||||
const asks = alignedOrders.filter(_.partial(directionFilter, 'sell'))
|
||||
return {bids, asks}
|
||||
return new BigNumber(qualityA).comparedTo(qualityB);
|
||||
})
|
||||
.map(parseOrderbookOrder);
|
||||
|
||||
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base));
|
||||
const bids = alignedOrders.filter(_.partial(directionFilter, "buy"));
|
||||
const asks = alignedOrders.filter(_.partial(directionFilter, "sell"));
|
||||
return { bids, asks };
|
||||
}
|
||||
|
||||
// account is to specify a "perspective", which affects which unfunded offers
|
||||
@@ -79,25 +83,26 @@ async function makeRequest(
|
||||
) {
|
||||
const orderData = utils.renameCounterpartyToIssuerInOrder({
|
||||
taker_gets: takerGets,
|
||||
taker_pays: takerPays
|
||||
})
|
||||
return client.requestAll({command: 'book_offers',
|
||||
taker_pays: takerPays,
|
||||
});
|
||||
return client.requestAll({
|
||||
command: "book_offers",
|
||||
taker_gets: orderData.taker_gets,
|
||||
taker_pays: orderData.taker_pays,
|
||||
ledger_index: options.ledgerVersion || 'validated',
|
||||
ledger_index: options.ledgerVersion || "validated",
|
||||
limit: options.limit,
|
||||
taker
|
||||
})
|
||||
taker,
|
||||
});
|
||||
}
|
||||
|
||||
export type GetOrderbookOptions = {
|
||||
limit?: number
|
||||
ledgerVersion?: number
|
||||
export interface GetOrderbookOptions {
|
||||
limit?: number;
|
||||
ledgerVersion?: number;
|
||||
}
|
||||
|
||||
export type OrderbookInfo = {
|
||||
base: Issue
|
||||
counter: Issue
|
||||
export interface OrderbookInfo {
|
||||
base: Issue;
|
||||
counter: Issue;
|
||||
}
|
||||
|
||||
export async function getOrderbook(
|
||||
@@ -106,21 +111,19 @@ export async function getOrderbook(
|
||||
orderbook: OrderbookInfo,
|
||||
options: GetOrderbookOptions = {}
|
||||
): Promise<FormattedOrderbook> {
|
||||
// 1. Validate
|
||||
validate.getOrderbook({address, orderbook, options})
|
||||
// 2. Make Request
|
||||
const [directOfferResults, reverseOfferResults] = await Promise.all([
|
||||
makeRequest(this, address, options, orderbook.base, orderbook.counter),
|
||||
makeRequest(this, address, options, orderbook.counter, orderbook.base)
|
||||
])
|
||||
makeRequest(this, address, options, orderbook.counter, orderbook.base),
|
||||
]);
|
||||
// 3. Return Formatted Response
|
||||
const directOffers = _.flatMap(
|
||||
directOfferResults,
|
||||
(directOfferResult) => directOfferResult.result.offers
|
||||
)
|
||||
);
|
||||
const reverseOffers = _.flatMap(
|
||||
reverseOfferResults,
|
||||
(reverseOfferResult) => reverseOfferResult.result.offers
|
||||
)
|
||||
return formatBidsAndAsks(orderbook, [...directOffers, ...reverseOffers])
|
||||
);
|
||||
return formatBidsAndAsks(orderbook, [...directOffers, ...reverseOffers]);
|
||||
}
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {classicAddressToXAddress} from 'ripple-address-codec'
|
||||
import {parseMemos} from './utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
export type FormattedAccountDelete = {
|
||||
import { classicAddressToXAddress } from "ripple-address-codec";
|
||||
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
export interface 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
|
||||
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
|
||||
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
|
||||
destinationXAddress: string;
|
||||
}
|
||||
|
||||
function parseAccountDelete(tx: any): FormattedAccountDelete {
|
||||
assert.ok(tx.TransactionType === 'AccountDelete')
|
||||
assert.ok(tx.TransactionType === "AccountDelete");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
@@ -29,8 +32,8 @@ function parseAccountDelete(tx: any): FormattedAccountDelete {
|
||||
tx.Destination,
|
||||
tx.DestinationTag == null ? false : tx.DestinationTag,
|
||||
false
|
||||
)
|
||||
})
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
export default parseAccountDelete
|
||||
export default parseAccountDelete;
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import parseAmount from './amount'
|
||||
import {parseTimestamp, adjustQualityForXRP} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {orderFlags} from './flags'
|
||||
import {FormattedOrderSpecification} from '../../common/types/objects'
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
export type FormattedAccountOrder = {
|
||||
specification: FormattedOrderSpecification
|
||||
import { FormattedOrderSpecification } from "../../common/types/objects";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import { orderFlags } from "./flags";
|
||||
import { parseTimestamp, adjustQualityForXRP } from "./utils";
|
||||
|
||||
export interface FormattedAccountOrder {
|
||||
specification: FormattedOrderSpecification;
|
||||
properties: {
|
||||
maker: string
|
||||
sequence: number
|
||||
makerExchangeRate: string
|
||||
}
|
||||
maker: string;
|
||||
sequence: number;
|
||||
makerExchangeRate: string;
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: remove this function once rippled provides quality directly
|
||||
function computeQuality(takerGets, takerPays) {
|
||||
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value)
|
||||
return quotient.precision(16, BigNumber.ROUND_HALF_UP).toString()
|
||||
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value);
|
||||
return quotient.precision(16, BigNumber.ROUND_HALF_UP).toString();
|
||||
}
|
||||
|
||||
// rippled 'account_offers' returns a different format for orders than 'tx'
|
||||
@@ -26,22 +28,22 @@ export function parseAccountOrder(
|
||||
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 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;
|
||||
|
||||
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
|
||||
// so we can omit those flags here
|
||||
const specification = removeUndefined({
|
||||
direction: direction,
|
||||
quantity: quantity,
|
||||
totalPrice: totalPrice,
|
||||
direction,
|
||||
quantity,
|
||||
totalPrice,
|
||||
passive: (order.flags & orderFlags.Passive) !== 0 || undefined,
|
||||
// rippled currently does not provide "expiration" in account_offers
|
||||
expirationTime: parseTimestamp(order.expiration)
|
||||
})
|
||||
expirationTime: parseTimestamp(order.expiration),
|
||||
});
|
||||
|
||||
const makerExchangeRate = order.quality
|
||||
? adjustQualityForXRP(
|
||||
@@ -49,12 +51,12 @@ export function parseAccountOrder(
|
||||
takerGetsAmount.currency,
|
||||
takerPaysAmount.currency
|
||||
)
|
||||
: computeQuality(takerGetsAmount, takerPaysAmount)
|
||||
: computeQuality(takerGetsAmount, takerPaysAmount);
|
||||
const properties = {
|
||||
maker: address,
|
||||
sequence: order.seq,
|
||||
makerExchangeRate: makerExchangeRate
|
||||
}
|
||||
makerExchangeRate,
|
||||
};
|
||||
|
||||
return {specification, properties}
|
||||
return { specification, properties };
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import {parseQuality} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {
|
||||
Trustline,
|
||||
FormattedTrustline
|
||||
} from '../../common/types/objects/trustlines'
|
||||
FormattedTrustline,
|
||||
} from "../../common/types/objects/trustlines";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseQuality } from "./utils";
|
||||
|
||||
// rippled 'account_lines' returns a different format for
|
||||
// trustlines than 'tx'
|
||||
@@ -16,19 +17,19 @@ function parseAccountTrustline(trustline: Trustline): FormattedTrustline {
|
||||
qualityOut: parseQuality(trustline.quality_out) || undefined,
|
||||
ripplingDisabled: trustline.no_ripple,
|
||||
frozen: trustline.freeze,
|
||||
authorized: trustline.authorized
|
||||
})
|
||||
authorized: trustline.authorized,
|
||||
});
|
||||
// rippled doesn't provide the counterparty's qualities
|
||||
const counterparty = removeUndefined({
|
||||
limit: trustline.limit_peer,
|
||||
ripplingDisabled: trustline.no_ripple_peer,
|
||||
frozen: trustline.freeze_peer,
|
||||
authorized: trustline.peer_authorized
|
||||
})
|
||||
authorized: trustline.peer_authorized,
|
||||
});
|
||||
const state = {
|
||||
balance: trustline.balance
|
||||
}
|
||||
return {specification, counterparty, state}
|
||||
balance: trustline.balance,
|
||||
};
|
||||
return { specification, counterparty, state };
|
||||
}
|
||||
|
||||
export default parseAccountTrustline
|
||||
export default parseAccountTrustline;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
function parseAmendment(tx: any) {
|
||||
return {
|
||||
amendment: tx.Amendment
|
||||
}
|
||||
amendment: tx.Amendment,
|
||||
};
|
||||
}
|
||||
|
||||
export default parseAmendment
|
||||
export default parseAmendment;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import {Amount, RippledAmount} from '../../common/types/objects'
|
||||
import {dropsToXrp} from '../../utils'
|
||||
import { Amount, RippledAmount } from "../../common/types/objects";
|
||||
import { dropsToXrp } from "../../utils";
|
||||
|
||||
function parseAmount(amount: RippledAmount): Amount {
|
||||
if (typeof amount === 'string') {
|
||||
if (typeof amount === "string") {
|
||||
return {
|
||||
currency: 'XRP',
|
||||
value: dropsToXrp(amount)
|
||||
}
|
||||
currency: "XRP",
|
||||
value: dropsToXrp(amount),
|
||||
};
|
||||
}
|
||||
return {
|
||||
currency: amount.currency,
|
||||
value: amount.value,
|
||||
counterparty: amount.issuer
|
||||
}
|
||||
counterparty: amount.issuer,
|
||||
};
|
||||
}
|
||||
|
||||
export default parseAmount
|
||||
export default parseAmount;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseMemos} from './utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
function parseOrderCancellation(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'OfferCancel')
|
||||
assert.ok(tx.TransactionType === "OfferCancel");
|
||||
return {
|
||||
memos: parseMemos(tx),
|
||||
orderSequence: tx.OfferSequence
|
||||
}
|
||||
orderSequence: tx.OfferSequence,
|
||||
};
|
||||
}
|
||||
|
||||
export default parseOrderCancellation
|
||||
export default parseOrderCancellation;
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {parseMemos} from './utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
export type FormattedCheckCancel = {
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
export interface FormattedCheckCancel {
|
||||
// ID of the Check ledger object to cancel.
|
||||
checkID: string
|
||||
checkID: string;
|
||||
}
|
||||
|
||||
function parseCheckCancel(tx: any): FormattedCheckCancel {
|
||||
assert.ok(tx.TransactionType === 'CheckCancel')
|
||||
assert.ok(tx.TransactionType === "CheckCancel");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
checkID: tx.CheckID
|
||||
})
|
||||
checkID: tx.CheckID,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseCheckCancel
|
||||
export default parseCheckCancel;
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import parseAmount from './amount'
|
||||
import {Amount} from '../../common/types/objects'
|
||||
import {parseMemos} from './utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
export type FormattedCheckCash = {
|
||||
import { Amount } from "../../common/types/objects";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
export interface 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.
|
||||
// The currency must match that of the `SendMax` of the corresponding
|
||||
// `CheckCreate` transaction.
|
||||
deliverMin: Amount
|
||||
deliverMin: Amount;
|
||||
|
||||
// *must* include either Amount or DeliverMin, but not both.
|
||||
}
|
||||
|
||||
function parseCheckCash(tx: any): FormattedCheckCash {
|
||||
assert.ok(tx.TransactionType === 'CheckCash')
|
||||
assert.ok(tx.TransactionType === "CheckCash");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
checkID: tx.CheckID,
|
||||
amount: tx.Amount && parseAmount(tx.Amount),
|
||||
deliverMin: tx.DeliverMin && parseAmount(tx.DeliverMin)
|
||||
})
|
||||
deliverMin: tx.DeliverMin && parseAmount(tx.DeliverMin),
|
||||
});
|
||||
}
|
||||
|
||||
export default parseCheckCash
|
||||
export default parseCheckCash;
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseTimestamp} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import parseAmount from './amount'
|
||||
import {Amount} from '../../common/types/objects'
|
||||
import {parseMemos} from './utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
export type FormattedCheckCreate = {
|
||||
import { Amount } from "../../common/types/objects";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import { parseTimestamp, parseMemos } from "./utils";
|
||||
|
||||
export interface 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
|
||||
invoiceID?: string;
|
||||
}
|
||||
|
||||
function parseCheckCreate(tx: any): FormattedCheckCreate {
|
||||
assert.ok(tx.TransactionType === 'CheckCreate')
|
||||
assert.ok(tx.TransactionType === "CheckCreate");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
@@ -32,8 +33,8 @@ function parseCheckCreate(tx: any): FormattedCheckCreate {
|
||||
sendMax: parseAmount(tx.SendMax),
|
||||
destinationTag: tx.DestinationTag,
|
||||
expiration: tx.Expiration && parseTimestamp(tx.Expiration),
|
||||
invoiceID: tx.InvoiceID
|
||||
})
|
||||
invoiceID: tx.InvoiceID,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseCheckCreate
|
||||
export default parseCheckCreate;
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {parseMemos} from './utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
export type FormattedDepositPreauth = {
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
export interface FormattedDepositPreauth {
|
||||
// account (address) of the sender to preauthorize
|
||||
authorize: string
|
||||
authorize: string;
|
||||
|
||||
// account (address) of the sender whose preauthorization should be revoked
|
||||
unauthorize: string
|
||||
unauthorize: string;
|
||||
}
|
||||
|
||||
function parseDepositPreauth(tx: any): FormattedDepositPreauth {
|
||||
assert.ok(tx.TransactionType === 'DepositPreauth')
|
||||
assert.ok(tx.TransactionType === "DepositPreauth");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
authorize: tx.Authorize,
|
||||
unauthorize: tx.Unauthorize
|
||||
})
|
||||
unauthorize: tx.Unauthorize,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseDepositPreauth
|
||||
export default parseDepositPreauth;
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseMemos} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
function parseEscrowCancellation(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'EscrowCancel')
|
||||
assert.ok(tx.TransactionType === "EscrowCancel");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
owner: tx.Owner,
|
||||
escrowSequence: tx.OfferSequence
|
||||
})
|
||||
escrowSequence: tx.OfferSequence,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseEscrowCancellation
|
||||
export default parseEscrowCancellation;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as assert from 'assert'
|
||||
import parseAmount from './amount'
|
||||
import {parseTimestamp, parseMemos} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import { parseTimestamp, parseMemos } from "./utils";
|
||||
|
||||
function parseEscrowCreation(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'EscrowCreate')
|
||||
assert.ok(tx.TransactionType === "EscrowCreate");
|
||||
|
||||
return removeUndefined({
|
||||
amount: parseAmount(tx.Amount).value,
|
||||
@@ -14,8 +16,8 @@ function parseEscrowCreation(tx: any): object {
|
||||
allowCancelAfter: parseTimestamp(tx.CancelAfter),
|
||||
allowExecuteAfter: parseTimestamp(tx.FinishAfter),
|
||||
sourceTag: tx.SourceTag,
|
||||
destinationTag: tx.DestinationTag
|
||||
})
|
||||
destinationTag: tx.DestinationTag,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseEscrowCreation
|
||||
export default parseEscrowCreation;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseMemos} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
function parseEscrowExecution(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'EscrowFinish')
|
||||
assert.ok(tx.TransactionType === "EscrowFinish");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
owner: tx.Owner,
|
||||
escrowSequence: tx.OfferSequence,
|
||||
condition: tx.Condition,
|
||||
fulfillment: tx.Fulfillment
|
||||
})
|
||||
fulfillment: tx.Fulfillment,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseEscrowExecution
|
||||
export default parseEscrowExecution;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {dropsToXrp} from '../../utils'
|
||||
import {parseMemos} from './utils'
|
||||
import BigNumber from "bignumber.js";
|
||||
|
||||
import { dropsToXrp } from "../../utils";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
function parseFeeUpdate(tx: any) {
|
||||
const baseFeeDrops = new BigNumber(tx.BaseFee, 16).toString()
|
||||
const baseFeeDrops = new BigNumber(tx.BaseFee, 16).toString();
|
||||
return {
|
||||
memos: parseMemos(tx),
|
||||
baseFeeXRP: dropsToXrp(baseFeeDrops),
|
||||
referenceFeeUnits: tx.ReferenceFeeUnits,
|
||||
reserveBaseXRP: dropsToXrp(tx.ReserveBase),
|
||||
reserveIncrementXRP: dropsToXrp(tx.ReserveIncrement)
|
||||
}
|
||||
reserveIncrementXRP: dropsToXrp(tx.ReserveIncrement),
|
||||
};
|
||||
}
|
||||
|
||||
export default parseFeeUpdate
|
||||
export default parseFeeUpdate;
|
||||
|
||||
@@ -1,52 +1,54 @@
|
||||
import _ from 'lodash'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {constants} from '../../common'
|
||||
const AccountFields = constants.AccountFields
|
||||
import BigNumber from "bignumber.js";
|
||||
import _ from "lodash";
|
||||
|
||||
import { constants } from "../../common";
|
||||
|
||||
const AccountFields = constants.AccountFields;
|
||||
|
||||
function parseField(info, value) {
|
||||
if (info.encoding === 'hex' && !info.length) {
|
||||
if (info.encoding === "hex" && !info.length) {
|
||||
// e.g. "domain"
|
||||
return Buffer.from(value, 'hex').toString('ascii')
|
||||
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
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseFields(data: any): object {
|
||||
const settings: any = {}
|
||||
const settings: any = {};
|
||||
for (const fieldName in AccountFields) {
|
||||
const fieldValue = data[fieldName]
|
||||
const fieldValue = data[fieldName];
|
||||
if (fieldValue != null) {
|
||||
const info = AccountFields[fieldName]
|
||||
settings[info.name] = parseField(info, fieldValue)
|
||||
const info = AccountFields[fieldName];
|
||||
settings[info.name] = parseField(info, fieldValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.RegularKey) {
|
||||
settings.regularKey = data.RegularKey
|
||||
settings.regularKey = data.RegularKey;
|
||||
}
|
||||
|
||||
// Since an account can own at most one SignerList,
|
||||
// this array must have exactly one member if it is present.
|
||||
if (data.signer_lists && data.signer_lists.length === 1) {
|
||||
settings.signers = {}
|
||||
settings.signers = {};
|
||||
if (data.signer_lists[0].SignerQuorum) {
|
||||
settings.signers.threshold = data.signer_lists[0].SignerQuorum
|
||||
settings.signers.threshold = data.signer_lists[0].SignerQuorum;
|
||||
}
|
||||
if (data.signer_lists[0].SignerEntries) {
|
||||
settings.signers.weights = data.signer_lists[0].SignerEntries.map(
|
||||
(entry: any) => {
|
||||
return {
|
||||
address: entry.SignerEntry.Account,
|
||||
weight: entry.SignerEntry.SignerWeight
|
||||
}
|
||||
weight: entry.SignerEntry.SignerWeight,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return settings
|
||||
return settings;
|
||||
}
|
||||
|
||||
export default parseFields
|
||||
export default parseFields;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const orderFlags = {
|
||||
Passive: 0x00010000,
|
||||
Sell: 0x00020000 // offer was placed as a sell
|
||||
}
|
||||
Sell: 0x00020000, // offer was placed as a sell
|
||||
};
|
||||
|
||||
const trustlineFlags = {
|
||||
LowReserve: 0x00010000, // entry counts toward reserve
|
||||
@@ -11,7 +11,7 @@ const trustlineFlags = {
|
||||
LowNoRipple: 0x00100000,
|
||||
HighNoRipple: 0x00200000,
|
||||
LowFreeze: 0x00400000,
|
||||
HighFreeze: 0x00800000
|
||||
}
|
||||
HighFreeze: 0x00800000,
|
||||
};
|
||||
|
||||
export {orderFlags, trustlineFlags}
|
||||
export { orderFlags, trustlineFlags };
|
||||
|
||||
@@ -1,87 +1,92 @@
|
||||
import _ from 'lodash'
|
||||
import {removeUndefined, rippleTimeToISOTime} from '../../utils'
|
||||
import parseTransaction from './transaction'
|
||||
import { TransactionAndMetadata } from '../../models/transactions'
|
||||
import _ from "lodash";
|
||||
|
||||
export type FormattedLedger = {
|
||||
import { TransactionAndMetadata } from "../../models/transactions";
|
||||
import { removeUndefined, rippleTimeToISOTime } from "../../utils";
|
||||
|
||||
import parseTransaction from "./transaction";
|
||||
|
||||
export interface FormattedLedger {
|
||||
// TODO: properties in type don't match response object. Fix!
|
||||
// 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
|
||||
stateHashes?: Array<string>
|
||||
stateHash: string;
|
||||
closeTime: string;
|
||||
closeTimeResolution: number;
|
||||
closeFlags: number;
|
||||
ledgerHash: string;
|
||||
ledgerVersion: number;
|
||||
parentLedgerHash: string;
|
||||
parentCloseTime: string;
|
||||
totalDrops: string;
|
||||
transactionHash: string;
|
||||
transactions?: object[];
|
||||
transactionHashes?: string[];
|
||||
rawState?: string;
|
||||
stateHashes?: string[];
|
||||
}
|
||||
|
||||
function parseTransactionWrapper(ledgerVersion: number, tx: TransactionAndMetadata) {
|
||||
function parseTransactionWrapper(
|
||||
ledgerVersion: number,
|
||||
tx: TransactionAndMetadata
|
||||
) {
|
||||
// renames metaData to meta and adds ledger_index
|
||||
const transaction = Object.assign({}, _.omit(tx, 'metadata'), {
|
||||
const transaction = {
|
||||
..._.omit(tx, "metadata"),
|
||||
meta: tx.metadata,
|
||||
ledger_index: ledgerVersion
|
||||
})
|
||||
const result = parseTransaction(transaction, true)
|
||||
ledger_index: ledgerVersion,
|
||||
};
|
||||
const result = parseTransaction(transaction, true);
|
||||
if (!result.outcome.ledgerVersion) {
|
||||
result.outcome.ledgerVersion = ledgerVersion
|
||||
result.outcome.ledgerVersion = ledgerVersion;
|
||||
}
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
|
||||
function parseTransactions(transactions: string[] | TransactionAndMetadata[], ledgerVersion: number) {
|
||||
function parseTransactions(
|
||||
transactions: string[] | TransactionAndMetadata[],
|
||||
ledgerVersion: number
|
||||
) {
|
||||
if (_.isEmpty(transactions)) {
|
||||
return {}
|
||||
return {};
|
||||
}
|
||||
if (typeof transactions[0] === 'string') {
|
||||
return {transactionHashes: transactions as unknown as string[]}
|
||||
if (typeof transactions[0] === "string") {
|
||||
return { transactionHashes: transactions as unknown as string[] };
|
||||
}
|
||||
return {
|
||||
transactions: (transactions as unknown as TransactionAndMetadata[]).map(
|
||||
_.partial(parseTransactionWrapper, ledgerVersion)
|
||||
)
|
||||
}
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
function parseState(state) {
|
||||
if (_.isEmpty(state)) {
|
||||
return {}
|
||||
return {};
|
||||
}
|
||||
if (typeof state[0] === 'string') {
|
||||
return {stateHashes: state}
|
||||
if (typeof state[0] === "string") {
|
||||
return { stateHashes: state };
|
||||
}
|
||||
return {rawState: JSON.stringify(state)}
|
||||
return { rawState: JSON.stringify(state) };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Ledger} ledger must be a *closed* ledger with valid `close_time` and `parent_close_time`
|
||||
* @returns {FormattedLedger} formatted ledger
|
||||
* @throws RangeError: Invalid time value (rippleTimeToISOTime)
|
||||
* @param ledger - Must be a *closed* ledger with valid `close_time` and `parent_close_time`.
|
||||
* @returns Formatted ledger.
|
||||
* @throws RangeError: Invalid time value (rippleTimeToISOTime).
|
||||
*/
|
||||
export function parseLedger(ledger): FormattedLedger {
|
||||
const ledgerVersion = parseInt(ledger.ledger_index, 10)
|
||||
return removeUndefined(
|
||||
Object.assign(
|
||||
{
|
||||
stateHash: ledger.account_hash,
|
||||
closeTime: rippleTimeToISOTime(ledger.close_time),
|
||||
closeTimeResolution: ledger.close_time_resolution,
|
||||
closeFlags: ledger.close_flags,
|
||||
ledgerHash: ledger.ledger_hash,
|
||||
ledgerVersion: ledgerVersion,
|
||||
parentLedgerHash: ledger.parent_hash,
|
||||
parentCloseTime: rippleTimeToISOTime(ledger.parent_close_time),
|
||||
totalDrops: ledger.total_coins,
|
||||
transactionHash: ledger.transaction_hash
|
||||
},
|
||||
parseTransactions(ledger.transactions, ledgerVersion),
|
||||
parseState(ledger.accountState)
|
||||
)
|
||||
)
|
||||
const ledgerVersion = parseInt(ledger.ledger_index, 10);
|
||||
return removeUndefined({
|
||||
stateHash: ledger.account_hash,
|
||||
closeTime: rippleTimeToISOTime(ledger.close_time),
|
||||
closeTimeResolution: ledger.close_time_resolution,
|
||||
closeFlags: ledger.close_flags,
|
||||
ledgerHash: ledger.ledger_hash,
|
||||
ledgerVersion,
|
||||
parentLedgerHash: ledger.parent_hash,
|
||||
parentCloseTime: rippleTimeToISOTime(ledger.parent_close_time),
|
||||
totalDrops: ledger.total_coins,
|
||||
transactionHash: ledger.transaction_hash,
|
||||
...parseTransactions(ledger.transactions, ledgerVersion),
|
||||
...parseState(ledger.accountState),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseTimestamp} from './utils'
|
||||
import {parseMemos} from './utils'
|
||||
import parseAmount from './amount'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {txFlags} from '../../common'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { txFlags } from "../../common";
|
||||
import {
|
||||
FormattedOrderSpecification,
|
||||
OfferCreateTransaction
|
||||
} from '../../common/types/objects/index'
|
||||
OfferCreateTransaction,
|
||||
} from "../../common/types/objects/index";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
const flags = txFlags.OfferCreate
|
||||
import parseAmount from "./amount";
|
||||
import { parseTimestamp, parseMemos } from "./utils";
|
||||
|
||||
const flags = txFlags.OfferCreate;
|
||||
|
||||
function parseOrder(tx: OfferCreateTransaction): FormattedOrderSpecification {
|
||||
assert.ok(tx.TransactionType === 'OfferCreate')
|
||||
assert.ok(tx.TransactionType === "OfferCreate");
|
||||
|
||||
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 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;
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
direction: direction,
|
||||
quantity: quantity,
|
||||
totalPrice: totalPrice,
|
||||
direction,
|
||||
quantity,
|
||||
totalPrice,
|
||||
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)
|
||||
})
|
||||
expirationTime: parseTimestamp(tx.Expiration),
|
||||
});
|
||||
}
|
||||
|
||||
export default parseOrder
|
||||
export default parseOrder;
|
||||
|
||||
@@ -1,42 +1,50 @@
|
||||
import _ from 'lodash'
|
||||
import {parseTimestamp, adjustQualityForXRP} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import _ from "lodash";
|
||||
|
||||
import {orderFlags} from './flags'
|
||||
import parseAmount from './amount'
|
||||
import {BookOffer} from '../../common/types/commands'
|
||||
import {Amount, FormattedOrderSpecification} from '../../common/types/objects'
|
||||
import { BookOffer } from "../../common/types/commands";
|
||||
import {
|
||||
Amount,
|
||||
FormattedOrderSpecification,
|
||||
} from "../../common/types/objects";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
export type FormattedOrderbookOrder = {
|
||||
specification: FormattedOrderSpecification
|
||||
import parseAmount from "./amount";
|
||||
import { orderFlags } from "./flags";
|
||||
import { parseTimestamp, adjustQualityForXRP } from "./utils";
|
||||
|
||||
export interface FormattedOrderbookOrder {
|
||||
specification: FormattedOrderSpecification;
|
||||
properties: {
|
||||
maker: string
|
||||
sequence: number
|
||||
makerExchangeRate: string
|
||||
}
|
||||
maker: string;
|
||||
sequence: number;
|
||||
makerExchangeRate: string;
|
||||
};
|
||||
state?: {
|
||||
fundedAmount: Amount
|
||||
priceOfFundedAmount: Amount
|
||||
}
|
||||
data: BookOffer
|
||||
fundedAmount: Amount;
|
||||
priceOfFundedAmount: Amount;
|
||||
};
|
||||
data: BookOffer;
|
||||
}
|
||||
|
||||
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 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;
|
||||
|
||||
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
|
||||
// so we can omit those flags here
|
||||
const specification: FormattedOrderSpecification = removeUndefined({
|
||||
direction: direction,
|
||||
quantity: quantity,
|
||||
totalPrice: totalPrice,
|
||||
direction,
|
||||
quantity,
|
||||
totalPrice,
|
||||
passive: (data.Flags & orderFlags.Passive) !== 0 || undefined,
|
||||
expirationTime: parseTimestamp(data.Expiration)
|
||||
})
|
||||
expirationTime: parseTimestamp(data.Expiration),
|
||||
});
|
||||
|
||||
if (data.quality == null) {
|
||||
throw new Error("parseOrderBookOrder: Could not find quality");
|
||||
}
|
||||
|
||||
const properties = {
|
||||
maker: data.Account,
|
||||
@@ -45,19 +53,25 @@ export function parseOrderbookOrder(data: BookOffer): FormattedOrderbookOrder {
|
||||
data.quality,
|
||||
takerGetsAmount.currency,
|
||||
takerPaysAmount.currency
|
||||
)
|
||||
}
|
||||
),
|
||||
};
|
||||
|
||||
const takerGetsFunded = data.taker_gets_funded
|
||||
? parseAmount(data.taker_gets_funded)
|
||||
: undefined
|
||||
: undefined;
|
||||
const takerPaysFunded = data.taker_pays_funded
|
||||
? parseAmount(data.taker_pays_funded)
|
||||
: undefined
|
||||
: undefined;
|
||||
const available = removeUndefined({
|
||||
fundedAmount: takerGetsFunded,
|
||||
priceOfFundedAmount: takerPaysFunded
|
||||
})
|
||||
const state = _.isEmpty(available) ? undefined : available
|
||||
return removeUndefined({specification, properties, state, data})
|
||||
priceOfFundedAmount: takerPaysFunded,
|
||||
});
|
||||
const state = _.isEmpty(available) ? undefined : available;
|
||||
|
||||
return removeUndefined({
|
||||
specification,
|
||||
properties,
|
||||
state,
|
||||
data,
|
||||
}) as FormattedOrderbookOrder;
|
||||
}
|
||||
|
||||
@@ -1,31 +1,33 @@
|
||||
import _ from 'lodash'
|
||||
import parseAmount from './amount'
|
||||
import {Amount, RippledAmount} from '../../common/types/objects'
|
||||
import {Path, GetPaths, RippledPathsResponse} from '../pathfind-types'
|
||||
import _ from "lodash";
|
||||
|
||||
import { Amount, RippledAmount } from "../../common/types/objects";
|
||||
import { Path, GetPaths, RippledPathsResponse } from "../pathfind-types";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
|
||||
function parsePaths(paths) {
|
||||
return paths.map((steps) =>
|
||||
steps.map((step) => _.omit(step, ['type', 'type_hex']))
|
||||
)
|
||||
steps.map((step) => _.omit(step, ["type", "type_hex"]))
|
||||
);
|
||||
}
|
||||
|
||||
function removeAnyCounterpartyEncoding(address: string, amount: Amount) {
|
||||
return amount.counterparty === address
|
||||
? _.omit(amount, 'counterparty')
|
||||
: amount
|
||||
? _.omit(amount, "counterparty")
|
||||
: amount;
|
||||
}
|
||||
|
||||
function createAdjustment(
|
||||
address: string,
|
||||
adjustmentWithoutAddress: object
|
||||
): any {
|
||||
const amountKey = Object.keys(adjustmentWithoutAddress)[0]
|
||||
const amount = adjustmentWithoutAddress[amountKey]
|
||||
const amountKey = Object.keys(adjustmentWithoutAddress)[0];
|
||||
const amount = adjustmentWithoutAddress[amountKey];
|
||||
return _.set(
|
||||
{address: address},
|
||||
{ address },
|
||||
amountKey,
|
||||
removeAnyCounterpartyEncoding(address, amount)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function parseAlternative(
|
||||
@@ -39,28 +41,30 @@ function parseAlternative(
|
||||
const amounts =
|
||||
alternative.destination_amount != null
|
||||
? {
|
||||
source: {amount: parseAmount(alternative.source_amount)},
|
||||
destination: {minAmount: parseAmount(alternative.destination_amount)}
|
||||
source: { amount: parseAmount(alternative.source_amount) },
|
||||
destination: {
|
||||
minAmount: parseAmount(alternative.destination_amount),
|
||||
},
|
||||
}
|
||||
: {
|
||||
source: {maxAmount: parseAmount(alternative.source_amount)},
|
||||
destination: {amount: parseAmount(destinationAmount)}
|
||||
}
|
||||
source: { maxAmount: parseAmount(alternative.source_amount) },
|
||||
destination: { amount: parseAmount(destinationAmount) },
|
||||
};
|
||||
|
||||
return {
|
||||
source: createAdjustment(sourceAddress, amounts.source),
|
||||
destination: createAdjustment(destinationAddress, amounts.destination),
|
||||
paths: JSON.stringify(parsePaths(alternative.paths_computed))
|
||||
}
|
||||
paths: JSON.stringify(parsePaths(alternative.paths_computed)),
|
||||
};
|
||||
}
|
||||
|
||||
function parsePathfind(pathfindResult: RippledPathsResponse): GetPaths {
|
||||
const sourceAddress = pathfindResult.source_account
|
||||
const destinationAddress = pathfindResult.destination_account
|
||||
const destinationAmount = pathfindResult.destination_amount
|
||||
const sourceAddress = pathfindResult.source_account;
|
||||
const destinationAddress = pathfindResult.destination_account;
|
||||
const destinationAmount = pathfindResult.destination_amount;
|
||||
return pathfindResult.alternatives.map((alt) =>
|
||||
parseAlternative(sourceAddress, destinationAddress, destinationAmount, alt)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export default parsePathfind
|
||||
export default parsePathfind;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {txFlags} from '../../common'
|
||||
import parseAmount from './amount'
|
||||
import {parseMemos} from './utils'
|
||||
const claimFlags = txFlags.PaymentChannelClaim
|
||||
import * as assert from "assert";
|
||||
|
||||
import { txFlags } from "../../common";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
const claimFlags = txFlags.PaymentChannelClaim;
|
||||
|
||||
function parsePaymentChannelClaim(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelClaim')
|
||||
assert.ok(tx.TransactionType === "PaymentChannelClaim");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
@@ -16,8 +19,8 @@ function parsePaymentChannelClaim(tx: any): object {
|
||||
signature: tx.Signature,
|
||||
publicKey: tx.PublicKey,
|
||||
renew: Boolean(tx.Flags & claimFlags.Renew) || undefined,
|
||||
close: Boolean(tx.Flags & claimFlags.Close) || undefined
|
||||
})
|
||||
close: Boolean(tx.Flags & claimFlags.Close) || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export default parsePaymentChannelClaim
|
||||
export default parsePaymentChannelClaim;
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseTimestamp,parseMemos} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import parseAmount from './amount'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import { parseTimestamp, parseMemos } from "./utils";
|
||||
|
||||
function parsePaymentChannelCreate(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelCreate')
|
||||
assert.ok(tx.TransactionType === "PaymentChannelCreate");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
@@ -14,8 +16,8 @@ function parsePaymentChannelCreate(tx: any): object {
|
||||
publicKey: tx.PublicKey,
|
||||
cancelAfter: tx.CancelAfter && parseTimestamp(tx.CancelAfter),
|
||||
sourceTag: tx.SourceTag,
|
||||
destinationTag: tx.DestinationTag
|
||||
})
|
||||
destinationTag: tx.DestinationTag,
|
||||
});
|
||||
}
|
||||
|
||||
export default parsePaymentChannelCreate
|
||||
export default parsePaymentChannelCreate;
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseTimestamp,parseMemos} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import parseAmount from './amount'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import { parseTimestamp, parseMemos } from "./utils";
|
||||
|
||||
function parsePaymentChannelFund(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'PaymentChannelFund')
|
||||
assert.ok(tx.TransactionType === "PaymentChannelFund");
|
||||
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
channel: tx.Channel,
|
||||
amount: parseAmount(tx.Amount).value,
|
||||
expiration: tx.Expiration && parseTimestamp(tx.Expiration)
|
||||
})
|
||||
expiration: tx.Expiration && parseTimestamp(tx.Expiration),
|
||||
});
|
||||
}
|
||||
|
||||
export default parsePaymentChannelFund
|
||||
export default parsePaymentChannelFund;
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
import {parseTimestamp, parseMemos} from './utils'
|
||||
import {removeUndefined, dropsToXrp} from '../../utils'
|
||||
import { PayChannel } from '../../models/ledger'
|
||||
import { PayChannel } from "../../models/ledger";
|
||||
import { removeUndefined, dropsToXrp } from "../../utils";
|
||||
|
||||
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
|
||||
previousAffectingTransactionLedgerVersion: number
|
||||
import { parseTimestamp, parseMemos } from "./utils";
|
||||
|
||||
export interface FormattedPaymentChannel {
|
||||
account: string;
|
||||
amount: string;
|
||||
balance: string;
|
||||
publicKey: string;
|
||||
destination: string;
|
||||
settleDelay: number;
|
||||
expiration?: string;
|
||||
cancelAfter?: string;
|
||||
sourceTag?: number;
|
||||
destinationTag?: number;
|
||||
previousAffectingTransactionID: string;
|
||||
previousAffectingTransactionLedgerVersion: number;
|
||||
}
|
||||
|
||||
export function parsePaymentChannel(
|
||||
data: PayChannel
|
||||
): FormattedPaymentChannel {
|
||||
export function parsePaymentChannel(data: PayChannel): FormattedPaymentChannel {
|
||||
return removeUndefined({
|
||||
memos: parseMemos(data),
|
||||
account: data.Account,
|
||||
@@ -33,6 +32,6 @@ export function parsePaymentChannel(
|
||||
sourceTag: data.SourceTag,
|
||||
destinationTag: data.DestinationTag,
|
||||
previousAffectingTransactionID: data.PreviousTxnID,
|
||||
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq
|
||||
})
|
||||
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
import _ from 'lodash'
|
||||
import * as assert from 'assert'
|
||||
import * as utils from './utils'
|
||||
import {txFlags} from '../../common'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import parseAmount from './amount'
|
||||
import * as assert from "assert";
|
||||
|
||||
import _ from "lodash";
|
||||
|
||||
import { txFlags } from "../../common";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import parseAmount from "./amount";
|
||||
import * as utils from "./utils";
|
||||
|
||||
function isNoDirectRipple(tx) {
|
||||
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0
|
||||
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0;
|
||||
}
|
||||
|
||||
function isQualityLimited(tx) {
|
||||
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0
|
||||
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0;
|
||||
}
|
||||
|
||||
function removeGenericCounterparty(amount, address) {
|
||||
return amount.counterparty === address
|
||||
? _.omit(amount, 'counterparty')
|
||||
: amount
|
||||
? _.omit(amount, "counterparty")
|
||||
: amount;
|
||||
}
|
||||
|
||||
// Payment specification
|
||||
function parsePayment(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'Payment')
|
||||
assert.ok(tx.TransactionType === "Payment");
|
||||
|
||||
const source = {
|
||||
address: tx.Account,
|
||||
@@ -29,17 +32,17 @@ function parsePayment(tx: any): object {
|
||||
parseAmount(tx.SendMax || tx.Amount),
|
||||
tx.Account
|
||||
),
|
||||
tag: tx.SourceTag
|
||||
}
|
||||
tag: tx.SourceTag,
|
||||
};
|
||||
|
||||
const destination: {
|
||||
address: string
|
||||
tag: number | undefined
|
||||
address: string;
|
||||
tag: number | undefined;
|
||||
} = {
|
||||
address: tx.Destination,
|
||||
tag: tx.DestinationTag
|
||||
tag: tx.DestinationTag,
|
||||
// Notice that `amount` is omitted to prevent misinterpretation
|
||||
}
|
||||
};
|
||||
|
||||
return removeUndefined({
|
||||
source: removeUndefined(source),
|
||||
@@ -49,8 +52,8 @@ function parsePayment(tx: any): object {
|
||||
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
|
||||
allowPartialPayment: utils.isPartialPayment(tx) || undefined,
|
||||
noDirectRipple: isNoDirectRipple(tx) || undefined,
|
||||
limitQuality: isQualityLimited(tx) || undefined
|
||||
})
|
||||
limitQuality: isQualityLimited(tx) || undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export default parsePayment
|
||||
export default parsePayment;
|
||||
|
||||
@@ -1,65 +1,69 @@
|
||||
import _ from 'lodash'
|
||||
import * as assert from 'assert'
|
||||
import {constants} from '../../common'
|
||||
const AccountFlags = constants.AccountFlags
|
||||
import parseFields from './fields'
|
||||
import * as assert from "assert";
|
||||
|
||||
import _ from "lodash";
|
||||
|
||||
import { constants } from "../../common";
|
||||
|
||||
import parseFields from "./fields";
|
||||
|
||||
const AccountFlags = constants.AccountFlags;
|
||||
|
||||
function getAccountRootModifiedNode(tx: any) {
|
||||
const modifiedNodes = tx.meta.AffectedNodes.filter(
|
||||
(node) => node.ModifiedNode.LedgerEntryType === 'AccountRoot'
|
||||
)
|
||||
assert.ok(modifiedNodes.length === 1)
|
||||
return modifiedNodes[0].ModifiedNode
|
||||
(node) => node.ModifiedNode.LedgerEntryType === "AccountRoot"
|
||||
);
|
||||
assert.ok(modifiedNodes.length === 1);
|
||||
return modifiedNodes[0].ModifiedNode;
|
||||
}
|
||||
|
||||
function parseFlags(tx: any): any {
|
||||
const settings: any = {}
|
||||
if (tx.TransactionType !== 'AccountSet') {
|
||||
return settings
|
||||
const settings: any = {};
|
||||
if (tx.TransactionType !== "AccountSet") {
|
||||
return settings;
|
||||
}
|
||||
|
||||
const node = getAccountRootModifiedNode(tx)
|
||||
const oldFlags = _.get(node.PreviousFields, 'Flags')
|
||||
const newFlags = _.get(node.FinalFields, 'Flags')
|
||||
const node = getAccountRootModifiedNode(tx);
|
||||
const oldFlags = _.get(node.PreviousFields, "Flags");
|
||||
const newFlags = _.get(node.FinalFields, "Flags");
|
||||
|
||||
if (oldFlags != null && newFlags != null) {
|
||||
const changedFlags = oldFlags ^ newFlags
|
||||
const setFlags = newFlags & changedFlags
|
||||
const clearedFlags = oldFlags & changedFlags
|
||||
Object.entries(AccountFlags).forEach(entry => {
|
||||
const changedFlags = oldFlags ^ newFlags;
|
||||
const setFlags = newFlags & changedFlags;
|
||||
const clearedFlags = oldFlags & changedFlags;
|
||||
Object.entries(AccountFlags).forEach((entry) => {
|
||||
const [flagName, flagValue] = entry;
|
||||
if (setFlags & flagValue) {
|
||||
settings[flagName] = true
|
||||
settings[flagName] = true;
|
||||
} else if (clearedFlags & flagValue) {
|
||||
settings[flagName] = false
|
||||
settings[flagName] = false;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// enableTransactionIDTracking requires a special case because it
|
||||
// does not affect the Flags field; instead it adds/removes a field called
|
||||
// "AccountTxnID" to/from the account root.
|
||||
|
||||
const oldField = _.get(node.PreviousFields, 'AccountTxnID')
|
||||
const newField = _.get(node.FinalFields, 'AccountTxnID')
|
||||
const oldField = _.get(node.PreviousFields, "AccountTxnID");
|
||||
const newField = _.get(node.FinalFields, "AccountTxnID");
|
||||
if (newField && !oldField) {
|
||||
settings.enableTransactionIDTracking = true
|
||||
settings.enableTransactionIDTracking = true;
|
||||
} else if (oldField && !newField) {
|
||||
settings.enableTransactionIDTracking = false
|
||||
settings.enableTransactionIDTracking = false;
|
||||
}
|
||||
|
||||
return settings
|
||||
return settings;
|
||||
}
|
||||
|
||||
function parseSettings(tx: any) {
|
||||
const txType = tx.TransactionType
|
||||
const txType = tx.TransactionType;
|
||||
assert.ok(
|
||||
txType === 'AccountSet' ||
|
||||
txType === 'SetRegularKey' ||
|
||||
txType === 'SignerListSet'
|
||||
)
|
||||
txType === "AccountSet" ||
|
||||
txType === "SetRegularKey" ||
|
||||
txType === "SignerListSet"
|
||||
);
|
||||
|
||||
return Object.assign({}, parseFlags(tx), parseFields(tx))
|
||||
return { ...parseFlags(tx), ...parseFields(tx) };
|
||||
}
|
||||
|
||||
export default parseSettings
|
||||
export default parseSettings;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import * as assert from 'assert'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import {parseMemos} from './utils'
|
||||
import * as assert from "assert";
|
||||
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseMemos } from "./utils";
|
||||
|
||||
function parseTicketCreate(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'TicketCreate')
|
||||
assert.ok(tx.TransactionType === "TicketCreate");
|
||||
return removeUndefined({
|
||||
memos: parseMemos(tx),
|
||||
ticketCount: tx.TicketCount
|
||||
})
|
||||
ticketCount: tx.TicketCount,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseTicketCreate
|
||||
export default parseTicketCreate;
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
import {parseOutcome} from './utils'
|
||||
import {removeUndefined} from '../../utils'
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
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 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 parseTicketCreate from './ticket-create'
|
||||
import parseTrustline from './trustline'
|
||||
|
||||
import parseAmendment from './amendment' // pseudo-transaction
|
||||
import parseFeeUpdate from './fee-update' // pseudo-transaction
|
||||
import parseAccountDelete from "./account-delete";
|
||||
import parseAmendment from "./amendment"; // pseudo-transaction
|
||||
import parseOrderCancellation from "./cancellation";
|
||||
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 parseFeeUpdate from "./fee-update"; // pseudo-transaction
|
||||
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 parseSettings from "./settings";
|
||||
import parseTicketCreate from "./ticket-create";
|
||||
import parseTrustline from "./trustline";
|
||||
import { parseOutcome } from "./utils";
|
||||
|
||||
function parseTransactionType(type) {
|
||||
// Ordering matches https://developers.ripple.com/transaction-types.html
|
||||
const mapping = {
|
||||
AccountSet: 'settings',
|
||||
AccountDelete: 'accountDelete',
|
||||
CheckCancel: 'checkCancel',
|
||||
CheckCash: 'checkCash',
|
||||
CheckCreate: 'checkCreate',
|
||||
DepositPreauth: 'depositPreauth',
|
||||
EscrowCancel: 'escrowCancellation',
|
||||
EscrowCreate: 'escrowCreation',
|
||||
EscrowFinish: 'escrowExecution',
|
||||
OfferCancel: 'orderCancellation',
|
||||
OfferCreate: 'order',
|
||||
Payment: 'payment',
|
||||
PaymentChannelClaim: 'paymentChannelClaim',
|
||||
PaymentChannelCreate: 'paymentChannelCreate',
|
||||
PaymentChannelFund: 'paymentChannelFund',
|
||||
SetRegularKey: 'settings',
|
||||
SignerListSet: 'settings',
|
||||
TicketCreate: 'ticketCreate',
|
||||
TrustSet: 'trustline',
|
||||
AccountSet: "settings",
|
||||
AccountDelete: "accountDelete",
|
||||
CheckCancel: "checkCancel",
|
||||
CheckCash: "checkCash",
|
||||
CheckCreate: "checkCreate",
|
||||
DepositPreauth: "depositPreauth",
|
||||
EscrowCancel: "escrowCancellation",
|
||||
EscrowCreate: "escrowCreation",
|
||||
EscrowFinish: "escrowExecution",
|
||||
OfferCancel: "orderCancellation",
|
||||
OfferCreate: "order",
|
||||
Payment: "payment",
|
||||
PaymentChannelClaim: "paymentChannelClaim",
|
||||
PaymentChannelCreate: "paymentChannelCreate",
|
||||
PaymentChannelFund: "paymentChannelFund",
|
||||
SetRegularKey: "settings",
|
||||
SignerListSet: "settings",
|
||||
TicketCreate: "ticketCreate",
|
||||
TrustSet: "trustline",
|
||||
|
||||
EnableAmendment: 'amendment', // pseudo-transaction
|
||||
SetFee: 'feeUpdate' // pseudo-transaction
|
||||
}
|
||||
return mapping[type] || null
|
||||
EnableAmendment: "amendment", // pseudo-transaction
|
||||
SetFee: "feeUpdate", // pseudo-transaction
|
||||
};
|
||||
return mapping[type] || null;
|
||||
}
|
||||
|
||||
// includeRawTransaction: undefined by default (getTransaction)
|
||||
function parseTransaction(tx: any, includeRawTransaction: boolean): any {
|
||||
const type = parseTransactionType(tx.TransactionType)
|
||||
const type = parseTransactionType(tx.TransactionType);
|
||||
const mapping = {
|
||||
settings: parseSettings,
|
||||
accountDelete: parseAccountDelete,
|
||||
@@ -74,31 +73,31 @@ function parseTransaction(tx: any, includeRawTransaction: boolean): any {
|
||||
trustline: parseTrustline,
|
||||
|
||||
amendment: parseAmendment, // pseudo-transaction
|
||||
feeUpdate: parseFeeUpdate // pseudo-transaction
|
||||
}
|
||||
const parser: Function = mapping[type]
|
||||
feeUpdate: parseFeeUpdate, // pseudo-transaction
|
||||
};
|
||||
const parser: Function = mapping[type];
|
||||
|
||||
const specification = parser
|
||||
? parser(tx)
|
||||
: {
|
||||
UNAVAILABLE: 'Unrecognized transaction type.',
|
||||
UNAVAILABLE: "Unrecognized transaction type.",
|
||||
SEE_RAW_TRANSACTION:
|
||||
'Since this type is unrecognized, `rawTransaction` is included in this response.'
|
||||
}
|
||||
"Since this type is unrecognized, `rawTransaction` is included in this response.",
|
||||
};
|
||||
if (!parser) {
|
||||
includeRawTransaction = true
|
||||
includeRawTransaction = true;
|
||||
}
|
||||
|
||||
const outcome = parseOutcome(tx)
|
||||
const outcome = parseOutcome(tx);
|
||||
return removeUndefined({
|
||||
type: type,
|
||||
type,
|
||||
address: tx.Account,
|
||||
sequence: tx.Sequence,
|
||||
id: tx.hash,
|
||||
specification: removeUndefined(specification),
|
||||
outcome: outcome ? removeUndefined(outcome) : undefined,
|
||||
rawTransaction: includeRawTransaction ? JSON.stringify(tx) : undefined
|
||||
})
|
||||
rawTransaction: includeRawTransaction ? JSON.stringify(tx) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
export default parseTransaction
|
||||
export default parseTransaction;
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
import * as assert from 'assert'
|
||||
import {parseQuality, parseMemos} from './utils'
|
||||
import {txFlags} from '../../common'
|
||||
import {removeUndefined} from '../../utils'
|
||||
const flags = txFlags.TrustSet
|
||||
import * as assert from "assert";
|
||||
|
||||
import { txFlags } from "../../common";
|
||||
import { removeUndefined } from "../../utils";
|
||||
|
||||
import { parseQuality, parseMemos } from "./utils";
|
||||
|
||||
const flags = txFlags.TrustSet;
|
||||
|
||||
function parseFlag(flagsValue, trueValue, falseValue) {
|
||||
if (flagsValue & trueValue) {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
if (flagsValue & falseValue) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseTrustline(tx: any): object {
|
||||
assert.ok(tx.TransactionType === 'TrustSet')
|
||||
assert.ok(tx.TransactionType === "TrustSet");
|
||||
|
||||
return removeUndefined({
|
||||
limit: tx.LimitAmount.value,
|
||||
@@ -30,8 +33,8 @@ function parseTrustline(tx: any): object {
|
||||
flags.ClearNoRipple
|
||||
),
|
||||
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
|
||||
authorized: parseFlag(tx.Flags, flags.SetAuth, 0)
|
||||
})
|
||||
authorized: parseFlag(tx.Flags, flags.SetAuth, 0),
|
||||
});
|
||||
}
|
||||
|
||||
export default parseTrustline
|
||||
export default parseTrustline;
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
import transactionParser from 'ripple-lib-transactionparser'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import parseAmount from './amount'
|
||||
import BigNumber from "bignumber.js";
|
||||
import transactionParser from "ripple-lib-transactionparser";
|
||||
|
||||
import {Amount, Memo} from '../../common/types/objects'
|
||||
import {txFlags} from '../../common'
|
||||
import {removeUndefined, dropsToXrp, rippleTimeToISOTime} from '../../utils'
|
||||
import { txFlags } from "../../common";
|
||||
import { Amount, Memo } from "../../common/types/objects";
|
||||
import { removeUndefined, dropsToXrp, rippleTimeToISOTime } from "../../utils";
|
||||
|
||||
type OfferDescription = {
|
||||
direction: string,
|
||||
quantity: any,
|
||||
totalPrice: any,
|
||||
sequence: number,
|
||||
status: string,
|
||||
makerExchangeRate: string
|
||||
import parseAmount from "./amount";
|
||||
|
||||
interface OfferDescription {
|
||||
direction: string;
|
||||
quantity: any;
|
||||
totalPrice: any;
|
||||
sequence: number;
|
||||
status: string;
|
||||
makerExchangeRate: string;
|
||||
}
|
||||
|
||||
type Orderbook = {
|
||||
[key: string]: OfferDescription[]
|
||||
interface Orderbook {
|
||||
[key: string]: OfferDescription[];
|
||||
}
|
||||
|
||||
type BalanceSheetItem = {
|
||||
counterparty: string,
|
||||
currency: string,
|
||||
value: string
|
||||
interface BalanceSheetItem {
|
||||
counterparty: string;
|
||||
currency: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
type BalanceSheet = {
|
||||
[key: string]: BalanceSheetItem[]
|
||||
interface BalanceSheet {
|
||||
[key: string]: BalanceSheetItem[];
|
||||
}
|
||||
|
||||
function adjustQualityForXRP(
|
||||
@@ -36,77 +37,79 @@ function adjustQualityForXRP(
|
||||
) {
|
||||
// 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 shift = numeratorShift - denominatorShift
|
||||
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()
|
||||
: new BigNumber(quality).shiftedBy(shift).toString();
|
||||
}
|
||||
|
||||
function parseQuality(quality?: number | null): number | undefined {
|
||||
if (typeof quality !== 'number') {
|
||||
return 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 {
|
||||
if (typeof rippleTime !== 'number') {
|
||||
return undefined
|
||||
if (typeof rippleTime !== "number") {
|
||||
return undefined;
|
||||
}
|
||||
return rippleTimeToISOTime(rippleTime)
|
||||
return rippleTimeToISOTime(rippleTime);
|
||||
}
|
||||
|
||||
function removeEmptyCounterparty(amount) {
|
||||
if (amount.counterparty === '') {
|
||||
delete amount.counterparty
|
||||
if (amount.counterparty === "") {
|
||||
delete amount.counterparty;
|
||||
}
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInBalanceChanges(balanceChanges: BalanceSheet) {
|
||||
Object.entries(balanceChanges).forEach(([_, changes]) => {
|
||||
changes.forEach(removeEmptyCounterparty)
|
||||
})
|
||||
changes.forEach(removeEmptyCounterparty);
|
||||
});
|
||||
}
|
||||
|
||||
function removeEmptyCounterpartyInOrderbookChanges(orderbookChanges: Orderbook) {
|
||||
function removeEmptyCounterpartyInOrderbookChanges(
|
||||
orderbookChanges: Orderbook
|
||||
) {
|
||||
Object.entries(orderbookChanges).forEach(([_, changes]) => {
|
||||
changes.forEach((change) => {
|
||||
Object.entries(change).forEach(removeEmptyCounterparty)
|
||||
})
|
||||
})
|
||||
Object.entries(change).forEach(removeEmptyCounterparty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isPartialPayment(tx: any) {
|
||||
return (tx.Flags & txFlags.Payment.PartialPayment) !== 0
|
||||
return (tx.Flags & txFlags.Payment.PartialPayment) !== 0;
|
||||
}
|
||||
|
||||
function parseDeliveredAmount(tx: any): Amount | void {
|
||||
if (
|
||||
tx.TransactionType !== 'Payment' ||
|
||||
tx.meta.TransactionResult !== 'tesSUCCESS'
|
||||
tx.TransactionType !== "Payment" ||
|
||||
tx.meta.TransactionResult !== "tesSUCCESS"
|
||||
) {
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (tx.meta.delivered_amount && tx.meta.delivered_amount === 'unavailable') {
|
||||
return undefined
|
||||
if (tx.meta.delivered_amount && tx.meta.delivered_amount === "unavailable") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// parsable delivered_amount
|
||||
if (tx.meta.delivered_amount) {
|
||||
return parseAmount(tx.meta.delivered_amount)
|
||||
return parseAmount(tx.meta.delivered_amount);
|
||||
}
|
||||
|
||||
// DeliveredAmount only present on partial payments
|
||||
if (tx.meta.DeliveredAmount) {
|
||||
return parseAmount(tx.meta.DeliveredAmount)
|
||||
return parseAmount(tx.meta.DeliveredAmount);
|
||||
}
|
||||
|
||||
// no partial payment flag, use tx.Amount
|
||||
if (tx.Amount && !isPartialPayment(tx)) {
|
||||
return parseAmount(tx.Amount)
|
||||
return parseAmount(tx.Amount);
|
||||
}
|
||||
|
||||
// DeliveredAmount field was introduced at
|
||||
@@ -116,52 +119,52 @@ function parseDeliveredAmount(tx: any): Amount | void {
|
||||
// transferred with a partial payment before
|
||||
// that date must be derived from metadata.
|
||||
if (tx.Amount && tx.ledger_index > 4594094) {
|
||||
return parseAmount(tx.Amount)
|
||||
return parseAmount(tx.Amount);
|
||||
}
|
||||
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function parseOutcome(tx: any): any | undefined {
|
||||
const metadata = tx.meta || tx.metaData
|
||||
const metadata = tx.meta || tx.metaData;
|
||||
if (!metadata) {
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
const balanceChanges = transactionParser.parseBalanceChanges(metadata)
|
||||
const orderbookChanges = transactionParser.parseOrderbookChanges(metadata)
|
||||
const channelChanges = transactionParser.parseChannelChanges(metadata)
|
||||
const balanceChanges = transactionParser.parseBalanceChanges(metadata);
|
||||
const orderbookChanges = transactionParser.parseOrderbookChanges(metadata);
|
||||
const channelChanges = transactionParser.parseChannelChanges(metadata);
|
||||
|
||||
removeEmptyCounterpartyInBalanceChanges(balanceChanges)
|
||||
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges)
|
||||
removeEmptyCounterpartyInBalanceChanges(balanceChanges);
|
||||
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges);
|
||||
|
||||
return removeUndefined({
|
||||
result: tx.meta.TransactionResult,
|
||||
timestamp: parseTimestamp(tx.date),
|
||||
fee: dropsToXrp(tx.Fee),
|
||||
balanceChanges: balanceChanges,
|
||||
orderbookChanges: orderbookChanges,
|
||||
channelChanges: channelChanges,
|
||||
balanceChanges,
|
||||
orderbookChanges,
|
||||
channelChanges,
|
||||
ledgerVersion: tx.ledger_index,
|
||||
indexInLedger: tx.meta.TransactionIndex,
|
||||
deliveredAmount: parseDeliveredAmount(tx)
|
||||
})
|
||||
deliveredAmount: parseDeliveredAmount(tx),
|
||||
});
|
||||
}
|
||||
|
||||
function hexToString(hex: string): string | undefined {
|
||||
return hex ? Buffer.from(hex, 'hex').toString('utf-8') : undefined
|
||||
return hex ? Buffer.from(hex, "hex").toString("utf-8") : undefined;
|
||||
}
|
||||
|
||||
function parseMemos(tx: any): Array<Memo> | undefined {
|
||||
function parseMemos(tx: any): Memo[] | undefined {
|
||||
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
|
||||
return undefined
|
||||
return undefined;
|
||||
}
|
||||
return tx.Memos.map((m) => {
|
||||
return removeUndefined({
|
||||
type: m.Memo.parsed_memo_type || hexToString(m.Memo.MemoType),
|
||||
format: m.Memo.parsed_memo_format || hexToString(m.Memo.MemoFormat),
|
||||
data: m.Memo.parsed_memo_data || hexToString(m.Memo.MemoData)
|
||||
})
|
||||
})
|
||||
data: m.Memo.parsed_memo_data || hexToString(m.Memo.MemoData),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -171,5 +174,5 @@ export {
|
||||
hexToString,
|
||||
parseTimestamp,
|
||||
adjustQualityForXRP,
|
||||
isPartialPayment
|
||||
}
|
||||
isPartialPayment,
|
||||
};
|
||||
|
||||
@@ -3,64 +3,64 @@ import {
|
||||
RippledAmount,
|
||||
Adjustment,
|
||||
MaxAdjustment,
|
||||
MinAdjustment
|
||||
} from '../common/types/objects'
|
||||
MinAdjustment,
|
||||
} from "../common/types/objects";
|
||||
|
||||
// Amount where counterparty and value are optional
|
||||
export type LaxLaxAmount = {
|
||||
currency: string
|
||||
value?: string
|
||||
issuer?: string
|
||||
counterparty?: string
|
||||
export interface LaxLaxAmount {
|
||||
currency: string;
|
||||
value?: string;
|
||||
issuer?: string;
|
||||
counterparty?: string;
|
||||
}
|
||||
|
||||
export type Path = {
|
||||
source: Adjustment | MaxAdjustment
|
||||
destination: Adjustment | MinAdjustment
|
||||
paths: string
|
||||
export interface Path {
|
||||
source: Adjustment | MaxAdjustment;
|
||||
destination: Adjustment | MinAdjustment;
|
||||
paths: string;
|
||||
}
|
||||
|
||||
export type GetPaths = Array<Path>
|
||||
export type GetPaths = Path[];
|
||||
|
||||
export type PathFind = {
|
||||
export interface 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
|
||||
amount: LaxLaxAmount
|
||||
}
|
||||
address: string;
|
||||
amount: LaxLaxAmount;
|
||||
};
|
||||
}
|
||||
|
||||
export type PathFindRequest = {
|
||||
command: string
|
||||
source_account: string
|
||||
destination_amount: RippledAmount
|
||||
destination_account: string
|
||||
source_currencies?: {currency: string; issuer?: string}[]
|
||||
send_max?: RippledAmount
|
||||
export interface PathFindRequest {
|
||||
command: string;
|
||||
source_account: string;
|
||||
destination_amount: RippledAmount;
|
||||
destination_account: string;
|
||||
source_currencies?: Array<{ currency: string; issuer?: string }>;
|
||||
send_max?: RippledAmount;
|
||||
}
|
||||
|
||||
export type RippledPathsResponse = {
|
||||
export interface RippledPathsResponse {
|
||||
alternatives: Array<{
|
||||
paths_computed: Array<
|
||||
Array<{
|
||||
type: number
|
||||
type_hex: string
|
||||
account?: string
|
||||
issuer?: string
|
||||
currency?: string
|
||||
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}>
|
||||
full_reply?: boolean
|
||||
>;
|
||||
source_amount: RippledAmount;
|
||||
}>;
|
||||
type: string;
|
||||
destination_account: string;
|
||||
destination_amount: RippledAmount;
|
||||
destination_currencies?: string[];
|
||||
source_account: string;
|
||||
source_currencies?: Array<{ currency: string }>;
|
||||
full_reply?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,87 +1,90 @@
|
||||
import _ from 'lodash'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {getXRPBalance, renameCounterpartyToIssuer} from './utils'
|
||||
import {
|
||||
validate,
|
||||
errors
|
||||
} from '../common'
|
||||
import {toRippledAmount, xrpToDrops, dropsToXrp} from '../utils'
|
||||
import {Connection} from '../client'
|
||||
import parsePathfind from './parse/pathfind'
|
||||
import {RippledAmount, Amount} from '../common/types/objects'
|
||||
import BigNumber from "bignumber.js";
|
||||
import _ from "lodash";
|
||||
|
||||
import type { Client } from "..";
|
||||
import { Connection } from "../client";
|
||||
import { validate, errors } from "../common";
|
||||
import { RippledAmount, Amount } from "../common/types/objects";
|
||||
import { RipplePathFindRequest } from "../models/methods";
|
||||
import { toRippledAmount, xrpToDrops, dropsToXrp } from "../utils";
|
||||
|
||||
import parsePathfind from "./parse/pathfind";
|
||||
import {
|
||||
GetPaths,
|
||||
PathFind,
|
||||
RippledPathsResponse,
|
||||
PathFindRequest
|
||||
} from './pathfind-types'
|
||||
import {Client} from '..'
|
||||
import { RipplePathFindRequest } from '../models/methods'
|
||||
const NotFoundError = errors.NotFoundError
|
||||
const ValidationError = errors.ValidationError
|
||||
PathFindRequest,
|
||||
} from "./pathfind-types";
|
||||
import { getXRPBalance, renameCounterpartyToIssuer } from "./utils";
|
||||
|
||||
const NotFoundError = errors.NotFoundError;
|
||||
const ValidationError = errors.ValidationError;
|
||||
|
||||
function addParams(
|
||||
request: PathFindRequest,
|
||||
result: RippledPathsResponse
|
||||
): RippledPathsResponse {
|
||||
return _.defaults(
|
||||
Object.assign({}, result, {
|
||||
{
|
||||
...result,
|
||||
source_account: request.source_account,
|
||||
source_currencies: request.source_currencies
|
||||
}),
|
||||
{destination_amount: request.destination_amount}
|
||||
)
|
||||
source_currencies: request.source_currencies,
|
||||
},
|
||||
{ destination_amount: request.destination_amount }
|
||||
);
|
||||
}
|
||||
|
||||
function requestPathFind(
|
||||
connection: Connection,
|
||||
pathfind: PathFind
|
||||
): Promise<RippledPathsResponse> {
|
||||
const destinationAmount: Amount = Object.assign(
|
||||
{
|
||||
// This is converted back to drops by toRippledAmount()
|
||||
value:
|
||||
pathfind.destination.amount.currency === 'XRP' ? dropsToXrp('-1') : '-1'
|
||||
},
|
||||
pathfind.destination.amount
|
||||
)
|
||||
const destinationAmount: Amount = {
|
||||
// This is converted back to drops by toRippledAmount()
|
||||
value:
|
||||
pathfind.destination.amount.currency === "XRP" ? dropsToXrp("-1") : "-1",
|
||||
...pathfind.destination.amount,
|
||||
};
|
||||
const request: RipplePathFindRequest = {
|
||||
command: 'ripple_path_find',
|
||||
command: "ripple_path_find",
|
||||
source_account: pathfind.source.address,
|
||||
destination_account: pathfind.destination.address,
|
||||
// @ts-ignore
|
||||
destination_amount: destinationAmount
|
||||
}
|
||||
// @ts-expect-error
|
||||
destination_amount: destinationAmount,
|
||||
};
|
||||
if (
|
||||
typeof request.destination_amount === 'object' &&
|
||||
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
|
||||
request.destination_amount.issuer = request.destination_account;
|
||||
}
|
||||
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
|
||||
// @ts-ignore
|
||||
// @ts-expect-error
|
||||
request.source_currencies = pathfind.source.currencies.map((amount) =>
|
||||
renameCounterpartyToIssuer(amount)
|
||||
)
|
||||
);
|
||||
}
|
||||
if (pathfind.source.amount) {
|
||||
if (pathfind.destination.amount.value != null) {
|
||||
throw new ValidationError(
|
||||
'Cannot specify both source.amount' +
|
||||
' and destination.amount.value in getPaths'
|
||||
)
|
||||
"Cannot specify both source.amount" +
|
||||
" and destination.amount.value in getPaths"
|
||||
);
|
||||
}
|
||||
// @ts-ignore
|
||||
request.send_max = toRippledAmount(pathfind.source.amount)
|
||||
if (typeof request.send_max !== 'string' && !request.send_max.issuer) {
|
||||
request.send_max.issuer = pathfind.source.address
|
||||
// @ts-expect-error
|
||||
request.send_max = toRippledAmount(pathfind.source.amount);
|
||||
if (
|
||||
request.send_max != null &&
|
||||
typeof request.send_max !== "string" &&
|
||||
!request.send_max.issuer
|
||||
) {
|
||||
request.send_max.issuer = pathfind.source.address;
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
return connection.request(request).then((paths) => addParams(request, paths))
|
||||
// @ts-expect-error
|
||||
return connection.request(request).then((paths) => addParams(request, paths));
|
||||
}
|
||||
|
||||
function addDirectXrpPath(
|
||||
@@ -89,22 +92,22 @@ function addDirectXrpPath(
|
||||
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!
|
||||
const destinationAmount = paths.destination_amount;
|
||||
// @ts-expect-error: destinationAmount can be a currency amount object! Fix!
|
||||
if (new BigNumber(xrpBalance).isGreaterThanOrEqualTo(destinationAmount)) {
|
||||
paths.alternatives.unshift({
|
||||
paths_computed: [],
|
||||
source_amount: paths.destination_amount
|
||||
})
|
||||
source_amount: paths.destination_amount,
|
||||
});
|
||||
}
|
||||
return paths
|
||||
return paths;
|
||||
}
|
||||
|
||||
function isRippledIOUAmount(amount: RippledAmount) {
|
||||
// rippled XRP amounts are specified as decimal strings
|
||||
return (
|
||||
typeof amount === 'object' && amount.currency && amount.currency !== 'XRP'
|
||||
)
|
||||
typeof amount === "object" && amount.currency && amount.currency !== "XRP"
|
||||
);
|
||||
}
|
||||
|
||||
function conditionallyAddDirectXRPPath(
|
||||
@@ -114,13 +117,14 @@ function conditionallyAddDirectXRPPath(
|
||||
): Promise<RippledPathsResponse> {
|
||||
if (
|
||||
isRippledIOUAmount(paths.destination_amount) ||
|
||||
!paths.destination_currencies.includes('XRP')
|
||||
(paths.destination_currencies &&
|
||||
!paths.destination_currencies.includes("XRP"))
|
||||
) {
|
||||
return Promise.resolve(paths)
|
||||
return Promise.resolve(paths);
|
||||
}
|
||||
return getXRPBalance(client, address, undefined).then((xrpBalance) =>
|
||||
addDirectXrpPath(paths, xrpBalance)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function filterSourceFundsLowPaths(
|
||||
@@ -129,74 +133,72 @@ function filterSourceFundsLowPaths(
|
||||
): RippledPathsResponse {
|
||||
if (
|
||||
pathfind.source.amount &&
|
||||
pathfind.destination.amount.value == null &&
|
||||
paths.alternatives
|
||||
paths.alternatives &&
|
||||
pathfind.destination.amount.value == null
|
||||
) {
|
||||
paths.alternatives = paths.alternatives.filter((alt) => {
|
||||
if (!alt.source_amount) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
if (pathfind.source.amount === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pathfindSourceAmountValue = new BigNumber(
|
||||
pathfind.source.amount.currency === 'XRP'
|
||||
pathfind.source.amount.currency === "XRP"
|
||||
? xrpToDrops(pathfind.source.amount.value)
|
||||
: pathfind.source.amount.value
|
||||
)
|
||||
);
|
||||
const altSourceAmountValue = new BigNumber(
|
||||
typeof alt.source_amount === 'string'
|
||||
typeof alt.source_amount === "string"
|
||||
? alt.source_amount
|
||||
: alt.source_amount.value
|
||||
)
|
||||
return altSourceAmountValue.eq(pathfindSourceAmountValue)
|
||||
})
|
||||
);
|
||||
return altSourceAmountValue.eq(pathfindSourceAmountValue);
|
||||
});
|
||||
}
|
||||
return paths
|
||||
return paths;
|
||||
}
|
||||
|
||||
function formatResponse(pathfind: PathFind, paths: RippledPathsResponse) {
|
||||
if (paths.alternatives && paths.alternatives.length > 0) {
|
||||
return parsePathfind(paths)
|
||||
return parsePathfind(paths);
|
||||
}
|
||||
if (
|
||||
paths.destination_currencies != null &&
|
||||
!paths.destination_currencies.includes(
|
||||
pathfind.destination.amount.currency
|
||||
)
|
||||
!paths.destination_currencies.includes(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(', ')
|
||||
)
|
||||
`${"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'
|
||||
)
|
||||
"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'
|
||||
)
|
||||
"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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getPaths(this: Client, pathfind: PathFind): Promise<GetPaths> {
|
||||
validate.getPaths({pathfind})
|
||||
validate.getPaths({ pathfind });
|
||||
|
||||
const address = pathfind.source.address
|
||||
const address = pathfind.source.address;
|
||||
return requestPathFind(this.connection, pathfind)
|
||||
.then((paths) =>
|
||||
conditionallyAddDirectXRPPath(this, address, paths)
|
||||
)
|
||||
.then((paths) => conditionallyAddDirectXRPPath(this, address, paths))
|
||||
.then((paths) => filterSourceFundsLowPaths(pathfind, paths))
|
||||
.then((paths) => formatResponse(pathfind, paths))
|
||||
.then((paths) => formatResponse(pathfind, paths));
|
||||
}
|
||||
|
||||
export default getPaths
|
||||
export default getPaths;
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import _ from 'lodash'
|
||||
import {validate, ensureClassicAddress} from '../common'
|
||||
import parseAccountTrustline from './parse/account-trustline'
|
||||
import {Client} from '..'
|
||||
import {FormattedTrustline} from '../common/types/objects/trustlines'
|
||||
import _ from "lodash";
|
||||
|
||||
export type GetTrustlinesOptions = {
|
||||
counterparty?: string
|
||||
currency?: string
|
||||
limit?: number
|
||||
ledgerVersion?: number
|
||||
import type { Client } from "..";
|
||||
import { validate, ensureClassicAddress } from "../common";
|
||||
import { FormattedTrustline } from "../common/types/objects";
|
||||
|
||||
import parseAccountTrustline from "./parse/account-trustline";
|
||||
|
||||
export interface GetTrustlinesOptions {
|
||||
counterparty?: string;
|
||||
currency?: string;
|
||||
limit?: number;
|
||||
ledgerVersion?: number;
|
||||
}
|
||||
|
||||
function currencyFilter(currency: string, trustline: FormattedTrustline) {
|
||||
return currency === null || trustline.specification.currency === currency
|
||||
function currencyFilter(
|
||||
currency: string | null,
|
||||
trustline: FormattedTrustline
|
||||
) {
|
||||
return currency === null || trustline.specification.currency === currency;
|
||||
}
|
||||
|
||||
async function getTrustlines(
|
||||
@@ -21,25 +26,26 @@ async function getTrustlines(
|
||||
options: GetTrustlinesOptions = {}
|
||||
): Promise<FormattedTrustline[]> {
|
||||
// 1. Validate
|
||||
validate.getTrustlines({address, options})
|
||||
validate.getTrustlines({ address, options });
|
||||
|
||||
// Only support retrieving trustlines without a tag,
|
||||
// since it does not make sense to filter trustlines
|
||||
// by tag.
|
||||
address = ensureClassicAddress(address)
|
||||
address = ensureClassicAddress(address);
|
||||
|
||||
// 2. Make Request
|
||||
const responses = await this.requestAll({command: 'account_lines',
|
||||
const responses = await this.requestAll({
|
||||
command: "account_lines",
|
||||
account: address,
|
||||
ledger_index: options.ledgerVersion ?? 'validated',
|
||||
ledger_index: options.ledgerVersion ?? "validated",
|
||||
limit: options.limit,
|
||||
peer: options.counterparty
|
||||
})
|
||||
peer: options.counterparty,
|
||||
});
|
||||
// 3. Return Formatted Response
|
||||
const trustlines = _.flatMap(responses, (response) => response.result.lines)
|
||||
const trustlines = _.flatMap(responses, (response) => response.result.lines);
|
||||
return trustlines.map(parseAccountTrustline).filter((trustline) => {
|
||||
return currencyFilter(options.currency || null, trustline)
|
||||
})
|
||||
return currencyFilter(options.currency ?? null, trustline);
|
||||
});
|
||||
}
|
||||
|
||||
export default getTrustlines
|
||||
export default getTrustlines;
|
||||
|
||||
@@ -1,23 +1,27 @@
|
||||
import _ from 'lodash'
|
||||
import * as assert from 'assert'
|
||||
import * as common from '../common'
|
||||
import {Connection} from '../client'
|
||||
import {FormattedTransactionType} from '../transaction/types'
|
||||
import {Issue} from '../common/types/objects'
|
||||
import {Client} from '..'
|
||||
import {AccountInfoRequest} from '../models/methods'
|
||||
import {dropsToXrp} from '..'
|
||||
import * as assert from "assert";
|
||||
|
||||
export type RecursiveData = {
|
||||
marker: string
|
||||
results: Array<any>
|
||||
import _ from "lodash";
|
||||
|
||||
import { Client, dropsToXrp } from "..";
|
||||
import { Connection } from "../client";
|
||||
import * as common from "../common";
|
||||
import { Issue } from "../common/types/objects";
|
||||
import { AccountInfoRequest } from "../models/methods";
|
||||
import { FormattedTransactionType } from "../transaction/types";
|
||||
|
||||
export interface RecursiveData {
|
||||
marker: string;
|
||||
results: any[];
|
||||
}
|
||||
|
||||
export type Getter = (marker?: string, limit?: number) => Promise<RecursiveData>
|
||||
export type Getter = (
|
||||
marker?: string,
|
||||
limit?: number
|
||||
) => Promise<RecursiveData>;
|
||||
|
||||
function clamp(value: number, min: number, max: number): number {
|
||||
assert.ok(min <= max, 'Illegal clamp bounds')
|
||||
return Math.min(Math.max(value, min), max)
|
||||
assert.ok(min <= max, "Illegal clamp bounds");
|
||||
return Math.min(Math.max(value, min), max);
|
||||
}
|
||||
|
||||
async function getXRPBalance(
|
||||
@@ -26,13 +30,12 @@ async function getXRPBalance(
|
||||
ledgerVersion?: number
|
||||
): Promise<string> {
|
||||
const request: AccountInfoRequest = {
|
||||
command: 'account_info',
|
||||
command: "account_info",
|
||||
account: address,
|
||||
ledger_index: ledgerVersion
|
||||
}
|
||||
const data = await client
|
||||
.request(request)
|
||||
return dropsToXrp(data.result.account_data.Balance)
|
||||
ledger_index: ledgerVersion,
|
||||
};
|
||||
const data = await client.request(request);
|
||||
return dropsToXrp(data.result.account_data.Balance);
|
||||
}
|
||||
|
||||
// If the marker is omitted from a response, you have reached the end
|
||||
@@ -40,64 +43,71 @@ async function getRecursiveRecur(
|
||||
getter: Getter,
|
||||
marker: string | undefined,
|
||||
limit: number
|
||||
): Promise<Array<any>> {
|
||||
const data = await getter(marker, limit)
|
||||
const remaining = limit - data.results.length
|
||||
): Promise<any[]> {
|
||||
const data = await getter(marker, limit);
|
||||
const remaining = limit - data.results.length;
|
||||
if (remaining > 0 && data.marker != null) {
|
||||
return getRecursiveRecur(getter, data.marker, remaining).then((results) => data.results.concat(results)
|
||||
)
|
||||
return getRecursiveRecur(getter, data.marker, remaining).then((results) =>
|
||||
data.results.concat(results)
|
||||
);
|
||||
}
|
||||
return data.results.slice(0, limit)
|
||||
return data.results.slice(0, limit);
|
||||
}
|
||||
|
||||
function getRecursive(getter: Getter, limit?: number): Promise<Array<any>> {
|
||||
return getRecursiveRecur(getter, undefined, limit || Infinity)
|
||||
function getRecursive(getter: Getter, limit?: number): Promise<any[]> {
|
||||
return getRecursiveRecur(getter, undefined, limit || Infinity);
|
||||
}
|
||||
|
||||
function renameCounterpartyToIssuer<T>(
|
||||
obj: T & {counterparty?: string; issuer?: string}
|
||||
): T & {issuer?: string} {
|
||||
obj: T & { counterparty?: string; issuer?: string }
|
||||
): T & { issuer?: string } {
|
||||
const issuer =
|
||||
obj.counterparty != null
|
||||
? obj.counterparty
|
||||
: obj.issuer != null
|
||||
? obj.issuer
|
||||
: undefined
|
||||
const withIssuer = Object.assign({}, obj, {issuer})
|
||||
delete withIssuer.counterparty
|
||||
return withIssuer
|
||||
: undefined;
|
||||
const withIssuer = { ...obj, issuer };
|
||||
delete withIssuer.counterparty;
|
||||
return withIssuer;
|
||||
}
|
||||
|
||||
export type RequestBookOffersArgs = {taker_gets: Issue; taker_pays: Issue}
|
||||
export interface RequestBookOffersArgs {
|
||||
taker_gets: Issue;
|
||||
taker_pays: Issue;
|
||||
}
|
||||
|
||||
function renameCounterpartyToIssuerInOrder(order: RequestBookOffersArgs) {
|
||||
const taker_gets = renameCounterpartyToIssuer(order.taker_gets)
|
||||
const taker_pays = renameCounterpartyToIssuer(order.taker_pays)
|
||||
const changes = {taker_gets, taker_pays}
|
||||
return Object.assign({}, order, _.omitBy(changes, value => value == null))
|
||||
const taker_gets = renameCounterpartyToIssuer(order.taker_gets);
|
||||
const taker_pays = renameCounterpartyToIssuer(order.taker_pays);
|
||||
const changes = { taker_gets, taker_pays };
|
||||
return { ...order, ..._.omitBy(changes, (value) => value == null) };
|
||||
}
|
||||
|
||||
function signum(num) {
|
||||
return num === 0 ? 0 : num > 0 ? 1 : -1
|
||||
return num === 0 ? 0 : num > 0 ? 1 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order two rippled transactions based on their ledger_index.
|
||||
* If two transactions took place in the same ledger, sort
|
||||
* them based on TransactionIndex
|
||||
* See: https://developers.ripple.com/transaction-metadata.html
|
||||
* Order two rippled transactions based on their ledger_index.
|
||||
* If two transactions took place in the same ledger, sort
|
||||
* them based on TransactionIndex
|
||||
* See: https://developers.ripple.com/transaction-metadata.html.
|
||||
*
|
||||
* @param first
|
||||
* @param second
|
||||
*/
|
||||
function compareTransactions(
|
||||
first: FormattedTransactionType,
|
||||
second: FormattedTransactionType
|
||||
): number {
|
||||
if (!first.outcome || !second.outcome) {
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) {
|
||||
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger)
|
||||
return signum(first.outcome.indexInLedger - second.outcome.indexInLedger);
|
||||
}
|
||||
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1
|
||||
return first.outcome.ledgerVersion < second.outcome.ledgerVersion ? -1 : 1;
|
||||
}
|
||||
|
||||
async function isPendingLedgerVersion(
|
||||
@@ -105,27 +115,30 @@ async function isPendingLedgerVersion(
|
||||
maxLedgerVersion?: number
|
||||
): Promise<boolean> {
|
||||
const response = await client.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated'
|
||||
})
|
||||
const ledgerVersion = response.result.ledger_index
|
||||
return ledgerVersion < (maxLedgerVersion || 0)
|
||||
command: "ledger",
|
||||
ledger_index: "validated",
|
||||
});
|
||||
const ledgerVersion = response.result.ledger_index;
|
||||
return ledgerVersion < (maxLedgerVersion || 0);
|
||||
}
|
||||
|
||||
async function ensureLedgerVersion(this: Client, options: any): Promise<object> {
|
||||
async function ensureLedgerVersion(
|
||||
this: Client,
|
||||
options: any
|
||||
): Promise<object> {
|
||||
if (
|
||||
Boolean(options) &&
|
||||
options.ledgerVersion != null &&
|
||||
options.ledgerVersion !== null
|
||||
) {
|
||||
return Promise.resolve(options)
|
||||
return Promise.resolve(options);
|
||||
}
|
||||
const response = await this.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated'
|
||||
})
|
||||
const ledgerVersion = response.result.ledger_index
|
||||
return Object.assign({}, options, { ledgerVersion })
|
||||
command: "ledger",
|
||||
ledger_index: "validated",
|
||||
});
|
||||
const ledgerVersion = response.result.ledger_index;
|
||||
return { ...options, ledgerVersion };
|
||||
}
|
||||
|
||||
export {
|
||||
@@ -138,5 +151,5 @@ export {
|
||||
isPendingLedgerVersion,
|
||||
clamp,
|
||||
common,
|
||||
Connection
|
||||
}
|
||||
Connection,
|
||||
};
|
||||
|
||||
@@ -1,23 +1,29 @@
|
||||
export type LedgerIndex = number | ('validated' | 'closed' | 'current')
|
||||
export type LedgerIndex = number | ("validated" | "closed" | "current");
|
||||
|
||||
export type AccountObjectType = 'check' | 'escrow' | 'offer' | 'payment_channel' | 'signer_list' | 'state'
|
||||
export type AccountObjectType =
|
||||
| "check"
|
||||
| "escrow"
|
||||
| "offer"
|
||||
| "payment_channel"
|
||||
| "signer_list"
|
||||
| "state";
|
||||
|
||||
export interface XRP {
|
||||
currency: "XRP"
|
||||
currency: "XRP";
|
||||
}
|
||||
|
||||
export interface IssuedCurrency {
|
||||
currency: string
|
||||
issuer: string
|
||||
currency: string;
|
||||
issuer: string;
|
||||
}
|
||||
|
||||
export type Currency = IssuedCurrency | XRP
|
||||
export type Currency = IssuedCurrency | XRP;
|
||||
|
||||
export interface IssuedCurrencyAmount extends IssuedCurrency {
|
||||
value: string
|
||||
value: string;
|
||||
}
|
||||
|
||||
export type Amount = IssuedCurrencyAmount | string
|
||||
export type Amount = IssuedCurrencyAmount | string;
|
||||
|
||||
export interface Signer {
|
||||
Account: string;
|
||||
@@ -31,17 +37,25 @@ export interface Memo {
|
||||
MemoFormat?: string;
|
||||
}
|
||||
|
||||
export type StreamType = "consensus" | "ledger" | "manifests" | "peer_status" | "transactions" | "transactions_proposed" | "server" | "validations"
|
||||
export type StreamType =
|
||||
| "consensus"
|
||||
| "ledger"
|
||||
| "manifests"
|
||||
| "peer_status"
|
||||
| "transactions"
|
||||
| "transactions_proposed"
|
||||
| "server"
|
||||
| "validations";
|
||||
|
||||
interface PathStep {
|
||||
account?: string
|
||||
currency?: string
|
||||
issuer?: string
|
||||
account?: string;
|
||||
currency?: string;
|
||||
issuer?: string;
|
||||
}
|
||||
|
||||
export type Path = PathStep[]
|
||||
export type Path = PathStep[];
|
||||
|
||||
export interface SignerEntry {
|
||||
Account: string;
|
||||
SignerWeight: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import { Amount } from ".";
|
||||
|
||||
interface CreatedNode {
|
||||
CreatedNode: {
|
||||
LedgerEntryType: string
|
||||
LedgerIndex: string
|
||||
NewFields: {[field: string]: any}
|
||||
}
|
||||
CreatedNode: {
|
||||
LedgerEntryType: string;
|
||||
LedgerIndex: string;
|
||||
NewFields: { [field: string]: any };
|
||||
};
|
||||
}
|
||||
|
||||
interface ModifiedNode {
|
||||
ModifiedNode: {
|
||||
LedgerEntryType: string
|
||||
LedgerIndex: string
|
||||
FinalFields: {[field: string]: any}
|
||||
PreviousFields: {[field: string]: any}
|
||||
PreviousTxnID?: string
|
||||
PreviouTxnLgrSeq?: number
|
||||
}
|
||||
ModifiedNode: {
|
||||
LedgerEntryType: string;
|
||||
LedgerIndex: string;
|
||||
FinalFields: { [field: string]: any };
|
||||
PreviousFields: { [field: string]: any };
|
||||
PreviousTxnID?: string;
|
||||
PreviouTxnLgrSeq?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeletedNode {
|
||||
DeletedNode: {
|
||||
LedgerEntryType: string
|
||||
LedgerIndex: string
|
||||
FinalFields: {[field: string]: any}
|
||||
}
|
||||
DeletedNode: {
|
||||
LedgerEntryType: string;
|
||||
LedgerIndex: string;
|
||||
FinalFields: { [field: string]: any };
|
||||
};
|
||||
}
|
||||
|
||||
type Node = CreatedNode | ModifiedNode | DeletedNode
|
||||
type Node = CreatedNode | ModifiedNode | DeletedNode;
|
||||
|
||||
export default interface Metadata {
|
||||
AffectedNodes: Node[]
|
||||
DeliveredAmount?: Amount
|
||||
delivered_amount?: Amount
|
||||
TransactionIndex: number
|
||||
TransactionResult: string
|
||||
}
|
||||
AffectedNodes: Node[];
|
||||
DeliveredAmount?: Amount;
|
||||
delivered_amount?: Amount;
|
||||
TransactionIndex: number;
|
||||
TransactionResult: string;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
import { Amount } from ".";
|
||||
|
||||
interface CreatedNode {
|
||||
CreatedNode: {
|
||||
LedgerEntryType: string
|
||||
LedgerIndex: string
|
||||
NewFields: {[field: string]: any}
|
||||
}
|
||||
CreatedNode: {
|
||||
LedgerEntryType: string;
|
||||
LedgerIndex: string;
|
||||
NewFields: { [field: string]: any };
|
||||
};
|
||||
}
|
||||
|
||||
interface ModifiedNode {
|
||||
ModifiedNode: {
|
||||
LedgerEntryType: string
|
||||
LedgerIndex: string
|
||||
FinalFields: {[field: string]: any}
|
||||
PreviousFields: {[field: string]: any}
|
||||
PreviousTxnID?: string
|
||||
PreviouTxnLgrSeq?: number
|
||||
}
|
||||
ModifiedNode: {
|
||||
LedgerEntryType: string;
|
||||
LedgerIndex: string;
|
||||
FinalFields: { [field: string]: any };
|
||||
PreviousFields: { [field: string]: any };
|
||||
PreviousTxnID?: string;
|
||||
PreviouTxnLgrSeq?: number;
|
||||
};
|
||||
}
|
||||
|
||||
interface DeletedNode {
|
||||
DeletedNode: {
|
||||
LedgerEntryType: string
|
||||
LedgerIndex: string
|
||||
FinalFields: {[field: string]: any}
|
||||
}
|
||||
DeletedNode: {
|
||||
LedgerEntryType: string;
|
||||
LedgerIndex: string;
|
||||
FinalFields: { [field: string]: any };
|
||||
};
|
||||
}
|
||||
|
||||
type Node = CreatedNode | ModifiedNode | DeletedNode
|
||||
type Node = CreatedNode | ModifiedNode | DeletedNode;
|
||||
|
||||
export interface TransactionMetadata {
|
||||
AffectedNodes: Node[]
|
||||
DeliveredAmount?: Amount
|
||||
delivered_amount?: Amount
|
||||
TransactionIndex: number
|
||||
TransactionResult: string
|
||||
}
|
||||
AffectedNodes: Node[];
|
||||
DeliveredAmount?: Amount;
|
||||
delivered_amount?: Amount;
|
||||
TransactionIndex: number;
|
||||
TransactionResult: string;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { BaseLedgerEntry } from "./baseLedgerEntry";
|
||||
|
||||
export interface AccountRoot extends BaseLedgerEntry{
|
||||
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
|
||||
TicketCount?: number
|
||||
TickSize?: number
|
||||
TransferRate?: number
|
||||
}
|
||||
export interface AccountRoot extends BaseLedgerEntry {
|
||||
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;
|
||||
TicketCount?: number;
|
||||
TickSize?: number;
|
||||
TransferRate?: number;
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ import { BaseLedgerEntry } from "./baseLedgerEntry";
|
||||
|
||||
interface Majority {
|
||||
Majority: {
|
||||
Amendment: string
|
||||
CloseTime: number
|
||||
}
|
||||
Amendment: string;
|
||||
CloseTime: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Amendments extends BaseLedgerEntry {
|
||||
LedgerEntryType: 'Amendments'
|
||||
Amendments?: string[]
|
||||
Majorities?: Majority[]
|
||||
Flags: 0
|
||||
}
|
||||
LedgerEntryType: "Amendments";
|
||||
Amendments?: string[];
|
||||
Majorities?: Majority[];
|
||||
Flags: 0;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export interface BaseLedgerEntry {
|
||||
index: string
|
||||
}
|
||||
index: string;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { Amount } from "../common";
|
||||
|
||||
import { BaseLedgerEntry } from "./baseLedgerEntry";
|
||||
|
||||
export interface Check extends BaseLedgerEntry {
|
||||
LedgerEntryType: 'Check'
|
||||
Account: string
|
||||
Destination: string
|
||||
Flags: 0
|
||||
OwnerNode: string
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
SendMax: Amount
|
||||
Sequence: number
|
||||
DestinationNode?: string
|
||||
DestinationTag?: number
|
||||
Expiration?: number
|
||||
InvoiceID?: string
|
||||
SourceTag?: number
|
||||
}
|
||||
LedgerEntryType: "Check";
|
||||
Account: string;
|
||||
Destination: string;
|
||||
Flags: 0;
|
||||
OwnerNode: string;
|
||||
PreviousTxnID: string;
|
||||
PreviousTxnLgrSeq: number;
|
||||
SendMax: Amount;
|
||||
Sequence: number;
|
||||
DestinationNode?: string;
|
||||
DestinationTag?: number;
|
||||
Expiration?: number;
|
||||
InvoiceID?: string;
|
||||
SourceTag?: number;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { BaseLedgerEntry } from "./baseLedgerEntry";
|
||||
|
||||
export interface DepositPreauth extends BaseLedgerEntry {
|
||||
LedgerEntryType: 'DepositPreauth'
|
||||
Account: string
|
||||
Authorize: string
|
||||
Flags: 0
|
||||
OwnerNode: string
|
||||
PreviousTxnID: string
|
||||
PreviousTxnLgrSeq: number
|
||||
}
|
||||
LedgerEntryType: "DepositPreauth";
|
||||
Account: string;
|
||||
Authorize: string;
|
||||
Flags: 0;
|
||||
OwnerNode: string;
|
||||
PreviousTxnID: string;
|
||||
PreviousTxnLgrSeq: number;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { BaseLedgerEntry } from "./baseLedgerEntry";
|
||||
|
||||
export interface DirectoryNode extends BaseLedgerEntry {
|
||||
LedgerEntryType: 'DirectoryNode'
|
||||
Flags: number
|
||||
RootIndex: string
|
||||
Indexes: string[]
|
||||
IndexNext?: number
|
||||
IndexPrevious?: number
|
||||
Owner?: string
|
||||
TakerPaysCurrency?: string
|
||||
TakerPaysIssuer?: string
|
||||
TakerGetsCurrency?: string
|
||||
TakerGetsIssuer?: string
|
||||
}
|
||||
LedgerEntryType: "DirectoryNode";
|
||||
Flags: number;
|
||||
RootIndex: string;
|
||||
Indexes: string[];
|
||||
IndexNext?: number;
|
||||
IndexPrevious?: number;
|
||||
Owner?: string;
|
||||
TakerPaysCurrency?: string;
|
||||
TakerPaysIssuer?: string;
|
||||
TakerGetsCurrency?: string;
|
||||
TakerGetsIssuer?: string;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { BaseLedgerEntry } from "./baseLedgerEntry";
|
||||
|
||||
export interface Escrow extends BaseLedgerEntry {
|
||||
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
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user