Compare commits

...

175 Commits

Author SHA1 Message Date
Matthew Fettig
1eddbf995f 0.17.5 2017-03-24 12:31:10 -07:00
Matthew Fettig
592385ac73 Merge pull request #750 from ripple/feature/dependancy-update
update ripple-binary-codec to version 0.1.8
2017-03-24 12:26:35 -07:00
Matthew Fettig
56d626f5b1 Merge pull request #744 from choiip/develop
Added .json in missing require()
2017-03-24 10:53:42 -07:00
Matthew Fettig
7a14300409 update ripple-binary-codec to version 0.1.8 2017-03-24 10:40:19 -07:00
alex.choi
30fa8d658e Added .json in missing require() 2017-02-15 18:51:49 +08:00
Chris Clark
c0101cb5e7 Merge pull request #740 from wilsonianb/shamapv2
Check SHAMapV2 ledger close flag
2016-12-02 12:31:46 -08:00
wilsonianb
fd640cd65e Check SHAMapV2 ledger close flag 2016-12-02 12:25:32 -08:00
Chris Clark
11528eff92 Merge pull request #741 from wilsonianb/fix-lint-errors
Fix eslint errors
2016-12-02 11:55:21 -08:00
wilsonianb
3c9175459d Fix eslint errors 2016-12-02 08:16:16 -08:00
Chris Clark
bd4e0e01e2 Merge pull request #739 from clark800/develop
Update ripple-binary-codec and ripple-hashes dependencies
2016-11-30 14:59:52 -08:00
Chris Clark
cfcf6e473c Update ripple-binary-codec and ripple-hashes dependencies 2016-11-30 14:49:40 -08:00
Chris Clark
50c6af3158 Merge pull request #734 from clark800/bump-codec
Bump version to 0.17.3 and bump ripple-binary-codec to 0.1.4
2016-09-29 11:24:04 -07:00
Chris Clark
1d4310cd3a Disable flow type checking in CircleCI 2016-09-29 11:19:46 -07:00
Chris Clark
2191596e68 Bump version to 0.17.3 and bump ripple-binary-codec to 0.1.4 2016-09-29 11:19:32 -07:00
Chris Clark
acf8f87a88 Merge pull request #727 from clark800/order-seq
Add orderToReplace option
2016-08-01 14:34:27 -07:00
Chris Clark
cfac146620 Add orderToReplace option 2016-08-01 14:27:08 -07:00
Chris Clark
f3234ad853 Merge pull request #726 from clark800/doc-typos
Fix typos in docs
2016-08-01 14:24:22 -07:00
Chris Clark
b65fee3d85 Merge pull request #725 from shekenahglory/develop
update parseDeliveredAmount for cases where delivered_amount is not p…
2016-08-01 14:21:43 -07:00
Chris Clark
82b294bc7d Update flow 2016-08-01 14:13:00 -07:00
Chris Clark
2a58573823 Fix typos in docs 2016-08-01 11:53:46 -07:00
Matthew Fettig
a96f71b7fd fix missing deliveredAmount data from getLedger requests 2016-07-19 21:29:50 -07:00
Chris Clark
077f4a4c79 Merge pull request #723 from clark800/fix-coverage
Fix code coverage tool
2016-07-08 15:04:38 -07:00
Chris Clark
9a495467fb Disable sauce tests because they usually timeout 2016-07-08 11:45:27 -07:00
Chris Clark
db2e62b219 Fix code coverage tool 2016-07-08 11:28:10 -07:00
Alan Cohen
2fafa493a2 Version 0.17.2 2016-06-24 10:59:04 -07:00
Alan Cohen
7617c3005c Merge pull request #721 from mDuo13/fix_transferrate_docs
[DOC] fix transferRate description
2016-06-22 11:13:14 -07:00
mDuo13
5cdbb71277 [DOC] fix transferRate description 2016-06-16 15:11:08 -07:00
Chris Clark
f9339c36bf Merge pull request #720 from clark800/update-binary-codec
0.17.1
2016-05-11 18:20:29 -07:00
Chris Clark
67dc57e9d0 Disable PhantomJS tests due to issue downloading from bitbucket 2016-05-11 17:49:57 -07:00
Chris Clark
757f3190d1 0.17.1 2016-05-11 17:49:54 -07:00
Chris Clark
0d94a15ee7 0.17.0 2016-05-05 18:35:13 -07:00
Chris Clark
7f1c80da1b Merge pull request #718 from clark800/pseudo
Add support for parsing SetFee and EnableAmendment pseudo-transactions
2016-05-05 16:15:32 -07:00
Chris Clark
f74e11bce0 Add support for parsing SetFee and EnableAmendment pseudo-transactions 2016-05-05 15:37:23 -07:00
Chris Clark
d4c843e8e3 Merge pull request #714 from darkdarkdragon/remove_tej
[FIX] remove check for `tej` class of errors.
2016-04-05 15:15:42 -07:00
Ivan Tivonenko
bae190b282 [FIX] remove check for tej class of errors. 2016-04-05 23:50:41 +03:00
Chris Clark
d2cbd70da8 Merge pull request #712 from darkdarkdragon/reconnect_fix_browser
[FIX] on reconnect wait for server to be synced
2016-03-30 15:50:58 -07:00
Ivan Tivonenko
5da78ce583 [FIX] handle websocket errors in browsers
emit not RippledNotInitializedError if server doesn't have any
completed ledgers on connect
2016-03-31 00:46:00 +03:00
Chris Clark
14bbe3e30b Merge pull request #680 from ripple/sublimator-patch-1
Fix typo in docs
2016-03-29 10:27:34 -07:00
Nicholas Dudfield
e52e2bbc68 Fix IOU Amount precision related typo in docs 2016-03-29 13:51:30 +07:00
Alan Cohen
b56752e45b 0.16.10 2016-03-24 14:52:36 -07:00
Chris Clark
4632f511ab Merge pull request #711 from darkdarkdragon/reconnect_fix2
[FIX] on connect return error if server doesn't have validated ledgers
2016-03-24 14:27:50 -07:00
Ivan Tivonenko
e17b6f172d [FIX] on connect return error if server doesn't have validated ledgers 2016-03-24 22:26:33 +02:00
Alan Cohen
eb04e878ba 0.16.9 2016-03-22 18:39:33 -07:00
Chris Clark
c71febd116 Merge pull request #709 from darkdarkdragon/reconnect_fix
Reconnection fix
2016-03-22 18:12:28 -07:00
Ivan Tivonenko
69c1ccbb6b [TASK] emit connected and disconnected events from api 2016-03-23 02:05:07 +02:00
Ivan Tivonenko
499b8c8d8b [FIX] fix multiple reconnections issue 2016-03-23 01:18:08 +02:00
Chris Clark
ea009f9a84 Merge pull request #708 from clark800/fix-maker-exchange-rate
Fix makerExchangeRate for getOrders when rippled provides quality
2016-03-22 14:14:23 -07:00
Chris Clark
dc784d4567 Fix makerExchangeRate for getOrders when rippled provides quality 2016-03-22 13:21:26 -07:00
Alan Cohen
9ffc8a2c0b Merge pull request #707 from ripple/fix/filter-source-amount
FIX: Filtering source_amount pathfind correctly
2016-03-21 14:48:37 -07:00
Alan Cohen
5b20fe573e FIX: Filtering source_amount pathfind correctly
Bug:

```js
api.connect().then(() => {
  const pathfind = {
    source: {
      address: USDCold,
      amount: {
        currency: 'USD',
        value: '1.00' // <<<< Rippled response has "1" not "1.00"
      }
    },
    destination: {
      address: EURCold,
      amount: {currency: 'EUR'}
    }
  };
  return api.getPaths(pathfind).then(paths => {
    console.log('PATHS: \n', JSON.stringify(paths, null, 2));
  });
}).catch(console.log);

```
2016-03-21 11:44:55 -07:00
Chris Clark
7e466bb80f Merge pull request #705 from darkdarkdragon/code_comment
[FIX] add small code comment
2016-03-15 12:14:05 -07:00
Ivan Tivonenko
1f8418b447 [FIX] add small code comment 2016-03-15 20:58:22 +02:00
Alan Cohen
ccfc57fc62 Merge pull request #704 from darkdarkdragon/fix_edge_test
[FIX] fix test hang in microsoft edge
2016-03-15 10:27:01 -07:00
Ivan Tivonenko
1d31fccd72 [FIX] fix test hang in microsoft edge 2016-03-15 05:04:47 +02:00
Alan Cohen
9ac1a89e48 0.16.8 2016-03-09 12:30:10 -08:00
Chris Clark
bfc0696324 Merge pull request #701 from h0vhannes/develop
Documentation fixes
2016-03-09 12:24:55 -08:00
Hovhannes Kuloghlyan
e33e782f9e [DOC] Fix transaction spelling in getTransaction description 2016-03-09 22:14:09 +04:00
Hovhannes Kuloghlyan
f2b591d1b2 [DOC] Fix transaction responses link in submit() description 2016-03-09 22:07:54 +04:00
Chris Clark
fe9af5153d Merge pull request #699 from darkdarkdragon/develop_connection_error
[FIX] fix connection error handling
2016-02-24 14:12:12 -08:00
Ivan Tivonenko
0dfdd0a601 [FIX] change eslint version to 2.1 2016-02-24 23:08:49 +02:00
Ivan Tivonenko
4acc42e1b6 [FIX] fix connection error handling
if connection to server can't be established, reject Promise of `connect()` method caller,
instead of throwing error event on base object.
2016-02-24 23:08:47 +02:00
Alan Cohen
7c9a179865 Merge pull request #698 from h0vhannes/develop
[FIX] Fix typo in PrepareTrustline description
2016-02-23 08:44:43 -07:00
Hovhannes Kuloghlyan
c6296a4918 [FIX] Modify PrepareTrustline doc source to pass tests 2016-02-23 16:45:32 +04:00
Hovhannes Kuloghlyan
cc399f1164 [FIX] Fix typo in PrepareTrustline description 2016-02-21 17:02:27 +03:00
Alan Cohen
e4ffb96646 Update version to 0.16.7 2016-02-12 11:38:51 -08:00
Chris Clark
8d34428dac Merge pull request #694 from lumberj/add-deliveredAmount
Add deliveredAmount to payment outcome
2016-02-12 11:25:01 -08:00
Alan Cohen
353637a0c0 Add TODO comment for fixing workaround for rippled bug 2016-02-12 10:56:35 -08:00
Alan Cohen
00713d8ec1 Update version to 0.16.6 2016-02-11 17:02:18 -08:00
Alan Cohen
d949881e9f Merge pull request #695 from lumberj/fix-typo
Fix typo in common/connection.js
2016-02-11 16:30:23 -08:00
Alan Cohen
5075441a69 Fix lint error 2016-02-11 13:39:35 -08:00
Alan Cohen
94a852cb8b Fix typo in common/connection.js 2016-02-11 13:36:12 -08:00
Alan Cohen
06f847c2d0 Fix lint errors 2016-02-10 19:45:51 -08:00
Alan Cohen
0c2f9d0e62 Add deliveredAmount to payment outcome
It is impossible to reliably compute the delivered amount from the metadata due
to fixed precision. If the partial payment flag is not set and the transaction
succeeded, the delivered amount should always be considered to be the amount
specified in the transaction.
2016-02-10 17:25:27 -08:00
Chris Clark
11ed6b124f Merge pull request #692 from darkdarkdragon/develop-minify
fixes for minified version
2016-02-08 17:22:14 -08:00
Ivan Tivonenko
8767fc0068 fixes for minified version test minified version in SauceLabs 2016-02-09 03:08:23 +02:00
Chris Clark
66b07623b0 Merge pull request #691 from darkdarkdragon/develop-sauce
add SauceLabs testing
2016-02-05 10:10:37 -08:00
Ivan Tivonenko
4f3635eef0 add SauceLabs testing 2016-02-05 07:59:34 +02:00
Chris Clark
f638833759 Merge pull request #686 from darkdarkdragon/develop-RLJS-565
switch from Babel 5 to Babel 6
2016-01-21 15:42:00 -08:00
Ivan Tivonenko
ab9d1936d9 fix reconnect error - in case of connection was unexpectedly closed before it was open,
resolve first promise that was returned from call to connect
2016-01-21 18:35:59 +02:00
Ivan Tivonenko
0fefb2bd2c do not pack lodash inside browser version 2016-01-16 01:12:48 +02:00
Ivan Tivonenko
6740eee495 combine with different transaction test
test for trying to sign already signed transaction
ignore http server in coverage
2016-01-16 01:12:46 +02:00
Ivan Tivonenko
aa6020e00d switch from Babel 5 to Babel 6
use of isparta for coverage reporting
2016-01-16 01:12:43 +02:00
Alan Cohen
7bfe4a6cd8 Update version to 0.16.5 2016-01-15 14:44:05 -08:00
Chris Clark
aa467681e4 Merge pull request #688 from lumberj/source-amount-paths
Filter insufficient source funds paths from pathfind results
2016-01-15 13:47:17 -08:00
Alan Cohen
6b8cd6151d Filter insufficient source funds paths from pathfind results
When pathfinding with source amount, we need to filter out paths where source
amount is not equal to the specified source amount. This is due to the behavior
of rippled when specifying a source amount during pathfinding.

Example:

 {
  "command": "ripple_path_find",
  "source_account": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF",
  "destination_account": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb",
  "destination_amount": {
    "currency": "EUR",
    "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb",
    "value": -1
  },
  "send_max": {
    "currency": "USD",
    "issuer": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF",
    "value": "1234567891"
  },
  "id": 2
}
{
  "id": 2,
  "result": {
    "alternatives": [
      {
        "destination_amount": {
          "currency": "EUR",
          "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb",
          "value": "3999889.62127857"
        },
        "paths_canonical": [],
        "paths_computed": [
          [
            {
              "account": "rcsxQxEqU2qquAKp3tBUJy8Z2t19ioQPJ",
              "type": 1,
              "type_hex": "0000000000000001"
            },
            {
              "currency": "EUR",
              "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb",
              "type": 48,
              "type_hex": "0000000000000030"
            }
          ]
        ],
        "source_amount": {
          "currency": "USD",
          "issuer": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF",
          "value": "4170759.906037564"
        }
      }
    ],
    "destination_account": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb",
    "destination_amount": {
      "currency": "EUR",
      "issuer": "rp91GUd5R3Rk3ipqW7XBdtrUJcX8epzGyb",
      "value": "-1"
    },
    "destination_currencies": [
      "EUR",
      "XRP"
    ],
    "full_reply": true,
    "id": 2,
    "source_account": "rhFQQ4ATC6MDF9ghTq3qAoCsGbGtjnhcXF",
    "status": "success"
  },
  "status": "success",
  "type": "response"
}
2016-01-15 13:38:57 -08:00
Chris Clark
0d6aaee12a Merge pull request #681 from darkdarkdragon/develop-RLJS-564
fix for browser
2016-01-13 11:31:14 -08:00
Ivan Tivonenko
dc03c6e0ac fix to work in browser
run unit tests and integration tests in PhantomJS
add JUnit reporter to unit test so CircleCI can show results
2016-01-13 07:11:39 +02:00
Alan Cohen
0f4d957d14 Update version to 0.16.4 2016-01-04 19:48:59 -08:00
Alan Cohen
71a13224a1 Merge branch 'develop' into release
* develop:
  Update ws to 1.0.1
  run integration tests using standalone server
  Add multisignature support
  Add sample code to cancel all orders for a specified account
  Remove references to browser support
  add check for windows eol on commit removes windows eols from some files
  Fix error on ledger subscription message without validated_ledgers
