From 251653dbf3f6f9c743ce2699bb9c1295c83968c4 Mon Sep 17 00:00:00 2001 From: vimageDE Date: Thu, 30 Jan 2025 10:41:19 +0100 Subject: [PATCH] verify allocator signature in transfer --- src/core/TheCompactCore.sol | 8 ++++---- src/core/lib/Deposit.sol | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/core/TheCompactCore.sol b/src/core/TheCompactCore.sol index f0465f8..df4d01a 100644 --- a/src/core/TheCompactCore.sol +++ b/src/core/TheCompactCore.sol @@ -122,7 +122,7 @@ contract TheCompactCore is ERC6909, Deposit { function allocatedTransfer(ITheCompactCore.Transfer calldata transfer_, bytes calldata allocatorSignature) external returns (bool) { address allocator = _checkNonce(transfer_.recipients[0].id, transfer_.nonce); - uint256 length = _ensureBatchAttested(msg.sender, transfer_, allocatorSignature); + uint256 length = _ensureBatchAttested(msg.sender, msg.sender, transfer_, allocatorSignature); // The allocator has successfully attested to the withdrawal. If the nonce is not 0, it must be consumed if(transfer_.nonce != 0) { // If the nonce is 0, it must be an on chain allocator that does not require a nonce to attest. @@ -137,7 +137,7 @@ contract TheCompactCore is ERC6909, Deposit { function allocatedTransferFrom(ITheCompactCore.DelegatedTransfer calldata delegatedTransfer, bytes calldata allocatorSignature) external returns (bool) { address allocator = _checkNonce(delegatedTransfer.transfer.recipients[0].id, delegatedTransfer.transfer.nonce); - uint256 length = _ensureBatchAttested(msg.sender, delegatedTransfer.transfer, allocatorSignature); + uint256 length = _ensureBatchAttested(msg.sender, delegatedTransfer.from, delegatedTransfer.transfer, allocatorSignature); // The allocator has successfully attested to the withdrawal. If the nonce is not 0, it must be consumed if(delegatedTransfer.transfer.nonce != 0) { // If the nonce is 0, it must be an on chain allocator that does not require a nonce to attest. @@ -152,7 +152,7 @@ contract TheCompactCore is ERC6909, Deposit { function withdrawal(ITheCompactCore.Transfer calldata withdrawal_, bytes calldata allocatorSignature) external returns (bool) { address allocator = _checkNonce(withdrawal_.recipients[0].id, withdrawal_.nonce); - uint256 length = _ensureBatchAttested(msg.sender, withdrawal_, allocatorSignature); + uint256 length = _ensureBatchAttested(msg.sender, msg.sender, withdrawal_, allocatorSignature); // The allocator has successfully attested to the withdrawal. If the nonce is not 0, it must be consumed if(withdrawal_.nonce != 0) { // If the nonce is 0, it must be an on chain allocator that does not require a nonce to attest. @@ -168,7 +168,7 @@ contract TheCompactCore is ERC6909, Deposit { function withdrawalFrom(ITheCompactCore.DelegatedTransfer calldata delegatedWithdrawal, bytes calldata allocatorSignature) external returns (bool) { address allocator = _checkNonce(delegatedWithdrawal.transfer.recipients[0].id, delegatedWithdrawal.transfer.nonce); - uint256 length = _ensureBatchAttested(msg.sender, delegatedWithdrawal.transfer, allocatorSignature); + uint256 length = _ensureBatchAttested(msg.sender, delegatedWithdrawal.from, delegatedWithdrawal.transfer, allocatorSignature); // The allocator has successfully attested to the withdrawal. If the nonce is not 0, it must be consumed if(delegatedWithdrawal.transfer.nonce != 0) { // If the nonce is 0, it must be an on chain allocator that does not require a nonce to attest. diff --git a/src/core/lib/Deposit.sol b/src/core/lib/Deposit.sol index 4ab4f2b..48bfcd3 100644 --- a/src/core/lib/Deposit.sol +++ b/src/core/lib/Deposit.sol @@ -29,6 +29,9 @@ contract Deposit { // keccak256("Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,Allocation[] inputs)EnhancedCompact(Compact compact,uint256 chainId)MultiChainCompact(EnhancedCompact[])") bytes32 private constant _MULTICHAIN_COMPACT_TYPEHASH = 0x4527c6867b5e06d14c0c8048cabe293f468c8ce4d78c2cdbaf193934751a96f0; + // keccak256("Transfer(address from,address[] to,uint256[] id,uint256[] amount,uint256 nonce,uint256 expires)") + bytes32 private constant _TRANSFER_TYPEHASH = 0x6d44c9455c8398fa551f6b1e552d67be7e70cf294bbb32590a4baf18180519e6; + // keccak256("Permit(address owner,address spender,uint256 id,uint256 value,uint256 nonce,uint256 deadline)") bytes32 private constant _PERMIT_TYPEHASH = 0x41b82e2b5a0c36576b0cbe551120f192388f4a0e73168b730f27a8a467e1f79f; @@ -282,6 +285,7 @@ contract Deposit { return _permit2Nonces[owner]; } + // @dev Since there is no nonce, a single attestation can only be verified by an on chain allocator. function _ensureAttested(address from, address to, uint256 id, uint256 amount) internal { // Derive the allocator address from the supplied id. address allocator = IdLib.toAllocator(id); @@ -291,7 +295,7 @@ contract Deposit { } } - function _ensureBatchAttested(address caller, ITheCompactCore.Transfer calldata transfer, bytes calldata allocatorSignature) internal returns (uint256 length) { + function _ensureBatchAttested(address caller, address from, ITheCompactCore.Transfer calldata transfer, bytes calldata allocatorSignature) internal returns (uint256 length) { address expectedAllocator = IdLib.toAllocator(transfer.recipients[0].id); // Ensure the allocator attests the transfers. length = transfer.recipients.length; @@ -308,8 +312,11 @@ contract Deposit { amount[i] = transfer.recipients[i].amount; } - if( IAllocator(expectedAllocator).attest(caller, caller, to, id, amount, transfer.nonce, transfer.expires, allocatorSignature) != _ATTEST_BATCH_SELECTOR) { - revert Errors.AllocatorDenied(expectedAllocator); + bytes32 digest = _transferDigest(from, to, id, amount, transfer.nonce, transfer.expires); + if(!SignatureCheckerLib.isValidSignatureNowCalldata(expectedAllocator, digest, allocatorSignature)) { + if( IAllocator(expectedAllocator).attest(caller, from, to, id, amount, transfer.nonce, transfer.expires, allocatorSignature) != _ATTEST_BATCH_SELECTOR) { + revert Errors.AllocatorDenied(expectedAllocator); + } } } @@ -432,6 +439,26 @@ contract Deposit { ); } + function _transferDigest(address from, address[] memory to, uint256[] memory id, uint256[] memory amount, uint256 nonce, uint256 expires) internal view returns (bytes32) { + return keccak256( + abi.encodePacked( + bytes2(0x1901), + _DOMAIN_SEPARATOR, + keccak256( + abi.encode( + _TRANSFER_TYPEHASH, + from, + to, + id, + amount, + nonce, + expires + ) + ) + ) + ); + } + function _computePermitHash(address owner, address spender, uint256 id, uint256 value, uint256 nonce, uint256 deadline) internal view returns (bytes32) { return keccak256( abi.encodePacked(