Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/factories/ERC1155StrategyFactory.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import {ERC1155Strategy} from "../strategies/ERC1155Strategy.sol";
import {INFTStrategyHook, INFTStrategyFactory} from "../Interfaces.sol";
import {LibClone} from "solady/utils/LibClone.sol";
import {StrategyFactory} from "./StrategyFactory.sol";
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
/// @title ERC1155StrategyFactory - Factory for deploying ERC1155Strategy contracts
/// @author TokenWorks (https://token.works/)
/// @notice This factory deploys and manages ERC1155Strategy contracts with Uniswap V4 integration
/// @dev Uses ERC1967 proxy pattern for upgradeable ERC1155Strategy deployments
contract ERC1155StrategyFactory is StrategyFactory {
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™ ™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™ ™™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CONSTANTS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* STATE VARIABLES */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Mapping of NFT collection addresses to their ERC1155Strategy contracts
mapping(bytes32 => address) private _keyToStrategy;
/// @notice Mapping of ERC1155Strategy addresses to their collection contracts
mapping(address => bytes32) private _strategyToKey;
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CUSTOM ERRORS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Collection already has an ERC1155Strategy deployed
error CollectionAlreadyLaunched();
/// @notice Incorrect ETH amount sent with launch transaction
error WrongEthAmount();
/// @notice Contract does not implement ERC721 interface
error NotERC1155();
/// @notice Buy increment is outside valid range
error InvalidIncrement();
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CUSTOM EVENTS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Event emitted when a new ERC1155Strategy instance is launched
/// @param collection The NFT collection address
/// @param strategy The deployed ERC1155Strategy contract address
/// @param key The strategy uniq "key"
/// @param ids The strategy ids
/// @param tokenName The name of the strategy token
/// @param tokenSymbol The symbol of the strategy token
event ERC1155StrategyLaunched(
address indexed collection,
address indexed strategy,
bytes32 key,
uint256[] ids,
string tokenName,
string tokenSymbol
);
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CONSTRUCTOR */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Constructor initializes the factory with required dependencies
/// @param implementation the ERC1155Strategy implementation
/// @param _posm Uniswap V4 Position Manager address
/// @param _permit2 Permit2 contract address
/// @param _router Uniswap V4 Router address
/// @param _poolManager Uniswap V4 Pool Manager address
/// @param _feeAddress Address to receive deployment fees
/// @dev Sets up immutable references and creates ERC1155Strategy implementation
constructor(
address implementation,
address _posm,
address _permit2,
address payable _router,
address _poolManager,
address _feeAddress
) StrategyFactory(_posm, _permit2, _router, _poolManager, _feeAddress) {
strategyImplementation = implementation;
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* USER FUNCTIONS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Launches a new ERC1155Strategy contract with owner permissions
/// @param collection Address of the NFT collection contract
/// @param ids ids for the ERC1155Strategy
/// @param tokenName Name of the strategy token
/// @param tokenSymbol Symbol of the strategy token
/// @param collectionOwner Address that will receive fees from the strategy
/// @param buyIncrement The buy increment for price calculations
/// @return The deployed ERC1155Strategy contract
/// @dev Only callable by contract owner, deploys ERC1155Strategy and initializes liquidity
function ownerLaunchStrategy(
address collection,
ERC1155Strategy.StrategyType strategyType,
uint256[] memory ids,
string memory tokenName,
string memory tokenSymbol,
address collectionOwner,
uint256 buyIncrement
) external payable onlyLauncher returns (ERC1155Strategy) {
// Validate the parameters passed
if (hookAddress == address(0)) {
revert HookNotSet();
}
if (checkIfAlreadyLaunched(collection, strategyType, ids)) {
revert CollectionAlreadyLaunched();
}
if (buyIncrement < 0.01 ether || buyIncrement > 0.1 ether) {
revert InvalidIncrement();
}
if (!IERC165(collection).supportsInterface(0xd9b67a26)) {
revert NotERC1155();
}
ERC1155Strategy strategy = _deployUpgradeableNFTStrategy(
collection,
strategyType,
ids,
hookAddress,
tokenName,
tokenSymbol,
buyIncrement,
owner()
);
bytes32 key = getKey(collection, strategyType, ids);
_keyToStrategy[key] = address(strategy);
_strategyToKey[address(strategy)] = key;
// Costs 2 wei
_loadLiquidity(address(strategy));
// Set fees to collectionOwner
INFTStrategyHook(hookAddress).adminUpdateFeeAddress(
address(strategy),
collectionOwner
);
emit ERC1155StrategyLaunched(
collection,
address(strategy),
key,
ids,
tokenName,
tokenSymbol
);
return strategy;
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* INTERNAL FUNCTIONS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Deploys a new upgradeable ERC1155Strategy contract
/// @param _collection The NFT collection address
/// @param strategyType The strategy type for ERC1155Strategy
/// @param _ids The ids for ethe ERC1155Strategy
/// @param _hook The hook contract address
/// @param _tokenName The name of the strategy token
/// @param _tokenSymbol The symbol of the strategy token
/// @param _buyIncrement The buy increment for price calculations
/// @param _owner The owner of the ERC1155Strategy contract
/// @return proxy The deployed ERC1155Strategy proxy contract
/// @dev Uses ERC1967 proxy pattern with packed immutable args
function _deployUpgradeableNFTStrategy(
address _collection,
ERC1155Strategy.StrategyType strategyType,
uint256[] memory _ids,
address _hook,
string memory _tokenName,
string memory _tokenSymbol,
uint256 _buyIncrement,
address _owner
) internal returns (ERC1155Strategy proxy) {
bytes memory args = abi.encodePacked(
address(this),
router,
poolManager
);
proxy = ERC1155Strategy(
payable(
LibClone.deployERC1967(address(strategyImplementation), args)
)
);
proxy.initialize(
_collection,
strategyType,
_ids,
_hook,
_tokenName,
_tokenSymbol,
_buyIncrement,
launchUpgradeable ? _owner : address(0)
);
}
/// @param collection The address of the NFT collection to check
/// @return True if collection already has a strategy, false otherwise
/// @dev Checks both current factory and old factory for existing strategies
function checkIfAlreadyLaunched(
address collection,
ERC1155Strategy.StrategyType strategyType,
uint256[] memory ids
) public view returns (bool) {
bytes32 key = getKey(collection, strategyType, ids);
if (_keyToStrategy[key] != address(0)) {
return true;
}
return false;
}
function getKey(
address collection,
ERC1155Strategy.StrategyType strategyType,
uint256[] memory ids
) public pure returns (bytes32) {
return keccak256(abi.encode(collection, strategyType, ids));
}
}
"
},
"src/strategies/ERC1155Strategy.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
import {ERC1155} from "solady/tokens/ERC1155.sol";
import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol";
import {BaseStrategy} from "./BaseStrategy.sol";
/// @title ERC1155Strategy - An ERC20 token that constantly churns NFTs from a collection
/// @author TokenWorks (https://token.works/)
/// @notice This contract implements an ERC20 token backed by another token.
/// Users can trade the token on Uniswap V4, and the contract uses trading fees to buy bags of the underlying token.
/// @dev Uses ERC1967 proxy pattern with immutable args for gas-efficient upgrades
contract ERC1155Strategy is BaseStrategy {
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™ ™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ ™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™™™ ™™™™™™™™™™™™™™ */
/* ™™™™™™™™™™™ ™™™™™™™™™™™™ ™™™™™™™™™™™™ */
struct StrategyRange {
uint256 start;
uint256 end;
}
enum StrategyType {
Range,
List,
All
}
struct Listing {
uint256 price;
uint256 tokenId;
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CONSTANTS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice The collection this strategy is tied to
ERC1155 public collection;
/// @notice the range of ids, only if strategyType == StrategyType.Range
StrategyRange public range;
/// @notice the list of ids this strategy focuses on, only if strategyType == StrategyType.List
mapping(uint256 => bool) acceptedIds;
/// @notice the strategy type
StrategyType public strategyType;
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* STATE VARIABLES */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Mapping of listingId => Listing
mapping(uint256 => Listing) public listings;
/// @notice Counter for listings
uint256 lastListingId;
/// @notice Storage gap for future upgrades (prevents storage collisions)
uint256[50] private __gap;
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CUSTOM EVENTS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Emitted when the protocol buys from the collection
event ERC1155BoughtByProtocol(
uint256 indexed listingId,
uint256 indexed tokenId,
uint256 purchasePrice,
uint256 listPrice
);
/// @notice Emitted when the protocol buys from the collection
event ERC1155SoldByProtocol(
uint256 indexed listingId,
uint256 indexed tokenId,
uint256 price,
address buyer
);
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CUSTOM ERRORS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice given sale id is not currently for sale
error NotForSale();
/// @notice Sent ETH amount is less than the bag sale price
error PriceTooLow();
/// @notice Call didn't result in buying the right amount of the token
error NeedToBuyNFT();
/// @notice triggered when trying to buy tokens for 0
error NoZeroBuys();
/// @notice triggered when there is an error in inputs
error InputsError();
/// @notice triggered when target is collection
error InvalidTarget();
/// @notice triggered when trying to buy a token not in the list (or range)
error InvalidTokenId();
/// @notice triggered when the external call fails when buying an NFT
error ExternalCallFailed(bytes reason);
/// @notice triggered when the given ids are not in the right order
error InvalidIds();
/// @notice triggered when receiving ERC1155 items from a wrong collection
error InvalidCollection();
/// @notice triggered when receiving too many items
error InvalidAmount();
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* CONSTRUCTOR */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Constructor calls BaseStrategy() to disable initializers
/// @dev This is required for the proxy pattern to work correctly
constructor() BaseStrategy() {}
/// @notice Initializes the contract with required addresses and permissions
/// @param _collection Address of the underlying ERC20 contract
/// @param _strategyType type of ERC1155Strategy: Range|List
/// @param _ids ids to initialize the type (list must be in ascending order)
/// @param _hook Address of the StrategyHook contract
/// @param _tokenName Name of the token
/// @param _tokenSymbol Symbol of the token
/// @param _buyIncrement Buy increment for the token
/// @param _owner Owner of the contract
function initialize(
address _collection,
StrategyType _strategyType,
uint256[] memory _ids,
address _hook,
string memory _tokenName,
string memory _tokenSymbol,
uint256 _buyIncrement,
address _owner
) external initializer {
require(_collection != address(0), "Invalid collection");
collection = ERC1155(_collection);
strategyType = _strategyType;
_initializeIds(_strategyType, _ids);
__BaseStrategy_init(
_hook,
_tokenName,
_tokenSymbol,
_buyIncrement,
_owner
);
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* GETTERS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @dev this must be incremented whenever there is any change in BaseStrategy or this strategy
function VERSION() public pure override returns (uint256) {
return 1;
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* MECHANISM FUNCTIONS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Buys a specific NFT using accumulated fees
/// @param value Amount of ETH to spend on the NFT purchase
/// @param data Calldata for the external marketplace call
/// @param tokenId The token ID expected to be acquired
/// @param target The marketplace contract to call
/// @dev Protected against reentrancy, validates NFT acquisition
function buyTargetNFT(
uint256 value,
bytes calldata data,
uint256 tokenId,
address target
) external nonReentrant {
if (target == address(collection)) revert InvalidTarget();
// here we do not check the tokenId validity
// it will be checked in onERC1155Received since it's always called
// for ERC1155 collections.
// checking in onERC1155Received ensures only wanted ids of that collection are accepted
uint256 ethBalanceBefore = address(this).balance;
uint256 funds = availableFunds();
// this prevents tokens from being locked in contract because if salePrice == 0
// tokens would be seen as not on sale
if (funds == 0) {
revert NoZeroBuys();
}
// ensure the users doesn't ask for too much value
if (value > funds) {
revert NotEnoughEth();
}
uint256 listingId = (++lastListingId);
uint256 tokenBalanceBefore = collection.balanceOf(
address(this),
tokenId
);
// Call external
(bool success, bytes memory reason) = target.call{value: value}(data);
if (!success) {
revert ExternalCallFailed(reason);
}
if (
collection.balanceOf(address(this), tokenId) !=
tokenBalanceBefore + 1
) {
revert NeedToBuyNFT();
}
// Calculate actual cost of the NFT to base new price on
uint256 cost = ethBalanceBefore - address(this).balance;
currentFees -= cost;
uint256 listPrice = (cost * priceMultiplier) / 1000;
listings[listingId] = Listing({price: listPrice, tokenId: tokenId});
// Update last buy block to reset max price calculation
lastBuyBlock = block.number;
emit ERC1155BoughtByProtocol(listingId, tokenId, cost, listPrice);
}
/// @notice Sell an NFT that was previously bought and listed
/// @param listingId The ID of the bag to sell
function sellListing(uint256 listingId) external payable nonReentrant {
Listing memory listing = listings[listingId];
uint256 salePrice = listing.price;
uint256 tokenId = listing.tokenId;
// Verify listing is for sale
if (salePrice == 0) revert NotForSale();
// Verify sent ETH matches sale price
if (msg.value != salePrice) revert PriceTooLow();
// Remove from listings
delete listings[listingId];
// Transfer token to buyer
collection.safeTransferFrom(address(this), msg.sender, tokenId, 1, "");
// Add sale price to fees
ethToTwap += salePrice;
emit ERC1155SoldByProtocol(listingId, tokenId, salePrice, msg.sender);
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* GETTER FUNCTIONS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Returns all listings from id 0 to {lastListingId} included
/// @dev switch to list(start, end) if this function ever revert because too much gas
/// @return all listing from id 0 to {lastListingId}
function list() external view returns (Listing[] memory) {
return list(0, lastListingId);
}
/// @notice Returns all listings from id {startId} to {endId} included
/// @param startId the id of the first bag
/// @param endId the id of the last bag
/// @return _listings all listings from id {startId} to {endId}
function list(
uint256 startId,
uint256 endId
) public view returns (Listing[] memory _listings) {
if (endId < startId) {
revert InputsError();
}
uint256 length = endId - startId + 1;
_listings = new Listing[](length);
for (uint256 i; i < length; i++) {
_listings[i] = listings[startId + i];
}
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* Internal FUNCTIONS */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @dev initialize the strategy with type and ids
/// @dev if type == Range, ids.length must be 2, in ascending order
/// @dev if type == List, any number of ids, in ascending order
/// @dev if type == All, ids must be empty
/// @param _strategyType the strategy type (Range|List|All)
/// @param ids the list of ids (must be in ascending order)
function _initializeIds(
StrategyType _strategyType,
uint256[] memory ids
) internal {
// initialize the range or the list
if (_strategyType == StrategyType.All) {
if (ids.length != 0) {
revert InvalidIds();
}
} else if (_strategyType == StrategyType.Range) {
if (ids.length != 2) {
revert InvalidIds();
}
uint256 start = ids[0];
uint256 end = ids[1];
if (end < start) {
revert InvalidIds();
}
range = StrategyRange(start, end);
} else if (_strategyType == StrategyType.List) {
uint256 length = ids.length;
if (length == 0) {
revert InvalidIds();
}
uint256 previousId;
uint256 id;
for (uint256 i; i < length; i++) {
id = ids[i];
if (i > 0) {
if (id <= previousId) {
revert InvalidIds();
}
}
acceptedIds[id] = true;
previousId = id;
}
}
}
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/* Callbacks */
/* ™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™™ */
/// @notice Handles the receipt of items from the collection
function onERC1155Received(
address,
address,
uint256 tokenId,
uint256 amount,
bytes calldata
) external returns (bytes4) {
if (msg.sender != address(collection)) {
revert InvalidCollection();
}
if (amount != 1) {
revert InvalidAmount();
}
if (strategyType == StrategyType.Range) {
if (tokenId < range.start || tokenId > range.end) {
revert InvalidTokenId();
}
} else if (strategyType == StrategyType.List) {
if (!acceptedIds[tokenId]) {
revert InvalidTokenId();
}
}
return this.onERC1155Received.selector;
}
}
"
},
"src/Interfaces.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.7.5;
import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
import {IUniswapV4Router04} from "v4-router/interfaces/IUniswapV4Router04.sol";
/// @title Interfaces - Core interfaces for the strategy ecosystem
/// @author TokenWorks (https://token.works/)
/// @notice This file contains all the interfaces used by the strategy contracts
/// @notice Interface for Universal Router (legacy, kept for compatibility)
interface IUniversalRouter {
/// @notice Thrown when a required command has failed
error ExecutionFailed(uint256 commandIndex, bytes message);
/// @notice Thrown when attempting to send ETH directly to the contract
error ETHNotAccepted();
/// @notice Thrown when executing commands with an expired deadline
error TransactionDeadlinePassed();
/// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
error LengthMismatch();
// @notice Thrown when an address that isn't WETH tries to send ETH to the router without calldata
error InvalidEthSender();
/// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.
/// @param commands A set of concatenated commands, each 1 byte in length
/// @param inputs An array of byte strings containing abi encoded inputs for each command
/// @param deadline The deadline by which the transaction must be executed
function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}
/// @notice Parameters for exact input single swaps (legacy, kept for compatibility)
struct ExactInputSingleParams {
PoolKey poolKey;
bool zeroForOne;
uint128 amountIn;
uint128 amountOutMinimum;
bytes hookData;
}
/// @notice Interface for PunkStrategy contracts
/// @dev Interface for the original PunkStrategy token contract
interface IPunkStrategy {
// View functions
function loadingLiquidity() external view returns (bool);
function owner() external view returns (address);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function hookAddress() external view returns (address);
function currentFees() external view returns (uint256);
function reward() external view returns (uint256);
function lastPunkSalePrice() external view returns (uint256);
function priceMultiplier() external view returns (uint256);
function canProcessPunkSale() external view returns (bool);
// Admin functions
function loadLiquidity(address _hook) external payable;
function transferEther(address _to, uint256 _amount) external payable;
function setReward(uint256 _newReward) external;
function setPriceMultiplier(uint256 _newMultiplier) external;
function transferOwnership(address newOwner) external;
// Mechanism functions
function addFees() external payable;
function buyPunkAndRelist(uint256 punkId) external returns (uint256);
function processPunkSale() external returns (uint256);
// Constants
function MAX_SUPPLY() external pure returns (uint256);
function DEADADDRESS() external pure returns (address);
}
/// @notice Interface for PunkStrategyHook contracts
interface IPunkStrategyHook {
// View functions
function feeBips() external view returns (uint128);
function prePunkSellBips() external view returns (uint128);
function feeSplit() external view returns (IFeeSplit);
function calculateFee(bool isBuying) external view returns (uint128);
function getHookPermissions() external pure returns (Hooks.Permissions memory);
// Admin functions
function transferToken(address _token, address _to, uint256 _amount) external payable;
function updateFeeBips(uint128 _feeBips) external;
function updateManualFees(bool _manuallyProcessFees) external;
function updateFeeSplit(IFeeSplit _feeSplit) external;
// Mechanism functions
function feeCooldown() external;
function punksAreAccumulating() external;
function processAccumulatedFees() external;
}
/// @notice Interface for fee splitting contracts
interface IFeeSplit {
function processDeposit() external payable;
}
/// @notice Offer struct for CryptoPunks marketplace
struct Offer {
bool isForSale;
uint256 punkIndex;
address seller;
uint256 minValue;
address onlySellTo;
}
/// @notice Interface for CryptoPunks contract
/// @dev Interface for interacting with the original CryptoPunks contract
interface IPunks {
function buyPunk(uint256 punkIndex) external payable;
function offerPunkForSale(uint256 punkIndex, uint256 minSalePriceInWei) external;
function punksOfferedForSale(uint256 punkId)
external
view
returns (bool isForSale, uint256 punkIndex, address seller, uint256 minValue, address onlySellTo);
function balanceOf(address owner) external view returns (uint256);
function punkIndexToAddress(uint256 punkIndex) external view returns (address);
function withdraw() external;
function pendingWithdrawals(address owner) external view returns (uint256);
function transferPunk(address to, uint256 punkIndex) external;
}
/// @notice Interface for ERC20 tokens (standard interface)
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
/// @notice Interface for ERC721 NFT collections
/// @dev Standard ERC721 interface with additional owner() function for collection ownership
interface IERC721 {
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function setApprovalForAll(address operator, bool approved) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function isApprovedForAll(address owner, address operator) external view returns (bool);
function supportsInterface(bytes4 interfaceId) external view returns (bool);
function owner() external view returns (address);
}
interface IStrategy {
function factory() external view returns (address);
function router() external view returns (address);
function poolManager() external view returns (address);
function owner() external view returns (address);
function addFees() external payable;
function setPriceMultiplier(uint256 _newMultiplier) external;
function updateName(string memory _tokenName) external;
function updateSymbol(string memory _tokenSymbol) external;
function updateHookAddress(address _hookAddress) external;
function increaseTransferAllowance(uint256 amountAllowed) external;
function getTransferAllowance() external view returns (uint256);
function getImplementation() external view returns (address);
function upgradeToAndCall(address newImplementation, bytes memory data) external;
}
/// @notice Interface for NFTStrategy contracts
/// @dev Core interface for NFT-backed ERC20 strategy tokens
interface INFTStrategy is IStrategy {
function initialize(
address _collection,
address _hook,
string memory _tokenName,
string memory _tokenSymbol,
uint256 _buyIncrement,
address _owner
) external;
function nftForSale(uint256 tokenId) external view returns (uint256);
function sellTargetNFT(uint256 tokenId) external payable;
}
/// @notice Interface for BaseStrategyFactory contracts
/// @dev Factory interface for deploying and managing NFTStrategy contracts
interface IBaseStrategyFactory {
function loadingLiquidity() external view returns (bool);
function owner() external view returns (address);
function strategyImplementation() external view returns (address);
function setStrategyImplementation(address _strategyImplementation) external returns (address);
function updateHookAddress(address _hookAddress) external returns (address);
function disableLaunchUpgradeable() external;
}
/// @notice Interface for NFTStrategyFactory contracts
/// @dev Factory interface for deploying and managing NFTStrategy contracts
interface INFTStrategyFactory is IBaseStrategyFactory {
function nftStrategyImplementation() external view returns (address);
function collectionToNFTStrategy(address collection) external view returns (address);
function nftStrategyToCollection(address collection) external view returns (address);
function setNftStrategyImplementation(address _nftStrategyImplementation) external returns (address);
function ownerLaunchNFTStrategy(
address collection,
string memory tokenName,
string memory tokenSymbol,
address collectionOwner,
uint256 buyIncrement
) external payable returns (address);
}
/// @notice Interface for NFTStrategyHook contracts
/// @dev Hook interface for fee management and distribution
interface INFTStrategyHook {
function adminUpdateFeeAddress(address collection, address destination) external;
}
/// @notice Interface for NFTStrategyRange contracts
interface INFTStrategyRange {
function addFees() external payable;
function setPriceMultiplier(uint256 _newMultiplier) external;
function updateName(string memory _tokenName) external;
function updateSymbol(string memory _tokenSymbol) external;
function nftForSale(uint256 tokenId) external view returns (uint256);
function midSwap() external view returns (bool);
function setMidSwap(bool value) external;
function sellTargetNFT(uint256 tokenId) external payable;
function increaseTransferAllowance(uint256 amountAllowed) external;
function getTransferAllowance() external view returns (uint256);
}
/// @notice Interface for NFTStrategyRangeFactory contracts
interface INFTStrategyRangeFactory {
function poolManager() external view returns (address);
function loadingLiquidity() external view returns (bool);
function deployerBuying() external view returns (bool);
function owner() external view returns (address);
function setRouter(address _router, bool status) external;
function collectionToNFTStrategy(address collection) external view returns (address);
function nftStrategyToCollection(address collection) external view returns (address);
function routerRestrict() external view returns (bool);
function setRouterRestrict(bool status) external;
function validTransfer(address to, address from, address tokenAddress) external view returns (bool);
}
/// @notice Interface for NFTStrategyRangeHook contracts
interface INFTStrategyRangeHook {
function adminUpdateFeeAddress(address collection, address destination) external;
}
/// @notice Interface for router validation
interface IValidRouter {
function msgSender() external view returns (address);
}
/// @notice Interface for PunkStrategyPatch contracts
/// @dev Interface for patch contracts that manage PunkStrategy operations
interface IPunkStrategyPatch {
function updateFeeBips(uint128 _feeBips) external;
function setPriceMultiplier(uint256 _newMultiplier) external;
function transferOwnership(address newOwner) external;
function transferEther(address _to, uint256 _amount) external payable;
function setReward(uint256 _newReward) external;
function setTwapIncrement(uint256 _newIncrement) external;
function setTwapDelayInBlocks(uint256 _newDelay) external;
function buyPunkAndRelist(uint256 punkId) external returns (uint256);
function processPunkSale() external returns (uint256);
function processTokenTwap() external;
function transferPunkStrategyOwnership(address newOwner) external;
function addFees() external payable;
function transferToken(address _token, address _to, uint256 _amount) external payable;
function updateManualFees(bool _manuallyProcessFees) external;
function updateFeeSplit(IFeeSplit _feeSplit) external;
function owner() external view returns (address);
}
interface IGlobalDistributor {
function isGlobalDistributor(address) external view returns (bool);
}
interface IERC20Strategy is IStrategy {
function initialize(
address _collection,
uint256 _bagSize,
address _hook,
string memory _tokenName,
string memory _tokenSymbol,
uint256 _buyIncrement,
address _owner
) external;
}
interface IERC20StrategyFactory {
function ownerLaunchStrategy(
address token,
uint256 bagSize,
string memory tokenName,
string memory tokenSymbol,
address strategyFeeAddress,
uint256 buyIncrement
) external payable returns (address);
}
"
},
"lib/solady/src/utils/LibClone.sol": {
"content": "// 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 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:
/// A 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:
/// A 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:
/// A 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:
/// A 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:
/// A 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.
funct
Submitted on: 2025-11-07 11:26:48
Comments
Log in to comment.
No comments yet.