Skip to content

Commit

Permalink
Merge pull request #16 from Uniswap/more-natspec
Browse files Browse the repository at this point in the history
start adding comments + natspec to functions and add ClaimProcessorLib
  • Loading branch information
0age authored Nov 2, 2024
2 parents 4663588 + 591a1b3 commit 2713e22
Show file tree
Hide file tree
Showing 4 changed files with 473 additions and 246 deletions.
28 changes: 14 additions & 14 deletions snapshots/TheCompactTest.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"basicTransfer": "56858",
"basicWithdrawal": "59816",
"batchClaim": "111828",
"batchClaimRegisteredWithDeposit": "111828",
"batchClaimRegisteredWithDepositWithWitness": "112590",
"batchClaimWithWitness": "112584",
"batchClaim": "111913",
"batchClaimRegisteredWithDeposit": "111913",
"batchClaimRegisteredWithDepositWithWitness": "112675",
"batchClaimWithWitness": "112669",
"batchDepositAndRegisterViaPermit2": "221900",
"batchDepositAndRegisterWithWitnessViaPermit2": "221878",
"batchTransfer": "81536",
Expand All @@ -22,21 +22,21 @@
"depositERC20ViaPermit2AndURI": "98312",
"depositETHAndURI": "26777",
"depositETHBasic": "28384",
"qualifiedBatchClaim": "113246",
"qualifiedBatchClaimWithWitness": "112710",
"qualifiedBatchClaim": "113331",
"qualifiedBatchClaimWithWitness": "112795",
"qualifiedClaim": "60234",
"qualifiedClaimWithWitness": "58774",
"qualifiedSplitBatchClaim": "140934",
"qualifiedSplitBatchClaimWithWitness": "140926",
"qualifiedSplitClaim": "86521",
"qualifiedSplitClaimWithWitness": "86887",
"qualifiedSplitBatchClaim": "140913",
"qualifiedSplitBatchClaimWithWitness": "140905",
"qualifiedSplitClaim": "86518",
"qualifiedSplitClaimWithWitness": "86884",
"register": "25379",
"splitBatchClaim": "140396",
"splitBatchClaimWithWitness": "140360",
"splitBatchClaim": "140375",
"splitBatchClaimWithWitness": "140339",
"splitBatchTransfer": "110611",
"splitBatchWithdrawal": "139819",
"splitClaim": "86444",
"splitClaimWithWitness": "85925",
"splitClaim": "86441",
"splitClaimWithWitness": "85922",
"splitTransfer": "82748",
"splitWithdrawal": "93702"
}
30 changes: 30 additions & 0 deletions src/lib/AllocatorLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ contract AllocatorLogic {
using EfficiencyLib for uint256;
using ValidityLib for address;

/**
* @notice Internal function for marking allocator nonces as consumed. Once consumed, a nonce
* cannot be reused to claim resource locks referencing that allocator. Called by the external
* consume function and during claim processing to prevent replay attacks.
* @param nonces Array of nonces to mark as consumed for the calling allocator.
* @return Whether all nonces were successfully marked as consumed.
*/
function _consume(uint256[] calldata nonces) internal returns (bool) {
// NOTE: this may not be necessary, consider removing
msg.sender.usingAllocatorId().mustHaveARegisteredAllocator();
Expand All @@ -47,6 +54,14 @@ contract AllocatorLogic {
return true;
}

/**
* @notice Internal function for registering an allocator. Validates that one of three
* conditions is met: caller is the allocator address, allocator address contains code, or
* proof represents valid create2 deployment parameters that derive the allocator address.
* @param allocator The address to register as an allocator.
* @param proof An 85-byte value containing create2 address derivation parameters.
* @return allocatorId A unique identifier assigned to the registered allocator.
*/
function _registerAllocator(address allocator, bytes calldata proof) internal returns (uint96 allocatorId) {
allocator = uint256(uint160(allocator)).asSanitizedAddress();
if (!allocator.canBeRegistered(proof)) {
Expand All @@ -61,10 +76,25 @@ contract AllocatorLogic {
allocatorId = allocator.register();
}

/**
* @notice Internal view function for checking whether a specific nonce has been consumed by
* an allocator.
* @param nonce The nonce to check.
* @param allocator The address of the allocator.
* @return Whether the nonce has been consumed.
*/
function _hasConsumedAllocatorNonce(uint256 nonce, address allocator) internal view returns (bool) {
return allocator.hasConsumedAllocatorNonce(nonce);
}

/**
* @notice Internal view function for retrieving the details of a resource lock.
* @param id The ERC6909 token identifier for the resource lock.
* @return token The address of the underlying token (or address(0) for native tokens).
* @return allocator The address of the allocator mediating the resource lock.
* @return resetPeriod The duration after which the underlying tokens can be withdrawn once a forced withdrawal is initiated.
* @return scope The scope of the resource lock (multichain or single chain).
*/
function _getLockDetails(uint256 id) internal view returns (address token, address allocator, ResetPeriod resetPeriod, Scope scope) {
token = id.toToken();
allocator = id.toAllocatorId().toRegisteredAllocator();
Expand Down
142 changes: 142 additions & 0 deletions src/lib/ClaimProcessorLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;

import { COMPACT_TYPEHASH, BATCH_COMPACT_TYPEHASH, MULTICHAIN_COMPACT_TYPEHASH } from "../types/EIP712Types.sol";
import { SplitComponent } from "../types/Components.sol";
import { Scope } from "../types/Scope.sol";

import { EfficiencyLib } from "./EfficiencyLib.sol";
import { IdLib } from "./IdLib.sol";

/**
* @title ClaimProcessorLib
* @notice Library contract implementing internal functions with helper logic for
* processing claims against a signed or registered compact.
*/
library ClaimProcessorLib {
using ClaimProcessorLib for uint256;
using EfficiencyLib for bool;
using IdLib for uint256;

/**
* @notice Internal function for verifying and processing split components. Ensures that the
* sum of split amounts doesn't exceed the allocated amount, checks for arithmetic overflow,
* and executes the specified operation for each split recipient. Reverts if the total
* claimed amount exceeds the allocation or if arithmetic overflow occurs during summation.
* @param claimants Array of split components specifying recipients and their amounts.
* @param sponsor The address of the claim sponsor.
* @param id The ERC6909 token identifier of the resource lock.
* @param allocatedAmount The total amount allocated for this claim.
* @param operation Function pointer to either _release or _withdraw for executing the claim.
* @return Whether all split components were successfully processed.
*/
function verifyAndProcessSplitComponents(
SplitComponent[] calldata claimants,
address sponsor,
uint256 id,
uint256 allocatedAmount,
function(address, address, uint256, uint256) internal returns (bool) operation
) internal returns (bool) {
// Initialize tracking variables.
uint256 totalClaims = claimants.length;
uint256 spentAmount = 0;
uint256 errorBuffer = (totalClaims == 0).asUint256();

unchecked {
// Process each split component while tracking total amount and checking for overflow.
for (uint256 i = 0; i < totalClaims; ++i) {
SplitComponent calldata component = claimants[i];
uint256 amount = component.amount;

// Track total amount claimed, checking for overflow.
uint256 updatedSpentAmount = amount + spentAmount;
errorBuffer |= (updatedSpentAmount < spentAmount).asUint256();
spentAmount = updatedSpentAmount;

// Execute transfer or withdrawal for the split component.
operation(sponsor, component.claimant, id, amount);
}
}

// Revert if an overflow occurred or if total claimed amount exceeds allocation.
errorBuffer |= (allocatedAmount < spentAmount).asUint256();
assembly ("memory-safe") {
if errorBuffer {
// revert AllocatedAmountExceeded(allocatedAmount, amount);
mstore(0, 0x3078b2f6)
mstore(0x20, allocatedAmount)
mstore(0x40, spentAmount)
revert(0x1c, 0x44)
}
}

return true;
}

/**
* @notice Internal pure function for validating that a resource lock's scope is compatible
* with the provided sponsor domain separator. Reverts if an exogenous claim (indicated by
* a non-zero sponsor domain separator) attempts to claim against a chain-specific resource
* lock (indicated by the most significant bit of the id).
* @param sponsorDomainSeparator The domain separator for the sponsor's signature, or zero for non-exogenous claims.
* @param id The ERC6909 token identifier of the resource lock.
*/
function ensureValidScope(bytes32 sponsorDomainSeparator, uint256 id) internal pure {
assembly ("memory-safe") {
if iszero(or(iszero(sponsorDomainSeparator), iszero(shr(255, id)))) {
// revert InvalidScope(id)
mstore(0, 0xa06356f5)
mstore(0x20, id)
revert(0x1c, 0x24)
}
}
}

/**
* @notice Internal pure function for retrieving EIP-712 typehashes where no witness data is
* provided, returning the corresponding typehash based on the index provided. The available
* typehashes are:
* - 0: COMPACT_TYPEHASH
* - 1: BATCH_COMPACT_TYPEHASH
* - 2: MULTICHAIN_COMPACT_TYPEHASH
* @param i The index of the EIP-712 typehash to retrieve.
* @return typehash The corresponding EIP-712 typehash.
*/
function typehashes(uint256 i) internal pure returns (bytes32 typehash) {
assembly ("memory-safe") {
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)
}
}

/**
* @notice Internal function for determining if a resource lock has chain-specific scope
* in the context of an exogenous claim. Returns true if the claim is exogenous (indicated
* by non-zero sponsor domain separator) and the resource lock is chain-specific.
* @param id The ERC6909 token identifier of the resource lock.
* @param sponsorDomainSeparator The domain separator for the sponsor's signature, or zero for non-exogenous claims.
* @return Whether the resource lock's scope is incompatible with the claim context.
*/
function scopeNotMultichain(uint256 id, bytes32 sponsorDomainSeparator) internal pure returns (bool) {
return (sponsorDomainSeparator != bytes32(0)).and(id.toScope() == Scope.ChainSpecific);
}

/**
* @notice Internal function that combines two claim validations: whether the amount exceeds
* allocation and whether the resource lock's scope is compatible with the claim context.
* Returns true if either the allocated amount is exceeded or if the claim is exogenous but
* the resource lock is chain-specific.
* @param allocatedAmount The total amount allocated for the claim.
* @param amount The amount being claimed.
* @param id The ERC6909 token identifier of the resource lock.
* @param sponsorDomainSeparator The domain separator for the sponsor's signature, or zero for non-exogenous claims.
* @return Whether either validation fails.
*/
function allocationExceededOrScopeNotMultichain(uint256 allocatedAmount, uint256 amount, uint256 id, bytes32 sponsorDomainSeparator) internal pure returns (bool) {
return (allocatedAmount < amount).or(id.scopeNotMultichain(sponsorDomainSeparator));
}
}
Loading

0 comments on commit 2713e22

Please sign in to comment.