2016-01-04 19:47:24 -08:00
Alan Cohen
8097ed60ba Update version to 0.16.3 2016-01-04 19:45:47 -08:00
Alan Cohen
408bb74214 Merge pull request #682 from lumberj/update-ws
Update ws to 1.0.1
2016-01-04 19:12:11 -08:00
Alan Cohen
9433b43873 Update ws to 1.0.1
Also, addresses remote memory disclosure vulnerability
https://nodesecurity.io/advisories/67
2016-01-04 19:06:30 -08:00
Chris Clark
896bf48c79 Merge pull request #672 from darkdarkdragon/develop-RLJS-558
testing circleci standalone integration
2015-12-18 16:11:13 -08:00
Ivan Tivonenko
3dd21a7e11 run integration tests using standalone server 2015-12-19 02:04:05 +02:00
Chris Clark
ed79a04018 Merge pull request #679 from clark800/multisign
Add multisignature support
2015-12-17 13:27:52 -08:00
Chris Clark
ebfe20defb Add multisignature support 2015-12-17 11:45:08 -08:00
Chris Clark
28b148348d Merge pull request #678 from clark800/cancel-all
Add sample code to cancel all orders for a specified account
2015-12-16 12:05:09 -08:00
Chris Clark
fe099f2c8b Add sample code to cancel all orders for a specified account 2015-12-16 11:40:39 -08:00
Chris Clark
107c8c9f0f Merge pull request #677 from lumberj/no-browser
Remove references to browser support
2015-12-16 11:35:33 -08:00
Alan Cohen
39e818b3e5 Remove references to browser support 2015-12-16 10:34:57 -08:00
Chris Clark
588ffa3d5c Merge pull request #674 from darkdarkdragon/develop-eol
add check for windows eol
2015-12-14 12:57:57 -08:00
Ivan Tivonenko
691e4dd114 add check for windows eol on commit
removes windows eols from some files
2015-12-14 22:15:30 +02:00
Chris Clark
55bc42725f Merge pull request #675 from clark800/fix-validated-ledgers
Fix error on ledger subscription message without validated_ledgers
2015-12-14 11:30:35 -08:00
Chris Clark
cce55b9361 Fix error on ledger subscription message without validated_ledgers 2015-12-14 11:07:58 -08:00
Chris Clark
c23c6e4fc9 Bump version to 0.16.2 2015-12-10 14:41:45 -08:00
Chris Clark
afdd60efe8 Merge pull request #671 from clark800/bump-binary
Bump ripple-binary-codec dependency version to 0.1.1 to fix issue wit…
2015-12-10 14:38:11 -08:00
Chris Clark
8f6ea573ff Bump ripple-binary-codec dependency version to 0.1.1 to fix issue with computeLedgerHash for transactions with DeliverMin 2015-12-10 14:29:28 -08:00
Alan Cohen
3271b544ef Bump version to 0.16.1 2015-12-09 16:51:26 -08:00
Alan Cohen
6e83130754 Merge pull request #669 from lumberj/fix-assertdiff
FIX: Use assert not assert-diff
2015-12-09 16:49:36 -08:00
Alan Cohen
f6ebe32519 FIX: Use assert not assert-diff 2015-12-09 16:45:08 -08:00
Chris Clark
3caed3c761 Bump version to 0.16.0 2015-12-09 13:17:04 -08:00
Chris Clark
ce1c55427a Merge pull request #668 from clark800/fix-ws-error
BREAKING CHANGE: Change error event format and fix crash due to error event on websocket
2015-12-09 13:06:45 -08:00
Chris Clark
9cd72595f0 BREAKING CHANGE: Change error event format and fix crash due to error event on websocket 2015-12-09 12:56:45 -08:00
Chris Clark
ad1d3e135f Merge pull request #663 from darkdarkdragon/develop-http-server
http server example
2015-12-09 11:15:52 -08:00
Ivan Tivonenko
76866ab901 http server example
allows to use both positional and named parameters
2015-12-09 21:00:47 +02:00
Chris Clark
20b647dfbf Merge pull request #667 from clark800/fix-server-info
BREAKING CHANGE: Fix types of XRP values in getServerInfo response
2015-12-07 17:03:09 -08:00
Chris Clark
99d08065e4 BREAKING CHANGE: Fix types of XRP values in getServerInfo response 2015-12-07 16:47:56 -08:00
Chris Clark
261fba3d21 Merge pull request #666 from clark800/fix-deliver-min
Fix DeliverMin value when specifying minAmount
2015-12-04 15:35:30 -08:00
Chris Clark
e1d9de7b1f Fix DeliverMin value when specifying minAmount 2015-12-04 15:18:03 -08:00
Chris Clark
391b2f3622 Merge pull request #665 from clark800/fix-quality
Fix parsing of quality for getTrustlines
2015-12-04 13:55:46 -08:00
Chris Clark
86ff315ef2 Fix parsing of quality for getTrustlines 2015-12-04 13:40:59 -08:00
Chris Clark
8d8a850864 Merge pull request #662 from lumberj/doc-fix
getFee returns a string not float
2015-12-02 13:32:11 -08:00
Alan Cohen
7bf2da6014 getFee returns a string not float
> api.connect().then(() => api.getFee().then(fee => console.log(typeof fee)));
> string
2015-12-02 13:17:35 -08:00
Chris Clark
7eae3ce709 Merge pull request #661 from clark800/client-cert
Add support for client certificates
2015-11-30 16:48:52 -08:00
Chris Clark
5f5e48e414 Add support for client certificates 2015-11-30 16:26:27 -08:00
Chris Clark
4f6a37f7b1 Merge pull request #660 from clark800/null-max
Allow setting maxLedgerVersion to null to specify no maximum
2015-11-30 16:13:41 -08:00
Chris Clark
82613e7e8b Allow setting maxLedgerVersion to null to specify no maximum 2015-11-30 15:52:36 -08:00
Chris Clark
588aa382a1 Merge pull request #659 from clark800/doc-fixes
Fix generateAddress docs and add error event listener to boilerplate
2015-11-30 15:32:02 -08:00
Chris Clark
809d981987 Fix generateAddress docs and add error event listener to boilerplate 2015-11-30 15:16:37 -08:00
Chris Clark
cfc21fde8c Bump version to 0.15.2 2015-11-25 13:13:53 -08:00
Geert Weening
5c06ef547b Merge pull request #658 from clark800/fix-proxy-auth
Fix support for proxy credentials in proxy URL and fix error when the…
2015-11-25 13:11:42 -08:00
Chris Clark
0990ad4a6f Fix support for proxy credentials in proxy URL and fix error when there are more than 10 outstanding requests 2015-11-25 12:53:50 -08:00
Chris Clark
d8f967d2b8 Bump version to 0.15.1 2015-11-25 11:46:33 -08:00
Geert Weening
fe1c3e7130 Merge pull request #657 from clark800/fix-polyfill
Fix babel-polyfill require
2015-11-25 11:38:52 -08:00
Chris Clark
062148674c Fix babel-polyfill require 2015-11-25 11:31:32 -08:00
Chris Clark
11320693fd Merge pull request #656 from clark800/fix-samples
Fix samples
2015-11-25 11:08:39 -08:00
Chris Clark
7af7eaccb4 Merge pull request #655 from darkdarkdragon/develop-RLJS-549-3
add unit tests for RippleAPIBroadcast
2015-11-25 11:04:55 -08:00
Chris Clark
5d5cf868a2 Fix samples 2015-11-25 10:26:30 -08:00
Ivan Tivonenko
ddf8fe5b1a add unit tests for RippleAPIBroadcast 2015-11-25 05:58:48 +02:00
Chris Clark
dc24f6afe0 Bump version to 0.15.0 2015-11-24 17:53:53 -08:00
Chris Clark
f7dac6ab25 Merge pull request #654 from clark800/mDuo13-docs-patch-3
Docs: SusPay warnings, offline mode, and other tweaks
2015-11-24 17:49:01 -08:00
mDuo13
4b4fc36ebd Docs: SusPay warnings, offline mode, and other tweaks 2015-11-24 17:37:52 -08:00
Chris Clark
7626ea5ed8 Merge pull request #653 from clark800/multi
BREAKING CHANGE: "servers" parameter changed to single "server" and a…
2015-11-24 17:08:58 -08:00
Chris Clark
7061e9afe4 BREAKING CHANGE: "servers" parameter changed to single "server" and added a new broadcast wrapper class for multiple servers 2015-11-24 16:47:12 -08:00
Chris Clark
a124635c2c Merge pull request #652 from darkdarkdragon/develop-RLJS-549-2
fix handling memos in prepareSettings
2015-11-24 16:33:43 -08:00
Ivan Tivonenko
c9704137b7 fix handling memos in prepareSettings
boost coverage back to 99.88%
move connection tests to separate file
group test fixtures into namespaces
2015-11-25 02:10:54 +02:00
Chris Clark
ab8d75d3cc Merge pull request #650 from clark800/cancel-doc-fix
Fix prepareOrderCancellation documentation
2015-11-24 14:32:24 -08:00
Chris Clark
5e720891f5 Fix prepareOrderCancellation documentation 2015-11-23 18:24:19 -08:00
Chris Clark
27ed1aadd5 Bump version to 0.14.0 2015-11-23 17:36:16 -08:00
Chris Clark
001f203983 Merge pull request #648 from clark800/memos
Allow memos on all transaction types
2015-11-23 17:25:09 -08:00
Chris Clark
515047d2dc Merge pull request #649 from clark800/darkdarkdragon-develop-RLJS-549
boost coverage to almost 100%
2015-11-23 17:24:46 -08:00
Ivan Tivonenko
995606b1e6 boost coverage to almost 100% 2015-11-23 17:06:55 -08:00
Chris Clark
b5081344da Allow memos on all transaction types 2015-11-23 14:12:32 -08:00
Chris Clark
4f86691fb8 Merge pull request #647 from clark800/cancellation-spec
BREAKING CHANGE: prepareOrderCancellation now takes orderCancellation specification instead of sequence
2015-11-23 13:14:00 -08:00
Chris Clark
45aca016d4 Merge pull request #646 from clark800/api-options-doc
Add documentation for RippleAPI options
2015-11-23 12:47:43 -08:00
Chris Clark
7f33d8a71e BREAKING CHANGE: prepareOrderCancellation now takes orderCancellation specification instead of sequence 2015-11-23 12:45:44 -08:00
Chris Clark
af620755c5 Fix trace option 2015-11-23 12:01:28 -08:00
Chris Clark
a76b554cad Add documentation for RippleAPI options 2015-11-23 12:01:28 -08:00
Chris Clark
ef1e9e1b70 Merge pull request #644 from mDuo13/patch-1
Docs: more on basic types, tx types
2015-11-23 10:23:38 -08:00
mDuo13
fdbac63f46 Docs: more on basic types, tx types 2015-11-20 18:49:03 -08:00
Chris Clark
377f1dbfa1 Merge pull request #643 from clark800/proxy-auth
Fix proxy support and add support for proxy authorization
2015-11-20 16:32:53 -08:00
Chris Clark
14b840f3fe Fix proxy support and add support for proxy authorization 2015-11-20 15:23:56 -08:00
Chris Clark
beb1cc0cde Merge pull request #642 from mDuo13/patch-1
Doc introduction changes
2015-11-20 14:30:05 -08:00
Rome Reginelli
ef2515507d Docs: revised introductory material 2015-11-20 14:15:40 -08:00
Chris Clark
a271b9e816 Merge pull request #641 from clark800/ledger-event
BREAKING CHANGE: Rename "ledgerClosed" event to "ledger" and convert drops amounts to XRP
2015-11-20 11:30:35 -08:00
Chris Clark
8a3d4a64db BREAKING CHANGE: Rename "ledgerClosed" event to "ledger" and convert drops amounts to XRP 2015-11-20 11:23:19 -08:00
Alan Cohen
9cd8beb778 Bump version to 0.13.2 2015-11-20 10:54:01 -08:00
Chris Clark
aaa165a0f3 Merge pull request #640 from lumberj/fix-pathfind
Fix: Specify send_max when pathfinding with a source amount
2015-11-20 10:48:39 -08:00
Alan Cohen
7514213918 Fix: Specify send_max when pathfinding with a source amount
When specifying a fixed sending amount during a pathfind request, the source
amount is specified as a `send_max` field. This fixes a bug where the amount was
specified as `source_amount`. Leading to strange pathfinding behavior.

Example bad behavior (rippled pathfind request/response):

```
 {
  "command": "ripple_path_find",
  "source_account": "rM21sWyMAJY1oqzgnweDatURxGMurBs7Kf",
  "destination_account": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
  "destination_amount": {
    "currency": "EUR",
    "issuer": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
    "value": -1
  },
  "source_amount": {
    "currency": "USD",
    "issuer": "rM21sWyMAJY1oqzgnweDatURxGMurBs7Kf",
    "value": "9.9"
  },
  "id": 2
}
{
  "id": 2,
  "result": {
    "alternatives": [
      {
        "destination_amount": {
          "currency": "EUR",
          "issuer": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
          "value": "147520.7583553951"
        },
        "paths_canonical": [],
        "paths_computed": [
          [
            {
              "account": "r9HqF3wexBb1vtu2DfZKiFuyy3HoTAwUnH",
              "type": 1,
              "type_hex": "0000000000000001"
            },
            {
              "currency": "EUR",
              "issuer": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
              "type": 48,
              "type_hex": "0000000000000030"
            }
          ]
        ],
        "source_amount": {
          "currency": "USD",
          "issuer": "rM21sWyMAJY1oqzgnweDatURxGMurBs7Kf",
          "value": "160510.6025237665"
        }
      }
    ],
    "destination_account": "raDjTrcEtyMqZT6s3PyKGcikUJqYNXUPPv",
    "destination_currencies": [
      "EUR",
      "XRP"
    ],
    "ledger_current_index": 2771044,
    "validated": false
  },
  "status": "success",
  "type": "response"
}
```

https://ripple.com/build/rippled-apis/#ripple-path-find
2015-11-20 10:44:21 -08:00
Alan Cohen
849ba999cb Bump version to 0.13.1 2015-11-19 18:00:22 -08:00
Chris Clark
10eb08095a Merge pull request #639 from lumberj/babel-polyfill
Add babel-polyfill
2015-11-19 17:46:54 -08:00
Chris Clark
23eb4c90fd Merge pull request #636 from clark800/events-doc
Add documentation for API events
2015-11-19 17:45:00 -08:00
Alan Cohen
12e5765c64 Bump version on ripple-hashes 2015-11-19 17:42:54 -08:00
Alan Cohen
8a53abc32f Add babel-polyfill
» node
> ripple = require('ripple-lib')
Error: Cannot find module 'babel-core/polyfill'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:278:25)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)
    at Object.<anonymous> (/Users/alan/Projects/ripple-connect/node_modules/ripple-lib/dist/npm/index.js:23:3)
    at Module._compile (module.js:460:26)
    at Object.Module._extensions..js (module.js:478:10)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Module.require (module.js:365:17)
2015-11-19 17:31:52 -08:00
Chris Clark
25d1ac0c5f Add documentation for API events 2015-11-19 15:40:35 -08:00
257 changed files with 14433 additions and 2814 deletions

4
.babelrc Normal file
View File

@@ -0,0 +1,4 @@
{
"presets": ["es2015", "stage-1"],
"plugins": ["syntax-flow", "transform-flow-strip-types"]
}

View File

@@ -4,6 +4,11 @@
.*/ripple-lib/test/fixtures/.*
.*/node_modules/flow-bin/.*
.*/node_modules/webpack/.*
.*/node_modules/babel-core/.*
.*/node_modules/babel-eslint/.*
.*/node_modules/babel-preset-es2015/.*
.*/node_modules/babel-preset-stage-1/.*
.*/node_modules/babel-register/.*
[include]
./node_modules/

View File

