Compare commits

...

279 Commits

Author SHA1 Message Date
Geert Weening
37198bde66 Bump version to 0.13.0-rc4 2015-08-07 15:31:56 -07:00
Geert Weening
281c056f6c Update release notes 2015-08-07 15:31:24 -07:00
Geert Weening
49a513cd07 Merge branch 'release' into develop 2015-08-07 14:47:26 -07:00
Alan Cohen
7a95aabbf4 Merge pull request #491 from lumberj/empty-paths
Don't set empty paths
2015-08-07 10:35:41 -07:00
Alan Cohen
83874ec096 Don't set empty paths
"The Paths field must not be an empty array, nor an array whose members are all
empty arrays."
https://ripple.com/build/transactions/#paths

Fix test file lint errors
2015-08-07 10:26:45 -07:00
Chris Clark
9270d0a33d Merge pull request #490 from clark800/test-dist
Clean up package.json
2015-08-06 15:30:09 -07:00
Chris Clark
30295efdb4 Clean up package.json 2015-08-06 15:09:49 -07:00
Chris Clark
f1342c1456 Merge pull request #489 from clark800/test-compiled
Test compiled code in dist/npm on CI server
2015-08-06 15:02:56 -07:00
Chris Clark
194b73c293 Test compiled code in dist/npm on CI server 2015-08-06 14:59:48 -07:00
Chris Clark
89e5f79bbb Merge pull request #487 from clark800/orderbook-changes
Switch to direction/quantity/totalPrice for orderbookChanges
2015-08-06 14:53:18 -07:00
Chris Clark
82d7ce7ac2 Switch to direction/quantity/totalPrice for orderbookChanges 2015-08-06 12:27:03 -07:00
Geert Weening
0cc4c704f8 Merge pull request #482 from clark800/deprecate
Deprecate core
2015-08-06 11:35:12 -07:00
Chris Clark
dde762a1d6 Merge pull request #483 from clark800/fix-prepare-trustline
Fix prepareTrustline with no quality setting
2015-08-06 10:40:34 -07:00
Chris Clark
1c86e246c7 Merge pull request #484 from darkdarkdragon/develop-schema-fix
small adjustment.json schema fix
2015-08-05 16:31:27 -07:00
Ivan Tivonenko
2d173c8e69 small adjustment.json schema fix 2015-08-06 02:25:59 +03:00
Chris Clark
600fd34d30 Fix prepareTrustline with no quality setting 2015-08-05 15:42:48 -07:00
Geert Weening
4cb9cf801c Bump version to 0.13.0-rc3 2015-08-04 17:20:25 -07:00
Geert Weening
50fb8789b4 Update release notes 2015-08-04 17:06:57 -07:00
Chris Clark
fb8dc44ec1 Deprecate core 2015-08-04 16:57:39 -07:00
Geert Weening
0781caa8bc Merge pull request #481 from clark800/pathfind-fix
Add test case for createPathFind and convert snake case function calls to camel case
2015-08-04 16:57:18 -07:00
Chris Clark
2cdb23f0dd Fix lint errors in pathfind.js 2015-08-04 15:51:52 -07:00
Chris Clark
8e536c00b9 Add test case for createPathFind and convert snake case function calls to camel case 2015-08-04 15:51:18 -07:00
Geert Weening
8ff154cc2d Merge pull request #480 from clark800/promise-docs
Update RippleAPI documentation for promises
2015-08-04 15:29:08 -07:00
Chris Clark
daaae6e01e Update RippleAPI documentation for promises 2015-08-04 14:53:46 -07:00
Chris Clark
a64a4e697a Merge pull request #478 from clark800/promises
Convert API to promises
2015-08-04 11:32:52 -07:00
wltsmrz
3f51d8cc12 Merge pull request #479 from wltsmrz/fix-validated-ledgers-single-ledger
Fix RangeSet for validated_ledger as single ledger
2015-08-05 00:48:48 +07:00
wltsmrz
9f9e76f8b9 Fix RangeSet for validated_ledger as single ledger 2015-08-04 10:45:29 -07:00
sublimator
5b51db158d Merge pull request #476 from clark800/badge
Replace Travis badge with CircleCI badge
2015-08-04 07:44:59 +07:00
Chris Clark
a4d1509448 Replace Travis badge with CircleCI badge 2015-08-03 17:39:01 -07:00
Chris Clark
bbd51a03b6 Convert API to promises 2015-08-03 17:22:17 -07:00
sublimator
b55b82b2fd Merge pull request #474 from shekenahglory/develop
add 'DeliveredAmount' as optional metadata field
2015-08-01 06:30:36 +07:00
Matthew Fettig
fdb0f101bd add 'DeliveredAmount' as optional metadata field 2015-07-31 16:21:18 -07:00
Chris Clark
0afca5633d Merge pull request #473 from clark800/remove-slippage
Remove slippage parameter
2015-07-31 11:06:32 -07:00
Chris Clark
7c0d9a7172 Remove slippage parameter 2015-07-31 10:43:39 -07:00
sublimator
f6b7e27c67 Merge pull request #472 from clark800/offline
Add unit test for offline prepare and sign
2015-07-31 08:47:29 +07:00
Chris Clark
b8624bc55f Add unit test for offline prepare and sign 2015-07-30 18:19:52 -07:00
sublimator
2eec30756d Merge pull request #471 from clark800/no-xrp-paths
Don't set paths for XRP to XRP payment
2015-07-31 06:46:12 +07:00
sublimator
6b44ce8973 Merge pull request #458 from clark800/fix-pathfind
Fix counterparty logic in pathfind parsing
2015-07-31 06:26:58 +07:00
Chris Clark
ed0b501716 Don't set paths for XRP to XRP payment 2015-07-30 13:43:44 -07:00
Geert Weening
0fd391af72 bump version to 0.13.0.rc2 2015-07-30 12:18:48 -07:00
Chris Clark
fe9c1ada88 Fix counterparty logic in pathfind parsing 2015-07-30 11:55:52 -07:00
Alan Cohen
4c1f4ef58c Merge pull request #470 from clark800/remove-asyncify
Remove simple-asyncify dependency
2015-07-30 11:47:03 -07:00
Chris Clark
10afc770ff Remove simple-asyncify dependency 2015-07-30 11:38:46 -07:00
Alan Cohen
8543e60f86 Merge pull request #469 from lumberj/update-flow
Update Flow to 0.14
2015-07-30 11:09:57 -07:00
Alan Cohen
116d7e0f29 Update Flow to 0.14
Fixes issue with fs.readFileSync return type
See: https://github.com/facebook/flow/issues/416
https://github.com/facebook/flow/compare/v0.13.1...v0.14.0
2015-07-30 10:06:53 -07:00
Chris Clark
68adaec55b Merge pull request #467 from darkdarkdragon/develop-RLJS-440
change snake_case to camelCase in responses from api.submit and api.g…
2015-07-29 17:31:36 -07:00
Ivan Tivonenko
03640efef5 change snake_case to camelCase in responses from api.submit and api.getServerInfo and add schema for it 2015-07-30 03:10:10 +03:00
Alan Cohen
c6f450842e Merge pull request #466 from lumberj/continue-flowing
Typecheck remaining files in src/api
2015-07-29 16:03:15 -07:00
Alan Cohen
e583eb4592 Typecheck remaining files in src/api 2015-07-29 15:59:07 -07:00
Chris Clark
7fffbe0c64 Merge pull request #465 from darkdarkdragon/develop-RLJS-370-12
more unit tests coverage
2015-07-29 11:22:06 -07:00
Ivan Tivonenko
63e3b71eb5 cover api/common/errors.js with tests 2015-07-29 21:15:28 +03:00
Ivan Tivonenko
823ef738fe cover api/common/utils.js with tests 2015-07-29 17:40:59 +03:00
Ivan Tivonenko
0977ef0ec2 cover api/common/validate.js with tests 2015-07-29 17:15:34 +03:00
Ivan Tivonenko
cecf3f3d22 cover api/ledger/parse/utils.js with tests 2015-07-29 16:42:23 +03:00
Ivan Tivonenko
472fbce23a cover api/ledger/parse/trustline.js with tests 2015-07-29 15:46:15 +03:00
sublimator
e44aea1767 Merge pull request #464 from wltsmrz/add-delivermin
Add DeliverMin setter
2015-07-29 14:52:56 +07:00
wltsmrz
d682d90d86 Add DeliverMin setter 2015-07-29 00:25:27 -07:00
Chris Clark
141aa17dfc Merge pull request #463 from darkdarkdragon/develop-RLJS-370-11
cover api/common/schema-validator.js with tests
2015-07-28 17:40:48 -07:00
Ivan Tivonenko
0b09e53479 cover api/common/schema-validator.js with tests 2015-07-29 03:22:17 +03:00
Chris Clark
528d8bf25d Merge pull request #457 from darkdarkdragon/develop-sign-with-regular-key
allow to sign transaction in api.sign using regular key
2015-07-28 15:53:37 -07:00
Ivan Tivonenko
03a2109e24 allow to sign transaction in api.sign using regular key
make Seed.parse_json try different input types instead
of stopping on first failing
2015-07-29 01:49:25 +03:00
Chris Clark
b38b9bced6 Merge pull request #462 from darkdarkdragon/develop-RLJS-370-10
cover api/ledger/parse/settings.js with tests
2015-07-28 14:05:04 -07:00
Ivan Tivonenko
ea063d0c95 cover api/ledger/parse/settings.js with tests 2015-07-28 22:21:25 +03:00
Chris Clark
7f93929014 Merge pull request #461 from lumberj/update-parser
Update ripple-lib-transactionparser to 0.4
2015-07-28 11:23:06 -07:00
Alan Cohen
4cd10ecb87 Update ripple-lib-transactionparser to 0.4 2015-07-28 09:55:01 -07:00
sublimator
6ef30debd2 Merge pull request #459 from wltsmrz/fix-transaction-constructor
Fix transaction construction for SetRegularKey transactions
2015-07-28 14:50:58 +07:00
wltsmrz
4766bace4e Fix transaction construction for SetRegularKey transactions 2015-07-28 00:47:30 -07:00
sublimator
261500a3a4 Merge pull request #460 from wltsmrz/fix-transaction-sequencing
Fix transaction sequencing
2015-07-28 14:37:32 +07:00
wltsmrz
fae22b7023 Fix transaction sequencing 2015-07-27 23:17:05 -07:00
Chris Clark
4568b39997 Merge pull request #450 from clark800/api-names
Rename API parameters
2015-07-27 17:06:14 -07:00
Chris Clark
4a218cacfa Merge pull request #449 from clark800/numbers
Express trustline quality and account transferRate as floats
2015-07-27 17:02:35 -07:00
Chris Clark
34a4dd3077 Express transferRate as a float 2015-07-27 16:59:58 -07:00
Chris Clark
a383bd7e52 Express trustline quality as a float 2015-07-27 16:56:46 -07:00
Chris Clark
e76e693bdb Merge pull request #452 from darkdarkdragon/develop-RLJS-370-9-1
Increase tests coverage even more
2015-07-27 16:35:59 -07:00
Ivan Tivonenko
2c52e4aa69 more unit tests coverage 2015-07-28 02:24:27 +03:00
Ivan Tivonenko
13dee36e93 propagate message from remote error to RippledNetworkError 2015-07-28 02:17:40 +03:00
Ivan Tivonenko
6e180439d1 cover api/ledger/transaction.js with unit tests 2015-07-28 02:17:38 +03:00
Ivan Tivonenko
e8d0c1ae95 make api.getTransaction return NotFound error in case of transaction not found
instead of core.RippleError
2015-07-28 02:16:30 +03:00
Ivan Tivonenko
068bda0c95 cover api/ledger/transactions.js with unit tests 2015-07-28 02:16:28 +03:00
Ivan Tivonenko
ab694381d5 fix some api functions to work with parsed version of transaction 2015-07-28 02:13:54 +03:00
Chris Clark
dc2a6c75cf Merge pull request #456 from clark800/docs
Update api.html doc
2015-07-27 14:57:13 -07:00
Chris Clark
98dbba8f27 Add api.html doc 2015-07-27 14:54:15 -07:00
Chris Clark
9a1b80d77a Merge pull request #451 from clark800/sample
Add sample API usage for getting balances and making a payment
2015-07-27 14:46:59 -07:00
Chris Clark
a655be30d6 Merge pull request #455 from clark800/doc
Add api.html
2015-07-27 14:46:50 -07:00
Chris Clark
e5aabc3072 Add api.html 2015-07-27 14:42:29 -07:00
Chris Clark
2cd32d58ad Merge pull request #454 from clark800/response-schemas
Add response schemas and small fixes
2015-07-27 14:22:55 -07:00
Chris Clark
0c02b92717 Add response schemas and small fixes 2015-07-27 14:20:09 -07:00
Geert Weening
c58a077a2f Merge branch 'release' into develop 2015-07-24 15:39:50 -07:00
Geert Weening
6e7dc9d7d3 Merge branch 'master' into release
Conflicts:
	npm-shrinkwrap.json
	package.json
2015-07-24 15:39:00 -07:00
wltsmrz
2695f4302a Merge pull request #436 from wltsmrz/fix-orderbook-notification
Fix orderbook notification while disconnected
2015-07-25 06:25:31 +08:00
Geert Weening
9a533ab807 Merge branch 'develop' into release 2015-07-24 15:22:35 -07:00
wltsmrz
a037952493 Fix orderbook notification while disconnected 2015-07-24 15:22:21 -07:00
Geert Weening
dc96795a02 Merge pull request #453 from clark800/webpack-fix
Fix webpack require failure due to "./" notation
2015-07-24 15:03:44 -07:00
Chris Clark
8d9746d7b1 Fix webpack require failure due to "./" notation 2015-07-24 14:59:25 -07:00
Chris Clark
00342c4239 Merge pull request #446 from darkdarkdragon/develop-RLJS-370-8
Increase tests coverage
2015-07-23 16:47:42 -07:00
Chris Clark
e48df2c1fd Add sample API usage for getting balances and making a payment 2015-07-23 15:51:15 -07:00
Ivan Tivonenko
6ade0f6554 cover api/ledger/pathfind.js with tests 2015-07-24 01:34:54 +03:00
Ivan Tivonenko
a88157bb92 change pathfind scheme so destination can be specified without counterparty 2015-07-24 01:34:51 +03:00
Ivan Tivonenko
00f318284f cover api/transaction/utils.js with tests 2015-07-24 01:34:49 +03:00
Chris Clark
2e12dc6d53 Merge pull request #445 from darkdarkdragon/develop-RLJS-370-7
fix settings.json schema to allow empty stings to
2015-07-23 15:27:50 -07:00
Chris Clark
34435d4d05 Rename API parameters 2015-07-23 14:24:11 -07:00
Ivan Tivonenko
a99452b773 fix settings.json schema to allow null to
'walletSize', 'transferRate', 'emailHash' and
'walletLocator' fields so they
can be cleared

cover api/transaction/settings.js with tests
2015-07-23 23:29:00 +03:00
Geert Weening
a05cb39ab0 Merge pull request #447 from geertweening/develop
Remove `src/js`
2015-07-23 11:12:21 -07:00
Geert Weening
0c69f7f10e Remove src/js 2015-07-23 11:06:12 -07:00
Geert Weening
8d50034265 Bump version to 0.13.0-rc1 2015-07-22 12:02:15 -07:00
Geert Weening
64f451e904 Merge branch 'release' into develop
Conflicts:
	npm-shrinkwrap.json
	package.json
2015-07-22 11:59:36 -07:00
Chris Clark
4787e5d29a Merge pull request #444 from darkdarkdragon/develop-RLJS-370-6
cover api/transaction/order.js with tests
2015-07-21 18:26:36 -07:00
Ivan Tivonenko
194f76d57f cover api/transaction/order.js with tests 2015-07-22 04:18:35 +03:00
Chris Clark
d2ee5cb0bc Merge pull request #443 from darkdarkdragon/develop-RLJS-370-5
cover src/api/transaction/payment.js with unit tests
2015-07-21 17:50:05 -07:00
Ivan Tivonenko
040298db2c cover src/api/transaction/payment.js with unit tests 2015-07-22 03:29:30 +03:00
Chris Clark
b4e6d4b98c Merge pull request #442 from reedrosenbluth/proxy
check if proxy option is undefined, not if it exists
2015-07-21 17:11:42 -07:00
Reed Rosenbluth
a2b31e2677 check if proxy option is undefined, not if it exists 2015-07-21 17:01:02 -07:00
Chris Clark
e997c44a18 Merge pull request #440 from darkdarkdragon/develop-RLJS-370-4
increase test coverage of Account object
2015-07-21 11:37:53 -07:00
Alan Cohen
b27011fb38 Merge pull request #441 from lumberj/add-flow-to-api
Add flow to api
2015-07-21 11:22:47 -07:00
Alan Cohen
b477eb238b Add flow-bin to dev dependencies and ignore it in flowconfig
Add flow to api/server/server.js

