How SatoshiPay leverages Stellar’s decentralized currency exchange
SatoshiPay’s recently introduced service DTransfer is a system to execute instant cross-border payments. Instead of the traditional banking system, it utilizes Stellar, a decentralized network for storing and moving money. The Stellar network maintains a list of accounts, their balances and a history of their transactions. It supports an unlimited number of different tokens that accounts can hold and exchange. There are many different types of tokens; for the purposes of this blog post we want to focus on one-to-one fiat-backed tokens (for short fiat tokens, also known as stablecoins) that are issued by regulated financial institutions that maintain reserves of fiat currencies equivalent to the value of the issued tokens. Such financial institutions are called anchors [1]. Anchors usually also allow to swap fiat currencies outside of Stellar for their fiat tokens and vice versa.
When transferring money from a business client in a source country to a recipient in a target country, DTransfer goes through the following steps:
- On-ramp: convert the source currency into corresponding fiat tokens on the Stellar network through an anchor
- Exchange: exchange the tokens of the source currency into tokens of the target currency
- Off-ramp: convert the target tokens into fiat currency in the target country, again through an anchor
Step 2 happens entirely within the Stellar network. In this article we will focus on this step and describe three approaches of how to achieve such an exchange with Stellar.
Over-The-Counter Exchange
A direct token swap with another party is the most straightforward solution. Simply negotiate an exchange rate R, e.g., the mid-market exchange rate of the according fiat currencies and agree on an amount A to swap. Then send A source fiat tokens to the other party and receive A * R target fiat tokens in return. Conventionally, such a two-way payment poses the risk that only one payment is executed due to technical reasons or because one party cannot be trusted. Fortunately, on Stellar this can be accomplished in a way that requires no trust from the counterparty and vice versa by employing Stellar’s built-in system of transactions. Essentially a transaction is a collection of payment instructions that will be executed atomically: either all payments will happen simultaneously or none of them will. An over the counter exchange is executed by creating a transaction with the two payment instructions comprising the swap.
Example Code
Here we show a TypeScript example for creating a direct over-the-counter exchange. The following code makes use the Stellar SDK NPM package. It takes the keypairs for the two accounts, the two assets (defining the token types) and the amount and price as inputs and creates, signs and submits the transaction to Stellar using its Horizon Server. Note that the transaction consists of two payment operations and needs to be signed by both parties.
Limitations of over-the-counter swaps
This still leaves one other crucial problem: how to find a counterparty in a timely manner that is willing to exchange the tokens at an acceptable exchange rate. Solving such a problem is the purpose of a market. Luckily, such a market mechanism is built in directly into the Stellar protocol.
The Stellar Decentralized Exchange
Stellar has a powerful built-in system to exchange any pair of tokens: the SDEX or Stellar Decentralized Exchange. For every possible pair of tokens (here called the source token and the target token) the SDEX maintains an order book, which is simply a collection of offers. Each offer represents an intent to sell a certain amount of the source tokens in exchange for a certain amount of the target tokens or vice versa.
Let us have a closer look at offers. Each offer consists of
- issuer
- direction
- price
- amount
The issuer is any account registered on the Stellar network and represents the creator of the offer. The direction can be either “source to target” or “target to source”. A “source to target” offer is the intent to sell source tokens and to buy target tokens. A “target to source” offer has the opposite meaning. The price is the exchange rate and defined as the number of target tokens for one source token. The amount is the number of source tokens intended to be sold or bought.
Let us look at an example. Suppose we consider the order book where the source token is a Euro fiat token and the target token is a US Dollar fiat token. We will show the order book graphically, where each colored box in the following chart represents one offer. The color of the box denotes its direction: red is “source to target” and blue is “target to source” — in the sequel we will also say red offers and blue offers instead of “source to target” and “target to source” offers for the sake of intuition. The position on the x-axis is the price and its height is the amount of the offer.
In our example diagram there are two “source to target” offers, one with price=1.4 and amount=10 and one with price=1.5 and amount=20. The latter offer expresses that the issuer of the offer is willing to sell 20 EUR tokens in exchange for 30 USD tokens. We will stack boxes that have the same price — in this diagram there are two “target to source” offers that have the price 0.9. The smaller one of these offers expresses that the issuer intends to sell 9 USD tokens in exchange for 10 EUR tokens.
The order book has an important invariant: none of the offers in the order book match. A red offer and a blue offer match if they have the same price or if the price of the former is lower than the price of the latter. Put differently: if two offers match, then the issuers of these offers can successfully swap tokens for an exchange rate that both parties would find acceptable.
The order book is publicly available [2] and every Stellar account can add new offers to the order book at any time. Incoming offers are processed one by one and the behavior of the SDEX differs depending on whether the new offer matches any of the offers in the order book.
As an example consider that an issuer adds a new blue offer having price=1.0 and amount=30; then the offer will not match any existing offer and will just be added to the order book so that the result will look as follows:
Suppose that we intend to add another blue offer to the order book, this time having amount=30 and price=1.4. This offer will match the other offer in the order book at this price. The SDEX will now cross these offers and will completely fill the smaller one (the one having a smaller amount — in this case the red one): It will
- Move 10 EUR tokens from the issuer of the red offer to the issuer of the new offer
- Move 14 USD tokens in the opposite direction
- Remove the red offer from the order book as it has been resolved
The new offer is not resolved yet as its issuer only bought 10 EUR tokens instead of the intended 30 EUR tokens. An according offer with the remaining amount is now placed in the order book:
Note that the order book satisfies its main invariant again: red boxes are right of blue boxes. However, the boundary between the blue and red boxes shifted to the right.
As a last example let us consider what happens if multiple offers match. To this end let us add a red offer having price=1.0 and amount=40. This time there are three candidate blue offers that match. The SDEX requires a strategy to choose one or more blue offers to cross and — when selecting multiple offers — which one to cross first. Note that this is a crucial choice: suppose we cross the new offer with the offer at price=1.4. The issuers can exchange tokens at any price between 1.0 and 1.4 because any price in that range would be acceptable for both of them. However, if we cross the new offer with the offer at price 1.0, then only an exchange rate of 1.0 would be acceptable.
The strategy of the SDEX is to maximize the benefit of the issuer of the new offer. For that reason it will
- Pick the rightmost blue offer to cross (or, if the new offer is a blue offer, then pick the leftmost red offer) and
- Use the price of that offer as the exchange rate (in our case 1.4)
When crossing the offer in the order book, the issuer of the new offer will sell 20 EUR tokens and buy 28 USD tokens. Clearly the new offer is not resolved yet after crossing the rightmost offer, 20 EUR tokens are still to be sold. As there are more matching offers, this procedure repeats and the SDEX will cross the new offer with the offer at price 1.1 — the issuer sells 10 EUR tokens and buys 11 USD tokens. This leaves 10 EUR tokens to be sold. Finally the offer at price 1.0 is crossed, swapping 10 EUR tokens for 10 USD tokens. This procedure ends here: the new offer is resolved and nothing will be added to the order book. Only the existing offer at price 1.0 will be reduced by the exchanged amount. The resulting order book looks as follows:
The issuer sold 40 EUR tokens in exchange for 28 + 11 + 10 = 49 USD tokens, an effective exchange rate of 49/40 = 1.225.
Example Code
The following code snippet shows how to create an offer and submit it to the order book. In addition to the four constituents that make up an offer the function also takes the source and target assets as inputs in order to select the proper order book. Stellar supports two different operations for creating an offer: createSellOffer and createBuyOffer. The former function creates “source to target” offer and the latter function a “target to source” offer.
Market Makers
After this more technical account of the SDEX let us discuss whether it solves our problem to find a trading partner. As long as there is enough liquidity in the market, i.e., enough offers in the order book at an acceptable price, then we can effectively utilize it to exchange tokens in real time. This can be ensured by market makers, entities that add offers to the SDEX in order to add liquidity to the market. In the next section, we will look at an innovative approach to market making in the Stellar ecosystem.
Automated Market Makers
A solution to that problem is to automate the market making process. An automated market maker (AMM) is an algorithmic agent that facilitates a trade that would not occur in the market [3]. There is a lot of development and research about AMMs in the Decentralized Finance (DeFi) space as they allow for high liquidity for digital token exchanges. DeFi and AMMs in particular employ the power of smart contracts, programs that run on a decentralized network and have their own account.
Traditionally, general smart contracts can only be implemented in Turing-complete distributed ledgers such as Ethereum. Although there is considerable activity to add Turing-complete concepts to Stellar [4], there currently is no solution available that is on par with Ethereum smart contracts, but there are simpler alternatives, which will be able to satisfy most scenarios where an AMM is required. In order to understand how we can employ AMMs, we will explain how an AMM works and how to implement a smart contract-like solution on Stellar.
How AMMs work
Let us consider the example of exchanging EUR and USD tokens again. An automated market maker for this token pair is like a vending machine that contains a pool of EUR tokens and USD tokens. Feed the vending machine some tokens and you will receive some of the tokens of the other type in return. The vending machine ensures that it never runs out of either token type: the fewer number of tokens it has left of the token type to be bought, the fewer it will return for the same amount of tokens to be sold. As a result the vending machine exhibits diminishing returns.
In addition the AMM ensures that it will also not lose funds in the long run. Suppose that we feed the machine 10 EUR tokens and the machine returns 12 USD tokens. If subsequently we put 12 USD tokens back into the machine, it should return at most 10 EUR tokens, otherwise it would lose funds. The simplest way to ensure this is to predefine a relationship between amounts of EUR tokens and amounts of USD tokens inside the machine: for each EUR token amount we fix a corresponding USD token amount. At every point in time the machine ensures that it only holds amounts that correspond in this way. Such a correspondence can be depicted as a graph as follows:
The blue curve is the set of admissible combinations of amounts of EUR tokens and amounts of USD tokens in the vending machine. Suppose that currently it holds tokens that correspond to the red dot in the upper left of the curve. If we put EUR tokens in the machine, this will move this dot to the right (indicated by the arrow pointing to the right). The curve then determines the amount of USD tokens to be returned (the arrow pointing downward). Evidently, the slope of the line through these two dots is the resulting exchange rate (or the negative exchange rate to be more precise).
This simple method will also have the effect that there is no need to develop a strategy for exchanging tokens using the AMM: independent of whether the token exchange is done at once or split into many small parts, the total number of tokens in return is always the same.
It is clear that the curve chosen to define the correspondence between both tokens needs to be monotonically decreasing: when putting tokens into the machine, it should always return tokens of the other type. Furthermore, it should never reach the axes — only approach them asymptotically — to ensure that there is always a non-zero amount of either token type left to always make an exchange possible and to always provide liquidity. The simplest curve having these properties is a hyperbola, which is defined by
“EUR token amount” = k / “USD token amount”,
where k is some constant. Equivalently, the product of the amounts of either token type is defined to be constant. For that reason this model is called a constant-product market maker. This is the system used by, e.g., Uniswap. We will see that for fiat tokens there are better alternatives than this simple model: a crucial property is how the exchange rate changes when the red dot moves along the curve.
As pointed out above the exchange rate is closely related to the slope of the curve at the red dot. Due to the curve’s property to only approach the axes asymptotically it becomes clear that the price increases and approaches infinity when the number of EUR tokens decreases. Likewise, the price approaches 0 if the number of USD tokens decreases. This sensitive reaction of the price to changing amounts of tokens is called slippage. The slippage can be thought of as the curve’s mathematical curvature. One usually wants to avoid high slippage because that would make it unfavorable to exchange larger amounts, which in turn implies that the AMM is not effective at providing liquidity.
The lowest slippage is exhibited by a linear curve because it has constant slope. However, a linear curve reaches and crosses the axes and therefore does not satisfy the criteria put forward above. For that reason one can combine both approaches and design a curve that is almost linear in some central section and then curves towards the axes like a hyperbola outside that section [5]. Note that this method works better if we can expect that the state of the AMM will stay in this central section most of the time — a reasonable assumption for, e.g., an AMM for different fiat tokens that represent the same fiat currency. An example of an AMM that uses this approach is Curve.
Arbitrage and Incentive
Arbitrageurs will make sure that the red dot on the curve is always pushed to a point where the slope of the curve equals the market exchange rate of the fiat currencies and for that reason the exchange rate of the AMM is usually close to the market rate. Of course this only holds if slippage is low and the amounts to be exchanged are not excessively large.
The slippage can be further reduced by having a big original pool of tokens in the vending machine. The more parties that provide tokens to this pool, the better liquidity the AMM can provide. For that reason AMMs usually incentivize investors to provide tokens for the pool by taking a fee for every exchange and distributing it among the investors.
AMMs as Smart Contracts
Let us now leave the vending machine analogy behind and look at how AMMs can be realized in Stellar. An AMM comprises a single Stellar account that holds EUR tokens and USD tokens. The logic of how the AMM behaves when it is supposed to exchange tokens is defined as an algorithm — this is the smart contract. In Ethereum, which supports smart contracts as first-class citizens, the smart contract is directly attached to the account and is executed on-chain, i.e., the same distributed network that manages the account also executes the smart contract algorithm.
This is not possible in Stellar: currently Stellar is restricted to manage accounts and tokens and is not capable of running arbitrary smart contracts. For that reason we need to separate the smart contract algorithm from Stellar and run it outside the network. The idea is to perform a simple over the counter swap as explained at the beginning of this article. We instruct Stellar to execute such a swap by first creating a transaction containing two payment instructions. This transaction is algorithmically created by the smart contract.
It is evident how such an algorithm will look like, its logic is basically defined by the blue curve in the diagram above. It is crucial that there is no way to create improper transactions, e.g., transactions that pay out too much money to the user of the AMM. Stellar has a built in mechanism for that: it will only accept and handle transactions that are properly signed by all stakeholders. In this case the AMM account is set up in a way that it requires the computer running the smart contract algorithm to properly sign exchange transactions. This way we make sure that the only way to create a valid transaction is to use the smart contract.
This system has a potential problem: the providers of the tokens of the AMM pool need to trust the operator that runs the smart contract. As this computer is able to sign transactions for the AMM account, the operator could simply take over the account and steal the funds. This problem does not occur in systems where smart contracts are run on-chain: those systems operate completely trustless as the complete network and not a single entity runs the contract.
We can improve this situation significantly as follows:
- Decentralize and distribute the smart contract over a network of computers
- Set up the AMM account in a way that exchange transactions become valid only if (almost) all machines in that network sign the transaction
A user of the AMM will first contact each machine in the network and request to create and sign the exchange transaction and will then collect the signatures and submit a valid transaction to Stellar. Using this approach it suffices to trust a single (or a few) entities of the network. A proposal for such a system is called Turing Signing Servers (TSS) as they can execute Turing-complete smart contracts.
TSS is a recent proposal. At SatoshiPay we have been actively researching how to build AMMs utilizing TSS for DTransfer. TSS is powerful but has some weaknesses compared to on-chain smart contracts: for one it does not reach the same level of trustlessness and the number of transactions that can be executed in a certain amount of time is more limited. For that reason we are actively investigating approaches that go beyond TSS.
Conclusion
In this post we discussed three methods to exchange tokens in Stellar: (1) direct over-the-counter swaps, (2) utilizing the Stellar’s built-in decentralized exchange SDEX and (3) employing automated market makers using smart contracts. Whereas over-the-counter swaps are very convenient to use, they only work if a trading partner is readily available. The SDEX allows to freely trade tokens on a market with other entities and market makers in particular. Automated market makers are built using smart contracts and are an efficient way to provide liquidity.
There is high activity to further develop DeFi concepts and to build DeFi applications on top of Stellar. Automated market makers will become more scalable in the future and will be one of the most promising methods for exchanging currencies.
References
[1] https://www.stellar.org/learn/anchor-basics?locale=en
[2] As an example, the live order book for XLM, Stellar’s native tokens, and Anchor USD tokens can be found here: https://horizon.stellar.org/order_book?buying_asset_type=native&selling_asset_type=credit_alphanum4&selling_asset_code=USD&selling_asset_issuer=GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX
A graphical view of the same order book is here: https://stellar.expert/explorer/public/asset/USD-GDUKMGUGDZQK6YHYA5Z6AY2G4XDSZPSZ3SW5UN3ARVMO6QSRDWP5YLEX?filter=markets
[3] http://reports-archive.adm.cs.cmu.edu/anon/2012/CMU-CS-12-123.pdf