@@ -8,24 +8,49 @@ var rename = require('gulp-rename');
var webpack = require('webpack');
var bump = require('gulp-bump');
var argv = require('yargs').argv;
var assert = require('assert');
var fs = require('fs');
var pkg = require('./package.json');
var uglifyOptions = {
mangle: {
except: ['_', 'RippleError', 'RippledError', 'UnexpectedError',
'LedgerVersionError', 'ConnectionError', 'NotConnectedError',
'DisconnectedError', 'TimeoutError', 'ResponseFormatError',
'ValidationError', 'NotFoundError', 'MissingLedgerHistoryError',
'PendingLedgerVersionError'
]
}
};
function webpackConfig(extension, overrides) {
overrides = overrides || {};
var defaults = {
cache: true,
externals: [{
'lodash': '_'
}],
entry: './src/index.js',
output: {
library: 'ripple',
path: './build/',
filename: ['ripple-', extension].join(pkg.version)
},
plugins: [
new webpack.NormalModuleReplacementPlugin(/^ws$/, './wswrapper'),
new webpack.NormalModuleReplacementPlugin(/^\.\/wallet$/, './wallet-web'),
new webpack.NormalModuleReplacementPlugin(/^.*setup-api$/,
'./setup-api-web')
],
module: {
loaders: [{
test: /jayson/,
loader: 'null'
}, {
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader?optional=runtime'
exclude: [/node_modules/],
loader: 'babel-loader'
}, {
test: /\.json/,
loader: 'json-loader'
@@ -35,15 +60,70 @@ function webpackConfig(extension, overrides) {
return _.assign({}, defaults, overrides);
}
function webpackConfigForWebTest(testFileName, path) {
var match = testFileName.match(/\/?([^\/]*)-test.js$/);
if (!match) {
assert(false, 'wrong filename:' + testFileName);
}
var configOverrides = {
externals: [{
'lodash': '_',
'ripple-api': 'ripple',
'net': 'null'
}],
entry: testFileName,
output: {
library: match[1].replace(/-/g, '_'),
path: './test-compiled-for-web/' + (path ? path : ''),
filename: match[1] + '-test.js'
}
};
return webpackConfig('.js', configOverrides);
}
gulp.task('build-tests', function(callback) {
var times = 0;
function done() {
if (++times >= 5) {
callback();
}
}
webpack(webpackConfigForWebTest('./test/rangeset-test.js'), done);
webpack(webpackConfigForWebTest('./test/connection-test.js'), done);
webpack(webpackConfigForWebTest('./test/api-test.js'), done);
webpack(webpackConfigForWebTest('./test/broadcast-api-test.js'), done);
webpack(webpackConfigForWebTest('./test/integration/integration-test.js',
'integration/'), done);
});
function createLink(from, to) {
if (fs.existsSync(to)) {
fs.unlinkSync(to);
}
fs.linkSync(from, to);
}
function createBuildLink(callback) {
return function(err, res) {
createLink('./build/ripple-' + pkg.version + '.js',
'./build/ripple-latest.js');
callback(err, res);
};
}
gulp.task('build', function(callback) {
webpack(webpackConfig('.js'), callback);
webpack(webpackConfig('.js'), createBuildLink(callback));
});
gulp.task('build-min', ['build'], function() {
return gulp.src(['./build/ripple-', '.js'].join(pkg.version))
.pipe(uglify())
.pipe(uglify(uglifyOptions))
.pipe(rename(['ripple-', '-min.js'].join(pkg.version)))
.pipe(gulp.dest('./build/'));
.pipe(gulp.dest('./build/'))
.on('end', function() {
createLink('./build/ripple-' + pkg.version + '-min.js',
'./build/ripple-latest-min.js');
});
});
gulp.task('build-debug', function(callback) {

View File

@@ -1,3 +1,79 @@
##0.16.5
**Changes**
+ [Filter insufficient source funds paths from pathfind results](https://github.com/ripple/ripple-lib/pull/688)
##0.16.4
**Changes**
+ [Update ws to 1.0.1](https://github.com/ripple/ripple-lib/pull/682)
##0.16.2
**Changes**
+ [Bump ripple-binary-codec dependency version to 0.1.1 to fix issue with computeLedgerHash for transactions with DeliverMin]
##0.16.1
**Changes**
+ [FIX: Use assert not assert-diff](https://github.com/ripple/ripple-lib/commit/f6ebe325193e7208c5ee8d8e84a7504714f0009e)
##0.16.0
**Breaking Changes**
+ [Fix types of XRP values in getServerInfo response](https://github.com/ripple/ripple-lib/commit/99d08065e4bda3dda6ae1f183adbd11abc70a9b7)
+ [Change error event format and fix crash due to error event on webscocket](https://github.com/ripple/ripple-lib/commit/9cd72595f0efc062d77b9d625695d6030c524cc6)
**Changes**
+ [Fix generateAddress docs and add error event listener to boilerplate](https://github.com/ripple/ripple-lib/commit/809d981987a2890fac3a73a40a05c598b9040334)
+ [Allow setting maxLedgerVersion to null to specify no maximum](https://github.com/ripple/ripple-lib/commit/82613e7e8b360d1ae1552eab4559ab4763c06d7e)
+ [Add support for client certificates](https://github.com/ripple/ripple-lib/commit/5f5e48e4140345d166b8c1a3ee0847b0d9e2d893)
+ [getFee returns a string not float](https://github.com/ripple/ripple-lib/commit/7bf2da6014c87e164542e69356efeaabb575a157)
+ [Fix parsing of quality for getTrustlines](https://github.com/ripple/ripple-lib/commit/86ff315ef2a39dfdc2ce97e0e1c4aa73f04e363b)
+ [Fix DeliverMin value when specifying minAmount](Fix DeliverMin value when specifying minAmount)
+ [http server example](https://github.com/ripple/ripple-lib/commit/76866ab901ea46a2dd73181605e0f7f4220043d4)
##0.15.2
**Changes**
+ [Fix support for proxy credentials in proxy URL and fix error when there are more than 10 outstanding requests](https://github.com/ripple/ripple-lib/commit/0990ad4a6f1d59ca9d2cb859b4e2d71693f3fc4b)
##0.15.1
**Changes**
+ [Fix babel-polyfill require](https://github.com/ripple/ripple-lib/commit/062148674c3b1293ab82c28e25615ddd530339fa)
+ [Fix samples](https://github.com/ripple/ripple-lib/commit/5d5cf868a2ddb1b1cd40e4a4f0a782d0066c2055)
+ [add unit tests for RippleAPIBroadcast](https://github.com/ripple/ripple-lib/commit/ddf8fe5b1a9c750490dca98fb9ffaaf8017f87e0)
##0.15.0
**Breaking Changes**
+ ["servers" parameter changed to single "server"](https://github.com/ripple/ripple-lib/commit/7061e9afe46f0682254d098adeff3dd7157521a1)
**Changes**
+ [fix handling memos in prepareSettings](https://github.com/ripple/ripple-lib/commit/c9704137b7b538e8dbf31c483bcdcf2dcfd7cd75)
+ [Docs: SusPay warnings, offline mode, and other tweaks](https://github.com/ripple/ripple-lib/commit/4b4fc36ebd93f1360781a65f2869bd2c4f0a5093)
+ [Fix prepareOrderCancellation documentation](https://github.com/ripple/ripple-lib/commit/5e720891f579fd73d43c64e5ec519d9121023c10)
##0.14.0
**Breaking Changes**
+ [prepareOrderCancellation now takes orderCancellation specification](https://github.com/ripple/ripple-lib/commit/7f33d8a71e56289e5a5e0ead1c74f75ebcde72bc)
+ [Rename "ledgerClosed" event to "ledger" and change format](https://github.com/ripple/ripple-lib/commit/8a3d4a64db5fbf560ebf87dc62e0212513c5e18a)
**Changes**
+ [Fix proxy support and add support for proxy authorization](https://github.com/ripple/ripple-lib/commit/14b840f3feca758e0384b746c94e36d8bf59b8c2)
+ [Fix trace option](https://github.com/ripple/ripple-lib/commit/af620755c53556c55eed12de4b0013ef5a349ce2)
+ [Allow memos on all transaction types](https://github.com/ripple/ripple-lib/commit/b5081344da8e66fbd3a5113cc3313325ef72a494)
+ [Add documentation for RippleAPI options](https://github.com/ripple/ripple-lib/commit/a76b554cadb9f9f918b06f8386bc29355682a1a4)
+ [Docs: more on basic types, tx types](https://github.com/ripple/ripple-lib/commit/fdbac63f466b4fd3be701d4878800d856692e26e)
+ [Docs: revised introductory material](https://github.com/ripple/ripple-lib/commit/ef2515507dbd3c6a426ab5b31332a1bdf72d5b2d)
+ [boost coverage to almost 100%](https://github.com/ripple/ripple-lib/commit/995606b1e6f3643af34d9fd442ccd31f320b03eb)
##0.13.2
**Changes**
+ [Fix: Specify send_max when pathfinding with a source amount](https://github.com/ripple/ripple-lib/commit/75142139184625c8b9fcc480b1825d9985337813)
##0.13.1
+ [Add documentation for API events](https://github.com/ripple/ripple-lib/commit/25d1ac0c5f95cad32ea4ceebb))
**Changes**
+ [Fix: Add babel-polyfill](https://github.com/ripple/ripple-lib/commit/8a53abc32f6ec6c7d50cd182492d6fb511b86704)
+ [Fix: Bump version on ripple-hashes](https://github.com/ripple/ripple-lib/commit/12e5765c64aea31b3c2fb65ff989cf01e6368f58)
##0.13.0
**Breaking Changes**

View File

@@ -1,6 +1,6 @@
#ripple-lib
A JavaScript API for interacting with Ripple in Node.js and the browser
A JavaScript API for interacting with Ripple in Node.js
[![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)
@@ -8,7 +8,7 @@ A JavaScript API for interacting with Ripple in Node.js and the browser
###Features
+ Connect to a rippled server in JavaScript (Node.js or browser)
+ Connect to a rippled server in Node.js
+ Issue [rippled API](https://ripple.com/build/rippled-apis/) requests
+ Listen to events on the Ripple network (transaction, ledger, etc.)
+ Sign and submit transactions to the Ripple network
@@ -30,7 +30,7 @@ Then see the [documentation](https://github.com/ripple/ripple-lib/blob/develop/d
##Generating Documentation
The continuous integration tests require that the documentation stays up-to-date. If you make changes the the JSON schemas, fixtures, or documentation sources, you must update the documentation by running `npm run docgen`.
The continuous integration tests require that the documentation stays up-to-date. If you make changes to the JSON schemas, fixtures, or documentation sources, you must update the documentation by running `npm run docgen`.
##More Information

View File

@@ -1,7 +1,19 @@
machine:
node:
version: 0.12.0
hosts:
testripple.circleci.com: 127.0.0.1
dependencies:
pre:
- npm -g install npm@latest-2
- wget https://s3-us-west-2.amazonaws.com/ripple-debs/rippled_0.30.1-b11-1.deb
- sudo dpkg -i rippled_0.30.1-b11-1.deb
test:
pre:
- rippled -a --start --conf "$HOME/$CIRCLE_PROJECT_REPONAME/test/integration/rippled.cfg":
background: true
override:
- scripts/ci.sh "$CIRCLE_NODE_INDEX" "$CIRCLE_NODE_TOTAL":
parallel: true
post:
- killall /usr/bin/rippled

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
'use strict';
const RippleAPI = require('../../src').RippleAPI; // require('ripple-lib')
const api = new RippleAPI({servers: ['wss://s1.ripple.com:443']});
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
const address = 'r3kmLJN5D28dHuH8vZNUZpMC43pEHpaocV';
api.connect().then(() => {

38
docs/samples/cancelall.js Normal file
View File

@@ -0,0 +1,38 @@
'use strict';
const RippleAPI = require('../../dist/npm').RippleAPI; // require('ripple-lib')
const address = 'rLDYrujdKUfVx28T9vRDAbyJ7G2WVXKo4K';
const secret = '';
const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
const instructions = {maxLedgerVersionOffset: 5};
function fail(message) {
console.error(message);
process.exit(1);
}
function cancelOrder(orderSequence) {
console.log('Cancelling order: ' + orderSequence.toString());
return api.prepareOrderCancellation(address, {orderSequence}, instructions)
.then(prepared => {
const signing = api.sign(prepared.txJSON, secret);
return api.submit(signing.signedTransaction);
});
}
function cancelAllOrders(orderSequences) {
if (orderSequences.length === 0) {
return Promise.resolve();
}
const orderSequence = orderSequences.pop();
return cancelOrder(orderSequence).then(() => cancelAllOrders(orderSequences));
}
api.connect().then(() => {
console.log('Connected...');
return api.getOrders(address).then(orders => {
const orderSequences = orders.map(order => order.properties.sequence);
return cancelAllOrders(orderSequences);
}).then(() => process.exit(0));
}).catch(fail);

View File

@@ -4,13 +4,13 @@ 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 api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
const instructions = {maxLedgerVersionOffset: 5};
const payment = {
source: {
address: address,
amount: {
maxAmount: {
value: '0.01',
currency: 'XRP'
}

View File

@@ -6,23 +6,28 @@
"r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59"
```
Every Ripple account has an *address*, which is a base58-encoding of a hash of the account's public key.
Every Ripple account has an *address*, which is a base58-encoding of a hash of the account's public key. Ripple addresses always start with the lowercase letter `r`.
## Account Sequence Number
Every Ripple account has a *sequence number* that is used to order transactions. Every transaction must have a sequence number and transaction can only be executed in order by sequence number. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
Every Ripple account has a *sequence number* that is used to keep transactions in order. Every transaction must have a sequence number. A transaction can only be executed if it has the next sequence number in order, of the account sending it. This prevents one transaction from executing twice and transactions executing out of order. The sequence number starts at `1` and increments for each transaction that the account makes.
## Currency
Currencies are represented as either 3-character currency codes or 40-character uppercase hexadecimal strings. We recommend using uppercase [ISO 4217 Currency Codes](http://www.xe.com/iso4217.php) only. The string "XRP" is disallowed on trustlines because it is reserved for the Ripple native currency. The following characters are permitted: all uppercase and lowercase letters, digits, as well as the symbols `?`, `!`, `@`, `#`, `$`, `%`, `^`, `&`, `*`, `<`, `>`, `(`, `)`, `{`, `}`, `[`, `]`, and `|`.
## Value
A *value* is a quantity of a currency represented as a decimal string (string encoding is used because javascript numbers do not have sufficient precision).
A *value* is a quantity of a currency represented as a decimal string. Be careful: JavaScript's native number format does not have sufficient precision to represent all values. XRP has different precision from other currencies.
**XRP** has 6 significant digits past the decimal point. In other words, XRP cannot be divided into positive values smaller than `0.000001` (1e-6). XRP has a maximum value of `100000000000` (1e11).
**Non-XRP values** have 16 decimal digits of precision, with a maximum value of `9999999999999999e80`. The smallest positive non-XRP value is `1e-81`.
An XRP value has 6 significant digits past the decimal point. A non-XRP value has 16 total digits of precision.
## Amount
Example amount:
```json
{
"currency": "USD",
@@ -31,6 +36,7 @@ An XRP value has 6 significant digits past the decimal point. A non-XRP value ha
}
```
Example XRP amount:
```json
{
"currency": "XRP",
@@ -38,7 +44,7 @@ An XRP value has 6 significant digits past the decimal point. A non-XRP value ha
}
```
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value (for all currencies besides "XRP").
An *amount* is data structure representing a currency, a quantity of that currency, and the counterparty on the trustline that holds the value. For XRP, there is no counterparty.
A *lax amount* allows the counterparty to be omitted for all currencies. If the counterparty is not specified in an amount within a transaction specification, then any counterparty may be used for that amount.

View File

@@ -1,10 +1,23 @@
## Boilerplate
Use the following [boilerplate code](https://en.wikipedia.org/wiki/Boilerplate_code) to wrap your custom code using RippleAPI.
```javascript
const {RippleAPI} = require('ripple-lib');
const api = new RippleAPI({
servers: ['wss://s1.ripple.com']
server: 'wss://s1.ripple.com' // Public rippled server hosted by Ripple, Inc.
});
api.on('error', (errorCode, errorMessage) => {
console.log(errorCode + ': ' + errorMessage);
});
api.on('connected', () => {
console.log('connected');
});
api.on('disconnected', (code) => {
// code - [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) sent by the server
// will be 1000 if this was normal closure
console.log('disconnected, code:', code);
});
api.connect().then(() => {
/* insert code here */
@@ -13,22 +26,43 @@ api.connect().then(() => {
}).catch(console.error);
```
To get started, first install [nodejs](https://nodejs.org) version `0.12.0` or greater, then:
RippleAPI is designed to work in [NodeJS](https://nodejs.org) (version `0.12.0` or greater) using [Babel](https://babeljs.io/) for [ECMAScript 6](https://babeljs.io/docs/learn-es2015/) support.
`npm install -g babel`
`npm install ripple-lib`
Then create a script based on the boilerplate shown here and run with:
`babel-node script.js`
The code samples in this documentation are written in ES6, but `RippleAPI` will work with ES5 also. Regardless of whether you use ES5 or ES6, the methods that return promises will return ES6-style promises.
The code samples in this documentation are written in ES6, but `RippleAPI` will work with ES5 also. Regardless of whether you use ES5 or ES6, the methods that return promises will return [ES6-style promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
<aside class="notice">
All the code snippets in this documentation assume that you have surrounded them with this boilerplate.
</aside>
<aside class="notice">
Dont forget the "catch" or errors may not be visible.
If you omit the "catch" section, errors may not be visible.
</aside>
<aside class="notice">
The "error" event is emitted whenever an error occurs that cannot be associated with a specific request. If the listener is not registered, an exception will be thrown whenever the event is emitted.
</aside>
### Parameters
The RippleAPI constructor optionally takes one argument, an object with the following options:
<%- renderSchema('input/api-options.json') %>
If you omit the `server` parameter, RippleAPI operates [offline](#offline-functionality).
### Installation ###
1. Install [NodeJS](https://nodejs.org) and the Node Package Manager (npm). Most Linux distros have a package for NodeJS, but make sure you have version `0.12.0` or higher.
2. Use npm to install [Babel](https://babeljs.io/) globally:
`npm install -g babel-cli`
3. Use npm to install RippleAPI:
`npm install ripple-lib`
After you have installed ripple-lib, you can create scripts using the [boilerplate](#boilerplate) and run them using babel-node:
`babel-node script.js`
<aside class="notice">
Instead of using babel-node in production, we recommend using Babel to transpile to ECMAScript 5 first.
</aside>

24
docs/src/combine.md.ejs Normal file
View File

@@ -0,0 +1,24 @@
## combine
`combine(signedTransactions: Array<string>): {signedTransaction: string, id: string}`
Combines signed transactions from multiple accounts for a multisignature transaction. The signed transaction must subsequently be [submitted](#submit).
### Parameters
<%- renderSchema("input/combine.json") %>
### Return Value
This method returns an object with the following structure:
<%- renderSchema("output/sign.json") %>
### Example
```javascript
const signedTransactions = <%- importFile('test/fixtures/requests/combine.json') %>;
return api.combine(signedTransactions);
```
<%- renderFixture("responses/combine.json") %>

View File

@@ -2,7 +2,7 @@
`connect(): Promise<void>`
Tells the RippleAPI instance to connect to its server(s).
Tells the RippleAPI instance to connect to its rippled server.
### Parameters

View File

@@ -2,7 +2,7 @@
`disconnect(): Promise<void>`
Tells the RippleAPI instance to disconnect from its server(s).
Tells the RippleAPI instance to disconnect from its rippled server.
### Parameters

81
docs/src/events.md.ejs Normal file
View File

@@ -0,0 +1,81 @@
# API Events
## ledger
This event is emitted whenever a new ledger version is validated on the connected server.
### Return Value
<%- renderSchema('output/ledger-event.json') %>
### Example
```javascript
api.on('ledger', ledger => {
console.log(JSON.stringify(ledger, null, 2));
});
```
<%- renderFixture('responses/ledger-event.json') %>
## error
This event is emitted when there is an error on the connection to the server that cannot be associated to a specific request.
### Return Value
The first parameter is a string indicating the error type:
* `badMessage` - rippled returned a malformed message
* `websocket` - the websocket library emitted an error
* one of the error codes found in the [rippled Universal Errors](https://ripple.com/build/rippled-apis/#universal-errors).
The second parameter is a message explaining the error.
The third parameter is:
* the message that caused the error for `badMessage`
* the error object emitted for `websocket`
* the parsed response for rippled errors
### Example
```javascript
api.on('error', (errorCode, errorMessage, data) => {
console.log(errorCode + ': ' + errorMessage);
});
```
```
tooBusy: The server is too busy to help you now.
```
## connected
This event is emitted after connection successfully opened.
### Example
```javascript
api.on('connected', () => {
console.log('Connection is open now.');
});
```
## disconnected
This event is emitted when connection is closed.
### Return Value
The only parameter is a number containing the [close code](https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent) send by the server.
### Example
```javascript
api.on('disconnected', (code) => {
if (code !== 1000) {
console.log('Connection is closed due to error.');
} else {
console.log('Connection is closed normally.');
}
});
```

View File

@@ -6,7 +6,7 @@ Generate a new Ripple address and corresponding secret.
### Parameters
This method has no parameters.
<%- renderSchema('input/generate-address.json') %>
### Return Value
@@ -17,8 +17,7 @@ This method returns an object with the following structure:
### Example
```javascript
return api.generateAddress()
.then(result => {/* ... */});
return api.generateAddress();
```
<%- renderFixture('responses/generate-address.json') %>

View File

@@ -10,7 +10,7 @@ Returns aggregate balances by currency plus a breakdown of assets and obligation
### Return Value
This method returns a promise that resolves with an array of objects with the following structure:
This method returns a promise that resolves with an object with the following structure:
<%- renderSchema('output/get-balance-sheet.json') %>

View File

@@ -2,7 +2,7 @@
`getFee(): Promise<number>`
Returns the estimated transaction fee for the server(s) the RippleAPI instance is connected to.
Returns the estimated transaction fee for the rippled server the RippleAPI instance is connected to.
### Parameters
@@ -10,7 +10,7 @@ This method has no parameters.
### Return Value
This method returns a promise that resolves with a floating point value representing the estimated fee to submit a transaction, expressed in XRP.
This method returns a promise that resolves with a string encoded floating point value representing the estimated fee to submit a transaction, expressed in XRP.
### Example
@@ -19,5 +19,5 @@ return api.getFee().then(fee => {/* ... */});
```
```json
0.012
"0.012"
```

View File

@@ -1,5 +1,6 @@
<% include introduction.md.ejs %>
<% include boilerplate.md.ejs %>
<% include offline.md.ejs %>
<% include basictypes.md.ejs %>
<% include transactions.md.ejs %>
<% include specifications.md.ejs %>
@@ -30,6 +31,8 @@
<% include prepareSuspendedPaymentCancellation.md.ejs %>
<% include prepareSuspendedPaymentExecution.md.ejs %>
<% include sign.md.ejs %>
<% include combine.md.ejs %>
<% include submit.md.ejs %>
<% include generateAddress.md.ejs %>
<% include computeLedgerHash.md.ejs %>
<% include events.md.ejs %>

View File

@@ -1,5 +1,12 @@
# Introduction
RippleAPI allows you to query and submit transactions to a node on the Ripple network.
RippleAPI is the official client library to the Ripple Consensus Ledger. Currently, RippleAPI is only available in JavaScript.
Using RippleAPI, you can:
* [Query transactions from the network](#gettransaction)
* [Sign](#sign) transactions securely without connecting to any server
* [Submit](#submit) transactions to the Ripple Consensus Ledger, including [Payments](#payment), [Orders](#order), [Settings changes](#settings), and [other types](#transaction-types)
* [Generate a new Ripple Address](#generateaddress)
* ... and [much more](#api-methods).
RippleAPI only provides access to *validated*, *immutable* transaction data.

View File

@@ -2,7 +2,7 @@
`isConnected(): boolean`
Checks if the RippleAPI instance is connected to its server(s).
Checks if the RippleAPI instance is connected to its rippled server.
### Parameters

27
docs/src/offline.md.ejs Normal file
View File

@@ -0,0 +1,27 @@
## Offline functionality
RippleAPI can also function without internet connectivity. This can be useful in order to generate secrets and sign transactions from a secure, isolated machine.
To instantiate RippleAPI in offline mode, use the following boilerplate code:
```javascript
const {RippleAPI} = require('ripple-lib');
const api = new RippleAPI();
/* insert code here */
```
Methods that depend on the state of the Ripple Consensus Ledger are unavailable in offline mode. To prepare transactions offline, you **must** specify the `fee`, `sequence`, and `maxLedgerVersion` parameters in the [transaction instructions](#transaction-instructions). The following methods should work offline:
* [preparePayment](#preparepayment)
* [prepareTrustline](#preparetrustline)
* [prepareOrder](#prepareorder)
* [prepareOrderCancellation](#prepareordercancellation)
* [prepareSettings](#preparesettings)
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
* [sign](#sign)
* [generateAddress](#generateaddress)
* [computeLedgerHash](#computeledgerhash)

View File

@@ -13,7 +13,7 @@ Prepare an order transaction. The prepared transaction must subsequently be [sig
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare" methods have the same return type.
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema('output/prepare.json') %>

View File

@@ -1,6 +1,6 @@
## prepareOrderCancellation
`prepareOrderCancellation(address: string, sequence: number, instructions: Object): Promise<Object>`
`prepareOrderCancellation(address: string, orderCancellation: Object, instructions: Object): Promise<Object>`
Prepare an order cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
@@ -13,7 +13,7 @@ Prepare an order cancellation transaction. The prepared transaction must subsequ
This method returns a promise that resolves with an object with the following structure:
<aside class="notice">
All "prepare" methods have the same return type.
All "prepare*" methods have the same return type.
</aside>
<%- renderSchema("output/prepare.json") %>
@@ -22,8 +22,8 @@ All "prepare" methods have the same return type.
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const sequence = 123;
return api.prepareOrderCancellation(address, sequence)
const orderCancellation = {orderSequence: 123};
return api.prepareOrderCancellation(address, orderCancellation)
.then(prepared => {/* ... */});
```

View File

@@ -4,6 +4,8 @@
Prepare a suspended payment cancellation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-suspended-payment-cancellation.json') %>

View File

@@ -4,6 +4,8 @@
Prepare a suspended payment creation transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-suspended-payment-creation.json') %>

View File

@@ -4,6 +4,8 @@
Prepare a suspended payment execution transaction. The prepared transaction must subsequently be [signed](#sign) and [submitted](#submit).
**Caution:** Suspended Payments are currently available on the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) only.
### Parameters
<%- renderSchema('input/prepare-suspended-payment-execution.json') %>

View File

@@ -23,7 +23,7 @@ All "prepare*" methods have the same return type.
```javascript
const address = 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59';
const trustline = <%- importFile('test/fixtures/requests/prepare-trustline.json') %>;
return api.preparePayment(address, trustline).then(prepared =>
return api.prepareTrustline(address, trustline).then(prepared =>
{/* ... */});
```

View File

@@ -1,6 +1,6 @@
## sign
`sign(txJSON: string, secret: string): {signedTransaction: string, id: string}`
`sign(txJSON: string, secret: string, options: Object): {signedTransaction: string, id: string}`
Sign a prepared transaction. The signed transaction must subsequently be [submitted](#submit).

View File

@@ -4,7 +4,7 @@ A *transaction specification* specifies what a transaction should do. Each [Tran
## Payment
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/payment.json') %>
@@ -14,7 +14,7 @@ See [Transcation Types](#transaction-types) for a description.
## Trustline
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/trustline.json') %>
@@ -24,7 +24,7 @@ See [Transcation Types](#transaction-types) for a description.
## Order
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/order.json') %>
@@ -34,7 +34,7 @@ See [Transcation Types](#transaction-types) for a description.
## Order Cancellation
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/order-cancellation.json') %>
@@ -44,7 +44,7 @@ See [Transcation Types](#transaction-types) for a description.
## Settings
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('output/get-settings.json') %>
@@ -54,7 +54,7 @@ See [Transcation Types](#transaction-types) for a description.
## Suspended Payment Creation
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/suspended-payment-creation.json') %>
@@ -64,7 +64,7 @@ See [Transcation Types](#transaction-types) for a description.
## Suspended Payment Cancellation
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/suspended-payment-cancellation.json') %>
@@ -74,7 +74,7 @@ See [Transcation Types](#transaction-types) for a description.
## Suspended Payment Execution
See [Transcation Types](#transaction-types) for a description.
See [Transaction Types](#transaction-types) for a description.
<%- renderSchema('specifications/suspended-payment-execution.json') %>

View File

@@ -6,7 +6,7 @@ A transaction type is specified by the strings in the first column in the table
Type | Description
---- | -----------
[payment](#payment) | A `payment` transaction represents a transfer of value from one account to another. Depending on the path taken, additional exchanges of value may occur atomically to facilitate the payment.
[payment](#payment) | A `payment` transaction represents a transfer of value from one account to another. Depending on the [path](https://ripple.com/build/paths/) taken, additional exchanges of value may occur atomically to facilitate the payment.
[order](#order) | An `order` transaction creates a limit order. It defines an intent to exchange currencies, and creates an order in the Ripple Consensus Ledger's order book if not completely fulfilled when placed. Orders can be partially fulfilled.
[orderCancellation](#order-cancellation) | An `orderCancellation` transaction cancels an order in the Ripple Consensus Ledger's order book.
[trustline](#trustline) | A `trustline` transactions creates or modifies a trust line between two accounts.
@@ -15,31 +15,51 @@ Type | Description
[suspendedPaymentCancellation](#suspended-payment-cancellation) | A `suspendedPaymentCancellation` transaction unlocks the funds in a suspended payment and sends them back to the creator of the suspended payment, but it will only work after the suspended payment expires.
[suspendedPaymentExecution](#suspended-payment-execution) | A `suspendedPaymentExecution` transaction unlocks the funds in a suspended payment and sends them to the destination of the suspended payment, but it will only work if the cryptographic condition is provided.
The three "suspended payment" transaction types are not supported by the production Ripple peer-to-peer network at this time. They are available for testing purposes if you [configure RippleAPI](#boilerplate) to connect to the [Ripple Test Net](https://ripple.com/build/ripple-test-net/) instead.
## Transaction Flow
Executing a transaction with `RippleAPI` requires the following four steps:
1. prepare - Create an unsigned transaction based on a [specification](#transaction-specifications) and [instructions](#transaction-instructions).
2. sign - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place.
3. submit - Submit the transaction to the connected server.
4. verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted. It is recommended that you specify a `maxLedgerVersion` in the instructions when preparing a transaction because without it there is no way to know that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the network ledger version exceeds the `maxLedgerVersion` provided in the transaction instructions.
1. Prepare - Create an unsigned transaction based on a [specification](#transaction-specifications) and [instructions](#transaction-instructions). There is a method to prepare each type of transaction:
* [preparePayment](#preparepayment)
* [prepareTrustline](#preparetrustline)
* [prepareOrder](#prepareorder)
* [prepareOrderCancellation](#prepareordercancellation)
* [prepareSettings](#preparesettings)
* [prepareSuspendedPaymentCreation](#preparesuspendedpaymentcreation)
* [prepareSuspendedPaymentCancellation](#preparesuspendedpaymentcancellation)
* [prepareSuspendedPaymentExecution](#preparesuspendedpaymentexecution)
2. [Sign](#sign) - Cryptographically sign the transaction locally and save the [transaction ID](#transaction-id). Signing is how the owner of an account authorizes a transaction to take place. For multisignature transactions, the `signedTransaction` fields returned by `sign` must be collected and passed to the [combine](#combine) method.
3. [Submit](#submit) - Submit the transaction to the connected server.
4. Verify - Verify that the transaction got validated by querying with [getTransaction](#gettransaction). This is necessary because transactions may fail even if they were successfully submitted.
## Transaction Fees
Every transaction requires a *fee* to be paid in XRP. The fee is destroyed; it is not sent to any other party. The purpose of the fee is to prevent denial of service attacks on the Ripple network.
Every transaction must destroy a small amount of XRP as a cost to send the transaction. This is also called a *transaction fee*. The transaction cost is designed to increase along with the load on the Ripple network, making it very expensive to deliberately or inadvertently overload the network.
You can choose the size of the fee you want to pay or let a default be used. The fee is like a bid in an auction for slots in the next ledger closing. If the fee you choose is too low, your transaction will not be included in the next ledger closing. You can get an estimate of the fee required to be included in the next ledger closing with the [getFee](#getfee) method.
You can choose the size of the fee you want to pay or let a default be used. You can get an estimate of the fee required to be included in the next ledger closing with the [getFee](#getfee) method.
## Transaction Instructions
Transactions instructions indicates how to execute a transaction, complementary with the [transaction specification](#transaction-specifications).
Transaction instructions indicate how to execute a transaction, complementary with the [transaction specification](#transaction-specifications).
<%- renderSchema("objects/instructions.json") %>
We recommended that you specify a `maxLedgerVersion` so that you can quickly determine that a failed transaction will never succeeed in the future. It is impossible for a transaction to succeed after the network ledger version exceeds the transaction's `maxLedgerVersion`. If you omit `maxLedgerVersion`, the "prepare*" method automatically supplies a `maxLedgerVersion` equal to the current ledger plus 3, which it includes in the return value from the "prepare*" method.
## Transaction ID
```json
"F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF"
```
A hash of the transaction that can be used to identify it. A transaction can be looked up by its ID using the [getTransaction](#gettransaction) method.
A transaction ID is a 64-bit hexadecimal string that uniquely identifies the transaction. The transaction ID is derived from the transaction instruction and specifications, using a strong hash function.
You can look up a transaction by ID using the [getTransaction](#gettransaction) method.
## Transaction Memos
Every transaction can optionally have an array of memos for user applications. The `memos` field in each [transaction specification](#transaction-specifications) is an array of objects with the following structure:
<%- renderSchema('objects/memos.json') %>

4258
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ripple-lib",
"version": "0.13.0",
"version": "0.17.5",
"license": "ISC",
"description": "A JavaScript API for interacting with Ripple in Node.js and the browser",
"files": [
@@ -15,39 +15,49 @@
"test": "test"
},
"dependencies": {
"ajv": "^1.4.8",
"babel-runtime": "^5.5.4",
"ajv": "^4.0.5",
"ajv-i18n": "^1.2.0",
"babel-polyfill": "^6.3.14",
"babel-runtime": "^6.3.19",
"bignumber.js": "^2.0.3",
"https-proxy-agent": "^1.0.0",
"jayson": "^1.2.2",
"lodash": "^3.1.0",
"ripple-address-codec": "^2.0.1",
"ripple-binary-codec": "^0.1.0",
"ripple-hashes": "^0.0.1",
"ripple-binary-codec": "^0.1.8",
"ripple-hashes": "^0.2.0",
"ripple-keypairs": "^0.10.0",
"ripple-lib-transactionparser": "^0.6.0",
"ws": "^0.7.1"
"ws": "^1.0.1"
},
"devDependencies": {
"assert-diff": "^1.0.1",
"babel": "^5.8.21",
"babel-core": "^5.8.22",
"babel-eslint": "^4.1.3",
"babel-loader": "^5.3.2",
"coveralls": "^2.10.0",
"babel-cli": "^6.4.0",
"babel-core": "^6.4.0",
"babel-eslint": "^6.0.4",
"babel-loader": "^6.2.1",
"babel-plugin-syntax-flow": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.4.0",
"babel-preset-es2015": "^6.3.13",
"babel-preset-stage-1": "^6.3.13",
"babel-register": "^6.3.13",
"coveralls": "^2.11.9",
"doctoc": "^0.15.0",
"ejs": "^2.3.4",
"eslint": "^1.3.0",
"eslint-plugin-flowtype": "^1.0.0",
"eslint": "^2.9.0",
"eventemitter2": "^0.4.14",
"flow-bin": "^0.14",
"gulp": "^3.8.10",
"gulp-bump": "^0.1.13",
"gulp-rename": "^1.2.0",
"gulp-uglify": "^1.1.0",
"istanbul": "^0.3.5",
"http-server": "^0.8.5",
"istanbul": "^1.1.0-alpha.1",
"json-loader": "^0.5.2",
"json-schema-to-markdown-table": "^0.4.0",
"mocha": "^2.1.0",
"mocha-in-sauce": "^0.0.1",
"mocha-junit-reporter": "^1.9.1",
"null-loader": "^0.1.1",
"webpack": "^1.5.3",
"yargs": "^1.3.1"
},
@@ -61,10 +71,12 @@
"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",
"test": "babel-node ./node_modules/.bin/istanbul cover ./node_modules/mocha/bin/_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'; echo 'parser: babel-eslint' >> eslintrc; fi; eslint -c eslintrc src/",
"perf": "./scripts/perf_test.sh"
"perf": "./scripts/perf_test.sh",
"start": "babel-node scripts/http.js",
"sauce": "babel-node scripts/sauce-runner.js"
},
"repository": {
"type": "git",

18
scripts/checkeol.sh Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/bash
function checkEOL {
local changedFiles=$(git --no-pager diff --name-only -M100% --diff-filter=AM --relative $(git merge-base FETCH_HEAD origin/HEAD) FETCH_HEAD)
local result=0
for name in $changedFiles; do
grep -c -U -q $'\r' $name
if [ $? -eq 0 ]; then
echo "windows eol found in $name" >&2
result=1
fi
done
if [ $result -eq 1 ]; then
false
fi
}
checkEOL

View File

@@ -3,6 +3,10 @@
NODE_INDEX="$1"
TOTAL_NODES="$2"
function checkEOL {
./scripts/checkeol.sh
}
typecheck() {
npm install -g flow-bin
flow --version
@@ -11,7 +15,7 @@ typecheck() {
lint() {
echo "eslint $(node_modules/.bin/eslint --version)"
npm list babel-eslint | grep babel-eslint
npm list babel-eslint
REPO_URL="https://raw.githubusercontent.com/ripple/javascript-style-guide"
curl "$REPO_URL/es6/eslintrc" > ./eslintrc
echo "parser: babel-eslint" >> ./eslintrc
@@ -20,6 +24,7 @@ lint() {
unittest() {
# test "src"
mocha test --reporter mocha-junit-reporter --reporter-options mochaFile=$CIRCLE_TEST_REPORTS/test-results.xml
npm test --coverage
npm run coveralls
@@ -29,11 +34,33 @@ unittest() {
mkdir -p test-compiled/node_modules
ln -nfs ../../dist/npm test-compiled/node_modules/ripple-api
mocha --opts test-compiled/mocha.opts test-compiled
#compile tests for browser testing
#gulp build-min build-tests
#node --harmony test-compiled/mocked-server.js > /dev/null &
#echo "Running tests in PhantomJS"
#mocha-phantomjs test/localrunner.html
#echo "Running tests using minified version in PhantomJS"
#mocha-phantomjs test/localrunnermin.html
#echo "Running tests in SauceLabs"
#http-server &
#npm run sauce
#pkill -f mocked-server.js
#pkill -f http-server
rm -rf test-compiled
}
integrationtest() {
mocha test/integration/integration-test.js
mocha test/integration/http-integration-test.js
# run integration tests in PhantomJS
#gulp build-tests build-min
#echo "Running integragtion tests in PhantomJS"
#mocha-phantomjs test/localintegrationrunner.html
}
doctest() {
@@ -46,9 +73,9 @@ doctest() {
}
oneNode() {
checkEOL
doctest
lint
typecheck
unittest
integrationtest
}
@@ -56,7 +83,7 @@ oneNode() {
twoNodes() {
case "$NODE_INDEX" in
0) doctest; lint; integrationtest;;
1) typecheck; unittest;;
1) checkEOL; unittest;;
*) echo "ERROR: invalid usage"; exit 2;;
esac
}
@@ -64,7 +91,7 @@ twoNodes() {
threeNodes() {
case "$NODE_INDEX" in
0) doctest; lint; integrationtest;;
1) typecheck;;
1) checkEOL;;
2) unittest;;
*) echo "ERROR: invalid usage"; exit 2;;
esac

16
scripts/http.js Normal file
View File

@@ -0,0 +1,16 @@
'use strict';
const createHTTPServer = require('../src/index').createHTTPServer;
const port = 5990;
const serverUrl = 'wss://s1.ripple.com';
function main() {
const server = createHTTPServer({server: serverUrl}, port);
server.start().then(() => {
console.log('Server started on port ' + String(port));
});
}
main();

View File

@@ -16,28 +16,3 @@ echo ""
echo "publish to npm"
npm publish
exit_on_error
rm -rf dist/bower
echo ""
echo "publish to bower"
git clone git@github.com:ripple/bower-ripple.git dist/bower
gulp bower
exit_on_error
cd dist/bower
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc([0-9])+)?')
echo "version: $version"
git add ripple.js ripple-debug.js ripple-min.js bower.json
exit_on_error
git commit -m "[TASK] add v$version"
exit_on_error
git tag "v$version"
exit_on_error
git push origin master
git push --tags origin master
cd ../..

View File

@@ -16,28 +16,3 @@ echo ""
echo "publish rc to npm"
npm publish --tag beta
exit_on_error
rm -rf dist/bower
echo ""
echo "publish to bower"
git clone git@github.com:ripple/bower-ripple.git dist/bower
gulp bower
exit_on_error
cd dist/bower
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc([0-9])+)?')
echo "version: $version"
git add ripple.js ripple-debug.js ripple-min.js bower.json
exit_on_error
git commit -m "[TASK] add v$version"
exit_on_error
git tag "v$version"
exit_on_error
git push origin master
git push --tags origin master
cd ../..

View File

@@ -1,12 +0,0 @@
rm -rf dist/bower
git clone git@github.com:ripple/bower-ripple.git dist/bower
gulp bower
cd dist/bower
version=$(cat bower.json | grep -Eo '([0-9]\.?)+(-rc[0-9])?')
echo "version: $version"
git add ripple.js ripple-debug.js ripple-min.js bower.json
git commit -m "[TASK] add v$version"
git tag "v$version"
git push origin master
git push --tags origin master
cd ..

100
scripts/sauce-runner.js Normal file
View File

@@ -0,0 +1,100 @@
const _ = require('lodash');
const MochaSauce = require('mocha-in-sauce');
const testUrl = 'http://testripple.circleci.com:8080/test/saucerunner.html';
function main() {
// uncomment for more debug info
// process.env.DEBUG = '*';
// configure
const config = {
name: 'RippleAPI',
host: 'localhost',
port: 4445,
maxDuration: 180000,
// the current build name (optional)
build: Date.now(),
url: testUrl,
runSauceConnect: true
};
if (process.env.CIRCLE_BUILD_NUM) {
config.build = process.env.CIRCLE_BUILD_NUM;
config.tags = [process.env.CIRCLE_BRANCH, process.env.CIRCLE_SHA1];
config.tunnelIdentifier = process.env.CIRCLE_BUILD_NUM;
}
const sauce = new MochaSauce(config);
sauce.concurrency(5);
// setup what browsers to test with
sauce.browser({browserName: 'firefox', platform: 'Linux',
version: '43'});
sauce.browser({browserName: 'firefox', platform: 'Windows 8.1',
version: '43'});
sauce.browser({browserName: 'firefox', platform: 'OS X 10.11',
version: '43'});
sauce.browser({browserName: 'safari', platform: 'OS X 10.11',
version: '9'});
sauce.browser({browserName: 'safari', platform: 'OS X 10.10',
version: '8'});
sauce.browser({browserName: 'safari', platform: 'OS X 10.9',
version: '7'});
sauce.browser({browserName: 'chrome', platform: 'OS X 10.11',
version: '47'});
sauce.browser({browserName: 'chrome', platform: 'Linux',
version: '47'});
sauce.browser({browserName: 'chrome', platform: 'Windows 8.1',
version: '47'});
sauce.browser({browserName: 'internet explorer', platform: 'Windows 10',
version: '11'});
sauce.browser({browserName: 'MicrosoftEdge', platform: 'Windows 10',
version: '20'});
sauce.on('init', function(browser) {
console.log(' init : %s %s', browser.browserName, browser.platform);
});
sauce.on('start', function(browser) {
console.log(' start : %s %s', browser.browserName, browser.platform);
});
sauce.on('end', function(browser, res) {
console.log(' end : %s %s : %d failures', browser.browserName,
browser.platform, res && res.failures);
});
sauce.on('connected', sauceConnectProcess => {
sauceConnectProcess.on('exit', function(code, /* signal */) {
if (code > 0) {
console.log('something wrong - exiting');
process.exit();
} else {
console.log('normal tunnel exit');
}
});
});
sauce.start(function(err, res) {
let failure = false;
if (err) {
console.log('Error starting Sauce');
console.error(err);
process.exitCode = 2;
} else {
console.log('-------------- done --------------');
failure = _.some(res, 'failures');
console.log('Tests are failed:', failure);
if (failure) {
process.exitCode = 1;
}
}
});
}
main();

153
src/api.js Normal file
View File

@@ -0,0 +1,153 @@
/* @flow */
'use strict' // eslint-disable-line strict
/* eslint-disable max-len */
// Enable core-js polyfills. This allows use of ES6/7 extensions listed here:
// https://github.com/zloirock/core-js/blob/fb0890f32dabe8d4d88a4350d1b268446127132e/shim.js#L1-L103
/* eslint-enable max-len */
// In node.js env, polyfill might be already loaded (from any npm package),
// that's why we do this check.
if (!global._babelPolyfill) {
require('babel-polyfill')
}
const _ = require('lodash')
const EventEmitter = require('events').EventEmitter
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 getBalanceSheet = require('./ledger/balance-sheet')
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 prepareSuspendedPaymentCreation =
require('./transaction/suspended-payment-creation')
const prepareSuspendedPaymentExecution =
require('./transaction/suspended-payment-execution')
const prepareSuspendedPaymentCancellation =
require('./transaction/suspended-payment-cancellation')
const prepareSettings = require('./transaction/settings')
const sign = require('./transaction/sign')
const combine = require('./transaction/combine')
const submit = require('./transaction/submit')
const errors = require('./common').errors
const generateAddress =
require('./offline/generate-address').generateAddressAPI
const computeLedgerHash = require('./offline/ledgerhash')
const getLedger = require('./ledger/ledger')
type APIOptions = {
server?: string,
feeCushion?: number,
trace?: boolean,
proxy?: string,
timeout?: number
}
// prevent access to non-validated ledger versions
class RestrictedConnection extends common.Connection {
request(request, timeout) {
const ledger_index = request.ledger_index
if (ledger_index !== undefined && ledger_index !== 'validated') {
if (!_.isNumber(ledger_index) || ledger_index > this._ledgerVersion) {
return Promise.reject(new errors.LedgerVersionError(
`ledgerVersion ${ledger_index} is greater than server\'s ` +
`most recent validated ledger: ${this._ledgerVersion}`))
}
}
return super.request(request, timeout)
}
}
class RippleAPI extends EventEmitter {
constructor(options: APIOptions = {}) {
common.validate.apiOptions(options)
super()
this._feeCushion = options.feeCushion || 1.2
const serverURL = options.server
if (serverURL !== undefined) {
this.connection = new RestrictedConnection(serverURL, options)
this.connection.on('ledgerClosed', message => {
this.emit('ledger', server.formatLedgerClose(message))
})
this.connection.on('error', (errorCode, errorMessage, data) => {
this.emit('error', errorCode, errorMessage, data)
})
this.connection.on('connected', () => {
this.emit('connected')
})
this.connection.on('disconnected', code => {
this.emit('disconnected', code)
})
} else {
// use null object pattern to provide better error message if user
// tries to call a method that requires a connection
this.connection = new RestrictedConnection(null, options)
}
}
}
_.assign(RippleAPI.prototype, {
connect,
disconnect,
isConnected,
getServerInfo,
getFee,
getLedgerVersion,
getTransaction,
getTransactions,
getTrustlines,
getBalances,
getBalanceSheet,
getPaths,
getOrders,
getOrderbook,
getSettings,
getAccountInfo,
getLedger,
preparePayment,
prepareTrustline,
prepareOrder,
prepareOrderCancellation,
prepareSuspendedPaymentCreation,
prepareSuspendedPaymentExecution,
prepareSuspendedPaymentCancellation,
prepareSettings,
sign,
combine,
submit,
generateAddress,
computeLedgerHash,
errors
})
// these are exposed only for use by unit tests; they are not part of the API
RippleAPI._PRIVATE = {
validate: common.validate,
RangeSet: require('./common/rangeset').RangeSet,
ledgerUtils: require('./ledger/utils'),
schemaValidator: require('./common/schema-validator')
}
module.exports = {
RippleAPI
}

70
src/broadcast.js Normal file
View File

@@ -0,0 +1,70 @@
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const RippleAPI = require('./api').RippleAPI
class RippleAPIBroadcast extends RippleAPI {
constructor(servers, options) {
super(options)
this.ledgerVersion = 0
const apis = servers.map(server => new RippleAPI(
_.assign({}, options, {server})
))
// exposed for testing
this._apis = apis
this.getMethodNames().forEach(name => {
this[name] = function() { // eslint-disable-line no-loop-func
return Promise.race(apis.map(api => api[name](...arguments)))
}
})
// connection methods must be overridden to apply to all api instances
this.connect = function() {
return Promise.all(apis.map(api => api.connect()))
}
this.disconnect = function() {
return Promise.all(apis.map(api => api.disconnect()))
}
this.isConnected = function() {
return _.every(apis.map(api => api.isConnected()))
}
// synchronous methods are all passed directly to the first api instance
const defaultAPI = apis[0]
const syncMethods = ['sign', 'generateAddress', 'computeLedgerHash']
syncMethods.forEach(name => {
this[name] = defaultAPI[name].bind(defaultAPI)
})
apis.forEach(api => {
api.on('ledger', this.onLedgerEvent.bind(this))
api.on('error', (errorCode, errorMessage, data) =>
this.emit('error', errorCode, errorMessage, data))
})
}
onLedgerEvent(ledger) {
if (ledger.ledgerVersion > this.ledgerVersion) {
this.ledgerVersion = ledger.ledgerVersion
this.emit('ledger', ledger)
}
}
getMethodNames() {
const methodNames = []
for (const name in RippleAPI.prototype) {
if (RippleAPI.prototype.hasOwnProperty(name)) {
if (typeof RippleAPI.prototype[name] === 'function') {
methodNames.push(name)
}
}
}
return methodNames
}
}
module.exports = {
RippleAPIBroadcast
}

View File

@@ -0,0 +1,21 @@
'use strict' // eslint-disable-line strict
function setPrototypeOf(object, prototype) {
// Object.setPrototypeOf not supported on Internet Explorer 9
/* eslint-disable */
Object.setPrototypeOf ? Object.setPrototypeOf(object, prototype) :
object.__proto__ = prototype;
/* eslint-enable */
}
function getConstructorName(object) {
// hack for internet explorer
return process.browser ?
object.constructor.toString().match(/^function\s+([^(]*)/)[1] :
object.constructor.name
}
module.exports = {
getConstructorName,
setPrototypeOf
}

View File

@@ -1,256 +1,432 @@
'use strict';
const {EventEmitter} = require('events');
const WebSocket = require('ws');
const parseURL = require('url').parse;
const RangeSet = require('./rangeset').RangeSet;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const {EventEmitter} = require('events')
const WebSocket = require('ws')
const parseURL = require('url').parse
const RangeSet = require('./rangeset').RangeSet
const {RippledError, DisconnectedError, NotConnectedError,
TimeoutError, ResponseFormatError, ConnectionError} = require('./errors');
TimeoutError, ResponseFormatError, ConnectionError,
RippledNotInitializedError} = require('./errors')
function isStreamMessageType(type) {
return type === 'ledgerClosed' ||
type === 'transaction' ||
type === 'path_find';
type === 'path_find'
}
class Connection extends EventEmitter {
constructor(url, options = {}) {
super();
this._url = url;
this._proxyURL = options.proxyURL;
this._authorization = options.authorization;
this._timeout = options.timeout || (20 * 1000);
this._isReady = false;
this._ws = null;
this._ledgerVersion = null;
this._availableLedgerVersions = new RangeSet();
this._nextRequestID = 1;
super()
this.setMaxListeners(Infinity)
this._url = url
this._trace = options.trace
if (this._trace) {
// for easier unit testing
this._console = console
}
this._proxyURL = options.proxy
this._proxyAuthorization = options.proxyAuthorization
this._authorization = options.authorization
this._trustedCertificates = options.trustedCertificates
this._key = options.key
this._passphrase = options.passphrase
this._certificate = options.certificate
this._timeout = options.timeout || (20 * 1000)
this._isReady = false
this._ws = null
this._ledgerVersion = null
this._availableLedgerVersions = new RangeSet()
this._nextRequestID = 1
this._retry = 0
this._retryTimer = null
this._onOpenErrorBound = null
this._onUnexpectedCloseBound = null
}
_updateLedgerVersions(data) {
this._ledgerVersion = Number(data.ledger_index)
if (data.validated_ledgers) {
this._availableLedgerVersions.reset()
this._availableLedgerVersions.parseAndAddRanges(
data.validated_ledgers)
} else {
this._availableLedgerVersions.addValue(this._ledgerVersion)
}
}
// return value is array of arguments to Connection.emit
_parseMessage(message) {
const data = JSON.parse(message);
const data = JSON.parse(message)
if (data.type === 'response') {
if (!(Number.isInteger(data.id) && data.id >= 0)) {
throw new ResponseFormatError('valid id not found in response');
throw new ResponseFormatError('valid id not found in response')
}
return [data.id.toString(), data];
return [data.id.toString(), data]
} else if (isStreamMessageType(data.type)) {
if (data.type === 'ledgerClosed') {
this._ledgerVersion = Number(data.ledger_index);
this._availableLedgerVersions.reset();
this._availableLedgerVersions.parseAndAddRanges(
data.validated_ledgers);
this._updateLedgerVersions(data)
}
return [data.type, data];
return [data.type, data]
} else if (data.type === undefined && data.error) {
return ['error', data.error, data.error_message]; // e.g. slowDown
return ['error', data.error, data.error_message, data] // e.g. slowDown
}
throw new ResponseFormatError('unrecognized message type: ' + data.type);
throw new ResponseFormatError('unrecognized message type: ' + data.type)
}
_onMessage(message) {
let parameters;
let parameters
if (this._trace) {
this._console.log(message)
}
try {
parameters = this._parseMessage(message);
parameters = this._parseMessage(message)
} catch (error) {
this.emit('error', 'badMessage', message);
return;
this.emit('error', 'badMessage', error.message, message)
return
}
// we don't want this inside the try/catch or exceptions in listener
// will be caught
this.emit.apply(this, parameters);
this.emit(...parameters)
}
get _state() {
return this._ws ? this._ws.readyState : WebSocket.CLOSED;
return this._ws ? this._ws.readyState : WebSocket.CLOSED
}
get _shouldBeConnected() {
return this._ws !== null;
return this._ws !== null
}
isConnected() {
return this._state === WebSocket.OPEN && this._isReady;
return this._state === WebSocket.OPEN && this._isReady
}
_onUnexpectedClose() {
this._isReady = false;
this.connect().then();
_onUnexpectedClose(beforeOpen, resolve, reject, code) {
if (this._onOpenErrorBound) {
this._ws.removeListener('error', this._onOpenErrorBound)
this._onOpenErrorBound = null
}
// just in case
this._ws.removeAllListeners('open')
this._ws = null
this._isReady = false
if (beforeOpen) {
// connection was closed before it was properly opened, so we must return
// error to connect's caller
this.connect().then(resolve, reject)
} else {
// if first parameter ws lib sends close code,
// but sometimes it forgots about it, so default to 1006 - CLOSE_ABNORMAL
this.emit('disconnected', code || 1006)
this._retryConnect()
}
}
_calculateTimeout(retriesCount) {
return (retriesCount < 40)
// First, for 2 seconds: 20 times per second
? (1000 / 20)
: (retriesCount < 40 + 60)
// Then, for 1 minute: once per second
? (1000)
: (retriesCount < 40 + 60 + 60)
// Then, for 10 minutes: once every 10 seconds
? (10 * 1000)
// Then: once every 30 seconds
: (30 * 1000)
}
_retryConnect() {
this._retry += 1
const retryTimeout = this._calculateTimeout(this._retry)
this._retryTimer = setTimeout(() => {
this.emit('reconnecting', this._retry)
this.connect().catch(this._retryConnect.bind(this))
}, retryTimeout)
}
_clearReconnectTimer() {
clearTimeout(this._retryTimer)
this._retryTimer = null
}
_onOpen() {
if (!this._ws) {
return Promise.reject(new DisconnectedError())
}
if (this._onOpenErrorBound) {
this._ws.removeListener('error', this._onOpenErrorBound)
this._onOpenErrorBound = null
}
const request = {
command: 'subscribe',
streams: ['ledger']
};
return this.request(request).then(response => {
this._ledgerVersion = Number(response.ledger_index);
this._availableLedgerVersions.parseAndAddRanges(
response.validated_ledgers);
this._isReady = true;
this.emit('connected');
});
}
return this.request(request).then(data => {
if (_.isEmpty(data) || !data.ledger_index) {
// rippled instance doesn't have validated ledgers
return this._disconnect(false).then(() => {
throw new RippledNotInitializedError('Rippled not initialized')
})
}
this._updateLedgerVersions(data)
this._rebindOnUnxpectedClose()
this._retry = 0
this._ws.on('error', error => {
if (process.browser && error && error.type === 'error') {
// we are in browser, ignore error - `close` event will be fired
// after error
return
}
this.emit('error', 'websocket', error.message, error)
})
this._isReady = true
this.emit('connected')
return undefined
})
}
_createWebSocket(url, proxyURL, authorization) {
const options = {};
if (proxyURL !== undefined) {
const parsedURL = parseURL(url);
const proxyOptions = parseURL(proxyURL);
proxyOptions.secureEndpoint = (parsedURL.protocol === 'wss:');
let HttpsProxyAgent;
_rebindOnUnxpectedClose() {
if (this._onUnexpectedCloseBound) {
this._ws.removeListener('close', this._onUnexpectedCloseBound)
}
this._onUnexpectedCloseBound =
this._onUnexpectedClose.bind(this, false, null, null)
this._ws.once('close', this._onUnexpectedCloseBound)
}
_unbindOnUnxpectedClose() {
if (this._onUnexpectedCloseBound) {
this._ws.removeListener('close', this._onUnexpectedCloseBound)
}
this._onUnexpectedCloseBound = null
}
_onOpenError(reject, error) {
this._onOpenErrorBound = null
this._unbindOnUnxpectedClose()
reject(new NotConnectedError(error && error.message))
}
_createWebSocket() {
const options = {}
if (this._proxyURL !== undefined) {
const parsedURL = parseURL(this._url)
const parsedProxyURL = parseURL(this._proxyURL)
const proxyOverrides = _.omit({
secureEndpoint: (parsedURL.protocol === 'wss:'),
secureProxy: (parsedProxyURL.protocol === 'https:'),
auth: this._proxyAuthorization,
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined)
const proxyOptions = _.assign({}, parsedProxyURL, proxyOverrides)
let HttpsProxyAgent
try {
HttpsProxyAgent = require('https-proxy-agent');
HttpsProxyAgent = require('https-proxy-agent')
} catch (error) {
throw new Error('"proxy" option is not supported in the browser');
throw new Error('"proxy" option is not supported in the browser')
}
options.agent = new HttpsProxyAgent(proxyOptions);
options.agent = new HttpsProxyAgent(proxyOptions)
}
if (authorization !== undefined) {
const base64 = new Buffer(authorization).toString('base64');
options.headers = {Authorization: `Basic ${base64}`};
if (this._authorization !== undefined) {
const base64 = new Buffer(this._authorization).toString('base64')
options.headers = {Authorization: `Basic ${base64}`}
}
return new WebSocket(url, options);
const optionsOverrides = _.omit({
ca: this._trustedCertificates,
key: this._key,
passphrase: this._passphrase,
cert: this._certificate
}, _.isUndefined)
const websocketOptions = _.assign({}, options, optionsOverrides)
const websocket = new WebSocket(this._url, null, websocketOptions)
// we will have a listener for each outstanding request,
// so we have to raise the limit (the default is 10)
if (typeof websocket.setMaxListeners === 'function') {
websocket.setMaxListeners(Infinity)
}
return websocket
}
connect() {
this._clearReconnectTimer()
return new Promise((resolve, reject) => {
if (!this._url) {
reject(new ConnectionError(
'Cannot connect because no server was specified'));
'Cannot connect because no server was specified'))
}
if (this._state === WebSocket.OPEN) {
resolve();
resolve()
} else if (this._state === WebSocket.CONNECTING) {
this._ws.once('open', resolve);
this._ws.once('open', resolve)
} else {
this._ws = this._createWebSocket(this._url, this._proxyURL,
this._authorization);
this._ws.on('message', this._onMessage.bind(this));
this._ws.once('close', () => this._onUnexpectedClose);
this._ws.once('open', () => this._onOpen().then(resolve, reject));
this._ws = this._createWebSocket()
// when an error causes the connection to close, the close event
// should still be emitted; the "ws" documentation says: "The close
// event is also emitted when then underlying net.Socket closes the
// connection (end or close)."
// In case if there is connection error (say, server is not responding)
// we must return this error to connection's caller. After successful
// opening, we will forward all errors to main api object.
this._onOpenErrorBound = this._onOpenError.bind(this, reject)
this._ws.once('error', this._onOpenErrorBound)
this._ws.on('message', this._onMessage.bind(this))
// in browser close event can came before open event, so we must
// resolve connect's promise after reconnect in that case.
// after open event we will rebound _onUnexpectedCloseBound
// without resolve and reject functions
this._onUnexpectedCloseBound = this._onUnexpectedClose.bind(this, true,
resolve, reject)
this._ws.once('close', this._onUnexpectedCloseBound)
this._ws.once('open', () => this._onOpen().then(resolve, reject))
}
});
})
}
disconnect() {
return this._disconnect(true)
}
_disconnect(calledByUser) {
if (calledByUser) {
this._clearReconnectTimer()
this._retry = 0
}
return new Promise(resolve => {
if (this._state === WebSocket.CLOSED) {
resolve();
resolve()
} else if (this._state === WebSocket.CLOSING) {
this._ws.once('close', resolve);
this._ws.once('close', resolve)
} else {
this._ws.removeListener('close', this._onUnexpectedClose);
this._ws.once('close', () => {
this._ws = null;
this._isReady = false;
resolve();
});
this._ws.close();
if (this._onUnexpectedCloseBound) {
this._ws.removeListener('close', this._onUnexpectedCloseBound)
this._onUnexpectedCloseBound = null
}
this._ws.once('close', code => {
this._ws = null
this._isReady = false
if (calledByUser) {
this.emit('disconnected', code || 1000) // 1000 - CLOSE_NORMAL
}
resolve()
})
this._ws.close()
}
});
})
}
reconnect() {
return this.disconnect().then(() => this.connect());
return this.disconnect().then(() => this.connect())
}
_whenReady(promise) {
return new Promise((resolve, reject) => {
if (!this._shouldBeConnected) {
reject(new NotConnectedError());
reject(new NotConnectedError())
} else if (this._state === WebSocket.OPEN && this._isReady) {
promise.then(resolve, reject);
promise.then(resolve, reject)
} else {
this.once('connected', () => promise.then(resolve, reject));
this.once('connected', () => promise.then(resolve, reject))
}
});
})
}
getLedgerVersion() {
return this._whenReady(Promise.resolve(this._ledgerVersion));
return this._whenReady(Promise.resolve(this._ledgerVersion))
}
hasLedgerVersions(lowLedgerVersion, highLedgerVersion) {
return this._whenReady(Promise.resolve(
this._availableLedgerVersions.containsRange(
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)));
lowLedgerVersion, highLedgerVersion || this._ledgerVersion)))
}
hasLedgerVersion(ledgerVersion) {
return this.hasLedgerVersions(ledgerVersion, ledgerVersion);
return this.hasLedgerVersions(ledgerVersion, ledgerVersion)
}
_send(message) {
if (this._trace) {
this._console.log(message)
}
return new Promise((resolve, reject) => {
this._ws.send(message, undefined, (error, result) => {
if (error) {
reject(new DisconnectedError(error.message));
reject(new DisconnectedError(error.message))
} else {
resolve(result);
resolve(result)
}
});
});
})
})
}
request(request, timeout) {
return new Promise((resolve, reject) => {
if (!this._shouldBeConnected) {
reject(new NotConnectedError());
reject(new NotConnectedError())
}
let timer = null;
const self = this;
const id = this._nextRequestID;
this._nextRequestID += 1;
const eventName = id.toString();
let timer = null
const self = this
const id = this._nextRequestID
this._nextRequestID += 1
const eventName = id.toString()
function onDisconnect() {
clearTimeout(timer);
self.removeAllListeners(eventName);
reject(new DisconnectedError());
clearTimeout(timer)
self.removeAllListeners(eventName)
reject(new DisconnectedError())
}
function cleanup() {
clearTimeout(timer);
self.removeAllListeners(eventName);
clearTimeout(timer)
self.removeAllListeners(eventName)
if (self._ws !== null) {
self._ws.removeListener('close', onDisconnect);
self._ws.removeListener('close', onDisconnect)
}
}
function _resolve(response) {
cleanup();
resolve(response);
cleanup()
resolve(response)
}
function _reject(error) {
cleanup();
reject(error);
cleanup()
reject(error)
}
this.once(eventName, response => {
if (response.status === 'error') {
_reject(new RippledError(response.error));
_reject(new RippledError(response.error))
} else if (response.status === 'success') {
_resolve(response.result);
_resolve(response.result)
} else {
_reject(new ResponseFormatError(
'unrecognized status: ' + response.status));
'unrecognized status: ' + response.status))
}
});
})
this._ws.once('close', onDisconnect);
this._ws.once('close', onDisconnect)
// JSON.stringify automatically removes keys with value of 'undefined'
const message = JSON.stringify(Object.assign({}, request, {id}));
const message = JSON.stringify(Object.assign({}, request, {id}))
this._whenReady(this._send(message)).then(() => {
const delay = timeout || this._timeout;
timer = setTimeout(() => _reject(new TimeoutError()), delay);
}).catch(_reject);
});
const delay = timeout || this._timeout
timer = setTimeout(() => _reject(new TimeoutError()), delay)
}).catch(_reject)
})
}
}
module.exports = Connection;
module.exports = Connection

View File

@@ -1,5 +1,5 @@
'use strict';
const flagIndices = require('./txflags').txFlagIndices.AccountSet;
'use strict' // eslint-disable-line strict
const flagIndices = require('./txflags').txFlagIndices.AccountSet
const accountRootFlags = {
PasswordSpent: 0x00010000, // password set fee is spent
@@ -10,7 +10,7 @@ const accountRootFlags = {
NoFreeze: 0x00200000, // permanently disallowed freezing trustlines
GlobalFreeze: 0x00400000, // trustlines globally frozen
DefaultRipple: 0x00800000
};
}
const AccountFlags = {
passwordSpent: accountRootFlags.PasswordSpent,
@@ -21,7 +21,7 @@ const AccountFlags = {
noFreeze: accountRootFlags.NoFreeze,
globalFreeze: accountRootFlags.GlobalFreeze,
defaultRipple: accountRootFlags.DefaultRipple
};
}
const AccountFlagIndices = {
requireDestinationTag: flagIndices.asfRequireDest,
@@ -32,7 +32,7 @@ const AccountFlagIndices = {
noFreeze: flagIndices.asfNoFreeze,
globalFreeze: flagIndices.asfGlobalFreeze,
defaultRipple: flagIndices.asfDefaultRipple
};
}
const AccountFields = {
EmailHash: {name: 'emailHash', encoding: 'hex',
@@ -40,10 +40,10 @@ const AccountFields = {
MessageKey: {name: 'messageKey'},
Domain: {name: 'domain', encoding: 'hex'},
TransferRate: {name: 'transferRate', defaults: 0, shift: 9}
};
}
module.exports = {
AccountFields,
AccountFlagIndices,
AccountFlags
};
}

View File

@@ -1,29 +1,43 @@
'use strict';
const util = require('util');
'use strict' // eslint-disable-line strict
const util = require('util')
const browserHacks = require('./browser-hacks')
class RippleError extends Error {
// this is needed because extending builtins doesn't work in babel 6.x
function extendableBuiltin(cls) {
function ExtendableBuiltin() {
cls.apply(this, arguments)
}
ExtendableBuiltin.prototype = Object.create(cls.prototype)
browserHacks.setPrototypeOf(ExtendableBuiltin, cls)
return ExtendableBuiltin
}
class RippleError extends extendableBuiltin(Error) {
constructor(message, data) {
super(message);
this.name = this.constructor.name;
this.message = message;
this.data = data;
Error.captureStackTrace(this, this.constructor.name);
super(message)
this.name = browserHacks.getConstructorName(this)
this.message = message
this.data = data
if (Error.captureStackTrace) {
Error.captureStackTrace(this, this.constructor.name)
}
}
toString() {
let result = '[' + this.name + '(' + this.message;
let result = '[' + this.name + '(' + this.message
if (this.data) {
result += ', ' + util.inspect(this.data);
result += ', ' + util.inspect(this.data)
}
result += ')]';
return result;
result += ')]'
return result
}
/* console.log in node uses util.inspect on object, and util.inspect allows
us to cutomize its output:
https://nodejs.org/api/util.html#util_custom_inspect_function_on_objects */
inspect() {
return this.toString();
return this.toString()
}
}
@@ -39,6 +53,8 @@ class NotConnectedError extends ConnectionError {}
class DisconnectedError extends ConnectionError {}
class RippledNotInitializedError extends ConnectionError {}
class TimeoutError extends ConnectionError {}
class ResponseFormatError extends ConnectionError {}
@@ -47,20 +63,20 @@ class ValidationError extends RippleError {}
class NotFoundError extends RippleError {
constructor(message) {
super(message || 'Not found');
super(message || 'Not found')
}
}
class MissingLedgerHistoryError extends RippleError {
constructor(message) {
super(message || 'Server is missing ledger history in the specified range');
super(message || 'Server is missing ledger history in the specified range')
}
}
class PendingLedgerVersionError extends RippleError {
constructor(message) {
super(message || 'maxLedgerVersion is greater than server\'s'
+ ' most recent validated ledger');
+ ' most recent validated ledger')
}
}
@@ -71,6 +87,7 @@ module.exports = {
RippledError,
NotConnectedError,
DisconnectedError,
RippledNotInitializedError,
TimeoutError,
ResponseFormatError,
ValidationError,
@@ -78,4 +95,4 @@ module.exports = {
PendingLedgerVersionError,
MissingLedgerHistoryError,
LedgerVersionError
};
}

View File

@@ -1,5 +1,5 @@
'use strict';
const utils = require('./utils');
'use strict' // eslint-disable-line strict
const utils = require('./utils')
module.exports = {
Connection: require('./connection'),
@@ -19,4 +19,4 @@ module.exports = {
iso8601ToRippleTime: utils.iso8601ToRippleTime,
rippleTimeToISO8601: utils.rippleTimeToISO8601,
isValidSecret: utils.isValidSecret
};
}

View File

@@ -1,61 +1,61 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const ranges = Symbol();
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const ranges = Symbol()
function mergeIntervals(intervals: Array<[number, number]>) {
const stack = [[-Infinity, -Infinity]];
const stack = [[-Infinity, -Infinity]]
_.forEach(_.sortBy(intervals, x => x[0]), interval => {
const lastInterval = stack.pop();
const lastInterval = stack.pop()
if (interval[0] <= lastInterval[1] + 1) {
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])]);
stack.push([lastInterval[0], Math.max(interval[1], lastInterval[1])])
} else {
stack.push(lastInterval);
stack.push(interval);
stack.push(lastInterval)
stack.push(interval)
}
});
return stack.slice(1);
})
return stack.slice(1)
}
class RangeSet {
constructor() {
this.reset();
this.reset()
}
reset() {
this[ranges] = [];
this[ranges] = []
}
serialize() {
return this[ranges].map(range =>
range[0].toString() + '-' + range[1].toString()).join(',');
range[0].toString() + '-' + range[1].toString()).join(',')
}
addRange(start: number, end: number) {
assert(start <= end, 'invalid range');
this[ranges] = mergeIntervals(this[ranges].concat([[start, end]]));
assert(start <= end, 'invalid range')
this[ranges] = mergeIntervals(this[ranges].concat([[start, end]]))
}
addValue(value: number) {
this.addRange(value, value);
this.addRange(value, value)
}
parseAndAddRanges(rangesString: string) {
const rangeStrings = rangesString.split(',');
const rangeStrings = rangesString.split(',')
_.forEach(rangeStrings, rangeString => {
const range = rangeString.split('-').map(Number);
this.addRange(range[0], range.length === 1 ? range[0] : range[1]);
});
const range = rangeString.split('-').map(Number)
this.addRange(range[0], range.length === 1 ? range[0] : range[1])
})
}
containsRange(start: number, end: number) {
return _.some(this[ranges], range => range[0] <= start && range[1] >= end);
return _.some(this[ranges], range => range[0] <= start && range[1] >= end)
}
containsValue(value: number) {
return this.containsRange(value, value);
return this.containsRange(value, value)
}
}
module.exports.RangeSet = RangeSet;
module.exports.RangeSet = RangeSet

View File

@@ -1,12 +1,12 @@
// flow is disabled for this file until support for requiring json is added:
// https://github.com/facebook/flow/issues/167
'use strict';
const _ = require('lodash');
const assert = require('assert');
const Ajv = require('ajv');
const ValidationError = require('./errors').ValidationError;
const {isValidAddress} = require('ripple-address-codec');
const {isValidSecret} = require('./utils');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const Ajv = require('ajv')
const ValidationError = require('./errors').ValidationError
const {isValidAddress} = require('ripple-address-codec')
const {isValidSecret} = require('./utils')
function loadSchemas() {
// listed explicitly for webpack (instead of scanning schemas directory)
@@ -43,6 +43,7 @@ function loadSchemas() {
require('./schemas/objects/signed-value.json'),
require('./schemas/objects/orderbook.json'),
require('./schemas/objects/instructions.json'),
require('./schemas/objects/settings.json'),
require('./schemas/specifications/settings.json'),
require('./schemas/specifications/payment.json'),
require('./schemas/specifications/suspended-payment-cancellation.json'),
@@ -55,13 +56,13 @@ function loadSchemas() {
require('./schemas/output/submit.json'),
require('./schemas/output/get-account-info.json'),
require('./schemas/output/get-balances.json'),
require('./schemas/output/get-balance-sheet'),
require('./schemas/output/get-balance-sheet.json'),
require('./schemas/output/get-ledger.json'),
require('./schemas/output/get-orderbook.json'),
require('./schemas/output/get-orders.json'),
require('./schemas/output/order-change.json'),
require('./schemas/output/prepare.json'),
require('./schemas/output/ledger-closed.json'),
require('./schemas/output/ledger-event.json'),
require('./schemas/output/get-paths.json'),
require('./schemas/output/get-server-info.json'),
require('./schemas/output/get-settings.json'),
@@ -90,30 +91,32 @@ function loadSchemas() {
require('./schemas/input/prepare-suspended-payment-creation.json'),
require('./schemas/input/prepare-suspended-payment-cancellation.json'),
require('./schemas/input/prepare-suspended-payment-execution.json'),
require('./schemas/input/compute-ledger-hash'),
require('./schemas/input/sign'),
require('./schemas/input/submit')
];
const titles = _.map(schemas, schema => schema.title);
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1));
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates);
const ajv = new Ajv();
_.forEach(schemas, schema => ajv.addSchema(schema, schema.title));
ajv.addFormat('address', isValidAddress);
ajv.addFormat('secret', isValidSecret);
return ajv;
require('./schemas/input/compute-ledger-hash.json'),
require('./schemas/input/sign.json'),
require('./schemas/input/submit.json'),
require('./schemas/input/generate-address.json'),
require('./schemas/input/combine.json')
]
const titles = _.map(schemas, schema => schema.title)
const duplicates = _.keys(_.pick(_.countBy(titles), count => count > 1))
assert(duplicates.length === 0, 'Duplicate schemas for: ' + duplicates)
const ajv = new Ajv()
_.forEach(schemas, schema => ajv.addSchema(schema, schema.title))
ajv.addFormat('address', isValidAddress)
ajv.addFormat('secret', isValidSecret)
return ajv
}
const ajv = loadSchemas();
const ajv = loadSchemas()
function schemaValidate(schemaName: string, object: any): void {
const isValid = ajv.validate(schemaName, object);
const isValid = ajv.validate(schemaName, object)
if (!isValid) {
throw new ValidationError(ajv.errorsText());
throw new ValidationError(ajv.errorsText())
}
}
module.exports = {
schemaValidate,
isValidSecret
};
}

View File

@@ -3,21 +3,57 @@
"title": "api-options",
"type": "object",
"properties": {
"trace": {"type": "boolean"},
"feeCushion": {"$ref": "value"},
"servers": {
"type": "array",
"items": {
"type": "string",
"format": "uri",
"pattern": "^wss?://"
}
"trace": {
"type": "boolean",
"description": "If true, log rippled requests and responses to stdout."
},
"feeCushion": {
"type": "number",
"minimum": 1,
"description": "Factor to multiply estimated fee by to provide a cushion in case the required fee rises during submission of a transaction. Defaults to `1.2`."
},
"server": {
"type": "string",
"description": "URI for rippled websocket port to connect to. Must start with `wss://` or `ws://`.",
"format": "uri",
"pattern": "^wss?://"
},
"proxy": {
"format": "uri"
"format": "uri",
"description": "URI for HTTP/HTTPS proxy to use to connect to the rippled server."
},
"timeout": {
"type": "integer",
"description": "Timeout in milliseconds before considering a request to have failed.",
"minimum": 1
},
"proxyAuthorization": {
"type": "string",
"description": "Username and password for HTTP basic authentication to the proxy in the format **username:password**."
},
"authorization": {
"type": "string"
"type": "string",
"description": "Username and password for HTTP basic authentication to the rippled server in the format **username:password**."
},
"trustedCertificates": {
"type": "array",
"description": "Array of PEM-formatted SSL certificates to trust when connecting to a proxy. This is useful if you want to use a self-signed certificate on the proxy server. Note: Each element must contain a single certificate; concatenated certificates are not valid.",
"items": {
"type": "string",
"description": "A PEM-formatted SSL certificate to trust when connecting to a proxy."
}
},
"key": {
"type": "string",
"description": "A string containing the private key of the client in PEM format. (Can be an array of keys)."
},
"passphrase": {
"type": "string",
"description": "The passphrase for the private key of the client."
},
"certificate": {
"type": "string",
"description": "A string containing the certificate key of the client in PEM format. (Can be an array of certificates)."
}
},
"additionalProperties": false

View File

@@ -0,0 +1,19 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "combineParameters",
"type": "object",
"properties": {
"signedTransactions": {
"type": "array",
"description": "An array of signed transactions (from the output of [sign](#sign)) to combine.",
"items": {
"type": "string",
"pattern": "^[A-F0-9]+$",
"description": "A single-signed transaction represented as an uppercase hexadecimal string (from the output of [sign](#sign))"
},
"minLength": 1
}
},
"additionalProperties": false,
"required": ["signedTransactions"]
}

View File

@@ -0,0 +1,29 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "generateAddressParameters",
"type": "object",
"properties": {
"options": {
"type": "object",
"description": "Options to control how the address and secret are generated.",
"properties": {
"entropy": {
"type": "array",
"items": {
"type": "integer",
"minimum": 0,
"maximum": 255
},
"description": "The entropy to use to generate the seed."
},
"algorithm": {
"type": "string",
"enum": ["ecdsa-secp256k1", "ed25519"],
"description": "The digital signature algorithm to generate an address for. Can be `ecdsa-secp256k1` (default) or `ed25519`."
}
},
"additionalProperties": false
}
},
"additionalProperties": false
}

View File

@@ -7,12 +7,12 @@
"$ref": "address",
"description": "The address of the account that is creating the transaction."
},
"sequence": {
"$ref": "sequence",
"description": "The account sequence number of the transaction that created the order to cancel."
"orderCancellation": {
"$ref": "orderCancellation",
"description": "The specification of the order cancellation to prepare."
},
"instructions": {"$ref": "instructions"}
},
"additionalProperties": false,
"required": ["address", "sequence"]
"required": ["address", "orderCancellation"]
}

View File

@@ -11,6 +11,17 @@
"type": "string",
"format": "secret",
"description": "The secret of the account that is initiating the transaction."
},
"options": {
"type": "object",
"description": "Options that control the type of signature that will be generated.",
"properties": {
"signAs": {
"$ref": "address",
"description": "The account that the signature should count for in multisigning."
}
},
"additionalProperties": false
}
},
"additionalProperties": false,

View File

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

View File

@@ -18,13 +18,21 @@
"$ref": "value"
},
"maxLedgerVersion": {
"description": "The highest ledger version that the transaction can be included in.",
"$ref": "ledgerVersion"
"description": "The highest ledger version that the transaction can be included in. If this option and `maxLedgerVersionOffset` are both omitted, the `maxLedgerVersion` option will default to 3 greater than the current validated ledger version (equivalent to `maxLedgerVersionOffset=3`). Use `null` to not set a maximum ledger version.",
"oneOf": [
{"$ref": "ledgerVersion"},
{"type": "null"}
]
},
"maxLedgerVersionOffset": {
"description": "Offset from current legder version to highest ledger version that the transaction can be included in.",
"description": "Offset from current validated legder version to highest ledger version that the transaction can be included in.",
"type": "integer",
"minimum": 0
},
"signersCount": {
"description": "Number of signers that will be signing this transaction.",
"type": "integer",
"minimum": 1
}
},
"additionalProperties": false,

View File

@@ -1,6 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "memos",
"link": "transaction-memos",
"description": "Array of memos to attach to the transaction.",
"type": "array",
"items": {

View File

@@ -0,0 +1,103 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "settingsPlusMemos",
"type": "object",
"properties": {
"passwordSpent": {
"type": "boolean",
"description": "Indicates that the account has used its free SetRegularKey transaction."
},
"requireDestinationTag": {
"type": "boolean",
"description": "Requires incoming payments to specify a destination tag."
},
"requireAuthorization": {
"type": "boolean",
"description": "If set, this account must individually approve other users in order for those users to hold this accounts issuances."
},
"disallowIncomingXRP": {
"type": "boolean",
"description": "Indicates that client applications should not send XRP to this account. Not enforced by rippled."
},
"disableMasterKey": {
"type": "boolean",
"description": "Disallows use of the master key to sign transactions for this account."
},
"enableTransactionIDTracking": {
"type": "boolean",
"description": "Track the ID of this accounts most recent transaction."
},
"noFreeze": {
"type": "boolean",
"description": "Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled."
},
"globalFreeze": {
"type": "boolean",
"description": "Freeze all assets issued by this account."
},
"defaultRipple": {
"type": "boolean",
"description": "Enable [rippling](https://ripple.com/knowledge_center/understanding-the-noripple-flag/) on this accounts trust lines by default. (New in [rippled 0.27.3](https://github.com/ripple/rippled/releases/tag/0.27.3))"
},
"emailHash": {
"description": "Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.",
"oneOf": [
{"type": "null"},
{"$ref": "hash128"}
]
},
"messageKey": {
"type": "string",
"description": "Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple."
},
"domain": {
"type": "string",
"description": " The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase."
},
"transferRate": {
"description": " The fee to charge when users transfer this accounts issuances, as the decimal amount that must be sent to deliver 1 unit. Has precision up to 9 digits beyond the decimal point. Use `null` to set no fee.",
"oneOf": [
{"type": "null"},
{"type": "number", "minimum": 1, "maximum": 4.294967295}
]
},
"regularKey": {
"oneOf": [
{"$ref": "address"},
{"type": "null"}
],
"description": "The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key."
},
"signers": {
"type": "object",
"description": "Settings that determine what sets of accounts can be used to sign a transaction on behalf of this account using multisigning.",
"properties": {
"threshold": {
"$ref": "uint32",
"description": "A target number for the signer weights. A multi-signature from this list is valid only if the sum weights of the signatures provided is equal or greater than this value. To delete the signers setting, use the value `0`."
},
"weights": {
"type": "array",
"description": "Weights of signatures for each signer.",
"items": {
"type": "object",
"description": "An association of an address and a weight.",
"properties": {
"address": {"$ref": "address"},
"weight": {
"$ref": "uint32",
"description": "The weight that the signature of this account counts as towards the threshold."
}
},
"required": ["address", "weight"],
"additionalProperties": false
},
"minItems": 1,
"maxItems": 8
}
}
},
"memos": {"$ref": "memos"}
},
"additionalProperties": false
}

View File

@@ -1,7 +1,7 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "tag",
"description": "An arbitrary unsigned 32-bit integer most commonly used to identify a non-Ripple account.",
"description": "An arbitrary unsigned 32-bit integer that identifies a reason for payment or a non-Ripple account.",
"type": "integer",
"$ref": "uint32"
}

View File

@@ -2,7 +2,7 @@
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "transactionType",
"link": "transaction-types",
"description": "The type of the tranasction.",
"description": "The type of the transaction.",
"type": "string",
"enum": ["payment", "order", "orderCancellation", "trustline", "settings",
"suspendedPaymentCreation", "suspendedPaymentCancellation",

View File

@@ -83,7 +83,7 @@
"description": "The time since the ledger was closed, in seconds."
},
"baseFeeXRP": {
"type": "number",
"$ref": "value",
"description": "Base fee, in XRP. This may be represented in scientific notation such as 1e-05 for 0.00005."
},
"hash": {
@@ -91,14 +91,12 @@
"description": "Unique hash for the ledger, as an uppercase hexadecimal string."
},
"reserveBaseXRP": {
"type": "integer",
"minimum": 0,
"description": "Minimum amount of XRP (not drops) necessary for every account to keep in reserve."
"$ref": "value",
"description": "Minimum amount of XRP necessary for every account to keep in reserve."
},
"reserveIncrementXRP": {
"type": "integer",
"minimum": 0,
"description": "Amount of XRP (not drops) added to the account reserve for each object an account is responsible for in the ledger."
"$ref": "value",
"description": "Amount of XRP added to the account reserve for each object an account is responsible for in the ledger."
},
"ledgerVersion": {
"type": "integer",

View File

@@ -1,73 +1,8 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "getSettings",
"type": "object",
"properties": {
"passwordSpent": {
"type": "boolean",
"description": "Indicates that the account has used its free SetRegularKey transaction."
},
"requireDestinationTag": {
"type": "boolean",
"description": "Requires incoming payments to specify a destination tag."
},
"requireAuthorization": {
"type": "boolean",
"description": "If set, this account must individually approve other users in order for those users to hold this accounts issuances."
},
"disallowIncomingXRP": {
"type": "boolean",
"description": "Indicates that client applications should not send XRP to this account. Not enforced by rippled."
},
"disableMasterKey": {
"type": "boolean",
"description": "Disallows use of the master key to sign transactions for this account."
},
"enableTransactionIDTracking": {
"type": "boolean",
"description": "Track the ID of this accounts most recent transaction."
},
"noFreeze": {
"type": "boolean",
"description": "Permanently give up the ability to freeze individual trust lines. This flag can never be disabled after being enabled."
},
"globalFreeze": {
"type": "boolean",
"description": "Freeze all assets issued by this account."
},
"defaultRipple": {
"type": "boolean",
"description": "Enable [rippling](https://ripple.com/knowledge_center/understanding-the-noripple-flag/) on this accounts trust lines by default. (New in [rippled 0.27.3](https://github.com/ripple/rippled/releases/tag/0.27.3))"
},
"emailHash": {
"description": "Hash of an email address to be used for generating an avatar image. Conventionally, clients use Gravatar to display this image. Use `null` to clear.",
"oneOf": [
{"type": "null"},
{"$ref": "hash128"}
]
},
"messageKey": {
"type": "string",
"description": "Public key for sending encrypted messages to this account. Conventionally, it should be a secp256k1 key, the same encryption that is used by the rest of Ripple."
},
"domain": {
"type": "string",
"description": " The domain that owns this account, as a hexadecimal string representing the ASCII for the domain in lowercase."
},
"transferRate": {
"description": " The fee to charge when users transfer this accounts issuances, represented as billionths of a unit. Use `null` to set no fee.",
"oneOf": [
{"type": "null"},
{"type": "number", "minimum": 1, "maximum": 4.294967295}
]
},
"regularKey": {
"oneOf": [
{"$ref": "address"},
{"type": "null"}
],
"description": "The public key of a new keypair, to use as the regular key to this account, as a base-58-encoded string in the same format as an account address. Use `null` to remove the regular key."
}
},
"additionalProperties": false
"$ref": "settingsPlusMemos",
"not": {
"required": ["memos"]
}
}

View File

@@ -34,7 +34,7 @@
"state": {
"properties": {
"balance": {
"$ref": "value",
"$ref": "signedValue",
"description": "The balance on the trustline, representing which party owes the other and by how much."
}
},

View File

@@ -1,21 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ledgerClosed",
"description": "A ledgerClosed event message",
"type": "object",
"properties": {
"feeBase": {"type": "integer", "minimum": 0},
"feeReference": {"type": "integer", "minimum": 0},
"ledgerHash": {"$ref": "hash256"},
"ledgerVersion": {"$ref": "ledgerVersion"},
"ledgerTimestamp": {"type": "string", "format": "date-time"},
"reserveBase": {"type": "integer", "minimum": 0},
"reserveIncrement": {"type": "integer", "minimum": 0},
"transactionCount": {"type": "integer", "minimum": 0},
"validatedLedgerVersions": {"type": "string"}
},
"addtionalProperties": false,
"required": ["feeBase", "feeReference", "ledgerHash", "ledgerTimestamp",
"reserveBase", "reserveIncrement", "transactionCount",
"ledgerVersion", "validatedLedgerVersions"]
}

View File

@@ -0,0 +1,46 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ledgerEvent",
"description": "A ledger event message",
"type": "object",
"properties": {
"baseFeeXRP": {
"$ref": "value",
"description": "Base fee, in XRP."
},
"ledgerHash": {
"$ref": "hash256",
"description": "Unique hash of the ledger that was closed, as hex."
},
"ledgerVersion": {
"$ref": "ledgerVersion",
"description": "Ledger version of the ledger that closed."
},
"ledgerTimestamp": {
"type": "string",
"format": "date-time",
"description": "The time at which this ledger closed."
},
"reserveBaseXRP": {
"$ref": "value",
"description": "The minimum reserve, in XRP, that is required for an account."
},
"reserveIncrementXRP": {
"$ref": "value",
"description": "The increase in account reserve that is added for each item the account owns, such as offers or trust lines."
},
"transactionCount": {
"type": "integer",
"minimum": 0,
"description": "Number of new transactions included in this ledger."
},
"validatedLedgerVersions": {
"type": "string",
"description": "Range of ledgers that the server has available. This may be discontiguous."
}
},
"addtionalProperties": false,
"required": ["baseFeeXRP", "ledgerHash", "ledgerTimestamp",
"reserveBaseXRP", "reserveIncrementXRP", "transactionCount",
"ledgerVersion", "validatedLedgerVersions"]
}

View File

@@ -17,6 +17,10 @@
"$ref": "value",
"description": "The XRP fee that was charged for the transaction."
},
"deliveredAmount": {
"$ref": "amount",
"description": "For payment transactions, it is impossible to reliably compute the actual delivered amount from the balanceChanges due to fixed precision. If the payment is not a partial payment and the transaction succeeded, the deliveredAmount should always be considered to be the amount specified in the transaction."
},
"balanceChanges": {
"type": "object",
"additionalProperties": {

View File

@@ -21,12 +21,15 @@
"description": "The initiating account's sequence number for this transaction."
},
"maxLedgerVersion": {
"$ref": "ledgerVersion",
"description": "The highest ledger version that the transaction can be included in."
"oneOf": [
{"$ref": "ledgerVersion"},
{"type": "null"}
],
"description": "The highest ledger version that the transaction can be included in. Set to `null` if there is no maximum."
}
},
"additionalProperties": false,
"required": ["fee", "sequence"]
"required": ["fee", "sequence", "maxLedgerVersion"]
}
},
"additionalProperties": false,

View File

@@ -5,7 +5,7 @@
"properties": {
"resultCode": {
"type": "string",
"description": "The result code returned by rippled. [List of tranasction responses](http://pages.lightthenight.org/gba/SanFran15/ripple)"
"description": "The result code returned by rippled. [List of transaction responses](https://ripple.com/build/transactions/#full-transaction-response-list)"
},
"resultMessage": {
"type": "string",

View File

@@ -7,7 +7,8 @@
"orderSequence": {
"$ref": "sequence",
"description": "The [account sequence number](#account-sequence-number) of the order to cancel."
}
},
"memos": {"$ref": "memos"}
},
"required": ["orderSequence"],
"additionalProperties": false

View File

@@ -33,7 +33,12 @@
"type": "string",
"format": "date-time",
"description": "Time after which the offer is no longer active, as an [ISO 8601 date-time](https://en.wikipedia.org/wiki/ISO_8601)."
}
},
"orderToReplace": {
"$ref": "sequence",
"description": "The [account sequence number](#account-sequence-number) of an order to cancel before the new order is created, effectively replacing the old order."
},
"memos": {"$ref": "memos"}
},
"required": ["direction", "quantity", "totalPrice"],
"additionalProperties": false,

View File

@@ -2,11 +2,17 @@
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "settings",
"link": "settings",
"allOf": [
"$ref": "settingsPlusMemos",
"oneOf": [
{
"$ref": "getSettings"
"required": ["memos"],
"minProperties": 2,
"maxProperties": 2
},
{
"not": {
"required": ["memos"]
},
"minProperties": 1,
"maxProperties": 1
}

View File

@@ -35,7 +35,8 @@
"frozen": {
"type": "boolean",
"description": "If true, the trustline is frozen, which means that funds can only be sent to the owner."
}
},
"memos": {"$ref": "memos"}
},
"required": ["currency", "counterparty", "limit"],
"additionalProperties": false

View File

@@ -1,12 +1,12 @@
'use strict';
const _ = require('lodash');
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils');
import type {Connection} from './connection';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const {convertKeysFromSnakeCaseToCamelCase} = require('./utils')
import type {Connection} from './connection'
export type GetServerInfoResponse = {
buildVersion: string,
completeLedgers: string,
hostid: string,
hostID: string,
ioLatencyMs: number,
load?: {
jobTypes: Array<Object>,
@@ -23,49 +23,57 @@ export type GetServerInfoResponse = {
serverState: string,
validatedLedger: {
age: number,
baseFeeXrp: number,
baseFeeXRP: string,
hash: string,
reserveBaseXrp: number,
reserveIncXrp: number,
seq: number
reserveBaseXRP: string,
reserveIncrementXRP: string,
ledgerVersion: number
},
validationQuorum: number
}
function renameKeys(object, mapping) {
_.forEach(mapping, (to, from) => {
object[to] = object[from];
delete object[from];
});
object[to] = object[from]
delete object[from]
})
}
function getServerInfo(connection: Connection): Promise<GetServerInfoResponse> {
return connection.request({command: 'server_info'}).then(response => {
const info = convertKeysFromSnakeCaseToCamelCase(response.info);
renameKeys(info, {hostid: 'hostID'});
renameKeys(info.validatedLedger, {
baseFeeXrp: 'baseFeeXRP',
reserveBaseXrp: 'reserveBaseXRP',
reserveIncXrp: 'reserveIncrementXRP',
seq: 'ledgerVersion'
});
return info;
});
const info = convertKeysFromSnakeCaseToCamelCase(response.info)
renameKeys(info, {hostid: 'hostID'})
if (info.validatedLedger) {
renameKeys(info.validatedLedger, {
baseFeeXrp: 'baseFeeXRP',
reserveBaseXrp: 'reserveBaseXRP',
reserveIncXrp: 'reserveIncrementXRP',
seq: 'ledgerVersion'
})
info.validatedLedger.baseFeeXRP =
info.validatedLedger.baseFeeXRP.toString()
info.validatedLedger.reserveBaseXRP =
info.validatedLedger.reserveBaseXRP.toString()
info.validatedLedger.reserveIncrementXRP =
info.validatedLedger.reserveIncrementXRP.toString()
}
return info
})
}
function computeFeeFromServerInfo(cushion: number,
serverInfo: GetServerInfoResponse
): number {
return (Number(serverInfo.validatedLedger.baseFeeXRP)
* Number(serverInfo.loadFactor) * cushion).toString();
* Number(serverInfo.loadFactor) * cushion).toString()
}
function getFee(connection: Connection, cushion: number) {
return getServerInfo(connection).then(
_.partial(computeFeeFromServerInfo, cushion));
_.partial(computeFeeFromServerInfo, cushion))
}
module.exports = {
getServerInfo,
getFee
};
}

View File

@@ -1,4 +1,4 @@
'use strict';
'use strict' // eslint-disable-line strict
const txFlags = {
// Universal flags can apply to any transaction type
@@ -36,7 +36,7 @@ const txFlags = {
PartialPayment: 0x00020000,
LimitQuality: 0x00040000
}
};
}
// The following are integer (as opposed to bit) flags
// that can be set for particular transactions in the
@@ -52,9 +52,9 @@ const txFlagIndices = {
asfGlobalFreeze: 7,
asfDefaultRipple: 8
}
};
}
module.exports = {
txFlags,
txFlagIndices
};
}

View File

@@ -1,55 +1,55 @@
/* @flow */
'use strict';
export type RippledAmountIOU = {
currency: string,
value: string,
issuer?: string
}
export type RippledAmount = string | RippledAmountIOU
export type Amount = {
value: string,
currency: string,
counterparty?: string
}
// Amount where counterparty and value are optional
export type LaxLaxAmount = {
currency: string,
value?: string,
counterparty?: string
}
// A currency-counterparty pair, or just currency if it's XRP
export type Issue = {
currency: string,
counterparty?: string
}
export type Adjustment = {
address: string,
amount: Amount,
tag?: number
}
export type MaxAdjustment = {
address: string,
maxAmount: Amount,
tag?: number
}
export type MinAdjustment = {
address: string,
minAmount: Amount,
tag?: number
}
export type Memo = {
type?: string,
format?: string,
data?: string
}
/* @flow */
'use strict' // eslint-disable-line strict
export type RippledAmountIOU = {
currency: string,
value: string,
issuer?: string
}
export type RippledAmount = string | RippledAmountIOU
export type Amount = {
value: string,
currency: string,
counterparty?: string
}
// Amount where counterparty and value are optional
export type LaxLaxAmount = {
currency: string,
value?: string,
counterparty?: string
}
// A currency-counterparty pair, or just currency if it's XRP
export type Issue = {
currency: string,
counterparty?: string
}
export type Adjustment = {
address: string,
amount: Amount,
tag?: number
}
export type MaxAdjustment = {
address: string,
maxAmount: Amount,
tag?: number
}
export type MinAdjustment = {
address: string,
minAmount: Amount,
tag?: number
}
export type Memo = {
type?: string,
format?: string,
data?: string
}

View File

@@ -1,75 +1,59 @@
/* @flow */
'use strict';
const _ = require('lodash');
const BigNumber = require('bignumber.js');
const errors = require('./errors');
const keypairs = require('ripple-keypairs');
const {deriveKeypair} = require('ripple-keypairs');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const BigNumber = require('bignumber.js')
const {deriveKeypair} = require('ripple-keypairs')
import type {Amount, RippledAmount} from './types.js';
import type {Amount, RippledAmount} from './types.js'
function isValidSecret(secret: string): boolean {
try {
deriveKeypair(secret);
return true;
deriveKeypair(secret)
return true
} catch (err) {
return false;
return false
}
}
function dropsToXrp(drops: string): string {
return (new BigNumber(drops)).dividedBy(1000000.0).toString();
return (new BigNumber(drops)).dividedBy(1000000.0).toString()
}
function xrpToDrops(xrp: string): string {
return (new BigNumber(xrp)).times(1000000.0).floor().toString();
return (new BigNumber(xrp)).times(1000000.0).floor().toString()
}
function toRippledAmount(amount: Amount): RippledAmount {
if (amount.currency === 'XRP') {
return xrpToDrops(amount.value);
return xrpToDrops(amount.value)
}
return {
currency: amount.currency,
issuer: amount.counterparty ? amount.counterparty :
(amount.issuer ? amount.issuer : undefined),
value: amount.value
};
}
function generateAddress(options?: Object): Object {
const secret = keypairs.generateSeed(options);
const keypair = keypairs.deriveKeypair(secret);
const address = keypairs.deriveAddress(keypair.publicKey);
return {secret, address};
}
function generateAddressAPI(options?: Object): Object {
try {
return generateAddress(options);
} catch (error) {
throw new errors.UnexpectedError(error.message);
}
}
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g;
function convertKeysFromSnakeCaseToCamelCase(obj: any): any {
if (typeof obj === 'object') {
let newKey;
let newKey
return _.reduce(obj, (result, value, key) => {
newKey = key;
newKey = key
// taking this out of function leads to error in PhantomJS
const FINDSNAKE = /([a-zA-Z]_[a-zA-Z])/g
if (FINDSNAKE.test(key)) {
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase());
newKey = key.replace(FINDSNAKE, r => r[0] + r[2].toUpperCase())
}
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value);
return result;
}, {});
result[newKey] = convertKeysFromSnakeCaseToCamelCase(value)
return result
}, {})
}
return obj;
return obj
}
function removeUndefined(obj: Object): Object {
return _.omit(obj, _.isUndefined);
return _.omit(obj, _.isUndefined)
}
/**
@@ -78,7 +62,7 @@ function removeUndefined(obj: Object): Object {
*
*/
function rippleToUnixTimestamp(rpepoch: number): number {
return (rpepoch + 0x386D4380) * 1000;
return (rpepoch + 0x386D4380) * 1000
}
/**
@@ -86,26 +70,24 @@ function rippleToUnixTimestamp(rpepoch: number): number {
* @return {Number} seconds since ripple epoch ( 1/1/2000 GMT)
*/
function unixToRippleTimestamp(timestamp: number): number {
return Math.round(timestamp / 1000) - 0x386D4380;
return Math.round(timestamp / 1000) - 0x386D4380
}
function rippleTimeToISO8601(rippleTime: number): string {
return new Date(rippleToUnixTimestamp(rippleTime)).toISOString();
return new Date(rippleToUnixTimestamp(rippleTime)).toISOString()
}
function iso8601ToRippleTime(iso8601: string): number {
return unixToRippleTimestamp(Date.parse(iso8601));
return unixToRippleTimestamp(Date.parse(iso8601))
}
module.exports = {
dropsToXrp,
xrpToDrops,
toRippledAmount,
generateAddress,
generateAddressAPI,
convertKeysFromSnakeCaseToCamelCase,
removeUndefined,
rippleTimeToISO8601,
iso8601ToRippleTime,
isValidSecret
};
}

View File

@@ -1,25 +1,25 @@
/* @flow */
'use strict';
const _ = require('lodash');
const ValidationError = require('./errors').ValidationError;
const schemaValidate = require('./schema-validator').schemaValidate;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const ValidationError = require('./errors').ValidationError
const schemaValidate = require('./schema-validator').schemaValidate
function error(text) {
return new ValidationError(text);
return new ValidationError(text)
}
function validateLedgerRange(options) {
if (!_.isUndefined(options) && !_.isUndefined(options.minLedgerVersion)
&& !_.isUndefined(options.maxLedgerVersion)) {
if (Number(options.minLedgerVersion) > Number(options.maxLedgerVersion)) {
throw error('minLedgerVersion must not be greater than maxLedgerVersion');
throw error('minLedgerVersion must not be greater than maxLedgerVersion')
}
}
}
function validateOptions(schema, instance) {
schemaValidate(schema, instance);
validateLedgerRange(instance.options);
schemaValidate(schema, instance)
validateLedgerRange(instance.options)
}
module.exports = {
@@ -47,8 +47,10 @@ module.exports = {
prepareSuspendedPaymentExecution: _.partial(schemaValidate,
'prepareSuspendedPaymentExecutionParameters'),
sign: _.partial(schemaValidate, 'signParameters'),
combine: _.partial(schemaValidate, 'combineParameters'),
submit: _.partial(schemaValidate, 'submitParameters'),
computeLedgerHash: _.partial(schemaValidate, 'computeLedgerHashParameters'),
generateAddress: _.partial(schemaValidate, 'generateAddressParameters'),
apiOptions: _.partial(schemaValidate, 'api-options'),
instructions: _.partial(schemaValidate, 'instructions')
};
}

59
src/common/wswrapper.js Normal file
View File

@@ -0,0 +1,59 @@
'use strict' // eslint-disable-line strict
const {EventEmitter} = require('events')
function unsused() {}
/**
* Provides `EventEmitter` interface for native browser `WebSocket`,
* same, as `ws` package provides.
*/
class WSWrapper extends EventEmitter {
constructor(url, protocols = null, websocketOptions = {}) {
super()
unsused(protocols)
unsused(websocketOptions)
this.setMaxListeners(Infinity)
this._ws = new WebSocket(url)
this._ws.onclose = () => {
this.emit('close')
}
this._ws.onopen = () => {
this.emit('open')
}
this._ws.onerror = error => {
this.emit('error', error)
}
this._ws.onmessage = message => {
this.emit('message', message.data)
}
}
close() {
if (this.readyState === 1) {
this._ws.close()
}
}
send(message) {
this._ws.send(message)
}
get readyState() {
return this._ws.readyState
}
}
WSWrapper.CONNECTING = 0
WSWrapper.OPEN = 1
WSWrapper.CLOSING = 2
WSWrapper.CLOSED = 3
module.exports = WSWrapper

86
src/http.js Normal file
View File

@@ -0,0 +1,86 @@
/* eslint-disable new-cap */
'use strict' // eslint-disable-line strict
const assert = require('assert')
const _ = require('lodash')
const jayson = require('jayson')
const RippleAPI = require('./api').RippleAPI
/* istanbul ignore next */
function createHTTPServer(options, httpPort) {
const rippleAPI = new RippleAPI(options)
const methodNames = _.filter(_.keys(RippleAPI.prototype), k => {
return typeof RippleAPI.prototype[k] === 'function'
&& k !== 'connect'
&& k !== 'disconnect'
&& k !== 'constructor'
&& k !== 'RippleAPI'
})
function applyPromiseWithCallback(fnName, callback, args_) {
try {
let args = args_
if (!_.isArray(args_)) {
const fnParameters = jayson.Utils.getParameterNames(rippleAPI[fnName])
args = fnParameters.map(name => args_[name])
const defaultArgs = _.omit(args_, fnParameters)
assert(_.size(defaultArgs) <= 1,
'Function must have no more than one default argument')
if (_.size(defaultArgs) > 0) {
args.push(defaultArgs[_.keys(defaultArgs)[0]])
}
}
Promise.resolve(rippleAPI[fnName](...args))
.then(res => callback(null, res))
.catch(err => {
callback({code: 99, message: err.message, data: {name: err.name}})
})
} catch (err) {
callback({code: 99, message: err.message, data: {name: err.name}})
}
}
const methods = {}
_.forEach(methodNames, fn => {
methods[fn] = jayson.Method((args, cb) => {
applyPromiseWithCallback(fn, cb, args)
}, {collect: true})
})
const server = jayson.server(methods)
let httpServer = null
return {
server: server,
start: function() {
if (httpServer !== null) {
return Promise.reject('Already started')
}
return new Promise(resolve => {
rippleAPI.connect().then(() => {
httpServer = server.http()
httpServer.listen(httpPort, resolve)
})
})
},
stop: function() {
if (httpServer === null) {
return Promise.reject('Not started')
}
return new Promise(resolve => {
rippleAPI.disconnect()
httpServer.close(() => {
httpServer = null
resolve()
})
})
}
}
}
module.exports = {
createHTTPServer
}

View File

@@ -1,145 +1,9 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
/* eslint-disable max-len */
// Enable core-js polyfills. This allows use of ES6/7 extensions listed here:
// https://github.com/zloirock/core-js/blob/fb0890f32dabe8d4d88a4350d1b268446127132e/shim.js#L1-L103
/* eslint-enable max-len */
// In node.js env, polyfill might be already loaded (from any npm package),
// that's why we do this check.
if (!global._babelPolyfill) {
require('babel-core/polyfill');
module.exports = {
RippleAPI: require('./api').RippleAPI,
// Broadcast api is experimental
RippleAPIBroadcast: require('./broadcast').RippleAPIBroadcast,
// HTTP server is experimental
createHTTPServer: require('./http').createHTTPServer
}
const _ = require('lodash');
const EventEmitter = require('events').EventEmitter;
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 getBalanceSheet = require('./ledger/balance-sheet');
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 prepareSuspendedPaymentCreation =
require('./transaction/suspended-payment-creation');
const prepareSuspendedPaymentExecution =
require('./transaction/suspended-payment-execution');
const prepareSuspendedPaymentCancellation =
require('./transaction/suspended-payment-cancellation');
const prepareSettings = require('./transaction/settings');
const sign = require('./transaction/sign');
const submit = require('./transaction/submit');
const errors = require('./common').errors;
const generateAddress = common.generateAddressAPI;
const computeLedgerHash = require('./offline/ledgerhash');
const getLedger = require('./ledger/ledger');
type APIOptions = {
servers?: Array<string>,
feeCushion?: number,
trace?: boolean,
proxy?: string
}
// prevent access to non-validated ledger versions
class RestrictedConnection extends common.Connection {
request(request, timeout) {
const ledger_index = request.ledger_index;
if (ledger_index !== undefined && ledger_index !== 'validated') {
if (!_.isNumber(ledger_index) || ledger_index > this._ledgerVersion) {
return Promise.reject(new errors.LedgerVersionError(
`ledgerVersion ${ledger_index} is greater than server\'s ` +
`most recent validated ledger: ${this._ledgerVersion}`));
}
}
return super.request(request, timeout);
}
}
class RippleAPI extends EventEmitter {
constructor(options: APIOptions = {}) {
common.validate.apiOptions(options);
super();
this._feeCushion = options.feeCushion || 1.2;
if (options.servers !== undefined) {
const servers: Array<string> = options.servers;
if (servers.length === 1) {
this.connection = new RestrictedConnection(servers[0], options);
this.connection.on('ledgerClosed', message => {
this.emit('ledgerClosed', server.formatLedgerClose(message));
});
this.connection.on('error', (type, info) => {
this.emit('error', type, info);
});
} else {
throw new errors.RippleError('Multi-server not implemented');
}
} else {
// use null object pattern to provide better error message if user
// tries to call a method that requires a connection
this.connection = new RestrictedConnection(null, options);
}
}
}
_.assign(RippleAPI.prototype, {
connect,
disconnect,
isConnected,
getServerInfo,
getFee,
getLedgerVersion,
getTransaction,
getTransactions,
getTrustlines,
getBalances,
getBalanceSheet,
getPaths,
getOrders,
getOrderbook,
getSettings,
getAccountInfo,
getLedger,
preparePayment,
prepareTrustline,
prepareOrder,
prepareOrderCancellation,
prepareSuspendedPaymentCreation,
prepareSuspendedPaymentExecution,
prepareSuspendedPaymentCancellation,
prepareSettings,
sign,
submit,
generateAddress,
computeLedgerHash,
errors
});
// these are exposed only for use by unit tests; they are not part of the API
RippleAPI._PRIVATE = {
validate: common.validate,
RangeSet: require('./common/rangeset').RangeSet,
ledgerUtils: require('./ledger/utils'),
schemaValidator: require('./common/schema-validator')
};
module.exports.RippleAPI = RippleAPI;

View File

@@ -1,8 +1,8 @@
/* @flow */
'use strict';
const utils = require('./utils');
const {validate, removeUndefined} = utils.common;
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const {validate, removeUndefined} = utils.common
type AccountData = {
Sequence: number,
@@ -39,7 +39,7 @@ type AccountInfoResponse = {
}
function formatAccountInfo(response: AccountDataResponse) {
const data = response.account_data;
const data = response.account_data
return removeUndefined({
sequence: data.Sequence,
xrpBalance: utils.common.dropsToXrp(data.Balance),
@@ -47,20 +47,20 @@ function formatAccountInfo(response: AccountDataResponse) {
previousInitiatedTransactionID: data.AccountTxnID,
previousAffectingTransactionID: data.PreviousTxnID,
previousAffectingTransactionLedgerVersion: data.PreviousTxnLgrSeq
});
})
}
function getAccountInfo(address: string, options: AccountInfoOptions = {}
): Promise<AccountInfoResponse> {
validate.getAccountInfo({address, options});
validate.getAccountInfo({address, options})
const request = {
command: 'account_info',
account: address,
ledger_index: options.ledgerVersion || 'validated'
};
}
return this.connection.request(request).then(formatAccountInfo);
return this.connection.request(request).then(formatAccountInfo)
}
module.exports = getAccountInfo;
module.exports = getAccountInfo

View File

@@ -1,10 +1,10 @@
/* @flow */
'use strict';
'use strict' // eslint-disable-line strict
const _ = require('lodash');
const utils = require('./utils');
const {validate} = utils.common;
import type {Amount} from '../common/types.js';
const _ = require('lodash')
const utils = require('./utils')
const {validate} = utils.common
import type {Amount} from '../common/types.js'
type BalanceSheetOptions = {
excludeAddresses?: Array<string>,
@@ -21,35 +21,35 @@ type GetBalanceSheet = {
}
function formatBalanceSheet(balanceSheet): GetBalanceSheet {
const result = {};
const result = {}
if (!_.isUndefined(balanceSheet.balances)) {
result.balances = [];
result.balances = []
_.forEach(balanceSheet.balances, (balances, counterparty) => {
_.forEach(balances, (balance) => {
result.balances.push(Object.assign({counterparty}, balance));
});
});
_.forEach(balances, balance => {
result.balances.push(Object.assign({counterparty}, balance))
})
})
}
if (!_.isUndefined(balanceSheet.assets)) {
result.assets = [];
result.assets = []
_.forEach(balanceSheet.assets, (assets, counterparty) => {
_.forEach(assets, (balance) => {
result.assets.push(Object.assign({counterparty}, balance));
});
});
_.forEach(assets, balance => {
result.assets.push(Object.assign({counterparty}, balance))
})
})
}
if (!_.isUndefined(balanceSheet.obligations)) {
result.obligations = _.map(balanceSheet.obligations, (value, currency) =>
({currency, value}));
({currency, value}))
}
return result;
return result
}
function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
): Promise<GetBalanceSheet> {
validate.getBalanceSheet({address, options});
validate.getBalanceSheet({address, options})
return utils.ensureLedgerVersion.call(this, options).then(_options => {
const request = {
@@ -58,10 +58,10 @@ function getBalanceSheet(address: string, options: BalanceSheetOptions = {}
strict: true,
hotwallet: _options.excludeAddresses,
ledger_index: _options.ledgerVersion
};
}
return this.connection.request(request).then(formatBalanceSheet);
});
return this.connection.request(request).then(formatBalanceSheet)
})
}
module.exports = getBalanceSheet;
module.exports = getBalanceSheet

View File

@@ -1,9 +1,9 @@
/* @flow */
'use strict';
const utils = require('./utils');
const {validate} = utils.common;
import type {Connection} from '../common/connection.js';
import type {TrustlinesOptions, Trustline} from './trustlines-types.js';
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const {validate} = utils.common
import type {Connection} from '../common/connection.js'
import type {TrustlinesOptions, Trustline} from './trustlines-types.js'
type Balance = {
@@ -19,38 +19,38 @@ function getTrustlineBalanceAmount(trustline: Trustline) {
currency: trustline.specification.currency,
counterparty: trustline.specification.counterparty,
value: trustline.state.balance
};
}
}
function formatBalances(options, balances) {
const result = balances.trustlines.map(getTrustlineBalanceAmount);
const result = balances.trustlines.map(getTrustlineBalanceAmount)
if (!(options.counterparty ||
(options.currency && options.currency !== 'XRP')
)) {
const xrpBalance = {
currency: 'XRP',
value: balances.xrp
};
result.unshift(xrpBalance);
}
result.unshift(xrpBalance)
}
if (options.limit && result.length > options.limit) {
const toRemove = result.length - options.limit;
result.splice(-toRemove, toRemove);
const toRemove = result.length - options.limit
result.splice(-toRemove, toRemove)
}
return result;
return result
}
function getLedgerVersionHelper(connection: Connection, optionValue?: number
): Promise<number> {
if (optionValue !== undefined && optionValue !== null) {
return Promise.resolve(optionValue);
return Promise.resolve(optionValue)
}
return connection.getLedgerVersion();
return connection.getLedgerVersion()
}
function getBalances(address: string, options: TrustlinesOptions = {}
): Promise<GetBalances> {
validate.getTrustlines({address, options});
validate.getTrustlines({address, options})
return Promise.all([
getLedgerVersionHelper(this.connection, options.ledgerVersion).then(
@@ -58,7 +58,7 @@ function getBalances(address: string, options: TrustlinesOptions = {}
utils.getXRPBalance(this.connection, address, ledgerVersion)),
this.getTrustlines(address, options)
]).then(results =>
formatBalances(options, {xrp: results[0], trustlines: results[1]}));
formatBalances(options, {xrp: results[0], trustlines: results[1]}))
}
module.exports = getBalances;
module.exports = getBalances

View File

@@ -1,9 +1,9 @@
/* @flow */
'use strict';
const utils = require('./utils');
const {validate} = utils.common;
const parseLedger = require('./parse/ledger');
import type {GetLedger} from './types.js';
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const {validate} = utils.common
const parseLedger = require('./parse/ledger')
import type {GetLedger} from './types.js'
type LedgerOptions = {
ledgerVersion?: number,
@@ -14,7 +14,7 @@ type LedgerOptions = {
function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
validate.getLedger({options});
validate.getLedger({options})
const request = {
command: 'ledger',
@@ -22,10 +22,10 @@ function getLedger(options: LedgerOptions = {}): Promise<GetLedger> {
expand: options.includeAllData,
transactions: options.includeTransactions,
accounts: options.includeState
};
}
return this.connection.request(request).then(response =>
parseLedger(response.ledger));
parseLedger(response.ledger))
}
module.exports = getLedger;
module.exports = getLedger

View File

@@ -1,12 +1,12 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const {validate} = utils.common;
const parseOrderbookOrder = require('./parse/orderbook-order');
import type {Connection} from '../common/connection.js';
import type {OrdersOptions, OrderSpecification} from './types.js';
import type {Amount, Issue} from '../common/types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const {validate} = utils.common
const parseOrderbookOrder = require('./parse/orderbook-order')
import type {Connection} from '../common/connection.js'
import type {OrdersOptions, OrderSpecification} from './types.js'
import type {Amount, Issue} from '../common/types.js'
type Orderbook = {
base: Issue,
@@ -46,31 +46,31 @@ function getBookOffers(connection: Connection, account: string,
ledger_index: ledgerVersion || 'validated',
limit: limit,
taker: account
})).then(data => data.offers);
})).then(data => data.offers)
}
function isSameIssue(a: Amount, b: Amount) {
return a.currency === b.currency && a.counterparty === b.counterparty;
return a.currency === b.currency && a.counterparty === b.counterparty
}
function directionFilter(direction: string, order: OrderbookItem) {
return order.specification.direction === direction;
return order.specification.direction === direction
}
function flipOrder(order: OrderbookItem) {
const specification = order.specification;
const specification = order.specification
const flippedSpecification = {
quantity: specification.totalPrice,
totalPrice: specification.quantity,
direction: specification.direction === 'buy' ? 'sell' : 'buy'
};
const newSpecification = _.merge({}, specification, flippedSpecification);
return _.merge({}, order, {specification: newSpecification});
}
const newSpecification = _.merge({}, specification, flippedSpecification)
return _.merge({}, order, {specification: newSpecification})
}
function alignOrder(base: Amount, order: OrderbookItem) {
const quantity = order.specification.quantity;
return isSameIssue(quantity, base) ? order : flipOrder(order);
const quantity = order.specification.quantity
return isSameIssue(quantity, base) ? order : flipOrder(order)
}
function formatBidsAndAsks(orderbook: Orderbook, offers) {
@@ -84,24 +84,24 @@ function formatBidsAndAsks(orderbook: Orderbook, offers) {
// 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};
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 getOrderbook(address: string, orderbook: Orderbook,
options: OrdersOptions = {}
): Promise<GetOrderbook> {
validate.getOrderbook({address, orderbook, options});
validate.getOrderbook({address, orderbook, options})
const getter = _.partial(getBookOffers, this.connection, address,
options.ledgerVersion, options.limit);
const getOffers = _.partial(getter, orderbook.base, orderbook.counter);
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base);
options.ledgerVersion, options.limit)
const getOffers = _.partial(getter, orderbook.base, orderbook.counter)
const getReverseOffers = _.partial(getter, orderbook.counter, orderbook.base)
return Promise.all([getOffers(), getReverseOffers()]).then(data =>
formatBidsAndAsks(orderbook, _.flatten(data)));
formatBidsAndAsks(orderbook, _.flatten(data)))
}
module.exports = getOrderbook;
module.exports = getOrderbook

View File

@@ -1,11 +1,11 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const {validate} = utils.common;
const parseAccountOrder = require('./parse/account-order');
import type {Connection} from '../common/connection.js';
import type {OrdersOptions, Order} from './types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const {validate} = utils.common
const parseAccountOrder = require('./parse/account-order')
import type {Connection} from '../common/connection.js'
import type {OrdersOptions, Order} from './types.js'
type GetOrders = Array<Order>
@@ -22,20 +22,20 @@ function requestAccountOffers(connection: Connection, address: string,
return {
marker: data.marker,
results: data.offers.map(_.partial(parseAccountOrder, address))
};
});
}
})
}
function getOrders(address: string, options: OrdersOptions = {}
): Promise<GetOrders> {
validate.getOrders({address, options});
validate.getOrders({address, options})
return utils.ensureLedgerVersion.call(this, options).then(_options => {
const getter = _.partial(requestAccountOffers, this.connection, address,
_options.ledgerVersion);
_options.ledgerVersion)
return utils.getRecursive(getter, _options.limit).then(orders =>
_.sortBy(orders, (order) => order.properties.sequence));
});
_.sortBy(orders, order => order.properties.sequence))
})
}
module.exports = getOrders;
module.exports = getOrders

View File

@@ -1,24 +1,24 @@
/* @flow */
'use strict';
const utils = require('./utils');
const flags = require('./flags').orderFlags;
const parseAmount = require('./amount');
const BigNumber = require('bignumber.js');
'use strict' // eslint-disable-line strict
const utils = require('./utils')
const flags = require('./flags').orderFlags
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();
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;
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
@@ -29,16 +29,19 @@ function parseAccountOrder(address: string, order: Object): Object {
passive: ((order.flags & flags.Passive) !== 0) || undefined,
// rippled currently does not provide "expiration" in account_offers
expirationTime: utils.parseTimestamp(order.expiration)
});
})
const makerExchangeRate = order.quality ?
utils.adjustQualityForXRP(order.quality.toString(),
takerGetsAmount.currency, takerPaysAmount.currency) :
computeQuality(takerGetsAmount, takerPaysAmount)
const properties = {
maker: address,
sequence: order.seq,
makerExchangeRate: order.quality ? order.quality.toString()
: computeQuality(takerGetsAmount, takerPaysAmount)
};
makerExchangeRate: makerExchangeRate
}
return {specification, properties};
return {specification, properties}
}
module.exports = parseAccountOrder;
module.exports = parseAccountOrder

View File

@@ -1,6 +1,6 @@
/* @flow */
'use strict';
const utils = require('./utils');
'use strict' // eslint-disable-line strict
const utils = require('./utils')
type Trustline = {
account: string, limit: number, currency: string, quality_in: ?number,
@@ -24,23 +24,23 @@ function parseAccountTrustline(trustline: Trustline): AccountTrustline {
limit: trustline.limit,
currency: trustline.currency,
counterparty: trustline.account,
qualityIn: trustline.quality_in || undefined,
qualityOut: trustline.quality_out || undefined,
qualityIn: utils.parseQuality(trustline.quality_in) || undefined,
qualityOut: utils.parseQuality(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};
}
return {specification, counterparty, state}
}
module.exports = parseAccountTrustline;
module.exports = parseAccountTrustline

View File

@@ -0,0 +1,9 @@
'use strict' // eslint-disable-line strict
function parseAmendment(tx: Object) {
return {
amendment: tx.Amendment
}
}
module.exports = parseAmendment

View File

@@ -1,21 +1,21 @@
/* @flow */
'use strict';
const utils = require('./utils');
import type {Amount, RippledAmount} from '../../common/types.js';
'use strict' // eslint-disable-line strict
const utils = require('../utils')
import type {Amount, RippledAmount} from '../../common/types.js'
function parseAmount(amount: RippledAmount): Amount {
if (typeof amount === 'string') {
return {
currency: 'XRP',
value: utils.dropsToXrp(amount)
};
value: utils.common.dropsToXrp(amount)
}
}
return {
currency: amount.currency,
value: amount.value,
counterparty: amount.issuer
};
}
}
module.exports = parseAmount;
module.exports = parseAmount

View File

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

View File

@@ -0,0 +1,15 @@
'use strict' // eslint-disable-line strict
const BigNumber = require('bignumber.js')
const {dropsToXrp} = require('./utils')
function parseFeeUpdate(tx: Object) {
const baseFeeDrops = (new BigNumber(tx.BaseFee, 16)).toString()
return {
baseFeeXRP: dropsToXrp(baseFeeDrops),
referenceFeeUnits: tx.ReferenceFeeUnits,
reserveBaseXRP: dropsToXrp(tx.ReserveBase),
reserveIncrementXRP: dropsToXrp(tx.ReserveIncrement)
}
}
module.exports = parseFeeUpdate

View File

@@ -1,28 +1,49 @@
/* @flow */
'use strict';
const BigNumber = require('bignumber.js');
const AccountFields = require('./utils').constants.AccountFields;
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const BigNumber = require('bignumber.js')
const AccountFields = require('./utils').constants.AccountFields
function parseField(info, value) {
if (info.encoding === 'hex' && !info.length) { // e.g. "domain"
return new Buffer(value, 'hex').toString('ascii');
return new Buffer(value, 'hex').toString('ascii')
}
if (info.shift) {
return (new BigNumber(value)).shift(-info.shift).toNumber();
return (new BigNumber(value)).shift(-info.shift).toNumber()
}
return value;
return value
}
function parseFields(data: Object): Object {
const settings = {};
const settings = {}
for (const fieldName in AccountFields) {
const fieldValue = data[fieldName];
const fieldValue = data[fieldName]
if (fieldValue !== undefined) {
const info = AccountFields[fieldName];
settings[info.name] = parseField(info, fieldValue);
const info = AccountFields[fieldName]
settings[info.name] = parseField(info, fieldValue)
}
}
return settings;
if (data.RegularKey) {
settings.regularKey = data.RegularKey
}
// TODO: this isn't implemented in rippled yet, may have to change this later
if (data.SignerQuorum || data.SignerEntries) {
settings.signers = {}
if (data.SignerQuorum) {
settings.signers.threshold = data.SignerQuorum
}
if (data.SignerEntries) {
settings.signers.weights = _.map(data.SignerEntries, entry => {
return {
address: entry.SignerEntry.Account,
weight: entry.SignerEntry.SignerWeight
}
})
}
}
return settings
}
module.exports = parseFields;
module.exports = parseFields

View File

@@ -1,9 +1,9 @@
'use strict';
'use strict' // eslint-disable-line strict
const orderFlags = {
Passive: 0x00010000,
Sell: 0x00020000 // offer was placed as a sell
};
}
const trustlineFlags = {
LowReserve: 0x00010000, // entry counts toward reserve
@@ -14,9 +14,9 @@ const trustlineFlags = {
HighNoRipple: 0x00200000,
LowFreeze: 0x00400000,
HighFreeze: 0x00800000
};
}
module.exports = {
orderFlags,
trustlineFlags
};
}

