diff --git a/contracts/utils/cryptography/EIP712.sol b/contracts/utils/cryptography/EIP712.sol index e06d0066b..56dfceaf4 100644 --- a/contracts/utils/cryptography/EIP712.sol +++ b/contracts/utils/cryptography/EIP712.sol @@ -111,6 +111,8 @@ abstract contract EIP712 is IERC5267 { /** * @dev See {EIP-5267}. + * + * _Available since v4.9._ */ function eip712Domain() public diff --git a/test/utils/cryptography/EIP712.test.js b/test/utils/cryptography/EIP712.test.js index f12f22673..52e322d3d 100644 --- a/test/utils/cryptography/EIP712.test.js +++ b/test/utils/cryptography/EIP712.test.js @@ -6,65 +6,98 @@ const { getChainId } = require('../../helpers/chainid'); const { mapValues } = require('../../helpers/map-values'); const EIP712Verifier = artifacts.require('$EIP712Verifier'); +const Clones = artifacts.require('$Clones'); contract('EIP712', function (accounts) { const [mailTo] = accounts; - const name = 'A Name'; - const version = '1'; + const shortName = 'A Name'; + const shortVersion = '1'; - beforeEach('deploying', async function () { - this.eip712 = await EIP712Verifier.new(name, version); + const longName = 'A'.repeat(40); + const longVersion = 'B'.repeat(40); - this.domain = { - name, - version, - chainId: await getChainId(), - verifyingContract: this.eip712.address, - }; - this.domainType = domainType(this.domain); - }); + const cases = [ + ['short', shortName, shortVersion], + ['long', longName, longVersion], + ]; - describe('domain separator', function () { - it('is internally available', async function () { - const expected = await domainSeparator(this.domain); + for (const [shortOrLong, name, version] of cases) { + describe(`with ${shortOrLong} name and version`, function () { + beforeEach('deploying', async function () { + this.eip712 = await EIP712Verifier.new(name, version); - expect(await this.eip712.$_domainSeparatorV4()).to.equal(expected); + this.domain = { + name, + version, + chainId: await getChainId(), + verifyingContract: this.eip712.address, + }; + this.domainType = domainType(this.domain); + }); + + describe('domain separator', function () { + it('is internally available', async function () { + const expected = await domainSeparator(this.domain); + + expect(await this.eip712.$_domainSeparatorV4()).to.equal(expected); + }); + + it("can be rebuilt using EIP-5267's eip712Domain", async function () { + const rebuildDomain = await getDomain(this.eip712); + expect(mapValues(rebuildDomain, String)).to.be.deep.equal(mapValues(this.domain, String)); + }); + + if (shortOrLong === 'short') { + // Long strings are in storage, and the proxy will not be properly initialized unless + // the upgradeable contract variant is used and the initializer is invoked. + + it('adjusts when behind proxy', async function () { + const factory = await Clones.new(); + const cloneReceipt = await factory.$clone(this.eip712.address); + const cloneAddress = cloneReceipt.logs.find(({ event }) => event === 'return$clone').args.instance; + const clone = new EIP712Verifier(cloneAddress); + + const cloneDomain = { ...this.domain, verifyingContract: clone.address }; + + const expectedSeparator = await domainSeparator(cloneDomain); + expect(await clone.$_domainSeparatorV4()).to.equal(expectedSeparator); + + const reportedDomain = await getDomain(clone); + expect(mapValues(reportedDomain, String)).to.be.deep.equal(mapValues(cloneDomain, String)); + }); + } + }); + + it('hash digest', async function () { + const structhash = web3.utils.randomHex(32); + expect(await this.eip712.$_hashTypedDataV4(structhash)).to.be.equal(hashTypedData(this.domain, structhash)); + }); + + it('digest', async function () { + const message = { + to: mailTo, + contents: 'very interesting', + }; + + const data = { + types: { + EIP712Domain: this.domainType, + Mail: [ + { name: 'to', type: 'address' }, + { name: 'contents', type: 'string' }, + ], + }, + domain: this.domain, + primaryType: 'Mail', + message, + }; + + const wallet = Wallet.generate(); + const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }); + + await this.eip712.verify(signature, wallet.getAddressString(), message.to, message.contents); + }); }); - - it("can be rebuilt using EIP-5267's eip712Domain", async function () { - const rebuildDomain = await getDomain(this.eip712); - expect(mapValues(rebuildDomain, String)).to.be.deep.equal(mapValues(this.domain, String)); - }); - }); - - it('hash digest', async function () { - const structhash = web3.utils.randomHex(32); - expect(await this.eip712.$_hashTypedDataV4(structhash)).to.be.equal(hashTypedData(this.domain, structhash)); - }); - - it('digest', async function () { - const message = { - to: mailTo, - contents: 'very interesting', - }; - - const data = { - types: { - EIP712Domain: this.domainType, - Mail: [ - { name: 'to', type: 'address' }, - { name: 'contents', type: 'string' }, - ], - }, - domain: this.domain, - primaryType: 'Mail', - message, - }; - - const wallet = Wallet.generate(); - const signature = ethSigUtil.signTypedMessage(wallet.getPrivateKey(), { data }); - - await this.eip712.verify(signature, wallet.getAddressString(), message.to, message.contents); - }); + } });