UniswapV2Pair.spec.ts 11 KB

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