Ripple WebSocket Client: lightweight ripple-lib (connection) replacement for rippled
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
dist Docs + last changes Mar 7, 2018
samples Handle instant disconnect Aug 13, 2018
src Fix memleak Aug 15, 2018
.gitignore Handle instant disconnect Aug 13, 2018
LICENSE Initial commit Mar 7, 2018
README.md Add signing using rippled-ws-client-sign Mar 8, 2018
package-lock.json 1.0.0 Aug 15, 2018
package.json 1.0.0 Aug 15, 2018

README.md

rippled-ws-client

A lightweight reconnecting (health checking) websocket client for rippled

This is a websocket client for rippled. Of course there's ripple-lib, but the uncompressed browserified version of ripple-lib is ~ 1.4MB. That's quite a lot for mobile users.

But you can compress, gzip, minify, etc.

That's right, but since we're in the business of crypto, we might want to offer our users the entire uncompressed source, so they can audit it.

But ripple-lib comes with all the signing goodies.

That's right. But you can build amazing things without signing anything. If you want to sign offline, or online and await the transaction state, check out the complementary sign repo: rippled-ws-client-sign.

But ripple-lib has helper methods

Yeah, well... Sometimes the helper methods are not that great. The Ripple docs show you all the plain JSON commands. I'd rather send those, so the client is 1:1 compatible with the docs.

So. You want to use rippled-ws-client?

Great 🎉! A few notes:

  • rippled-ws-client is promise-based. You'll need a polyfill for IE10 and other crappy browsers.
  • You can use rippled-ws-client in the browser and in nodejs 😎
  • rippled-ws-client will auto-reconnect. But: not only when the WebSocket disconnects! The client will send ping requests to the rippled-server every few seconds. If the client (four times in a row) doesn't receive a response, the WebSocket will disconnect. Either the client or the server is offline.
  • rippled-ws-client will always subscribe on ledger-events. This way the getState() method can always tell you the most recent ledger.
  • rippled-ws-client will emit events.
  • If you want to test the reconnecting and health checking (e.g.: connected but no response from the rippled server = auto-reconnect) you can use this gist to setup a rippled websocket proxy. You can start / quit / silence any time by restarting the script 🍻 (The script is configured to stop passing messages from rippled to the client 20 seconds after connecting).

Use rippled-ws-client in nodejs

First install the module;

npm install --save rippled-ws-client

Now require:

const RippledWsClient = require('rippled-ws-client')

... and use:

new RippledWsClient('wss://...').then(...).catch(...)

Samples are available in the Github repo. There are samples on signing and submitting transactions over here, in the rippled-ws-client-sign repo.

Use rippled-ws-client in vanillajs

The client is written in ES6 (e.g. arrow functions). The browser won't understand all the code.

Thank goodness you can use browserify to convert the code.

  1. Checkout the Github-repo
  2. Enter the directory of your checkout using your commandline (make sure you have nodejs installed)
  3. Install the dependencies; npm install
  4. Install browserify; npm install -g browserify (the -g will install globally)
  5. Browserify the code:
browserify -r .:rippled-ws-client -o dist/rippled-ws-client.js

This will convert/compile the ES6 code to a browser-compatible .js file, stored in the dist/ folder.

Here's a sample jsfiddle with a precompiled version 😎

Use rippled-ws-client in vue-webpack

Just npm install --save rippled-ws-client in your vue-webpack project. You can now start your module with:

import RippledWsClient from './npm-module/rippled-ws-client.js'

... and kick off the constructor somewhere;

new RippledWsClient('wss://...').then(...).catch(...)

If vue-webpack starts bugging you with an error like this: Cannot assign to read only property 'exports' of object '#<Object>', remove the transform-runtime plugin from .babelrc.

Docs

