mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-15 18:15:49 +00:00
Compare commits
58 Commits
0.13.0-rc1
...
0.13.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed40eec711 | ||
|
|
43488c55f1 | ||
|
|
2f727b553c | ||
|
|
29c37aa6da | ||
|
|
64baef431d | ||
|
|
634fe5683a | ||
|
|
225ca3f852 | ||
|
|
ff2ac6c3cd | ||
|
|
607777f2a3 | ||
|
|
78eeb40322 | ||
|
|
772f79ae21 | ||
|
|
806a4e823f | ||
|
|
323e402e0c | ||
|
|
9ebb59580d | ||
|
|
bafab6eb18 | ||
|
|
35acbb62c3 | ||
|
|
4676ade4ee | ||
|
|
8edc3b1f36 | ||
|
|
8acfb1a537 | ||
|
|
ac78171099 | ||
|
|
d573c5746b | ||
|
|
51e8f9a87a | ||
|
|
bfe590d96d | ||
|
|
60e2d10775 | ||
|
|
2f432cef62 | ||
|
|
5217b66396 | ||
|
|
b8bb191d24 | ||
|
|
8070a52dc7 | ||
|
|
3205f3cf8c | ||
|
|
e0cdd610dd | ||
|
|
ed3b04ed6f | ||
|
|
62a2d2ae39 | ||
|
|
e2c853e40d | ||
|
|
b9b5a71869 | ||
|
|
87fdbc932f | ||
|
|
84838b2e9f | ||
|
|
c2ca37a790 | ||
|
|
c6805b9f0d | ||
|
|
b1dbdc03dd | ||
|
|
88a3f3d43b | ||
|
|
5f8dcd71a5 | ||
|
|
45db95da79 | ||
|
|
c79b12b27f | ||
|
|
135da6108d | ||
|
|
0d6dda579f | ||
|
|
e641a347db | ||
|
|
3e17d91edf | ||
|
|
715c648d52 | ||
|
|
d0ebed9822 | ||
|
|
a3775f18ba | ||
|
|
7b5d6e9fc5 | ||
|
|
368ac0b9e0 | ||
|
|
0448696bd8 | ||
|
|
deb75ed0d7 | ||
|
|
1c023c4377 | ||
|
|
de67570230 | ||
|
|
f282585c3f | ||
|
|
ae5ff31c96 |
19
HISTORY.md
19
HISTORY.md
@@ -1,14 +1,29 @@
|
||||
##0.13.0 (release candidate)
|
||||
|
||||
+ [Fix: Emit error events and return error on pathfind](https://github.com/ripple/ripple-lib/commit/1ccbaf677631a1944eb05d90f7afc5f3690a03dd)
|
||||
+ [Deprecate core and remove snake case method copying](https://github.com/ripple/ripple-lib/commit/fb8dc44ec1d49bb05cd0cdbe6dd4ab211195868a)
|
||||
**Breaking Changes**
|
||||
+ [Removed timeout method of Request and added default timeout](https://github.com/ripple/ripple-lib/commit/634fe5683a9082e57682ff7d5c4fb9483b4af818)
|
||||
+ Add new RippleAPI interface
|
||||
- [RippleAPI README and samples](https://github.com/ripple/ripple-lib/tree/develop/docs/samples)
|
||||
- [Method documentation](https://rawgit.com/ripple/ripple-lib/develop/docs/api.html)
|
||||
|
||||
**Changes**
|
||||
+ [Implement Balance Sheet API](https://github.com/ripple/ripple-lib/pull/579)
|
||||
+ [Fix crash due to rippled slowDown error](https://github.com/ripple/ripple-lib/commit/84838b2e9f6969b593b8462a62a6b8f516ada937)
|
||||
+ [Fix: Emit error events and return error on pathfind](https://github.com/ripple/ripple-lib/commit/1ccbaf677631a1944eb05d90f7afc5f3690a03dd)
|
||||
+ [Deprecate core and remove snake case method copying](https://github.com/ripple/ripple-lib/commit/fb8dc44ec1d49bb05cd0cdbe6dd4ab211195868a)
|
||||
|
||||
+ [Fix RangeSet for validated_ledger as single ledger](https://github.com/ripple/ripple-lib/commit/9f9e76f8b933201651af59307135f67cfa7d60e8)
|
||||
+ [Fix bug where the paths would be set with an empty array](https://github.com/ripple/ripple-lib/commit/83874ec0962da311b76f2385623e51c68bc39035)
|
||||
+ [Fix reserve calculation](https://github.com/ripple/ripple-lib/commit/52879febb92d876f01f2e4d70871baa07af631fb)
|
||||
|
||||
##0.12.7 and 0.12.8
|
||||
|
||||
+ [Improve performance of orderbook](https://github.com/ripple/ripple-lib/commit/c745faaaf0956ca98448a754b4fe97fb50574fc7)
|
||||
|
||||
+ [Remove Firefox warning about prototype overwrite](https://github.com/ripple/ripple-lib/commit/0c62fa21123b220b066871e1c41a3b4fe6f51885)
|
||||
|
||||
+ [Fix compare bug in Amount class](https://github.com/ripple/ripple-lib/commit/806547dd154e1b0bf252e8a74ad3ac6aa8a97660)
|
||||
|
||||
##0.12.6
|
||||
|
||||
+ [Fix webpack require failure due to "./" notation](https://github.com/ripple/ripple-lib/commit/8d9746d7b10be203ee613df523c2522012ff1baf)
|
||||
|
||||
48
npm-shrinkwrap.json
generated
48
npm-shrinkwrap.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.13.0-rc10",
|
||||
"version": "0.13.0-rc12",
|
||||
"npm-shrinkwrap-version": "5.4.0",
|
||||
"node-version": "v0.12.7",
|
||||
"dependencies": {
|
||||
@@ -9,8 +9,8 @@
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "5.8.24",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.24.tgz",
|
||||
"version": "5.8.25",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.25.tgz",
|
||||
"dependencies": {
|
||||
"core-js": {
|
||||
"version": "1.1.4",
|
||||
@@ -117,12 +117,12 @@
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.tgz"
|
||||
},
|
||||
"ripple-address-codec": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-1.6.0.tgz",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-2.0.1.tgz",
|
||||
"dependencies": {
|
||||
"x-address-codec": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.6.0.tgz",
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.7.0.tgz",
|
||||
"dependencies": {
|
||||
"base-x": {
|
||||
"version": "1.0.1",
|
||||
@@ -133,8 +133,8 @@
|
||||
}
|
||||
},
|
||||
"ripple-keypairs": {
|
||||
"version": "0.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.8.0.tgz",
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-0.9.0.tgz",
|
||||
"dependencies": {
|
||||
"brorand": {
|
||||
"version": "1.0.5",
|
||||
@@ -149,18 +149,32 @@
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-address-codec": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-address-codec/-/ripple-address-codec-1.6.0.tgz",
|
||||
"dependencies": {
|
||||
"x-address-codec": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/x-address-codec/-/x-address-codec-0.6.0.tgz",
|
||||
"dependencies": {
|
||||
"base-x": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/base-x/-/base-x-1.0.1.tgz"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ripple-lib-transactionparser": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.0.tgz",
|
||||
"dependencies": {
|
||||
"bignumber.js": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-1.4.1.tgz"
|
||||
}
|
||||
}
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-transactionparser/-/ripple-lib-transactionparser-0.5.1.tgz"
|
||||
},
|
||||
"ripple-lib-value": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ripple-lib-value/-/ripple-lib-value-0.1.0.tgz"
|
||||
},
|
||||
"sjcl-codec": {
|
||||
"version": "0.1.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ripple-lib",
|
||||
"version": "0.13.0-rc10",
|
||||
"version": "0.13.0-rc12",
|
||||
"license": "ISC",
|
||||
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
|
||||
"files": [
|
||||
@@ -26,9 +26,10 @@
|
||||
"is-my-json-valid": "^2.12.2",
|
||||
"lodash": "^3.1.0",
|
||||
"lru-cache": "~2.5.0",
|
||||
"ripple-address-codec": "^1.6.0",
|
||||
"ripple-keypairs": "^0.8.0",
|
||||
"ripple-lib-transactionparser": "^0.5.0",
|
||||
"ripple-address-codec": "^2.0.1",
|
||||
"ripple-keypairs": "^0.9.0",
|
||||
"ripple-lib-transactionparser": "^0.5.1",
|
||||
"ripple-lib-value": "0.1.0",
|
||||
"sjcl-codec": "0.1.0",
|
||||
"ws": "~0.7.1"
|
||||
},
|
||||
|
||||
@@ -31,6 +31,8 @@ function loadSchemas() {
|
||||
require('./schemas/currency.json'),
|
||||
require('./schemas/get-account-info.json'),
|
||||
require('./schemas/get-balances.json'),
|
||||
require('./schemas/get-balance-sheet'),
|
||||
require('./schemas/balance-sheet-options.json'),
|
||||
require('./schemas/get-ledger.json'),
|
||||
require('./schemas/get-orderbook.json'),
|
||||
require('./schemas/get-orders.json'),
|
||||
|
||||
@@ -4,20 +4,8 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"amount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tag": {
|
||||
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
"amount": {"$ref": "amount"},
|
||||
"tag": {"$ref": "tag"}
|
||||
},
|
||||
"required": ["address", "amount"],
|
||||
"additionalProperties": false
|
||||
|
||||
15
src/api/common/schemas/balance-sheet-options.json
Normal file
15
src/api/common/schemas/balance-sheet-options.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "balance-sheet-options",
|
||||
"description": "Options for getBalanceSheet",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"excludeAddresses": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "address"},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"ledgerVersion": {"$ref": "ledgerVersion"}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
9
src/api/common/schemas/destinationAdjustment.json
Normal file
9
src/api/common/schemas/destinationAdjustment.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "destinationAdjustment",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{"$ref": "adjustment"},
|
||||
{"$ref": "minAdjustment"}
|
||||
]
|
||||
}
|
||||
29
src/api/common/schemas/get-balance-sheet.json
Normal file
29
src/api/common/schemas/get-balance-sheet.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "getBalanceSheet",
|
||||
"description": "getBalanceSheet response",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"balances": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "amount"}
|
||||
},
|
||||
"assets": {
|
||||
"type": "array",
|
||||
"items": {"$ref": "amount"}
|
||||
},
|
||||
"obligations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"value": {"$ref": "value"}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "maxAdjustment"},
|
||||
"destination": {"$ref": "adjustment"},
|
||||
"source": {"$ref": "sourceAdjustment"},
|
||||
"destination": {"$ref": "destinationAdjustment"},
|
||||
"paths": {"type": "string"}
|
||||
},
|
||||
"required": ["source", "destination", "paths"],
|
||||
|
||||
13
src/api/common/schemas/lax-amount.json
Normal file
13
src/api/common/schemas/lax-amount.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "laxAmount",
|
||||
"description": "Amount where counterparty is optional",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
13
src/api/common/schemas/lax-lax-amount.json
Normal file
13
src/api/common/schemas/lax-lax-amount.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "laxLaxAmount",
|
||||
"description": "Amount where counterparty and value are optional",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -4,20 +4,8 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"maxAmount": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"currency": {"$ref": "currency"},
|
||||
"counterparty": {"$ref": "address"},
|
||||
"value": {"$ref": "value"}
|
||||
},
|
||||
"required": ["currency", "value"],
|
||||
"additionalProperties": false
|
||||
},
|
||||
"tag": {
|
||||
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
"maxAmount": {"$ref": "laxAmount"},
|
||||
"tag": {"$ref": "tag"}
|
||||
},
|
||||
"required": ["address", "maxAmount"],
|
||||
"additionalProperties": false
|
||||
|
||||
12
src/api/common/schemas/min-adjustment.json
Normal file
12
src/api/common/schemas/min-adjustment.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "minAdjustment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"minAmount": {"$ref": "laxAmount"},
|
||||
"tag": {"$ref": "tag"}
|
||||
},
|
||||
"required": ["address", "minAmount"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
},
|
||||
"quantity": {"$ref": "balance"},
|
||||
"totalPrice": {"$ref": "balance"},
|
||||
"makerExchangeRate": {"$ref": "value"},
|
||||
"sequence": {"$ref": "sequence"},
|
||||
"status": {"enum": ["created", "open", "closed", "canceled"]}
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"amount": {"$ref": "laxAmount"},
|
||||
"currencies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -19,12 +20,23 @@
|
||||
"additionalProperties": false
|
||||
},
|
||||
"uniqueItems": true
|
||||
},
|
||||
"not": {
|
||||
"required": ["amount", "currencies"]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["address"]
|
||||
},
|
||||
"destination": {"$ref": "adjustment"}
|
||||
"destination": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {"$ref": "address"},
|
||||
"amount": {"$ref": "laxLaxAmount"}
|
||||
},
|
||||
"required": ["address", "amount"],
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"required": ["source", "destination"],
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"title": "payment",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"source": {"$ref": "maxAdjustment"},
|
||||
"destination": {"$ref": "adjustment"},
|
||||
"source": {"$ref": "sourceAdjustment"},
|
||||
"destination": {"$ref": "destinationAdjustment"},
|
||||
"paths": {"type": "string"},
|
||||
"memos": {
|
||||
"type": "array",
|
||||
|
||||
9
src/api/common/schemas/sourceAdjustment.json
Normal file
9
src/api/common/schemas/sourceAdjustment.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "sourceAdjustment",
|
||||
"type": "object",
|
||||
"oneOf": [
|
||||
{"$ref": "adjustment"},
|
||||
{"$ref": "maxAdjustment"}
|
||||
]
|
||||
}
|
||||
6
src/api/common/schemas/tag.json
Normal file
6
src/api/common/schemas/tag.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "tag",
|
||||
"description": "A string representing an unsigned 32-bit integer most commonly used to refer to a sender's hosted account at a Ripple gateway",
|
||||
"$ref": "uint32"
|
||||
}
|
||||
@@ -29,8 +29,10 @@ function toRippledAmount(amount: Amount): string|Amount {
|
||||
}
|
||||
|
||||
function generateAddress(options?: Object): Object {
|
||||
const {accountID, seed} = keypairs.generateWallet(options);
|
||||
return {secret: seed, address: accountID};
|
||||
const secret = keypairs.generateSeed(options);
|
||||
const keypair = keypairs.deriveKeypair(secret);
|
||||
const address = keypairs.deriveAddress(keypair.publicKey);
|
||||
return {secret, address};
|
||||
}
|
||||
|
||||
type AsyncFunction = (...x: any) => void
|
||||
@@ -69,7 +71,8 @@ function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
|
||||
function convertErrors(callback: Callback): () => void {
|
||||
return function(error, data) {
|
||||
if (error && !(error instanceof errors.RippleError)) {
|
||||
const error_ = new errors.RippleError(error);
|
||||
const message = _.get(error, ['remote', 'error_message'], error.message);
|
||||
const error_ = new errors.RippleError(message);
|
||||
error_.data = data;
|
||||
callback(error_, data);
|
||||
} else if (error) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const core = require('./utils').core;
|
||||
const deriveKeypair = require('ripple-keypairs').deriveKeypair;
|
||||
const ValidationError = require('./errors').ValidationError;
|
||||
const schemaValidate = require('./schema-validator').schemaValidate;
|
||||
|
||||
@@ -9,6 +9,15 @@ function error(text) {
|
||||
return new ValidationError(text);
|
||||
}
|
||||
|
||||
function isValidSecret(secret) {
|
||||
try {
|
||||
deriveKeypair(secret);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function validateAddressAndSecret(obj: {address: string, secret: string}
|
||||
): void {
|
||||
const address = obj.address;
|
||||
@@ -17,8 +26,8 @@ function validateAddressAndSecret(obj: {address: string, secret: string}
|
||||
if (!secret) {
|
||||
throw error('Parameter missing: secret');
|
||||
}
|
||||
if (!core.Seed.from_json(secret).is_valid()) {
|
||||
throw error('secret is invalid');
|
||||
if (!isValidSecret(secret)) {
|
||||
throw error('Invalid parameter: secret');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +35,9 @@ function validateSecret(secret: string): void {
|
||||
if (!secret) {
|
||||
throw error('Parameter missing: secret');
|
||||
}
|
||||
if (typeof secret !== 'string' || secret[0] !== 's') {
|
||||
throw error('Invalid parameter');
|
||||
}
|
||||
|
||||
const seed = new core.Seed().parse_base58(secret);
|
||||
if (!seed.is_valid()) {
|
||||
throw error('invalid seed');
|
||||
if (typeof secret !== 'string' || secret[0] !== 's'
|
||||
|| !isValidSecret(secret)) {
|
||||
throw error('Invalid parameter: secret');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +82,7 @@ module.exports = {
|
||||
getAccountInfoOptions: _.partial(validateOptions, 'settings-options'),
|
||||
getTrustlinesOptions: _.partial(validateOptions, 'trustlines-options'),
|
||||
getBalancesOptions: _.partial(validateOptions, 'trustlines-options'),
|
||||
getBalanceSheetOptions: _.partial(validateOptions, 'balance-sheet-options'),
|
||||
getOrdersOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getOrderbookOptions: _.partial(validateOptions, 'orders-options'),
|
||||
getTransactionOptions: _.partial(validateOptions, 'transaction-options'),
|
||||
|
||||
@@ -16,6 +16,7 @@ const getTransaction = require('./ledger/transaction');
|
||||
const getTransactions = require('./ledger/transactions');
|
||||
const getTrustlines = require('./ledger/trustlines');
|
||||
const getBalances = require('./ledger/balances');
|
||||
const getBalanceSheet = require('./ledger/balance-sheet');
|
||||
const getPaths = require('./ledger/pathfind');
|
||||
const getOrders = require('./ledger/orders');
|
||||
const getOrderbook = require('./ledger/orderbook');
|
||||
@@ -66,6 +67,7 @@ _.assign(RippleAPI.prototype, {
|
||||
getTransactions,
|
||||
getTrustlines,
|
||||
getBalances,
|
||||
getBalanceSheet,
|
||||
getPaths,
|
||||
getOrders,
|
||||
getOrderbook,
|
||||
|
||||
68
src/api/ledger/balance-sheet.js
Normal file
68
src/api/ledger/balance-sheet.js
Normal file
@@ -0,0 +1,68 @@
|
||||
'use strict';
|
||||
|
||||
const _ = require('lodash');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const convertErrors = utils.common.convertErrors;
|
||||
|
||||
function formatBalanceSheet(balanceSheet) {
|
||||
const result = {};
|
||||
|
||||
if (!_.isUndefined(balanceSheet.balances)) {
|
||||
result.balances = [];
|
||||
_.forEach(balanceSheet.balances, (balances, counterparty) => {
|
||||
_.forEach(balances, (balance) => {
|
||||
result.balances.push(Object.assign({counterparty}, balance));
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!_.isUndefined(balanceSheet.assets)) {
|
||||
result.assets = [];
|
||||
_.forEach(balanceSheet.assets, (assets, counterparty) => {
|
||||
_.forEach(assets, (balance) => {
|
||||
result.assets.push(Object.assign({counterparty}, balance));
|
||||
});
|
||||
});
|
||||
}
|
||||
if (!_.isUndefined(balanceSheet.obligations)) {
|
||||
result.obligations = _.map(balanceSheet.obligations, (value, currency) =>
|
||||
({currency, value}));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function getBalanceSheetAsync(address, options, callback) {
|
||||
validate.address(address);
|
||||
validate.getBalanceSheetOptions(options);
|
||||
|
||||
const requestOptions = Object.assign({}, {
|
||||
account: address,
|
||||
strict: true,
|
||||
hotwallet: options.excludeAddresses,
|
||||
ledger: options.ledgerVersion
|
||||
});
|
||||
|
||||
const requestCallback = composeAsync(
|
||||
formatBalanceSheet, convertErrors(callback));
|
||||
|
||||
this.remote.getLedgerSequence((err, ledgerVersion) => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isUndefined(requestOptions.ledger)) {
|
||||
requestOptions.ledger = ledgerVersion;
|
||||
}
|
||||
|
||||
this.remote.requestGatewayBalances(requestOptions, requestCallback);
|
||||
});
|
||||
}
|
||||
|
||||
function getBalanceSheet(address: string, options = {}) {
|
||||
return utils.promisify(getBalanceSheetAsync).call(this, address, options);
|
||||
}
|
||||
|
||||
module.exports = getBalanceSheet;
|
||||
@@ -7,6 +7,9 @@ const getTrustlines = require('./trustlines');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const convertErrors = utils.common.convertErrors;
|
||||
import type {Remote} from '../../core/remote';
|
||||
import type {GetLedgerSequenceCallback} from '../../core/remote';
|
||||
|
||||
|
||||
function getTrustlineBalanceAmount(trustline) {
|
||||
return {
|
||||
@@ -31,14 +34,25 @@ function getTrustlinesAsync(account, options, callback) {
|
||||
.catch(callback);
|
||||
}
|
||||
|
||||
function getLedgerVersionHelper(remote: Remote, optionValue?: number,
|
||||
callback: GetLedgerSequenceCallback
|
||||
) {
|
||||
if (optionValue !== undefined && optionValue !== null) {
|
||||
callback(null, optionValue);
|
||||
} else {
|
||||
remote.getLedgerSequence(callback);
|
||||
}
|
||||
}
|
||||
|
||||
function getBalancesAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getBalancesOptions(options);
|
||||
|
||||
const ledgerVersion = options.ledgerVersion
|
||||
|| this.remote.getLedgerSequence();
|
||||
async.parallel({
|
||||
xrp: _.partial(utils.getXRPBalance, this.remote, account, ledgerVersion),
|
||||
xrp: async.seq(
|
||||
_.partial(getLedgerVersionHelper, this.remote, options.ledgerVersion),
|
||||
_.partial(utils.getXRPBalance, this.remote, account)
|
||||
),
|
||||
trustlines: _.partial(getTrustlinesAsync.bind(this), account, options)
|
||||
}, composeAsync(formatBalances, convertErrors(callback)));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
@@ -26,17 +27,17 @@ function getOrdersAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getOrdersOptions(options);
|
||||
|
||||
const ledgerVersion = options.ledgerVersion
|
||||
|| this.remote.getLedgerSequence();
|
||||
const getter = _.partial(requestAccountOffers, this.remote, account,
|
||||
ledgerVersion);
|
||||
options.ledgerVersion);
|
||||
utils.getRecursive(getter, options.limit,
|
||||
composeAsync((orders) => _.sortBy(orders,
|
||||
(order) => order.properties.sequence), callback));
|
||||
}
|
||||
|
||||
function getOrders(account: string, options = {}) {
|
||||
return utils.promisify(getOrdersAsync).call(this, account, options);
|
||||
return utils.promisify(async.seq(
|
||||
utils.getLedgerOptionsWithLedgerVersion,
|
||||
getOrdersAsync)).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getOrders;
|
||||
|
||||
@@ -8,22 +8,42 @@ function parsePaths(paths) {
|
||||
_.omit(step, ['type', 'type_hex'])));
|
||||
}
|
||||
|
||||
function parsePathfind(sourceAddress: string,
|
||||
destinationAmount: Object, pathfindResult: Object
|
||||
): Object {
|
||||
return pathfindResult.alternatives.map(function(alternative) {
|
||||
return {
|
||||
source: {
|
||||
address: sourceAddress,
|
||||
maxAmount: parseAmount(alternative.source_amount)
|
||||
},
|
||||
destination: {
|
||||
address: pathfindResult.destination_account,
|
||||
amount: destinationAmount
|
||||
},
|
||||
paths: JSON.stringify(parsePaths(alternative.paths_computed))
|
||||
};
|
||||
});
|
||||
function removeAnyCounterpartyEncoding(address: string, amount: Object) {
|
||||
return amount.counterparty === address ?
|
||||
_.omit(amount, 'counterparty') : amount;
|
||||
}
|
||||
|
||||
function createAdjustment(address: string, adjustmentWithoutAddress: Object) {
|
||||
const amountKey = _.keys(adjustmentWithoutAddress)[0];
|
||||
const amount = adjustmentWithoutAddress[amountKey];
|
||||
return _.set({address: address}, amountKey,
|
||||
removeAnyCounterpartyEncoding(address, amount));
|
||||
}
|
||||
|
||||
function parseAlternative(sourceAddress: string, destinationAddress: string,
|
||||
destinationAmount: Object, alternative: Object
|
||||
) {
|
||||
// we use "maxAmount"/"minAmount" here so that the result can be passed
|
||||
// directly to preparePayment
|
||||
const amounts = (alternative.destination_amount !== undefined) ?
|
||||
{source: {amount: parseAmount(alternative.source_amount)},
|
||||
destination: {minAmount: parseAmount(alternative.destination_amount)}} :
|
||||
{source: {maxAmount: parseAmount(alternative.source_amount)},
|
||||
destination: {amount: parseAmount(destinationAmount)}};
|
||||
|
||||
return {
|
||||
source: createAdjustment(sourceAddress, amounts.source),
|
||||
destination: createAdjustment(destinationAddress, amounts.destination),
|
||||
paths: JSON.stringify(parsePaths(alternative.paths_computed))
|
||||
};
|
||||
}
|
||||
|
||||
function parsePathfind(pathfindResult: Object): Object {
|
||||
const sourceAddress = pathfindResult.source_account;
|
||||
const destinationAddress = pathfindResult.destination_account;
|
||||
const destinationAmount = pathfindResult.destination_amount;
|
||||
return pathfindResult.alternatives.map(_.partial(parseAlternative,
|
||||
sourceAddress, destinationAddress, destinationAmount));
|
||||
}
|
||||
|
||||
module.exports = parsePathfind;
|
||||
|
||||
@@ -4,15 +4,18 @@ const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const parsePathfind = require('./parse/pathfind');
|
||||
const validate = utils.common.validate;
|
||||
const NotFoundError = utils.common.errors.NotFoundError;
|
||||
const ValidationError = utils.common.errors.ValidationError;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
const convertErrors = utils.common.convertErrors;
|
||||
const toRippledAmount = utils.common.toRippledAmount;
|
||||
|
||||
type PathFindParams = {
|
||||
src_currencies?: Array<string>, src_account: string, dst_amount: string,
|
||||
dst_account?: string
|
||||
src_currencies?: Array<string>, src_account: string,
|
||||
dst_amount: string | Object, dst_account?: string,
|
||||
src_amount?: string | Object
|
||||
}
|
||||
|
||||
function addParams(params: PathFindParams, result: {}) {
|
||||
@@ -29,10 +32,11 @@ type PathFind = {
|
||||
}
|
||||
|
||||
function requestPathFind(remote, pathfind: PathFind, callback) {
|
||||
const destinationAmount = _.assign({value: -1}, pathfind.destination.amount);
|
||||
const params: PathFindParams = {
|
||||
src_account: pathfind.source.address,
|
||||
dst_account: pathfind.destination.address,
|
||||
dst_amount: utils.common.toRippledAmount(pathfind.destination.amount)
|
||||
dst_amount: toRippledAmount(destinationAmount)
|
||||
};
|
||||
if (typeof params.dst_amount === 'object' && !params.dst_amount.issuer) {
|
||||
// Convert blank issuer to sender's address
|
||||
@@ -44,7 +48,17 @@ function requestPathFind(remote, pathfind: PathFind, callback) {
|
||||
}
|
||||
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
|
||||
params.src_currencies = pathfind.source.currencies.map(amount =>
|
||||
_.omit(utils.common.toRippledAmount(amount), 'value'));
|
||||
_.omit(toRippledAmount(amount), 'value'));
|
||||
}
|
||||
if (pathfind.source.amount) {
|
||||
if (pathfind.destination.amount.value !== undefined) {
|
||||
throw new ValidationError('Cannot specify both source.amount'
|
||||
+ ' and destination.amount.value in getPaths');
|
||||
}
|
||||
params.src_amount = toRippledAmount(pathfind.source.amount);
|
||||
if (params.src_amount.currency && !params.src_amount.issuer) {
|
||||
params.src_amount.issuer = pathfind.source.address;
|
||||
}
|
||||
}
|
||||
|
||||
remote.createPathFind(params,
|
||||
@@ -81,8 +95,7 @@ function conditionallyAddDirectXRPPath(remote, address, paths, callback) {
|
||||
|
||||
function formatResponse(pathfind, paths) {
|
||||
if (paths.alternatives && paths.alternatives.length > 0) {
|
||||
const address = pathfind.source.address;
|
||||
return parsePathfind(address, pathfind.destination.amount, paths);
|
||||
return parsePathfind(paths);
|
||||
}
|
||||
if (paths.destination_currencies !== undefined &&
|
||||
!_.includes(paths.destination_currencies,
|
||||
|
||||
@@ -52,10 +52,10 @@ function getTransactionAsync(identifier: string, options: TransactionOptions,
|
||||
validate.getTransactionOptions(options);
|
||||
|
||||
const remote = this.remote;
|
||||
const maxLedgerVersion =
|
||||
options.maxLedgerVersion || remote.getLedgerSequence();
|
||||
|
||||
function callbackWrapper(error_?: Error, tx?: Object) {
|
||||
function callbackWrapper(error_?: Error, tx?: Object,
|
||||
maxLedgerVersion?: number
|
||||
) {
|
||||
let error = error_;
|
||||
|
||||
if (!error && tx && tx.validated !== true) {
|
||||
@@ -89,11 +89,19 @@ function getTransactionAsync(identifier: string, options: TransactionOptions,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function maxLedgerGetter(error_?: Error, tx?: Object) {
|
||||
this.getLedgerVersion().then((version) => {
|
||||
const maxLedgerVersion = options.maxLedgerVersion || version;
|
||||
callbackWrapper(error_, tx, maxLedgerVersion);
|
||||
}, callbackWrapper);
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
_.partial(remote.requestTx.bind(remote),
|
||||
{hash: identifier, binary: false}),
|
||||
_.partial(attachTransactionDate, remote)
|
||||
], callbackWrapper);
|
||||
], maxLedgerGetter.bind(this));
|
||||
}
|
||||
|
||||
function getTransaction(identifier: string,
|
||||
|
||||
@@ -70,8 +70,10 @@ function formatPartialResponse(address, options, data) {
|
||||
function getAccountTx(remote, address, options, marker, limit, callback) {
|
||||
const params = {
|
||||
account: address,
|
||||
// -1 is equivalent to earliest available validated ledger
|
||||
ledger_index_min: options.minLedgerVersion || -1,
|
||||
ledger_index_max: options.maxLedgerVersion || remote.getLedgerSequence(),
|
||||
// -1 is equivalent to most recent available validated ledger
|
||||
ledger_index_max: options.maxLedgerVersion || -1,
|
||||
forward: options.earliestFirst,
|
||||
binary: options.binary,
|
||||
limit: utils.clamp(limit, 10, 400),
|
||||
@@ -121,7 +123,7 @@ function getTransactionsAsync(account, options, callback) {
|
||||
validate.address(account);
|
||||
validate.getTransactionsOptions(options);
|
||||
|
||||
const defaults = {maxLedgerVersion: this.remote.getLedgerSequence()};
|
||||
const defaults = {maxLedgerVersion: -1};
|
||||
if (options.start) {
|
||||
getTransaction.call(this, options.start).then(tx => {
|
||||
const ledgerVersion = tx.outcome.ledgerVersion;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const composeAsync = utils.common.composeAsync;
|
||||
@@ -42,15 +43,15 @@ function getTrustlinesAsync(account: string, options: {currency: string,
|
||||
validate.address(account);
|
||||
validate.getTrustlinesOptions(options);
|
||||
|
||||
const ledgerVersion = options.ledgerVersion
|
||||
|| this.remote.getLedgerSequence();
|
||||
const getter = _.partial(getAccountLines, this.remote, account,
|
||||
ledgerVersion, options);
|
||||
options.ledgerVersion, options);
|
||||
utils.getRecursive(getter, options.limit, callback);
|
||||
}
|
||||
|
||||
function getTrustlines(account: string, options = {}) {
|
||||
return utils.promisify(getTrustlinesAsync).call(this, account, options);
|
||||
return utils.promisify(async.seq(
|
||||
utils.getLedgerOptionsWithLedgerVersion,
|
||||
getTrustlinesAsync)).call(this, account, options);
|
||||
}
|
||||
|
||||
module.exports = getTrustlines;
|
||||
|
||||
@@ -103,17 +103,32 @@ function hasCompleteLedgerRange(remote: Remote, minLedgerVersion?: number,
|
||||
const firstLedgerVersion = 32570; // earlier versions have been lost
|
||||
return remote.getServer().hasLedgerRange(
|
||||
minLedgerVersion || firstLedgerVersion,
|
||||
maxLedgerVersion || remote.getLedgerSequence());
|
||||
maxLedgerVersion || remote.getLedgerSequenceSync());
|
||||
}
|
||||
|
||||
function isPendingLedgerVersion(remote: Remote, maxLedgerVersion: ?number
|
||||
): boolean {
|
||||
const currentLedger = remote.getLedgerSequence();
|
||||
const currentLedger = remote.getLedgerSequenceSync();
|
||||
return currentLedger < (maxLedgerVersion || 0);
|
||||
}
|
||||
|
||||
function getLedgerOptionsWithLedgerVersion(account: string, options: Object,
|
||||
callback: (err?: ?Error, account?: string, options: Object) => void
|
||||
) {
|
||||
if (Boolean(options) && options.ledgerVersion !== undefined &&
|
||||
options.ledgerVersion !== null
|
||||
) {
|
||||
callback(null, account, options);
|
||||
} else {
|
||||
this.getLedgerVersion().then((version) => {
|
||||
callback(null, account, _.assign({}, options, {ledgerVersion: version}));
|
||||
}, callback);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getXRPBalance,
|
||||
getLedgerOptionsWithLedgerVersion,
|
||||
compareTransactions,
|
||||
renameCounterpartyToIssuer,
|
||||
renameCounterpartyToIssuerInOrder,
|
||||
|
||||
@@ -53,8 +53,7 @@ function getServerInfoAsync(
|
||||
): void {
|
||||
this.remote.requestServerInfo((error, response) => {
|
||||
if (error) {
|
||||
const message =
|
||||
_.get(error, ['remote', 'error_message'], error.message);
|
||||
const message = _.get(error, ['remote', 'error_message'], error.message);
|
||||
callback(new common.errors.RippledNetworkError(message));
|
||||
} else {
|
||||
callback(null,
|
||||
@@ -63,28 +62,40 @@ function getServerInfoAsync(
|
||||
});
|
||||
}
|
||||
|
||||
function getFee(): number {
|
||||
return common.dropsToXrp(this.remote.createTransaction()._computeFee());
|
||||
function getFee(): ?number {
|
||||
if (!this.remote.getConnectedServers().length) {
|
||||
throw new common.errors.RippledNetworkError('No servers available.');
|
||||
}
|
||||
const fee = this.remote.createTransaction()._computeFee();
|
||||
return fee === undefined ? undefined : common.dropsToXrp(fee);
|
||||
}
|
||||
|
||||
function getLedgerVersion(): number {
|
||||
return this.remote.getLedgerSequence();
|
||||
function getLedgerVersion(): Promise<number> {
|
||||
return common.promisify(this.remote.getLedgerSequence).call(this.remote);
|
||||
}
|
||||
|
||||
function connect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.connect(() => callback(null));
|
||||
try {
|
||||
this.remote.connect(() => callback(null));
|
||||
} catch(error) {
|
||||
callback(new common.errors.RippledNetworkError(error.message));
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function disconnect(): Promise<void> {
|
||||
return common.promisify(callback => {
|
||||
this.remote.disconnect(() => callback(null));
|
||||
try {
|
||||
this.remote.disconnect(() => callback(null));
|
||||
} catch(error) {
|
||||
callback(new common.errors.RippledNetworkError(error.message));
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function getServerInfo(): Promise<GetServerInfoResponse> {
|
||||
return common.promisify(getServerInfoAsync.bind(this))();
|
||||
return common.promisify(getServerInfoAsync).call(this);
|
||||
}
|
||||
|
||||
function rippleTimeToISO8601(rippleTime: string): string {
|
||||
|
||||
@@ -5,6 +5,7 @@ const utils = require('./utils');
|
||||
const validate = utils.common.validate;
|
||||
const toRippledAmount = utils.common.toRippledAmount;
|
||||
const Transaction = utils.common.core.Transaction;
|
||||
const ValidationError = utils.common.errors.ValidationError;
|
||||
|
||||
function isXRPToXRPPayment(payment) {
|
||||
const sourceCurrency = _.get(payment, 'source.maxAmount.currency');
|
||||
@@ -23,24 +24,49 @@ function applyAnyCounterpartyEncoding(payment) {
|
||||
// https://ripple.com/build/transactions/
|
||||
// #special-issuer-values-for-sendmax-and-amount
|
||||
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
|
||||
if (isIOUWithoutCounterparty(payment.source.maxAmount)) {
|
||||
payment.source.maxAmount.counterparty = payment.source.address;
|
||||
}
|
||||
if (isIOUWithoutCounterparty(payment.destination.amount)) {
|
||||
payment.destination.amount.counterparty = payment.destination.address;
|
||||
}
|
||||
_.forEach([payment.source, payment.destination], (adjustment) => {
|
||||
_.forEach(['amount', 'minAmount', 'maxAmount'], (key) => {
|
||||
if (isIOUWithoutCounterparty(adjustment[key])) {
|
||||
adjustment[key].counterparty = adjustment.address;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createPaymentTransaction(account, payment) {
|
||||
function createMaximalAmount(amount) {
|
||||
const maxXRPValue = '100000000000';
|
||||
const maxIOUValue = '9999999999999999e80';
|
||||
const maxValue = amount.currency === 'XRP' ? maxXRPValue : maxIOUValue;
|
||||
return _.assign(amount, {value: maxValue});
|
||||
}
|
||||
|
||||
function createPaymentTransaction(account, paymentArgument) {
|
||||
const payment = _.cloneDeep(paymentArgument);
|
||||
applyAnyCounterpartyEncoding(payment);
|
||||
validate.address(account);
|
||||
validate.payment(payment);
|
||||
|
||||
if ((payment.source.maxAmount && payment.destination.minAmount) ||
|
||||
(payment.source.amount && payment.destination.amount)) {
|
||||
throw new ValidationError('payment must specify either (source.maxAmount '
|
||||
+ 'and destination.amount) or (source.amount and destination.minAmount)');
|
||||
}
|
||||
|
||||
// when using destination.minAmount, rippled still requires that we set
|
||||
// a destination amount in addition to DeliverMin. the destination amount
|
||||
// is interpreted as the maximum amount to send. we want to be sure to
|
||||
// send the whole source amount, so we set the destination amount to the
|
||||
// maximum possible amount. otherwise it's possible that the destination
|
||||
// cap could be hit before the source cap.
|
||||
const amount = payment.destination.minAmount && !isXRPToXRPPayment(payment) ?
|
||||
createMaximalAmount(payment.destination.minAmount) :
|
||||
(payment.destination.amount || payment.destination.minAmount);
|
||||
|
||||
const transaction = new Transaction();
|
||||
transaction.payment({
|
||||
from: payment.source.address,
|
||||
to: payment.destination.address,
|
||||
amount: toRippledAmount(payment.destination.amount)
|
||||
amount: toRippledAmount(amount)
|
||||
});
|
||||
|
||||
if (payment.invoiceID) {
|
||||
@@ -57,9 +83,6 @@ function createPaymentTransaction(account, payment) {
|
||||
transaction.addMemo(memo.type, memo.format, memo.data)
|
||||
);
|
||||
}
|
||||
if (payment.allowPartialPayment) {
|
||||
transaction.setFlags(['PartialPayment']);
|
||||
}
|
||||
if (payment.noDirectRipple) {
|
||||
transaction.setFlags(['NoRippleDirect']);
|
||||
}
|
||||
@@ -71,11 +94,22 @@ function createPaymentTransaction(account, payment) {
|
||||
// temREDUNDANT_SEND_MAX removed in:
|
||||
// https://github.com/ripple/rippled/commit/
|
||||
// c522ffa6db2648f1d8a987843e7feabf1a0b7de8/
|
||||
transaction.sendMax(toRippledAmount(payment.source.maxAmount));
|
||||
if (payment.allowPartialPayment || payment.destination.minAmount) {
|
||||
transaction.setFlags(['PartialPayment']);
|
||||
}
|
||||
|
||||
transaction.setSendMax(toRippledAmount(
|
||||
payment.source.maxAmount || payment.source.amount));
|
||||
|
||||
if (payment.destination.minAmount) {
|
||||
transaction.setDeliverMin(toRippledAmount(payment.destination.minAmount));
|
||||
}
|
||||
|
||||
if (payment.paths) {
|
||||
transaction.paths(JSON.parse(payment.paths));
|
||||
}
|
||||
} else if (payment.allowPartialPayment) {
|
||||
throw new ValidationError('XRP to XRP payments cannot be partial payments');
|
||||
}
|
||||
|
||||
return transaction;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const utils = require('./utils');
|
||||
const keypairs = require('ripple-keypairs');
|
||||
const core = utils.common.core;
|
||||
const validate = utils.common.validate;
|
||||
|
||||
@@ -16,14 +17,6 @@ const validate = utils.common.validate;
|
||||
*/
|
||||
const HASH_TX_ID = 0x54584E00; // 'TXN'
|
||||
|
||||
function getKeyPair(secret) {
|
||||
return core.Seed.from_json(secret).get_key();
|
||||
}
|
||||
|
||||
function getPublicKeyHex(keypair) {
|
||||
return keypair.pubKeyHex();
|
||||
}
|
||||
|
||||
function serialize(txJSON) {
|
||||
return core.SerializedObject.from_json(txJSON);
|
||||
}
|
||||
@@ -36,8 +29,8 @@ function signingData(txJSON) {
|
||||
return core.Transaction.from_json(txJSON).signingData().buffer;
|
||||
}
|
||||
|
||||
function computeSignature(txJSON, keypair) {
|
||||
return keypair.signHex(signingData(txJSON));
|
||||
function computeSignature(txJSON, privateKey) {
|
||||
return keypairs.sign(signingData(txJSON), privateKey);
|
||||
}
|
||||
|
||||
function sign(txJSON: string, secret: string
|
||||
@@ -46,11 +39,11 @@ function sign(txJSON: string, secret: string
|
||||
validate.txJSON(tx);
|
||||
validate.secret(secret);
|
||||
|
||||
const keypair = getKeyPair(secret);
|
||||
const keypair = keypairs.deriveKeypair(secret);
|
||||
if (tx.SigningPubKey === undefined) {
|
||||
tx.SigningPubKey = getPublicKeyHex(keypair);
|
||||
tx.SigningPubKey = keypair.publicKey;
|
||||
}
|
||||
tx.TxnSignature = computeSignature(tx, keypair);
|
||||
tx.TxnSignature = computeSignature(tx, keypair.privateKey);
|
||||
const serialized = serialize(tx);
|
||||
return {
|
||||
signedTransaction: serialized.to_hex(),
|
||||
|
||||
@@ -18,7 +18,7 @@ function isImmediateRejection(engineResult) {
|
||||
|
||||
function convertSubmitErrors(callback) {
|
||||
return function(error, data) {
|
||||
if (isImmediateRejection(data.engineResult)) {
|
||||
if (!error && isImmediateRejection(data.engineResult)) {
|
||||
callback(new utils.common.errors.RippleError('Submit failed'), data);
|
||||
} else {
|
||||
callback(error, data);
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* @flow */
|
||||
'use strict';
|
||||
const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const common = require('../common');
|
||||
const composeAsync = common.composeAsync;
|
||||
|
||||
function setTransactionBitFlags(transaction: any, values: any, flags: any
|
||||
): void {
|
||||
@@ -19,9 +21,11 @@ function setTransactionBitFlags(transaction: any, values: any, flags: any
|
||||
}
|
||||
}
|
||||
|
||||
function getFeeDrops(remote) {
|
||||
function getFeeDrops(remote, callback) {
|
||||
const feeUnits = 10; // all transactions currently have a fee of 10 fee units
|
||||
return remote.feeTx(feeUnits).to_text();
|
||||
remote.feeTxAsync(feeUnits, (err, data) => {
|
||||
callback(err, data ? data.to_text() : undefined);
|
||||
});
|
||||
}
|
||||
|
||||
function formatPrepareResponse(txJSON) {
|
||||
@@ -39,42 +43,64 @@ function formatPrepareResponse(txJSON) {
|
||||
type Callback = (err: ?(typeof Error),
|
||||
data: {txJSON: string, instructions: any}) => void;
|
||||
function prepareTransaction(transaction: any, remote: any, instructions: any,
|
||||
callback: Callback): void {
|
||||
callback: Callback
|
||||
): void {
|
||||
common.validate.instructions(instructions);
|
||||
|
||||
transaction.complete();
|
||||
const account = transaction.getAccount();
|
||||
const txJSON = transaction.tx_json;
|
||||
|
||||
if (instructions.maxLedgerVersion !== undefined) {
|
||||
txJSON.LastLedgerSequence = parseInt(instructions.maxLedgerVersion, 10);
|
||||
} else {
|
||||
const offset = instructions.maxLedgerVersionOffset !== undefined ?
|
||||
parseInt(instructions.maxLedgerVersionOffset, 10) : 3;
|
||||
txJSON.LastLedgerSequence = remote.getLedgerSequence() + offset;
|
||||
}
|
||||
|
||||
if (instructions.fee !== undefined) {
|
||||
txJSON.Fee = common.xrpToDrops(instructions.fee);
|
||||
} else {
|
||||
const serverFeeDrops = getFeeDrops(remote);
|
||||
if (instructions.maxFee !== undefined) {
|
||||
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
|
||||
txJSON.Fee = BigNumber.min(serverFeeDrops, maxFeeDrops).toString();
|
||||
function prepareMaxLedgerVersion(callback_) {
|
||||
if (instructions.maxLedgerVersion !== undefined) {
|
||||
txJSON.LastLedgerSequence = parseInt(instructions.maxLedgerVersion, 10);
|
||||
callback_();
|
||||
} else {
|
||||
txJSON.Fee = serverFeeDrops;
|
||||
const offset = instructions.maxLedgerVersionOffset !== undefined ?
|
||||
parseInt(instructions.maxLedgerVersionOffset, 10) : 3;
|
||||
remote.getLedgerSequence((error, ledgerVersion) => {
|
||||
txJSON.LastLedgerSequence = ledgerVersion + offset;
|
||||
callback_(error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (instructions.sequence !== undefined) {
|
||||
txJSON.Sequence = parseInt(instructions.sequence, 10);
|
||||
callback(null, formatPrepareResponse(txJSON));
|
||||
} else {
|
||||
remote.findAccount(account).getNextSequence(function(error, sequence) {
|
||||
txJSON.Sequence = sequence;
|
||||
callback(error, formatPrepareResponse(txJSON));
|
||||
});
|
||||
function prepareFee(callback_) {
|
||||
if (instructions.fee !== undefined) {
|
||||
txJSON.Fee = common.xrpToDrops(instructions.fee);
|
||||
callback_();
|
||||
} else {
|
||||
getFeeDrops(remote, composeAsync((serverFeeDrops) => {
|
||||
if (instructions.maxFee !== undefined) {
|
||||
const maxFeeDrops = common.xrpToDrops(instructions.maxFee);
|
||||
txJSON.Fee = BigNumber.min(serverFeeDrops, maxFeeDrops).toString();
|
||||
} else {
|
||||
txJSON.Fee = serverFeeDrops;
|
||||
}
|
||||
}, callback_));
|
||||
}
|
||||
}
|
||||
|
||||
function prepareSequence(callback_) {
|
||||
if (instructions.sequence !== undefined) {
|
||||
txJSON.Sequence = parseInt(instructions.sequence, 10);
|
||||
callback_(null, formatPrepareResponse(txJSON));
|
||||
} else {
|
||||
remote.findAccount(account).getNextSequence(function(error, sequence) {
|
||||
txJSON.Sequence = sequence;
|
||||
callback_(error, formatPrepareResponse(txJSON));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async.series([
|
||||
prepareMaxLedgerVersion,
|
||||
prepareFee,
|
||||
prepareSequence
|
||||
], common.convertErrors(function(error, results) {
|
||||
callback(error, results && results[2]);
|
||||
}));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -14,12 +14,10 @@ const _ = require('lodash');
|
||||
const async = require('async');
|
||||
const extend = require('extend');
|
||||
const util = require('util');
|
||||
const {createAccountID} = require('ripple-keypairs');
|
||||
const {encodeAccountID} = require('ripple-address-codec');
|
||||
const {deriveAddress} = require('ripple-keypairs');
|
||||
const {EventEmitter} = require('events');
|
||||
const {hexToArray} = require('./utils');
|
||||
const {TransactionManager} = require('./transactionmanager');
|
||||
const {UInt160} = require('./uint160');
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
|
||||
/**
|
||||
* @constructor Account
|
||||
@@ -27,14 +25,13 @@ const {UInt160} = require('./uint160');
|
||||
* @param {String} account
|
||||
*/
|
||||
|
||||
function Account(remote, account) {
|
||||
function Account(remote, address) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
const self = this;
|
||||
|
||||
this._remote = remote;
|
||||
this._account = UInt160.from_json(account);
|
||||
this._account_id = this._account.to_json();
|
||||
this._address = address;
|
||||
this._subs = 0;
|
||||
|
||||
// Ledger entry object
|
||||
@@ -45,7 +42,7 @@ function Account(remote, account) {
|
||||
if (_.includes(Account.subscribeEvents, type)) {
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestSubscribe()
|
||||
.addAccount(self._account_id)
|
||||
.addAccount(self._address)
|
||||
.broadcast().request();
|
||||
}
|
||||
self._subs += 1;
|
||||
@@ -59,7 +56,7 @@ function Account(remote, account) {
|
||||
self._subs -= 1;
|
||||
if (!self._subs && self._remote._connected) {
|
||||
self._remote.requestUnsubscribe()
|
||||
.addAccount(self._account_id)
|
||||
.addAccount(self._address)
|
||||
.broadcast().request();
|
||||
}
|
||||
}
|
||||
@@ -68,8 +65,8 @@ function Account(remote, account) {
|
||||
this.on('removeListener', listenerRemoved);
|
||||
|
||||
function attachAccount(request) {
|
||||
if (self._account.is_valid() && self._subs) {
|
||||
request.addAccount(self._account_id);
|
||||
if (isValidAddress(self._address) && self._subs) {
|
||||
request.addAccount(self._address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +80,7 @@ function Account(remote, account) {
|
||||
let changed = false;
|
||||
|
||||
transaction.mmeta.each(function(an) {
|
||||
const isAccount = an.fields.Account === self._account_id;
|
||||
const isAccount = an.fields.Account === self._address;
|
||||
const isAccountRoot = isAccount && (an.entryType === 'AccountRoot');
|
||||
|
||||
if (isAccountRoot) {
|
||||
@@ -113,7 +110,7 @@ util.inherits(Account, EventEmitter);
|
||||
Account.subscribeEvents = ['transaction', 'entry'];
|
||||
|
||||
Account.prototype.toJson = function() {
|
||||
return this._account.to_json();
|
||||
return this._address;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -123,7 +120,7 @@ Account.prototype.toJson = function() {
|
||||
*/
|
||||
|
||||
Account.prototype.isValid = function() {
|
||||
return this._account.is_valid();
|
||||
return isValidAddress(this._address);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -133,7 +130,7 @@ Account.prototype.isValid = function() {
|
||||
*/
|
||||
|
||||
Account.prototype.getInfo = function(callback) {
|
||||
return this._remote.requestAccountInfo({account: this._account_id}, callback);
|
||||
return this._remote.requestAccountInfo({account: this._address}, callback);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -212,7 +209,7 @@ Account.prototype.lines = function(callback_) {
|
||||
}
|
||||
}
|
||||
|
||||
this._remote.requestAccountLines({account: this._account_id}, accountLines);
|
||||
this._remote.requestAccountLines({account: this._address}, accountLines);
|
||||
|
||||
return this;
|
||||
};
|
||||
@@ -277,7 +274,7 @@ Account.prototype.notifyTx = function(transaction) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isThisAccount = (account === this._account_id);
|
||||
const isThisAccount = (account === this._address);
|
||||
|
||||
this.emit(isThisAccount ? 'transaction-outbound' : 'transaction-inbound',
|
||||
transaction);
|
||||
@@ -333,7 +330,7 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
|
||||
// Catch the case of unfunded accounts
|
||||
if (!account_info_res) {
|
||||
|
||||
if (public_key_as_uint160 === self._account_id) {
|
||||
if (public_key_as_uint160 === self._address) {
|
||||
async_callback(null, true);
|
||||
} else {
|
||||
async_callback(null, false);
|
||||
@@ -375,20 +372,17 @@ Account.prototype.publicKeyIsActive = function(public_key, callback) {
|
||||
* @returns {RippleAddress} Ripple Address
|
||||
*/
|
||||
Account._publicKeyToAddress = function(public_key) {
|
||||
// Based on functions in /src/js/ripple/keypair.js
|
||||
function hexToUInt160(publicKey) {
|
||||
return encodeAccountID(createAccountID(hexToArray(publicKey)));
|
||||
}
|
||||
|
||||
if (UInt160.is_valid(public_key)) {
|
||||
if (isValidAddress(public_key)) {
|
||||
return public_key;
|
||||
} else if (/^[0-9a-fA-F]+$/.test(public_key)) {
|
||||
return hexToUInt160(public_key);
|
||||
return deriveAddress(public_key);
|
||||
} else { // eslint-disable-line no-else-return
|
||||
throw new Error('Public key is invalid. Must be a UInt160 or a hex string');
|
||||
}
|
||||
};
|
||||
|
||||
exports.Account = Account;
|
||||
module.exports = {
|
||||
Account
|
||||
};
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -6,23 +6,23 @@
|
||||
const assert = require('assert');
|
||||
const extend = require('extend');
|
||||
const utils = require('./utils');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Seed = require('./seed').Seed;
|
||||
const Currency = require('./currency').Currency;
|
||||
const Value = require('./value').Value;
|
||||
const IOUValue = require('./iouvalue').IOUValue;
|
||||
const XRPValue = require('./xrpvalue').XRPValue;
|
||||
const {XRPValue, IOUValue} = require('ripple-lib-value');
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
const {ACCOUNT_ONE, ACCOUNT_ZERO} = require('./constants');
|
||||
|
||||
type Value = XRPValue | IOUValue;
|
||||
|
||||
function Amount(value = new XRPValue(NaN)) {
|
||||
// Json format:
|
||||
// integer : XRP
|
||||
// { 'value' : ..., 'currency' : ..., 'issuer' : ...}
|
||||
assert(value instanceof Value);
|
||||
assert(value instanceof XRPValue || value instanceof IOUValue);
|
||||
|
||||
this._value = value;
|
||||
this._is_native = true; // Default to XRP. Only valid if value is not NaN.
|
||||
this._currency = new Currency();
|
||||
this._issuer = new UInt160();
|
||||
this._issuer = 'NaN';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +106,7 @@ Amount.NaN = function() {
|
||||
};
|
||||
|
||||
Amount.from_components_unsafe = function(value: Value, currency: Currency,
|
||||
issuer: UInt160, isNative: boolean
|
||||
issuer: string, isNative: boolean
|
||||
) {
|
||||
const result = new Amount(value);
|
||||
result._is_native = isNative;
|
||||
@@ -120,18 +120,14 @@ Amount.from_components_unsafe = function(value: Value, currency: Currency,
|
||||
|
||||
// be sure that _is_native is set properly BEFORE calling _set_value
|
||||
Amount.prototype._set_value = function(value: Value) {
|
||||
|
||||
this._value = value.isZero() && value.isNegative() ?
|
||||
value.negate() : value;
|
||||
this._check_limits();
|
||||
|
||||
};
|
||||
|
||||
// Returns a new value which is the absolute value of this.
|
||||
Amount.prototype.abs = function() {
|
||||
|
||||
return this._copy(this._value.abs());
|
||||
|
||||
};
|
||||
|
||||
Amount.prototype.add = function(addend) {
|
||||
@@ -229,7 +225,7 @@ Amount.prototype.ratio_human = function(denom, opts) {
|
||||
//
|
||||
// To compensate, we multiply the numerator by 10^xns_precision.
|
||||
if (denominator._is_native) {
|
||||
numerator._set_value(numerator.multiply(bi_xns_unit));
|
||||
numerator._set_value(numerator._value.multiply(bi_xns_unit));
|
||||
}
|
||||
|
||||
return numerator.divide(denominator);
|
||||
@@ -403,7 +399,7 @@ Amount.prototype.equals = function(d, ignore_issuer) {
|
||||
&& this._is_native === d._is_native
|
||||
&& this._value.equals(d._value)
|
||||
&& (this._is_native || (this._currency.equals(d._currency)
|
||||
&& (ignore_issuer || this._issuer.equals(d._issuer))));
|
||||
&& (ignore_issuer || this._issuer === d._issuer)));
|
||||
};
|
||||
|
||||
// True if Amounts are valid and both native or non-native.
|
||||
@@ -429,9 +425,8 @@ Amount.prototype.is_valid = function() {
|
||||
};
|
||||
|
||||
Amount.prototype.is_valid_full = function() {
|
||||
return this.is_valid()
|
||||
&& this._currency.is_valid()
|
||||
&& this._issuer.is_valid();
|
||||
return this.is_valid() && this._currency.is_valid()
|
||||
&& isValidAddress(this._issuer) && this._issuer !== ACCOUNT_ZERO;
|
||||
};
|
||||
|
||||
Amount.prototype.is_zero = function() {
|
||||
@@ -533,7 +528,7 @@ Amount.prototype.parse_human = function(j, options) {
|
||||
};
|
||||
|
||||
Amount.prototype.parse_issuer = function(issuer) {
|
||||
this._issuer = UInt160.from_json(issuer);
|
||||
this._issuer = issuer;
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -584,7 +579,7 @@ function(quality, counterCurrency, counterIssuer, opts) {
|
||||
const offset = parseInt(offset_hex, 16) - 100;
|
||||
|
||||
this._currency = Currency.from_json(counterCurrency);
|
||||
this._issuer = UInt160.from_json(counterIssuer);
|
||||
this._issuer = counterIssuer;
|
||||
this._is_native = this._currency.is_native();
|
||||
|
||||
if (this._is_native && baseCurrency.is_native()) {
|
||||
@@ -626,7 +621,8 @@ function(quality, counterCurrency, counterIssuer, opts) {
|
||||
}
|
||||
if (this._is_native) {
|
||||
this._set_value(
|
||||
new XRPValue(nativeAdjusted.round(6, Value.getBNRoundDown()).toString()));
|
||||
new XRPValue(nativeAdjusted.round(6, XRPValue.getBNRoundDown())
|
||||
.toString()));
|
||||
} else {
|
||||
this._set_value(nativeAdjusted);
|
||||
}
|
||||
@@ -643,7 +639,7 @@ function(quality, counterCurrency, counterIssuer, opts) {
|
||||
Amount.prototype.parse_number = function(n) {
|
||||
this._is_native = false;
|
||||
this._currency = Currency.from_json(1);
|
||||
this._issuer = UInt160.from_json(1);
|
||||
this._issuer = ACCOUNT_ONE;
|
||||
this._set_value(new IOUValue(n));
|
||||
return this;
|
||||
};
|
||||
@@ -659,15 +655,15 @@ Amount.prototype.parse_json = function(j) {
|
||||
if (m) {
|
||||
this._currency = Currency.from_json(m[2]);
|
||||
if (m[3]) {
|
||||
this._issuer = UInt160.from_json(m[3]);
|
||||
this._issuer = m[3];
|
||||
} else {
|
||||
this._issuer = UInt160.from_json('1');
|
||||
this._issuer = 'NaN';
|
||||
}
|
||||
this.parse_value(m[1]);
|
||||
} else {
|
||||
this.parse_native(j);
|
||||
this._currency = Currency.from_json('0');
|
||||
this._issuer = UInt160.from_json('0');
|
||||
this._issuer = ACCOUNT_ZERO;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -686,9 +682,10 @@ Amount.prototype.parse_json = function(j) {
|
||||
// Parse the passed value to sanitize and copy it.
|
||||
this._currency.parse_json(j.currency, true); // Never XRP.
|
||||
|
||||
if (typeof j.issuer === 'string') {
|
||||
this._issuer.parse_json(j.issuer);
|
||||
if (typeof j.issuer !== 'string') {
|
||||
throw new Error('issuer must be a string');
|
||||
}
|
||||
this._issuer = j.issuer;
|
||||
|
||||
this.parse_value(j.value);
|
||||
}
|
||||
@@ -724,7 +721,7 @@ Amount.prototype.parse_native = function(j) {
|
||||
// Requires _currency to be set!
|
||||
Amount.prototype.parse_value = function(j) {
|
||||
this._is_native = false;
|
||||
const newValue = new IOUValue(j, Value.getBNRoundDown());
|
||||
const newValue = new IOUValue(j, IOUValue.getBNRoundDown());
|
||||
this._set_value(newValue);
|
||||
return this;
|
||||
};
|
||||
@@ -736,12 +733,7 @@ Amount.prototype.set_currency = function(c) {
|
||||
};
|
||||
|
||||
Amount.prototype.set_issuer = function(issuer) {
|
||||
if (issuer instanceof UInt160) {
|
||||
this._issuer = issuer;
|
||||
} else {
|
||||
this._issuer = UInt160.from_json(issuer);
|
||||
}
|
||||
|
||||
this._issuer = issuer;
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -939,7 +931,7 @@ Amount.prototype.to_human_full = function(options) {
|
||||
const opts = options || {};
|
||||
const value = this.to_human(opts);
|
||||
const currency = this._currency.to_human();
|
||||
const issuer = this._issuer.to_json(opts);
|
||||
const issuer = this._issuer;
|
||||
const base = value + '/' + currency;
|
||||
return this.is_native() ? base : (base + '/' + issuer);
|
||||
};
|
||||
@@ -955,21 +947,21 @@ Amount.prototype.to_json = function() {
|
||||
this._currency.to_hex() : this._currency.to_json()
|
||||
};
|
||||
|
||||
if (this._issuer.is_valid()) {
|
||||
amount_json.issuer = this._issuer.to_json();
|
||||
if (isValidAddress(this._issuer)) {
|
||||
amount_json.issuer = this._issuer;
|
||||
}
|
||||
|
||||
return amount_json;
|
||||
};
|
||||
|
||||
Amount.prototype.to_text_full = function(opts) {
|
||||
Amount.prototype.to_text_full = function() {
|
||||
if (!this.is_valid()) {
|
||||
return 'NaN';
|
||||
}
|
||||
return this._is_native
|
||||
? this.to_human() + '/XRP'
|
||||
: this.to_text() + '/' + this._currency.to_json()
|
||||
+ '/' + this._issuer.to_json(opts);
|
||||
+ '/' + this._issuer;
|
||||
};
|
||||
|
||||
// For debugging.
|
||||
@@ -998,20 +990,11 @@ Amount.prototype.not_equals_why = function(d, ignore_issuer) {
|
||||
if (!this._currency.equals(d._currency)) {
|
||||
return 'Non-XRP currency differs.';
|
||||
}
|
||||
if (!ignore_issuer && !this._issuer.equals(d._issuer)) {
|
||||
return 'Non-XRP issuer differs: '
|
||||
+ d._issuer.to_json()
|
||||
+ '/'
|
||||
+ this._issuer.to_json();
|
||||
if (!ignore_issuer && this._issuer !== d._issuer) {
|
||||
return 'Non-XRP issuer differs: ' + d._issuer + '/' + this._issuer;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.Amount = Amount;
|
||||
|
||||
// DEPRECATED: Include the corresponding files instead.
|
||||
exports.Currency = Currency;
|
||||
exports.Seed = Seed;
|
||||
exports.UInt160 = UInt160;
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Amount = require('./amount').Amount;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Utils = require('./orderbookutils');
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
@@ -42,18 +42,46 @@ const NULL_AMOUNT = Utils.normalizeAmount('0');
|
||||
* @return {Array}
|
||||
*/
|
||||
|
||||
AutobridgeCalculator.prototype.calculate = function() {
|
||||
const oldMode = Amount.strict_mode;
|
||||
Amount.strict_mode = false;
|
||||
AutobridgeCalculator.prototype.calculate = function(callback) {
|
||||
|
||||
let legOnePointer = 0;
|
||||
let legTwoPointer = 0;
|
||||
const legOnePointer = 0;
|
||||
const legTwoPointer = 0;
|
||||
|
||||
const offersAutobridged = [];
|
||||
|
||||
this.clearOwnerFundsLeftover();
|
||||
|
||||
this._calculateInternal(legOnePointer, legTwoPointer, offersAutobridged,
|
||||
callback);
|
||||
};
|
||||
|
||||
AutobridgeCalculator.prototype._calculateInternal = function(
|
||||
legOnePointer_, legTwoPointer_, offersAutobridged, callback
|
||||
) {
|
||||
|
||||
// Amount class is calling _check_limits after each operation in strict mode,
|
||||
// and _check_limits is very computationally expensive, so we turning it off
|
||||
// whle doing calculations
|
||||
this._oldMode = Amount.strict_mode;
|
||||
Amount.strict_mode = false;
|
||||
|
||||
let legOnePointer = legOnePointer_;
|
||||
let legTwoPointer = legTwoPointer_;
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
while (this.legOneOffers[legOnePointer] && this.legTwoOffers[legTwoPointer]) {
|
||||
// manually implement cooperative multitasking that yields after 30ms
|
||||
// of execution so user's browser stays responsive
|
||||
const lasted = (Date.now() - startTime);
|
||||
if (lasted > 30) {
|
||||
setTimeout(this._calculateInternal.bind(this, legOnePointer,
|
||||
legTwoPointer, offersAutobridged, callback), 0);
|
||||
|
||||
Amount.strict_mode = this._oldMode;
|
||||
return;
|
||||
}
|
||||
|
||||
const legOneOffer = this.legOneOffers[legOnePointer];
|
||||
const legTwoOffer = this.legTwoOffers[legTwoPointer];
|
||||
const leftoverFunds = this.getLeftoverOwnerFunds(legOneOffer.Account);
|
||||
@@ -112,8 +140,8 @@ AutobridgeCalculator.prototype.calculate = function() {
|
||||
offersAutobridged.push(autobridgedOffer);
|
||||
}
|
||||
|
||||
Amount.strict_mode = oldMode;
|
||||
return offersAutobridged;
|
||||
Amount.strict_mode = this._oldMode;
|
||||
callback(offersAutobridged);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
6
src/core/constants.js
Normal file
6
src/core/constants.js
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
ACCOUNT_ZERO: 'rrrrrrrrrrrrrrrrrrrrrhoLvTp',
|
||||
ACCOUNT_ONE: 'rrrrrrrrrrrrrrrrrrrrBZbvji'
|
||||
};
|
||||
@@ -9,7 +9,6 @@ exports.Base = require('./base').Base;
|
||||
exports.UInt128 = require('./uint128').UInt128;
|
||||
exports.UInt160 = require('./uint160').UInt160;
|
||||
exports.UInt256 = require('./uint256').UInt256;
|
||||
exports.Seed = require('./seed').Seed;
|
||||
exports.Meta = require('./meta').Meta;
|
||||
exports.SerializedObject = require('./serializedobject').SerializedObject;
|
||||
exports.RippleError = require('./rippleerror').RippleError;
|
||||
@@ -21,7 +20,6 @@ exports.TransactionQueue = require('./transactionqueue').TransactionQueue;
|
||||
exports.convertBase = require('./baseconverter');
|
||||
|
||||
exports._test = {
|
||||
IOUValue: require('./iouvalue').IOUValue,
|
||||
Log: require('./log'),
|
||||
PathFind: require('./pathfind').PathFind,
|
||||
TransactionManager: require('./transactionmanager').TransactionManager,
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Value = require('./value').Value;
|
||||
const XRPValue = require('./xrpvalue').XRPValue;
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
const BigNumber = GlobalBigNumber.another({
|
||||
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
|
||||
DECIMAL_PLACES: 40
|
||||
});
|
||||
const rippleUnits = new BigNumber(1e6);
|
||||
|
||||
class IOUValue extends Value {
|
||||
|
||||
constructor(value: string | BigNumber, roundingMode: ?number = null,
|
||||
base: ?number = null) {
|
||||
|
||||
super(new BigNumber(value, base).toDigits(16, roundingMode));
|
||||
}
|
||||
|
||||
multiply(multiplicand: Value) {
|
||||
if (multiplicand instanceof XRPValue) {
|
||||
return super.multiply(
|
||||
new IOUValue(
|
||||
multiplicand._value.times(rippleUnits)));
|
||||
}
|
||||
return super.multiply(multiplicand);
|
||||
}
|
||||
|
||||
divide(divisor: Value) {
|
||||
if (divisor instanceof XRPValue) {
|
||||
return super.divide(
|
||||
new IOUValue(divisor._value.times(rippleUnits)));
|
||||
}
|
||||
return super.divide(divisor);
|
||||
}
|
||||
|
||||
negate() {
|
||||
return new IOUValue(this._value.neg());
|
||||
}
|
||||
|
||||
_canonicalize(value) {
|
||||
if (value.isNaN()) {
|
||||
throw new Error('Invalid result');
|
||||
}
|
||||
return new IOUValue(value.toPrecision(16));
|
||||
}
|
||||
|
||||
equals(comparator) {
|
||||
return (comparator instanceof IOUValue)
|
||||
&& this._value.equals(comparator._value);
|
||||
}
|
||||
}
|
||||
|
||||
exports.IOUValue = IOUValue;
|
||||
107
src/core/meta.js
107
src/core/meta.js
@@ -1,7 +1,10 @@
|
||||
var extend = require('extend');
|
||||
var utils = require('./utils');
|
||||
var UInt160 = require('./uint160').UInt160;
|
||||
var Amount = require('./amount').Amount;
|
||||
'use strict';
|
||||
const extend = require('extend');
|
||||
const utils = require('./utils');
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const Amount = require('./amount').Amount;
|
||||
const ACCOUNT_ZERO = require('./constants').ACCOUNT_ZERO;
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
|
||||
/**
|
||||
* Meta data processing facility
|
||||
@@ -11,8 +14,6 @@ var Amount = require('./amount').Amount;
|
||||
*/
|
||||
|
||||
function Meta(data) {
|
||||
var self = this;
|
||||
|
||||
this.nodes = [ ];
|
||||
|
||||
if (typeof data !== 'object') {
|
||||
@@ -24,7 +25,7 @@ function Meta(data) {
|
||||
}
|
||||
|
||||
data.AffectedNodes.forEach(this.addNode, this);
|
||||
};
|
||||
}
|
||||
|
||||
Meta.NODE_TYPES = [
|
||||
'CreatedNode',
|
||||
@@ -53,10 +54,10 @@ Meta.ACCOUNT_FIELDS = [
|
||||
*/
|
||||
|
||||
Meta.prototype.getNodeType = function(node) {
|
||||
var result = null;
|
||||
let result = null;
|
||||
|
||||
for (var i=0; i<Meta.NODE_TYPES.length; i++) {
|
||||
var type = Meta.NODE_TYPES[i];
|
||||
for (let i = 0; i < Meta.NODE_TYPES.length; i++) {
|
||||
const type = Meta.NODE_TYPES[i];
|
||||
if (node.hasOwnProperty(type)) {
|
||||
result = type;
|
||||
break;
|
||||
@@ -83,20 +84,22 @@ Meta.prototype.isAccountField = function(field) {
|
||||
*/
|
||||
|
||||
Meta.prototype.addNode = function(node) {
|
||||
this._affectedAccounts = void(0);
|
||||
this._affectedBooks = void(0);
|
||||
this._affectedAccounts = undefined;
|
||||
this._affectedBooks = undefined;
|
||||
|
||||
var result = { };
|
||||
const result = { };
|
||||
|
||||
if ((result.nodeType = this.getNodeType(node))) {
|
||||
node = node[result.nodeType];
|
||||
result.diffType = result.nodeType;
|
||||
result.entryType = node.LedgerEntryType;
|
||||
result.ledgerIndex = node.LedgerIndex;
|
||||
result.fields = extend({ }, node.PreviousFields, node.NewFields, node.FinalFields);
|
||||
result.fieldsPrev = node.PreviousFields || { };
|
||||
result.fieldsNew = node.NewFields || { };
|
||||
result.fieldsFinal = node.FinalFields || { };
|
||||
result.nodeType = this.getNodeType(node);
|
||||
if (result.nodeType) {
|
||||
const _node = node[result.nodeType];
|
||||
result.diffType = result.nodeType;
|
||||
result.entryType = _node.LedgerEntryType;
|
||||
result.ledgerIndex = _node.LedgerIndex;
|
||||
result.fields = extend({ }, _node.PreviousFields,
|
||||
_node.NewFields, _node.FinalFields);
|
||||
result.fieldsPrev = _node.PreviousFields || { };
|
||||
result.fieldsNew = _node.NewFields || { };
|
||||
result.fieldsFinal = _node.FinalFields || { };
|
||||
|
||||
// getAffectedBooks will set this
|
||||
// result.bookKey = undefined;
|
||||
@@ -126,36 +129,36 @@ Meta.prototype.getNodes = function(options) {
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
return this.nodes;
|
||||
}
|
||||
return this.nodes;
|
||||
};
|
||||
|
||||
Meta.prototype.getAffectedAccounts = function(from) {
|
||||
Meta.prototype.getAffectedAccounts = function() {
|
||||
if (this._affectedAccounts) {
|
||||
return this._affectedAccounts;
|
||||
}
|
||||
|
||||
var accounts = [ ];
|
||||
const accounts = [ ];
|
||||
|
||||
// This code should match the behavior of the C++ method:
|
||||
// TransactionMetaSet::getAffectedAccounts
|
||||
for (var i=0; i<this.nodes.length; i++) {
|
||||
var node = this.nodes[i];
|
||||
var fields = (node.nodeType === 'CreatedNode')
|
||||
for (let i = 0; i < this.nodes.length; i++) {
|
||||
const node = this.nodes[i];
|
||||
const fields = (node.nodeType === 'CreatedNode')
|
||||
? node.fieldsNew
|
||||
: node.fieldsFinal;
|
||||
|
||||
for (var fieldName in fields) {
|
||||
var field = fields[fieldName];
|
||||
for (const fieldName in fields) {
|
||||
const field = fields[fieldName];
|
||||
|
||||
if (this.isAccountField(fieldName) && UInt160.is_valid(field)) {
|
||||
accounts.push(field);
|
||||
} else if (~Meta.AMOUNT_FIELDS_AFFECTING_ISSUER.indexOf(fieldName)) {
|
||||
var amount = Amount.from_json(field);
|
||||
var issuer = amount.issuer();
|
||||
if (issuer.is_valid() && !issuer.is_zero()) {
|
||||
accounts.push(issuer.to_json());
|
||||
} else if (
|
||||
Meta.AMOUNT_FIELDS_AFFECTING_ISSUER.indexOf(fieldName) !== -1) {
|
||||
const amount = Amount.from_json(field);
|
||||
const issuer = amount.issuer();
|
||||
if (isValidAddress(issuer) && issuer !== ACCOUNT_ZERO) {
|
||||
accounts.push(issuer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,29 +174,29 @@ Meta.prototype.getAffectedBooks = function() {
|
||||
return this._affectedBooks;
|
||||
}
|
||||
|
||||
var books = [ ];
|
||||
const books = [ ];
|
||||
|
||||
for (var i=0; i<this.nodes.length; i++) {
|
||||
var node = this.nodes[i];
|
||||
for (let i = 0; i < this.nodes.length; i++) {
|
||||
const node = this.nodes[i];
|
||||
|
||||
if (node.entryType !== 'Offer') {
|
||||
continue;
|
||||
}
|
||||
|
||||
var gets = Amount.from_json(node.fields.TakerGets);
|
||||
var pays = Amount.from_json(node.fields.TakerPays);
|
||||
var getsKey = gets.currency().to_json();
|
||||
var paysKey = pays.currency().to_json();
|
||||
const gets = Amount.from_json(node.fields.TakerGets);
|
||||
const pays = Amount.from_json(node.fields.TakerPays);
|
||||
let getsKey = gets.currency().to_json();
|
||||
let paysKey = pays.currency().to_json();
|
||||
|
||||
if (getsKey !== 'XRP') {
|
||||
getsKey += '/' + gets.issuer().to_json();
|
||||
getsKey += '/' + gets.issuer();
|
||||
}
|
||||
|
||||
if (paysKey !== 'XRP') {
|
||||
paysKey += '/' + pays.issuer().to_json();
|
||||
paysKey += '/' + pays.issuer();
|
||||
}
|
||||
|
||||
var key = getsKey + ':' + paysKey;
|
||||
const key = getsKey + ':' + paysKey;
|
||||
|
||||
// Hell of a lot of work, so we are going to cache this. We can use this
|
||||
// later to good effect in OrderBook.notify to make sure we only process
|
||||
@@ -243,12 +246,12 @@ Meta.prototype.getAffectedBooks = function() {
|
||||
*/
|
||||
|
||||
[
|
||||
'forEach',
|
||||
'map',
|
||||
'filter',
|
||||
'every',
|
||||
'some',
|
||||
'reduce'
|
||||
'forEach',
|
||||
'map',
|
||||
'filter',
|
||||
'every',
|
||||
'some',
|
||||
'reduce'
|
||||
].forEach(function(fn) {
|
||||
Meta.prototype[fn] = function() {
|
||||
return Array.prototype[fn].apply(this.nodes, arguments);
|
||||
|
||||
@@ -22,7 +22,7 @@ const Currency = require('./currency').Currency;
|
||||
const AutobridgeCalculator = require('./autobridgecalculator');
|
||||
const OrderBookUtils = require('./orderbookutils');
|
||||
const log = require('./log').internal.sub('orderbook');
|
||||
const IOUValue = require('./iouvalue').IOUValue;
|
||||
const {IOUValue} = require('ripple-lib-value');
|
||||
|
||||
function _sortOffers(a, b) {
|
||||
const aQuality = OrderBookUtils.getOfferQuality(a, this._currencyGets);
|
||||
@@ -87,29 +87,30 @@ function OrderBook(remote,
|
||||
this._gotOffersFromLegOne = false;
|
||||
this._gotOffersFromLegTwo = false;
|
||||
|
||||
this._waitingForOffers = false;
|
||||
this._lastUpdateLedgerSequence = 0;
|
||||
this._transactionsLeft = 0;
|
||||
this._calculatorRunning = false;
|
||||
|
||||
|
||||
this.sortOffers = this._currencyGets.has_interest() ?
|
||||
_sortOffers.bind(this) : _sortOffersQuick;
|
||||
|
||||
this.notifyDirectOffersChanged =
|
||||
_.debounce(
|
||||
this.notifyDirectOffersChangedInternal,
|
||||
OrderBook.NOTIFY_TIMEOUT,
|
||||
{maxWait: OrderBook.NOTIFY_MAXWAIT});
|
||||
|
||||
this._isAutobridgeable = !this._currencyGets.is_native()
|
||||
&& !this._currencyPays.is_native();
|
||||
|
||||
this._autobridgeThrottleTimeMultiplier = 1;
|
||||
this.createDebouncedOffersWrapper();
|
||||
|
||||
function computeAutobridgedOffersWrapperOne() {
|
||||
self._gotOffersFromLegOne = true;
|
||||
self.computeAutobridgedOffersThrottled();
|
||||
if (!self._gotOffersFromLegOne) {
|
||||
self._gotOffersFromLegOne = true;
|
||||
self.computeAutobridgedOffersWrapper();
|
||||
}
|
||||
}
|
||||
|
||||
function computeAutobridgedOffersWrapperTwo() {
|
||||
self._gotOffersFromLegTwo = true;
|
||||
self.computeAutobridgedOffersThrottled();
|
||||
if (!self._gotOffersFromLegTwo) {
|
||||
self._gotOffersFromLegTwo = true;
|
||||
self.computeAutobridgedOffersWrapper();
|
||||
}
|
||||
}
|
||||
|
||||
function onDisconnect() {
|
||||
@@ -138,8 +139,12 @@ function OrderBook(remote,
|
||||
});
|
||||
}
|
||||
|
||||
function updateFundedAmountsWrapper(transaction) {
|
||||
self.updateFundedAmounts(transaction);
|
||||
function onTransactionWrapper(transaction) {
|
||||
self.onTransaction(transaction);
|
||||
}
|
||||
|
||||
function onLedgerClosedWrapper(message) {
|
||||
self.onLedgerClosed(message);
|
||||
}
|
||||
|
||||
function listenersModified(action, event) {
|
||||
@@ -152,7 +157,8 @@ function OrderBook(remote,
|
||||
self._shouldSubscribe = true;
|
||||
self.subscribe();
|
||||
|
||||
self._remote.on('transaction', updateFundedAmountsWrapper);
|
||||
self._remote.on('transaction', onTransactionWrapper);
|
||||
self._remote.on('ledger_closed', onLedgerClosedWrapper);
|
||||
self._remote.once('disconnect', onDisconnect);
|
||||
|
||||
if (self._isAutobridgeable) {
|
||||
@@ -181,7 +187,8 @@ function OrderBook(remote,
|
||||
this.on('unsubscribe', function() {
|
||||
self.resetCache();
|
||||
|
||||
self._remote.removeListener('transaction', updateFundedAmountsWrapper);
|
||||
self._remote.removeListener('transaction', onTransactionWrapper);
|
||||
self._remote.removeListener('ledger_closed', onLedgerClosedWrapper);
|
||||
self._remote.removeListener('disconnect', onDisconnect);
|
||||
|
||||
self._gotOffersFromLegOne = false;
|
||||
@@ -212,16 +219,6 @@ OrderBook.EVENTS = [
|
||||
|
||||
OrderBook.DEFAULT_TRANSFER_RATE = new IOUValue(1000000000);
|
||||
|
||||
OrderBook.NOTIFY_TIMEOUT = 100;
|
||||
|
||||
OrderBook.NOTIFY_MAXWAIT = 250;
|
||||
|
||||
OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME = 1000;
|
||||
|
||||
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME = 250;
|
||||
|
||||
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT = 500;
|
||||
|
||||
OrderBook.ZERO_NATIVE_AMOUNT = Amount.from_json('0');
|
||||
|
||||
OrderBook.ZERO_NORMALIZED_AMOUNT = OrderBookUtils.normalizeAmount('0');
|
||||
@@ -358,6 +355,8 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
|
||||
log.info('requesting offers', this._key);
|
||||
}
|
||||
|
||||
this._synchronized = false;
|
||||
|
||||
if (this._isAutobridgeable && !internal) {
|
||||
this._gotOffersFromLegOne = false;
|
||||
this._gotOffersFromLegTwo = false;
|
||||
@@ -372,9 +371,12 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
|
||||
return;
|
||||
}
|
||||
|
||||
self._waitingForOffers = false;
|
||||
|
||||
if (!Array.isArray(res.offers)) {
|
||||
// XXX What now?
|
||||
callback(new Error('Invalid response'));
|
||||
self.emit('model', []);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -382,7 +384,12 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
|
||||
log.info('requested offers', self._key, 'offers: ' + res.offers.length);
|
||||
}
|
||||
self.setOffers(res.offers);
|
||||
self.notifyDirectOffersChanged();
|
||||
|
||||
if (self._isAutobridgeable) {
|
||||
self.computeAutobridgedOffersWrapper();
|
||||
} else {
|
||||
self.emit('model', self._offers);
|
||||
}
|
||||
|
||||
callback(null, self._offers);
|
||||
}
|
||||
@@ -393,9 +400,12 @@ OrderBook.prototype.requestOffers = function(callback = function() {},
|
||||
log.info('failed to request offers', self._key, err);
|
||||
}
|
||||
|
||||
self._waitingForOffers = false;
|
||||
callback(err);
|
||||
}
|
||||
|
||||
this._waitingForOffers = true;
|
||||
|
||||
const requestOptions = _.merge({}, this.toJSON(), {ledger: 'validated'});
|
||||
const request = this._remote.requestBookOffers(requestOptions);
|
||||
request.once('success', handleOffers);
|
||||
@@ -493,18 +503,6 @@ OrderBook.prototype.subscribeTransactions = function(callback) {
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles notifying listeners that direct offers have changed. For autobridged
|
||||
* books, an additional merge step is also performed
|
||||
*/
|
||||
|
||||
OrderBook.prototype.notifyDirectOffersChangedInternal = function() {
|
||||
if (this._isAutobridgeable) {
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
} else {
|
||||
this.emit('model', this._offers);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset cached owner's funds, offer counts, and offer sums
|
||||
@@ -839,6 +837,32 @@ OrderBook.prototype.isBalanceChangeNode = function(node) {
|
||||
return true;
|
||||
};
|
||||
|
||||
OrderBook.prototype._canRunAutobridgeCalc = function(): boolean {
|
||||
return !this._calculatorRunning;
|
||||
};
|
||||
|
||||
OrderBook.prototype.onTransaction = function(transaction) {
|
||||
this.updateFundedAmounts(transaction);
|
||||
|
||||
|
||||
if (--this._transactionsLeft === 0 && !this._waitingForOffers) {
|
||||
const lastClosedLedger = this._remote.getLedgerSequenceSync();
|
||||
if (this._isAutobridgeable) {
|
||||
if (this._canRunAutobridgeCalc()) {
|
||||
if (this._legOneBook._lastUpdateLedgerSequence === lastClosedLedger ||
|
||||
this._legTwoBook._lastUpdateLedgerSequence === lastClosedLedger
|
||||
) {
|
||||
this.computeAutobridgedOffersWrapper();
|
||||
} else if (this._lastUpdateLedgerSequence === lastClosedLedger) {
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
}
|
||||
}
|
||||
} else if (this._lastUpdateLedgerSequence === lastClosedLedger) {
|
||||
this.emit('model', this._offers);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates funded amounts/balances using modified balance nodes
|
||||
*
|
||||
@@ -946,6 +970,16 @@ OrderBook.prototype.updateOwnerOffersFundedAmount = function(account) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
OrderBook.prototype.onLedgerClosed = function(message) {
|
||||
if (!message || (message && !_.isNumber(message.txn_count)) ||
|
||||
!this._subscribed || this._destroyed || this._waitingForOffers
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this._transactionsLeft = message.txn_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify orderbook of a relevant transaction
|
||||
*
|
||||
@@ -1046,7 +1080,7 @@ OrderBook.prototype.notify = function(transaction) {
|
||||
|
||||
this.emit('transaction', transaction);
|
||||
|
||||
this.notifyDirectOffersChanged();
|
||||
this._lastUpdateLedgerSequence = this._remote.getLedgerSequenceSync();
|
||||
|
||||
if (!takerGetsTotal.is_zero()) {
|
||||
this.emit('trade', takerPaysTotal, takerGetsTotal);
|
||||
@@ -1331,15 +1365,13 @@ OrderBook.prototype.is_valid = function() {
|
||||
* IOU:XRP and XRP:IOU books
|
||||
*/
|
||||
|
||||
OrderBook.prototype.computeAutobridgedOffers = function() {
|
||||
OrderBook.prototype.computeAutobridgedOffers = function(callback = function() {}
|
||||
) {
|
||||
assert(!this._currencyGets.is_native() && !this._currencyPays.is_native(),
|
||||
'Autobridging is only for IOU:IOU orderbooks');
|
||||
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._gotOffersFromLegOne || !this._gotOffersFromLegTwo) {
|
||||
if (this._destroyed) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1352,36 +1384,26 @@ OrderBook.prototype.computeAutobridgedOffers = function() {
|
||||
this._issuerPays
|
||||
);
|
||||
|
||||
this._offersAutobridged = autobridgeCalculator.calculate();
|
||||
autobridgeCalculator.calculate((autobridgedOffers) => {
|
||||
this._offersAutobridged = autobridgedOffers;
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
OrderBook.prototype.computeAutobridgedOffersWrapper = function() {
|
||||
const startTime = Date.now();
|
||||
this.computeAutobridgedOffers();
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
const lasted = (Date.now() - startTime);
|
||||
|
||||
const newMult =
|
||||
Math.floor(lasted * 2 / OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME) + 1;
|
||||
if (newMult !== this._autobridgeThrottleTimeMultiplier) {
|
||||
this._autobridgeThrottleTimeMultiplier = newMult;
|
||||
this.createDebouncedOffersWrapper();
|
||||
if (!this._gotOffersFromLegOne || !this._gotOffersFromLegTwo ||
|
||||
!this._synchronized || this._destroyed || this._calculatorRunning
|
||||
) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
OrderBook.prototype.createDebouncedOffersWrapper = function() {
|
||||
const m = this._autobridgeThrottleTimeMultiplier;
|
||||
this.computeAutobridgedOffersThrottled =
|
||||
_.debounce(
|
||||
_.throttle(
|
||||
this.computeAutobridgedOffersWrapper,
|
||||
OrderBook.AUTOBRIDGE_CALCULATE_THROTTLE_TIME * m,
|
||||
{leading: true, trailing: true}),
|
||||
OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_TIME,
|
||||
{maxWait: OrderBook.AUTOBRIDGE_CALCULATE_DEBOUNCE_MAXWAIT});
|
||||
this._calculatorRunning = true;
|
||||
this.computeAutobridgedOffers(() => {
|
||||
this.mergeDirectAndAutobridgedBooks();
|
||||
this._calculatorRunning = false;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Merge direct and autobridged offers into a combined orderbook
|
||||
*
|
||||
|
||||
@@ -7,7 +7,7 @@ const Types = require('./serializedtypes');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./currency').Currency;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const IOUValue = require('./iouvalue').IOUValue;
|
||||
const {IOUValue} = require('ripple-lib-value');
|
||||
const OrderBookUtils = {};
|
||||
|
||||
function assertValidNumber(number, message) {
|
||||
@@ -29,8 +29,7 @@ function createAmount(value, currency_, counterparty_) {
|
||||
Currency.from_json(currency_);
|
||||
|
||||
const counterparty = counterparty_ instanceof UInt160 ?
|
||||
counterparty_ :
|
||||
UInt160.from_json(counterparty_);
|
||||
counterparty_.to_json() : counterparty_;
|
||||
|
||||
return Amount.from_components_unsafe(new IOUValue(value),
|
||||
currency, counterparty, false);
|
||||
|
||||
@@ -11,7 +11,8 @@ const Amount = require('./amount').Amount;
|
||||
* the 'end' and 'superceded' events.
|
||||
*/
|
||||
|
||||
function PathFind(remote, src_account, dst_account, dst_amount, src_currencies
|
||||
function PathFind(remote, src_account, dst_account, dst_amount,
|
||||
src_currencies, src_amount
|
||||
) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
@@ -21,6 +22,7 @@ function PathFind(remote, src_account, dst_account, dst_amount, src_currencies
|
||||
this.dst_account = dst_account;
|
||||
this.dst_amount = dst_amount;
|
||||
this.src_currencies = src_currencies;
|
||||
this.src_amount = src_amount;
|
||||
}
|
||||
|
||||
util.inherits(PathFind, EventEmitter);
|
||||
@@ -42,7 +44,8 @@ PathFind.prototype.create = function() {
|
||||
source_account: this.src_account,
|
||||
destination_account: this.dst_account,
|
||||
destination_amount: this.dst_amount,
|
||||
source_currencies: this.src_currencies
|
||||
source_currencies: this.src_currencies,
|
||||
send_max: this.src_amount
|
||||
});
|
||||
|
||||
req.once('error', function(err) {
|
||||
|
||||
@@ -36,6 +36,8 @@ const utils = require('./utils');
|
||||
const hashprefixes = require('./hashprefixes');
|
||||
const log = require('./log').internal.sub('remote');
|
||||
|
||||
export type GetLedgerSequenceCallback = (err?: ?Error, index?: number) => void;
|
||||
|
||||
/**
|
||||
* Interface to manage connections to rippled servers
|
||||
*
|
||||
@@ -421,22 +423,23 @@ Remote.prototype.reconnect = function() {
|
||||
/**
|
||||
* Connect to the Ripple network
|
||||
*
|
||||
* @param {Function} [callback]
|
||||
* @param [Function] [callback]
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Remote.prototype.connect = function(callback) {
|
||||
if (!this._servers.length) {
|
||||
Remote.prototype.connect = function(callback = function() {}) {
|
||||
if (_.isEmpty(this._servers)) {
|
||||
throw new Error('No servers available.');
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
this.once('connect', callback);
|
||||
if (this.isConnected()) {
|
||||
callback();
|
||||
return this;
|
||||
}
|
||||
|
||||
this.once('connect', callback);
|
||||
this._should_connect = true;
|
||||
|
||||
this._servers.forEach(function(server) {
|
||||
this._servers.forEach(server => {
|
||||
server.connect();
|
||||
});
|
||||
|
||||
@@ -450,25 +453,19 @@ Remote.prototype.connect = function(callback) {
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Remote.prototype.disconnect = function(callback_) {
|
||||
if (!this._servers.length) {
|
||||
Remote.prototype.disconnect = function(callback = function() {}) {
|
||||
if (_.isEmpty(this._servers)) {
|
||||
throw new Error('No servers available, not disconnecting');
|
||||
}
|
||||
|
||||
const callback = _.isFunction(callback_)
|
||||
? callback_
|
||||
: function() {};
|
||||
|
||||
this._should_connect = false;
|
||||
|
||||
if (!this.isConnected()) {
|
||||
callback();
|
||||
return this;
|
||||
}
|
||||
|
||||
this._should_connect = false;
|
||||
this.once('disconnect', callback);
|
||||
|
||||
this._servers.forEach(function(server) {
|
||||
this._servers.forEach(server => {
|
||||
server.disconnect();
|
||||
});
|
||||
|
||||
@@ -523,7 +520,34 @@ Remote.prototype._handleMessage = function(message, server) {
|
||||
}
|
||||
};
|
||||
|
||||
Remote.prototype.getLedgerSequence = function() {
|
||||
/**
|
||||
*
|
||||
* @param {Function} [callback]
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Remote.prototype.getLedgerSequence = function(callback = function() {}) {
|
||||
if (!this._servers.length) {
|
||||
callback(new Error('No servers available.'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isFinite(this._ledger_current_index)) {
|
||||
// the "current" ledger is the one after the most recently closed ledger
|
||||
callback(null, this._ledger_current_index - 1);
|
||||
} else {
|
||||
this.once('ledger_closed', () => {
|
||||
callback(null, this._ledger_current_index - 1);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Remote.prototype.getLedgerSequenceSync = function(): number {
|
||||
if (!this._ledger_current_index) {
|
||||
throw new Error('Ledger sequence has not yet been initialized');
|
||||
}
|
||||
@@ -1800,7 +1824,7 @@ Remote.prototype.createPathFind = function(options, callback) {
|
||||
|
||||
const pathFind = new PathFind(this,
|
||||
options.src_account, options.dst_account,
|
||||
options.dst_amount, options.src_currencies);
|
||||
options.dst_amount, options.src_currencies, options.src_amount);
|
||||
|
||||
if (this._cur_path_find) {
|
||||
this._cur_path_find.notify_superceded();
|
||||
@@ -1808,9 +1832,20 @@ Remote.prototype.createPathFind = function(options, callback) {
|
||||
|
||||
if (callback) {
|
||||
pathFind.on('update', (data) => {
|
||||
if (data.full_reply) {
|
||||
pathFind.close();
|
||||
if (data.full_reply && !data.closed) {
|
||||
this._cur_path_find = null;
|
||||
callback(null, data);
|
||||
// "A client can only have one pathfinding request open at a time.
|
||||
// If another pathfinding request is already open on the same
|
||||
// connection, the old request is automatically closed and replaced
|
||||
// with the new request."
|
||||
// - ripple.com/build/rippled-apis/#path-find-create
|
||||
if (this._queued_path_finds.length > 0) {
|
||||
const pathfind = this._queued_path_finds.shift();
|
||||
this.createPathFind(pathfind.options, pathfind.callback);
|
||||
} else {
|
||||
pathFind.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
pathFind.on('error', callback);
|
||||
@@ -2137,6 +2172,10 @@ Remote.prototype.requestPathFindCreate = function(options, callback) {
|
||||
options.source_currencies.map(Remote.prepareCurrency);
|
||||
}
|
||||
|
||||
if (options.send_max) {
|
||||
request.message.send_max = Amount.json_rewrite(options.send_max);
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
return request;
|
||||
};
|
||||
@@ -2150,19 +2189,8 @@ Remote.prototype.requestPathFindCreate = function(options, callback) {
|
||||
|
||||
Remote.prototype.requestPathFindClose = function(callback) {
|
||||
const request = new Request(this, 'path_find');
|
||||
|
||||
request.message.subcommand = 'close';
|
||||
request.callback((error, data) => {
|
||||
this._cur_path_find = null;
|
||||
if (this._queued_path_finds.length > 0) {
|
||||
const pathfind = this._queued_path_finds.shift();
|
||||
this.createPathFind(pathfind.options, pathfind.callback);
|
||||
}
|
||||
if (callback) {
|
||||
callback(error, data);
|
||||
}
|
||||
});
|
||||
|
||||
request.callback(callback);
|
||||
return request;
|
||||
};
|
||||
|
||||
@@ -2252,6 +2280,29 @@ Remote.prototype.requestConnect = function(ip, port, callback) {
|
||||
return request;
|
||||
};
|
||||
|
||||
Remote.prototype.requestGatewayBalances = function(options, callback) {
|
||||
assert(_.isObject(options), 'Options missing');
|
||||
assert(options.account, 'Account missing');
|
||||
|
||||
const request = new Request(this, 'gateway_balances');
|
||||
|
||||
request.message.account = UInt160.json_rewrite(options.account);
|
||||
|
||||
if (!_.isUndefined(options.hotwallet)) {
|
||||
request.message.hotwallet = options.hotwallet;
|
||||
}
|
||||
if (!_.isUndefined(options.strict)) {
|
||||
request.message.strict = options.strict;
|
||||
}
|
||||
if (!_.isUndefined(options.ledger)) {
|
||||
request.selectLedger(options.ledger);
|
||||
}
|
||||
|
||||
request.callback(callback);
|
||||
|
||||
return request;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a Transaction
|
||||
*
|
||||
@@ -2312,6 +2363,32 @@ Remote.prototype.feeTx = function(units) {
|
||||
return server._feeTx(units);
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as feeTx, but will wait to connect to server if currently
|
||||
* disconnected.
|
||||
*
|
||||
* @param {Number} fee units
|
||||
* @param {Function} callback
|
||||
*/
|
||||
|
||||
Remote.prototype.feeTxAsync = function(units, callback) {
|
||||
if (!this._servers.length) {
|
||||
callback(new Error('No servers available.'));
|
||||
return;
|
||||
}
|
||||
|
||||
let server = this.getServer();
|
||||
|
||||
if (!server) {
|
||||
this.once('connected', () => {
|
||||
server = this.getServer();
|
||||
callback(null, server._feeTx(units));
|
||||
});
|
||||
} else {
|
||||
callback(null, server._feeTx(units));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current recommended transaction fee unit.
|
||||
*
|
||||
|
||||
@@ -34,23 +34,29 @@ function Request(remote, command) {
|
||||
command: command,
|
||||
id: undefined
|
||||
};
|
||||
this._timeout = this.remote.submission_timeout;
|
||||
}
|
||||
|
||||
util.inherits(Request, EventEmitter);
|
||||
|
||||
// Send the request to a remote.
|
||||
Request.prototype.request = function(servers, callback_) {
|
||||
const self = this;
|
||||
const callback = typeof servers === 'function' ? servers : callback_;
|
||||
const self = this;
|
||||
|
||||
if (this.requested) {
|
||||
throw new Error('Already requested');
|
||||
}
|
||||
|
||||
this.emit('before');
|
||||
this.callback(callback);
|
||||
|
||||
// emit handler can set requested flag
|
||||
if (this.requested) {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.requested = true;
|
||||
this.callback(callback);
|
||||
|
||||
this.on('error', function() {});
|
||||
this.emit('request', this.remote);
|
||||
|
||||
@@ -65,17 +71,25 @@ Request.prototype.request = function(servers, callback_) {
|
||||
}
|
||||
}
|
||||
|
||||
function onReconnect() {
|
||||
doRequest();
|
||||
}
|
||||
const timeout = setTimeout(() => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(new RippleError('tejTimeout'));
|
||||
}
|
||||
|
||||
this.emit('timeout');
|
||||
// just in case
|
||||
this.emit = _.noop;
|
||||
this.cancel();
|
||||
}, this._timeout);
|
||||
|
||||
function onResponse() {
|
||||
self.remote.removeListener('connected', onReconnect);
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
|
||||
if (this.remote.isConnected()) {
|
||||
this.remote.on('connected', onReconnect);
|
||||
this.remote.on('connected', doRequest);
|
||||
}
|
||||
|
||||
this.once('response', onResponse);
|
||||
|
||||
doRequest();
|
||||
@@ -227,14 +241,8 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
|
||||
|
||||
let called = false;
|
||||
|
||||
function requestSuccess(message) {
|
||||
if (!called) {
|
||||
called = true;
|
||||
callback.call(self, null, message);
|
||||
}
|
||||
}
|
||||
|
||||
function requestError(error) {
|
||||
self.remote.removeListener('error', requestError);
|
||||
if (!called) {
|
||||
called = true;
|
||||
|
||||
@@ -246,8 +254,18 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
function requestSuccess(message) {
|
||||
self.remote.removeListener('error', requestError);
|
||||
if (!called) {
|
||||
called = true;
|
||||
callback.call(self, null, message);
|
||||
}
|
||||
}
|
||||
|
||||
this.remote.once('error', requestError); // e.g. rate-limiting slowDown error
|
||||
this.once(this.successEvent, requestSuccess);
|
||||
this.once(this.errorEvent, requestError);
|
||||
|
||||
if (!this.requested) {
|
||||
this.request();
|
||||
}
|
||||
@@ -255,38 +273,11 @@ Request.prototype.callback = function(callback, successEvent, errorEvent) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Request.prototype.timeout = function(duration, callback) {
|
||||
const self = this;
|
||||
|
||||
function requested() {
|
||||
self.timeout(duration, callback);
|
||||
Request.prototype.setTimeout = function(delay) {
|
||||
if (!_.isFinite(delay)) {
|
||||
throw new Error('delay must be number');
|
||||
}
|
||||
|
||||
if (!this.requested) {
|
||||
// Defer until requested
|
||||
return this.once('request', requested);
|
||||
}
|
||||
|
||||
const emit = this.emit;
|
||||
let timed_out = false;
|
||||
|
||||
const timeout = setTimeout(function() {
|
||||
timed_out = true;
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
|
||||
emit.call(self, 'timeout');
|
||||
self.cancel();
|
||||
}, duration);
|
||||
|
||||
this.emit = function() {
|
||||
if (!timed_out) {
|
||||
clearTimeout(timeout);
|
||||
emit.apply(self, arguments);
|
||||
}
|
||||
};
|
||||
this._timeout = delay;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -1,30 +1,42 @@
|
||||
var util = require('util');
|
||||
var extend = require('extend');
|
||||
'use strict';
|
||||
|
||||
function RippleError(code, message) {
|
||||
switch (typeof code) {
|
||||
case 'object':
|
||||
extend(this, code);
|
||||
break;
|
||||
const util = require('util');
|
||||
const _ = require('lodash');
|
||||
|
||||
case 'string':
|
||||
this.result = code;
|
||||
this.result_message = message;
|
||||
break;
|
||||
function RippleError(code?: any, message?: string) {
|
||||
if (code instanceof Error) {
|
||||
this.result = code;
|
||||
this.result_message = code.message;
|
||||
} else {
|
||||
switch (typeof code) {
|
||||
case 'object':
|
||||
_.extend(this, code);
|
||||
break;
|
||||
|
||||
case 'string':
|
||||
this.result = code;
|
||||
this.result_message = message;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.engine_result = this.result = (this.result || this.engine_result || this.error || 'Error');
|
||||
this.engine_result_message = this.result_message = (this.result_message || this.engine_result_message || this.error_message || 'Error');
|
||||
this.result_message = this.message = (this.result_message);
|
||||
this.engine_result = this.result = (this.result || this.engine_result ||
|
||||
this.error || 'Error');
|
||||
this.engine_result_message = this.result_message = (this.result_message ||
|
||||
this.engine_result_message || this.error_message || 'Error');
|
||||
this.message = this.result_message;
|
||||
|
||||
var stack;
|
||||
let stack;
|
||||
|
||||
if (!!Error.captureStackTrace) {
|
||||
if (Boolean(Error.captureStackTrace)) {
|
||||
Error.captureStackTrace(this, code || this);
|
||||
} else if ((stack = new Error().stack)) {
|
||||
this.stack = stack;
|
||||
} else {
|
||||
stack = new Error().stack;
|
||||
if (Boolean(stack)) {
|
||||
this.stack = stack;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
util.inherits(RippleError, Error);
|
||||
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
//
|
||||
// Seed support
|
||||
//
|
||||
|
||||
const {KeyPair, KeyType} = require('ripple-keypairs');
|
||||
const {decodeSeed, encodeSeed} = require('ripple-address-codec');
|
||||
const extend = require('extend');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const BN = require('bn.js');
|
||||
const hashjs = require('hash.js');
|
||||
|
||||
const UInt = require('./uint').UInt;
|
||||
|
||||
const Seed = extend(function() {
|
||||
this._value = NaN;
|
||||
this._type = KeyType.secp256k1;
|
||||
}, UInt);
|
||||
|
||||
Seed.width = 16;
|
||||
Seed.prototype = Object.create(extend({}, UInt.prototype));
|
||||
Seed.prototype.constructor = Seed;
|
||||
|
||||
// value = NaN on error.
|
||||
// One day this will support rfc1751 too.
|
||||
Seed.prototype.parse_json = function(j) {
|
||||
if (typeof j === 'string') {
|
||||
if (!j.length) {
|
||||
this._value = NaN;
|
||||
} else {
|
||||
this.parse_base58(j);
|
||||
if (!this.is_valid()) {
|
||||
this.parse_hex(j);
|
||||
// XXX Should also try 1751
|
||||
}
|
||||
if (!this.is_valid() && j[0] !== 's') {
|
||||
this.parse_passphrase(j);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._value = NaN;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.parse_base58 = function(j) {
|
||||
if (typeof j !== 'string') {
|
||||
throw new Error('Value must be a string');
|
||||
}
|
||||
if (!j.length || j[0] !== 's') {
|
||||
this._value = NaN;
|
||||
} else {
|
||||
try {
|
||||
const {bytes, type} = decodeSeed(j);
|
||||
this._value = new BN(bytes);
|
||||
this._type = type;
|
||||
} catch (e) {
|
||||
this._value = NaN;
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.set_ed25519 = function() {
|
||||
this._type = KeyType.ed25519;
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.parse_passphrase = function(j) {
|
||||
if (typeof j !== 'string') {
|
||||
throw new Error('Passphrase must be a string');
|
||||
}
|
||||
|
||||
const phraseBytes = sjclcodec.bytes.fromBits(sjclcodec.utf8String.toBits(j));
|
||||
const hash = hashjs.sha512().update(phraseBytes).digest();
|
||||
this.parse_bytes(hash.slice(0, 16));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Seed.prototype.to_json = function() {
|
||||
if (!(this.is_valid())) {
|
||||
return NaN;
|
||||
}
|
||||
return encodeSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
Seed.prototype.get_key = function() {
|
||||
if (!this.is_valid()) {
|
||||
throw new Error('Cannot generate keys from invalid seed!');
|
||||
}
|
||||
return KeyPair.fromSeed(this.to_bytes(), this._type);
|
||||
};
|
||||
|
||||
exports.Seed = Seed;
|
||||
@@ -100,7 +100,8 @@ SerializedType.serialize_varint = function(so, val) {
|
||||
|
||||
SerializedType.prototype.parse_varint = function(so) {
|
||||
const b1 = so.read(1)[0];
|
||||
let b2, b3;
|
||||
let b2;
|
||||
let b3;
|
||||
let result;
|
||||
|
||||
if (b1 > 254) {
|
||||
@@ -416,7 +417,8 @@ exports.Quality = new SerializedType({
|
||||
value = new BigNumber(val);
|
||||
}
|
||||
|
||||
let hi = 0, lo = 0;
|
||||
let hi = 0;
|
||||
let lo = 0;
|
||||
|
||||
const offset = value.e - 15;
|
||||
if (val !== 0) {
|
||||
@@ -483,7 +485,8 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
valueBytes[0] |= 0x40;
|
||||
}
|
||||
} else {
|
||||
let hi = 0, lo = 0;
|
||||
let hi = 0;
|
||||
let lo = 0;
|
||||
|
||||
// First bit: non-native
|
||||
hi |= 1 << 31;
|
||||
@@ -518,7 +521,7 @@ const STAmount = exports.Amount = new SerializedType({
|
||||
STCurrency.serialize(so, currency, true);
|
||||
|
||||
// Issuer (160-bit hash)
|
||||
so.append(amount.issuer().to_bytes());
|
||||
so.append(UInt160.from_json(amount.issuer()).to_bytes());
|
||||
}
|
||||
},
|
||||
parse: function(so) {
|
||||
@@ -834,7 +837,7 @@ exports.STMemo = new SerializedType({
|
||||
output.parsed_memo_data = convertHexToString(output.MemoData);
|
||||
}
|
||||
/* eslint-disable no-empty */
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
// empty
|
||||
// we'll fail in case the content does not match what the MemoFormat
|
||||
// described
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
const _ = require('lodash');
|
||||
const {deriveKeypair, sign} = require('ripple-keypairs');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const utils = require('./utils');
|
||||
const sjclcodec = require('sjcl-codec');
|
||||
const Amount = require('./amount').Amount;
|
||||
const Currency = require('./amount').Currency;
|
||||
const UInt160 = require('./amount').UInt160;
|
||||
const Seed = require('./seed').Seed;
|
||||
const KeyPair = require('ripple-keypairs').KeyPair;
|
||||
const Currency = require('./currency').Currency;
|
||||
const UInt160 = require('./uint160').UInt160;
|
||||
const SerializedObject = require('./serializedobject').SerializedObject;
|
||||
const RippleError = require('./rippleerror').RippleError;
|
||||
const hashprefixes = require('./hashprefixes');
|
||||
const log = require('./log').internal.sub('transaction');
|
||||
const {isValidAddress} = require('ripple-address-codec');
|
||||
|
||||
/**
|
||||
* @constructor Transaction
|
||||
@@ -45,6 +45,7 @@ function Transaction(remote) {
|
||||
? this.remote.automatic_resubmission
|
||||
: true;
|
||||
this._maxFee = remoteExists ? this.remote.max_fee : undefined;
|
||||
this._lastLedgerOffset = remoteExists ? this.remote.last_ledger_offset : 3;
|
||||
this.state = 'unsubmitted';
|
||||
this.finalized = false;
|
||||
this.previousSigningHash = undefined;
|
||||
@@ -429,31 +430,12 @@ Transaction.prototype.complete = function() {
|
||||
return this.tx_json;
|
||||
};
|
||||
|
||||
Transaction.prototype.getKeyPair = function(secret_) {
|
||||
if (this._keyPair) {
|
||||
return this._keyPair;
|
||||
}
|
||||
|
||||
const secret = secret_ || this._secret;
|
||||
assert(secret, 'Secret missing');
|
||||
|
||||
const keyPair = Seed.from_json(secret).get_key();
|
||||
this._keyPair = keyPair;
|
||||
|
||||
return keyPair;
|
||||
};
|
||||
|
||||
Transaction.prototype.getSigningPubKey = function(secret) {
|
||||
return this.getKeyPair(secret).pubKeyHex();
|
||||
return deriveKeypair(secret || this._secret).publicKey;
|
||||
};
|
||||
|
||||
Transaction.prototype.setSigningPubKey = function(key) {
|
||||
if (_.isString(key)) {
|
||||
this.tx_json.SigningPubKey = key;
|
||||
} else if (key instanceof KeyPair) {
|
||||
this.tx_json.SigningPubKey = key.pubKeyHex();
|
||||
}
|
||||
|
||||
this.tx_json.SigningPubKey = key;
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -508,16 +490,13 @@ Transaction.prototype.hash = function(prefix_, asUINT256, serialized) {
|
||||
return asUINT256 ? hash : hash.to_hex();
|
||||
};
|
||||
|
||||
Transaction.prototype.sign = function() {
|
||||
Transaction.prototype.sign = function(secret) {
|
||||
if (this.hasMultiSigners()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const keyPair = this.getKeyPair();
|
||||
const prev_sig = this.tx_json.TxnSignature;
|
||||
|
||||
delete this.tx_json.TxnSignature;
|
||||
|
||||
const hash = this.signingHash();
|
||||
|
||||
// If the hash is the same, we can re-use the previous signature
|
||||
@@ -526,7 +505,9 @@ Transaction.prototype.sign = function() {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.tx_json.TxnSignature = keyPair.signHex(this.signingData().buffer);
|
||||
const keypair = deriveKeypair(secret || this._secret);
|
||||
this.tx_json.TxnSignature = sign(this.signingData().buffer,
|
||||
keypair.privateKey);
|
||||
this.previousSigningHash = hash;
|
||||
|
||||
return this;
|
||||
@@ -582,19 +563,30 @@ Transaction.prototype.clientID = function(id) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set LastLedgerSequence as the absolute last ledger sequence the transaction
|
||||
* is valid for. LastLedgerSequence is set automatically if not set using this
|
||||
* method
|
||||
*
|
||||
* @param {Number} ledger index
|
||||
*/
|
||||
Transaction.prototype.setLastLedgerSequenceOffset = function(offset) {
|
||||
this._lastLedgerOffset = offset;
|
||||
};
|
||||
|
||||
Transaction.prototype.setLastLedgerSequence =
|
||||
Transaction.prototype.getLastLedgerSequenceOffset = function() {
|
||||
return this._lastLedgerOffset;
|
||||
};
|
||||
|
||||
Transaction.prototype.lastLedger =
|
||||
Transaction.prototype.setLastLedger =
|
||||
Transaction.prototype.lastLedger = function(sequence) {
|
||||
this._setUInt32('LastLedgerSequence', sequence);
|
||||
Transaction.prototype.setLastLedgerSequence = function(sequence) {
|
||||
if (!_.isUndefined(sequence)) {
|
||||
this._setUInt32('LastLedgerSequence', sequence);
|
||||
} else {
|
||||
// Autofill LastLedgerSequence
|
||||
assert(this.remote, 'Unable to set LastLedgerSequence, missing Remote');
|
||||
|
||||
this._setUInt32('LastLedgerSequence',
|
||||
this.remote.getLedgerSequenceSync() + 1
|
||||
+ this.getLastLedgerSequenceOffset());
|
||||
}
|
||||
|
||||
this._setLastLedger = true;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -731,7 +723,7 @@ Transaction.prototype._setAmount = function(name, amount, options_) {
|
||||
if (!(isNative || parsedAmount.currency().is_valid())) {
|
||||
throw new Error(name + ' must have a valid currency');
|
||||
}
|
||||
if (!(isNative || parsedAmount.issuer().is_valid())) {
|
||||
if (!(isNative || isValidAddress(parsedAmount.issuer()))) {
|
||||
throw new Error(name + ' must have a valid issuer');
|
||||
}
|
||||
|
||||
@@ -1479,7 +1471,7 @@ Transaction.prototype.summary = function() {
|
||||
submissionAttempts: this.attempts,
|
||||
submitIndex: this.submitIndex,
|
||||
initialSubmitIndex: this.initialSubmitIndex,
|
||||
lastLedgerSequence: this.lastLedgerSequence,
|
||||
lastLedgerSequence: this.tx_json.LastLedgerSequence,
|
||||
state: this.state,
|
||||
finalized: this.finalized
|
||||
};
|
||||
@@ -1616,30 +1608,44 @@ Transaction.prototype.setSigners = function(signers) {
|
||||
Transaction.prototype.addMultiSigner = function(signer) {
|
||||
assert(UInt160.is_valid(signer.Account), 'Signer must have a valid Account');
|
||||
|
||||
if (_.isUndefined(this.multi_signers)) {
|
||||
this.multi_signers = [];
|
||||
if (_.isUndefined(this.tx_json.Signers)) {
|
||||
this.tx_json.Signers = [];
|
||||
}
|
||||
|
||||
this.multi_signers.push({Signer: signer});
|
||||
this.tx_json.Signers.push({Signer: signer});
|
||||
|
||||
this.multi_signers.sort((a, b) => {
|
||||
this.tx_json.Signers.sort((a, b) => {
|
||||
return UInt160.from_json(a.Signer.Account)
|
||||
.cmp(UInt160.from_json(b.Signer.Account));
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
Transaction.prototype.hasMultiSigners = function() {
|
||||
return !_.isEmpty(this.multi_signers);
|
||||
return !_.isEmpty(this.tx_json.Signers);
|
||||
};
|
||||
|
||||
Transaction.prototype.getMultiSigners = function() {
|
||||
return this.multi_signers;
|
||||
return this.tx_json.Signers;
|
||||
};
|
||||
|
||||
Transaction.prototype.getMultiSigningJson = function() {
|
||||
assert(this.tx_json.Sequence, 'Sequence must be set before multi-signing');
|
||||
assert(this.tx_json.Fee, 'Fee must be set before multi-signing');
|
||||
|
||||
const signingTx = Transaction.from_json(this.tx_json);
|
||||
if (_.isUndefined(this.tx_json.LastLedgerSequence)) {
|
||||
// Auto-fill LastLedgerSequence
|
||||
this.setLastLedgerSequence();
|
||||
}
|
||||
|
||||
const cleanedJson = _.omit(this.tx_json, [
|
||||
'SigningPubKey',
|
||||
'Signers',
|
||||
'TxnSignature'
|
||||
]);
|
||||
|
||||
const signingTx = Transaction.from_json(cleanedJson);
|
||||
signingTx.remote = this.remote;
|
||||
signingTx.setSigningPubKey('');
|
||||
signingTx.setCanonicalFlag();
|
||||
@@ -1649,12 +1655,12 @@ Transaction.prototype.getMultiSigningJson = function() {
|
||||
|
||||
Transaction.prototype.multiSign = function(account, secret) {
|
||||
const signingData = this.multiSigningData(account);
|
||||
const keyPair = Seed.from_json(secret).get_key();
|
||||
const keypair = deriveKeypair(secret);
|
||||
|
||||
const signer = {
|
||||
Account: account,
|
||||
TxnSignature: keyPair.signHex(signingData.buffer),
|
||||
SigningPubKey: keyPair.pubKeyHex()
|
||||
TxnSignature: sign(signingData.buffer, keypair.privateKey),
|
||||
SigningPubKey: keypair.publicKey
|
||||
};
|
||||
|
||||
return signer;
|
||||
|
||||
@@ -21,13 +21,12 @@ function TransactionManager(account) {
|
||||
const self = this;
|
||||
|
||||
this._account = account;
|
||||
this._accountID = account._account_id;
|
||||
this._accountID = account._address;
|
||||
this._remote = account._remote;
|
||||
this._nextSequence = undefined;
|
||||
this._maxFee = this._remote.max_fee;
|
||||
this._maxAttempts = this._remote.max_attempts;
|
||||
this._submissionTimeout = this._remote.submission_timeout;
|
||||
this._lastLedgerOffset = this._remote.last_ledger_offset;
|
||||
this._pending = new PendingQueue();
|
||||
|
||||
this._account.on('transaction-outbound', function(res) {
|
||||
@@ -521,17 +520,6 @@ TransactionManager.prototype._resubmit = function(ledgers_, pending_) {
|
||||
TransactionManager.prototype._prepareRequest = function(tx) {
|
||||
const submitRequest = this._remote.requestSubmit();
|
||||
|
||||
if (tx.hasMultiSigners()) {
|
||||
tx.setSigningPubKey('');
|
||||
|
||||
if (this._remote.local_signing) {
|
||||
tx.setSigners(tx.getMultiSigners());
|
||||
} else {
|
||||
submitRequest.message.command = 'submit_multisigned';
|
||||
submitRequest.message.Signers = tx.getMultiSigners();
|
||||
}
|
||||
}
|
||||
|
||||
if (this._remote.local_signing) {
|
||||
tx.sign();
|
||||
|
||||
@@ -541,6 +529,10 @@ TransactionManager.prototype._prepareRequest = function(tx) {
|
||||
const hash = tx.hash(null, null, serialized);
|
||||
tx.addId(hash);
|
||||
} else {
|
||||
if (tx.hasMultiSigners()) {
|
||||
submitRequest.message.command = 'submit_multisigned';
|
||||
}
|
||||
|
||||
// ND: `build_path` is completely ignored when doing local signing as
|
||||
// `Paths` is a component of the signed blob, the `tx_blob` is signed,
|
||||
// sealed and delivered, and the txn unmodified.
|
||||
@@ -580,6 +572,11 @@ TransactionManager.prototype._request = function(tx) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number(tx.tx_json.Fee) > tx._maxFee) {
|
||||
tx.emit('error', new RippleError('tejMaxFeeExceeded'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (remote.trace) {
|
||||
log.info('submit transaction:', tx.tx_json);
|
||||
}
|
||||
@@ -684,24 +681,12 @@ TransactionManager.prototype._request = function(tx) {
|
||||
}
|
||||
}
|
||||
|
||||
tx.submitIndex = this._remote._ledger_current_index;
|
||||
tx.submitIndex = this._remote.getLedgerSequenceSync() + 1;
|
||||
|
||||
if (tx.attempts === 0) {
|
||||
tx.initialSubmitIndex = tx.submitIndex;
|
||||
}
|
||||
|
||||
if (!tx._setLastLedger && !tx.hasMultiSigners()) {
|
||||
// Honor LastLedgerSequence set with tx.lastLedger()
|
||||
tx.tx_json.LastLedgerSequence = tx.initialSubmitIndex
|
||||
+ this._lastLedgerOffset;
|
||||
}
|
||||
|
||||
tx.lastLedgerSequence = tx.tx_json.LastLedgerSequence;
|
||||
|
||||
if (remote.local_signing) {
|
||||
tx.sign();
|
||||
}
|
||||
|
||||
const submitRequest = this._prepareRequest(tx);
|
||||
submitRequest.once('error', submitted);
|
||||
submitRequest.once('success', submitted);
|
||||
@@ -713,7 +698,8 @@ TransactionManager.prototype._request = function(tx) {
|
||||
|
||||
tx.emit('postsubmit');
|
||||
|
||||
submitRequest.timeout(self._submissionTimeout, requestTimeout);
|
||||
submitRequest.setTimeout(self._submissionTimeout);
|
||||
submitRequest.once('timeout', requestTimeout);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -743,8 +729,13 @@ TransactionManager.prototype.submit = function(tx) {
|
||||
tx.setSequence(this._nextSequence++);
|
||||
}
|
||||
|
||||
if (_.isUndefined(tx.tx_json.LastLedgerSequence)) {
|
||||
tx.setLastLedgerSequence();
|
||||
}
|
||||
|
||||
if (tx.hasMultiSigners()) {
|
||||
tx.setResubmittable(false);
|
||||
tx.setSigningPubKey('');
|
||||
}
|
||||
|
||||
tx.once('cleanup', function() {
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
|
||||
const BigNumber = GlobalBigNumber.another({
|
||||
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
|
||||
DECIMAL_PLACES: 40
|
||||
});
|
||||
|
||||
const assert = require('assert');
|
||||
|
||||
class Value {
|
||||
|
||||
constructor(value: string | BigNumber) {
|
||||
if (this.constructor === 'Value') {
|
||||
throw new Error(
|
||||
'Cannot instantiate Value directly, it is an abstract base class');
|
||||
}
|
||||
this._value = new BigNumber(value);
|
||||
}
|
||||
|
||||
static getBNRoundDown() {
|
||||
return BigNumber.ROUND_DOWN;
|
||||
}
|
||||
|
||||
abs() {
|
||||
const result = this._value.abs();
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
add(addend: Value) {
|
||||
assert(this.constructor === addend.constructor);
|
||||
const result = this._value.plus(addend._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
subtract(subtrahend: Value) {
|
||||
assert(this.constructor === subtrahend.constructor);
|
||||
const result = this._value.minus(subtrahend._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
multiply(multiplicand: Value) {
|
||||
const result = this._value.times(multiplicand._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
divide(divisor: Value) {
|
||||
if (divisor.isZero()) {
|
||||
throw new Error('divide by zero');
|
||||
}
|
||||
const result = this._value.dividedBy(divisor._value);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
invert() {
|
||||
const result = (new BigNumber(this._value)).toPower(-1);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
round(decimalPlaces: number, roundingMode: number) {
|
||||
const result = this._value.round(decimalPlaces, roundingMode);
|
||||
return this._canonicalize(result);
|
||||
}
|
||||
|
||||
toFixed(decimalPlaces: number, roundingMode: number) {
|
||||
return this._value.toFixed(decimalPlaces, roundingMode);
|
||||
}
|
||||
|
||||
getExponent() {
|
||||
return this._value.e;
|
||||
}
|
||||
|
||||
isNaN() {
|
||||
return this._value.isNaN();
|
||||
}
|
||||
|
||||
isZero() {
|
||||
return this._value.isZero();
|
||||
}
|
||||
|
||||
isNegative() {
|
||||
return this._value.isNegative();
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this._value.toString();
|
||||
}
|
||||
|
||||
greaterThan(comparator: Value) {
|
||||
assert(this.constructor === comparator.constructor);
|
||||
return this._value.greaterThan(comparator._value);
|
||||
}
|
||||
|
||||
lessThan(comparator: Value) {
|
||||
assert(this.constructor === comparator.constructor);
|
||||
return this._value.lessThan(comparator._value);
|
||||
}
|
||||
|
||||
comparedTo(comparator: Value) {
|
||||
assert(this.constructor === comparator.constructor);
|
||||
return this._value.comparedTo(comparator._value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
exports.Value = Value;
|
||||
@@ -1,59 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
'use strict';
|
||||
|
||||
const GlobalBigNumber = require('bignumber.js');
|
||||
const BigNumber = GlobalBigNumber.another({
|
||||
ROUNDING_MODE: GlobalBigNumber.ROUND_HALF_UP,
|
||||
DECIMAL_PLACES: 40
|
||||
});
|
||||
|
||||
const Value = require('./value').Value;
|
||||
const rippleUnits = new BigNumber(1e6);
|
||||
|
||||
class XRPValue extends Value {
|
||||
|
||||
constructor(value: string | BigNumber) {
|
||||
super(value);
|
||||
if (this._value.dp() > 6) {
|
||||
throw new Error(
|
||||
'Value has more than 6 digits of precision past the decimal point, '
|
||||
+ 'an IOUValue may be being cast to an XRPValue'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
multiply(multiplicand: Value) {
|
||||
if (multiplicand instanceof XRPValue) {
|
||||
return super.multiply(
|
||||
new XRPValue(multiplicand._value.times(rippleUnits)));
|
||||
}
|
||||
return super.multiply(multiplicand);
|
||||
}
|
||||
|
||||
divide(divisor: Value) {
|
||||
if (divisor instanceof XRPValue) {
|
||||
return super.divide(
|
||||
new XRPValue(divisor._value.times(rippleUnits)));
|
||||
}
|
||||
return super.divide(divisor);
|
||||
}
|
||||
|
||||
negate() {
|
||||
return new XRPValue(this._value.neg());
|
||||
}
|
||||
|
||||
_canonicalize(value) {
|
||||
if (value.isNaN()) {
|
||||
throw new Error('Invalid result');
|
||||
}
|
||||
return new XRPValue(value.round(6, BigNumber.ROUND_DOWN));
|
||||
}
|
||||
|
||||
equals(comparator) {
|
||||
return (comparator instanceof XRPValue)
|
||||
&& this._value.equals(comparator._value);
|
||||
}
|
||||
}
|
||||
|
||||
exports.XRPValue = XRPValue;
|
||||
@@ -1048,6 +1048,17 @@ describe('Amount', function() {
|
||||
});
|
||||
|
||||
describe('ratio_human', function() {
|
||||
it('Divide USD by XRP', function() {
|
||||
const a = Amount.from_json({
|
||||
value: '0.08161672093323858',
|
||||
currency: 'USD',
|
||||
issuer: 'rLFPPebckMYZf3urdomLsaqRGmQ6zHVrrK'
|
||||
});
|
||||
const b = Amount.from_json('15000000');
|
||||
const c = a.ratio_human(b);
|
||||
assert.deepEqual(c.to_json(), {value: '0.005441114728882572',
|
||||
currency: 'USD', issuer: 'rLFPPebckMYZf3urdomLsaqRGmQ6zHVrrK'});
|
||||
});
|
||||
it('Divide USD by XAU (dem)', function() {
|
||||
assert.strictEqual(Amount.from_json('2000/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh').ratio_human(Amount.from_json('10/015841551A748AD2C1F76FF6ECB0CCCD00000000/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh'), {reference_date: 443845330 + 31535000}).to_text_full(), '201.0049931765529/USD/rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
});
|
||||
|
||||
@@ -29,7 +29,6 @@ const orderbook = {
|
||||
};
|
||||
|
||||
function checkResult(expected, schemaName, response) {
|
||||
// console.log(JSON.stringify(response, null, 2));
|
||||
assert.deepEqual(response, expected);
|
||||
if (schemaName) {
|
||||
schemaValidator.schemaValidate(schemaName, response);
|
||||
@@ -48,27 +47,35 @@ describe('RippleAPI', function() {
|
||||
}, instructions);
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePayment, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePayment, 'prepare'));
|
||||
_.partial(checkResult, responses.preparePayment.normal, 'prepare'));
|
||||
});
|
||||
|
||||
it('preparePayment with all options specified', function() {
|
||||
const localInstructions = {
|
||||
maxLedgerVersion: this.api.getLedgerVersion() + 100,
|
||||
fee: '0.000012'
|
||||
};
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentAllOptions, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePaymentAllOptions, 'prepare'));
|
||||
return this.api.getLedgerVersion().then((ver) => {
|
||||
const localInstructions = {
|
||||
maxLedgerVersion: ver + 100,
|
||||
fee: '0.000012'
|
||||
};
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentAllOptions, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePayment.allOptions, 'prepare'));
|
||||
});
|
||||
});
|
||||
|
||||
it('preparePayment without counterparty set', function() {
|
||||
const localInstructions = _.defaults({sequence: 23}, instructions);
|
||||
return this.api.preparePayment(
|
||||
address, requests.preparePaymentNoCounterparty, localInstructions).then(
|
||||
_.partial(checkResult, responses.preparePaymentNoCounterparty,
|
||||
_.partial(checkResult, responses.preparePayment.noCounterparty,
|
||||
'prepare'));
|
||||
});
|
||||
|
||||
it('preparePayment - destination.minAmount', function() {
|
||||
return this.api.preparePayment(address, responses.getPaths.sendAll[0],
|
||||
instructions).then(_.partial(checkResult,
|
||||
responses.preparePayment.minAmount, 'prepare'));
|
||||
});
|
||||
|
||||
it('prepareOrder - buy order', function() {
|
||||
return this.api.prepareOrder(address, requests.prepareOrder, instructions)
|
||||
.then(_.partial(checkResult, responses.prepareOrder, 'prepare'));
|
||||
@@ -194,6 +201,11 @@ describe('RippleAPI', function() {
|
||||
_.partial(checkResult, responses.getBalances, 'getBalances'));
|
||||
});
|
||||
|
||||
it('getBalanceSheet', function() {
|
||||
return this.api.getBalanceSheet(address).then(
|
||||
_.partial(checkResult, responses.getBalanceSheet, 'getBalanceSheet'));
|
||||
});
|
||||
|
||||
describe('getTransaction', () => {
|
||||
it('getTransaction - payment', function() {
|
||||
return this.api.getTransaction(hashes.VALID_TRANSACTION_HASH).then(
|
||||
@@ -481,7 +493,7 @@ describe('RippleAPI', function() {
|
||||
function random() {
|
||||
return _.fill(Array(16), 0);
|
||||
}
|
||||
assert.deepEqual(this.api.generateAddress({random}),
|
||||
assert.deepEqual(this.api.generateAddress({entropy: random()}),
|
||||
responses.generateAddress);
|
||||
});
|
||||
|
||||
@@ -570,6 +582,18 @@ describe('RippleAPI', function() {
|
||||
_.partial(checkResult, responses.getPaths.XrpToUsd, 'getPaths'));
|
||||
});
|
||||
|
||||
it('getPaths - queuing', function() {
|
||||
return Promise.all([
|
||||
this.api.getPaths(requests.getPaths.normal),
|
||||
this.api.getPaths(requests.getPaths.UsdToUsd),
|
||||
this.api.getPaths(requests.getPaths.XrpToXrp)
|
||||
]).then(results => {
|
||||
checkResult(responses.getPaths.XrpToUsd, 'getPaths', results[0]);
|
||||
checkResult(responses.getPaths.UsdToUsd, 'getPaths', results[1]);
|
||||
checkResult(responses.getPaths.XrpToXrp, 'getPaths', results[2]);
|
||||
});
|
||||
});
|
||||
|
||||
// @TODO
|
||||
// need decide what to do with currencies/XRP:
|
||||
// if add 'XRP' in currencies, then there will be exception in
|
||||
@@ -625,8 +649,16 @@ describe('RippleAPI', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('getLedgerVersion', function() {
|
||||
assert.strictEqual(this.api.getLedgerVersion(), 8819951);
|
||||
it('getPaths - send all', function() {
|
||||
return this.api.getPaths(requests.getPaths.sendAll).then(
|
||||
_.partial(checkResult, responses.getPaths.sendAll, 'getPaths'));
|
||||
});
|
||||
|
||||
it('getLedgerVersion', function(done) {
|
||||
this.api.getLedgerVersion().then((ver) => {
|
||||
assert.strictEqual(ver, 8819951);
|
||||
done();
|
||||
}, done);
|
||||
});
|
||||
|
||||
it('getLedger', function() {
|
||||
|
||||
15
test/fixtures/api/requests/getpaths/send-all.json
vendored
Normal file
15
test/fixtures/api/requests/getpaths/send-all.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"amount": {
|
||||
"currency": "USD"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
test/fixtures/api/requests/index.js
vendored
3
test/fixtures/api/requests/index.js
vendored
@@ -25,7 +25,8 @@ module.exports = {
|
||||
XrpToXrpNotEnough: require('./getpaths/xrp2xrp-not-enough'),
|
||||
NotAcceptCurrency: require('./getpaths/not-accept-currency'),
|
||||
NoPaths: require('./getpaths/no-paths'),
|
||||
NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies')
|
||||
NoPathsWithCurrencies: require('./getpaths/no-paths-with-currencies'),
|
||||
sendAll: require('./getpaths/send-all')
|
||||
},
|
||||
computeLedgerHash: {
|
||||
header: require('./compute-ledger-hash'),
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
}
|
||||
],
|
||||
"invoiceID": "A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A",
|
||||
"allowPartialPayment": true,
|
||||
"noDirectRipple": true,
|
||||
"limitQuality": true,
|
||||
"paths": "[[{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"type\":49,\"type_hex\":\"0000000000000031\"},{\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":48,\"type_hex\":\"0000000000000030\"},{\"account\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"currency\":\"LTC\",\"issuer\":\"rfYv1TXnwgDDK4WQNbFALykYuEBnrR4pDX\",\"type\":49,\"type_hex\":\"0000000000000031\"}]]"
|
||||
|
||||
54
test/fixtures/api/responses/get-balance-sheet.json
vendored
Normal file
54
test/fixtures/api/responses/get-balance-sheet.json
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"balances": [
|
||||
{
|
||||
"counterparty": "rKm4uWpg9tfwbVSeATv4KxDe6mpE9yPkgJ",
|
||||
"currency": "EUR",
|
||||
"value": "29826.1965999999"
|
||||
},
|
||||
{
|
||||
"counterparty": "rKm4uWpg9tfwbVSeATv4KxDe6mpE9yPkgJ",
|
||||
"currency": "USD",
|
||||
"value": "10.0"
|
||||
},
|
||||
{
|
||||
"counterparty": "ra7JkEzrgeKHdzKgo4EUUVBnxggY4z37kt",
|
||||
"currency": "USD",
|
||||
"value": "13857.70416"
|
||||
}
|
||||
],
|
||||
"assets": [
|
||||
{
|
||||
"counterparty": "r9F6wk8HkXrgYWoJ7fsv4VrUBVoqDVtzkH",
|
||||
"currency": "BTC",
|
||||
"value": "5444166510000000e-26"
|
||||
},
|
||||
{
|
||||
"counterparty": "r9F6wk8HkXrgYWoJ7fsv4VrUBVoqDVtzkH",
|
||||
"currency": "USD",
|
||||
"value": "100.0"
|
||||
},
|
||||
{
|
||||
"counterparty": "rwmUaXsWtXU4Z843xSYwgt1is97bgY8yj6",
|
||||
"currency": "BTC",
|
||||
"value": "8700000000000000e-30"
|
||||
}
|
||||
],
|
||||
"obligations": [
|
||||
{
|
||||
"currency": "BTC",
|
||||
"value": "5908.324927635318"
|
||||
},
|
||||
{
|
||||
"currency": "EUR",
|
||||
"value": "992471.7419793958"
|
||||
},
|
||||
{
|
||||
"currency": "GBP",
|
||||
"value": "4991.38706013193"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"value": "1997134.20229482"
|
||||
}
|
||||
]
|
||||
}
|
||||
70
test/fixtures/api/responses/get-paths-send-all.json
vendored
Normal file
70
test/fixtures/api/responses/get-paths-send-all.json
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
[
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.93463759481038"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.93463759481038"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"EUR\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}],[{\"account\":\"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun\"},{\"currency\":\"USD\",\"issuer\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"}]]"
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.93463759481038"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"currency\":\"USD\",\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"JPY\",\"issuer\":\"rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
|
||||
},
|
||||
{
|
||||
"source": {
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"amount": {
|
||||
"currency": "USD",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
"address": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"minAmount": {
|
||||
"currency": "USD",
|
||||
"value": "4.990019960079841"
|
||||
}
|
||||
},
|
||||
"paths": "[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"USD\",\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]]"
|
||||
}
|
||||
]
|
||||
@@ -4,8 +4,7 @@
|
||||
"address": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||
"maxAmount": {
|
||||
"currency": "USD",
|
||||
"value": "0.000001002",
|
||||
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo"
|
||||
"value": "0.000001002"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
|
||||
6
test/fixtures/api/responses/get-paths.json
vendored
6
test/fixtures/api/responses/get-paths.json
vendored
@@ -4,8 +4,7 @@
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"maxAmount": {
|
||||
"currency": "JPY",
|
||||
"value": "0.1117218827811721",
|
||||
"counterparty": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||
"value": "0.1117218827811721"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
@@ -23,8 +22,7 @@
|
||||
"address": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"maxAmount": {
|
||||
"currency": "USD",
|
||||
"value": "0.001002",
|
||||
"counterparty": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
|
||||
"value": "0.001002"
|
||||
}
|
||||
},
|
||||
"destination": {
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"currency": "XRP",
|
||||
"value": "0"
|
||||
},
|
||||
"makerExchangeRate": "1185000",
|
||||
"sequence": 465,
|
||||
"status": "canceled"
|
||||
}
|
||||
@@ -39,4 +40,4 @@
|
||||
"ledgerVersion": 14661789,
|
||||
"indexInLedger": 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"currency": "XRP",
|
||||
"value": "0.0002"
|
||||
},
|
||||
"makerExchangeRate": "1185000",
|
||||
"sequence": 465,
|
||||
"status": "created"
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||
"value": "-0.001002"
|
||||
},
|
||||
"makerExchangeRate": "1099",
|
||||
"sequence": 58,
|
||||
"status": "open"
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||
"value": "-0.001002"
|
||||
},
|
||||
"makerExchangeRate": "1099",
|
||||
"sequence": 58,
|
||||
"status": "open"
|
||||
}
|
||||
@@ -172,6 +173,7 @@
|
||||
"counterparty": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo",
|
||||
"value": "-0.001002"
|
||||
},
|
||||
"makerExchangeRate": "1099",
|
||||
"sequence": 58,
|
||||
"status": "open"
|
||||
}
|
||||
|
||||
14
test/fixtures/api/responses/index.js
vendored
14
test/fixtures/api/responses/index.js
vendored
@@ -4,12 +4,14 @@ module.exports = {
|
||||
generateAddress: require('./generate-address.json'),
|
||||
getAccountInfo: require('./get-account-info.json'),
|
||||
getBalances: require('./get-balances.json'),
|
||||
getBalanceSheet: require('./get-balance-sheet.json'),
|
||||
getOrderbook: require('./get-orderbook.json'),
|
||||
getOrders: require('./get-orders.json'),
|
||||
getPaths: {
|
||||
XrpToUsd: require('./get-paths.json'),
|
||||
UsdToUsd: require('./get-paths-send-usd.json'),
|
||||
XrpToXrp: require('./get-paths-xrp-to-xrp.json')
|
||||
XrpToXrp: require('./get-paths-xrp-to-xrp.json'),
|
||||
sendAll: require('./get-paths-send-all.json')
|
||||
},
|
||||
getServerInfo: require('./get-server-info.json'),
|
||||
getSettings: require('./get-settings.json'),
|
||||
@@ -35,10 +37,12 @@ module.exports = {
|
||||
prepareOrderCancellation: require('./prepare-order-cancellation.json'),
|
||||
prepareOrder: require('./prepare-order.json'),
|
||||
prepareOrderSell: require('./prepare-order-sell.json'),
|
||||
preparePayment: require('./prepare-payment.json'),
|
||||
preparePaymentAllOptions: require('./prepare-payment-all-options.json'),
|
||||
preparePaymentNoCounterparty:
|
||||
require('./prepare-payment-no-counterparty.json'),
|
||||
preparePayment: {
|
||||
normal: require('./prepare-payment.json'),
|
||||
allOptions: require('./prepare-payment-all-options.json'),
|
||||
noCounterparty: require('./prepare-payment-no-counterparty.json'),
|
||||
minAmount: require('./prepare-payment-min-amount.json')
|
||||
},
|
||||
prepareSettings: {
|
||||
regularKey: require('./prepare-settings-regular-key.json'),
|
||||
flags: require('./prepare-settings.json'),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"txJSON": "{\"Flags\":458752,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"10000\",\"InvoiceID\":\"A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A\",\"SourceTag\":14,\"DestinationTag\":58,\"Memos\":[{\"Memo\":{\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\",\"MemoData\":\"7465787465642064617461\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
||||
"txJSON": "{\"Flags\":327680,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo\",\"Amount\":\"10000\",\"InvoiceID\":\"A98FD36C17BE2B8511AD36DC335478E7E89F06262949F36EB88E2D683BBCC50A\",\"SourceTag\":14,\"DestinationTag\":58,\"Memos\":[{\"Memo\":{\"MemoType\":\"74657374\",\"MemoFormat\":\"706C61696E2F74657874\",\"MemoData\":\"7465787465642064617461\"}}],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
||||
"instructions": {
|
||||
"fee": "12",
|
||||
"sequence": 23,
|
||||
|
||||
8
test/fixtures/api/responses/prepare-payment-min-amount.json
vendored
Normal file
8
test/fixtures/api/responses/prepare-payment-min-amount.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"txJSON": "{\"Flags\":131072,\"TransactionType\":\"Payment\",\"Account\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\",\"Destination\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\",\"Amount\":{\"value\":\"9999999999999999e80\",\"currency\":\"USD\",\"issuer\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"},\"SendMax\":{\"value\":\"5\",\"currency\":\"USD\",\"issuer\":\"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59\"},\"DeliverMin\":{\"value\":\"9999999999999999e80\",\"currency\":\"USD\",\"issuer\":\"ra5nK24KXen9AHvsdFTKHSANinZseWnPcX\"},\"Paths\":[[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"issuer\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\",\"currency\":\"USD\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"},{\"account\":\"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn\"}],[{\"account\":\"rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B\"},{\"currency\":\"XRP\"},{\"issuer\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\",\"currency\":\"USD\"},{\"account\":\"rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP\"},{\"account\":\"rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q\"}]],\"LastLedgerSequence\":8820051,\"Fee\":\"12\",\"Sequence\":23}",
|
||||
"instructions": {
|
||||
"fee": "12",
|
||||
"sequence": 23,
|
||||
"maxLedgerVersion": 8820051
|
||||
}
|
||||
}
|
||||
52
test/fixtures/api/rippled/gateway-balances.json
vendored
Normal file
52
test/fixtures/api/rippled/gateway-balances.json
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"id": 0,
|
||||
"status": "success",
|
||||
"type": "response",
|
||||
"result": {
|
||||
"account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"assets": {
|
||||
"r9F6wk8HkXrgYWoJ7fsv4VrUBVoqDVtzkH": [
|
||||
{
|
||||
"currency": "BTC",
|
||||
"value": "5444166510000000e-26"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"value": "100.0"
|
||||
}
|
||||
],
|
||||
"rwmUaXsWtXU4Z843xSYwgt1is97bgY8yj6": [
|
||||
{
|
||||
"currency": "BTC",
|
||||
"value": "8700000000000000e-30"
|
||||
}
|
||||
]
|
||||
},
|
||||
"balances": {
|
||||
"rKm4uWpg9tfwbVSeATv4KxDe6mpE9yPkgJ": [
|
||||
{
|
||||
"currency": "EUR",
|
||||
"value": "29826.1965999999"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"value": "10.0"
|
||||
}
|
||||
],
|
||||
"ra7JkEzrgeKHdzKgo4EUUVBnxggY4z37kt": [
|
||||
{
|
||||
"currency": "USD",
|
||||
"value": "13857.70416"
|
||||
}
|
||||
]
|
||||
},
|
||||
"obligations": {
|
||||
"BTC": "5908.324927635318",
|
||||
"EUR": "992471.7419793958",
|
||||
"GBP": "4991.38706013193",
|
||||
"USD": "1997134.20229482"
|
||||
},
|
||||
"ledger_current_index": 9592219,
|
||||
"validated": true
|
||||
}
|
||||
}
|
||||
2
test/fixtures/api/rippled/index.js
vendored
2
test/fixtures/api/rippled/index.js
vendored
@@ -16,12 +16,14 @@ module.exports = {
|
||||
},
|
||||
account_offers: require('./account-offers'),
|
||||
account_tx: require('./account-tx'),
|
||||
gateway_balances: require('./gateway-balances'),
|
||||
book_offers: require('./book-offers'),
|
||||
server_info: require('./server-info'),
|
||||
server_info_error: require('./server-info-error'),
|
||||
path_find: {
|
||||
generate: require('./path-find'),
|
||||
sendUSD: require('./path-find-send-usd'),
|
||||
sendAll: require('./path-find-send-all'),
|
||||
XrpToXrp: require('./path-find-xrp-to-xrp'),
|
||||
srcActNotFound: require('./path-find-srcActNotFound')
|
||||
},
|
||||
|
||||
313
test/fixtures/api/rippled/path-find-send-all.json
vendored
Normal file
313
test/fixtures/api/rippled/path-find-send-all.json
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
{
|
||||
"alternatives": [
|
||||
{
|
||||
"destination_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"value": "4.93463759481038"
|
||||
},
|
||||
"paths_computed": [
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "XRP",
|
||||
"type": 16,
|
||||
"type_hex": "0000000000000010"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "XRP",
|
||||
"type": 16,
|
||||
"type_hex": "0000000000000010"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
]
|
||||
],
|
||||
"source_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"destination_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"value": "4.93463759481038"
|
||||
},
|
||||
"paths_computed": [
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "XRP",
|
||||
"type": 16,
|
||||
"type_hex": "0000000000000010"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "EUR",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
]
|
||||
],
|
||||
"source_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"destination_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"value": "4.93463759481038"
|
||||
},
|
||||
"paths_computed": [
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "XRP",
|
||||
"type": 16,
|
||||
"type_hex": "0000000000000010"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "XRP",
|
||||
"type": 16,
|
||||
"type_hex": "0000000000000010"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rfsEoNBUBbvkf4jPcFe2u9CyaQagLVHGfP",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "JPY",
|
||||
"issuer": "rMAz5ZnK73nyNUL4foAvaxdreczCkG3vA6",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
]
|
||||
],
|
||||
"source_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"value": "5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"destination_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"value": "4.990019960079841"
|
||||
},
|
||||
"paths_computed": [
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"account": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
},
|
||||
{
|
||||
"currency": "USD",
|
||||
"issuer": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 48,
|
||||
"type_hex": "0000000000000030"
|
||||
},
|
||||
{
|
||||
"account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
|
||||
"type": 1,
|
||||
"type_hex": "0000000000000001"
|
||||
}
|
||||
]
|
||||
],
|
||||
"source_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"value": "5"
|
||||
}
|
||||
}
|
||||
],
|
||||
"destination_account": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"destination_amount": {
|
||||
"currency": "USD",
|
||||
"issuer": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX",
|
||||
"value": "-1"
|
||||
},
|
||||
"full_reply": true,
|
||||
"id": 1,
|
||||
"source_account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
|
||||
"type": "path_find"
|
||||
}
|
||||
2
test/fixtures/orderbook.js
vendored
2
test/fixtures/orderbook.js
vendored
@@ -7,7 +7,7 @@ const addresses = require('./addresses');
|
||||
const Meta = require('ripple-lib').Meta;
|
||||
const SerializedObject = require('ripple-lib').SerializedObject;
|
||||
const Types = require('ripple-lib').types;
|
||||
const IOUValue = require('ripple-lib')._test.IOUValue;
|
||||
const IOUValue = require('ripple-lib-value').IOUValue;
|
||||
|
||||
module.exports.FIAT_BALANCE = '10';
|
||||
module.exports.NATIVE_BALANCE = '55';
|
||||
|
||||
@@ -266,12 +266,25 @@ module.exports = function(port) {
|
||||
destination_amount: request.destination_amount,
|
||||
destination_address: request.destination_address
|
||||
});
|
||||
} else if (request.source_account === addresses.ACCOUNT) {
|
||||
if (request.destination_account ===
|
||||
'ra5nK24KXen9AHvsdFTKHSANinZseWnPcX') {
|
||||
response = createResponse(request, fixtures.path_find.sendAll);
|
||||
} else {
|
||||
response = fixtures.path_find.generate.generateIOUPaymentPaths(
|
||||
request.id, request.source_account, request.destination_account,
|
||||
request.destination_amount);
|
||||
}
|
||||
} else {
|
||||
response = fixtures.path_find.generate.generateIOUPaymentPaths(
|
||||
request.id, request.source_account, request.destination_account,
|
||||
request.destination_amount);
|
||||
assert(false, 'Unrecognized path find request: '
|
||||
+ JSON.stringify(request));
|
||||
}
|
||||
conn.send(response);
|
||||
// delay response to simulate calculation time so we can test queuing
|
||||
setTimeout(() => conn.send(response), 20);
|
||||
});
|
||||
|
||||
mock.on('request_gateway_balances', function(request, conn) {
|
||||
conn.send(createResponse(request, fixtures.gateway_balances));
|
||||
});
|
||||
|
||||
return mock;
|
||||
|
||||
@@ -8,7 +8,7 @@ const Remote = require('ripple-lib').Remote;
|
||||
const Currency = require('ripple-lib').Currency;
|
||||
const addresses = require('./fixtures/addresses');
|
||||
const fixtures = require('./fixtures/orderbook');
|
||||
const IOUValue = require('ripple-lib')._test.IOUValue;
|
||||
const IOUValue = require('ripple-lib-value').IOUValue;
|
||||
|
||||
describe('OrderBook Autobridging', function() {
|
||||
this.timeout(0);
|
||||
@@ -16,6 +16,7 @@ describe('OrderBook Autobridging', function() {
|
||||
function createRemote() {
|
||||
const remote = new Remote();
|
||||
|
||||
remote._ledger_current_index = 32570;
|
||||
remote.isConnected = function() {
|
||||
return true;
|
||||
};
|
||||
@@ -37,7 +38,7 @@ describe('OrderBook Autobridging', function() {
|
||||
assert.deepEqual(book._legTwoBook._currencyPays.to_hex(), Currency.from_json('XRP').to_hex());
|
||||
});
|
||||
|
||||
it('Compute autobridged offers', function() {
|
||||
it('Compute autobridged offers', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -56,20 +57,23 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
assert.strictEqual(book._offersAutobridged[0].taker_gets_funded, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].taker_pays_funded, '58.61727326122974');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].taker_gets_funded, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].taker_pays_funded, '58.61727326122974');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one partially funded', function() {
|
||||
it('Compute autobridged offers - leg one partially funded', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -90,17 +94,20 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '7.273651248813431');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '24.96789265329184');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '7.273651248813431');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '24.96789265329184');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg two partially funded', function() {
|
||||
it('Compute autobridged offers - leg two partially funded', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -121,17 +128,20 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '10');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.32649132449533');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '10');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.32649132449533');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg two transfer rate', function() {
|
||||
it('Compute autobridged offers - leg two transfer rate', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -152,15 +162,18 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '9.980039920159681');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.25797537722665');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '9.980039920159681');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '34.25797537722665');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - taker funds < leg two in', function() {
|
||||
it('Compute autobridged offers - taker funds < leg two in', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -185,17 +198,20 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one partially funded - owners equal', function() {
|
||||
it('Compute autobridged offers - leg one partially funded - owners equal', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -218,17 +234,20 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one partially funded - owners equal - leg two in > leg one out', function() {
|
||||
it('Compute autobridged offers - leg one partially funded - owners equal - leg two in > leg one out', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -254,17 +273,20 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.0199210049999');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.0199210049999');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg one consumes leg two fully', function() {
|
||||
it('Compute autobridged offers - leg one consumes leg two fully', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -283,22 +305,25 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - leg two consumes first leg one offer fully', function() {
|
||||
it('Compute autobridged offers - leg two consumes first leg one offer fully', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -321,22 +346,25 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '108.6682345172846');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '373.019921005');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '62.0957179050155');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '213.1791399943838');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '62.0957179050155');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '213.1791399943838');
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - owners equal', function() {
|
||||
it('Compute autobridged offers - owners equal', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -360,22 +388,25 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '0.4001139945128008');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '24.96789265329184');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '0.4001139945128008');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '24.96789265329184');
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - owners equal - leg one overfunded', function() {
|
||||
it('Compute autobridged offers - owners equal - leg one overfunded', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -400,22 +431,25 @@ describe('OrderBook Autobridging', function() {
|
||||
book._legTwoBook.setOffers(legTwoOffers);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 2);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '17.07639524223001');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '58.61727326122974');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5.038346688725268');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '314.4026477437702');
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - TakerPays < Quality * TakerGets', function() {
|
||||
it('Compute autobridged offers - TakerPays < Quality * TakerGets', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -458,17 +492,20 @@ describe('OrderBook Autobridging', function() {
|
||||
]);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - update funded amount', function() {
|
||||
it('Compute autobridged offers - update funded amount', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -534,27 +571,30 @@ describe('OrderBook Autobridging', function() {
|
||||
]);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 3);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 3);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '20');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '80');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '20');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '80');
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - update funded amount - owners equal', function() {
|
||||
it('Compute autobridged offers - update funded amount - owners equal', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -620,27 +660,30 @@ describe('OrderBook Autobridging', function() {
|
||||
]);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 3);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 3);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '10');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '10');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - update funded amount - first two owners equal', function() {
|
||||
it('Compute autobridged offers - update funded amount - first two owners equal', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -717,32 +760,35 @@ describe('OrderBook Autobridging', function() {
|
||||
]);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 4);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 4);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '90');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '90');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerGets.value, '5');
|
||||
assert.strictEqual(book._offersAutobridged[1].TakerPays.value, '10');
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[1].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '25');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerGets.value, '25');
|
||||
assert.strictEqual(book._offersAutobridged[2].TakerPays.value, '100');
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
|
||||
assert(book._offersAutobridged[2].autobridged);
|
||||
assert.strictEqual(book._offersAutobridged[3].TakerGets.value, '20');
|
||||
assert.strictEqual(book._offersAutobridged[3].TakerPays.value, '80');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[3].TakerGets.value, '20');
|
||||
assert.strictEqual(book._offersAutobridged[3].TakerPays.value, '80');
|
||||
assert(book._offersAutobridged[3].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[3].autobridged);
|
||||
});
|
||||
|
||||
it('Compute autobridged offers - unfunded offer - owners equal', function() {
|
||||
it('Compute autobridged offers - unfunded offer - owners equal', function(done) {
|
||||
const book = createRemote().createOrderBook({
|
||||
currency_gets: 'EUR',
|
||||
issuer_gets: addresses.ISSUER,
|
||||
@@ -785,13 +831,16 @@ describe('OrderBook Autobridging', function() {
|
||||
]);
|
||||
|
||||
book._gotOffersFromLegOne = book._gotOffersFromLegTwo = true;
|
||||
book.computeAutobridgedOffers();
|
||||
book.computeAutobridgedOffers(() => {
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
|
||||
assert.strictEqual(book._offersAutobridged.length, 1);
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
|
||||
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerGets.value, '75');
|
||||
assert.strictEqual(book._offersAutobridged[0].TakerPays.value, '75');
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
assert(book._offersAutobridged[0].autobridged);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,14 +9,14 @@ const Amount = require('ripple-lib').Amount;
|
||||
const Meta = require('ripple-lib').Meta;
|
||||
const addresses = require('./fixtures/addresses');
|
||||
const fixtures = require('./fixtures/orderbook');
|
||||
const IOUValue = require('ripple-lib')._test.IOUValue;
|
||||
const IOUValue = require('ripple-lib-value').IOUValue;
|
||||
|
||||
describe('OrderBook', function() {
|
||||
this.timeout(0);
|
||||
|
||||
function createRemote() {
|
||||
const remote = new Remote();
|
||||
|
||||
remote._ledger_current_index = 32570;
|
||||
remote.isConnected = function() {
|
||||
return true;
|
||||
};
|
||||
@@ -1605,9 +1605,15 @@ describe('OrderBook', function() {
|
||||
const offer2 = fixtures.transactionWithCreatedOffer();
|
||||
const offer3 = fixtures.transactionWithCreatedOffer();
|
||||
|
||||
remote.emit('ledger_closed', {txn_count: 3});
|
||||
|
||||
book.notify(offer);
|
||||
remote.emit('transaction', offer);
|
||||
book.notify(offer2);
|
||||
remote.emit('transaction', offer2);
|
||||
book.notify(offer3);
|
||||
remote.emit('transaction', offer3);
|
||||
|
||||
|
||||
assert.strictEqual(numTransactionEvents, 3);
|
||||
assert.strictEqual(numOfferAddedEvents, 3);
|
||||
@@ -1698,7 +1704,10 @@ describe('OrderBook', function() {
|
||||
|
||||
const message = fixtures.transactionWithDeletedOffer();
|
||||
|
||||
remote.emit('ledger_closed', {txn_count: 1});
|
||||
|
||||
book.notify(message);
|
||||
remote.emit('transaction', message);
|
||||
|
||||
assert.strictEqual(numTransactionEvents, 1);
|
||||
assert.strictEqual(numTradeEvents, 1);
|
||||
@@ -1857,7 +1866,10 @@ describe('OrderBook', function() {
|
||||
|
||||
const message = fixtures.transactionWithModifiedOffer();
|
||||
|
||||
remote.emit('ledger_closed', {txn_count: 1});
|
||||
|
||||
book.notify(message);
|
||||
remote.emit('transaction', message);
|
||||
|
||||
assert.strictEqual(numTransactionEvents, 1);
|
||||
assert.strictEqual(numTradeEvents, 1);
|
||||
|
||||
@@ -716,6 +716,7 @@ describe('Remote', function() {
|
||||
function() {
|
||||
const message = require('./fixtures/transaction-offercreate');
|
||||
let i = 0;
|
||||
remote._ledger_current_index = 32570;
|
||||
const orderbook = remote.createOrderBook({
|
||||
currency_gets: 'USD',
|
||||
issuer_gets: 'rJy64aCJLP3vf8o3WPKn4iQKtfpjh6voAR',
|
||||
@@ -1965,6 +1966,22 @@ describe('Remote', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('Construct gateway_balances request', function() {
|
||||
const request = remote.requestGatewayBalances({
|
||||
account: 'rGr9PjmVe7MqEXTSbd3njhgJc2s5vpHV54',
|
||||
hotwallet: 'rwxBjBC9fPzyQ9GgPZw6YYLNeRTSx5',
|
||||
strict: true
|
||||
});
|
||||
|
||||
assert.deepEqual(request.message, {
|
||||
command: 'gateway_balances',
|
||||
id: undefined,
|
||||
account: 'rGr9PjmVe7MqEXTSbd3njhgJc2s5vpHV54',
|
||||
hotwallet: 'rwxBjBC9fPzyQ9GgPZw6YYLNeRTSx5',
|
||||
strict: true
|
||||
});
|
||||
});
|
||||
|
||||
it('Construct Payment transaction', function() {
|
||||
const tx = remote.createTransaction('Payment', {
|
||||
account: TX_JSON.Account,
|
||||
|
||||
@@ -40,13 +40,12 @@ const SERVER_INFO = {
|
||||
};
|
||||
|
||||
describe('Request', function() {
|
||||
it('Send request', function(done) {
|
||||
it('Send request', function() {
|
||||
const remote = {
|
||||
request: function(req) {
|
||||
assert(req instanceof Request);
|
||||
assert.strictEqual(typeof req.message, 'object');
|
||||
assert.strictEqual(req.message.command, 'server_info');
|
||||
done();
|
||||
},
|
||||
on: function() {
|
||||
},
|
||||
@@ -60,7 +59,42 @@ describe('Request', function() {
|
||||
request.request();
|
||||
|
||||
// Should only request once
|
||||
request.request();
|
||||
assert.throws(function() {
|
||||
request.request();
|
||||
}, Error);
|
||||
|
||||
});
|
||||
|
||||
it('Send request - reconnect', function(done) {
|
||||
const server = makeServer('wss://localhost:5006');
|
||||
let emitted = 0;
|
||||
|
||||
const remote = new Remote();
|
||||
remote._connected = true;
|
||||
remote._servers = [server];
|
||||
|
||||
server._request = function(req) {
|
||||
assert(req instanceof Request);
|
||||
assert.strictEqual(typeof req.message, 'object');
|
||||
assert.strictEqual(req.message.command, 'server_info');
|
||||
if (++emitted === 1) {
|
||||
setTimeout(function() {
|
||||
remote.emit('connected');
|
||||
}, 2);
|
||||
} if (emitted === 2) {
|
||||
setTimeout(function() {
|
||||
req.emit('success', SERVER_INFO);
|
||||
req.emit('response', SERVER_INFO);
|
||||
}, 2);
|
||||
}
|
||||
};
|
||||
|
||||
const request = new Request(remote, 'server_info');
|
||||
|
||||
request.callback(function() {
|
||||
assert.strictEqual(emitted, 2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Send request -- filterRequest', function(done) {
|
||||
@@ -536,6 +570,7 @@ describe('Request', function() {
|
||||
setTimeout(function() {
|
||||
successEmitted = true;
|
||||
req.emit('success', SERVER_INFO);
|
||||
req.emit('response', SERVER_INFO);
|
||||
}, 200);
|
||||
};
|
||||
|
||||
@@ -544,8 +579,9 @@ describe('Request', function() {
|
||||
remote._servers = [server];
|
||||
|
||||
const request = new Request(remote, 'server_info');
|
||||
request.setTimeout(10);
|
||||
|
||||
request.timeout(10, function() {
|
||||
request.on('timeout', function() {
|
||||
setTimeout(function() {
|
||||
assert(successEmitted);
|
||||
done();
|
||||
@@ -566,7 +602,8 @@ describe('Request', function() {
|
||||
assert.strictEqual(req.message.command, 'server_info');
|
||||
setTimeout(function() {
|
||||
req.emit('success', SERVER_INFO);
|
||||
}, 200);
|
||||
req.emit('response', SERVER_INFO);
|
||||
}, 20);
|
||||
};
|
||||
|
||||
const remote = new Remote();
|
||||
@@ -581,13 +618,15 @@ describe('Request', function() {
|
||||
timedOut = true;
|
||||
});
|
||||
|
||||
request.timeout(1000);
|
||||
request.setTimeout(100);
|
||||
|
||||
request.callback(function(err, res) {
|
||||
assert(!timedOut);
|
||||
assert.ifError(err);
|
||||
assert.deepEqual(res, SERVER_INFO);
|
||||
done();
|
||||
setTimeout(function() {
|
||||
assert(!timedOut, 'must not timeout');
|
||||
assert.ifError(err);
|
||||
assert.deepEqual(res, SERVER_INFO);
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1207,4 +1246,22 @@ describe('Request', function() {
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('Emit "before" only once', function(done) {
|
||||
const remote = new Remote();
|
||||
remote._connected = true;
|
||||
|
||||
const request = new Request(remote, 'server_info');
|
||||
|
||||
let beforeCalled = 0;
|
||||
|
||||
request.on('before', () => {
|
||||
beforeCalled++;
|
||||
});
|
||||
|
||||
request.request(function() {});
|
||||
assert.strictEqual(beforeCalled, 1);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/* eslint max-len: 0 */
|
||||
/* eslint-disable max-nested-callbacks */
|
||||
'use strict';
|
||||
const assert = require('assert');
|
||||
const Seed = require('ripple-lib').Seed;
|
||||
|
||||
describe('Seed', function() {
|
||||
it('saESc82Vun7Ta5EJRzGJbrXb5HNYk', function() {
|
||||
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
assert.strictEqual(seed.to_hex(), 'FF1CF838D02B2CF7B45BAC27F5F24F4F');
|
||||
});
|
||||
it('can create ed25519 seeds from a phrase', function() {
|
||||
const seed = Seed.from_json('phrase').set_ed25519().to_json();
|
||||
assert.strictEqual(seed, 'sEdT7U4WpkoiH6wBoNeLzDi1eu9N64Y');
|
||||
});
|
||||
it('sp6iDHnmiPN7tQFHm5sCW59ax3hfE', function() {
|
||||
const seed = Seed.from_json('sp6iDHnmiPN7tQFHm5sCW59ax3hfE');
|
||||
assert.strictEqual(seed.to_hex(), '00AD8DA764C3C8AF5F9B8D51C94B9E49');
|
||||
});
|
||||
it('sp6iDHnmiPN7tQFHm5sCW59ax3hfE using parse_base58', function() {
|
||||
const seed = new Seed().parse_base58('sp6iDHnmiPN7tQFHm5sCW59ax3hfE');
|
||||
assert.strictEqual(seed.to_hex(), '00AD8DA764C3C8AF5F9B8D51C94B9E49');
|
||||
});
|
||||
it('parse_base58 should throw on non-string input', function() {
|
||||
assert.throws(function() {
|
||||
new Seed().parse_base58(1);
|
||||
});
|
||||
});
|
||||
it('parse_base58 should make invalid seed from empty string', function() {
|
||||
const seed = new Seed().parse_base58('');
|
||||
assert(!seed.is_valid());
|
||||
});
|
||||
it('parse_base58 should make invalid seed from invalid input', function() {
|
||||
const seed = new Seed().parse_base58('Xs');
|
||||
assert(!seed.is_valid());
|
||||
});
|
||||
it('should return the key_pair for a valid account and secret pair', function() {
|
||||
const address = 'r3GgMwvgvP8h4yVWvjH1dPZNvC37TjzBBE';
|
||||
const seed = Seed.from_json('shsWGZcmZz6YsWWmcnpfr6fLTdtFV');
|
||||
const keyPair = seed.get_key();
|
||||
assert.strictEqual(keyPair.accountID(), address);
|
||||
assert.strictEqual(keyPair.pubKeyHex(), '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8');
|
||||
});
|
||||
});
|
||||
|
||||
// vim:sw=2:sts=2:ts=8:et
|
||||
@@ -1249,4 +1249,71 @@ describe('Server', function() {
|
||||
|
||||
server.connect();
|
||||
});
|
||||
|
||||
it('Automatic reconnect', function(done) {
|
||||
const port = 5748;
|
||||
let connections = 0;
|
||||
|
||||
function handleWsConnection(_ws) {
|
||||
++connections;
|
||||
|
||||
_ws.on('message', (message) => {
|
||||
const parsed = JSON.parse(message);
|
||||
assert.strictEqual(parsed.command, 'subscribe');
|
||||
|
||||
_ws.send(JSON.stringify({
|
||||
id: parsed.id,
|
||||
status: 'success',
|
||||
type: 'response',
|
||||
result: {
|
||||
fee_base: 10,
|
||||
fee_ref: 10,
|
||||
ledger_hash:
|
||||
'1838539EE12463C36F2C53B079D807C697E3D93A1936B717E565A4A912E11776',
|
||||
ledger_index: 7053695,
|
||||
ledger_time: 455414390,
|
||||
load_base: 256,
|
||||
load_factor: 256,
|
||||
random:
|
||||
'E56C9154D9BE94D49C581179356C2E084E16D18D74E8B09093F2D61207625E6A',
|
||||
reserve_base: 20000000,
|
||||
reserve_inc: 5000000,
|
||||
server_status: 'full',
|
||||
validated_ledgers: '32570-7053695',
|
||||
pubkey_node: 'n94pSqypSfddzAVj9qoezHyUoetsrMnwgNuBqRJ3WHvM8aMMf7rW'
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
let wss = new ws.Server({port});
|
||||
|
||||
wss.once('connection', handleWsConnection);
|
||||
|
||||
const remote = new Remote();
|
||||
const server = remote.addServer(`ws://localhost:${port}`);
|
||||
|
||||
let receivedReconnectAttempt = false;
|
||||
server.once('disconnect', () => {
|
||||
assert.equal(remote.getServer(), null);
|
||||
server.once('connect', () => {
|
||||
assert(receivedReconnectAttempt);
|
||||
assert.equal(connections, 2);
|
||||
assert(remote.getServer() instanceof Server);
|
||||
done();
|
||||
});
|
||||
server.once('connecting', () => {
|
||||
receivedReconnectAttempt = true;
|
||||
wss = new ws.Server({port});
|
||||
wss.once('connection', handleWsConnection);
|
||||
});
|
||||
});
|
||||
server.once('connect', () => {
|
||||
assert(remote.getServer() instanceof Server);
|
||||
// Force server disconnect, ripple-lib should attempt
|
||||
// to reconnect automatically
|
||||
wss.close();
|
||||
});
|
||||
server.connect();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const Seed = require('ripple-lib').Seed;
|
||||
|
||||
function _isNaN(n) {
|
||||
return typeof n === 'number' && isNaN(n);
|
||||
}
|
||||
|
||||
describe('Signing', function() {
|
||||
describe('Keys', function() {
|
||||
it('SigningPubKey 1 (ripple-client issue #245)', function() {
|
||||
const seed = Seed.from_json('saESc82Vun7Ta5EJRzGJbrXb5HNYk');
|
||||
const key = seed.get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
|
||||
const pub = key.pubKeyHex();
|
||||
assert.strictEqual(
|
||||
pub,
|
||||
'0396941B22791A448E5877A44CE98434DB217D6FB97D63F0DAD23BE49ED45173C9');
|
||||
});
|
||||
it('SigningPubKey 2 (master seed)', function() {
|
||||
const seed = Seed.from_json('snoPBrXtMeMyMHUVTgbuqAfg1SUTb');
|
||||
const key = seed.get_key('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh');
|
||||
const pub = key.pubKeyHex();
|
||||
assert.strictEqual(
|
||||
pub,
|
||||
'0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020');
|
||||
});
|
||||
});
|
||||
describe('parse_json', function() {
|
||||
it('empty string', function() {
|
||||
assert(_isNaN(new Seed().parse_json('').to_json()));
|
||||
});
|
||||
it('hex string', function() {
|
||||
// 32 0s is a valid hex repr of seed bytes
|
||||
const str = new Array(33).join('0');
|
||||
assert.strictEqual((new Seed().parse_json(str).to_json()),
|
||||
'sp6JS7f14BuwFY8Mw6bTtLKWauoUs');
|
||||
});
|
||||
it('passphrase', function() {
|
||||
const str = new Array(60).join('0');
|
||||
assert.strictEqual('snFRPnVL3secohdpwSie8ANXdFQvG',
|
||||
new Seed().parse_json(str).to_json());
|
||||
});
|
||||
it('null', function() {
|
||||
assert(_isNaN(new Seed().parse_json(null).to_json()));
|
||||
});
|
||||
});
|
||||
describe('parse_passphrase', function() {
|
||||
it('invalid passphrase', function() {
|
||||
assert.throws(function() {
|
||||
new Seed().parse_passphrase(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('get_key', function() {
|
||||
it('get key from invalid seed', function() {
|
||||
assert.throws(function() {
|
||||
new Seed().get_key('rBZ4j6MsoctipM6GEyHSjQKzXG3yambDnZ');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -518,7 +518,9 @@ describe('TransactionManager', function() {
|
||||
break;
|
||||
}
|
||||
});
|
||||
/* eslint-disable no-unused-vars */
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
/* eslint-enable no-unused-vars */
|
||||
req.sendJSON(lodash.extend({}, LEDGER, {
|
||||
ledger_index: transaction.tx_json.LastLedgerSequence + 1
|
||||
}));
|
||||
@@ -574,7 +576,9 @@ describe('TransactionManager', function() {
|
||||
req.sendResponse(SUBMIT_TEF_RESPONSE, {id: m.id});
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
/* eslint-enable no-unused-vars */
|
||||
transaction.once('resubmitted', function() {
|
||||
receivedResubmitted = true;
|
||||
req.sendJSON(lodash.extend({}, LEDGER, {
|
||||
@@ -634,7 +638,9 @@ describe('TransactionManager', function() {
|
||||
req.sendResponse(SUBMIT_TEL_RESPONSE, {id: m.id});
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
/* eslint-enable no-unused-vars */
|
||||
transaction.once('resubmitted', function() {
|
||||
receivedResubmitted = true;
|
||||
req.sendJSON(lodash.extend({}, LEDGER, {
|
||||
@@ -690,7 +696,7 @@ describe('TransactionManager', function() {
|
||||
assert.strictEqual(summary.submissionAttempts, 0);
|
||||
assert.strictEqual(summary.submitIndex, undefined);
|
||||
assert.strictEqual(summary.initialSubmitIndex, undefined);
|
||||
assert.strictEqual(summary.lastLedgerSequence, undefined);
|
||||
assert.strictEqual(summary.lastLedgerSequence, remote.getLedgerSequenceSync() + 1 + Remote.DEFAULTS.last_ledger_offset);
|
||||
assert.strictEqual(summary.state, 'failed');
|
||||
assert.strictEqual(summary.finalized, true);
|
||||
assert.deepEqual(summary.result, {
|
||||
@@ -799,7 +805,9 @@ describe('TransactionManager', function() {
|
||||
req.sendResponse(SUBMIT_TEL_RESPONSE, {id: m.id});
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
/* eslint-enable no-unused-vars */
|
||||
transaction.once('resubmitted', function() {
|
||||
receivedResubmitted = true;
|
||||
});
|
||||
@@ -857,6 +865,7 @@ describe('TransactionManager', function() {
|
||||
receivedSubmitted = true;
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
rippled.on('request_submit', function(m, req) {
|
||||
assert.strictEqual(m.tx_blob, SerializedObject.from_json(
|
||||
transaction.tx_json).to_hex());
|
||||
@@ -867,7 +876,9 @@ describe('TransactionManager', function() {
|
||||
req.sendResponse(SUBMIT_TOO_BUSY_ERROR, {id: m.id});
|
||||
});
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
rippled.once('request_submit', function(m, req) {
|
||||
/* eslint-enable no-unused-vars */
|
||||
transaction.once('resubmitted', function() {
|
||||
receivedResubmitted = true;
|
||||
req.sendJSON(lodash.extend({}, LEDGER, {
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const assert = require('assert-diff');
|
||||
const lodash = require('lodash');
|
||||
const addresses = require('./fixtures/addresses');
|
||||
const ripple = require('ripple-lib');
|
||||
const Transaction = require('ripple-lib').Transaction;
|
||||
const TransactionQueue = require('ripple-lib').TransactionQueue;
|
||||
@@ -340,7 +341,7 @@ describe('Transaction', function() {
|
||||
const dst = 'rGihwhaqU8g7ahwAvTq6iX5rvsfcbgZw6v';
|
||||
|
||||
transaction.payment(src, dst, '100');
|
||||
remote.setSecret(src, 'masterpassphrase');
|
||||
remote.setSecret(src, addresses.SECRET);
|
||||
|
||||
assert(transaction.complete());
|
||||
const json = transaction.serialize().to_json();
|
||||
@@ -983,9 +984,6 @@ describe('Transaction', function() {
|
||||
it('Set LastLedgerSequence', function() {
|
||||
const transaction = new Transaction();
|
||||
|
||||
assert.throws(function() {
|
||||
transaction.lastLedger('a');
|
||||
}, /Error: LastLedgerSequence must be a valid UInt32/);
|
||||
assert.throws(function() {
|
||||
transaction.setLastLedgerSequence('a');
|
||||
}, /Error: LastLedgerSequence must be a valid UInt32/);
|
||||
@@ -1945,6 +1943,8 @@ describe('Transaction', function() {
|
||||
|
||||
it('Submit transaction', function(done) {
|
||||
const remote = new Remote();
|
||||
remote._ledger_current_index = 1;
|
||||
|
||||
const transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
|
||||
|
||||
assert.strictEqual(transaction.callback, undefined);
|
||||
@@ -1989,6 +1989,8 @@ describe('Transaction', function() {
|
||||
it('Submit transaction - submission error', function(done) {
|
||||
const remote = new Remote();
|
||||
|
||||
remote._ledger_current_index = 1;
|
||||
|
||||
const transaction = new Transaction(remote).accountSet('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
|
||||
|
||||
const account = remote.addAccount('r36xtKNKR43SeXnGn7kN4r4JdQzcrkqpWe');
|
||||
@@ -2107,82 +2109,6 @@ describe('Transaction', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('Add multisigner', function() {
|
||||
const transaction = new Transaction();
|
||||
const s1 = {
|
||||
Account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
|
||||
TxnSignature: '304402203020865BDC995431325C371E6A3CE89BFC40597D9CFAF77DBB16E9D159824EA402203645A6462A6DCEC7B5D0811882DC54CEA66258A227A2762BE6EFCD9EB62C27BF',
|
||||
SigningPubKey: '02691AC5AE1C4C333AE5DF8A93BDC495F0EEBFC6DB0DA7EB6EF808F3AFC006E3FE'
|
||||
};
|
||||
const s2 = {
|
||||
Account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
|
||||
TxnSignature: '30450221009C84E455DC199A7DB4B800D68C92269D60972E8850AFC0D50B1AE6B08BBB02EA02206FA93A560BE96844DF7D96D07F6400EF9534A32FBA352DD10E855DA8923A3AF8',
|
||||
SigningPubKey: '028949021029D5CC87E78BCF053AFEC0CAFD15108EC119EAAFEC466F5C095407BF'
|
||||
};
|
||||
|
||||
transaction.addMultiSigner(s1);
|
||||
transaction.addMultiSigner(s2);
|
||||
|
||||
assert.deepEqual(transaction.getMultiSigners(), [
|
||||
{Signer: s2}, {Signer: s1}]);
|
||||
});
|
||||
|
||||
it('Get multisign data', function() {
|
||||
const transaction = Transaction.from_json({
|
||||
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
|
||||
Sequence: 1,
|
||||
Fee: '100',
|
||||
TransactionType: 'AccountSet',
|
||||
Flags: 0
|
||||
});
|
||||
|
||||
transaction.setSigningPubKey('');
|
||||
|
||||
const a1 = 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK';
|
||||
const d1 = transaction.multiSigningData(a1);
|
||||
|
||||
const tbytes = ripple.SerializedObject.from_json(
|
||||
lodash.merge(transaction.tx_json, {SigningPubKey: ''})).buffer;
|
||||
const abytes = ripple.UInt160.from_json(a1).to_bytes();
|
||||
const prefix = require('ripple-lib')._test.HashPrefixes.HASH_TX_MULTISIGN_BYTES;
|
||||
|
||||
assert.deepEqual(d1.buffer, prefix.concat(tbytes, abytes));
|
||||
});
|
||||
|
||||
it('Multisign', function() {
|
||||
const transaction = Transaction.from_json({
|
||||
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
|
||||
Sequence: 1,
|
||||
Fee: '100',
|
||||
TransactionType: 'AccountSet',
|
||||
Flags: 0
|
||||
});
|
||||
|
||||
const multiSigningJson = transaction.getMultiSigningJson();
|
||||
const t1 = Transaction.from_json(multiSigningJson);
|
||||
|
||||
const a1 = 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK';
|
||||
const a2 = 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW';
|
||||
|
||||
const s1 = t1.multiSign(a1, 'alice');
|
||||
assert.strictEqual(s1.Account, a1);
|
||||
assert.strictEqual(s1.SigningPubKey, '0388935426E0D08083314842EDFBB2D517BD47699F9A4527318A8E10468C97C052');
|
||||
assert.strictEqual(s1.TxnSignature, '30440220611256E46B2946152695FFEF34D5C71BB3AE569C3D919A270BFBCA9ADF260D9202202FAE24FC8A575FE3265A6D7CFA596094A7950E0011706431A11C2A9ABEF60B3B');
|
||||
|
||||
const s2 = t1.multiSign(a2, 'bob');
|
||||
assert.strictEqual(s2.Account, a2);
|
||||
assert.strictEqual(s2.SigningPubKey, '02691AC5AE1C4C333AE5DF8A93BDC495F0EEBFC6DB0DA7EB6EF808F3AFC006E3FE');
|
||||
assert.strictEqual(s2.TxnSignature, '3044022067F769BE0A4CC2B4F26E7B52B366F861FED02DA0F564F98B44009C8181A9655702206D882919139DF8E9D7F2FC1DD54D8B4FEAC40203349AE21519FD388925A4DE83');
|
||||
|
||||
transaction.addMultiSigner(s1);
|
||||
transaction.addMultiSigner(s2);
|
||||
|
||||
assert.deepEqual(transaction.getMultiSigners(), [
|
||||
{Signer: s2},
|
||||
{Signer: s1}
|
||||
]);
|
||||
});
|
||||
|
||||
it('Construct SuspendedPaymentCreate transaction', function() {
|
||||
const transaction = new Transaction().suspendedPaymentCreate({
|
||||
account: 'rsLEU1TPdCJPPysqhWYw9jD97xtG5WqSJm',
|
||||
@@ -2294,4 +2220,100 @@ describe('Transaction', function() {
|
||||
OfferSequence: 1234
|
||||
});
|
||||
});
|
||||
|
||||
it('Add multisigner', function() {
|
||||
const transaction = new Transaction();
|
||||
const s1 = {
|
||||
Account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
|
||||
TxnSignature: '304402203020865BDC995431325C371E6A3CE89BFC40597D9CFAF77DBB16E9D159824EA402203645A6462A6DCEC7B5D0811882DC54CEA66258A227A2762BE6EFCD9EB62C27BF',
|
||||
SigningPubKey: '02691AC5AE1C4C333AE5DF8A93BDC495F0EEBFC6DB0DA7EB6EF808F3AFC006E3FE'
|
||||
};
|
||||
const s2 = {
|
||||
Account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
|
||||
TxnSignature: '30450221009C84E455DC199A7DB4B800D68C92269D60972E8850AFC0D50B1AE6B08BBB02EA02206FA93A560BE96844DF7D96D07F6400EF9534A32FBA352DD10E855DA8923A3AF8',
|
||||
SigningPubKey: '028949021029D5CC87E78BCF053AFEC0CAFD15108EC119EAAFEC466F5C095407BF'
|
||||
};
|
||||
|
||||
transaction.addMultiSigner(s1);
|
||||
transaction.addMultiSigner(s2);
|
||||
|
||||
assert.deepEqual(transaction.getMultiSigners(), [
|
||||
{Signer: s2}, {Signer: s1}]);
|
||||
});
|
||||
|
||||
it('Get multisign data', function() {
|
||||
const transaction = Transaction.from_json({
|
||||
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
|
||||
Sequence: 1,
|
||||
Fee: '100',
|
||||
TransactionType: 'AccountSet',
|
||||
Flags: 0
|
||||
});
|
||||
|
||||
transaction.setSigningPubKey('');
|
||||
|
||||
const a1 = 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK';
|
||||
const d1 = transaction.multiSigningData(a1);
|
||||
|
||||
const tbytes = ripple.SerializedObject.from_json(
|
||||
lodash.merge(transaction.tx_json, {SigningPubKey: ''})).buffer;
|
||||
const abytes = ripple.UInt160.from_json(a1).to_bytes();
|
||||
const prefix = require('ripple-lib')._test.HashPrefixes.HASH_TX_MULTISIGN_BYTES;
|
||||
|
||||
assert.deepEqual(d1.buffer, prefix.concat(tbytes, abytes));
|
||||
});
|
||||
|
||||
it('Multisign', function() {
|
||||
const transaction = Transaction.from_json({
|
||||
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
|
||||
Sequence: 1,
|
||||
Fee: '100',
|
||||
TransactionType: 'AccountSet',
|
||||
Flags: 0,
|
||||
LastLedgerSequence: 1
|
||||
});
|
||||
|
||||
const multiSigningJson = transaction.getMultiSigningJson();
|
||||
const t1 = Transaction.from_json(multiSigningJson);
|
||||
|
||||
const a1 = 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK';
|
||||
const a2 = 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW';
|
||||
|
||||
const s1 = t1.multiSign(a1, addresses.SECRET);
|
||||
assert.deepEqual(s1, {
|
||||
Account: 'rPMh7Pi9ct699iZUTWaytJUoHcJ7cgyziK',
|
||||
TxnSignature: '30440220613DF9410B4844C7FAB637FD707F5185A2107DD10D0C2F59155844CD1910AB99022004A2AE607C15DD0959FDB3AAEE6A0337AA5337515230CE6EC11E32B74EEE896E',
|
||||
SigningPubKey: '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
|
||||
|
||||
});
|
||||
|
||||
const s2 = t1.multiSign(a2, addresses.SECRET);
|
||||
assert.deepEqual(s2, {
|
||||
Account: 'rH4KEcG9dEwGwpn6AyoWK9cZPLL4RLSmWW',
|
||||
TxnSignature: '3044022011762BC175E166EF540ABB162F0E8B48250E7C95DE5E8464E3F648EAF9A94A50022022439146DC3C6BB943C719F89696E7EBED14888A653F4618F62F8DA5CE202A45',
|
||||
SigningPubKey: '02F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D8'
|
||||
});
|
||||
|
||||
transaction.addMultiSigner(s1);
|
||||
transaction.addMultiSigner(s2);
|
||||
|
||||
assert.deepEqual(transaction.getMultiSigners(), [
|
||||
{Signer: s2},
|
||||
{Signer: s1}
|
||||
]);
|
||||
});
|
||||
|
||||
it('Multisign -- missing LastLedgerSequence', function() {
|
||||
const transaction = Transaction.from_json({
|
||||
Account: 'rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn',
|
||||
Sequence: 1,
|
||||
Fee: '100',
|
||||
TransactionType: 'AccountSet',
|
||||
Flags: 0
|
||||
});
|
||||
|
||||
assert.throws(function() {
|
||||
transaction.getMultiSigningJson();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user