Skip to content
GitHub

Accounting in Rafiki

Rafiki uses double-entry accounting to record financial transactions. In this method of bookkeeping, a transaction recorded to one account results in an equal and opposite entry to another account. For example, a $50 credit to one account will result in a $50 debit from another account.

Transactions in Rafiki represent Interledger packet interactions, denominated in a given asset. Packet interactions can be successful, fail, or be rejected. Rafiki’s accounting layer processes the interactions and converts the activities into financial records, which are then written to your accounting database.

Accounts within Rafiki are your internal liquidity and settlement accounts used to fund payments, not the accounts that you service for your customers. This distinction is crucial for understanding how Rafiki handles transactions and settlements. Some accounts are created on-the-fly during payment processing, while others must be created when integrating with Rafiki or setting up a peer.

Assets

An asset represents a transferrable item of value. Although the Interledger Protocol (ILP) supports the transfer of any asset deemed to have value, assets are generally denominated in a currency. For example fiat currencies, central bank digital currencies, and branded currencies (such as merchant reward points).

Part of Rafiki’s integration requirements include adding one or more assets that you support.

An asset is made up of the following properties.

PropertyTypeDescriptionExample
valueBigIntA numerical amount10000
assetCodeStringA code representing the asset. An ISO 4217 currency code should be used whenever possible."USD"
assetScaleIntegerDifference in order of magnitude between the standard unit and a fractional unit2

To convert an asset’s value into an amount that’s easier to interpret, apply the following formula.

= currencyAmount

Using the example data from the table above, the formula looks like this:

100.00 USD

Accounts

Rafiki uses a combination of liquidity and settlement accounts to track the amounts available to fund transactions. Rafiki does not physically hold funds in each account. Instead, it uses double-entry accounting to record the transactions. The actual settlement of amounts owed, in which funds are physically exchanged, occurs outside of both Rafiki and the Interledger Protocol.

Liquidity accounts

Liquidity accounts are used to track deposits, withdrawals, and transfers that occur during the course of a transaction. Liquidity accounts are provided for assets, peers, and payments.

Liquidity accounts hold either a zero or a positive balance. Rafiki ensures that the total debits to a liquidity account will not exceed the account’s total credits.

Account typeWhat the account representsNumber of accounts
Asset liquidityThe value, denominated in a given asset, that Rafiki has at its disposal for sending or forwarding ILP packetsOne per asset
Peer liquidityThe credit line, denominated in the asset of your peering relationship, that you extend to a peerOne per peer
Incoming payment liquidityThe value received from a completed incoming paymentOne per incoming payment
Outgoing payment liquidityThe value that Rafiki will attempt to send in an outgoing paymentOne per outgoing payment
Wallet address liquidityThe value of an incoming payment received to a wallet address via either SPSP or Web MonetizationOne per wallet address

Asset liquidity accounts

Asset liquidity ensures there is enough value available to handle a foreign exchange transaction between you and a peer. You have one asset liquidity account for each asset you transact in.

An asset liquidity account represents the value that Rafiki has at its disposal for sending or forwarding ILP packets. The amount in an asset liquidity account increases when packets are received and decreases when packets are sent/forwarded. Any transaction that would result in a negative balance will fail.

You should define and adjust asset liquidity based on your liquidity risk profile. You can deposit or withdraw asset liquidity as needed through Rafiki Admin or by using the Backend Admin API.

Asset liquidity example - cross-currency transactions Your Rafiki instance is configured for two assets: EUR and USD.

  • Rafiki holds an asset liquidity account for both EUR and USD.
  • You’ve set the asset scale of both currencies to 0.
  • Your starting EUR liquidity is 10 and your USD liquidity is 50.

Cross-currency transaction #1:

  1. Rafiki receives packets worth 10 EUR. Your EUR liquidity increases to 20 (10 + 10).
  2. The current EUR-to-USD exchange rate is applied, with 10 EUR equating to 12 USD.
  3. Rafiki sends packets worth 12 USD. Your USD liquidity decreases to 38 (50 - 12).

Cross-currency transaction #2:

  1. Rafiki receives packets worth 50 EUR. Your EUR liquidity increases to 70 (20 + 50).
  2. The current EUR-to-USD exchange rate is applied, with 50 EUR equating to 55 USD.
  3. The transaction fails. Since your USD liquidity is 38, you do not have enough liquidity to cover the transaction.
  4. Your EUR liquidity reduces back to 20.