Add flow check to src/api: index, accountinfo, balances, orderbook..

orders, settings, transaction, transactions

Add flow typecheck to api/ledger/pathfind

Use eslint plugin flowtype to allow flow type annotations

Babel-eslint emits errors when using the type keyword. This plugin works
around the issue by stripping flow annotations before linting.

Source: https://www.npmjs.com/package/eslint-plugin-flowtype

Add flow to ledger/parse/account-order, trustline, orderbook-order
2015-07-21 10:59:49 -07:00
Ivan Tivonenko
02bc256225 increase test coverage of Account object 2015-07-21 09:19:25 +03:00
sublimator
9f7293127c Merge pull request #439 from clark800/cleanup
Cleanup
2015-07-21 10:38:57 +07:00
sublimator
61a0ea7eac Merge pull request #437 from clark800/naming
Rename "incoming"/"outgoing" to "initiated"
2015-07-21 10:37:40 +07:00
sublimator
51ef93e23b Merge pull request #435 from clark800/consistency
Don't show flags that are set to false
2015-07-21 09:06:05 +07:00
Chris Clark
ab11220e27 Merge pull request #438 from reedrosenbluth/proxy
Proxy Support
2015-07-20 18:22:59 -07:00
Reed Rosenbluth
b23d588747 disable eslint for lines 538 - 548 2015-07-20 18:18:45 -07:00
Reed Rosenbluth
a71dc28523 updated shrinkwrap 2015-07-20 18:02:42 -07:00
Reed Rosenbluth
2de0e13ec5 proxy support 2015-07-20 17:39:46 -07:00
Chris Clark
d0c922ed13 Require uppercase hex for currency codes 2015-07-20 17:34:03 -07:00
Chris Clark
8c82ebec79 Use "core" instead of "utils.core" 2015-07-20 17:33:19 -07:00
Chris Clark
ddd83d10fa Don't show flags that are set to false 2015-07-20 16:52:43 -07:00
Chris Clark
031375e701 Rename "incoming"/"outgoing" to "initiated" 2015-07-20 14:59:59 -07:00
sublimator
4bc285313c Merge pull request #434 from clark800/fixes
Simplify getServerInfo response
2015-07-21 01:21:16 +07:00
Chris Clark
f42dd69b53 Simplify getServerInfo response 2015-07-20 11:15:54 -07:00
sublimator
1dae06fdd8 Merge pull request #433 from clark800/mock-fixtures
Organize rippled mock fixtures
2015-07-20 14:26:17 +07:00
Chris Clark
ff29247b9e Organize rippled mock fixtures 2015-07-17 15:36:37 -07:00
Chris Clark
d9eca203ed Merge pull request #431 from darkdarkdragon/develop-RLJS-370-2
test parseTrustline in getTransaction
2015-07-17 14:22:40 -07:00
Chris Clark
4a85182a79 Merge pull request #432 from darkdarkdragon/develop-RLJS-370-3
testing - add memo field into payment transaction so parsePaymentMemo…
2015-07-17 14:21:46 -07:00
Ivan Tivonenko
055d275f0a test parseTrustline in getTransaction 2015-07-18 00:18:35 +03:00
Ivan Tivonenko
eb7bbe5715 testing - add memo field into payment transaction so parsePaymentMemos from parsePayment is tested 2015-07-18 00:13:31 +03:00
Chris Clark
8d5752883f Merge pull request #430 from clark800/fixtures
Organize fixtures
2015-07-16 15:29:59 -07:00
Chris Clark
2b5f356de8 Organize fixtures 2015-07-16 15:02:27 -07:00
Chris Clark
c9610900fd Merge pull request #429 from darkdarkdragon/develop-RLJS-370-1
add test for getting order and order cancellation transations
2015-07-16 13:31:15 -07:00
Ivan Tivonenko
79b1a65a7b add test for getting order and order cancellation transations 2015-07-16 23:08:57 +03:00
Chris Clark
df21b9453f Merge pull request #428 from darkdarkdragon/develop-fix-transaction-parse
fix attachTransactionDate in getTransaction
2015-07-16 10:25:42 -07:00
Ivan Tivonenko
22257bdd2b fix attachTransactionDate in getTransaction 2015-07-16 05:23:56 +03:00
sublimator
aa3767b180 Merge pull request #427 from clark800/account-info
Add getAccountInfo method and move sequence field in response
2015-07-16 09:48:02 +08:00
Chris Clark
c073c2b7de Add getAccountInfo method and unit test 2015-07-15 17:13:14 -07:00
Chris Clark
529a55efb0 Remove sequence from AccountFields 2015-07-15 15:49:05 -07:00
Chris Clark
c36567e062 Merge pull request #426 from clark800/cleanup
Fix hash128 schema
2015-07-15 15:19:02 -07:00
Chris Clark
f7873f3b41 Fix hash128 schema 2015-07-15 15:15:31 -07:00
Chris Clark
6b54f1c1eb Merge pull request #423 from clark800/cleanup
Cleanup RippleAPI
2015-07-15 15:06:52 -07:00
Chris Clark
1fb1bc7404 Cleanup RippleAPI 2015-07-15 14:55:24 -07:00
Chris Clark
aa646a3acf Merge pull request #421 from clark800/bugfix
Fix parsing of settings transactions
2015-07-15 14:54:25 -07:00
Chris Clark
18ac8a9d03 Fix parsing of settings transactions 2015-07-15 14:43:04 -07:00
Chris Clark
7c357c5d52 Merge pull request #424 from clark800/disable-resubmit
Disable automatic transaction resubmission for RippleAPI
2015-07-15 14:24:18 -07:00
Chris Clark
816db9e0dc Merge pull request #425 from clark800/tx-hash
Add transaction id to response of getTransaction and getTransactions
2015-07-15 14:22:08 -07:00
Chris Clark
fa9e1de4df Add transaction id to response of getTransaction and getTransactions 2015-07-15 13:57:00 -07:00
Chris Clark
137d947606 Disable automatic transaction resubmission for RippleAPI 2015-07-15 13:45:27 -07:00
sublimator
77a05c1881 2000 2015-07-15 19:49:39 +08:00
sublimator
ab13e3fe18 Merge pull request #422 from darentuzi/patch-1
Ternary operator syntax fix
2015-07-15 19:47:22 +08:00
darentuzi
fc4b085026 Ternary operator syntax fix
let to const and syntax update
2015-07-15 14:44:41 +03:00
Chris Clark
67e8f6ab65 Merge pull request #420 from darkdarkdragon/develop-RLJS-408
Test all methods of RippleApi in integration-test
2015-07-14 17:21:37 -07:00
Ivan Tivonenko
792b30c8b5 Test all methods of RippleApi in integration-test 2015-07-15 03:17:39 +03:00
sublimator
3407aadfb8 Merge pull request #418 from clark800/ledger-gaps
Refine ledger gap detection to reduce false positives
2015-07-15 07:21:09 +08:00
sublimator
29e59ebfd2 Merge pull request #419 from clark800/set-regular-key
Add support for regularKey in prepareSettings
2015-07-14 15:29:46 +08:00
Chris Clark
286a684963 Add support for regularKey in prepareSettings 2015-07-13 16:46:57 -07:00
Chris Clark
3ef586d2d3 Refine ledger gap detection to reduce false positives 2015-07-13 15:57:50 -07:00
sublimator
5889037a71 Merge pull request #414 from clark800/merge-orderbook
Merge flipped orderbook in getOrderbook
2015-07-14 06:54:54 +08:00
sublimator
5e3f3969f5 Merge pull request #403 from clark800/no-limit
Get all results when limit is not specified
2015-07-14 06:52:20 +08:00
Chris Clark
eecf45918b Merge pull request #415 from darkdarkdragon/develop-RLJS-404
Port integration tests from ripple-rest
2015-07-13 15:50:54 -07:00
Chris Clark
6d91e6e6b2 Merge pull request #417 from darkdarkdragon/develop-fix-gettransaction-missing-history
throw MissingLedgerHistoryError in getTransaction only when ledgers a…
2015-07-13 15:43:06 -07:00
Ivan Tivonenko
7808f87060 Throw MissingLedgerHistoryError in getTransaction only when ledgers are missing prior to the last closed ledger. 2015-07-14 01:25:07 +03:00
Ivan Tivonenko
6c48b22eea Port integration tests from ripple-rest 2015-07-14 01:10:42 +03:00
Chris Clark
7344f1178b Merge flipped orderbook in getOrderbook 2015-07-13 14:09:33 -07:00
Chris Clark
5a6a3ce6e0 Merge pull request #413 from darkdarkdragon/develop-fix-gettransaction
[FIX] fix getTransaction
2015-07-13 11:06:59 -07:00
Chris Clark
f3a54bf02a Get all results when limit is not specified 2015-07-13 10:30:51 -07:00
Ivan Tivonenko
766dc5d0ce [FIX] fix getTransaction
Do not try to parse transaction if there was error

Throw NotFoundError instead of ApiError in case of ledger_index not defined.
2015-07-11 15:30:41 +03:00
wltsmrz
f8f196903a Merge pull request #406 from wltsmrz/add-resubmission-switch
Add switch to disable transaction resubmission
2015-07-11 02:22:30 +08:00
sublimator
337c41fe10 Merge pull request #400 from clark800/refactor
Refactor to make quality adjustment for XRP more clear
2015-07-11 01:48:35 +08:00
Chris Clark
fb8eefd00d Refactor to make quality adjustment for XRP more clear 2015-07-10 10:24:52 -07:00
wltsmrz
3082b959f6 Add switch to disable transaction resubmission
* Add Transaction.setResumittable(Boolean)
* Add Transaction.isResumittable() -> Boolean
* Add automatic_resubmission (Boolean) config option to Remote
2015-07-10 09:53:31 -07:00
wltsmrz
487371737c Merge pull request #408 from wilsonianb/subscribe-vals
Handle validations subscription messages
2015-07-11 00:51:36 +08:00
Chris Clark
14ccc9359d Merge pull request #410 from clark800/range-check
Check for ledger gaps in getTransactions
2015-07-09 15:19:33 -07:00
Chris Clark
665b76271c Merge pull request #411 from clark800/get-paths
Rename getPathFind to getPaths
2015-07-09 15:15:49 -07:00
Chris Clark
11ca00bb45 Check for ledger gaps in getTransactions 2015-07-09 15:14:59 -07:00
Chris Clark
046d397dfb Rename getPathFind to getPaths 2015-07-09 15:12:45 -07:00
wilsonianb
58cc01b6d3 Handle validations subscription messages 2015-07-09 15:08:39 -07:00
Chris Clark
6a3eb7b9b9 Merge pull request #409 from clark800/counterparty
Add counterparty option to getTransactions
2015-07-09 14:59:10 -07:00
Chris Clark
935a463d3b Merge pull request #407 from clark800/rangeset
Add support for efficient range checking to RangeSet
2015-07-09 13:57:47 -07:00
Chris Clark
f2f4173d7b Add support for efficient range checking to RangeSet 2015-07-09 13:51:27 -07:00
Chris Clark
941aaf6d8b Add counterparty option to getTransactions 2015-07-09 13:46:12 -07:00
Chris Clark
1068b68568 Merge pull request #404 from clark800/options-schemas
Add schemas for options for each ledger request
2015-07-08 16:59:50 -07:00
Chris Clark
e76b9a9051 Add schemas for options for each ledger request 2015-07-08 16:49:47 -07:00
Chris Clark
410ac117f6 Merge pull request #402 from clark800/get-ledger-version
Add ledgerVersion option to getSettings
2015-07-08 16:23:45 -07:00
Chris Clark
fa89c4dee8 Add ledgerVersion option to getSettings 2015-07-08 14:48:29 -07:00
Chris Clark
a464ca2368 Merge pull request #401 from clark800/get-ledger-version
Add getLedgerVersion and unit test
2015-07-08 14:21:16 -07:00
Chris Clark
befd89c3d7 Add getLedgerVersion and unit test 2015-07-08 14:05:25 -07:00
Chris Clark
49640cf282 Merge pull request #384 from mwshortt/IOU-suffix-removal
Decouple the Amount class from math performed on values
2015-07-08 12:14:05 -07:00
Madeline Shortt
2ac4549712 Rename IOUValue.js and XRPValue.js to lowercase and other fixes 2015-07-08 11:49:15 -07:00
Madeline Shortt
6bffe06c3b Change this._value to be an instance of IOU or XRP Value 2015-07-08 11:49:14 -07:00
Madeline Shortt
13e9ad45f9 Remove BigNumber completely from Amount and small changes 2015-07-08 11:49:13 -07:00
Madeline Shortt
193fcc9014 Create separate Value classes to decouple currency math and the Amount class 2015-07-08 10:25:45 -07:00
Madeline Shortt
30529b7a04 Remove IOU_SUFFIX from orderbookutils.js and clean up serializedtypes.js 2015-07-08 10:25:45 -07:00
sublimator
38b254e7f0 Merge pull request #399 from clark800/rename
Rename getAccountTransactions to getTransactions
2015-07-08 09:08:16 +07:00
sublimator
c4b98d2139 Merge pull request #398 from clark800/orders
Add makerExchangeRate (quality) to orders, and maker to account orders
2015-07-08 08:55:41 +07:00
Chris Clark
1309b58592 Add makerExchangeRate (quality) to orders, and maker to account orders 2015-07-07 18:28:37 -07:00
Chris Clark
9b9d2dc32b Rename getAccountTransactions to getTransactions 2015-07-07 17:52:28 -07:00
Chris Clark
73020fb8ae Merge pull request #397 from lukeburns/develop
Added issuer to payment example in guide
2015-07-07 14:00:02 -07:00
sublimator
8221db833f Merge pull request #391 from clark800/get-pathfind
Convert getPathFind and add unit test
2015-07-07 13:16:16 +07:00
Chris Clark
09b10d3f2c Convert getPathFind and add unit test 2015-07-06 16:11:23 -07:00
Luke Burns
c31798c7f8 changed currency to USD in submit payment example 2015-07-03 00:43:46 -04:00
Luke Burns
064c5e9e50 added issuer to submit payment example 2015-07-02 23:21:19 -04:00
Chris Clark
077a53475d Merge pull request #392 from clark800/server
Convert server methods, add unit tests, and cleanup
2015-07-02 11:23:29 -07:00
Chris Clark
32ca23a00b Convert server methods, add unit tests, and cleanup 2015-07-02 11:17:06 -07:00
Chris Clark
1a7cdd7d04 Merge pull request #396 from sublimator/eslint-update
Use parser: babel-eslint which supports facebook flow
2015-07-02 10:13:43 -07:00
Nicholas Dudfield
85befa467b Use parser: babel-eslint which supports facebook flow 2015-07-02 18:30:11 +07:00
wltsmrz
9b956e57ac Merge pull request #394 from sublimator/eslint-update
Update eslint
2015-07-02 18:56:40 +08:00
sublimator
93c0c49002 Merge pull request #390 from wltsmrz/refactor-api-deprecation
Refactor API deprecation, fix request constructors with positional args
2015-07-02 17:51:28 +07:00
wltsmrz
44954621e0 Refactor API deprecation, fix request constructors with positional args 2015-07-02 03:46:55 -07:00
Nicholas Dudfield
96e5d484da Update eslint 2015-07-02 17:44:38 +07:00
sublimator
4efe0b920e Merge pull request #388 from clark800/get-orderbook
Convert getOrderBook and add unit test
2015-07-01 12:43:00 +07:00
Chris Clark
d158cc7fc1 Fix other indentation problems 2015-06-30 16:53:48 -07:00
Chris Clark
034cd4eaf6 Fix indentation 2015-06-30 16:36:14 -07:00
Chris Clark
d5f3d90486 Add flipped orderbook to getOrderBook response 2015-06-30 15:43:30 -07:00
Chris Clark
99e076b7dd Sort orderbook orders by price 2015-06-30 15:21:28 -07:00
Chris Clark
8f9aec83f2 Fix counterparty/issuer bug 2015-06-30 13:02:42 -07:00
Chris Clark
87a8745a64 Fix unit tests 2015-06-30 12:52:21 -07:00
Chris Clark
2ab51d7a8a Revert remote.js 2015-06-30 12:12:21 -07:00
Chris Clark
3e0f43e44e Convert getOrderBook and add unit test 2015-06-26 16:27:34 -07:00
Chris Clark
6a763fab18 Merge pull request #385 from clark800/get-orders
Convert getOrders and add unit test
2015-06-26 10:46:41 -07:00
Chris Clark
3fe6726789 Merge pull request #386 from sublimator/rm-message
Remove message.js
2015-06-26 10:30:04 -07:00
Nicholas Dudfield
5b4deabd90 Remove message.js 2015-06-26 15:33:26 +07:00
Chris Clark
84bc7dd4aa Convert getOrders and add unit test 2015-06-25 14:21:39 -07:00
Chris Clark
3960b4e11f Merge pull request #383 from clark800/remove
Add unit test for generateWallet and reorganize API files
2015-06-25 11:43:39 -07:00
Chris Clark
141215fc38 Add unit test for getSettings 2015-06-25 11:23:56 -07:00
Chris Clark
2446e2f6da Add unit test for generateWallet and reorganize API files 2015-06-24 18:03:03 -07:00
Chris Clark
81a9bc0739 Merge pull request #382 from clark800/notifications
Merge notifications functionality into getAccountTransactions
2015-06-24 16:44:53 -07:00
Chris Clark
64e86f403e Merge notifications functionality into getAccountTransactions 2015-06-24 16:14:28 -07:00
Chris Clark
77f1351e5b Merge pull request #381 from clark800/balances
Update getBalances to use getTrustlines
2015-06-24 12:53:58 -07:00
Chris Clark
47a87f3a92 Update getBalances to use getTrustlines 2015-06-24 12:38:13 -07:00
Chris Clark
d2df75dc25 Merge pull request #380 from clark800/trustlines
Add getTrustlines and unit test
2015-06-24 11:36:34 -07:00
Chris Clark
d92fbfb7aa Add getTrustlines and unit test 2015-06-24 11:17:46 -07:00
wltsmrz
1b3be55711 Merge pull request #378 from sublimator/signing-data
Add Transaction.signingData
2015-06-23 20:58:21 +08:00
Nicholas Dudfield
8d98e443c5 Add Transaction.signingData() 2015-06-23 19:52:41 +07:00
Nicholas Dudfield
46121edd62 Remove global install for CI dependencies:
* Add them to package.json
* Fixes weird error with missing `esprima-fb`
2015-06-23 19:47:05 +07:00
sublimator
1a32536ac8 Merge pull request #355 from ripple/validate-uint-hex-input
Validate UInt* hex input
2015-06-22 13:59:17 +07:00
Chris Clark
85bf6891f9 Merge pull request #374 from clark800/empty-strings
Remove empty counterparties in XRP amounts
2015-06-18 17:39:18 -07:00
Chris Clark
1ccca1c4ec Remove empty counterparties in XRP amounts 2015-06-18 17:36:45 -07:00
Geert Weening
22cd70e53f Merge pull request #373 from clark800/get-acct-tx
Convert getAccountTransactions
2015-06-18 17:29:38 -07:00
Chris Clark
d8aad1444e Merge pull request #375 from clark800/flow-check
Use "flow check" to prevent "out of retries" errors
2015-06-18 15:12:56 -07:00
Chris Clark
621dfd9ca5 Use "flow check" to prevent "out of retries" errors 2015-06-18 15:10:16 -07:00
wltsmrz
2ad6a1a77e Validate UInt hex input 2015-06-18 13:50:11 -07:00
Chris Clark
8f37438a08 Convert getAccountTransactions 2015-06-17 18:04:24 -07:00
Chris Clark
ff6ac0333c Merge pull request #369 from clark800/parse-tx
Rewrite transaction parser and add unit test for getTransaction
2015-06-16 16:55:15 -07:00
Chris Clark
1b936d2aa2 Rewrite transaction parser and add unit test for getTransaction 2015-06-16 16:24:21 -07:00
sublimator
df0cff969c Merge pull request #372 from clark800/license
Add license to package.json
2015-06-16 18:33:49 +07:00
Chris Clark
46e2598499 Add license to package.json 2015-06-15 17:28:38 -07:00
Chris Clark
0b32378ab5 Merge pull request #364 from clark800/submit-test
Add unit test for submit
2015-06-15 14:39:19 -07:00
Chris Clark
bf25eb350f Display git log in travis output 2015-06-15 14:32:21 -07:00
Chris Clark
97cea2ce4d Add unit test for submit 2015-06-12 16:54:38 -07:00
Chris Clark
82ed402b16 Merge pull request #370 from clark800/fix-perf-test
Fix perf test
2015-06-12 16:28:24 -07:00
Chris Clark
ca7b69a2a0 Fix perf test 2015-06-12 16:22:05 -07:00
Chris Clark
d9c61a9431 Merge pull request #366 from darkdarkdragon/develop-RT-3462
[FIX] emit empty offers list (RT-3462)
2015-06-10 11:38:25 -07:00
Ivan Tivonenko
3b636ce2d1 [FIX] emit empty offers list (RT-3462)
For autobridged order books, emit 'model' event,
even if offers is empty, to indicate that
orderbook is loaded
2015-06-10 21:10:18 +03:00
wltsmrz
2196352335 Merge pull request #363 from clark800/parallel
Parallelize CircleCI tests
2015-06-10 05:06:11 +08:00
Chris Clark
823d7048ba Parallelize CircleCI tests 2015-06-09 13:13:13 -07:00
Chris Clark
d066e1145d Merge pull request #361 from clark800/prepare-trustline
Add unit tests for prepareTrustline, prepareOrderCancellation, and sign
2015-06-09 13:12:24 -07:00
Chris Clark
fb7021abcc Add unit tests for prepareTrustline, prepareOrderCancellation, and sign 2015-06-09 11:30:51 -07:00
Chris Clark
9c14fb2379 Merge pull request #360 from clark800/prepare-order
Add unit test for prepareOrder and fix bugs
2015-06-08 14:36:13 -07:00
Chris Clark
a114bf42c4 Add unit test for prepareOrder and fix bugs 2015-06-08 14:11:35 -07:00
Chris Clark
87b6c09de3 Merge pull request #358 from clark800/flow-type
Add flow type checking
2015-06-08 14:08:07 -07:00
Chris Clark
f1c95112bd Add circle.yml file 2015-06-08 14:00:08 -07:00
Chris Clark
76d8c8b061 Add flow type checking 2015-06-08 14:00:08 -07:00
Chris Clark
7cbcb9a220 Merge pull request #359 from darkdarkdragon/develop-RT-3445
[FIX] fix AutobridgeCalculator (RT-3445)
2015-06-08 11:08:44 -07:00
Ivan Tivonenko
4433ac57bd [FIX] change var to let and const 2015-06-08 20:38:22 +03:00
Ivan Tivonenko
5379da4874 [FIX] fix AutobridgeCalculator (RT-3445)
pass issuers to AutobridgeCalculator so
it can create offers with right issuers
2015-06-07 01:35:49 +03:00
Chris Clark
d488ce55b3 Merge pull request #357 from clark800/v2-schemas
Update schemas, use schemas for validation, and switch to is-my-json-valid
2015-06-05 15:25:32 -07:00
Chris Clark
908e306f04 Update schemas, use schemas for validation, and switch to is-my-json-valid 2015-06-05 14:58:23 -07:00
Chris Clark
62b5953abe Merge pull request #356 from sublimator/remove-sjcl-custom
Remove sjcl-custom in favor of sjcl-extended module in separate repo
2015-06-04 11:50:10 -07:00
Nicholas Dudfield
35ae968d9f Remove sjcl-custom in favor of sjcl-extended module in separate repo 2015-06-04 16:19:31 +07:00
Chris Clark
22dc39b920 Merge pull request #354 from clark800/v2
Move ripple-rest/api into src/api, exposing RippleAPI
2015-05-29 11:12:39 -07:00
Chris Clark
278331cc4a Move ripple-rest/api into src/api, exposing RippleAPI 2015-05-29 10:49:14 -07:00
wltsmrz
16e3541a10 Merge pull request #353 from clark800/develop
Fix bug in trace message for updateOwnerOffersFundedAmount
2015-05-28 13:54:43 -07:00
281 changed files with 15987 additions and 3826 deletions

