ERC20FlashMint.test.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /* eslint-disable */
  2. const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers');
  3. const { expect } = require('chai');
  4. const { MAX_UINT256, ZERO_ADDRESS } = constants;
  5. const ERC20FlashMintMock = artifacts.require('$ERC20FlashMintMock');
  6. const ERC3156FlashBorrowerMock = artifacts.require('ERC3156FlashBorrowerMock');
  7. contract('ERC20FlashMint', function (accounts) {
  8. const [initialHolder, other, anotherAccount] = accounts;
  9. const name = 'My Token';
  10. const symbol = 'MTKN';
  11. const initialSupply = new BN(100);
  12. const loanAmount = new BN(10000000000000);
  13. beforeEach(async function () {
  14. this.token = await ERC20FlashMintMock.new(name, symbol);
  15. await this.token.$_mint(initialHolder, initialSupply);
  16. });
  17. describe('maxFlashLoan', function () {
  18. it('token match', async function () {
  19. expect(await this.token.maxFlashLoan(this.token.address)).to.be.bignumber.equal(MAX_UINT256.sub(initialSupply));
  20. });
  21. it('token mismatch', async function () {
  22. expect(await this.token.maxFlashLoan(ZERO_ADDRESS)).to.be.bignumber.equal('0');
  23. });
  24. });
  25. describe('flashFee', function () {
  26. it('token match', async function () {
  27. expect(await this.token.flashFee(this.token.address, loanAmount)).to.be.bignumber.equal('0');
  28. });
  29. it('token mismatch', async function () {
  30. await expectRevert(this.token.flashFee(ZERO_ADDRESS, loanAmount), 'ERC20FlashMint: wrong token');
  31. });
  32. });
  33. describe('flashFeeReceiver', function () {
  34. it('default receiver', async function () {
  35. expect(await this.token.$_flashFeeReceiver()).to.be.eq(ZERO_ADDRESS);
  36. });
  37. });
  38. describe('flashLoan', function () {
  39. it('success', async function () {
  40. const receiver = await ERC3156FlashBorrowerMock.new(true, true);
  41. const { tx } = await this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x');
  42. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  43. from: ZERO_ADDRESS,
  44. to: receiver.address,
  45. value: loanAmount,
  46. });
  47. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  48. from: receiver.address,
  49. to: ZERO_ADDRESS,
  50. value: loanAmount,
  51. });
  52. await expectEvent.inTransaction(tx, receiver, 'BalanceOf', {
  53. token: this.token.address,
  54. account: receiver.address,
  55. value: loanAmount,
  56. });
  57. await expectEvent.inTransaction(tx, receiver, 'TotalSupply', {
  58. token: this.token.address,
  59. value: initialSupply.add(loanAmount),
  60. });
  61. expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply);
  62. expect(await this.token.balanceOf(receiver.address)).to.be.bignumber.equal('0');
  63. expect(await this.token.allowance(receiver.address, this.token.address)).to.be.bignumber.equal('0');
  64. });
  65. it('missing return value', async function () {
  66. const receiver = await ERC3156FlashBorrowerMock.new(false, true);
  67. await expectRevert(
  68. this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x'),
  69. 'ERC20FlashMint: invalid return value',
  70. );
  71. });
  72. it('missing approval', async function () {
  73. const receiver = await ERC3156FlashBorrowerMock.new(true, false);
  74. await expectRevert(
  75. this.token.flashLoan(receiver.address, this.token.address, loanAmount, '0x'),
  76. 'ERC20: insufficient allowance',
  77. );
  78. });
  79. it('unavailable funds', async function () {
  80. const receiver = await ERC3156FlashBorrowerMock.new(true, true);
  81. const data = this.token.contract.methods.transfer(other, 10).encodeABI();
  82. await expectRevert(
  83. this.token.flashLoan(receiver.address, this.token.address, loanAmount, data),
  84. 'ERC20: burn amount exceeds balance',
  85. );
  86. });
  87. it('more than maxFlashLoan', async function () {
  88. const receiver = await ERC3156FlashBorrowerMock.new(true, true);
  89. const data = this.token.contract.methods.transfer(other, 10).encodeABI();
  90. // _mint overflow reverts using a panic code. No reason string.
  91. await expectRevert.unspecified(this.token.flashLoan(receiver.address, this.token.address, MAX_UINT256, data));
  92. });
  93. describe('custom flash fee & custom fee receiver', function () {
  94. const receiverInitialBalance = new BN(200000);
  95. const flashFee = new BN(5000);
  96. beforeEach('init receiver balance & set flash fee', async function () {
  97. this.receiver = await ERC3156FlashBorrowerMock.new(true, true);
  98. const receipt = await this.token.$_mint(this.receiver.address, receiverInitialBalance);
  99. await expectEvent(receipt, 'Transfer', {
  100. from: ZERO_ADDRESS,
  101. to: this.receiver.address,
  102. value: receiverInitialBalance,
  103. });
  104. expect(await this.token.balanceOf(this.receiver.address)).to.be.bignumber.equal(receiverInitialBalance);
  105. await this.token.setFlashFee(flashFee);
  106. expect(await this.token.flashFee(this.token.address, loanAmount)).to.be.bignumber.equal(flashFee);
  107. });
  108. it('default flash fee receiver', async function () {
  109. const { tx } = await this.token.flashLoan(this.receiver.address, this.token.address, loanAmount, '0x');
  110. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  111. from: ZERO_ADDRESS,
  112. to: this.receiver.address,
  113. value: loanAmount,
  114. });
  115. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  116. from: this.receiver.address,
  117. to: ZERO_ADDRESS,
  118. value: loanAmount.add(flashFee),
  119. });
  120. await expectEvent.inTransaction(tx, this.receiver, 'BalanceOf', {
  121. token: this.token.address,
  122. account: this.receiver.address,
  123. value: receiverInitialBalance.add(loanAmount),
  124. });
  125. await expectEvent.inTransaction(tx, this.receiver, 'TotalSupply', {
  126. token: this.token.address,
  127. value: initialSupply.add(receiverInitialBalance).add(loanAmount),
  128. });
  129. expect(await this.token.totalSupply()).to.be.bignumber.equal(
  130. initialSupply.add(receiverInitialBalance).sub(flashFee),
  131. );
  132. expect(await this.token.balanceOf(this.receiver.address)).to.be.bignumber.equal(
  133. receiverInitialBalance.sub(flashFee),
  134. );
  135. expect(await this.token.balanceOf(await this.token.$_flashFeeReceiver())).to.be.bignumber.equal('0');
  136. expect(await this.token.allowance(this.receiver.address, this.token.address)).to.be.bignumber.equal('0');
  137. });
  138. it('custom flash fee receiver', async function () {
  139. const flashFeeReceiverAddress = anotherAccount;
  140. await this.token.setFlashFeeReceiver(flashFeeReceiverAddress);
  141. expect(await this.token.$_flashFeeReceiver()).to.be.eq(flashFeeReceiverAddress);
  142. expect(await this.token.balanceOf(flashFeeReceiverAddress)).to.be.bignumber.equal('0');
  143. const { tx } = await this.token.flashLoan(this.receiver.address, this.token.address, loanAmount, '0x');
  144. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  145. from: ZERO_ADDRESS,
  146. to: this.receiver.address,
  147. value: loanAmount,
  148. });
  149. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  150. from: this.receiver.address,
  151. to: ZERO_ADDRESS,
  152. value: loanAmount,
  153. });
  154. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  155. from: this.receiver.address,
  156. to: flashFeeReceiverAddress,
  157. value: flashFee,
  158. });
  159. await expectEvent.inTransaction(tx, this.receiver, 'BalanceOf', {
  160. token: this.token.address,
  161. account: this.receiver.address,
  162. value: receiverInitialBalance.add(loanAmount),
  163. });
  164. await expectEvent.inTransaction(tx, this.receiver, 'TotalSupply', {
  165. token: this.token.address,
  166. value: initialSupply.add(receiverInitialBalance).add(loanAmount),
  167. });
  168. expect(await this.token.totalSupply()).to.be.bignumber.equal(initialSupply.add(receiverInitialBalance));
  169. expect(await this.token.balanceOf(this.receiver.address)).to.be.bignumber.equal(
  170. receiverInitialBalance.sub(flashFee),
  171. );
  172. expect(await this.token.balanceOf(flashFeeReceiverAddress)).to.be.bignumber.equal(flashFee);
  173. expect(await this.token.allowance(this.receiver.address, flashFeeReceiverAddress)).to.be.bignumber.equal('0');
  174. });
  175. });
  176. });
  177. });