diff --git a/src/TheCompact.sol b/src/TheCompact.sol index ef86ab9..8b0b716 100644 --- a/src/TheCompact.sol +++ b/src/TheCompact.sol @@ -154,6 +154,22 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { using HashLib for ExogenousSplitMultichainClaimWithWitness; using HashLib for ExogenousQualifiedSplitMultichainClaim; using HashLib for ExogenousQualifiedSplitMultichainClaimWithWitness; + using HashLib for BatchMultichainClaim; + using HashLib for QualifiedBatchMultichainClaim; + using HashLib for BatchMultichainClaimWithWitness; + using HashLib for QualifiedBatchMultichainClaimWithWitness; + using HashLib for SplitBatchMultichainClaim; + using HashLib for SplitBatchMultichainClaimWithWitness; + using HashLib for QualifiedSplitBatchMultichainClaim; + using HashLib for QualifiedSplitBatchMultichainClaimWithWitness; + using HashLib for ExogenousBatchMultichainClaim; + using HashLib for ExogenousQualifiedBatchMultichainClaim; + using HashLib for ExogenousBatchMultichainClaimWithWitness; + using HashLib for ExogenousQualifiedBatchMultichainClaimWithWitness; + using HashLib for ExogenousSplitBatchMultichainClaim; + using HashLib for ExogenousSplitBatchMultichainClaimWithWitness; + using HashLib for ExogenousQualifiedSplitBatchMultichainClaim; + using HashLib for ExogenousQualifiedSplitBatchMultichainClaimWithWitness; using IdLib for uint96; using IdLib for uint256; using IdLib for address; @@ -590,6 +606,22 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { return _processExogenousSplitMultichainClaim(claimPayload, _withdraw); } + function claim(BatchMultichainClaim calldata claimPayload) external returns (bool) { + return _processBatchMultichainClaim(claimPayload, _release); + } + + function claimAndWithdraw(BatchMultichainClaim calldata claimPayload) external returns (bool) { + return _processBatchMultichainClaim(claimPayload, _withdraw); + } + + function claim(ExogenousBatchMultichainClaim calldata claimPayload) external returns (bool) { + return _processExogenousBatchMultichainClaim(claimPayload, _release); + } + + function claimAndWithdraw(ExogenousBatchMultichainClaim calldata claimPayload) external returns (bool) { + return _processExogenousBatchMultichainClaim(claimPayload, _withdraw); + } + function enableForcedWithdrawal(uint256 id) external returns (uint256 withdrawableAt) { // overflow check not necessary as reset period is capped unchecked { @@ -2300,6 +2332,10 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { return usingSplitMultichainClaim(_processSimpleSplitClaim)(claimPayload.toMessageHash(), claimPayload, 0xc0, operation); } + function _processBatchMultichainClaim(BatchMultichainClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation) internal returns (bool) { + return usingBatchMultichainClaim(_processSimpleBatchClaim)(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); } @@ -2342,6 +2378,15 @@ contract TheCompact is ITheCompact, ERC6909, Extsload { ); } + function _processExogenousBatchMultichainClaim(ExogenousBatchMultichainClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation) + internal + returns (bool) + { + return usingExogenousBatchMultichainClaim(_processBatchClaimWithSponsorDomain)( + 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 427fb3a..3a07a29 100644 --- a/src/lib/HashLib.sol +++ b/src/lib/HashLib.sol @@ -71,6 +71,25 @@ import { ExogenousQualifiedSplitMultichainClaimWithWitness } from "../types/MultichainClaims.sol"; +import { + BatchMultichainClaim, + QualifiedBatchMultichainClaim, + BatchMultichainClaimWithWitness, + QualifiedBatchMultichainClaimWithWitness, + SplitBatchMultichainClaim, + SplitBatchMultichainClaimWithWitness, + QualifiedSplitBatchMultichainClaim, + QualifiedSplitBatchMultichainClaimWithWitness, + ExogenousBatchMultichainClaim, + ExogenousQualifiedBatchMultichainClaim, + ExogenousBatchMultichainClaimWithWitness, + ExogenousQualifiedBatchMultichainClaimWithWitness, + ExogenousSplitBatchMultichainClaim, + ExogenousSplitBatchMultichainClaimWithWitness, + ExogenousQualifiedSplitBatchMultichainClaim, + ExogenousQualifiedSplitBatchMultichainClaimWithWitness +} from "../types/BatchMultichainClaims.sol"; + import { BatchClaimComponent, SplitBatchClaimComponent } from "../types/Components.sol"; import { ResetPeriod } from "../types/ResetPeriod.sol"; @@ -399,7 +418,18 @@ library HashLib { qualificationMessageHash = toQualificationMessageHash.usingQualifiedSplitBatchClaimWithWitness()(claim, messageHash, 0x40); } - function toMultichainClaimMessageHash(MultichainClaim calldata claim, uint256 additionalOffset, bytes32 allocationTypehash, bytes32 multichainCompactTypehash) + function deriveIdsAndAmountsHash(MultichainClaim calldata claim, uint256 additionalOffset) internal pure returns (bytes32 idsAndAmountsHash) { + assembly ("memory-safe") { + let claimWithAdditionalOffset := add(claim, additionalOffset) + + mstore(0, calldataload(add(claimWithAdditionalOffset, 0xc0))) // id + mstore(0x20, calldataload(add(claimWithAdditionalOffset, 0xe0))) // amount + + idsAndAmountsHash := keccak256(0, 0x40) + } + } + + function toMultichainClaimMessageHash(MultichainClaim calldata claim, uint256 additionalOffset, bytes32 allocationTypehash, bytes32 multichainCompactTypehash, bytes32 idsAndAmountsHash) internal view returns (bytes32 messageHash) @@ -407,12 +437,7 @@ library HashLib { assembly ("memory-safe") { let m := mload(0x40) // Grab the free memory pointer; memory will be left dirtied. - let claimWithAdditionalOffset := add(claim, additionalOffset) - - mstore(0, calldataload(add(claimWithAdditionalOffset, 0xc0))) // id - mstore(0x20, calldataload(add(claimWithAdditionalOffset, 0xe0))) // amount - - mstore(add(m, 0x60), keccak256(0, 0x40)) + mstore(add(m, 0x60), idsAndAmountsHash) mstore(m, allocationTypehash) mstore(add(m, 0x20), caller()) // arbiter mstore(add(m, 0x40), chainid()) @@ -423,7 +448,7 @@ library HashLib { mstore(m, keccak256(m, add(0x80, mul(0x20, hasWitness)))) // first allocation hash // subsequent allocation hashes - let additionalChainsPtr := add(claim, calldataload(add(claimWithAdditionalOffset, 0xa0))) + let additionalChainsPtr := add(claim, calldataload(add(add(claim, additionalOffset), 0xa0))) let additionalChainsLength := shl(5, calldataload(additionalChainsPtr)) calldatacopy(add(m, 0x20), add(0x20, additionalChainsPtr), additionalChainsLength) @@ -438,23 +463,67 @@ library HashLib { } function toMessageHash(MultichainClaim calldata claim) internal view returns (bytes32 messageHash) { - messageHash = toMultichainClaimMessageHash(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + messageHash = toMultichainClaimMessageHash(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, deriveIdsAndAmountsHash(claim, 0)); + } + + function toMessageHash(BatchMultichainClaim calldata claim) internal view returns (bytes32 messageHash) { + messageHash = usingBatchMultichainClaim(toMultichainClaimMessageHash)(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, toIdsAndAmountsHash(claim.claims)); + } + + function usingQualifiedMultichainClaim(function (MultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) + internal + pure + returns (function (QualifiedMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } } - function usingQualifiedMultichainClaim(function (MultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingQualifiedMultichainClaim(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) internal pure - returns (function (QualifiedMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function (QualifiedMultichainClaim calldata, uint256) internal pure returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn } } - function usingQualifiedMultichainClaimWithWitness(function (MultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingExogenousQualifiedMultichainClaim(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) internal pure - returns (function (QualifiedMultichainClaimWithWitness calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function (ExogenousQualifiedMultichainClaim calldata, uint256) internal pure returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingQualifiedMultichainClaimWithWitness(function (MultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) + internal + pure + returns (function (QualifiedMultichainClaimWithWitness calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingQualifiedMultichainClaimWithWitness(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) + internal + pure + returns (function (QualifiedMultichainClaimWithWitness calldata, uint256) internal pure returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingExogenousQualifiedMultichainClaimWithWitness(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) + internal + pure + returns (function (ExogenousQualifiedMultichainClaimWithWitness calldata, uint256) internal pure returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn @@ -481,10 +550,30 @@ library HashLib { } } - function usingSplitMultichainClaim(function (MultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingSplitMultichainClaim(function (MultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) internal pure - returns (function (SplitMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function (SplitMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingBatchMultichainClaim(function (MultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) + internal + pure + returns (function (BatchMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingSplitMultichainClaim(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) + internal + pure + returns (function (SplitMultichainClaim calldata, uint256) internal pure returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn @@ -492,33 +581,53 @@ library HashLib { } function toMessageHash(SplitMultichainClaim calldata claim) internal view returns (bytes32 messageHash) { - messageHash = usingSplitMultichainClaim(toMultichainClaimMessageHash)(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + messageHash = usingSplitMultichainClaim(toMultichainClaimMessageHash)(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingSplitMultichainClaim(deriveIdsAndAmountsHash)(claim, 0)); + } + + function usingExogenousMultichainClaimWithWitness(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) + internal + pure + returns (function (ExogenousMultichainClaimWithWitness calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } } - function usingExogenousMultichainClaimWithWitness(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingExogenousQualifiedMultichainClaimWithWitness(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) internal pure - returns (function (ExogenousMultichainClaimWithWitness calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function (ExogenousQualifiedMultichainClaimWithWitness calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn } } - function usingExogenousQualifiedMultichainClaimWithWitness(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingExogenousSplitMultichainClaim(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) internal pure - returns (function (ExogenousQualifiedMultichainClaimWithWitness calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function (ExogenousSplitMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn } } - function usingExogenousSplitMultichainClaim(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingExogenousBatchMultichainClaim(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) internal pure - returns (function (ExogenousSplitMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function (ExogenousBatchMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingExogenousSplitMultichainClaim(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) + internal + pure + returns (function (ExogenousSplitMultichainClaim calldata, uint256) internal pure returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn @@ -535,10 +644,40 @@ library HashLib { } } - function usingMultichainClaimWithWitness(function (MultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingMultichainClaimWithWitness(function (MultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) + internal + pure + returns (function (MultichainClaimWithWitness calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingMultichainClaimWithWitness(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) + internal + pure + returns (function (MultichainClaimWithWitness calldata, uint256) internal pure returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingExogenousMultichainClaimWithWitness(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) + internal + pure + returns (function (ExogenousMultichainClaimWithWitness calldata, uint256) internal pure returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingExogenousMultichainClaim(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) internal pure - returns (function (MultichainClaimWithWitness calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function (ExogenousMultichainClaim calldata, uint256) internal pure returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn @@ -547,30 +686,48 @@ library HashLib { function toMessageHash(MultichainClaimWithWitness calldata claim) internal view returns (bytes32 messageHash) { (bytes32 allocationTypehash, bytes32 multichainCompactTypehash) = getMultichainTypehashes(claim); - messageHash = usingMultichainClaimWithWitness(toMultichainClaimMessageHash)(claim, 0x40, allocationTypehash, multichainCompactTypehash); + messageHash = usingMultichainClaimWithWitness(toMultichainClaimMessageHash)( + claim, 0x40, allocationTypehash, multichainCompactTypehash, usingMultichainClaimWithWitness(deriveIdsAndAmountsHash)(claim, 0x40) + ); } function toMessageHash(ExogenousMultichainClaimWithWitness calldata claim) internal view returns (bytes32 messageHash) { (bytes32 allocationTypehash, bytes32 multichainCompactTypehash) = usingExogenousMultichainClaimWithWitness(getMultichainTypehashes)(claim); - messageHash = usingExogenousMultichainClaimWithWitness(toExogenousMultichainClaimMessageHash)(claim, 0x40, allocationTypehash, multichainCompactTypehash); + messageHash = usingExogenousMultichainClaimWithWitness(toExogenousMultichainClaimMessageHash)( + claim, 0x40, allocationTypehash, multichainCompactTypehash, usingExogenousMultichainClaimWithWitness(deriveIdsAndAmountsHash)(claim, 0x80) + ); } function toMessageHash(QualifiedMultichainClaimWithWitness calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) { (bytes32 allocationTypehash, bytes32 multichainCompactTypehash) = usingQualifiedMultichainClaimWithWitness(getMultichainTypehashes)(claim); - messageHash = usingQualifiedMultichainClaimWithWitness(toMultichainClaimMessageHash)(claim, 0x80, allocationTypehash, multichainCompactTypehash); + messageHash = usingQualifiedMultichainClaimWithWitness(toMultichainClaimMessageHash)( + claim, 0x80, allocationTypehash, multichainCompactTypehash, usingQualifiedMultichainClaimWithWitness(deriveIdsAndAmountsHash)(claim, 0x80) + ); qualificationMessageHash = usingQualifiedMultichainClaimWithWitness(toQualificationMessageHash)(claim, messageHash, 0x40); } function toMessageHash(QualifiedMultichainClaim calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) { - messageHash = usingQualifiedMultichainClaim(toMultichainClaimMessageHash)(claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + messageHash = usingQualifiedMultichainClaim(toMultichainClaimMessageHash)( + claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingQualifiedMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x40) + ); qualificationMessageHash = usingQualifiedMultichainClaim(toQualificationMessageHash)(claim, messageHash, 0); } - function usingExogenousQualifiedMultichainClaim(function(ExogenousMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnIn) + function usingExogenousQualifiedMultichainClaim(function(ExogenousMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn) internal pure - returns (function(ExogenousQualifiedMultichainClaim calldata, uint256, bytes32, bytes32) internal view returns (bytes32) fnOut) + returns (function(ExogenousQualifiedMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut) + { + assembly ("memory-safe") { + fnOut := fnIn + } + } + + function usingExogenousQualifiedMultichainClaim(function(ExogenousMultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn) + internal + pure + returns (function(ExogenousQualifiedMultichainClaim calldata, uint256) internal pure returns (bytes32) fnOut) { assembly ("memory-safe") { fnOut := fnIn @@ -650,20 +807,17 @@ library HashLib { } } - function toExogenousMultichainClaimMessageHash(ExogenousMultichainClaim calldata claim, uint256 additionalOffset, bytes32 allocationTypehash, bytes32 multichainCompactTypehash) - internal - view - returns (bytes32 messageHash) - { + function toExogenousMultichainClaimMessageHash( + ExogenousMultichainClaim calldata claim, + uint256 additionalOffset, + bytes32 allocationTypehash, + bytes32 multichainCompactTypehash, + bytes32 idsAndAmountsHash + ) internal view returns (bytes32 messageHash) { assembly ("memory-safe") { let m := mload(0x40) // Grab the free memory pointer; memory will be left dirtied. - let claimWithAdditionalOffset := add(claim, additionalOffset) - - mstore(0, calldataload(add(claimWithAdditionalOffset, 0x100))) // id - mstore(0x20, calldataload(add(claimWithAdditionalOffset, 0x120))) // amount - - mstore(add(m, 0x60), keccak256(0, 0x40)) + mstore(add(m, 0x60), idsAndAmountsHash) mstore(m, allocationTypehash) mstore(add(m, 0x20), caller()) // arbiter mstore(add(m, 0x40), chainid()) @@ -671,9 +825,10 @@ library HashLib { let hasWitness := iszero(eq(allocationTypehash, ALLOCATION_TYPEHASH)) if hasWitness { mstore(add(m, 0x80), calldataload(add(claim, 0xa0))) } // witness - let allocationHash := keccak256(m, add(0x80, mul(0x20, hasWitness))) // first allocation hash + let allocationHash := keccak256(m, add(0x80, mul(0x20, hasWitness))) // allocation hash // additional allocation hashes + let claimWithAdditionalOffset := add(claim, additionalOffset) let additionalChainsPtr := add(claim, calldataload(add(claimWithAdditionalOffset, 0xa0))) let additionalChainsLength := shl(5, calldataload(additionalChainsPtr)) let additionalChainsData := add(0x20, additionalChainsPtr) @@ -701,22 +856,32 @@ library HashLib { } function toMessageHash(ExogenousMultichainClaim calldata claim) internal view returns (bytes32 messageHash) { - return toExogenousMultichainClaimMessageHash(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + return toExogenousMultichainClaimMessageHash(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingExogenousMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x40)); } function toMessageHash(ExogenousSplitMultichainClaim calldata claim) internal view returns (bytes32 messageHash) { - return usingExogenousSplitMultichainClaim(toExogenousMultichainClaimMessageHash)(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + return usingExogenousSplitMultichainClaim(toExogenousMultichainClaimMessageHash)( + claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingExogenousSplitMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x40) + ); + } + + function toMessageHash(ExogenousBatchMultichainClaim calldata claim) internal view returns (bytes32 messageHash) { + return usingExogenousBatchMultichainClaim(toExogenousMultichainClaimMessageHash)(claim, 0, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, toIdsAndAmountsHash(claim.claims)); } function toMessageHash(ExogenousQualifiedMultichainClaim calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) { - messageHash = usingExogenousQualifiedMultichainClaim(toExogenousMultichainClaimMessageHash)(claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH); + messageHash = usingExogenousQualifiedMultichainClaim(toExogenousMultichainClaimMessageHash)( + claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingExogenousQualifiedMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x80) + ); qualificationMessageHash = usingExogenousQualifiedMultichainClaim(toQualificationMessageHash)(claim, messageHash, 0); } function toMessageHash(ExogenousQualifiedMultichainClaimWithWitness calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) { (bytes32 allocationTypehash, bytes32 multichainCompactTypehash) = usingExogenousQualifiedMultichainClaimWithWitness(getMultichainTypehashes)(claim); - messageHash = usingExogenousQualifiedMultichainClaimWithWitness(toExogenousMultichainClaimMessageHash)(claim, 0x80, allocationTypehash, multichainCompactTypehash); + messageHash = usingExogenousQualifiedMultichainClaimWithWitness(toExogenousMultichainClaimMessageHash)( + claim, 0x80, allocationTypehash, multichainCompactTypehash, usingExogenousQualifiedMultichainClaimWithWitness(deriveIdsAndAmountsHash)(claim, 0xc0) + ); qualificationMessageHash = usingExogenousQualifiedMultichainClaimWithWitness(toQualificationMessageHash)(claim, messageHash, 0x40); } diff --git a/test/TheCompact.t.sol b/test/TheCompact.t.sol index 38fb61e..e42601c 100644 --- a/test/TheCompact.t.sol +++ b/test/TheCompact.t.sol @@ -55,6 +55,25 @@ import { ExogenousQualifiedSplitMultichainClaimWithWitness } from "../src/types/MultichainClaims.sol"; +import { + BatchMultichainClaim, + ExogenousBatchMultichainClaim, + QualifiedBatchMultichainClaim, + ExogenousQualifiedBatchMultichainClaim, + BatchMultichainClaimWithWitness, + ExogenousBatchMultichainClaimWithWitness, + QualifiedBatchMultichainClaimWithWitness, + ExogenousQualifiedBatchMultichainClaimWithWitness, + SplitBatchMultichainClaim, + ExogenousSplitBatchMultichainClaim, + QualifiedSplitBatchMultichainClaim, + ExogenousQualifiedSplitBatchMultichainClaim, + SplitBatchMultichainClaimWithWitness, + ExogenousSplitBatchMultichainClaimWithWitness, + QualifiedSplitBatchMultichainClaimWithWitness, + ExogenousQualifiedSplitBatchMultichainClaimWithWitness +} from "../src/types/BatchMultichainClaims.sol"; + import { SplitComponent, TransferComponent, SplitByIdComponent, BatchClaimComponent, SplitBatchClaimComponent } from "../src/types/Components.sol"; interface EIP712 { @@ -2638,4 +2657,129 @@ contract TheCompactTest is Test { vm.chainId(notarizedChainId); assertEq(block.chainid, notarizedChainId); } + + function test_batchMultichainClaim() public { + uint256 amount = 1e18; + uint256 anotherAmount = 1e18; + uint256 aThirdAmount = 1e18; + uint256 nonce = 0; + uint256 expires = block.timestamp + 1000; + address claimant = 0x1111111111111111111111111111111111111111; + address arbiter = 0x2222222222222222222222222222222222222222; + + vm.prank(allocator); + theCompact.__register(allocator, ""); + + vm.startPrank(swapper); + uint256 id = theCompact.deposit{ value: amount }(allocator, ResetPeriod.TenMinutes, Scope.Multichain, swapper); + + uint256 anotherId = theCompact.deposit(address(token), allocator, ResetPeriod.TenMinutes, Scope.Multichain, anotherAmount, swapper); + assertEq(theCompact.balanceOf(swapper, anotherId), anotherAmount); + + uint256 aThirdId = theCompact.deposit(address(anotherToken), allocator, ResetPeriod.TenMinutes, Scope.Multichain, aThirdAmount, swapper); + assertEq(theCompact.balanceOf(swapper, aThirdId), aThirdAmount); + + vm.stopPrank(); + + assertEq(theCompact.balanceOf(swapper, id), amount); + assertEq(theCompact.balanceOf(swapper, anotherId), anotherAmount); + assertEq(theCompact.balanceOf(swapper, aThirdId), aThirdAmount); + + uint256[2][] memory idsAndAmounts = new uint256[2][](3); + idsAndAmounts[0] = [id, amount]; + idsAndAmounts[1] = [anotherId, anotherAmount]; + idsAndAmounts[2] = [aThirdId, aThirdAmount]; + + uint256 anotherChainId = 7171717; + + uint256[2][] memory idsAndAmountsOne = new uint256[2][](1); + idsAndAmountsOne[0] = [id, amount]; + + uint256[2][] memory idsAndAmountsTwo = new uint256[2][](2); + idsAndAmountsTwo[0] = [anotherId, anotherAmount]; + idsAndAmountsTwo[1] = [aThirdId, aThirdAmount]; + + 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; + + BatchClaimComponent[] memory claims = new BatchClaimComponent[](1); + claims[0] = BatchClaimComponent({ id: id, allocatedAmount: amount, amount: amount }); + + BatchMultichainClaim memory claim = BatchMultichainClaim(allocatorSignature, sponsorSignature, swapper, nonce, expires, additionalChains, claims, claimant); + + uint256 snapshotId = vm.snapshot(); + vm.prank(arbiter); + (bool status) = theCompact.claim(claim); + assert(status); + + assertEq(address(theCompact).balance, amount); + assertEq(claimant.balance, 0); + assertEq(theCompact.balanceOf(swapper, id), 0); + assertEq(theCompact.balanceOf(claimant, id), amount); + 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; + + claims = new BatchClaimComponent[](2); + claims[0] = BatchClaimComponent({ id: anotherId, allocatedAmount: anotherAmount, amount: anotherAmount }); + claims[1] = BatchClaimComponent({ id: aThirdId, allocatedAmount: aThirdAmount, amount: aThirdAmount }); + + ExogenousBatchMultichainClaim memory anotherClaim = + ExogenousBatchMultichainClaim(exogenousAllocatorSignature, sponsorSignature, swapper, nonce, expires, additionalChains, chainIndex, notarizedChainId, claims, claimant); + + vm.prank(arbiter); + (bool exogenousStatus) = theCompact.claim(anotherClaim); + assert(exogenousStatus); + + assertEq(theCompact.balanceOf(swapper, anotherId), 0); + assertEq(theCompact.balanceOf(claimant, anotherId), anotherAmount); + assertEq(theCompact.balanceOf(claimant, aThirdId), aThirdAmount); + + // change back + vm.chainId(notarizedChainId); + assertEq(block.chainid, notarizedChainId); + } }