Skip to content

Commit

Permalink
extract basically all logic from top-level contract
Browse files Browse the repository at this point in the history
  • Loading branch information
0age committed Oct 31, 2024
1 parent 1c6f627 commit 2bcc570
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 229 deletions.
2 changes: 1 addition & 1 deletion snapshots/TheCompactTest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"depositBatchViaPermit2NativeAndERC20": "129592",
"depositBatchViaPermit2SingleERC20": "104746",
"depositERC20AndURI": "67117",
"depositERC20Basic": "67146",
"depositERC20Basic": "67119",
"depositERC20ViaPermit2AndURI": "98312",
"depositETHAndURI": "26777",
"depositETHBasic": "28384",
Expand Down
243 changes: 25 additions & 218 deletions src/TheCompact.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity ^0.8.27;

import { ITheCompact } from "./interfaces/ITheCompact.sol";
import { ITheCompactClaims } from "./interfaces/ITheCompactClaims.sol";

import { BatchTransfer, SplitBatchTransfer } from "./types/BatchClaims.sol";
import { BasicTransfer, SplitTransfer } from "./types/Claims.sol";
Expand All @@ -13,12 +12,7 @@ import { ResetPeriod } from "./types/ResetPeriod.sol";
import { ForcedWithdrawalStatus } from "./types/ForcedWithdrawalStatus.sol";

import { ClaimProcessor } from "./lib/ClaimProcessor.sol";
import { ConsumerLib } from "./lib/ConsumerLib.sol";
import { EfficiencyLib } from "./lib/EfficiencyLib.sol";
import { Extsload } from "./lib/Extsload.sol";
import { HashLib } from "./lib/HashLib.sol";
import { IdLib } from "./lib/IdLib.sol";
import { ValidityLib } from "./lib/ValidityLib.sol";

import { ERC6909 } from "solady/tokens/ERC6909.sol";
import { ISignatureTransfer } from "permit2/src/interfaces/ISignatureTransfer.sol";
Expand All @@ -28,33 +22,12 @@ import { ISignatureTransfer } from "permit2/src/interfaces/ISignatureTransfer.so
* @custom:version 0 (early-stage proof-of-concept)
* @author 0age (0age.eth)
* @notice The Compact is an ownerless ERC6909 contract that facilitates the voluntary
* formation (and, if necessary, involuntary dissolution) of "resource locks."
* formation and mediation of reusable "resource locks."
* This contract has not yet been properly tested, audited, or reviewed.
*/
contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
using HashLib for address;
using HashLib for bytes32;
using HashLib for uint256;
using HashLib for BasicTransfer;
using HashLib for SplitTransfer;
using HashLib for BatchTransfer;
using HashLib for SplitBatchTransfer;
using IdLib for uint96;
using IdLib for uint256;
using IdLib for address;
using IdLib for Lock;
using IdLib for ResetPeriod;
using IdLib for CompactCategory;
using ConsumerLib for uint256;
using EfficiencyLib for bool;
using EfficiencyLib for uint256;
using ValidityLib for address;
using ValidityLib for uint96;
using ValidityLib for uint256;
using ValidityLib for bytes32;

