draft-ERC20Permit.test.js 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. /* eslint-disable */
  2. const { BN, constants, expectRevert, time } = require('@openzeppelin/test-helpers');
  3. const { expect } = require('chai');
  4. const { MAX_UINT256 } = constants;
  5. const { fromRpcSig } = require('ethereumjs-util');
  6. const ethSigUtil = require('eth-sig-util');
  7. const Wallet = require('ethereumjs-wallet').default;
  8. const ERC20PermitMock = artifacts.require('ERC20PermitMock');
  9. const { EIP712Domain, Permit, domainSeparator } = require('../../../helpers/eip712');
  10. contract('ERC20Permit', function (accounts) {
  11. const [ initialHolder, spender ] = accounts;
  12. const name = 'My Token';
  13. const symbol = 'MTKN';
  14. const version = '1';
  15. const initialSupply = new BN(100);
  16. beforeEach(async function () {
  17. this.token = await ERC20PermitMock.new(name, symbol, initialHolder, initialSupply);
  18. // We get the chain id from the contract because Ganache (used for coverage) does not return the same chain id
  19. // from within the EVM as from the JSON RPC interface.
  20. // See https://github.com/trufflesuite/ganache-core/issues/515
  21. this.chainId = await this.token.getChainId();
  22. });
  23. it('initial nonce is 0', async function () {
  24. expect(await this.token.nonces(initialHolder)).to.be.bignumber.equal('0');
  25. });
  26. it('domain separator', async function () {
  27. expect(
  28. await this.token.DOMAIN_SEPARATOR(),
  29. ).to.equal(
  30. await domainSeparator(name, version, this.chainId, this.token.address),
  31. );
  32. });
  33. describe('permit', function () {
  34. const wallet = Wallet.generate();
  35. const owner = wallet.getAddressString();
  36. const value = new BN(42);
  37. const nonce = 0;
  38. const maxDeadline = MAX_UINT256;
  39. const buildData = (chainId, verifyingContract, deadline = maxDeadline) => ({
  40. primaryType: 'Permit',
  41. types: { EIP712Domain, Permit },
  42. domain: { name, version, chainId, verifyingContract },
  43. message: { owner, spender, value, nonce, deadline },
  44. });
  45. it('accepts owner signature', async function () {
  46. const data = buildData(this.chainId, this.token.address);
  47. const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data });
  48. const { v, r, s } = fromRpcSig(signature);
  49. await this.token.permit(owner, spender, value, maxDeadline, v, r, s);
  50. expect(await this.token.nonces(owner)).to.be.bignumber.equal('1');
  51. expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(value);
  52. });
  53. it('rejects reused signature', async function () {
  54. const data = buildData(this.chainId, this.token.address);
  55. const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data });
  56. const { v, r, s } = fromRpcSig(signature);
  57. await this.token.permit(owner, spender, value, maxDeadline, v, r, s);
  58. await expectRevert(
  59. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  60. 'ERC20Permit: invalid signature',
  61. );
  62. });
  63. it('rejects other signature', async function () {
  64. const otherWallet = Wallet.generate();
  65. const data = buildData(this.chainId, this.token.address);
  66. const signature = ethSigUtil.signTypedMessage(otherWallet.getPrivateKey(), { data });
  67. const { v, r, s } = fromRpcSig(signature);
  68. await expectRevert(
  69. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  70. 'ERC20Permit: invalid signature',
  71. );
  72. });
  73. it('rejects expired permit', async function () {
  74. const deadline = (await time.latest()) - time.duration.weeks(1);
  75. const data = buildData(this.chainId, this.token.address, deadline);
  76. const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data });
  77. const { v, r, s } = fromRpcSig(signature);
  78. await expectRevert(
  79. this.token.permit(owner, spender, value, deadline, v, r, s),
  80. 'ERC20Permit: expired deadline',
  81. );
  82. });
  83. });
  84. });