View File

@@ -1,7 +1,14 @@
[ignore]
.*/src/api/.*
.*/src/core/.*
.*/dist/.*
.*/test/fixtures/.*
.*/node_modules/flow-bin/.*
[include]
./node_modules/
[libs]
[options]
module.system=node

View File

@@ -1,18 +1,9 @@
sudo: false # use faster docker containers
language: node_js
node_js:
- "0.12"
before_script:
- npm install -g eslint
- curl 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc' > ./eslintrc
- eslint --reset -c ./eslintrc $(git --no-pager diff --name-only -M100% --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD | grep "\.js$")
script: MOCHA_REPORTER=tap npm test --coverage
after_success:
- npm run coveralls
- sh -c "git log | head -12"
script: bin/ci.sh
notifications:
email: false
webhooks:
urls:
- https://webhooks.gitter.im/e/d1ec4245f90231619d30
on_success: change # options: [always|never|change] default: always
on_failure: always # options: [always|never|change] default: always
on_start: false # default: false

View File

@@ -3,17 +3,10 @@
'use strict';
var _ = require('lodash');
var gulp = require('gulp');
var gutil = require('gulp-util');
var watch = require('gulp-watch');
var plumber = require('gulp-plumber');
var filelog = require('gulp-filelog');
var cleanDest = require('gulp-clean-dest');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var webpack = require('webpack');
var bump = require('gulp-bump');
var react = require('gulp-react');
var flow = require('gulp-flowtype');
var argv = require('yargs').argv;
var pkg = require('./package.json');
@@ -39,10 +32,6 @@ function webpackConfig(extension, overrides) {
return _.assign({}, defaults, overrides);
}
function logPluginError(error) {
gutil.log(error.toString());
}
gulp.task('build', function(callback) {
webpack(webpackConfig('.js'), callback);
});
@@ -118,25 +107,6 @@ gulp.task('watch', function() {
gulp.watch('src/*', ['build-debug']);
});
// To use this, each javascript file must have /* @flow */ on the first line
gulp.task('typecheck', function() {
return gulp.src('src/*.js')
.pipe(flow({ // note: do not set the 'all' option, it is broken
weak: true, // remove this after all errors are addressed
killFlow: true
}));
});
gulp.task('strip', function() {
return gulp.src('src/*.js')
.pipe(watch('src/*.js'))
.pipe(cleanDest('out')) // delete outdated output file before stripping
.pipe(plumber()) // prevent an error in one file from ending build
.pipe(react({stripTypes: true}).on('error', logPluginError))
.pipe(filelog())
.pipe(gulp.dest('out'));
});
gulp.task('version-bump', function() {
if (!argv.type) {
throw new Error('No type found, pass it in using the --type argument');

View File

@@ -1,3 +1,10 @@
##0.13.0
+ Added experimental version of RippleAPI
- [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)
+ [Fix bug where the paths would be set with an empty array](https://github.com/ripple/ripple-lib/commit/83874ec0962da311b76f2385623e51c68bc39035)
##0.12.6
+ [Fix webpack require failure due to "./" notation](https://github.com/ripple/ripple-lib/commit/8d9746d7b10be203ee613df523c2522012ff1baf)

View File

@@ -2,7 +2,7 @@
A JavaScript API for interacting with Ripple in Node.js and the browser
[![Build Status](https://travis-ci.org/ripple/ripple-lib.svg?branch=develop)](https://travis-ci.org/ripple/ripple-lib) [![Coverage Status](https://coveralls.io/repos/ripple/ripple-lib/badge.png?branch=develop)](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
[![Circle CI](https://circleci.com/gh/ripple/ripple-lib/tree/develop.svg?style=svg)](https://circleci.com/gh/ripple/ripple-lib/tree/develop) [![Coverage Status](https://coveralls.io/repos/ripple/ripple-lib/badge.png?branch=develop)](https://coveralls.io/r/ripple/ripple-lib?branch=develop)
[![NPM](https://nodei.co/npm/ripple-lib.png)](https://www.npmjs.org/package/ripple-lib)
@@ -44,7 +44,7 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
$ bower install ripple
```
See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for additional bower instructions
See the [bower-ripple repo](https://github.com/ripple/bower-ripple) for additional bower instructions.
**Building ripple-lib for browser environments**

58
bin/ci.sh Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash -ex
NODE_INDEX="$1"
TOTAL_NODES="$2"
typecheck() {
npm install -g flow-bin
npm run typecheck
}
lint() {
REPO_URL="https://raw.githubusercontent.com/ripple/javascript-style-guide"
curl "$REPO_URL/es6/eslintrc" > ./eslintrc
echo "plugins: [flowtype]" >> ./eslintrc
node_modules/.bin/eslint -c ./eslintrc $(git --no-pager diff --name-only -M100% --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD | grep "\.js$")
}
unittest() {
# test "src"
npm test --coverage
npm run coveralls
# test compiled version in "dist/npm"
ln -nfs ../../dist/npm/core test/node_modules/ripple-lib
ln -nfs ../../dist/npm test/node_modules/ripple-api
npm test
}
oneNode() {
lint
typecheck
unittest
}
twoNodes() {
case "$NODE_INDEX" in
0) lint && unittest;;
1) typecheck;;
*) echo "ERROR: invalid usage"; exit 2;;
esac
}
threeNodes() {
case "$NODE_INDEX" in
0) lint;;
1) typecheck;;
2) unittest;;
*) echo "ERROR: invalid usage"; exit 2;;
esac
}
case "$TOTAL_NODES" in
"") oneNode;;
1) oneNode;;
2) twoNodes;;
3) threeNodes;;
*) echo "ERROR: invalid usage"; exit 2;;
esac

7
circle.yml Normal file
View File

@@ -0,0 +1,7 @@
machine:
node:
version: 0.12.0
test:
override:
- bin/ci.sh "$CIRCLE_NODE_INDEX" "$CIRCLE_NODE_TOTAL":
parallel: true

View File

@@ -173,7 +173,7 @@ See the [wiki](https://ripple.com/wiki/JSON_Messages#subscribe) for details on s
##Submitting a payment to the network
Submitting a payment transaction to the Ripple network involves connecting to a `Remote`, creating a transaction, signing it with the user's secret, and submitting it to the `rippled` server. Note that the `Amount` module is used to convert human-readable amounts like '1XRP' or '10.50USD' to the type of Amount object used by the Ripple network.
Submitting a payment transaction to the Ripple network involves connecting to a `Remote`, creating a transaction, signing it with the user's secret, and submitting it to the `rippled` server. Note that the `Amount` module is used to convert human-readable amounts like '1 XRP' or '10.50 USD' to the type of Amount object used by the Ripple network.
```js
/* Loading ripple-lib Remote and Amount modules in Node.js */
@@ -187,7 +187,7 @@ var Amount = require('ripple-lib').Amount;
var MY_ADDRESS = 'rrrMyAddress';
var MY_SECRET = 'secret';
var RECIPIENT = 'rrrRecipient';
var AMOUNT = Amount.from_human('1XRP');
var AMOUNT = Amount.from_human('1 USD').set_issuer('rrrIssuer');
var remote = new Remote({ /* Remote options */ });

358
docs/api.html Normal file
View File

@@ -0,0 +1,358 @@
<html>
<head>
<style>
a {
text-decoration: none;
}
.method {
margin-bottom: 10px;
}
.details {
font-family: "Courier New", monospace;
white-space: pre;
display: none;
}
</style>
<script type="text/javascript">
function toggle(element) {
var results = element.parentElement.getElementsByClassName('details');
if (results.length > 0) {
var style = results[0].style;
style.display = (style.display === 'block') ? 'none' : 'block';
}
}
</script>
</head>
<body>
<h2><a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/index.js">RippleAPI</a></h2>
<div class="method">
connect()
</div>
<div class="method">
disconnect()
</div>
<div class="method">
isConnected()
</div>
<div class="method">
getServerInfo()
</div>
<div class="method">
getFee()
</div>
<div class="method">
getLedgerVersion()
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-transaction.json">getTransaction</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/hash256.json">identifier</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/transaction-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
identifier: txhash
options: {minLedgerVersion: int, maxLedgerVersion: int}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-transactions.json">getTransactions</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/transactions-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
options: {
start: txhash,
limit: int,
minLedgerVersion: int,
maxLedgerVersion: int,
earliestFirst: bool,
excludeFailures: bool,
initiated: bool,
counterparty: address,
types: [string],
binary: bool
}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-trustlines.json">getTrustlines</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/trustlines-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
options: {counterparty: address, currency: string, limit: int, ledgerVersion: int}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-balances.json">getBalances</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/trustlines-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
options: {counterparty: address, currency: string, limit: int, ledgerVersion: int}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-paths.json">getPaths</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/pathfind.json">pathfind</a>
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
pathfind: {
source: {
address: address,
currencies: [{
currency: string,
counterparty: address
}]
},
destination: adjustment
}
adjustment = {address: address, amount: amount, tag?: int}
amount = {currency: string, counterparty: address, value: floatstr}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-orders.json">getOrders</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/orders-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
options: {limit: int, ledverVersion: int}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-orderbook.json">getOrderbook</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/orderbook.json">orderbook</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/orders-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
orderbook: {
base: {
currency,
counterparty
},
counter: {
currency,
counterparty
}
}
options: {limit: int, ledverVersion: int}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-settings.json">getSettings</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/settings-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
options: {ledgerVersion: int}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/get-account-info.json">getAccountInfo</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/settings-options.json">options</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
options: {ledgerVersion: int}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">preparePayment</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/payment.json">payment</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
payment: {
source: adjustment,
destination: adjustment,
paths?: string,
slippage?: strfloat,
memos?: [{
type: string,
format: string,
data: string
}],
invoiceID?: hash256,
allowPartialPayment?: bool,
noDirectRipple?: bool,
limitQuality?: bool
}
adjustment = {address: address, amount: amount, tag?: int}
amount = {currency: string, counterparty: address, value: floatstr}
instructions: {
sequence: int,
fee: floatstr,
maxFee: floatstr,
maxLedgerVersion: int,
maxLedgerVersionOffset: int
}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareTrustline</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/trustline.json">trustline</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
trustline: {
currency: string,
counterparty: address,
limit: strfloat,
qualityIn?: float,
qualityOut?: float,
allowRippling?: bool,
authorized?: bool,
frozen?: bool
}
instructions: {
sequence: int,
fee: floatstr,
maxFee: floatstr,
maxLedgerVersion: int,
maxLedgerVersionOffset: int
}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareOrder</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/order.json">order</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
order: {
direction: ("buy"|"sell"),
quantity: amount,
totalPrice: amount,
immediateOrCancel?: bool,
fillOrKill?: bool,
passive?: bool
}
amount = {currency: string, counterparty: address, value: floatstr}
instructions: {
sequence: int,
fee: floatstr,
maxFee: floatstr,
maxLedgerVersion: int,
maxLedgerVersionOffset: int
}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareOrderCancellation</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/sequence.json">sequence</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
sequence: int
instructions: {
sequence: int,
fee: floatstr,
maxFee: floatstr,
maxLedgerVersion: int,
maxLedgerVersionOffset: int
}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">prepareSettings</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/address.json">account</a>,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/settings.json">settings</a>[,
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/instructions.json">instructions</a>]
)
<a href="#" onclick="javascript:toggle(this)">+</a>
<div class="details">
settings: {
passwordSpent: bool,
requireDestinationTag: bool,
requireAuthorization: bool,
disallowIncomingXRP: bool,
disableMasterKey: bool,
enableTransactionIDTracking: bool,
noFreeze: bool,
globalFreeze: bool,
defaultRipple: bool,
emailHash: hash128,
walletLocator: hash256,
walletSize: int,
messageKey: string,
domain: string,
transferRate: float,
signers: string,
regularKey: address
}
instructions: {
sequence: int,
fee: floatstr,
maxFee: floatstr,
maxLedgerVersion: int,
maxLedgerVersionOffset: int
}
</div>
</div>
<div class="method">
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/sign.json">sign</a>(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/tx.json">txJSON</a>,
secret)
</div>
<div class="method">
submit(
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/schemas/blob.json">txBlob</a>
)
</div>
<div class="method">
generateWallet()
</div>
<a href="https://github.com/ripple/ripple-lib/blob/develop/src/api/common/errors.js">errors</a>
</body>
</html>

3
docs/samples/README Normal file
View File

@@ -0,0 +1,3 @@
Usage:
babel-node balances.js
babel-node payment.js (requires setting address and secret in source file first)

12
docs/samples/balances.js Normal file
View File

@@ -0,0 +1,12 @@
'use strict';
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
const address = 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV';
api.connect().then(() => {
api.getBalances(address).then(balances => {
console.log(JSON.stringify(balances, null, 2));
process.exit();
});
});

41
docs/samples/payment.js Normal file
View File

@@ -0,0 +1,41 @@
'use strict';
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
const address = 'INSERT ADDRESS HERE';
const secret = 'INSERT SECRET HERE';
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
const instructions = {maxLedgerVersionOffset: 5};
const payment = {
source: {
address: address,
amount: {
value: '0.01',
currency: 'XRP'
}
},
destination: {
address: 'rKmBGxocj9Abgy25J51Mk1iqFzW9aVF9Tc',
amount: {
value: '0.01',
currency: 'XRP'
}
}
};
api.connect().then(() => {
console.log('Connected...');
api.preparePayment(address, payment, instructions).then(txJSON => {
console.log('Payment transaction prepared...');
const signedTransaction = api.sign(txJSON, secret).signedTransaction;
console.log('Payment transaction signed...');
api.submit(signedTransaction).then(response => {
console.log(response);
process.exit(0);
}).catch(error => {
console.log(error);
process.exit(1);
});
});
});

104
npm-shrinkwrap.json generated
View File

@@ -1,20 +1,20 @@
{
"name": "ripple-lib",
"version": "0.12.6",
"version": "0.13.0-rc4",
"npm-shrinkwrap-version": "5.4.0",
"node-version": "v0.12.6",
"node-version": "v0.12.7",
"dependencies": {
"async": {
"version": "0.9.2",
"resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz"
},
"babel-runtime": {
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.3.tgz",
"version": "5.8.20",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-5.8.20.tgz",
"dependencies": {
"core-js": {
"version": "0.9.18",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-0.9.18.tgz"
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.0.1.tgz"
}
}
},
@@ -22,25 +22,109 @@
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.0.7.tgz"
},
"es6-promisify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-2.0.0.tgz",
"dependencies": {
"es6-promise": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-2.3.0.tgz"
}
}
},
"extend": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz"
},
"https-proxy-agent": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz",
"dependencies": {
"agent-base": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-2.0.0.tgz",
"dependencies": {
"semver": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
}
}
},
"debug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"dependencies": {
"ms": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz"
}
}
},
"extend": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
}
}
},
"is-my-json-valid": {
"version": "2.12.1",
"resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.12.1.tgz",
"dependencies": {
"generate-function": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz"
},
"generate-object-property": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
"dependencies": {
"is-property": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz"
}
}
},
"jsonpointer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-1.1.0.tgz"
},
"xtend": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.0.tgz"
}
}
},
"lodash": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.0.tgz"
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz"
},
"lru-cache": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.5.2.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"
}
}
},
"ripple-wallet-generator": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/ripple-wallet-generator/-/ripple-wallet-generator-1.0.3.tgz"
},
"sjcl": {
"sjcl-extended": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.3.tgz"
"resolved": "git://github.com/ripple/sjcl-extended.git#d8cf8b22e7d97193c54e1f65113e3edcf200ca17",
"dependencies": {
"sjcl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.3.tgz"
}
}
},
"ws": {
"version": "0.7.2",

View File

@@ -1,6 +1,7 @@
{
"name": "ripple-lib",
"version": "0.12.6",
"version": "0.13.0-rc4",
"license": "ISC",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
"dist/npm/*",
@@ -15,49 +16,50 @@
},
"dependencies": {
"async": "~0.9.0",
"babel-runtime": "^5.3.2",
"babel-runtime": "^5.5.4",
"bignumber.js": "^2.0.3",
"es6-promisify": "^2.0.0",
"extend": "~1.2.1",
"https-proxy-agent": "^1.0.0",
"is-my-json-valid": "^2.12.0",
"lodash": "^3.1.0",
"lru-cache": "~2.5.0",
"ripple-lib-transactionparser": "^0.5.0",
"ripple-wallet-generator": "^1.0.3",
"sjcl": "^1.0.3",
"sjcl-extended": "ripple/sjcl-extended#1.0.3",
"ws": "~0.7.1"
},
"devDependencies": {
"assert-diff": "^1.0.1",
"babel": "^5.3.3",
"babel-core": "^5.3.2",
"babel": "^5.5.4",
"babel-core": "^5.5.4",
"babel-eslint": "^3.1.23",
"babel-loader": "^5.0.0",
"coveralls": "~2.10.0",
"eslint": "^0.18.0",
"eslint": "^1.0.0",
"eslint-plugin-flowtype": "^1.0.0",
"eventemitter2": "^0.4.14",
"flow-bin": "^0.14",
"gulp": "~3.8.10",
"gulp-bump": "~0.1.13",
"gulp-clean-dest": "^0.1.0",
"gulp-filelog": "^0.4.1",
"gulp-flowtype": "^0.4.1",
"gulp-plumber": "^0.6.6",
"gulp-react": "^2.0.0",
"gulp-rename": "~1.2.0",
"gulp-uglify": "~1.1.0",
"gulp-util": "^3.0.3",
"gulp-watch": "^4.1.0",
"istanbul": "~0.3.5",
"map-stream": "~0.1.0",
"mocha": "~2.1.0",
"nock": "^0.34.1",
"webpack": "~1.5.3",
"yargs": "~1.3.1"
},
"scripts": {
"build": "gulp",
"compile": "babel -i runtime -d dist/npm/ src/",
"compile-with-source-maps": "babel -i runtime -s -t -d dist/npm/ src/",
"prepublish": "npm run compile",
"postinstall": "cd node_modules/sjcl; ./configure --with-all --compress=none; make",
"clean": "rm -rf dist/npm && rm -rf build/flow",
"typecheck": "babel --optional runtime --blacklist flow -d build/flow/ src/ && flow check",
"compile": "babel -D --optional runtime -d dist/npm/ src/",
"watch": "babel -w -D --optional runtime -d dist/npm/ src/",
"compile-with-source-maps": "babel -D --optional runtime -s -t -d dist/npm/ src/",
"prepublish": "npm run clean && npm run compile",
"test": "istanbul test _mocha",
"coveralls": "cat ./coverage/lcov.info | coveralls",
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; fi; eslint --reset -c eslintrc src/*.js",
"lint": "if ! [ -f eslintrc ]; then curl -o eslintrc 'https://raw.githubusercontent.com/ripple/javascript-style-guide/es6/eslintrc'; echo 'plugins:\n - flowtype' >> eslintrc; fi; eslint --reset -c eslintrc src/",
"perf": "./scripts/perf_test.sh"
},
"repository": {
@@ -66,6 +68,6 @@
},
"readmeFilename": "README.md",
"engines": {
"node": ">=0.10.0"
"node": ">=0.12.0"
}
}

View File

@@ -3,7 +3,7 @@
var fs = require('fs');
var Amount = require('../dist/npm').Amount;
var Ledger = require('../dist/npm/ledger').Ledger;
var Ledger = require('../dist/npm').Ledger;
function parse_options(from, flags) {
var argv = from.slice(),

View File

@@ -0,0 +1,44 @@
'use strict';
const core = require('./utils').core;
const flagIndices = core.Transaction.set_clear_flags.AccountSet;
const flags = core.Remote.flags.account_root;
const AccountFlags = {
passwordSpent: flags.PasswordSpent,
requireDestinationTag: flags.RequireDestTag,
requireAuthorization: flags.RequireAuth,
disallowIncomingXRP: flags.DisallowXRP,
disableMasterKey: flags.DisableMaster,
noFreeze: flags.NoFreeze,
globalFreeze: flags.GlobalFreeze,
defaultRipple: flags.DefaultRipple
};
const AccountFlagIndices = {
requireDestinationTag: flagIndices.asfRequireDest,
requireAuthorization: flagIndices.asfRequireAuth,
disallowIncomingXRP: flagIndices.asfDisallowXRP,
disableMasterKey: flagIndices.asfDisableMaster,
enableTransactionIDTracking: flagIndices.asfAccountTxnID,
noFreeze: flagIndices.asfNoFreeze,
globalFreeze: flagIndices.asfGlobalFreeze,
defaultRipple: flagIndices.asfDefaultRipple
};
const AccountFields = {
EmailHash: {name: 'emailHash', encoding: 'hex',
length: 32, defaults: '0'},
WalletLocator: {name: 'walletLocator', encoding: 'hex',
length: 64, defaults: '0'},
WalletSize: {name: 'walletSize', defaults: 0},
MessageKey: {name: 'messageKey'},
Domain: {name: 'domain', encoding: 'hex'},
TransferRate: {name: 'transferRate', defaults: 0, shift: 9},
Signers: {name: 'signers'}
};
module.exports = {
AccountFields,
AccountFlagIndices,
AccountFlags
};

89
src/api/common/errors.js Normal file
View File

@@ -0,0 +1,89 @@
/* eslint-disable valid-jsdoc */
'use strict';
/**
* Base class for all errors
*/
function RippleError(message) {
this.message = message;
}
RippleError.prototype = new Error();
RippleError.prototype.name = 'RippleError';
function ValidationError(message) {
this.message = message;
}
ValidationError.prototype = new RippleError();
ValidationError.prototype.name = 'ValidationError';
/**
* Timeout, disconnects and too busy
*/
function NetworkError(message) {
this.message = message;
}
NetworkError.prototype = new RippleError();
NetworkError.prototype.name = 'NetworkError';
/**
* Failed transactions, no paths found, not enough balance, etc.
*/
function RippledNetworkError(message) {
this.message = message !== undefined ? message : 'Cannot connect to rippled';
}
RippledNetworkError.prototype = new NetworkError();
/**
* Failed transactions, no paths found, not enough balance, etc.
*/
function TransactionError(message) {
this.message = message;
}
TransactionError.prototype = new RippleError();
TransactionError.prototype.name = 'TransactionError';
/**
* Asset could not be found
*/
function NotFoundError(message) {
this.message = message;
}
NotFoundError.prototype = new RippleError();
NotFoundError.prototype.name = 'NotFoundError';
function MissingLedgerHistoryError(message) {
this.message = message ||
'Server is missing ledger history in the specified range';
}
MissingLedgerHistoryError.prototype = new RippleError();
MissingLedgerHistoryError.prototype.name = 'MissingLedgerHistoryError';
/**
* Request timed out
*/
function TimeOutError(message) {
this.message = message;
}
TimeOutError.prototype = new RippleError();
TimeOutError.prototype.name = 'TimeOutError';
/**
* API logic failed to do what it intended
*/
function ApiError(message) {
this.message = message;
}
ApiError.prototype = new RippleError();
ApiError.prototype.name = 'ApiError';
module.exports = {
ValidationError: ValidationError,
NetworkError: NetworkError,
TransactionError: TransactionError,
RippledNetworkError: RippledNetworkError,
NotFoundError: NotFoundError,
MissingLedgerHistoryError: MissingLedgerHistoryError,
TimeOutError: TimeOutError,
ApiError: ApiError,
RippleError: RippleError
};

18
src/api/common/index.js Normal file
View File

@@ -0,0 +1,18 @@
'use strict';
const utils = require('./utils');
module.exports = {
core: utils.core,
constants: require('./constants'),
errors: require('./errors'),
validate: require('./validate'),
dropsToXrp: utils.dropsToXrp,
xrpToDrops: utils.xrpToDrops,
toRippledAmount: utils.toRippledAmount,
composeAsync: utils.composeAsync,
wrapCatch: utils.wrapCatch,
convertExceptions: utils.convertExceptions,
convertKeysFromSnakeCaseToCamelCase:
utils.convertKeysFromSnakeCaseToCamelCase,
promisify: utils.promisify
};

View File

@@ -0,0 +1,69 @@
/* @flow */
'use strict';
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const validator = require('is-my-json-valid');
const core = require('./utils').core;
const ValidationError = require('./errors').ValidationError;
let SCHEMAS = {};
function isValidAddress(address) {
return core.UInt160.is_valid(address);
}
function isValidLedgerHash(ledgerHash) {
return core.UInt256.is_valid(ledgerHash);
}
function loadSchema(filepath: string): {} {
try {
return JSON.parse(fs.readFileSync(filepath, 'utf8'));
} catch (e) {
throw new Error('Failed to parse schema: ' + filepath);
}
}
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
function loadSchemas(dir) {
const filenames = fs.readdirSync(dir).filter(name => endsWith(name, '.json'));
const schemas = filenames.map(name => loadSchema(path.join(dir, name)));
return _.indexBy(schemas, 'title');
}
function formatSchemaError(error) {
return error.field + ' ' + error.message
+ (error.value ? ' (' + JSON.stringify(error.value) + ')' : '');
}
function formatSchemaErrors(errors) {
return errors.map(formatSchemaError).join(', ');
}
function schemaValidate(schemaName: string, object: any): void {
const formats = {address: isValidAddress,
ledgerHash: isValidLedgerHash};
const options = {schemas: SCHEMAS, formats: formats,
verbose: true, greedy: true};
const schema = SCHEMAS[schemaName];
if (schema === undefined) {
throw new Error('schema not found for: ' + schemaName);
}
const validate = validator(schema, options);
const isValid = validate(object);
if (!isValid) {
throw new ValidationError(formatSchemaErrors(validate.errors));
}
}
SCHEMAS = loadSchemas(path.join(__dirname, './schemas'));
module.exports = {
schemaValidate: schemaValidate,
loadSchema: loadSchema,
SCHEMAS: SCHEMAS
};

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "address",
"description": "A Ripple account address",
"type": "string",
"format": "address",
"pattern": "^r[1-9A-HJ-NP-Za-km-z]{25,34}$"
}

View File

@@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "adjustment",
"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"
}
},
"required": ["address", "amount"],
"additionalProperties": false
}

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "amount",
"description": "An Amount on the Ripple Protocol, used also for XRP in the ripple-rest API",
"allOf": [
{"$ref": "amountbase"},
{"required": ["value"]}
]
}

