ERC1271.behavior.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { Permit, formatType, getDomain } = require('../../helpers/eip712');
  4. const { ERC7739Signer } = require('../../helpers/erc7739');
  5. function shouldBehaveLikeERC1271({ erc7739 = false } = {}) {
  6. const MAGIC_VALUE = '0x1626ba7e';
  7. describe(`supports ERC-${erc7739 ? 7739 : 1271}`, function () {
  8. beforeEach(async function () {
  9. // if deploy function is present, check that code is already in place
  10. if (this.mock.deploy) {
  11. await ethers.provider.getCode(this.mock.address).then(code => code != '0x' || this.mock.deploy());
  12. }
  13. this._signer = erc7739
  14. ? new ERC7739Signer(this.signer, this.domain ?? (await getDomain(this.mock)))
  15. : this.signer;
  16. });
  17. describe('PersonalSign', function () {
  18. it('returns true for a valid personal signature', async function () {
  19. const text = 'Hello, world!';
  20. const hash = ethers.hashMessage(text);
  21. const signature = await this._signer.signMessage(text);
  22. await expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE);
  23. });
  24. it('returns false for an invalid personal signature', async function () {
  25. const message = 'Message the app expects';
  26. const otherMessage = 'Message signed is different';
  27. const hash = ethers.hashMessage(message);
  28. const signature = await this._signer.signMessage(otherMessage);
  29. await expect(this.mock.isValidSignature(hash, signature)).to.eventually.not.equal(MAGIC_VALUE);
  30. });
  31. });
  32. describe('TypedDataSign', function () {
  33. beforeEach(async function () {
  34. // Dummy app domain, different from the ERC7739's domain
  35. // Note the difference of format (signer domain doesn't include a salt, but app domain does)
  36. this.appDomain = {
  37. name: 'SomeApp',
  38. version: '1',
  39. chainId: await ethers.provider.getNetwork().then(({ chainId }) => chainId),
  40. verifyingContract: '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512',
  41. salt: '0x02cb3d8cb5e8928c9c6de41e935e16a4e28b2d54e7e7ba47e99f16071efab785',
  42. };
  43. });
  44. it('returns true for a valid typed data signature', async function () {
  45. const contents = {
  46. owner: '0x1ab5E417d9AF00f1ca9d159007e12c401337a4bb',
  47. spender: '0xD68E96620804446c4B1faB3103A08C98d4A8F55f',
  48. value: 1_000_000n,
  49. nonce: 0n,
  50. deadline: ethers.MaxUint256,
  51. };
  52. const hash = ethers.TypedDataEncoder.hash(this.appDomain, { Permit }, contents);
  53. const signature = await this._signer.signTypedData(this.appDomain, { Permit }, contents);
  54. await expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE);
  55. });
  56. it('returns true for valid typed data signature (nested types)', async function () {
  57. const contentsTypes = {
  58. B: formatType({ z: 'Z' }),
  59. Z: formatType({ a: 'A' }),
  60. A: formatType({ v: 'uint256' }),
  61. };
  62. const contents = { z: { a: { v: 1n } } };
  63. const hash = ethers.TypedDataEncoder.hash(this.appDomain, contentsTypes, contents);
  64. const signature = await this._signer.signTypedData(this.appDomain, contentsTypes, contents);
  65. await expect(this.mock.isValidSignature(hash, signature)).to.eventually.equal(MAGIC_VALUE);
  66. });
  67. it('returns false for an invalid typed data signature', async function () {
  68. const contents = {
  69. owner: '0x1ab5E417d9AF00f1ca9d159007e12c401337a4bb',
  70. spender: '0xD68E96620804446c4B1faB3103A08C98d4A8F55f',
  71. value: 1_000_000n,
  72. nonce: 0n,
  73. deadline: ethers.MaxUint256,
  74. };
  75. const hash = ethers.TypedDataEncoder.hash(this.appDomain, { Permit }, contents);
  76. // message signed by the user is for a lower amount.
  77. const signature = await this._signer.signTypedData(this.appDomain, { Permit }, { ...contents, value: 1_000n });
  78. await expect(this.mock.isValidSignature(hash, signature)).to.eventually.not.equal(MAGIC_VALUE);
  79. });
  80. });
  81. erc7739 &&
  82. it('support ERC-7739 detection', async function () {
  83. const hash = '0x7739773977397739773977397739773977397739773977397739773977397739';
  84. await expect(this.mock.isValidSignature(hash, '0x')).to.eventually.equal('0x77390001');
  85. });
  86. });
  87. }
  88. module.exports = {
  89. shouldBehaveLikeERC1271,
  90. };