Skip to content

Commit b8a2db8

Browse files
committed
Add implicit failing test
1 parent 4e8f640 commit b8a2db8

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-0
lines changed

test/ERC7739.t.sol

+35
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,39 @@ contract ERC7739Test is DelegationHandler, TokenHandler, ERC1271Handler, FFISign
7272
// Assert that the signature is valid when compared against the ffi generated signature
7373
assertEq(signature, key.sign(typedDataSignDigest));
7474
}
75+
76+
function test_signTypedSignData_usingImplicitType_wrongSignature_ffi() public {
77+
TestKey memory key = TestKeyManager.withSeed(KeyType.Secp256k1, signerPrivateKey);
78+
79+
PermitSingle memory permitSingle = PermitSingle({
80+
details: PermitDetails({token: address(0), amount: 0, expiration: 0, nonce: 0}),
81+
spender: address(0),
82+
sigDeadline: 0
83+
});
84+
// Locally generate the full TypedSignData hash
85+
bytes32 contentsHash = mockERC1271VerifyingContract.hash(permitSingle);
86+
bytes32 appSeparator = mockERC1271VerifyingContract.domainSeparator();
87+
88+
// Incorrectly use the implicit type descriptor string, causing the top level type to be
89+
// TypeDataSign(...)PermitSingle(...)PermitDetails(...) which does not follow EIP-712 ordering
90+
string memory contentsDescrImplicit = mockERC1271VerifyingContract.contentsDescrImplicit();
91+
92+
bytes memory signerAccountDomainBytes = IERC5267(address(signerAccount)).toDomainBytes();
93+
bytes32 typedDataSignDigest =
94+
contentsHash.hashTypedDataSign(signerAccountDomainBytes, appSeparator, contentsDescrImplicit);
95+
96+
// Make it clear that the verifying contract is set properly.
97+
address verifyingContract = address(signerAccount);
98+
99+
(bytes memory signature) = ffi_signWrappedTypedData(
100+
signerPrivateKey,
101+
verifyingContract,
102+
DOMAIN_NAME,
103+
DOMAIN_VERSION,
104+
address(mockERC1271VerifyingContract),
105+
permitSingle
106+
);
107+
// Assert that the ffi generated signature is NOT the same as the locally generated signature
108+
assertNotEq(signature, key.sign(typedDataSignDigest));
109+
}
75110
}

test/utils/MockERC1271VerifyingContract.sol

+6
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ contract MockERC1271VerifyingContract is EIP712 {
4646
return _domainSeparatorV4();
4747
}
4848

49+
/// @notice The EIP-712 typestring with PermitSingle as the top level type
50+
/// @dev This should NOT be used for correct ERC-7739 nested TypedDataSign signatures
51+
function contentsDescrImplicit() external pure returns (string memory) {
52+
return "PermitSingle(PermitDetails details,address spender,uint256 sigDeadline)PermitDetails(address token,uint160 amount,uint48 expiration,uint48 nonce)";
53+
}
54+
4955
/// @notice Per ERC-7739, use the explicit mode for content descriptor strings since the top level type
5056
/// PermitSingle is alphabetically ordered after PermitDetails
5157
/// @dev return the full contents descriptor string in explicit mode

0 commit comments

Comments
 (0)