More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 658 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Withdraw ETH | 19391495 | 345 days ago | IN | 0 ETH | 0.00448838 | ||||
Create ERC20 | 19372351 | 348 days ago | IN | 0.7 ETH | 0.43905486 | ||||
Create ERC20 | 19372202 | 348 days ago | IN | 1.2 ETH | 0.49814077 | ||||
Create ERC20 | 19363955 | 349 days ago | IN | 0.2 ETH | 0.66539051 | ||||
Create ERC20 | 19357718 | 350 days ago | IN | 0.3 ETH | 0.33427458 | ||||
Create ERC20 | 19350219 | 351 days ago | IN | 0.2 ETH | 0.30245669 | ||||
Create ERC20 | 19343410 | 352 days ago | IN | 0.2 ETH | 0.35842562 | ||||
Create ERC20 | 19333645 | 353 days ago | IN | 1.2 ETH | 0.59697008 | ||||
Create ERC20 | 19330261 | 354 days ago | IN | 1.3 ETH | 0.32936884 | ||||
Create ERC20 | 19321038 | 355 days ago | IN | 1.3 ETH | 0.44307902 | ||||
Create ERC20 | 19275240 | 361 days ago | IN | 0.5 ETH | 0.21890665 | ||||
Create ERC20 | 19274270 | 361 days ago | IN | 0.4 ETH | 0.22734805 | ||||
Create ERC20 | 19255590 | 364 days ago | IN | 1.3 ETH | 0.25440024 | ||||
Create ERC20 | 19250209 | 365 days ago | IN | 1.3 ETH | 0.12608386 | ||||
Create ERC20 | 19242107 | 366 days ago | IN | 1.3 ETH | 0.25016313 | ||||
Create ERC20 | 19234837 | 367 days ago | IN | 1.3 ETH | 0.27902481 | ||||
Create ERC20 | 19222064 | 369 days ago | IN | 1.1 ETH | 0.18218552 | ||||
Create ERC20 | 19214341 | 370 days ago | IN | 1.3 ETH | 0.39416661 | ||||
Create ERC20 | 19213111 | 370 days ago | IN | 1.1 ETH | 0.26691095 | ||||
Create ERC20 | 19207923 | 371 days ago | IN | 0.3 ETH | 0.17856914 | ||||
Create ERC20 | 19206683 | 371 days ago | IN | 1.2 ETH | 0.20236374 | ||||
Create ERC20 | 19204956 | 371 days ago | IN | 1.3 ETH | 0.14038182 | ||||
Create ERC20 | 19199887 | 372 days ago | IN | 1.1 ETH | 0.27709796 | ||||
Create ERC20 | 19192004 | 373 days ago | IN | 1.3 ETH | 0.49843811 | ||||
Create ERC20 | 19186259 | 374 days ago | IN | 1.3 ETH | 0.56360129 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19391495 | 345 days ago | 8.6 ETH | ||||
19372351 | 348 days ago | 0.5 ETH | ||||
19372202 | 348 days ago | 1 ETH | ||||
19357718 | 350 days ago | 0.1 ETH | ||||
19333645 | 353 days ago | 1 ETH | ||||
19330261 | 354 days ago | 1.1 ETH | ||||
19321038 | 355 days ago | 1.1 ETH | ||||
19275240 | 361 days ago | 0.3 ETH | ||||
19274270 | 361 days ago | 0.2 ETH | ||||
19255590 | 364 days ago | 1.1 ETH | ||||
19250209 | 365 days ago | 1.1 ETH | ||||
19242107 | 366 days ago | 1.1 ETH | ||||
19234837 | 367 days ago | 1.1 ETH | ||||
19222064 | 369 days ago | 0.9 ETH | ||||
19214341 | 370 days ago | 1.1 ETH | ||||
19213111 | 370 days ago | 0.9 ETH | ||||
19207923 | 371 days ago | 0.1 ETH | ||||
19206683 | 371 days ago | 1 ETH | ||||
19204956 | 371 days ago | 1.1 ETH | ||||
19199887 | 372 days ago | 0.9 ETH | ||||
19192004 | 373 days ago | 1.1 ETH | ||||
19186259 | 374 days ago | 1.1 ETH | ||||
19186166 | 374 days ago | 1.1 ETH | ||||
19136062 | 381 days ago | 1.1 ETH | ||||
19114180 | 384 days ago | 1.1 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 200 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 {IERC20DRIPool} from "../ERC20Pools/IERC20DRIPool.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 { (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 returns (address driPoolAddress_) { if (usesDRIPool_) { // Create a minimal proxy DRIPool: driPoolAddress_ = driPoolTemplate.clone(); // Initialise it: IERC20DRIPool(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 in 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(); } IERC20DRIPool(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); } } } } /** * @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 {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 {IUniswapV2Locker} from "../../ThirdParty/Unicrypt/IUniswapV2Locker.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"; /** * @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_SWAP_THRESHOLD_MULTIPLE = 20; bytes32 internal constant GNOSIS_SAFE_PROXY = 0xb89c1b3bdf2cf8827818646bce9a8f6e372885f8c55e5c07acbd307cb133b000; uint256 public immutable lpSupply; uint256 public immutable projectSupply; uint256 public immutable botProtectionDurationInSeconds; address public immutable metadropTaxRecipient; address public immutable uniswapV2Pair; address public immutable driPool; address public immutable lpOwner; address public immutable projectSupplyRecipient; address public immutable metadropFactory; uint256 public immutable metadropTaxPeriodInDays; bool internal immutable _tokenHasTax; IUniswapV2Locker 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: * 128 * 32 * 16 * 5 * 8 * 2 * ------ * 256 * ------ */ uint128 private _totalSupply; uint32 public fundedDate; uint16 public projectBuyTaxBasisPoints; uint16 public projectSellTaxBasisPoints; uint16 public metadropBuyTaxBasisPoints; uint16 public metadropSellTaxBasisPoints; uint16 public swapThresholdBasisPoints; /** @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: * 128 * 128 * ------ * 256 * ------ */ uint128 public maxTokensPerTransaction; uint128 public maxTokensPerWallet; /** @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 {_validCallerCodeHashes} Code hashes of callers we consider valid */ EnumerableSet.Bytes32Set private _validCallerCodeHashes; /** @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, unicrypt vault, 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_ ) { _decodeBaseParams(integrationAddresses_[0], baseParams_); _uniswapRouter = IUniswapV2Router02(integrationAddresses_[1]); _tokenVault = IUniswapV2Locker(integrationAddresses_[2]); metadropFactory = (integrationAddresses_[3]); ERC20SupplyParameters memory supplyParams = abi.decode( supplyParams_, (ERC20SupplyParameters) ); ERC20TaxParameters memory taxParams = abi.decode( taxParams_, (ERC20TaxParameters) ); driPool = integrationAddresses_[4]; ERC20PoolParameters memory poolParams; if (integrationAddresses_[4] != address(0)) { poolParams = abi.decode(poolParams_, (ERC20PoolParameters)); } _processSupplyParams(supplyParams, poolParams); projectSupplyRecipient = supplyParams.projectSupplyRecipient; lpSupply = supplyParams.lpSupply * (10 ** decimals()); projectSupply = supplyParams.projectSupply * (10 ** decimals()); maxTokensPerWallet = uint128( supplyParams.maxTokensPerWallet * (10 ** decimals()) ); maxTokensPerTransaction = uint128( supplyParams.maxTokensPerTxn * (10 ** decimals()) ); lpLockupInDays = uint88(supplyParams.lpLockupInDays); botProtectionDurationInSeconds = supplyParams .botProtectionDurationInSeconds; lpOwner = supplyParams.projectLPOwner; burnLPTokens = supplyParams.burnLPTokens; _tokenHasTax = _processTaxParams(taxParams); metadropTaxPeriodInDays = taxParams.metadropTaxPeriodInDays; swapThresholdBasisPoints = uint16(taxParams.taxSwapThresholdBasisPoints); projectTaxRecipient = taxParams.projectTaxRecipient; metadropTaxRecipient = taxParams.metadropTaxRecipient; _mintBalances( lpSupply, projectSupply, 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); } _; } /** * @dev function {_decodeBaseParams} * * Decode NFT Parameters * * @param projectOwner_ The owner of this contract * @param encodedBaseParams_ The base params encoded into a bytes array */ function _decodeBaseParams( address projectOwner_, bytes memory encodedBaseParams_ ) internal { _transferOwnership(projectOwner_); (_name, _symbol) = abi.decode(encodedBaseParams_, (string, string)); } /** * @dev function {_processSupplyParams} * * Process provided supply params * * @param erc20SupplyParameters_ The supply params * @param erc20PoolParameters_ The pool params */ function _processSupplyParams( ERC20SupplyParameters memory erc20SupplyParameters_, ERC20PoolParameters memory erc20PoolParameters_ ) internal { if ( erc20SupplyParameters_.maxSupply != (erc20SupplyParameters_.lpSupply + erc20SupplyParameters_.projectSupply + erc20PoolParameters_.poolSupply) ) { _revert(SupplyTotalMismatch.selector); } if (erc20SupplyParameters_.maxSupply > type(uint128).max) { _revert(MaxSupplyTooHigh.selector); } if (erc20SupplyParameters_.lpLockupInDays > type(uint88).max) { _revert(LPLockUpMustFitUint88.selector); } _unlimited.add(erc20SupplyParameters_.projectSupplyRecipient); _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 { projectBuyTaxBasisPoints = uint16( erc20TaxParameters_.projectBuyTaxBasisPoints ); projectSellTaxBasisPoints = uint16( erc20TaxParameters_.projectSellTaxBasisPoints ); metadropBuyTaxBasisPoints = uint16( erc20TaxParameters_.metadropBuyTaxBasisPoints ); metadropSellTaxBasisPoints = uint16( erc20TaxParameters_.metadropSellTaxBasisPoints ); return true; } } /** * @dev function {_mintBalances} * * Mint initial balances * * @param lpMint_ The number of tokens for liquidity * @param projectMint_ The number of tokens for the project treasury * @param poolMint_ The number of tokens for the launch pool */ function _mintBalances( uint256 lpMint_, uint256 projectMint_, uint256 poolMint_ ) internal { if (lpMint_ > 0) { _mint(address(this), lpMint_); } if (projectMint_ > 0) { _mint(projectSupplyRecipient, projectMint_); } 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); // 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 { // 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 { IERC20(uniswapV2Pair).approve(address(_tokenVault), lpTokens_); _tokenVault.lockLPToken{value: vaultFee_}( uniswapV2Pair, IERC20(uniswapV2Pair).balanceOf(address(this)), block.timestamp + (lpLockupInDays * 1 days), payable(address(0)), true, payable(lpOwner) ); emit LiquidityLocked(lpTokens_, lpLockupInDays); } /** * @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 {isValidCaller} * * Return if an address is a valid caller * * @param queryHash_ The code hash being queried * @return bool The address is / isn't a valid caller */ function isValidCaller(bytes32 queryHash_) public view returns (bool) { return (_validCallerCodeHashes.contains(queryHash_)); } /** * @dev function {validCallers} * * Returns a list of all valid caller code hashes * * @return validCallerHashes_ a list of all valid caller code hashes */ function validCallers() external view returns (bytes32[] memory validCallerHashes_) { return (_validCallerCodeHashes.values()); } /** * @dev function {addValidCaller} onlyOwner * * Allows the owner to add the hash of a valid caller * * @param newValidCallerHash_ The hash of the new valid caller */ function addValidCaller(bytes32 newValidCallerHash_) external onlyOwner { _validCallerCodeHashes.add(newValidCallerHash_); emit ValidCallerAdded(newValidCallerHash_); } /** * @dev function {removeValidCaller} onlyOwner * * Allows the owner to remove a valid caller * * @param removedValidCallerHash_ The hash of the old removed valid caller */ function removeValidCaller( bytes32 removedValidCallerHash_ ) external onlyOwner { // Remove this from the enumerated list: _validCallerCodeHashes.remove(removedValidCallerHash_); emit ValidCallerRemoved(removedValidCallerHash_); } /** * @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 { 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_; // If either rate has been reduced to zero we set the metadrop tax rate // (if non zero) to zero as well: if ( newProjectBuyTaxBasisPoints_ == 0 || newProjectSellTaxBasisPoints_ == 0 ) { uint16 oldMetadropBuyTaxBasisPoints = metadropBuyTaxBasisPoints; uint16 oldMetadropSellTaxBasisPoints = metadropSellTaxBasisPoints; uint16 newMetadropBuyTaxBasisPoints = oldMetadropBuyTaxBasisPoints; uint16 newMetadropSellTaxBasisPoints = oldMetadropSellTaxBasisPoints; if (newProjectBuyTaxBasisPoints_ == 0) { newMetadropBuyTaxBasisPoints = 0; metadropBuyTaxBasisPoints = 0; } if (newProjectSellTaxBasisPoints_ == 0) { newMetadropSellTaxBasisPoints = 0; metadropSellTaxBasisPoints = 0; } emit MetadropTaxBasisPointsChanged( oldMetadropBuyTaxBasisPoints, newMetadropBuyTaxBasisPoints, oldMetadropSellTaxBasisPoints, newMetadropSellTaxBasisPoints ); } 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 { 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 = uint128(newMaxTokensPerTransaction_); maxTokensPerWallet = uint128(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 then 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); // Process taxes uint256 amountMinusTax = _taxProcessing(applyTax, to, from, amount); // Perform post-tax validation (e.g. total balance after post-tax amount applied) _posttaxValidationAndLimits(from, to, amountMinusTax); _balances[from] = fromBalance - amount; _balances[to] += amountMinusTax; emit Transfer(from, to, amountMinusTax); _afterTokenTransfer(from, to, amount); } /** * @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_; unchecked { 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 {_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 contractBalance = balanceOf(address(this)); uint256 swapBalance = contractBalance; uint256 swapThresholdInTokens = (_totalSupply * swapThresholdBasisPoints) / BP_DENOM; if (_eligibleForSwap(from_, to_, swapBalance, swapThresholdInTokens)) { // Store that a swap back is in progress: _autoSwapInProgress = true; // Check if we need to reduce the amount of tokens for this swap: if (swapBalance > swapThresholdInTokens * MAX_SWAP_THRESHOLD_MULTIPLE) { swapBalance = swapThresholdInTokens * MAX_SWAP_THRESHOLD_MULTIPLE; } // Perform the auto swap to native token: _swapTaxForNative(swapBalance, contractBalance); // 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)); } /** * @dev function {_swapTaxForNative} * * Swap tokens taken as tax for native token * * @param swapBalance_ The current accumulated tax balance to swap * @param contractBalance_ The current accumulated total tax balance */ function _swapTaxForNative( uint256 swapBalance_, uint256 contractBalance_ ) 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 totalPendingSwap = projectTaxPendingSwap + metadropTaxPendingSwap; uint256 projectBalanceToDistribute = (balanceToDistribute * projectTaxPendingSwap) / totalPendingSwap; uint256 metadropBalanceToDistribute = (balanceToDistribute * metadropTaxPendingSwap) / totalPendingSwap; // 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_ < contractBalance_) { projectTaxPendingSwap -= uint128( (projectTaxPendingSwap * swapBalance_) / contractBalance_ ); metadropTaxPendingSwap -= uint128( (metadropTaxPendingSwap * swapBalance_) / contractBalance_ ); } 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_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 { 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 {withdrawETH} onlyOwner * * A withdraw function to allow ETH to be withdrawn by the manager * * 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 withdrawETH(uint256 amount_) external onlyOwner { (bool success, ) = _msgSender().call{value: amount_}(""); if (!success) { _revert(TransferFailed.selector); } } /** * @dev function {withdrawERC20} onlyOwner * * A withdraw function to allow ERC20s (except address(this)) to be withdrawn. * * 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 withdrawERC20(address token_, uint256 amount_) external onlyOwner { if (token_ == address(this)) { _revert(CannotWithdrawThisToken.selector); } IERC20(token_).safeTransfer(_msgSender(), 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 += uint128(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 -= uint128(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); 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 {isValidCaller} * * Return if an address is a valid caller * * @param queryHash_ The code hash being queried * @return bool The address is / isn't a valid caller */ function isValidCaller(bytes32 queryHash_) external view returns (bool); /** * @dev function {validCallers} * * Returns a list of all valid caller code hashes * * @return validCallerHashes_ a list of all valid caller code hashes */ function validCallers() external view returns (bytes32[] memory validCallerHashes_); /** * @dev function {addValidCaller} onlyOwner * * Allows the owner to add the hash of a valid caller * * @param newValidCallerHash_ The hash of the new valid caller */ function addValidCaller(bytes32 newValidCallerHash_) external; /** * @dev function {removeValidCaller} onlyOwner * * Allows the owner to remove a valid caller * * @param removedValidCallerHash_ The hash of the old removed valid caller */ function removeValidCaller(bytes32 removedValidCallerHash_) 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 {withdrawETH} onlyOwner * * A withdraw function to allow ETH to be withdrawn by the manager * * 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 withdrawETH(uint256 amount_) external; /** * @dev function {withdrawERC20} onlyOwner * * A withdraw function to allow ERC20s (except address(this)) to be withdrawn. * * 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 withdrawERC20(address token_, 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 { struct ERC20Config { bytes baseParameters; bytes supplyParameters; bytes taxParameters; bytes poolParameters; } struct ERC20BaseParameters { string name; string symbol; bool addLiquidityOnCreate; bool usesDRIPool; } struct ERC20SupplyParameters { uint256 maxSupply; uint256 lpSupply; uint256 projectSupply; uint256 maxTokensPerWallet; uint256 maxTokensPerTxn; uint256 lpLockupInDays; uint256 botProtectionDurationInSeconds; address projectSupplyRecipient; address projectLPOwner; bool burnLPTokens; } struct ERC20TaxParameters { uint256 projectBuyTaxBasisPoints; uint256 projectSellTaxBasisPoints; uint256 taxSwapThresholdBasisPoints; uint256 metadropBuyTaxBasisPoints; uint256 metadropSellTaxBasisPoints; uint256 metadropTaxPeriodInDays; address projectTaxRecipient; address metadropTaxRecipient; } struct ERC20PoolParameters { uint256 poolSupply; uint256 poolStartDate; uint256 poolEndDate; uint256 poolVestingInDays; uint256 poolMaxETH; uint256 poolPerAddressMaxETH; uint256 poolMinETH; uint256 poolPerTransactionMinETH; } }
// 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, ERC20ByMetadrop} 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 {IERC20ConfigByMetadrop} from "../ERC20/IERC20ConfigByMetadrop.sol"; import {IErrors} from "../../Global/IErrors.sol"; interface IERC20DRIPool is IERC20ConfigByMetadrop, IErrors { enum PhaseStatus { beforePoolPhase, duringPoolPhase, afterPoolPhase } event AddToPool(address dripHolder, uint256 dripTokenMinted); event ClaimFromPool( address dripHolder, uint256 dripTokenBurned, uint256 pooledTokenClaimed ); event RefundFromPool( address dripHolder, uint256 dripTokenBurned, uint256 ethRefunded ); event LiquidityAddedFromPool(uint256 ethTotal, uint256 tokenTotal); /** * @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 {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 {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. */ function addToPool() external payable; /** * @dev {claimFromPool} * * A user calls this to burn their DRIP and claim their ERC-20 tokens * */ function claimFromPool() external; /** * @dev {refundFromPool} * * A user calls this to burn their DRIP and claim an ETH refund where the * minimum ETH pooled amount was not exceeded * */ function refundFromPool() 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. * * @param lockerFee_ The ETH fee required to lock LP tokens * */ function supplyLiquidity(uint256 lockerFee_) external payable; }
// 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 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 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 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 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 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 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 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 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 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 ParamTooLargePoolPerTxnMinETH(); // 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 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 PoolPhaseIsClosed(); // The block.timestamp is either before the pool is open or after it is closed. error PoolPhaseIsNotAfter(); // The block.timestamp is either before or during the pool open phase. 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 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 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 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 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)). }
// 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) } } }
// Interface definition for UniswapV2Locker.sol pragma solidity 0.8.21; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; interface IERCBurn { function burn(uint256 _amount) external; function approve(address spender, uint256 amount) external returns (bool); function allowance(address owner, address spender) external returns (uint256); function balanceOf(address account) external view returns (uint256); } interface IMigrator { function migrate( address lpToken, uint256 amount, uint256 unlockDate, address owner ) external returns (bool); } interface IUniswapV2Locker { struct UserInfo { EnumerableSet.AddressSet lockedTokens; // records all tokens the user has locked mapping(address => uint256[]) locksForToken; // map erc20 address to lock id for that token } struct TokenLock { uint256 lockDate; // the date the token was locked uint256 amount; // the amount of tokens still locked (initialAmount minus withdrawls) uint256 initialAmount; // the initial lock amount uint256 unlockDate; // the date the token can be withdrawn uint256 lockID; // lockID nonce per uni pair address owner; } struct FeeStruct { uint256 ethFee; // Small eth fee to prevent spam on the platform IERCBurn secondaryFeeToken; // UNCX or UNCL uint256 secondaryTokenFee; // optional, UNCX or UNCL uint256 secondaryTokenDiscount; // discount on liquidity fee for burning secondaryToken uint256 liquidityFee; // fee on univ2 liquidity tokens uint256 referralPercent; // fee for referrals IERCBurn referralToken; // token the refferer must hold to qualify as a referrer uint256 referralHold; // balance the referrer must hold to qualify as a referrer uint256 referralDiscount; // discount on flatrate fees for using a valid referral address } function setDev(address payable _devaddr) external; /** * @notice set the migrator contract which allows locked lp tokens to be migrated to uniswap v3 */ function setMigrator(IMigrator _migrator) external; function setSecondaryFeeToken(address _secondaryFeeToken) external; /** * @notice referrers need to hold the specified token and hold amount to be elegible for referral fees */ function setReferralTokenAndHold( IERCBurn _referralToken, uint256 _hold ) external; function setFees( uint256 _referralPercent, uint256 _referralDiscount, uint256 _ethFee, uint256 _secondaryTokenFee, uint256 _secondaryTokenDiscount, uint256 _liquidityFee ) external; /** * @notice whitelisted accounts dont pay flatrate fees on locking */ function whitelistFeeAccount(address _user, bool _add) external; /** * @notice Creates a new lock * @param _lpToken the univ2 token address * @param _amount amount of LP tokens to lock * @param _unlock_date the unix timestamp (in seconds) until unlock * @param _referral the referrer address if any or address(0) for none * @param _fee_in_eth fees can be paid in eth or in a secondary token such as UNCX with a discount on univ2 tokens * @param _withdrawer the user who can withdraw liquidity once the lock expires. */ function lockLPToken( address _lpToken, uint256 _amount, uint256 _unlock_date, address payable _referral, bool _fee_in_eth, address payable _withdrawer ) external payable; /** * @notice extend a lock with a new unlock date, _index and _lockID ensure the correct lock is changed * this prevents errors when a user performs multiple tx per block possibly with varying gas prices */ function relock( address _lpToken, uint256 _index, uint256 _lockID, uint256 _unlock_date ) external; /** * @notice withdraw a specified amount from a lock. _index and _lockID ensure the correct lock is changed * this prevents errors when a user performs multiple tx per block possibly with varying gas prices */ function withdraw( address _lpToken, uint256 _index, uint256 _lockID, uint256 _amount ) external; /** * @notice increase the amount of tokens per a specific lock, this is preferable to creating a new lock, less fees, and faster loading on our live block explorer */ function incrementLock( address _lpToken, uint256 _index, uint256 _lockID, uint256 _amount ) external; /** * @notice split a lock into two seperate locks, useful when a lock is about to expire and youd like to relock a portion * and withdraw a smaller portion */ function splitLock( address _lpToken, uint256 _index, uint256 _lockID, uint256 _amount ) external payable; /** * @notice transfer a lock to a new owner, e.g. presale project -> project owner */ function transferLockOwnership( address _lpToken, uint256 _index, uint256 _lockID, address payable _newOwner ) external; /** * @notice migrates liquidity to uniswap v3 */ function migrate( address _lpToken, uint256 _index, uint256 _lockID, uint256 _amount ) external; function getNumLocksForToken( address _lpToken ) external view returns (uint256); function getNumLockedTokens() external view returns (uint256); function getLockedTokenAtIndex( uint256 _index ) external view returns (address); // user functions function getUserNumLockedTokens( address _user ) external view returns (uint256); function getUserLockedTokenAtIndex( address _user, uint256 _index ) external view returns (address); function getUserNumLocksForToken( address _user, address _lpToken ) external view returns (uint256); function getUserLockForTokenAtIndex( address _user, address _lpToken, uint256 _index ) external view returns (uint256, uint256, uint256, uint256, uint256, address); // whitelist function getWhitelistedUsersLength() external view returns (uint256); function getWhitelistedUserAtIndex( uint256 _index ) external view returns (address); function getUserWhitelistStatus(address _user) external view returns (bool); }
// 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; }
{ "optimizer": { "enabled": true, "runs": 200 }, "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":"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":"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":"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":"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":"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":"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":[{"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":"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":"ParamTooLargeEndDate","type":"error"},{"inputs":[],"name":"ParamTooLargeMinETH","type":"error"},{"inputs":[],"name":"ParamTooLargePerAddressMax","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":"PoolIsAboveMinimum","type":"error"},{"inputs":[],"name":"PoolIsBelowMinimum","type":"error"},{"inputs":[],"name":"PoolPhaseIsClosed","type":"error"},{"inputs":[],"name":"PoolPhaseIsNotAfter","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":"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":"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":"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":"UnrecognisedVRFMode","type":"error"},{"inputs":[],"name":"VRFCoordinatorCannotBeAddressZero","type":"error"},{"inputs":[],"name":"ValueExceedsMaximum","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
60e06040526007805461ffff60a01b191660e160a31b17905534801562000024575f80fd5b506040516200278c3803806200278c83398101604081905262000047916200055e565b3360c0526001600160a01b0387166200006c576200006c63905c833360e01b62000205565b600180546001600160a01b031916339081179091556200008c906200020d565b600162000099886200020d565b5f5b87518110156200011a57336001600160a01b0316888281518110620000c457620000c46200069c565b60200260200101516001600160a01b031603620000e4575f915062000111565b62000111888281518110620000fd57620000fd6200069c565b60200260200101516200020d60201b60201c565b6001016200009b565b506001600160a01b0386166200013c576200013c6359a1934760e11b62000205565b600780546001600160a01b0319166001600160a01b03888116919091179091558516620001755762000175631efe047760e01b62000205565b600680546001600160a01b038088166001600160a01b03199283161790925585821660805284821660a0526004805492851661010002610100600160a81b0319909316929092179091555f8054909116301790558015620001db57620001db33620002af565b6001600160a01b0388163314620001f757620001f78862000326565b5050505050505050620006e4565b805f5260045ffd5b6001546001600160a01b03163314620002405760405163fa1bd1d760e01b81523360048201526024015b60405180910390fd5b6001600160a01b038116620002615762000261637d2c13fb60e11b62000205565b6200026e600282620003b6565b506040516001600160a01b03821681527fcccafc3787229e84e106a9df64cb34531403b5a7934a4066bcb047daa3f4da38906020015b60405180910390a150565b6001546001600160a01b03163314620002de5760405163fa1bd1d760e01b815233600482015260240162000237565b620002eb600282620003d5565b506040516001600160a01b03821681527fe46b802d853473508f897bd3184086171ec26a81681dc34e23913f7ef03c9a0c90602001620002a4565b6001546001600160a01b03163314620003555760405163fa1bd1d760e01b815233600482015260240162000237565b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f0f62530a074f4e1e883a8c916fa7f8639d52598edb7f9b5aa3148d991db5610d910160405180910390a15050565b5f620003cc836001600160a01b038416620003eb565b90505b92915050565b5f620003cc836001600160a01b0384166200043a565b5f8181526001830160205260408120546200043257508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155620003cf565b505f620003cf565b5f818152600183016020526040812054801562000524575f6200045f600183620006b0565b85549091505f906200047490600190620006b0565b9050818114620004da575f865f0182815481106200049657620004966200069c565b905f5260205f200154905080875f018481548110620004b957620004b96200069c565b5f918252602080832090910192909255918252600188019052604090208390555b8554869080620004ee57620004ee620006d0565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050620003cf565b5f915050620003cf565b80516001600160a01b038116811462000545575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f805f805f805f60e0888a03121562000575575f80fd5b62000580886200052e565b602089810151919850906001600160401b03808211156200059f575f80fd5b818b0191508b601f830112620005b3575f80fd5b815181811115620005c857620005c86200054a565b8060051b604051601f19603f83011681018181108582111715620005f057620005f06200054a565b60405291825284820192508381018501918e8311156200060e575f80fd5b938501935b82851015620006375762000627856200052e565b8452938501939285019262000613565b809b505050505050506200064e604089016200052e565b94506200065e606089016200052e565b93506200066e608089016200052e565b92506200067e60a089016200052e565b91506200068e60c089016200052e565b905092959891949750929550565b634e487b7160e01b5f52603260045260245ffd5b81810381811115620003cf57634e487b7160e01b5f52601160045260245ffd5b634e487b7160e01b5f52603160045260245ffd5b60805160a05160c05161206f6200071d5f395f61052b01525f818161023a0152610f2801525f81816102cc0152610f00015261206f5ff3fe608060405260043610610147575f3560e01c80637d135c41116100b3578063ba93c21b1161006d578063ba93c21b146103bc578063c45a0155146103db578063df7f453b146103f9578063e138818c14610437578063f14210a614610456578063f918385414610475575f80fd5b80637d135c411461030d578063882f73561461032c57806388d343571461034b5780638a43b74c1461035f578063942a26ba1461037e578063a1db97821461039d575f80fd5b80635bc789d9116101045780635bc789d9146102295780635f80cba61461025c57806369565a851461027b5780636e3fa5d1146102a8578063735de9f7146102bb5780637cd86d60146102ee575f80fd5b80630c6dd9fd1461014b5780631e4bad871461016c57806329575f6a146101a85780634c33002a146101c75780634d8ee090146101eb57806354e7ff1d1461020a575b5f80fd5b348015610156575f80fd5b5061016a61016536600461196f565b610494565b005b348015610177575f80fd5b5060065461018b906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101b3575f80fd5b5060015461018b906001600160a01b031681565b3480156101d2575f80fd5b5060045461018b9061010090046001600160a01b031681565b3480156101f6575f80fd5b5061016a61020536600461196f565b610528565b348015610215575f80fd5b5061016a61022436600461196f565b6105e5565b348015610234575f80fd5b5061018b7f000000000000000000000000000000000000000000000000000000000000000081565b348015610267575f80fd5b5061016a61027636600461196f565b610657565b348015610286575f80fd5b5061029a6102953660046119de565b610701565b60405190815260200161019f565b61018b6102b6366004611a7b565b61074d565b3480156102c6575f80fd5b5061018b7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102f9575f80fd5b5061016a61030836600461196f565b610898565b348015610318575f80fd5b5061016a61032736600461196f565b61093d565b348015610337575f80fd5b5061016a610346366004611b23565b6109dd565b348015610356575f80fd5b5061016a610a60565b34801561036a575f80fd5b5061016a61037936600461196f565b610a97565b348015610389575f80fd5b5061016a61039836600461196f565b610b27565b3480156103a8575f80fd5b5061016a6103b7366004611b3a565b610bc7565b3480156103c7575f80fd5b5060055461018b906001600160a01b031681565b3480156103e6575f80fd5b505f5461018b906001600160a01b031681565b348015610404575f80fd5b5061042761041336600461196f565b6001546001600160a01b0390811691161490565b604051901515815260200161019f565b348015610442575f80fd5b5060075461018b906001600160a01b031681565b348015610461575f80fd5b5061016a610470366004611b23565b610c0d565b348015610480575f80fd5b5061042761048f36600461196f565b610c9d565b6001546001600160a01b031633146104c65760405163fa1bd1d760e01b81523360048201526024015b60405180910390fd5b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f0f62530a074f4e1e883a8c916fa7f8639d52598edb7f9b5aa3148d991db5610d91015b60405180910390a15050565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146105685761056863120a1f9760e21b610caf565b6005546001600160a01b03161561058957610589637b1616c160e11b610caf565b600580546001600160a01b0319166001600160a01b038316908117909155604080515f815260208101929092527f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae91015b60405180910390a150565b6001546001600160a01b031633146106125760405163fa1bd1d760e01b81523360048201526024016104bd565b61061d600282610cb7565b506040516001600160a01b03821681527fe46b802d853473508f897bd3184086171ec26a81681dc34e23913f7ef03c9a0c906020016105da565b61066033610c9d565b61067f5760405163cd40902d60e01b81523360048201526024016104bd565b6001600160a01b03811661069d5761069d630ba68dbb60e41b610caf565b600480546001600160a01b03838116610100818102610100600160a81b031985161790945560408051949093049091168084526020840191909152917f0ace8cc48451c2115e4b583399a0d93e969dd6fff132ecfda7d73f7fb34726d7910161051c565b5f88888861070e89610cd2565b88888888604051602001610729989796959493929190611b64565b60405160208183030381529060405280519060200120905098975050505050505050565b6004545f9060ff161561076a5761076a636b3e1fe160e11b610caf565b61077384610d38565b61078288888888888888610dc6565b5f80806107cb6107928980611baf565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610dff92505050565b919450925090505f6107ea826107e460608c018c611baf565b87610e43565b90505f806108328e8e604051602001610804929190611bf2565b60408051601f1981840301815291905280516020909101206005548e908e906001600160a01b031687610edb565b91509150610843858a8a8587611037565b6108878e8e8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508692508791508a9050856111ac565b509c9b505050505050505050505050565b6001546001600160a01b031633146108c55760405163fa1bd1d760e01b81523360048201526024016104bd565b6001600160a01b0381166108e3576108e36359a1934760e11b610caf565b600780546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527ff3814cc4b6220f5bf02409e4cebe75e5032fdb20cef0db459431fd5cba574b93910161051c565b61094633610c9d565b6109655760405163cd40902d60e01b81523360048201526024016104bd565b6001600160a01b03811661098357610983631efe047760e01b610caf565b600680546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527fd3afd1676752459d3aa8b6f73633fac8efd724610da9c4cd8d8e4fa1631841fb910161051c565b6109e633610c9d565b610a055760405163cd40902d60e01b81523360048201526024016104bd565b6007805461ffff838116600160a01b90810261ffff60a01b1984161790935560408051939092041680835260208301849052917ff78e983a5bebb7320738ee1be825e0751accce1a4a79dd82e4e93171a2c20e30910161051c565b6001546001600160a01b03163314610a8d5760405163fa1bd1d760e01b81523360048201526024016104bd565b610a956111fa565b565b6001546001600160a01b03163314610ac45760405163fa1bd1d760e01b81523360048201526024016104bd565b6001600160a01b038116610ae257610ae2637d2c13fb60e11b610caf565b610aed600282611231565b506040516001600160a01b03821681527fcccafc3787229e84e106a9df64cb34531403b5a7934a4066bcb047daa3f4da38906020016105da565b610b3033610c9d565b610b4f5760405163cd40902d60e01b81523360048201526024016104bd565b6001600160a01b038116610b6d57610b6d636dad5a6360e11b610caf565b600580546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae910161051c565b610bd033610c9d565b610bef5760405163cd40902d60e01b81523360048201526024016104bd565b600754610c09906001600160a01b03848116911683611245565b5050565b610c1633610c9d565b610c355760405163cd40902d60e01b81523360048201526024016104bd565b6007546040515f916001600160a01b03169083908381818185875af1925050503d805f8114610c7f576040519150601f19603f3d011682016040523d82523d5f602084013e610c84565b606091505b5050905080610c0957610c096312171d8360e31b610caf565b5f610ca960028361129c565b92915050565b805f5260045ffd5b5f610ccb836001600160a01b0384166112bd565b9392505050565b5f610cdd8280611baf565b610cea6020850185611baf565b610cf76040870187611baf565b610d046060890189611baf565b604051602001610d1b989796959493929190611c01565b604051602081830303815290604052805190602001209050919050565b610d866020820135610d4d6040840184611baf565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506113a092505050565b610d9a57610d9a632f58c7cd60e01b610caf565b60075442600160a01b90910461ffff168235011015610dc357610dc3631870a45d60e21b610caf565b50565b5f610dd8888888888835888833610701565b905083602001358114610df557610df5631a9848b160e01b610caf565b5050505050505050565b610e07611934565b5f8060608085806020019051810190610e209190611cfa565b604080518082019091529384526020840192909252919891975095509350505050565b5f8415610ed057600454610e649061010090046001600160a01b0316611409565b8251602084015160405163b8c1707360e01b81529293506001600160a01b0384169263b8c1707392610e9e92899289929190600401611de1565b5f604051808303815f87803b158015610eb5575f80fd5b505af1158015610ec7573d5f803e3d5ffd5b50505050610ed3565b505f5b949350505050565b5f60605f6040518060a00160405280610ef13390565b6001600160a01b0390811682527f0000000000000000000000000000000000000000000000000000000000000000811660208301527f000000000000000000000000000000000000000000000000000000000000000081166040830152306060830152861660809091015290505f81610f6a8880611baf565b610f7760208b018b611baf565b610f8460408d018d611baf565b610f9160608f018f611baf565b604051602001610fa999989796959493929190611e1a565b60408051601f198184030181529082905263d76fad2360e01b825291506001600160a01b0387169063d76fad2390610fe9908c908c908690600401611eb0565b6020604051808303815f875af1158015611005573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110299190611ed7565b999098509650505050505050565b84801561104b57506001600160a01b038116155b156110e85761105a8385611f06565b3410156110715761107163569e8c1160e01b610caf565b6001600160a01b0382166393bf570561108a8534611f19565b6040516001600160e01b031960e084901b168152600481018890525f6024820181905260448201526064015f604051808303818588803b1580156110cc575f80fd5b505af11580156110de573d5f803e3d5ffd5b50505050506111a5565b6001600160a01b0381161561118e578234101561110f5761110f63569e8c1160e01b610caf565b5f61111a8434611f19565b90505f81156111265750335b6040516361b2761d60e11b81526001600160a01b038581166004830152828116602483015284169063c364ec3a9084906044015f604051808303818588803b158015611170575f80fd5b505af1158015611182573d5f803e3d5ffd5b505050505050506111a5565b8234146111a5576111a563569e8c1160e01b610caf565b5050505050565b8151602083015160405133927ff65d3a11fa2b7663a8d9ddb6a63494bf4b09240815636a0332849a3179a000c9926111eb928a928a928a928990611f2c565b60405180910390a25050505050565b6004805460ff191660011790556040517fa5f3a64ab759ee06c492853089ad840d35deaf0f93c21423227115491a8502a9905f90a1565b5f610ccb836001600160a01b0384166114a0565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526112979084906114ec565b505050565b6001600160a01b0381165f9081526001830160205260408120541515610ccb565b5f8181526001830160205260408120548015611397575f6112df600183611f19565b85549091505f906112f290600190611f19565b9050818114611351575f865f01828154811061131057611310611d7a565b905f5260205f200154905080875f01848154811061133057611330611d7a565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061136257611362611f9c565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610ca9565b5f915050610ca9565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018390525f908190605c0160408051601f198184030181529190528051602090910120600654909150610ed3906001600160a01b0316828561156e565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c175f526e5af43d82803e903d91602b57fd5bf38260781b17602052603760095ff090506001600160a01b03811661149b5760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b60448201526064016104bd565b919050565b5f8181526001830160205260408120546114e557508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610ca9565b505f610ca9565b5f61152c826040518060400160405280600981526020016818d85b1b0819985a5b60ba1b815250856001600160a01b03166115cc9092919063ffffffff16565b905080515f1415801561155057508080602001905181019061154e9190611fb0565b155b15611297576040516308575cad60e01b815260040160405180910390fd5b5f805f61157b85856115da565b90925090505f81600481111561159357611593611fc9565b1480156115b15750856001600160a01b0316826001600160a01b0316145b806115c257506115c286868661161c565b9695505050505050565b6060610ed384845f85611703565b5f80825160410361160e576020830151604084015160608501515f1a611602878285856117da565b94509450505050611615565b505f905060025b9250929050565b5f805f856001600160a01b0316631626ba7e60e01b8686604051602401611644929190611fdd565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516116829190611ff5565b5f60405180830381855afa9150503d805f81146116ba576040519150601f19603f3d011682016040523d82523d5f602084013e6116bf565b606091505b50915091508180156116d357506020815110155b80156115c257508051630b135d3f60e11b906116f89083016020908101908401612010565b149695505050505050565b6060824710156117645760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104bd565b5f80866001600160a01b0316858760405161177f9190611ff5565b5f6040518083038185875af1925050503d805f81146117b9576040519150601f19603f3d011682016040523d82523d5f602084013e6117be565b606091505b50915091506117cf87838387611897565b979650505050505050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561180f57505f9050600361188e565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611860573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116611888575f6001925092505061188e565b91505f90505b94509492505050565b606083156119055782515f036118fe576001600160a01b0385163b6118fe5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104bd565b5081610ed3565b610ed3838381511561191a5781518083602001fd5b8060405162461bcd60e51b81526004016104bd9190612027565b60405180604001604052806002905b60608152602001906001900390816119435790505090565b6001600160a01b0381168114610dc3575f80fd5b5f6020828403121561197f575f80fd5b8135610ccb8161195b565b5f8083601f84011261199a575f80fd5b50813567ffffffffffffffff8111156119b1575f80fd5b602083019150836020828501011115611615575f80fd5b5f608082840312156119d8575f80fd5b50919050565b5f805f805f805f8060e0898b0312156119f5575f80fd5b883567ffffffffffffffff80821115611a0c575f80fd5b611a188c838d0161198a565b909a50985060208b0135975060408b0135915080821115611a37575f80fd5b50611a448b828c016119c8565b955050606089013593506080890135925060a0890135915060c0890135611a6a8161195b565b809150509295985092959890939650565b5f805f805f805f60c0888a031215611a91575f80fd5b873567ffffffffffffffff80821115611aa8575f80fd5b611ab48b838c0161198a565b909950975060208a0135965060408a0135915080821115611ad3575f80fd5b611adf8b838c016119c8565b955060608a0135915080821115611af4575f80fd5b5088016060818b031215611b06575f80fd5b969995985093969295946080840135945060a09093013592915050565b5f60208284031215611b33575f80fd5b5035919050565b5f8060408385031215611b4b575f80fd5b8235611b568161195b565b946020939093013593505050565b878982379096019485526020850193909352604084019190915260608084019190915260808301919091529190911b6bffffffffffffffffffffffff191660a082015260b401919050565b5f808335601e19843603018112611bc4575f80fd5b83018035915067ffffffffffffffff821115611bde575f80fd5b602001915036819003821315611615575f80fd5b818382375f9101908152919050565b878982375f8882015f8152878982375f908801908152858782375f908601908152838582375f9301928352509098975050505050505050565b634e487b7160e01b5f52604160045260245ffd5b5f5b83811015611c68578181015183820152602001611c50565b50505f910152565b5f82601f830112611c7f575f80fd5b815167ffffffffffffffff80821115611c9a57611c9a611c3a565b604051601f8301601f19908116603f01168101908282118183101715611cc257611cc2611c3a565b81604052838152866020858801011115611cda575f80fd5b6115c2846020830160208901611c4e565b8051801515811461149b575f80fd5b5f805f8060808587031215611d0d575f80fd5b845167ffffffffffffffff80821115611d24575f80fd5b611d3088838901611c70565b95506020870151915080821115611d45575f80fd5b50611d5287828801611c70565b935050611d6160408601611ceb565b9150611d6f60608601611ceb565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b5f8151808452611dcd816020860160208601611c4e565b601f01601f19169290920160200192915050565b606081525f611df4606083018688611d8e565b8281036020840152611e068186611db6565b905082810360408401526117cf8185611db6565b5f610120828c835b6005811015611e4a5781516001600160a01b0316835260209283019290910190600101611e22565b5050508060a0840152611e608184018b8d611d8e565b905082810360c0840152611e7581898b611d8e565b905082810360e0840152611e8a818789611d8e565b9050828103610100840152611ea0818587611d8e565b9c9b505050505050505050505050565b838152826020820152606060408201525f611ece6060830184611db6565b95945050505050565b5f60208284031215611ee7575f80fd5b8151610ccb8161195b565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610ca957610ca9611ef2565b81810381811115610ca957610ca9611ef2565b60c081525f611f3e60c0830189611db6565b6001600160a01b038881166020850152871660408401528281036060840152611f678187611db6565b90508281036080840152611f7b8186611db6565b905082810360a0840152611f8f8185611db6565b9998505050505050505050565b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215611fc0575f80fd5b610ccb82611ceb565b634e487b7160e01b5f52602160045260245ffd5b828152604060208201525f610ed36040830184611db6565b5f8251612006818460208701611c4e565b9190910192915050565b5f60208284031215612020575f80fd5b5051919050565b602081525f610ccb6020830184611db656fea264697066735822122075fc7f6d14c4611f168a201b527d099e26c65a940e4773698a50cc5b1ecfd43564736f6c63430008150033000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada5600000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56000000000000000000000000abcc7736c42713cd40ae8c2915c3b3dcdef6b83d0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d000000000000000000000000663a5c229c09b049e36dcc11a9b0d4a8eb9db214000000000000000000000000b38d53a9338f3778b5be6765fdea46f6ff8501cc0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56000000000000000000000000bf52436dbb09c97a301be66930392f61ee61c7b8000000000000000000000000e79f7a6af8d3bc37d30a172b2c9d2fd003af032b000000000000000000000000c0eba66b055aed2cc8e9e38f04e64501950cb470
Deployed Bytecode
0x608060405260043610610147575f3560e01c80637d135c41116100b3578063ba93c21b1161006d578063ba93c21b146103bc578063c45a0155146103db578063df7f453b146103f9578063e138818c14610437578063f14210a614610456578063f918385414610475575f80fd5b80637d135c411461030d578063882f73561461032c57806388d343571461034b5780638a43b74c1461035f578063942a26ba1461037e578063a1db97821461039d575f80fd5b80635bc789d9116101045780635bc789d9146102295780635f80cba61461025c57806369565a851461027b5780636e3fa5d1146102a8578063735de9f7146102bb5780637cd86d60146102ee575f80fd5b80630c6dd9fd1461014b5780631e4bad871461016c57806329575f6a146101a85780634c33002a146101c75780634d8ee090146101eb57806354e7ff1d1461020a575b5f80fd5b348015610156575f80fd5b5061016a61016536600461196f565b610494565b005b348015610177575f80fd5b5060065461018b906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101b3575f80fd5b5060015461018b906001600160a01b031681565b3480156101d2575f80fd5b5060045461018b9061010090046001600160a01b031681565b3480156101f6575f80fd5b5061016a61020536600461196f565b610528565b348015610215575f80fd5b5061016a61022436600461196f565b6105e5565b348015610234575f80fd5b5061018b7f000000000000000000000000663a5c229c09b049e36dcc11a9b0d4a8eb9db21481565b348015610267575f80fd5b5061016a61027636600461196f565b610657565b348015610286575f80fd5b5061029a6102953660046119de565b610701565b60405190815260200161019f565b61018b6102b6366004611a7b565b61074d565b3480156102c6575f80fd5b5061018b7f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d81565b3480156102f9575f80fd5b5061016a61030836600461196f565b610898565b348015610318575f80fd5b5061016a61032736600461196f565b61093d565b348015610337575f80fd5b5061016a610346366004611b23565b6109dd565b348015610356575f80fd5b5061016a610a60565b34801561036a575f80fd5b5061016a61037936600461196f565b610a97565b348015610389575f80fd5b5061016a61039836600461196f565b610b27565b3480156103a8575f80fd5b5061016a6103b7366004611b3a565b610bc7565b3480156103c7575f80fd5b5060055461018b906001600160a01b031681565b3480156103e6575f80fd5b505f5461018b906001600160a01b031681565b348015610404575f80fd5b5061042761041336600461196f565b6001546001600160a01b0390811691161490565b604051901515815260200161019f565b348015610442575f80fd5b5060075461018b906001600160a01b031681565b348015610461575f80fd5b5061016a610470366004611b23565b610c0d565b348015610480575f80fd5b5061042761048f36600461196f565b610c9d565b6001546001600160a01b031633146104c65760405163fa1bd1d760e01b81523360048201526024015b60405180910390fd5b600180546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f0f62530a074f4e1e883a8c916fa7f8639d52598edb7f9b5aa3148d991db5610d91015b60405180910390a15050565b337f000000000000000000000000d0aaa52a08883ac6c960314831bfbadf8f035dbe6001600160a01b0316146105685761056863120a1f9760e21b610caf565b6005546001600160a01b03161561058957610589637b1616c160e11b610caf565b600580546001600160a01b0319166001600160a01b038316908117909155604080515f815260208101929092527f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae91015b60405180910390a150565b6001546001600160a01b031633146106125760405163fa1bd1d760e01b81523360048201526024016104bd565b61061d600282610cb7565b506040516001600160a01b03821681527fe46b802d853473508f897bd3184086171ec26a81681dc34e23913f7ef03c9a0c906020016105da565b61066033610c9d565b61067f5760405163cd40902d60e01b81523360048201526024016104bd565b6001600160a01b03811661069d5761069d630ba68dbb60e41b610caf565b600480546001600160a01b03838116610100818102610100600160a81b031985161790945560408051949093049091168084526020840191909152917f0ace8cc48451c2115e4b583399a0d93e969dd6fff132ecfda7d73f7fb34726d7910161051c565b5f88888861070e89610cd2565b88888888604051602001610729989796959493929190611b64565b60405160208183030381529060405280519060200120905098975050505050505050565b6004545f9060ff161561076a5761076a636b3e1fe160e11b610caf565b61077384610d38565b61078288888888888888610dc6565b5f80806107cb6107928980611baf565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f92019190915250610dff92505050565b919450925090505f6107ea826107e460608c018c611baf565b87610e43565b90505f806108328e8e604051602001610804929190611bf2565b60408051601f1981840301815291905280516020909101206005548e908e906001600160a01b031687610edb565b91509150610843858a8a8587611037565b6108878e8e8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152508692508791508a9050856111ac565b509c9b505050505050505050505050565b6001546001600160a01b031633146108c55760405163fa1bd1d760e01b81523360048201526024016104bd565b6001600160a01b0381166108e3576108e36359a1934760e11b610caf565b600780546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527ff3814cc4b6220f5bf02409e4cebe75e5032fdb20cef0db459431fd5cba574b93910161051c565b61094633610c9d565b6109655760405163cd40902d60e01b81523360048201526024016104bd565b6001600160a01b03811661098357610983631efe047760e01b610caf565b600680546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527fd3afd1676752459d3aa8b6f73633fac8efd724610da9c4cd8d8e4fa1631841fb910161051c565b6109e633610c9d565b610a055760405163cd40902d60e01b81523360048201526024016104bd565b6007805461ffff838116600160a01b90810261ffff60a01b1984161790935560408051939092041680835260208301849052917ff78e983a5bebb7320738ee1be825e0751accce1a4a79dd82e4e93171a2c20e30910161051c565b6001546001600160a01b03163314610a8d5760405163fa1bd1d760e01b81523360048201526024016104bd565b610a956111fa565b565b6001546001600160a01b03163314610ac45760405163fa1bd1d760e01b81523360048201526024016104bd565b6001600160a01b038116610ae257610ae2637d2c13fb60e11b610caf565b610aed600282611231565b506040516001600160a01b03821681527fcccafc3787229e84e106a9df64cb34531403b5a7934a4066bcb047daa3f4da38906020016105da565b610b3033610c9d565b610b4f5760405163cd40902d60e01b81523360048201526024016104bd565b6001600160a01b038116610b6d57610b6d636dad5a6360e11b610caf565b600580546001600160a01b038381166001600160a01b031983168117909355604080519190921680825260208201939093527f8a420ba2df3f68b3cbd59f7ce8228da532006406e46b515ca5d973da9d88a6ae910161051c565b610bd033610c9d565b610bef5760405163cd40902d60e01b81523360048201526024016104bd565b600754610c09906001600160a01b03848116911683611245565b5050565b610c1633610c9d565b610c355760405163cd40902d60e01b81523360048201526024016104bd565b6007546040515f916001600160a01b03169083908381818185875af1925050503d805f8114610c7f576040519150601f19603f3d011682016040523d82523d5f602084013e610c84565b606091505b5050905080610c0957610c096312171d8360e31b610caf565b5f610ca960028361129c565b92915050565b805f5260045ffd5b5f610ccb836001600160a01b0384166112bd565b9392505050565b5f610cdd8280611baf565b610cea6020850185611baf565b610cf76040870187611baf565b610d046060890189611baf565b604051602001610d1b989796959493929190611c01565b604051602081830303815290604052805190602001209050919050565b610d866020820135610d4d6040840184611baf565b8080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506113a092505050565b610d9a57610d9a632f58c7cd60e01b610caf565b60075442600160a01b90910461ffff168235011015610dc357610dc3631870a45d60e21b610caf565b50565b5f610dd8888888888835888833610701565b905083602001358114610df557610df5631a9848b160e01b610caf565b5050505050505050565b610e07611934565b5f8060608085806020019051810190610e209190611cfa565b604080518082019091529384526020840192909252919891975095509350505050565b5f8415610ed057600454610e649061010090046001600160a01b0316611409565b8251602084015160405163b8c1707360e01b81529293506001600160a01b0384169263b8c1707392610e9e92899289929190600401611de1565b5f604051808303815f87803b158015610eb5575f80fd5b505af1158015610ec7573d5f803e3d5ffd5b50505050610ed3565b505f5b949350505050565b5f60605f6040518060a00160405280610ef13390565b6001600160a01b0390811682527f0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d811660208301527f000000000000000000000000663a5c229c09b049e36dcc11a9b0d4a8eb9db21481166040830152306060830152861660809091015290505f81610f6a8880611baf565b610f7760208b018b611baf565b610f8460408d018d611baf565b610f9160608f018f611baf565b604051602001610fa999989796959493929190611e1a565b60408051601f198184030181529082905263d76fad2360e01b825291506001600160a01b0387169063d76fad2390610fe9908c908c908690600401611eb0565b6020604051808303815f875af1158015611005573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110299190611ed7565b999098509650505050505050565b84801561104b57506001600160a01b038116155b156110e85761105a8385611f06565b3410156110715761107163569e8c1160e01b610caf565b6001600160a01b0382166393bf570561108a8534611f19565b6040516001600160e01b031960e084901b168152600481018890525f6024820181905260448201526064015f604051808303818588803b1580156110cc575f80fd5b505af11580156110de573d5f803e3d5ffd5b50505050506111a5565b6001600160a01b0381161561118e578234101561110f5761110f63569e8c1160e01b610caf565b5f61111a8434611f19565b90505f81156111265750335b6040516361b2761d60e11b81526001600160a01b038581166004830152828116602483015284169063c364ec3a9084906044015f604051808303818588803b158015611170575f80fd5b505af1158015611182573d5f803e3d5ffd5b505050505050506111a5565b8234146111a5576111a563569e8c1160e01b610caf565b5050505050565b8151602083015160405133927ff65d3a11fa2b7663a8d9ddb6a63494bf4b09240815636a0332849a3179a000c9926111eb928a928a928a928990611f2c565b60405180910390a25050505050565b6004805460ff191660011790556040517fa5f3a64ab759ee06c492853089ad840d35deaf0f93c21423227115491a8502a9905f90a1565b5f610ccb836001600160a01b0384166114a0565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b1790526112979084906114ec565b505050565b6001600160a01b0381165f9081526001830160205260408120541515610ccb565b5f8181526001830160205260408120548015611397575f6112df600183611f19565b85549091505f906112f290600190611f19565b9050818114611351575f865f01828154811061131057611310611d7a565b905f5260205f200154905080875f01848154811061133057611330611d7a565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061136257611362611f9c565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f905560019350505050610ca9565b5f915050610ca9565b6040517f19457468657265756d205369676e6564204d6573736167653a0a3332000000006020820152603c81018390525f908190605c0160408051601f198184030181529190528051602090910120600654909150610ed3906001600160a01b0316828561156e565b5f763d602d80600a3d3981f3363d3d373d3d3d363d730000008260601b60e81c175f526e5af43d82803e903d91602b57fd5bf38260781b17602052603760095ff090506001600160a01b03811661149b5760405162461bcd60e51b8152602060048201526016602482015275115490cc4c4d8dce8818dc99585d194819985a5b195960521b60448201526064016104bd565b919050565b5f8181526001830160205260408120546114e557508154600181810184555f848152602080822090930184905584548482528286019093526040902091909155610ca9565b505f610ca9565b5f61152c826040518060400160405280600981526020016818d85b1b0819985a5b60ba1b815250856001600160a01b03166115cc9092919063ffffffff16565b905080515f1415801561155057508080602001905181019061154e9190611fb0565b155b15611297576040516308575cad60e01b815260040160405180910390fd5b5f805f61157b85856115da565b90925090505f81600481111561159357611593611fc9565b1480156115b15750856001600160a01b0316826001600160a01b0316145b806115c257506115c286868661161c565b9695505050505050565b6060610ed384845f85611703565b5f80825160410361160e576020830151604084015160608501515f1a611602878285856117da565b94509450505050611615565b505f905060025b9250929050565b5f805f856001600160a01b0316631626ba7e60e01b8686604051602401611644929190611fdd565b60408051601f198184030181529181526020820180516001600160e01b03166001600160e01b03199094169390931790925290516116829190611ff5565b5f60405180830381855afa9150503d805f81146116ba576040519150601f19603f3d011682016040523d82523d5f602084013e6116bf565b606091505b50915091508180156116d357506020815110155b80156115c257508051630b135d3f60e11b906116f89083016020908101908401612010565b149695505050505050565b6060824710156117645760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016104bd565b5f80866001600160a01b0316858760405161177f9190611ff5565b5f6040518083038185875af1925050503d805f81146117b9576040519150601f19603f3d011682016040523d82523d5f602084013e6117be565b606091505b50915091506117cf87838387611897565b979650505050505050565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561180f57505f9050600361188e565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015611860573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b038116611888575f6001925092505061188e565b91505f90505b94509492505050565b606083156119055782515f036118fe576001600160a01b0385163b6118fe5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016104bd565b5081610ed3565b610ed3838381511561191a5781518083602001fd5b8060405162461bcd60e51b81526004016104bd9190612027565b60405180604001604052806002905b60608152602001906001900390816119435790505090565b6001600160a01b0381168114610dc3575f80fd5b5f6020828403121561197f575f80fd5b8135610ccb8161195b565b5f8083601f84011261199a575f80fd5b50813567ffffffffffffffff8111156119b1575f80fd5b602083019150836020828501011115611615575f80fd5b5f608082840312156119d8575f80fd5b50919050565b5f805f805f805f8060e0898b0312156119f5575f80fd5b883567ffffffffffffffff80821115611a0c575f80fd5b611a188c838d0161198a565b909a50985060208b0135975060408b0135915080821115611a37575f80fd5b50611a448b828c016119c8565b955050606089013593506080890135925060a0890135915060c0890135611a6a8161195b565b809150509295985092959890939650565b5f805f805f805f60c0888a031215611a91575f80fd5b873567ffffffffffffffff80821115611aa8575f80fd5b611ab48b838c0161198a565b909950975060208a0135965060408a0135915080821115611ad3575f80fd5b611adf8b838c016119c8565b955060608a0135915080821115611af4575f80fd5b5088016060818b031215611b06575f80fd5b969995985093969295946080840135945060a09093013592915050565b5f60208284031215611b33575f80fd5b5035919050565b5f8060408385031215611b4b575f80fd5b8235611b568161195b565b946020939093013593505050565b878982379096019485526020850193909352604084019190915260608084019190915260808301919091529190911b6bffffffffffffffffffffffff191660a082015260b401919050565b5f808335601e19843603018112611bc4575f80fd5b83018035915067ffffffffffffffff821115611bde575f80fd5b602001915036819003821315611615575f80fd5b818382375f9101908152919050565b878982375f8882015f8152878982375f908801908152858782375f908601908152838582375f9301928352509098975050505050505050565b634e487b7160e01b5f52604160045260245ffd5b5f5b83811015611c68578181015183820152602001611c50565b50505f910152565b5f82601f830112611c7f575f80fd5b815167ffffffffffffffff80821115611c9a57611c9a611c3a565b604051601f8301601f19908116603f01168101908282118183101715611cc257611cc2611c3a565b81604052838152866020858801011115611cda575f80fd5b6115c2846020830160208901611c4e565b8051801515811461149b575f80fd5b5f805f8060808587031215611d0d575f80fd5b845167ffffffffffffffff80821115611d24575f80fd5b611d3088838901611c70565b95506020870151915080821115611d45575f80fd5b50611d5287828801611c70565b935050611d6160408601611ceb565b9150611d6f60608601611ceb565b905092959194509250565b634e487b7160e01b5f52603260045260245ffd5b81835281816020850137505f828201602090810191909152601f909101601f19169091010190565b5f8151808452611dcd816020860160208601611c4e565b601f01601f19169290920160200192915050565b606081525f611df4606083018688611d8e565b8281036020840152611e068186611db6565b905082810360408401526117cf8185611db6565b5f610120828c835b6005811015611e4a5781516001600160a01b0316835260209283019290910190600101611e22565b5050508060a0840152611e608184018b8d611d8e565b905082810360c0840152611e7581898b611d8e565b905082810360e0840152611e8a818789611d8e565b9050828103610100840152611ea0818587611d8e565b9c9b505050505050505050505050565b838152826020820152606060408201525f611ece6060830184611db6565b95945050505050565b5f60208284031215611ee7575f80fd5b8151610ccb8161195b565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610ca957610ca9611ef2565b81810381811115610ca957610ca9611ef2565b60c081525f611f3e60c0830189611db6565b6001600160a01b038881166020850152871660408401528281036060840152611f678187611db6565b90508281036080840152611f7b8186611db6565b905082810360a0840152611f8f8185611db6565b9998505050505050505050565b634e487b7160e01b5f52603160045260245ffd5b5f60208284031215611fc0575f80fd5b610ccb82611ceb565b634e487b7160e01b5f52602160045260245ffd5b828152604060208201525f610ed36040830184611db6565b5f8251612006818460208701611c4e565b9190910192915050565b5f60208284031215612020575f80fd5b5051919050565b602081525f610ccb6020830184611db656fea264697066735822122075fc7f6d14c4611f168a201b527d099e26c65a940e4773698a50cc5b1ecfd43564736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada5600000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56000000000000000000000000abcc7736c42713cd40ae8c2915c3b3dcdef6b83d0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d000000000000000000000000663a5c229c09b049e36dcc11a9b0d4a8eb9db214000000000000000000000000b38d53a9338f3778b5be6765fdea46f6ff8501cc0000000000000000000000000000000000000000000000000000000000000004000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56000000000000000000000000bf52436dbb09c97a301be66930392f61ee61c7b8000000000000000000000000e79f7a6af8d3bc37d30a172b2c9d2fd003af032b000000000000000000000000c0eba66b055aed2cc8e9e38f04e64501950cb470
-----Decoded View---------------
Arg [0] : superAdmin_ (address): 0xbf9f7E7046b7a90A759cdc06CED11Be634AAda56
Arg [1] : platformAdmins_ (address[]): 0xbf9f7E7046b7a90A759cdc06CED11Be634AAda56,0xBf52436dBb09C97a301be66930392F61Ee61C7B8,0xe79f7a6AF8D3bc37d30A172B2C9D2fD003af032B,0xc0Eba66b055AEd2CC8e9E38F04e64501950Cb470
Arg [2] : platformTreasury_ (address): 0xbf9f7E7046b7a90A759cdc06CED11Be634AAda56
Arg [3] : metadropOracleAddress_ (address): 0xabcC7736c42713cD40aE8c2915C3b3DcdeF6b83d
Arg [4] : uniswapRouter_ (address): 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
Arg [5] : tokenVault_ (address): 0x663A5C229c09b049E36dCc11a9B0d4a8Eb9db214
Arg [6] : driPoolTemplate_ (address): 0xB38D53A9338f3778B5be6765fDEa46F6ff8501Cc
-----Encoded View---------------
12 Constructor Arguments found :
Arg [0] : 000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [2] : 000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56
Arg [3] : 000000000000000000000000abcc7736c42713cd40ae8c2915c3b3dcdef6b83d
Arg [4] : 0000000000000000000000007a250d5630b4cf539739df2c5dacb4c659f2488d
Arg [5] : 000000000000000000000000663a5c229c09b049e36dcc11a9b0d4a8eb9db214
Arg [6] : 000000000000000000000000b38d53a9338f3778b5be6765fdea46f6ff8501cc
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [8] : 000000000000000000000000bf9f7e7046b7a90a759cdc06ced11be634aada56
Arg [9] : 000000000000000000000000bf52436dbb09c97a301be66930392f61ee61c7b8
Arg [10] : 000000000000000000000000e79f7a6af8d3bc37d30a172b2c9d2fd003af032b
Arg [11] : 000000000000000000000000c0eba66b055aed2cc8e9e38f04e64501950cb470
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 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.