npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

ilp-settlement-core

v0.1.1

Published

Framework for creating Interledger settlement engines in JS

Downloads

2

Readme

Settlement Core

Framework for creating Interledger settlement engines in JS

NPM Package CircleCI Codecov Prettier Apache 2.0 License

Get Started

If you're looking to operate a settlement engine with your connector or integrate one into your app or service, check out these awesome implementations!

| Settlement Engine | Supported Assets | Status | Language | Authors | | ------------------------------------------------------------------ | ---------------- | ------------------- | ---------- | -------------------------------------------------------------------------------------------------- | | Ethereum (on-ledger) | ETH, ERC-20s | Beta | Rust | Georgios Konstantopoulos | | XRP (on-ledger) | XRP | Beta | TypeScript | Matt de Haast, Kincaid O'Neil | | Lightning | BTC | Under development | TypeScript | Kincaid O'Neil |

If you want learn more about settlement engines or develop a new one, keep reading!

Overview

Settlement in Interledger

In the Interledger protocol, connectors maintain peers, or counterparties whom they transact with. Connectors clear and fulfill ILP packets with their peers, which represent conditional IOUs that affect financial accounting balances betwen them.

A connector may extend a given peer a limited line of credit, or none at all, depending upon their trustworthiness. As the connector receives incoming ILP Prepare packets from a peer, forwards them, and returns corresponding ILP Fulfill packets, that peer's liabilities to the connector accrue. If the peer's liabilities exceed the credit limit assigned to it, the connector may reject and decline to forward additional packets.

In order to continue transacting, the peer must settle their liabilities. In most cases, this is accomplished through sending a payment on a settlement system that both peers have agreed to use. The connector should credit the incoming payment, irrevocably discharging a sum the peer owed to it, which enables clearing subsequent Interledger packets from the peer.

Settlement systems transfer value from one participant to another, and include:

  • Cryptocurrencies and distributed ledgers (Bitcoin, Ethereum, XRP...)
  • Payment channels and layer 2 networks (Lightning, state channels...)
  • Traditional banking infrastructure (ACH, SWIFT, wire transfers, card processors...)
  • Money transfer services (PayPal, Venmo, Square Cash...)
  • Mobile money (WeChat, Alipay, M-PESA...)
  • Cash or physically delivering assets

Settlement Engines

Settlement engines are services that integrate with a settlement system to send and receive settlements. Two Interledger peers each operate compatible settlement engines. Since an Interledger connector may have many peers using the same asset, one settlement engine may manage multiple accounts, to settle with many different peers.

The Settlement Engine specification defines a standardized HTTP API for Interledger connectors to interface with their settlement engines, and vice versa. Connectors trigger the settlement engine to perform settlements, and settlement engines trigger the connector to adjust accounting balances when incoming settlements are received, like so:

(TODO insert diagram from spec)

Settlement engines may also use the same HTTP API to send and receive messages with a peer's settlement engine. Settlement Core manages all this communication with the connector in the background, exposing a simple interface.

Design Goals

  • Intuitive. Provide the essential primitives to quickly develop safe, reliable settlement engine implementations.
  • Scalable. Support standalone clients all the way up to high-volume, low-latency service providers.
  • Interoperable. Settlement engines should fully support each next-generation connector, including Interledger.rs, Rafiki, and the Java connector.
  • Isomorphic. JavaScript settlement engines should operate seamlessly across Node.js, desktop & mobile browsers, and Electron apps.

API

TODO Add installation instructions

🚨 Since this tech is hot off the press, note that the APIs here are beta and subject to change!

Settlement engines can be defined as a factory function: given account services, the function returns a Promise with a constructed settlement engine:

export const connectEngine = async services => {
  // Async tasks to connect engine ...

  return {
    // Settlement engine instance ...
  }
}

The services parameter includes these callback functions, provided by Settlement Core, to each settlement engine:

interface AccountServices {
  /**
   * Send a message to the given account and return their response
   *
   * @param accountId Unique account identifier to send message to
   * @param message Object to be serialized as JSON
   */
  sendMessage(accountId: string, message: any): Promise<any>

  /**
   * Send a notification to the connector to credit the given incoming settlement
   *
   * @param accountId Unique account identifier (recipient of settlement)
   * @param amount Amount received as an incoming settlement
   * @param settlementId Unique identifier for this settlement
   */
  creditSettlement(accountId: string, amount: BigNumber, settlementId?: string): void

  /**
   * Retry failed or queued outgoing settlements
   * - Automatically called after the settlement engine is instantiated
   *
   * @param accountId Unique account identifier
   */
  trySettlement(accountId: string): void
}

