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 }