Skip to content

Commit

Permalink
qualified split multichain
Browse files Browse the repository at this point in the history
  • Loading branch information
0age committed Oct 19, 2024
1 parent 7564393 commit 2cf5733
Show file tree
Hide file tree
Showing 4 changed files with 251 additions and 1 deletion.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ solc = '0.8.28'
evm_version='cancun'
via_ir = true
# optimizer_runs = 4_294_967_295
optimizer_runs = 20_000
optimizer_runs = 200
bytecode_hash = 'none'
src = "src"
out = "out"
Expand Down
34 changes: 34 additions & 0 deletions src/TheCompact.sol
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,22 @@ contract TheCompact is ITheCompact, ERC6909, Extsload {
return _processExogenousSplitMultichainClaim(claimPayload, _withdraw);
}

function claim(QualifiedSplitMultichainClaim calldata claimPayload) external returns (bool) {
return _processQualifiedSplitMultichainClaim(claimPayload, _release);
}

function claimAndWithdraw(QualifiedSplitMultichainClaim calldata claimPayload) external returns (bool) {
return _processQualifiedSplitMultichainClaim(claimPayload, _withdraw);
}

function claim(ExogenousQualifiedSplitMultichainClaim calldata claimPayload) external returns (bool) {
return _processExogenousQualifiedSplitMultichainClaim(claimPayload, _release);
}

function claimAndWithdraw(ExogenousQualifiedSplitMultichainClaim calldata claimPayload) external returns (bool) {
return _processExogenousQualifiedSplitMultichainClaim(claimPayload, _withdraw);
}

function claim(BatchMultichainClaim calldata claimPayload) external returns (bool) {
return _processBatchMultichainClaim(claimPayload, _release);
}
Expand Down Expand Up @@ -1219,6 +1235,14 @@ contract TheCompact is ITheCompact, ERC6909, Extsload {
return _processSimpleSplitClaim.usingSplitMultichainClaim()(claimPayload.toMessageHash(), claimPayload, 0xc0, operation);
}

function _processQualifiedSplitMultichainClaim(QualifiedSplitMultichainClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation)
internal
returns (bool)
{
(bytes32 messageHash, bytes32 qualificationMessageHash) = claimPayload.toMessageHash();
return _processSplitClaimWithQualification.usingQualifiedSplitMultichainClaim()(messageHash, qualificationMessageHash, claimPayload, 0x100, operation);
}

function _processBatchMultichainClaim(BatchMultichainClaim calldata claimPayload, function(address, address, uint256, uint256) internal returns (bool) operation) internal returns (bool) {
return _processSimpleBatchClaim.usingBatchMultichainClaim()(claimPayload.toMessageHash(), claimPayload, 0xc0, operation);
}
Expand Down Expand Up @@ -1253,6 +1277,16 @@ contract TheCompact is ITheCompact, ERC6909, Extsload {
);
}

function _processExogenousQualifiedSplitMultichainClaim(
ExogenousQualifiedSplitMultichainClaim calldata claimPayload,
function(address, address, uint256, uint256) internal returns (bool) operation
) internal returns (bool) {
(bytes32 messageHash, bytes32 qualificationMessageHash) = claimPayload.toMessageHash();
return _processSplitClaimWithQualificationAndSponsorDomain.usingExogenousQualifiedSplitMultichainClaim()(
messageHash, qualificationMessageHash, claimPayload, 0x140, claimPayload.notarizedChainId.toNotarizedDomainSeparator(), operation
);
}

function _processExogenousQualifiedMultichainClaimWithWitness(
ExogenousQualifiedMultichainClaimWithWitness calldata claimPayload,
function(address, address, uint256, uint256) internal returns (bool) operation
Expand Down
75 changes: 75 additions & 0 deletions src/lib/HashLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,26 @@ library HashLib {
}
}

function usingQualifiedSplitMultichainClaim(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn)
internal
pure
returns (function (QualifiedSplitMultichainClaim calldata, uint256) internal pure returns (bytes32) fnOut)
{
assembly ("memory-safe") {
fnOut := fnIn
}
}

