draft-ERC20Permit.test.js 3.6 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 { EIP712Domain, Permit, 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(
  27. await domainSeparator({ name, version, chainId: this.chainId, verifyingContract: this.token.address }),
  28. );
  29. });
  30. describe('permit', function () {
  31. const wallet = Wallet.generate();
  32. const owner = wallet.getAddressString();
  33. const value = new BN(42);
  34. const nonce = 0;
  35. const maxDeadline = MAX_UINT256;
  36. const buildData = (chainId, verifyingContract, deadline = maxDeadline) => ({
  37. primaryType: 'Permit',
  38. types: { EIP712Domain, Permit },
  39. domain: { name, version, chainId, verifyingContract },
  40. message: { owner, spender, value, nonce, deadline },
  41. });
  42. it('accepts owner signature', async function () {
  43. const data = buildData(this.chainId, this.token.address);
  44. const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data });
  45. const { v, r, s } = fromRpcSig(signature);
  46. await this.token.permit(owner, spender, value, maxDeadline, v, r, s);
  47. expect(await this.token.nonces(owner)).to.be.bignumber.equal('1');
  48. expect(await this.token.allowance(owner, spender)).to.be.bignumber.equal(value);
  49. });
  50. it('rejects reused signature', async function () {
  51. const data = buildData(this.chainId, this.token.address);
  52. const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data });
  53. const { v, r, s } = fromRpcSig(signature);
  54. await this.token.permit(owner, spender, value, maxDeadline, v, r, s);
  55. await expectRevert(
  56. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  57. 'ERC20Permit: invalid signature',
  58. );
  59. });
  60. it('rejects other signature', async function () {
  61. const otherWallet = Wallet.generate();
  62. const data = buildData(this.chainId, this.token.address);
  63. const signature = ethSigUtil.signTypedMessage(otherWallet.getPrivateKey(), { data });
  64. const { v, r, s } = fromRpcSig(signature);
  65. await expectRevert(
  66. this.token.permit(owner, spender, value, maxDeadline, v, r, s),
  67. 'ERC20Permit: invalid signature',
  68. );
  69. });
  70. it('rejects expired permit', async function () {
  71. const deadline = (await time.latest()) - time.duration.weeks(1);
  72. const data = buildData(this.chainId, this.token.address, deadline);
  73. const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data });
  74. const { v, r, s } = fromRpcSig(signature);
  75. await expectRevert(this.token.permit(owner, spender, value, deadline, v, r, s), 'ERC20Permit: expired deadline');
  76. });
  77. });
  78. });