View File

@@ -1,46 +1,48 @@
/* @flow */
'use strict';
const _ = require('lodash');
const {removeUndefined, rippleTimeToISO8601} = require('./utils');
const parseTransaction = require('./transaction');
import type {GetLedger} from '../types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const {removeUndefined, rippleTimeToISO8601} = require('./utils')
const parseTransaction = require('./transaction')
import type {GetLedger} from '../types.js'
function parseTransactionWrapper(ledgerVersion, tx) {
const transaction = _.assign({}, _.omit(tx, 'metaData'),
{meta: tx.metaData});
const result = parseTransaction(transaction);
const transaction = _.assign({}, _.omit(tx, 'metaData'), {
meta: tx.metaData,
ledger_index: ledgerVersion
})
const result = parseTransaction(transaction)
if (!result.outcome.ledgerVersion) {
result.outcome.ledgerVersion = ledgerVersion;
result.outcome.ledgerVersion = ledgerVersion
}
return result;
return result
}
function parseTransactions(transactions, ledgerVersion) {
if (_.isEmpty(transactions)) {
return {};
return {}
}
if (_.isString(transactions[0])) {
return {transactionHashes: transactions};
return {transactionHashes: transactions}
}
return {
transactions: _.map(transactions,
_.partial(parseTransactionWrapper, ledgerVersion)),
rawTransactions: JSON.stringify(transactions)
};
}
}
function parseState(state) {
if (_.isEmpty(state)) {
return {};
return {}
}
if (_.isString(state[0])) {
return {stateHashes: state};
return {stateHashes: state}
}
return {rawState: JSON.stringify(state)};
return {rawState: JSON.stringify(state)}
}
function parseLedger(ledger: Object): GetLedger {
const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10);
const ledgerVersion = parseInt(ledger.ledger_index || ledger.seqNum, 10)
return removeUndefined(_.assign({
stateHash: ledger.account_hash,
closeTime: rippleTimeToISO8601(ledger.close_time),
@@ -55,7 +57,7 @@ function parseLedger(ledger: Object): GetLedger {
},
parseTransactions(ledger.transactions, ledgerVersion),
parseState(ledger.accountState)
));
))
}
module.exports = parseLedger;
module.exports = parseLedger

