diff --git a/script/BatchCallAndSponsor.s.sol b/script/BatchCallAndSponsor.s.sol index ca983fe..a21492c 100644 --- a/script/BatchCallAndSponsor.s.sol +++ b/script/BatchCallAndSponsor.s.sol @@ -97,7 +97,8 @@ contract BatchCallAndSponsorScript is Script { for (uint256 i = 0; i < calls.length; i++) { encodedCalls = abi.encodePacked(encodedCalls, calls[i].to, calls[i].value, calls[i].data); } - bytes32 digest = keccak256(abi.encodePacked(BatchCallAndSponsor(ALICE_ADDRESS).nonce(), encodedCalls)); + bytes32 digest = + keccak256(abi.encodePacked(block.chainid, BatchCallAndSponsor(ALICE_ADDRESS).nonce(), encodedCalls)); (uint8 v, bytes32 r, bytes32 s) = vm.sign(ALICE_PK, MessageHashUtils.toEthSignedMessageHash(digest)); bytes memory signature = abi.encodePacked(r, s, v); diff --git a/src/BatchCallAndSponsor.sol b/src/BatchCallAndSponsor.sol index b4196a4..b3206b2 100644 --- a/src/BatchCallAndSponsor.sol +++ b/src/BatchCallAndSponsor.sol @@ -18,6 +18,7 @@ import "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; * 2. Directly by the smart account: When the account itself (i.e. address(this)) calls the function, no signature is required. * * Replay protection is achieved by using a nonce that is included in the signed message. + * Cross chain replay protection is achieved by using the chainid in the signed message. */ contract BatchCallAndSponsor { using ECDSA for bytes32; @@ -51,7 +52,7 @@ contract BatchCallAndSponsor { for (uint256 i = 0; i < calls.length; i++) { encodedCalls = abi.encodePacked(encodedCalls, calls[i].to, calls[i].value, calls[i].data); } - bytes32 digest = keccak256(abi.encodePacked(nonce, encodedCalls)); + bytes32 digest = keccak256(abi.encodePacked(block.chainid, nonce, encodedCalls)); bytes32 ethSignedMessageHash = MessageHashUtils.toEthSignedMessageHash(digest); diff --git a/test/BatchCallAndSponsor.t.sol b/test/BatchCallAndSponsor.t.sol index cc07cc2..2ca66d9 100644 --- a/test/BatchCallAndSponsor.t.sol +++ b/test/BatchCallAndSponsor.t.sol @@ -97,7 +97,8 @@ contract BatchCallAndSponsorTest is Test { encodedCalls = abi.encodePacked(encodedCalls, calls[i].to, calls[i].value, calls[i].data); } - bytes32 digest = keccak256(abi.encodePacked(BatchCallAndSponsor(ALICE_ADDRESS).nonce(), encodedCalls)); + bytes32 digest = + keccak256(abi.encodePacked(block.chainid, BatchCallAndSponsor(ALICE_ADDRESS).nonce(), encodedCalls)); (uint8 v, bytes32 r, bytes32 s) = vm.sign(ALICE_PK, MessageHashUtils.toEthSignedMessageHash(digest)); bytes memory signature = abi.encodePacked(r, s, v); @@ -171,7 +172,7 @@ contract BatchCallAndSponsorTest is Test { vm.attachDelegation(signedDelegation); uint256 nonceBefore = BatchCallAndSponsor(ALICE_ADDRESS).nonce(); - bytes32 digest = keccak256(abi.encodePacked(nonceBefore, encodedCalls)); + bytes32 digest = keccak256(abi.encodePacked(block.chainid, nonceBefore, encodedCalls)); (uint8 v, bytes32 r, bytes32 s) = vm.sign(ALICE_PK, MessageHashUtils.toEthSignedMessageHash(digest)); bytes memory signature = abi.encodePacked(r, s, v);