The docs will soon move to a dedicated website. For now (since we're in the beta phase anyway) the samples below will have to do.

The code below is written using vanillajs code. I'd prefer to use arrow functions, but to make the docs compatible with plain old javascript, I decided to do it the old fashioned way.

1. Connecting

That's easy. You construct a new RippledWsClient to the WebSocket-server. Please note: use ws:// for http connections, and wss:// for https connection. Actually: don't use http. Use https. Always. Except if you are developing on your own machine.

new RippledWsClient('wss://s1.ripple.com').then(function (connection) {
  // We have liftoff!
  // All or other code lives here, using the 'connection' variable
}).catch(function (error) {
  // Oops!
})

You should never reach the catch. Not even when the connection drops. The client will auto-reconnect. You'll only reach the catch if something goes wrong initially, e.g. the URI you are connecting to is invalid.

Reconnecting

When the connection can't be setup, the connection drops, the internet connection times out, the rippled server goes offline, whatever: the client will reconnect at increasing intervals (min: 2 sec., max: 60 sec.). When the connection is online again, all the subscriptions you sent will be re-sent.

If your initial connection is ready, the (promse) then will execute. If you had a working connection before, you lost the connection and the client reconnected, an event will be emitted (see 2. Events).

State

If you have / had a connection, in the (promise) then the method getState is available. Assuming your then-callback variable is called connection:

console.log(connection.getState())

... will show you the online/offline state, fee, latency (ms), most recent ledger, etc. Sample response:

{ online: true,
  latencyMs: { last: 536, avg: 536, secAgo: 3.338 },
  server:
   { version: '0.90.0',
     publicKey: 'n9KcmEKTW3ggFgTjNMVkJwJ5R8RhQZeacYLTVgWFcnwheniS7zGA',
     uri: 'wss://s2.ripple.com' },
  ledger: { last: 37064724, validated: '32570-37064724', count: 37032154 },
  fee: { last: 17.578125, avg: 17.578125, secAgo: 3.339 },
  secLastContact: 0.001 }

Disconnecting

To disconnect:

connection.close().then(funtion (closeInfo) {
 console.log('Closed', closeInfo)
}).catch(function (error) {
 console.log('Close error', error)
})

Once closed no reconnects will be attempted. The catch will only fire if the connection is already closed.

2. Events

The rippled-ws-client will emit the events below. You can subscribe to events this way:

connection.on('ledger', function (ledger) {
  console.log('Ledger Closed:', ledger)
})
  • error An error occurred (See 4. Errors)
  • retry The could not be setup. A reconnect attempt will take place.
  • close The connection closed
  • reconnect The connection is lost or assumed lost. A reconnect will take place.
  • state The connection state changed (online (true) / offline (false))
  • ledger A ledger closed
  • transaction A new transaction event (subscription required, see 3A. Subscribe)
  • validation
  • A new validation event (subscription required, see 3A. Subscribe)

3. Methods (sending commands)

These methods are available on the connection (promise then callback argument):

  • on, see 2. Events
  • getState, see State
  • close, see Disconnecting
  • send: send a command, and return a promise (with the reply from rippled)

Any (non-admin) command from the rippled WebSocket docs can be sent.

Here's a sample jsfiddle. Below are two examples:

Example 1: getting server_info: server_info

connection.send({
  command: 'server_info'
}).then(function (info) {
  console.log('Got server info:', info)
}).catch(function (error) {
  console.log('Got error', error)
})

Example 2: getting transaction details: tx

connection.send({
  command: 'tx',
  transaction: 'D72D85F6FE2399D134227D505AD5ED031E713C7760798C4AE13A1A6799F4CA0A'
}).then(function (info) {
  console.log('Got server info:', info)
}).catch(function (error) {
  console.log('Got error', error)
})

3A. Subscribe (events)

If you want the rippled server to send you events, you have to subscribe. After subscribing to events, the rippled-ws-client will emit events when they come in (like: transaction or validation, see 2. Events)

You can subscribe to accounts or streams:

connection.send({
  command: 'subscribe',
  streams: [ 'validations', 'transactions_proposed' ]
}).then(function (response) {
  console.log('Subscribed', response)
}).catch(function (error) {
  console.log('Subscribe error', error)
})

And / or you can subscribe to all transaction events for one or more wallets:

connection.send({
  command: 'subscribe',
  accounts: [ 'rDsbeomae4FXwgQTJp9Rs64Qg9vDiTCdBv', 'rUZwBRmxtK9PwoJqAsgJg5P5was3Bd7wjA', 'rUZwBRmxtK9PwoJqAsgJg5P5was3Bd7wjA' ]
}).then(function (response) {
  console.log('Subscribed', response)
}).catch(function (error) {
  console.log('Subscribe error', error)
})

You can always add more subscriptions or unsubscribe.

Please note: if you unsubscribe the ledger-stream, the class may reconnect!

4. Errors

When shit doesn't hit the fan but something goes wrong, an error event will be emitted. An error event will contain at least an type and error attribute. The following type values exist:

  • ping_hiccup if no ping/pong response was received from the server after a few seconds. Maybe we are offline, or the server is offline or unresponsive? After 4 hiccups in a row the client will reconnect.
  • ping_error if a ping resulted in an (WebSocket) error
  • ping_timeout if a ping was sent, but timed out
  • serverinfo_timeout if we requested server_info (after setting up the connection) but we didn't get a response
  • message_invalid_json if we received a message from the server, but it wasn't JSON (should never happen, except if you are not conneting to a rippled WebSocket)
  • message_invalid_response if we received a message from the server, but it didn't contain a message id nor was it an event (should never happen, except if you are not conneting to a rippled WebSocket)