Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 18 from a total of 18 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Create ERC20 | 19577178 | 297 days ago | IN | 1.2 ETH | 0.25714931 | ||||
Create ERC20 | 19542575 | 302 days ago | IN | 0.3 ETH | 0.13377759 | ||||
Create ERC20 | 19542532 | 302 days ago | IN | 0.7 ETH | 0.15466811 | ||||
Create ERC20 | 19533182 | 303 days ago | IN | 1.2 ETH | 0.45474571 | ||||
Create ERC20 | 19528097 | 304 days ago | IN | 2.2 ETH | 0.23819064 | ||||
Create ERC20 | 19507348 | 307 days ago | IN | 1.2 ETH | 0.12794723 | ||||
Create ERC20 | 19464287 | 313 days ago | IN | 1.2 ETH | 0.28090105 | ||||
Create ERC20 | 19441846 | 316 days ago | IN | 1.2 ETH | 0.26803644 | ||||
Create ERC20 | 19433698 | 318 days ago | IN | 1.2 ETH | 0.48753933 | ||||
Create ERC20 | 19421230 | 319 days ago | IN | 1.2 ETH | 0.40884697 | ||||
Create ERC20 | 19417254 | 320 days ago | IN | 1.2 ETH | 0.37210235 | ||||
Create ERC20 | 19412819 | 320 days ago | IN | 0.2 ETH | 0.60184031 | ||||
Create ERC20 | 19392984 | 323 days ago | IN | 0.7 ETH | 0.47161164 | ||||
Create ERC20 | 19385576 | 324 days ago | IN | 2.2 ETH | 0.49200646 | ||||
Create ERC20 | 19379375 | 325 days ago | IN | 1.2 ETH | 0.53493386 | ||||
Create ERC20 | 19378180 | 325 days ago | IN | 0.7 ETH | 0.55425584 | ||||
Create ERC20 | 19377130 | 325 days ago | IN | 2.53 ETH | 0.5962433 | ||||
Initialise Machi... | 19372981 | 326 days ago | IN | 0 ETH | 0.00248427 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19577178 | 297 days ago | 0.2 ETH | ||||
19577178 | 297 days ago | 1 ETH | ||||
19542575 | 302 days ago | 0.2 ETH | ||||
19542575 | 302 days ago | 0.1 ETH | ||||
19542532 | 302 days ago | 0.2 ETH | ||||
19542532 | 302 days ago | 0.5 ETH | ||||
19533182 | 303 days ago | 0.2 ETH | ||||
19533182 | 303 days ago | 1 ETH | ||||
19528097 | 304 days ago | 0.2 ETH | ||||
19528097 | 304 days ago | 2 ETH | ||||
19507348 | 307 days ago | 0.2 ETH | ||||
19507348 | 307 days ago | 1 ETH | ||||
19464287 | 313 days ago | 0.2 ETH | ||||
19464287 | 313 days ago | 1 ETH | ||||
19441846 | 316 days ago | 0.2 ETH | ||||
19441846 | 316 days ago | 1 ETH | ||||
19433698 | 318 days ago | 0.2 ETH | ||||
19433698 | 318 days ago | 1 ETH | ||||
19421230 | 319 days ago | 0.2 ETH | ||||
19421230 | 319 days ago | 1 ETH | ||||
19417254 | 320 days ago | 0.2 ETH | ||||
19417254 | 320 days ago | 1 ETH | ||||
19412819 | 320 days ago | 0.2 ETH | ||||
19392984 | 323 days ago | 0.2 ETH | ||||
19392984 | 323 days ago | 0.5 ETH |
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 Source Code Verified (Exact Match)
Contract Name:
ERC20FactoryByMetadrop
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 90 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1 // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; import {AuthorityModel} from "../../Global/AuthorityModel.sol"; import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol"; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; import {Decommissionable} from "../../Global/Decommissionable.sol"; import {IERC20ByMetadrop} from "../ERC20/IERC20ByMetadrop.sol"; import {IERC20DRIPoolByMetadrop} from "../ERC20Pools/IERC20DRIPoolByMetadrop.sol"; import {IERC20FactoryByMetadrop} from "./IERC20FactoryByMetadrop.sol"; import {IERC20MachineByMetadrop} from "../ERC20Machine/IERC20MachineByMetadrop.sol"; import {SafeERC20, IERC20} from "../../Global/OZ/SafeERC20.sol"; import {SignatureChecker} from "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol"; /** * @dev Metadrop ERC-20 factory */ contract ERC20FactoryByMetadrop is AuthorityModel, Context, Decommissionable, IERC20FactoryByMetadrop { using SafeERC20 for IERC20; using Clones for address payable; // Uniswap router address address public immutable uniswapRouter; // Token vault address address public immutable tokenVault; // Deployer address address private immutable deployer; // DRIPool template: address payable public driPoolTemplate; // Machine template: address payable public machineTemplate; // Metadrop trusted oracle address address public metadropOracleAddress; // Address for all platform fee payments address public platformTreasury; // The oracle signed message validity period: // Note that maximum is 65,535, therefore 18.2 hours (which seems plenty) uint16 private messageValidityInSeconds = 30 minutes; /** * @dev {constructor} * * @param superAdmin_ The address that can add and remove user authority roles. Will also be added as the * first platform admin. * @param platformAdmins_ The address(es) for the platform admin(s) * @param platformTreasury_ The address of the platform treasury. This will be used on primary vesting * for the platform share of funds and on the royalty payment splitter for the platform share. * @param metadropOracleAddress_ The address of the metadrop oracle signer * @param uniswapRouter_ The address of the uniswap router * @param tokenVault_ The address of the token vault * @param driPoolTemplate_ The address of the launch pool template */ constructor( address superAdmin_, address[] memory platformAdmins_, address platformTreasury_, address metadropOracleAddress_, address uniswapRouter_, address tokenVault_, address driPoolTemplate_ ) { // Save the deployer address: deployer = _msgSender(); // The initial instance owner is set as the Ownable owner on all cloned contracts: if (superAdmin_ == address(0)) { _revert(SuperAdminCannotBeAddressZero.selector); } // superAdmin can grant and revoke all other roles. This address MUST be secured. // For the duration of this constructor only the super admin is the deployer. // This is so the deployer can set initial authorities. // We set to the configured super admin address at the end of the constructor. superAdmin = _msgSender(); // Grant platform admin to the deployer for the duration of the constructor: grantPlatformAdmin(_msgSender()); // By default we will revoke the temporary authority for the deployer, BUT, // if the deployer is in the platform admin array then we want to keep that // authority, as it has been explicitly set. We handle that situation using // a bool: bool revokeDeployerPlatformAdmin = true; grantPlatformAdmin(superAdmin_); for (uint256 i = 0; i < platformAdmins_.length; ) { // Check if the address we are granting for is the deployer. If it is, // then the deployer address already IS a platform admin and it would be // a waste of gas to grant again. Instead, we update the bool to show that // we DON'T want to revoke this permission at the end of this method: if (platformAdmins_[i] == _msgSender()) { revokeDeployerPlatformAdmin = false; } else { grantPlatformAdmin(platformAdmins_[i]); } unchecked { i++; } } // Set platform treasury: if (platformTreasury_ == address(0)) { _revert(PlatformTreasuryCannotBeAddressZero.selector); } platformTreasury = platformTreasury_; if (metadropOracleAddress_ == address(0)) { _revert(MetadropOracleCannotBeAddressZero.selector); } metadropOracleAddress = metadropOracleAddress_; uniswapRouter = uniswapRouter_; tokenVault = tokenVault_; driPoolTemplate = payable(driPoolTemplate_); // This is the factory factory = address(this); // Revoke platform admin status of the deployer and transfer superAdmin // and ownable owner to the superAdmin_. // Revoke platform admin based on the bool flag set earlier (see above // for an explanation of how this flag is set) if (revokeDeployerPlatformAdmin) { revokePlatformAdmin(_msgSender()); } if (superAdmin_ != _msgSender()) { transferSuperAdmin(superAdmin_); } } /** * @dev function {initialiseMachineAddress} * * Initialise the machine template address. This needs to be separate from * the constructor as the machine needs the factory address on its constructor. * * This must ALWAYS be called as part of deployment. * * @param machineTemplate_ the machine address */ function initialiseMachineAddress(address machineTemplate_) external { if (_msgSender() != deployer) { _revert(DeployerOnly.selector); } if (machineTemplate != address(0)) { _revert(AddressAlreadySet.selector); } machineTemplate = payable(machineTemplate_); emit MachineAddressUpdated(address(0), machineTemplate_); } /** * @dev function {decommissionFactory} onlySuperAdmin * * Make this factory unusable for creating new ERC20s, forever * */ function decommissionFactory() external onlySuperAdmin { _decommission(); } /** * @dev function {setMetadropOracleAddress} onlyPlatformAdmin * * Set the metadrop trusted oracle address * * @param metadropOracleAddress_ Trusted metadrop oracle address */ function setMetadropOracleAddress( address metadropOracleAddress_ ) external onlyPlatformAdmin { if (metadropOracleAddress_ == address(0)) { _revert(MetadropOracleCannotBeAddressZero.selector); } address oldMetadropOracleAddress = metadropOracleAddress; metadropOracleAddress = metadropOracleAddress_; emit OracleAddressUpdated(oldMetadropOracleAddress, metadropOracleAddress_); } /** * @dev function {setMessageValidityInSeconds} onlyPlatformAdmin * * Set the validity period of signed messages * * @param messageValidityInSeconds_ Validity period in seconds for messages signed by the trusted oracle */ function setMessageValidityInSeconds( uint256 messageValidityInSeconds_ ) external onlyPlatformAdmin { uint16 oldMessageValidityInSeconds = messageValidityInSeconds; messageValidityInSeconds = uint16(messageValidityInSeconds_); emit MessageValidityInSecondsUpdated( oldMessageValidityInSeconds, messageValidityInSeconds_ ); } /** * @dev function {setPlatformTreasury} onlySuperAdmin * * Set the address that platform fees will be paid to / can be withdrawn to. * Note that this is restricted to the highest authority level, the super * admin. Platform admins can trigger a withdrawal to the treasury, but only * the default admin can set or alter the treasury address. It is recommended * that the default admin is highly secured and restrited e.g. a multi-sig. * * @param platformTreasury_ New treasury address */ function setPlatformTreasury( address platformTreasury_ ) external onlySuperAdmin { if (platformTreasury_ == address(0)) { _revert(PlatformTreasuryCannotBeAddressZero.selector); } address oldPlatformTreasury = platformTreasury; platformTreasury = platformTreasury_; emit PlatformTreasuryUpdated(oldPlatformTreasury, platformTreasury_); } /** * @dev function {setMachineAddress} onlyPlatformAdmin * * Set a new machine template address * * @param newMachineAddress_ the new machine address */ function setMachineAddress( address newMachineAddress_ ) external onlyPlatformAdmin { if (newMachineAddress_ == address(0)) { _revert(MachineAddressCannotBeAddressZero.selector); } address oldMachineAddress = machineTemplate; machineTemplate = payable(newMachineAddress_); emit MachineAddressUpdated(oldMachineAddress, newMachineAddress_); } /** * @dev function {setDriPoolAddress} onlyPlatformAdmin * * Set a new launch pool template address * * @param newDriPoolAddress_ the new launch pool address */ function setDriPoolAddress( address newDriPoolAddress_ ) external onlyPlatformAdmin { if (newDriPoolAddress_ == address(0)) { _revert(DriPoolAddressCannotBeAddressZero.selector); } address oldDriPoolAddress = driPoolTemplate; driPoolTemplate = payable(newDriPoolAddress_); emit DriPoolAddressUpdated(oldDriPoolAddress, newDriPoolAddress_); } /** * @dev function {withdrawETH} onlyPlatformAdmin * * A withdraw function to allow ETH to be withdrawn to the treasury * * @param amount_ The amount to withdraw */ function withdrawETH(uint256 amount_) external onlyPlatformAdmin { _withdrawETH(amount_); } /** * @dev function {_withdrawETH} * * Internal function to allow ETH to be withdrawn to the treasury * * @param amount_ The amount to withdraw */ function _withdrawETH(uint256 amount_) internal { (bool success, ) = platformTreasury.call{value: amount_}(""); if (!success) { _revert(TransferFailed.selector); } } /** * @dev function {withdrawERC20} onlyPlatformAdmin * * A withdraw function to allow ERC20s to be withdrawn to the treasury * * @param token_ The contract address of the token being withdrawn * @param amount_ The amount to withdraw */ function withdrawERC20( IERC20 token_, uint256 amount_ ) external onlyPlatformAdmin { token_.safeTransfer(platformTreasury, amount_); } /** * @dev function {createERC20} * * Create an ERC-20 * * @param metaId_ The drop Id being approved * @param salt_ Salt for create2 * @param erc20Config_ ERC20 configuration * @param signedMessage_ The signed message object * @param vaultFee_ The fee for the token vault * @param deploymentFee_ The fee for deployment, if any * @return deployedAddress_ The deployed ERC20 contract address */ function createERC20( string calldata metaId_, bytes32 salt_, ERC20Config calldata erc20Config_, SignedDropMessageDetails calldata signedMessage_, uint256 vaultFee_, uint256 deploymentFee_ ) external payable notWhenDecommissioned returns (address deployedAddress_) { // Check the signed message origin and time: _verifyMessage(signedMessage_); // We can only proceed if the hash of the passed configuration matches the hash // signed by our oracle signer: _checkConfigHashMatches( metaId_, salt_, erc20Config_, signedMessage_, vaultFee_, deploymentFee_ ); // Decode required base parameters: ( string[2] memory tokenDetails, bool addLiquidityOnCreate, bool usesDRIPool ) = _decodeBaseData(erc20Config_.baseParameters); address driPool = _processDRIPool( usesDRIPool, erc20Config_.poolParameters, tokenDetails ); // Deploy the new contract: (address newERC20, bytes memory contructorArgs) = _deployERC20Contract( keccak256(abi.encodePacked(metaId_)), salt_, erc20Config_, IERC20MachineByMetadrop(machineTemplate), driPool ); // Process liquidity operations (if any): _processLiquidity( addLiquidityOnCreate, vaultFee_, deploymentFee_, newERC20, driPool ); // Emit details of the new ERC20: _emitOnCreation(metaId_, newERC20, driPool, tokenDetails, contructorArgs); // Return the address of the new contract up the call stack: return (newERC20); } /** * @dev function {_emitOnCreation} Emit the creation event * * @param metaId_ The string ID for this ERC-20 deployment * @param newERC20_ Address of the new ERC-20 * @param driPool_ Address of the pool (if any) * @param tokenDetails_ The name [0] and symbol [1] of this token */ function _emitOnCreation( string memory metaId_, address newERC20_, address driPool_, string[2] memory tokenDetails_, bytes memory constructorArgs_ ) internal { emit ERC20Created( metaId_, _msgSender(), newERC20_, driPool_, tokenDetails_[0], tokenDetails_[1], constructorArgs_ ); } /** * @dev function {_processDRIPool} Process DRIPool creation, if applicable. * * @param usesDRIPool_ Are we using a DRIPool? * @param poolParameters_ Pool parameters in bytes. * @param tokenDetails_ Name and symbol of the token. * @return driPoolAddress_ address of the launch pool */ function _processDRIPool( bool usesDRIPool_, bytes calldata poolParameters_, string[2] memory tokenDetails_ ) internal virtual returns (address driPoolAddress_) { if (usesDRIPool_) { // Create a minimal proxy DRIPool: driPoolAddress_ = driPoolTemplate.clone(); // Initialise it: IERC20DRIPoolByMetadrop(driPoolAddress_).initialiseDRIP( poolParameters_, tokenDetails_[0], tokenDetails_[1] ); } else { driPoolAddress_ = address(0); } return (driPoolAddress_); } /** * @dev function {_processLiquidity} Process liquidity, if relevant. * * @param addLiquidityOnCreate_ If we are adding liquidity now * @param vaultFee_ The fee for the token vault * @param deploymentFee_ The fee for deployment, if any * @param newERC20_ The address of the new ERC20 * @param driPool_ The address of the launch pool */ function _processLiquidity( bool addLiquidityOnCreate_, uint256 vaultFee_, uint256 deploymentFee_, address newERC20_, address driPool_ ) internal { if (addLiquidityOnCreate_ && (driPool_ == address(0))) { // Check the fee, we must have enough ETH for the fees, plus at least ONE wei if adding liquidity: if (msg.value < (vaultFee_ + deploymentFee_)) { _revert(IncorrectPayment.selector); } // Value to pass on (for storage is the vault fee plus liquidity, if any) is the sent // amount minus the deployment fee (if any) IERC20ByMetadrop(newERC20_).addInitialLiquidity{ value: msg.value - deploymentFee_ }(vaultFee_, 0, false); } else { // Check if we have a DRIPool for this token. If so, we need to initialise the // token address on the pool. We pass in the DRIPool address on the constructor // of the ERC-20, but as they BOTH need to know each others address we need to perform // this update. // We also fund any intial seed ETH from the project in this call. if (driPool_ != address(0)) { // We must have at least the deploymentFee. If we have more, this is ETH that the project // is seeding into the launch pool if (msg.value < deploymentFee_) { _revert(IncorrectPayment.selector); } uint256 ethSeedAmount = msg.value - deploymentFee_; address ethSeeContributor = address(0); if (ethSeedAmount > 0) { ethSeeContributor = _msgSender(); } IERC20DRIPoolByMetadrop(driPool_).loadERC20AddressAndSeedETH{ value: ethSeedAmount }(newERC20_, ethSeeContributor); } else { // Check the fee, we must have ETH for ONLY the deployment fee if (msg.value != deploymentFee_) { _revert(IncorrectPayment.selector); } } } // Forward the deployment fee to the recipient: _withdrawETH(deploymentFee_); } /** * @dev function {_deployERC20Contract} Deploy the ERC20 using CREATE2 * * @param metaIdHash_ Bytes32 hash of the metaId * @param salt_ Salt for create2 * @param erc20Config_ ERC20 configuration * @param machine_ The machine contract * @param driPool_ The address of the launch pool (if any) * @return erc20ContractAddress_ Address of the newly deployed ERC20 */ function _deployERC20Contract( bytes32 metaIdHash_, bytes32 salt_, ERC20Config calldata erc20Config_, IERC20MachineByMetadrop machine_, address driPool_ ) internal returns (address erc20ContractAddress_, bytes memory constructorArgs_) { address[5] memory integrationAddresses = [ _msgSender(), uniswapRouter, tokenVault, address(this), driPool_ ]; bytes memory args = abi.encode( integrationAddresses, erc20Config_.baseParameters, erc20Config_.supplyParameters, erc20Config_.taxParameters, erc20Config_.poolParameters ); return (machine_.deploy(metaIdHash_, salt_, args), args); } /** * @dev function {_decodeBaseData} Create an ERC-20 * * Decode the name, symbol and if we are adding liquidity on create * * @param encodedBaseParams_ Base ERC20 params * @return tokenDetails_ The name [0] and symbol [1] of this token * @return addLiquidityOnCreate_ bool to indicate we are adding liquidity on create * @return usesDRIPool_ bool to indicate we are using a launch pool */ function _decodeBaseData( bytes memory encodedBaseParams_ ) internal pure returns ( string[2] memory tokenDetails_, bool addLiquidityOnCreate_, bool usesDRIPool_ ) { string memory name; string memory symbol; (name, symbol, addLiquidityOnCreate_, usesDRIPool_) = abi.decode( encodedBaseParams_, (string, string, bool, bool) ); return ([name, symbol], addLiquidityOnCreate_, usesDRIPool_); } /** * @dev function {_verifyMessage} * * Check the signature and expiry of the passed message * * @param signedMessage_ The signed message object */ function _verifyMessage( SignedDropMessageDetails calldata signedMessage_ ) internal view { // Check that this signature is from the oracle signer: if ( !_validSignature( signedMessage_.messageHash, signedMessage_.messageSignature ) ) { _revert(InvalidOracleSignature.selector); } // Check that the signature has not expired: unchecked { if ( (signedMessage_.messageTimeStamp + messageValidityInSeconds) < block.timestamp ) { _revert(OracleSignatureHasExpired.selector); } } } /** * @dev function {_validSignature} * * Checks the the signature on the signed message is from the metadrop oracle * * @param messageHash_ The message hash signed by the trusted oracle signer. This will be the * keccack256 hash of received data about this token. * @param messageSignature_ The signed message from the backend oracle signer for validation. * @return messageIsValid_ If the message is valid (or not) */ function _validSignature( bytes32 messageHash_, bytes memory messageSignature_ ) internal view returns (bool messageIsValid_) { bytes32 ethSignedMessageHash = keccak256( abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash_) ); // Check the signature is valid: return ( SignatureChecker.isValidSignatureNow( metadropOracleAddress, ethSignedMessageHash, messageSignature_ ) ); } /** * @dev function {_checkConfigHashMatches} * * Check the passed config against the stored config hash * * @param metaId_ The drop Id being approved * @param salt_ Salt for create2 * @param erc20Config_ ERC20 configuration * @param signedMessage_ The signed message object * @param vaultFee_ The fee for the token vault * @param deploymentFee_ The fee for deployment, if any */ function _checkConfigHashMatches( string calldata metaId_, bytes32 salt_, ERC20Config calldata erc20Config_, SignedDropMessageDetails calldata signedMessage_, uint256 vaultFee_, uint256 deploymentFee_ ) internal view { // Create the hash of the passed data for comparison: bytes32 passedConfigHash = createConfigHash( metaId_, salt_, erc20Config_, signedMessage_.messageTimeStamp, vaultFee_, deploymentFee_, _msgSender() ); // Must equal the stored hash: if (passedConfigHash != signedMessage_.messageHash) { _revert(PassedConfigDoesNotMatchApproved.selector); } } /** * @dev function {createConfigHash} * * Create the config hash * * @param metaId_ The drop Id being approved * @param salt_ Salt for create2 * @param erc20Config_ ERC20 configuration * @param messageTimeStamp_ When the message for this config hash was signed * @param vaultFee_ The fee for the token vault * @param deploymentFee_ The fee for deployment, if any * @param deployer_ Address performing the deployment * @return configHash_ The bytes32 config hash */ function createConfigHash( string calldata metaId_, bytes32 salt_, ERC20Config calldata erc20Config_, uint256 messageTimeStamp_, uint256 vaultFee_, uint256 deploymentFee_, address deployer_ ) public pure returns (bytes32 configHash_) { configHash_ = keccak256( abi.encodePacked( metaId_, salt_, _createBytesParamsHash(erc20Config_), messageTimeStamp_, vaultFee_, deploymentFee_, deployer_ ) ); return (configHash_); } /** * @dev function {_createBytesParamsHash} * * Create a hash of the bytes params objects * * @param erc20Config_ ERC20 configuration * @return configHash_ The bytes32 config hash */ function _createBytesParamsHash( ERC20Config calldata erc20Config_ ) internal pure returns (bytes32 configHash_) { configHash_ = keccak256( abi.encodePacked( erc20Config_.baseParameters, erc20Config_.supplyParameters, erc20Config_.taxParameters, erc20Config_.poolParameters ) ); return (configHash_); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. * * _Available since v4.1._ */ interface IERC1271 { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/Clones.sol) pragma solidity ^0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt ) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ 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 amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` 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 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @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; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol) pragma solidity ^0.8.0; import "./ECDSA.sol"; import "../../interfaces/IERC1271.sol"; /** * @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA * signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like * Argent and Gnosis Safe. * * _Available since v4.1._ */ library SignatureChecker { /** * @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the * signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) { (address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature); return (error == ECDSA.RecoverError.NoError && recovered == signer) || isValidERC1271SignatureNow(signer, hash, signature); } /** * @dev Checks if a signature is valid for a given signer and data hash. The signature is validated * against the signer smart contract using ERC1271. * * NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus * change through time. It could return true at block N and false at block N+1 (or the opposite). */ function isValidERC1271SignatureNow( address signer, bytes32 hash, bytes memory signature ) internal view returns (bool) { (bool success, bytes memory result) = signer.staticcall( abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature) ); return (success && result.length >= 32 && abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
pragma solidity >=0.5.0; interface IUniswapV2Factory { event PairCreated(address indexed token0, address indexed token1, address pair, uint); function feeTo() external view returns (address); function feeToSetter() external view returns (address); function getPair(address tokenA, address tokenB) external view returns (address pair); function allPairs(uint) external view returns (address pair); function allPairsLength() external view returns (uint); function createPair(address tokenA, address tokenB) external returns (address pair); function setFeeTo(address) external; function setFeeToSetter(address) external; }
pragma solidity >=0.6.2; interface IUniswapV2Router01 { function factory() external pure returns (address); function WETH() external pure returns (address); function addLiquidity( address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB, uint liquidity); function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function removeLiquidity( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline ) external returns (uint amountA, uint amountB); function removeLiquidityETH( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountToken, uint amountETH); function removeLiquidityWithPermit( address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountA, uint amountB); function removeLiquidityETHWithPermit( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountToken, uint amountETH); function swapExactTokensForTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapTokensForExactTokens( uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline ) external returns (uint[] memory amounts); function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts); function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts); function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB); function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut); function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn); function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); }
pragma solidity >=0.6.2; import './IUniswapV2Router01.sol'; interface IUniswapV2Router02 is IUniswapV2Router01 { function removeLiquidityETHSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external returns (uint amountETH); function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s ) external returns (uint amountETH); function swapExactTokensForTokensSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; function swapExactETHForTokensSupportingFeeOnTransferTokens( uint amountOutMin, address[] calldata path, address to, uint deadline ) external payable; function swapExactTokensForETHSupportingFeeOnTransferTokens( uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline ) external; }
// SPDX-License-Identifier: BUSL-1.1 // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IERC20ByMetadrop} from "./IERC20ByMetadrop.sol"; import {IERC20DRIPoolByMetadrop} from "../ERC20Pools/IERC20DRIPoolByMetadrop.sol"; import {IERC20FactoryByMetadrop} from "../ERC20Factory/IERC20FactoryByMetadrop.sol"; import {IUniswapV2Factory} from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol"; import {IUniswapV2Router02} from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; import {IWETH} from "../../ThirdParty/WETH/IWETH.sol"; import {Ownable2Step} from "../../Global/OZ/Ownable2Step.sol"; import {Revert} from "../../Global/Revert.sol"; import {IERC20, SafeERC20} from "../../Global/OZ/SafeERC20.sol"; import {ERC20ByMetadropParamDecoder} from "../../lib/erc20/ERC20ByMetadropParamDecoder.sol"; import {ERC20ByMetadropSablierV2LinearLockup} from "../../lib/erc20/ERC20ByMetadropSablierV2LinearLockup.sol"; /** * @dev Metadrop core ERC-20 contract * * @dev Implementation of the {IERC20} interface. * */ contract ERC20ByMetadrop is Context, IERC20ByMetadrop, Ownable2Step { bytes32 public constant x_META_ID_HASH = 0x4D45544144524F504D45544144524F504D45544144524F504D45544144524F50; using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.Bytes32Set; using SafeERC20 for IERC20; uint256 public constant x_VERSION = 100020001000000000; uint256 internal constant BP_DENOM = 10000; uint256 internal constant ROUND_DEC = 100000000000; uint256 internal constant CALL_GAS_LIMIT = 50000; uint256 internal constant MAX_AUTOSWAP_THRESHOLD_MULTIPLE = 20; uint256 internal constant MIN_AUTOSWAP_THRESHOLD_BP = 1; uint256 internal constant MAX_AUTOSWAPS_PER_BLOCK = 1; uint256 internal constant MAX_BUYS_PER_ORIGIN_PER_BLOCK = 1; uint256 public immutable lpSupply; uint256 public immutable botProtectionDurationInSeconds; uint256 public immutable metadropTaxPeriodInDays; uint256 public immutable metadropBuyTaxProportionBasisPoints; uint256 public immutable metadropSellTaxProportionBasisPoints; uint256 public immutable metadropMinBuyTaxBasisPoints; uint256 public immutable metadropMinSellTaxBasisPoints; uint256 public immutable autoBurnDurationInBlocks; uint256 public immutable autoBurnBasisPoints; address public immutable metadropTaxRecipient; address public immutable uniswapV2Pair; address public immutable driPool; address public immutable lpOwner; address public immutable metadropFactory; bool internal immutable _tokenHasTax; address internal immutable _tokenVault; IUniswapV2Router02 internal immutable _uniswapRouter; /** @dev {Storage Slot 1} Vars read as part of transfers packed to a single * slot for warm reads. * Slot 1: * 120 * 32 * 32 * 16 * 4 * 8 * ------ * 256 * ------ */ uint120 private _totalSupply; uint32 public fundedDate; uint32 public fundedBlock; uint16 public projectBuyTaxBasisPoints; uint16 public projectSellTaxBasisPoints; uint16 public metadropBuyTaxBasisPoints; uint16 public metadropSellTaxBasisPoints; /** @dev {_autoSwapInProgress} We start with {_autoSwapInProgress} ON, as we don't want to * call autoswap when processing initial liquidity from this address. We turn this OFF when * liquidity has been loaded, and use this bool to control processing during auto-swaps * from that point onwards. */ bool private _autoSwapInProgress = true; /** @dev {Storage Slot 2} Vars read as part of transfers packed to a single * slot for warm reads. * Slot 1: * 120 * 112 * 16 * 8 * ------ * 256 * ------ */ uint120 public maxTokensPerWallet; uint112 public maxTokensPerTransaction; uint16 public swapThresholdBasisPoints; bool public initialBuyRelevantAndNotYetCompleted; /** @dev {Storage Slot 3} Not read / written in transfers (unless autoswap taking place): * 160 * 88 * 8 * ------ * 256 * ------ */ address public projectTaxRecipient; uint88 public lpLockupInDays; bool public burnLPTokens; /** @dev {Storage Slot 4} Potentially written in transfers: * Slot 3: * 128 * 128 * ------ * 256 * ------ */ uint128 public projectTaxPendingSwap; uint128 public metadropTaxPendingSwap; /** @dev {Storage Slot 5 to n} Not read as part of transfers etc. */ string private _name; string private _symbol; /** @dev {_balances} Addresses balances */ mapping(address => uint256) private _balances; /** @dev {_allowances} Addresses allocance details */ mapping(address => mapping(address => uint256)) private _allowances; /** @dev {_autoswapForBlock} Limit autoswaps per block */ mapping(uint256 => uint256) private _autoswapForBlock; /** @dev {_originBuysPerBlock} Number of buys from this txn.origin by block */ mapping(address => mapping(uint256 => uint256)) private _originBuysPerBlock; /** @dev {_liquidityPools} Enumerable set for liquidity pool addresses */ EnumerableSet.AddressSet private _liquidityPools; /** @dev {_unlimited} Enumerable set for addresses where limits do not apply */ EnumerableSet.AddressSet private _unlimited; /** * @dev {constructor} * * @param integrationAddresses_ The project owner, uniswap router, vault contract (currently Sablier), metadrop factory and pool template. * @param baseParams_ configuration of this ERC20. * @param supplyParams_ Supply configuration of this ERC20. * @param taxParams_ Tax configuration of this ERC20 * @param taxParams_ Launch pool configuration of this ERC20 */ constructor( address[5] memory integrationAddresses_, bytes memory baseParams_, bytes memory supplyParams_, bytes memory taxParams_, bytes memory poolParams_ ) { _transferOwnership(integrationAddresses_[0]); address[] memory supplyRecipients; uint256[] memory supplyAmounts; ( _name, _symbol, supplyRecipients, supplyAmounts ) = ERC20ByMetadropParamDecoder.decodeBaseParams(baseParams_); uint256 distributedSupply = _processDistribution( supplyRecipients, supplyAmounts ); _uniswapRouter = IUniswapV2Router02(integrationAddresses_[1]); _tokenVault = integrationAddresses_[2]; metadropFactory = (integrationAddresses_[3]); ERC20SupplyParameters memory supplyParams = abi.decode( supplyParams_, (ERC20SupplyParameters) ); ERC20TaxParameters memory taxParams = abi.decode( taxParams_, (ERC20TaxParameters) ); // If we have a DRI Pool record the address and also set the variable // initialBuyRelevantAndNotYetCompleted based on the DRI pool type. For // initial buy type DRI pools we do NOT tax or take autoburn from the very // first buy: if (integrationAddresses_[4] != address(0)) { driPool = integrationAddresses_[4]; if ( IERC20DRIPoolByMetadrop(integrationAddresses_[4]).driType() == DRIPoolType.initialBuy ) { initialBuyRelevantAndNotYetCompleted = true; } else { initialBuyRelevantAndNotYetCompleted = false; } } else { initialBuyRelevantAndNotYetCompleted = false; } ERC20PoolParameters memory poolParams; if (integrationAddresses_[4] != address(0)) { poolParams = abi.decode(poolParams_, (ERC20PoolParameters)); _unlimited.add(integrationAddresses_[4]); } _processSupplyParams(supplyParams, poolParams, distributedSupply); // projectSupplyRecipient = supplyParams.projectSupplyRecipient; lpSupply = supplyParams.lpSupply * (10 ** decimals()); // projectSupply = supplyParams.projectSupply * (10 ** decimals()); botProtectionDurationInSeconds = supplyParams .botProtectionDurationInSeconds; lpOwner = supplyParams.projectLPOwner; _tokenHasTax = _processTaxParams(taxParams); metadropTaxPeriodInDays = taxParams.metadropTaxPeriodInDays; metadropTaxRecipient = taxParams.metadropTaxRecipient; metadropBuyTaxProportionBasisPoints = taxParams .metadropBuyTaxProportionBasisPoints; metadropSellTaxProportionBasisPoints = taxParams .metadropSellTaxProportionBasisPoints; metadropMinBuyTaxBasisPoints = uint16( taxParams.metadropMinBuyTaxBasisPoints ); metadropMinSellTaxBasisPoints = uint16( taxParams.metadropMinSellTaxBasisPoints ); autoBurnDurationInBlocks = taxParams.autoBurnDurationInBlocks; autoBurnBasisPoints = taxParams.autoBurnBasisPoints; _mintBalances(lpSupply, poolParams.poolSupply * (10 ** decimals())); uniswapV2Pair = _createPair(); } /** * @dev {onlyOwnerFactoryOrPool} * * Throws if called by any account other than the owner, factory or pool. */ modifier onlyOwnerFactoryOrPool() { if ( metadropFactory != _msgSender() && owner() != _msgSender() && driPool != _msgSender() ) { _revert(CallerIsNotFactoryProjectOwnerOrPool.selector); } if (owner() == _msgSender() && driPool != address(0)) { _revert(CannotManuallyFundLPWhenUsingADRIPool.selector); } _; } /** * @dev {notDuringAutoswap} * * Throws if called during an autoswap */ modifier notDuringAutoswap() { if (_autoSwapInProgress) { _revert(CannotPerformDuringAutoswap.selector); } _; } /** * @dev function {_processDistribution} * * Mints and set unlimited to the initial mint recipients * * @param recipients_ The distribution addresses * @param amounts_ The distribution amounts * */ function _processDistribution( address[] memory recipients_, uint256[] memory amounts_ ) internal returns (uint256 distributedSupply) { if (recipients_.length != amounts_.length) { _revert(RecipientsAndAmountsMismatch.selector); } distributedSupply = 0; for (uint256 i = 0; i < recipients_.length; i++) { _unlimited.add(recipients_[i]); _mint(recipients_[i], amounts_[i] * (10 ** decimals())); distributedSupply += amounts_[i]; } return distributedSupply; } /** * @dev function {_processSupplyParams} * * Process provided supply params * * @param erc20SupplyParameters_ The supply params * @param erc20PoolParameters_ The pool params * @param distributedSupply_ The distributed supply */ function _processSupplyParams( ERC20SupplyParameters memory erc20SupplyParameters_, ERC20PoolParameters memory erc20PoolParameters_, uint256 distributedSupply_ ) internal { if ( erc20SupplyParameters_.maxSupply != (erc20SupplyParameters_.lpSupply + distributedSupply_ + erc20PoolParameters_.poolSupply) ) { _revert(SupplyTotalMismatch.selector); } if (erc20SupplyParameters_.maxSupply > type(uint120).max) { _revert(MaxSupplyTooHigh.selector); } if (erc20SupplyParameters_.lpLockupInDays > type(uint88).max) { _revert(LPLockUpMustFitUint88.selector); } if ( erc20SupplyParameters_.botProtectionDurationInSeconds > type(uint128).max ) { _revert(botProtectionDurationInSecondsMustFitUint128.selector); } if (erc20SupplyParameters_.maxTokensPerWallet > type(uint120).max) { _revert(LimitTooHigh.selector); } if (erc20SupplyParameters_.maxTokensPerTxn > type(uint120).max) { _revert(LimitTooHigh.selector); } maxTokensPerWallet = uint120( erc20SupplyParameters_.maxTokensPerWallet * (10 ** decimals()) ); maxTokensPerTransaction = uint112( erc20SupplyParameters_.maxTokensPerTxn * (10 ** decimals()) ); lpLockupInDays = uint88(erc20SupplyParameters_.lpLockupInDays); burnLPTokens = erc20SupplyParameters_.burnLPTokens; _unlimited.add(address(this)); _unlimited.add(address(0)); } /** * @dev function {_processTaxParams} * * Process provided tax params * * @param erc20TaxParameters_ The tax params */ function _processTaxParams( ERC20TaxParameters memory erc20TaxParameters_ ) internal returns (bool tokenHasTax_) { /** * @dev We use the immutable var {_tokenHasTax} to avoid unneccesary storage writes and reads. If this * token does NOT have tax applied then there is no need to store or read these parameters, and we can * avoid this simply by checking the immutable var. Pass back the value for this var from this method. */ if ( erc20TaxParameters_.projectBuyTaxBasisPoints == 0 && erc20TaxParameters_.projectSellTaxBasisPoints == 0 && erc20TaxParameters_.metadropBuyTaxBasisPoints == 0 && erc20TaxParameters_.metadropSellTaxBasisPoints == 0 ) { return false; } else { // Validate that the sum of all buy deductions does not equal or exceed // 10,000 basis points (i.e. 100%). if ( (erc20TaxParameters_.projectBuyTaxBasisPoints + erc20TaxParameters_.metadropBuyTaxBasisPoints + erc20TaxParameters_.autoBurnBasisPoints) >= BP_DENOM ) { _revert(DeductionsOnBuyExceedOrEqualOneHundredPercent.selector); } projectBuyTaxBasisPoints = uint16( erc20TaxParameters_.projectBuyTaxBasisPoints ); projectSellTaxBasisPoints = uint16( erc20TaxParameters_.projectSellTaxBasisPoints ); metadropBuyTaxBasisPoints = uint16( erc20TaxParameters_.metadropBuyTaxBasisPoints ); metadropSellTaxBasisPoints = uint16( erc20TaxParameters_.metadropSellTaxBasisPoints ); if ( erc20TaxParameters_.taxSwapThresholdBasisPoints < MIN_AUTOSWAP_THRESHOLD_BP ) { _revert(SwapThresholdTooLow.selector); } swapThresholdBasisPoints = uint16( erc20TaxParameters_.taxSwapThresholdBasisPoints ); projectTaxRecipient = erc20TaxParameters_.projectTaxRecipient; return true; } } /** * @dev function {_mintBalances} * * Mint initial balances * * @param lpMint_ The number of tokens for liquidity * @param poolMint_ The number of tokens for the launch pool */ function _mintBalances(uint256 lpMint_, uint256 poolMint_) internal { if (lpMint_ > 0) { _mint(address(this), lpMint_); } if (poolMint_ > 0) { _mint(driPool, poolMint_); } } /** * @dev function {_createPair} * * Create the uniswap pair * * @return uniswapV2Pair_ The pair address */ function _createPair() internal returns (address uniswapV2Pair_) { if (_totalSupply > 0) { uniswapV2Pair_ = IUniswapV2Factory(_uniswapRouter.factory()).createPair( address(this), _uniswapRouter.WETH() ); _liquidityPools.add(uniswapV2Pair_); emit LiquidityPoolCreated(uniswapV2Pair_); } _unlimited.add(address(_uniswapRouter)); _unlimited.add(uniswapV2Pair_); return (uniswapV2Pair_); } /** * @dev function {addInitialLiquidity} * * Add initial liquidity to the uniswap pair * * @param vaultFee_ The vault fee in wei. This must match the required fee from the external vault contract. * @param lpLockupInDaysOverride_ The number of days to lock liquidity NOTE you can pass 0 to use the stored value. * This value is an override, and will override a stored value which is LOWER that it. If the value you are passing is * LOWER than the stored value the stored value will not be reduced. * * Example usage 1: When creating the coin the lpLockupInDays is set to 0. This means that on this call the * user can set the lockup to any value they like, as all integer values greater than zero will be used to override * that set in storage. * * Example usage 2: When using a DRI Pool the lockup period is set on this contract and the pool need not know anything * about this setting. The pool can pass back a 0 on this call and know that the existing value stored on this contract * will be used. * @param burnLPTokensOverride_ If the LP tokens should be burned (otherwise they are locked). This is an override field * that can ONLY be used to override a held value of FALSE with a new value of TRUE. * * Example usage 1: When creating the coin the user didn't add liquidity, or specify that the LP tokens were to be burned. * So burnLPTokens is held as FALSE. When they add liquidity they want to lock tokens, so they pass this in as FALSE again, * and it remains FALSE. * * Example usage 2: As above, but when later adding liquidity the user wants to burn the LP. So the stored value is FALSE * and the user passes TRUE into this method. The TRUE overrides the held value of FALSE and the tokens are burned. * * Example uusage 3: The user is using a DRI pool and they have specified on the coin creation that the LP tokens are to * be burned. This contract therefore holds TRUE for burnLPTokens. The DRI pool does not need to know what the user has * selected. It can safely pass back FALSE to this method call and the stored value of TRUE will remain, resulting in the * LP tokens being burned. */ function addInitialLiquidity( uint256 vaultFee_, uint256 lpLockupInDaysOverride_, bool burnLPTokensOverride_ ) external payable onlyOwnerFactoryOrPool { uint256 ethForLiquidity; if ((burnLPTokens == false) && (burnLPTokensOverride_ == true)) { burnLPTokens = true; } if (burnLPTokens) { if (msg.value == 0) { _revert(NoETHForLiquidityPair.selector); } ethForLiquidity = msg.value; } else { if (vaultFee_ >= msg.value) { // The amount of ETH MUST exceed the vault fee, otherwise what liquidity are we adding? _revert(NoETHForLiquidityPair.selector); } ethForLiquidity = msg.value - vaultFee_; } if (lpLockupInDaysOverride_ > lpLockupInDays) { lpLockupInDays = uint88(lpLockupInDaysOverride_); } _addInitialLiquidity(ethForLiquidity, vaultFee_); } /** * @dev function {_addInitialLiquidity} * * Add initial liquidity to the uniswap pair (internal function that does processing) * * @param ethAmount_ The amount of ETH passed into the call * @param vaultFee_ The vault fee in wei. This must match the required fee from the external vault contract. */ function _addInitialLiquidity( uint256 ethAmount_, uint256 vaultFee_ ) internal { // Funded date is the date of first funding. We can only add initial liquidity once. If this date is set, // we cannot proceed if (fundedDate != 0) { _revert(InitialLiquidityAlreadyAdded.selector); } fundedDate = uint32(block.timestamp); fundedBlock = uint32(block.number); // Can only do this if this contract holds tokens: if (balanceOf(address(this)) == 0) { _revert(NoTokenForLiquidityPair.selector); } // Approve the uniswap router for an inifinite amount (max uint256) // This means that we don't need to worry about later incrememtal // approvals on tax swaps, as the uniswap router allowance will never // be decreased (see code in decreaseAllowance for reference) _approve(address(this), address(_uniswapRouter), type(uint256).max); // Add the liquidity: (uint256 amountA, uint256 amountB, uint256 lpTokens) = _uniswapRouter .addLiquidityETH{value: ethAmount_}( address(this), balanceOf(address(this)), 0, 0, address(this), block.timestamp ); emit InitialLiquidityAdded(amountA, amountB, lpTokens); // We now set this to false so that future transactions can be eligibile for autoswaps _autoSwapInProgress = false; // Are we locking, or burning? if (burnLPTokens) { _burnLiquidity(lpTokens); } else { // The LP token amount to lock should be equal to the LP token balance in the contract. If it's not, something is wrong. if (lpTokens != IERC20(uniswapV2Pair).balanceOf(address(this))) { _revert(LPTokensBalanceMismatch.selector); } // Lock the liquidity: _addLiquidityToVault(vaultFee_, lpTokens); } } /** * @dev function {_addLiquidityToVault} * * Lock initial liquidity on vault contract * * @param vaultFee_ The vault fee in wei. This must match the required fee from the external vault contract. * @param lpTokens_ The amount of LP tokens to be locked */ function _addLiquidityToVault(uint256 vaultFee_, uint256 lpTokens_) internal { // Approve the vault contract to spend the LP tokens. IERC20(uniswapV2Pair).approve(_tokenVault, lpTokens_); uint256 streamId = ERC20ByMetadropSablierV2LinearLockup.createWithDurations( lpOwner, uniswapV2Pair, _tokenVault, lpTokens_, uint40(lpLockupInDays * 1 days) ); emit LiquidityLocked(lpTokens_, lpLockupInDays, streamId); } /** * @dev function {_burnLiquidity} * * Burn LP tokens * * @param lpTokens_ The amount of LP tokens to be locked */ function _burnLiquidity(uint256 lpTokens_) internal { IERC20(uniswapV2Pair).transfer(address(0), lpTokens_); emit LiquidityBurned(lpTokens_); } /** * @dev function {isLiquidityPool} * * Return if an address is a liquidity pool * * @param queryAddress_ The address being queried * @return bool The address is / isn't a liquidity pool */ function isLiquidityPool(address queryAddress_) public view returns (bool) { /** @dev We check the uniswapV2Pair address first as this is an immutable variable and therefore does not need * to be fetched from storage, saving gas if this address IS the uniswapV2Pool. We also add this address * to the enumerated set for ease of reference (for example it is returned in the getter), and it does * not add gas to any other calls, that still complete in 0(1) time. */ return (queryAddress_ == uniswapV2Pair || _liquidityPools.contains(queryAddress_)); } /** * @dev function {liquidityPools} * * Returns a list of all liquidity pools * * @return liquidityPools_ a list of all liquidity pools */ function liquidityPools() external view returns (address[] memory liquidityPools_) { return (_liquidityPools.values()); } /** * @dev function {addLiquidityPool} onlyOwner * * Allows the manager to add a liquidity pool to the pool enumerable set * * @param newLiquidityPool_ The address of the new liquidity pool */ function addLiquidityPool(address newLiquidityPool_) public onlyOwner { // Don't allow calls that didn't pass an address: if (newLiquidityPool_ == address(0)) { _revert(LiquidityPoolCannotBeAddressZero.selector); } // Only allow smart contract addresses to be added, as only these can be pools: if (newLiquidityPool_.code.length == 0) { _revert(LiquidityPoolMustBeAContractAddress.selector); } // Add this to the enumerated list: _liquidityPools.add(newLiquidityPool_); emit LiquidityPoolAdded(newLiquidityPool_); } /** * @dev function {removeLiquidityPool} onlyOwner * * Allows the manager to remove a liquidity pool * * @param removedLiquidityPool_ The address of the old removed liquidity pool */ function removeLiquidityPool( address removedLiquidityPool_ ) external onlyOwner { // Remove this from the enumerated list: _liquidityPools.remove(removedLiquidityPool_); emit LiquidityPoolRemoved(removedLiquidityPool_); } /** * @dev function {isUnlimited} * * Return if an address is unlimited (is not subject to per txn and per wallet limits) * * @param queryAddress_ The address being queried * @return bool The address is / isn't unlimited */ function isUnlimited(address queryAddress_) public view returns (bool) { return (_unlimited.contains(queryAddress_)); } /** * @dev function {unlimitedAddresses} * * Returns a list of all unlimited addresses * * @return unlimitedAddresses_ a list of all unlimited addresses */ function unlimitedAddresses() external view returns (address[] memory unlimitedAddresses_) { return (_unlimited.values()); } /** * @dev function {addUnlimited} onlyOwner * * Allows the manager to add an unlimited address * * @param newUnlimited_ The address of the new unlimited address */ function addUnlimited(address newUnlimited_) external onlyOwner { // Add this to the enumerated list: _unlimited.add(newUnlimited_); emit UnlimitedAddressAdded(newUnlimited_); } /** * @dev function {removeUnlimited} onlyOwner * * Allows the manager to remove an unlimited address * * @param removedUnlimited_ The address of the old removed unlimited address */ function removeUnlimited(address removedUnlimited_) external onlyOwner { // Remove this from the enumerated list: _unlimited.remove(removedUnlimited_); emit UnlimitedAddressRemoved(removedUnlimited_); } /** * @dev function {setProjectTaxRecipient} onlyOwner * * Allows the manager to set the project tax recipient address * * @param projectTaxRecipient_ New recipient address */ function setProjectTaxRecipient( address projectTaxRecipient_ ) external onlyOwner { projectTaxRecipient = projectTaxRecipient_; emit ProjectTaxRecipientUpdated(projectTaxRecipient_); } /** * @dev function {setSwapThresholdBasisPoints} onlyOwner * * Allows the manager to set the autoswap threshold * * @param swapThresholdBasisPoints_ New swap threshold in basis points */ function setSwapThresholdBasisPoints( uint16 swapThresholdBasisPoints_ ) external onlyOwner { if (swapThresholdBasisPoints < MIN_AUTOSWAP_THRESHOLD_BP) { _revert(SwapThresholdTooLow.selector); } uint256 oldswapThresholdBasisPoints = swapThresholdBasisPoints; swapThresholdBasisPoints = swapThresholdBasisPoints_; emit AutoSwapThresholdUpdated( oldswapThresholdBasisPoints, swapThresholdBasisPoints_ ); } /** * @dev function {setProjectTaxRates} onlyOwner * * Change the tax rates, subject to only ever decreasing * * @param newProjectBuyTaxBasisPoints_ The new buy tax rate * @param newProjectSellTaxBasisPoints_ The new sell tax rate */ function setProjectTaxRates( uint16 newProjectBuyTaxBasisPoints_, uint16 newProjectSellTaxBasisPoints_ ) external onlyOwner { uint16 oldBuyTaxBasisPoints = projectBuyTaxBasisPoints; uint16 oldSellTaxBasisPoints = projectSellTaxBasisPoints; // Cannot increase, down only if (newProjectBuyTaxBasisPoints_ > oldBuyTaxBasisPoints) { _revert(CanOnlyReduce.selector); } // Cannot increase, down only if (newProjectSellTaxBasisPoints_ > oldSellTaxBasisPoints) { _revert(CanOnlyReduce.selector); } projectBuyTaxBasisPoints = newProjectBuyTaxBasisPoints_; projectSellTaxBasisPoints = newProjectSellTaxBasisPoints_; // We set the metadrop tax rates off of the project tax rates: // // 1) If the project tax rate is zero then the metadrop tax rate is zero // 2) If the project tax rate is not zero the metadrop tax rate is the // greater of: // a) The metadrop tax proportion basis points of the project rate // b) the base metadrop tax rate. // // Examples: // // A) The project buy tax rate is zero and the sell tax rate is 3%. The metadrop // tax proportion basis points is 1000, meaning the metadrop proportion is 10% of the // project tax rate. The base metadrop tax rate is 50 basis points i.e. 0.5%. // // * Metadrop buy tax = 0% (as the project buy tax is zero) // * Metadrop sell tax = 0.5%. 10% of the project sell tax is 0.3%. As this is below // the base level of 0.5% we set the metadrop tax to 0.5% // // B) The project buy tax rate is 4% and the sell tax rate is 20%. The metadrop tax // proportion basis points is 1000, meaning the metadrop proportion is 10% of the // project tax rate. The base metadrop tax rate is 50 basis points i.e. 0.5%. // // * Metadrop buy tax = 0.5%. 10% of the project rate would be 0.4%, so we use the base rate) // * Metadrop sell tax = 2%. 10% of the project rate is 2%, which is higher than the // base rate of 0.5%. uint16 oldMetadropBuyTaxBasisPoints = metadropBuyTaxBasisPoints; uint16 oldMetadropSellTaxBasisPoints = metadropSellTaxBasisPoints; // Process the buy tax rate first: if (newProjectBuyTaxBasisPoints_ == 0) { metadropBuyTaxBasisPoints = 0; } else { uint256 derivedMetadropBuyTaxRate = (newProjectBuyTaxBasisPoints_ * metadropBuyTaxProportionBasisPoints) / BP_DENOM; if (derivedMetadropBuyTaxRate < metadropMinBuyTaxBasisPoints) { metadropBuyTaxBasisPoints = uint16(metadropMinBuyTaxBasisPoints); } else { metadropBuyTaxBasisPoints = uint16(derivedMetadropBuyTaxRate); } } // And now the sell tax rate: if (newProjectSellTaxBasisPoints_ == 0) { metadropSellTaxBasisPoints = 0; } else { uint256 derivedMetadropSellTaxRate = (newProjectSellTaxBasisPoints_ * metadropSellTaxProportionBasisPoints) / BP_DENOM; if (derivedMetadropSellTaxRate < metadropMinSellTaxBasisPoints) { metadropSellTaxBasisPoints = uint16(metadropMinSellTaxBasisPoints); } else { metadropSellTaxBasisPoints = uint16(derivedMetadropSellTaxRate); } } // Emit a message if there has been a change: if ( oldMetadropBuyTaxBasisPoints != metadropBuyTaxBasisPoints || oldMetadropSellTaxBasisPoints != metadropSellTaxBasisPoints ) { emit MetadropTaxBasisPointsChanged( oldMetadropBuyTaxBasisPoints, metadropBuyTaxBasisPoints, oldMetadropSellTaxBasisPoints, metadropSellTaxBasisPoints ); } emit ProjectTaxBasisPointsChanged( oldBuyTaxBasisPoints, newProjectBuyTaxBasisPoints_, oldSellTaxBasisPoints, newProjectSellTaxBasisPoints_ ); } /** * @dev function {setLimits} onlyOwner * * Change the limits on transactions and holdings * * @param newMaxTokensPerTransaction_ The new per txn limit * @param newMaxTokensPerWallet_ The new tokens per wallet limit */ function setLimits( uint256 newMaxTokensPerTransaction_, uint256 newMaxTokensPerWallet_ ) external onlyOwner { if (newMaxTokensPerWallet_ > type(uint120).max) { _revert(LimitTooHigh.selector); } if (newMaxTokensPerTransaction_ > type(uint120).max) { _revert(LimitTooHigh.selector); } uint256 oldMaxTokensPerTransaction = maxTokensPerTransaction; uint256 oldMaxTokensPerWallet = maxTokensPerWallet; // Limit can only be increased: if ( (oldMaxTokensPerTransaction == 0 && newMaxTokensPerTransaction_ != 0) || (oldMaxTokensPerWallet == 0 && newMaxTokensPerWallet_ != 0) ) { _revert(LimitsCanOnlyBeRaised.selector); } if ( ((newMaxTokensPerTransaction_ != 0) && newMaxTokensPerTransaction_ < oldMaxTokensPerTransaction) || ((newMaxTokensPerWallet_ != 0) && newMaxTokensPerWallet_ < oldMaxTokensPerWallet) ) { _revert(LimitsCanOnlyBeRaised.selector); } maxTokensPerTransaction = uint112(newMaxTokensPerTransaction_); maxTokensPerWallet = uint120(newMaxTokensPerWallet_); emit LimitsUpdated( oldMaxTokensPerTransaction, newMaxTokensPerTransaction_, oldMaxTokensPerWallet, newMaxTokensPerWallet_ ); } /** * @dev function {limitsEnforced} * * Return if limits are enforced on this contract * * @return bool : they are / aren't */ function limitsEnforced() public view returns (bool) { // Limits are not enforced if // this is renounced AND after the protection end date // OR prior to LP funding: // The second clause of 'fundedDate == 0' isn't strictly needed, since with a funded // date of 0 we would always expect the block.timestamp to be less than 0 plus // the botProtectionDurationInSeconds. But, to cover the miniscule chance of a user // selecting a truly enormous bot protection period, such that when added to 0 it // is more than the current block.timestamp, we have included this second clause. There // is no permanent gas overhead (the logic will be returning from the first clause after // the bot protection period has expired). During the bot protection period there is a minor // gas overhead from evaluating the fundedDate == 0 (which will be false), but this is minimal. if ( (owner() == address(0) && block.timestamp > fundedDate + botProtectionDurationInSeconds) || fundedDate == 0 ) { return false; } else { // LP has been funded AND we are within the protection period: return true; } } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev getMetadropBuyTaxBasisPoints * * Return the metadrop buy tax basis points given the timed expiry */ function getMetadropBuyTaxBasisPoints() public view returns (uint256) { // If we are outside the metadrop tax period this is ZERO if (block.timestamp > (fundedDate + (metadropTaxPeriodInDays * 1 days))) { return 0; } else { return metadropBuyTaxBasisPoints; } } /** * @dev getMetadropSellTaxBasisPoints * * Return the metadrop sell tax basis points given the timed expiry */ function getMetadropSellTaxBasisPoints() public view returns (uint256) { // If we are outside the metadrop tax period this is ZERO if (block.timestamp > (fundedDate + (metadropTaxPeriodInDays * 1 days))) { return 0; } else { return metadropSellTaxBasisPoints; } } /** * @dev totalBuyTaxBasisPoints * * Provide easy to view tax total: */ function totalBuyTaxBasisPoints() public view returns (uint256) { return projectBuyTaxBasisPoints + getMetadropBuyTaxBasisPoints(); } /** * @dev totalSellTaxBasisPoints * * Provide easy to view tax total: */ function totalSellTaxBasisPoints() public view returns (uint256) { return projectSellTaxBasisPoints + getMetadropSellTaxBasisPoints(); } /** * @dev See {IERC20-balanceOf}. */ function balanceOf( address account ) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer( address to, uint256 amount ) public virtual override(IERC20) returns (bool) { address owner = _msgSender(); _transfer( owner, to, amount, (isLiquidityPool(owner) || isLiquidityPool(to)) ); return true; } /** * @dev See {IERC20-allowance}. */ function allowance( address owner, address spender ) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve( address spender, uint256 amount ) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount, (isLiquidityPool(from) || isLiquidityPool(to))); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance( address spender, uint256 addedValue ) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance( address spender, uint256 subtractedValue ) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); if (currentAllowance < subtractedValue) { _revert(AllowanceDecreasedBelowZero.selector); } unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount, bool applyTax ) internal virtual { _beforeTokenTransfer(from, to, amount); // Perform pre-tax validation (e.g. amount doesn't exceed balance, max txn amount) uint256 fromBalance = _pretaxValidationAndLimits(from, to, amount); // Perform autoswap if eligible _autoSwap(from, to); uint256 amountMinusDeductions; // We limit the number of buys per tx.origin per block: if (isLiquidityPool(from)) { if (_blockMaxBuysPerOriginExceeded()) { _revert(MaxBuysPerBlockExceeded.selector); } _originBuysPerBlock[tx.origin][block.number] += 1; } // The first by from a liquidity pool is relevant for tokens that use an initial buy // type DRI pool, as the very first buy does not have tax or autoburn applied. In all // cases where this is NOT the initial buy, or where the initial buy is not relevant, // the processing is the same: if (_intialBuyTreatmentApplies(from)) { initialBuyRelevantAndNotYetCompleted = false; amountMinusDeductions = amount; } else { // Process taxes amountMinusDeductions = _taxProcessing(applyTax, to, from, amount); // Process autoburn amountMinusDeductions = _autoburnProcessing( from, amount, amountMinusDeductions ); } // Perform post-tax validation (e.g. total balance after post-tax amount applied) _posttaxValidationAndLimits(from, to, amountMinusDeductions); _balances[from] = fromBalance - amount; _balances[to] += amountMinusDeductions; emit Transfer(from, to, amountMinusDeductions); _afterTokenTransfer(from, to, amount); } /** * @dev function {_blockMaxBuysPerOriginExceeded} * * Has the max buy per origin per block been exceeded * * @return bool this does / doesn't exceed the maximum */ function _blockMaxBuysPerOriginExceeded() internal view returns (bool) { return _originBuysPerBlock[tx.origin][block.number] >= MAX_BUYS_PER_ORIGIN_PER_BLOCK; } /** * @dev function {_intialBuyTreatmentApplies} * * Does a tax free initial buy apply to this contract? * * @param from_ the from address on the transfer * @return bool tax free treatment does / doesn't apply */ function _intialBuyTreatmentApplies( address from_ ) internal view returns (bool) { return initialBuyRelevantAndNotYetCompleted && isLiquidityPool(from_); } /** * @dev function {_pretaxValidationAndLimits} * * Perform validation on pre-tax amounts * * @param from_ From address for the transaction * @param to_ To address for the transaction * @param amount_ Amount of the transaction */ function _pretaxValidationAndLimits( address from_, address to_, uint256 amount_ ) internal view returns (uint256 fromBalance_) { // This can't be a transfer to the liquidity pool before the funding date // UNLESS the from address is this contract. This ensures that the initial // LP funding transaction is from this contract using the supply of tokens // designated for the LP pool, and therefore the initial price in the pool // is being set as expected. // // This protects from, for example, tokens from a team minted supply being // paired with ETH and added to the pool, setting the initial price, BEFORE // the initial liquidity is added through this contract. if (to_ == uniswapV2Pair && from_ != address(this) && fundedDate == 0) { _revert(InitialLiquidityNotYetAdded.selector); } if (from_ == address(0)) { _revert(TransferFromZeroAddress.selector); } if (to_ == address(0)) { _revert(TransferToZeroAddress.selector); } fromBalance_ = _balances[from_]; if (fromBalance_ < amount_) { _revert(TransferAmountExceedsBalance.selector); } if ( limitsEnforced() && (maxTokensPerTransaction != 0) && ((isLiquidityPool(from_) && !isUnlimited(to_)) || (isLiquidityPool(to_) && !isUnlimited(from_))) ) { // Liquidity pools aren't always going to round cleanly. This can (and does) // mean that a limit of 5,000 tokens (for example) will trigger on a transfer // of 5,000 tokens, as the transfer is actually for 5,000.00000000000000213. // While 4,999 will work fine, it isn't hugely user friendly. So we buffer // the limit with rounding decimals, which in all cases are considerably less // than one whole token: uint256 roundedLimited; unchecked { roundedLimited = maxTokensPerTransaction + ROUND_DEC; } if (amount_ > roundedLimited) { _revert(MaxTokensPerTxnExceeded.selector); } } return (fromBalance_); } /** * @dev function {_posttaxValidationAndLimits} * * Perform validation on post-tax amounts * * @param to_ To address for the transaction * @param amount_ Amount of the transaction */ function _posttaxValidationAndLimits( address from_, address to_, uint256 amount_ ) internal view { if ( limitsEnforced() && (maxTokensPerWallet != 0) && !isUnlimited(to_) && // If this is a buy (from a liquidity pool), we apply if the to_ // address isn't noted as unlimited: (isLiquidityPool(from_) && !isUnlimited(to_)) ) { // Liquidity pools aren't always going to round cleanly. This can (and does) // mean that a limit of 5,000 tokens (for example) will trigger on a max holding // of 5,000 tokens, as the transfer to achieve that is actually for // 5,000.00000000000000213. While 4,999 will work fine, it isn't hugely user friendly. // So we buffer the limit with rounding decimals, which in all cases are considerably // less than one whole token: uint256 roundedLimited; unchecked { roundedLimited = maxTokensPerWallet + ROUND_DEC; } if ((amount_ + balanceOf(to_) > roundedLimited)) { _revert(MaxTokensPerWalletExceeded.selector); } } } /** * @dev function {_taxProcessing} * * Perform tax processing * * @param applyTax_ Do we apply tax to this transaction? * @param to_ The reciever of the token * @param from_ The sender of the token * @param sentAmount_ The amount being send * @return amountLessTax_ The amount that will be recieved, i.e. the send amount minus tax */ function _taxProcessing( bool applyTax_, address to_, address from_, uint256 sentAmount_ ) internal returns (uint256 amountLessTax_) { amountLessTax_ = sentAmount_; if (_tokenHasTax && applyTax_ && !_autoSwapInProgress) { uint256 tax; // on sell if (isLiquidityPool(to_) && totalSellTaxBasisPoints() > 0) { if (projectSellTaxBasisPoints > 0) { uint256 projectTax = ((sentAmount_ * projectSellTaxBasisPoints) / BP_DENOM); projectTaxPendingSwap += uint128(projectTax); tax += projectTax; } uint256 metadropSellTax = getMetadropSellTaxBasisPoints(); if (metadropSellTax > 0) { uint256 metadropTax = ((sentAmount_ * metadropSellTax) / BP_DENOM); metadropTaxPendingSwap += uint128(metadropTax); tax += metadropTax; } } // on buy else if (isLiquidityPool(from_) && totalBuyTaxBasisPoints() > 0) { if (projectBuyTaxBasisPoints > 0) { uint256 projectTax = ((sentAmount_ * projectBuyTaxBasisPoints) / BP_DENOM); projectTaxPendingSwap += uint128(projectTax); tax += projectTax; } uint256 metadropBuyTax = getMetadropBuyTaxBasisPoints(); if (metadropBuyTax > 0) { uint256 metadropTax = ((sentAmount_ * metadropBuyTax) / BP_DENOM); metadropTaxPendingSwap += uint128(metadropTax); tax += metadropTax; } } if (tax > 0) { _balances[address(this)] += tax; emit Transfer(from_, address(this), tax); amountLessTax_ -= tax; } } return (amountLessTax_); } /** * @dev function {_autoburnProcessing} * * Perform autoburn processing * * @param from_ The sender of the token * @param originalSentAmount_ The original amount being sent, before any deductions (if appropriate) * @param currentRecipientAmount_ The amount the recipient is currently due to receive * @return amountLessBurn_ The amount that will be recieved, i.e. the currentRecipientAmount_ * minus the burn applied here */ function _autoburnProcessing( address from_, uint256 originalSentAmount_, uint256 currentRecipientAmount_ ) internal returns (uint256 amountLessBurn_) { amountLessBurn_ = currentRecipientAmount_; // Perform autoBurn processing, if appropriate: if ( autoBurnDurationInBlocks != 0 && autoBurnBasisPoints != 0 && !_autoSwapInProgress && isLiquidityPool(from_) ) { uint256 blocksElapsed = block.number - fundedBlock; if (blocksElapsed < autoBurnDurationInBlocks) { // Get the blocks remaining in the autoburn period. The more blocks // remaining, the higher the proportion of the burn we apply: uint256 burnBlocksRemaining = autoBurnDurationInBlocks - blocksElapsed; // Calculate the linear burn basis point per remaining block. For example, if our // burn basis points = 1500 (15%) and we are burning for three blocks then this // will be 1500 / 3 = 500 (5%): uint256 linearBurnPerRemainingBlock = autoBurnBasisPoints / autoBurnDurationInBlocks; // Finally, determine the burn basis points for this block by multiplying the per remaining // block burn % by the number of blocks remaining. To follow our example, in the 0th // block since funding there are three blocks remaining in the burn period, therefore // 500 * 3 = 1500 (15%). Two blocks after funding we have one block remaining in the burn // period, and therefore are burning 500 * 1 = 500 (5%). Three blocks after funding we do not // reach this point in the logic, as the blocksElapsed is 3 and needs to be UNDER 3 to enter // this code. uint256 burnBasisPointsForThisBlock = burnBlocksRemaining * linearBurnPerRemainingBlock; // This is eligible for burn. Send the basis points amount of // the originalSentAmount_ to the zero address: uint256 burnAmount = ((originalSentAmount_ * burnBasisPointsForThisBlock) / BP_DENOM); _burn(from_, burnAmount); amountLessBurn_ -= burnAmount; } } return (amountLessBurn_); } /** * @dev totalTaxPendingSwap * * Return the total tax awaiting swap: */ function totalTaxPendingSwap() public view returns (uint256) { return projectTaxPendingSwap + metadropTaxPendingSwap; } /** * @dev function {_autoSwap} * * Automate the swap of accumulated tax fees to native token * * @param from_ The sender of the token * @param to_ The recipient of the token */ function _autoSwap(address from_, address to_) internal { if (_tokenHasTax) { uint256 totalTaxBalance = totalTaxPendingSwap(); uint256 swapBalance = totalTaxBalance; uint256 swapThresholdInTokens = (_totalSupply * swapThresholdBasisPoints) / BP_DENOM; if (_eligibleForSwap(from_, to_, swapBalance, swapThresholdInTokens)) { // Store that a swap back is in progress: _autoSwapInProgress = true; // Increment the swaps per block counter: _autoswapForBlock[block.number] += 1; // Check if we need to reduce the amount of tokens for this swap: if ( swapBalance > swapThresholdInTokens * MAX_AUTOSWAP_THRESHOLD_MULTIPLE ) { swapBalance = swapThresholdInTokens * MAX_AUTOSWAP_THRESHOLD_MULTIPLE; } // Perform the auto swap to native token: _swapTaxForNative(swapBalance, totalTaxBalance); // Flag that the autoswap is complete: _autoSwapInProgress = false; } } } /** * @dev function {_eligibleForSwap} * * Is the current transfer eligible for autoswap * * @param from_ The sender of the token * @param to_ The recipient of the token * @param taxBalance_ The current accumulated tax balance * @param swapThresholdInTokens_ The swap threshold as a token amount */ function _eligibleForSwap( address from_, address to_, uint256 taxBalance_, uint256 swapThresholdInTokens_ ) internal view returns (bool) { return (taxBalance_ >= swapThresholdInTokens_ && !_autoSwapInProgress && !isLiquidityPool(from_) && from_ != address(_uniswapRouter) && to_ != address(_uniswapRouter) && from_ != address(driPool) && _autoswapForBlock[block.number] < MAX_AUTOSWAPS_PER_BLOCK); } /** * @dev function {_swapTaxForNative} * * Swap tokens taken as tax for native token * * @param swapBalance_ The current accumulated tax balance to swap * @param totalTaxBalance_ The current accumulated total tax balance */ function _swapTaxForNative( uint256 swapBalance_, uint256 totalTaxBalance_ ) internal { uint256 preSwapBalance = address(this).balance; address[] memory path = new address[](2); path[0] = address(this); path[1] = _uniswapRouter.WETH(); // Wrap external calls in try / catch to handle errors try _uniswapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens( swapBalance_, 0, path, address(this), block.timestamp + 600 ) { uint256 postSwapBalance = address(this).balance; uint256 balanceToDistribute = postSwapBalance - preSwapBalance; uint256 projectBalanceToDistribute = (balanceToDistribute * projectTaxPendingSwap) / totalTaxBalance_; uint256 metadropBalanceToDistribute = (balanceToDistribute * metadropTaxPendingSwap) / totalTaxBalance_; // We will not have swapped all tax tokens IF the amount was greater than the max auto swap. // We therefore cannot just set the pending swap counters to 0. Instead, in this scenario, // we must reduce them in proportion to the swap amount vs the remaining balance + swap // amount. // // For example: // * swap Balance is 250 // * contract balance is 385. // * projectTaxPendingSwap is 300 // * metadropTaxPendingSwap is 85. // // The new total for the projectTaxPendingSwap is: // = 300 - ((300 * 250) / 385) // = 300 - 194 // = 106 // The new total for the metadropTaxPendingSwap is: // = 85 - ((85 * 250) / 385) // = 85 - 55 // = 30 // if (swapBalance_ < totalTaxBalance_) { // Calculate the project tax spending swap reduction amount: uint256 projectTaxPendingSwapReduction = (projectTaxPendingSwap * swapBalance_) / totalTaxBalance_; projectTaxPendingSwap -= uint128(projectTaxPendingSwapReduction); // The metadrop tax pending swap reduction is therefore the total swap amount minus the // project tax spending swap reduction: metadropTaxPendingSwap -= uint128( swapBalance_ - projectTaxPendingSwapReduction ); } else { (projectTaxPendingSwap, metadropTaxPendingSwap) = (0, 0); } // Distribute to treasuries: bool success; address weth; uint256 gas; if (projectBalanceToDistribute > 0) { // If no gas limit was provided or provided gas limit greater than gas left, just use the remaining gas. gas = (CALL_GAS_LIMIT == 0 || CALL_GAS_LIMIT > gasleft()) ? gasleft() : CALL_GAS_LIMIT; // We limit the gas passed so that a called address cannot cause a block out of gas error: (success, ) = projectTaxRecipient.call{ value: projectBalanceToDistribute, gas: gas }(""); // If the ETH transfer fails, wrap the ETH and send it as WETH. We do this so that a called // address cannot cause this transfer to fail, either intentionally or by mistake: if (!success) { if (weth == address(0)) { weth = _uniswapRouter.WETH(); } try IWETH(weth).deposit{value: projectBalanceToDistribute}() { try IERC20(address(weth)).transfer( projectTaxRecipient, projectBalanceToDistribute ) {} catch { // Dont allow a failed external call (in this case to WETH) to stop a transfer. // Emit that this has occured and continue. emit ExternalCallError(1); } } catch { // Dont allow a failed external call (in this case to WETH) to stop a transfer. // Emit that this has occured and continue. emit ExternalCallError(2); } } } if (metadropBalanceToDistribute > 0) { // If no gas limit was provided or provided gas limit greater than gas left, just use the remaining gas. gas = (CALL_GAS_LIMIT == 0 || CALL_GAS_LIMIT > gasleft()) ? gasleft() : CALL_GAS_LIMIT; (success, ) = metadropTaxRecipient.call{ value: metadropBalanceToDistribute, gas: gas }(""); // If the ETH transfer fails, wrap the ETH and send it as WETH. We do this so that a called // address cannot cause this transfer to fail, either intentionally or by mistake: if (!success) { if (weth == address(0)) { weth = _uniswapRouter.WETH(); } try IWETH(weth).deposit{value: metadropBalanceToDistribute}() { try IERC20(address(weth)).transfer( metadropTaxRecipient, metadropBalanceToDistribute ) {} catch { // Dont allow a failed external call (in this case to WETH) to stop a transfer. // Emit that this has occured and continue. emit ExternalCallError(3); } } catch { // Dont allow a failed external call (in this case to WETH) to stop a transfer. // Emit that this has occured and continue. emit ExternalCallError(4); } } } } catch { // Dont allow a failed external call (in this case to uniswap) to stop a transfer. // Emit that this has occured and continue. emit ExternalCallError(5); } } /** * @dev distributeTaxTokens * * Allows the distribution of tax tokens to the designated recipient(s) * * As part of standard processing the tax token balance being above the threshold * will trigger an autoswap to ETH and distribution of this ETH to the designated * recipients. This is automatic and there is no need for user involvement. * * As part of this swap there are a number of calculations performed, particularly * if the tax balance is above MAX_AUTOSWAP_THRESHOLD_MULTIPLE. * * Testing indicates that these calculations are safe. But given the data / code * interactions it remains possible that some edge case set of scenarios may cause * an issue with these calculations. * * This method is therefore provided as a 'fallback' option to safely distribute * accumulated taxes from the contract, with a direct transfer of the ERC20 tokens * themselves. */ function distributeTaxTokens() external notDuringAutoswap { if (projectTaxPendingSwap > 0) { uint256 projectDistribution = projectTaxPendingSwap; projectTaxPendingSwap = 0; _transfer(address(this), projectTaxRecipient, projectDistribution, false); } if (metadropTaxPendingSwap > 0) { uint256 metadropDistribution = metadropTaxPendingSwap; metadropTaxPendingSwap = 0; _transfer( address(this), metadropTaxRecipient, metadropDistribution, false ); } } /** * @dev function {rescueETH} * * A withdraw function to allow ETH to be rescued. * * This contract should never hold ETH. The only envisaged scenario where * it might hold ETH is a failed autoswap where the uniswap swap has completed, * the recipient of ETH reverts, the contract then wraps to WETH and the * wrap to WETH fails. * * This feels unlikely. But, for safety, we include this method. * * @param amount_ The amount to withdraw */ function rescueETH(uint256 amount_) external notDuringAutoswap { (bool success, ) = projectTaxRecipient.call{value: amount_}(""); if (!success) { _revert(TransferFailed.selector); } } /** * @dev function {rescueERC20} * * A withdraw function to allow ERC20s (except address(this)) to be rescued. * * This contract should never hold ERC20s other than tax tokens. The only envisaged * scenario where it might hold an ERC20 is a failed autoswap where the uniswap swap * has completed, the recipient of ETH reverts, the contract then wraps to WETH, the * wrap to WETH succeeds, BUT then the transfer of WETH fails. * * This feels even less likely than the scenario where ETH is held on the contract. * But, for safety, we include this method. * * @param token_ The ERC20 contract * @param amount_ The amount to withdraw */ function rescueERC20( address token_, uint256 amount_ ) external notDuringAutoswap { if (token_ == address(this)) { _revert(CannotWithdrawThisToken.selector); } IERC20(token_).safeTransfer(projectTaxRecipient, amount_); } /** * @dev function {rescueExcessToken} * * A withdraw function to allow ERC20s from this address that are above * the accrued tax balance to be rescued. */ function rescueExcessToken(uint256 amount_) external notDuringAutoswap { // Cannot perform this before the token has been funded: if (fundedDate == 0) { _revert(CannotPerformPriorToFunding.selector); } uint256 excessToken = balanceOf(address(this)) - totalTaxPendingSwap(); if (amount_ > excessToken) { _revert(AmountExceedsAvailable.selector); } IERC20(address(this)).safeTransfer(projectTaxRecipient, amount_); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { if (account == address(0)) { _revert(MintToZeroAddress.selector); } _beforeTokenTransfer(address(0), account, amount); _totalSupply += uint120(amount); unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { if (account == address(0)) { _revert(BurnFromTheZeroAddress.selector); } _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; if (accountBalance < amount) { _revert(BurnExceedsBalance.selector); } unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= uint120(amount); } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { if (owner == address(0)) { _revert(ApproveFromTheZeroAddress.selector); } if (spender == address(0)) { _revert(ApproveToTheZeroAddress.selector); } _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < amount) { _revert(InsufficientAllowance.selector); } unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Destroys a `value` amount of tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 value) public virtual { _burn(_msgSender(), value); } /** * @dev Destroys a `value` amount of tokens from `account`, deducting from * the caller's allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `value`. */ function burnFrom(address account, uint256 value) public virtual { _spendAllowance(account, _msgSender(), value); _burn(account, value); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} receive() external payable {} }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; import {IConfigStructures} from "../../Global/IConfigStructures.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20ConfigByMetadrop} from "./IERC20ConfigByMetadrop.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; /** * @dev Metadrop core ERC-20 contract, interface */ interface IERC20ByMetadrop is IConfigStructures, IERC20, IERC20ConfigByMetadrop, IERC20Metadata { event AutoSwapThresholdUpdated(uint256 oldThreshold, uint256 newThreshold); event ExternalCallError(uint256 identifier); event InitialLiquidityAdded(uint256 tokenA, uint256 tokenB, uint256 lpToken); event LimitsUpdated( uint256 oldMaxTokensPerTransaction, uint256 newMaxTokensPerTransaction, uint256 oldMaxTokensPerWallet, uint256 newMaxTokensPerWallet ); event LiquidityLocked( uint256 lpTokens, uint256 lpLockupInDays, uint256 streamId ); event LiquidityBurned(uint256 lpTokens); event LiquidityPoolCreated(address addedPool); event LiquidityPoolAdded(address addedPool); event LiquidityPoolRemoved(address removedPool); event MetadropTaxBasisPointsChanged( uint256 oldBuyBasisPoints, uint256 newBuyBasisPoints, uint256 oldSellBasisPoints, uint256 newSellBasisPoints ); event ProjectTaxBasisPointsChanged( uint256 oldBuyBasisPoints, uint256 newBuyBasisPoints, uint256 oldSellBasisPoints, uint256 newSellBasisPoints ); event RevenueAutoSwap(); event ProjectTaxRecipientUpdated(address treasury); event UnlimitedAddressAdded(address addedUnlimted); event UnlimitedAddressRemoved(address removedUnlimted); event ValidCallerAdded(bytes32 addedValidCaller); event ValidCallerRemoved(bytes32 removedValidCaller); /** * @dev function {addInitialLiquidity} * * Add initial liquidity to the uniswap pair * * @param vaultFee_ The vault fee in wei. This must match the required fee from the external vault contract. * @param lpLockupInDaysOverride_ The number of days to lock liquidity NOTE you can pass 0 to use the stored value. * This value is an override, and will override a stored value which is LOWER that it. If the value you are passing is * LOWER than the stored value the stored value will not be reduced. * * Example usage 1: When creating the coin the lpLockupInDays is set to 0. This means that on this call the * user can set the lockup to any value they like, as all integer values greater than zero will be used to override * that set in storage. * * Example usage 2: When using a DRI Pool the lockup period is set on this contract and the pool need not know anything * about this setting. The pool can pass back a 0 on this call and know that the existing value stored on this contract * will be used. * @param burnLPTokensOverride_ If the LP tokens should be burned (otherwise they are locked). This is an override field * that can ONLY be used to override a held value of FALSE with a new value of TRUE. * * Example usage 1: When creating the coin the user didn't add liquidity, or specify that the LP tokens were to be burned. * So burnLPTokens is held as FALSE. When they add liquidity they want to lock tokens, so they pass this in as FALSE again, * and it remains FALSE. * * Example usage 2: As above, but when later adding liquidity the user wants to burn the LP. So the stored value is FALSE * and the user passes TRUE into this method. The TRUE overrides the held value of FALSE and the tokens are burned. * * Example uusage 3: The user is using a DRI pool and they have specified on the coin creation that the LP tokens are to * be burned. This contract therefore holds TRUE for burnLPTokens. The DRI pool does not need to know what the user has * selected. It can safely pass back FALSE to this method call and the stored value of TRUE will remain, resulting in the * LP tokens being burned. */ function addInitialLiquidity( uint256 vaultFee_, uint256 lpLockupInDaysOverride_, bool burnLPTokensOverride_ ) external payable; /** * @dev function {isLiquidityPool} * * Return if an address is a liquidity pool * * @param queryAddress_ The address being queried * @return bool The address is / isn't a liquidity pool */ function isLiquidityPool(address queryAddress_) external view returns (bool); /** * @dev function {liquidityPools} * * Returns a list of all liquidity pools * * @return liquidityPools_ a list of all liquidity pools */ function liquidityPools() external view returns (address[] memory liquidityPools_); /** * @dev function {addLiquidityPool} onlyOwner * * Allows the manager to add a liquidity pool to the pool enumerable set * * @param newLiquidityPool_ The address of the new liquidity pool */ function addLiquidityPool(address newLiquidityPool_) external; /** * @dev function {removeLiquidityPool} onlyOwner * * Allows the manager to remove a liquidity pool * * @param removedLiquidityPool_ The address of the old removed liquidity pool */ function removeLiquidityPool(address removedLiquidityPool_) external; /** * @dev function {isUnlimited} * * Return if an address is unlimited (is not subject to per txn and per wallet limits) * * @param queryAddress_ The address being queried * @return bool The address is / isn't unlimited */ function isUnlimited(address queryAddress_) external view returns (bool); /** * @dev function {unlimitedAddresses} * * Returns a list of all unlimited addresses * * @return unlimitedAddresses_ a list of all unlimited addresses */ function unlimitedAddresses() external view returns (address[] memory unlimitedAddresses_); /** * @dev function {addUnlimited} onlyOwner * * Allows the manager to add an unlimited address * * @param newUnlimited_ The address of the new unlimited address */ function addUnlimited(address newUnlimited_) external; /** * @dev function {removeUnlimited} onlyOwner * * Allows the manager to remove an unlimited address * * @param removedUnlimited_ The address of the old removed unlimited address */ function removeUnlimited(address removedUnlimited_) external; /** * @dev function {setProjectTaxRecipient} onlyOwner * * Allows the manager to set the project tax recipient address * * @param projectTaxRecipient_ New recipient address */ function setProjectTaxRecipient(address projectTaxRecipient_) external; /** * @dev function {setSwapThresholdBasisPoints} onlyOwner * * Allows the manager to set the autoswap threshold * * @param swapThresholdBasisPoints_ New swap threshold in basis points */ function setSwapThresholdBasisPoints( uint16 swapThresholdBasisPoints_ ) external; /** * @dev function {setProjectTaxRates} onlyOwner * * Change the tax rates, subject to only ever decreasing * * @param newProjectBuyTaxBasisPoints_ The new buy tax rate * @param newProjectSellTaxBasisPoints_ The new sell tax rate */ function setProjectTaxRates( uint16 newProjectBuyTaxBasisPoints_, uint16 newProjectSellTaxBasisPoints_ ) external; /** * @dev function {setLimits} onlyOwner * * Change the limits on transactions and holdings * * @param newMaxTokensPerTransaction_ The new per txn limit * @param newMaxTokensPerWallet_ The new tokens per wallet limit */ function setLimits( uint256 newMaxTokensPerTransaction_, uint256 newMaxTokensPerWallet_ ) external; /** * @dev function {limitsEnforced} * * Return if limits are enforced on this contract * * @return bool : they are / aren't */ function limitsEnforced() external view returns (bool); /** * @dev getMetadropBuyTaxBasisPoints * * Return the metadrop buy tax basis points given the timed expiry */ function getMetadropBuyTaxBasisPoints() external view returns (uint256); /** * @dev getMetadropSellTaxBasisPoints * * Return the metadrop sell tax basis points given the timed expiry */ function getMetadropSellTaxBasisPoints() external view returns (uint256); /** * @dev totalBuyTaxBasisPoints * * Provide easy to view tax total: */ function totalBuyTaxBasisPoints() external view returns (uint256); /** * @dev totalSellTaxBasisPoints * * Provide easy to view tax total: */ function totalSellTaxBasisPoints() external view returns (uint256); /** * @dev distributeTaxTokens * * Allows the distribution of tax tokens to the designated recipient(s) * * As part of standard processing the tax token balance being above the threshold * will trigger an autoswap to ETH and distribution of this ETH to the designated * recipients. This is automatic and there is no need for user involvement. * * As part of this swap there are a number of calculations performed, particularly * if the tax balance is above MAX_SWAP_THRESHOLD_MULTIPLE. * * Testing indicates that these calculations are safe. But given the data / code * interactions it remains possible that some edge case set of scenarios may cause * an issue with these calculations. * * This method is therefore provided as a 'fallback' option to safely distribute * accumulated taxes from the contract, with a direct transfer of the ERC20 tokens * themselves. */ function distributeTaxTokens() external; /** * @dev function {rescueETH} onlyOwner * * A withdraw function to allow ETH to be rescued. * * This contract should never hold ETH. The only envisaged scenario where * it might hold ETH is a failed autoswap where the uniswap swap has completed, * the recipient of ETH reverts, the contract then wraps to WETH and the * wrap to WETH fails. * * This feels unlikely. But, for safety, we include this method. * * @param amount_ The amount to withdraw */ function rescueETH(uint256 amount_) external; /** * @dev function {rescueERC20} * * A withdraw function to allow ERC20s (except address(this)) to be rescued. * * This contract should never hold ERC20s other than tax tokens. The only envisaged * scenario where it might hold an ERC20 is a failed autoswap where the uniswap swap * has completed, the recipient of ETH reverts, the contract then wraps to WETH, the * wrap to WETH succeeds, BUT then the transfer of WETH fails. * * This feels even less likely than the scenario where ETH is held on the contract. * But, for safety, we include this method. * * @param token_ The ERC20 contract * @param amount_ The amount to withdraw */ function rescueERC20(address token_, uint256 amount_) external; /** * @dev function {rescueExcessToken} * * A withdraw function to allow ERC20s from this address that are above * the accrued tax balance to be rescued. */ function rescueExcessToken(uint256 amount_) external; /** * @dev Destroys a `value` amount of tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 value) external; /** * @dev Destroys a `value` amount of tokens from `account`, deducting from * the caller's allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `value`. */ function burnFrom(address account, uint256 value) external; }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) /** * * @title IERC20ByMetadrop.sol. Interface for metadrop ERC20 standard * * @author metadrop https://metadrop.com/ * */ pragma solidity 0.8.21; interface IERC20ConfigByMetadrop { enum DRIPoolType { fundingLP, initialBuy } struct ERC20Config { bytes baseParameters; bytes supplyParameters; bytes taxParameters; bytes poolParameters; } struct ERC20BaseParameters { string name; string symbol; bool addLiquidityOnCreate; bool usesDRIPool; bytes distribution; } struct ERC20SupplyParameters { uint256 maxSupply; uint256 lpSupply; uint256 maxTokensPerWallet; uint256 maxTokensPerTxn; uint256 lpLockupInDays; uint256 botProtectionDurationInSeconds; address projectLPOwner; bool burnLPTokens; } struct ERC20TaxParameters { uint256 projectBuyTaxBasisPoints; uint256 projectSellTaxBasisPoints; uint256 taxSwapThresholdBasisPoints; uint256 metadropBuyTaxBasisPoints; uint256 metadropSellTaxBasisPoints; uint256 metadropTaxPeriodInDays; address projectTaxRecipient; address metadropTaxRecipient; uint256 metadropMinBuyTaxBasisPoints; uint256 metadropMinSellTaxBasisPoints; uint256 metadropBuyTaxProportionBasisPoints; uint256 metadropSellTaxProportionBasisPoints; uint256 autoBurnDurationInBlocks; uint256 autoBurnBasisPoints; } struct ERC20PoolParameters { uint256 poolType; uint256 poolSupply; uint256 poolStartDate; uint256 poolEndDate; uint256 poolVestingInSeconds; uint256 poolMaxETH; uint256 poolPerAddressMaxETH; uint256 poolMinETH; uint256 poolPerTransactionMinETH; uint256 poolContributionFeeBasisPoints; uint256 poolMaxInitialBuy; uint256 poolMaxInitialLiquidity; address poolFeeRecipient; address poolOwner; } }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IConfigStructures} from "../../Global/IConfigStructures.sol"; import {IErrors} from "../../Global/IErrors.sol"; import {IERC20ConfigByMetadrop} from "../ERC20/IERC20ConfigByMetadrop.sol"; /** * @dev Metadrop ERC-20 factory, interface */ interface IERC20FactoryByMetadrop is IConfigStructures, IErrors, IERC20ConfigByMetadrop { event DriPoolAddressUpdated(address oldAddress, address newAddress); event ERC20Created( string metaId, address indexed deployer, address contractInstance, address driPoolInstance, string symbol, string name, bytes constructorArgs ); event MachineAddressUpdated(address oldAddress, address newAddress); event OracleAddressUpdated(address oldAddress, address newAddress); event MessageValidityInSecondsUpdated( uint256 oldMessageValidityInSeconds, uint256 newMessageValidityInSeconds ); event PlatformTreasuryUpdated(address oldAddress, address newAddress); /** * @dev function {initialiseMachineAddress} * * Initialise the machine template address. This needs to be separate from * the constructor as the machine needs the factory address on its constructor. * * This must ALWAYS be called as part of deployment. * * @param machineTemplate_ the machine address */ function initialiseMachineAddress(address machineTemplate_) external; /** * @dev function {decommissionFactory} onlySuperAdmin * * Make this factory unusable for creating new ERC20s, forever * */ function decommissionFactory() external; /** * @dev function {setMetadropOracleAddress} onlyPlatformAdmin * * Set the metadrop trusted oracle address * * @param metadropOracleAddress_ Trusted metadrop oracle address */ function setMetadropOracleAddress(address metadropOracleAddress_) external; /** * @dev function {setMessageValidityInSeconds} onlyPlatformAdmin * * Set the validity period of signed messages * * @param messageValidityInSeconds_ Validity period in seconds for messages signed by the trusted oracle */ function setMessageValidityInSeconds( uint256 messageValidityInSeconds_ ) external; /** * @dev function {setPlatformTreasury} onlySuperAdmin * * Set the address that platform fees will be paid to / can be withdrawn to. * Note that this is restricted to the highest authority level, the super * admin. Platform admins can trigger a withdrawal to the treasury, but only * the default admin can set or alter the treasury address. It is recommended * that the default admin is highly secured and restrited e.g. a multi-sig. * * @param platformTreasury_ New treasury address */ function setPlatformTreasury(address platformTreasury_) external; /** * @dev function {setMachineAddress} onlyPlatformAdmin * * Set a new machine template address * * @param newMachineAddress_ the new machine address */ function setMachineAddress(address newMachineAddress_) external; /** * @dev function {setDriPoolAddress} onlyPlatformAdmin * * Set a new launch pool template address * * @param newDriPoolAddress_ the new launch pool address */ function setDriPoolAddress(address newDriPoolAddress_) external; /** * @dev function {withdrawETH} onlyPlatformAdmin * * A withdraw function to allow ETH to be withdrawn to the treasury * * @param amount_ The amount to withdraw */ function withdrawETH(uint256 amount_) external; /** * @dev function {withdrawERC20} onlyPlatformAdmin * * A withdraw function to allow ERC20s to be withdrawn to the treasury * * @param token_ The contract address of the token being withdrawn * @param amount_ The amount to withdraw */ function withdrawERC20(IERC20 token_, uint256 amount_) external; /** * @dev function {createERC20} * * Create an ERC-20 * * @param metaId_ The drop Id being approved * @param salt_ Salt for create2 * @param erc20Config_ ERC20 configuration * @param signedMessage_ The signed message object * @param vaultFee_ The fee for the token vault * @param deploymentFee_ The fee for deployment, if any * @return deployedAddress_ The deployed ERC20 contract address */ function createERC20( string calldata metaId_, bytes32 salt_, ERC20Config calldata erc20Config_, SignedDropMessageDetails calldata signedMessage_, uint256 vaultFee_, uint256 deploymentFee_ ) external payable returns (address deployedAddress_); /** * @dev function {createConfigHash} * * Create the config hash * * @param metaId_ The drop Id being approved * @param salt_ Salt for create2 * @param erc20Config_ ERC20 configuration * @param messageTimeStamp_ When the message for this config hash was signed * @param vaultFee_ The fee for the token vault * @param deploymentFee_ The fee for deployment, if any * @param deployer_ Address performing the deployment * @return configHash_ The bytes32 config hash */ function createConfigHash( string calldata metaId_, bytes32 salt_, ERC20Config calldata erc20Config_, uint256 messageTimeStamp_, uint256 vaultFee_, uint256 deploymentFee_, address deployer_ ) external pure returns (bytes32 configHash_); }
// SPDX-License-Identifier: BUSL-1.1 // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; import {IConfigStructures} from "../../Global/IConfigStructures.sol"; import {IERC20ConfigByMetadrop} from "../ERC20/IERC20ConfigByMetadrop.sol"; import {IERC20ByMetadrop} from "../ERC20/ERC20ByMetadrop.sol"; import {IErrors} from "../../Global/IErrors.sol"; /** * @dev Metadrop ERC-20 contract deployer * * @dev Implementation of the {IERC20DeployerByMetasdrop} interface. * * Lightweight deployment module for use with template contracts */ interface IERC20MachineByMetadrop is IERC20ConfigByMetadrop, IErrors { /** * @dev function {deploy} * * Deploy a fresh instance */ function deploy( bytes32 metaIdHash_, bytes32 salt_, bytes memory args_ ) external payable returns (address erc20ContractAddress_); }
// SPDX-License-Identifier: BUSL-1.1 // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; import {IConfigStructures} from "../../Global/IConfigStructures.sol"; import {IERC20ConfigByMetadrop} from "../ERC20/IERC20ConfigByMetadrop.sol"; import {IErrors} from "../../Global/IErrors.sol"; interface IERC20DRIPoolByMetadrop is IConfigStructures, IERC20ConfigByMetadrop, IErrors { enum PhaseStatus { before, open, succeeded, failed } struct Participant { uint128 contribution; uint128 excessRefunded; } event DRIPoolCreatedAndInitialised(); event AddToPool(address dripHolder, uint256 ethPooled, uint256 ethFee); event ClaimFromPool( address participant, uint256 dripTokenBurned, uint256 pooledTokenClaimed, uint256 pooledTokenBurnt, uint256 ethRefunded ); event ExcessRefunded(address participant, uint256 ethRefunded); event RefundFromFailedPool( address participant, uint256 dripTokenBurned, uint256 ethRefunded ); event InitialBuyMade(uint256 ethBuy); event UnexpectedTotalETHPooled( uint256 totalETHPooled, uint256 contractBalance, uint256 totalETHFundedToLPAndTokenBuy, uint256 totalExcessETHRefunded, uint256 projectSeedContributionETH, uint256 accumulatedFees ); event PoolClosedSuccessfully(uint256 totalETHPooled, uint256 totalETHFee); /** * @dev {driType} * * Returns the type of this DRI pool */ function driType() external view returns (DRIPoolType); /** * @dev {initialiseDRIP} * * Initalise configuration on a new minimal proxy clone * * @param poolParams_ bytes parameter object that will be decoded into configuration items. * @param name_ the name of the associated ERC20 token * @param symbol_ the symbol of the associated ERC20 token */ function initialiseDRIP( bytes calldata poolParams_, string calldata name_, string calldata symbol_ ) external; /** * @dev {supplyForLP} * * Convenience function to return the LP supply from the ERC-20 token contract. * * @return supplyForLP_ The total supply for LP creation. */ function supplyForLP() external view returns (uint256 supplyForLP_); /** * @dev {poolPhaseStatus} * * Convenience function to return the pool status in string format. * * @return poolPhaseStatus_ The pool phase status as a string */ function poolPhaseStatus() external view returns (string memory poolPhaseStatus_); /** * @dev {vestingEndDate} * * The vesting end date, being the end of the pool phase plus number of days vesting, if any. * * @return vestingEndDate_ The vesting end date as a timestamp */ function vestingEndDate() external view returns (uint256 vestingEndDate_); /** * @dev Return if the pool total has exceeded the minimum: * * @return poolIsAboveMinimum_ If the pool is above the minimum (or not) */ function poolIsAboveMinimum() external view returns (bool poolIsAboveMinimum_); /** * @dev Return if the pool is at the maximum. * * @return poolIsAtMaximum_ If the pool is at the maximum ETH. */ function poolIsAtMaximum() external view returns (bool poolIsAtMaximum_); /** * @dev Return the total ETH pooled (whether in the balance of this contract * or supplied as LP / token buy already). * * Note that this INCLUDES any seed ETH from the project on create. * * @return totalETHPooled_ the total ETH pooled in this contract */ function totalETHPooled() external view returns (uint256 totalETHPooled_); /** * @dev Return the total ETH contributed (whether in the balance of this contract * or supplied as LP already). * * Note that this EXCLUDES any seed ETH from the project on create. * * @return totalETHContributed_ the total ETH pooled in this contract */ function totalETHContributed() external view returns (uint256 totalETHContributed_); /** * @dev Return the total ETH pooled that is in excess of requirements * * @return totalExcessETHPooled_ the total ETH pooled in this contract * that is not needed for the initial lp / buy */ function totalExcessETHPooled() external view returns (uint256 totalExcessETHPooled_); /** * @dev Return the ETH pooled for this recipient * * @return participantETHPooled_ the total ETH pooled for this address */ function participantETHPooled( address participant_ ) external view returns (uint256 participantETHPooled_); /** * @dev Return the excess ETH already refunded for this recipient * * @return participantExcessETHRefunded_ the total excess ETH refunded for this participant */ function participantExcessETHRefunded( address participant_ ) external view returns (uint256 participantExcessETHRefunded_); /** * @dev Return the excess refund currently owing for the query address * * Note that this EXCLUDES any seed ETH from the project on create. * * @return participantExcessRefund_ the total ETH pooled in this contract */ function participantExcessRefundAvailable( address participant_ ) external view returns (uint256 participantExcessRefund_); /** * @dev Return if the max initial buy has been exceeded * * @return maxInitialBuyExceeded_ */ function maxInitialBuyExceeded() external view returns (bool maxInitialBuyExceeded_); /** * @dev Return if the max initial lp funding has been exceeded * * @return maxInitialLiquidityExceeded_ */ function maxInitialLiquidityExceeded() external view returns (bool maxInitialLiquidityExceeded_); /** * @dev {loadERC20AddressAndSeedETH} * * Load the target ERC-20 address. This is called by the factory in the same transaction as the clone * is instantiated * * @param createdERC20_ The ERC-20 address * @param poolCreator_ The creator of this pool */ function loadERC20AddressAndSeedETH( address createdERC20_, address poolCreator_ ) external payable; /** * @dev {startPool} * * The pool owner starts the pool manually. * * Note this can only be called by the owner IF the pool was setup with a manual * start date. See _setPoolDates * * @param signedMessage_ The signed message object * @param poolDuration_ The desired duration of the pool */ function startPool( SignedDropMessageDetails calldata signedMessage_, uint256 poolDuration_ ) external payable; /** * @dev {addToPool} * * A user calls this to contribute to the pool * * Note that we could have used the receive method for this, and processed any ETH send to the * contract as a contribution to the pool. We've opted for the clarity of a specific method, * with the recieve method reverting an unidentified ETH. * * @param signedMessage_ The signed message object */ function addToPool( SignedDropMessageDetails calldata signedMessage_ ) external payable; /** * @dev function {createAddToPoolMessageHash} * * Create the message hash * * @param sender_ The sender of the transcation * @param value_ The value of the transaction * @return messageHash_ The hash for the signed message */ function createAddToPoolMessageHash( address sender_, uint256 value_ ) external view returns (bytes32 messageHash_); /** * @dev function {createStartPoolMessageHash} * * Create the message hash * * @param poolDuration_ The duration of the pool * @return messageHash_ The hash for the signed message */ function createStartPoolMessageHash( uint256 poolDuration_ ) external view returns (bytes32 messageHash_); /** * @dev {claimFromPool} * * A user calls this to burn their DRIP and claim their ERC-20 tokens * */ function claimFromPool() external; /** * @dev {refundExcess} * * Can be called at any time by a participant to claim and ETH refund of any * ETH that will not be used to either fund the pool or for an initial buy * */ function refundExcess() external; /** * @dev {refundFromFailedPool} * * A user calls this to burn their DRIP and claim an ETH refund where the * minimum ETH pooled amount was not exceeded. * */ function refundFromFailedPool() external; /** * @dev {supplyLiquidity} * * When the pool phase is over this can be called to supply the pooled ETH to * the token contract. There it will be forwarded along with the LP supply of * tokens to uniswap to create the funded pair * * Note that this function can be called by anyone. While clearly it is likely * that this will be the project team, having this method open to anyone ensures that * liquidity will not be trapped in this contract if the team as unable to perform * this action. * * This method behaves differently depending on the pool type: * * IN A FUNDING LP POOL: * * All of the ETH held on this contract is provided to fund the LP * * IN AN INITIAL BUY POOL: * * ONLY the project supplied ETH is used to fund the liquidity. The remaining ETH * on this contract will fall into two possible categories: * * 1) ETH used to perform an initial token purchase immediately after the funding of * the LP. This will be the total remaining ETH on this contract IF that amount is * below the maximum initial buy amount. Otherwise it will be the max initial buy amount and the * remaining ETH will remain for refunds. * * 2) If the ETH on this contract is above the max initial buy amount there will be a * proportion of ETH remaining on this contract for refunds. * * @param lockerFee_ The ETH fee required to lock LP tokens * */ function supplyLiquidity(uint256 lockerFee_) external payable; /** * @dev function {rescueETH} * * A withdraw function to allow ETH to be rescued. * * Fallback safety method, only callable by the fee recipient. * * @param amount_ The amount to withdraw */ function rescueETH(uint256 amount_) external; /** * @dev function {rescueERC20} * * A withdraw function to allow ERC20s to be rescued. * * Fallback safety method, only callable by the fee recipient. * * @param token_ The ERC20 contract * @param amount_ The amount to withdraw */ function rescueERC20(address token_, uint256 amount_) external; }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) /** * * @title AuthorityModel.sol. Library for global authority components * * @author metadrop https://metadrop.com/ * */ pragma solidity 0.8.21; /** * * @dev Inheritance details: * EnumerableSet OZ enumerable mapping sets * IErrors Interface for platform error definitions * */ import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {IErrors} from "./IErrors.sol"; import {Revert} from "./Revert.sol"; contract AuthorityModel is IErrors, Revert { using EnumerableSet for EnumerableSet.AddressSet; event SuperAdminTransferred(address oldSuperAdmin, address newSuperAdmin); event PlatformAdminAdded(address platformAdmin); event PlatformAdminRevoked(address platformAdmin); // Address for the factory: address public factory; // The super admin can grant and revoke roles address public superAdmin; // Enumerable set to store platform admins: EnumerableSet.AddressSet private _platformAdmins; /** ==================================================================================================================== * MODIFIERS * ===================================================================================================================== */ /** ____________________________________________________________________________________________________________________ * -->ACCESS CONTROL * @dev (modifier) onlySuperAdmin. The associated action can only be taken by the super admin (an address with the * default admin role). * * _____________________________________________________________________________________________________________________ */ modifier onlySuperAdmin() { if (!isSuperAdmin(msg.sender)) revert CallerIsNotSuperAdmin(msg.sender); _; } /** ____________________________________________________________________________________________________________________ * -->ACCESS CONTROL * @dev (modifier) onlyPlatformAdmin. The associated action can only be taken by an address with the * platform admin role. * * _____________________________________________________________________________________________________________________ */ modifier onlyPlatformAdmin() { if (!isPlatformAdmin(msg.sender)) revert CallerIsNotPlatformAdmin(msg.sender); _; } /** ____________________________________________________________________________________________________________________ * -->GETTER * @dev (function) isSuperAdmin check if an address is the super admin * * --------------------------------------------------------------------------------------------------------------------- * @return bool * --------------------------------------------------------------------------------------------------------------------- * _____________________________________________________________________________________________________________________ */ function isSuperAdmin(address queryAddress_) public view returns (bool) { return (superAdmin == queryAddress_); } /** ____________________________________________________________________________________________________________________ * -->GETTER * @dev (function) isPlatformAdmin check if an address is a platform admin * * --------------------------------------------------------------------------------------------------------------------- * @return bool * --------------------------------------------------------------------------------------------------------------------- * _____________________________________________________________________________________________________________________ */ function isPlatformAdmin(address queryAddress_) public view returns (bool) { return (_platformAdmins.contains(queryAddress_)); } /** ____________________________________________________________________________________________________________________ * -->ACCESS CONTROL * @dev (function) grantPlatformAdmin Allows the super user Default Admin to add an address to the platform admin group * * --------------------------------------------------------------------------------------------------------------------- * @param newPlatformAdmin_ The address of the new platform admin * --------------------------------------------------------------------------------------------------------------------- * _____________________________________________________________________________________________________________________ */ function grantPlatformAdmin(address newPlatformAdmin_) public onlySuperAdmin { if (newPlatformAdmin_ == address(0)) { _revert(PlatformAdminCannotBeAddressZero.selector); } // Add this to the enumerated list: _platformAdmins.add(newPlatformAdmin_); emit PlatformAdminAdded(newPlatformAdmin_); } /** ____________________________________________________________________________________________________________________ * -->ACCESS CONTROL * @dev (function) revokePlatformAdmin Allows the super user Default Admin to revoke from the platform admin group * * --------------------------------------------------------------------------------------------------------------------- * @param oldPlatformAdmin_ The address of the old platform admin * --------------------------------------------------------------------------------------------------------------------- * _____________________________________________________________________________________________________________________ */ function revokePlatformAdmin( address oldPlatformAdmin_ ) public onlySuperAdmin { // Remove this from the enumerated list: _platformAdmins.remove(oldPlatformAdmin_); emit PlatformAdminRevoked(oldPlatformAdmin_); } /** ____________________________________________________________________________________________________________________ * -->ACCESS CONTROL * @dev (function) transferSuperAdmin Allows the super user Default Admin to transfer this right to another address * * --------------------------------------------------------------------------------------------------------------------- * @param newSuperAdmin_ The address of the new default admin * --------------------------------------------------------------------------------------------------------------------- * _____________________________________________________________________________________________________________________ */ function transferSuperAdmin(address newSuperAdmin_) public onlySuperAdmin { address oldSuperAdmin = superAdmin; // Update storage of this address: superAdmin = newSuperAdmin_; emit SuperAdminTransferred(oldSuperAdmin, newSuperAdmin_); } }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) /** * * @title Decommissionable.sol. Simple contract to implement a decommission 'kill switch' * * @author metadrop https://metadrop.com/ * */ pragma solidity 0.8.21; import {IErrors} from "./IErrors.sol"; import {Revert} from "./Revert.sol"; abstract contract Decommissionable is IErrors, Revert { bool internal _decommissioned; event Decomissioned(); /** * @dev {notWhenDecommissioned} * * Throws if the contract has been decommissioned */ modifier notWhenDecommissioned() { if (_decommissioned) { _revert(ContractIsDecommissioned.selector); } _; } /** * @dev Internal method to set the decommissioned flag. Should be called * by an external method with access control (e.g. ownable etc.) */ function _decommission() internal { _decommissioned = true; emit Decomissioned(); } }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) /** * * @title IConfigStructures.sol. Interface for common config structures used accross the platform * * @author metadrop https://metadrop.com/ * */ pragma solidity 0.8.21; interface IConfigStructures { enum DropStatus { approved, deployed, cancelled } enum TemplateStatus { live, terminated } // The current status of the mint: // - notEnabled: This type of mint is not part of this drop // - notYetOpen: This type of mint is part of the drop, but it hasn't started yet // - open: it's ready for ya, get in there. // - finished: been and gone. // - unknown: theoretically impossible. enum MintStatus { notEnabled, notYetOpen, open, finished, unknown } struct SubListConfig { uint256 start; uint256 end; uint256 phaseMaxSupply; } struct PrimarySaleModuleInstance { address instanceAddress; string instanceDescription; } struct NFTModuleConfig { uint256 templateId; bytes configData; bytes vestingData; } struct PrimarySaleModuleConfig { uint256 templateId; bytes configData; } struct ProjectBeneficiary { address payable payeeAddress; uint256 payeeShares; } struct VestingConfig { uint256 start; uint256 projectUpFrontShare; uint256 projectVestedShare; uint256 vestingPeriodInDays; uint256 vestingCliff; ProjectBeneficiary[] projectPayees; } struct RoyaltySplitterModuleConfig { uint256 templateId; bytes configData; } struct InLifeModuleConfig { uint256 templateId; bytes configData; } struct InLifeModules { InLifeModuleConfig[] modules; } struct NFTConfig { uint256 supply; string name; string symbol; bytes32 positionProof; bool includePriorPhasesInMintTracking; bool singleMetadataCollection; uint256 reservedAllocation; uint256 assistanceRequestWindowInSeconds; } struct Template { TemplateStatus status; uint16 templateNumber; uint32 loadedDate; address payable templateAddress; string templateDescription; } struct RoyaltyDetails { address newRoyaltyPaymentSplitterInstance; uint96 royaltyFromSalesInBasisPoints; } struct SignedDropMessageDetails { uint256 messageTimeStamp; bytes32 messageHash; bytes messageSignature; } }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) /** * * @title IErrors.sol. Interface for error definitions used across the platform * * @author metadrop https://metadrop.com/ * */ pragma solidity 0.8.21; interface IErrors { enum BondingCurveErrorType { OK, // No error INVALID_NUMITEMS, // The numItem value is 0 SPOT_PRICE_OVERFLOW // The updated spot price doesn't fit into 128 bits } error AdapterParamsMustBeEmpty(); // The adapter parameters on this LZ call must be empty. error AdditionToPoolIsBelowPerTransactionMinimum(); // The contribution amount is less than the minimum. error AdditionToPoolWouldExceedPoolCap(); // This addition to the pool would exceed the pool cap. error AdditionToPoolWouldExceedPerAddressCap(); // This addition to the pool would exceed the per address cap. error AddressAlreadySet(); // The address being set can only be set once, and is already non-0. error AllowanceDecreasedBelowZero(); // You cannot decrease the allowance below zero. error AlreadyInitialised(); // The contract is already initialised: it cannot be initialised twice! error AmountExceedsAvailable(); // You are requesting more token than is available. error ApprovalCallerNotOwnerNorApproved(); // The caller must own the token or be an approved operator. error ApproveFromTheZeroAddress(); // Approval cannot be called from the zero address (indeed, how have you??). error ApproveToTheZeroAddress(); // Approval cannot be given to the zero address. error ApprovalQueryForNonexistentToken(); // The token does not exist. error AuctionStatusIsNotEnded(); // Throw if the action required the auction to be closed, and it isn't. error AuctionStatusIsNotOpen(); // Throw if the action requires the auction to be open, and it isn't. error AuxCallFailed( address[] modules, uint256 value, bytes data, uint256 txGas ); // An auxilliary call from the drop factory failed. error BalanceMismatch(); // An error when comparing balance amounts. error BalanceQueryForZeroAddress(); // Cannot query the balance for the zero address. error BidMustBeBelowTheFloorWhenReducingQuantity(); // Only bids that are below the floor can reduce the quantity of the bid. error BidMustBeBelowTheFloorForRefundDuringAuction(); // Only bids that are below the floor can be refunded during the auction. error BondingCurveError(BondingCurveErrorType error); // An error of the type specified has occured in bonding curve processing. error botProtectionDurationInSecondsMustFitUint128(); // botProtectionDurationInSeconds cannot be too large. error BurnExceedsBalance(); // The amount you have selected to burn exceeds the addresses balance. error BurnFromTheZeroAddress(); // Tokens cannot be burned from the zero address. (Also, how have you called this!?!) error CallerIsNotDepositBoxOwner(); // The caller is not the owner of the deposit box. error CallerIsNotFactory(); // The caller of this function must match the factory address in storage. error CallerIsNotFactoryOrProjectOwner(); // The caller of this function must match the factory address OR project owner address. error CallerIsNotFactoryProjectOwnerOrPool(); // The caller of this function must match the factory address, project owner or pool address. error CallerIsNotTheFeeRecipient(); // The caller is not the fee recipient. error CallerIsNotTheOwner(); // The caller is not the owner of this contract. error CallerIsNotTheManager(); // The caller is not the manager of this contract. error CallerMustBeLzApp(); // The caller must be an LZ application. error CallerIsNotPlatformAdmin(address caller); // The caller of this function must be part of the platformAdmin group. error CallerIsNotSuperAdmin(address caller); // The caller of this function must match the superAdmin address in storage. error CannotAddLiquidityOnCreateAndUseDRIPool(); // Cannot use both liquidity added on create and a DRIPool in the same token. error CannotManuallyFundLPWhenUsingADRIPool(); // Cannot add liquidity manually when using a DRI pool. error CannotPerformDuringAutoswap(); // Cannot call this function during an autoswap. error CannotPerformPriorToFunding(); // Cannot perform this operation before a token is funded (i.e. liquidity added). error CannotSetNewOwnerToTheZeroAddress(); // You can't set the owner of this contract to the zero address (address(0)). error CannotSetToZeroAddress(); // The corresponding address cannot be set to the zero address (address(0)). error CannotSetNewManagerToTheZeroAddress(); // Cannot transfer the manager to the zero address (address(0)). error CannotWithdrawThisToken(); // Cannot withdraw the specified token. error CanOnlyReduce(); // The given operation can only reduce the value specified. error CollectionAlreadyRevealed(); // The collection is already revealed; you cannot call reveal again. error ContractIsDecommissioned(); // This contract is decommissioned! error ContractIsPaused(); // The call requires the contract to be unpaused, and it is paused. error ContractIsNotPaused(); // The call required the contract to be paused, and it is NOT paused. error DecreasedAllowanceBelowZero(); // The request would decrease the allowance below zero, and that is not allowed. error DestinationIsNotTrustedSource(); // The destination that is being called through LZ has not been set as trusted. error DeductionsOnBuyExceedOrEqualOneHundredPercent(); // The total of all buy deductions cannot equal or exceed 100%. error DeployerOnly(); // This method can only be called by the deployer address. error DeploymentError(); // Error on deployment. error DepositBoxIsNotOpen(); // This action cannot complete as the deposit box is not open. error DriPoolAddressCannotBeAddressZero(); // The Dri Pool address cannot be the zero address. error GasLimitIsTooLow(); // The gas limit for the LayerZero call is too low. error IncorrectConfirmationValue(); // You need to enter the right confirmation value to call this funtion (usually 69420). error IncorrectPayment(); // The function call did not include passing the correct payment. error InitialLiquidityAlreadyAdded(); // Initial liquidity has already been added. You can't do it again. error InitialLiquidityNotYetAdded(); // Initial liquidity needs to have been added for this to succedd. error InsufficientAllowance(); // There is not a high enough allowance for this operation. error InvalidAdapterParams(); // The current adapter params for LayerZero on this contract won't work :(. error InvalidAddress(); // An address being processed in the function is not valid. error InvalidEndpointCaller(); // The calling address is not a valid LZ endpoint. The LZ endpoint was set at contract creation // and cannot be altered after. Check the address LZ endpoint address on the contract. error InvalidHash(); // The passed hash does not meet requirements. error InvalidMinGas(); // The minimum gas setting for LZ in invalid. error InvalidOracleSignature(); // The signature provided with the contract call is not valid, either in format or signer. error InvalidPayload(); // The LZ payload is invalid error InvalidReceiver(); // The address used as a target for funds is not valid. error InvalidSourceSendingContract(); // The LZ message is being related from a source contract on another chain that is NOT trusted. error InvalidTotalShares(); // Total shares must equal 100 percent in basis points. error LimitsCanOnlyBeRaised(); // Limits are UP ONLY. error LimitTooHigh(); // The limit has been set too high. error ListLengthMismatch(); // Two or more lists were compared and they did not match length. error LiquidityPoolMustBeAContractAddress(); // Cannot add a non-contract as a liquidity pool. error LiquidityPoolCannotBeAddressZero(); // Cannot add a liquidity pool from the zero address. error LPLockUpMustFitUint88(); // LP lockup is held in a uint88, so must fit. error NoTrustedPathRecord(); // LZ needs a trusted path record for this to work. What's that, you ask? error MachineAddressCannotBeAddressZero(); // Cannot set the machine address to the zero address. error ManagerUnauthorizedAccount(); // The caller is not the pending manager. error MaxBidQuantityIs255(); // Validation: as we use a uint8 array to track bid positions the max bid quantity is 255. error MaxBuysPerBlockExceeded(); // You have exceeded the max buys per block. error MaxPublicMintAllowanceExceeded( uint256 requested, uint256 alreadyMinted, uint256 maxAllowance ); // The calling address has requested a quantity that would exceed the max allowance. error MaxSupplyTooHigh(); // Max supply must fit in a uint128. error MaxTokensPerWalletExceeded(); // The transfer would exceed the max tokens per wallet limit. error MaxTokensPerTxnExceeded(); // The transfer would exceed the max tokens per transaction limit. error MetadataIsLocked(); // The metadata on this contract is locked; it cannot be altered! error MetadropFactoryOnlyOncePerReveal(); // This function can only be called (a) by the factory and, (b) just one time! error MetadropModulesOnly(); // Can only be called from a metadrop contract. error MetadropOracleCannotBeAddressZero(); // The metadrop Oracle cannot be the zero address (address(0)). error MinETHCannotExceedMaxBuy(); // The min ETH amount cannot exceed the max buy amount. error MinETHCannotExceedMaxLiquidity(); // The min ETH amount cannot exceed the max liquidity amount. error MinGasLimitNotSet(); // The minimum gas limit for LayerZero has not been set. error MintERC2309QuantityExceedsLimit(); // The `quantity` minted with ERC2309 exceeds the safety limit. error MintingIsClosedForever(); // Minting is, as the error suggests, so over (and locked forever). error MintToZeroAddress(); // Cannot mint to the zero address. error MintZeroQuantity(); // The quantity of tokens minted must be more than zero. error NewBuyTaxBasisPointsExceedsMaximum(); // Project owner trying to set the tax rate too high. error NewSellTaxBasisPointsExceedsMaximum(); // Project owner trying to set the tax rate too high. error NoETHForLiquidityPair(); // No ETH has been provided for the liquidity pair. error TaxPeriodStillInForce(); // The minimum tax period has not yet expired. error NoPaymentDue(); // No payment is due for this address. error NoRefundForCaller(); // Error thrown when the calling address has no refund owed. error NoStoredMessage(); // There is no stored message matching the passed parameters. error NothingToClaim(); // The calling address has nothing to claim. error NoTokenForLiquidityPair(); // There is no token to add to the LP. error OperationDidNotSucceed(); // The operation failed (vague much?). error OracleSignatureHasExpired(); // A signature has been provided but it is too old. error OwnableUnauthorizedAccount(); // The caller is not the pending owner. error OwnershipNotInitializedForExtraData(); // The `extraData` cannot be set on an uninitialized ownership slot. error OwnerQueryForNonexistentToken(); // The token does not exist. error ParametersDoNotMatchSignedMessage(); // The parameters passed with the signed message do not match the message itself. error ParamTooLargeStartDate(); // The passed parameter exceeds the var type max. error ParamTooLargeEndDate(); // The passed parameter exceeds the var type max. error ParamTooLargeMinETH(); // The passed parameter exceeds the var type max. error ParamTooLargePerAddressMax(); // The passed parameter exceeds the var type max. error ParamTooLargeVestingDays(); // The passed parameter exceeds the var type max. error ParamTooLargePoolSupply(); // The passed parameter exceeds the var type max. error ParamTooLargePoolMaxETH(); // The passed parameter exceeds the var type max. error ParamTooLargePoolPerTxnMinETH(); // The passed parameter exceeds the var type max. error ParamTooLargeContributionFee(); // The passed parameter exceeds the var type max. error ParamTooLargeMaxInitialBuy(); // The passed parameter exceeds the var type max. error ParamTooLargeMaxInitialLiquidity(); // The passed parameter exceeds the var type max. error PassedConfigDoesNotMatchApproved(); // The config provided on the call does not match the approved config. error PauseCutOffHasPassed(); // The time period in which we can pause has passed; this contract can no longer be paused. error PaymentMustCoverPerMintFee(); // The payment passed must at least cover the per mint fee for the quantity requested. error PermitDidNotSucceed(); // The safeERC20 permit failed. error PlatformAdminCannotBeAddressZero(); // We cannot use the zero address (address(0)) as a platformAdmin. error PlatformTreasuryCannotBeAddressZero(); // The treasury address cannot be set to the zero address. error PoolCannotBeManuallyStarted(); // This pool cannot be manually started. error PoolIsAboveMinimum(); // You required the pool to be below the minimum, and it is not error PoolIsBelowMinimum(); // You required the pool to be above the minimum, and it is not error PoolMustBeSeededWithETHForInitialLiquidity(); // You must pass ETH for liquidity with this type of pool. error PoolPhaseIsNotOpen(); // The block.timestamp is either before the pool is open or after it is closed. error PoolPhaseIsNotFailed(); // The pool status must be failed. error PoolPhaseIsNotSucceeded(); // The pool status must be succeeded. error PoolVestingNotYetComplete(); // Tokens in the pool are not yet vested. error ProjectOwnerCannotBeAddressZero(); // The project owner has to be a non zero address. error ProofInvalid(); // The provided proof is not valid with the provided arguments. error QuantityExceedsRemainingCollectionSupply(); // The requested quantity would breach the collection supply. error QuantityExceedsRemainingPhaseSupply(); // The requested quantity would breach the phase supply. error QuantityExceedsMaxPossibleCollectionSupply(); // The requested quantity would breach the maximum trackable supply error RecipientsAndAmountsMismatch(); // The number of recipients and amounts do not match. error ReferralIdAlreadyUsed(); // This referral ID has already been used; they are one use only. error RequestingMoreThanAvailableBalance(); // The request exceeds the available balance. error RequestingMoreThanRemainingAllocation( uint256 previouslyMinted, uint256 requested, uint256 remainingAllocation ); // Number of tokens requested for this mint exceeds the remaining allocation (taking the // original allocation from the list and deducting minted tokens). error RouterCannotBeZeroAddress(); // The router address cannot be Zero. error RoyaltyFeeWillExceedSalePrice(); // The ERC2981 royalty specified will exceed the sale price. error ShareTotalCannotBeZero(); // The total of all the shares cannot be nothing. error SliceOutOfBounds(); // The bytes slice operation was out of bounds. error SliceOverflow(); // The bytes slice operation overlowed. error SuperAdminCannotBeAddressZero(); // The superAdmin cannot be the sero address (address(0)). error SupplyTotalMismatch(); // The sum of the team supply and lp supply does not match. error SupportWindowIsNotOpen(); // The project owner has not requested support within the support request expiry window. error SwapThresholdTooLow(); // The select swap threshold is below the minimum. error TaxFreeAddressCannotBeAddressZero(); // A tax free address cannot be address(0) error TemplateCannotBeAddressZero(); // The address for a template cannot be address zero (address(0)). error TemplateNotFound(); // There is no template that matches the passed template Id. error ThisMintIsClosed(); // It's over (well, this mint is, anyway). error TotalSharesMustMatchDenominator(); // The total of all shares must equal the denominator value. error TransferAmountExceedsBalance(); // The transfer amount exceeds the accounts available balance. error TransferCallerNotOwnerNorApproved(); // The caller must own the token or be an approved operator. error TransferFailed(); // The transfer has failed. error TransferFromIncorrectOwner(); // The token must be owned by `from`. error TransferToNonERC721ReceiverImplementer(); // Cannot safely transfer to a contract that does not implement the ERC721Receiver interface. error TransferFromZeroAddress(); // Cannot transfer from the zero address. Indeed, this surely is impossible, and likely a waste to check?? error TransferToZeroAddress(); // Cannot transfer to the zero address. error UnrecognisedVRFMode(); // Currently supported VRF modes are 0: chainlink and 1: arrng error UnrecognisedType(); // Pool type not found. error URIQueryForNonexistentToken(); // The token does not exist. error ValueExceedsMaximum(); // The value sent exceeds the maximum allowed (super useful explanation huh?). error VRFCoordinatorCannotBeAddressZero(); // The VRF coordinator cannot be the zero address (address(0)). error VestedBalanceExceedsTotalBalance(); // The vested balance cannot exceed the total balance. error LPTokensBalanceMismatch(); // The balace of LP tokens is not as expected. }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) // Metadrop based on OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity 0.8.21; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; import {IErrors} from "../IErrors.sol"; import {Revert} from "../Revert.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is IErrors, Revert, Context { address private _owner; event OwnershipTransferred( address indexed previousOwner, address indexed newOwner ); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { _revert(CallerIsNotTheOwner.selector); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { if (newOwner == address(0)) { _revert(CannotSetNewOwnerToTheZeroAddress.selector); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) // Metadrop based on OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol) pragma solidity 0.8.21; import {Ownable} from "./Ownable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The initial owner is specified at deployment time in the constructor for `Ownable`. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private _pendingOwner; event OwnershipTransferStarted( address indexed previousOwner, address indexed newOwner ); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership( address newOwner ) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); if (pendingOwner() != sender) { _revert(OwnableUnauthorizedAccount.selector); } _transferOwnership(sender); } }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) // Metadrop based on OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity 0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {IErrors} from "../IErrors.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 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 { using Address for address; /** * @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. */ 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 `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); if (oldAllowance < value) { revert IErrors.DecreasedAllowanceBelowZero(); } forceApprove(token, spender, oldAllowance - value); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to * 0 before setting it to a non-zero value. */ 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 Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); if (nonceAfter != (nonceBefore + 1)) { revert IErrors.PermitDidNotSucceed(); } } /** * @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). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "call fail"); if ((returndata.length != 0) && !abi.decode(returndata, (bool))) { revert IErrors.OperationDidNotSucceed(); } } /** * @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 silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool( IERC20 token, bytes memory data ) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // Metadrop Contracts (v2.1.0) /** * * @title Revert.sol. For efficient reverts * * @author metadrop https://metadrop.com/ * */ pragma solidity 0.8.21; abstract contract Revert { /** * @dev For more efficient reverts. */ function _revert(bytes4 errorSelector) internal pure { assembly { mstore(0x00, errorSelector) revert(0x00, 0x04) } } }
// SPDX-License-Identifier: BUSL-1.1 // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; library ERC20ByMetadropParamDecoder { /** * @dev function {decodeBaseParams} * * Decode NFT Parameters * * @param encodedBaseParams_ The base params encoded into a bytes array */ function decodeBaseParams( bytes memory encodedBaseParams_ ) pure public returns (string memory name, string memory symbol, address[] memory recipients, uint256[] memory amounts){ bytes memory distribution; // Decoding the initial parameters including the distribution bytes array. (name, symbol, , , distribution) = abi.decode(encodedBaseParams_, (string, string, bool, bool, bytes)); // Decoding the distribution into recipients and amounts. (recipients, amounts) = abi.decode(distribution, (address[], uint256[])); return (name, symbol, recipients, amounts); } }
// SPDX-License-Identifier: BUSL-1.1 // Metadrop Contracts (v2.1.0) pragma solidity 0.8.21; import {ISablierV2LockupLinear, LockupLinear} from "../../vendor/sablier/ISablierV2LockupLinear.sol"; import {IERC20, SafeERC20} from "../../Global/OZ/SafeERC20.sol"; library ERC20ByMetadropSablierV2LinearLockup { using SafeERC20 for IERC20; function createWithDurations( address lpOwner_, address uniswapV2Pair_, address tokenVault_, uint256 lpTokenAmount_, uint40 lpTokenLockupDuration_ ) public returns (uint256 streamId) { // Setup lock parameters. LockupLinear.CreateWithDurations memory params; params.sender = msg.sender; // The sender will be able to cancel the stream params.recipient = lpOwner_; // The recipient of the streamed assets params.totalAmount = uint128( lpTokenAmount_ ); // Total amount is the amount inclusive of all fees. params.asset = IERC20(uniswapV2Pair_); // The streaming asset params.cancelable = false; // Whether the stream will be cancelable or not params.durations = LockupLinear.Durations({ cliff: lpTokenLockupDuration_ - 1, // Cliff duration (must be less than total duration or will revert) total: lpTokenLockupDuration_ // Total duration }); // Create the LockupLinear stream using a function that sets the start time to `block.timestamp` streamId = ISablierV2LockupLinear(tokenVault_).createWithDurations(params); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IWETH is IERC20 { function deposit() external payable; function withdraw(uint256 wad) external; }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.19; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; type UD60x18 is uint256; struct Broker { address account; UD60x18 fee; } library LockupLinear { struct CreateWithDurations { address sender; address recipient; uint128 totalAmount; IERC20 asset; bool cancelable; bool transferable; Durations durations; Broker broker; } /// @notice Struct encapsulating the cliff duration and the total duration. /// @param cliff The cliff duration in seconds. /// @param total The total duration in seconds. struct Durations { uint40 cliff; uint40 total; } } interface ISablierV2LockupLinear { function createWithDurations(LockupLinear.CreateWithDurations calldata params) external returns (uint256 streamId); }
{ "optimizer": { "enabled": true, "runs": 90 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"superAdmin_","type":"address"},{"internalType":"address[]","name":"platformAdmins_","type":"address[]"},{"internalType":"address","name":"platformTreasury_","type":"address"},{"internalType":"address","name":"metadropOracleAddress_","type":"address"},{"internalType":"address","name":"uniswapRouter_","type":"address"},{"internalType":"address","name":"tokenVault_","type":"address"},{"internalType":"address","name":"driPoolTemplate_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AdapterParamsMustBeEmpty","type":"error"},{"inputs":[],"name":"AdditionToPoolIsBelowPerTransactionMinimum","type":"error"},{"inputs":[],"name":"AdditionToPoolWouldExceedPerAddressCap","type":"error"},{"inputs":[],"name":"AdditionToPoolWouldExceedPoolCap","type":"error"},{"inputs":[],"name":"AddressAlreadySet","type":"error"},{"inputs":[],"name":"AllowanceDecreasedBelowZero","type":"error"},{"inputs":[],"name":"AlreadyInitialised","type":"error"},{"inputs":[],"name":"AmountExceedsAvailable","type":"error"},{"inputs":[],"name":"ApprovalCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"ApprovalQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"ApproveFromTheZeroAddress","type":"error"},{"inputs":[],"name":"ApproveToTheZeroAddress","type":"error"},{"inputs":[],"name":"AuctionStatusIsNotEnded","type":"error"},{"inputs":[],"name":"AuctionStatusIsNotOpen","type":"error"},{"inputs":[{"internalType":"address[]","name":"modules","type":"address[]"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"},{"internalType":"uint256","name":"txGas","type":"uint256"}],"name":"AuxCallFailed","type":"error"},{"inputs":[],"name":"BalanceMismatch","type":"error"},{"inputs":[],"name":"BalanceQueryForZeroAddress","type":"error"},{"inputs":[],"name":"BidMustBeBelowTheFloorForRefundDuringAuction","type":"error"},{"inputs":[],"name":"BidMustBeBelowTheFloorWhenReducingQuantity","type":"error"},{"inputs":[{"internalType":"enum IErrors.BondingCurveErrorType","name":"error","type":"uint8"}],"name":"BondingCurveError","type":"error"},{"inputs":[],"name":"BurnExceedsBalance","type":"error"},{"inputs":[],"name":"BurnFromTheZeroAddress","type":"error"},{"inputs":[],"name":"CallerIsNotDepositBoxOwner","type":"error"},{"inputs":[],"name":"CallerIsNotFactory","type":"error"},{"inputs":[],"name":"CallerIsNotFactoryOrProjectOwner","type":"error"},{"inputs":[],"name":"CallerIsNotFactoryProjectOwnerOrPool","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotPlatformAdmin","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotSuperAdmin","type":"error"},{"inputs":[],"name":"CallerIsNotTheFeeRecipient","type":"error"},{"inputs":[],"name":"CallerIsNotTheManager","type":"error"},{"inputs":[],"name":"CallerIsNotTheOwner","type":"error"},{"inputs":[],"name":"CallerMustBeLzApp","type":"error"},{"inputs":[],"name":"CanOnlyReduce","type":"error"},{"inputs":[],"name":"CannotAddLiquidityOnCreateAndUseDRIPool","type":"error"},{"inputs":[],"name":"CannotManuallyFundLPWhenUsingADRIPool","type":"error"},{"inputs":[],"name":"CannotPerformDuringAutoswap","type":"error"},{"inputs":[],"name":"CannotPerformPriorToFunding","type":"error"},{"inputs":[],"name":"CannotSetNewManagerToTheZeroAddress","type":"error"},{"inputs":[],"name":"CannotSetNewOwnerToTheZeroAddress","type":"error"},{"inputs":[],"name":"CannotSetToZeroAddress","type":"error"},{"inputs":[],"name":"CannotWithdrawThisToken","type":"error"},{"inputs":[],"name":"CollectionAlreadyRevealed","type":"error"},{"inputs":[],"name":"ContractIsDecommissioned","type":"error"},{"inputs":[],"name":"ContractIsNotPaused","type":"error"},{"inputs":[],"name":"ContractIsPaused","type":"error"},{"inputs":[],"name":"DecreasedAllowanceBelowZero","type":"error"},{"inputs":[],"name":"DeductionsOnBuyExceedOrEqualOneHundredPercent","type":"error"},{"inputs":[],"name":"DeployerOnly","type":"error"},{"inputs":[],"name":"DeploymentError","type":"error"},{"inputs":[],"name":"DepositBoxIsNotOpen","type":"error"},{"inputs":[],"name":"DestinationIsNotTrustedSource","type":"error"},{"inputs":[],"name":"DriPoolAddressCannotBeAddressZero","type":"error"},{"inputs":[],"name":"GasLimitIsTooLow","type":"error"},{"inputs":[],"name":"IncorrectConfirmationValue","type":"error"},{"inputs":[],"name":"IncorrectPayment","type":"error"},{"inputs":[],"name":"InitialLiquidityAlreadyAdded","type":"error"},{"inputs":[],"name":"InitialLiquidityNotYetAdded","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InvalidAdapterParams","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidEndpointCaller","type":"error"},{"inputs":[],"name":"InvalidHash","type":"error"},{"inputs":[],"name":"InvalidMinGas","type":"error"},{"inputs":[],"name":"InvalidOracleSignature","type":"error"},{"inputs":[],"name":"InvalidPayload","type":"error"},{"inputs":[],"name":"InvalidReceiver","type":"error"},{"inputs":[],"name":"InvalidSourceSendingContract","type":"error"},{"inputs":[],"name":"InvalidTotalShares","type":"error"},{"inputs":[],"name":"LPLockUpMustFitUint88","type":"error"},{"inputs":[],"name":"LPTokensBalanceMismatch","type":"error"},{"inputs":[],"name":"LimitTooHigh","type":"error"},{"inputs":[],"name":"LimitsCanOnlyBeRaised","type":"error"},{"inputs":[],"name":"LiquidityPoolCannotBeAddressZero","type":"error"},{"inputs":[],"name":"LiquidityPoolMustBeAContractAddress","type":"error"},{"inputs":[],"name":"ListLengthMismatch","type":"error"},{"inputs":[],"name":"MachineAddressCannotBeAddressZero","type":"error"},{"inputs":[],"name":"ManagerUnauthorizedAccount","type":"error"},{"inputs":[],"name":"MaxBidQuantityIs255","type":"error"},{"inputs":[],"name":"MaxBuysPerBlockExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"alreadyMinted","type":"uint256"},{"internalType":"uint256","name":"maxAllowance","type":"uint256"}],"name":"MaxPublicMintAllowanceExceeded","type":"error"},{"inputs":[],"name":"MaxSupplyTooHigh","type":"error"},{"inputs":[],"name":"MaxTokensPerTxnExceeded","type":"error"},{"inputs":[],"name":"MaxTokensPerWalletExceeded","type":"error"},{"inputs":[],"name":"MetadataIsLocked","type":"error"},{"inputs":[],"name":"MetadropFactoryOnlyOncePerReveal","type":"error"},{"inputs":[],"name":"MetadropModulesOnly","type":"error"},{"inputs":[],"name":"MetadropOracleCannotBeAddressZero","type":"error"},{"inputs":[],"name":"MinETHCannotExceedMaxBuy","type":"error"},{"inputs":[],"name":"MinETHCannotExceedMaxLiquidity","type":"error"},{"inputs":[],"name":"MinGasLimitNotSet","type":"error"},{"inputs":[],"name":"MintERC2309QuantityExceedsLimit","type":"error"},{"inputs":[],"name":"MintToZeroAddress","type":"error"},{"inputs":[],"name":"MintZeroQuantity","type":"error"},{"inputs":[],"name":"MintingIsClosedForever","type":"error"},{"inputs":[],"name":"NewBuyTaxBasisPointsExceedsMaximum","type":"error"},{"inputs":[],"name":"NewSellTaxBasisPointsExceedsMaximum","type":"error"},{"inputs":[],"name":"NoETHForLiquidityPair","type":"error"},{"inputs":[],"name":"NoPaymentDue","type":"error"},{"inputs":[],"name":"NoRefundForCaller","type":"error"},{"inputs":[],"name":"NoStoredMessage","type":"error"},{"inputs":[],"name":"NoTokenForLiquidityPair","type":"error"},{"inputs":[],"name":"NoTrustedPathRecord","type":"error"},{"inputs":[],"name":"NothingToClaim","type":"error"},{"inputs":[],"name":"OperationDidNotSucceed","type":"error"},{"inputs":[],"name":"OracleSignatureHasExpired","type":"error"},{"inputs":[],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"OwnerQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"OwnershipNotInitializedForExtraData","type":"error"},{"inputs":[],"name":"ParamTooLargeContributionFee","type":"error"},{"inputs":[],"name":"ParamTooLargeEndDate","type":"error"},{"inputs":[],"name":"ParamTooLargeMaxInitialBuy","type":"error"},{"inputs":[],"name":"ParamTooLargeMaxInitialLiquidity","type":"error"},{"inputs":[],"name":"ParamTooLargeMinETH","type":"error"},{"inputs":[],"name":"ParamTooLargePerAddressMax","type":"error"},{"inputs":[],"name":"ParamTooLargePoolMaxETH","type":"error"},{"inputs":[],"name":"ParamTooLargePoolPerTxnMinETH","type":"error"},{"inputs":[],"name":"ParamTooLargePoolSupply","type":"error"},{"inputs":[],"name":"ParamTooLargeStartDate","type":"error"},{"inputs":[],"name":"ParamTooLargeVestingDays","type":"error"},{"inputs":[],"name":"ParametersDoNotMatchSignedMessage","type":"error"},{"inputs":[],"name":"PassedConfigDoesNotMatchApproved","type":"error"},{"inputs":[],"name":"PauseCutOffHasPassed","type":"error"},{"inputs":[],"name":"PaymentMustCoverPerMintFee","type":"error"},{"inputs":[],"name":"PermitDidNotSucceed","type":"error"},{"inputs":[],"name":"PlatformAdminCannotBeAddressZero","type":"error"},{"inputs":[],"name":"PlatformTreasuryCannotBeAddressZero","type":"error"},{"inputs":[],"name":"PoolCannotBeManuallyStarted","type":"error"},{"inputs":[],"name":"PoolIsAboveMinimum","type":"error"},{"inputs":[],"name":"PoolIsBelowMinimum","type":"error"},{"inputs":[],"name":"PoolMustBeSeededWithETHForInitialLiquidity","type":"error"},{"inputs":[],"name":"PoolPhaseIsNotFailed","type":"error"},{"inputs":[],"name":"PoolPhaseIsNotOpen","type":"error"},{"inputs":[],"name":"PoolPhaseIsNotSucceeded","type":"error"},{"inputs":[],"name":"PoolVestingNotYetComplete","type":"error"},{"inputs":[],"name":"ProjectOwnerCannotBeAddressZero","type":"error"},{"inputs":[],"name":"ProofInvalid","type":"error"},{"inputs":[],"name":"QuantityExceedsMaxPossibleCollectionSupply","type":"error"},{"inputs":[],"name":"QuantityExceedsRemainingCollectionSupply","type":"error"},{"inputs":[],"name":"QuantityExceedsRemainingPhaseSupply","type":"error"},{"inputs":[],"name":"RecipientsAndAmountsMismatch","type":"error"},{"inputs":[],"name":"ReferralIdAlreadyUsed","type":"error"},{"inputs":[],"name":"RequestingMoreThanAvailableBalance","type":"error"},{"inputs":[{"internalType":"uint256","name":"previouslyMinted","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"uint256","name":"remainingAllocation","type":"uint256"}],"name":"RequestingMoreThanRemainingAllocation","type":"error"},{"inputs":[],"name":"RouterCannotBeZeroAddress","type":"error"},{"inputs":[],"name":"RoyaltyFeeWillExceedSalePrice","type":"error"},{"inputs":[],"name":"ShareTotalCannotBeZero","type":"error"},{"inputs":[],"name":"SliceOutOfBounds","type":"error"},{"inputs":[],"name":"SliceOverflow","type":"error"},{"inputs":[],"name":"SuperAdminCannotBeAddressZero","type":"error"},{"inputs":[],"name":"SupplyTotalMismatch","type":"error"},{"inputs":[],"name":"SupportWindowIsNotOpen","type":"error"},{"inputs":[],"name":"SwapThresholdTooLow","type":"error"},{"inputs":[],"name":"TaxFreeAddressCannotBeAddressZero","type":"error"},{"inputs":[],"name":"TaxPeriodStillInForce","type":"error"},{"inputs":[],"name":"TemplateCannotBeAddressZero","type":"error"},{"inputs":[],"name":"TemplateNotFound","type":"error"},{"inputs":[],"name":"ThisMintIsClosed","type":"error"},{"inputs":[],"name":"TotalSharesMustMatchDenominator","type":"error"},{"inputs":[],"name":"TransferAmountExceedsBalance","type":"error"},{"inputs":[],"name":"TransferCallerNotOwnerNorApproved","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"TransferFromIncorrectOwner","type":"error"},{"inputs":[],"name":"TransferFromZeroAddress","type":"error"},{"inputs":[],"name":"TransferToNonERC721ReceiverImplementer","type":"error"},{"inputs":[],"name":"TransferToZeroAddress","type":"error"},{"inputs":[],"name":"URIQueryForNonexistentToken","type":"error"},{"inputs":[],"name":"UnrecognisedType","type":"error"},{"inputs":[],"name":"UnrecognisedVRFMode","type":"error"},{"inputs":[],"name":"VRFCoordinatorCannotBeAddressZero","type":"error"},{"inputs":[],"name":"ValueExceedsMaximum","type":"error"},{"inputs":[],"name":"VestedBalanceExceedsTotalBalance","type":"error"},{"inputs":[],"name":"botProtectionDurationInSecondsMustFitUint128","type":"error"},{"anonymous":false,"inputs":[],"name":"Decomissioned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"DriPoolAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"metaId","type":"string"},{"indexed":true,"internalType":"address","name":"deployer","type":"address"},{"indexed":false,"internalType":"address","name":"contractInstance","type":"address"},{"indexed":false,"internalType":"address","name":"driPoolInstance","type":"address"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"bytes","name":"constructorArgs","type":"bytes"}],"name":"ERC20Created","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"MachineAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldMessageValidityInSeconds","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newMessageValidityInSeconds","type":"uint256"}],"name":"MessageValidityInSecondsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"OracleAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"platformAdmin","type":"address"}],"name":"PlatformAdminAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"platformAdmin","type":"address"}],"name":"PlatformAdminRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"PlatformTreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldSuperAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newSuperAdmin","type":"address"}],"name":"SuperAdminTransferred","type":"event"},{"inputs":[{"internalType":"string","name":"metaId_","type":"string"},{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"components":[{"internalType":"bytes","name":"baseParameters","type":"bytes"},{"internalType":"bytes","name":"supplyParameters","type":"bytes"},{"internalType":"bytes","name":"taxParameters","type":"bytes"},{"internalType":"bytes","name":"poolParameters","type":"bytes"}],"internalType":"struct IERC20ConfigByMetadrop.ERC20Config","name":"erc20Config_","type":"tuple"},{"internalType":"uint256","name":"messageTimeStamp_","type":"uint256"},{"internalType":"uint256","name":"vaultFee_","type":"uint256"},{"internalType":"uint256","name":"deploymentFee_","type":"uint256"},{"internalType":"address","name":"deployer_","type":"address"}],"name":"createConfigHash","outputs":[{"internalType":"bytes32","name":"configHash_","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"string","name":"metaId_","type":"string"},{"internalType":"bytes32","name":"salt_","type":"bytes32"},{"components":[{"internalType":"bytes","name":"baseParameters","type":"bytes"},{"internalType":"bytes","name":"supplyParameters","type":"bytes"},{"internalType":"bytes","name":"taxParameters","type":"bytes"},{"internalType":"bytes","name":"poolParameters","type":"bytes"}],"internalType":"struct IERC20ConfigByMetadrop.ERC20Config","name":"erc20Config_","type":"tuple"},{"components":[{"internalType":"uint256","name":"messageTimeStamp","type":"uint256"},{"internalType":"bytes32","name":"messageHash","type":"bytes32"},{"internalType":"bytes","name":"messageSignature","type":"bytes"}],"internalType":"struct IConfigStructures.SignedDropMessageDetails","name":"signedMessage_","type":"tuple"},{"internalType":"uint256","name":"vaultFee_","type":"uint256"},{"internalType":"uint256","name":"deploymentFee_","type":"uint256"}],"name":"createERC20","outputs":[{"internalType":"address","name":"deployedAddress_","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"decommissionFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"driPoolTemplate","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"factory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newPlatformAdmin_","type":"address"}],"name":"grantPlatformAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"machineTemplate_","type":"address"}],"name":"initialiseMachineAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"queryAddress_","type":"address"}],"name":"isPlatformAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"queryAddress_","type":"address"}],"name":"isSuperAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"machineTemplate","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"metadropOracleAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"oldPlatformAdmin_","type":"address"}],"name":"revokePlatformAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newDriPoolAddress_","type":"address"}],"name":"setDriPoolAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newMachineAddress_","type":"address"}],"name":"setMachineAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"messageValidityInSeconds_","type":"uint256"}],"name":"setMessageValidityInSeconds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"metadropOracleAddress_","type":"address"}],"name":"setMetadropOracleAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"platformTreasury_","type":"address"}],"name":"setPlatformTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"superAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newSuperAdmin_","type":"address"}],"name":"transferSuperAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"uniswapRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"withdrawERC20","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"withdrawETH","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60e06040526007805461ffff60a01b191660e160a31b17905534801562000024575f80fd5b506040516200276b3803806200276b83398101604081905262000047916200055e565b3360c0526001600160a01b0387166200006c576200006c63905c833360e01b62000205565b600180546001600160a01b031916339081179091556200008c906200020d565b600162000099886200020d565b5f5b87518110156200011a57336001600160a01b0316888281518110620000c457620000c46200069c565b60200260200101516001600160a01b031603620000e4575f915062000111565b62000111888281518110620000fd57620000fd6200069c565b60200260200101516200020d60201b60201c565b6001016200009b565b506001600160a01b0386166200013c576200013c6359a1934760e11b62000205565b600780546001600160a01b0319166001600160a01b03888116919091179091558516620001755762000175631efe047760e01b62000205565b600680546001600160a01b038088166001600160a01b03199283161790925585821660805284821660a0526004805492851661010002610100600160a81b0319909316929092179091555f8054909116301790558015620001db57620001db33620002af565b6001600160a01b0388163314620001f757620001f78862000326565b5050505050505050620006e4565b805f5260045ffd5b6001546001600160a01b03163314620002405760405163fa1bd1d760e01b81523360048201526024015b60405180910390fd5b6001600160a01b038116620002615762000261637d2c13fb60e11b62000205565b6200026e600282620003b6565b506040516001600160a01b03821681527fcccafc3787229e84e106a9df64cb34531403b5a7934a4066bcb047daa3f4da38906020015b60405180910390a150565b6001546001600160a01b03163314620002de5760405163fa1bd1d760e01b815233600482015260240162000237565b620002eb600282620003d5565b506040516001600160a01b03821681527fe46b802d853473508f897bd3184086171ec26a81681dc34e23913f7ef03c9a0c90602001620002a4565b6001546001600160a01b03163314620003555760405163fa1bd1d760e01b815233600482015260240162000237565b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f0f62530a074f4e1e883a8c916fa7f8639d52598edb7f9b5aa3148d991db5610d910160405180910390a15050565b5f620003cc836001600160a01b038416620003eb565b90505b92915050565b5f620003cc836001600160a01b0384166200043a565b5f8181526001830160205260408120546200043257508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155620003cf565b505f620003cf565b5f818152600183016020526040812054801562000524575f6200045f600183620006b0565b85549091505f906200047490600190620006b0565b9050818114620004da575f865f0182815481106200049657620004966200069c565b905f5260205f200154905080875f018481548110620004b957620004b96200069c565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080620004ee57620004ee620006d0565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050620003cf565b5f915050620003cf565b80516001600160a01b038116811462000545575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f805f805f805f60e0888a03121562000575575f80fd5b62000580886200052e565b602089810151919850906001600160401b03808211156200059f575f80fd5b818b0191508b601f830112620005b3575f80fd5b815181811115620005c857620005c86200054a565b8060051b604051601f19603f83011681018181108582111715620005f057620005f06200054a565b60405291825284820192508381018501918e8311156200060e575f80fd5b938501935b82851015620006375762000627856200052e565b8452938501939285019262000613565b809b505050505050506200064e604089016200052e565b94506200065e606089016200052e565b93506200066e608089016200052e565b92506200067e60a089016200052e565b91506200068e60c089016200052e565b905092959891949750929550565b634e487b7160e01b5f52603260045260245ffd5b81810381811115620003cf57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603160045260245ffd5b60805160a05160c05161204e6200071d5f395f61051101525f81816102330152610e7b01525f81816102c50152610e53015261204e5ff3fe608060405260043610610147575f3560e01c80637d135c41116100b3578063ba93c21b1161006d578063ba93c21b146103b5578063c45a0155146103d4578063df7f453b146103f2578063e138818c14610421578063f14210a614610440578063f91838541461045f575f80fd5b80637d135c4114610306578063882f73561461032557806388d34357146103445780638a43b74c14610358578063942a26ba14610377578063a1db978214610396575f80fd5b80635bc789d9116101045780635bc789d9146102225780635f80cba61461025557806369565a85146102745780636e3fa5d1146102a1578063735de9f7146102b45780637cd86d60146102e7575f80fd5b80630c6dd9fd1461014b5780631e4bad871461016c57806329575f6a146101a15780634c33002a146101c05780634d8ee090146101e457806354e7ff1d14610203575b5f80fd5b348015610156575f80fd5b5061016a610165366004611926565b61047e565b005b348015610177575f80fd5b5060065461018b906001600160a01b031681565b6040516101989190611941565b60405180910390f35b3480156101ac575f80fd5b5060015461018b906001600160a01b031681565b3480156101cb575f80fd5b5060045461018b9061010090046001600160a01b031681565b3480156101ef575f80fd5b5061016a6101fe366004611926565b61050e565b34801561020e575f80fd5b5061016a61021d366004611926565b6105c7565b34801561022d575f80fd5b5061018b7f000000000000000000000000000000000000000000000000000000000000000081565b348015610260575f80fd5b5061016a61026f366004611926565b61062a565b34801561027f575f80fd5b5061029361028e3660046119a8565b6106cd565b604051908152602001610198565b61018b6102af366004611a44565b610719565b3480156102bf575f80fd5b5061018b7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102f2575f80fd5b5061016a610301366004611926565b610864565b348015610311575f80fd5b5061016a610320366004611926565b6108fd565b348015610330575f80fd5b5061016a61033f366004611aeb565b610996565b34801561034f575f80fd5b5061016a610a19565b348015610363575f80fd5b5061016a610372366004611926565b610a4b565b348015610382575f80fd5b5061016a610391366004611926565b610acc565b3480156103a1575f80fd5b5061016a6103b0366004611b02565b610b65565b3480156103c0575f80fd5b5060055461018b906001600160a01b031681565b3480156103df575f80fd5b505f5461018b906001600160a01b031681565b3480156103fd575f80fd5b5061041161040c366004611926565b610bab565b6040519015158152602001610198565b34801561042c575f80fd5b5060075461018b906001600160a01b031681565b34801561044b575f80fd5b5061016a61045a366004611aeb565b610bbf565b34801561046a575f80fd5b50610411610479366004611926565b610bf3565b61048733610bab565b6104af573360405163fa1bd1d760e01b81526004016104a69190611941565b60405180910390fd5b600180546001600160a01b038381166001600160a01b03198316179092556040519116907f0f62530a074f4e1e883a8c916fa7f8639d52598edb7f9b5aa3148d991db5610d906105029083908590611b2c565b60405180910390a15050565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161461054e5761054e63120a1f9760e21b610c05565b6005546001600160a01b03161561056f5761056f637b1616c160e11b610c05565b600580546001600160a01b0319166001600160a01b0383161790556040517f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae906105bc905f908490611b2c565b60405180910390a150565b6105d033610bab565b6105ef573360405163fa1bd1d760e01b81526004016104a69190611941565b6105fa600282610c0d565b507fe46b802d853473508f897bd3184086171ec26a81681dc34e23913f7ef03c9a0c816040516105bc9190611941565b61063333610bf3565b610652573360405163cd40902d60e01b81526004016104a69190611941565b6001600160a01b03811661067057610670630ba68dbb60e41b610c05565b600480546001600160a01b03838116610100908102610100600160a81b03198416179093556040519290910416907f0ace8cc48451c2115e4b583399a0d93e969dd6fff132ecfda7d73f7fb34726d7906105029083908590611b2c565b5f8888886106da89610c28565b888888886040516020016106f5989796959493929190611b46565b60405160208183030381529060405280519060200120905098975050505050505050565b6004545f9060ff161561073657610736636b3e1fe160e11b610c05565b61073f84610c8e565b61074e88888888888888610d19565b5f808061079761075e8980611b91565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610d5292505050565b919450925090505f6107b6826107b060608c018c611b91565b87610d96565b90505f806107fe8e8e6040516020016107d0929190611bd3565b60408051601f1981840301815291905280516020909101206005548e908e906001600160a01b031687610e2e565b9150915061080f858a8a8587610f8a565b6108538e8e8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508692508791508a905085611105565b509c9b505050505050505050505050565b61086d33610bab565b61088c573360405163fa1bd1d760e01b81526004016104a69190611941565b6001600160a01b0381166108aa576108aa6359a1934760e11b610c05565b600780546001600160a01b038381166001600160a01b03198316179092556040519116907ff3814cc4b6220f5bf02409e4cebe75e5032fdb20cef0db459431fd5cba574b93906105029083908590611b2c565b61090633610bf3565b610925573360405163cd40902d60e01b81526004016104a69190611941565b6001600160a01b03811661094357610943631efe047760e01b610c05565b600680546001600160a01b038381166001600160a01b03198316179092556040519116907fd3afd1676752459d3aa8b6f73633fac8efd724610da9c4cd8d8e4fa1631841fb906105029083908590611b2c565b61099f33610bf3565b6109be573360405163cd40902d60e01b81526004016104a69190611941565b6007805461ffff838116600160a01b90810261ffff60a01b1984161790935560408051939092041680835260208301849052917ff78e983a5bebb7320738ee1be825e0751accce1a4a79dd82e4e93171a2c20e309101610502565b610a2233610bab565b610a41573360405163fa1bd1d760e01b81526004016104a69190611941565b610a49611153565b565b610a5433610bab565b610a73573360405163fa1bd1d760e01b81526004016104a69190611941565b6001600160a01b038116610a9157610a91637d2c13fb60e11b610c05565b610a9c60028261118a565b507fcccafc3787229e84e106a9df64cb34531403b5a7934a4066bcb047daa3f4da38816040516105bc9190611941565b610ad533610bf3565b610af4573360405163cd40902d60e01b81526004016104a69190611941565b6001600160a01b038116610b1257610b12636dad5a6360e11b610c05565b600580546001600160a01b038381166001600160a01b03198316179092556040519116907f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae906105029083908590611b2c565b610b6e33610bf3565b610b8d573360405163cd40902d60e01b81526004016104a69190611941565b600754610ba7906001600160a01b0384811691168361119e565b5050565b6001546001600160a01b0390811691161490565b610bc833610bf3565b610be7573360405163cd40902d60e01b81526004016104a69190611941565b610bf0816111f5565b50565b5f610bff60028361125d565b92915050565b805f5260045ffd5b5f610c21836001600160a01b03841661127e565b9392505050565b5f610c338280611b91565b610c406020850185611b91565b610c4d6040870187611b91565b610c5a6060890189611b91565b604051602001610c71989796959493929190611be2565b604051602081830303815290604052805190602001209050919050565b610cdc6020820135610ca36040840184611b91565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061136192505050565b610cf057610cf0632f58c7cd60e01b610c05565b60075442600160a01b90910461ffff168235011015610bf057610bf0631870a45d60e21b610c05565b5f610d2b8888888888358888336106cd565b905083602001358114610d4857610d48631a9848b160e01b610c05565b5050505050505050565b610d5a6118eb565b5f8060608085806020019051810190610d739190611cda565b604080518082019091529384526020840192909252919891975095509350505050565b5f8415610e2357600454610db79061010090046001600160a01b03166113ca565b8251602084015160405163b8c1707360e01b81529293506001600160a01b0384169263b8c1707392610df192899289929190600401611dc0565b5f604051808303815f87803b158015610e08575f80fd5b505af1158015610e1a573d5f803e3d5ffd5b50505050610e26565b505f5b949350505050565b5f60605f6040518060a00160405280610e443390565b6001600160a01b0390811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f000000000000000000000000000000000000000000000000000000000000000081166040830152306060830152861660809091015290505f81610ebd8880611b91565b610eca60208b018b611b91565b610ed760408d018d611b91565b610ee460608f018f611b91565b604051602001610efc99989796959493929190611df9565b60408051601f198184030181529082905263d76fad2360e01b825291506001600160a01b0387169063d76fad2390610f3c908c908c908690600401611e8f565b6020604051808303815f875af1158015610f58573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f7c9190611eb6565b999098509650505050505050565b848015610f9e57506001600160a01b038116155b1561103b57610fad8385611ee5565b341015610fc457610fc463569e8c1160e01b610c05565b6001600160a01b0382166393bf5705610fdd8534611ef8565b6040516001600160e01b031960e084901b168152600481018890525f6024820181905260448201526064015f604051808303818588803b15801561101f575f80fd5b505af1158015611031573d5f803e3d5ffd5b50505050506110f5565b6001600160a01b038116156110de57823410156110625761106263569e8c1160e01b610c05565b5f61106d8434611ef8565b90505f81156110795750335b6040516361b2761d60e11b81526001600160a01b0384169063c364ec3a9084906110a99088908690600401611b2c565b5f604051808303818588803b1580156110c0575f80fd5b505af11580156110d2573d5f803e3d5ffd5b505050505050506110f5565b8234146110f5576110f563569e8c1160e01b610c05565b6110fe836111f5565b5050505050565b8151602083015160405133927ff65d3a11fa2b7663a8d9ddb6a63494bf4b09240815636a0332849a3179a000c992611144928a928a928a928990611f0b565b60405180910390a25050505050565b6004805460ff191660011790556040517fa5f3a64ab759ee06c492853089ad840d35deaf0f93c21423227115491a8502a9905f90a1565b5f610c21836001600160a01b038416611461565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526111f09084906114ad565b505050565b6007546040515f916001600160a01b03169083908381818185875af1925050503d805f811461123f576040519150601f19603f3d011682016040523d82523d5f602084013e611244565b606091505b5050905080610ba757610ba76312171d8360e31b610c05565b6001600160a01b0381165f9081526001830160205260408120541515610c21565b5f8181526001830160205260408120548015611358575f6112a0600183611ef8565b85549091505f906112b390600190611ef8565b9050818114611312575f865f0182815481106112d1576112d1611d59565b905f5260205f200154905080875f0184815481106112f1576112f1611d59565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061132357611323611f7b565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610bff565b5f915050610bff565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018390525f908190605c0160408051601f198184030181529190528051602090910120600654909150610e26906001600160a01b0316828561152f565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c175f526e5af43d82803e903d91602b57fd5bf38260781b17602052603760095ff090506001600160a01b03811661145c5760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b60448201526064016104a6565b919050565b5f8181526001830160205260408120546114a657508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610bff565b505f610bff565b5f6114ed826040518060400160405280600981526020016818d85b1b0819985a5b60ba1b815250856001600160a01b031661158d9092919063ffffffff16565b905080515f1415801561151157508080602001905181019061150f9190611f8f565b155b156111f0576040516308575cad60e01b815260040160405180910390fd5b5f805f61153c858561159b565b90925090505f81600481111561155457611554611fa8565b1480156115725750856001600160a01b0316826001600160a01b0316145b8061158357506115838686866115dd565b9695505050505050565b6060610e2684845f856116c4565b5f8082516041036115cf576020830151604084015160608501515f1a6115c38782858561179b565b945094505050506115d6565b505f905060025b9250929050565b5f805f856001600160a01b0316631626ba7e60e01b8686604051602401611605929190611fbc565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516116439190611fd4565b5f60405180830381855afa9150503d805f811461167b576040519150601f19603f3d011682016040523d82523d5f602084013e611680565b606091505b509150915081801561169457506020815110155b801561158357508051630b135d3f60e11b906116b99083016020908101908401611fef565b149695505050505050565b6060824710156117255760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104a6565b5f80866001600160a01b031685876040516117409190611fd4565b5f6040518083038185875af1925050503d805f811461177a576040519150601f19603f3d011682016040523d82523d5f602084013e61177f565b606091505b50915091506117908783838761184e565b979650505050505050565b5f806fa2a8918ca85bafe22016d0b997e4df60600160ff1b038311156117c657505f90506003611845565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611817573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b03811661183f575f60019250925050611845565b91505f90505b94509492505050565b606083156118bc5782515f036118b5576001600160a01b0385163b6118b55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104a6565b5081610e26565b610e2683838151156118d15781518083602001fd5b8060405162461bcd60e51b81526004016104a69190612006565b60405180604001604052806002905b60608152602001906001900390816118fa5790505090565b6001600160a01b0381168114610bf0575f80fd5b5f60208284031215611936575f80fd5b8135610c2181611912565b6001600160a01b0391909116815260200190565b5f8083601f840112611965575f80fd5b5081356001600160401b0381111561197b575f80fd5b6020830191508360208285010111156115d6575f80fd5b5f608082840312156119a2575f80fd5b50919050565b5f805f805f805f8060e0898b0312156119bf575f80fd5b88356001600160401b03808211156119d5575f80fd5b6119e18c838d01611955565b909a50985060208b0135975060408b0135915080821115611a00575f80fd5b50611a0d8b828c01611992565b955050606089013593506080890135925060a0890135915060c0890135611a3381611912565b809150509295985092959890939650565b5f805f805f805f60c0888a031215611a5a575f80fd5b87356001600160401b0380821115611a70575f80fd5b611a7c8b838c01611955565b909950975060208a0135965060408a0135915080821115611a9b575f80fd5b611aa78b838c01611992565b955060608a0135915080821115611abc575f80fd5b5088016060818b031215611ace575f80fd5b969995985093969295946080840135945060a09093013592915050565b5f60208284031215611afb575f80fd5b5035919050565b5f8060408385031215611b13575f80fd5b8235611b1e81611912565b946020939093013593505050565b6001600160a01b0392831681529116602082015260400190565b878982379096019485526020850193909352604084019190915260608084019190915260808301919091529190911b6bffffffffffffffffffffffff191660a082015260b401919050565b5f808335601e19843603018112611ba6575f80fd5b8301803591506001600160401b03821115611bbf575f80fd5b6020019150368190038213156115d6575f80fd5b818382375f9101908152919050565b878982375f8882015f8152878982375f908801908152858782375f908601908152838582375f9301928352509098975050505050505050565b634e487b7160e01b5f52604160045260245ffd5b5f5b83811015611c49578181015183820152602001611c31565b50505f910152565b5f82601f830112611c60575f80fd5b81516001600160401b0380821115611c7a57611c7a611c1b565b604051601f8301601f19908116603f01168101908282118183101715611ca257611ca2611c1b565b81604052838152866020858801011115611cba575f80fd5b611583846020830160208901611c2f565b8051801515811461145c575f80fd5b5f805f8060808587031215611ced575f80fd5b84516001600160401b0380821115611d03575f80fd5b611d0f88838901611c51565b95506020870151915080821115611d24575f80fd5b50611d3187828801611c51565b935050611d4060408601611ccb565b9150611d4e60608601611ccb565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b5f8151808452611dac816020860160208601611c2f565b601f01601f19169290920160200192915050565b606081525f611dd3606083018688611d6d565b8281036020840152611de58186611d95565b905082810360408401526117908185611d95565b5f610120828c835b6005811015611e295781516001600160a01b0316835260209283019290910190600101611e01565b5050508060a0840152611e3f8184018b8d611d6d565b905082810360c0840152611e5481898b611d6d565b905082810360e0840152611e69818789611d6d565b9050828103610100840152611e7f818587611d6d565b9c9b505050505050505050505050565b838152826020820152606060408201525f611ead6060830184611d95565b95945050505050565b5f60208284031215611ec6575f80fd5b8151610c2181611912565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610bff57610bff611ed1565b81810381811115610bff57610bff611ed1565b60c081525f611f1d60c0830189611d95565b6001600160a01b038881166020850152871660408401528281036060840152611f468187611d95565b90508281036080840152611f5a8186611d95565b905082810360a0840152611f6e8185611d95565b9998505050505050505050565b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215611f9f575f80fd5b610c2182611ccb565b634e487b7160e01b5f52602160045260245ffd5b828152604060208201525f610e266040830184611d95565b5f8251611fe5818460208701611c2f565b9190910192915050565b5f60208284031215611fff575f80fd5b5051919050565b602081525f610c216020830184611d9556fea2646970667358221220420dc2d2effe049e06a7d42f5e4bf0c39d9278357d77165e80911108c1b0a25864736f6c63430008150033000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada5600000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000de3ff2a50bd1ba1bd6a608ea0138946dddf595bc000000000000000000000000abcc7736c42713cd40ae8c2915c3b3dcdef6b83d0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d000000000000000000000000afb979d9afad1ad27c5eff4e27226e3ab9e5dcc900000000000000000000000081e0f16506de3042024021abda3e4de4fcf052d10000000000000000000000000000000000000000000000000000000000000004000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56000000000000000000000000bf52436dbb09c97a301be66930392f61ee61c7b8000000000000000000000000e79f7a6af8d3bc37d30a172b2c9d2fd003af032b000000000000000000000000c0eba66b055aed2cc8e9e38f04e64501950cb470
Deployed Bytecode
0x608060405260043610610147575f3560e01c80637d135c41116100b3578063ba93c21b1161006d578063ba93c21b146103b5578063c45a0155146103d4578063df7f453b146103f2578063e138818c14610421578063f14210a614610440578063f91838541461045f575f80fd5b80637d135c4114610306578063882f73561461032557806388d34357146103445780638a43b74c14610358578063942a26ba14610377578063a1db978214610396575f80fd5b80635bc789d9116101045780635bc789d9146102225780635f80cba61461025557806369565a85146102745780636e3fa5d1146102a1578063735de9f7146102b45780637cd86d60146102e7575f80fd5b80630c6dd9fd1461014b5780631e4bad871461016c57806329575f6a146101a15780634c33002a146101c05780634d8ee090146101e457806354e7ff1d14610203575b5f80fd5b348015610156575f80fd5b5061016a610165366004611926565b61047e565b005b348015610177575f80fd5b5060065461018b906001600160a01b031681565b6040516101989190611941565b60405180910390f35b3480156101ac575f80fd5b5060015461018b906001600160a01b031681565b3480156101cb575f80fd5b5060045461018b9061010090046001600160a01b031681565b3480156101ef575f80fd5b5061016a6101fe366004611926565b61050e565b34801561020e575f80fd5b5061016a61021d366004611926565b6105c7565b34801561022d575f80fd5b5061018b7f000000000000000000000000afb979d9afad1ad27c5eff4e27226e3ab9e5dcc981565b348015610260575f80fd5b5061016a61026f366004611926565b61062a565b34801561027f575f80fd5b5061029361028e3660046119a8565b6106cd565b604051908152602001610198565b61018b6102af366004611a44565b610719565b3480156102bf575f80fd5b5061018b7f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d81565b3480156102f2575f80fd5b5061016a610301366004611926565b610864565b348015610311575f80fd5b5061016a610320366004611926565b6108fd565b348015610330575f80fd5b5061016a61033f366004611aeb565b610996565b34801561034f575f80fd5b5061016a610a19565b348015610363575f80fd5b5061016a610372366004611926565b610a4b565b348015610382575f80fd5b5061016a610391366004611926565b610acc565b3480156103a1575f80fd5b5061016a6103b0366004611b02565b610b65565b3480156103c0575f80fd5b5060055461018b906001600160a01b031681565b3480156103df575f80fd5b505f5461018b906001600160a01b031681565b3480156103fd575f80fd5b5061041161040c366004611926565b610bab565b6040519015158152602001610198565b34801561042c575f80fd5b5060075461018b906001600160a01b031681565b34801561044b575f80fd5b5061016a61045a366004611aeb565b610bbf565b34801561046a575f80fd5b50610411610479366004611926565b610bf3565b61048733610bab565b6104af573360405163fa1bd1d760e01b81526004016104a69190611941565b60405180910390fd5b600180546001600160a01b038381166001600160a01b03198316179092556040519116907f0f62530a074f4e1e883a8c916fa7f8639d52598edb7f9b5aa3148d991db5610d906105029083908590611b2c565b60405180910390a15050565b337f0000000000000000000000009135fd30af7daa2fedd1786e0fc204b18d130d566001600160a01b03161461054e5761054e63120a1f9760e21b610c05565b6005546001600160a01b03161561056f5761056f637b1616c160e11b610c05565b600580546001600160a01b0319166001600160a01b0383161790556040517f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae906105bc905f908490611b2c565b60405180910390a150565b6105d033610bab565b6105ef573360405163fa1bd1d760e01b81526004016104a69190611941565b6105fa600282610c0d565b507fe46b802d853473508f897bd3184086171ec26a81681dc34e23913f7ef03c9a0c816040516105bc9190611941565b61063333610bf3565b610652573360405163cd40902d60e01b81526004016104a69190611941565b6001600160a01b03811661067057610670630ba68dbb60e41b610c05565b600480546001600160a01b03838116610100908102610100600160a81b03198416179093556040519290910416907f0ace8cc48451c2115e4b583399a0d93e969dd6fff132ecfda7d73f7fb34726d7906105029083908590611b2c565b5f8888886106da89610c28565b888888886040516020016106f5989796959493929190611b46565b60405160208183030381529060405280519060200120905098975050505050505050565b6004545f9060ff161561073657610736636b3e1fe160e11b610c05565b61073f84610c8e565b61074e88888888888888610d19565b5f808061079761075e8980611b91565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610d5292505050565b919450925090505f6107b6826107b060608c018c611b91565b87610d96565b90505f806107fe8e8e6040516020016107d0929190611bd3565b60408051601f1981840301815291905280516020909101206005548e908e906001600160a01b031687610e2e565b9150915061080f858a8a8587610f8a565b6108538e8e8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508692508791508a905085611105565b509c9b505050505050505050505050565b61086d33610bab565b61088c573360405163fa1bd1d760e01b81526004016104a69190611941565b6001600160a01b0381166108aa576108aa6359a1934760e11b610c05565b600780546001600160a01b038381166001600160a01b03198316179092556040519116907ff3814cc4b6220f5bf02409e4cebe75e5032fdb20cef0db459431fd5cba574b93906105029083908590611b2c565b61090633610bf3565b610925573360405163cd40902d60e01b81526004016104a69190611941565b6001600160a01b03811661094357610943631efe047760e01b610c05565b600680546001600160a01b038381166001600160a01b03198316179092556040519116907fd3afd1676752459d3aa8b6f73633fac8efd724610da9c4cd8d8e4fa1631841fb906105029083908590611b2c565b61099f33610bf3565b6109be573360405163cd40902d60e01b81526004016104a69190611941565b6007805461ffff838116600160a01b90810261ffff60a01b1984161790935560408051939092041680835260208301849052917ff78e983a5bebb7320738ee1be825e0751accce1a4a79dd82e4e93171a2c20e309101610502565b610a2233610bab565b610a41573360405163fa1bd1d760e01b81526004016104a69190611941565b610a49611153565b565b610a5433610bab565b610a73573360405163fa1bd1d760e01b81526004016104a69190611941565b6001600160a01b038116610a9157610a91637d2c13fb60e11b610c05565b610a9c60028261118a565b507fcccafc3787229e84e106a9df64cb34531403b5a7934a4066bcb047daa3f4da38816040516105bc9190611941565b610ad533610bf3565b610af4573360405163cd40902d60e01b81526004016104a69190611941565b6001600160a01b038116610b1257610b12636dad5a6360e11b610c05565b600580546001600160a01b038381166001600160a01b03198316179092556040519116907f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae906105029083908590611b2c565b610b6e33610bf3565b610b8d573360405163cd40902d60e01b81526004016104a69190611941565b600754610ba7906001600160a01b0384811691168361119e565b5050565b6001546001600160a01b0390811691161490565b610bc833610bf3565b610be7573360405163cd40902d60e01b81526004016104a69190611941565b610bf0816111f5565b50565b5f610bff60028361125d565b92915050565b805f5260045ffd5b5f610c21836001600160a01b03841661127e565b9392505050565b5f610c338280611b91565b610c406020850185611b91565b610c4d6040870187611b91565b610c5a6060890189611b91565b604051602001610c71989796959493929190611be2565b604051602081830303815290604052805190602001209050919050565b610cdc6020820135610ca36040840184611b91565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061136192505050565b610cf057610cf0632f58c7cd60e01b610c05565b60075442600160a01b90910461ffff168235011015610bf057610bf0631870a45d60e21b610c05565b5f610d2b8888888888358888336106cd565b905083602001358114610d4857610d48631a9848b160e01b610c05565b5050505050505050565b610d5a6118eb565b5f8060608085806020019051810190610d739190611cda565b604080518082019091529384526020840192909252919891975095509350505050565b5f8415610e2357600454610db79061010090046001600160a01b03166113ca565b8251602084015160405163b8c1707360e01b81529293506001600160a01b0384169263b8c1707392610df192899289929190600401611dc0565b5f604051808303815f87803b158015610e08575f80fd5b505af1158015610e1a573d5f803e3d5ffd5b50505050610e26565b505f5b949350505050565b5f60605f6040518060a00160405280610e443390565b6001600160a01b0390811682527f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d811660208301527f000000000000000000000000afb979d9afad1ad27c5eff4e27226e3ab9e5dcc981166040830152306060830152861660809091015290505f81610ebd8880611b91565b610eca60208b018b611b91565b610ed760408d018d611b91565b610ee460608f018f611b91565b604051602001610efc99989796959493929190611df9565b60408051601f198184030181529082905263d76fad2360e01b825291506001600160a01b0387169063d76fad2390610f3c908c908c908690600401611e8f565b6020604051808303815f875af1158015610f58573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f7c9190611eb6565b999098509650505050505050565b848015610f9e57506001600160a01b038116155b1561103b57610fad8385611ee5565b341015610fc457610fc463569e8c1160e01b610c05565b6001600160a01b0382166393bf5705610fdd8534611ef8565b6040516001600160e01b031960e084901b168152600481018890525f6024820181905260448201526064015f604051808303818588803b15801561101f575f80fd5b505af1158015611031573d5f803e3d5ffd5b50505050506110f5565b6001600160a01b038116156110de57823410156110625761106263569e8c1160e01b610c05565b5f61106d8434611ef8565b90505f81156110795750335b6040516361b2761d60e11b81526001600160a01b0384169063c364ec3a9084906110a99088908690600401611b2c565b5f604051808303818588803b1580156110c0575f80fd5b505af11580156110d2573d5f803e3d5ffd5b505050505050506110f5565b8234146110f5576110f563569e8c1160e01b610c05565b6110fe836111f5565b5050505050565b8151602083015160405133927ff65d3a11fa2b7663a8d9ddb6a63494bf4b09240815636a0332849a3179a000c992611144928a928a928a928990611f0b565b60405180910390a25050505050565b6004805460ff191660011790556040517fa5f3a64ab759ee06c492853089ad840d35deaf0f93c21423227115491a8502a9905f90a1565b5f610c21836001600160a01b038416611461565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526111f09084906114ad565b505050565b6007546040515f916001600160a01b03169083908381818185875af1925050503d805f811461123f576040519150601f19603f3d011682016040523d82523d5f602084013e611244565b606091505b5050905080610ba757610ba76312171d8360e31b610c05565b6001600160a01b0381165f9081526001830160205260408120541515610c21565b5f8181526001830160205260408120548015611358575f6112a0600183611ef8565b85549091505f906112b390600190611ef8565b9050818114611312575f865f0182815481106112d1576112d1611d59565b905f5260205f200154905080875f0184815481106112f1576112f1611d59565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061132357611323611f7b565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610bff565b5f915050610bff565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018390525f908190605c0160408051601f198184030181529190528051602090910120600654909150610e26906001600160a01b0316828561152f565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c175f526e5af43d82803e903d91602b57fd5bf38260781b17602052603760095ff090506001600160a01b03811661145c5760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b60448201526064016104a6565b919050565b5f8181526001830160205260408120546114a657508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610bff565b505f610bff565b5f6114ed826040518060400160405280600981526020016818d85b1b0819985a5b60ba1b815250856001600160a01b031661158d9092919063ffffffff16565b905080515f1415801561151157508080602001905181019061150f9190611f8f565b155b156111f0576040516308575cad60e01b815260040160405180910390fd5b5f805f61153c858561159b565b90925090505f81600481111561155457611554611fa8565b1480156115725750856001600160a01b0316826001600160a01b0316145b8061158357506115838686866115dd565b9695505050505050565b6060610e2684845f856116c4565b5f8082516041036115cf576020830151604084015160608501515f1a6115c38782858561179b565b945094505050506115d6565b505f905060025b9250929050565b5f805f856001600160a01b0316631626ba7e60e01b8686604051602401611605929190611fbc565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516116439190611fd4565b5f60405180830381855afa9150503d805f811461167b576040519150601f19603f3d011682016040523d82523d5f602084013e611680565b606091505b509150915081801561169457506020815110155b801561158357508051630b135d3f60e11b906116b99083016020908101908401611fef565b149695505050505050565b6060824710156117255760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104a6565b5f80866001600160a01b031685876040516117409190611fd4565b5f6040518083038185875af1925050503d805f811461177a576040519150601f19603f3d011682016040523d82523d5f602084013e61177f565b606091505b50915091506117908783838761184e565b979650505050505050565b5f806fa2a8918ca85bafe22016d0b997e4df60600160ff1b038311156117c657505f90506003611845565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611817573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b03811661183f575f60019250925050611845565b91505f90505b94509492505050565b606083156118bc5782515f036118b5576001600160a01b0385163b6118b55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104a6565b5081610e26565b610e2683838151156118d15781518083602001fd5b8060405162461bcd60e51b81526004016104a69190612006565b60405180604001604052806002905b60608152602001906001900390816118fa5790505090565b6001600160a01b0381168114610bf0575f80fd5b5f60208284031215611936575f80fd5b8135610c2181611912565b6001600160a01b0391909116815260200190565b5f8083601f840112611965575f80fd5b5081356001600160401b0381111561197b575f80fd5b6020830191508360208285010111156115d6575f80fd5b5f608082840312156119a2575f80fd5b50919050565b5f805f805f805f8060e0898b0312156119bf575f80fd5b88356001600160401b03808211156119d5575f80fd5b6119e18c838d01611955565b909a50985060208b0135975060408b0135915080821115611a00575f80fd5b50611a0d8b828c01611992565b955050606089013593506080890135925060a0890135915060c0890135611a3381611912565b809150509295985092959890939650565b5f805f805f805f60c0888a031215611a5a575f80fd5b87356001600160401b0380821115611a70575f80fd5b611a7c8b838c01611955565b909950975060208a0135965060408a0135915080821115611a9b575f80fd5b611aa78b838c01611992565b955060608a0135915080821115611abc575f80fd5b5088016060818b031215611ace575f80fd5b969995985093969295946080840135945060a09093013592915050565b5f60208284031215611afb575f80fd5b5035919050565b5f8060408385031215611b13575f80fd5b8235611b1e81611912565b946020939093013593505050565b6001600160a01b0392831681529116602082015260400190565b878982379096019485526020850193909352604084019190915260608084019190915260808301919091529190911b6bffffffffffffffffffffffff191660a082015260b401919050565b5f808335601e19843603018112611ba6575f80fd5b8301803591506001600160401b03821115611bbf575f80fd5b6020019150368190038213156115d6575f80fd5b818382375f9101908152919050565b878982375f8882015f8152878982375f908801908152858782375f908601908152838582375f9301928352509098975050505050505050565b634e487b7160e01b5f52604160045260245ffd5b5f5b83811015611c49578181015183820152602001611c31565b50505f910152565b5f82601f830112611c60575f80fd5b81516001600160401b0380821115611c7a57611c7a611c1b565b604051601f8301601f19908116603f01168101908282118183101715611ca257611ca2611c1b565b81604052838152866020858801011115611cba575f80fd5b611583846020830160208901611c2f565b8051801515811461145c575f80fd5b5f805f8060808587031215611ced575f80fd5b84516001600160401b0380821115611d03575f80fd5b611d0f88838901611c51565b95506020870151915080821115611d24575f80fd5b50611d3187828801611c51565b935050611d4060408601611ccb565b9150611d4e60608601611ccb565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b5f8151808452611dac816020860160208601611c2f565b601f01601f19169290920160200192915050565b606081525f611dd3606083018688611d6d565b8281036020840152611de58186611d95565b905082810360408401526117908185611d95565b5f610120828c835b6005811015611e295781516001600160a01b0316835260209283019290910190600101611e01565b5050508060a0840152611e3f8184018b8d611d6d565b905082810360c0840152611e5481898b611d6d565b905082810360e0840152611e69818789611d6d565b9050828103610100840152611e7f818587611d6d565b9c9b505050505050505050505050565b838152826020820152606060408201525f611ead6060830184611d95565b95945050505050565b5f60208284031215611ec6575f80fd5b8151610c2181611912565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610bff57610bff611ed1565b81810381811115610bff57610bff611ed1565b60c081525f611f1d60c0830189611d95565b6001600160a01b038881166020850152871660408401528281036060840152611f468187611d95565b90508281036080840152611f5a8186611d95565b905082810360a0840152611f6e8185611d95565b9998505050505050505050565b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215611f9f575f80fd5b610c2182611ccb565b634e487b7160e01b5f52602160045260245ffd5b828152604060208201525f610e266040830184611d95565b5f8251611fe5818460208701611c2f565b9190910192915050565b5f60208284031215611fff575f80fd5b5051919050565b602081525f610c216020830184611d9556fea2646970667358221220420dc2d2effe049e06a7d42f5e4bf0c39d9278357d77165e80911108c1b0a25864736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada5600000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000de3ff2a50bd1ba1bd6a608ea0138946dddf595bc000000000000000000000000abcc7736c42713cd40ae8c2915c3b3dcdef6b83d0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d000000000000000000000000afb979d9afad1ad27c5eff4e27226e3ab9e5dcc900000000000000000000000081e0f16506de3042024021abda3e4de4fcf052d10000000000000000000000000000000000000000000000000000000000000004000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56000000000000000000000000bf52436dbb09c97a301be66930392f61ee61c7b8000000000000000000000000e79f7a6af8d3bc37d30a172b2c9d2fd003af032b000000000000000000000000c0eba66b055aed2cc8e9e38f04e64501950cb470
-----Decoded View---------------
Arg [0] : superAdmin_ (address): 0xbf9f7E7046b7a90A759cdc06CED11Be634AAda56
Arg [1] : platformAdmins_ (address[]): 0xbf9f7E7046b7a90A759cdc06CED11Be634AAda56,0xBf52436dBb09C97a301be66930392F61Ee61C7B8,0xe79f7a6AF8D3bc37d30A172B2C9D2fD003af032B,0xc0Eba66b055AEd2CC8e9E38F04e64501950Cb470
Arg [2] : platformTreasury_ (address): 0xDE3FF2A50bd1BA1bd6a608EA0138946Dddf595bc
Arg [3] : metadropOracleAddress_ (address): 0xabcC7736c42713cD40aE8c2915C3b3DcdeF6b83d
Arg [4] : uniswapRouter_ (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
Arg [5] : tokenVault_ (address): 0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9
Arg [6] : driPoolTemplate_ (address): 0x81E0f16506DE3042024021abdA3E4dE4fCf052d1
-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [2] : 000000000000000000000000de3ff2a50bd1ba1bd6a608ea0138946dddf595bc
Arg [3] : 000000000000000000000000abcc7736c42713cd40ae8c2915c3b3dcdef6b83d
Arg [4] : 0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d
Arg [5] : 000000000000000000000000afb979d9afad1ad27c5eff4e27226e3ab9e5dcc9
Arg [6] : 00000000000000000000000081e0f16506de3042024021abda3e4de4fcf052d1
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56
Arg [9] : 000000000000000000000000bf52436dbb09c97a301be66930392f61ee61c7b8
Arg [10] : 000000000000000000000000e79f7a6af8d3bc37d30a172b2c9d2fd003af032b
Arg [11] : 000000000000000000000000c0eba66b055aed2cc8e9e38f04e64501950cb470
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.