UniswapV2Pair.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. // SPDX-License-Identifier: Apache-2.0
  2. import expect from 'expect';
  3. import { weight, query, createConnection, deploy, transaction, aliceKeypair, daveKeypair } from './index';
  4. import { ContractPromise } from '@polkadot/api-contract';
  5. import { ApiPromise } from '@polkadot/api';
  6. import { KeyringPair } from '@polkadot/keyring/types';
  7. const MINIMUM_LIQUIDITY = BigInt(1000);
  8. const TOTAL_SUPPLY = BigInt(10000e18);
  9. describe('UniswapV2Pair', () => {
  10. let conn: ApiPromise;
  11. let factory: ContractPromise;
  12. let pair: ContractPromise;
  13. let token0: ContractPromise;
  14. let token1: ContractPromise;
  15. let alice: KeyringPair;
  16. let dave: KeyringPair;
  17. beforeEach(async function () {
  18. conn = await createConnection();
  19. alice = aliceKeypair();
  20. dave = daveKeypair();
  21. // Upload UniswapV2Pair contract code so that it can instantiated from the factory
  22. // there probably is a better way of doing this than deploying a contract. Patches welcome.
  23. const pairTmp = await deploy(conn, alice, 'UniswapV2Pair.contract', 0n);
  24. const pairAbi = pairTmp.abi;
  25. let deploy_contract = await deploy(conn, alice, 'UniswapV2Factory.contract', 10000000000000000n, alice.address);
  26. factory = new ContractPromise(conn, deploy_contract.abi, deploy_contract.address);
  27. const tokenA_contract = await deploy(conn, alice, 'ERC20.contract', 0n, TOTAL_SUPPLY);
  28. const tokenA = new ContractPromise(conn, tokenA_contract.abi, tokenA_contract.address);
  29. const tokenB_contract = await deploy(conn, alice, 'ERC20.contract', 0n, TOTAL_SUPPLY);
  30. const tokenB = new ContractPromise(conn, tokenB_contract.abi, tokenB_contract.address);
  31. let gasLimit = await weight(conn, factory, "createPair", [tokenA.address, tokenB.address]);
  32. let tx = factory.tx.createPair({ gasLimit }, tokenA.address, tokenB.address);
  33. await transaction(tx, alice);
  34. const { output: get_pair } = await query(conn, alice, factory, "getPair", [tokenA.address, tokenB.address]);
  35. pair = new ContractPromise(conn, pairAbi, get_pair!.toString());
  36. const { output: token0_address } = await query(conn, alice, pair, "token0");
  37. if (tokenA.address.toString() == token0_address!.toString()) {
  38. token0 = tokenA;
  39. token1 = tokenB;
  40. } else {
  41. expect(tokenB.address.toString()).toEqual(token0_address!.toString());
  42. token0 = tokenB;
  43. token1 = tokenA;
  44. }
  45. });
  46. afterEach(async function () {
  47. await conn.disconnect();
  48. });
  49. it('mint', async () => {
  50. const token0Amount = BigInt(1e18)
  51. const token1Amount = BigInt(4e18)
  52. let gasLimit = await weight(conn, token0, "transfer", [pair.address, token0Amount]);
  53. let tx = token0.tx.transfer({ gasLimit }, pair.address, token0Amount);
  54. await transaction(tx, alice);
  55. gasLimit = await weight(conn, token1, "transfer", [pair.address, token1Amount]);
  56. tx = token1.tx.transfer({ gasLimit }, pair.address, token1Amount);
  57. await transaction(tx, alice);
  58. const expectedLiquidity = BigInt(2e18)
  59. gasLimit = await weight(conn, pair, "mint", [alice.address]);
  60. tx = pair.tx.mint({ gasLimit }, alice.address);
  61. await transaction(tx, alice);
  62. const { output: totalSupply } = await query(conn, alice, pair, "totalSupply");
  63. expect(totalSupply?.eq(expectedLiquidity)).toBeTruthy();
  64. const { output: bal } = await query(conn, alice, pair, "balanceOfAddress", [alice.address]);
  65. expect(bal?.eq(expectedLiquidity - MINIMUM_LIQUIDITY)).toBeTruthy();
  66. const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [pair.address]);
  67. expect(bal0?.eq(token0Amount)).toBeTruthy();
  68. const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [pair.address]);
  69. expect(bal1?.eq(token1Amount)).toBeTruthy();
  70. const { output: reserves } = await query(conn, alice, pair, "getReserves");
  71. // surely there must be a better way.
  72. let v: any = reserves;
  73. expect(v[0].eq(token0Amount)).toBeTruthy();
  74. expect(v[1].eq(token1Amount)).toBeTruthy();
  75. })
  76. async function addLiquidity(token0Amount: BigInt, token1Amount: BigInt) {
  77. let gasLimit = await weight(conn, token0, "transfer", [pair.address, token0Amount]);
  78. let tx = token0.tx.transfer({ gasLimit }, pair.address, token0Amount);
  79. await transaction(tx, alice);
  80. gasLimit = await weight(conn, token1, "transfer", [pair.address, token1Amount]);
  81. tx = token1.tx.transfer({ gasLimit }, pair.address, token1Amount);
  82. await transaction(tx, alice);
  83. gasLimit = await weight(conn, pair, "mint", [alice.address]);
  84. tx = pair.tx.mint({ gasLimit }, alice.address);
  85. await transaction(tx, alice);
  86. }
  87. it('swap:token0', async () => {
  88. const token0Amount = BigInt(5e18)
  89. const token1Amount = BigInt(10e18)
  90. await addLiquidity(token0Amount, token1Amount)
  91. const swapAmount = BigInt(1e18)
  92. const expectedOutputAmount = BigInt(1662497915624478906)
  93. let gasLimit = await weight(conn, token0, "transfer", [pair.address, swapAmount]);
  94. let tx = token0.tx.transfer({ gasLimit }, pair.address, swapAmount);
  95. await transaction(tx, alice);
  96. gasLimit = await weight(conn, pair, "swap", [0, expectedOutputAmount, alice.address, '']);
  97. tx = pair.tx.swap({ gasLimit }, 0, expectedOutputAmount, alice.address, '');
  98. await transaction(tx, alice);
  99. const { output: reserves } = await query(conn, alice, pair, "getReserves");
  100. // surely there must be a better way.
  101. let v: any = reserves;
  102. expect(v[0].eq(token0Amount + swapAmount)).toBeTruthy();
  103. expect(v[1].eq(token1Amount - expectedOutputAmount)).toBeTruthy();
  104. const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [pair.address]);
  105. expect(bal0?.eq(token0Amount + swapAmount)).toBeTruthy();
  106. const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [pair.address]);
  107. expect(bal1?.eq(token1Amount - expectedOutputAmount)).toBeTruthy();
  108. const { output: returnTotalSupplyToken0 } = await query(conn, alice, token0, "totalSupply");
  109. const { output: returnTotalSupplyToken1 } = await query(conn, alice, token1, "totalSupply");
  110. const { output: walletBal0 } = await query(conn, alice, token0, "balanceOf", [alice.address]);
  111. const { output: walletBal1 } = await query(conn, alice, token1, "balanceOf", [alice.address]);
  112. const totalSupplyToken0 = BigInt(returnTotalSupplyToken0!.toString());
  113. const totalSupplyToken1 = BigInt(returnTotalSupplyToken1!.toString());
  114. expect(walletBal0?.eq(totalSupplyToken0 - token0Amount - swapAmount)).toBeTruthy();
  115. expect(walletBal1?.eq(totalSupplyToken1 - token1Amount + expectedOutputAmount)).toBeTruthy();
  116. })
  117. it('swap:token1', async () => {
  118. const token0Amount = BigInt(5e18)
  119. const token1Amount = BigInt(10e18)
  120. await addLiquidity(token0Amount, token1Amount)
  121. const swapAmount = BigInt(1e18)
  122. const expectedOutputAmount = BigInt(453305446940074565)
  123. let gasLimit = await weight(conn, token1, "transfer", [pair.address, swapAmount]);
  124. let tx = token1.tx.transfer({ gasLimit }, pair.address, swapAmount);
  125. await transaction(tx, alice);
  126. gasLimit = await weight(conn, pair, "swap", [expectedOutputAmount, 0, alice.address, '']);
  127. tx = pair.tx.swap({ gasLimit }, expectedOutputAmount, 0, alice.address, '');
  128. await transaction(tx, alice);
  129. const { output: reserves } = await query(conn, alice, pair, "getReserves");
  130. // surely there must be a better way.
  131. let v: any = reserves;
  132. expect(v[0].eq(token0Amount - expectedOutputAmount)).toBeTruthy();
  133. expect(v[1].eq(token1Amount + swapAmount)).toBeTruthy();
  134. const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [pair.address]);
  135. expect(bal0?.eq(token0Amount - expectedOutputAmount)).toBeTruthy();
  136. const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [pair.address]);
  137. expect(bal1?.eq(token1Amount + swapAmount)).toBeTruthy();
  138. const { output: returnTotalSupplyToken0 } = await query(conn, alice, token0, "totalSupply");
  139. const { output: returnTotalSupplyToken1 } = await query(conn, alice, token1, "totalSupply");
  140. const { output: walletBal0 } = await query(conn, alice, token0, "balanceOf", [alice.address]);
  141. const { output: walletBal1 } = await query(conn, alice, token1, "balanceOf", [alice.address]);
  142. const totalSupplyToken0 = BigInt(returnTotalSupplyToken0!.toString());
  143. const totalSupplyToken1 = BigInt(returnTotalSupplyToken1!.toString());
  144. expect(walletBal0?.eq(totalSupplyToken0 - token0Amount + expectedOutputAmount)).toBeTruthy();
  145. expect(walletBal1?.eq(totalSupplyToken1 - token1Amount - swapAmount)).toBeTruthy();
  146. })
  147. it('burn', async () => {
  148. const token0Amount = BigInt(3e18)
  149. const token1Amount = BigInt(3e18)
  150. await addLiquidity(token0Amount, token1Amount)
  151. const expectedLiquidity = BigInt(3e18)
  152. let gasLimit = await weight(conn, pair, "transferAddressUint256", [pair.address, expectedLiquidity - MINIMUM_LIQUIDITY]);
  153. let tx = pair.tx.transferAddressUint256({ gasLimit }, pair.address, expectedLiquidity - MINIMUM_LIQUIDITY);
  154. await transaction(tx, alice);
  155. gasLimit = await weight(conn, pair, "burn", [alice.address]);
  156. tx = pair.tx.burn({ gasLimit }, alice.address);
  157. await transaction(tx, alice);
  158. const { output: walletBal0 } = await query(conn, alice, pair, "balanceOfAddress", [alice.address]);
  159. expect(walletBal0?.eq(0)).toBeTruthy();
  160. const { output: pairTotalSupply } = await query(conn, alice, pair, "totalSupply");
  161. expect(pairTotalSupply?.eq(MINIMUM_LIQUIDITY)).toBeTruthy();
  162. const { output: token0pairBal } = await query(conn, alice, token0, "balanceOf", [pair.address]);
  163. expect(token0pairBal?.eq(1000)).toBeTruthy();
  164. const { output: token1pairBal } = await query(conn, alice, token1, "balanceOf", [pair.address]);
  165. expect(token1pairBal?.eq(1000)).toBeTruthy();
  166. const { output: retToken0TotalSupply } = await query(conn, alice, token0, "totalSupply");
  167. const { output: retToken1TotalSupply } = await query(conn, alice, token1, "totalSupply");
  168. const totalSupplyToken0 = BigInt(retToken0TotalSupply!.toString());
  169. const totalSupplyToken1 = BigInt(retToken1TotalSupply!.toString());
  170. const { output: bal0 } = await query(conn, alice, token0, "balanceOf", [alice.address]);
  171. expect(bal0?.eq(totalSupplyToken0 - 1000n)).toBeTruthy();
  172. const { output: bal1 } = await query(conn, alice, token1, "balanceOf", [alice.address]);
  173. expect(bal1?.eq(totalSupplyToken1 - 1000n)).toBeTruthy();
  174. })
  175. });