function deposit(address allocator) external payable returns (uint256 id) {
id = _performBasicNativeTokenDeposit(allocator);
function deposit(address allocator) external payable returns (uint256) {
return _performBasicNativeTokenDeposit(allocator);
}

function depositAndRegister(address allocator, bytes32 claimHash, bytes32 typehash) external payable returns (uint256 id) {
Expand All @@ -73,18 +46,12 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
_registerWithDefaults(claimHash, typehash);
}

function deposit(address allocator, ResetPeriod resetPeriod, Scope scope, address recipient) external payable returns (uint256 id) {
id = address(0).toIdIfRegistered(scope, resetPeriod, allocator);

_deposit(recipient, id, msg.value);
function deposit(address allocator, ResetPeriod resetPeriod, Scope scope, address recipient) external payable returns (uint256) {
return _performCustomNativeTokenDeposit(allocator, resetPeriod, scope, recipient);
}

function deposit(address token, address allocator, ResetPeriod resetPeriod, Scope scope, uint256 amount, address recipient) external returns (uint256 id) {
_setReentrancyGuard();
id = token.excludingNative().toIdIfRegistered(scope, resetPeriod, allocator);

_transferAndDeposit(token, recipient, id, amount);
_clearReentrancyGuard();
function deposit(address token, address allocator, ResetPeriod resetPeriod, Scope scope, uint256 amount, address recipient) external returns (uint256) {
return _performCustomERC20Deposit(token, allocator, resetPeriod, scope, amount, recipient);
}

function deposit(uint256[2][] calldata idsAndAmounts, address recipient) external payable returns (bool) {
Expand All @@ -96,9 +63,7 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
function depositAndRegister(uint256[2][] calldata idsAndAmounts, bytes32[2][] calldata claimHashesAndTypehashes, uint256 duration) external payable returns (bool) {
_processBatchDeposit(idsAndAmounts, msg.sender);

_registerBatch(claimHashesAndTypehashes, duration);

return true;
return _registerBatch(claimHashesAndTypehashes, duration);
}

function deposit(
Expand All @@ -113,23 +78,7 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
address recipient,
bytes calldata signature
) external returns (uint256) {
bytes32 witness = _deriveCompactDepositWitnessHash(uint256(0xa4).asStubborn());

(uint256 id, uint256 initialBalance, uint256 m, uint256 typestringMemoryLocation) = _setReentrancyLockAndStartPreparingPermit2Call(token);

_insertCompactDepositTypestringAt(typestringMemoryLocation);

assembly ("memory-safe") {
mstore(add(m, 0x100), witness)
}

_writeSignatureAndPerformPermit2Call(m, uint256(0x140).asStubborn(), uint256(0x200).asStubborn(), signature);

_checkBalanceAndDeposit(token, recipient, id, initialBalance);

_clearReentrancyGuard();

return id;
return _depositViaPermit2(token, recipient, signature);
}

function depositAndRegister(
Expand All @@ -146,26 +95,7 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
string calldata witness,
bytes calldata signature
) external returns (uint256) {
(uint256 id, uint256 initialBalance, uint256 m, uint256 typestringMemoryLocation) = _setReentrancyLockAndStartPreparingPermit2Call(token);

(bytes32 activationTypehash, bytes32 compactTypehash) = _writeWitnessAndGetTypehashes(typestringMemoryLocation, compactCategory, witness, bool(false).asStubborn());

_deriveAndWriteWitnessHash(activationTypehash, id, claimHash, m, 0x100);

uint256 signatureOffsetValue;
assembly ("memory-safe") {
signatureOffsetValue := and(add(mload(add(m, 0x160)), 0x17f), not(0x1f))
}

_writeSignatureAndPerformPermit2Call(m, uint256(0x140).asStubborn(), signatureOffsetValue, signature);

_checkBalanceAndDeposit(token, depositor, id, initialBalance);

_register(depositor, claimHash, compactTypehash, resetPeriod.toSeconds());

_clearReentrancyGuard();

return id;
return _depositAndRegisterViaPermit2(token, depositor, resetPeriod, claimHash, compactCategory, witness, signature);
}

function deposit(
Expand All @@ -179,28 +109,7 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
address recipient,
bytes calldata signature
) external payable returns (uint256[] memory) {
(uint256 totalTokensLessInitialNative, bool firstUnderlyingTokenIsNative, uint256[] memory ids, uint256[] memory initialTokenBalances) =
_preprocessAndPerformInitialNativeDeposit(permitted, recipient);

bytes32 witness = _deriveCompactDepositWitnessHash(uint256(0x84).asStubborn());

(uint256 m, uint256 typestringMemoryLocation) = _beginPreparingBatchDepositPermit2Calldata(totalTokensLessInitialNative, firstUnderlyingTokenIsNative);

unchecked {
_insertCompactDepositTypestringAt(typestringMemoryLocation);
}

uint256 signatureOffsetValue;
assembly ("memory-safe") {
mstore(add(m, 0x80), witness)
signatureOffsetValue := add(0x220, shl(7, totalTokensLessInitialNative))
}

_writeSignatureAndPerformPermit2Call(m, uint256(0xc0).asStubborn(), signatureOffsetValue, signature);

_verifyBalancesAndPerformDeposits(ids, permitted, initialTokenBalances, recipient, firstUnderlyingTokenIsNative);

return ids;
return _depositBatchViaPermit2(permitted, recipient, signature);
}

function depositAndRegister(
Expand All @@ -216,34 +125,7 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
string calldata witness,
bytes calldata signature
) external payable returns (uint256[] memory) {
(uint256 totalTokensLessInitialNative, bool firstUnderlyingTokenIsNative, uint256[] memory ids, uint256[] memory initialTokenBalances) =
_preprocessAndPerformInitialNativeDeposit(permitted, depositor);

uint256 idsHash;
assembly ("memory-safe") {
idsHash := keccak256(add(ids, 0x20), shl(5, add(totalTokensLessInitialNative, firstUnderlyingTokenIsNative)))
}

(uint256 m, uint256 typestringMemoryLocation) = _beginPreparingBatchDepositPermit2Calldata(totalTokensLessInitialNative, firstUnderlyingTokenIsNative);

(bytes32 activationTypehash, bytes32 compactTypehash) = _writeWitnessAndGetTypehashes(typestringMemoryLocation, compactCategory, witness, bool(true).asStubborn());

_deriveAndWriteWitnessHash(activationTypehash, idsHash, claimHash, m, 0x80);

uint256 signatureOffsetValue;
assembly ("memory-safe") {
let witnessLength := witness.length
let totalWitnessMemoryOffset := and(add(add(0xf3, add(witnessLength, iszero(iszero(witnessLength)))), add(mul(eq(compactCategory, 1), 0x0b), shl(6, eq(compactCategory, 2)))), not(0x1f))
signatureOffsetValue := add(add(0x180, shl(7, totalTokensLessInitialNative)), totalWitnessMemoryOffset)
}

_writeSignatureAndPerformPermit2Call(m, uint256(0xc0).asStubborn(), signatureOffsetValue, signature);

_verifyBalancesAndPerformDeposits(ids, permitted, initialTokenBalances, depositor, firstUnderlyingTokenIsNative);

_register(depositor, claimHash, compactTypehash, resetPeriod.toSeconds());

return ids;
return _depositBatchAndRegisterViaPermit2(depositor, permitted, resetPeriod, claimHash, compactCategory, witness, signature);
}

function allocatedTransfer(BasicTransfer calldata transfer) external returns (bool) {
Expand Down Expand Up @@ -278,54 +160,16 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
return _processSplitBatchTransfer(withdrawal, _withdraw);
}

function enableForcedWithdrawal(uint256 id) external returns (uint256 withdrawableAt) {
// overflow check not necessary as reset period is capped
unchecked {
withdrawableAt = block.timestamp + id.toResetPeriod().toSeconds();
}

uint256 cutoffTimeSlotLocation = _getCutoffTimeSlot(msg.sender, id);
assembly ("memory-safe") {
sstore(cutoffTimeSlotLocation, withdrawableAt)
}

_emitForcedWithdrawalStatusUpdatedEvent(id, withdrawableAt);
function enableForcedWithdrawal(uint256 id) external returns (uint256) {
return _enableForcedWithdrawal(id);
}

function disableForcedWithdrawal(uint256 id) external returns (bool) {
uint256 cutoffTimeSlotLocation = _getCutoffTimeSlot(msg.sender, id);

assembly ("memory-safe") {
if iszero(sload(cutoffTimeSlotLocation)) {
// revert ForcedWithdrawalAlreadyDisabled(msg.sender, id)
mstore(0, 0xe632dbad)
mstore(0x20, caller())
mstore(0x40, id)
revert(0x1c, 0x44)
}

sstore(cutoffTimeSlotLocation, 0)
}

_emitForcedWithdrawalStatusUpdatedEvent(id, uint256(0).asStubborn());

return true;
return _disableForcedWithdrawal(id);
}

function forcedWithdrawal(uint256 id, address recipient, uint256 amount) external returns (bool) {
uint256 cutoffTimeSlotLocation = _getCutoffTimeSlot(msg.sender, id);

assembly ("memory-safe") {
let withdrawableAt := sload(cutoffTimeSlotLocation)
if or(iszero(withdrawableAt), gt(withdrawableAt, timestamp())) {
// revert PrematureWithdrawal(id)
mstore(0, 0x9287bcb0)
mstore(0x20, id)
revert(0x1c, 0x24)
}
}

return _withdraw(msg.sender, recipient, id, amount);
return _processForcedWithdrawal(id, recipient, amount);
}

function register(bytes32 claimHash, bytes32 typehash, uint256 duration) external returns (bool) {
Expand All @@ -343,60 +187,23 @@ contract TheCompact is ITheCompact, ClaimProcessor, ERC6909, Extsload {
}

function consume(uint256[] calldata nonces) external returns (bool) {
// NOTE: this may not be necessary, consider removing
msg.sender.usingAllocatorId().mustHaveARegisteredAllocator();

unchecked {
uint256 i;

assembly ("memory-safe") {
i := nonces.offset
}

uint256 end = i + (nonces.length << 5);
uint256 nonce;
for (; i < end; i += 0x20) {
assembly ("memory-safe") {
nonce := calldataload(i)
}
nonce.consumeNonceAsAllocator(msg.sender);
}
}

return true;
return _consume(nonces);
}

function __registerAllocator(address allocator, bytes calldata proof) external returns (uint96 allocatorId) {
allocator = uint256(uint160(allocator)).asSanitizedAddress();
if (!allocator.canBeRegistered(proof)) {
assembly ("memory-safe") {
// revert InvalidRegistrationProof(allocator)
mstore(0, 0x4e7f492b)
mstore(0x20, allocator)
revert(0x1c, 0x24)
}
}

allocatorId = allocator.register();
function __registerAllocator(address allocator, bytes calldata proof) external returns (uint96) {
return _registerAllocator(allocator, proof);
}

function getForcedWithdrawalStatus(address account, uint256 id) external view returns (ForcedWithdrawalStatus status, uint256 forcedWithdrawalAvailableAt) {
uint256 cutoffTimeSlotLocation = _getCutoffTimeSlot(account, id);
assembly ("memory-safe") {
forcedWithdrawalAvailableAt := sload(cutoffTimeSlotLocation)
status := mul(iszero(iszero(forcedWithdrawalAvailableAt)), sub(2, gt(forcedWithdrawalAvailableAt, timestamp())))
}
function getForcedWithdrawalStatus(address account, uint256 id) external view returns (ForcedWithdrawalStatus, uint256) {
return _getForcedWithdrawalStatus(account, id);
}

function getLockDetails(uint256 id) external view returns (address token, address allocator, ResetPeriod resetPeriod, Scope scope) {
token = id.toToken();
allocator = id.toAllocatorId().toRegisteredAllocator();
resetPeriod = id.toResetPeriod();
scope = id.toScope();
function getLockDetails(uint256 id) external view returns (address, address, ResetPeriod, Scope) {
return _getLockDetails(id);
}

function hasConsumedAllocatorNonce(uint256 nonce, address allocator) external view returns (bool consumed) {
consumed = allocator.hasConsumedAllocatorNonce(nonce);
function hasConsumedAllocatorNonce(uint256 nonce, address allocator) external view returns (bool) {
return _hasConsumedAllocatorNonce(nonce, allocator);
}

function DOMAIN_SEPARATOR() external view returns (bytes32) {
Expand Down
Loading

0 comments on commit 2bcc570

Please sign in to comment.