Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 0 additions & 145 deletions src/ERC7683Tribunal.sol

This file was deleted.

110 changes: 108 additions & 2 deletions src/Tribunal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ import {
MANDATE_LOCK_TYPEHASH,
COMPACT_TYPEHASH_WITH_MANDATE,
ADJUSTMENT_TYPEHASH,
WITNESS_TYPESTRING
WITNESS_TYPESTRING,
CONDITIONAL_CLAIM_TYPEHASH,
CONDITIONAL_MANDATE_TYPEHASH
} from "./types/TribunalTypeHashes.sol";

/**
Expand Down Expand Up @@ -239,10 +241,11 @@ contract Tribunal is BlockNumberish, ITribunal {
function dispatch(
BatchCompact calldata compact,
bytes32 mandateHash,
bytes32 compactTypehash,
DispatchParameters calldata dispatchParams
) external payable nonReentrant returns (bytes32 claimHash, uint256[] memory claimAmounts) {
// Derive claim hash.
claimHash = deriveClaimHash(compact, mandateHash);
claimHash = _deriveClaimHash(compact, mandateHash, LOCK_TYPEHASH, compactTypehash);

// Check if claim has been filled.
bytes32 claimant = _dispositions[claimHash];
Expand All @@ -267,6 +270,38 @@ contract Tribunal is BlockNumberish, ITribunal {
return (claimHash, claimAmounts);
}

/// @inheritdoc ITribunal
function fillConditional(
BatchCompact calldata compact,
bytes32 conditionalClaimHash,
bytes32 claimant
) external returns (bytes32 claimHash, bytes32 mandateHash, uint256[] memory claimAmounts) {
(claimHash, mandateHash, claimAmounts) =
_fillConditional(compact, conditionalClaimHash, claimant);
}

/// @inheritdoc ITribunal
function fillAndDispatchConditional(
BatchCompact calldata compact,
bytes32 conditionalClaimHash,
bytes32 claimant,
DispatchParameters calldata dispatchParameters
)
external
payable
nonReentrant
returns (bytes32 claimHash, bytes32 mandateHash, uint256[] memory claimAmounts)
{
(claimHash, mandateHash, claimAmounts) = _fillConditional(
compact, conditionalClaimHash, claimant
);

// Trigger dispatch callback to relay information to provided target.
_performDispatchCallback(
compact, mandateHash, claimHash, claimant, claimAmounts, dispatchParameters
);
}

/// @inheritdoc ITribunal
function settleOrRegister(
bytes32 sourceClaimHash,
Expand Down Expand Up @@ -881,6 +916,56 @@ contract Tribunal is BlockNumberish, ITribunal {
performRecipientCallback(mandate, claimHash, mandateHash, fillAmounts);
}

function _fillConditional(
BatchCompact calldata compact,
bytes32 conditionalClaimHash,
bytes32 claimant
) internal returns (bytes32 claimHash, bytes32 mandateHash, uint256[] memory claimAmounts) {
(claimHash, mandateHash) = _toConditionalClaimHash(compact, conditionalClaimHash);

// Check the tag along was not previously filled
if (_dispositions[claimHash] != bytes32(0)) {
revert AlreadyFilled();
}

// Get the scaling factor for the tag along fill
uint256 scalingFactor = _getClaimReductionScalingFactor(conditionalClaimHash);

bytes32 originalFiller = _dispositions[conditionalClaimHash];

// If claim was cancelled, anyone can fill (for a zero amount). Otherwise, the original filler must be the caller.
assembly ("memory-safe") {
// To pass, either the originalFiller must be the caller, or the scaling factor must be 0 (indicating a cancelled claim)
if mul(
iszero(
eq(and(0xffffffffffffffffffffffffffffffffffffffff, originalFiller), caller())
),
scalingFactor
) {
mstore(0, 0x6770cd44) // ValidityConditionsNotMet()
revert(0x1c, 0x04)
}
}

// Store the scalingFactor for the tag along claim
_claimReductionScalingFactors[claimHash] = scalingFactor;
// Set the disposition for the tag along claim.
_dispositions[claimHash] = claimant;

// Compute claim amounts using the stored scaling factor.
claimAmounts = new uint256[](compact.commitments.length);
for (uint256 i = 0; i < compact.commitments.length; i++) {
claimAmounts[i] = compact.commitments[i].amount.mulWad(scalingFactor);
}

// Emit the fill event.
emit Fill(
compact.sponsor, claimant, claimHash, new FillRecipient[](0), claimAmounts, block.number
);

return (claimHash, mandateHash, claimAmounts);
}

/**
* @notice Triggers the recipient callback if one is specified in the mandate.
* @dev The recipient callback is signed by the sponsor as part of the mandate and executes
Expand Down Expand Up @@ -1739,4 +1824,25 @@ contract Tribunal is BlockNumberish, ITribunal {
)
);
}

function _toConditionalClaimHash(BatchCompact calldata compact, bytes32 conditionalHash)
internal
pure
returns (bytes32 claimHash, bytes32 mandateHash)
{
mandateHash = keccak256(abi.encode(CONDITIONAL_MANDATE_TYPEHASH, conditionalHash));
bytes32 commitmentsHash = _deriveCommitmentsHash(compact.commitments, LOCK_TYPEHASH);
claimHash = keccak256(
abi.encode(
CONDITIONAL_CLAIM_TYPEHASH,
compact.arbiter,
compact.sponsor,
compact.nonce,
compact.expires,
commitmentsHash,
mandateHash
)
);
return (claimHash, mandateHash);
}
}
36 changes: 36 additions & 0 deletions src/interfaces/ITribunal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -218,16 +218,52 @@ interface ITribunal {
* where a claimant has already been recorded. Not needed for read-based systems that can query state directly.
* @param compact The compact parameters from the original fill.
* @param mandateHash The mandate hash from the original fill.
* @param compactTypehash The typehash of the compact.
* @param dispatch The dispatch callback parameters (target, chainId, value, context).
* @return claimHash The claim hash derived from the compact and mandate.
* @return claimAmounts The amounts of tokens claimed.
*/
function dispatch(
BatchCompact calldata compact,
bytes32 mandateHash,
bytes32 compactTypehash,
DispatchParameters calldata dispatch
) external payable returns (bytes32 claimHash, uint256[] memory claimAmounts);