View File

@@ -1,18 +1,18 @@
/* @flow */
'use strict';
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const flags = utils.txFlags.OfferCreate;
'use strict' // eslint-disable-line strict
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
const flags = utils.txFlags.OfferCreate
function parseOrder(tx: Object): Object {
assert(tx.TransactionType === 'OfferCreate');
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;
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,
@@ -23,7 +23,7 @@ function parseOrder(tx: Object): Object {
|| undefined,
fillOrKill: ((tx.Flags & flags.FillOrKill) !== 0) || undefined,
expirationTime: utils.parseTimestamp(tx.Expiration)
});
})
}
module.exports = parseOrder;
module.exports = parseOrder

View File

@@ -1,16 +1,16 @@
/* @flow */
'use strict';
const _ = require('lodash');
const utils = require('./utils');
const flags = require('./flags').orderFlags;
const parseAmount = require('./amount');
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const utils = require('./utils')
const flags = require('./flags').orderFlags
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;
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
@@ -20,25 +20,25 @@ function parseOrderbookOrder(order: Object): Object {
totalPrice: totalPrice,
passive: ((order.Flags & flags.Passive) !== 0) || undefined,
expirationTime: utils.parseTimestamp(order.Expiration)
});
})
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;
parseAmount(order.taker_gets_funded) : undefined
const takerPaysFunded = order.taker_pays_funded ?
parseAmount(order.taker_pays_funded) : undefined;
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});
})
const state = _.isEmpty(available) ? undefined : available
return utils.removeUndefined({specification, properties, state})
}
module.exports = parseOrderbookOrder;
module.exports = parseOrderbookOrder

