Skip to content

Commit 7947cec

Browse files
authored
Refactor and fix async operations in ERC2771Context.test.js (#5532)
1 parent efd1878 commit 7947cec

File tree

1 file changed

+35
-59
lines changed

1 file changed

+35
-59
lines changed

test/metatx/ERC2771Context.test.js

Lines changed: 35 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,20 @@ async function fixture() {
1515
const forwarderAsSigner = await impersonate(forwarder.target);
1616
const context = await ethers.deployContract('ERC2771ContextMock', [forwarder]);
1717
const domain = await getDomain(forwarder);
18-
const types = { ForwardRequest };
1918

20-
return { sender, other, forwarder, forwarderAsSigner, context, domain, types };
19+
const prepareAndSignRequest = async (signer, request) => {
20+
// request.to is mandatory
21+
request.from ??= signer.address;
22+
request.value ??= 0n;
23+
request.data ??= '0x';
24+
request.gas ??= 100000n;
25+
request.nonce ??= await forwarder.nonces(signer);
26+
request.deadline ??= MAX_UINT48;
27+
request.signature = await signer.signTypedData(domain, { ForwardRequest }, request);
28+
return request;
29+
};
30+
31+
return { sender, other, forwarder, forwarderAsSigner, context, prepareAndSignRequest };
2132
}
2233

2334
describe('ERC2771Context', function () {
@@ -26,11 +37,11 @@ describe('ERC2771Context', function () {
2637
});
2738

2839
it('recognize trusted forwarder', async function () {
29-
expect(await this.context.isTrustedForwarder(this.forwarder)).to.be.true;
40+
await expect(this.context.isTrustedForwarder(this.forwarder)).to.eventually.be.true;
3041
});
3142

3243
it('returns the trusted forwarder', async function () {
33-
expect(await this.context.trustedForwarder()).to.equal(this.forwarder);
44+
await expect(this.context.trustedForwarder()).to.eventually.equal(this.forwarder);
3445
});
3546

3647
describe('when called directly', function () {
@@ -40,23 +51,12 @@ describe('ERC2771Context', function () {
4051
describe('when receiving a relayed call', function () {
4152
describe('msgSender', function () {
4253
it('returns the relayed transaction original sender', async function () {
43-
const nonce = await this.forwarder.nonces(this.sender);
44-
const data = this.context.interface.encodeFunctionData('msgSender');
45-
46-
const req = {
47-
from: await this.sender.getAddress(),
48-
to: await this.context.getAddress(),
49-
value: 0n,
50-
data,
51-
gas: 100000n,
52-
nonce,
53-
deadline: MAX_UINT48,
54-
};
55-
56-
req.signature = await this.sender.signTypedData(this.domain, this.types, req);
57-
58-
expect(await this.forwarder.verify(req)).to.be.true;
54+
const req = await this.prepareAndSignRequest(this.sender, {
55+
to: this.context.target,
56+
data: this.context.interface.encodeFunctionData('msgSender'),
57+
});
5958

59+
await expect(this.forwarder.verify(req)).to.eventually.be.true;
6060
await expect(this.forwarder.execute(req)).to.emit(this.context, 'Sender').withArgs(this.sender);
6161
});
6262

@@ -72,62 +72,38 @@ describe('ERC2771Context', function () {
7272
it('returns the relayed transaction original data', async function () {
7373
const args = [42n, 'OpenZeppelin'];
7474

75-
const nonce = await this.forwarder.nonces(this.sender);
76-
const data = this.context.interface.encodeFunctionData('msgData', args);
77-
78-
const req = {
79-
from: await this.sender.getAddress(),
80-
to: await this.context.getAddress(),
81-
value: 0n,
82-
data,
83-
gas: 100000n,
84-
nonce,
85-
deadline: MAX_UINT48,
86-
};
87-
88-
req.signature = this.sender.signTypedData(this.domain, this.types, req);
89-
90-
expect(await this.forwarder.verify(req)).to.be.true;
75+
const req = await this.prepareAndSignRequest(this.sender, {
76+
to: this.context.target,
77+
data: this.context.interface.encodeFunctionData('msgData', args),
78+
});
9179

80+
await expect(this.forwarder.verify(req)).to.eventually.be.true;
9281
await expect(this.forwarder.execute(req))
9382
.to.emit(this.context, 'Data')
94-
.withArgs(data, ...args);
83+
.withArgs(req.data, ...args);
9584
});
9685
});
9786

9887
it('returns the full original data when calldata length is less than 20 bytes (address length)', async function () {
9988
const data = this.context.interface.encodeFunctionData('msgDataShort');
10089

10190
// The forwarder doesn't produce calls with calldata length less than 20 bytes so `this.forwarderAsSigner` is used instead.
102-
await expect(await this.context.connect(this.forwarderAsSigner).msgDataShort())
91+
await expect(this.context.connect(this.forwarderAsSigner).msgDataShort())
10392
.to.emit(this.context, 'DataShort')
10493
.withArgs(data);
10594
});
10695
});
10796

10897
it('multicall poison attack', async function () {
109-
const nonce = await this.forwarder.nonces(this.sender);
110-
const data = this.context.interface.encodeFunctionData('multicall', [
111-
[
112-
// poisoned call to 'msgSender()'
113-
ethers.concat([this.context.interface.encodeFunctionData('msgSender'), this.other.address]),
114-
],
115-
]);
116-
117-
const req = {
118-
from: await this.sender.getAddress(),
119-
to: await this.context.getAddress(),
120-
value: 0n,
121-
data,
122-
gas: 100000n,
123-
nonce,
124-
deadline: MAX_UINT48,
125-
};
126-
127-
req.signature = await this.sender.signTypedData(this.domain, this.types, req);
128-
129-
expect(await this.forwarder.verify(req)).to.be.true;
98+
const req = await this.prepareAndSignRequest(this.sender, {
99+
to: this.context.target,
100+
data: this.context.interface.encodeFunctionData('multicall', [
101+
// poisonned call to 'msgSender()'
102+
[ethers.concat([this.context.interface.encodeFunctionData('msgSender'), this.other.address])],
103+
]),
104+
});
130105

106+
await expect(this.forwarder.verify(req)).to.eventually.be.true;
131107
await expect(this.forwarder.execute(req)).to.emit(this.context, 'Sender').withArgs(this.sender);
132108
});
133109
});

0 commit comments

Comments
 (0)