Peer liquidity accounts

Peer liquidity is the credit line you’ve extended to a peer. A peer liquidity account represents the amount of the line of credit that the peer still has available to them. You have one liquidity account for each peer and the account is denominated in the asset you both agreed to transact in.

The amount of credit that you extend to a peer, the asset that you transact in, and the mechanism you use to settle are just a few items that should be defined in your respective peering agreements.

If a peer’s liquidity is insufficient (e.g., they’ve used up their allotted credit line), payments will not be processed. Your peer should settle with you so that you can reset their liquidity.

You should define and adjust each peer’s liquidity based on your liquidity risk profile. You can deposit or withdraw peer liquidity as needed through Rafiki Admin or by using the Backend Admin API.

Peer liquidity example You and Cloud Nine Wallet are peers. You’ve agreed to extend Cloud Nine Wallet a line of credit worth 100.00 USD. This means Cloud Nine Wallet has 100.00 in their peer liquidity account on your Rafiki instance.

Your Rafiki instance can send packets that total up to 100.00 to wallet addresses issued by Cloud Nine Wallet. When the 100.00 is used up, Cloud Nine Wallet settles with you by sending 100.00 via the shared settlement mechanism outlined in your peering agreement. When you receive the funds, you reset their liquidity in Rafiki.

Payment liquidity accounts

Payment liquidity is the amount that’s available because of an incoming or outgoing payment. Rafiki has three types of payment liquidity accounts.

Payment typePurpose
IncomingFor incoming payments created via the Open Payments APIs
OutgoingFor outgoing payments created via the Open Payments APIs
Wallet addressFor incoming payments created via SPSP or Web Monetization
Incoming payment liquidity accounts

An incoming payment liquidity account represents the value received from a completed incoming payment. When an incoming payment is created via the Open Payments APIs, a corresponding liquidity account is automatically created. You will have one liquidity account per incoming payment.

You are notified of created, completed, and expired incoming payments by listening for the appropriate webhook events. Since Rafiki doesn’t hold funds, anything you receive must be withdrawn and then credited to the recipient’s account on your ledger.

The liquidity account isn’t used again after the payment completes, but its record remains in your accounting database. When a new incoming payment occurs, a new liquidity account is created.

Outgoing payment liquidity accounts

An outgoing payment liquidity account represents the value available to send in an outgoing payment. When an outgoing payment is created via the Open Payments APIs, a corresponding liquidity account is automatically created. You will have one liquidity account per outgoing payment.

You are notified of created, completed, and failed outgoing payments by listening for the appropriate webhook events. Liquidity must be deposited into the outgoing payment account before the payment can be processed.

Since Rafiki doesn’t hold funds, any excess liquidity that remains after the outgoing payment completes must be withdrawn. While we recommend refunding the excess to the sender, the choice is ultimately up to you.

The account isn’t used again after the payment completes, but its record remains in your accounting database. When a new outgoing payment is created, a new liquidity account is created.

Wallet address liquidity accounts

A wallet address liquidity account contains the value received to a wallet address via either SPSP or Web Monetization. When an incoming payment is created by one of these two methods, a corresponding liquidity account is automatically created. You will have one account per wallet address.

Since Rafiki doesn’t hold funds, you must withdraw the liquidity when the payment completes and credit the funds to the recipient’s account on your ledger. You are notified to withdraw liquidity by listening for the appropriate webhook event.

Unlike the incoming and outgoing payment liquidity accounts, the same wallet address liquidity account is used for future incoming SPSP or Web Monetization payments.

Settlement accounts

A settlement account represents the total funds, denominated in a specified asset, that you have deposited into Rafiki. You have one settlement account for each asset you transact in.

Settlement accounts hold either a zero or a negative balance. A negative balance on a settlement account means you’ve deposited more funds into Rafiki than you’ve taken out.

Rafiki ensures that the total credits to a settlement account will not exceed its total debits.

Settlement account example You deposit $10,000 into a peer’s liquidity account, meaning you’ve extended a credit line of $10,000 to your peer.

Your peer liquidity account balance is $10,000 and your USD settlement account balance is now -$10,000.

