draft-ERC20Permit.test.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  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 ERC20Permit = artifacts.require('$ERC20Permit');
  9. const { Permit, getDomain, domainType, domainSeparator } = require('../../../helpers/eip712');
  10. const { getChainId } = require('../../../helpers/chainid');
  11. contract('ERC20Permit', function (accounts) {
  12. const [initialHolder, spender] = accounts;
  13. const name = 'My Token';
  14. const symbol = 'MTKN';
  15. const version = '1';
  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 { v, r, s } = await buildData(this.token)
  51. .then(data => ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }))
  52. .then(fromRpcSig);
  53. await this.token.permit(owner, spender, value, maxDeadline, v, r, s);
  54. await expectRevert(
  55. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  56. 'ERC20Permit: invalid signature',
  57. );
  58. });
  59. it('rejects other signature', async function () {
  60. const otherWallet = Wallet.generate();
  61. const { v, r, s } = await buildData(this.token)
  62. .then(data => ethSigUtil.signTypedMessage(otherWallet.getPrivateKey(), { data }))
  63. .then(fromRpcSig);
  64. await expectRevert(
  65. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  66. 'ERC20Permit: invalid signature',
  67. );
  68. });
  69. it('rejects expired permit', async function () {
  70. const deadline = (await time.latest()) - time.duration.weeks(1);
  71. const { v, r, s } = await buildData(this.token, deadline)
  72. .then(data => ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }))
  73. .then(fromRpcSig);
  74. await expectRevert(this.token.permit(owner, spender, value, deadline, v, r, s), 'ERC20Permit: expired deadline');
  75. });
  76. });
  77. });