View File

@@ -0,0 +1,44 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "amountbase",
"description": "Base class for amount and issue",
"type": "object",
"properties": {
"value": {
"description": "The quantity of the currency, denoted as a string to retain floating point precision",
"$ref": "value"
},
"currency": {
"description": "The three-character code or hex string used to denote currencies",
"$ref": "currency"
},
"counterparty": {
"description": "The Ripple account address of the currency's issuer or gateway",
"$ref": "address"
}
},
"additionalProperties": false,
"required": ["currency"],
"oneOf": [
{
"properties": {
"currency": {
"not": {
"enum": ["XRP"]
}
}
},
"required": ["counterparty"]
},
{
"properties": {
"currency": {
"enum": ["XRP"]
}
},
"not": {
"required": ["counterparty"]
}
}
]
}

View File

@@ -0,0 +1,44 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "balance",
"description": "Balance amount",
"type": "object",
"properties": {
"value": {
"description": "The balance on the trustline",
"$ref": "signed-value"
},
"currency": {
"description": "The three-character code or hex string used to denote currencies",
"$ref": "currency"
},
"counterparty": {
"description": "The Ripple account address of the currency's issuer or gateway",
"$ref": "address"
}
},
"additionalProperties": false,
"required": ["currency", "value"],
"oneOf": [
{
"properties": {
"currency": {
"not": {
"enum": ["XRP"]
}
}
},
"required": ["counterparty"]
},
{
"properties": {
"currency": {
"enum": ["XRP"]
}
},
"not": {
"required": ["counterparty"]
}
}
]
}

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "blob",
"description": "An uppercase hex string representation of a transaction",
"type": "string",
"minLength": "1",
"pattern": "^[0-9A-F]*$"
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "currency",
"description": "The three-character code or hex string used to denote currencies",
"type": "string",
"pattern": "^([a-zA-Z0-9<>(){}[\\]|?!@#$%^&*]{3}|[A-F0-9]{40})$"
}