An incoming payment from your peer for $100 is created, meaning your peer is using $100 of their line of credit. Since Rafiki doesn’t hold funds, you must withdraw the liquidity and credit the amount to the recipient’s account on your ledger.

Now, your peer liquidity account’s balance is $9,900 and your USD settlement account’s balance is -$9,900.

A negative balance on a settlement account means you’ve deposited more funds into Rafiki than you’ve withdrawn. The closer a settlement account’s balance is to 0, the more likely it is you need to settle with your peer for the amount owed and then deposit the amount back into Rafiki.

Accounting databases

TigerBeetle

TigerBeetle is a high-performance distributed financial accounting database used by Rafiki’s backend service to store account balance data at the ILP layer. Both liquidity and settlement accounts in Rafiki correspond to TigerBeetle credit and debit accounts, respectively.

TigerBeetle only holds balance data without any additional ILP packet metadata. For detailed information on TigerBeetle, including its consensus mechanism and its limitations, visit the official TigerBeetle documentation and blog. For more information about TigerBeetle in a production Rafiki environment, see Running Rafiki in production.

Postgres

You can choose to use a separate Postgres database for accounting instead of using TigerBeetle. However, TigerBeetle is recommended due to its speed, efficiency, and dedicated design for handling double-entry/double-ledger accounting.

Transfers

As with the accounts described above, Rafiki performs double-entry accounting for transfers, where increasing the total debits of one account increases the total credits of another account by the same amount, and vice versa.

Transfers can be completed in either a single phase or in two phases.

Single-phase transfer

A single-phase transfer posts funds to accounts immediately when the transfer is created.

Example of successful single-phase incoming payment

sequenceDiagram
    participant R as Rafiki
    participant ASE as Account servicing entity

    R->>ASE: Fires webhook event when incoming payment completes
    ASE->>R: Withdraws payment amount from incoming payment liquidity account
    ASE->>ASE: Credits the recipient's account by the payment amount
  

Two-phase transfer

A two-phase transfer moves funds in two stages.

  1. Reserve funds (pending)
  2. Resolve funds (post, void, or expire)

Example of successful two-phase incoming payment

sequenceDiagram
    Rafiki->>ASE: Fires webhook event when incoming payment completes
    ASE->>Rafiki: Withdraws payment amount from incoming payment liquidity account (reserve funds pending)
    ASE->>ASE: Credits the recipient's account by the payment amount
    ASE->>Rafiki: Resolve funds (post)
    Rafiki->>Rafiki: Two-phase transfer complete
  

The name two-phase transfer is a reference to the two-phase commit protocol for distributed transactions.

Transfer examples

Intra-Rafiki transfer examples

Remember that a settlement account will always have a zero or negative balance and a liquidity account will always have a zero or positive balance.

Deposits

A deposit is the act of debiting the settlement account and crediting the liquidity account.

Example: Depositing 100 USD asset liquidity

Debit AccountCredit Account
SettlementAsset liquidity
USD settlement acct USD asset liquidity acct
DebitCredit
100
DebitCredit
100

Example: Depositing 100 USD peer liquidity

Debit AccountCredit Account
SettlementPeer liquidity
USD settlement acct USD peer liquidity acct
DebitCredit
100
DebitCredit
100

Example: Depositing 35 USD outgoing payment liquidity

Debit AccountCredit Account
SettlementOutgoing payment
USD settlement acctUSD outgoing payment liquidity acct
DebitCredit
35
DebitCredit
35

Withdrawals

A withdrawal is the act of debiting the liquidity account and crediting the settlement account.

Example: Withdrawing 50 USD in asset liquidity

Debit AccountCredit Account
Asset liquiditySettlement
USD asset liquidity acctUSD settlement acct
DebitCredit
50
DebitCredit
50

Example: Withdrawing 50 USD in peer liquidity

Debit AccountCredit Account
Peer liquiditySettlement
USD peer liquidity acctUSD settlement acct
DebitCredit
50
DebitCredit
50

Example: Withdrawing 2 USD in wallet address liquidity

Debit AccountCredit Account
Wallet addressSettlement
USD wallet address liquidity acctUSD settlement acct
DebitCredit
2
DebitCredit
2

Example: Withdrawing 2 USD in incoming payment liquidity

