ERC20Wrapper.test.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. const { BN, constants, expectEvent } = require('@openzeppelin/test-helpers');
  2. const { expect } = require('chai');
  3. const { ZERO_ADDRESS, MAX_UINT256 } = constants;
  4. const { shouldBehaveLikeERC20 } = require('../ERC20.behavior');
  5. const { expectRevertCustomError } = require('../../../helpers/customError');
  6. const NotAnERC20 = artifacts.require('CallReceiverMock');
  7. const ERC20Decimals = artifacts.require('$ERC20DecimalsMock');
  8. const ERC20Wrapper = artifacts.require('$ERC20Wrapper');
  9. contract('ERC20Wrapper', function (accounts) {
  10. const [initialHolder, receiver] = accounts;
  11. const name = 'My Token';
  12. const symbol = 'MTKN';
  13. const initialSupply = new BN(100);
  14. beforeEach(async function () {
  15. this.underlying = await ERC20Decimals.new(name, symbol, 9);
  16. await this.underlying.$_mint(initialHolder, initialSupply);
  17. this.token = await ERC20Wrapper.new(`Wrapped ${name}`, `W${symbol}`, this.underlying.address);
  18. });
  19. afterEach(async function () {
  20. expect(await this.underlying.balanceOf(this.token.address)).to.be.bignumber.equal(await this.token.totalSupply());
  21. });
  22. it('has a name', async function () {
  23. expect(await this.token.name()).to.equal(`Wrapped ${name}`);
  24. });
  25. it('has a symbol', async function () {
  26. expect(await this.token.symbol()).to.equal(`W${symbol}`);
  27. });
  28. it('has the same decimals as the underlying token', async function () {
  29. expect(await this.token.decimals()).to.be.bignumber.equal('9');
  30. });
  31. it('decimals default back to 18 if token has no metadata', async function () {
  32. const noDecimals = await NotAnERC20.new();
  33. const otherToken = await ERC20Wrapper.new(`Wrapped ${name}`, `W${symbol}`, noDecimals.address);
  34. expect(await otherToken.decimals()).to.be.bignumber.equal('18');
  35. });
  36. it('has underlying', async function () {
  37. expect(await this.token.underlying()).to.be.bignumber.equal(this.underlying.address);
  38. });
  39. describe('deposit', function () {
  40. it('valid', async function () {
  41. await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder });
  42. const { tx } = await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder });
  43. await expectEvent.inTransaction(tx, this.underlying, 'Transfer', {
  44. from: initialHolder,
  45. to: this.token.address,
  46. value: initialSupply,
  47. });
  48. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  49. from: ZERO_ADDRESS,
  50. to: initialHolder,
  51. value: initialSupply,
  52. });
  53. });
  54. it('missing approval', async function () {
  55. await expectRevertCustomError(
  56. this.token.depositFor(initialHolder, initialSupply, { from: initialHolder }),
  57. 'ERC20InsufficientAllowance',
  58. [this.token.address, 0, initialSupply],
  59. );
  60. });
  61. it('missing balance', async function () {
  62. await this.underlying.approve(this.token.address, MAX_UINT256, { from: initialHolder });
  63. await expectRevertCustomError(
  64. this.token.depositFor(initialHolder, MAX_UINT256, { from: initialHolder }),
  65. 'ERC20InsufficientBalance',
  66. [initialHolder, initialSupply, MAX_UINT256],
  67. );
  68. });
  69. it('to other account', async function () {
  70. await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder });
  71. const { tx } = await this.token.depositFor(receiver, initialSupply, { from: initialHolder });
  72. await expectEvent.inTransaction(tx, this.underlying, 'Transfer', {
  73. from: initialHolder,
  74. to: this.token.address,
  75. value: initialSupply,
  76. });
  77. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  78. from: ZERO_ADDRESS,
  79. to: receiver,
  80. value: initialSupply,
  81. });
  82. });
  83. it('reverts minting to the wrapper contract', async function () {
  84. await this.underlying.approve(this.token.address, MAX_UINT256, { from: initialHolder });
  85. await expectRevertCustomError(
  86. this.token.depositFor(this.token.address, MAX_UINT256, { from: initialHolder }),
  87. 'ERC20InvalidReceiver',
  88. [this.token.address],
  89. );
  90. });
  91. });
  92. describe('withdraw', function () {
  93. beforeEach(async function () {
  94. await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder });
  95. await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder });
  96. });
  97. it('missing balance', async function () {
  98. await expectRevertCustomError(
  99. this.token.withdrawTo(initialHolder, MAX_UINT256, { from: initialHolder }),
  100. 'ERC20InsufficientBalance',
  101. [initialHolder, initialSupply, MAX_UINT256],
  102. );
  103. });
  104. it('valid', async function () {
  105. const value = new BN(42);
  106. const { tx } = await this.token.withdrawTo(initialHolder, value, { from: initialHolder });
  107. await expectEvent.inTransaction(tx, this.underlying, 'Transfer', {
  108. from: this.token.address,
  109. to: initialHolder,
  110. value: value,
  111. });
  112. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  113. from: initialHolder,
  114. to: ZERO_ADDRESS,
  115. value: value,
  116. });
  117. });
  118. it('entire balance', async function () {
  119. const { tx } = await this.token.withdrawTo(initialHolder, initialSupply, { from: initialHolder });
  120. await expectEvent.inTransaction(tx, this.underlying, 'Transfer', {
  121. from: this.token.address,
  122. to: initialHolder,
  123. value: initialSupply,
  124. });
  125. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  126. from: initialHolder,
  127. to: ZERO_ADDRESS,
  128. value: initialSupply,
  129. });
  130. });
  131. it('to other account', async function () {
  132. const { tx } = await this.token.withdrawTo(receiver, initialSupply, { from: initialHolder });
  133. await expectEvent.inTransaction(tx, this.underlying, 'Transfer', {
  134. from: this.token.address,
  135. to: receiver,
  136. value: initialSupply,
  137. });
  138. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  139. from: initialHolder,
  140. to: ZERO_ADDRESS,
  141. value: initialSupply,
  142. });
  143. });
  144. it('reverts withdrawing to the wrapper contract', async function () {
  145. expectRevertCustomError(
  146. this.token.withdrawTo(this.token.address, initialSupply, { from: initialHolder }),
  147. 'ERC20InvalidReceiver',
  148. [this.token.address],
  149. );
  150. });
  151. });
  152. describe('recover', function () {
  153. it('nothing to recover', async function () {
  154. await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder });
  155. await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder });
  156. const { tx } = await this.token.$_recover(receiver);
  157. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  158. from: ZERO_ADDRESS,
  159. to: receiver,
  160. value: '0',
  161. });
  162. });
  163. it('something to recover', async function () {
  164. await this.underlying.transfer(this.token.address, initialSupply, { from: initialHolder });
  165. const { tx } = await this.token.$_recover(receiver);
  166. await expectEvent.inTransaction(tx, this.token, 'Transfer', {
  167. from: ZERO_ADDRESS,
  168. to: receiver,
  169. value: initialSupply,
  170. });
  171. });
  172. });
  173. describe('erc20 behaviour', function () {
  174. beforeEach(async function () {
  175. await this.underlying.approve(this.token.address, initialSupply, { from: initialHolder });
  176. await this.token.depositFor(initialHolder, initialSupply, { from: initialHolder });
  177. });
  178. shouldBehaveLikeERC20(initialSupply, accounts);
  179. });
  180. });