View File

@@ -0,0 +1,21 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getAccountInfo",
"type": "object",
"properties": {
"sequence": {"$ref": "sequence"},
"xrpBalance": {"$ref": "value"},
"ownerCount": {"type": "integer", "minimum": 0},
"previousInitiatedTransactionID": {"$ref": "hash256"},
"previousAffectingTransactionID": {"$ref": "hash256"},
"previousAffectingTransactionLedgerVersion": {"$ref": "ledgerVersion"}
},
"required": [
"sequence",
"xrpBalance",
"ownerCount",
"previousAffectingTransactionID",
"previousAffectingTransactionLedgerVersion"
],
"additionalProperties": false
}

View File

@@ -0,0 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getBalances",
"type": "array",
"items": {"$ref": "balance"}
}

View File

@@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getOrderbook",
"type": "object",
"properties": {
"bids": {"$ref": "orderbookOrders"},
"asks": {"$ref": "orderbookOrders"}
},
"required": ["bids", "asks"],
"additionalProperties": false
}

View File

@@ -0,0 +1,23 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getOrders",
"type": "array",
"items": {
"type": "object",
"properties": {
"specification": {"$ref": "order"},
"properties": {
"type": "object",
"properties": {
"maker": {"$ref": "address"},
"sequence": {"$ref": "sequence"},
"makerExchangeRate": {"$ref": "value"}
},
"required": ["maker", "sequence", "makerExchangeRate"],
"addtionalProperties": false
}
},
"required": ["specification", "properties"],
"additionalProperties": false
}
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getPaths",
"type": "array",
"items": {
"type": "object",
"properties": {
"source": {"$ref": "adjustment"},
"destination": {"$ref": "adjustment"},
"paths": {"type": "string"}
},
"required": ["source", "destination", "paths"],
"additionalProperties": false
}
}

View File

@@ -0,0 +1,49 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getServerInfo",
"type": "object",
"properties": {
"buildVersion": {"type": "string"},
"completeLedgers": {"type": "string", "pattern": "[0-9,-]+"},
"hostid": {"type": "string"},
"ioLatencyMs": {"type": "number"},
"load": {
"type": "object",
"properties": {
"job_types": {
"type": "array",
"items": {"type": "object"}
},
"threads": {"type": "number"}
}
},
"lastClose": {
"type": "object",
"properties": {
"convergeTimeS": {"type": "number"},
"proposers": {"type": "integer", "minimum": 0}
}
},
"loadFactor": {"type": "number"},
"peers": {"type": "integer", "minimum": 0},
"pubkeyNode": {"type": "string"},
"pubkeyValidator": {"type": "string"},
"serverState": {
"type": "string",
"enum": ["disconnected", "connected", "syncing", "tracking", "full", "validating", "proposing"]
},
"validatedLedger": {
"type": "object",
"properties": {
"age": {"type": "integer", "minimum": 0},
"baseFeeXrp": {"type": "number"},
"hash": {"$ref": "hash256"},
"reserveBaseXrp": {"type": "integer", "minimum": 0},
"reserveIncXrp": {"type": "integer", "minimum": 0},
"seq": {"type": "integer", "minimum": 0}
}
},
"validationQuorum": {"type": "number"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getSettings",
"type": "object",
"properties": {
"passwordSpent": {"type": "boolean"},
"requireDestinationTag": {"type": "boolean"},
"requireAuthorization": {"type": "boolean"},
"disallowIncomingXRP": {"type": "boolean"},
"disableMasterKey": {"type": "boolean"},
"enableTransactionIDTracking": {"type": "boolean"},
"noFreeze": {"type": "boolean"},
"globalFreeze": {"type": "boolean"},
"defaultRipple": {"type": "boolean"},
"emailHash": {
"oneOf": [
{"type": "null"},
{"$ref": "hash128"}
]
},
"walletLocator": {
"oneOf": [
{"type": "null"},
{"$ref": "hash256"}
]
},
"walletSize": {"type": ["integer", "null"]},
"messageKey": {"type": "string"},
"domain": {"type": "string"},
"transferRate": {
"oneOf": [
{"type": "null"},
{"type": "number", "minimum": 1, "maximum": 4.294967295}
]
},
"signers": {"type": "string"},
"regularKey": {"$ref": "address"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getTransaction",
"oneOf": [
{"$ref": "paymentTransaction"},
{"$ref": "orderTransaction"},
{"$ref": "orderCancellationTransaction"},
{"$ref": "trustlineTransaction"},
{"$ref": "settingsTransaction"}
]
}

View File

@@ -0,0 +1,6 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getTransactions",
"type": "array",
"items": {"$ref": "getTransaction"}
}

View File

@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getTrustlines",
"type": "array",
"items": {
"properties": {
"specification": {"$ref": "trustline"},
"counterparty": {
"properties": {
"limit": {"$ref": "value"},
"ripplingDisabled": {"type": "boolean"},
"frozen": {"type": "boolean"},
"authorized": {"type": "boolean"}
},
"required": ["limit"],
"additionalProperties": false
},
"state": {
"properties": {
"balance": {"$ref": "value"}
},
"required": ["balance"],
"additionalProperties": false
}
},
"required": ["specification", "counterparty", "state"],
"additionalProperties": false
}
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "hash128",
"description": "The hex representation of a 128-bit hash",
"type": "string",
"pattern": "^[A-F0-9]{32}$"
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "hash256",
"description": "The hex representation of a 256-bit hash",
"type": "string",
"pattern": "^[A-F0-9]{64}$"
}

View File

@@ -0,0 +1,42 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "instructions",
"description": "Instructions for executing a transaction",
"type": "object",
"properties": {
"sequence": {
"description": "The sequence number, relative to the initiating account, of this transaction.",
"$ref": "sequence"
},
"fee": {
"description": "Fixed Fee",
"$ref": "value"
},
"maxFee": {
"description": "Max Fee",
"$ref": "value"
},
"maxLedgerVersion": {
"description": "Highest ledger version number that a transaction can appear in.",
"$ref": "ledgerVersion"
},
"maxLedgerVersionOffset": {
"description": "Offset from current legder version to highest ledger version that a transaction can appear in.",
"type": "integer",
"minimum": 0
}
},
"additionalProperties": false,
"not": {
"anyOf": [
{
"description": "fee and maxFee are mutually exclusive",
"required": ["fee", "maxFee"]
},
{
"description": "maxLedgerVersion and maxLedgerVersionOffset are mutually exclusive",
"required": ["maxLedgerVersion", "maxLedgerVersionOffset"]
}
]
}
}

View File

@@ -0,0 +1,9 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "issue",
"description": "A currency-counterparty pair, or just currency if it's XRP",
"allOf": [
{"$ref": "amountbase"},
{"not": {"required": ["value"]}}
]
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ledgerVersion",
"description": "A ledger version number",
"type": "integer",
"minimum": 1
}

View File

@@ -0,0 +1,22 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "memo",
"description": "Memo objects represent arbitrary data that can be included in a transaction",
"type": "object",
"properties": {
"type": {
"pattern": "^[A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=%]*$"
},
"format": {
"pattern": "^[A-Za-z0-9\\-._~:/?#[\\]@!$&'()*+,;=%]*$"
},
"data": {
"type": "string"
}
},
"additionalProperties": false,
"anyOf": [
{"required": ["data"]},
{"required": ["type"]}
]
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orderCancellationTransaction",
"type": "object",
"properties": {
"type": {"enum": ["orderCancellation"]},
"specification": {"$ref": "orderCancellation"},
"outcome": {"$ref": "outcome"},
"id": {"$ref": "hash256"},
"address": {"$ref": "address"},
"sequence": {"$ref": "sequence"}
},
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
"additionalProperties": false
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orderCancellation",
"type": "object",
"properties": {
"orderSequence": {"$ref": "sequence"}
},
"required": ["orderSequence"],
"additionalProperties": false
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "order-change",
"type": "object",
"properties": {
"direction": {
"type": "string",
"enum": ["buy", "sell"]
},
"quantity": {"$ref": "balance"},
"totalPrice": {"$ref": "balance"},
"sequence": {"$ref": "sequence"},
"status": {"enum": ["created", "open", "closed", "canceled"]}
},
"required": ["direction", "quantity", "totalPrice", "sequence", "status"],
"additionalProperties": false
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orderTransaction",
"type": "object",
"properties": {
"type": {"enum": ["order"]},
"specification": {"$ref": "order"},
"outcome": {"$ref": "outcome"},
"id": {"$ref": "hash256"},
"address": {"$ref": "address"},
"sequence": {"$ref": "sequence"}
},
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
"additionalProperties": false
}

View File

@@ -0,0 +1,25 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "order",
"type": "object",
"properties": {
"direction": {
"type": "string",
"enum": ["buy", "sell"]
},
"quantity": {"$ref": "amount"},
"totalPrice": {"$ref": "amount"},
"immediateOrCancel": {"type": "boolean"},
"fillOrKill": {"type": "boolean"},
"passive": {
"description": "If enabled, the offer will not consume offers that exactly match it, and instead becomes an Offer node in the ledger. It will still consume offers that cross it.",
"type": "boolean"
}
},
"required": ["direction", "quantity", "totalPrice"],
"additionalProperties": false,
"not": {
"description": "immediateOrCancel and fillOrKill are mutually exclusive",
"required": ["immediateOrCancel", "fillOrKill"]
}
}

View File

@@ -0,0 +1,32 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orderbookOrders",
"type": "array",
"items": {
"type": "object",
"properties": {
"specification": {"$ref": "order"},
"properties": {
"type": "object",
"properties": {
"maker": {"$ref": "address"},
"sequence": {"$ref": "sequence"},
"makerExchangeRate": {"$ref": "value"}
},
"required": ["maker", "sequence", "makerExchangeRate"],
"addtionalProperties": false
},
"state": {
"type": "object",
"properties": {
"fundedAmount": {"$ref": "amount"},
"priceOfFundedAmount": {"$ref": "amount"}
},
"required": ["fundedAmount", "priceOfFundedAmount"],
"additionalProperties": false
}
},
"required": ["specification", "properties"],
"additionalProperties": false
}
}

View File

@@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orderbook",
"type": "object",
"properties": {
"base": {"$ref": "issue"},
"counter": {"$ref": "issue"}
},
"required": ["base", "counter"],
"additionalProperties": false
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "orders-options",
"description": "Options for getOrders and getOrderbook",
"type": "object",
"properties": {
"limit": {
"type": "integer",
"minimum": 1
},
"ledgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "outcome",
"type": "object",
"properties": {
"result": {"type": "string"},
"timestamp": {"type": "string"},
"fee": {"$ref": "value"},
"balanceChanges": {
"type": "object",
"description": "Key is the ripple address; value is an array of changes",
"additionalProperties": {
"type": "array",
"items": {"$ref": "balance"}
}
},
"orderbookChanges": {
"type": "object",
"description": "Key is the maker's ripple address; value is an array of changes",
"additionalProperties": {
"type": "array",
"items": {"$ref": "order-change"}
}
},
"ledgerVersion": {"$ref": "ledgerVersion"},
"indexInLedger": {"type": "integer", "minimum": 0}
},
"required": ["result", "fee", "balanceChanges",
"orderbookChanges", "ledgerVersion", "indexInLedger"],
"additionalProperties": false
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "pathfind",
"type": "object",
"properties": {
"source": {
"type": "object",
"properties": {
"address": {"$ref": "address"},
"currencies": {
"type": "array",
"items": {
"type": "object",
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"}
},
"required": ["currency"],
"additionalProperties": false
},
"uniqueItems": true
}
},
"additionalProperties": false,
"required": ["address"]
},
"destination": {"$ref": "adjustment"}
},
"required": ["source", "destination"],
"additionalProperties": false
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "paymentTransaction",
"type": "object",
"properties": {
"type": {"enum": ["payment"]},
"specification": {"$ref": "payment"},
"outcome": {"$ref": "outcome"},
"id": {"$ref": "hash256"},
"address": {"$ref": "address"},
"sequence": {"$ref": "sequence"}
},
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
"additionalProperties": false
}

View File

@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "payment",
"type": "object",
"properties": {
"source": {"$ref": "adjustment"},
"destination": {"$ref": "adjustment"},
"paths": {"type": "string"},
"memos": {
"type": "array",
"items": {
"$ref": "memo"
}
},
"invoiceID": {
"description": "A 256-bit hash that can be used to identify a particular payment",
"$ref": "hash256"
},
"allowPartialPayment": {
"description": "A boolean that, if set to true, indicates that this payment should go through even if the whole amount cannot be delivered because of a lack of liquidity or funds in the source_account account",
"type": "boolean"
},
"noDirectRipple": {
"description": "A boolean that can be set to true if paths are specified and the sender would like the Ripple Network to disregard any direct paths from the source_account to the destination_account. This may be used to take advantage of an arbitrage opportunity or by gateways wishing to issue balances from a hot wallet to a user who has mistakenly set a trustline directly to the hot wallet",
"type": "boolean"
},
"limitQuality": {"type": "boolean"}
},
"required": ["source", "destination"],
"additionalProperties": false
}

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "quality",
"description": "Ratio for incoming/outgoing transit fees.",
"type": "number",
"minimum": 0.000000001,
"maximum": 4.294967295
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "sequence",
"description": "An account transaction sequence number",
"type": "integer",
"minimum": 1
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "settings-options",
"description": "Options for getSettings and getAccountInfo",
"type": "object",
"properties": {
"ledgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "settingsTransaction",
"type": "object",
"properties": {
"type": {"enum": ["settings"]},
"specification": {"$ref": "getSettings"},
"outcome": {"$ref": "outcome"},
"id": {"$ref": "hash256"},
"address": {"$ref": "address"},
"sequence": {"$ref": "sequence"}
},
"required": ["type", "id", "address", "sequence", "specification"],
"additionalProperties": false
}

View File

@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "settings",
"allOf": [
{
"$ref": "getSettings"
},
{
"minProperties": 1,
"maxProperties": 1
}
]
}

View File

@@ -0,0 +1,14 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "sign",
"type": "object",
"properties": {
"signedTransaction": {
"type": "string",
"pattern": "^[A-F0-9]+$"
},
"id": {"$ref": "hash256"}
},
"required": ["signedTransaction", "id"],
"additionalProperties": false
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "value",
"description": "A string representation of a floating point number",
"type": "string",
"pattern": "[-]?^[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?$"
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "submit",
"type": "object",
"properties": {
"success": {"type": "boolean"},
"engineResult": {"type": "string"},
"engineResultCode": {"type": "integer"},
"engineResultMessage": {"type": "string"},
"txBlob": {"type": "string"},
"txJson": {"type": "object"}
},
"required": ["success", "engineResult", "engineResultCode"],
"additionalProperties": false
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "timestamp",
"description": "An ISO 8601 combined date and time timestamp",
"type": "string",
"pattern": "^$|^[0-9]{4}-[0-1][0-9]-[0-3][0-9]T(2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9](Z|[+](2[0-3]|[01][0-9]):[0-5][0-9])$"
}