View File

@@ -1,25 +1,25 @@
/* @flow */
'use strict';
const _ = require('lodash');
const parseAmount = require('./amount');
import type {Amount, RippledAmount} from '../../common/types.js';
import type {GetPaths, RippledPathsResponse} from '../pathfind-types.js';
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const parseAmount = require('./amount')
import type {Amount, RippledAmount} from '../../common/types.js'
import type {GetPaths, RippledPathsResponse} from '../pathfind-types.js'
function parsePaths(paths) {
return paths.map(steps => steps.map(step =>
_.omit(step, ['type', 'type_hex'])));
_.omit(step, ['type', 'type_hex'])))
}
function removeAnyCounterpartyEncoding(address: string, amount: Amount) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
_.omit(amount, 'counterparty') : amount
}
function createAdjustment(address: string, adjustmentWithoutAddress: Object) {
const amountKey = _.keys(adjustmentWithoutAddress)[0];
const amount = adjustmentWithoutAddress[amountKey];
const amountKey = _.keys(adjustmentWithoutAddress)[0]
const amount = adjustmentWithoutAddress[amountKey]
return _.set({address: address}, amountKey,
removeAnyCounterpartyEncoding(address, amount));
removeAnyCounterpartyEncoding(address, amount))
}
function parseAlternative(sourceAddress: string, destinationAddress: string,
@@ -31,21 +31,21 @@ function parseAlternative(sourceAddress: string, destinationAddress: string,
{source: {amount: parseAmount(alternative.source_amount)},
destination: {minAmount: parseAmount(alternative.destination_amount)}} :
{source: {maxAmount: parseAmount(alternative.source_amount)},
destination: {amount: parseAmount(destinationAmount)}};
destination: {amount: parseAmount(destinationAmount)}}
return {
source: createAdjustment(sourceAddress, amounts.source),
destination: createAdjustment(destinationAddress, amounts.destination),
paths: JSON.stringify(parsePaths(alternative.paths_computed))
};
}
}
function parsePathfind(pathfindResult: RippledPathsResponse): GetPaths {
const sourceAddress = pathfindResult.source_account;
const destinationAddress = pathfindResult.destination_account;
const destinationAmount = pathfindResult.destination_amount;
const sourceAddress = pathfindResult.source_account
const destinationAddress = pathfindResult.destination_account
const destinationAmount = pathfindResult.destination_amount
return pathfindResult.alternatives.map(_.partial(parseAlternative,
sourceAddress, destinationAddress, destinationAmount));
sourceAddress, destinationAddress, destinationAmount))
}
module.exports = parsePathfind;
module.exports = parsePathfind

