ERC20FlashMint.test.js 8.5 KB

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