Debit AccountCredit Account
Incoming paymentSettlement
USD incoming payment liquidity acctUSD settlement acct
DebitCredit
25
DebitCredit
25

Example: Withdrawing 1 USD in outgoing payment liquidity

Debit AccountCredit Account
Outgoing paymentSettlement
USD outgoing payment liquidity acctUSD settlement acct
DebitCredit
1
DebitCredit
1

Payments in the same asset

Example: Sender consented to a payment of 14 USD but the quote promised to deliver 15 USD. The send amount is less than the receive amount.

Debit AccountCredit Account
Outgoing paymentIncoming payment
Asset liquidityIncoming payment
USD outgoing payment liquidity acctUSD asset liquidity acctUSD incoming payment liquidity acct
DebitCredit
14
DebitCredit
1
DebitCredit
15

Example: Sender consented to a payment of 15 USD but the quote promised to deliver 14 USD. The send amount is more than the receive amount.

Debit AccountCredit Account
Outgoing paymentIncoming payment
Outgoing paymentAsset liquidity
USD outgoing payment liquidity acctUSD asset liquidity acctUSD incoming payment liquidity acct
DebitCredit
15
DebitCredit
1
DebitCredit
14

Cross currency payments

Example: Outgoing payment is for 10 USD, incoming payment receives 9 EUR after a currency exchange

Debit AccountCredit AccountAsset
Outgoing paymentAsset liquidityUSD
Asset liquidityIncoming paymentEUR
USD outgoing payment liquidity acctUSD asset liquidity acct
DebitCredit
10
DebitCredit
10
EUR asset liquidity acctEUR incoming payment liquidity acct
DebitCredit
9
DebitCredit
9

Interledger transfer examples

In these examples, the sender and receiver do not have wallet addresses at the same Rafiki instance.

Remember that a settlement account will always have a zero or negative balance and a liquidity account will always have a zero or positive balance.

Sending connector - same asset

Example: Sender creates an outgoing payment for 100 USD to an incoming payment in the same asset at a peer’s Rafiki instance

Debit AccountCredit Account
Outgoing paymentPeer liquidity
USD outgoing payment liquidity acctUSD peer liquidity acct
DebitCredit
100
DebitCredit
100

Sending connector - cross currency

Example: Sender creates an outgoing payment for 100 USD to an incoming payment at a peer’s Rafiki instance. The peering relationship is in EUR, so the payment is converted on the sending side.

Debit AccountCredit AccountAsset
Outgoing paymentAsset liquidityUSD
Asset LiquidityPeer LiquidityEUR
USD outgoing payment liquidity acctUSD asset liquidity acct
DebitCredit
100
DebitCredit
100
EUR asset liquidity acctEUR peer liquidity acct
DebitCredit
90
DebitCredit
90

Receiving connector - same asset

Example: An incoming payment receives 100 USD from an outgoing payment in the same asset at a peer’s Rafiki instance.

Debit AccountCredit Account
Peer liquidityIncoming payment
USD peer liquidity acctUSD incoming payment liquidity acct
DebitCredit
100
DebitCredit
100

Receiving connector - cross currency

Example: A Rafiki instance receives 10 USD from a peer (peering relationship in USD) to be deposited in an incoming payment liquidity account denominated in EUR. The payment is converted to EUR and deposited.

Debit AccountCredit AccountAsset
Peer liquidityAsset liquidityUSD
Asset liquidityIncoming paymentEUR
USD peer liquidity acctUSD asset liquidity acct
DebitCredit
10
DebitCredit
10
EUR asset liquidity acctEUR incoming payment liquidity acct
DebitCredit
9
DebitCredit
9

Connector - same asset

Example: Rafiki forwards 10 USD from peer A to peer B.

Debit AccountCredit Account
Peer liquidityPeer liquidity
USD peer “A” liquidity acctUSD peer “B” liquidity acct
DebitCredit
10
DebitCredit
10

Connector - cross currency

Example: Rafiki receives 100 USD from peer A and forwards 90 EUR to peer B.

Debit AccountCredit AccountAsset
Peer liquidityAsset liquidityUSD
Asset liquidityPeer liquidityEUR
USD peer “A” liquidity acctUSD asset liquidity acct
DebitCredit
100
DebitCredit
100
EUR asset liquidity acctEUR peer “B” liquidity acct
DebitCredit
90
DebitCredit
90