Then, the function should a return a contructed settlement engine instance that implements this interface:

interface SettlementEngine {
  /**
   * Setup the given account and perform tasks as a pre-requisite to send settlements
   * - For example, send a message to the peer to exchange ledger identifiers
   *
   * @param accountId Unique account identifier
   */
  setupAccount?(accountId: string): Promise<void>

  /**
   * Send a settlement to the peer for up to the given amount
   * - Since the amount is provided in arbitrary precision, round to the correct
   *   precision first
   * - The leftover, unsettled amount will automatically be tracked and retried later
   *   based on the amount returned
   * - If Promise is rejected, for safety, the full amount will assumed to be settled
   *
   * @param accountId Unique account identifier
   * @param amount Maximum amount to settle, in standard unit of asset (arbitrary precision)
   * @return Amount settled, in standard unit of asset (arbitrary precision)
   */
  settle(accountId: string, amount: BigNumber): Promise<BigNumber>

  /**
   * Handle and respond to an incoming message from the given peer
   *
   * @param accountId Unique account identifier
   * @param message Parsed JSON message from peer
   * @return Response message, to be serialized as JSON
   */
  handleMessage?(accountId: string, message: any): Promise<any>

  /**
   * Delete or close the given account
   * - For example, clean up database records associated with the account
   *
   * @param accountId Unique account identifier
   */
  closeAccount?(accountId: string): Promise<void>

  /**
   * Disconnect the settlement engine
   * - For example, gracefully closes connections to the ledger and/or databases
   */
  disconnect?(): Promise<void>
}

Running settlement engines

The factory to connect a settlement engine can be provided to Settlement Core, which will start an HTTP server to communicate with the connector using its startServer function. For example:

import { connectEngine } from '.'
import { startServer, connectRedis } from 'ilp-settlement-core'

async function run() {
  const store = await connectRedis()
  await startServer(connectEngine, store)
}

run().catch(err => console.error(err))

The startServer function should also be provided a database (Settlement Core currently supports Redis and a simple in-memory store) and configuration options to connect to the connector. It returns a Promise exposing hooks to shutdown the settlement engine server.

type StartServer = (
  /** Factory to connect and instantiate a settlement engine */
  createEngine: ConnectSettlementEngine,

  /** Database for balance logic and basic account handling (default: memory store) */
  store?: SettlementStore,

  /** Configuration for the server with the connector */
  config?: {
    /** URL of the connector's server for this settlement engine (default: http://localhost:7771) */
    connectorUrl?: string

    /** Port to operate the settlement server on (default: 3000) */
    port?: string | number
  }
) => Promise<{
  /** Stop the server interacting with the connector and disconnect the settlement engine */
  shutdown(): Promise<void>
}>

Configuration

How settlement engines are configured is up to implementations: they may use environment variables, config files, or their own method. Settlement engines can take in configuration options using a higher-order function, like so:

export const createEngine = config => async services => {
  // Construct settlement engine...
}

Then, to instantiate a settlement engine, first pass in the configuration options, and then pass the factory to Settlement Core when starting the settlement server:

async function run() {
  // Config using environment variables
  const connectorUrl = process.env.CONNECTOR_URL
  const ledgerCredential = process.env.LEDGER_CREDENTIAL

  const store = await connectRedis()

  // Inject config options, which returns factory function to connect settlement engine
  const connectEngine = createEngine({ ledgerCredential })

  // Pass factory function when starting settlement server
  await startServer(connectEngine, store, { connectorUrl })
}

Amounts

Settlement Core using BigNumber.js to pass around arbitrary precision numbers. For example, in Bitcoin, transaction amounts may denominated in precision of satoshis, which are 8 decimal places. The BigNumber type enables these amounts to be represented precisely without losing precision compared to JavaScript numbers.

When interfacing with Settlement Core, settlement engines should always handle amounts in the standard unit of the asset, such as USD or BTC. For example, 1 satoshi would be passed as a BigNumber of 0.00000001, denominanted in BTC.

Due to the way connectors track balances, Settlement Core may pass amounts to the settlement engine more precise than can actually be settled on the underlying ledger. In this case of Bitcoin, this would be the equivalent passing a BigNumber representing 0.05000003769, since there ar more than 8 decimal places. To handle this case, settlement engines should truncate this amount by using calling the decimalPlaces method on each amount with the maximum number of decimal places they can settle (always round down). Settlement engines should then return this truncated amount from the settle method. Then, Settlement Core will automatically calculate and track this "leftover" amount, which will accrue and be retried later.

Advanced

Coming soon: recommendations for an admin API, database/pub-subs, and a more step-by-step guide!