diff --git a/images/TheCompact-Inheritance-Graph.png b/images/TheCompact-Inheritance-Graph.png index 1f0f2d6..b52515f 100644 Binary files a/images/TheCompact-Inheritance-Graph.png and b/images/TheCompact-Inheritance-Graph.png differ diff --git a/src/lib/ClaimProcessorLogic.sol b/src/lib/ClaimProcessorLogic.sol index d4e7129..9018fea 100644 --- a/src/lib/ClaimProcessorLogic.sol +++ b/src/lib/ClaimProcessorLogic.sol @@ -56,11 +56,11 @@ import { EfficiencyLib } from "./EfficiencyLib.sol"; import { FunctionCastLib } from "./FunctionCastLib.sol"; import { HashLib } from "./HashLib.sol"; import { IdLib } from "./IdLib.sol"; -import { RegistrationLogic } from "./RegistrationLogic.sol"; +import { RegistrationLib } from "./RegistrationLib.sol"; import { ValidityLib } from "./ValidityLib.sol"; import { SharedLogic } from "./SharedLogic.sol"; -contract ClaimProcessorLogic is SharedLogic, RegistrationLogic { +contract ClaimProcessorLogic is SharedLogic { using HashLib for address; using HashLib for bytes32; using HashLib for uint256; @@ -116,6 +116,7 @@ contract ClaimProcessorLogic is SharedLogic, RegistrationLogic { using EfficiencyLib for bool; using EfficiencyLib for bytes32; using EfficiencyLib for uint256; + using RegistrationLib for address; using ValidityLib for uint96; using ValidityLib for uint256; using ValidityLib for bytes32; @@ -527,7 +528,7 @@ contract ClaimProcessorLogic is SharedLogic, RegistrationLogic { sponsorDomainSeparator := add(sponsorDomainSeparator, mul(iszero(sponsorDomainSeparator), domainSeparator)) } - if ((sponsorDomainSeparator != domainSeparator).or(sponsorSignature.length != 0) || _hasNoActiveRegistration(sponsor, messageHash, typehash)) { + if ((sponsorDomainSeparator != domainSeparator).or(sponsorSignature.length != 0) || sponsor.hasNoActiveRegistration(messageHash, typehash)) { messageHash.signedBy(sponsor, sponsorSignature, sponsorDomainSeparator); } qualificationMessageHash.signedBy(allocator, allocatorSignature, domainSeparator); @@ -878,10 +879,6 @@ contract ClaimProcessorLogic is SharedLogic, RegistrationLogic { return true; } - function _hasNoActiveRegistration(address sponsor, bytes32 claimHash, bytes32 typehash) private view returns (bool) { - return _getRegistrationStatus(sponsor, claimHash, typehash) <= block.timestamp; - } - function _ensureValidScope(bytes32 sponsorDomainSeparator, uint256 id) private pure { assembly ("memory-safe") { if iszero(or(iszero(sponsorDomainSeparator), iszero(shr(255, id)))) { diff --git a/src/lib/DepositViaPermit2Logic.sol b/src/lib/DepositViaPermit2Logic.sol index 8b9ca8f..d1aba6c 100644 --- a/src/lib/DepositViaPermit2Logic.sol +++ b/src/lib/DepositViaPermit2Logic.sol @@ -43,7 +43,7 @@ import { ResetPeriod } from "../types/ResetPeriod.sol"; import { Scope } from "../types/Scope.sol"; import { DepositLogic } from "./DepositLogic.sol"; -import { RegistrationLogic } from "./RegistrationLogic.sol"; +import { RegistrationLib } from "./RegistrationLib.sol"; import { EfficiencyLib } from "./EfficiencyLib.sol"; import { IdLib } from "./IdLib.sol"; import { ValidityLib } from "./ValidityLib.sol"; @@ -51,12 +51,13 @@ import { ValidityLib } from "./ValidityLib.sol"; import { SafeTransferLib } from "solady/utils/SafeTransferLib.sol"; import { ISignatureTransfer } from "permit2/src/interfaces/ISignatureTransfer.sol"; -contract DepositViaPermit2Logic is DepositLogic, RegistrationLogic { +contract DepositViaPermit2Logic is DepositLogic { using IdLib for uint256; using IdLib for address; using IdLib for ResetPeriod; using EfficiencyLib for bool; using EfficiencyLib for uint256; + using RegistrationLib for address; using ValidityLib for address; using SafeTransferLib for address; @@ -109,7 +110,7 @@ contract DepositViaPermit2Logic is DepositLogic, RegistrationLogic { _checkBalanceAndDeposit(token, depositor, id, initialBalance); - _register(depositor, claimHash, compactTypehash, resetPeriod.toSeconds()); + depositor.registerCompact(claimHash, compactTypehash, resetPeriod); _clearReentrancyGuard(); @@ -175,7 +176,7 @@ contract DepositViaPermit2Logic is DepositLogic, RegistrationLogic { _verifyBalancesAndPerformDeposits(ids, permitted, initialTokenBalances, depositor, firstUnderlyingTokenIsNative); - _register(depositor, claimHash, compactTypehash, resetPeriod.toSeconds()); + depositor.registerCompact(claimHash, compactTypehash, resetPeriod); return ids; } diff --git a/src/lib/RegistrationLib.sol b/src/lib/RegistrationLib.sol new file mode 100644 index 0000000..d9fb187 --- /dev/null +++ b/src/lib/RegistrationLib.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.27; + +import { ResetPeriod } from "../types/ResetPeriod.sol"; + +import { EfficiencyLib } from "./EfficiencyLib.sol"; +import { IdLib } from "./IdLib.sol"; + +library RegistrationLib { + using RegistrationLib for address; + using EfficiencyLib for uint256; + using IdLib for ResetPeriod; + + /// @dev `keccak256(bytes("CompactRegistered(address,bytes32,bytes32,uint256)"))`. + uint256 private constant _COMPACT_REGISTERED_SIGNATURE = 0xf78a2f33ff80ef4391f7449c748dc2d577a62cd645108f4f4069f4a7e0635b6a; + + // slot: keccak256(_ACTIVE_REGISTRATIONS_SCOPE ++ sponsor ++ claimHash ++ typehash) => expires + uint256 private constant _ACTIVE_REGISTRATIONS_SCOPE = 0x68a30dd0; + + function registerCompactWithSpecificDuration(address sponsor, bytes32 claimHash, bytes32 typehash, uint256 duration) internal { + assembly ("memory-safe") { + let m := mload(0x40) + mstore(add(m, 0x14), sponsor) + mstore(m, _ACTIVE_REGISTRATIONS_SCOPE) + mstore(add(m, 0x34), claimHash) + mstore(add(m, 0x54), typehash) + let cutoffSlot := keccak256(add(m, 0x1c), 0x58) + + let expires := add(timestamp(), duration) + if or(lt(expires, sload(cutoffSlot)), gt(duration, 0x278d00)) { + // revert InvalidRegistrationDuration(uint256 duration) + mstore(0, 0x1f9a96f4) + mstore(0x20, duration) + revert(0x1c, 0x24) + } + + sstore(cutoffSlot, expires) + mstore(add(m, 0x74), expires) + log2(add(m, 0x34), 0x60, _COMPACT_REGISTERED_SIGNATURE, shr(0x60, shl(0x60, sponsor))) + } + } + + function registerCompact(address sponsor, bytes32 claimHash, bytes32 typehash, ResetPeriod duration) internal { + sponsor.registerCompactWithSpecificDuration(claimHash, typehash, duration.toSeconds()); + } + + function registerAsCallerWithDefaultDuration(bytes32 claimHash, bytes32 typehash) internal { + msg.sender.registerCompactWithSpecificDuration(claimHash, typehash, uint256(0x258).asStubborn()); + } + + function registerBatchAsCaller(bytes32[2][] calldata claimHashesAndTypehashes, uint256 duration) internal returns (bool) { + unchecked { + uint256 totalClaimHashes = claimHashesAndTypehashes.length; + for (uint256 i = 0; i < totalClaimHashes; ++i) { + bytes32[2] calldata claimHashAndTypehash = claimHashesAndTypehashes[i]; + msg.sender.registerCompactWithSpecificDuration(claimHashAndTypehash[0], claimHashAndTypehash[1], duration); + } + } + + return true; + } + + function toRegistrationExpiration(address sponsor, bytes32 claimHash, bytes32 typehash) internal view returns (uint256 expires) { + assembly ("memory-safe") { + let m := mload(0x40) + mstore(add(m, 0x14), sponsor) + mstore(m, _ACTIVE_REGISTRATIONS_SCOPE) + mstore(add(m, 0x34), claimHash) + mstore(add(m, 0x54), typehash) + expires := sload(keccak256(add(m, 0x1c), 0x58)) + } + } + + function hasNoActiveRegistration(address sponsor, bytes32 claimHash, bytes32 typehash) internal view returns (bool) { + return sponsor.toRegistrationExpiration(claimHash, typehash) <= block.timestamp; + } +} diff --git a/src/lib/RegistrationLogic.sol b/src/lib/RegistrationLogic.sol index 5166086..b6f2047 100644 --- a/src/lib/RegistrationLogic.sol +++ b/src/lib/RegistrationLogic.sol @@ -1,64 +1,26 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.27; -import { EfficiencyLib } from "./EfficiencyLib.sol"; +import { RegistrationLib } from "./RegistrationLib.sol"; contract RegistrationLogic { - using EfficiencyLib for uint256; - - /// @dev `keccak256(bytes("CompactRegistered(address,bytes32,bytes32,uint256)"))`. - uint256 private constant _COMPACT_REGISTERED_SIGNATURE = 0xf78a2f33ff80ef4391f7449c748dc2d577a62cd645108f4f4069f4a7e0635b6a; - - // slot: keccak256(_ACTIVE_REGISTRATIONS_SCOPE ++ sponsor ++ claimHash ++ typehash) => expires - uint256 private constant _ACTIVE_REGISTRATIONS_SCOPE = 0x68a30dd0; + using RegistrationLib for address; + using RegistrationLib for bytes32; + using RegistrationLib for bytes32[2][]; function _register(address sponsor, bytes32 claimHash, bytes32 typehash, uint256 duration) internal { - assembly ("memory-safe") { - let m := mload(0x40) - mstore(add(m, 0x14), sponsor) - mstore(m, _ACTIVE_REGISTRATIONS_SCOPE) - mstore(add(m, 0x34), claimHash) - mstore(add(m, 0x54), typehash) - let cutoffSlot := keccak256(add(m, 0x1c), 0x58) - - let expires := add(timestamp(), duration) - if or(lt(expires, sload(cutoffSlot)), gt(duration, 0x278d00)) { - // revert InvalidRegistrationDuration(uint256 duration) - mstore(0, 0x1f9a96f4) - mstore(0x20, duration) - revert(0x1c, 0x24) - } - - sstore(cutoffSlot, expires) - mstore(add(m, 0x74), expires) - log2(add(m, 0x34), 0x60, _COMPACT_REGISTERED_SIGNATURE, shr(0x60, shl(0x60, sponsor))) - } + sponsor.registerCompactWithSpecificDuration(claimHash, typehash, duration); } function _registerWithDefaults(bytes32 claimHash, bytes32 typehash) internal { - _register(msg.sender, claimHash, typehash, uint256(0x258).asStubborn()); + claimHash.registerAsCallerWithDefaultDuration(typehash); } function _registerBatch(bytes32[2][] calldata claimHashesAndTypehashes, uint256 duration) internal returns (bool) { - unchecked { - uint256 totalClaimHashes = claimHashesAndTypehashes.length; - for (uint256 i = 0; i < totalClaimHashes; ++i) { - bytes32[2] calldata claimHashAndTypehash = claimHashesAndTypehashes[i]; - _register(msg.sender, claimHashAndTypehash[0], claimHashAndTypehash[1], duration); - } - } - - return true; + return claimHashesAndTypehashes.registerBatchAsCaller(duration); } function _getRegistrationStatus(address sponsor, bytes32 claimHash, bytes32 typehash) internal view returns (uint256 expires) { - assembly ("memory-safe") { - let m := mload(0x40) - mstore(add(m, 0x14), sponsor) - mstore(m, _ACTIVE_REGISTRATIONS_SCOPE) - mstore(add(m, 0x34), claimHash) - mstore(add(m, 0x54), typehash) - expires := sload(keccak256(add(m, 0x1c), 0x58)) - } + return sponsor.toRegistrationExpiration(claimHash, typehash); } } diff --git a/src/lib/TheCompactLogic.sol b/src/lib/TheCompactLogic.sol index db8eabf..bcc1890 100644 --- a/src/lib/TheCompactLogic.sol +++ b/src/lib/TheCompactLogic.sol @@ -6,7 +6,8 @@ import { ClaimProcessor } from "./ClaimProcessor.sol"; import { DepositViaPermit2Logic } from "./DepositViaPermit2Logic.sol"; import { DirectDepositLogic } from "./DirectDepositLogic.sol"; import { Extsload } from "./Extsload.sol"; +import { RegistrationLogic } from "./RegistrationLogic.sol"; import { TransferLogic } from "./TransferLogic.sol"; import { WithdrawalLogic } from "./WithdrawalLogic.sol"; -contract TheCompactLogic is AllocatorLogic, ClaimProcessor, DepositViaPermit2Logic, DirectDepositLogic, Extsload, TransferLogic, WithdrawalLogic { } +contract TheCompactLogic is AllocatorLogic, ClaimProcessor, DepositViaPermit2Logic, DirectDepositLogic, Extsload, RegistrationLogic, TransferLogic, WithdrawalLogic { }