function usingExogenousQualifiedSplitMultichainClaim(function (MultichainClaim calldata, uint256) internal pure returns (bytes32) fnIn)
internal
pure
returns (function (ExogenousQualifiedSplitMultichainClaim 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
Expand Down Expand Up @@ -661,6 +681,26 @@ library HashLib {
}
}

function usingQualifiedSplitMultichainClaim(function (MultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn)
internal
pure
returns (function (QualifiedSplitMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnOut)
{
assembly ("memory-safe") {
fnOut := fnIn
}
}

function usingExogenousQualifiedSplitMultichainClaim(function (ExogenousMultichainClaim calldata, uint256, bytes32, bytes32, bytes32) internal view returns (bytes32) fnIn)
internal
pure
returns (function (ExogenousQualifiedSplitMultichainClaim 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
Expand Down Expand Up @@ -711,6 +751,26 @@ library HashLib {
}
}

function usingQualifiedSplitMultichainClaim(function (QualifiedClaim calldata, bytes32, uint256) internal pure returns (bytes32) fnIn)
internal
pure
returns (function (QualifiedSplitMultichainClaim calldata, bytes32, uint256) internal pure returns (bytes32) fnOut)
{
assembly ("memory-safe") {
fnOut := fnIn
}
}

function usingExogenousQualifiedSplitMultichainClaim(function (QualifiedClaim calldata, bytes32, uint256) internal pure returns (bytes32) fnIn)
internal
pure
returns (function (ExogenousQualifiedSplitMultichainClaim calldata, bytes32, uint256) internal pure 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, usingSplitMultichainClaim(deriveIdsAndAmountsHash)(claim, 0));
}
Expand Down Expand Up @@ -848,6 +908,13 @@ library HashLib {
qualificationMessageHash = usingQualifiedMultichainClaimWithWitness(toQualificationMessageHash)(claim, messageHash, 0x40);
}

function toMessageHash(QualifiedSplitMultichainClaim calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) {
messageHash = usingQualifiedSplitMultichainClaim(toMultichainClaimMessageHash)(
claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingQualifiedSplitMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x40)
);
qualificationMessageHash = usingQualifiedSplitMultichainClaim(toQualificationMessageHash)(claim, messageHash, 0);
}

function toMessageHash(QualifiedMultichainClaim calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) {
messageHash = usingQualifiedMultichainClaim(toMultichainClaimMessageHash)(
claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingQualifiedMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x40)
Expand Down Expand Up @@ -1050,6 +1117,14 @@ library HashLib {
qualificationMessageHash = usingExogenousQualifiedMultichainClaimWithWitness(toQualificationMessageHash)(claim, messageHash, 0x40);
}

function toMessageHash(ExogenousQualifiedSplitMultichainClaim calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) {
bytes32 idsAndAmountsHash = usingExogenousQualifiedSplitMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x80);
messageHash = usingExogenousQualifiedSplitMultichainClaim(toExogenousMultichainClaimMessageHash)(
claim, 0x40, ALLOCATION_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH, usingExogenousQualifiedSplitMultichainClaim(deriveIdsAndAmountsHash)(claim, 0x80)
);
qualificationMessageHash = usingExogenousQualifiedSplitMultichainClaim(toQualificationMessageHash)(claim, messageHash, 0);
}

function toMessageHash(ExogenousQualifiedSplitBatchMultichainClaimWithWitness calldata claim) internal view returns (bytes32 messageHash, bytes32 qualificationMessageHash) {
(bytes32 allocationTypehash, bytes32 multichainCompactTypehash) = usingExogenousQualifiedSplitBatchMultichainClaimWithWitness(getMultichainTypehashes)(claim);

Expand Down
141 changes: 141 additions & 0 deletions test/TheCompact.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2658,6 +2658,147 @@ contract TheCompactTest is Test {
assertEq(block.chainid, notarizedChainId);
}

function test_qualifiedSplitMultichainClaim() 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);

bytes32 qualificationTypehash = keccak256("ExampleQualifiedClaim(bytes32 claimHash,uint256 qualifiedClaimArgument)");

uint256 qualifiedClaimArgument = 123;
bytes memory qualificationPayload = abi.encode(qualifiedClaimArgument);

bytes32 qualifiedClaimHash = keccak256(abi.encode(qualificationTypehash, claimHash, qualifiedClaimArgument));

digest = keccak256(abi.encodePacked(bytes2(0x1901), initialDomainSeparator, qualifiedClaimHash));

(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;

QualifiedSplitMultichainClaim memory claim =
QualifiedSplitMultichainClaim(allocatorSignature, sponsorSignature, swapper, nonce, expires, qualificationTypehash, qualificationPayload, 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, qualifiedClaimHash));

(r, vs) = vm.signCompact(allocatorPrivateKey, digest);
bytes memory exogenousAllocatorSignature = abi.encodePacked(r, vs);

additionalChains[0] = allocationHashOne;
uint256 chainIndex = 0;

ExogenousQualifiedSplitMultichainClaim memory anotherClaim = ExogenousQualifiedSplitMultichainClaim(
exogenousAllocatorSignature,
sponsorSignature,
swapper,
nonce,
expires,
qualificationTypehash,
qualificationPayload,
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);
}

function test_batchMultichainClaim() public {
uint256 amount = 1e18;
uint256 anotherAmount = 1e18;
Expand Down

0 comments on commit 2cf5733

Please sign in to comment.