View File

@@ -1,43 +1,39 @@
/* @flow */
'use strict';
const _ = require('lodash');
const assert = require('assert');
const utils = require('./utils');
const parseAmount = require('./amount');
const txFlags = utils.txFlags;
function isPartialPayment(tx) {
return (tx.Flags & txFlags.Payment.PartialPayment) !== 0;
}
'use strict' // eslint-disable-line strict
const _ = require('lodash')
const assert = require('assert')
const utils = require('./utils')
const parseAmount = require('./amount')
const txFlags = utils.txFlags
function isNoDirectRipple(tx) {
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0;
return (tx.Flags & txFlags.Payment.NoRippleDirect) !== 0
}
function isQualityLimited(tx) {
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0;
return (tx.Flags & txFlags.Payment.LimitQuality) !== 0
}
function removeGenericCounterparty(amount, address) {
return amount.counterparty === address ?
_.omit(amount, 'counterparty') : amount;
_.omit(amount, 'counterparty') : amount
}
function parsePayment(tx: Object): Object {
assert(tx.TransactionType === 'Payment');
assert(tx.TransactionType === 'Payment')
const source = {
address: tx.Account,
maxAmount: 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),
@@ -45,10 +41,10 @@ function parsePayment(tx: Object): Object {
memos: utils.parseMemos(tx),
invoiceID: tx.InvoiceID,
paths: tx.Paths ? JSON.stringify(tx.Paths) : undefined,
allowPartialPayment: isPartialPayment(tx) || undefined,
allowPartialPayment: utils.isPartialPayment(tx) || undefined,
noDirectRipple: isNoDirectRipple(tx) || undefined,
limitQuality: isQualityLimited(tx) || undefined
});
})
}
module.exports = parsePayment;
module.exports = parsePayment

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