/**
* @notice Fill a tag along claim. Does not require any assets to be provided.
* @dev The tag along claim can only be filled if and by whom the source claim has been filled.
* @param compact The compact parameters and constraints.
* @param conditionalClaimHash The hash of the conditional claim.
* @param claimant The recipient of claimed tokens on the claim chain.
* @return claimHash The derived claim hash.
*/
function fillConditional(
BatchCompact calldata compact,
bytes32 conditionalClaimHash,
bytes32 claimant
) external returns (bytes32 claimHash, bytes32 mandateHash, uint256[] memory claimAmounts);

/**
*
* @param compact The compact parameters and constraints.
* @param conditionalClaimHash The hash of the conditional claim.
* @param claimant The recipient of claimed tokens on the claim chain.
* @param dispatchParameters The dispatch callback parameters (target, chainId, value, context).
* @return claimHash The derived claim hash.
* @return mandateHash The derived mandate hash.
* @return claimAmounts The amounts of tokens claimed.
*/
function fillAndDispatchConditional(
BatchCompact calldata compact,
bytes32 conditionalClaimHash,
bytes32 claimant,
DispatchParameters calldata dispatchParameters
)
external
payable
returns (bytes32 claimHash, bytes32 mandateHash, uint256[] memory claimAmounts);

/**
* @notice Handles token receipt on destination chains after bridging, with race condition protection.
* @dev This function is typically triggered by a recipient callback from a same-chain fill on the source chain.
Expand Down
11 changes: 11 additions & 0 deletions src/types/TribunalTypeHashes.sol
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ string constant COMPACT_WITH_MANDATE_TYPESTRING =
string constant ADJUSTMENT_TYPESTRING =
"Adjustment(bytes32 claimHash,uint256 fillIndex,uint256 targetBlock,uint256[] supplementalPriceCurve,bytes32 validityConditions)";

string constant CONDITIONAL_CLAIM_TYPESTRING =
"BatchCompact(address arbiter,address sponsor,uint256 nonce,uint256 expires,Lock[] commitments,ConditionalMandate mandate)ConditionalMandate(bytes32 conditionalHash)";

string constant CONDITIONAL_MANDATE_TYPESTRING = "ConditionalMandate(bytes32 conditionalHash)";

// Typehash constants (hardcoded to reduce init code size)
bytes32 constant MANDATE_TYPEHASH =
0xd98eceb6e5c7770b3b664a99c269855402fe5255294a30970d25376caea662c6;
Expand All @@ -134,6 +139,12 @@ bytes32 constant COMPACT_TYPEHASH_WITH_MANDATE =
bytes32 constant ADJUSTMENT_TYPEHASH =
0xe829b2a82439f37ac7578a226e337d334e0ee0da2f05ab63891c19cb84714414;

bytes32 constant CONDITIONAL_CLAIM_TYPEHASH =
0xe7aa01adeb9772e270fcbf54fc0deffc57de6f14df09413ebf053090ce4b2cc1;

bytes32 constant CONDITIONAL_MANDATE_TYPEHASH =
0x160ea0d3e26cb934d0c096c875e9c0e33baffd19ffab2eafb061c860873d03ee;

// Witness typestring (partial string that is provided to The Compact by Tribunal to process claims)
string constant WITNESS_TYPESTRING =
"address adjuster,Mandate_Fill[] fills)Mandate_BatchCompact(address arbiter,address sponsor,uint256 nonce,uint256 expires,Mandate_Lock[] commitments,Mandate mandate)Mandate_Fill(uint256 chainId,address tribunal,uint256 expires,Mandate_FillComponent[] components,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] priceCurve,Mandate_RecipientCallback[] recipientCallback,bytes32 salt)Mandate_FillComponent(address fillToken,uint256 minimumFillAmount,address recipient,bool applyScaling)Mandate_Lock(bytes12 lockTag,address token,uint256 amount)Mandate_RecipientCallback(uint256 chainId,Mandate_BatchCompact compact,bytes context";
Loading