Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 9 from a total of 9 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Create OW Recipi... | 21131449 | 31 days ago | IN | 0 ETH | 0.00203613 | ||||
Create OW Recipi... | 20610254 | 104 days ago | IN | 0 ETH | 0.00007575 | ||||
Create OW Recipi... | 20565657 | 110 days ago | IN | 0 ETH | 0.00009649 | ||||
Create OW Recipi... | 20547657 | 113 days ago | IN | 0 ETH | 0.00011135 | ||||
Create OW Recipi... | 20463224 | 124 days ago | IN | 0 ETH | 0.00297372 | ||||
Create OW Recipi... | 20373898 | 137 days ago | IN | 0 ETH | 0.00052442 | ||||
Create OW Recipi... | 20267928 | 152 days ago | IN | 0 ETH | 0.0003788 | ||||
Create OW Recipi... | 19841506 | 211 days ago | IN | 0 ETH | 0.00095463 | ||||
Create OW Recipi... | 18442464 | 407 days ago | IN | 0 ETH | 0.00375847 |
Latest 25 internal transactions (View All)
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
OptimisticWithdrawalRecipientFactory
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.19; import {OptimisticWithdrawalRecipient} from "./OptimisticWithdrawalRecipient.sol"; import {LibClone} from "solady/utils/LibClone.sol"; import {IENSReverseRegistrar} from "../interfaces/IENSReverseRegistrar.sol"; /// @title OptimisticWithdrawalRecipientFactory /// @author Obol /// @notice A factory contract for cheaply deploying /// OptimisticWithdrawalRecipient. /// @dev This contract uses token = address(0) to refer to ETH. contract OptimisticWithdrawalRecipientFactory { /// ----------------------------------------------------------------------- /// errors /// ----------------------------------------------------------------------- /// Invalid number of recipients, must be 2 error Invalid__Recipients(); /// Thresholds must be positive error Invalid__ZeroThreshold(); /// Invalid threshold at `index`; must be < 2^96 /// @param threshold threshold of too-large threshold error Invalid__ThresholdTooLarge(uint256 threshold); /// ----------------------------------------------------------------------- /// libraries /// ----------------------------------------------------------------------- using LibClone for address; /// ----------------------------------------------------------------------- /// events /// ----------------------------------------------------------------------- /// Emitted after a new OptimisticWithdrawalRecipient module is deployed /// @param owr Address of newly created OptimisticWithdrawalRecipient clone /// @param recoveryAddress Address to recover non-OWR tokens to /// @param principalRecipient Address to distribute principal payment to /// @param rewardRecipient Address to distribute reward payment to /// @param threshold Absolute payment threshold for OWR first recipient /// (reward recipient has no threshold & receives all residual flows) event CreateOWRecipient( address indexed owr, address recoveryAddress, address principalRecipient, address rewardRecipient, uint256 threshold ); /// ----------------------------------------------------------------------- /// storage /// ----------------------------------------------------------------------- uint256 internal constant ADDRESS_BITS = 160; /// OptimisticWithdrawalRecipient implementation address OptimisticWithdrawalRecipient public immutable owrImpl; /// ----------------------------------------------------------------------- /// constructor /// ----------------------------------------------------------------------- constructor( string memory _ensName, address _ensReverseRegistrar, address _ensOwner ) { owrImpl = new OptimisticWithdrawalRecipient(); IENSReverseRegistrar(_ensReverseRegistrar).setName(_ensName); IENSReverseRegistrar(_ensReverseRegistrar).claim(_ensOwner); } /// ----------------------------------------------------------------------- /// functions /// ----------------------------------------------------------------------- /// ----------------------------------------------------------------------- /// functions - public & external /// ----------------------------------------------------------------------- /// Create a new OptimisticWithdrawalRecipient clone /// @param recoveryAddress Address to recover tokens to /// If this address is 0x0, recovery of unrelated tokens can be completed by /// either the principal or reward recipients. If this address is set, only /// this address can recover /// tokens (or ether) that isn't the token of the OWRecipient contract /// @param principalRecipient Address to distribute principal payments to /// @param rewardRecipient Address to distribute reward payments to /// @param amountOfPrincipalStake Absolute amount of stake to be paid to /// principal recipient (multiple of 32 ETH) /// (reward recipient has no threshold & receives all residual flows) /// it cannot be greater than uint96 /// @return owr Address of new OptimisticWithdrawalRecipient clone function createOWRecipient( address recoveryAddress, address principalRecipient, address rewardRecipient, uint256 amountOfPrincipalStake ) external returns (OptimisticWithdrawalRecipient owr) { /// checks // ensure doesn't have address(0) if (principalRecipient == address(0) || rewardRecipient == address(0)) revert Invalid__Recipients(); // ensure threshold isn't zero if (amountOfPrincipalStake == 0) revert Invalid__ZeroThreshold(); // ensure threshold isn't too large if (amountOfPrincipalStake > type(uint96).max) revert Invalid__ThresholdTooLarge(amountOfPrincipalStake); /// effects uint256 principalData = (amountOfPrincipalStake << ADDRESS_BITS) | uint256(uint160(principalRecipient)); uint256 rewardData = uint256(uint160(rewardRecipient)); // would not exceed contract size limits // important to not reorder bytes memory data = abi.encodePacked(recoveryAddress, principalData, rewardData); owr = OptimisticWithdrawalRecipient(address(owrImpl).clone(data)); emit CreateOWRecipient(address(owr), recoveryAddress, principalRecipient, rewardRecipient, amountOfPrincipalStake); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.19; import {Clone} from "solady/utils/Clone.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; /// @title OptimisticWithdrawalRecipient /// @author Obol /// @notice A maximally-composable contract that distributes payments /// based on threshold to it's recipients /// @dev Only ETH can be distributed for a given deployment. There is a /// recovery method for tokens sent by accident. contract OptimisticWithdrawalRecipient is Clone { /// ----------------------------------------------------------------------- /// libraries /// ----------------------------------------------------------------------- using SafeTransferLib for address; /// ----------------------------------------------------------------------- /// errors /// ----------------------------------------------------------------------- /// Invalid token recovery recipient error InvalidTokenRecovery_InvalidRecipient(); /// Invalid distribution error InvalidDistribution_TooLarge(); /// ----------------------------------------------------------------------- /// events /// ----------------------------------------------------------------------- /// Emitted after each successful ETH transfer to proxy /// @param amount Amount of ETH received /// @dev embedded in & emitted from clone bytecode event ReceiveETH(uint256 amount); /// Emitted after funds are distributed to recipients /// @param principalPayout Amount of principal paid out /// @param rewardPayout Amount of reward paid out /// @param pullFlowFlag Flag for pushing funds to recipients or storing for /// pulling event DistributeFunds(uint256 principalPayout, uint256 rewardPayout, uint256 pullFlowFlag); /// Emitted after tokens are recovered to a recipient /// @param recoveryAddressToken Recovered token (cannot be /// ETH) /// @param recipient Address receiving recovered token /// @param amount Amount of recovered token event RecoverNonOWRecipientFunds(address recoveryAddressToken, address recipient, uint256 amount); /// Emitted after funds withdrawn using pull flow /// @param account Account withdrawing funds for /// @param amount Amount withdrawn event Withdrawal(address account, uint256 amount); /// ----------------------------------------------------------------------- /// storage /// ----------------------------------------------------------------------- /// ----------------------------------------------------------------------- /// storage - constants /// ----------------------------------------------------------------------- uint256 internal constant PUSH = 0; uint256 internal constant PULL = 1; uint256 internal constant ONE_WORD = 32; uint256 internal constant ADDRESS_BITS = 160; /// @dev threshold for pushing balance update as reward or principal uint256 internal constant BALANCE_CLASSIFICATION_THRESHOLD = 16 ether; uint256 internal constant PRINCIPAL_RECIPIENT_INDEX = 0; uint256 internal constant REWARD_RECIPIENT_INDEX = 1; /// ----------------------------------------------------------------------- /// storage - cwia offsets /// ----------------------------------------------------------------------- // recoveryAddress (address, 20 bytes), // tranches (uint256[], numTranches * 32 bytes) // 0; first item uint256 internal constant RECOVERY_ADDRESS_OFFSET = 0; // 20 = recoveryAddress_offset (0) + recoveryAddress_size (address, 20 // bytes) uint256 internal constant TRANCHES_OFFSET = 20; /// Address to recover non-OWR tokens to /// @dev equivalent to address public immutable recoveryAddress; function recoveryAddress() public pure returns (address) { return _getArgAddress(RECOVERY_ADDRESS_OFFSET); } /// Get OWR tranche `i` /// @dev emulates to uint256[] internal immutable tranche; function _getTranche(uint256 i) internal pure returns (uint256) { unchecked { // shouldn't overflow return _getArgUint256(TRANCHES_OFFSET + i * ONE_WORD); } } /// ----------------------------------------------------------------------- /// storage - mutables /// ----------------------------------------------------------------------- /// Amount of active balance set aside for pulls /// @dev ERC20s with very large decimals may overflow & cause issues uint128 public fundsPendingWithdrawal; /// Amount of distributed OWRecipient token for principal /// @dev Would be less than or equal to amount of stake /// @dev ERC20s with very large decimals may overflow & cause issues uint128 public claimedPrincipalFunds; /// Mapping to account balances for pulling mapping(address => uint256) internal pullBalances; /// ----------------------------------------------------------------------- /// constructor /// ----------------------------------------------------------------------- // solhint-disable-next-line no-empty-blocks /// clone implementation doesn't use constructor constructor() {} /// ----------------------------------------------------------------------- /// functions /// ----------------------------------------------------------------------- /// ----------------------------------------------------------------------- /// functions - public & external /// ----------------------------------------------------------------------- /// emit event when receiving ETH /// @dev implemented w/i clone bytecode /* receive() external payable { */ /* emit ReceiveETH(msg.value); */ /* } */ /// Distributes target token inside the contract to recipients /// @dev pushes funds to recipients function distributeFunds() external payable { _distributeFunds(PUSH); } /// Distributes target token inside the contract to recipients /// @dev backup recovery if any recipient tries to brick the OWRecipient for /// remaining recipients function distributeFundsPull() external payable { _distributeFunds(PULL); } /// Recover non-OWR tokens to a recipient /// @param nonOWRToken Token to recover (cannot be OWR token) /// @param recipient Address to receive recovered token function recoverFunds(address nonOWRToken, address recipient) external payable { /// checks // if recoveryAddress is set, recipient must match it // else, recipient must be one of the OWR recipients address _recoveryAddress = recoveryAddress(); if (_recoveryAddress == address(0)) { // ensure txn recipient is a valid OWR recipient (address principalRecipient, address rewardRecipient,) = getTranches(); if (recipient != principalRecipient && recipient != rewardRecipient) { revert InvalidTokenRecovery_InvalidRecipient(); } } else if (recipient != _recoveryAddress) { revert InvalidTokenRecovery_InvalidRecipient(); } /// effects /// interactions // recover non-target token uint256 amount = ERC20(nonOWRToken).balanceOf(address(this)); nonOWRToken.safeTransfer(recipient, amount); emit RecoverNonOWRecipientFunds(nonOWRToken, recipient, amount); } /// Withdraw token balance for account `account` /// @param account Address to withdraw on behalf of function withdraw(address account) external { uint256 tokenAmount = pullBalances[account]; unchecked { // shouldn't underflow; fundsPendingWithdrawal = sum(pullBalances) fundsPendingWithdrawal -= uint128(tokenAmount); } pullBalances[account] = 0; account.safeTransferETH(tokenAmount); emit Withdrawal(account, tokenAmount); } /// ----------------------------------------------------------------------- /// functions - view & pure /// ----------------------------------------------------------------------- /// Return unpacked tranches /// @return principalRecipient Addres of principal recipient /// @return rewardRecipient Address of reward recipient /// @return amountOfPrincipalStake Absolute payment threshold for principal function getTranches() public pure returns (address principalRecipient, address rewardRecipient, uint256 amountOfPrincipalStake) { uint256 tranche = _getTranche(PRINCIPAL_RECIPIENT_INDEX); principalRecipient = address(uint160(tranche)); amountOfPrincipalStake = tranche >> ADDRESS_BITS; rewardRecipient = address(uint160(_getTranche(REWARD_RECIPIENT_INDEX))); } /// Returns the balance for account `account` /// @param account Account to return balance for /// @return Account's balance OWR token function getPullBalance(address account) external view returns (uint256) { return pullBalances[account]; } /// ----------------------------------------------------------------------- /// functions - private & internal /// ----------------------------------------------------------------------- /// Distributes target token inside the contract to next-in-line recipients /// @dev can PUSH or PULL funds to recipients function _distributeFunds(uint256 pullFlowFlag) internal { /// checks /// effects // load storage into memory uint256 currentbalance = address(this).balance; uint256 _claimedPrincipalFunds = uint256(claimedPrincipalFunds); uint256 _memoryFundsPendingWithdrawal = uint256(fundsPendingWithdrawal); uint256 _fundsToBeDistributed = currentbalance - _memoryFundsPendingWithdrawal; (address principalRecipient, address rewardRecipient, uint256 amountOfPrincipalStake) = getTranches(); // determine which recipeint is getting paid based on funds to be // distributed uint256 _principalPayout = 0; uint256 _rewardPayout = 0; unchecked { // _claimedPrincipalFunds should always be <= amountOfPrincipalStake uint256 principalStakeRemaining = amountOfPrincipalStake - _claimedPrincipalFunds; if (_fundsToBeDistributed >= BALANCE_CLASSIFICATION_THRESHOLD && principalStakeRemaining > 0) { if (_fundsToBeDistributed > principalStakeRemaining) { // this means there is reward part of the funds to be // distributed _principalPayout = principalStakeRemaining; // shouldn't underflow _rewardPayout = _fundsToBeDistributed - principalStakeRemaining; } else { // this means there is no reward part of the funds to be // distributed _principalPayout = _fundsToBeDistributed; } } else { _rewardPayout = _fundsToBeDistributed; } } { if (_fundsToBeDistributed > type(uint128).max) revert InvalidDistribution_TooLarge(); // Write to storage // the principal value // it cannot overflow because _principalPayout < _fundsToBeDistributed if (_principalPayout > 0) claimedPrincipalFunds += uint128(_principalPayout); } /// interactions // pay outs // earlier tranche recipients may try to re-enter but will cause fn to // revert // when later external calls fail (bc balance is emptied early) // pay out principal _payout(principalRecipient, _principalPayout, pullFlowFlag); // pay out reward _payout(rewardRecipient, _rewardPayout, pullFlowFlag); if (pullFlowFlag == PULL) { if (_principalPayout > 0 || _rewardPayout > 0) { // Write to storage fundsPendingWithdrawal = uint128(_memoryFundsPendingWithdrawal + _principalPayout + _rewardPayout); } } emit DistributeFunds(_principalPayout, _rewardPayout, pullFlowFlag); } function _payout(address recipient, uint256 payoutAmount, uint256 pullFlowFlag) internal { if (payoutAmount > 0) { if (pullFlowFlag == PULL) { // Write to Storage pullBalances[recipient] += payoutAmount; } else { recipient.safeTransferETH(payoutAmount); } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Minimal proxy library. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol) /// @author Minimal proxy by 0age (https://github.com/0age) /// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie /// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) /// /// @dev Minimal proxy: /// Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime, /// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern, /// which saves 4 gas over the erc-1167 pattern during runtime, and has the smallest bytecode. /// /// @dev Minimal proxy (PUSH0 variant): /// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai. /// It is optimized first for minimal runtime gas, then for minimal bytecode. /// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as /// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai. /// Please use with caution. /// /// @dev Clones with immutable args (CWIA): /// The implementation of CWIA here implements a `receive()` method that emits the /// `ReceiveETH(uint256)` event. This skips the `DELEGATECALL` when there is no calldata, /// enabling us to accept hard gas-capped `sends` & `transfers` for maximum backwards /// composability. The minimal proxy implementation does not offer this feature. library LibClone { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Unable to deploy the clone. error DeploymentFailed(); /// @dev The salt must start with either the zero address or the caller. error SaltDoesNotStartWithCaller(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL PROXY OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a clone of `implementation`. function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * --------------------------------------------------------------------------+ * CREATION (9 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * --------------------------------------------------------------------------| * RUNTIME (44 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * | * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | | * 3d | RETURNDATASIZE | 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 0 0 | | * | * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | | * 3d | RETURNDATASIZE | 0 0 cds 0 0 0 0 | | * 37 | CALLDATACOPY | 0 0 0 0 | [0..cds): calldata | * | * ::: delegate call to the implementation contract :::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 cds 0 0 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata | * | * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata | * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata | * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata | * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | * | * 60 0x2a | PUSH1 0x2a | 0x2a success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * --------------------------------------------------------------------------+ */ mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create(0, 0x0c, 0x35) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that has been overwritten. mstore(0x21, 0) } } /// @dev Deploys a deterministic clone of `implementation` with `salt`. function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) instance := create2(0, 0x0c, 0x35, salt) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that has been overwritten. mstore(0x21, 0) } } /// @dev Returns the initialization code hash of the clone of `implementation`. /// Used for mining vanity addresses with create2crunch. function initCodeHash(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { mstore(0x21, 0x5af43d3d93803e602a57fd5bf3) mstore(0x14, implementation) mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73) hash := keccak256(0x0c, 0x35) // Restore the part of the free memory pointer that has been overwritten. mstore(0x21, 0) } } /// @dev Returns the address of the deterministic clone of `implementation`, /// with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress(address implementation, bytes32 salt, address deployer) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MINIMAL PROXY OPERATIONS (PUSH0 VARIANT) */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a PUSH0 clone of `implementation`. function clone_PUSH0(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { /** * --------------------------------------------------------------------------+ * CREATION (9 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * 60 runSize | PUSH1 runSize | r | | * 5f | PUSH0 | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 5f | PUSH0 | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * --------------------------------------------------------------------------| * RUNTIME (45 bytes) | * --------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * --------------------------------------------------------------------------| * | * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: | * 5f | PUSH0 | 0 | | * 5f | PUSH0 | 0 0 | | * | * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 | | * 5f | PUSH0 | 0 cds 0 0 | | * 5f | PUSH0 | 0 0 cds 0 0 | | * 37 | CALLDATACOPY | 0 0 | [0..cds): calldata | * | * ::: delegate call to the implementation contract :::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds 0 0 | [0..cds): calldata | * 5f | PUSH0 | 0 cds 0 0 | [0..cds): calldata | * 73 addr | PUSH20 addr | addr 0 cds 0 0 | [0..cds): calldata | * 5a | GAS | gas addr 0 cds 0 0 | [0..cds): calldata | * f4 | DELEGATECALL | success | [0..cds): calldata | * | * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success | [0..cds): calldata | * 5f | PUSH0 | 0 rds success | [0..cds): calldata | * 5f | PUSH0 | 0 0 rds success | [0..cds): calldata | * 3e | RETURNDATACOPY | success | [0..rds): returndata | * | * 60 0x29 | PUSH1 0x29 | 0x29 success | [0..rds): returndata | * 57 | JUMPI | | [0..rds): returndata | * | * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds | [0..rds): returndata | * 5f | PUSH0 | 0 rds | [0..rds): returndata | * fd | REVERT | | [0..rds): returndata | * | * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | | [0..rds): returndata | * 3d | RETURNDATASIZE | rds | [0..rds): returndata | * 5f | PUSH0 | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * --------------------------------------------------------------------------+ */ mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 instance := create(0, 0x0e, 0x36) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that has been overwritten. mstore(0x24, 0) } } /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`. function cloneDeterministic_PUSH0(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 instance := create2(0, 0x0e, 0x36, salt) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that has been overwritten. mstore(0x24, 0) } } /// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`. /// Used for mining vanity addresses with create2crunch. function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) { /// @solidity memory-safe-assembly assembly { mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16 mstore(0x14, implementation) // 20 mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9 hash := keccak256(0x0e, 0x36) // Restore the part of the free memory pointer that has been overwritten. mstore(0x24, 0) } } /// @dev Returns the address of the deterministic PUSH0 clone of `implementation`, /// with `salt` by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress_PUSH0( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHash_PUSH0(implementation); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CLONES WITH IMMUTABLE ARGS OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Deploys a minimal proxy with `implementation`, /// using immutable arguments encoded in `data`. /// /// Note: This implementation of CWIA differs from the original implementation. /// If the calldata is empty, it will emit a `ReceiveETH(uint256)` event and skip the `DELEGATECALL`. function clone(address implementation, bytes memory data) internal returns (address instance) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) // The `creationSize` is `extraLength + 108` // The `runSize` is `creationSize - 10`. /** * ---------------------------------------------------------------------------------------------------+ * CREATION (10 bytes) | * ---------------------------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------------------------| * 61 runSize | PUSH2 runSize | r | | * 3d | RETURNDATASIZE | 0 r | | * 81 | DUP2 | r 0 r | | * 60 offset | PUSH1 offset | o r 0 r | | * 3d | RETURNDATASIZE | 0 o r 0 r | | * 39 | CODECOPY | 0 r | [0..runSize): runtime code | * f3 | RETURN | | [0..runSize): runtime code | * ---------------------------------------------------------------------------------------------------| * RUNTIME (98 bytes + extraLength) | * ---------------------------------------------------------------------------------------------------| * Opcode | Mnemonic | Stack | Memory | * ---------------------------------------------------------------------------------------------------| * | * ::: if no calldata, emit event & return w/o `DELEGATECALL` ::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 60 0x2c | PUSH1 0x2c | 0x2c cds | | * 57 | JUMPI | | | * 34 | CALLVALUE | cv | | * 3d | RETURNDATASIZE | 0 cv | | * 52 | MSTORE | | [0..0x20): callvalue | * 7f sig | PUSH32 0x9e.. | sig | [0..0x20): callvalue | * 59 | MSIZE | 0x20 sig | [0..0x20): callvalue | * 3d | RETURNDATASIZE | 0 0x20 sig | [0..0x20): callvalue | * a1 | LOG1 | | [0..0x20): callvalue | * 00 | STOP | | [0..0x20): callvalue | * 5b | JUMPDEST | | | * | * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds | | * 3d | RETURNDATASIZE | 0 cds | | * 3d | RETURNDATASIZE | 0 0 cds | | * 37 | CALLDATACOPY | | [0..cds): calldata | * | * ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata | * 3d | RETURNDATASIZE | 0 0 0 0 | [0..cds): calldata | * 61 extra | PUSH2 extra | e 0 0 0 0 | [0..cds): calldata | * | * ::: copy extra data to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 80 | DUP1 | e e 0 0 0 0 | [0..cds): calldata | * 60 0x62 | PUSH1 0x62 | 0x62 e e 0 0 0 0 | [0..cds): calldata | * 36 | CALLDATASIZE | cds 0x62 e e 0 0 0 0 | [0..cds): calldata | * 39 | CODECOPY | e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * | * ::: delegate call to the implementation contract ::::::::::::::::::::::::::::::::::::::::::::::::: | * 36 | CALLDATASIZE | cds e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 01 | ADD | cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 3d | RETURNDATASIZE | 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 73 addr | PUSH20 addr | addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 5a | GAS | gas addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * f4 | DELEGATECALL | success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * | * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 3d | RETURNDATASIZE | rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 3d | RETURNDATASIZE | rds rds success 0 0 | [0..cds): calldata, [cds..cds+e): extraData | * 93 | SWAP4 | 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | * 80 | DUP1 | 0 0 rds success 0 rds | [0..cds): calldata, [cds..cds+e): extraData | * 3e | RETURNDATACOPY | success 0 rds | [0..rds): returndata | * | * 60 0x60 | PUSH1 0x60 | 0x60 success 0 rds | [0..rds): returndata | * 57 | JUMPI | 0 rds | [0..rds): returndata | * | * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * fd | REVERT | | [0..rds): returndata | * | * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: | * 5b | JUMPDEST | 0 rds | [0..rds): returndata | * f3 | RETURN | | [0..rds): returndata | * ---------------------------------------------------------------------------------------------------+ */ // Write the bytecode before the data. mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the address of the implementation. mstore(sub(data, 0x0d), implementation) // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e. // The actual EVM limit may be smaller and may change over time. sub(data, add(0x59, lt(extraLength, 0xff9e))), or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) // Create the instance. instance := create(0, sub(data, 0x4c), add(extraLength, 0x6c)) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Deploys a deterministic clone of `implementation`, /// using immutable arguments encoded in `data`, with `salt`. /// /// Note: This implementation of CWIA differs from the original implementation. /// If the calldata is empty, it will emit a `ReceiveETH(uint256)` event and skip the `DELEGATECALL`. function cloneDeterministic(address implementation, bytes memory data, bytes32 salt) internal returns (address instance) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) // Write the bytecode before the data. mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the address of the implementation. mstore(sub(data, 0x0d), implementation) // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e. // The actual EVM limit may be smaller and may change over time. sub(data, add(0x59, lt(extraLength, 0xff9e))), or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) // Create the instance. instance := create2(0, sub(data, 0x4c), add(extraLength, 0x6c), salt) // If `instance` is zero, revert. if iszero(instance) { // Store the function selector of `DeploymentFailed()`. mstore(0x00, 0x30116425) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Returns the initialization code hash of the clone of `implementation` /// using immutable arguments encoded in `data`. /// Used for mining vanity addresses with create2crunch. function initCodeHash(address implementation, bytes memory data) internal pure returns (bytes32 hash) { assembly { // Compute the boundaries of the data and cache the memory slots around it. let mBefore3 := mload(sub(data, 0x60)) let mBefore2 := mload(sub(data, 0x40)) let mBefore1 := mload(sub(data, 0x20)) let dataLength := mload(data) let dataEnd := add(add(data, 0x20), dataLength) let mAfter1 := mload(dataEnd) // Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b. // The actual EVM limit may be smaller and may change over time. returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b)) // +2 bytes for telling how much data there is appended to the call. let extraLength := add(dataLength, 2) // Write the bytecode before the data. mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the address of the implementation. mstore(sub(data, 0x0d), implementation) // Write the rest of the bytecode. mstore( sub(data, 0x21), or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73) ) // `keccak256("ReceiveETH(uint256)")` mstore( sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff ) mstore( sub(data, 0x5a), or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f) ) mstore(dataEnd, shl(0xf0, extraLength)) // Compute and store the bytecode hash. hash := keccak256(sub(data, 0x4c), add(extraLength, 0x6c)) // Restore the overwritten memory surrounding `data`. mstore(dataEnd, mAfter1) mstore(data, dataLength) mstore(sub(data, 0x20), mBefore1) mstore(sub(data, 0x40), mBefore2) mstore(sub(data, 0x60), mBefore3) } } /// @dev Returns the address of the deterministic clone of /// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress( address implementation, bytes memory data, bytes32 salt, address deployer ) internal pure returns (address predicted) { bytes32 hash = initCodeHash(implementation, data); predicted = predictDeterministicAddress(hash, salt, deployer); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* OTHER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the address when a contract with initialization code hash, /// `hash`, is deployed with `salt`, by `deployer`. /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly. function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { // Compute and store the bytecode hash. mstore8(0x00, 0xff) // Write the prefix. mstore(0x35, hash) mstore(0x01, shl(96, deployer)) mstore(0x15, salt) predicted := keccak256(0x00, 0x55) // Restore the part of the free memory pointer that has been overwritten. mstore(0x35, 0) } } /// @dev Reverts if `salt` does not start with either the zero address or the caller. function checkStartsWithCaller(bytes32 salt) internal view { /// @solidity memory-safe-assembly assembly { // If the salt does not start with the zero address or the caller. if iszero(or(iszero(shr(96, salt)), eq(caller(), shr(96, salt)))) { // Store the function selector of `SaltDoesNotStartWithCaller()`. mstore(0x00, 0x2f634836) // Revert with (offset, size). revert(0x1c, 0x04) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; interface IENSReverseRegistrar { function claim(address owner) external returns (bytes32); function defaultResolver() external view returns (address); function ens() external view returns (address); function node(address addr) external pure returns (bytes32); function setName(string memory name) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Class with helper read functions for clone with immutable args. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Clone.sol) /// @author Adapted from clones with immutable args by zefram.eth, Saw-mon & Natalie /// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args) abstract contract Clone { /// @dev Reads all of the immutable args. function _getArgBytes() internal pure returns (bytes memory arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := mload(0x40) let length := sub(calldatasize(), add(2, offset)) // 2 bytes are used for the length. mstore(arg, length) // Store the length. calldatacopy(add(arg, 0x20), offset, length) let o := add(add(arg, 0x20), length) mstore(o, 0) // Zeroize the slot after the bytes. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } /// @dev Reads an immutable arg with type bytes. function _getArgBytes(uint256 argOffset, uint256 length) internal pure returns (bytes memory arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := mload(0x40) mstore(arg, length) // Store the length. calldatacopy(add(arg, 0x20), add(offset, argOffset), length) let o := add(add(arg, 0x20), length) mstore(o, 0) // Zeroize the slot after the bytes. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } /// @dev Reads an immutable arg with type address. function _getArgAddress(uint256 argOffset) internal pure returns (address arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(96, calldataload(add(offset, argOffset))) } } /// @dev Reads a uint256 array stored in the immutable args. function _getArgUint256Array(uint256 argOffset, uint256 length) internal pure returns (uint256[] memory arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := mload(0x40) mstore(arg, length) // Store the length. calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length)) mstore(0x40, add(add(arg, 0x20), shl(5, length))) // Allocate the memory. } } /// @dev Reads a bytes32 array stored in the immutable args. function _getArgBytes32Array(uint256 argOffset, uint256 length) internal pure returns (bytes32[] memory arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := mload(0x40) mstore(arg, length) // Store the length. calldatacopy(add(arg, 0x20), add(offset, argOffset), shl(5, length)) mstore(0x40, add(add(arg, 0x20), shl(5, length))) // Allocate the memory. } } /// @dev Reads an immutable arg with type bytes32. function _getArgBytes32(uint256 argOffset) internal pure returns (bytes32 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := calldataload(add(offset, argOffset)) } } /// @dev Reads an immutable arg with type uint256. function _getArgUint256(uint256 argOffset) internal pure returns (uint256 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := calldataload(add(offset, argOffset)) } } /// @dev Reads an immutable arg with type uint248. function _getArgUint248(uint256 argOffset) internal pure returns (uint248 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(8, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint240. function _getArgUint240(uint256 argOffset) internal pure returns (uint240 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(16, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint232. function _getArgUint232(uint256 argOffset) internal pure returns (uint232 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(24, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint224. function _getArgUint224(uint256 argOffset) internal pure returns (uint224 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(0x20, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint216. function _getArgUint216(uint256 argOffset) internal pure returns (uint216 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(40, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint208. function _getArgUint208(uint256 argOffset) internal pure returns (uint208 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(48, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint200. function _getArgUint200(uint256 argOffset) internal pure returns (uint200 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(56, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint192. function _getArgUint192(uint256 argOffset) internal pure returns (uint192 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(64, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint184. function _getArgUint184(uint256 argOffset) internal pure returns (uint184 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(72, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint176. function _getArgUint176(uint256 argOffset) internal pure returns (uint176 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(80, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint168. function _getArgUint168(uint256 argOffset) internal pure returns (uint168 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(88, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint160. function _getArgUint160(uint256 argOffset) internal pure returns (uint160 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(96, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint152. function _getArgUint152(uint256 argOffset) internal pure returns (uint152 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(104, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint144. function _getArgUint144(uint256 argOffset) internal pure returns (uint144 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(112, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint136. function _getArgUint136(uint256 argOffset) internal pure returns (uint136 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(120, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint128. function _getArgUint128(uint256 argOffset) internal pure returns (uint128 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(128, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint120. function _getArgUint120(uint256 argOffset) internal pure returns (uint120 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(136, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint112. function _getArgUint112(uint256 argOffset) internal pure returns (uint112 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(144, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint104. function _getArgUint104(uint256 argOffset) internal pure returns (uint104 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(152, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint96. function _getArgUint96(uint256 argOffset) internal pure returns (uint96 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(160, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint88. function _getArgUint88(uint256 argOffset) internal pure returns (uint88 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(168, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint80. function _getArgUint80(uint256 argOffset) internal pure returns (uint80 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(176, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint72. function _getArgUint72(uint256 argOffset) internal pure returns (uint72 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(184, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint64. function _getArgUint64(uint256 argOffset) internal pure returns (uint64 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(192, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint56. function _getArgUint56(uint256 argOffset) internal pure returns (uint56 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(200, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint48. function _getArgUint48(uint256 argOffset) internal pure returns (uint48 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(208, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint40. function _getArgUint40(uint256 argOffset) internal pure returns (uint40 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(216, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint32. function _getArgUint32(uint256 argOffset) internal pure returns (uint32 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(224, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint24. function _getArgUint24(uint256 argOffset) internal pure returns (uint24 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(232, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint16. function _getArgUint16(uint256 argOffset) internal pure returns (uint16 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(240, calldataload(add(offset, argOffset))) } } /// @dev Reads an immutable arg with type uint8. function _getArgUint8(uint256 argOffset) internal pure returns (uint8 arg) { uint256 offset = _getImmutableArgsOffset(); /// @solidity memory-safe-assembly assembly { arg := shr(248, calldataload(add(offset, argOffset))) } } /// @return offset The offset of the packed immutable args in calldata. function _getImmutableArgsOffset() internal pure returns (uint256 offset) { /// @solidity memory-safe-assembly assembly { offset := sub(calldatasize(), shr(240, calldataload(sub(calldatasize(), 2)))) } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// /// @dev Note: /// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection. /// - For ERC20s, this implementation won't check that a token has code, /// responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes. uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants. // // The regular variants: // - Forwards all remaining gas to the target. // - Reverts if the target reverts. // - Reverts if the current contract has insufficient balance. // // The force variants: // - Forwards with an optional gas stipend // (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases). // - If the target reverts, or if the gas stipend is exhausted, // creates a temporary contract to force send the ETH via `SELFDESTRUCT`. // Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758. // - Reverts if the current contract has insufficient balance. // // The try variants: // - Forwards with a mandatory gas stipend. // - Instead of reverting, returns whether the transfer succeeded. /// @dev Sends `amount` (in wei) ETH to `to`. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gas(), to, amount, gas(), 0x00, gas(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Sends all the ETH in the current contract to `to`. function safeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { // Transfer all the ETH and check if it succeeded or not. if iszero(call(gas(), to, selfbalance(), gas(), 0x00, gas(), 0x00)) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`. function forceSafeTransferAllETH(address to, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { if lt(selfbalance(), amount) { mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`. revert(0x1c, 0x04) } if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(amount, 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`. function forceSafeTransferAllETH(address to) internal { /// @solidity memory-safe-assembly assembly { if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), gas(), 0x00, gas(), 0x00)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. if iszero(create(selfbalance(), 0x0b, 0x16)) { returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation. } } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00) } } /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`. function trySafeTransferAllETH(address to, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { success := call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have their entire balance approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`. amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { mstore(0x00, 0x7939f424) // `TransferFromFailed()`. revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. // Read the balance, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x14, to) // Store the `to` argument. amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it. mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`. // Perform the transfer, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x90b8ec18) // `TransferFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, reverting upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// If the initial attempt to approve fails, attempts to reset the approved amount to zero, /// then retries the approval again (some tokens, e.g. USDT, requires this). /// Reverts upon failure. function safeApproveWithRetry(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. // Perform the approval, retrying upon failure. if iszero( and( // The arguments of `and` are evaluated from right to left. or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x34, 0) // Store 0 for the `amount`. mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`. pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval. mstore(0x34, amount) // Store back the original `amount`. // Retry the approval, reverting upon failure. if iszero( and( or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing. call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`. revert(0x1c, 0x04) } } mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten. } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`. amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } }
{ "remappings": [ "ds-test/=lib/ds-test/src/", "solmate/=lib/solmate/src/", "splits-tests/=lib/splits-utils/test/", "solady/=lib/solady/src/", "forge-std/=lib/forge-std/src/", "splits-utils/=lib/splits-utils/src/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"string","name":"_ensName","type":"string"},{"internalType":"address","name":"_ensReverseRegistrar","type":"address"},{"internalType":"address","name":"_ensOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"Invalid__Recipients","type":"error"},{"inputs":[{"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"Invalid__ThresholdTooLarge","type":"error"},{"inputs":[],"name":"Invalid__ZeroThreshold","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owr","type":"address"},{"indexed":false,"internalType":"address","name":"recoveryAddress","type":"address"},{"indexed":false,"internalType":"address","name":"principalRecipient","type":"address"},{"indexed":false,"internalType":"address","name":"rewardRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"threshold","type":"uint256"}],"name":"CreateOWRecipient","type":"event"},{"inputs":[{"internalType":"address","name":"recoveryAddress","type":"address"},{"internalType":"address","name":"principalRecipient","type":"address"},{"internalType":"address","name":"rewardRecipient","type":"address"},{"internalType":"uint256","name":"amountOfPrincipalStake","type":"uint256"}],"name":"createOWRecipient","outputs":[{"internalType":"contract OptimisticWithdrawalRecipient","name":"owr","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owrImpl","outputs":[{"internalType":"contract OptimisticWithdrawalRecipient","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b50604051610e78380380610e7883398101604081905261002f916101a7565b60405161003b90610144565b604051809103906000f080158015610057573d6000803e3d6000fd5b506001600160a01b0390811660805260405163c47f002760e01b81529083169063c47f00279061008b908690600401610276565b6020604051808303816000875af11580156100aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100ce91906102a9565b50604051630f41a04d60e11b81526001600160a01b038281166004830152831690631e83409a906024016020604051808303816000875af1158015610117573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061013b91906102a9565b505050506102c2565b6107f98061067f83390190565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561018257818101518382015260200161016a565b50506000910152565b80516001600160a01b03811681146101a257600080fd5b919050565b6000806000606084860312156101bc57600080fd5b83516001600160401b03808211156101d357600080fd5b818601915086601f8301126101e757600080fd5b8151818111156101f9576101f9610151565b604051601f8201601f19908116603f0116810190838211818310171561022157610221610151565b8160405282815289602084870101111561023a57600080fd5b61024b836020830160208801610167565b809750505050505061025f6020850161018b565b915061026d6040850161018b565b90509250925092565b6020815260008251806020840152610295816040850160208701610167565b601f01601f19169190910160400192915050565b6000602082840312156102bb57600080fd5b5051919050565b60805161039c6102e360003960008181606f0152610188015261039c6000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063c3d7aa861461003b578063c52425e41461006a575b600080fd5b61004e61004936600461031b565b610091565b6040516001600160a01b03909116815260200160405180910390f35b61004e7f000000000000000000000000000000000000000000000000000000000000000081565b60006001600160a01b03841615806100b057506001600160a01b038316155b156100ce57604051637e511f1560e11b815260040160405180910390fd5b816000036100ef57604051638966ee9560e01b815260040160405180910390fd5b6bffffffffffffffffffffffff82111561012357604051637a95f47560e01b81526004810183905260240160405180910390fd5b604051606086901b6bffffffffffffffffffffffff191660208201526001600160a01b0385811660a085901b176034830181905290851660548301819052909160009060740160408051601f1981840301815291905290506101ae6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001682610213565b604080516001600160a01b038b811682528a8116602083015289811682840152606082018990529151929650908616917ff818472e7ef9970531a3465a1d70817d221d2c79dc98ea49bdb1254783dba6eb9181900360800190a2505050949350505050565b600060608203516040830351602084035184518060208701018051600283016c5af43d3d93803e606057fd5bf3895289600d8a035278593da1005b363d3d373d3d3d3d610000806062363936013d738160481b1760218a03527f9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff603a8a035272fd6100003d81600a3d39f336602c57343d527f6062820160781b1761ff9e82106059018a03528060f01b8352606c8101604c8a036000f0975050866102e05763301164256000526004601cfd5b90528552601f19850152603f19840152605f1990920191909152919050565b80356001600160a01b038116811461031657600080fd5b919050565b6000806000806080858703121561033157600080fd5b61033a856102ff565b9350610348602086016102ff565b9250610356604086016102ff565b939692955092936060013592505056fea26469706673582212207e26706744441b67ebb26904afecebf545348e41ee746030e32bb85594999a1264736f6c63430008130033608060405234801561001057600080fd5b506107d9806100206000396000f3fe6080604052600436106100865760003560e01c80633a6a4d2e116100595780633a6a4d2e1461012957806351cff8d914610131578063710eb26c146101515780638677025d1461017b578063dc2f511b146101a257600080fd5b806303615ba31461008b578063043edae8146100d457806324ae6a271461010c5780632d7fd50714610121575b600080fd5b34801561009757600080fd5b506100c16100a63660046106d2565b6001600160a01b031660009081526001602052604090205490565b6040519081526020015b60405180910390f35b3480156100e057600080fd5b506000546100f4906001600160801b031681565b6040516001600160801b0390911681526020016100cb565b61011f61011a3660046106f4565b6101dd565b005b61011f61035f565b61011f61036b565b34801561013d57600080fd5b5061011f61014c3660046106d2565b610375565b34801561015d57600080fd5b506040513660011981013560f01c90033560601c81526020016100cb565b34801561018757600080fd5b506000546100f490600160801b90046001600160801b031681565b3480156101ae57600080fd5b506101b7610403565b604080516001600160a01b039485168152939092166020840152908201526060016100cb565b3660011981013560f01c90033560601c8061025b576000806101fd610403565b5091509150816001600160a01b0316846001600160a01b0316141580156102365750806001600160a01b0316846001600160a01b031614155b15610254576040516332fe787760e21b815260040160405180910390fd5b505061028d565b806001600160a01b0316826001600160a01b03161461028d576040516332fe787760e21b815260040160405180910390fd5b6040516370a0823160e01b81523060048201526000906001600160a01b038516906370a0823190602401602060405180830381865afa1580156102d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f89190610727565b905061030e6001600160a01b038516848361042f565b604080516001600160a01b038087168252851660208201529081018290527f14fe52dffd0b082cffd03bb905909e989fb4f608897a13c49c2880d92e556ae59060600160405180910390a150505050565b6103696001610475565b565b6103696000610475565b6001600160a01b0381166000818152600160205260408120805482546001600160801b03808216839003166001600160801b0319909116178355919055906103bd908261061f565b604080516001600160a01b0384168152602081018390527f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65910160405180910390a15050565b600080600080610413600061063f565b9350505060a082901c82610427600161063f565b925050909192565b816014528060345263a9059cbb60601b60005260206000604460106000875af13d15600160005114171661046b576390b8ec186000526004601cfd5b6000603452505050565b6000805447916001600160801b03600160801b830481169216906104998285610756565b905060008060006104a8610403565b9194509250905060008087830367de0b6b3a7640000087108015906104cd5750600081115b156104ee57808711156104e657915050808503816104f2565b8692506104f2565b8691505b506001600160801b0386111561051b576040516328165e5960e11b815260040160405180910390fd5b811561056a5781600060108282829054906101000a90046001600160801b03166105459190610769565b92506101000a8154816001600160801b0302191690836001600160801b031602179055505b61057585838c61065d565b61058084828c61065d565b60018a036105d25760008211806105975750600081115b156105d257806105a78389610790565b6105b19190610790565b600080546001600160801b0319166001600160801b03929092169190911790555b60408051838152602081018390529081018b90527f8b367b31582d5c638694615c1cfc77ffcf9febcf6e6c793358089f7769cfeee69060600160405180910390a150505050505050505050565b60005a60005a84865af161063b5763b12d13eb6000526004601cfd5b5050565b60003660011981013560f01c90036020830201601401355b92915050565b81156106b1576001810361069e576001600160a01b03831660009081526001602052604081208054849290610693908490610790565b909155506106b19050565b6106b16001600160a01b0384168361061f565b505050565b80356001600160a01b03811681146106cd57600080fd5b919050565b6000602082840312156106e457600080fd5b6106ed826106b6565b9392505050565b6000806040838503121561070757600080fd5b610710836106b6565b915061071e602084016106b6565b90509250929050565b60006020828403121561073957600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b8181038181111561065757610657610740565b6001600160801b0381811683821601908082111561078957610789610740565b5092915050565b808201808211156106575761065761074056fea2646970667358221220081bd96d296fc64e8dd8fc75c0fc932757e92d571c125227154d159872013eea64736f6c634300081300330000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a58e81fe9b61b5c3fe2afd33cf304c454abfc7cb00000000000000000000000043f641fa70e09f0326ac66b4ef0c416eaecbc6f500000000000000000000000000000000000000000000000000000000000000136f7772666163746f72792e6f626f6c2e65746800000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100365760003560e01c8063c3d7aa861461003b578063c52425e41461006a575b600080fd5b61004e61004936600461031b565b610091565b6040516001600160a01b03909116815260200160405180910390f35b61004e7f000000000000000000000000e11eabf19a49c389d3e8735c35f8f34f28bdcb2281565b60006001600160a01b03841615806100b057506001600160a01b038316155b156100ce57604051637e511f1560e11b815260040160405180910390fd5b816000036100ef57604051638966ee9560e01b815260040160405180910390fd5b6bffffffffffffffffffffffff82111561012357604051637a95f47560e01b81526004810183905260240160405180910390fd5b604051606086901b6bffffffffffffffffffffffff191660208201526001600160a01b0385811660a085901b176034830181905290851660548301819052909160009060740160408051601f1981840301815291905290506101ae6001600160a01b037f000000000000000000000000e11eabf19a49c389d3e8735c35f8f34f28bdcb221682610213565b604080516001600160a01b038b811682528a8116602083015289811682840152606082018990529151929650908616917ff818472e7ef9970531a3465a1d70817d221d2c79dc98ea49bdb1254783dba6eb9181900360800190a2505050949350505050565b600060608203516040830351602084035184518060208701018051600283016c5af43d3d93803e606057fd5bf3895289600d8a035278593da1005b363d3d373d3d3d3d610000806062363936013d738160481b1760218a03527f9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff603a8a035272fd6100003d81600a3d39f336602c57343d527f6062820160781b1761ff9e82106059018a03528060f01b8352606c8101604c8a036000f0975050866102e05763301164256000526004601cfd5b90528552601f19850152603f19840152605f1990920191909152919050565b80356001600160a01b038116811461031657600080fd5b919050565b6000806000806080858703121561033157600080fd5b61033a856102ff565b9350610348602086016102ff565b9250610356604086016102ff565b939692955092936060013592505056fea26469706673582212207e26706744441b67ebb26904afecebf545348e41ee746030e32bb85594999a1264736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000000000000000000000000000000000000000060000000000000000000000000a58e81fe9b61b5c3fe2afd33cf304c454abfc7cb00000000000000000000000043f641fa70e09f0326ac66b4ef0c416eaecbc6f500000000000000000000000000000000000000000000000000000000000000136f7772666163746f72792e6f626f6c2e65746800000000000000000000000000
-----Decoded View---------------
Arg [0] : _ensName (string): owrfactory.obol.eth
Arg [1] : _ensReverseRegistrar (address): 0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb
Arg [2] : _ensOwner (address): 0x43F641fA70e09f0326ac66b4Ef0C416EaEcBC6f5
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [1] : 000000000000000000000000a58e81fe9b61b5c3fe2afd33cf304c454abfc7cb
Arg [2] : 00000000000000000000000043f641fa70e09f0326ac66b4ef0c416eaecbc6f5
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000013
Arg [4] : 6f7772666163746f72792e6f626f6c2e65746800000000000000000000000000
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.