ERC20Permit.test.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. /* eslint-disable */
  2. const { BN, constants, 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 ERC20Permit = artifacts.require('$ERC20Permit');
  9. const { Permit, getDomain, domainType, domainSeparator } = require('../../../helpers/eip712');
  10. const { getChainId } = require('../../../helpers/chainid');
  11. const { expectRevertCustomError } = require('../../../helpers/customError');
  12. contract('ERC20Permit', function (accounts) {
  13. const [initialHolder, spender] = accounts;
  14. const name = 'My Token';
  15. const symbol = 'MTKN';
  16. const initialSupply = new BN(100);
  17. beforeEach(async function () {
  18. this.chainId = await getChainId();
  19. this.token = await ERC20Permit.new(name, symbol, name);
  20. await this.token.$_mint(initialHolder, initialSupply);
  21. });
  22. it('initial nonce is 0', async function () {
  23. expect(await this.token.nonces(initialHolder)).to.be.bignumber.equal('0');
  24. });
  25. it('domain separator', async function () {
  26. expect(await this.token.DOMAIN_SEPARATOR()).to.equal(await getDomain(this.token).then(domainSeparator));
  27. });
  28. describe('permit', function () {
  29. const wallet = Wallet.generate();
  30. const owner = wallet.getAddressString();
  31. const value = new BN(42);
  32. const nonce = 0;
  33. const maxDeadline = MAX_UINT256;
  34. const buildData = (contract, deadline = maxDeadline) =>
  35. getDomain(contract).then(domain => ({
  36. primaryType: 'Permit',
  37. types: { EIP712Domain: domainType(domain), Permit },
  38. domain,
  39. message: { owner, spender, value, nonce, deadline },
  40. }));
  41. it('accepts owner signature', async function () {
  42. const { v, r, s } = await buildData(this.token)
  43. .then(data => ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }))
  44. .then(fromRpcSig);
  45. await this.token.permit(owner, spender, value, maxDeadline, v, r, s);
  46. expect(await this.token.nonces(owner)).to.be.bignumber.equal('1');
  47. expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(value);
  48. });
  49. it('rejects reused signature', async function () {
  50. const sig = await buildData(this.token).then(data =>
  51. ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }),
  52. );
  53. const { r, s, v } = fromRpcSig(sig);
  54. await this.token.permit(owner, spender, value, maxDeadline, v, r, s);
  55. const domain = await getDomain(this.token);
  56. const typedMessage = {
  57. primaryType: 'Permit',
  58. types: { EIP712Domain: domainType(domain), Permit },
  59. domain,
  60. message: { owner, spender, value, nonce: nonce + 1, deadline: maxDeadline },
  61. };
  62. await expectRevertCustomError(
  63. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  64. 'ERC2612InvalidSigner',
  65. [ethSigUtil.recoverTypedSignature({ data: typedMessage, sig }), owner],
  66. );
  67. });
  68. it('rejects other signature', async function () {
  69. const otherWallet = Wallet.generate();
  70. const { v, r, s } = await buildData(this.token)
  71. .then(data => ethSigUtil.signTypedMessage(otherWallet.getPrivateKey(), { data }))
  72. .then(fromRpcSig);
  73. await expectRevertCustomError(
  74. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  75. 'ERC2612InvalidSigner',
  76. [await otherWallet.getAddressString(), owner],
  77. );
  78. });
  79. it('rejects expired permit', async function () {
  80. const deadline = (await time.latest()) - time.duration.weeks(1);
  81. const { v, r, s } = await buildData(this.token, deadline)
  82. .then(data => ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }))
  83. .then(fromRpcSig);
  84. await expectRevertCustomError(
  85. this.token.permit(owner, spender, value, deadline, v, r, s),
  86. 'ERC2612ExpiredSignature',
  87. [deadline],
  88. );
  89. });
  90. });
  91. });