diff --git a/src/TheCompact.sol b/src/TheCompact.sol index 44edaaa..ef86ab9 100644 --- a/src/TheCompact.sol +++ b/src/TheCompact.sol @@ -451,7 +451,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(BatchClaim calldata claimPayload) external returns (bool) { - return _processBatchClaim(claimPayload, _release); + return _processBatchClaim(claimPayload, _withdraw); } function claim(QualifiedBatchClaim calldata claimPayload) external returns (bool) { @@ -459,7 +459,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(QualifiedBatchClaim calldata claimPayload) external returns (bool) { - return _processQualifiedBatchClaim(claimPayload, _release); + return _processQualifiedBatchClaim(claimPayload, _withdraw); } function claim(BatchClaimWithWitness calldata claimPayload) external returns (bool) { @@ -467,7 +467,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(BatchClaimWithWitness calldata claimPayload) external returns (bool) { - return _processBatchClaimWithWitness(claimPayload, _release); + return _processBatchClaimWithWitness(claimPayload, _withdraw); } function claim(QualifiedBatchClaimWithWitness calldata claimPayload) external returns (bool) { @@ -475,7 +475,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(QualifiedBatchClaimWithWitness calldata claimPayload) external returns (bool) { - return _processQualifiedBatchClaimWithWitness(claimPayload, _release); + return _processQualifiedBatchClaimWithWitness(claimPayload, _withdraw); } function claim(SplitBatchClaim calldata claimPayload) external returns (bool) { @@ -483,7 +483,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(SplitBatchClaim calldata claimPayload) external returns (bool) { - return _processSplitBatchClaim(claimPayload, _release); + return _processSplitBatchClaim(claimPayload, _withdraw); } function claim(QualifiedSplitBatchClaim calldata claimPayload) external returns (bool) { @@ -491,7 +491,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(QualifiedSplitBatchClaim calldata claimPayload) external returns (bool) { - return _processQualifiedSplitBatchClaim(claimPayload, _release); + return _processQualifiedSplitBatchClaim(claimPayload, _withdraw); } function claim(SplitBatchClaimWithWitness calldata claimPayload) external returns (bool) { @@ -499,7 +499,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(SplitBatchClaimWithWitness calldata claimPayload) external returns (bool) { - return _processSplitBatchClaimWithWitness(claimPayload, _release); + return _processSplitBatchClaimWithWitness(claimPayload, _withdraw); } function claim(QualifiedSplitBatchClaimWithWitness calldata claimPayload) external returns (bool) { @@ -507,7 +507,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(QualifiedSplitBatchClaimWithWitness calldata claimPayload) external returns (bool) { - return _processQualifiedSplitBatchClaimWithWitness(claimPayload, _release); + return _processQualifiedSplitBatchClaimWithWitness(claimPayload, _withdraw); } function claim(MultichainClaim calldata claimPayload) external returns (bool) { @@ -515,7 +515,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(MultichainClaim calldata claimPayload) external returns (bool) { - return _processMultichainClaim(claimPayload, _release); + return _processMultichainClaim(claimPayload, _withdraw); } function claim(ExogenousMultichainClaim calldata claimPayload) external returns (bool) { @@ -523,7 +523,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(ExogenousMultichainClaim calldata claimPayload) external returns (bool) { - return _processExogenousMultichainClaim(claimPayload, _release); + return _processExogenousMultichainClaim(claimPayload, _withdraw); } function claim(QualifiedMultichainClaim calldata claimPayload) external returns (bool) { @@ -531,7 +531,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(QualifiedMultichainClaim calldata claimPayload) external returns (bool) { - return _processQualifiedMultichainClaim(claimPayload, _release); + return _processQualifiedMultichainClaim(claimPayload, _withdraw); } function claim(ExogenousQualifiedMultichainClaim calldata claimPayload) external returns (bool) { @@ -539,7 +539,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(ExogenousQualifiedMultichainClaim calldata claimPayload) external returns (bool) { - return _processExogenousQualifiedMultichainClaim(claimPayload, _release); + return _processExogenousQualifiedMultichainClaim(claimPayload, _withdraw); } function claim(MultichainClaimWithWitness calldata claimPayload) external returns (bool) { @@ -547,7 +547,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(MultichainClaimWithWitness calldata claimPayload) external returns (bool) { - return _processMultichainClaimWithWitness(claimPayload, _release); + return _processMultichainClaimWithWitness(claimPayload, _withdraw); } function claim(ExogenousMultichainClaimWithWitness calldata claimPayload) external returns (bool) { @@ -555,7 +555,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(ExogenousMultichainClaimWithWitness calldata claimPayload) external returns (bool) { - return _processExogenousMultichainClaimWithWitness(claimPayload, _release); + return _processExogenousMultichainClaimWithWitness(claimPayload, _withdraw); } function claim(QualifiedMultichainClaimWithWitness calldata claimPayload) external returns (bool) { @@ -563,7 +563,7 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(QualifiedMultichainClaimWithWitness calldata claimPayload) external returns (bool) { - return _processQualifiedMultichainClaimWithWitness(claimPayload, _release); + return _processQualifiedMultichainClaimWithWitness(claimPayload, _withdraw); } function claim(ExogenousQualifiedMultichainClaimWithWitness calldata claimPayload) external returns (bool) { @@ -571,7 +571,23 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { } function claimAndWithdraw(ExogenousQualifiedMultichainClaimWithWitness calldata claimPayload) external returns (bool) { - return _processExogenousQualifiedMultichainClaimWithWitness(claimPayload, _release); + return _processExogenousQualifiedMultichainClaimWithWitness(claimPayload, _withdraw); + } + + function claim(SplitMultichainClaim calldata claimPayload) external returns (bool) { + return _processSplitMultichainClaim(claimPayload, _release); + } + + function claimAndWithdraw(SplitMultichainClaim calldata claimPayload) external returns (bool) { + return _processSplitMultichainClaim(claimPayload, _withdraw); + } + + function claim(ExogenousSplitMultichainClaim calldata claimPayload) external returns (bool) { + return _processExogenousSplitMultichainClaim(claimPayload, _release); + } + + function claimAndWithdraw(ExogenousSplitMultichainClaim calldata claimPayload) external returns (bool) { + return _processExogenousSplitMultichainClaim(claimPayload, _withdraw); } function enableForcedWithdrawal(uint256 id) external returns (uint256 withdrawableAt) { @@ -2280,6 +2296,10 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { return usingQualifiedMultichainClaimWithWitness(_processClaimWithQualification)(messageHash, qualificationMessageHash, claimPayload, 0x140, operation); } + function _processSplitMultichainClaim(SplitMultichainClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation) internal returns (bool) { + return usingSplitMultichainClaim(_processSimpleSplitClaim)(claimPayload.toMessageHash(), claimPayload, 0xc0, operation); + } + function _processExogenousMultichainClaim(ExogenousMultichainClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation) internal returns (bool) { return usingExogenousMultichainClaim(_processClaimWithSponsorDomain)(claimPayload.toMessageHash(), claimPayload, 0x100, claimPayload.notarizedChainId.toNotarizedDomainSeparator(), operation); } @@ -2313,6 +2333,15 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { ); } + function _processExogenousSplitMultichainClaim(ExogenousSplitMultichainClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation) + internal + returns (bool) + { + return usingExogenousSplitMultichainClaim(_processSplitClaimWithSponsorDomain)( + claimPayload.toMessageHash(), claimPayload, 0x100, claimPayload.notarizedChainId.toNotarizedDomainSeparator(), operation + ); + } + function _processSplitClaim(SplitClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation) internal returns (bool) { return usingSplitClaim(_processSimpleSplitClaim)(claimPayload.toMessageHash(), claimPayload, 0xa0, operation); } diff --git a/src/lib/HashLib.sol b/src/lib/HashLib.sol index 067168a..427fb3a 100644 --- a/src/lib/HashLib.sol +++ b/src/lib/HashLib.sol @@ -481,6 +481,20 @@ library HashLib { } } + function usingSplitMultichainClaim(function (MultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + internal + pure + returns (function (SplitMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function toMessageHash(SplitMultichainClaim calldata claim) internal view returns (bytes32 messageHash) { + messageHash = usingSplitMultichainClaim(toMultichainClaimMessageHash)(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + } + function usingExogenousMultichainClaimWithWitness(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) internal pure @@ -501,6 +515,16 @@ library HashLib { } } + function usingExogenousSplitMultichainClaim(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + internal + pure + returns (function (ExogenousSplitMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + function usingExogenousMultichainClaimWithWitness(function (QualifiedClaim calldata, bytes32, uint256) internal pure returns (bytes32) fnIn) internal pure @@ -680,6 +704,10 @@ library HashLib { return toExogenousMultichainClaimMessageHash(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); } + function toMessageHash(ExogenousSplitMultichainClaim calldata claim) internal view returns (bytes32 messageHash) { + return usingExogenousSplitMultichainClaim(toExogenousMultichainClaimMessageHash)(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + } + function toMessageHash(ExogenousQualifiedMultichainClaim calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) { messageHash = usingExogenousQualifiedMultichainClaim(toExogenousMultichainClaimMessageHash)(claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); qualificationMessageHash = usingExogenousQualifiedMultichainClaim(toQualificationMessageHash)(claim, messageHash, 0); diff --git a/test/TheCompact.t.sol b/test/TheCompact.t.sol index 22e41e8..38fb61e 100644 --- a/test/TheCompact.t.sol +++ b/test/TheCompact.t.sol @@ -44,7 +44,15 @@ import { MultichainClaimWithWitness, ExogenousMultichainClaimWithWitness, QualifiedMultichainClaimWithWitness, - ExogenousQualifiedMultichainClaimWithWitness + ExogenousQualifiedMultichainClaimWithWitness, + SplitMultichainClaim, + ExogenousSplitMultichainClaim, + QualifiedSplitMultichainClaim, + ExogenousQualifiedSplitMultichainClaim, + SplitMultichainClaimWithWitness, + ExogenousSplitMultichainClaimWithWitness, + QualifiedSplitMultichainClaimWithWitness, + ExogenousQualifiedSplitMultichainClaimWithWitness } from "../src/types/MultichainClaims.sol"; import { SplitComponent, TransferComponent, SplitByIdComponent, BatchClaimComponent, SplitBatchClaimComponent } from "../src/types/Components.sol"; @@ -2512,4 +2520,122 @@ contract TheCompactTest is Test { vm.chainId(notarizedChainId); assertEq(block.chainid, notarizedChainId); } + + function test_splitMultichainClaim() public { + ResetPeriod resetPeriod = ResetPeriod.TenMinutes; + Scope scope = Scope.Multichain; + uint256 amount = 1e18; + uint256 anotherAmount = 1e18; + uint256 nonce = 0; + uint256 expires = block.timestamp + 1000; + address arbiter = 0x2222222222222222222222222222222222222222; + uint256 anotherChainId = 7171717; + + address recipientOne = 0x1111111111111111111111111111111111111111; + address recipientTwo = 0x3333333333333333333333333333333333333333; + uint256 amountOne = 4e17; + uint256 amountTwo = 6e17; + + vm.prank(allocator); + theCompact.__register(allocator, ""); + + vm.startPrank(swapper); + uint256 id = theCompact.deposit{ value: amount }(allocator, resetPeriod, scope, swapper); + uint256 anotherId = theCompact.deposit(address(token), allocator, ResetPeriod.TenMinutes, Scope.Multichain, anotherAmount, swapper); + vm.stopPrank(); + + assertEq(theCompact.balanceOf(swapper, id), amount); + assertEq(theCompact.balanceOf(swapper, anotherId), anotherAmount); + + uint256[2][] memory idsAndAmountsOne = new uint256[2][](1); + idsAndAmountsOne[0] = [id, amount]; + + uint256[2][] memory idsAndAmountsTwo = new uint256[2][](1); + idsAndAmountsTwo[0] = [anotherId, anotherAmount]; + + bytes32 allocationHashOne = + keccak256(abi.encode(keccak256("Allocation(address arbiter,uint256 chainId,uint256[2][] idsAndAmounts)"), arbiter, block.chainid, keccak256(abi.encodePacked(idsAndAmountsOne)))); + + bytes32 allocationHashTwo = + keccak256(abi.encode(keccak256("Allocation(address arbiter,uint256 chainId,uint256[2][] idsAndAmounts)"), arbiter, anotherChainId, keccak256(abi.encodePacked(idsAndAmountsTwo)))); + + bytes32 claimHash = keccak256( + abi.encode( + keccak256("MultichainCompact(address sponsor,uint256 nonce,uint256 expires,Allocation[] allocations)Allocation(address arbiter,uint256 chainId,uint256[2][] idsAndAmounts)"), + swapper, + nonce, + expires, + keccak256(abi.encodePacked(allocationHashOne, allocationHashTwo)) + ) + ); + + bytes32 initialDomainSeparator = theCompact.DOMAIN_SEPARATOR(); + + bytes32 digest = keccak256(abi.encodePacked(bytes2(0x1901), initialDomainSeparator, claimHash)); + + (bytes32 r, bytes32 vs) = vm.signCompact(swapperPrivateKey, digest); + bytes memory sponsorSignature = abi.encodePacked(r, vs); + + (r, vs) = vm.signCompact(allocatorPrivateKey, digest); + bytes memory allocatorSignature = abi.encodePacked(r, vs); + + bytes32[] memory additionalChains = new bytes32[](1); + additionalChains[0] = allocationHashTwo; + + SplitComponent memory splitOne = SplitComponent({ claimant: recipientOne, amount: amountOne }); + + SplitComponent memory splitTwo = SplitComponent({ claimant: recipientTwo, amount: amountTwo }); + + SplitComponent[] memory recipients = new SplitComponent[](2); + recipients[0] = splitOne; + recipients[1] = splitTwo; + + SplitMultichainClaim memory claim = SplitMultichainClaim(allocatorSignature, sponsorSignature, swapper, nonce, expires, additionalChains, id, amount, recipients); + + uint256 snapshotId = vm.snapshot(); + vm.prank(arbiter); + (bool status) = theCompact.claim(claim); + assert(status); + + assertEq(address(theCompact).balance, amount); + assertEq(recipientOne.balance, 0); + assertEq(recipientTwo.balance, 0); + assertEq(theCompact.balanceOf(recipientOne, id), amountOne); + assertEq(theCompact.balanceOf(recipientTwo, id), amountTwo); + vm.revertToAndDelete(snapshotId); + + // change to "new chain" (this hack is so the original one gets stored) + uint256 notarizedChainId = abi.decode(abi.encode(block.chainid), (uint256)); + assert(notarizedChainId != anotherChainId); + vm.chainId(anotherChainId); + assertEq(block.chainid, anotherChainId); + assert(notarizedChainId != anotherChainId); + + bytes32 anotherDomainSeparator = theCompact.DOMAIN_SEPARATOR(); + + assert(initialDomainSeparator != anotherDomainSeparator); + + digest = keccak256(abi.encodePacked(bytes2(0x1901), anotherDomainSeparator, claimHash)); + + (r, vs) = vm.signCompact(allocatorPrivateKey, digest); + bytes memory exogenousAllocatorSignature = abi.encodePacked(r, vs); + + additionalChains[0] = allocationHashOne; + uint256 chainIndex = 0; + + ExogenousSplitMultichainClaim memory anotherClaim = + ExogenousSplitMultichainClaim(exogenousAllocatorSignature, sponsorSignature, swapper, nonce, expires, additionalChains, chainIndex, notarizedChainId, anotherId, anotherAmount, recipients); + + vm.prank(arbiter); + (bool exogenousStatus) = theCompact.claim(anotherClaim); + assert(exogenousStatus); + + assertEq(theCompact.balanceOf(swapper, anotherId), 0); + assertEq(theCompact.balanceOf(recipientOne, anotherId), amountOne); + assertEq(theCompact.balanceOf(recipientTwo, anotherId), amountTwo); + + // change back + vm.chainId(notarizedChainId); + assertEq(block.chainid, notarizedChainId); + } }