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.
Property | Type | Description | Example |
---|---|---|---|
value | BigInt | A numerical amount | 10000 |
assetCode | String | A code representing the asset. An ISO 4217 currency code should be used whenever possible. | "USD" |
assetScale | Integer | Difference in order of magnitude between the standard unit and a fractional unit | 2 |
To convert an asset’s value into an amount that’s easier to interpret, apply the following formula.
Using the example data from the table above, the formula looks like this:
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 type | What the account represents | Number of accounts |
---|---|---|
Asset liquidity | The value, denominated in a given asset, that Rafiki has at its disposal for sending or forwarding ILP packets | One per asset |
Peer liquidity | The credit line, denominated in the asset of your peering relationship, that you extend to a peer | One per peer |
Incoming payment liquidity | The value received from a completed incoming payment | One per incoming payment |
Outgoing payment liquidity | The value that Rafiki will attempt to send in an outgoing payment | One per outgoing payment |
Wallet address liquidity | The value of an incoming payment received to a wallet address via either SPSP or Web Monetization | One 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:
- Rafiki receives packets worth 10 EUR. Your EUR liquidity increases to 20 (10 + 10).
- The current EUR-to-USD exchange rate is applied, with 10 EUR equating to 12 USD.
- Rafiki sends packets worth 12 USD. Your USD liquidity decreases to 38 (50 - 12).
Cross-currency transaction #2:
- Rafiki receives packets worth 50 EUR. Your EUR liquidity increases to 70 (20 + 50).
- The current EUR-to-USD exchange rate is applied, with 50 EUR equating to 55 USD.
- The transaction fails. Since your USD liquidity is 38, you do not have enough liquidity to cover the transaction.
- 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 type | Purpose |
---|---|
Incoming | For incoming payments created via the Open Payments APIs |
Outgoing | For outgoing payments created via the Open Payments APIs |
Wallet address | For 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.
- Reserve funds (
pending
) - Resolve funds (
post
,void
, orexpire
)
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 Account | Credit Account |
---|---|
Settlement | Asset liquidity |
USD settlement acct | USD asset liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Example: Depositing 100 USD
peer liquidity
Debit Account | Credit Account |
---|---|
Settlement | Peer liquidity |
USD settlement acct | USD peer liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Example: Depositing 35 USD
outgoing payment liquidity
Debit Account | Credit Account |
---|---|
Settlement | Outgoing payment |
USD settlement acct | USD outgoing payment liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Withdrawals
A withdrawal is the act of debiting the liquidity account and crediting the settlement account.
Example: Withdrawing 50 USD
in asset liquidity
Debit Account | Credit Account |
---|---|
Asset liquidity | Settlement |
USD asset liquidity acct | USD settlement acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Example: Withdrawing 50 USD
in peer liquidity
Debit Account | Credit Account |
---|---|
Peer liquidity | Settlement |
USD peer liquidity acct | USD settlement acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Example: Withdrawing 2 USD
in wallet address liquidity
Debit Account | Credit Account |
---|---|
Wallet address | Settlement |
USD wallet address liquidity acct | USD settlement acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Example: Withdrawing 2 USD
in incoming payment liquidity
Debit Account | Credit Account |
---|---|
Incoming payment | Settlement |
USD incoming payment liquidity acct | USD settlement acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Example: Withdrawing 1 USD
in outgoing payment liquidity
Debit Account | Credit Account |
---|---|
Outgoing payment | Settlement |
USD outgoing payment liquidity acct | USD settlement acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
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 Account | Credit Account |
---|---|
Outgoing payment | Incoming payment |
Asset liquidity | Incoming payment |
USD outgoing payment liquidity acct | USD asset liquidity acct | USD incoming payment liquidity acct | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
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 Account | Credit Account |
---|---|
Outgoing payment | Incoming payment |
Outgoing payment | Asset liquidity |
USD outgoing payment liquidity acct | USD asset liquidity acct | USD incoming payment liquidity acct | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
|
Cross currency payments
Example: Outgoing payment is for 10 USD
, incoming payment receives 9 EUR
after a currency exchange
Debit Account | Credit Account | Asset |
---|---|---|
Outgoing payment | Asset liquidity | USD |
Asset liquidity | Incoming payment | EUR |
USD outgoing payment liquidity acct | USD asset liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
| ||||||||
EUR asset liquidity acct | EUR incoming payment liquidity acct | ||||||||
|
|
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 Account | Credit Account |
---|---|
Outgoing payment | Peer liquidity |
USD outgoing payment liquidity acct | USD peer liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
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 Account | Credit Account | Asset |
---|---|---|
Outgoing payment | Asset liquidity | USD |
Asset Liquidity | Peer Liquidity | EUR |
USD outgoing payment liquidity acct | USD asset liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
| ||||||||
EUR asset liquidity acct | EUR peer liquidity acct | ||||||||
|
|
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 Account | Credit Account |
---|---|
Peer liquidity | Incoming payment |
USD peer liquidity acct | USD incoming payment liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
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 Account | Credit Account | Asset |
---|---|---|
Peer liquidity | Asset liquidity | USD |
Asset liquidity | Incoming payment | EUR |
USD peer liquidity acct | USD asset liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
| ||||||||
EUR asset liquidity acct | EUR incoming payment liquidity acct | ||||||||
|
|
Connector - same asset
Example: Rafiki forwards 10 USD
from peer A to peer B.
Debit Account | Credit Account |
---|---|
Peer liquidity | Peer liquidity |
USD peer “A” liquidity acct | USD peer “B” liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
|
Connector - cross currency
Example: Rafiki receives 100 USD
from peer A and forwards 90 EUR
to peer B.
Debit Account | Credit Account | Asset |
---|---|---|
Peer liquidity | Asset liquidity | USD |
Asset liquidity | Peer liquidity | EUR |
USD peer “A” liquidity acct | USD asset liquidity acct | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
| ||||||||
EUR asset liquidity acct | EUR peer “B” liquidity acct | ||||||||
|
|