Overview
ETH Balance
0 ETH
Eth Value
$0.00| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 5 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x602c3d81 | 23347229 | 171 days ago | Contract Creation | 0 ETH | |||
| 0x602c3d81 | 23347087 | 171 days ago | Contract Creation | 0 ETH | |||
| 0x602c3d81 | 23346922 | 171 days ago | Contract Creation | 0 ETH | |||
| 0x602c3d81 | 23346746 | 171 days ago | Contract Creation | 0 ETH | |||
| 0x61012060 | 23334417 | 172 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
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:
JBOptimismSuckerDeployer
Compiler Version
v0.8.23+commit.f704f362
Optimization Enabled:
Yes with 3000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import "./JBSuckerDeployer.sol";
import "../interfaces/IJBArbitrumSuckerDeployer.sol";
import {JBOptimismSucker} from "../JBOptimismSucker.sol";
import {JBAddToBalanceMode} from "../enums/JBAddToBalanceMode.sol";
import {IJBOpSuckerDeployer} from "./../interfaces/IJBOpSuckerDeployer.sol";
import {IJBSuckerDeployer} from "./../interfaces/IJBSuckerDeployer.sol";
import {IOPMessenger} from "../interfaces/IOPMessenger.sol";
import {IOPStandardBridge} from "../interfaces/IOPStandardBridge.sol";
/// @notice An `IJBSuckerDeployerFeeless` implementation to deploy `JBOptimismSucker` contracts.
contract JBOptimismSuckerDeployer is JBSuckerDeployer, IJBOpSuckerDeployer {
//*********************************************************************//
// ---------------------- public stored properties ------------------- //
//*********************************************************************//
/// @notice The messenger used to send messages between the local and remote sucker.
IOPMessenger public override opMessenger;
/// @notice The bridge used to bridge tokens between the local and remote chain.
IOPStandardBridge public override opBridge;
//*********************************************************************//
// ---------------------------- constructor -------------------------- //
//*********************************************************************//
/// @param directory The directory of terminals and controllers for projects.
/// @param permissions The permissions contract for the deployer.
/// @param tokens The contract that manages token minting and burning.
/// @param configurator The address of the configurator.
constructor(
IJBDirectory directory,
IJBPermissions permissions,
IJBTokens tokens,
address configurator,
address trusted_forwarder
)
JBSuckerDeployer(directory, permissions, tokens, configurator, trusted_forwarder)
{}
//*********************************************************************//
// ------------------------ internal views --------------------------- //
//*********************************************************************//
/// @notice Check if the layer specific configuration is set or not. Used as a sanity check.
function _layerSpecificConfigurationIsSet() internal view override returns (bool) {
return address(opMessenger) != address(0) || address(opBridge) != address(0);
}
//*********************************************************************//
// --------------------- external transactions ----------------------- //
//*********************************************************************//
/// @notice handles some layer specific configuration that can't be done in the constructor otherwise deployment
/// addresses would change.
/// @notice messenger the OPMesssenger on this layer.
/// @notice bridge the OPStandardBridge on this layer.
function setChainSpecificConstants(IOPMessenger messenger, IOPStandardBridge bridge) external {
if (_layerSpecificConfigurationIsSet()) {
revert JBSuckerDeployer_AlreadyConfigured();
}
if (_msgSender() != LAYER_SPECIFIC_CONFIGURATOR) {
revert JBSuckerDeployer_Unauthorized(_msgSender(), LAYER_SPECIFIC_CONFIGURATOR);
}
// Configure these layer specific properties.
// This is done in a separate call to make the deployment code chain agnostic.
opMessenger = messenger;
opBridge = bridge;
// Make sure the layer specific configuration is properly configured.
if (!_layerSpecificConfigurationIsSet()) {
revert JBSuckerDeployer_InvalidLayerSpecificConfiguration();
}
}
}// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
import "./IOwnable.sol";
interface IBridge {
/// @dev This is an instruction to offchain readers to inform them where to look
/// for sequencer inbox batch data. This is not the type of data (eg. das, brotli encoded, or blob versioned hash)
/// and this enum is not used in the state transition function, rather it informs an offchain
/// reader where to find the data so that they can supply it to the replay binary
enum BatchDataLocation {
/// @notice The data can be found in the transaction call data
TxInput,
/// @notice The data can be found in an event emitted during the transaction
SeparateBatchEvent,
/// @notice This batch contains no data
NoData,
/// @notice The data can be found in the 4844 data blobs on this transaction
Blob
}
struct TimeBounds {
uint64 minTimestamp;
uint64 maxTimestamp;
uint64 minBlockNumber;
uint64 maxBlockNumber;
}
event MessageDelivered(
uint256 indexed messageIndex,
bytes32 indexed beforeInboxAcc,
address inbox,
uint8 kind,
address sender,
bytes32 messageDataHash,
uint256 baseFeeL1,
uint64 timestamp
);
event BridgeCallTriggered(
address indexed outbox,
address indexed to,
uint256 value,
bytes data
);
event InboxToggle(address indexed inbox, bool enabled);
event OutboxToggle(address indexed outbox, bool enabled);
event SequencerInboxUpdated(address newSequencerInbox);
event RollupUpdated(address rollup);
function allowedDelayedInboxList(uint256) external returns (address);
function allowedOutboxList(uint256) external returns (address);
/// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
function delayedInboxAccs(uint256) external view returns (bytes32);
/// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message.
function sequencerInboxAccs(uint256) external view returns (bytes32);
function rollup() external view returns (IOwnable);
function sequencerInbox() external view returns (address);
function activeOutbox() external view returns (address);
function allowedDelayedInboxes(address inbox) external view returns (bool);
function allowedOutboxes(address outbox) external view returns (bool);
function sequencerReportedSubMessageCount() external view returns (uint256);
function executeCall(
address to,
uint256 value,
bytes calldata data
) external returns (bool success, bytes memory returnData);
function delayedMessageCount() external view returns (uint256);
function sequencerMessageCount() external view returns (uint256);
// ---------- onlySequencerInbox functions ----------
function enqueueSequencerMessage(
bytes32 dataHash,
uint256 afterDelayedMessagesRead,
uint256 prevMessageCount,
uint256 newMessageCount
)
external
returns (
uint256 seqMessageIndex,
bytes32 beforeAcc,
bytes32 delayedAcc,
bytes32 acc
);
/**
* @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type
* This is done through a separate function entrypoint instead of allowing the sequencer inbox
* to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either
* every delayed inbox or every sequencer inbox call.
*/
function submitBatchSpendingReport(address batchPoster, bytes32 dataHash)
external
returns (uint256 msgNum);
// ---------- onlyRollupOrOwner functions ----------
function setSequencerInbox(address _sequencerInbox) external;
function setDelayedInbox(address inbox, bool enabled) external;
function setOutbox(address inbox, bool enabled) external;
function updateRollupAddress(IOwnable _rollup) external;
}// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
interface IDelayedMessageProvider {
/// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
event InboxMessageDelivered(uint256 indexed messageNum, bytes data);
/// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator
/// same as InboxMessageDelivered but the batch data is available in tx.input
event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum);
}// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/nitro/blob/master/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
import "./IBridge.sol";
import "./IInboxBase.sol";
interface IInbox is IInboxBase {
function sendL1FundedUnsignedTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 nonce,
address to,
bytes calldata data
) external payable returns (uint256);
function sendL1FundedContractTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
address to,
bytes calldata data
) external payable returns (uint256);
/**
* @dev This method can only be called upon L1 fork and will not alias the caller
* This method will revert if not called from origin
*/
function sendL1FundedUnsignedTransactionToFork(
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 nonce,
address to,
bytes calldata data
) external payable returns (uint256);
/**
* @dev This method can only be called upon L1 fork and will not alias the caller
* This method will revert if not called from origin
*/
function sendUnsignedTransactionToFork(
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 nonce,
address to,
uint256 value,
bytes calldata data
) external returns (uint256);
/**
* @notice Send a message to initiate L2 withdrawal
* @dev This method can only be called upon L1 fork and will not alias the caller
* This method will revert if not called from origin
*/
function sendWithdrawEthToFork(
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 nonce,
uint256 value,
address withdrawTo
) external returns (uint256);
/**
* @notice Deposit eth from L1 to L2 to address of the sender if sender is an EOA, and to its aliased address if the sender is a contract
* @dev This does not trigger the fallback function when receiving in the L2 side.
* Look into retryable tickets if you are interested in this functionality.
* @dev This function should not be called inside contract constructors
*/
function depositEth() external payable returns (uint256);
/**
* @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
* @dev all msg.value will deposited to callValueRefundAddress on L2
* @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
* @param to destination L2 contract address
* @param l2CallValue call value for retryable L2 message
* @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
* @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
* @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
* @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param data ABI encoded data of L2 message
* @return unique message number of the retryable transaction
*/
function createRetryableTicket(
address to,
uint256 l2CallValue,
uint256 maxSubmissionCost,
address excessFeeRefundAddress,
address callValueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable returns (uint256);
/**
* @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts
* @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds
* come from the deposit alone, rather than falling back on the user's L2 balance
* @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress).
* createRetryableTicket method is the recommended standard.
* @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error
* @param to destination L2 contract address
* @param l2CallValue call value for retryable L2 message
* @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee
* @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance
* @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled
* @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error)
* @param data ABI encoded data of L2 message
* @return unique message number of the retryable transaction
*/
function unsafeCreateRetryableTicket(
address to,
uint256 l2CallValue,
uint256 maxSubmissionCost,
address excessFeeRefundAddress,
address callValueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
bytes calldata data
) external payable returns (uint256);
// ---------- initializer ----------
/**
* @dev function to be called one time during the inbox upgrade process
* this is used to fix the storage slots
*/
function postUpgradeInit(IBridge _bridge) external;
}// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
import "./IBridge.sol";
import "./IDelayedMessageProvider.sol";
import "./ISequencerInbox.sol";
interface IInboxBase is IDelayedMessageProvider {
function bridge() external view returns (IBridge);
function sequencerInbox() external view returns (ISequencerInbox);
function maxDataSize() external view returns (uint256);
/**
* @notice Send a generic L2 message to the chain
* @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input
* @param messageData Data of the message being sent
*/
function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256);
/**
* @notice Send a generic L2 message to the chain
* @dev This method can be used to send any type of message that doesn't require L1 validation
* @param messageData Data of the message being sent
*/
function sendL2Message(bytes calldata messageData) external returns (uint256);
function sendUnsignedTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 nonce,
address to,
uint256 value,
bytes calldata data
) external returns (uint256);
function sendContractTransaction(
uint256 gasLimit,
uint256 maxFeePerGas,
address to,
uint256 value,
bytes calldata data
) external returns (uint256);
/**
* @notice Get the L1 fee for submitting a retryable
* @dev This fee can be paid by funds already in the L2 aliased address or by the current message value
* @dev This formula may change in the future, to future proof your code query this method instead of inlining!!
* @param dataLength The length of the retryable's calldata, in bytes
* @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used
*/
function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee)
external
view
returns (uint256);
// ---------- onlyRollupOrOwner functions ----------
/// @notice pauses all inbox functionality
function pause() external;
/// @notice unpauses all inbox functionality
function unpause() external;
/// @notice add or remove users from allowList
function setAllowList(address[] memory user, bool[] memory val) external;
/// @notice enable or disable allowList
function setAllowListEnabled(bool _allowListEnabled) external;
/// @notice check if user is in allowList
function isAllowed(address user) external view returns (bool);
/// @notice check if allowList is enabled
function allowListEnabled() external view returns (bool);
function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external;
/// @notice returns the current admin
function getProxyAdmin() external view returns (address);
}// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.4.21 <0.9.0;
interface IOwnable {
function owner() external view returns (address);
}// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
pragma experimental ABIEncoderV2;
import "../libraries/IGasRefunder.sol";
import "./IDelayedMessageProvider.sol";
import "./IBridge.sol";
interface ISequencerInbox is IDelayedMessageProvider {
struct MaxTimeVariation {
uint256 delayBlocks;
uint256 futureBlocks;
uint256 delaySeconds;
uint256 futureSeconds;
}
event SequencerBatchDelivered(
uint256 indexed batchSequenceNumber,
bytes32 indexed beforeAcc,
bytes32 indexed afterAcc,
bytes32 delayedAcc,
uint256 afterDelayedMessagesRead,
IBridge.TimeBounds timeBounds,
IBridge.BatchDataLocation dataLocation
);
event OwnerFunctionCalled(uint256 indexed id);
/// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input
event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data);
/// @dev a valid keyset was added
event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes);
/// @dev a keyset was invalidated
event InvalidateKeyset(bytes32 indexed keysetHash);
function totalDelayedMessagesRead() external view returns (uint256);
function bridge() external view returns (IBridge);
/// @dev The size of the batch header
// solhint-disable-next-line func-name-mixedcase
function HEADER_LENGTH() external view returns (uint256);
/// @dev If the first batch data byte after the header has this bit set,
/// the sequencer inbox has authenticated the data. Currently only used for 4844 blob support.
/// See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
// solhint-disable-next-line func-name-mixedcase
function DATA_AUTHENTICATED_FLAG() external view returns (bytes1);
/// @dev If the first data byte after the header has this bit set,
/// then the batch data is to be found in 4844 data blobs
/// See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
// solhint-disable-next-line func-name-mixedcase
function DATA_BLOB_HEADER_FLAG() external view returns (bytes1);
/// @dev If the first data byte after the header has this bit set,
/// then the batch data is a das message
/// See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
// solhint-disable-next-line func-name-mixedcase
function DAS_MESSAGE_HEADER_FLAG() external view returns (bytes1);
/// @dev If the first data byte after the header has this bit set,
/// then the batch data is a das message that employs a merklesization strategy
/// See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
// solhint-disable-next-line func-name-mixedcase
function TREE_DAS_MESSAGE_HEADER_FLAG() external view returns (bytes1);
/// @dev If the first data byte after the header has this bit set,
/// then the batch data has been brotli compressed
/// See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
// solhint-disable-next-line func-name-mixedcase
function BROTLI_MESSAGE_HEADER_FLAG() external view returns (bytes1);
/// @dev If the first data byte after the header has this bit set,
/// then the batch data uses a zero heavy encoding
/// See: https://github.com/OffchainLabs/nitro/blob/69de0603abf6f900a4128cab7933df60cad54ded/arbstate/das_reader.go
// solhint-disable-next-line func-name-mixedcase
function ZERO_HEAVY_MESSAGE_HEADER_FLAG() external view returns (bytes1);
function rollup() external view returns (IOwnable);
function isBatchPoster(address) external view returns (bool);
function isSequencer(address) external view returns (bool);
function maxDataSize() external view returns (uint256);
/// @notice The batch poster manager has the ability to change the batch poster addresses
/// This enables the batch poster to do key rotation
function batchPosterManager() external view returns (address);
struct DasKeySetInfo {
bool isValidKeyset;
uint64 creationBlock;
}
/// @dev returns 4 uint256 to be compatible with older version
function maxTimeVariation()
external
view
returns (
uint256 delayBlocks,
uint256 futureBlocks,
uint256 delaySeconds,
uint256 futureSeconds
);
function dasKeySetInfo(bytes32) external view returns (bool, uint64);
/// @notice Remove force inclusion delay after a L1 chainId fork
function removeDelayAfterFork() external;
/// @notice Force messages from the delayed inbox to be included in the chain
/// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and
/// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these
/// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages.
/// @param _totalDelayedMessagesRead The total number of messages to read up to
/// @param kind The kind of the last message to be included
/// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included
/// @param baseFeeL1 The l1 gas price of the last message to be included
/// @param sender The sender of the last message to be included
/// @param messageDataHash The messageDataHash of the last message to be included
function forceInclusion(
uint256 _totalDelayedMessagesRead,
uint8 kind,
uint64[2] calldata l1BlockAndTime,
uint256 baseFeeL1,
address sender,
bytes32 messageDataHash
) external;
function inboxAccs(uint256 index) external view returns (bytes32);
function batchCount() external view returns (uint256);
function isValidKeysetHash(bytes32 ksHash) external view returns (bool);
/// @notice the creation block is intended to still be available after a keyset is deleted
function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256);
// ---------- BatchPoster functions ----------
function addSequencerL2BatchFromOrigin(
uint256 sequenceNumber,
bytes calldata data,
uint256 afterDelayedMessagesRead,
IGasRefunder gasRefunder
) external;
function addSequencerL2BatchFromOrigin(
uint256 sequenceNumber,
bytes calldata data,
uint256 afterDelayedMessagesRead,
IGasRefunder gasRefunder,
uint256 prevMessageCount,
uint256 newMessageCount
) external;
function addSequencerL2Batch(
uint256 sequenceNumber,
bytes calldata data,
uint256 afterDelayedMessagesRead,
IGasRefunder gasRefunder,
uint256 prevMessageCount,
uint256 newMessageCount
) external;
function addSequencerL2BatchFromBlobs(
uint256 sequenceNumber,
uint256 afterDelayedMessagesRead,
IGasRefunder gasRefunder,
uint256 prevMessageCount,
uint256 newMessageCount
) external;
// ---------- onlyRollupOrOwner functions ----------
/**
* @notice Set max delay for sequencer inbox
* @param maxTimeVariation_ the maximum time variation parameters
*/
function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external;
/**
* @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox
* @param addr the address
* @param isBatchPoster_ if the specified address should be authorized as a batch poster
*/
function setIsBatchPoster(address addr, bool isBatchPoster_) external;
/**
* @notice Makes Data Availability Service keyset valid
* @param keysetBytes bytes of the serialized keyset
*/
function setValidKeyset(bytes calldata keysetBytes) external;
/**
* @notice Invalidates a Data Availability Service keyset
* @param ksHash hash of the keyset
*/
function invalidateKeysetHash(bytes32 ksHash) external;
/**
* @notice Updates whether an address is authorized to be a sequencer.
* @dev The IsSequencer information is used only off-chain by the nitro node to validate sequencer feed signer.
* @param addr the address
* @param isSequencer_ if the specified address should be authorized as a sequencer
*/
function setIsSequencer(address addr, bool isSequencer_) external;
/**
* @notice Updates the batch poster manager, the address which has the ability to rotate batch poster keys
* @param newBatchPosterManager The new batch poster manager to be set
*/
function setBatchPosterManager(address newBatchPosterManager) external;
/// @notice Allows the rollup owner to sync the rollup address
function updateRollupAddress() external;
// ---------- initializer ----------
function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external;
}// Copyright 2021-2022, Offchain Labs, Inc.
// For license information, see https://github.com/OffchainLabs/nitro-contracts/blob/main/LICENSE
// SPDX-License-Identifier: BUSL-1.1
// solhint-disable-next-line compiler-version
pragma solidity >=0.6.9 <0.9.0;
interface IGasRefunder {
function onGasSpent(
address payable spender,
uint256 gasUsed,
uint256 calldataSize
) external returns (bool success);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IJBPermissioned} from "./../interfaces/IJBPermissioned.sol";
import {IJBPermissions} from "./../interfaces/IJBPermissions.sol";
/// @notice Modifiers to allow access to transactions based on which permissions the message's sender has.
abstract contract JBPermissioned is Context, IJBPermissioned {
//*********************************************************************//
// --------------------------- custom errors -------------------------- //
//*********************************************************************//
error JBPermissioned_Unauthorized(address account, address sender, uint256 projectId, uint256 permissionId);
//*********************************************************************//
// ---------------- public immutable stored properties --------------- //
//*********************************************************************//
/// @notice A contract storing permissions.
IJBPermissions public immutable override PERMISSIONS;
//*********************************************************************//
// -------------------------- constructor ---------------------------- //
//*********************************************************************//
/// @param permissions A contract storing permissions.
constructor(IJBPermissions permissions) {
PERMISSIONS = permissions;
}
//*********************************************************************//
// -------------------------- internal views ------------------------- //
//*********************************************************************//
/// @notice Require the message sender to be the account or have the relevant permission.
/// @param account The account to allow.
/// @param projectId The project ID to check the permission under.
/// @param permissionId The required permission ID. The operator must have this permission within the specified
/// project ID.
function _requirePermissionFrom(address account, uint256 projectId, uint256 permissionId) internal view {
address sender = _msgSender();
if (
sender != account
&& !PERMISSIONS.hasPermission({
operator: sender,
account: account,
projectId: projectId,
permissionId: permissionId,
includeRoot: true,
includeWildcardProjectId: true
})
) revert JBPermissioned_Unauthorized(account, sender, projectId, permissionId);
}
/// @notice If the 'alsoGrantAccessIf' condition is truthy, proceed. Otherwise, require the message sender to be the
/// account or
/// have the relevant permission.
/// @param account The account to allow.
/// @param projectId The project ID to check the permission under.
/// @param permissionId The required permission ID. The operator must have this permission within the specified
/// project ID.
/// @param alsoGrantAccessIf An override condition which will allow access regardless of permissions.
function _requirePermissionAllowingOverrideFrom(
address account,
uint256 projectId,
uint256 permissionId,
bool alsoGrantAccessIf
)
internal
view
{
if (alsoGrantAccessIf) return;
_requirePermissionFrom(account, projectId, permissionId);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice A ruleset's approval status in a ruleset approval hook.
enum JBApprovalStatus {
Empty,
Upcoming,
Active,
ApprovalExpected,
Approved,
Failed
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {JBAfterCashOutRecordedContext} from "./../structs/JBAfterCashOutRecordedContext.sol";
/// @notice Hook called after a terminal's `cashOutTokensOf(...)` logic completes (if passed by the ruleset's data
/// hook).
interface IJBCashOutHook is IERC165 {
/// @notice This function is called by the terminal's `cashOutTokensOf(...)` function after the cash out has been
/// recorded in the terminal store.
/// @dev Critical business logic should be protected by appropriate access control.
/// @param context The context passed in by the terminal, as a `JBAfterCashOutRecordedContext` struct.
function afterCashOutRecordedWith(JBAfterCashOutRecordedContext calldata context) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBCashOutHook} from "./IJBCashOutHook.sol";
import {IJBTerminal} from "./IJBTerminal.sol";
import {JBAfterCashOutRecordedContext} from "../structs/JBAfterCashOutRecordedContext.sol";
/// @notice A terminal that can be cashed out from.
interface IJBCashOutTerminal is IJBTerminal {
event HookAfterRecordCashOut(
IJBCashOutHook indexed hook,
JBAfterCashOutRecordedContext context,
uint256 specificationAmount,
uint256 fee,
address caller
);
event CashOutTokens(
uint256 indexed rulesetId,
uint256 indexed rulesetCycleNumber,
uint256 indexed projectId,
address holder,
address beneficiary,
uint256 cashOutCount,
uint256 cashOutTaxRate,
uint256 reclaimAmount,
bytes metadata,
address caller
);
function cashOutTokensOf(
address holder,
uint256 projectId,
uint256 cashOutCount,
address tokenToReclaim,
uint256 minTokensReclaimed,
address payable beneficiary,
bytes calldata metadata
)
external
returns (uint256 reclaimAmount);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IJBDirectory} from "./IJBDirectory.sol";
import {IJBDirectoryAccessControl} from "./IJBDirectoryAccessControl.sol";
import {IJBFundAccessLimits} from "./IJBFundAccessLimits.sol";
import {IJBPriceFeed} from "./IJBPriceFeed.sol";
import {IJBPrices} from "./IJBPrices.sol";
import {IJBProjects} from "./IJBProjects.sol";
import {IJBProjectUriRegistry} from "./IJBProjectUriRegistry.sol";
import {IJBRulesets} from "./IJBRulesets.sol";
import {IJBSplits} from "./IJBSplits.sol";
import {IJBTerminal} from "./IJBTerminal.sol";
import {IJBToken} from "./IJBToken.sol";
import {IJBTokens} from "./IJBTokens.sol";
import {JBApprovalStatus} from "./../enums/JBApprovalStatus.sol";
import {JBRuleset} from "./../structs/JBRuleset.sol";
import {JBRulesetConfig} from "./../structs/JBRulesetConfig.sol";
import {JBRulesetMetadata} from "./../structs/JBRulesetMetadata.sol";
import {JBRulesetWithMetadata} from "./../structs/JBRulesetWithMetadata.sol";
import {JBSplit} from "./../structs/JBSplit.sol";
import {JBSplitGroup} from "./../structs/JBSplitGroup.sol";
import {JBTerminalConfig} from "./../structs/JBTerminalConfig.sol";
interface IJBController is IERC165, IJBProjectUriRegistry, IJBDirectoryAccessControl {
event BurnTokens(
address indexed holder, uint256 indexed projectId, uint256 tokenCount, string memo, address caller
);
event DeployERC20(
uint256 indexed projectId, address indexed deployer, bytes32 salt, bytes32 saltHash, address caller
);
event LaunchProject(uint256 rulesetId, uint256 projectId, string projectUri, string memo, address caller);
event LaunchRulesets(uint256 rulesetId, uint256 projectId, string memo, address caller);
event MintTokens(
address indexed beneficiary,
uint256 indexed projectId,
uint256 tokenCount,
uint256 beneficiaryTokenCount,
string memo,
uint256 reservedPercent,
address caller
);
event PrepMigration(uint256 indexed projectId, address from, address caller);
event QueueRulesets(uint256 rulesetId, uint256 projectId, string memo, address caller);
event ReservedDistributionReverted(
uint256 indexed projectId, JBSplit split, uint256 tokenCount, bytes reason, address caller
);
event SendReservedTokensToSplit(
uint256 indexed projectId,
uint256 indexed rulesetId,
uint256 indexed groupId,
JBSplit split,
uint256 tokenCount,
address caller
);
event SendReservedTokensToSplits(
uint256 indexed rulesetId,
uint256 indexed rulesetCycleNumber,
uint256 indexed projectId,
address owner,
uint256 tokenCount,
uint256 leftoverAmount,
address caller
);
event SetUri(uint256 indexed projectId, string uri, address caller);
function DIRECTORY() external view returns (IJBDirectory);
function FUND_ACCESS_LIMITS() external view returns (IJBFundAccessLimits);
function PRICES() external view returns (IJBPrices);
function PROJECTS() external view returns (IJBProjects);
function RULESETS() external view returns (IJBRulesets);
function SPLITS() external view returns (IJBSplits);
function TOKENS() external view returns (IJBTokens);
function OMNICHAIN_RULESET_OPERATOR() external view returns (address);
function allRulesetsOf(
uint256 projectId,
uint256 startingId,
uint256 size
)
external
view
returns (JBRulesetWithMetadata[] memory rulesets);
function currentRulesetOf(uint256 projectId)
external
view
returns (JBRuleset memory ruleset, JBRulesetMetadata memory metadata);
function getRulesetOf(
uint256 projectId,
uint256 rulesetId
)
external
view
returns (JBRuleset memory ruleset, JBRulesetMetadata memory metadata);
function latestQueuedRulesetOf(uint256 projectId)
external
view
returns (JBRuleset memory, JBRulesetMetadata memory metadata, JBApprovalStatus);
function pendingReservedTokenBalanceOf(uint256 projectId) external view returns (uint256);
function totalTokenSupplyWithReservedTokensOf(uint256 projectId) external view returns (uint256);
function upcomingRulesetOf(uint256 projectId)
external
view
returns (JBRuleset memory ruleset, JBRulesetMetadata memory metadata);
function addPriceFeed(
uint256 projectId,
uint256 pricingCurrency,
uint256 unitCurrency,
IJBPriceFeed feed
)
external;
function burnTokensOf(address holder, uint256 projectId, uint256 tokenCount, string calldata memo) external;
function claimTokensFor(address holder, uint256 projectId, uint256 tokenCount, address beneficiary) external;
function deployERC20For(
uint256 projectId,
string calldata name,
string calldata symbol,
bytes32 salt
)
external
returns (IJBToken token);
function launchProjectFor(
address owner,
string calldata projectUri,
JBRulesetConfig[] calldata rulesetConfigurations,
JBTerminalConfig[] memory terminalConfigurations,
string calldata memo
)
external
returns (uint256 projectId);
function launchRulesetsFor(
uint256 projectId,
JBRulesetConfig[] calldata rulesetConfigurations,
JBTerminalConfig[] memory terminalConfigurations,
string calldata memo
)
external
returns (uint256 rulesetId);
function mintTokensOf(
uint256 projectId,
uint256 tokenCount,
address beneficiary,
string calldata memo,
bool useReservedPercent
)
external
returns (uint256 beneficiaryTokenCount);
function queueRulesetsOf(
uint256 projectId,
JBRulesetConfig[] calldata rulesetConfigurations,
string calldata memo
)
external
returns (uint256 rulesetId);
function sendReservedTokensToSplitsOf(uint256 projectId) external returns (uint256);
function setSplitGroupsOf(uint256 projectId, uint256 rulesetId, JBSplitGroup[] calldata splitGroups) external;
function setTokenFor(uint256 projectId, IJBToken token) external;
function transferCreditsFrom(address holder, uint256 projectId, address recipient, uint256 creditCount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IJBProjects} from "./IJBProjects.sol";
import {IJBTerminal} from "./IJBTerminal.sol";
interface IJBDirectory {
event AddTerminal(uint256 indexed projectId, IJBTerminal indexed terminal, address caller);
event SetController(uint256 indexed projectId, IERC165 indexed controller, address caller);
event SetIsAllowedToSetFirstController(address indexed addr, bool indexed isAllowed, address caller);
event SetPrimaryTerminal(
uint256 indexed projectId, address indexed token, IJBTerminal indexed terminal, address caller
);
event SetTerminals(uint256 indexed projectId, IJBTerminal[] terminals, address caller);
function PROJECTS() external view returns (IJBProjects);
function controllerOf(uint256 projectId) external view returns (IERC165);
function isAllowedToSetFirstController(address account) external view returns (bool);
function isTerminalOf(uint256 projectId, IJBTerminal terminal) external view returns (bool);
function primaryTerminalOf(uint256 projectId, address token) external view returns (IJBTerminal);
function terminalsOf(uint256 projectId) external view returns (IJBTerminal[] memory);
function setControllerOf(uint256 projectId, IERC165 controller) external;
function setIsAllowedToSetFirstController(address account, bool flag) external;
function setPrimaryTerminalOf(uint256 projectId, address token, IJBTerminal terminal) external;
function setTerminalsOf(uint256 projectId, IJBTerminal[] calldata terminals) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IJBDirectoryAccessControl {
function setControllerAllowed(uint256 projectId) external view returns (bool);
function setTerminalsAllowed(uint256 projectId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBCurrencyAmount} from "./../structs/JBCurrencyAmount.sol";
import {JBFundAccessLimitGroup} from "./../structs/JBFundAccessLimitGroup.sol";
interface IJBFundAccessLimits {
event SetFundAccessLimits(
uint256 indexed rulesetId,
uint256 indexed projectId,
JBFundAccessLimitGroup fundAccessLimitGroup,
address caller
);
function payoutLimitOf(
uint256 projectId,
uint256 rulesetId,
address terminal,
address token,
uint256 currency
)
external
view
returns (uint256 payoutLimit);
function payoutLimitsOf(
uint256 projectId,
uint256 rulesetId,
address terminal,
address token
)
external
view
returns (JBCurrencyAmount[] memory payoutLimits);
function surplusAllowanceOf(
uint256 projectId,
uint256 rulesetId,
address terminal,
address token,
uint256 currency
)
external
view
returns (uint256 surplusAllowance);
function surplusAllowancesOf(
uint256 projectId,
uint256 rulesetId,
address terminal,
address token
)
external
view
returns (JBCurrencyAmount[] memory surplusAllowances);
function setFundAccessLimitsFor(
uint256 projectId,
uint256 rulesetId,
JBFundAccessLimitGroup[] memory fundAccessLimitGroups
)
external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {JBAfterPayRecordedContext} from "./../structs/JBAfterPayRecordedContext.sol";
/// @notice Hook called after a terminal's `pay(...)` logic completes (if passed by the ruleset's data hook).
interface IJBPayHook is IERC165 {
/// @notice This function is called by the terminal's `pay(...)` function after the payment has been recorded in the
/// terminal store.
/// @dev Critical business logic should be protected by appropriate access control.
/// @param context The context passed in by the terminal, as a `JBAfterPayRecordedContext` struct.
function afterPayRecordedWith(JBAfterPayRecordedContext calldata context) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBPermissions} from "./IJBPermissions.sol";
interface IJBPermissioned {
function PERMISSIONS() external view returns (IJBPermissions);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBPermissionsData} from "./../structs/JBPermissionsData.sol";
interface IJBPermissions {
event OperatorPermissionsSet(
address indexed operator,
address indexed account,
uint256 indexed projectId,
uint8[] permissionIds,
uint256 packed,
address caller
);
function WILDCARD_PROJECT_ID() external view returns (uint256);
function permissionsOf(address operator, address account, uint256 projectId) external view returns (uint256);
function hasPermission(
address operator,
address account,
uint256 projectId,
uint256 permissionId,
bool includeRoot,
bool includeWildcardProjectId
)
external
view
returns (bool);
function hasPermissions(
address operator,
address account,
uint256 projectId,
uint256[] calldata permissionIds,
bool includeRoot,
bool includeWildcardProjectId
)
external
view
returns (bool);
function setPermissionsFor(address account, JBPermissionsData calldata permissionsData) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IJBPriceFeed {
function currentUnitPrice(uint256 targetDecimals) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBPriceFeed} from "./IJBPriceFeed.sol";
import {IJBProjects} from "./IJBProjects.sol";
interface IJBPrices {
event AddPriceFeed(
uint256 indexed projectId,
uint256 indexed pricingCurrency,
uint256 indexed unitCurrency,
IJBPriceFeed feed,
address caller
);
function DEFAULT_PROJECT_ID() external view returns (uint256);
function PROJECTS() external view returns (IJBProjects);
function priceFeedFor(
uint256 projectId,
uint256 pricingCurrency,
uint256 unitCurrency
)
external
view
returns (IJBPriceFeed);
function pricePerUnitOf(
uint256 projectId,
uint256 pricingCurrency,
uint256 unitCurrency,
uint256 decimals
)
external
view
returns (uint256);
function addPriceFeedFor(
uint256 projectId,
uint256 pricingCurrency,
uint256 unitCurrency,
IJBPriceFeed feed
)
external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IJBProjectUriRegistry {
function uriOf(uint256 projectId) external view returns (string memory);
function setUriOf(uint256 projectId, string calldata uri) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IJBTokenUriResolver} from "./IJBTokenUriResolver.sol";
interface IJBProjects is IERC721 {
event Create(uint256 indexed projectId, address indexed owner, address caller);
event SetTokenUriResolver(IJBTokenUriResolver indexed resolver, address caller);
function count() external view returns (uint256);
function tokenUriResolver() external view returns (IJBTokenUriResolver);
function createFor(address owner) external returns (uint256 projectId);
function setTokenUriResolver(IJBTokenUriResolver resolver) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {JBRuleset} from "./../structs/JBRuleset.sol";
import {JBApprovalStatus} from "./../enums/JBApprovalStatus.sol";
/// @notice `IJBRulesetApprovalHook`s are used to determine whether the next ruleset in the ruleset queue is approved or
/// rejected.
/// @dev Project rulesets are stored in a queue. Rulesets take effect after the previous ruleset in the queue ends, and
/// only if they are approved by the previous ruleset's approval hook.
interface IJBRulesetApprovalHook is IERC165 {
function DURATION() external view returns (uint256);
function approvalStatusOf(uint256 projectId, JBRuleset memory ruleset) external view returns (JBApprovalStatus);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBApprovalStatus} from "./../enums/JBApprovalStatus.sol";
import {JBRuleset} from "./../structs/JBRuleset.sol";
import {IJBRulesetApprovalHook} from "./IJBRulesetApprovalHook.sol";
interface IJBRulesets {
event RulesetInitialized(
uint256 indexed rulesetId, uint256 indexed projectId, uint256 indexed basedOnId, address caller
);
event RulesetQueued(
uint256 indexed rulesetId,
uint256 indexed projectId,
uint256 duration,
uint256 weight,
uint256 weightCutPercent,
IJBRulesetApprovalHook approvalHook,
uint256 metadata,
uint256 mustStartAtOrAfter,
address caller
);
event WeightCacheUpdated(uint256 projectId, uint112 weight, uint256 weightCutMultiple, address caller);
function latestRulesetIdOf(uint256 projectId) external view returns (uint256);
function currentApprovalStatusForLatestRulesetOf(uint256 projectId) external view returns (JBApprovalStatus);
function currentOf(uint256 projectId) external view returns (JBRuleset memory ruleset);
function deriveCycleNumberFrom(
uint256 baseRulesetCycleNumber,
uint256 baseRulesetStart,
uint256 baseRulesetDuration,
uint256 start
)
external
returns (uint256);
function deriveStartFrom(
uint256 baseRulesetStart,
uint256 baseRulesetDuration,
uint256 mustStartAtOrAfter
)
external
view
returns (uint256 start);
function deriveWeightFrom(
uint256 projectId,
uint256 baseRulesetStart,
uint256 baseRulesetDuration,
uint256 baseRulesetWeight,
uint256 baseRulesetWeightCutPercent,
uint256 baseRulesetCacheId,
uint256 start
)
external
view
returns (uint256 weight);
function getRulesetOf(uint256 projectId, uint256 rulesetId) external view returns (JBRuleset memory);
function latestQueuedOf(uint256 projectId)
external
view
returns (JBRuleset memory ruleset, JBApprovalStatus approvalStatus);
function allOf(
uint256 projectId,
uint256 startingId,
uint256 size
)
external
view
returns (JBRuleset[] memory rulesets);
function upcomingOf(uint256 projectId) external view returns (JBRuleset memory ruleset);
function queueFor(
uint256 projectId,
uint256 duration,
uint256 weight,
uint256 weightCutPercent,
IJBRulesetApprovalHook approvalHook,
uint256 metadata,
uint256 mustStartAtOrAfter
)
external
returns (JBRuleset memory ruleset);
function updateRulesetWeightCache(uint256 projectId) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {JBSplitHookContext} from "../structs/JBSplitHookContext.sol";
/// @title Split hook
/// @notice Allows processing a single split with custom logic.
/// @dev The split hook's address should be set as the `hook` in the relevant split.
interface IJBSplitHook is IERC165 {
/// @notice If a split has a split hook, payment terminals and controllers call this function while processing the
/// split.
/// @dev Critical business logic should be protected by appropriate access control. The tokens and/or native tokens
/// are optimistically transferred to the split hook when this function is called.
/// @param context The context passed by the terminal/controller to the split hook as a `JBSplitHookContext` struct:
function processSplitWith(JBSplitHookContext calldata context) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBSplit} from "./../structs/JBSplit.sol";
import {JBSplitGroup} from "./../structs/JBSplitGroup.sol";
interface IJBSplits {
event SetSplit(
uint256 indexed projectId, uint256 indexed rulesetId, uint256 indexed groupId, JBSplit split, address caller
);
function FALLBACK_RULESET_ID() external view returns (uint256);
function splitsOf(uint256 projectId, uint256 rulesetId, uint256 groupId) external view returns (JBSplit[] memory);
function setSplitGroupsOf(uint256 projectId, uint256 rulesetId, JBSplitGroup[] memory splitGroups) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IJBPayHook} from "./IJBPayHook.sol";
import {JBAccountingContext} from "../structs/JBAccountingContext.sol";
import {JBAfterPayRecordedContext} from "../structs/JBAfterPayRecordedContext.sol";
/// @notice A terminal that accepts payments and can be migrated.
interface IJBTerminal is IERC165 {
event AddToBalance(
uint256 indexed projectId, uint256 amount, uint256 returnedFees, string memo, bytes metadata, address caller
);
event HookAfterRecordPay(
IJBPayHook indexed hook, JBAfterPayRecordedContext context, uint256 specificationAmount, address caller
);
event MigrateTerminal(
uint256 indexed projectId, address indexed token, IJBTerminal indexed to, uint256 amount, address caller
);
event Pay(
uint256 indexed rulesetId,
uint256 indexed rulesetCycleNumber,
uint256 indexed projectId,
address payer,
address beneficiary,
uint256 amount,
uint256 newlyIssuedTokenCount,
string memo,
bytes metadata,
address caller
);
event SetAccountingContext(uint256 indexed projectId, JBAccountingContext context, address caller);
function accountingContextForTokenOf(
uint256 projectId,
address token
)
external
view
returns (JBAccountingContext memory);
function accountingContextsOf(uint256 projectId) external view returns (JBAccountingContext[] memory);
function currentSurplusOf(
uint256 projectId,
JBAccountingContext[] memory accountingContexts,
uint256 decimals,
uint256 currency
)
external
view
returns (uint256);
function addAccountingContextsFor(uint256 projectId, JBAccountingContext[] calldata accountingContexts) external;
function addToBalanceOf(
uint256 projectId,
address token,
uint256 amount,
bool shouldReturnHeldFees,
string calldata memo,
bytes calldata metadata
)
external
payable;
function migrateBalanceOf(uint256 projectId, address token, IJBTerminal to) external returns (uint256 balance);
function pay(
uint256 projectId,
address token,
uint256 amount,
address beneficiary,
uint256 minReturnedTokens,
string calldata memo,
bytes calldata metadata
)
external
payable
returns (uint256 beneficiaryTokenCount);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IJBToken {
function balanceOf(address account) external view returns (uint256);
function canBeAddedTo(uint256 projectId) external view returns (bool);
function decimals() external view returns (uint8);
function totalSupply() external view returns (uint256);
function initialize(string memory name, string memory symbol, address owner) external;
function burn(address account, uint256 amount) external;
function mint(address account, uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IJBTokenUriResolver {
function getUri(uint256 projectId) external view returns (string memory tokenUri);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBToken} from "./IJBToken.sol";
interface IJBTokens {
event DeployERC20(
uint256 indexed projectId, IJBToken indexed token, string name, string symbol, bytes32 salt, address caller
);
event Burn(
address indexed holder,
uint256 indexed projectId,
uint256 count,
uint256 creditBalance,
uint256 tokenBalance,
address caller
);
event ClaimTokens(
address indexed holder,
uint256 indexed projectId,
uint256 creditBalance,
uint256 count,
address beneficiary,
address caller
);
event Mint(
address indexed holder, uint256 indexed projectId, uint256 count, bool tokensWereClaimed, address caller
);
event SetToken(uint256 indexed projectId, IJBToken indexed token, address caller);
event TransferCredits(
address indexed holder, uint256 indexed projectId, address indexed recipient, uint256 count, address caller
);
function creditBalanceOf(address holder, uint256 projectId) external view returns (uint256);
function projectIdOf(IJBToken token) external view returns (uint256);
function tokenOf(uint256 projectId) external view returns (IJBToken);
function totalCreditSupplyOf(uint256 projectId) external view returns (uint256);
function totalBalanceOf(address holder, uint256 projectId) external view returns (uint256 result);
function totalSupplyOf(uint256 projectId) external view returns (uint256);
function burnFrom(address holder, uint256 projectId, uint256 count) external;
function claimTokensFor(address holder, uint256 projectId, uint256 count, address beneficiary) external;
function deployERC20For(
uint256 projectId,
string calldata name,
string calldata symbol,
bytes32 salt
)
external
returns (IJBToken token);
function mintFor(address holder, uint256 projectId, uint256 count) external returns (IJBToken token);
function setTokenFor(uint256 projectId, IJBToken token) external;
function transferCreditsFrom(address holder, uint256 projectId, address recipient, uint256 count) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Global constants used across Juicebox contracts.
library JBConstants {
/// @notice Each chain's native token address in Juicebox is represented by
/// 0x000000000000000000000000000000000000EEEe.
address public constant NATIVE_TOKEN = address(0x000000000000000000000000000000000000EEEe);
uint16 public constant MAX_RESERVED_PERCENT = 10_000;
uint16 public constant MAX_CASH_OUT_TAX_RATE = 10_000;
uint32 public constant MAX_WEIGHT_CUT_PERCENT = 1_000_000_000;
uint32 public constant SPLITS_TOTAL_PERCENT = 1_000_000_000;
uint16 public constant MAX_FEE = 1000;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:member token The address of the token that accounting is being done with.
/// @custom:member decimals The number of decimals expected in that token's fixed point accounting.
/// @custom:member currency The currency that the token is priced in terms of. By convention, this is
/// `uint32(uint160(tokenAddress))` for tokens, or a constant ID from e.g. `JBCurrencyIds` for other currencies.
struct JBAccountingContext {
address token;
uint8 decimals;
uint32 currency;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBTokenAmount} from "./JBTokenAmount.sol";
/// @custom:member holder The holder of the tokens being cashed out.
/// @custom:member projectId The ID of the project being cashed out from.
/// @custom:member rulesetId The ID of the ruleset the cash out is being made during.
/// @custom:member cashOutCount The number of project tokens being cashed out.
/// @custom:member cashOutTaxRate The current ruleset's cash out tax rate.
/// @custom:member reclaimedAmount The token amount being reclaimed from the project's terminal balance. Includes the
/// token being
/// reclaimed, the value, the number of decimals included, and the currency of the amount.
/// @custom:member forwardedAmount The token amount being forwarded to the cash out hook. Includes the token
/// being forwarded, the value, the number of decimals included, and the currency of the amount.
/// @custom:member beneficiary The address the reclaimed amount will be sent to.
/// @custom:member hookMetadata Extra data specified by the data hook, which is sent to the cash out hook.
/// @custom:member cashOutMetadata Extra data specified by the account cashing out, which is sent to the cash out hook.
struct JBAfterCashOutRecordedContext {
address holder;
uint256 projectId;
uint256 rulesetId;
uint256 cashOutCount;
JBTokenAmount reclaimedAmount;
JBTokenAmount forwardedAmount;
uint256 cashOutTaxRate;
address payable beneficiary;
bytes hookMetadata;
bytes cashOutMetadata;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBTokenAmount} from "./JBTokenAmount.sol";
/// @custom:member payer The address the payment originated from.
/// @custom:member projectId The ID of the project being paid.
/// @custom:member rulesetId The ID of the ruleset the payment is being made during.
/// @custom:member amount The payment's token amount. Includes the token being paid, the value, the number of decimals
/// included, and the currency of the amount.
/// @custom:member forwardedAmount The token amount being forwarded to the pay hook. Includes the token
/// being paid, the value, the number of decimals included, and the currency of the amount.
/// @custom:member weight The current ruleset's weight (used to determine how many tokens should be minted).
/// @custom:member newlyIssuedTokenCount The number of project tokens minted for the beneficiary.
/// @custom:member beneficiary The address which receives any tokens this payment yields.
/// @custom:member hookMetadata Extra data specified by the data hook, which is sent to the pay hook.
/// @custom:member payerMetadata Extra data specified by the payer, which is sent to the pay hook.
struct JBAfterPayRecordedContext {
address payer;
uint256 projectId;
uint256 rulesetId;
JBTokenAmount amount;
JBTokenAmount forwardedAmount;
uint256 weight;
uint256 newlyIssuedTokenCount;
address beneficiary;
bytes hookMetadata;
bytes payerMetadata;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:member amount The amount of the currency.
/// @custom:member currency The currency. By convention, this is `uint32(uint160(tokenAddress))` for tokens, or a
/// constant ID from e.g. `JBCurrencyIds` for other currencies.
struct JBCurrencyAmount {
uint224 amount;
uint32 currency;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBCurrencyAmount} from "./JBCurrencyAmount.sol";
/// @dev Payout limit example: if the `amount` is 5, the `currency` is 1 (USD), and the terminal's token is ETH, then
/// the project can pay out 5 USD worth of ETH during a ruleset.
/// @dev Surplus allowance example: if the `amount` is 5, the `currency` is 1 (USD), and the terminal's token is ETH,
/// then the project can pay out 5 USD worth of ETH from its surplus during a ruleset. A project's surplus is its
/// balance minus its current combined payout limit.
/// @dev If a project has multiple payout limits or surplus allowances, they are all available. They can all be used
/// during a single ruleset.
/// @dev The payout limits' and surplus allowances' fixed point amounts have the same number of decimals as the
/// terminal.
/// @custom:member terminal The terminal that the payout limits and surplus allowances apply to.
/// @custom:member token The token that the payout limits and surplus allowances apply to within the `terminal`.
/// @custom:member payoutLimits An array of payout limits. The payout limits cumulatively dictate the maximum value of
/// `token`s a project can pay out from its balance in a terminal during a ruleset. Each payout limit can have a unique
/// currency and amount.
/// @custom:member surplusAllowances An array of surplus allowances. The surplus allowances cumulatively dictates the
/// maximum value of `token`s a project can pay out from its surplus (balance less payouts) in a terminal during a
/// ruleset. Each surplus allowance can have a unique currency and amount.
struct JBFundAccessLimitGroup {
address terminal;
address token;
JBCurrencyAmount[] payoutLimits;
JBCurrencyAmount[] surplusAllowances;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:member operator The address that permissions are being given to.
/// @custom:member projectId The ID of the project the operator is being given permissions for. Operators only have
/// permissions under this project's scope. An ID of 0 is a wildcard, which gives an operator permissions across all
/// projects.
/// @custom:member permissionIds The IDs of the permissions being given. See the `JBPermissionIds` library.
struct JBPermissionsData {
address operator;
uint64 projectId;
uint8[] permissionIds;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBRulesetApprovalHook} from "./../interfaces/IJBRulesetApprovalHook.sol";
/// @dev `JBRuleset` timestamps are unix timestamps (seconds since 00:00 January 1st, 1970 UTC).
/// @custom:member cycleNumber The ruleset's cycle number. Each ruleset's `cycleNumber` is the previous ruleset's
/// `cycleNumber` plus one. Each project's first ruleset has a `cycleNumber` of 1.
/// @custom:member id The ruleset's ID, which is a timestamp of when this ruleset's rules were initialized. The
/// `rulesetId` stays the same for rulesets that automatically cycle over from a manually queued ruleset.
/// @custom:member basedOnId The `rulesetId` of the ruleset which was active when this ruleset was created.
/// @custom:member start The timestamp from which this ruleset is considered active.
/// @custom:member duration The number of seconds the ruleset lasts for. After this duration, a new ruleset will start.
/// The project owner can queue new rulesets at any time, which will take effect once the current ruleset's duration is
/// over. If the `duration` is 0, newly queued rulesets will take effect immediately. If a ruleset ends and there are no
/// new rulesets queued, the current ruleset cycles over to another one with the same properties but a new `start`
/// timestamp and a `weight` reduced by the ruleset's `weightCutPercent`.
/// @custom:member weight A fixed point number with 18 decimals which is typically used by payment terminals to
/// determine how many tokens should be minted when a payment is received. This can be used by other contracts for
/// arbitrary calculations.
/// @custom:member weightCutPercent The percentage by which to reduce the `weight` each time a new ruleset starts.
/// `weight`
/// is
/// a percentage out of `JBConstants.MAX_WEIGHT_CUT_PERCENT`. If it's 0, the next ruleset will have the same `weight` by
/// default. If it's 90%, the next ruleset's `weight` will be 10% smaller. If a ruleset explicitly sets a new `weight`,
/// the `weightCutPercent` doesn't apply.
/// @custom:member approvalHook An address of a contract that says whether a queued ruleset should be approved or
/// rejected. If a
/// ruleset is rejected, it won't go into effect. An approval hook can be used to create rules which dictate how a
/// project owner can change their ruleset over time.
/// @custom:member metadata Extra data associated with a ruleset which can be used by other contracts.
struct JBRuleset {
uint48 cycleNumber;
uint48 id;
uint48 basedOnId;
uint48 start;
uint32 duration;
uint112 weight;
uint32 weightCutPercent;
IJBRulesetApprovalHook approvalHook;
uint256 metadata;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBRulesetApprovalHook} from "../interfaces/IJBRulesetApprovalHook.sol";
import {JBFundAccessLimitGroup} from "./JBFundAccessLimitGroup.sol";
import {JBRulesetMetadata} from "./JBRulesetMetadata.sol";
import {JBSplitGroup} from "./JBSplitGroup.sol";
/// @custom:member mustStartAtOrAfter The earliest time the ruleset can start.
/// @custom:member duration The number of seconds the ruleset lasts for, after which a new ruleset will start. A
/// duration of 0 means that the ruleset will stay active until the project owner explicitly issues a reconfiguration,
/// at which point a new ruleset will immediately start with the updated properties. If the duration is greater than 0,
/// a project owner cannot make changes to a ruleset's parameters while it is active – any proposed changes will apply
/// to the subsequent ruleset. If no changes are proposed, a ruleset rolls over to another one with the same properties
/// but new `start` timestamp and a cut `weight`.
/// @custom:member weight A fixed point number with 18 decimals that contracts can use to base arbitrary calculations
/// on. For example, payment terminals can use this to determine how many tokens should be minted when a payment is
/// received.
/// @custom:member weightCutPercent A percent by how much the `weight` of the subsequent ruleset should be reduced, if
/// the
/// project owner hasn't queued the subsequent ruleset with an explicit `weight`. If it's 0, each ruleset will have
/// equal weight. If the number is 90%, the next ruleset will have a 10% smaller weight. This weight is out of
/// `JBConstants.MAX_WEIGHT_CUT_PERCENT`.
/// @custom:member approvalHook An address of a contract that says whether a proposed ruleset should be accepted or
/// rejected. It
/// can be used to create rules around how a project owner can change ruleset parameters over time.
/// @custom:member metadata Metadata specifying the controller-specific parameters that a ruleset can have. These
/// properties cannot change until the next ruleset starts.
/// @custom:member splitGroups An array of splits to use for any number of groups while the ruleset is active.
/// @custom:member fundAccessLimitGroups An array of structs which dictate the amount of funds a project can access from
/// its balance in each payment terminal while the ruleset is active. Amounts are fixed point numbers using the same
/// number of decimals as the corresponding terminal. The `_payoutLimit` and `_surplusAllowance` parameters must fit in
/// a `uint232`.
struct JBRulesetConfig {
uint48 mustStartAtOrAfter;
uint32 duration;
uint112 weight;
uint32 weightCutPercent;
IJBRulesetApprovalHook approvalHook;
JBRulesetMetadata metadata;
JBSplitGroup[] splitGroups;
JBFundAccessLimitGroup[] fundAccessLimitGroups;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:member reservedPercent The reserved percent of the ruleset. This number is a percentage calculated out of
/// `JBConstants.MAX_RESERVED_PERCENT`.
/// @custom:member cashOutTaxRate The cash out tax rate of the ruleset. This number is a percentage calculated out of
/// `JBConstants.MAX_CASH_OUT_TAX_RATE`.
/// @custom:member baseCurrency The currency on which to base the ruleset's weight. By convention, this is
/// `uint32(uint160(tokenAddress))` for tokens, or a constant ID from e.g. `JBCurrencyIds` for other currencies.
/// @custom:member pausePay A flag indicating if the pay functionality should be paused during the ruleset.
/// @custom:member pauseCreditTransfers A flag indicating if the project token transfer functionality should be paused
/// during the funding cycle.
/// @custom:member allowOwnerMinting A flag indicating if the project owner or an operator with the `MINT_TOKENS`
/// permission from the owner should be allowed to mint project tokens on demand during this ruleset.
/// @custom:member allowTerminalMigration A flag indicating if migrating terminals should be allowed during this
/// ruleset.
/// @custom:member allowSetTerminals A flag indicating if a project's terminals can be added or removed.
/// @custom:member allowSetController A flag indicating if a project's controller can be changed.
/// @custom:member allowAddAccountingContext A flag indicating if a project can add new accounting contexts for its
/// terminals to use.
/// @custom:member allowAddPriceFeed A flag indicating if a project can add new price feeds to calculate exchange rates
/// between its tokens.
/// @custom:member ownerMustSendPayouts A flag indicating if privileged payout distribution should be
/// enforced, otherwise payouts can be distributed by anyone.
/// @custom:member holdFees A flag indicating if fees should be held during this ruleset.
/// @custom:member useTotalSurplusForCashOuts A flag indicating if cash outs should use the project's balance held
/// in all terminals instead of the project's local terminal balance from which the cash out is being fulfilled.
/// @custom:member useDataHookForPay A flag indicating if the data hook should be used for pay transactions during this
/// ruleset.
/// @custom:member useDataHookForCashOut A flag indicating if the data hook should be used for cash out transactions
/// during
/// this ruleset.
/// @custom:member dataHook The data hook to use during this ruleset.
/// @custom:member metadata Metadata of the metadata, only the 14 least significant bits can be used, the 2 most
/// significant bits are disregarded.
struct JBRulesetMetadata {
uint16 reservedPercent;
uint16 cashOutTaxRate;
uint32 baseCurrency;
bool pausePay;
bool pauseCreditTransfers;
bool allowOwnerMinting;
bool allowSetCustomToken;
bool allowTerminalMigration;
bool allowSetTerminals;
bool allowSetController;
bool allowAddAccountingContext;
bool allowAddPriceFeed;
bool ownerMustSendPayouts;
bool holdFees;
bool useTotalSurplusForCashOuts;
bool useDataHookForPay;
bool useDataHookForCashOut;
address dataHook;
uint16 metadata;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBRuleset} from "./JBRuleset.sol";
import {JBRulesetMetadata} from "./JBRulesetMetadata.sol";
/// @custom:member ruleset The ruleset.
/// @custom:member metadata The ruleset's metadata.
struct JBRulesetWithMetadata {
JBRuleset ruleset;
JBRulesetMetadata metadata;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBSplitHook} from "./../interfaces/IJBSplitHook.sol";
/// @notice Splits are used to send a percentage of a total token amount to a specific contract, project, or address.
/// Splits are used to send payouts and reserved tokens.
/// @dev 1. If a non-zero split hook contract is specified, this split's tokens are sent there along with this split's
/// properties.
/// @dev 2. Otherwise, if a non-zero project ID is specified, this split's tokens are used to `pay` it through its
/// terminal if possible, or sent to the project's owner if not. If this payment yields tokens, those go to the split's
/// `beneficiary`.
/// @dev 3. Otherwise, this split's tokens are sent directly to the `beneficiary`.
/// @dev To summarize, this split's tokens are sent according to the following priority: `split hook` > `projectId` >
/// `beneficiary`.
/// @custom:member percent The percent of the total token amount that this split sends. This number is out of
/// `JBConstants.SPLITS_TOTAL_PERCENT`.
/// @custom:member projectId The ID of a project to `pay`, if applicable. Resulting tokens will be routed to the
/// `beneficiary`.
/// @custom:member beneficiary Receives this split's tokens if the `hook` and `projectId` are zero. If the `projectId`
/// is specified, the `beneficiary` receives any project tokens minted by this split.
/// @custom:member preferAddToBalance If this split were to `pay` a project through its terminal, this flag indicates
/// whether it should prefer using the terminal's `addToBalance` function instead.
/// @custom:member lockedUntil The split cannot be changed until this timestamp. The `lockedUntil` timestamp can be
/// increased while a split is locked. If `lockedUntil` is zero, this split can be changed at any time.
/// @custom:member hook A contract which will receive this split's tokens and properties, and can define custom
/// behavior.
struct JBSplit {
uint32 percent;
uint64 projectId;
address payable beneficiary;
bool preferAddToBalance;
uint48 lockedUntil;
IJBSplitHook hook;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBSplit} from "./JBSplit.sol";
/// @custom:member groupId An identifier for the group. By convention, this ID is `uint256(uint160(tokenAddress))` for
/// payouts and `1` for reserved tokens.
/// @custom:member splits The splits in the group.
struct JBSplitGroup {
uint256 groupId;
JBSplit[] splits;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBSplit} from "./JBSplit.sol";
/// @custom:member token The token being sent to the split hook.
/// @custom:member amount The amount being sent to the split hook, as a fixed point number.
/// @custom:member decimals The number of decimals in the amount.
/// @custom:member projectId The project the split belongs to.
/// @custom:member groupId The group the split belongs to. By convention, this ID is `uint256(uint160(tokenAddress))`
/// for payouts and `1` for reserved tokens.
/// @custom:member split The split which specified the hook.
struct JBSplitHookContext {
address token;
uint256 amount;
uint256 decimals;
uint256 projectId;
uint256 groupId;
JBSplit split;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBAccountingContext} from "./JBAccountingContext.sol";
import {IJBTerminal} from "./../interfaces/IJBTerminal.sol";
/// @custom:member terminal The terminal to configure.
/// @custom:member accountingContextsToAccept The accounting contexts to accept from the terminal.
struct JBTerminalConfig {
IJBTerminal terminal;
JBAccountingContext[] accountingContextsToAccept;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:member token The token the payment was made in.
/// @custom:member decimals The number of decimals included in the value fixed point number.
/// @custom:member currency The currency. By convention, this is `uint32(uint160(tokenAddress))` for tokens, or a
/// constant ID from e.g. `JBCurrencyIds` for other currencies.
/// @custom:member value The amount of tokens that was paid, as a fixed point number.
struct JBTokenAmount {
address token;
uint8 decimals;
uint32 currency;
uint256 value;
}// SPDX-License-Identifier: MIT
// slither-disable-next-line solc-version
pragma solidity ^0.8.0;
/// @notice Permission IDs for `JBPermissions`, used throughout the Bananapus ecosystem. See
/// [`JBPermissions`](https://github.com/Bananapus/nana-core/blob/main/src/JBPermissions.sol)
/// @dev `JBPermissions` allows one address to grant another address permission to call functions in Juicebox contracts
/// on their behalf. Each ID in `JBPermissionIds` grants access to a specific set of these functions.
library JBPermissionIds {
uint8 internal constant ROOT = 1; // All permissions across every contract. Very dangerous. BE CAREFUL!
/* Used by `nana-core`: https://github.com/Bananapus/nana-core */
uint8 internal constant QUEUE_RULESETS = 2; // Permission to call `JBController.queueRulesetsOf` and
// `JBController.launchRulesetsFor`.
uint8 internal constant CASH_OUT_TOKENS = 3; // Permission to call `JBMultiTerminal.cashOutTokensOf`.
uint8 internal constant SEND_PAYOUTS = 4; // Permission to call `JBMultiTerminal.sendPayoutsOf`.
uint8 internal constant MIGRATE_TERMINAL = 5; // Permission to call `JBMultiTerminal.migrateBalanceOf`.
uint8 internal constant SET_PROJECT_URI = 6; // Permission to call `JBController.setUriOf`.
uint8 internal constant DEPLOY_ERC20 = 7; // Permission to call `JBController.deployERC20For`.
uint8 internal constant SET_TOKEN = 8; // Permission to call `JBController.setTokenFor`.
uint8 internal constant MINT_TOKENS = 9; // Permission to call `JBController.mintTokensOf`.
uint8 internal constant BURN_TOKENS = 10; // Permission to call `JBController.burnTokensOf`.
uint8 internal constant CLAIM_TOKENS = 11; // Permission to call `JBController.claimTokensFor`.
uint8 internal constant TRANSFER_CREDITS = 12; // Permission to call `JBController.transferCreditsFrom`.
uint8 internal constant SET_CONTROLLER = 13; // Permission to call `JBDirectory.setControllerOf`.
uint8 internal constant SET_TERMINALS = 14; // Permission to call `JBDirectory.setTerminalsOf`.
// Be careful - `SET_TERMINALS` can be used to remove the primary terminal.
uint8 internal constant SET_PRIMARY_TERMINAL = 15; // Permission to call `JBDirectory.setPrimaryTerminalOf`.
uint8 internal constant USE_ALLOWANCE = 16; // Permission to call `JBMultiTerminal.useAllowanceOf`.
uint8 internal constant SET_SPLIT_GROUPS = 17; // Permission to call `JBController.setSplitGroupsOf`.
uint8 internal constant ADD_PRICE_FEED = 18; // Permission to call `JBPrices.addPriceFeedFor`.
uint8 internal constant ADD_ACCOUNTING_CONTEXTS = 19; // Permission to call
// `JBMultiTerminal.addAccountingContextsFor`.
/* Used by `nana-721-hook`: https://github.com/Bananapus/nana-721-hook */
uint8 internal constant ADJUST_721_TIERS = 20; // Permission to call `JB721TiersHook.adjustTiers`.
uint8 internal constant SET_721_METADATA = 21; // Permission to call `JB721TiersHook.setMetadata`.
uint8 internal constant MINT_721 = 22; // Permission to call `JB721TiersHook.mintFor`.
uint8 internal constant SET_721_DISCOUNT_PERCENT = 23; // Permission to call `JB721TiersHook.setDiscountPercentOf`.
/* Used by `nana-buyback-hook`: https://github.com/Bananapus/nana-buyback-hook */
uint8 internal constant SET_BUYBACK_TWAP = 24; // Permission to call `JBBuybackHook.setTwapWindowOf` and
// `JBBuybackHook.setTwapSlippageToleranceOf`.
uint8 internal constant SET_BUYBACK_POOL = 25; // Permission to call `JBBuybackHook.setPoolFor`.
/* Used by `nana-swap-terminal`: https://github.com/Bananapus/nana-swap-terminal */
uint8 internal constant ADD_SWAP_TERMINAL_POOL = 26; // Permission to call `JBSwapTerminal.addDefaultPool`.
uint8 internal constant ADD_SWAP_TERMINAL_TWAP_PARAMS = 27; // Permission to call
// `JBSwapTerminal.addTwapParamsFor`.
/* Used by `nana-suckers`: https://github.com/Bananapus/nana-suckers */
uint8 internal constant MAP_SUCKER_TOKEN = 28; // Permission to call `BPSucker.mapToken`.
uint8 internal constant DEPLOY_SUCKERS = 29; // Permission to call `BPSuckerRegistry.deploySuckersFor`.
uint8 internal constant SUCKER_SAFETY = 30; // Permission to call `BPSucker.enableEmergencyHatchFor` and
// `BPSucker.setDeprecation`.
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (metatx/ERC2771Context.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Context variant with ERC-2771 support.
*
* WARNING: Avoid using this pattern in contracts that rely in a specific calldata length as they'll
* be affected by any forwarder whose `msg.data` is suffixed with the `from` address according to the ERC-2771
* specification adding the address size in bytes (20) to the calldata size. An example of an unexpected
* behavior could be an unintended fallback (or another function) invocation while trying to invoke the `receive`
* function only accessible if `msg.data.length == 0`.
*
* WARNING: The usage of `delegatecall` in this contract is dangerous and may result in context corruption.
* Any forwarded request to this contract triggering a `delegatecall` to itself will result in an invalid {_msgSender}
* recovery.
*/
abstract contract ERC2771Context is Context {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _trustedForwarder;
/**
* @dev Initializes the contract with a trusted forwarder, which will be able to
* invoke functions on this contract on behalf of other accounts.
*
* NOTE: The trusted forwarder can be replaced by overriding {trustedForwarder}.
*/
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address trustedForwarder_) {
_trustedForwarder = trustedForwarder_;
}
/**
* @dev Returns the address of the trusted forwarder.
*/
function trustedForwarder() public view virtual returns (address) {
return _trustedForwarder;
}
/**
* @dev Indicates whether any particular address is the trusted forwarder.
*/
function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
return forwarder == trustedForwarder();
}
/**
* @dev Override for `msg.sender`. Defaults to the original `msg.sender` whenever
* a call is not performed by the trusted forwarder or the calldata length is less than
* 20 bytes (an address length).
*/
function _msgSender() internal view virtual override returns (address) {
uint256 calldataLength = msg.data.length;
uint256 contextSuffixLength = _contextSuffixLength();
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
return address(bytes20(msg.data[calldataLength - contextSuffixLength:]));
} else {
return super._msgSender();
}
}
/**
* @dev Override for `msg.data`. Defaults to the original `msg.data` whenever
* a call is not performed by the trusted forwarder or the calldata length is less than
* 20 bytes (an address length).
*/
function _msgData() internal view virtual override returns (bytes calldata) {
uint256 calldataLength = msg.data.length;
uint256 contextSuffixLength = _contextSuffixLength();
if (isTrustedForwarder(msg.sender) && calldataLength >= contextSuffixLength) {
return msg.data[:calldataLength - contextSuffixLength];
} else {
return super._msgData();
}
}
/**
* @dev ERC-2771 specifies the context as being a single address (20 bytes).
*/
function _contextSuffixLength() internal view virtual override returns (uint256) {
return 20;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/BitMaps.sol)
pragma solidity ^0.8.20;
/**
* @dev Library for managing uint256 to bool mapping in a compact and efficient way, provided the keys are sequential.
* Largely inspired by Uniswap's https://github.com/Uniswap/merkle-distributor/blob/master/contracts/MerkleDistributor.sol[merkle-distributor].
*
* BitMaps pack 256 booleans across each bit of a single 256-bit slot of `uint256` type.
* Hence booleans corresponding to 256 _sequential_ indices would only consume a single slot,
* unlike the regular `bool` which would consume an entire slot for a single value.
*
* This results in gas savings in two ways:
*
* - Setting a zero value to non-zero only once every 256 times
* - Accessing the same warm slot for every 256 _sequential_ indices
*/
library BitMaps {
struct BitMap {
mapping(uint256 bucket => uint256) _data;
}
/**
* @dev Returns whether the bit at `index` is set.
*/
function get(BitMap storage bitmap, uint256 index) internal view returns (bool) {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
return bitmap._data[bucket] & mask != 0;
}
/**
* @dev Sets the bit at `index` to the boolean `value`.
*/
function setTo(BitMap storage bitmap, uint256 index, bool value) internal {
if (value) {
set(bitmap, index);
} else {
unset(bitmap, index);
}
}
/**
* @dev Sets the bit at `index`.
*/
function set(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] |= mask;
}
/**
* @dev Unsets the bit at `index`.
*/
function unset(BitMap storage bitmap, uint256 index) internal {
uint256 bucket = index >> 8;
uint256 mask = 1 << (index & 0xff);
bitmap._data[bucket] &= ~mask;
}
}// 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)
/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
///
/// @dev Minimal proxy:
/// Although the sw0nt pattern saves 5 gas over the ERC1167 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 ERC1167 pattern during runtime, and has the smallest bytecode.
/// - Automatically verified on Etherscan.
///
/// @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.
/// - Automatically verified on Etherscan.
///
/// @dev Clones with immutable args (CWIA):
/// The implementation of CWIA here is does NOT append the immutable args into the calldata
/// passed into delegatecall. It is simply an ERC1167 minimal proxy with the immutable arguments
/// appended to the back of the runtime bytecode.
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 proxy:
/// An minimal ERC1967 proxy, intended to be upgraded with UUPS.
/// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I proxy:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
///
/// @dev Minimal ERC1967 beacon proxy:
/// A minimal beacon proxy, intended to be upgraded with an upgradable beacon.
/// - Automatically verified on Etherscan.
///
/// @dev Minimal ERC1967 beacon proxy with immutable args:
/// - Uses the identity precompile (0x4) to copy args during deployment.
/// - Automatically verified on Etherscan.
///
/// @dev ERC1967I beacon proxy:
/// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
///
/// @dev ERC1967I proxy with immutable args:
/// An variant of the minimal ERC1967 beacon proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// - Uses the identity precompile (0x4) to copy args during deployment.
library LibClone {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CONSTANTS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev The keccak256 of deployed code for the clone proxy,
/// with the implementation set to `address(0)`.
bytes32 internal constant CLONE_CODE_HASH =
0x48db2cfdb2853fce0b464f1f93a1996469459df3ab6c812106074c4106a1eb1f;
/// @dev The keccak256 of deployed code for the PUSH0 proxy,
/// with the implementation set to `address(0)`.
bytes32 internal constant PUSH0_CLONE_CODE_HASH =
0x67bc6bde1b84d66e267c718ba44cf3928a615d29885537955cb43d44b3e789dc;
/// @dev The keccak256 of deployed code for the ERC-1167 CWIA proxy,
/// with the implementation set to `address(0)`.
bytes32 internal constant CWIA_CODE_HASH =
0x3cf92464268225a4513da40a34d967354684c32cd0edd67b5f668dfe3550e940;
/// @dev The keccak256 of the deployed code for the ERC1967 proxy.
bytes32 internal constant ERC1967_CODE_HASH =
0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d;
/// @dev The keccak256 of the deployed code for the ERC1967I proxy.
bytes32 internal constant ERC1967I_CODE_HASH =
0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7;
/// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
bytes32 internal constant ERC1967_BEACON_PROXY_CODE_HASH =
0x14044459af17bc4f0f5aa2f658cb692add77d1302c29fe2aebab005eea9d1162;
/// @dev The keccak256 of the deployed code for the ERC1967 beacon proxy.
bytes32 internal constant ERC1967I_BEACON_PROXY_CODE_HASH =
0xf8c46d2793d5aa984eb827aeaba4b63aedcab80119212fce827309788735519a;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* CUSTOM ERRORS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Unable to deploy the clone.
error DeploymentFailed();
/// @dev The salt must start with either the zero address or `by`.
error SaltDoesNotStartWith();
/// @dev The ETH transfer has failed.
error ETHTransferFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a clone of `implementation`.
function clone(address implementation) internal returns (address instance) {
instance = clone(0, implementation);
}
/// @dev Deploys a clone of `implementation`.
/// Deposits `value` ETH during deployment.
function clone(uint256 value, 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(value, 0x0c, 0x35)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Deploys a deterministic clone of `implementation` with `salt`.
function cloneDeterministic(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic(0, implementation, salt);
}
/// @dev Deploys a deterministic clone of `implementation` with `salt`.
/// Deposits `value` ETH during deployment.
function cloneDeterministic(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
mstore(0x14, implementation)
mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
instance := create2(value, 0x0c, 0x35, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the clone of `implementation`.
function initCode(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000)
mstore(add(c, 0x28), implementation)
mstore(add(c, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
mstore(c, 0x35) // Store the length.
mstore(0x40, add(c, 0x60)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`.
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)
mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the address of the 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) {
instance = clone_PUSH0(0, implementation);
}
/// @dev Deploys a PUSH0 clone of `implementation`.
/// Deposits `value` ETH during deployment.
function clone_PUSH0(uint256 value, 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(value, 0x0e, 0x36)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
function cloneDeterministic_PUSH0(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic_PUSH0(0, implementation, salt);
}
/// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
/// Deposits `value` ETH during deployment.
function cloneDeterministic_PUSH0(uint256 value, 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(value, 0x0e, 0x36, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the PUSH0 clone of `implementation`.
function initCode_PUSH0(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x40), 0x5af43d5f5f3e6029573d5ffd5b3d5ff300000000000000000000) // 16
mstore(add(c, 0x26), implementation) // 20
mstore(add(c, 0x12), 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
mstore(c, 0x36) // Store the length.
mstore(0x40, add(c, 0x60)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`.
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)
mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the address of the 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 clone of `implementation` with immutable arguments encoded in `args`.
function clone(address implementation, bytes memory args) internal returns (address instance) {
instance = clone(0, implementation, args);
}
/// @dev Deploys a clone of `implementation` with immutable arguments encoded in `args`.
/// Deposits `value` ETH during deployment.
function clone(uint256 value, address implementation, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------+
* 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 (45 bytes + extraLength) |
* ---------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..cds): calldata |
* |
* ::: delegate call to the implementation contract ::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 0 | [0..cds): calldata |
* 3d | RETURNDATASIZE | 0 0 0 | [0..cds): calldata |
* 36 | CALLDATASIZE | cds 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..cds): calldata |
* 82 | DUP3 | 0 rds success 0 | [0..cds): calldata |
* 80 | DUP1 | 0 0 rds success 0 | [0..cds): calldata |
* 3e | RETURNDATACOPY | success 0 | [0..rds): returndata |
* 90 | SWAP1 | 0 success | [0..rds): returndata |
* 3d | RETURNDATASIZE | rds 0 success | [0..rds): returndata |
* 91 | SWAP2 | success 0 rds | [0..rds): returndata |
* |
* 60 0x2b | PUSH1 0x2b | 0x2b 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 |
* ---------------------------------------------------------------------------+
*/
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
instance := create(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
function cloneDeterministic(address implementation, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = cloneDeterministic(0, implementation, args, salt);
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
function cloneDeterministic(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
instance := create2(value, add(m, add(0x0b, lt(n, 0xffd3))), add(n, 0x37), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
/// This method does not revert if the clone has already been deployed.
function createDeterministicClone(address implementation, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicClone(0, implementation, args, salt);
}
/// @dev Deploys a deterministic clone of `implementation`
/// with immutable arguments encoded in `args` and `salt`.
/// This method does not revert if the clone has already been deployed.
function createDeterministicClone(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x43), n))
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
// forgefmt: disable-next-item
mstore(add(m, gt(n, 0xffd2)), add(0xfe61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(add(m, 0x0c), add(n, 0x37)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, add(m, 0x0c), add(n, 0x37), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the clone of `implementation`
/// using immutable arguments encoded in `args`.
function initCode(address implementation, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x57), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x37), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(c, 0x28), implementation)
mstore(add(c, 0x14), add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
mstore(c, add(0x37, n)) // Store the length.
mstore(add(c, add(n, 0x57)), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(c, add(n, 0x77))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the clone of `implementation`
/// using immutable arguments encoded in `args`.
function initCodeHash(address implementation, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x2d = 0xffd2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffd2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x43), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x23), 0x5af43d82803e903d91602b57fd5bf3)
mstore(add(m, 0x14), implementation)
mstore(m, add(0x61002d3d81600a3d39f3363d3d373d3d3d363d73, shl(136, n)))
hash := keccak256(add(m, 0x0c), add(n, 0x37))
}
}
/// @dev Returns the address of the clone of
/// `implementation` using immutable arguments encoded in `args`, 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);
}
/// @dev Equivalent to `argsOnClone(instance, 0, 2 ** 256 - 1)`.
function argsOnClone(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x2d))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x2d, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnClone(instance, start, 2 ** 256 - 1)`.
function argsOnClone(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x2d))
let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
extcodecopy(instance, args, add(start, 0x0d), add(l, 0x40))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the clone with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnClone(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x0d), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x2d)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: The ERC1967 proxy here is intended to be upgraded with UUPS.
// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
/// @dev Deploys a minimal ERC1967 proxy with `implementation`.
function deployERC1967(address implementation) internal returns (address instance) {
instance = deployERC1967(0, implementation);
}
/// @dev Deploys a minimal ERC1967 proxy with `implementation`.
/// Deposits `value` ETH during deployment.
function deployERC1967(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 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 |
* 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (61 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata |
* 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata |
* 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x38 | PUSH1 0x38 | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
instance := create(value, 0x21, 0x5f)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
function deployDeterministicERC1967(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967(0, implementation, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
instance := create2(value, 0x21, 0x5f, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967(0, implementation, salt);
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x21, 0x5f))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x21, 0x5f, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`.
function initCodeERC1967(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x60), 0x3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f300)
mstore(add(c, 0x40), 0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc)
mstore(add(c, 0x20), or(shl(24, implementation), 0x600951))
mstore(add(c, 0x09), 0x603d3d8160223d3973)
mstore(c, 0x5f) // Store the length.
mstore(0x40, add(c, 0x80)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`.
function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x20, 0x6009)
mstore(0x1e, implementation)
mstore(0x0a, 0x603d3d8160223d3973)
hash := keccak256(0x21, 0x5f)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967 proxy of `implementation`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`.
function deployERC1967(address implementation, bytes memory args)
internal
returns (address instance)
{
instance = deployERC1967(0, implementation, args);
}
/// @dev Deploys a minimal ERC1967 proxy with `implementation` and `args`.
/// Deposits `value` ETH during deployment.
function deployERC1967(uint256 value, address implementation, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
instance := create(value, m, add(n, 0x60))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
function deployDeterministicERC1967(address implementation, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967(0, implementation, args, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
instance := create2(value, m, add(n, 0x60), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(address implementation, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967(0, implementation, args, salt);
}
/// @dev Creates a deterministic minimal ERC1967 proxy with `implementation`, `args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x60), n))
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
mstore(gt(n, 0xffc2), add(0xfe61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(m, add(n, 0x60)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, m, add(n, 0x60), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation` and `args`.
function initCodeERC1967(address implementation, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x80), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x60), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(c, 0x40), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(add(c, 0x20), 0x6009)
mstore(add(c, 0x1e), implementation)
mstore(add(c, 0x0a), add(0x61003d3d8160233d3973, shl(56, n)))
mstore(c, add(n, 0x60)) // Store the length.
mstore(add(c, add(n, 0x80)), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(c, add(n, 0xa0))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation` and `args`.
function initCodeHashERC1967(address implementation, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x3d = 0xffc2`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffc2))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x60), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x40), 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
mstore(add(m, 0x20), 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
mstore(0x16, 0x6009)
mstore(0x14, implementation)
mstore(0x00, add(0x61003d3d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
hash := keccak256(m, add(n, 0x60))
}
}
/// @dev Returns the address of the ERC1967 proxy of `implementation`, `args`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967(
address implementation,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967(implementation, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x3d))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x3d, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x3d))
let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
extcodecopy(instance, args, add(start, 0x1d), add(l, 0x40))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967 with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x1d), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x3d)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: This proxy has a special code path that activates if `calldatasize() == 1`.
// This code path skips the delegatecall and directly returns the `implementation` address.
// The returned implementation is guaranteed to be valid if the keccak256 of the
// proxy's code is equal to `ERC1967I_CODE_HASH`.
/// @dev Deploys a ERC1967I proxy with `implementation`.
function deployERC1967I(address implementation) internal returns (address instance) {
instance = deployERC1967I(0, implementation);
}
/// @dev Deploys a ERC1967I proxy with `implementation`.
/// Deposits `value` ETH during deployment.
function deployERC1967I(uint256 value, address implementation)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 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 |
* 73 impl | PUSH20 impl | impl 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos impl 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot impl 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (82 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: check calldatasize ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 58 | PC | 1 cds | |
* 14 | EQ | eqs | |
* 60 0x43 | PUSH1 0x43 | dest eqs | |
* 57 | JUMPI | | |
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* 7f slot | PUSH32 slot | s 0 cds 0 0 | [0..calldatasize): calldata |
* 54 | SLOAD | i 0 cds 0 0 | [0..calldatasize): calldata |
* 5a | GAS | g i 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x3E | PUSH1 0x3E | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* |
* ::: implementation , return :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | |
* 60 0x20 | PUSH1 0x20 | 32 | |
* 60 0x0F | PUSH1 0x0F | o 32 | |
* 3d | RETURNDATASIZE | 0 o 32 | |
* 39 | CODECOPY | | [0..32): implementation slot |
* 3d | RETURNDATASIZE | 0 | [0..32): implementation slot |
* 51 | MLOAD | slot | [0..32): implementation slot |
* 54 | SLOAD | impl | [0..32): implementation slot |
* 3d | RETURNDATASIZE | 0 impl | [0..32): implementation slot |
* 52 | MSTORE | | [0..32): implementation address |
* 59 | MSIZE | 32 | [0..32): implementation address |
* 3d | RETURNDATASIZE | 0 32 | [0..32): implementation address |
* f3 | RETURN | | [0..32): implementation address |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
instance := create(value, 0x0c, 0x74)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
function deployDeterministicERC1967I(address implementation, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967I(0, implementation, salt);
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967I(0, implementation, salt);
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x0c, 0x74))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the ERC1967I proxy of `implementation`.
function initCodeERC1967I(address implementation) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x74), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(c, 0x54), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(c, 0x34), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(c, 0x1d), implementation)
mstore(add(c, 0x09), 0x60523d8160223d3973)
mstore(add(c, 0x94), 0)
mstore(c, 0x74) // Store the length.
mstore(0x40, add(c, 0xa0)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation`.
function initCodeHashERC1967I(address implementation) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
hash := keccak256(0x0c, 0x74)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967I proxy of `implementation`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967I(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967I(implementation);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`.
function deployERC1967I(address implementation, bytes memory args) internal returns (address) {
return deployERC1967I(0, implementation, args);
}
/// @dev Deploys a minimal ERC1967I proxy with `implementation` and `args`.
/// Deposits `value` ETH during deployment.
function deployERC1967I(uint256 value, address implementation, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe6100523d8160233d3973, shl(56, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
instance := create(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation`, `args`, and `salt`.
function deployDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967I(0, implementation, args, salt);
}
/// @dev Deploys a deterministic ERC1967I proxy with `implementation`, `args`, and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967I(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x4b), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x2b), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(m, 0x14), implementation)
mstore(m, add(0xfe6100523d8160233d3973, shl(56, n)))
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
instance := create2(value, add(m, add(0x15, lt(n, 0xffae))), add(0x75, n), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation`, `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(address implementation, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967I(0, implementation, args, salt);
}
/// @dev Creates a deterministic ERC1967I proxy with `implementation`, `args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967I(
uint256 value,
address implementation,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x75), n))
mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x16, 0x600f)
mstore(0x14, implementation)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(gt(n, 0xffad), add(0xfe6100523d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(m, add(n, 0x75)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, m, add(0x75, n), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the ERC1967I proxy of `implementation` and `args`.
function initCodeERC1967I(address implementation, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x75), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(c, 0x55), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(c, 0x35), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
mstore(add(c, 0x1e), implementation)
mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n)))
mstore(add(c, add(n, 0x95)), 0)
mstore(c, add(0x75, n)) // Store the length.
mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I proxy of `implementation` and `args.
function initCodeHashERC1967I(address implementation, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x75), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x55), 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
mstore(add(m, 0x35), 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
mstore(add(m, 0x15), 0x5155f3365814604357363d3d373d3d363d7f360894)
mstore(0x16, 0x600f)
mstore(0x14, implementation)
mstore(0x00, add(0x6100523d8160233d3973, shl(56, n)))
mstore(m, mload(0x16))
hash := keccak256(m, add(0x75, n))
}
}
/// @dev Returns the address of the ERC1967I proxy of `implementation`, `args` with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967I(
address implementation,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967I(implementation, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967I(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x52))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967I(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967I(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x52))
let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
extcodecopy(instance, args, add(start, 0x32), add(l, 0x40))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967 with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967I(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x32), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x52)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967 BOOTSTRAP OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// A bootstrap is a minimal UUPS implementation that allows an ERC1967 proxy
// pointing to it to be upgraded. The ERC1967 proxy can then be deployed to a
// deterministic address independent of the implementation:
// ```
// address bootstrap = LibClone.erc1967Bootstrap();
// address instance = LibClone.deployDeterministicERC1967(0, bootstrap, salt);
// LibClone.bootstrapERC1967(bootstrap, implementation);
// ```
/// @dev Deploys the ERC1967 bootstrap if it has not been deployed.
function erc1967Bootstrap() internal returns (address) {
return erc1967Bootstrap(address(this));
}
/// @dev Deploys the ERC1967 bootstrap if it has not been deployed.
function erc1967Bootstrap(address authorizedUpgrader) internal returns (address bootstrap) {
bytes memory c = initCodeERC1967Bootstrap(authorizedUpgrader);
bootstrap = predictDeterministicAddress(keccak256(c), bytes32(0), address(this));
/// @solidity memory-safe-assembly
assembly {
if iszero(extcodesize(bootstrap)) {
if iszero(create2(0, add(c, 0x20), mload(c), 0)) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @dev Replaces the implementation at `instance`.
function bootstrapERC1967(address instance, address implementation) internal {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, implementation)
if iszero(call(gas(), instance, 0, 0x0c, 0x14, codesize(), 0x00)) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Replaces the implementation at `instance`, and then call it with `data`.
function bootstrapERC1967AndCall(address instance, address implementation, bytes memory data)
internal
{
/// @solidity memory-safe-assembly
assembly {
let n := mload(data)
mstore(data, implementation)
if iszero(call(gas(), instance, 0, add(data, 0x0c), add(n, 0x14), codesize(), 0x00)) {
if iszero(returndatasize()) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
returndatacopy(mload(0x40), 0x00, returndatasize())
revert(mload(0x40), returndatasize())
}
mstore(data, n) // Restore the length of `data`.
}
}
/// @dev Returns the implementation address of the ERC1967 bootstrap for this contract.
function predictDeterministicAddressERC1967Bootstrap() internal view returns (address) {
return predictDeterministicAddressERC1967Bootstrap(address(this), address(this));
}
/// @dev Returns the implementation address of the ERC1967 bootstrap for this contract.
function predictDeterministicAddressERC1967Bootstrap(
address authorizedUpgrader,
address deployer
) internal pure returns (address) {
bytes32 hash = initCodeHashERC1967Bootstrap(authorizedUpgrader);
return predictDeterministicAddress(hash, bytes32(0), deployer);
}
/// @dev Returns the initialization code of the ERC1967 bootstrap.
function initCodeERC1967Bootstrap(address authorizedUpgrader)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x80), 0x3d3560601c5af46047573d6000383e3d38fd0000000000000000000000000000)
mstore(add(c, 0x60), 0xa920a3ca505d382bbc55601436116049575b005b363d3d373d3d601436036014)
mstore(add(c, 0x40), 0x0338573d3560601c7f360894a13ba1a3210667c828492db98dca3e2076cc3735)
mstore(add(c, 0x20), authorizedUpgrader)
mstore(add(c, 0x0c), 0x606880600a3d393df3fe3373)
mstore(c, 0x72)
mstore(0x40, add(c, 0xa0))
}
}
/// @dev Returns the initialization code hash of the ERC1967 bootstrap.
function initCodeHashERC1967Bootstrap(address authorizedUpgrader)
internal
pure
returns (bytes32)
{
return keccak256(initCodeERC1967Bootstrap(authorizedUpgrader));
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MINIMAL ERC1967 BEACON PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: If you use this proxy, you MUST make sure that the beacon is a
// valid ERC1967 beacon. This means that the beacon must always return a valid
// address upon a staticcall to `implementation()`, given sufficient gas.
// For performance, the deployment operations and the proxy assumes that the
// beacon is always valid and will NOT validate it.
/// @dev Deploys a minimal ERC1967 beacon proxy.
function deployERC1967BeaconProxy(address beacon) internal returns (address instance) {
instance = deployERC1967BeaconProxy(0, beacon);
}
/// @dev Deploys a minimal ERC1967 beacon proxy.
/// Deposits `value` ETH during deployment.
function deployERC1967BeaconProxy(uint256 value, address beacon)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 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 |
* 73 beac | PUSH20 beac | beac 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos beac 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot beac 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (82 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* |
* ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 60 0x20 | PUSH1 0x20 | 32 | |
* 36 | CALLDATASIZE | cds 32 | |
* 60 0x04 | PUSH1 0x04 | 4 cds 32 | |
* 36 | CALLDATASIZE | cds 4 cds 32 | |
* 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32 | |
* 60 0xe0 | PUSH1 0xe0 | 224 0x5c60da1b cds 4 cds 32 | |
* 1b | SHL | sel cds 4 cds 32 | |
* 36 | CALLDATASIZE | cds sel cds 4 cds 32 | |
* 52 | MSTORE | cds 4 cds 32 | sel |
* 7f slot | PUSH32 slot | s cds 4 cds 32 | sel |
* 54 | SLOAD | beac cds 4 cds 32 | sel |
* 5a | GAS | g beac cds 4 cds 32 | sel |
* fa | STATICCALL | succ | impl |
* 50 | POP | | impl |
* 36 | CALLDATASIZE | cds | impl |
* 51 | MLOAD | impl | impl |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 5a | GAS | g impl 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 80 | DUP1 | 0 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [0..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x4d | PUSH1 0x4d | dest succ | [0..returndatasize): returndata |
* 57 | JUMPI | | [0..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* fd | REVERT | | [0..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [0..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [0..returndatasize): returndata |
* 60 0x00 | PUSH1 0x00 | 0 rds | [0..returndatasize): returndata |
* f3 | RETURN | | [0..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
instance := create(value, 0x0c, 0x74)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`.
function deployDeterministicERC1967BeaconProxy(address beacon, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967BeaconProxy(0, beacon, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967BeaconProxy(0, beacon, salt);
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x0c, 0x74))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x0c, 0x74, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 beacon proxy.
function initCodeERC1967BeaconProxy(address beacon) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x74), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(c, 0x54), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(c, 0x34), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(c, 0x1d), beacon)
mstore(add(c, 0x09), 0x60523d8160223d3973)
mstore(add(c, 0x94), 0)
mstore(c, 0x74) // Store the length.
mstore(0x40, add(c, 0xa0)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy.
function initCodeHashERC1967BeaconProxy(address beacon) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(0x40, 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, beacon))))
hash := keccak256(0x0c, 0x74)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967 beacon proxy, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967BeaconProxy(
address beacon,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967BeaconProxy(beacon);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967 BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a minimal ERC1967 beacon proxy with `args`.
function deployERC1967BeaconProxy(address beacon, bytes memory args)
internal
returns (address instance)
{
instance = deployERC1967BeaconProxy(0, beacon, args);
}
/// @dev Deploys a minimal ERC1967 beacon proxy with `args`.
/// Deposits `value` ETH during deployment.
function deployERC1967BeaconProxy(uint256 value, address beacon, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
instance := create(value, add(m, 0x16), add(n, 0x75))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
function deployDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967BeaconProxy(0, beacon, args, salt);
}
/// @dev Deploys a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967BeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
instance := create2(value, add(m, 0x16), add(n, 0x75), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967BeaconProxy(0, beacon, args, salt);
}
/// @dev Creates a deterministic minimal ERC1967 beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967BeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x8b), n))
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
mstore(add(m, gt(n, 0xffad)), add(0xfe6100523d8160233d3973, shl(56, n)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(add(m, 0x16), add(n, 0x75)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, add(m, 0x16), add(n, 0x75), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the minimal ERC1967 beacon proxy.
function initCodeERC1967BeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x95), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x75), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(c, 0x55), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(c, 0x35), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(c, 0x1e), beacon)
mstore(add(c, 0x0a), add(0x6100523d8160233d3973, shl(56, n)))
mstore(c, add(n, 0x75)) // Store the length.
mstore(add(c, add(n, 0x95)), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(c, add(n, 0xb5))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the minimal ERC1967 beacon proxy with `args`.
function initCodeHashERC1967BeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x52 = 0xffad`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffad))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(m, 0x8b), i), mload(add(add(args, 0x20), i)))
}
mstore(add(m, 0x6b), 0xb3582b35133d50545afa5036515af43d6000803e604d573d6000fd5b3d6000f3)
mstore(add(m, 0x4b), 0x1b60e01b36527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6c)
mstore(add(m, 0x2b), 0x60195155f3363d3d373d3d363d602036600436635c60da)
mstore(add(m, 0x14), beacon)
mstore(m, add(0x6100523d8160233d3973, shl(56, n)))
hash := keccak256(add(m, 0x16), add(n, 0x75))
}
}
/// @dev Returns the address of the ERC1967 beacon proxy with `args`, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967BeaconProxy(
address beacon,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967BeaconProxy(beacon, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967BeaconProxy(address instance) internal view returns (bytes memory args) {
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x52))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x52, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967BeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967BeaconProxy(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x52))
let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
extcodecopy(instance, args, add(start, 0x32), add(l, 0x40))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967 beacon proxy with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967BeaconProxy(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x32), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x52)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I BEACON PROXY OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// Note: This proxy has a special code path that activates if `calldatasize() == 1`.
// This code path skips the delegatecall and directly returns the `implementation` address.
// The returned implementation is guaranteed to be valid if the keccak256 of the
// proxy's code is equal to `ERC1967_BEACON_PROXY_CODE_HASH`.
//
// If you use this proxy, you MUST make sure that the beacon is a
// valid ERC1967 beacon. This means that the beacon must always return a valid
// address upon a staticcall to `implementation()`, given sufficient gas.
// For performance, the deployment operations and the proxy assumes that the
// beacon is always valid and will NOT validate it.
/// @dev Deploys a ERC1967I beacon proxy.
function deployERC1967IBeaconProxy(address beacon) internal returns (address instance) {
instance = deployERC1967IBeaconProxy(0, beacon);
}
/// @dev Deploys a ERC1967I beacon proxy.
/// Deposits `value` ETH during deployment.
function deployERC1967IBeaconProxy(uint256 value, address beacon)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
/**
* ---------------------------------------------------------------------------------+
* CREATION (34 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 |
* 73 beac | PUSH20 beac | beac 0 r | [0..runSize): runtime code |
* 60 slotPos | PUSH1 slotPos | slotPos beac 0 r | [0..runSize): runtime code |
* 51 | MLOAD | slot beac 0 r | [0..runSize): runtime code |
* 55 | SSTORE | 0 r | [0..runSize): runtime code |
* f3 | RETURN | | [0..runSize): runtime code |
* ---------------------------------------------------------------------------------|
* RUNTIME (87 bytes) |
* ---------------------------------------------------------------------------------|
* Opcode | Mnemonic | Stack | Memory |
* ---------------------------------------------------------------------------------|
* |
* ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
* 36 | CALLDATASIZE | cds | |
* 3d | RETURNDATASIZE | 0 cds | |
* 3d | RETURNDATASIZE | 0 0 cds | |
* 37 | CALLDATACOPY | | [0..calldatasize): calldata |
* |
* ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | 0 | |
* 3d | RETURNDATASIZE | 0 0 | |
* 36 | CALLDATASIZE | cds 0 0 | [0..calldatasize): calldata |
* 3d | RETURNDATASIZE | 0 cds 0 0 | [0..calldatasize): calldata |
* |
* ~~~~~~~ beacon staticcall sub procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 60 0x20 | PUSH1 0x20 | 32 | |
* 36 | CALLDATASIZE | cds 32 | |
* 60 0x04 | PUSH1 0x04 | 4 cds 32 | |
* 36 | CALLDATASIZE | cds 4 cds 32 | |
* 63 0x5c60da1b | PUSH4 0x5c60da1b | 0x5c60da1b cds 4 cds 32 | |
* 60 0xe0 | PUSH1 0xe0 | 224 0x5c60da1b cds 4 cds 32 | |
* 1b | SHL | sel cds 4 cds 32 | |
* 36 | CALLDATASIZE | cds sel cds 4 cds 32 | |
* 52 | MSTORE | cds 4 cds 32 | sel |
* 7f slot | PUSH32 slot | s cds 4 cds 32 | sel |
* 54 | SLOAD | beac cds 4 cds 32 | sel |
* 5a | GAS | g beac cds 4 cds 32 | sel |
* fa | STATICCALL | succ | impl |
* ~~~~~~ check calldatasize ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 36 | CALLDATASIZE | cds succ | |
* 14 | EQ | | impl |
* 60 0x52 | PUSH1 0x52 | | impl |
* 57 | JUMPI | | impl |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 36 | CALLDATASIZE | cds | impl |
* 51 | MLOAD | impl | impl |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
* 5a | GAS | g impl 0 cds 0 0 | [0..calldatasize): calldata |
* f4 | DELEGATECALL | succ | [0..calldatasize): calldata |
* |
* ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds succ | [0..calldatasize): calldata |
* 60 0x00 | PUSH1 0x00 | 0 rds succ | [0..calldatasize): calldata |
* 60 0x01 | PUSH1 0x01 | 1 0 rds succ | [0..calldatasize): calldata |
* 3e | RETURNDATACOPY | succ | [1..returndatasize): returndata |
* |
* ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
* 60 0x52 | PUSH1 0x52 | dest succ | [1..returndatasize): returndata |
* 57 | JUMPI | | [1..returndatasize): returndata |
* |
* ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
* 3d | RETURNDATASIZE | rds | [1..returndatasize): returndata |
* 60 0x01 | PUSH1 0x01 | 1 rds | [1..returndatasize): returndata |
* fd | REVERT | | [1..returndatasize): returndata |
* |
* ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
* 5b | JUMPDEST | | [1..returndatasize): returndata |
* 3d | RETURNDATASIZE | rds | [1..returndatasize): returndata |
* 60 0x01 | PUSH1 0x01 | 1 rds | [1..returndatasize): returndata |
* f3 | RETURN | | [1..returndatasize): returndata |
* ---------------------------------------------------------------------------------+
*/
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
instance := create(value, 0x07, 0x79)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`.
function deployDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967IBeaconProxy(0, beacon, salt);
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
instance := create2(value, 0x07, 0x79, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967IBeaconProxy(0, beacon, salt);
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(uint256 value, address beacon, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
// Compute and store the bytecode hash.
mstore(add(m, 0x35), keccak256(0x07, 0x79))
mstore(m, shl(88, address()))
mstore8(m, 0xff) // Write the prefix.
mstore(add(m, 0x15), salt)
instance := keccak256(m, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, 0x07, 0x79, salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the initialization code of the ERC1967I beacon proxy.
function initCodeERC1967IBeaconProxy(address beacon) internal pure returns (bytes memory c) {
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
mstore(add(c, 0x79), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(c, 0x59), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(c, 0x39), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(c, 0x1d), beacon)
mstore(add(c, 0x09), 0x60573d8160223d3973)
mstore(add(c, 0x99), 0)
mstore(c, 0x79) // Store the length.
mstore(0x40, add(c, 0xa0)) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I beacon proxy.
function initCodeHashERC1967IBeaconProxy(address beacon) internal pure returns (bytes32 hash) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
mstore(0x60, 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(0x40, 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(0x20, 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(0x04, or(shl(160, 0x60573d8160223d3973), shr(96, shl(96, beacon))))
hash := keccak256(0x07, 0x79)
mstore(0x40, m) // Restore the free memory pointer.
mstore(0x60, 0) // Restore the zero slot.
}
}
/// @dev Returns the address of the ERC1967I beacon proxy, with `salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967IBeaconProxy(
address beacon,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ERC1967I BEACON PROXY WITH IMMUTABLE ARGS OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Deploys a ERC1967I beacon proxy with `args.
function deployERC1967IBeaconProxy(address beacon, bytes memory args)
internal
returns (address instance)
{
instance = deployERC1967IBeaconProxy(0, beacon, args);
}
/// @dev Deploys a ERC1967I beacon proxy with `args.
/// Deposits `value` ETH during deployment.
function deployERC1967IBeaconProxy(uint256 value, address beacon, bytes memory args)
internal
returns (address instance)
{
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
instance := create(value, add(m, 0x16), add(n, 0x7a))
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`.
function deployDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (address instance)
{
instance = deployDeterministicERC1967IBeaconProxy(0, beacon, args, salt);
}
/// @dev Deploys a deterministic ERC1967I beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
function deployDeterministicERC1967IBeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
instance := create2(value, add(m, 0x16), add(n, 0x7a), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(address beacon, bytes memory args, bytes32 salt)
internal
returns (bool alreadyDeployed, address instance)
{
return createDeterministicERC1967IBeaconProxy(0, beacon, args, salt);
}
/// @dev Creates a deterministic ERC1967I beacon proxy with `args` and `salt`.
/// Deposits `value` ETH during deployment.
/// Note: This method is intended for use in ERC4337 factories,
/// which are expected to NOT revert if the proxy is already deployed.
function createDeterministicERC1967IBeaconProxy(
uint256 value,
address beacon,
bytes memory args,
bytes32 salt
) internal returns (bool alreadyDeployed, address instance) {
/// @solidity memory-safe-assembly
assembly {
let m := mload(0x40)
let n := mload(args)
pop(staticcall(gas(), 4, add(args, 0x20), n, add(m, 0x90), n))
mstore(add(m, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(m, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(m, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(m, 0x14), beacon)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
mstore(add(m, gt(n, 0xffa8)), add(0xfe6100573d8160233d3973, shl(56, n)))
// Compute and store the bytecode hash.
mstore8(0x00, 0xff) // Write the prefix.
mstore(0x35, keccak256(add(m, 0x16), add(n, 0x7a)))
mstore(0x01, shl(96, address()))
mstore(0x15, salt)
instance := keccak256(0x00, 0x55)
for {} 1 {} {
if iszero(extcodesize(instance)) {
instance := create2(value, add(m, 0x16), add(n, 0x7a), salt)
if iszero(instance) {
mstore(0x00, 0x30116425) // `DeploymentFailed()`.
revert(0x1c, 0x04)
}
break
}
alreadyDeployed := 1
if iszero(value) { break }
if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
break
}
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Returns the initialization code of the ERC1967I beacon proxy with `args`.
function initCodeERC1967IBeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes memory c)
{
/// @solidity memory-safe-assembly
assembly {
c := mload(0x40)
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x9a), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x7a), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(c, 0x5a), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(c, 0x3a), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(c, 0x1e), beacon)
mstore(add(c, 0x0a), add(0x6100573d8160233d3973, shl(56, n)))
mstore(add(c, add(n, 0x9a)), 0)
mstore(c, add(n, 0x7a)) // Store the length.
mstore(0x40, add(c, add(n, 0xba))) // Allocate memory.
}
}
/// @dev Returns the initialization code hash of the ERC1967I beacon proxy with `args`.
function initCodeHashERC1967IBeaconProxy(address beacon, bytes memory args)
internal
pure
returns (bytes32 hash)
{
/// @solidity memory-safe-assembly
assembly {
let c := mload(0x40) // Cache the free memory pointer.
let n := mload(args)
// Do a out-of-gas revert if `n` is greater than `0xffff - 0x57 = 0xffa8`.
returndatacopy(returndatasize(), returndatasize(), gt(n, 0xffa8))
for { let i := 0 } lt(i, n) { i := add(i, 0x20) } {
mstore(add(add(c, 0x90), i), mload(add(add(args, 0x20), i)))
}
mstore(add(c, 0x70), 0x3d50545afa361460525736515af43d600060013e6052573d6001fd5b3d6001f3)
mstore(add(c, 0x50), 0x527fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b3513)
mstore(add(c, 0x30), 0x60195155f3363d3d373d3d363d602036600436635c60da1b60e01b36)
mstore(add(c, 0x14), beacon)
mstore(c, add(0x6100573d8160233d3973, shl(56, n)))
hash := keccak256(add(c, 0x16), add(n, 0x7a))
}
}
/// @dev Returns the address of the ERC1967I beacon proxy, with `args` and salt` by `deployer`.
/// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
function predictDeterministicAddressERC1967IBeaconProxy(
address beacon,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes32 hash = initCodeHashERC1967IBeaconProxy(beacon, args);
predicted = predictDeterministicAddress(hash, salt, deployer);
}
/// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967IBeaconProxy(address instance)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
mstore(args, and(0xffffffffff, sub(extcodesize(instance), 0x57))) // Store the length.
extcodecopy(instance, add(args, 0x20), 0x57, add(mload(args), 0x20))
mstore(0x40, add(mload(args), add(args, 0x40))) // Allocate memory.
}
}
/// @dev Equivalent to `argsOnERC1967IBeaconProxy(instance, start, 2 ** 256 - 1)`.
function argsOnERC1967IBeaconProxy(address instance, uint256 start)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
let n := and(0xffffffffff, sub(extcodesize(instance), 0x57))
let l := sub(n, and(0xffffff, mul(lt(start, n), start)))
extcodecopy(instance, args, add(start, 0x37), add(l, 0x40))
mstore(args, mul(sub(n, start), lt(start, n))) // Store the length.
mstore(0x40, add(args, add(0x40, mload(args)))) // Allocate memory.
}
}
/// @dev Returns a slice of the immutable arguments on `instance` from `start` to `end`.
/// `start` and `end` will be clamped to the range `[0, args.length]`.
/// The `instance` MUST be deployed via the ERC1967I beacon proxy with immutable args functions.
/// Otherwise, the behavior is undefined.
/// Out-of-gas reverts if `instance` does not have any code.
function argsOnERC1967IBeaconProxy(address instance, uint256 start, uint256 end)
internal
view
returns (bytes memory args)
{
/// @solidity memory-safe-assembly
assembly {
args := mload(0x40)
if iszero(lt(end, 0xffff)) { end := 0xffff }
let d := mul(sub(end, start), lt(start, end))
extcodecopy(instance, args, add(start, 0x37), add(d, 0x20))
if iszero(and(0xff, mload(add(args, d)))) {
let n := sub(extcodesize(instance), 0x57)
returndatacopy(returndatasize(), returndatasize(), shr(40, n))
d := mul(gt(n, start), sub(d, mul(gt(end, n), sub(end, n))))
}
mstore(args, d) // Store the length.
mstore(add(add(args, 0x20), d), 0) // Zeroize the slot after the bytes.
mstore(0x40, add(add(args, 0x40), d)) // Allocate memory.
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Returns `address(0)` if the implementation address cannot be determined.
function implementationOf(address instance) internal view returns (address result) {
/// @solidity memory-safe-assembly
assembly {
for { extcodecopy(instance, 0x00, 0x00, 0x57) } 1 {} {
if mload(0x2d) {
// ERC1967I and ERC1967IBeaconProxy detection.
if or(
eq(keccak256(0x00, 0x52), ERC1967I_CODE_HASH),
eq(keccak256(0x00, 0x57), ERC1967I_BEACON_PROXY_CODE_HASH)
) {
pop(staticcall(gas(), instance, 0x00, 0x01, 0x00, 0x20))
result := mload(0x0c)
break
}
}
// 0age clone detection.
result := mload(0x0b)
codecopy(0x0b, codesize(), 0x14) // Zeroize the 20 bytes for the address.
if iszero(xor(keccak256(0x00, 0x2c), CLONE_CODE_HASH)) { break }
mstore(0x0b, result) // Restore the zeroized memory.
// CWIA detection.
result := mload(0x0a)
codecopy(0x0a, codesize(), 0x14) // Zeroize the 20 bytes for the address.
if iszero(xor(keccak256(0x00, 0x2d), CWIA_CODE_HASH)) { break }
mstore(0x0a, result) // Restore the zeroized memory.
// PUSH0 clone detection.
result := mload(0x09)
codecopy(0x09, codesize(), 0x14) // Zeroize the 20 bytes for the address.
result := shr(xor(keccak256(0x00, 0x2d), PUSH0_CLONE_CODE_HASH), result)
break
}
result := shr(96, result)
mstore(0x37, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @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)
mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
}
}
/// @dev Requires that `salt` starts with either the zero address or `by`.
function checkStartsWith(bytes32 salt, address by) internal pure {
/// @solidity memory-safe-assembly
assembly {
// If the salt does not start with the zero address or `by`.
if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) {
mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Returns the `bytes32` at `offset` in `args`, without any bounds checks.
/// To load an address, you can use `address(bytes20(argLoad(args, offset)))`.
function argLoad(bytes memory args, uint256 offset) internal pure returns (bytes32 result) {
/// @solidity memory-safe-assembly
assembly {
result := mload(add(add(args, 0x20), offset))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBPermissions} from "@bananapus/core-v5/src/interfaces/IJBPermissions.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {JBConstants} from "@bananapus/core-v5/src/libraries/JBConstants.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import {JBSucker} from "./JBSucker.sol";
import {JBOptimismSuckerDeployer} from "./deployers/JBOptimismSuckerDeployer.sol";
import {IJBOptimismSucker} from "./interfaces/IJBOptimismSucker.sol";
import {IJBSuckerDeployer} from "./interfaces/IJBSuckerDeployer.sol";
import {IOPMessenger} from "./interfaces/IOPMessenger.sol";
import {IOPStandardBridge} from "./interfaces/IOPStandardBridge.sol";
import {JBAddToBalanceMode} from "./enums/JBAddToBalanceMode.sol";
import {JBInboxTreeRoot} from "./structs/JBInboxTreeRoot.sol";
import {JBMessageRoot} from "./structs/JBMessageRoot.sol";
import {JBOutboxTree} from "./structs/JBOutboxTree.sol";
import {JBRemoteToken} from "./structs/JBRemoteToken.sol";
import {MerkleLib} from "./utils/MerkleLib.sol";
/// @notice A `JBSucker` implementation to suck tokens between two chains connected by an OP Bridge.
contract JBOptimismSucker is JBSucker, IJBOptimismSucker {
using BitMaps for BitMaps.BitMap;
using MerkleLib for MerkleLib.Tree;
//*********************************************************************//
// --------------- public immutable stored properties ---------------- //
//*********************************************************************//
/// @notice The bridge used to bridge tokens between the local and remote chain.
IOPStandardBridge public immutable override OPBRIDGE;
/// @notice The messenger used to send messages between the local and remote sucker.
IOPMessenger public immutable override OPMESSENGER;
//*********************************************************************//
// ---------------------------- constructor -------------------------- //
//*********************************************************************//
/// @param deployer A contract that deploys the clones for this contracts.
/// @param directory A contract storing directories of terminals and controllers for each project.
/// @param tokens A contract that manages token minting and burning.
/// @param permissions A contract storing permissions.
/// @param addToBalanceMode The mode of adding tokens to balance.
constructor(
JBOptimismSuckerDeployer deployer,
IJBDirectory directory,
IJBPermissions permissions,
IJBTokens tokens,
JBAddToBalanceMode addToBalanceMode,
address trusted_forwarder
)
JBSucker(directory, permissions, tokens, addToBalanceMode, trusted_forwarder)
{
// Fetch the messenger and bridge by doing a callback to the deployer contract.
OPBRIDGE = JBOptimismSuckerDeployer(deployer).opBridge();
OPMESSENGER = JBOptimismSuckerDeployer(deployer).opMessenger();
}
//*********************************************************************//
// ------------------------ external views --------------------------- //
//*********************************************************************//
/// @notice Returns the chain on which the peer is located.
/// @return chainId of the peer.
function peerChainId() external view virtual override returns (uint256) {
uint256 chainId = block.chainid;
if (chainId == 1) return 10;
if (chainId == 10) return 1;
if (chainId == 11_155_111) return 11_155_420;
if (chainId == 11_155_420) return 11_155_111;
return 0;
}
//*********************************************************************//
// --------------------- internal transactions ----------------------- //
//*********************************************************************//
/// @notice Checks if the `sender` (`_msgSender()`) is a valid representative of the remote peer.
/// @param sender The message's sender.
function _isRemotePeer(address sender) internal override returns (bool valid) {
return sender == address(OPMESSENGER) && OPMESSENGER.xDomainMessageSender() == peer();
}
/// @notice Use the `OPMESSENGER` to send the outbox tree for the `token` and the corresponding funds to the peer
/// over the `OPBRIDGE`.
/// @param transportPayment the amount of `msg.value` that is going to get paid for sending this message.
/// @param token The token to bridge the outbox tree for.
/// @param remoteToken Information about the remote token being bridged to.
function _sendRootOverAMB(
uint256 transportPayment,
uint256,
address token,
uint256 amount,
JBRemoteToken memory remoteToken,
JBMessageRoot memory message
)
internal
override
{
uint256 nativeValue;
// Revert if there's a `msg.value`. The OP bridge does not expect to be paid.
if (transportPayment != 0) {
revert JBSucker_UnexpectedMsgValue(transportPayment);
}
// If the token is an ERC20, bridge it to the peer.
// If the amount is `0` then we do not need to bridge any ERC20.
if (token != JBConstants.NATIVE_TOKEN && amount != 0) {
// Approve the tokens bing bridged.
// slither-disable-next-line reentrancy-events
SafeERC20.forceApprove({token: IERC20(token), spender: address(OPBRIDGE), value: amount});
// Bridge the tokens to the peer sucker.
// slither-disable-next-line reentrency-events,calls-loop
OPBRIDGE.bridgeERC20To({
localToken: token,
remoteToken: remoteToken.addr,
to: peer(),
amount: amount,
minGasLimit: remoteToken.minGas,
extraData: bytes("")
});
} else {
// Otherwise, the token is the native token, and the amount will be sent as `msg.value`.
nativeValue = amount;
}
// Send the message to the peer with the reclaimed ETH.
// slither-disable-next-line arbitrary-send-eth,reentrency-events,calls-loop
OPMESSENGER.sendMessage{value: nativeValue}(
peer(), abi.encodeCall(JBSucker.fromRemote, (message)), MESSENGER_BASE_GAS_LIMIT
);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {JBPermissioned} from "@bananapus/core-v5/src/abstract/JBPermissioned.sol";
import {IJBCashOutTerminal} from "@bananapus/core-v5/src/interfaces/IJBCashOutTerminal.sol";
import {IJBController} from "@bananapus/core-v5/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBPermissions} from "@bananapus/core-v5/src/interfaces/IJBPermissions.sol";
import {IJBPermissioned} from "@bananapus/core-v5/src/interfaces/IJBPermissioned.sol";
import {IJBTerminal} from "@bananapus/core-v5/src/interfaces/IJBTerminal.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {JBConstants} from "@bananapus/core-v5/src/libraries/JBConstants.sol";
import {JBPermissionIds} from "@bananapus/permission-ids-v5/src/JBPermissionIds.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
import {ERC165, IERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {JBAddToBalanceMode} from "./enums/JBAddToBalanceMode.sol";
import {IJBSucker} from "./interfaces/IJBSucker.sol";
import {IJBSuckerExtended} from "./interfaces/IJBSuckerExtended.sol";
import {IJBSuckerDeployer} from "./interfaces/IJBSuckerDeployer.sol";
import {JBClaim} from "./structs/JBClaim.sol";
import {JBInboxTreeRoot} from "./structs/JBInboxTreeRoot.sol";
import {JBMessageRoot} from "./structs/JBMessageRoot.sol";
import {JBOutboxTree} from "./structs/JBOutboxTree.sol";
import {JBRemoteToken} from "./structs/JBRemoteToken.sol";
import {JBTokenMapping} from "./structs/JBTokenMapping.sol";
import {MerkleLib} from "./utils/MerkleLib.sol";
import {JBSuckerState} from "./enums/JBSuckerState.sol";
/// @notice An abstract contract for bridging a Juicebox project's tokens and the corresponding funds to and from a
/// remote chain.
/// @dev Beneficiaries and balances are tracked on two merkle trees: the outbox tree is used to send from the local
/// chain to the remote chain, and the inbox tree is used to receive from the remote chain to the local chain.
/// @dev Throughout this contract, "terminal token" refers to any token accepted by a project's terminal.
/// @dev This contract does *NOT* support tokens that have a fee on regular transfers and rebasing tokens.
abstract contract JBSucker is ERC2771Context, JBPermissioned, Initializable, ERC165, IJBSuckerExtended {
using BitMaps for BitMaps.BitMap;
using MerkleLib for MerkleLib.Tree;
using SafeERC20 for IERC20;
//*********************************************************************//
// --------------------------- custom errors ------------------------- //
//*********************************************************************//
error JBSucker_BelowMinGas(uint256 minGas, uint256 minGasLimit);
error JBSucker_InsufficientBalance(uint256 amount, uint256 balance);
error JBSucker_InvalidNativeRemoteAddress(address remoteToken);
error JBSucker_InvalidProof(bytes32 root, bytes32 inboxRoot);
error JBSucker_LeafAlreadyExecuted(address token, uint256 index);
error JBSucker_ManualNotAllowed(JBAddToBalanceMode mode);
error JBSucker_DeprecationTimestampTooSoon(uint256 givenTime, uint256 minimumTime);
error JBSucker_NoTerminalForToken(uint256 projectId, address token);
error JBSucker_NotPeer(address caller);
error JBSucker_QueueInsufficientSize(uint256 amount, uint256 minimumAmount);
error JBSucker_TokenNotMapped(address token);
error JBSucker_TokenHasInvalidEmergencyHatchState(address token);
error JBSucker_TokenAlreadyMapped(address localToken, address mappedTo);
error JBSucker_UnexpectedMsgValue(uint256 value);
error JBSucker_ExpectedMsgValue();
error JBSucker_InsufficientMsgValue(uint256 received, uint256 expected);
error JBSucker_ZeroBeneficiary();
error JBSucker_ZeroERC20Token();
error JBSucker_Deprecated();
//*********************************************************************//
// ------------------------- public constants ------------------------ //
//*********************************************************************//
/// @notice A reasonable minimum gas limit for a basic cross-chain call. The minimum amount of gas required to call
/// the `fromRemote` (successfully/safely) on the remote chain.
uint32 public constant override MESSENGER_BASE_GAS_LIMIT = 300_000;
/// @notice A reasonable minimum gas limit used when bridging ERC-20s. The minimum amount of gas required to
/// (successfully/safely) perform a transfer on the remote chain.
uint32 public constant override MESSENGER_ERC20_MIN_GAS_LIMIT = 200_000;
//*********************************************************************//
// ------------------------- internal constants ----------------------- //
//*********************************************************************//
/// @notice The depth of the merkle tree used to store the outbox and inbox.
uint32 constant _TREE_DEPTH = 32;
//*********************************************************************//
// --------------- public immutable stored properties ---------------- //
//*********************************************************************//
/// @notice Whether the `amountToAddToBalance` gets added to the project's balance automatically when `claim` is
/// called or manually by calling `addOutstandingAmountToBalance`.
JBAddToBalanceMode public immutable override ADD_TO_BALANCE_MODE;
/// @notice The directory of terminals and controllers for projects.
IJBDirectory public immutable override DIRECTORY;
/// @notice The contract that manages token minting and burning.
IJBTokens public immutable override TOKENS;
//*********************************************************************//
// --------------------- public stored properties -------------------- //
//*********************************************************************//
/// @notice The address of this contract's deployer.
address public override deployer;
//*********************************************************************//
// --------------------- private stored properties ------------------- //
//*********************************************************************//
/// @notice The timestamp after which the sucker is entirely deprecated.
uint256 internal deprecatedAfter;
/// @notice The ID of the project (on the local chain) that this sucker is associated with.
uint256 private localProjectId;
//*********************************************************************//
// -------------------- internal stored properties ------------------- //
//*********************************************************************//
/// @notice Tracks whether individual leaves in a given token's merkle tree have been executed (to prevent
/// double-spending).
/// @dev A leaf is "executed" when the tokens it represents are minted for its beneficiary.
/// @custom:param token The token to get the executed bitmap of.
mapping(address token => BitMaps.BitMap) internal _executedFor;
/// @notice The inbox merkle tree root for a given token.
/// @custom:param token The local terminal token to get the inbox for.
mapping(address token => JBInboxTreeRoot root) internal _inboxOf;
/// @notice The outbox merkle tree for a given token.
/// @custom:param token The local terminal token to get the outbox for.
mapping(address token => JBOutboxTree) internal _outboxOf;
/// @notice Information about the token on the remote chain that the given token on the local chain is mapped to.
/// @custom:param token The local terminal token to get the remote token for.
mapping(address token => JBRemoteToken remoteToken) internal _remoteTokenFor;
//*********************************************************************//
// ---------------------------- constructor -------------------------- //
//*********************************************************************//
/// @param directory A contract storing directories of terminals and controllers for each project.
/// @param permissions A contract storing permissions.
/// @param tokens A contract that manages token minting and burning.
/// @param addToBalanceMode The mode of adding tokens to balance.
constructor(
IJBDirectory directory,
IJBPermissions permissions,
IJBTokens tokens,
JBAddToBalanceMode addToBalanceMode,
address trusted_forwarder
)
ERC2771Context(trusted_forwarder)
JBPermissioned(permissions)
{
DIRECTORY = directory;
TOKENS = tokens;
ADD_TO_BALANCE_MODE = addToBalanceMode;
// Make it so the singleton can't be initialized.
_disableInitializers();
// Sanity check: make sure the merkle lib uses the same tree depth.
assert(MerkleLib.TREE_DEPTH == _TREE_DEPTH);
}
//*********************************************************************//
// ------------------------ external views --------------------------- //
//*********************************************************************//
/// @notice The outstanding amount of tokens to be added to the project's balance by `claim` or
/// `addOutstandingAmountToBalance`.
/// @param token The local terminal token to get the amount to add to balance for.
function amountToAddToBalanceOf(address token) public view override returns (uint256) {
// Get the amount that is in this sucker to be bridged.
return _balanceOf(token, address(this)) - _outboxOf[token].balance;
}
/// @notice The inbox merkle tree root for a given token.
/// @param token The local terminal token to get the inbox for.
function inboxOf(address token) external view returns (JBInboxTreeRoot memory) {
return _inboxOf[token];
}
/// @notice Checks whether the specified token is mapped to a remote token.
/// @param token The terminal token to check.
/// @return A boolean which is `true` if the token is mapped to a remote token and `false` if it is not.
function isMapped(address token) external view override returns (bool) {
return _remoteTokenFor[token].addr != address(0);
}
/// @notice Information about the token on the remote chain that the given token on the local chain is mapped to.
/// @param token The local terminal token to get the remote token for.
function outboxOf(address token) external view returns (JBOutboxTree memory) {
return _outboxOf[token];
}
/// @notice Returns the chain on which the peer is located.
/// @return chain ID of the peer.
function peerChainId() external view virtual returns (uint256);
/// @notice Information about the token on the remote chain that the given token on the local chain is mapped to.
/// @param token The local terminal token to get the remote token for.
function remoteTokenFor(address token) external view returns (JBRemoteToken memory) {
return _remoteTokenFor[token];
}
//*********************************************************************//
// ------------------------- public views ---------------------------- //
//*********************************************************************//
/// @notice The peer sucker on the remote chain.
function peer() public view virtual returns (address) {
/// This can be overridden by the inheriting contract to return a different address. This is fully supported by
/// the sucker implementation and all its off-chain infrastructure, This does however break some
/// invariants/assumptions, for revnets it would break the assumption of matching configurations on both chains,
/// for this reason we only support a matching address.
// The peer is at the same address on the other chain.
return address(this);
}
/// @notice The ID of the project (on the local chain) that this sucker is associated with.
function projectId() public view returns (uint256) {
return localProjectId;
}
/// @notice Reports the deprecation state of the sucker.
/// @return state The current deprecation state
function state() public view override returns (JBSuckerState) {
uint256 _deprecatedAfter = deprecatedAfter;
// The sucker is fully functional, no deprecation has been set yet.
if (_deprecatedAfter == 0) {
return JBSuckerState.ENABLED;
}
// The sucker will soon be considered deprecated, this functions only as a warning to users.
if (block.timestamp < _deprecatedAfter - _maxMessagingDelay()) {
return JBSuckerState.DEPRECATION_PENDING;
}
// The sucker will no longer send new roots to the pair, but it will accept new incoming roots.
// Additionally it will let users exit here now that we can no longer send roots/tokens.
if (block.timestamp < _deprecatedAfter) {
return JBSuckerState.SENDING_DISABLED;
}
// The sucker is now in the final state of deprecation. It will no longer allow new roots.
return JBSuckerState.DEPRECATED;
}
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
return interfaceId == type(IJBSuckerExtended).interfaceId || interfaceId == type(IJBSucker).interfaceId
|| interfaceId == type(IJBPermissioned).interfaceId || super.supportsInterface(interfaceId);
}
//*********************************************************************//
// ------------------------ internal views --------------------------- //
//*********************************************************************//
/// @notice Helper to get the `addr`'s balance for a given `token`.
/// @param token The token to get the balance for.
/// @param addr The address to get the `token` balance of.
/// @return balance The address' `token` balance.
function _balanceOf(address token, address addr) internal view returns (uint256 balance) {
if (token == JBConstants.NATIVE_TOKEN) {
return addr.balance;
}
// slither-disable-next-line calls-loop
return IERC20(token).balanceOf(addr);
}
/// @notice Builds a hash as they are stored in the merkle tree.
/// @param projectTokenCount The number of project tokens being cashed out.
/// @param terminalTokenAmount The amount of terminal tokens being reclaimed by the cash out.
/// @param beneficiary The beneficiary which will receive the project tokens.
function _buildTreeHash(
uint256 projectTokenCount,
uint256 terminalTokenAmount,
address beneficiary
)
internal
pure
returns (bytes32)
{
return keccak256(abi.encode(projectTokenCount, terminalTokenAmount, beneficiary));
}
/// @notice Allow sucker implementations to add/override mapping rules to suite their specific needs.
function _validateTokenMapping(JBTokenMapping calldata map) internal pure virtual {
bool isNative = map.localToken == JBConstants.NATIVE_TOKEN;
// If the token being mapped is the native token, the `remoteToken` must also be the native token.
// The native token can also be mapped to the 0 address, which is used to disable native token bridging.
if (isNative && map.remoteToken != JBConstants.NATIVE_TOKEN && map.remoteToken != address(0)) {
revert JBSucker_InvalidNativeRemoteAddress(map.remoteToken);
}
// Enforce a reasonable minimum gas limit for bridging. A minimum which is too low could lead to the loss of
// funds.
if (map.minGas < MESSENGER_ERC20_MIN_GAS_LIMIT && !isNative) {
revert JBSucker_BelowMinGas(map.minGas, MESSENGER_ERC20_MIN_GAS_LIMIT);
}
}
/// @notice The calldata. Preferred to use over `msg.data`.
/// @return calldata The `msg.data` of this call.
function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
return ERC2771Context._msgData();
}
/// @notice The message's sender. Preferred to use over `msg.sender`.
/// @return sender The address which sent this call.
function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
return ERC2771Context._msgSender();
}
/// @dev ERC-2771 specifies the context as being a single address (20 bytes).
function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
return ERC2771Context._contextSuffixLength();
}
/// @notice Initializes the sucker with the project ID and peer address.
/// @param _projectId The ID of the project (on the local chain) that this sucker is associated with.
function initialize(uint256 _projectId) public initializer {
// slither-disable-next-line missing-zero-check
localProjectId = _projectId;
deployer = msg.sender;
}
//*********************************************************************//
// --------------------- external transactions ----------------------- //
//*********************************************************************//
/// @notice Adds the reclaimed `token` balance to the projects terminal. Can only be used if `ADD_TO_BALANCE_MODE`
/// is
/// `MANUAL`.
/// @param token The address of the terminal token to add to the project's balance.
function addOutstandingAmountToBalance(address token) external override {
if (ADD_TO_BALANCE_MODE != JBAddToBalanceMode.MANUAL) {
revert JBSucker_ManualNotAllowed(ADD_TO_BALANCE_MODE);
}
// Add entire outstanding amount to the project's balance.
_addToBalance({token: token, amount: amountToAddToBalanceOf(token)});
}
/// @notice Performs multiple claims.
/// @param claims A list of claims to perform (including the terminal token, merkle tree leaf, and proof for each
/// claim).
function claim(JBClaim[] calldata claims) external override {
// Claim each.
for (uint256 i; i < claims.length; i++) {
claim(claims[i]);
}
}
/// @notice `JBClaim` project tokens which have been bridged from the remote chain for their beneficiary.
/// @param claimData The terminal token, merkle tree leaf, and proof for the claim.
function claim(JBClaim calldata claimData) public override {
// Attempt to validate the proof against the inbox tree for the terminal token.
_validate({
projectTokenCount: claimData.leaf.projectTokenCount,
terminalToken: claimData.token,
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
beneficiary: claimData.leaf.beneficiary,
index: claimData.leaf.index,
leaves: claimData.proof
});
emit Claimed({
beneficiary: claimData.leaf.beneficiary,
token: claimData.token,
projectTokenCount: claimData.leaf.projectTokenCount,
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
index: claimData.leaf.index,
autoAddedToBalance: ADD_TO_BALANCE_MODE == JBAddToBalanceMode.ON_CLAIM ? true : false,
caller: _msgSender()
});
// Give the user their project tokens, send the project its funds.
_handleClaim({
terminalToken: claimData.token,
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
projectTokenAmount: claimData.leaf.projectTokenCount,
beneficiary: claimData.leaf.beneficiary
});
}
/// @notice Receive a merkle root for a terminal token from the remote project.
/// @dev This can only be called by the messenger contract on the local chain, with a message from the remote peer.
/// @param root The merkle root, token, and amount being received.
function fromRemote(JBMessageRoot calldata root) external payable {
// Make sure that the message came from our peer.
if (!_isRemotePeer(_msgSender())) {
revert JBSucker_NotPeer(_msgSender());
}
// Get the inbox in storage.
JBInboxTreeRoot storage inbox = _inboxOf[root.token];
// If the received tree's nonce is greater than the current inbox tree's nonce, update the inbox tree.
// We can't revert because this could be a native token transfer. If we reverted, we would lose the native
// tokens.
if (root.remoteRoot.nonce > inbox.nonce && state() != JBSuckerState.DEPRECATED) {
inbox.nonce = root.remoteRoot.nonce;
inbox.root = root.remoteRoot.root;
emit NewInboxTreeRoot({
token: root.token,
nonce: root.remoteRoot.nonce,
root: root.remoteRoot.root,
caller: _msgSender()
});
}
}
/// @notice Map an ERC-20 token on the local chain to an ERC-20 token on the remote chain, allowing that token to be
/// bridged.
/// @param map The local and remote terminal token addresses to map, and minimum amount/gas limits for bridging
/// them.
function mapToken(JBTokenMapping calldata map) public payable override {
_mapToken({map: map, transportPaymentValue: msg.value});
}
/// @notice Map multiple ERC-20 tokens on the local chain to ERC-20 tokens on the remote chain, allowing those
/// tokens to be bridged.
/// @param maps A list of local and remote terminal token addresses to map, and minimum amount/gas limits for
/// bridging them.
function mapTokens(JBTokenMapping[] calldata maps) external payable override {
uint256 numberToDisable;
// Loop over the number of mappings and increase numberToDisable to correctly set transportPaymentValue.
for (uint256 h; h < maps.length; h++) {
JBOutboxTree storage _outbox = _outboxOf[maps[h].localToken];
if (maps[h].remoteToken == address(0) && _outbox.numberOfClaimsSent != _outbox.tree.count) {
numberToDisable++;
}
}
// Perform each token mapping.
for (uint256 i; i < maps.length; i++) {
// slither-disable-next-line msg-value-loop
_mapToken({map: maps[i], transportPaymentValue: numberToDisable > 0 ? msg.value / numberToDisable : 0});
}
}
/// @notice Enables the emergency hatch for a list of tokens, allowing users to exit on the chain they deposited on.
/// @dev For use when a token or a few tokens are no longer compatible with a bridge.
/// @param tokens The terminal tokens to enable the emergency hatch for.
function enableEmergencyHatchFor(address[] calldata tokens) external override {
// The caller must be the project owner or have the `QUEUE_RULESETS` permission from them.
// slither-disable-next-line calls-loop
uint256 _projectId = projectId();
_requirePermissionFrom({
account: DIRECTORY.PROJECTS().ownerOf(_projectId),
projectId: _projectId,
permissionId: JBPermissionIds.SUCKER_SAFETY
});
// Enable the emergency hatch for each token.
for (uint256 i; i < tokens.length; i++) {
// We have an invariant where if emergencyHatch is true, enabled should be false.
_remoteTokenFor[tokens[i]].enabled = false;
_remoteTokenFor[tokens[i]].emergencyHatch = true;
}
emit EmergencyHatchOpened(tokens, _msgSender());
}
/// @notice Prepare project tokens and the cash out amount backing them to be bridged to the remote chain.
/// @dev This adds the tokens and funds to the outbox tree for the `token`. They will be bridged by the next call to
/// `toRemote` for the same `token`.
/// @param projectTokenCount The number of project tokens to prepare for bridging.
/// @param beneficiary The address of the recipient of the tokens on the remote chain.
/// @param minTokensReclaimed The minimum amount of terminal tokens to cash out for. If the amount cashed out is
/// less
/// than this, the transaction will revert.
/// @param token The address of the terminal token to cash out for.
function prepare(
uint256 projectTokenCount,
address beneficiary,
uint256 minTokensReclaimed,
address token
)
external
override
{
// Make sure the beneficiary is not the zero address, as this would revert when minting on the remote chain.
if (beneficiary == address(0)) {
revert JBSucker_ZeroBeneficiary();
}
// Get the project's token.
IERC20 projectToken = IERC20(address(TOKENS.tokenOf(projectId())));
if (address(projectToken) == address(0)) {
revert JBSucker_ZeroERC20Token();
}
// Make sure that the token is mapped to a remote token.
if (!_remoteTokenFor[token].enabled) {
revert JBSucker_TokenNotMapped(token);
}
// Make sure that the sucker still allows sending new messaged.
JBSuckerState deprecationState = state();
if (deprecationState == JBSuckerState.DEPRECATED || deprecationState == JBSuckerState.SENDING_DISABLED) {
revert JBSucker_Deprecated();
}
// Transfer the tokens to this contract.
// slither-disable-next-line reentrancy-events,reentrancy-benign
projectToken.safeTransferFrom({from: _msgSender(), to: address(this), value: projectTokenCount});
// Cash out the tokens.
// slither-disable-next-line reentrancy-events,reentrancy-benign
uint256 terminalTokenAmount = _pullBackingAssets({
projectToken: projectToken,
count: projectTokenCount,
token: token,
minTokensReclaimed: minTokensReclaimed
});
// Insert the item into the outbox tree for the terminal `token`.
_insertIntoTree({
projectTokenCount: projectTokenCount,
token: token,
terminalTokenAmount: terminalTokenAmount,
beneficiary: beneficiary
});
}
/// @notice Bridge the project tokens, cashed out funds, and beneficiary information for a given `token` to the
/// remote
/// chain.
/// @dev This sends the outbox root for the specified `token` to the remote chain.
/// @param token The terminal token being bridged.
function toRemote(address token) external payable override {
JBRemoteToken memory remoteToken = _remoteTokenFor[token];
// Ensure that the token does not have an emergency hatch enabled.
if (remoteToken.emergencyHatch) {
revert JBSucker_TokenHasInvalidEmergencyHatchState(token);
}
// Ensure that the amount being bridged exceeds the minimum bridge amount.
if (_outboxOf[token].balance < remoteToken.minBridgeAmount) {
revert JBSucker_QueueInsufficientSize(_outboxOf[token].balance, remoteToken.minBridgeAmount);
}
// Send the merkle root to the remote chain.
_sendRoot({transportPayment: msg.value, token: token, remoteToken: remoteToken});
}
/// @notice Lets user exit on the chain they deposited in a scenario where the bridge is no longer functional.
/// @param claimData The terminal token, merkle tree leaf, and proof for the claim
function exitThroughEmergencyHatch(JBClaim calldata claimData) external override {
// Does all the needed validation to ensure that the claim is valid *and* that claiming through the emergency
// hatch is allowed.
_validateForEmergencyExit({
projectTokenCount: claimData.leaf.projectTokenCount,
terminalToken: claimData.token,
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
beneficiary: claimData.leaf.beneficiary,
index: claimData.leaf.index,
leaves: claimData.proof
});
// Decrease the outstanding balance for this token.
_outboxOf[claimData.token].balance -= claimData.leaf.terminalTokenAmount;
// Give the user their project tokens, send the project its funds.
_handleClaim({
terminalToken: claimData.token,
terminalTokenAmount: claimData.leaf.terminalTokenAmount,
projectTokenAmount: claimData.leaf.projectTokenCount,
beneficiary: claimData.leaf.beneficiary
});
}
/// @notice Set or remove the time after which this sucker will be deprecated, once deprecated the sucker will no
/// longer be functional and it will let all users exit.
/// @param timestamp The time after which the sucker will be deprecated. Or `0` to remove the upcoming deprecation.
function setDeprecation(uint40 timestamp) external override {
// As long as the sucker has not started letting users withdrawal, its deprecation time can be
// extended/shortened.
JBSuckerState deprecationState = state();
if (deprecationState == JBSuckerState.DEPRECATED || deprecationState == JBSuckerState.SENDING_DISABLED) {
revert JBSucker_Deprecated();
}
// slither-disable-next-line calls-loop
uint256 _projectId = projectId();
_requirePermissionFrom({
account: DIRECTORY.PROJECTS().ownerOf(_projectId),
projectId: _projectId,
permissionId: JBPermissionIds.SUCKER_SAFETY
});
// This is the earliest time for when the sucker can be considered deprecated.
// There is a mandatory delay to allow for remaining messages to be received.
// This should be called on both sides of the suckers, preferably with a matching timestamp.
uint256 nextEarliestDeprecationTime = block.timestamp + _maxMessagingDelay();
// The deprecation can be entirely disabled *or* it has to be later than the earliest possible time.
if (timestamp != 0 && timestamp < nextEarliestDeprecationTime) {
revert JBSucker_DeprecationTimestampTooSoon(timestamp, nextEarliestDeprecationTime);
}
deprecatedAfter = timestamp;
emit DeprecationTimeUpdated(timestamp, _msgSender());
}
//*********************************************************************//
// ---------------------------- receive ----------------------------- //
//*********************************************************************//
/// @notice Used to receive cashed out native tokens.
receive() external payable {}
//*********************************************************************//
// --------------------- internal transactions ----------------------- //
//*********************************************************************//
/// @notice Adds funds to the projects balance.
/// @param token The terminal token to add to the project's balance.
/// @param amount The amount of terminal tokens to add to the project's balance.
function _addToBalance(address token, uint256 amount) internal {
// Make sure that the current `amountToAddToBalance` is greater than or equal to the amount being added.
uint256 addableAmount = amountToAddToBalanceOf(token);
if (amount > addableAmount) {
revert JBSucker_InsufficientBalance(amount, addableAmount);
}
uint256 _projectId = projectId();
// Get the project's primary terminal for the token.
// slither
// slither-disable-next-line calls-loop
IJBTerminal terminal = DIRECTORY.primaryTerminalOf({projectId: _projectId, token: token});
// slither-disable-next-line incorrect-equality
if (address(terminal) == address(0)) revert JBSucker_NoTerminalForToken(_projectId, token);
// Perform the `addToBalance`.
if (token != JBConstants.NATIVE_TOKEN) {
// slither-disable-next-line calls-loop
uint256 balanceBefore = IERC20(token).balanceOf(address(this));
SafeERC20.forceApprove({token: IERC20(token), spender: address(terminal), value: amount});
// slither-disable-next-line calls-loop
terminal.addToBalanceOf({
projectId: _projectId,
token: token,
amount: amount,
shouldReturnHeldFees: false,
memo: "",
metadata: ""
});
// Sanity check: make sure we transfer the full amount.
// slither-disable-next-line calls-loop,incorrect-equality
assert(IERC20(token).balanceOf(address(this)) == balanceBefore - amount);
} else {
// If the token is the native token, use `msg.value`.
// slither-disable-next-line arbitrary-send-eth,calls-loop
terminal.addToBalanceOf{value: amount}({
projectId: _projectId,
token: token,
amount: amount,
shouldReturnHeldFees: false,
memo: "",
metadata: ""
});
}
}
/// @notice The action(s) to perform after a user has succesfully proven their claim.
/// @param terminalToken The terminal token being sucked.
/// @param terminalTokenAmount The amount of terminal tokens.
/// @param projectTokenAmount The amount of project tokens.
/// @param beneficiary The beneficiary of the project tokens.
function _handleClaim(
address terminalToken,
uint256 terminalTokenAmount,
uint256 projectTokenAmount,
address beneficiary
)
internal
{
// If this contract's add to balance mode is `ON_CLAIM`, add the cashed out funds to the project's balance.
if (ADD_TO_BALANCE_MODE == JBAddToBalanceMode.ON_CLAIM && terminalTokenAmount != 0) {
_addToBalance({token: terminalToken, amount: terminalTokenAmount});
}
uint256 _projectId = projectId();
// Mint the project tokens for the beneficiary.
// slither-disable-next-line calls-loop,unused-return
IJBController(address(DIRECTORY.controllerOf(_projectId))).mintTokensOf({
projectId: _projectId,
tokenCount: projectTokenAmount,
beneficiary: beneficiary,
memo: "",
useReservedPercent: false
});
}
/// @notice Inserts a new leaf into the outbox merkle tree for the specified `token`.
/// @param projectTokenCount The amount of project tokens being cashed out.
/// @param token The terminal token being cashed out for.
/// @param terminalTokenAmount The amount of terminal tokens reclaimed by cashing out.
/// @param beneficiary The beneficiary of the project tokens on the remote chain.
function _insertIntoTree(
uint256 projectTokenCount,
address token,
uint256 terminalTokenAmount,
address beneficiary
)
internal
{
// Build a hash based on the token amounts and the beneficiary.
bytes32 hashed = _buildTreeHash({
projectTokenCount: projectTokenCount,
terminalTokenAmount: terminalTokenAmount,
beneficiary: beneficiary
});
// Get the outbox in storage.
JBOutboxTree storage outbox = _outboxOf[token];
// Create a new tree based on the outbox tree for the terminal token with the hash inserted.
MerkleLib.Tree memory tree = outbox.tree.insert(hashed);
// Update the outbox tree and balance for the terminal token.
outbox.tree = tree;
outbox.balance += terminalTokenAmount;
emit InsertToOutboxTree({
beneficiary: beneficiary,
token: token,
hashed: hashed,
index: tree.count - 1, // Subtract 1 since we want the 0-based index.
root: outbox.tree.root(),
projectTokenCount: projectTokenCount,
terminalTokenAmount: terminalTokenAmount,
caller: _msgSender()
});
}
/// @notice Checks if the `sender` (`_msgSender`) is a valid representative of the remote peer.
/// @param sender The message's sender.
function _isRemotePeer(address sender) internal virtual returns (bool valid);
/// @notice Map an ERC-20 token on the local chain to an ERC-20 token on the remote chain, allowing that token to be
/// bridged or disabled.
/// @param map The local and remote terminal token addresses to map, and minimum amount/gas limits for bridging
/// them.
/// @param transportPaymentValue The amount of `msg.value` to send for the token mapping.
function _mapToken(JBTokenMapping calldata map, uint256 transportPaymentValue) internal {
address token = map.localToken;
JBRemoteToken memory currentMapping = _remoteTokenFor[token];
// Once the emergency hatch for a token is enabled it can't be disabled.
if (currentMapping.emergencyHatch) {
revert JBSucker_TokenHasInvalidEmergencyHatchState(token);
}
// Validate the token mapping according to the rules of the sucker.
_validateTokenMapping(map);
// Reference the project id.
uint256 _projectId = projectId();
// slither-disable-next-line calls-loop
_requirePermissionFrom({
account: DIRECTORY.PROJECTS().ownerOf(_projectId),
projectId: _projectId,
permissionId: JBPermissionIds.MAP_SUCKER_TOKEN
});
// Make sure that the token does not get remapped to another remote token.
// As this would cause the funds for this token to be double spendable on the other side.
// It should not be possible to cause any issues even without this check
// a bridge *should* never accept such a request. This is mostly a sanity check.
if (
currentMapping.addr != address(0) && currentMapping.addr != map.remoteToken && map.remoteToken != address(0)
&& _outboxOf[token].tree.count != 0
) {
revert JBSucker_TokenAlreadyMapped(token, currentMapping.addr);
}
// If the remote token is being set to the 0 address (which disables bridging), send any remaining outbox funds
// to the remote chain.
if (map.remoteToken == address(0) && _outboxOf[token].numberOfClaimsSent != _outboxOf[token].tree.count) {
_sendRoot({transportPayment: transportPaymentValue, token: token, remoteToken: currentMapping});
}
// Update the token mapping.
_remoteTokenFor[token] = JBRemoteToken({
enabled: map.remoteToken != address(0),
emergencyHatch: false,
minGas: map.minGas,
// This is done so that a token can be disabled and then enabled again
// while ensuring the remoteToken never changes (unless it hasn't been used yet)
addr: map.remoteToken == address(0) ? currentMapping.addr : map.remoteToken,
minBridgeAmount: map.minBridgeAmount
});
}
/// @notice Cash out project tokens for terminal tokens.
/// @param projectToken The project token being cashed out.
/// @param count The number of project tokens to cash out.
/// @param token The terminal token to cash out for.
/// @param minTokensReclaimed The minimum amount of terminal tokens to reclaim. If the amount reclaimed is less than
/// this, the transaction will revert.
/// @return reclaimedAmount The amount of terminal tokens reclaimed by the cash out.
function _pullBackingAssets(
IERC20 projectToken,
uint256 count,
address token,
uint256 minTokensReclaimed
)
internal
virtual
returns (uint256 reclaimedAmount)
{
projectToken;
uint256 _projectId = projectId();
// Get the project's primary terminal for `token`. We will cash out from this terminal.
IJBCashOutTerminal terminal =
IJBCashOutTerminal(address(DIRECTORY.primaryTerminalOf({projectId: _projectId, token: token})));
// If the project doesn't have a primary terminal for `token`, revert.
if (address(terminal) == address(0)) {
revert JBSucker_NoTerminalForToken(_projectId, token);
}
// Cash out the tokens.
uint256 balanceBefore = _balanceOf(token, address(this));
reclaimedAmount = terminal.cashOutTokensOf({
holder: address(this),
projectId: _projectId,
cashOutCount: count,
tokenToReclaim: token,
minTokensReclaimed: minTokensReclaimed,
beneficiary: payable(address(this)),
metadata: bytes("")
});
// Sanity check to make sure we received the expected amount.
// This prevents malicious terminals from reporting amounts other than what they send.
// slither-disable-next-line incorrect-equality
assert(reclaimedAmount == _balanceOf({token: token, addr: address(this)}) - balanceBefore);
}
/// @notice Send the outbox root for the specified token to the remote peer.
/// @dev The call may have a `transportPayment` for bridging native tokens. Require it to be `0` if it is not
/// needed. Make sure if a value being paid to the bridge is expected to revert if the given value is `0`.
/// @param transportPayment the amount of `msg.value` that is going to get paid for sending this message. (usually
/// derived from `msg.value`)
/// @param token The terminal token to bridge the merkle tree of.
/// @param remoteToken The remote token which the `token` is mapped to.
function _sendRoot(uint256 transportPayment, address token, JBRemoteToken memory remoteToken) internal virtual {
// Ensure the token is mapped to an address on the remote chain.
if (remoteToken.addr == address(0)) revert JBSucker_TokenNotMapped(token);
// Make sure that the sucker still allows sending new messaged.
JBSuckerState deprecationState = state();
if (deprecationState == JBSuckerState.DEPRECATED || deprecationState == JBSuckerState.SENDING_DISABLED) {
revert JBSucker_Deprecated();
}
// Get the outbox in storage.
JBOutboxTree storage outbox = _outboxOf[token];
// Get the amount to send and then clear it from the outbox tree.
uint256 amount = outbox.balance;
delete outbox.balance;
// Increment the outbox tree's nonce.
uint64 nonce = ++outbox.nonce;
bytes32 root = outbox.tree.root();
uint256 count = outbox.tree.count;
// Update the numberOfClaimsSent to the current count of the tree.
// This is used as in the fallback to allow users to withdraw locally if the bridge is reverting.
outbox.numberOfClaimsSent = count;
uint256 index = count - 1;
// Emit an event for the relayers to watch for.
emit RootToRemote({root: root, token: token, index: index, nonce: nonce, caller: _msgSender()});
// Build the message to be send.
JBMessageRoot memory message = JBMessageRoot({
token: remoteToken.addr,
amount: amount,
remoteRoot: JBInboxTreeRoot({nonce: nonce, root: root})
});
// Execute the chain/sucker specific logic for transferring the assets and communicating the root.
_sendRootOverAMB(transportPayment, index, token, amount, remoteToken, message);
}
/// @notice Performs the logic to send a message to the peer over the AMB.
/// @dev This is chain/sucker/bridge specific logic.
/// @param transportPayment The amount of `msg.value` that is going to get paid for sending this message.
/// @param index The index of the most recent message that is part of the root.
/// @param token The terminal token being bridged.
/// @param amount The amount of terminal tokens being bridged.
/// @param remoteToken The remote token which the terminal token is mapped to.
/// @param message The message/root to send to the remote chain.
function _sendRootOverAMB(
uint256 transportPayment,
uint256 index,
address token,
uint256 amount,
JBRemoteToken memory remoteToken,
JBMessageRoot memory message
)
internal
virtual;
/// @notice What is the maximum time it takes for a message to be received on the other side.
/// @dev Be sure to keep in mind if a message fails having to retry and the time it takes to retry.
/// @return The maximum time it takes for a message to be received on the other side.
function _maxMessagingDelay() internal pure virtual returns (uint40) {
return 14 days;
}
/// @notice Validates a leaf as being in the inbox merkle tree and registers the leaf as executed (to prevent
/// double-spending).
/// @dev Reverts if the leaf is invalid.
/// @param projectTokenCount The number of project tokens which were cashed out.
/// @param terminalToken The terminal token that the project tokens were cashed out for.
/// @param terminalTokenAmount The amount of terminal tokens reclaimed by the cash out.
/// @param beneficiary The beneficiary which will receive the project tokens.
/// @param index The index of the leaf being proved in the terminal token's inbox tree.
/// @param leaves The leaves that prove that the leaf at the `index` is in the tree (i.e. the merkle branch that the
/// leaf is on).
function _validate(
uint256 projectTokenCount,
address terminalToken,
uint256 terminalTokenAmount,
address beneficiary,
uint256 index,
bytes32[_TREE_DEPTH] calldata leaves
)
internal
{
// Make sure the leaf has not already been executed.
if (_executedFor[terminalToken].get(index)) {
revert JBSucker_LeafAlreadyExecuted(terminalToken, index);
}
// Register the leaf as executed to prevent double-spending.
_executedFor[terminalToken].set(index);
// Calculate the root based on the leaf, the branch, and the index.
// Compare to the current root, Revert if they do not match.
_validateBranchRoot(
_inboxOf[terminalToken].root, projectTokenCount, terminalTokenAmount, beneficiary, index, leaves
);
}
/// @notice Validates a leaf as being in the outbox merkle tree and not being send over the amb, and registers the
/// leaf as executed (to prevent double-spending).
/// @dev Reverts if the leaf is invalid.
/// @param projectTokenCount The number of project tokens which were cashed out.
/// @param terminalToken The terminal token that the project tokens were cashed out for.
/// @param terminalTokenAmount The amount of terminal tokens reclaimed by the cash out.
/// @param beneficiary The beneficiary which will receive the project tokens.
/// @param index The index of the leaf being proved in the terminal token's inbox tree.
/// @param leaves The leaves that prove that the leaf at the `index` is in the tree (i.e. the merkle branch that the
/// leaf is on).
function _validateForEmergencyExit(
uint256 projectTokenCount,
address terminalToken,
uint256 terminalTokenAmount,
address beneficiary,
uint256 index,
bytes32[_TREE_DEPTH] calldata leaves
)
internal
{
// Make sure that the emergencyHatch is enabled for the token.
JBSuckerState deprecationState = state();
if (
deprecationState != JBSuckerState.DEPRECATED && deprecationState != JBSuckerState.SENDING_DISABLED
&& !_remoteTokenFor[terminalToken].emergencyHatch
) {
revert JBSucker_TokenHasInvalidEmergencyHatchState(terminalToken);
}
// Check that this claim is within the bounds of who can claim.
// If the root that this leaf is in was already send then we can not let the user claim here.
// As it could have also been received by the peer sucker, which would then let the user claim on each side.
// NOTE: We are comparing the *count* and the *index*, so `count - 1` is the last index that was sent.
// A count of 0 means that no root has ever been send for this token, so everyone can claim.
JBOutboxTree storage outboxOfToken = _outboxOf[terminalToken];
if (outboxOfToken.numberOfClaimsSent != 0 && outboxOfToken.numberOfClaimsSent - 1 >= index) {
revert JBSucker_LeafAlreadyExecuted(terminalToken, index);
}
{
// We re-use the same `_executedFor` mapping but we use a different slot.
// We can not use the regular mapping, since this claim is done for tokens being send from here to the pair.
// where the regular mapping is for tokens that were send on the pair to here. Even though these may seem
// similar they are actually completely unrelated.
address emergencyExitAddress = address(bytes20(keccak256(abi.encode(terminalToken))));
// Make sure the leaf has not already been executed.
if (_executedFor[emergencyExitAddress].get(index)) {
revert JBSucker_LeafAlreadyExecuted(terminalToken, index);
}
// Register the leaf as executed to prevent double-spending.
_executedFor[emergencyExitAddress].set(index);
}
// Calculate the root based on the leaf, the branch, and the index.
// Compare to the current root, Revert if they do not match.
_validateBranchRoot(
_outboxOf[terminalToken].tree.root(), projectTokenCount, terminalTokenAmount, beneficiary, index, leaves
);
}
/// @notice Validates a branch root against the expected root.
/// @dev This is a virtual function to allow a tests to override the behavior, it should never be overwritten
/// otherwise.
function _validateBranchRoot(
bytes32 expectedRoot,
uint256 projectTokenCount,
uint256 terminalTokenAmount,
address beneficiary,
uint256 index,
bytes32[_TREE_DEPTH] calldata leaves
)
internal
virtual
{
// Calculate the root based on the leaf, the branch, and the index.
bytes32 root = MerkleLib.branchRoot({
_item: _buildTreeHash({
projectTokenCount: projectTokenCount,
terminalTokenAmount: terminalTokenAmount,
beneficiary: beneficiary
}),
_branch: leaves,
_index: index
});
// Compare to the current root, Revert if they do not match.
if (root != expectedRoot) {
revert JBSucker_InvalidProof(root, expectedRoot);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.23;
import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBPermissions} from "@bananapus/core-v5/src/interfaces/IJBPermissions.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {IJBSuckerDeployer} from "./../interfaces/IJBSuckerDeployer.sol";
import {IJBSucker} from "./../interfaces/IJBSucker.sol";
import {JBPermissioned} from "@bananapus/core-v5/src/abstract/JBPermissioned.sol";
import {LibClone} from "solady/src/utils/LibClone.sol";
import {JBSucker} from "../JBSucker.sol";
import {ERC2771Context} from "@openzeppelin/contracts/metatx/ERC2771Context.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
/// @notice A base implementation for deploying suckers.
abstract contract JBSuckerDeployer is ERC2771Context, JBPermissioned, IJBSuckerDeployer {
//*********************************************************************//
// --------------- public immutable stored properties ---------------- //
//*********************************************************************//
/// @notice The directory of terminals and controllers for projects.
IJBDirectory public immutable override DIRECTORY;
/// @notice Only this address can configure this deployer, can only be used once.
address public immutable override LAYER_SPECIFIC_CONFIGURATOR;
/// @notice The contract that manages token minting and burning.
IJBTokens public immutable override TOKENS;
//*********************************************************************//
// ---------------------- public stored properties ------------------- //
//*********************************************************************//
/// @notice A mapping of suckers deployed by this contract.
mapping(address => bool) public override isSucker;
/// @notice The singleton used to clone suckers.
JBSucker public singleton;
//*********************************************************************//
// ---------------------------- constructor -------------------------- //
//*********************************************************************//
/// @param directory The directory of terminals and controllers for projects.
/// @param permissions The permissions contract for the deployer.
/// @param tokens The contract that manages token minting and burning.
/// @param configurator The address of the configurator.
constructor(
IJBDirectory directory,
IJBPermissions permissions,
IJBTokens tokens,
address configurator,
address trusted_forwarder
)
ERC2771Context(trusted_forwarder)
JBPermissioned(permissions)
{
DIRECTORY = directory;
TOKENS = tokens;
LAYER_SPECIFIC_CONFIGURATOR = configurator;
// There has to be a configurator address or the layer specific configuration has to already be configured.
if (configurator == address(0) && !_layerSpecificConfigurationIsSet()) {
revert JBSuckerDeployer_ZeroConfiguratorAddress();
}
}
//*********************************************************************//
// ------------------------ internal views --------------------------- //
//*********************************************************************//
/// @notice Check if the layer specific configuration is set or not. Used as a sanity check.
function _layerSpecificConfigurationIsSet() internal view virtual returns (bool);
/// @notice The message's sender. Preferred to use over `msg.sender`.
/// @return sender The address which sent this call.
function _msgSender() internal view override(ERC2771Context, Context) returns (address sender) {
return ERC2771Context._msgSender();
}
/// @notice The calldata. Preferred to use over `msg.data`.
/// @return calldata The `msg.data` of this call.
function _msgData() internal view override(ERC2771Context, Context) returns (bytes calldata) {
return ERC2771Context._msgData();
}
/// @dev ERC-2771 specifies the context as being a single address (20 bytes).
function _contextSuffixLength() internal view virtual override(ERC2771Context, Context) returns (uint256) {
return ERC2771Context._contextSuffixLength();
}
//*********************************************************************//
// --------------------- external transactions ----------------------- //
//*********************************************************************//
/// @notice Configure the singleton instance that is used to clone suckers.
/// @dev Can only be called *once* by the layer specific configurator.
/// @param _singleton The address of the singleton.
function configureSingleton(JBSucker _singleton) external {
// Make sure only the configurator can call this function.
if (_msgSender() != LAYER_SPECIFIC_CONFIGURATOR) {
revert JBSuckerDeployer_Unauthorized(_msgSender(), LAYER_SPECIFIC_CONFIGURATOR);
}
// Ensure that the layer specific configuration is set.
if (!_layerSpecificConfigurationIsSet()) {
revert JBSuckerDeployer_LayerSpecificNotConfigured();
}
// Make sure the singleton is not already configured.
if (address(singleton) != address(0)) revert JBSuckerDeployer_AlreadyConfigured();
singleton = _singleton;
}
/// @notice Create a new `JBSucker` for a specific project.
/// @dev Uses the sender address as the salt, which means the same sender must call this function on both chains.
/// @param localProjectId The project's ID on the local chain.
/// @param salt The salt to use for the `create2` address.
/// @return sucker The address of the new sucker.
function createForSender(
uint256 localProjectId,
bytes32 salt
)
external
override(IJBSuckerDeployer)
returns (IJBSucker sucker)
{
// Make sure that this deployer is configured properly.
if (address(singleton) == address(0)) {
revert JBSuckerDeployer_DeployerIsNotConfigured();
}
// Hash the salt with the sender address to ensure only a specific sender can create this sucker.
salt = keccak256(abi.encodePacked(_msgSender(), salt));
// Clone the singleton.
sucker = IJBSucker(LibClone.cloneDeterministic(address(singleton), salt));
// Mark it as a sucker that was deployed by this deployer.
isSucker[address(sucker)] = true;
// Initialize the clone.
JBSucker(payable(address(sucker))).initialize(localProjectId);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Options for how a `JBSucker`'s `amountToAddToBalance` gets added to its project's balance.
/// @custom:element MANUAL The amount gets added to the project's balance manually by calling
/// `addOutstandingAmountToBalance`.
/// @custom:element ON_CLAIM The amount gets added to the project's balance automatically when `claim` is called.
enum JBAddToBalanceMode {
MANUAL,
ON_CLAIM
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Whether a `JBArbitrumSucker` is on L1 or L2.
enum JBLayer {
L1,
L2
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice Options for the deprecation state of a `JBSucker`.
/// @custom:member ENABLED The `JBSucker` is not deprecated.
/// @custom:member DEPRECATION_PENDING The `JBSucker` has a deprecation set, but it is still fully functional.
/// @custom:member SENDING_DISABLED The `JBSucker` is deprecated and sending to the pair sucker is disabled.
/// @custom:member DEPRECATED The `JBSucker` is deprecated, but it continues to let users claim their funds.
enum JBSuckerState {
ENABLED,
DEPRECATION_PENDING,
SENDING_DISABLED,
DEPRECATED
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title Common interface for L1 and L2 Gateway Routers
*/
interface IArbGatewayRouter {
function defaultGateway() external view returns (address gateway);
function getGateway(address _token) external view returns (address gateway);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {JBLayer} from "../enums/JBLayer.sol";
import {IArbGatewayRouter} from "../interfaces/IArbGatewayRouter.sol";
import {IInbox} from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol";
interface IJBArbitrumSuckerDeployer {
function arbGatewayRouter() external view returns (IArbGatewayRouter);
function arbInbox() external view returns (IInbox);
function arbLayer() external view returns (JBLayer);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import {IOPStandardBridge} from "./IOPStandardBridge.sol";
import {IOPMessenger} from "./IOPMessenger.sol";
interface IJBOpSuckerDeployer {
function opBridge() external view returns (IOPStandardBridge);
function opMessenger() external view returns (IOPMessenger);
function setChainSpecificConstants(IOPMessenger messenger, IOPStandardBridge bridge) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOPMessenger} from "./IOPMessenger.sol";
import {IOPStandardBridge} from "./IOPStandardBridge.sol";
interface IJBOptimismSucker {
function OPBRIDGE() external view returns (IOPStandardBridge);
function OPMESSENGER() external view returns (IOPMessenger);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBController} from "@bananapus/core-v5/src/interfaces/IJBController.sol";
import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {JBAddToBalanceMode} from "../enums/JBAddToBalanceMode.sol";
import {JBSuckerState} from "../enums/JBSuckerState.sol";
import {JBClaim} from "../structs/JBClaim.sol";
import {JBInboxTreeRoot} from "../structs/JBInboxTreeRoot.sol";
import {JBOutboxTree} from "../structs/JBOutboxTree.sol";
import {JBRemoteToken} from "../structs/JBRemoteToken.sol";
import {JBTokenMapping} from "../structs/JBTokenMapping.sol";
import {JBMessageRoot} from "../structs/JBMessageRoot.sol";
// @notice The minimal interface for a sucker contract.
interface IJBSucker is IERC165 {
event Claimed(
address beneficiary,
address token,
uint256 projectTokenCount,
uint256 terminalTokenAmount,
uint256 index,
bool autoAddedToBalance,
address caller
);
event InsertToOutboxTree(
address indexed beneficiary,
address indexed token,
bytes32 hashed,
uint256 index,
bytes32 root,
uint256 projectTokenCount,
uint256 terminalTokenAmount,
address caller
);
event NewInboxTreeRoot(address indexed token, uint64 nonce, bytes32 root, address caller);
event RootToRemote(bytes32 indexed root, address indexed token, uint256 index, uint64 nonce, address caller);
function MESSENGER_BASE_GAS_LIMIT() external view returns (uint32);
function MESSENGER_ERC20_MIN_GAS_LIMIT() external view returns (uint32);
function ADD_TO_BALANCE_MODE() external view returns (JBAddToBalanceMode);
function DIRECTORY() external view returns (IJBDirectory);
function TOKENS() external view returns (IJBTokens);
function deployer() external view returns (address);
function peer() external view returns (address);
function projectId() external view returns (uint256);
function amountToAddToBalanceOf(address token) external view returns (uint256 amount);
function inboxOf(address token) external view returns (JBInboxTreeRoot memory);
function isMapped(address token) external view returns (bool);
function outboxOf(address token) external view returns (JBOutboxTree memory);
function peerChainId() external view returns (uint256 chainId);
function remoteTokenFor(address token) external view returns (JBRemoteToken memory);
function state() external view returns (JBSuckerState);
function addOutstandingAmountToBalance(address token) external;
function claim(JBClaim[] calldata claims) external;
function claim(JBClaim calldata claimData) external;
function mapToken(JBTokenMapping calldata map) external payable;
function mapTokens(JBTokenMapping[] calldata maps) external payable;
function prepare(
uint256 projectTokenAmount,
address beneficiary,
uint256 minTokensReclaimed,
address token
)
external;
function toRemote(address token) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBDirectory} from "@bananapus/core-v5/src/interfaces/IJBDirectory.sol";
import {IJBTokens} from "@bananapus/core-v5/src/interfaces/IJBTokens.sol";
import {IJBSucker} from "./IJBSucker.sol";
interface IJBSuckerDeployer {
error JBSuckerDeployer_AlreadyConfigured();
error JBSuckerDeployer_DeployerIsNotConfigured();
error JBSuckerDeployer_InvalidLayerSpecificConfiguration();
error JBSuckerDeployer_LayerSpecificNotConfigured();
error JBSuckerDeployer_Unauthorized(address caller, address expected);
error JBSuckerDeployer_ZeroConfiguratorAddress();
function DIRECTORY() external view returns (IJBDirectory);
function TOKENS() external view returns (IJBTokens);
function LAYER_SPECIFIC_CONFIGURATOR() external view returns (address);
function isSucker(address sucker) external view returns (bool);
function createForSender(uint256 localProjectId, bytes32 salt) external returns (IJBSucker sucker);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IJBSucker, JBClaim} from "./IJBSucker.sol";
// @notice Contains the IJBSucker interface and extends it with additional functions and events.
interface IJBSuckerExtended is IJBSucker {
event EmergencyHatchOpened(address[] tokens, address caller);
event DeprecationTimeUpdated(uint40 timestamp, address caller);
function enableEmergencyHatchFor(address[] calldata tokens) external;
function exitThroughEmergencyHatch(JBClaim calldata claimData) external;
function setDeprecation(uint40 timestamp) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOPMessenger {
function xDomainMessageSender() external returns (address);
function bridgeERC20To(
address localToken,
address remoteToken,
address to,
uint256 amount,
uint32 minGasLimit,
bytes calldata extraData
)
external;
function sendMessage(address target, bytes memory message, uint32 gasLimit) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOPStandardBridge {
/**
* @notice Sends ERC20 tokens to a receiver's address on the other chain. Note that if the
* ERC20 token on the other chain does not recognize the local token as the correct
* pair token, the ERC20 bridge will fail and the tokens will be returned to sender on
* this chain.
*
* @param localToken Address of the ERC20 on this chain.
* @param remoteToken Address of the corresponding token on the remote chain.
* @param to Address of the receiver.
* @param amount Amount of local tokens to deposit.
* @param minGasLimit Minimum amount of gas that the bridge can be relayed with.
* @param extraData Extra data to be sent with the transaction. Note that the recipient will
* not be triggered with this data, but it will be emitted and can be used
* to identify the transaction.
*/
function bridgeERC20To(
address localToken,
address remoteToken,
address to,
uint256 amount,
uint32 minGasLimit,
bytes calldata extraData
)
external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBLeaf} from "./JBLeaf.sol";
/// @custom:member token The token to claim.
/// @custom:member leaf The leaf to claim from.
/// @custom:member proof The proof to claim with. Must be of length `JBSucker._TREE_DEPTH`.
struct JBClaim {
address token;
JBLeaf leaf;
bytes32[32] proof;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice The root of an inbox tree for a given token in a `JBSucker`.
/// @dev Inbox trees are used to receive from the remote chain to the local chain. Tokens can be `claim`ed from the
/// inbox tree.
/// @custom:member nonce Tracks the nonce of the tree. The nonce cannot decrease.
/// @custom:member root The root of the tree.
struct JBInboxTreeRoot {
uint64 nonce;
bytes32 root;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice A leaf in the inbox or outbox tree of a `JBSucker`. Used to `claim` tokens from the inbox tree.
/// @custom:member index The index of the leaf.
/// @custom:member beneficiary The beneficiary of the leaf.
/// @custom:member projectTokenCount The number of project tokens to claim.
/// @custom:member terminalTokenAmount The amount of terminal tokens to claim.
struct JBLeaf {
uint256 index;
address beneficiary;
uint256 projectTokenCount;
uint256 terminalTokenAmount;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {JBInboxTreeRoot} from "./JBInboxTreeRoot.sol";
/// @notice Information about the remote (inbox) tree's root, passed in a message from the remote chain.
/// @custom:member token The address of the terminal token that the tree tracks.
/// @custom:member amount The amount of tokens being sent.
/// @custom:member remoteRoot The root of the merkle tree.
struct JBMessageRoot {
address token;
uint256 amount;
JBInboxTreeRoot remoteRoot;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {MerkleLib} from "../utils/MerkleLib.sol";
/// @notice A merkle tree used to track the outbox for a given token in a `JBSucker`.
/// @dev The outbox is used to send from the local chain to the remote chain.
/// @custom:member nonce The nonce of the outbox.
/// @custom:member balance The balance of the outbox.
/// @custom:member tree The merkle tree.
/// @custom:member numberOfClaimsSent the number of claims that have been sent to the peer. Used to determine which
/// claims have been sent.
struct JBOutboxTree {
uint64 nonce;
uint256 balance;
MerkleLib.Tree tree;
uint256 numberOfClaimsSent;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice A struct that represents a token on the remote chain.
/// @dev Invarient: If the `emergencyHatch` is true then the `enabled` is always false.
/// @custom:member enabled Whether the token is enabled.
/// @custom:member emergencyHatchOpened Whether the emergency hatch is opened.
/// @custom:member minGas The minimum gas to use when bridging.
/// @custom:member addr The address of the token on the remote chain.
/// @custom:member minBridgeAmount The minimum amount to bridge.
struct JBRemoteToken {
bool enabled;
bool emergencyHatch;
uint32 minGas;
address addr;
uint256 minBridgeAmount;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @custom:member localToken The local token address.
/// @custom:member minGas The minimum gas amount to bridge.
/// @custom:member remoteToken The remote token address.
/// @custom:member minBridgeAmount The minimum bridge amount.
struct JBTokenMapping {
address localToken;
uint32 minGas;
address remoteToken;
uint256 minBridgeAmount;
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.23;
/**
* @title MerkleLib
* @author Illusory Systems Inc.
* @notice An incremental merkle tree modeled on the eth2 deposit contract.
*
*/
library MerkleLib {
// ========== Custom Errors ===========
error MerkleLib__insert_treeIsFull();
// ============ Constants =============
uint256 internal constant TREE_DEPTH = 32;
uint256 internal constant MAX_LEAVES = 2 ** TREE_DEPTH - 1;
/**
* @dev Z_i represent the hash values at different heights for a binary tree with leaf values equal to `0`.
* (e.g. Z_1 is the keccak256 hash of (0x0, 0x0), Z_2 is the keccak256 hash of (Z_1, Z_1), etc...)
* Z_0 is the bottom of the 33-layer tree, Z_32 is the top (i.e. root).
* Used to shortcut calculation in root calculation methods below.
*/
bytes32 internal constant Z_0 = hex"0000000000000000000000000000000000000000000000000000000000000000";
bytes32 internal constant Z_1 = hex"ad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb5";
bytes32 internal constant Z_2 = hex"b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30";
bytes32 internal constant Z_3 = hex"21ddb9a356815c3fac1026b6dec5df3124afbadb485c9ba5a3e3398a04b7ba85";
bytes32 internal constant Z_4 = hex"e58769b32a1beaf1ea27375a44095a0d1fb664ce2dd358e7fcbfb78c26a19344";
bytes32 internal constant Z_5 = hex"0eb01ebfc9ed27500cd4dfc979272d1f0913cc9f66540d7e8005811109e1cf2d";
bytes32 internal constant Z_6 = hex"887c22bd8750d34016ac3c66b5ff102dacdd73f6b014e710b51e8022af9a1968";
bytes32 internal constant Z_7 = hex"ffd70157e48063fc33c97a050f7f640233bf646cc98d9524c6b92bcf3ab56f83";
bytes32 internal constant Z_8 = hex"9867cc5f7f196b93bae1e27e6320742445d290f2263827498b54fec539f756af";
bytes32 internal constant Z_9 = hex"cefad4e508c098b9a7e1d8feb19955fb02ba9675585078710969d3440f5054e0";
bytes32 internal constant Z_10 = hex"f9dc3e7fe016e050eff260334f18a5d4fe391d82092319f5964f2e2eb7c1c3a5";
bytes32 internal constant Z_11 = hex"f8b13a49e282f609c317a833fb8d976d11517c571d1221a265d25af778ecf892";
bytes32 internal constant Z_12 = hex"3490c6ceeb450aecdc82e28293031d10c7d73bf85e57bf041a97360aa2c5d99c";
bytes32 internal constant Z_13 = hex"c1df82d9c4b87413eae2ef048f94b4d3554cea73d92b0f7af96e0271c691e2bb";
bytes32 internal constant Z_14 = hex"5c67add7c6caf302256adedf7ab114da0acfe870d449a3a489f781d659e8becc";
bytes32 internal constant Z_15 = hex"da7bce9f4e8618b6bd2f4132ce798cdc7a60e7e1460a7299e3c6342a579626d2";
bytes32 internal constant Z_16 = hex"2733e50f526ec2fa19a22b31e8ed50f23cd1fdf94c9154ed3a7609a2f1ff981f";
bytes32 internal constant Z_17 = hex"e1d3b5c807b281e4683cc6d6315cf95b9ade8641defcb32372f1c126e398ef7a";
bytes32 internal constant Z_18 = hex"5a2dce0a8a7f68bb74560f8f71837c2c2ebbcbf7fffb42ae1896f13f7c7479a0";
bytes32 internal constant Z_19 = hex"b46a28b6f55540f89444f63de0378e3d121be09e06cc9ded1c20e65876d36aa0";
bytes32 internal constant Z_20 = hex"c65e9645644786b620e2dd2ad648ddfcbf4a7e5b1a3a4ecfe7f64667a3f0b7e2";
bytes32 internal constant Z_21 = hex"f4418588ed35a2458cffeb39b93d26f18d2ab13bdce6aee58e7b99359ec2dfd9";
bytes32 internal constant Z_22 = hex"5a9c16dc00d6ef18b7933a6f8dc65ccb55667138776f7dea101070dc8796e377";
bytes32 internal constant Z_23 = hex"4df84f40ae0c8229d0d6069e5c8f39a7c299677a09d367fc7b05e3bc380ee652";
bytes32 internal constant Z_24 = hex"cdc72595f74c7b1043d0e1ffbab734648c838dfb0527d971b602bc216c9619ef";
bytes32 internal constant Z_25 = hex"0abf5ac974a1ed57f4050aa510dd9c74f508277b39d7973bb2dfccc5eeb0618d";
bytes32 internal constant Z_26 = hex"b8cd74046ff337f0a7bf2c8e03e10f642c1886798d71806ab1e888d9e5ee87d0";
bytes32 internal constant Z_27 = hex"838c5655cb21c6cb83313b5a631175dff4963772cce9108188b34ac87c81c41e";
bytes32 internal constant Z_28 = hex"662ee4dd2dd7b2bc707961b1e646c4047669dcb6584f0d8d770daf5d7e7deb2e";
bytes32 internal constant Z_29 = hex"388ab20e2573d171a88108e79d820e98f26c0b84aa8b2f4aa4968dbb818ea322";
bytes32 internal constant Z_30 = hex"93237c50ba75ee485f4c22adf2f741400bdf8d6a9cc7df7ecae576221665d735";
bytes32 internal constant Z_31 = hex"8448818bb4ae4562849e949e17ac16e0be16688e156b5cf15e098c627c0056a9";
bytes32 internal constant Z_32 = hex"27ae5ba08d7291c96c8cbddcc148bf48a6d68c7974b94356f53754ef6171d757";
// ============= Structs ==============
/**
* @notice Struct representing incremental merkle tree. Contains current
* branch and the number of inserted leaves in the tree.
*
*/
struct Tree {
bytes32[TREE_DEPTH] branch;
uint256 count;
}
// ========= Write Methods =========
/**
* @notice Inserts a given node (leaf) into merkle tree. Operates on an in-memory tree and
* returns an updated version of that tree.
* @dev Reverts if the tree is already full.
* @param node Element to insert into tree.
* @return Tree Updated tree.
*
*/
function insert(Tree memory tree, bytes32 node) internal pure returns (Tree memory) {
// Update tree.count to increase the current count by 1 since we'll be including a new node.
uint256 size = ++tree.count;
if (size > MAX_LEAVES) revert MerkleLib__insert_treeIsFull();
// Loop starting at 0, ending when we've finished inserting the node (i.e. hashing it) into
// the active branch. Each loop we cut size in half, hashing the inserted node up the active
// branch along the way.
for (uint256 i; i < TREE_DEPTH;) {
// Check if the current size is odd; if so, we set this index in the branch to be the node.
if ((size & 1) == 1) {
// If i > 0, then this node will be a hash of the original node with every layer up
// until layer `i`.
tree.branch[i] = node;
return tree;
}
// If the size is not yet odd, we hash the current index in the tree branch with the node.
node = keccak256(abi.encodePacked(tree.branch[i], node));
size >>= 1; // Cut size in half (statement equivalent to: `size /= 2`).
unchecked {
++i;
}
}
// As the loop should always end prematurely with the `return` statement, this code should
// be unreachable. We revert here just to be safe.
revert MerkleLib__insert_treeIsFull();
}
// ========= Read Methods =========
/**
* @notice Calculates and returns tree's current root.
* @return _current bytes32 root.
*
*/
function root(Tree storage tree) internal view returns (bytes32 _current) {
uint256 _index = tree.count;
if (_index == 0) {
return Z_32;
}
uint256 i;
assembly {
let TREE_SLOT := tree.slot
for {} true {} {
for {} true {} {
if and(_index, 1) {
mstore(0, sload(TREE_SLOT))
mstore(0x20, Z_0)
_current := keccak256(0, 0x40)
break
}
if and(_index, shl(1, 1)) {
mstore(0, sload(add(TREE_SLOT, 1)))
mstore(0x20, Z_1)
_current := keccak256(0, 0x40)
i := 1
break
}
if and(_index, shl(2, 1)) {
mstore(0, sload(add(TREE_SLOT, 2)))
mstore(0x20, Z_2)
_current := keccak256(0, 0x40)
i := 2
break
}
if and(_index, shl(3, 1)) {
mstore(0, sload(add(TREE_SLOT, 3)))
mstore(0x20, Z_3)
_current := keccak256(0, 0x40)
i := 3
break
}
if and(_index, shl(4, 1)) {
mstore(0, sload(add(TREE_SLOT, 4)))
mstore(0x20, Z_4)
_current := keccak256(0, 0x40)
i := 4
break
}
if and(_index, shl(5, 1)) {
mstore(0, sload(add(TREE_SLOT, 5)))
mstore(0x20, Z_5)
_current := keccak256(0, 0x40)
i := 5
break
}
if and(_index, shl(6, 1)) {
mstore(0, sload(add(TREE_SLOT, 6)))
mstore(0x20, Z_6)
_current := keccak256(0, 0x40)
i := 6
break
}
if and(_index, shl(7, 1)) {
mstore(0, sload(add(TREE_SLOT, 7)))
mstore(0x20, Z_7)
_current := keccak256(0, 0x40)
i := 7
break
}
if and(_index, shl(8, 1)) {
mstore(0, sload(add(TREE_SLOT, 8)))
mstore(0x20, Z_8)
_current := keccak256(0, 0x40)
i := 8
break
}
if and(_index, shl(9, 1)) {
mstore(0, sload(add(TREE_SLOT, 9)))
mstore(0x20, Z_9)
_current := keccak256(0, 0x40)
i := 9
break
}
if and(_index, shl(10, 1)) {
mstore(0, sload(add(TREE_SLOT, 10)))
mstore(0x20, Z_10)
_current := keccak256(0, 0x40)
i := 10
break
}
if and(_index, shl(11, 1)) {
mstore(0, sload(add(TREE_SLOT, 11)))
mstore(0x20, Z_11)
_current := keccak256(0, 0x40)
i := 11
break
}
if and(_index, shl(12, 1)) {
mstore(0, sload(add(TREE_SLOT, 12)))
mstore(0x20, Z_12)
_current := keccak256(0, 0x40)
i := 12
break
}
if and(_index, shl(13, 1)) {
mstore(0, sload(add(TREE_SLOT, 13)))
mstore(0x20, Z_13)
_current := keccak256(0, 0x40)
i := 13
break
}
if and(_index, shl(14, 1)) {
mstore(0, sload(add(TREE_SLOT, 14)))
mstore(0x20, Z_14)
_current := keccak256(0, 0x40)
i := 14
break
}
if and(_index, shl(15, 1)) {
mstore(0, sload(add(TREE_SLOT, 15)))
mstore(0x20, Z_15)
_current := keccak256(0, 0x40)
i := 15
break
}
if and(_index, shl(16, 1)) {
mstore(0, sload(add(TREE_SLOT, 16)))
mstore(0x20, Z_16)
_current := keccak256(0, 0x40)
i := 16
break
}
if and(_index, shl(17, 1)) {
mstore(0, sload(add(TREE_SLOT, 17)))
mstore(0x20, Z_17)
_current := keccak256(0, 0x40)
i := 17
break
}
if and(_index, shl(18, 1)) {
mstore(0, sload(add(TREE_SLOT, 18)))
mstore(0x20, Z_18)
_current := keccak256(0, 0x40)
i := 18
break
}
if and(_index, shl(19, 1)) {
mstore(0, sload(add(TREE_SLOT, 19)))
mstore(0x20, Z_19)
_current := keccak256(0, 0x40)
i := 19
break
}
if and(_index, shl(20, 1)) {
mstore(0, sload(add(TREE_SLOT, 20)))
mstore(0x20, Z_20)
_current := keccak256(0, 0x40)
i := 20
break
}
if and(_index, shl(21, 1)) {
mstore(0, sload(add(TREE_SLOT, 21)))
mstore(0x20, Z_21)
_current := keccak256(0, 0x40)
i := 21
break
}
if and(_index, shl(22, 1)) {
mstore(0, sload(add(TREE_SLOT, 22)))
mstore(0x20, Z_22)
_current := keccak256(0, 0x40)
i := 22
break
}
if and(_index, shl(23, 1)) {
mstore(0, sload(add(TREE_SLOT, 23)))
mstore(0x20, Z_23)
_current := keccak256(0, 0x40)
i := 23
break
}
if and(_index, shl(24, 1)) {
mstore(0, sload(add(TREE_SLOT, 24)))
mstore(0x20, Z_24)
_current := keccak256(0, 0x40)
i := 24
break
}
if and(_index, shl(25, 1)) {
mstore(0, sload(add(TREE_SLOT, 25)))
mstore(0x20, Z_25)
_current := keccak256(0, 0x40)
i := 25
break
}
if and(_index, shl(26, 1)) {
mstore(0, sload(add(TREE_SLOT, 26)))
mstore(0x20, Z_26)
_current := keccak256(0, 0x40)
i := 26
break
}
if and(_index, shl(27, 1)) {
mstore(0, sload(add(TREE_SLOT, 27)))
mstore(0x20, Z_27)
_current := keccak256(0, 0x40)
i := 27
break
}
if and(_index, shl(28, 1)) {
mstore(0, sload(add(TREE_SLOT, 28)))
mstore(0x20, Z_28)
_current := keccak256(0, 0x40)
i := 28
break
}
if and(_index, shl(29, 1)) {
mstore(0, sload(add(TREE_SLOT, 29)))
mstore(0x20, Z_29)
_current := keccak256(0, 0x40)
i := 29
break
}
if and(_index, shl(30, 1)) {
mstore(0, sload(add(TREE_SLOT, 30)))
mstore(0x20, Z_30)
_current := keccak256(0, 0x40)
i := 30
break
}
if and(_index, shl(31, 1)) {
mstore(0, sload(add(TREE_SLOT, 31)))
mstore(0x20, Z_31)
_current := keccak256(0, 0x40)
// slither-disable-next-line write-after-write
i := 31
break
}
// slither-disable-next-line write-after-write
_current := Z_32
i := 32
break
}
if gt(i, 30) { break }
{
if lt(i, 1) {
switch and(_index, shl(1, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_1)
}
default {
mstore(0, sload(add(TREE_SLOT, 1)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 2) {
switch and(_index, shl(2, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_2)
}
default {
mstore(0, sload(add(TREE_SLOT, 2)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 3) {
switch and(_index, shl(3, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_3)
}
default {
mstore(0, sload(add(TREE_SLOT, 3)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 4) {
switch and(_index, shl(4, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_4)
}
default {
mstore(0, sload(add(TREE_SLOT, 4)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 5) {
switch and(_index, shl(5, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_5)
}
default {
mstore(0, sload(add(TREE_SLOT, 5)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 6) {
switch and(_index, shl(6, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_6)
}
default {
mstore(0, sload(add(TREE_SLOT, 6)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 7) {
switch and(_index, shl(7, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_7)
}
default {
mstore(0, sload(add(TREE_SLOT, 7)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 8) {
switch and(_index, shl(8, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_8)
}
default {
mstore(0, sload(add(TREE_SLOT, 8)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 9) {
switch and(_index, shl(9, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_9)
}
default {
mstore(0, sload(add(TREE_SLOT, 9)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 10) {
switch and(_index, shl(10, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_10)
}
default {
mstore(0, sload(add(TREE_SLOT, 10)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 11) {
switch and(_index, shl(11, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_11)
}
default {
mstore(0, sload(add(TREE_SLOT, 11)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 12) {
switch and(_index, shl(12, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_12)
}
default {
mstore(0, sload(add(TREE_SLOT, 12)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 13) {
switch and(_index, shl(13, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_13)
}
default {
mstore(0, sload(add(TREE_SLOT, 13)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 14) {
switch and(_index, shl(14, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_14)
}
default {
mstore(0, sload(add(TREE_SLOT, 14)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 15) {
switch and(_index, shl(15, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_15)
}
default {
mstore(0, sload(add(TREE_SLOT, 15)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 16) {
switch and(_index, shl(16, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_16)
}
default {
mstore(0, sload(add(TREE_SLOT, 16)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 17) {
switch and(_index, shl(17, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_17)
}
default {
mstore(0, sload(add(TREE_SLOT, 17)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 18) {
switch and(_index, shl(18, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_18)
}
default {
mstore(0, sload(add(TREE_SLOT, 18)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 19) {
switch and(_index, shl(19, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_19)
}
default {
mstore(0, sload(add(TREE_SLOT, 19)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 20) {
switch and(_index, shl(20, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_20)
}
default {
mstore(0, sload(add(TREE_SLOT, 20)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 21) {
switch and(_index, shl(21, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_21)
}
default {
mstore(0, sload(add(TREE_SLOT, 21)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 22) {
switch and(_index, shl(22, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_22)
}
default {
mstore(0, sload(add(TREE_SLOT, 22)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 23) {
switch and(_index, shl(23, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_23)
}
default {
mstore(0, sload(add(TREE_SLOT, 23)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 24) {
switch and(_index, shl(24, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_24)
}
default {
mstore(0, sload(add(TREE_SLOT, 24)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 25) {
switch and(_index, shl(25, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_25)
}
default {
mstore(0, sload(add(TREE_SLOT, 25)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 26) {
switch and(_index, shl(26, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_26)
}
default {
mstore(0, sload(add(TREE_SLOT, 26)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 27) {
switch and(_index, shl(27, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_27)
}
default {
mstore(0, sload(add(TREE_SLOT, 27)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 28) {
switch and(_index, shl(28, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_28)
}
default {
mstore(0, sload(add(TREE_SLOT, 28)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 29) {
switch and(_index, shl(29, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_29)
}
default {
mstore(0, sload(add(TREE_SLOT, 29)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 30) {
switch and(_index, shl(30, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_30)
}
default {
mstore(0, sload(add(TREE_SLOT, 30)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
if lt(i, 31) {
switch and(_index, shl(31, 1))
case 0 {
mstore(0, _current)
mstore(0x20, Z_31)
}
default {
mstore(0, sload(add(TREE_SLOT, 31)))
mstore(0x20, _current)
}
_current := keccak256(0, 0x40)
}
}
break
}
}
}
/**
* @notice Calculates and returns the merkle root for the given leaf `_item`,
* a merkle branch, and the index of `_item` in the tree.
* @param _item Merkle leaf
* @param _branch Merkle proof
* @param _index Index of `_item` in tree
* @return _current Calculated merkle root
*
*/
function branchRoot(
bytes32 _item,
bytes32[TREE_DEPTH] memory _branch,
uint256 _index
)
internal
pure
returns (bytes32 _current)
{
assembly {
_current := _item
let BRANCH_DATA_OFFSET := _branch
let f
f := shl(5, and(_index, 1))
mstore(f, _current)
mstore(sub(0x20, f), mload(BRANCH_DATA_OFFSET))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(1, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 1))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(2, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 2))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(3, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 3))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(4, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 4))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(5, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 5))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(6, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 6))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(7, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 7))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(8, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 8))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(9, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 9))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(10, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 10))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(11, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 11))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(12, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 12))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(13, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 13))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(14, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 14))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(15, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 15))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(16, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 16))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(17, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 17))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(18, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 18))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(19, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 19))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(20, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 20))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(21, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 21))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(22, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 22))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(23, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 23))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(24, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 24))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(25, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 25))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(26, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 26))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(27, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 27))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(28, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 28))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(29, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 29))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(30, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 30))))
_current := keccak256(0, 0x40)
f := shl(5, iszero(and(_index, shl(31, 1))))
mstore(sub(0x20, f), _current)
mstore(f, mload(add(BRANCH_DATA_OFFSET, shl(5, 31))))
_current := keccak256(0, 0x40)
}
}
}{
"evmVersion": "paris",
"metadata": {
"appendCBOR": true,
"bytecodeHash": "ipfs",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 3000
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"@sphinx-labs/contracts/=node_modules/@sphinx-labs/contracts/contracts/foundry/",
"@arbitrum/=node_modules/@arbitrum/",
"@bananapus/=node_modules/@bananapus/",
"@chainlink/=node_modules/@chainlink/",
"@eth-optimism/=node_modules/@eth-optimism/",
"@offchainlabs/=node_modules/@offchainlabs/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@prb/=node_modules/@prb/",
"@scroll-tech/=node_modules/@scroll-tech/",
"@uniswap/=node_modules/@uniswap/",
"@zksync/=node_modules/@zksync/",
"forge-std/=lib/forge-std/src/",
"hardhat/=node_modules/hardhat/",
"solady/=node_modules/solady/",
"solmate/=node_modules/solmate/",
"sphinx/=lib/sphinx/packages/contracts/contracts/forge-std/src/"
],
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IJBDirectory","name":"directory","type":"address"},{"internalType":"contract IJBPermissions","name":"permissions","type":"address"},{"internalType":"contract IJBTokens","name":"tokens","type":"address"},{"internalType":"address","name":"configurator","type":"address"},{"internalType":"address","name":"trusted_forwarder","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"projectId","type":"uint256"},{"internalType":"uint256","name":"permissionId","type":"uint256"}],"name":"JBPermissioned_Unauthorized","type":"error"},{"inputs":[],"name":"JBSuckerDeployer_AlreadyConfigured","type":"error"},{"inputs":[],"name":"JBSuckerDeployer_DeployerIsNotConfigured","type":"error"},{"inputs":[],"name":"JBSuckerDeployer_InvalidLayerSpecificConfiguration","type":"error"},{"inputs":[],"name":"JBSuckerDeployer_LayerSpecificNotConfigured","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"address","name":"expected","type":"address"}],"name":"JBSuckerDeployer_Unauthorized","type":"error"},{"inputs":[],"name":"JBSuckerDeployer_ZeroConfiguratorAddress","type":"error"},{"inputs":[],"name":"DIRECTORY","outputs":[{"internalType":"contract IJBDirectory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LAYER_SPECIFIC_CONFIGURATOR","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMISSIONS","outputs":[{"internalType":"contract IJBPermissions","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKENS","outputs":[{"internalType":"contract IJBTokens","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract JBSucker","name":"_singleton","type":"address"}],"name":"configureSingleton","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"localProjectId","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"createForSender","outputs":[{"internalType":"contract IJBSucker","name":"sucker","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isSucker","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"forwarder","type":"address"}],"name":"isTrustedForwarder","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"opBridge","outputs":[{"internalType":"contract IOPStandardBridge","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"opMessenger","outputs":[{"internalType":"contract IOPMessenger","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IOPMessenger","name":"messenger","type":"address"},{"internalType":"contract IOPStandardBridge","name":"bridge","type":"address"}],"name":"setChainSpecificConstants","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"singleton","outputs":[{"internalType":"contract JBSucker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
61012060405234801561001157600080fd5b50604051610b0b380380610b0b833981016040819052610030916100e4565b6001600160a01b0380821660805280851660a05280861660c05280841661010052821660e08190528590859085908590859015801561007457506100726100a1565b155b156100925760405163541b408960e11b815260040160405180910390fd5b50505050505050505050610159565b6002546000906001600160a01b03161515806100c757506003546001600160a01b031615155b905090565b6001600160a01b03811681146100e157600080fd5b50565b600080600080600060a086880312156100fc57600080fd5b8551610107816100cc565b6020870151909550610118816100cc565b6040870151909450610129816100cc565b606087015190935061013a816100cc565b608087015190925061014b816100cc565b809150509295509295909350565b60805160a05160c05160e0516101005161094b6101c060003960006101190152600081816101400152818161043d015281816104bb01526105ea01526000610218015260006102880152600081816101ad015281816101ef0152610717015261094b6000f3fe608060405234801561001057600080fd5b50600436106100df5760003560e01c8063572b6c051161008c578063b62654fb11610066578063b62654fb1461023a578063bac6b2ad1461024d578063c39ec12414610260578063f434c9141461028357600080fd5b8063572b6c051461019d5780637da0a877146101ed57806388bc2ef31461021357600080fd5b806328db992a116100bd57806328db992a146101625780633eeb32ee14610175578063516bf2f31461018a57600080fd5b806312130366146100e45780631d831d5c146101145780631e5d73df1461013b575b600080fd5b6100f76100f23660046107d3565b6102aa565b6040516001600160a01b0390911681526020015b60405180910390f35b6100f77f000000000000000000000000000000000000000000000000000000000000000081565b6100f77f000000000000000000000000000000000000000000000000000000000000000081565b6003546100f7906001600160a01b031681565b61018861018336600461080d565b61043b565b005b6002546100f7906001600160a01b031681565b6101dd6101ab36600461080d565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b604051901515815260200161010b565b7f00000000000000000000000000000000000000000000000000000000000000006100f7565b6100f77f000000000000000000000000000000000000000000000000000000000000000081565b6001546100f7906001600160a01b031681565b61018861025b36600461082a565b6105a9565b6101dd61026e36600461080d565b60006020819052908152604090205460ff1681565b6100f77f000000000000000000000000000000000000000000000000000000000000000081565b6001546000906001600160a01b03166102ef576040517fbc700a3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102f76106ba565b8260405160200161033992919060609290921b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168252601482015260340190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120600154909250610388906001600160a01b0316836106c9565b6001600160a01b0381166000818152602081905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517ffe4b84df000000000000000000000000000000000000000000000000000000008152600481018690529192509063fe4b84df90602401600060405180830381600087803b15801561041d57600080fd5b505af1158015610431573d6000803e3d6000fd5b5050505092915050565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661046d6106ba565b6001600160a01b0316146104ee576104836106ba565b6040517fdcd3b8230000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201527f0000000000000000000000000000000000000000000000000000000000000000909116602482015260440160405180910390fd5b6104f66106de565b61052c576040517fe2e55a4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546001600160a01b03161561056f576040517f52880bb100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6105b16106de565b156105e8576040517f52880bb100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031661061a6106ba565b6001600160a01b031614610630576104836106ba565b600280546001600160a01b038085167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790925560038054928416929091169190911790556106806106de565b6106b6576040517fb140414800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60006106c4610707565b905090565b60006106d76000848461077c565b9392505050565b6002546000906001600160a01b03161515806106c45750506003546001600160a01b0316151590565b6000366014336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480156107445750808210155b15610774576000366107568385610863565b6107619282906108a3565b61076a916108cd565b60601c9250505090565b339250505090565b60006c5af43d3d93803e602a57fd5bf36021528260145273602c3d8160093d39f33d3d3d3d363d3d37363d73600052816035600c86f59050806107c75763301164256000526004601cfd5b60006021529392505050565b600080604083850312156107e657600080fd5b50508035926020909101359150565b6001600160a01b038116811461080a57600080fd5b50565b60006020828403121561081f57600080fd5b81356106d7816107f5565b6000806040838503121561083d57600080fd5b8235610848816107f5565b91506020830135610858816107f5565b809150509250929050565b8181038181111561089d577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b600080858511156108b357600080fd5b838611156108c057600080fd5b5050820193919092039150565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000813581811691601485101561090d5780818660140360031b1b83161692505b50509291505056fea2646970667358221220632332caffe6b174cf5cf09579c0a3af10a7148dd60e4527a46111e59709683f64736f6c634300081700330000000000000000000000000061e516886a0540f63157f112c0588ee0651dcf00000000000000000000000004fd6913d6c32d8c216e153a43c04b1857a7793d0000000000000000000000004d0edd347fb1fa21589c1e109b3474924be8763600000000000000000000000080a8f7a4bd75b539ce26937016df607fdc9abeb5000000000000000000000000c29d6995ab3b0df4650ad643adeac55e7acbb566
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100df5760003560e01c8063572b6c051161008c578063b62654fb11610066578063b62654fb1461023a578063bac6b2ad1461024d578063c39ec12414610260578063f434c9141461028357600080fd5b8063572b6c051461019d5780637da0a877146101ed57806388bc2ef31461021357600080fd5b806328db992a116100bd57806328db992a146101625780633eeb32ee14610175578063516bf2f31461018a57600080fd5b806312130366146100e45780631d831d5c146101145780631e5d73df1461013b575b600080fd5b6100f76100f23660046107d3565b6102aa565b6040516001600160a01b0390911681526020015b60405180910390f35b6100f77f0000000000000000000000004d0edd347fb1fa21589c1e109b3474924be8763681565b6100f77f00000000000000000000000080a8f7a4bd75b539ce26937016df607fdc9abeb581565b6003546100f7906001600160a01b031681565b61018861018336600461080d565b61043b565b005b6002546100f7906001600160a01b031681565b6101dd6101ab36600461080d565b7f000000000000000000000000c29d6995ab3b0df4650ad643adeac55e7acbb5666001600160a01b0390811691161490565b604051901515815260200161010b565b7f000000000000000000000000c29d6995ab3b0df4650ad643adeac55e7acbb5666100f7565b6100f77f0000000000000000000000000061e516886a0540f63157f112c0588ee0651dcf81565b6001546100f7906001600160a01b031681565b61018861025b36600461082a565b6105a9565b6101dd61026e36600461080d565b60006020819052908152604090205460ff1681565b6100f77f00000000000000000000000004fd6913d6c32d8c216e153a43c04b1857a7793d81565b6001546000906001600160a01b03166102ef576040517fbc700a3700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6102f76106ba565b8260405160200161033992919060609290921b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168252601482015260340190565b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190528051602090910120600154909250610388906001600160a01b0316836106c9565b6001600160a01b0381166000818152602081905260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517ffe4b84df000000000000000000000000000000000000000000000000000000008152600481018690529192509063fe4b84df90602401600060405180830381600087803b15801561041d57600080fd5b505af1158015610431573d6000803e3d6000fd5b5050505092915050565b7f00000000000000000000000080a8f7a4bd75b539ce26937016df607fdc9abeb56001600160a01b031661046d6106ba565b6001600160a01b0316146104ee576104836106ba565b6040517fdcd3b8230000000000000000000000000000000000000000000000000000000081526001600160a01b0391821660048201527f00000000000000000000000080a8f7a4bd75b539ce26937016df607fdc9abeb5909116602482015260440160405180910390fd5b6104f66106de565b61052c576040517fe2e55a4b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546001600160a01b03161561056f576040517f52880bb100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6105b16106de565b156105e8576040517f52880bb100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000080a8f7a4bd75b539ce26937016df607fdc9abeb56001600160a01b031661061a6106ba565b6001600160a01b031614610630576104836106ba565b600280546001600160a01b038085167fffffffffffffffffffffffff00000000000000000000000000000000000000009283161790925560038054928416929091169190911790556106806106de565b6106b6576040517fb140414800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050565b60006106c4610707565b905090565b60006106d76000848461077c565b9392505050565b6002546000906001600160a01b03161515806106c45750506003546001600160a01b0316151590565b6000366014336001600160a01b037f000000000000000000000000c29d6995ab3b0df4650ad643adeac55e7acbb566161480156107445750808210155b15610774576000366107568385610863565b6107619282906108a3565b61076a916108cd565b60601c9250505090565b339250505090565b60006c5af43d3d93803e602a57fd5bf36021528260145273602c3d8160093d39f33d3d3d3d363d3d37363d73600052816035600c86f59050806107c75763301164256000526004601cfd5b60006021529392505050565b600080604083850312156107e657600080fd5b50508035926020909101359150565b6001600160a01b038116811461080a57600080fd5b50565b60006020828403121561081f57600080fd5b81356106d7816107f5565b6000806040838503121561083d57600080fd5b8235610848816107f5565b91506020830135610858816107f5565b809150509250929050565b8181038181111561089d577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b600080858511156108b357600080fd5b838611156108c057600080fd5b5050820193919092039150565b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000813581811691601485101561090d5780818660140360031b1b83161692505b50509291505056fea2646970667358221220632332caffe6b174cf5cf09579c0a3af10a7148dd60e4527a46111e59709683f64736f6c63430008170033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000061e516886a0540f63157f112c0588ee0651dcf00000000000000000000000004fd6913d6c32d8c216e153a43c04b1857a7793d0000000000000000000000004d0edd347fb1fa21589c1e109b3474924be8763600000000000000000000000080a8f7a4bd75b539ce26937016df607fdc9abeb5000000000000000000000000c29d6995ab3b0df4650ad643adeac55e7acbb566
-----Decoded View---------------
Arg [0] : directory (address): 0x0061E516886A0540F63157f112C0588eE0651dCF
Arg [1] : permissions (address): 0x04fD6913d6c32D8C216e153a43C04b1857a7793d
Arg [2] : tokens (address): 0x4d0Edd347FB1fA21589C1E109B3474924BE87636
Arg [3] : configurator (address): 0x80a8F7a4bD75b539CE26937016Df607fdC9ABeb5
Arg [4] : trusted_forwarder (address): 0xc29d6995AB3b0Df4650aD643adeAc55e7acBb566
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000000061e516886a0540f63157f112c0588ee0651dcf
Arg [1] : 00000000000000000000000004fd6913d6c32d8c216e153a43c04b1857a7793d
Arg [2] : 0000000000000000000000004d0edd347fb1fa21589c1e109b3474924be87636
Arg [3] : 00000000000000000000000080a8f7a4bd75b539ce26937016df607fdc9abeb5
Arg [4] : 000000000000000000000000c29d6995ab3b0df4650ad643adeac55e7acbb566
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.