Skip to content

Commit 1c13b81

Browse files
Transpile e4435eed
1 parent 231affb commit 1c13b81

File tree

4 files changed

+26
-5
lines changed

4 files changed

+26
-5
lines changed

.changeset/warm-guests-rule.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'openzeppelin-solidity': patch
3+
---
4+
5+
`ERC2771Context`: Prevent revert in `_msgData()` when a call originating from a trusted forwarder is not long enough to contain the request signer address (i.e. `msg.data.length` is less than 20 bytes). Return the full calldata in that case.

contracts/metatx/ERC2771ContextUpgradeable.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable
3535
}
3636

3737
function _msgData() internal view virtual override returns (bytes calldata) {
38-
if (isTrustedForwarder(msg.sender)) {
38+
if (isTrustedForwarder(msg.sender) && msg.data.length >= 20) {
3939
return msg.data[:msg.data.length - 20];
4040
} else {
4141
return super._msgData();

contracts/mocks/ContextMockUpgradeable.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ contract ContextMockUpgradeable is Initializable, ContextUpgradeable {
2323
emit Data(_msgData(), integerValue, stringValue);
2424
}
2525

26+
event DataShort(bytes data);
27+
28+
function msgDataShort() public {
29+
emit DataShort(_msgData());
30+
}
31+
2632
/**
2733
* @dev This empty reserved space is put in place to allow future versions to add new
2834
* variables without shifting down storage in the inheritance chain.

test/metatx/ERC2771Context.test.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const ContextMockCaller = artifacts.require('ContextMockCaller');
1212
const { shouldBehaveLikeRegularContext } = require('../utils/Context.behavior');
1313

1414
contract('ERC2771Context', function (accounts) {
15-
const [, anotherAccount] = accounts;
15+
const [, trustedForwarder] = accounts;
1616

1717
beforeEach(async function () {
1818
this.forwarder = await MinimalForwarder.new();
@@ -78,11 +78,11 @@ contract('ERC2771Context', function (accounts) {
7878

7979
it('returns the original sender when calldata length is less than 20 bytes (address length)', async function () {
8080
// The forwarder doesn't produce calls with calldata length less than 20 bytes
81-
const recipient = await ERC2771ContextMock.new(anotherAccount);
81+
const recipient = await ERC2771ContextMock.new(trustedForwarder);
8282

83-
const { receipt } = await recipient.msgSender({ from: anotherAccount });
83+
const { receipt } = await recipient.msgSender({ from: trustedForwarder });
8484

85-
await expectEvent(receipt, 'Sender', { sender: anotherAccount });
85+
await expectEvent(receipt, 'Sender', { sender: trustedForwarder });
8686
});
8787
});
8888

@@ -108,5 +108,15 @@ contract('ERC2771Context', function (accounts) {
108108
await expectEvent.inTransaction(tx, ERC2771ContextMock, 'Data', { data, integerValue, stringValue });
109109
});
110110
});
111+
112+
it('returns the full original data when calldata length is less than 20 bytes (address length)', async function () {
113+
// The forwarder doesn't produce calls with calldata length less than 20 bytes
114+
const recipient = await ERC2771ContextMock.new(trustedForwarder);
115+
116+
const { receipt } = await recipient.msgDataShort({ from: trustedForwarder });
117+
118+
const data = recipient.contract.methods.msgDataShort().encodeABI();
119+
await expectEvent(receipt, 'DataShort', { data });
120+
});
111121
});
112122
});

0 commit comments

Comments
 (0)