|
1 | 1 | ---
|
2 |
| -title: Token-Swap Program |
| 2 | +title: Token Swap Program |
3 | 3 | ---
|
4 | 4 |
|
5 |
| -A Uniswap-like exchange for the Token program on the Solana blockchain. |
| 5 | +A Uniswap-like exchange for the Token program on the Solana blockchain, |
| 6 | +implementing multiple automated market maker (AMM) curves. |
6 | 7 |
|
7 |
| -The project is under construction. |
| 8 | +## Overview |
| 9 | + |
| 10 | +The Token Swap Program allows simple trading of token pairs without a |
| 11 | +centralized limit order book. The program uses a mathematical formula called |
| 12 | +"curve" to calculate the price of all trades. Curves aim to mimic normal market |
| 13 | +dynamics: for example, as traders buy a lot of one token type, the value of the |
| 14 | +other token type goes up. |
| 15 | + |
| 16 | +Depositors in the token swap pool provide liquidity for the token pair. That |
| 17 | +liquidity enables trade execution at spot price. In exchange for their |
| 18 | +liquidity, depositors receive pool tokens, representing their fractional |
| 19 | +ownership in the pool. During each trade, a program withholds a portion of the |
| 20 | +input token as a fee. That fee increases the value of pool tokens by being |
| 21 | +stored in the pool. |
| 22 | + |
| 23 | +This program was heavily inspired by [Uniswap](https://uniswap.org/) and |
| 24 | +[Balancer](https://balancer.finance/). More information is available in their |
| 25 | +excellent documentation and whitepapers. |
| 26 | + |
| 27 | +## Background |
| 28 | + |
| 29 | +Solana's programming model and the definitions of the Solana terms used in this |
| 30 | +document are available at: |
| 31 | + |
| 32 | +- https://docs.solana.com/apps |
| 33 | +- https://docs.solana.com/terminology |
| 34 | + |
| 35 | +## Source |
| 36 | + |
| 37 | +The Token Swap Program's source is available on |
| 38 | +[github](https://github.com/solana-labs/solana-program-library). |
| 39 | + |
| 40 | +## Interface |
| 41 | + |
| 42 | +[JavaScript |
| 43 | +bindings](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/js/client/token-swap.js) |
| 44 | +are available that support loading the Token Swap Program on to a chain and |
| 45 | +issuing instructions. |
| 46 | + |
| 47 | +Example user interface built and maintained by Serum team is available |
| 48 | +[here](https://github.com/project-serum/oyster-swap) |
| 49 | + |
| 50 | +## Operational overview |
| 51 | + |
| 52 | +The following explains the instructions available in the Token Swap Program. |
| 53 | +Note that each instruction has a simple code example that can be found in the |
| 54 | +[end-to-end tests](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/js/cli/token-swap-test.js). |
| 55 | + |
| 56 | +### Creating a new token swap |
| 57 | + |
| 58 | +The creation of a token swap showcases the account, instruction, and authorization |
| 59 | +models on Solana, which can be very different compared to other blockchains. |
| 60 | + |
| 61 | +Initialization of a token swap between two token types, which we'll call "A" |
| 62 | +and "B" for simplicity, requires the following accounts: |
| 63 | + |
| 64 | +* empty token swap state account |
| 65 | +* token swap authority |
| 66 | +* token A account |
| 67 | +* token B account |
| 68 | +* pool token mint |
| 69 | +* pool token fee account |
| 70 | +* pool token recipient account |
| 71 | +* token program |
| 72 | + |
| 73 | +The token swap state account simply needs to be created using |
| 74 | +`system_instruction::create_account` with the correct size and enough lamports |
| 75 | +to be rent-free. |
| 76 | + |
| 77 | +The token swap authority is a |
| 78 | +[program derived address](https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses) |
| 79 | +that can "sign" instructions towards other programs. This is |
| 80 | +required for the Token Swap Program to mint pool tokens and transfer tokens from |
| 81 | +its token A and B accounts. |
| 82 | + |
| 83 | +The token A / B accounts, pool token mint, and pool token accounts must all be |
| 84 | +created (using `system_instruction::create_account`) and initialized (using |
| 85 | +`spl_token::instruction::initialize_mint` or |
| 86 | +`spl_token::instruction::initialize_account`). The token A and B accounts must |
| 87 | +be funded with tokens, and their owner set to the swap authority, and the mint |
| 88 | +must also be owned by the swap authority. |
| 89 | + |
| 90 | +Once all of these accounts are created, the Token Swap `initialize` instruction |
| 91 | +will properly set everything up and allow for immediate trading. Note |
| 92 | +that the token swap state account is not required to be a signer on `initialize`, |
| 93 | +so it's important to perform the `initialize` instruction in the same transaction |
| 94 | +as its `system_instruction::create_account`. |
| 95 | + |
| 96 | +### Swapping |
| 97 | + |
| 98 | +Once a token swap is created, users can immediately begin trading on it using |
| 99 | +the `swap` instruction. The swap instruction transfers tokens from a user's source |
| 100 | +account into the swap's source token account, and then transfers tokens from |
| 101 | +its destination token account into the user's destination token account. |
| 102 | + |
| 103 | +Since Solana programs require all accounts to be declared in the instruction, |
| 104 | +users need to gather all account information from the token swap state account: |
| 105 | +the token A and B accounts, pool token mint, and fee account. |
| 106 | + |
| 107 | +Additionally, the user must allow for tokens to be transferred from their source |
| 108 | +token account. The best practice is to `spl_token::instruction::approve` a |
| 109 | +precise amount to a new throwaway Keypair, and then have that new Keypair sign |
| 110 | +the swap transaction. This limits the amount of tokens that can be taken |
| 111 | +from the user's account by the program. |
| 112 | + |
| 113 | +### Depositing liquidity |
| 114 | + |
| 115 | +To allow any trading, the token swap needs liquidity provided from the |
| 116 | +outside. Using the `deposit_all_token_types` or |
| 117 | +`deposit_single_token_type_exact_amount_in` instructions, anyone can provide |
| 118 | +liquidity for others to trade, and in exchange, depositors receive a pool token |
| 119 | +representing fractional ownership of all A and B tokens in the token swap. |
| 120 | + |
| 121 | +Additionally, the user will need to approve a delegate to transfer tokens from |
| 122 | +their A and B token accounts. This limits the amount of tokens that can be taken |
| 123 | +from the user's account by the program. |
| 124 | + |
| 125 | +### Withdrawing liquidity |
| 126 | + |
| 127 | +At any time, pool token holders may redeem their pool tokens in exchange for |
| 128 | +tokens A and B, returned at the current "fair" rate as determined by the curve. |
| 129 | +In the `withdraw_all_token_types` and |
| 130 | +`withdraw_single_token_type_exact_amount_out` instructions, pool tokens are |
| 131 | +burned, and tokens A and B are transferred into the user's accounts. |
| 132 | + |
| 133 | +Additionally, the user will need to approve a delegate to transfer tokens from |
| 134 | +their pool token account. This limits the amount of tokens that can be taken |
| 135 | +from the user's account by the program. |
| 136 | + |
| 137 | +## Curves |
| 138 | + |
| 139 | +The Token Swap Program is completely customizable for any possible trading curve |
| 140 | +that implements the |
| 141 | +[CurveCalculator](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/src/curve/calculator.rs) |
| 142 | +trait. If you would like to implement a new automated market maker, it may be |
| 143 | +as easy as forking the Token Swap Program and implementing a new curve. The |
| 144 | +following curves are all provided out of the box for reference. |
| 145 | + |
| 146 | +### Constant product |
| 147 | + |
| 148 | +The [constant product |
| 149 | +curve](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/src/curve/constant_product.rs) |
| 150 | +is the well-known Uniswap and Balancer style curve that preserves an invariant |
| 151 | +on all swaps, expressed as the product of the quantity of token A and token B |
| 152 | +in the swap. |
| 153 | + |
| 154 | +``` |
| 155 | +A_total * B_total = invariant |
| 156 | +``` |
| 157 | + |
| 158 | +If a trader wishes to put in token A for some amount of token B, the calculation |
| 159 | +for token B becomes: |
| 160 | + |
| 161 | +``` |
| 162 | +(A_total + A_in) * (B_total - B_out) = invariant |
| 163 | +``` |
| 164 | + |
| 165 | +For example, if the swap has 100 token A and 5,000 token B, and a trader wishes |
| 166 | +to put in 10 token A, we can solve for the `invariant` and then `B_out`: |
| 167 | + |
| 168 | +``` |
| 169 | +A_total * B_total = 100 * 5,000 = 500,000 = invariant |
| 170 | +``` |
| 171 | + |
| 172 | +And |
| 173 | + |
| 174 | +``` |
| 175 | +(A_total + A_in) * (B_total - B_out) = invariant |
| 176 | +(100 + 10) * (5,000 - B_out) = 500,000 |
| 177 | +5,000 - B_out = 500,000 / 110 |
| 178 | +5,000 - (500,000 / 110) = B_out |
| 179 | +B_out = 454.5454... |
| 180 | +``` |
| 181 | + |
| 182 | +More information can be found on the [Uniswap |
| 183 | +whitepaper](https://uniswap.org/whitepaper.pdf) and the [Balancer |
| 184 | +whitepaper](https://balancer.finance/whitepaper/). |
| 185 | + |
| 186 | +### Constant price |
| 187 | + |
| 188 | +The [constant price curve](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/src/curve/constant_price.rs) |
| 189 | +is a simple curve that always maintains the price of token A with respect to |
| 190 | +token B. At initialization, the swap creator sets the cost for 1 token B in |
| 191 | +terms of token A. For example, if the price is set to 17, 17 token A will always |
| 192 | +be required to receive 1 token B, and 1 token B will always be required to |
| 193 | +receive 17 token A. |
| 194 | + |
| 195 | +Note that this curve does not follow traditional market dynamics, since the |
| 196 | +price is always the same. |
| 197 | + |
| 198 | +Constant price curves are most useful for fixed offerings of new tokens that |
| 199 | +explicitly should not have market dynamics. For example, a decentralized |
| 200 | +game creator wants to sell new "SOLGAME" tokens to be used in their |
| 201 | +game, so they create a constant price swap of 2 USDC per SOLGAME, and supply all |
| 202 | +of the SOLGAME tokens at swap creation. Users can go to the swap and purchase all |
| 203 | +of the tokens they want and not worry about the market making SOLGAME tokens too |
| 204 | +expensive. |
| 205 | + |
| 206 | +### Stable (under construction) |
| 207 | + |
| 208 | +The [stable curve](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/src/curve/stable.rs) |
| 209 | +from [curve.fi](https://www.curve.fi/), has a different shape to prioritize |
| 210 | +"stable" trading, meaning prices that stay constant through trading. Most |
| 211 | +importantly, prices don't change as quickly as the constant product curve, so a |
| 212 | +stable swap between two coins that represent the same value should be as close |
| 213 | +to 1:1 as possible. For example, stablecoins that represent a value in USD (USDC, |
| 214 | +TUSD, USDT, DAI), should not have big price discrepancies due to the amount of |
| 215 | +tokens in the swap. |
| 216 | + |
| 217 | +The curve mirrors the dynamics of the curve |
| 218 | +More information can be found on their [whitepaper](https://www.curve.fi/stableswap-paper.pdf). |
| 219 | + |
| 220 | +The Token Swap Program implementation of the stable curve is under construction, |
| 221 | +and a more complete version can be found at the |
| 222 | +[stable-swap-program](https://github.com/michaelhly/stable-swap-program/). |
| 223 | + |
| 224 | +### Offset |
| 225 | + |
| 226 | +The [offset curve](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/src/curve/offset.rs) |
| 227 | +can be seen as a combination of the constant price and constant product curve. |
| 228 | +It follows the constant product curve dynamics, but allows for the token swap |
| 229 | +creator to set an "offset" on one side. The invariant for the curve is: |
| 230 | + |
| 231 | +``` |
| 232 | +(A_total) * (B_total + B_offset) = invariant |
| 233 | +``` |
| 234 | + |
| 235 | +This is useful for initial token |
| 236 | +offerings, where the token creator wants to sell some new token as a swap without |
| 237 | +putting up the capital to fund the other side of the swap. This is similar to the |
| 238 | +constant price curve, but the key difference is that the offset curve captures |
| 239 | +normal market dynamics, in that the offered token price will increase as it is |
| 240 | +bought. |
| 241 | + |
| 242 | +For example, a decentralized betting application creator wants to sell new "SOLBET" |
| 243 | +tokens on the market in exchange for USDC, and they believe each token is worth |
| 244 | +at least 4 USDC. They create a token swap between SOLBET and USDC, funding |
| 245 | +one side with 1,000 SOLBET, and the other side with 0 USDC, but an offset |
| 246 | +of 4,000 USDC. |
| 247 | + |
| 248 | +If a trader tries to buy SOLBET with 40 USDC, the invariant is calculated |
| 249 | +with the offset: |
| 250 | + |
| 251 | +``` |
| 252 | +(SOLBET_total) * (USDC_total + USDC_offset) = invariant |
| 253 | +1,000 * (0 + 4,000) = 4,000,000 |
| 254 | +
|
| 255 | +(SOLBET_total - SOLBET_out) * (USDC_total + USDC_offset + USDC_in) = invariant |
| 256 | +SOLBET_out = 9.901 |
| 257 | +``` |
| 258 | + |
| 259 | +The trader received 9.901 SOLBET for 40 USDC, so the price per SOLBET was |
| 260 | +roughly 4.04, slightly higher than the minimum of 4 USDC per SOLBET. |
| 261 | + |
| 262 | +Conversely, if a trader tries to buy USDC with SOLBET immediately after creation, |
| 263 | +it will fail because there is no USDC actually present in the pool. |
| 264 | + |
| 265 | +## Testing |
| 266 | + |
| 267 | +The token-swap program is tested using various strategies, including unit tests, |
| 268 | +integration tests, property tests, and fuzzing. Since unit tests and integration |
| 269 | +tests are well-known, we highlight property tests and fuzzing here. |
| 270 | + |
| 271 | +### Property testing |
| 272 | + |
| 273 | +Using the [proptest](https://altsysrq.github.io/proptest-book/intro.html) |
| 274 | +crate, we test specific mathematical properties of curves, specifically to avoid |
| 275 | +leaking value on any trades, deposits, or withdrawals. It is out of scope of |
| 276 | +this document to explain property testing, but the specific property tests for |
| 277 | +the Token Swap Program can be found in the |
| 278 | +[curves](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/src/curve/constant_product.rs) |
| 279 | +and |
| 280 | +[math](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/src/curve/math.rs) |
| 281 | +portions of the repo. |
| 282 | + |
| 283 | +### Fuzzing |
| 284 | + |
| 285 | +Using [honggfuzz](https://github.com/rust-fuzz/honggfuzz-rs), we regularly |
| 286 | +test all possible inputs to the Token Swap Program, ensuring that the program |
| 287 | +does not crash unexpectedly or leak tokens. It is out of scope of this document |
| 288 | +to explain fuzzing, but the specific implementation for the program can be found |
| 289 | +in the [instruction fuzz |
| 290 | +tests](https://github.com/solana-labs/solana-program-library/blob/master/token-swap/program/fuzz/src/instructions.rs) |
| 291 | +of the repo. |
0 commit comments