/ test / pools / LinearLiquidityPool / TestMulticoinTrading.t.sol
TestMulticoinTrading.t.sol
  1  pragma solidity >=0.6.0;
  2  
  3  import "truffle/Assert.sol";
  4  import "./Base.t.sol";
  5  
  6  contract TestMulticoinTrading is Base {
  7  
  8      event LogUint(string, uint);
  9  
 10      ERC20Mock stablecoinA;
 11      ERC20Mock stablecoinB;
 12      ERC20Mock stablecoinC;
 13  
 14      uint optionBuyPrice;
 15      uint optionSellPrice;
 16  
 17  /* testBuyWithMultipleCoins
 18  
 19      Initialize the LiquidityPool
 20      Trader A deposits the amount of 20*P Stablecoins A in the LiquidityPool
 21      Trader B buys an option from the pool paying the amount of P with Stablecoin B
 22      Trader C buys an option from the pool paying the amount of P with Stablecoin C
 23      Verify that all options were issued correctly and that the liquidity pool balance is 22*P
 24  */
 25      function testBuyWithMultipleCoins() public {
 26  
 27          stablecoinA = erc20;
 28          stablecoinB = ERC20Mock(deployer.getContractAddress("StablecoinB"));
 29          stablecoinC = ERC20Mock(deployer.getContractAddress("StablecoinC"));
 30  
 31          addSymbol();
 32  
 33          (optionBuyPrice,) = IGovernableLiquidityPool(pool).queryBuy(symbol, true);
 34          (optionSellPrice,) = IGovernableLiquidityPool(pool).queryBuy(symbol, false);
 35  
 36          //emit LogUint("0.optionBuyPrice is", optionBuyPrice);
 37          //emit LogUint("0.optionSellPrice is", optionSellPrice);
 38  
 39          uint decimals_diff = 0;
 40  
 41          // Trader A deposits the amount of 10*P Stablecoins A in the LiquidityPool
 42          PoolTrader userA = createPoolTrader(address(stablecoinA));
 43          uint volumeA = 20; // this volume is base by Options, not decimals
 44          uint amount = volumeA * optionBuyPrice;
 45  
 46          stablecoinA.issue(address(this), amount);
 47          stablecoinA.approve(address(pool), amount);
 48          IGovernableLiquidityPool(pool).depositTokens(address(userA), address(stablecoinA), amount);
 49  
 50          Assert.equal(IERC20(pool).balanceOf(address(userA)), amount, "userA stablecoinA after deposit");
 51          emit LogUint("1.balanceOf(pool) userA deposit", exchange.balanceOf(address(pool)));
 52          uint volume = 1 * volumeBase;
 53  
 54          // Trader B buys an option from the pool paying the amount of P with Stablecoin B
 55          PoolTrader userB = createPoolTrader(address(stablecoinB));
 56          decimals_diff = 1e9;
 57          stablecoinB.issue(address(userB), volume * optionBuyPrice / decimals_diff);
 58  
 59          address(userB).call(
 60              abi.encodePacked(
 61                  userB.buyFromPool.selector,
 62                  abi.encode(symbol, optionBuyPrice, volume)
 63              )
 64          );
 65          
 66          OptionToken tk_B = OptionToken(symbolAddr);
 67          Assert.equal(tk_B.balanceOf(address(userB)), volume, "userB options");
 68          emit LogUint("1.balanceOf(pool) after userB buy", exchange.balanceOf(address(pool)));
 69  
 70          // Trader C buys an option from the pool paying the amount of P with Stablecoin C
 71          PoolTrader userC = createPoolTrader(address(stablecoinC));
 72          decimals_diff = 1e12;
 73          stablecoinC.issue(address(userC), volume * optionBuyPrice / decimals_diff);
 74  
 75          address(userC).call(
 76              abi.encodePacked(
 77                  userC.buyFromPool.selector,
 78                  abi.encode(symbol, optionBuyPrice, volume)
 79              )
 80          );
 81          OptionToken tk_C = OptionToken(symbolAddr);
 82          Assert.equal(tk_C.balanceOf(address(userC)), volume, "userC options");
 83  
 84          emit LogUint("1.balanceOf(pool) after userC buy",exchange.balanceOf(address(pool)));
 85  
 86          // Verify that all options were issued correctly and that the liquidity pool balance is 22*P
 87          Assert.equal(
 88              exchange.balanceOf(address(pool)),
 89              22 * optionBuyPrice,
 90              "get pool balance from exchange"
 91          );
 92      }
 93  
 94  /* testBuyWithCombinationOfCoins
 95  
 96      Initialize the LiquidityPool
 97      Trader A deposits the amount of 20*P Stablecoins A in the LiquidityPool
 98      Trader B deposits the amount of P/2 Stablecoins B in the OptionsExchange
 99      Trader B deposits the amount of P/2 Stablecoins C in the OptionsExchange
100      Trader B buys an option from the pool paying with his exchange balance
101      Verify that the option was issued correctly and that the liquidity pool balance is 21*P
102  */
103      function testBuyWithCombinationOfCoins() public {
104  
105          stablecoinA = erc20;
106          stablecoinB = ERC20Mock(deployer.getContractAddress("StablecoinB"));
107          stablecoinC = ERC20Mock(deployer.getContractAddress("StablecoinC"));
108  
109          addSymbol();
110  
111          (optionBuyPrice,) = IGovernableLiquidityPool(pool).queryBuy(symbol, true);
112          (optionSellPrice,) = IGovernableLiquidityPool(pool).queryBuy(symbol, false);
113  
114          emit LogUint("0.optionBuyPrice is", optionBuyPrice);
115          emit LogUint("0.optionSellPrice is", optionSellPrice);
116  
117          uint decimals_diff = 0;
118          uint amount = 0;
119          uint totalBalance_userB = 0;
120  
121          // Trader A deposits the amount of 20*P Stablecoins A in the LiquidityPool
122          PoolTrader userA = createPoolTrader(address(stablecoinA));
123          uint volumeA = 20; // this volume is base by Options, not decimals
124          amount = volumeA*optionBuyPrice;
125  
126          stablecoinA.issue(address(this), amount);
127          stablecoinA.approve(address(pool), amount);
128          IGovernableLiquidityPool(pool).depositTokens(address(userA), address(stablecoinA), amount);
129  
130          Assert.equal(IERC20(pool).balanceOf(address(userA)), amount, "userA stablecoinA after deposit");
131          emit LogUint("2.balanceOf(pool) userA deposit", exchange.balanceOf(address(pool)));
132          
133          // Trader B deposits the amount of P/2 Stablecoins B in the OptionsExchange
134          PoolTrader userB = createPoolTrader(address(stablecoinB)); 
135          decimals_diff = 1e9; // StablecoinB decimals is 9, diff=1e/(18-9)
136          amount = optionBuyPrice / 2 / decimals_diff; // optionBuyPrice decimals is 18
137  
138          stablecoinB.issue(address(this), amount);
139          emit LogUint("2.amount(stB) userB issue",amount);
140  
141          stablecoinB.approve(address(exchange), amount);
142          exchange.depositTokens(address(userB), address(stablecoinB), amount);
143          
144          emit LogUint("2.balanceOf(ex) userB deposit", exchange.balanceOf(address(userB)));
145  
146          totalBalance_userB = amount * decimals_diff;        
147          Assert.equal(exchange.balanceOf(address(userB)), totalBalance_userB, "userB stablecoinB after deposit");
148          
149          // Trader B deposits the amount of P/2 Stablecoins C in the OptionsExchange
150          decimals_diff = 1e12; // StablecoinC decimals is 6,diff=1e/(18-6)
151          amount = optionBuyPrice / 2 / decimals_diff; // optionBuyPrice decimals is 18
152  
153          stablecoinC.issue(address(this), amount);
154          emit LogUint("2.amount(stC) userB issue", amount);
155  
156          stablecoinC.approve(address(exchange), amount);
157          exchange.depositTokens(address(userB), address(stablecoinC), amount);
158          
159          emit LogUint("2.balanceOf(ex) userB deposit", exchange.balanceOf(address(userB)));
160          totalBalance_userB = totalBalance_userB+amount*decimals_diff;
161          Assert.equal(
162              exchange.balanceOf(address(userB)),
163              totalBalance_userB,
164              "userB stablecoinC after deposit"
165          );
166  
167          // Trader B buys an option from the pool paying with his exchange balance
168          uint volume = 1 * volumeBase;
169          decimals_diff = 1e9;
170          stablecoinB.issue(address(userB), volume * optionBuyPrice / decimals_diff);
171  
172          address(userB).call(
173              abi.encodePacked(
174                  userB.buyFromPool.selector,
175                  abi.encode(symbol, optionBuyPrice, volume)
176              )
177          );
178          
179          OptionToken tk = OptionToken(symbolAddr);
180          Assert.equal(tk.balanceOf(address(userB)), volume, "userB options");
181          emit LogUint("2.balanceOf(pool) after userB buy", exchange.balanceOf(address(pool)));
182          
183          // Verify that the option was issued correctly and that the liquidity pool balance is 21*P
184          Assert.equal(
185              exchange.balanceOf(address(pool)),
186              21 * optionBuyPrice,
187              "get pool balance from exchange"
188          );
189      }
190  
191  /* testSellAndWithdrawCombinationOfCoins
192  
193      Initialize the LiquidityPool
194      Trader A deposits the amount of 3*P/4 Stablecoins A in the LiquidityPool
195      Trader A deposits the amount of 3*P/4 Stablecoins B in the LiquidityPool
196      Trader B deposits the amount of 10*P Stablecoins C in the OptionsExchange
197      Trader B writes an option in the OptionsExchange
198      Trader B sells his option to the LiquidityPool
199      The option expires OTM (out-of-the-money)
200      Trader B withdraws all his exchange balance
201      Verify that Trader B balances is a combination of Stablecoins “A”, “B” and “C” whose value add up to 11*P
202  */
203      function testSellAndWithdrawCombinationOfCoins() public {
204  
205          stablecoinA = erc20;
206          stablecoinB = ERC20Mock(deployer.getContractAddress("StablecoinB"));
207          stablecoinC = ERC20Mock(deployer.getContractAddress("StablecoinC"));
208  
209          addSymbol();
210  
211          (optionBuyPrice,) = IGovernableLiquidityPool(pool).queryBuy(symbol, true);
212          (optionSellPrice,) = IGovernableLiquidityPool(pool).queryBuy(symbol, false);
213  
214          emit LogUint("0.optionBuyPrice is", optionBuyPrice);
215          emit LogUint("0.optionSellPrice is", optionSellPrice);
216  
217          uint decimals_diff = 0;
218          uint amount = 0;
219          uint pool_total = 0;
220  
221          //Trader A deposits the amount of 3*P/4 Stablecoins A in the LiquidityPool
222          PoolTrader userA = createPoolTrader(address(stablecoinA));  
223          amount = 3 * optionSellPrice / 4;
224  
225          stablecoinA.issue(address(this), amount);
226          stablecoinA.approve(address(pool), amount);
227          IGovernableLiquidityPool(pool).depositTokens(address(userA), address(stablecoinA), amount);
228          pool_total = amount;
229  
230          Assert.equal(IERC20(pool).balanceOf(address(userA)), pool_total, "userA stablecoinA after deposit");
231          emit LogUint("3.balanceOf(pool) userA deposit", exchange.balanceOf(address(pool)));
232  
233          // Trader A deposits the amount of 3*P/4 Stablecoins B in the LiquidityPool
234          decimals_diff = 1e9;
235          amount = 3 * optionSellPrice / 4 / decimals_diff;
236  
237          stablecoinB.issue(address(this), amount);
238          stablecoinB.approve(address(pool), amount);
239          IGovernableLiquidityPool(pool).depositTokens(address(userA), address(stablecoinB), amount);
240          pool_total = pool_total + amount * decimals_diff;
241  
242          Assert.equal(IERC20(pool).balanceOf(address(userA)), pool_total, "userA stablecoinB after deposit");
243          emit LogUint("3.balanceOf(pool) userA deposit", exchange.balanceOf(address(pool)));
244  
245          // Trader B deposits the amount of 10*P Stablecoins C in the OptionsExchange
246          PoolTrader userB = createPoolTrader(address(stablecoinA));  
247  
248          decimals_diff = 1e12;
249          amount = 10 * optionSellPrice / decimals_diff;
250  
251          stablecoinC.issue(address(this), amount);
252          stablecoinC.approve(address(exchange), amount);
253          exchange.depositTokens(address(userB), address(stablecoinC), amount);
254          
255          emit LogUint("3.balanceOf(ex) userB deposit", exchange.balanceOf(address(userB)));
256          Assert.equal(
257              exchange.balanceOf(address(userB)),
258              amount * decimals_diff,
259              "userB stablecoinC after deposit"
260          );
261  
262          // Trader B writes an option in the OptionsExchange
263          uint test_strike = 550e18;
264          emit LogUint("3.balanceOf(ex) userB write", exchange.balanceOf(address(userB)));
265  
266          // Trader B sells his option to the LiquidityPool
267          uint volume = 1 * volumeBase;
268          (uint sellPrice,) = IGovernableLiquidityPool(pool).queryBuy(symbol, false);
269  
270          
271          (bool success,) = address(userB).call(
272              abi.encodePacked(
273                  userB.sellToPool.selector,
274                  abi.encode(symbol, sellPrice, volume)
275              )
276          );
277  
278          Assert.equal(success,true, "userB sold to pool");
279  
280          emit LogUint("3.balanceOf(ex) userB selltopool", exchange.balanceOf(address(userB)));
281          emit LogUint("3.userB surplus before liquidate", exchange.calcSurplus(address(userB)));
282  
283          // The option expires OTM (out-of-the-money)
284          uint step = 40e18;
285          feed.setPrice(int(test_strike - step));
286          time.setTimeOffset(30 days);
287          
288          (bool success1,) = address(userB).call(
289              abi.encodePacked(
290                  collateralManager.liquidateOptions.selector,
291                  abi.encode(symbolAddr, address(userB))
292              )
293          );
294          emit LogUint("3.userB surplus after liquidate", exchange.calcSurplus(address(userB)));
295  
296          // Trader B withdraws all his exchange balance
297          emit LogUint("3.balanceOf(ex) userB after liquidate", exchange.balanceOf(address(userB)));
298          userB.withdrawTokens(exchange.calcSurplus(address(userB)));
299          Assert.equal(
300              exchange.balanceOf(address(userB)),
301              0,
302              "userB exchange balance is zero after withdraw."
303          );
304  
305          // Verify that Trader B balances is a combination of Stablecoins “A”, “B” and “C” whose value add up to 11*P
306          Assert.notEqual(stablecoinA.balanceOf(address(userB)), 0, "stablecoinA balance not zero");
307          Assert.notEqual(stablecoinB.balanceOf(address(userB)), 0, "stablecoinB balance not zero");
308          Assert.notEqual(stablecoinC.balanceOf(address(userB)), 0, "stablecoinC balance not zero");
309          uint totalValue =
310              stablecoinA.balanceOf(address(userB)) +
311              stablecoinB.balanceOf(address(userB)) * 1e9 +
312              stablecoinC.balanceOf(address(userB)) * 1e12;
313  
314          emit LogUint("3.balanceOf(total) userB", totalValue);
315          Assert.equal(optionSellPrice * 11, totalValue, "userB total value is 6*op");
316      }
317  }