View File

@@ -0,0 +1,11 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "transaction-options",
"description": "Options for getTransaction",
"type": "object",
"properties": {
"minLedgerVersion": {"$ref": "ledgerVersion"},
"maxLedgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,40 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "transactions-options",
"description": "Options for getTransactions",
"type": "object",
"properties": {
"start": {"$ref": "hash256"},
"limit": {
"type": "integer",
"minimum": 1
},
"minLedgerVersion": {"$ref": "ledgerVersion"},
"maxLedgerVersion": {"$ref": "ledgerVersion"},
"earliestFirst": {"type": "boolean"},
"excludeFailures": {"type": "boolean"},
"initiated": {"type": "boolean"},
"counterparty": {"$ref": "address"},
"types": {
"type": "array",
"items": {
"enum": [
"payment",
"trustline",
"order",
"orderCancellation",
"settings"
]
}
},
"binary": {"type": "boolean"}
},
"additionalProperties": false,
"not": {
"anyOf": [
{"required": ["incoming", "outgoing"]},
{"required": ["start", "minLedgerVersion"]},
{"required": ["start", "maxLedgerVersion"]}
]
}
}

View File

@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "trustlineTransaction",
"type": "object",
"properties": {
"type": {"enum": ["trustline"]},
"specification": {"$ref": "trustline"},
"outcome": {"$ref": "outcome"},
"id": {"$ref": "hash256"},
"address": {"$ref": "address"},
"sequence": {"$ref": "sequence"}
},
"required": ["type", "id", "address", "sequence", "specification", "outcome"],
"additionalProperties": false
}

View File

@@ -0,0 +1,17 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "trustline",
"type": "object",
"properties": {
"currency": {"$ref": "currency"},
"counterparty": {"$ref": "address"},
"limit": {"$ref": "value"},
"qualityIn": {"$ref": "quality"},
"qualityOut": {"$ref": "quality"},
"ripplingDisabled": {"type": "boolean"},
"authorized": {"type": "boolean"},
"frozen": {"type": "boolean"}
},
"required": ["currency", "counterparty", "limit"],
"additionalProperties": false
}

View File

@@ -0,0 +1,16 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "trustlines-options",
"description": "Options for getTrustlines and getBalances",
"type": "object",
"properties": {
"counterparty": {"$ref": "address"},
"currency": {"$ref": "currency"},
"limit": {
"type": "integer",
"minimum": 1
},
"ledgerVersion": {"$ref": "ledgerVersion"}
},
"additionalProperties": false
}

View File

@@ -0,0 +1,10 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "tx",
"description": "An object in rippled txJSON format",
"type": "object",
"properties": {
"Account": {"$ref": "address"}
},
"required": ["Account"]
}

View File

@@ -0,0 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "uint32",
"description": "A 32-bit unsigned integer",
"type": "integer",
"minimum": 0,
"maximum": 4294967295
}

View File

@@ -0,0 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "value",
"description": "A string representation of a non-negative floating point number",
"type": "string",
"pattern": "^[0-9]*[.]?[0-9]+([eE][-+]?[0-9]+)?$"
}

103
src/api/common/utils.js Normal file
View File

@@ -0,0 +1,103 @@
/* @flow */
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const core = require('../../core');
const errors = require('./errors');
const es6promisify = require('es6-promisify');
type Amount = {currency: string, issuer: string, value: string}
function dropsToXrp(drops: string): string {
return (new BigNumber(drops)).dividedBy(1000000.0).toString();
}
function xrpToDrops(xrp: string): string {
return (new BigNumber(xrp)).times(1000000.0).floor().toString();
}
function toRippledAmount(amount: Amount): string|Amount {
if (amount.currency === 'XRP') {
return xrpToDrops(amount.value);
}
return {
currency: amount.currency,
issuer: amount.counterparty ? amount.counterparty : amount.issuer,
value: amount.value
};
}
type AsyncFunction = (...x: any) => void
function wrapCatch(asyncFunction: AsyncFunction): AsyncFunction {
return function() {
try {
asyncFunction.apply(this, arguments);
} catch (error) {
const callback = arguments[arguments.length - 1];
callback(error);
}
};
}
type Callback = (err: any, data: any) => void
type Wrapper = (data: any) => any
function composeAsync(wrapper: Wrapper, callback: Callback): Callback {
return function(error, data) {
if (error) {
callback(error);
return;
}
let result;
try {
result = wrapper(data);
} catch (exception) {
callback(exception);
return;
}
callback(null, result);
};
}
function convertExceptions<T>(f: () => T): () => T {
return function() {
try {
return f.apply(this, arguments);
} catch (error) {
throw new errors.ApiError(error.message);
}
};
}
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
if (typeof obj === 'object') {
let newKey;
return _.reduce(obj, (result, value, key) => {
newKey = key;
if (FINDSNAKE.test(key)) {
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase());
}
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value);
return result;
}, {});
}
return obj;
}
function promisify<T>(asyncFunction: AsyncFunction): Function {
return es6promisify(wrapCatch(asyncFunction));
}
module.exports = {
core,
dropsToXrp,
xrpToDrops,
toRippledAmount,
composeAsync,
wrapCatch,
convertExceptions,
convertKeysFromSnakeCaseToCamelCase,
promisify
};

View File

@@ -0,0 +1,79 @@
/* @flow */
'use strict';
const _ = require('lodash');
const core = require('./utils').core;
const ValidationError = require('./errors').ValidationError;
const schemaValidate = require('./schema-validator').schemaValidate;
function error(text) {
return new ValidationError(text);
}
function validateAddressAndSecret(obj: {address: string, secret: string}): void {
const address = obj.address;
const secret = obj.secret;
schemaValidate('address', address);
if (!secret) {
throw error('Parameter missing: secret');
}
try {
core.Seed.from_json(secret).get_key(address);
} catch (exception) {
throw error('secret does not match address');
}
}
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');
}
}
function validateLedgerRange(options) {
if (!_.isUndefined(options.minLedgerVersion)
&& !_.isUndefined(options.maxLedgerVersion)) {
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
throw error('minLedgerVersion must not be greater than maxLedgerVersion');
}
}
}
function validateOptions(schema, options) {
schemaValidate(schema, options);
validateLedgerRange(options);
}
module.exports = {
address: _.partial(schemaValidate, 'address'),
addressAndSecret: validateAddressAndSecret,
secret: validateSecret,
currency: _.partial(schemaValidate, 'currency'),
identifier: _.partial(schemaValidate, 'hash256'),
sequence: _.partial(schemaValidate, 'sequence'),
order: _.partial(schemaValidate, 'order'),
orderbook: _.partial(schemaValidate, 'orderbook'),
payment: _.partial(schemaValidate, 'payment'),
pathfind: _.partial(schemaValidate, 'pathfind'),
settings: _.partial(schemaValidate, 'settings'),
trustline: _.partial(schemaValidate, 'trustline'),
txJSON: _.partial(schemaValidate, 'tx'),
blob: _.partial(schemaValidate, 'blob'),
getTransactionsOptions: _.partial(validateOptions, 'transactions-options'),
getSettingsOptions: _.partial(validateOptions, 'settings-options'),
getAccountInfoOptions: _.partial(validateOptions, 'settings-options'),
getTrustlinesOptions: _.partial(validateOptions, 'trustlines-options'),
getBalancesOptions: _.partial(validateOptions, 'trustlines-options'),
getOrdersOptions: _.partial(validateOptions, 'orders-options'),
getOrderbookOptions: _.partial(validateOptions, 'orders-options'),
getTransactionOptions: _.partial(validateOptions, 'transaction-options'),
options: _.partial(validateOptions, 'options'),
instructions: _.partial(schemaValidate, 'instructions')
};

75
src/api/index.js Normal file
View File

@@ -0,0 +1,75 @@
/* @flow */
'use strict';
const _ = require('lodash');
const common = require('./common');
const server = require('./server/server');
const connect = server.connect;
const disconnect = server.disconnect;
const getServerInfo = server.getServerInfo;
const getFee = server.getFee;
const isConnected = server.isConnected;
const getLedgerVersion = server.getLedgerVersion;
const getTransaction = require('./ledger/transaction');
const getTransactions = require('./ledger/transactions');
const getTrustlines = require('./ledger/trustlines');
const getBalances = require('./ledger/balances');
const getPaths = require('./ledger/pathfind');
const getOrders = require('./ledger/orders');
const getOrderbook = require('./ledger/orderbook');
const getSettings = require('./ledger/settings');
const getAccountInfo = require('./ledger/accountinfo');
const preparePayment = require('./transaction/payment');
const prepareTrustline = require('./transaction/trustline');
const prepareOrder = require('./transaction/order');
const prepareOrderCancellation = require('./transaction/ordercancellation');
const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const submit = require('./transaction/submit');
const errors = require('./common').errors;
const convertExceptions = require('./common').convertExceptions;
const generateWallet = convertExceptions(common.core.Wallet.generate);
function RippleAPI(options: {}) {
const _options = _.assign({}, options, {automatic_resubmission: false});
this.remote = new common.core.Remote(_options);
}
RippleAPI.prototype = {
connect,
disconnect,
isConnected,
getServerInfo,
getFee,
getLedgerVersion,
getTransaction,
getTransactions,
getTrustlines,
getBalances,
getPaths,
getOrders,
getOrderbook,
getSettings,
getAccountInfo,
preparePayment,
prepareTrustline,
prepareOrder,
prepareOrderCancellation,
prepareSettings,
sign,
submit,
generateWallet,
errors
};
// these are exposed only for use by unit tests; they are not part of the API
RippleAPI._PRIVATE = {
common: common,
ledgerUtils: require('./ledger/utils'),
schemaValidator: require('./common/schema-validator')
};
module.exports = RippleAPI;

View File

@@ -0,0 +1,38 @@
/* @flow */
'use strict';
const utils = require('./utils');
const removeUndefined = require('./parse/utils').removeUndefined;
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
function formatAccountInfo(response) {
const data = response.account_data;
return removeUndefined({
sequence: data.Sequence,
xrpBalance: utils.common.dropsToXrp(data.Balance),
ownerCount: data.OwnerCount,
previousInitiatedTransactionID: data.AccountTxnID,
previousAffectingTransactionID: data.PreviousTxnID,
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq
});
}
function getAccountInfoAsync(account, options, callback) {
validate.address(account);
validate.getAccountInfoOptions(options);
const request = {
account: account,
ledger: options.ledgerVersion
};
this.remote.requestAccountInfo(request,
composeAsync(formatAccountInfo, callback));
}
function getAccountInfo(account: string, options={}) {
return utils.promisify(getAccountInfoAsync.bind(this))(account, options);
}
module.exports = getAccountInfo;

View File

@@ -0,0 +1,49 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const getTrustlines = require('./trustlines');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
function getTrustlineBalanceAmount(trustline) {
return {
currency: trustline.specification.currency,
counterparty: trustline.specification.counterparty,
value: trustline.state.balance
};
}
function formatBalances(balances) {
const xrpBalance = {
currency: 'XRP',
value: balances.xrp
};
return [xrpBalance].concat(
balances.trustlines.map(getTrustlineBalanceAmount));
}
function getTrustlinesAsync(account, options, callback) {
getTrustlines.bind(this)(account, options)
.then(data => callback(null, data))
.catch(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),
trustlines: _.partial(getTrustlinesAsync.bind(this), account, options)
}, composeAsync(formatBalances, callback));
}
function getBalances(account: string, options={}) {
return utils.promisify(getBalancesAsync.bind(this))(account, options);
}
module.exports = getBalances;

View File

@@ -0,0 +1,84 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const parseOrderbookOrder = require('./parse/orderbook-order');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
// account is to specify a "perspective", which affects which unfunded offers
// are returned
function getBookOffers(remote, account, ledgerVersion, limit,
takerGets, takerPays, callback) {
remote.requestBookOffers(utils.renameCounterpartyToIssuerInOrder({
taker_gets: takerGets,
taker_pays: takerPays,
ledger: ledgerVersion || 'validated',
limit: limit,
taker: account
}), composeAsync(data => data.offers, callback));
}
function isSameIssue(a, b) {
return a.currency === b.currency && a.counterparty === b.counterparty;
}
function directionFilter(direction, order) {
return order.specification.direction === direction;
}
function flipOrder(order) {
const specification = order.specification;
const flippedSpecification = {
quantity: specification.totalPrice,
totalPrice: specification.quantity,
direction: specification.direction === 'buy' ? 'sell' : 'buy'
};
const newSpecification = _.merge({}, specification, flippedSpecification);
return _.merge({}, order, {specification: newSpecification});
}
function alignOrder(base, order) {
const quantity = order.specification.quantity;
return isSameIssue(quantity, base) ? order : flipOrder(order);
}
function formatBidsAndAsks(orderbook, offers) {
// the "base" currency is the currency that you are buying or selling
// the "counter" is the currency that the "base" is priced in
// a "bid"/"ask" is an order to buy/sell the base, respectively
// for bids: takerGets = totalPrice = counter, takerPays = quantity = base
// for asks: takerGets = quantity = base, takerPays = totalPrice = counter
// quality = takerPays / takerGets; price = totalPrice / quantity
// for bids: lowest quality => lowest quantity/totalPrice => highest price
// for asks: lowest quality => lowest totalPrice/quantity => lowest price
// for both bids and asks, lowest quality is closest to mid-market
// we sort the orders so that earlier orders are closer to mid-market
const orders = _.sortBy(offers, 'quality').map(parseOrderbookOrder);
const alignedOrders = orders.map(_.partial(alignOrder, orderbook.base));
const bids = alignedOrders.filter(_.partial(directionFilter, 'buy'));
const asks = alignedOrders.filter(_.partial(directionFilter, 'sell'));
return {bids, asks};
}
function getOrderbookAsync(account, orderbook, options, callback) {
validate.address(account);
validate.orderbook(orderbook);
validate.getOrderbookOptions(options);
const getter = _.partial(getBookOffers, this.remote, account,
options.ledgerVersion, options.limit);
const getOffers = _.partial(getter, orderbook.base, orderbook.counter);
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base);
async.parallel([getOffers, getReverseOffers],
composeAsync((data) => formatBidsAndAsks(orderbook, _.flatten(data)),
callback));
}
function getOrderbook(account: string, orderbook: Object, options={}) {
return utils.promisify(getOrderbookAsync.bind(this))(
account, orderbook, options);
}
module.exports = getOrderbook;

40
src/api/ledger/orders.js Normal file
View File

@@ -0,0 +1,40 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
const parseAccountOrder = require('./parse/account-order');
function requestAccountOffers(remote, address, ledgerVersion, options,
marker, limit, callback) {
remote.requestAccountOffers({
account: address,
marker: marker,
limit: utils.clamp(limit, 10, 400),
ledger: ledgerVersion
},
composeAsync((data) => ({
marker: data.marker,
results: data.offers.map(_.partial(parseAccountOrder, address))
}), callback));
}
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);
utils.getRecursive(getter, options.limit,
composeAsync((orders) => _.sortBy(orders,
(order) => order.properties.sequence), callback));
}
function getOrders(account: string, options={}) {
return utils.promisify(getOrdersAsync.bind(this))(account, options);
}
module.exports = getOrders;

View File

