diff --git a/snapshots/TheCompactTest.json b/snapshots/TheCompactTest.json index bfa3da6..34c1a34 100644 --- a/snapshots/TheCompactTest.json +++ b/snapshots/TheCompactTest.json @@ -1,12 +1,12 @@ { "basicTransfer": "57231", "basicWithdrawal": "60321", - "batchClaim": "112342", + "batchClaim": "112336", "batchClaimWithWitness": "112999", "batchTransfer": "82862", "batchWithdrawal": "101233", - "claim": "60835", - "claimAndWithdraw": "73718", + "claim": "57450", + "claimAndWithdraw": "73712", "claimWithWitness": "59909", "depositBatchSingleERC20": "67728", "depositERC20AndURI": "67021", @@ -14,19 +14,20 @@ "depositERC20ViaPermit2AndURI": "98015", "depositETHAndURI": "26694", "depositETHBasic": "28186", - "qualifiedBatchClaim": "113669", + "qualifiedBatchClaim": "113663", "qualifiedBatchClaimWithWitness": "113128", - "qualifiedClaim": "60733", + "qualifiedClaim": "60727", "qualifiedClaimWithWitness": "59312", - "qualifiedSplitBatchClaim": "141215", + "qualifiedSplitBatchClaim": "141209", "qualifiedSplitBatchClaimWithWitness": "141180", - "qualifiedSplitClaim": "86961", + "qualifiedSplitClaim": "86955", "qualifiedSplitClaimWithWitness": "87256", - "splitBatchClaim": "140724", + "register": "24890", + "splitBatchClaim": "140718", "splitBatchClaimWithWitness": "140655", "splitBatchTransfer": "113519", "splitBatchWithdrawal": "142771", - "splitClaim": "86887", + "splitClaim": "86881", "splitClaimWithWitness": "86379", "splitTransfer": "83187", "splitWithdrawal": "94053" diff --git a/src/TheCompact.sol b/src/TheCompact.sol index 59155e3..846ebfd 100644 --- a/src/TheCompact.sol +++ b/src/TheCompact.sol @@ -519,7 +519,7 @@ contract TheCompact is ITheCompact, ERC6909, Tstorish { _deposit(depositor, id, tokenBalance - initialBalance); } - _registeredClaimHashes[depositor][claimHash] = compactTypehash; + _register(depositor, claimHash, compactTypehash); _clearTstorish(_REENTRANCY_GUARD_SLOT); } @@ -672,7 +672,7 @@ contract TheCompact is ITheCompact, ERC6909, Tstorish { } } - _registeredClaimHashes[depositor][claimHash] = keccak256(bytes(compactTypestring)); + _register(depositor, claimHash], keccak256(bytes(compactTypestring)); _clearTstorish(_REENTRANCY_GUARD_SLOT); } @@ -1139,10 +1139,15 @@ contract TheCompact is ITheCompact, ERC6909, Tstorish { } function register(bytes32 claimHash, bytes32 typehash) external returns (bool) { - _registeredClaimHashes[msg.sender][claimHash] = typehash; + _register(msg.sender, claimHash, typehash); return true; } + function _register(address sponsor, bytes32 claimHash, bytes32 typehash) internal { + _registeredClaimHashes[sponsor][claimHash] = typehash; + emit CompactRegistered(sponsor, claimHash, typehash); + } + function register(bytes32[2][] calldata claimHashesAndTypehashes) external returns (bool) { return _registerFor(msg.sender, claimHashesAndTypehashes); } @@ -1152,7 +1157,7 @@ contract TheCompact is ITheCompact, ERC6909, Tstorish { uint256 totalClaimHashes = claimHashesAndTypehashes.length; for (uint256 i = 0; i < totalClaimHashes; ++i) { bytes32[2] calldata claimHashAndTypehash = claimHashesAndTypehashes[i]; - _registeredClaimHashes[sponsor][claimHashAndTypehash[0]] = claimHashAndTypehash[1]; + _register(sponsor, claimHashAndTypehash[0], claimHashAndTypehash[1]); } } @@ -1867,8 +1872,12 @@ contract TheCompact is ITheCompact, ERC6909, Tstorish { function _typehashes(uint256 i) internal pure returns (bytes32 typehash) { assembly ("memory-safe") { - let j := sub(i, 1) - typehash := add(mul(iszero(i), COMPACT_TYPEHASH), add(mul(iszero(j), BATCH_COMPACT_TYPEHASH), mul(iszero(iszero(j)), MULTICHAIN_COMPACT_TYPEHASH))) + let m := mload(0x40) + mstore(0, COMPACT_TYPEHASH) + mstore(0x20, BATCH_COMPACT_TYPEHASH) + mstore(0x40, MULTICHAIN_COMPACT_TYPEHASH) + typehash := mload(shl(5, i)) + mstore(0x40, m) } } diff --git a/src/interfaces/ITheCompact.sol b/src/interfaces/ITheCompact.sol index f7f99ed..e6f1954 100644 --- a/src/interfaces/ITheCompact.sol +++ b/src/interfaces/ITheCompact.sol @@ -83,6 +83,7 @@ interface ITheCompact { event ForcedWithdrawalEnabled(address indexed account, uint256 indexed id, uint256 withdrawableAt); event ForcedWithdrawalDisabled(address indexed account, uint256 indexed id); event AllocatorRegistered(uint96 allocatorId, address allocator); + event CompactRegistered(address indexed sponsor, bytes32 claimHash, bytes32 typehash); error InvalidToken(address token); error Expired(uint256 expiration); diff --git a/test/TheCompact.t.sol b/test/TheCompact.t.sol index 8dc9186..1d7852e 100644 --- a/test/TheCompact.t.sol +++ b/test/TheCompact.t.sol @@ -898,6 +898,51 @@ contract TheCompactTest is Test { assertEq(theCompact.balanceOf(claimant, id), amount); } + function test_registerAndClaim() public { + ResetPeriod resetPeriod = ResetPeriod.TenMinutes; + Scope scope = Scope.Multichain; + uint256 amount = 1e18; + uint256 nonce = 0; + uint256 expires = block.timestamp + 1000; + address claimant = 0x1111111111111111111111111111111111111111; + address arbiter = 0x2222222222222222222222222222222222222222; + + vm.prank(allocator); + theCompact.__registerAllocator(allocator, ""); + + vm.prank(swapper); + uint256 id = theCompact.deposit{ value: amount }(allocator, resetPeriod, scope, swapper); + assertEq(theCompact.balanceOf(swapper, id), amount); + + bytes32 typehash = keccak256("Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount)"); + + bytes32 claimHash = keccak256(abi.encode(typehash, arbiter, swapper, nonce, expires, id, amount)); + + bytes32 digest = keccak256(abi.encodePacked(bytes2(0x1901), theCompact.DOMAIN_SEPARATOR(), claimHash)); + + vm.prank(swapper); + (bool status) = theCompact.register(claimHash, typehash); + vm.snapshotGasLastCall("register"); + assert(status); + + bytes memory sponsorSignature = ""; + + (bytes32 r, bytes32 vs) = vm.signCompact(allocatorPrivateKey, digest); + bytes memory allocatorSignature = abi.encodePacked(r, vs); + + BasicClaim memory claim = BasicClaim(allocatorSignature, sponsorSignature, swapper, nonce, expires, id, amount, claimant, amount); + + vm.prank(arbiter); + (status) = theCompact.claim(claim); + vm.snapshotGasLastCall("claim"); + assert(status); + + assertEq(address(theCompact).balance, amount); + assertEq(claimant.balance, 0); + assertEq(theCompact.balanceOf(swapper, id), 0); + assertEq(theCompact.balanceOf(claimant, id), amount); + } + function test_claimAndWithdraw() public { ResetPeriod resetPeriod = ResetPeriod.TenMinutes; Scope scope = Scope.Multichain;