@@ -0,0 +1,41 @@
/* @flow */
'use strict';
const utils = require('./utils');
const flags = utils.core.Remote.flags.offer;
const parseAmount = require('./amount');
const BigNumber = require('bignumber.js');
// TODO: remove this function once rippled provides quality directly
function computeQuality(takerGets, takerPays) {
const quotient = new BigNumber(takerPays.value).dividedBy(takerGets.value);
return quotient.toDigits(16, BigNumber.ROUND_HALF_UP).toString();
}
// rippled 'account_offers' returns a different format for orders than 'tx'
// the flags are also different
function parseAccountOrder(address: string, order: Object): Object {
const direction = (order.flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(order.taker_gets);
const takerPaysAmount = parseAmount(order.taker_pays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
const specification = utils.removeUndefined({
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((order.flags & flags.Passive) !== 0) || undefined
});
const properties = {
maker: address,
sequence: order.seq,
makerExchangeRate: computeQuality(takerGetsAmount, takerPaysAmount)
};
return {specification, properties};
}
module.exports = parseAccountOrder;

View File

@@ -0,0 +1,46 @@
/* @flow */
'use strict';
const utils = require('./utils');
type Trustline = {
account: string, limit: number, currency: string, quality_in: ?number,
quality_out: ?number, no_ripple: boolean, freeze: boolean, authorized: boolean,
limit_peer: string, no_ripple_peer: boolean, freeze_peer: boolean,
peer_authorized: boolean, balance: any
}
type TrustlineSpecification = {}
type TrustlineCounterParty = {}
type TrustlineState = {balance: number}
type AccountTrustline = {
specification: TrustlineSpecification, counterparty: TrustlineCounterParty,
state: TrustlineState
}
// rippled 'account_lines' returns a different format for
// trustlines than 'tx'
function parseAccountTrustline(trustline: Trustline): AccountTrustline {
const specification = utils.removeUndefined({
limit: trustline.limit,
currency: trustline.currency,
counterparty: trustline.account,
qualityIn: trustline.quality_in || undefined,
qualityOut: trustline.quality_out || undefined,
ripplingDisabled: trustline.no_ripple || undefined,
frozen: trustline.freeze || undefined,
authorized: trustline.authorized || undefined
});
// rippled doesn't provide the counterparty's qualities
const counterparty = utils.removeUndefined({
limit: trustline.limit_peer,
ripplingDisabled: trustline.no_ripple_peer || undefined,
frozen: trustline.freeze_peer || undefined,
authorized: trustline.peer_authorized || undefined
});
const state = {
balance: trustline.balance
};
return {specification, counterparty, state};
}
module.exports = parseAccountTrustline;

View File

@@ -0,0 +1,24 @@
/* @flow */
'use strict';
const utils = require('./utils');
type Amount = string | {currency: string, issuer: string, value: string}
type XRPAmount = {currency: string, value: string}
type IOUAmount = {currency: string, value: string, counterparty: string}
type Output = XRPAmount | IOUAmount
function parseAmount(amount: Amount): Output {
if (typeof amount === 'string') {
return {
currency: 'XRP',
value: utils.dropsToXrp(amount)
};
}
return {
currency: amount.currency,
value: amount.value,
counterparty: amount.issuer
};
}
module.exports = parseAmount;

View File

@@ -0,0 +1,12 @@
/* @flow */
'use strict';
const assert = require('assert');
function parseOrderCancellation(tx: Object): Object {
assert(tx.TransactionType === 'OfferCancel');
return {
orderSequence: tx.OfferSequence
};
}
module.exports = parseOrderCancellation;

View File

@@ -0,0 +1,28 @@
/* @flow */
'use strict';
const BigNumber = require('bignumber.js');
const AccountFields = require('./utils').constants.AccountFields;
function parseField(info, value) {
if (info.encoding === 'hex' && !info.length) {
return new Buffer(value, 'hex').toString('ascii');
}
if (info.shift) {
return (new BigNumber(value)).shift(-info.shift).toNumber();
}
return value;
}
function parseFields(data: Object): Object {
const settings = {};
for (const fieldName in AccountFields) {
const fieldValue = data[fieldName];
if (fieldValue !== undefined) {
const info = AccountFields[fieldName];
settings[info.name] = parseField(info, fieldValue);
}
}
return settings;
}
module.exports = parseFields;

View File

@@ -0,0 +1,28 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const flags = utils.core.Transaction.flags.OfferCreate;
function parseOrder(tx: Object): Object {
assert(tx.TransactionType === 'OfferCreate');
const direction = (tx.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(tx.TakerGets);
const takerPaysAmount = parseAmount(tx.TakerPays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
return utils.removeUndefined({
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((tx.Flags & flags.Passive) !== 0) || undefined,
immediateOrCancel: ((tx.Flags & flags.ImmediateOrCancel) !== 0)
|| undefined,
fillOrKill: ((tx.Flags & flags.FillOrKill) !== 0) || undefined
});
}
module.exports = parseOrder;

View File

@@ -0,0 +1,43 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const flags = utils.core.Remote.flags.offer;
const parseAmount = require('./amount');
function parseOrderbookOrder(order: Object): Object {
const direction = (order.Flags & flags.Sell) === 0 ? 'buy' : 'sell';
const takerGetsAmount = parseAmount(order.TakerGets);
const takerPaysAmount = parseAmount(order.TakerPays);
const quantity = (direction === 'buy') ? takerPaysAmount : takerGetsAmount;
const totalPrice = (direction === 'buy') ? takerGetsAmount : takerPaysAmount;
// note: immediateOrCancel and fillOrKill orders cannot enter the order book
// so we can omit those flags here
const specification = utils.removeUndefined({
direction: direction,
quantity: quantity,
totalPrice: totalPrice,
passive: ((order.Flags & flags.Passive) !== 0) || undefined
});
const properties = {
maker: order.Account,
sequence: order.Sequence,
makerExchangeRate: utils.adjustQualityForXRP(order.quality,
takerGetsAmount.currency, takerPaysAmount.currency)
};
const takerGetsFunded = order.taker_gets_funded ?
parseAmount(order.taker_gets_funded) : undefined;
const takerPaysFunded = order.taker_pays_funded ?
parseAmount(order.taker_pays_funded) : undefined;
const available = utils.removeUndefined({
fundedAmount: takerGetsFunded,
priceOfFundedAmount: takerPaysFunded
});
const state = _.isEmpty(available) ? undefined : available;
return utils.removeUndefined({specification, properties, state});
}
module.exports = parseOrderbookOrder;

View File

@@ -0,0 +1,28 @@
/* @flow */
'use strict';
const _ = require('lodash');
const parseAmount = require('./amount');
function parsePaths(paths) {
return paths.map(steps => steps.map(step =>
_.omit(step, ['type', 'type_hex'])));
}
function parsePathfind(sourceAddress: string,
destinationAmount: Object, pathfindResult: Object): Object {
return pathfindResult.alternatives.map(function(alternative) {
return {
source: {
address: sourceAddress,
amount: parseAmount(alternative.source_amount)
},
destination: {
address: pathfindResult.destination_account,
amount: destinationAmount
},
paths: JSON.stringify(parsePaths(alternative.paths_computed))
};
});
}
module.exports = parsePathfind;

View File

@@ -0,0 +1,67 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const Transaction = utils.core.Transaction;
function isPartialPayment(tx) {
return (tx.Flags & Transaction.flags.Payment.PartialPayment) !== 0;
}
function isNoDirectRipple(tx) {
return (tx.Flags & Transaction.flags.Payment.NoRippleDirect) !== 0;
}
function isQualityLimited(tx) {
return (tx.Flags & Transaction.flags.Payment.LimitQuality) !== 0;
}
function parsePaymentMemos(tx) {
if (!Array.isArray(tx.Memos) || tx.Memos.length === 0) {
return undefined;
}
return tx.Memos.map((m) => {
return utils.removeUndefined({
type: m.Memo.parsed_memo_type,
format: m.Memo.parsed_memo_format,
data: m.Memo.parsed_memo_data
});
});
}
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
}
function parsePayment(tx: Object): Object {
assert(tx.TransactionType === 'Payment');
const source = {
address: tx.Account,
amount: removeGenericCounterparty(
parseAmount(tx.SendMax || tx.Amount), tx.Account),
tag: tx.SourceTag
};
const destination = {
address: tx.Destination,
amount: removeGenericCounterparty(parseAmount(tx.Amount), tx.Destination),
tag: tx.DestinationTag
};
return utils.removeUndefined({
source: utils.removeUndefined(source),
destination: utils.removeUndefined(destination),
memos: parsePaymentMemos(tx),
invoiceID: tx.InvoiceID,
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
allowPartialPayment: isPartialPayment(tx) || undefined,
noDirectRipple: isNoDirectRipple(tx) || undefined,
limitQuality: isQualityLimited(tx) || undefined
});
}
module.exports = parsePayment;

View File

@@ -0,0 +1,61 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const AccountFlags = require('./utils').constants.AccountFlags;
const parseFields = require('./fields');
function getAccountRootModifiedNode(tx: Object) {
const modifiedNodes = tx.meta.AffectedNodes.filter(node =>
node.ModifiedNode.LedgerEntryType === 'AccountRoot');
assert(modifiedNodes.length === 1);
return modifiedNodes[0].ModifiedNode;
}
function parseFlags(tx: Object) {
const settings = {};
if (tx.TransactionType !== 'AccountSet') {
return settings;
}
const node = getAccountRootModifiedNode(tx);
const oldFlags = _.get(node.PreviousFields, 'Flags');
const newFlags = _.get(node.FinalFields, 'Flags');
if (oldFlags !== undefined && newFlags !== undefined) {
const changedFlags = oldFlags ^ newFlags;
const setFlags = newFlags & changedFlags;
const clearedFlags = oldFlags & changedFlags;
_.forEach(AccountFlags, (flagValue, flagName) => {
if (setFlags & flagValue) {
settings[flagName] = true;
} else if (clearedFlags & flagValue) {
settings[flagName] = false;
}
});
}
// enableTransactionIDTracking requires a special case because it
// does not affect the Flags field; instead it adds/removes a field called
// "AccountTxnID" to/from the account root.
const oldField = _.get(node.PreviousFields, 'AccountTxnID');
const newField = _.get(node.FinalFields, 'AccountTxnID');
if (newField && !oldField) {
settings.enableTransactionIDTracking = true;
} else if (oldField && !newField) {
settings.enableTransactionIDTracking = false;
}
return settings;
}
function parseSettings(tx: Object) {
const txType = tx.TransactionType;
assert(txType === 'AccountSet' || txType === 'SetRegularKey');
const regularKey = tx.RegularKey ? {regularKey: tx.RegularKey} : {};
return _.assign(regularKey, parseFlags(tx), parseFields(tx));
}
module.exports = parseSettings;

View File

@@ -0,0 +1,46 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parsePayment = require('./payment');
const parseTrustline = require('./trustline');
const parseOrder = require('./order');
const parseOrderCancellation = require('./cancellation');
const parseSettings = require('./settings');
function parseTransactionType(type) {
const mapping = {
Payment: 'payment',
TrustSet: 'trustline',
OfferCreate: 'order',
OfferCancel: 'orderCancellation',
AccountSet: 'settings',
SetRegularKey: 'settings'
};
return mapping[type] || null;
}
function parseTransaction(tx: Object): Object {
const type = parseTransactionType(tx.TransactionType);
const mapping = {
'payment': parsePayment,
'trustline': parseTrustline,
'order': parseOrder,
'orderCancellation': parseOrderCancellation,
'settings': parseSettings
};
const parser = mapping[type];
assert(parser !== undefined, 'Unrecognized transaction type');
const specification = parser(tx);
const outcome = utils.parseOutcome(tx);
return utils.removeUndefined({
type: type,
address: tx.Account,
sequence: tx.Sequence,
id: tx.hash,
specification: utils.removeUndefined(specification),
outcome: outcome ? utils.removeUndefined(outcome) : undefined
});
}
module.exports = parseTransaction;

View File

@@ -0,0 +1,38 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const flags = utils.core.Transaction.flags.TrustSet;
const BigNumber = require('bignumber.js');
function parseFlag(flagsValue, trueValue, falseValue) {
if (flagsValue & trueValue) {
return true;
}
if (flagsValue & falseValue) {
return false;
}
return undefined;
}
function parseQuality(quality) {
return (new BigNumber(quality)).shift(-9).toNumber();
}
function parseTrustline(tx: Object): Object {
assert(tx.TransactionType === 'TrustSet');
return utils.removeUndefined({
limit: tx.LimitAmount.value,
currency: tx.LimitAmount.currency,
counterparty: tx.LimitAmount.issuer,
qualityIn: parseQuality(tx.QualityIn),
qualityOut: parseQuality(tx.QualityOut),
ripplingDisabled: parseFlag(
tx.Flags, flags.SetNoRipple, flags.ClearNoRipple),
frozen: parseFlag(tx.Flags, flags.SetFreeze, flags.ClearFreeze),
authorized: parseFlag(tx.Flags, flags.SetAuth, 0)
});
}
module.exports = parseTrustline;

View File

@@ -0,0 +1,77 @@
/* @flow */
'use strict';
const _ = require('lodash');
const transactionParser = require('ripple-lib-transactionparser');
const toTimestamp = require('../../../core/utils').toTimestamp;
const utils = require('../utils');
const BigNumber = require('bignumber.js');
function adjustQualityForXRP(
quality: string, takerGetsCurrency: string, takerPaysCurrency: string
) {
// quality = takerPays.value/takerGets.value
// using drops (1e-6 XRP) for XRP values
const numeratorShift = (takerPaysCurrency === 'XRP' ? -6 : 0);
const denominatorShift = (takerGetsCurrency === 'XRP' ? -6 : 0);
const shift = numeratorShift - denominatorShift;
return shift === 0 ? quality :
(new BigNumber(quality)).shift(shift).toString();
}
function parseTimestamp(tx: {date: string}): string | void {
return tx.date ? (new Date(toTimestamp(tx.date))).toISOString() : undefined;
}
function removeUndefined(obj: Object): Object {
return _.omit(obj, _.isUndefined);
}
function removeEmptyCounterparty(amount) {
if (amount.counterparty === '') {
delete amount.counterparty;
}
}
function removeEmptyCounterpartyInBalanceChanges(balanceChanges) {
_.forEach(balanceChanges, (changes) => {
_.forEach(changes, removeEmptyCounterparty);
});
}
function removeEmptyCounterpartyInOrderbookChanges(orderbookChanges) {
_.forEach(orderbookChanges, (changes) => {
_.forEach(changes, (change) => {
_.forEach(change, removeEmptyCounterparty);
});
});
}
function parseOutcome(tx: Object): ?Object {
if (!tx.validated) {
return undefined;
}
const balanceChanges = transactionParser.parseBalanceChanges(tx.meta);
const orderbookChanges = transactionParser.parseOrderbookChanges(tx.meta);
removeEmptyCounterpartyInBalanceChanges(balanceChanges);
removeEmptyCounterpartyInOrderbookChanges(orderbookChanges);
return {
result: tx.meta.TransactionResult,
timestamp: parseTimestamp(tx),
fee: utils.common.dropsToXrp(tx.Fee),
balanceChanges: balanceChanges,
orderbookChanges: orderbookChanges,
ledgerVersion: tx.ledger_index,
indexInLedger: tx.meta.TransactionIndex
};
}
module.exports = {
parseOutcome,
removeUndefined,
adjustQualityForXRP,
dropsToXrp: utils.common.dropsToXrp,
constants: utils.common.constants,
core: utils.common.core
};

120
src/api/ledger/pathfind.js Normal file
View File

@@ -0,0 +1,120 @@
/* @flow */
'use strict';
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 NotFoundError = utils.common.errors.NotFoundError;
const composeAsync = utils.common.composeAsync;
type PathFindParams = {
src_currencies?: Array<string>, src_account: string, dst_amount: string,
dst_account?: string
}
function addParams(params: PathFindParams, result: {}) {
return _.assign({}, result, {
source_account: params.src_account,
source_currencies: params.src_currencies,
destination_amount: params.dst_amount
});
}
type PathFind = {
source: {address: string, currencies: Array<string>},
destination: {address: string, amount: string}
}
function requestPathFind(remote, pathfind: PathFind, callback) {
const params: PathFindParams = {
src_account: pathfind.source.address,
dst_account: pathfind.destination.address,
dst_amount: utils.common.toRippledAmount(pathfind.destination.amount)
};
if (typeof params.dst_amount === 'object' && !params.dst_amount.issuer) {
// Convert blank issuer to sender's address
// (Ripple convention for 'any issuer')
// https://ripple.com/build/transactions/
// #special-issuer-values-for-sendmax-and-amount
// https://ripple.com/build/ripple-rest/#counterparties-in-payments
params.dst_amount.issuer = params.dst_account;
}
if (pathfind.source.currencies && pathfind.source.currencies.length > 0) {
params.src_currencies = pathfind.source.currencies.map(amount =>
_.omit(utils.common.toRippledAmount(amount), 'value'));
}
remote.requestRipplePathFind(params,
composeAsync(_.partial(addParams, params), callback));
}
function addDirectXrpPath(paths, xrpBalance) {
// Add XRP "path" only if the source acct has enough XRP to make the payment
const destinationAmount = paths.destination_amount;
if ((new BigNumber(xrpBalance)).greaterThanOrEqualTo(destinationAmount)) {
paths.alternatives.unshift({
paths_computed: [],
source_amount: paths.destination_amount
});
}
return paths;
}
function isRippledIOUAmount(amount) {
// rippled XRP amounts are specified as decimal strings
return (typeof amount === 'object') &&
amount.currency && (amount.currency !== 'XRP');
}
function conditionallyAddDirectXRPPath(remote, address, paths, callback) {
if (isRippledIOUAmount(paths.destination_amount)
|| !_.includes(paths.destination_currencies, 'XRP')) {
callback(null, paths);
} else {
utils.getXRPBalance(remote, address, undefined,
composeAsync(_.partial(addDirectXrpPath, 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);
}
if (!_.includes(paths.destination_currencies,
pathfind.destination.amount.currency)) {
throw new NotFoundError('No paths found. ' +
'The destination_account does not accept ' +
pathfind.destination.amount.currency + ', they only accept: ' +
paths.destination_currencies.join(', '));
} else if (paths.source_currencies && paths.source_currencies.length > 0) {
throw new NotFoundError('No paths found. Please ensure' +
' that the source_account has sufficient funds to execute' +
' the payment in one of the specified source_currencies. If it does' +
' there may be insufficient liquidity in the network to execute' +
' this payment right now');
} else {
throw new NotFoundError('No paths found.' +
' Please ensure that the source_account has sufficient funds to' +
' execute the payment. If it does there may be insufficient liquidity' +
' in the network to execute this payment right now');
}
}
function getPathsAsync(pathfind, callback) {
validate.pathfind(pathfind);
const address = pathfind.source.address;
async.waterfall([
_.partial(requestPathFind, this.remote, pathfind),
_.partial(conditionallyAddDirectXRPPath, this.remote, address)
], composeAsync(_.partial(formatResponse, pathfind), callback));
}
function getPaths(pathfind: Object) {
return utils.promisify(getPathsAsync.bind(this))(pathfind);
}
module.exports = getPaths;

View File

@@ -0,0 +1,44 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const parseFields = require('./parse/fields');
const composeAsync = utils.common.composeAsync;
const AccountFlags = utils.common.constants.AccountFlags;
function parseFlags(value) {
const settings = {};
for (const flagName in AccountFlags) {
if (value & AccountFlags[flagName]) {
settings[flagName] = true;
}
}
return settings;
}
function formatSettings(response) {
const data = response.account_data;
const parsedFlags = parseFlags(data.Flags);
const parsedFields = parseFields(data);
return _.assign({}, parsedFlags, parsedFields);
}
function getSettingsAsync(account, options, callback) {
validate.address(account);
validate.getSettingsOptions(options);
const request = {
account: account,
ledger: options.ledgerVersion
};
this.remote.requestAccountInfo(request,
composeAsync(formatSettings, callback));
}
function getSettings(account: string, options={}) {
return utils.promisify(getSettingsAsync.bind(this))(account, options);
}
module.exports = getSettings;

View File

@@ -0,0 +1,78 @@
/* @flow */
'use strict';
const _ = require('lodash');
const async = require('async');
const utils = require('./utils');
const parseTransaction = require('./parse/transaction');
const validate = utils.common.validate;
const errors = utils.common.errors;
const RippleError = require('../../core/rippleerror').RippleError;
function attachTransactionDate(remote, tx, callback) {
if (tx.date) {
callback(null, tx);
return;
}
if (!tx.ledger_index) {
callback(new errors.NotFoundError('ledger_index not found in tx'));
return;
}
remote.requestLedger(tx.ledger_index, (error, data) => {
if (error) {
callback(new errors.NotFoundError('Transaction ledger not found'));
} else if (typeof data.ledger.close_time === 'number') {
callback(null, _.assign({date: data.ledger.close_time}, tx));
} else {
callback(new errors.ApiError('Ledger missing close_time'));
}
});
}
function isTransactionInRange(tx, options) {
return (!options.minLedgerVersion
|| tx.ledger_index >= options.minLedgerVersion)
&& (!options.maxLedgerVersion
|| tx.ledger_index <= options.maxLedgerVersion);
}
function getTransactionAsync(identifier, options, callback) {
validate.identifier(identifier);
validate.getTransactionOptions(options);
const remote = this.remote;
const maxLedgerVersion = Math.min(options.maxLedgerVersion,
remote.getLedgerSequence());
function callbackWrapper(error_, tx) {
let error = error_;
if (error instanceof RippleError && error.remote &&
error.remote.error === 'txnNotFound') {
error = new errors.NotFoundError('Transaction not found');
}
if (error instanceof errors.NotFoundError
&& !utils.hasCompleteLedgerRange(remote,
options.minLedgerVersion, maxLedgerVersion)) {
callback(new errors.MissingLedgerHistoryError('Transaction not found,'
+ ' but the server\'s ledger history is incomplete'));
} else if (!error && !isTransactionInRange(tx, options)) {
callback(new errors.NotFoundError('Transaction not found'));
} else if (error) {
callback(error);
} else {
callback(error, parseTransaction(tx));
}
}
async.waterfall([
_.partial(remote.requestTx.bind(remote), {hash: identifier, binary: false}),
_.partial(attachTransactionDate, remote)
], callbackWrapper);
}
function getTransaction(identifier: string, options={}) {
return utils.promisify(getTransactionAsync.bind(this))(identifier, options);
}
module.exports = getTransaction;

View File

@@ -0,0 +1,124 @@
/* @flow */
/* eslint-disable max-params */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const parseTransaction = require('./parse/transaction');
const getTransaction = require('./transaction');
const validate = utils.common.validate;
const composeAsync = utils.common.composeAsync;
function parseAccountTxTransaction(tx) {
// rippled uses a different response format for 'account_tx' than 'tx'
tx.tx.meta = tx.meta;
tx.tx.validated = tx.validated;
return parseTransaction(tx.tx);
}
function transactionFilter(address, filters, tx) {
if (filters.excludeFailures && tx.outcome.result !== 'tesSUCCESS') {
return false;
}
if (filters.types && !_.includes(filters.types, tx.type)) {
return false;
}
if (filters.initiated === true && tx.address !== address) {
return false;
}
if (filters.initiated === false && tx.address === address) {
return false;
}
if (filters.counterparty && tx.address !== filters.counterparty
&& tx.specification.destination.address !== filters.counterparty) {
return false;
}
return true;
}
function orderFilter(options, tx) {
return !options.startTx || (options.earliestFirst ?
utils.compareTransactions(tx, options.startTx) > 0 :
utils.compareTransactions(tx, options.startTx) < 0);
}
function getAccountTx(remote, address, options, marker, limit, callback) {
const params = {
account: address,
ledger_index_min: options.minLedgerVersion || -1,
ledger_index_max: options.maxLedgerVersion || -1,
forward: options.earliestFirst,
binary: options.binary,
limit: utils.clamp(limit, 10, 400),
marker: marker
};
remote.requestAccountTx(params, (error, data) => {
return error ? callback(error) : callback(null, {
marker: data.marker,
results: data.transactions
.filter((tx) => tx.validated)
.map(parseAccountTxTransaction)
.filter(_.partial(transactionFilter, address, options))
.filter(_.partial(orderFilter, options))
});
});
}
function checkForLedgerGaps(remote, options, transactions) {
let {minLedgerVersion, maxLedgerVersion} = options;
// if we reached the limit on number of transactions, then we can shrink
// the required ledger range to only guarantee that there are no gaps in
// the range of ledgers spanned by those transactions
if (options.limit && transactions.length === options.limit) {
if (options.earliestFirst) {
maxLedgerVersion = _.last(transactions).outcome.ledgerVersion;
} else {
minLedgerVersion = _.last(transactions).outcome.ledgerVersion;
}
}
if (!utils.hasCompleteLedgerRange(remote, minLedgerVersion,
maxLedgerVersion)) {
throw new utils.common.errors.MissingLedgerHistoryError();
}
}
function formatResponse(remote, options, transactions) {
const compare = options.earliestFirst ? utils.compareTransactions :
_.rearg(utils.compareTransactions, 1, 0);
const sortedTransactions = transactions.sort(compare);
checkForLedgerGaps(remote, options, sortedTransactions);
return sortedTransactions;
}
function getTransactionsInternal(remote, address, options, callback) {
const getter = _.partial(getAccountTx, remote, address, options);
const format = _.partial(formatResponse, remote, options);
utils.getRecursive(getter, options.limit, composeAsync(format, callback));
}
function getTransactionsAsync(account, options, callback) {
validate.address(account);
validate.getTransactionsOptions(options);
const defaults = {maxLedgerVersion: this.remote.getLedgerSequence()};
if (options.start) {
getTransaction.bind(this)(options.start).then(tx => {
const ledgerVersion = tx.outcome.ledgerVersion;
const bound = options.earliestFirst ?
{minLedgerVersion: ledgerVersion} : {maxLedgerVersion: ledgerVersion};
const newOptions = _.assign(defaults, options, {startTx: tx}, bound);
getTransactionsInternal(this.remote, account, newOptions, callback);
}).catch(callback);
} else {
const newOptions = _.assign(defaults, options);
getTransactionsInternal(this.remote, account, newOptions, callback);
}
}
function getTransactions(account: string, options={}) {
return utils.promisify(getTransactionsAsync.bind(this))(account, options);
}
module.exports = getTransactions;

View File

@@ -0,0 +1,49 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const validate = utils.common.validate;
const parseAccountTrustline = require('./parse/account-trustline');
function currencyFilter(currency, trustline) {
return currency === null || trustline.specification.currency === currency;
}
function getAccountLines(remote, address, ledgerVersion, options, marker, limit,
callback) {
const requestOptions = {
account: address,
ledger: ledgerVersion,
marker: marker,
limit: utils.clamp(limit, 10, 400),
peer: options.counterparty
};
remote.requestAccountLines(requestOptions, (error, data) => {
return error ? callback(error) :
callback(null, {
marker: data.marker,
results: data.lines.map(parseAccountTrustline)
.filter(_.partial(currencyFilter, options.currency || null))
});
});
}
function getTrustlinesAsync(account: string, options: {currency: string,
counterparty: string, limit: number, ledgerVersion: number},
callback: () => void): void {
validate.address(account);
validate.getTrustlinesOptions(options);
const ledgerVersion = options.ledgerVersion
|| this.remote.getLedgerSequence();
const getter = _.partial(getAccountLines, this.remote, account,
ledgerVersion, options);
utils.getRecursive(getter, options.limit, callback);
}
function getTrustlines(account: string, options={}) {
return utils.promisify(getTrustlinesAsync.bind(this))(account, options);
}
module.exports = getTrustlines;

114
src/api/ledger/utils.js Normal file
View File

@@ -0,0 +1,114 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const common = require('../common');
const dropsToXrp = common.dropsToXrp;
const composeAsync = common.composeAsync;
type Callback = (err: any, data: any) => void
function clamp(value: number, min: number, max: number): number {
assert(min <= max, 'Illegal clamp bounds');
return Math.min(Math.max(value, min), max);
}
function getXRPBalance(remote: any, address: string, ledgerVersion?: number,
callback: Callback): void {
remote.requestAccountInfo({account: address, ledger: ledgerVersion},
composeAsync((data) => dropsToXrp(data.account_data.Balance), callback));
}
type Getter = (marker: ?string, limit: number, callback: Callback) => void
// If the marker is omitted from a response, you have reached the end
// getter(marker, limit, callback), callback(error, {marker, results})
function getRecursiveRecur(getter: Getter, marker?: string, limit: number,
callback: Callback): void {
getter(marker, limit, (error, data) => {
if (error) {
return callback(error);
}
const remaining = limit - data.results.length;
if (remaining > 0 && data.marker !== undefined) {
getRecursiveRecur(getter, data.marker, remaining, (_error, results) => {
return _error ? callback(_error) :
callback(null, data.results.concat(results));
});
} else {
return callback(null, data.results.slice(0, limit));
}
});
}
function getRecursive(getter: Getter, limit?: number, callback: Callback) {
getRecursiveRecur(getter, undefined, limit || Infinity, callback);
}
type Amount = {counterparty?: string, issuer?: string, value: string}
function renameCounterpartyToIssuer(amount?: Amount): ?{issuer?: string} {
if (amount === undefined) {
return undefined;
}
const issuer = amount.counterparty === undefined ?
amount.issuer : amount.counterparty;
const withIssuer = _.assign({}, amount, {issuer: issuer});
return _.omit(withIssuer, 'counterparty');
}
type Order = {taker_gets: Amount, taker_pays: Amount}
function renameCounterpartyToIssuerInOrder(order: Order) {
const taker_gets = renameCounterpartyToIssuer(order.taker_gets);
const taker_pays = renameCounterpartyToIssuer(order.taker_pays);
const changes = {taker_gets: taker_gets, taker_pays: taker_pays};
return _.assign({}, order, _.omit(changes, _.isUndefined));
}
function signum(num) {
return (num === 0) ? 0 : (num > 0 ? 1 : -1);
}
/**
* Order two rippled transactions based on their ledger_index.
* If two transactions took place in the same ledger, sort
* them based on TransactionIndex
* See: https://ripple.com/build/transactions/
*
* @param {Object} first
* @param {Object} second
* @returns {Number} [-1, 0, 1]
*/
type Outcome = {outcome: {ledgerVersion: string, indexInLedger: string}};
function compareTransactions(first: Outcome, second: Outcome): number {
if (first.outcome.ledgerVersion === second.outcome.ledgerVersion) {
return signum(Number(first.outcome.indexInLedger) -
Number(second.outcome.indexInLedger));
}
return Number(first.outcome.ledgerVersion) <
Number(second.outcome.ledgerVersion) ? -1 : 1;
}
function hasCompleteLedgerRange(remote: any, minLedgerVersion: number,
maxLedgerVersion: number): boolean {
const firstLedgerVersion = 32570; // earlier versions have been lost
return remote.getServer().hasLedgerRange(
minLedgerVersion || firstLedgerVersion,
maxLedgerVersion || remote.getLedgerSequence());
}
module.exports = {
getXRPBalance,
compareTransactions,
renameCounterpartyToIssuer,
renameCounterpartyToIssuerInOrder,
getRecursive,
hasCompleteLedgerRange,
promisify: common.promisify,
clamp: clamp,
common: common
};

63
src/api/server/server.js Normal file
View File

@@ -0,0 +1,63 @@
/* @flow */
'use strict';
const _ = require('lodash');
const common = require('../common');
// If a ledger is not received in this time, consider the connection offline
const CONNECTION_TIMEOUT = 1000 * 30;
function isUpToDate(remote): boolean {
const server = remote.getServer();
return Boolean(server) && (remote._stand_alone
|| (Date.now() - server._lastLedgerClose) <= CONNECTION_TIMEOUT);
}
function isConnected(): boolean {
return Boolean(this.remote._ledger_current_index) && isUpToDate(this.remote);
}
function getServerInfoAsync(callback: (err: any, data: any) => void): void {
this.remote.requestServerInfo((error, response) => {
if (error) {
const message = _.get(error, ['remote', 'error_message'], error.message);
callback(new common.errors.RippledNetworkError(message));
} else {
callback(null, common.convertKeysFromSnakeCaseToCamelCase(response.info));
}
});
}
function getFee(): number {
return common.dropsToXrp(this.remote.createTransaction()._computeFee());
}
function getLedgerVersion(): number {
return this.remote.getLedgerSequence();
}
function connect() {
return common.promisify(callback => {
this.remote.connect(() => callback(null));
})();
}
function disconnect() {
return common.promisify(callback => {
this.remote.disconnect(() => callback(null));
})();
}
function getServerInfo() {
return common.promisify(getServerInfoAsync.bind(this))();
}
module.exports = {
connect,
disconnect,
isConnected,
getServerInfo,
getFee,
getLedgerVersion
};

View File

@@ -0,0 +1,43 @@
/* @flow */
'use strict';
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
const OfferCreateFlags = {
passive: {set: 'Passive'},
immediateOrCancel: {set: 'ImmediateOrCancel'},
fillOrKill: {set: 'FillOrKill'}
};
function createOrderTransaction(account, order) {
validate.address(account);
validate.order(order);
const transaction = new Transaction();
const takerPays = utils.common.toRippledAmount(order.direction === 'buy' ?
order.quantity : order.totalPrice);
const takerGets = utils.common.toRippledAmount(order.direction === 'buy' ?
order.totalPrice : order.quantity);
transaction.offerCreate(account, takerPays, takerGets);
utils.setTransactionBitFlags(transaction, order, OfferCreateFlags);
if (order.direction === 'sell') {
transaction.setFlags('Sell');
}
return transaction;
}
function prepareOrderAsync(account, order, instructions, callback) {
const transaction = createOrderTransaction(account, order);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
function prepareOrder(account: string, order: Object, instructions={}) {
return utils.promisify(prepareOrderAsync.bind(this))(
account, order, instructions);
}
module.exports = prepareOrder;

View File

@@ -0,0 +1,28 @@
/* @flow */
'use strict';
const utils = require('./utils');
const validate = utils.common.validate;
const Transaction = utils.common.core.Transaction;
function createOrderCancellationTransaction(account, sequence) {
validate.address(account);
validate.sequence(sequence);
const transaction = new Transaction();
transaction.offerCancel(account, sequence);
return transaction;
}
function prepareOrderCancellationAsync(account, sequence, instructions,
callback) {
const transaction = createOrderCancellationTransaction(account, sequence);
utils.createTxJSON(transaction, this.remote, instructions, callback);
}
function prepareOrderCancellation(account: string, sequence: number,
instructions={}) {
return utils.promisify(prepareOrderCancellationAsync.bind(this))(
account, sequence, instructions);
}
module.exports = prepareOrderCancellation;

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