Forkchoice Ethereum Mainnet

Address Contract Verified

Address 0x362eAF2F166B5dED89a83147eD3b75CEf5685d3E
Balance 0 ETH
Nonce 1
Code Size 2128 bytes
Indexed Transactions 0
External Etherscan · Sourcify

Contract Bytecode

2128 bytes
0x608060405234801561000f575f5ffd5b50600436106100b1575f3560e01c806395d89b411161006e57806395d89b4114610152578063a5c42ef11461015a578063a9059cbb14610181578063c5a1d7f014610194578063dd62ed3e146101bb578063f302315d146101f3575f5ffd5b806306fdde03146100b5578063095ea7b3146100d357806318160ddd146100f657806323b872dd14610108578063313ce5671461011b57806370a082311461012a575b5f5ffd5b6100bd610232565b6040516100ca919061069b565b60405180910390f35b6100e66100e13660046106e7565b6102c2565b60405190151581526020016100ca565b6002545b6040519081526020016100ca565b6100e6610116366004610711565b6102db565b604051601281526020016100ca565b6100fa61013836600461074f565b6001600160a01b03165f9081526020819052604090205490565b6100bd610397565b6100fa7f000000000000000000000000000000000000000000000000000000000000000081565b6100e661018f3660046106e7565b6103a6565b6100fa7f3bdffc6186a60e7f32745b18531b4bd1646c28ce067781ecc948b7d4b9723ba581565b6100fa6101c9366004610771565b6001600160a01b039182165f90815260016020908152604080832093909416825291909152205490565b61021a7f000000000000000000000000b79cf624d9eb924b126b228910bc11e41755e51281565b6040516001600160a01b0390911681526020016100ca565b606060038054610241906107a8565b80601f016020809104026020016040519081016040528092919081815260200182805461026d906107a8565b80156102b85780601f1061028f576101008083540402835291602001916102b8565b820191905f5260205f20905b81548152906001019060200180831161029b57829003601f168201915b5050505050905090565b5f336102cf8185856103b3565b60019150505b92915050565b5f7f000000000000000000000000b79cf624d9eb924b126b228910bc11e41755e5126001600160a01b031663bcabc2586040518163ffffffff1660e01b8152600401602060405180830381865afa158015610338573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061035c91906107e0565b6001600160a01b0316336001600160a01b03161461038257336103808582856103c5565b505b61038d848484610446565b5060019392505050565b606060048054610241906107a8565b5f336102cf818585610446565b6103c083838360016104a3565b505050565b6001600160a01b038381165f908152600160209081526040808320938616835292905220545f19811015610440578181101561043257604051637dc7a0d960e11b81526001600160a01b038416600482015260248101829052604481018390526064015b60405180910390fd5b61044084848484035f6104a3565b50505050565b6001600160a01b03831661046f57604051634b637e8f60e11b81525f6004820152602401610429565b6001600160a01b0382166104985760405163ec442f0560e01b81525f6004820152602401610429565b6103c0838383610575565b6001600160a01b0384166104cc5760405163e602df0560e01b81525f6004820152602401610429565b6001600160a01b0383166104f557604051634a1406b160e11b81525f6004820152602401610429565b6001600160a01b038085165f908152600160209081526040808320938716835292905220829055801561044057826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161056791815260200190565b60405180910390a350505050565b6001600160a01b03831661059f578060025f82825461059491906107fb565b9091555061060f9050565b6001600160a01b0383165f90815260208190526040902054818110156105f15760405163391434e360e21b81526001600160a01b03851660048201526024810182905260448101839052606401610429565b6001600160a01b0384165f9081526020819052604090209082900390555b6001600160a01b03821661062b57600280548290039055610649565b6001600160a01b0382165f9081526020819052604090208054820190555b816001600160a01b0316836001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360405161068e91815260200190565b60405180910390a3505050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b6001600160a01b03811681146106e4575f5ffd5b50565b5f5f604083850312156106f8575f5ffd5b8235610703816106d0565b946020939093013593505050565b5f5f5f60608486031215610723575f5ffd5b833561072e816106d0565b9250602084013561073e816106d0565b929592945050506040919091013590565b5f6020828403121561075f575f5ffd5b813561076a816106d0565b9392505050565b5f5f60408385031215610782575f5ffd5b823561078d816106d0565b9150602083013561079d816106d0565b809150509250929050565b600181811c908216806107bc57607f821691505b6020821081036107da57634e487b7160e01b5f52602260045260245ffd5b50919050565b5f602082840312156107f0575f5ffd5b815161076a816106d0565b808201808211156102d557634e487b7160e01b5f52601160045260245ffdfea26469706673582212200d3ac3a6bf29278ead16957571d127b28c66d2ea27cfe080c60b79b81d08e72e64736f6c634300081d0033

Verified Source Code Full Match

Compiler: v0.8.29+commit.ab55807c EVM: cancun Optimization: Yes (200 runs)
standardERC20.sol 199 lines
/** Source code of this contract can be found on
 *  https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
 *  Changes for LFG.CLUB are marked with "added".
 *  Removed _msgData() and _contextSuffixLength()
 */
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.29;

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

interface IERC20Metadata is IERC20 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
}

// @dev    added
interface FactoryContract {
    function _poolAddress() external view returns (address);
}

interface IERC20Errors {
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
    error ERC20InvalidSender(address sender);
    error ERC20InvalidReceiver(address receiver);
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
    error ERC20InvalidApprover(address approver);
    error ERC20InvalidSpender(address spender);
}

abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }
}

abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    bytes32 public immutable metadataHash; // @dev   added

    string private _name;
    string private _symbol;

    address public immutable _factoryAddress; // @dev   added

    uint256 public immutable tokenID; // @dev   added

    FactoryContract immutable factory; // @dev   added

    constructor(string memory name_, string memory symbol_, bytes32 metaHash_, address factoryAddress_, uint256 tokenID_) {
        _name = name_;
        _symbol = symbol_;
        metadataHash = metaHash_; // @dev   added

        _factoryAddress = factoryAddress_; // @dev   added
        factory = FactoryContract(_factoryAddress); // @dev   added

        tokenID = tokenID_; // @dev   added

        _update(address(0), (msg.sender == _factoryAddress) ? factory._poolAddress() : msg.sender, 1e27); // @dev   added
    }

    function name() public view virtual returns (string memory) {
        return _name;
    }

    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        if (_msgSender() == factory._poolAddress()) {
            // @dev   This will revert if factory contract is not deployed.
            // @dev   This check has been added to make the pool contract
            // @dev   able to pull tokens on a sell without prior approving.
            // @dev   Approvals are needed for all other addresses.
        } else {
            address spender = _msgSender();
            _spendAllowance(from, spender, value);
        }
        _transfer(from, to, value);
        return true;
    }

    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                _totalSupply -= value;
            }
        } else {
            unchecked {
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// @dev   Added for LFG.CLUB
contract LFGClubToken is ERC20 {
    constructor(string memory name, string memory symbol, bytes32 metaHash, address factory, uint256 _tokenID)
    ERC20(name, symbol, metaHash, factory, _tokenID) {
    }
}
depositContract.sol 256 lines
/*                                                         
 *   :**-                                     :=*+:           
 *   +%%[))<=        :-+><][])*+=-:        =<])}@@=           
 *  :<@@@}*+<})*<][#@@@@@@@@@@@@@@@%#[)>*]#<+>#@@@*           
 *  :)@@@@@#]%@@@@@@@@@@@@@@@@@@@@@@@@@@@@#]%@@@@@>           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>           
 *  :>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *   *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *  :<@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}:          
 *  :=+>#@@@@#[<)[[[)<[@@@@%%@@@#))]}[)<]#@@@@@@@@@}=         
 *   =))#%]*<[[<)#@@@@@]<)[#})]<><)@@@@@@#))[@@@@@@@@[+       
 *    =]):++@< :)@@@@@@@@%][]#@: -%@@@@@@@@][@@@@@@@@@@#*:    
 *   =<=*#@[]= +%@@@@@@@@[<@@>}: <@@@@@@@@@=@@@}}@@@@@@@@%]+: 
 *  :+-)@@@#*<:>@@@@@@@@%[}@@+}: @@@@@@@@@@>@@@%>-])]#@@@@@@[:
 *   :)@@@@@*[-<@@@@@@@@[: += <> @@@@@@@@@*#@@@@@*:           
 *   *%@@[><%>[#@@@@@@@#=      <)@@@@@@@@<[%<<#@@%=           
 *  -]@[=-[@@}>)]}#}])><[%#[#%%]<))[##[])<#@%):+}@<           
 *  =}>:-}@[=*@@]+*--[@@@[:  ]@@@@]--+*#@@++}@]::)}:          
 *  -+ :]#* +@@}<]=*%@@@@%]><#@@@@@#++)<#@@=:<%<  *:          
 *     +[= :@@@%#>+#@@@@@@@@@@@@@@@@[=)#%@@#: *]=             
 *     >+  *@@@@}+]@@@@@@@@@@@@@@@@@@<>%@@@%=  >+             
 *    :-   <@@@@]+}@@@@@@@@@@@@@@@@@@[*#@@@%>  ::             
 *         <@%%@]<@@@@@@@@@@@@@@@@@@@@<}@#%%*                 
 *         >@=>@##@%@@@@@@@@@@@@@@@%%@#%@-=%=                 
 *         =[ :>@@}--<[%@@@@@@@@#]*-*%@%*  [:                 
 *          -   *#}-    -=***+=:    *%}=   =                  
 *               ->-                +>:                       
 *                                                            
 * LFG.CLUB DEPOSIT CONTRACT
 *
 */

// SPDX-License-Identifier: LicenseRef-LFG-Commercial
// Full license: https://github.com/lfgclub/lfgclub/blob/main/LICENSE
pragma solidity ^0.8.29;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./pool.sol";
import "./factoryERC20.sol";
import "./standardERC20_nocurve.sol";

contract depositor is ReentrancyGuard {

    uint256 public totalDeposited;
    address public immutable feeOwner;
    address immutable creator;
    address public immutable factory;

    address public token;

    uint256 public splitAmount;

    uint256 constant totalSupply = 1e27;
    uint256 immutable maxDeposit = totalSupply * 125 / 100000;

    bool tokenSet;

    mapping(address account => uint256) private _lastAction;
    mapping(address account => uint256) private _rewardAtDeposit;
    mapping(address account => uint256) public _depositedTokens;
    mapping(address account => uint256) public _missedPayout;

    uint256 public totalDepositors;
    uint256 totalRewardPerToken;

    address immutable WETH;

    modifier onlyFeeOwner() {
        require(feeOwner == msg.sender, "Caller is not the feeOwner");
        _;
    }

    modifier onlyFactoryCreator() {
        require(creator == msg.sender, "Caller is not the factoryCreator");
        _;
    }

    constructor(address factory_){
        creator = msg.sender;
        WETH = Factory(factory_).WETH9();
        feeOwner = Factory(factory_).feeOwner();
        factory = factory_;

        _setNative();
    }

    function _setNative() internal {
        address native = Factory(factory)._nativeAddress();
        if (native != address(0)) {
            tokenSet = true;
            token = native;
        }
    }

    function modifySplit(uint256 number) public onlyFeeOwner {
        require((number >= 3300) && (number <= 8500), "SPLIT_BETWEEN_3300_8500");
        require(tokenSet, "DEPOSIT_NOT_ACTIVE");
        splitAmount = number;
    }

    function getSplit() public view returns (uint256 number) {
        if (!tokenSet) {
            number = 0;
        } else {
            number = splitAmount;
        }
    }

    // @dev    This function only exists to really double check that we set the right contract address.
    function setToken(address ca, address poolToken0) public onlyFeeOwner {
        require(!tokenSet, "TOKEN_SET");
        require(poolToken0 == ca, "POOL_TOKEN_DIFFERENT");
        token = ca;
        tokenSet = true;
    }

    receive() external payable {
        // @dev   Max deposit is 1e27 (= total supply), to retain accuracy we need to multiplay with a multiplier. Lets say 1e30.
        totalRewardPerToken += (totalDeposited > 0) ? ((msg.value * 1e30) / totalDeposited) : 0;
    }
    fallback() external payable {
        totalRewardPerToken += (totalDeposited > 0) ? ((msg.value * 1e30) / totalDeposited) : 0;
    }

    // @dev    Deposit function
    function deposit(uint256 amount) public nonReentrant {
        require(tokenSet, "DEPOSIT_NOT_ACTIVE");
        require(amount > 0, "NO_0_DEPOSIT");
        require(amount <= IWETH9(token).balanceOf(msg.sender), "NOT_ENOUGH_TOKENS");
        require((_lastAction[msg.sender] + 60 minutes) <= block.timestamp, "WAIT_60_MIN");  //----!! testnet: 1min // mainnet: 60 min
        require((_depositedTokens[msg.sender] + amount) <= maxDeposit,"MAX_DEPOSIT_1250000");

        if (_calculateReward(msg.sender)) {
            _claim(msg.sender);
        }

        require(IWETH9(token).transferFrom(msg.sender, address(this), amount));

        totalDepositors += (_depositedTokens[msg.sender] == 0) ? 1 : 0;
        _depositedTokens[msg.sender] += amount;
        totalDeposited += amount;

        _rewardAtDeposit[msg.sender] = totalRewardPerToken;

        _lastAction[msg.sender] = block.timestamp;
    }

    // @dev    Shows how much payout the user would receive if he withdraws or claims right now.
    function pendingReward() public view returns (uint256 reward) {
        reward = (((totalRewardPerToken - _rewardAtDeposit[msg.sender]) * _depositedTokens[msg.sender]) / 1e30) + _missedPayout[msg.sender];
    }

    function _calculateReward(address account) internal returns (bool reward) {
        if (_depositedTokens[account] > 0) {
            uint256 out = ((totalRewardPerToken - _rewardAtDeposit[account]) * _depositedTokens[account]) / 1e30;
            _missedPayout[account] += out;

            _rewardAtDeposit[account] = totalRewardPerToken;

            reward = (_missedPayout[account] >= 5e15) ? true : false;

        } else {
            reward = false;
        }   
    }

    // @dev    Claim function
    function claim() public nonReentrant {
        require(tokenSet, "DEPOSIT_NOT_ACTIVE");
        require(_calculateReward(msg.sender), "ACCUMULATE_0.005ETH");
        require((_lastAction[msg.sender] + 60 minutes) <= block.timestamp, "WAIT_60_MIN"); //----!! testnet: 1min // mainnet: 60 min
        _lastAction[msg.sender] = block.timestamp;

        _claim(msg.sender);
    }

    function _claim(address account) internal {
        // @dev    We are already checking if there is a payout earlier, so here just transfer out.

        uint256 payout = _missedPayout[account];
        _missedPayout[account] = 0;        
        payable(account).transfer(payout);
    }

    // @dev    Withdraw function
    function withdraw(uint256 amount) public nonReentrant {
        require(tokenSet, "DEPOSIT_NOT_ACTIVE");
        require(amount > 0, "NO_0_WITHDRAW");
        require(amount <= _depositedTokens[msg.sender], "TOO_MUCH_REQUESTED");
        require((_lastAction[msg.sender] + 60 minutes) <= block.timestamp, "WAIT_60_MIN"); //----!! testnet: 1min // mainnet: 60 min

        if (_calculateReward(msg.sender)) {
            _claim(msg.sender);
        }

        _depositedTokens[msg.sender] -= amount;
        totalDeposited -= amount;

        totalDepositors -= (amount == _depositedTokens[msg.sender]) ? 1 : 0;

        require(IWETH9(token).transfer(msg.sender, amount));

        _lastAction[msg.sender] = block.timestamp;
    }

    // @dev    In case of any unforseen stuff...
    function _addETH() public payable onlyFeeOwner returns (bool) {
        return true;
    }

    // @dev   Splits the amount between feeOwner and depositors.
    function _split(uint256 amount) internal {
        uint256 split = 0;
        uint256 amnt = 0;
        // check if somebody has deposited
        uint256 totS = totalDepositors;
        uint256 splT = getSplit();
        if ((totS > 0) && (splT > 0)) {
            split = splT;
        }

        if (split > 0) {
        amnt = (amount)/10000 * split;
        }
        payable(feeOwner).transfer(amount - amnt);

        // @dev   receive() nor fallback() are triggered when using this function. So we need to add to rewards within the split.
        totalRewardPerToken += (totalDeposited > 0) ? ((amnt * 1e30) / totalDeposited) : 0;
    }

    // @dev    for toolbox: launch token without bonding curve
    function launchToken(string memory name, string memory symbol, uint256 _totalSupply, uint8 decimals) public payable nonReentrant returns (address _tokenAddress) {
        address _poolAddress = Factory(factory)._poolAddress();
        uint256 eTP = ThePool(payable(_poolAddress)).ethToPool();
        // @dev this lowers/highers the amount if certain USD/ETH is reached.
        if (eTP >= 6.9 * 10 ** 18) {
            require(msg.value >= (0.015 * (10 ** 18)), "FEE_TOO_LOW");
        } else if (eTP >= 4.2 * 10 ** 18) {
            require(msg.value >= (0.01 * (10 ** 18)), "FEE_TOO_LOW");
        } else if (eTP >= 2.1 * 10 ** 18) {
            require(msg.value >= (0.005 * (10 ** 18)), "FEE_TOO_LOW");
        } else {
            require(msg.value >= (0.015 * (10 ** 18)), "FEE_TOO_LOW");
        }

        _tokenAddress = address(new LFGClubTokenNoCurve(name, symbol, _totalSupply, decimals));
        require(iERC20(_tokenAddress).transfer(msg.sender, _totalSupply * decimals));

        _split(msg.value);
    }
}
standardERC20_nocurve.sol 175 lines
/** Source code of this contract can be found on
  * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol
  * Removed _msgData() and _contextSuffixLength()
  */
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.29;

interface iERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

interface iERC20Metadata is iERC20 {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function decimals() external view returns (uint8);
}

interface iERC20Errors {
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
    error ERC20InvalidSender(address sender);
    error ERC20InvalidReceiver(address receiver);
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
    error ERC20InvalidApprover(address approver);
    error ERC20InvalidSpender(address spender);
}

abstract contract iContext {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }
}

abstract contract lERC20 is iContext, iERC20, iERC20Metadata, iERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;
    uint8 private immutable _decimals; /*changed*/

    string private _name;
    string private _symbol;

    constructor(string memory name_, string memory symbol_, uint256 totalSupply_, uint8 decimals__) {
        _name = name_;
        _symbol = symbol_;
        _totalSupply = totalSupply_;
        _decimals = decimals__;

        _update(address(0), msg.sender, _totalSupply * _decimals); /*added*/
    }

    function name() public view virtual returns (string memory) {
        return _name;
    }

    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    function decimals() public view virtual returns (uint8) {
        return _decimals;
    }

    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                _totalSupply -= value;
            }
        } else {
            unchecked {
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// @dev    Added for LFG.CLUB
contract LFGClubTokenNoCurve is lERC20 {
    constructor(string memory name, string memory symbol, uint256 totalSupply, uint8 decimals)
    lERC20(name, symbol, totalSupply, decimals) {
    }
}
factoryERC20.sol 268 lines
/*                                                         
 *   :**-                                     :=*+:           
 *   +%%[))<=        :-+><][])*+=-:        =<])}@@=           
 *  :<@@@}*+<})*<][#@@@@@@@@@@@@@@@%#[)>*]#<+>#@@@*           
 *  :)@@@@@#]%@@@@@@@@@@@@@@@@@@@@@@@@@@@@#]%@@@@@>           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>           
 *  :>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *   *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *  :<@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}:          
 *  :=+>#@@@@#[<)[[[)<[@@@@%%@@@#))]}[)<]#@@@@@@@@@}=         
 *   =))#%]*<[[<)#@@@@@]<)[#})]<><)@@@@@@#))[@@@@@@@@[+       
 *    =]):++@< :)@@@@@@@@%][]#@: -%@@@@@@@@][@@@@@@@@@@#*:    
 *   =<=*#@[]= +%@@@@@@@@[<@@>}: <@@@@@@@@@=@@@}}@@@@@@@@%]+: 
 *  :+-)@@@#*<:>@@@@@@@@%[}@@+}: @@@@@@@@@@>@@@%>-])]#@@@@@@[:
 *   :)@@@@@*[-<@@@@@@@@[: += <> @@@@@@@@@*#@@@@@*:           
 *   *%@@[><%>[#@@@@@@@#=      <)@@@@@@@@<[%<<#@@%=           
 *  -]@[=-[@@}>)]}#}])><[%#[#%%]<))[##[])<#@%):+}@<           
 *  =}>:-}@[=*@@]+*--[@@@[:  ]@@@@]--+*#@@++}@]::)}:          
 *  -+ :]#* +@@}<]=*%@@@@%]><#@@@@@#++)<#@@=:<%<  *:          
 *     +[= :@@@%#>+#@@@@@@@@@@@@@@@@[=)#%@@#: *]=             
 *     >+  *@@@@}+]@@@@@@@@@@@@@@@@@@<>%@@@%=  >+             
 *    :-   <@@@@]+}@@@@@@@@@@@@@@@@@@[*#@@@%>  ::             
 *         <@%%@]<@@@@@@@@@@@@@@@@@@@@<}@#%%*                 
 *         >@=>@##@%@@@@@@@@@@@@@@@%%@#%@-=%=                 
 *         =[ :>@@}--<[%@@@@@@@@#]*-*%@%*  [:                 
 *          -   *#}-    -=***+=:    *%}=   =                  
 *               ->-                +>:                       
 *                                                            
 * LFG.CLUB FACTORY CONTRACT
 *
 */

// SPDX-License-Identifier: LicenseRef-LFG-Commercial
// Full license: https://github.com/lfgclub/lfgclub/blob/main/LICENSE
pragma solidity ^0.8.29;

import "./standardERC20.sol";
import "./pool.sol";
import "./feeAccount.sol";
import "./depositContract.sol";
import "./metadata.sol";

contract Factory {
    LFGClubToken public latestDeployedToken;
    address public constant WETH9 = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;

    address public immutable creator;

    // @dev    Uniswap V3 Factory Address
    address public constant _v3Factory = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
    // @dev    Uniswap V3 NFT Manager Address
    address public constant _v3positionManager = 0xC36442b4a4522E871399CD717aBDD847Ab11FE88;

    uint256 public tokenID = 1;

    address public immutable feeOwner;

    address public _poolAddress;
    address public _depositAddress;

    address public _nativeAddress;

    uint256 _lastDepositChange;
    bool _upgradeOff;

    bool offline;

    modifier onlyFeeOwner() {
        require(feeOwner == msg.sender, "NOT_OWNER");
        _;
    }

    modifier onlyCreator() {
        require(creator == msg.sender, "NOT_CREATOR");
        _;
    }

    struct TokenInfo {
        address tokenAddress;
        uint48 creationBlock;
        uint48 updateBlock;
    }

    mapping(uint256 _tokenID => TokenInfo) public _tokenAddress; 
    mapping(uint256 _tokenID => uint256) public lastMetaUpdate;  

    event CreateToken(bytes32 indexed metadataHash, address indexed token, uint256 indexed tokenId, string name, string symbol, string description, string image, string web, string twitter, string telegram);
    event UpdateMetadata(bytes32 indexed unused, address indexed token, uint256 indexed tokenId, string description, string image, string web, string twitter, string telegram);

    constructor() {
        feeOwner = address(new FeeCollector(address(this), msg.sender, WETH9, _v3Factory, _v3positionManager));
        offline = false;
        creator = msg.sender;
    }

    // @dev    We need to deploy the pool contract seperately because of init bytecode restrictions.
    // @dev    Set the poolAddress here.
    function setPoolAddress(address pool) public onlyCreator {
        require(_poolAddress == address(0), "POOL_ALREADY_SET");
        _poolAddress = pool;
    }

    // @dev    We calculate the keccak256 hash here to generate a unique hash that is associated with the
    // @dev    initial parameters that are provided during token creation. This is done in order to
    // @dev    make initial parameters fraud proof.
    function calculateHash(uint256 id, string[7] memory _meta) internal pure returns (bytes32) {
        return Metadata.calculateHash(id, _meta);
    }

    // @dev   Splits the amount between feeOwner and depositors.
    function _split(uint256 amount) internal {
        uint256 split = 0;
        uint256 amnt = 0;
        // @dev   Check if somebody has deposited
        if (_depositAddress != address(0)) {
            uint256 totS = depositor(payable(_depositAddress)).totalDepositors();
            uint256 splT = depositor(payable(_depositAddress)).getSplit();
            if ((totS > 0) && (splT > 0)) {
                split = splT;
            }
        }

        if (split > 0) {
        amnt = (amount)/10000 * split;
        (bool success, ) = payable(_depositAddress).call{value: amnt}("");
        require(success, "TX_FAIL");
        }
        payable(feeOwner).transfer(amount - amnt);
    }

    // @dev    Lets the creator update the metadata or the community, on community takeover. This metadata update will only
    // @dev    happen on the webpage, on-chain the old metadata hash will always be the real one. On the webpage the old
    // @dev    metadata will still be listed on "Show raw metadata".
    // @dev    This has a fix cost. For original creator 0.05 ETH or 0.2 BNB. For a community takeover the cost is
    // @dev    0.25 ETH or 1 BNB. The cost is that high to deter bots / scammers from abusing it. This will happen
    // @dev    automatically without oversight. We can enforce higher values if called via website.
    // @dev    feeOwner can update Metadata for free (use this if there are some shenanigans...).

    function _updateMetadata(uint256 id, string[5] memory metad) public payable {
        ThePool.BondingCurve memory bc = ThePool(payable(_poolAddress)).getBondingCurve(id);

        address crtr = bc.creator;
        uint256 eTP = ThePool(payable(_poolAddress)).ethToPool();
        uint256 required = 0.05 * 10 ** 18;
        // @dev this lowers/highers the amount if certain USD/ETH is reached.
        if (eTP >= 6.9 * 10 ** 18) {
            required = 0.05 * (10 ** 18);            
        } else if (eTP >= 4.2 * 10 ** 18) {
            required = 0.03 * (10 ** 18);              
        } else if (eTP >= 2.1 * 10 ** 18) {
            required = 0.02 * (10 ** 18);
        } else {
            required = 0.05 * (10 ** 18);            
        }
        // @dev   Token creator
        if (msg.sender == crtr) {
            require(id > 0);
            require((lastMetaUpdate[id] + 7 days) <= block.timestamp, "WAIT_7_DAYS");  //----!! testnet: 7min // mainnet: 7 days
            require(msg.value >= required, "FEE_TOO_LOW");
            emit UpdateMetadata(0, address(0), id, metad[0], metad[1], metad[2], metad[3], metad[4]);
            _tokenAddress[id].updateBlock = uint48(block.number);
            lastMetaUpdate[id] = block.timestamp;
            _split(msg.value);
        // @dev   FeeOwner               
        } else if (msg.sender == feeOwner) {
            emit UpdateMetadata(0, address(0), id, metad[0], metad[1], metad[2], metad[3], metad[4]);
            _tokenAddress[id].updateBlock = uint48(block.number);
            lastMetaUpdate[id] = block.timestamp;
        // @dev   Community
        } else {
            require(id > 0);
            require((lastMetaUpdate[id] + 7 days) <= block.timestamp, "WAIT_7_DAYS");  //----!! testnet: 7min // mainnet: 7 days
            require(msg.value >= (required*700/200), "FEE_TOO_LOW");
            emit UpdateMetadata(0, address(0), id, metad[0], metad[1], metad[2], metad[3], metad[4]);
            _tokenAddress[id].updateBlock = uint48(block.number);
            lastMetaUpdate[id] = block.timestamp;
            _split(msg.value);
        }
    }

    // @dev    Resets Metadata on the website to the one it was created with it. Only use if detected abuse.
    function _resetMetadata(uint256 id) public onlyFeeOwner {
        _tokenAddress[id].updateBlock = 0;
    }

    // @dev    Create Token function.
    function createToken(string memory name, string memory symbol, string[5] memory metad) public payable returns (uint256 id) {
        require(!offline,"DEACTIVATED");

        tokenID += 1;
        id = tokenID-1;

        bytes32 tempHash = calculateHash(id, [name, symbol, metad[0], metad[1], metad[2], metad[3], metad[4]]);

        latestDeployedToken = new LFGClubToken(name, symbol, tempHash, address(this), id);
        address _token = address(latestDeployedToken);

        _tokenAddress[id].tokenAddress = address(_token);
        _tokenAddress[id].creationBlock = uint48(block.number);

        ThePool(payable(_poolAddress)).create(id, msg.sender, _token);

        emit CreateToken(tempHash, _token, id, name, symbol, metad[0], metad[1], metad[2], metad[3], metad[4]);

        if (msg.value > 0) {
            ThePool(payable(_poolAddress)).buy{value: msg.value}(id, 0);    
        }
    }

    function update(bool flag) public onlyFeeOwner {
        offline = flag;
    }

    function getLatestTokenAddress() public view returns (address) {
        return address(latestDeployedToken);
    }

    // @dev    Set metadata for native token. n0 and n1 are fallbacks if token bridge doesn't have name() and symbol() functions.
    function setNative(address _address, string[5] memory metad, string memory n0, string memory n1) public onlyCreator {
        require(_nativeAddress == address(0), "NATIVE_SET");  
        _nativeAddress = _address;
        _tokenAddress[0].tokenAddress = address(_address);
        _tokenAddress[0].creationBlock = uint48(block.number);

        string memory sy = (bytes(n0).length == 0) ? IERC20Metadata(_address).symbol() : n0;
        string memory nm = (bytes(n1).length == 0) ? IERC20Metadata(_address).name() : n1;

        bytes32 tempHash = calculateHash(0, [nm, sy, metad[0], metad[1], metad[2], metad[3], metad[4]]);

        emit CreateToken(tempHash, _address, 0, nm, sy, metad[0], metad[1], metad[2], metad[3], metad[4]);        
        if (block.chainid == 1) { // change to mainnet
            require(ERC20(_nativeAddress)._factoryAddress() == address(this),"NATIVE_INCORRECT_FACTORY");
        }
    }

    function setDepositor(address _address) public onlyCreator {
        require(_depositAddress == address(0), "DEPOSITOR_SET");  
        _depositAddress = _address;    
        require(depositor(payable(_depositAddress)).token() == _nativeAddress,"DEPOSITOR_INCORRECT_NATIVE");
        require(depositor(payable(_depositAddress)).factory() == address(this),"DEPOSITOR_INCORRECT_FACTORY");
        ThePool(payable(_poolAddress))._setDepositor();
        _lastDepositChange = block.timestamp;
    }

    // @dev    This makes the staking address upgradable if a new contract version is deployed.
    // @dev    To prevent abuse this has a 6 month cooldown, even after initial staking address set.
    // @dev    Upgradeability can be renounced.
    function upgradeDepositor(address _address) public onlyCreator {
        require(_depositAddress != address(0), "DEPOSITOR_NOT_SET");
        require((_lastDepositChange + 366 days) <= block.timestamp,"WAIT_12_MONTHS");   //----!! testnet: 60 min // mainnet: 366 days
        require(!_upgradeOff,"UPGRADE_RENOUNCED");
        _depositAddress = _address;    
        // @dev    This prevents that a non-contract can be set.
        require(depositor(payable(_depositAddress)).token() == _nativeAddress,"DEPOSITOR_INCORRECT_NATIVE");
        ThePool(payable(_poolAddress))._setDepositor();
        _lastDepositChange = block.timestamp;
    }

    // @dev    Permanently deactives the ability to upgrade the staking address.
    function renounceUpgrade() public onlyCreator {
        _upgradeOff = true;
    }
}
pool.sol 873 lines
/*                                                         
 *   :**-                                     :=*+:           
 *   +%%[))<=        :-+><][])*+=-:        =<])}@@=           
 *  :<@@@}*+<})*<][#@@@@@@@@@@@@@@@%#[)>*]#<+>#@@@*           
 *  :)@@@@@#]%@@@@@@@@@@@@@@@@@@@@@@@@@@@@#]%@@@@@>           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>           
 *  :>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *   *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *  :<@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}:          
 *  :=+>#@@@@#[<)[[[)<[@@@@%%@@@#))]}[)<]#@@@@@@@@@}=         
 *   =))#%]*<[[<)#@@@@@]<)[#})]<><)@@@@@@#))[@@@@@@@@[+       
 *    =]):++@< :)@@@@@@@@%][]#@: -%@@@@@@@@][@@@@@@@@@@#*:    
 *   =<=*#@[]= +%@@@@@@@@[<@@>}: <@@@@@@@@@=@@@}}@@@@@@@@%]+: 
 *  :+-)@@@#*<:>@@@@@@@@%[}@@+}: @@@@@@@@@@>@@@%>-])]#@@@@@@[:
 *   :)@@@@@*[-<@@@@@@@@[: += <> @@@@@@@@@*#@@@@@*:           
 *   *%@@[><%>[#@@@@@@@#=      <)@@@@@@@@<[%<<#@@%=           
 *  -]@[=-[@@}>)]}#}])><[%#[#%%]<))[##[])<#@%):+}@<           
 *  =}>:-}@[=*@@]+*--[@@@[:  ]@@@@]--+*#@@++}@]::)}:          
 *  -+ :]#* +@@}<]=*%@@@@%]><#@@@@@#++)<#@@=:<%<  *:          
 *     +[= :@@@%#>+#@@@@@@@@@@@@@@@@[=)#%@@#: *]=             
 *     >+  *@@@@}+]@@@@@@@@@@@@@@@@@@<>%@@@%=  >+             
 *    :-   <@@@@]+}@@@@@@@@@@@@@@@@@@[*#@@@%>  ::             
 *         <@%%@]<@@@@@@@@@@@@@@@@@@@@<}@#%%*                 
 *         >@=>@##@%@@@@@@@@@@@@@@@%%@#%@-=%=                 
 *         =[ :>@@}--<[%@@@@@@@@#]*-*%@%*  [:                 
 *          -   *#}-    -=***+=:    *%}=   =                  
 *               ->-                +>:                       
 *                                                            
 * LFG.CLUB POOL CONTRACT (BONDING CURVE)
 *
 */

// SPDX-License-Identifier: LicenseRef-LFG-Commercial
// Full license: https://github.com/lfgclub/lfgclub/blob/main/LICENSE
pragma solidity ^0.8.29;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./standardERC20.sol";
import "./FullMath.sol";
import "./feeAccount.sol";
import "./depositContract.sol";
import "./sqrtX96Math.sol";

////////// ERC20 & WETH interface
interface IWETH9 {
    function deposit() external payable;
    function withdraw(uint256 wad) external;
    function approve(address spender, uint256 value) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

////////// Uniswap V4 interfaces
interface IUniswapV4PoolManager {
    struct PoolKey {
        address currency0; address currency1; uint24 fee;
        int24 tickSpacing; address hooks;
    }
}

interface V4PositionManager {
    function multicall(bytes[] calldata data) external payable returns (bytes[] memory results);
    function initializePool(IUniswapV4PoolManager.PoolKey calldata key, uint160 sqrtPriceX96) external payable returns (int24);
    function modifyLiquidities(bytes calldata unlockData, uint256 deadline) external payable;
    function nextTokenId() external view returns (uint256);
    function getPoolAndPositionInfo(uint256 tokenId) external view returns (IUniswapV4PoolManager.PoolKey memory poolKey, uint256 info);
    function poolKeys(bytes25 poolId) external view returns (IUniswapV4PoolManager.PoolKey memory poolKey);
}

interface IAllowanceTransfer {
    function approve(address token, address spender, uint160 amount, uint48 expiration) external;
}

////////// Uniswap V3 interfaces
interface Iv3Factory {
    function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
    function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool);
}

interface Iv3Pool {
    function initialize(uint160 sqrtPriceX96) external;
    function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked);
    function token0() external returns (address token0);
}

interface INonfungiblePositionManager {
    struct MintParams {
        address token0; address token1; uint24 fee; int24 tickLower; int24 tickUpper; uint256 amount0Desired;
        uint256 amount1Desired; uint256 amount0Min; uint256 amount1Min; address recipient; uint256 deadline;
    }

    struct CollectParams { uint256 tokenId; address recipient; uint128 amount0Max; uint128 amount1Max; }

    function mint(MintParams calldata params) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);
}

////////// Uniswap V2 interfaces
interface Iv2Factory {
    function createPair(address tokenA, address tokenB) external returns (address pair);
}

interface Iv2Router {
    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);
}

////////// Start of contract
contract ThePool is ReentrancyGuard {
    uint256 immutable startVETH;
    uint256 immutable initVirtualTokens;

    uint64 tokenMultiplier0 = 49044814340589;
    uint48 tokenMultiplier1 = 4000000000000;

    uint256 immutable minimumTokens;
    uint256 immutable minimumTokensTransfer;

    address token0addr;
    uint80 public ethToPool;

    address immutable feeOwner;
    uint256 public feeOnCurve = 100; /*in bps: 100=1%, 1=0.01% etc.*/

    address immutable factory;
    address public immutable WETH;

    // @dev    Uniswap V3 Factory Address
    address public immutable _v3Factory;
    // @dev    Uniswap V3 NFT Manager Address
    address public immutable _v3positionManager;
    // @dev    Uniswap V3/V4 Permit2 Address
    address public constant uniPermit2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

    // @dev    Uniswap V2 Router Address
    address public constant _v2router = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
    // @dev    Uniswap V2 Factory Address
    address public constant _v2Factory = 0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f;

    // @dev    Uniswap V4 Pool Manager Address
    address public constant uniswapV4Manager = 0x000000000004444c5dc75cB358380D2e3dE08A90;
    // @dev    Uniswap V4 Position Manager Address
    address public constant v4POSM = 0xbD216513d74C8cf14cf4747E6AaA6420FF64ee9e;
    // @dev    Launching as V4?
    bool launchAsV4;
    uint24 v4launcher;

    Iv3Factory immutable v3Factory;
    INonfungiblePositionManager immutable v3positionManager;

    Iv2Factory immutable v2Factory;
    Iv2Router immutable v2router;

    address depositAccount;

    mapping(uint24 _fee => int24 tickSpacing) public getTicks;  
    mapping(uint24 _fee => int24 maxTicks) public getMaxTicks;

    // @dev    The Bonding Curve struct is actually declared this way
    // @dev    as this is the most gas efficient storage, to minimize
    // @dev    storage slots used.

    // @dev    token0 is always the token, and token1 always ETH.
    struct BondingCurve {
        address creator;       // 20B
        bool launchAsV4;       // 1B
        bool migrated;         // 1B
        uint80 ethPool;        // 10B max: max: ~1.2 million eth (+18 decimals)
        // > 32B (1 slot)

        address token0;        // 20B
        uint96 reserve1;       // 12B max: ~309 million eth (+18 decimals)
        // > 32B (1 slot)

        address token1;        // 20B
        uint96 virtualETH;     // 12B max: ~309 million eth (+18 decimals)
        // > 32B (1 slot)

        uint96 reserve0;      // 12B max: ~79 billion tokens (+18 decimals)
        uint96 virtualTokens; // 12B max: ~79 billion tokens (+18 decimals)
        uint48 tokenMul1;      // 6B max: ~281 trillion
        bool manipulation;     // 1B
        bool v2pool;           // 1B
        // > 32B (1 slot)

        // We can cast the uniswap ID's down as even in a spam attack
        // which would utilize 100% of the block every 0.01s they would
        // need 21 trillions years to fill it up. (assuming 500k gas per
        // v4 deployment and 36M gas per block limit). As gas cost on
        // v3 mint is astronomically higher than v4, this question
        // doesn't even arise on v3.

        uint72 uniV4Id;        // 9B
        uint64 uniV3Id;        // 8B
        uint8[7] v4order;      // 7B
        uint64 tokenMul0;      // 8B  max: 1.8 * 10^19
        // > 32B (1 slot)
    } // only 5 storage slots used

    mapping(uint256 _tokenId => BondingCurve) internal _bondingCurve;
    mapping(uint256 _tokenId => uint48[]) public swapBlocks;

    event Swap(
        uint256 indexed tokenID,
        address indexed sender,
        address indexed recipient,
        uint256 amount0,
        uint256 amount1,
        address token0,
        address token1
        );

    // here we need to add if it migrated to v4, v3, or v2
    event Migrate(
        uint256 indexed tokenID,
        address indexed token0,
        address indexed token1,
        uint256 amount0,
        uint256 amount1,
        uint256 movedTo,
        uint256 movedFee
    );

    uint8[7] v4orderArray = [50, 45, 40, 35, 30, 25, 100]; // @dev    For the launch. Multiply by 100 to get real val.
    // I think it is better to let v4order 

    // @dev    Modifier for functions that only the FeeOwner Contract can call 
    modifier onlyFeeOwner() {
        require(feeOwner == msg.sender, "NOT_OWNER");
        _;
    }

    constructor(address _factory) {
        initVirtualTokens = 1073000000 * 10 ** 18;
        minimumTokens = 202000000 * 10 ** 18;
        minimumTokensTransfer = 200000000 * 10 ** 18;
        startVETH = 30 * 10 ** 18;
        ethToPool = 6.9 * 10 ** 18;

        factory = _factory;

        launchAsV4 = true;

        _v3Factory = Factory(factory)._v3Factory();
        _v3positionManager = Factory(factory)._v3positionManager();

        v3Factory = Iv3Factory(_v3Factory);
        v3positionManager = INonfungiblePositionManager(_v3positionManager);

        v2Factory = Iv2Factory(_v2Factory);
        v2router = Iv2Router(_v2router);

        feeOwner = Factory(factory).feeOwner();

        WETH = Factory(factory).WETH9();

        // @dev   Set Uniswap V3/V4 tick and fee levels
        getTicks[2500] = 50; getMaxTicks[2500] = 887250;
        getTicks[3000] = 60; getMaxTicks[3000] = 887220;
        getTicks[3500] = 70; getMaxTicks[3500] = 887250;
        getTicks[4000] = 80; getMaxTicks[4000] = 887200;
        getTicks[4500] = 90; getMaxTicks[4500] = 887220;
        getTicks[5000] = 100; getMaxTicks[5000] = 887200;
        getTicks[10000] = 200; getMaxTicks[10000] = 887200;
    }

    receive() external payable {}
    fallback() external payable {}

    function getBondingCurve(uint256 id) public view returns (BondingCurve memory) {
        return _bondingCurve[id];
    }

    // @dev    Creates the BondingCurve for the token.
    function create(uint256 id, address creator_, address token) external {
        require(msg.sender == factory,"ONLY_FACTORY");

        _bondingCurve[id] = BondingCurve({
            creator: creator_,
            token0: token,
            token1: WETH,
            reserve0: 1000000000 * 10 ** 18,
            reserve1: 0,
            virtualETH: 30 * 10 ** 18,
            virtualTokens: 1073000000 * 10 ** 18,
            launchAsV4: launchAsV4,
            migrated: false,
            manipulation: false,
            uniV4Id: 0,
            uniV3Id: 0,
            v2pool: false,
            v4order: v4orderArray,
            tokenMul0: tokenMultiplier0,
            tokenMul1: tokenMultiplier1,
            ethPool: ethToPool
        });
    }

    // @dev     Activate/Deactivate v4 launches for new pools
    function launchPoolAsV4(bool flag) public onlyFeeOwner {
        launchAsV4 = flag;
    }

    // @dev    helper function
    function getSwapBlocks(uint256 id, uint256 start, uint256 end) public view returns (uint256 length, uint48[] memory blocks)
    {
        uint48[] storage all = swapBlocks[id];
        length = all.length;

        if (start == 0 && end == 0) {
            return (length, new uint48[](0));
        }

        require(end <= length && start < end);
        blocks = new uint48[](end - start);

        for (uint256 i = 0; i < end - start; i++) {
            blocks[i] = all[start + i];
        }
        return (length, blocks);
    }

// todo: add that we can change the virtual values -> we can decrease how much eth is needed in the future that way if eth just simply explodes.
// todo: add that we stop at 7.1 eth, 0.2 to creator (no staking), or 0.1 / 0.1 if staking. 

    // @dev    As price of ETH is not fixed, we have a scheme how migration will work.
    // @dev    For ETH price < 3450 USD: 6.9 are migrated, and 7.1 are needed to complete curve.
    // @dev    For ETH price >= 3450 USD and < 6900 USD: 4.2 are migrated, and 4.32 are needed.
    // @dev    For ETH price >= 6900 USD: 2.1 are migrated, and 2.16 are needed.

    // @dev    For chains that are not using ETH as native coin we need an additional multiplier,
    // @dev    denominated as nativeCoin/ETH price.

    function _updateRequirements(uint256 priceETH) public onlyFeeOwner {
        if (priceETH < 3450) {
            tokenMultiplier0 = 49044814340589;
            tokenMultiplier1 = 4000000000000;
            ethToPool = 6.9 * 10 ** 18;
        } else if ((priceETH >= 3450) && (priceETH < 6900)) {
            tokenMultiplier0 = 1259469696969697;
            tokenMultiplier1 = 62500000000000;
            ethToPool = 4.2 * 10 ** 18;
        } else {
            tokenMultiplier0 = 8060606060606061; // uint96
            tokenMultiplier1 = 200000000000000; // uint64
            ethToPool = 2.1 * 10 ** 18;
        }
    }

    // @dev    Factory will call this function to set also the staking address here.
    function _setDepositor() external {
        require(msg.sender == factory,"NOT_FACTORY");
        depositAccount = Factory(factory)._depositAddress();
    }

    // @dev    This splits the fee if somebody is staking on native token.
    // @dev    Check if staker is even deployed.
    function _split(uint256 amount) internal {
        uint256 splitted = 0;
        if (depositAccount != address(0)) {
            uint256 splitAmt = depositor(payable(depositAccount)).getSplit();
            if ((depositor(payable(depositAccount)).totalDepositors() > 0) && (splitAmt > 0)) {
            splitted = FullMath.mulDiv(amount, splitAmt, 10000);
            (bool success, ) = payable(depositAccount).call{value: splitted}(""); // add gas limit
            require(success, "TX_FAIL");
            }
        }
        payable(feeOwner).transfer(amount - splitted);
    }

    // @dev   If somebody is staking reward is split 50/50 between creator and staker.
    // @dev   If not: 100% to creator
    function _migrationSplit(uint256 amount, address target) internal {
        uint256 splitted = 0;
        if (depositAccount != address(0)) {
            if ((depositor(payable(depositAccount)).totalDepositors() > 0)) {
            splitted = FullMath.mulDiv(amount, 5000, 10000);
            (bool success, ) = payable(depositAccount).call{value: splitted}(""); // add gas limit
            require(success, "TX_FAIL");
            }
        }
        payable(target).transfer(amount - splitted);        
    }

    // @dev    Function for buys. in UpdateReserves we get if pool is ready for migration,
    // @dev    if it is ready, then _migrate() is called.
    function buy(uint256 id, uint256 amountOutMin) public payable nonReentrant {
        BondingCurve storage bC = _bondingCurve[id];
        require(!bC.migrated,"POOL_MIGRATED");
        require(msg.value > 0, "0_ETH_ERROR");

        // @dev    When calling createAndBuy if factory is the one who buys it, that's why
        // @dev    we change it to creator in this case, as it can only be the creator who
        // @dev    calls this function. Saves gas this way instead of doing extra txs.
        address sendTo = (msg.sender == factory) ? bC.creator : msg.sender;
        uint256 realAmountToken = updateReserves(id, 0, msg.value, false, sendTo);
        require(realAmountToken >= amountOutMin, "INSUFFICIENT_OUTPUT_AMOUNT");
        require(IWETH9(bC.token0).transfer(sendTo, realAmountToken));

        if (bC.migrated) {
            _migrate(id);
        }
    }

    // @dev    Pool contract can call transferFrom without approval for sells,
    // @dev    this makes the bonding curve experience way faster and more user friendly
    // @dev    for users.
    // @dev    This is safe as the ERC20 Token transferFrom checks if the caller is
    // @dev    the pool contract. If it's not then allowance is needed.
    function sell(uint256 id, uint256 amountIn, uint256 amountOutMin) public nonReentrant returns (uint256 amountOut) {
        BondingCurve storage bC = _bondingCurve[id];
        require(!bC.migrated,"POOL_MIGRATED");
        require(amountIn > 0,"0_TOKEN_ERROR");
        require(amountIn <= IWETH9(bC.token0).balanceOf(msg.sender),"NOT_ENOUGH_TOKENS");

        require(IWETH9(bC.token0).transferFrom(msg.sender, address(this), amountIn));

        amountOut = FullMath.mulDiv(FullMath.mulDiv(bC.virtualETH,amountIn,(bC.virtualTokens + amountIn)),bC.tokenMul1,bC.tokenMul0);
        require(amountOut >= amountOutMin,"INSUFFICIENT_OUTPUT_AMOUNT");

        updateReserves(id, amountIn, amountOut, true, msg.sender);

        uint256 feeAmount = FullMath.mulDiv(amountOut, feeOnCurve, 10000);
        _split(feeAmount);
        payable(msg.sender).transfer(amountOut-feeAmount);
        emit Swap(id, msg.sender, address(this), amountIn, amountOut-feeAmount, bC.token0, address(0));
    }

    // @dev    Updates the reserves and also calculates the fees for the buy.
    function updateReserves(uint256 id, uint256 amountOut, uint256 amountIn, bool from0to1, address sender) internal returns (uint256 realOut) {
        BondingCurve storage bC = _bondingCurve[id];
        swapBlocks[id].push(uint48(block.number));
        uint256 realIn;
        uint256 wethAmount;
   
        // buy
        if (!from0to1) {
            // step1: get the real input with the subtracted fee
            wethAmount = FullMath.mulDiv(uint256(amountIn), 10000, (10000 + feeOnCurve));

            // step2: calculate the amountOut we'd receive
            uint256 thetokenMultiplier = FullMath.mulDiv(wethAmount, bC.tokenMul0, bC.tokenMul1);
            amountOut = FullMath.mulDiv(bC.virtualTokens, thetokenMultiplier,(bC.virtualETH + thetokenMultiplier));

            if ((bC.reserve0 - amountOut) <= minimumTokens) {
                // @dev    Here the migration is triggered. We are refunding the
                // @dev    one who triggered the migration with the buy the excess ETH.
                amountOut = bC.reserve0 - minimumTokens;
                realIn = _refund(id, amountOut);
                bC.migrated = true;

                uint256 value = amountIn - realIn;
                address payable target = (sender == factory) ? payable(bC.creator) : payable(sender);
                payable(target).transfer(value);

                wethAmount = FullMath.mulDiv(uint256(realIn), 10000, (10000 + feeOnCurve));
                _split(realIn - wethAmount);
            // else pay the feeowner contract    
            } else {
                _split(amountIn - wethAmount);
            }

            bC.reserve0 -= uint96(amountOut);
            bC.virtualTokens -= uint96(amountOut);    

            bC.reserve1 += uint96(wethAmount);
            bC.virtualETH += uint96(FullMath.mulDiv(wethAmount,bC.tokenMul0,bC.tokenMul1));

            emit Swap(id, sender, address(this), wethAmount, amountOut, address(0), bC.token0);

            realOut = amountOut;  
        // sell
        } else {
            bC.reserve0 += uint96(amountOut);
            bC.virtualTokens += uint96(amountOut);    

            uint96 tempReserve1 = bC.reserve1;
            tempReserve1 -= uint96(amountIn);
            if (tempReserve1 < 1e4) { // @dev    Dust cleanup
                tempReserve1 = 0;
            }
            bC.reserve1 = tempReserve1;  

            uint96 tempVETH = bC.virtualETH;
            tempVETH -= uint96(FullMath.mulDiv(amountIn, bC.tokenMul0, bC.tokenMul1));
            if (tempVETH < uint96(startVETH + 1e4)) { // @dev   Dust cleanup
                tempVETH = uint96(startVETH);
            }
            bC.virtualETH = tempVETH;
        } 
    }

    // @dev    If the last buy before pool migration sends too much ETH, refund
    // @dev    the difference.
    function _refund(uint256 id, uint256 realAmountOut) internal view returns (uint256 realIn) {
        BondingCurve storage bC = _bondingCurve[id];        
        uint256 tempIn = FullMath.mulDiv(FullMath.mulDiv(realAmountOut, bC.virtualETH, (bC.virtualTokens - realAmountOut)),bC.tokenMul1,bC.tokenMul0);
        realIn = FullMath.mulDiv(tempIn, (10000 + feeOnCurve), 10000);
    }

    // @dev    Sets the fee of the buy/sells in the bonding curve.
    function setCurveFee(uint256 bps) public onlyFeeOwner {
        require((bps >= 25) && (bps <= 100),"BPS_25_100");
        feeOnCurve = bps;
    }

    // @dev    Check if V2 Pool price has been manipulated before migration happened
    function _checkPriceManipulationV2(uint256 id) internal view returns (bool manipulation) {
        // @dev    UniswapV2Pair init hash code for SEPOLIA, MAINNET, ARBITRUM, POLYGON, BASE, UNICHAIN
        bytes32 initCodeHash = 0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f;

        address token0 = _bondingCurve[id].token0;
        address token1 = _bondingCurve[id].token1;

        (address token01, address token11) = token0 < token1 ? (token0, token1) : (token1, token0);

        address pair = address(uint160(uint256(keccak256(abi.encodePacked(
            hex'ff',
            _v2Factory,
            keccak256(abi.encodePacked(token01, token11)),
            initCodeHash
        )))));

        manipulation = ((IWETH9(token0).balanceOf(pair) > 0) || (IWETH9(token1).balanceOf(pair) > 0)) ? true : false;
    }

    // @dev    Creation of Uniswap V2 pool
    function _createV2pool(uint256 id) internal returns (address pair) {
        BondingCurve storage bC = _bondingCurve[id];
        address token0 = bC.token0;

        bC.v2pool = true;

        IWETH9(WETH).deposit{value: bC.ethPool}();

        require(IWETH9(WETH).approve(_v2router, type(uint256).max));
        require(IWETH9(token0).approve(_v2router, type(uint256).max));

        pair = v2Factory.createPair(WETH, token0);
        v2router.addLiquidity(WETH, token0, bC.ethPool, minimumTokensTransfer, 0, 0, address(0), block.timestamp);
    }

    // @dev    Check if V3 Pool price has been manipulated before migration happened
    function _checkPriceManipulationV3(uint256 id, uint24 feeAmount) internal returns (bool manipulation) {
        BondingCurve storage bC = _bondingCurve[id];
        address checkPool = v3Factory.getPool(bC.token0, WETH, feeAmount);

        if (checkPool != address(0)) {
            token0addr = Iv3Pool(checkPool).token0();
            uint160 sqrtX = (token0addr != bC.token1) ? SqrtX96Math.getSqrtPriceX96(minimumTokensTransfer, bC.ethPool) : SqrtX96Math.getSqrtPriceX96(bC.ethPool, minimumTokensTransfer);
            (uint160 sqrtPrice, , , , , , ) = Iv3Pool(checkPool).slot0();

            manipulation = (sqrtX == sqrtPrice) ? false : true;
        } else {
            manipulation = false;
        }
    }

    // @dev    Creation of the Uniswap V3 pool
    function _createV3pool(uint256 id, uint24 feeAmount) internal {
        BondingCurve storage bC = _bondingCurve[id];
        address token00 = bC.token0;
        address token11 = bC.token1;

        IWETH9(WETH).deposit{value: bC.ethPool}();

        require(IWETH9(WETH).approve(_v3positionManager, type(uint256).max));
        require(IWETH9(token00).approve(_v3positionManager, type(uint256).max));

        address v3pool = v3Factory.createPool(token00, token11, feeAmount);
        token0addr = Iv3Pool(v3pool).token0();

        uint160 sqrtX = (token0addr != token11) ? SqrtX96Math.getSqrtPriceX96(minimumTokensTransfer, bC.ethPool) : SqrtX96Math.getSqrtPriceX96(bC.ethPool, minimumTokensTransfer);
        Iv3Pool(v3pool).initialize(sqrtX);

        (uint256 tokenId, , , ) = v3positionManager.mint(
            INonfungiblePositionManager.MintParams({
                token0: (token0addr != token11) ? token00 : token11,
                token1: (token0addr != token11) ? token11 : token00,
                fee: feeAmount,
                tickLower: -getMaxTicks[feeAmount],
                tickUpper: getMaxTicks[feeAmount],
                amount0Desired: (token0addr != token11) ? minimumTokensTransfer : bC.ethPool,
                amount1Desired: (token0addr != token11) ? bC.ethPool : minimumTokensTransfer,
                amount0Min: 0,
                amount1Min: 0,
                recipient: address(this),
                deadline: block.timestamp
            })
        );

        bC.uniV3Id = uint64(tokenId);
    }

    // @dev    Check if Uniswap V4 Pool price has been manipulated
    function _checkPriceManipulationV4(uint256 id, uint24 setFee) internal view returns (bool manipulation) {
        BondingCurve storage bC = _bondingCurve[id];

        address token00 = address(0);
        address token11 = bC.token0;

        IUniswapV4PoolManager.PoolKey memory pool = IUniswapV4PoolManager.PoolKey({
            currency0: token00,
            currency1: token11,
            fee: setFee,
            tickSpacing: getTicks[setFee],
            hooks: address(0)
        });

        // calculate poolId

        bytes25 poolId = bytes25(keccak256(abi.encode(pool)));

        // check if tickSpacing is 0 or not

        IUniswapV4PoolManager.PoolKey memory returnedPool = V4PositionManager(v4POSM).poolKeys(poolId);

        manipulation = (returnedPool.tickSpacing != 0) ? true : false;
    }

    // @dev    Creation of the Uniswap V4 pool.
    // --
    // @dev     we create the v4 pool with native currency: ETH. No WETH.
    function _createV4pool(uint256 id, uint24 setFee) internal {
        BondingCurve storage bC = _bondingCurve[id];
        uint256 amount1 = minimumTokensTransfer; //bC.reserve0; // @dev   token reserve
        uint256 amount0 = bC.ethPool; // @dev   eth to pool

        // @dev    As we create the V4 pool with native ETH, currency0 will always be address(0),
        // @dev    as uint160(address(0)) is always 0 and other addresses are always >0.
        // @dev    Therefor a comparison if address(0) < address(x) will always result in true,
        // @dev    which proves that currency0 is always native ETH.

        uint160 sqrtPriceX96 = SqrtX96Math.getSqrtPriceX96(amount0, amount1);
        uint128 amount = SqrtX96Math.getLiquidityForAmounts(sqrtPriceX96, amount0, amount1, getMaxTicks[setFee]);
        IUniswapV4PoolManager.PoolKey memory pool;

        {
        address token00 = address(0);
        address token11 = bC.token0;

        pool = IUniswapV4PoolManager.PoolKey({
            currency0: token00,
            currency1: token11,
            fee: setFee,
            tickSpacing: getTicks[setFee],
            hooks: address(0)
        });

        // @dev    As we use native ETH with address(0) this will always result in token00 = ETH. We need to approve
        // @dev    the other token to permit2.
        require(IWETH9(token11).approve(uniPermit2, type(uint256).max));
        IAllowanceTransfer(address(uniPermit2)).approve(token11, address(v4POSM), type(uint160).max, type(uint48).max);
        }

        //bytes memory hookData = new bytes(0);

        (bytes memory actions, bytes[] memory mintParams) =
            _mintLiquidityParams(id, pool, -getMaxTicks[setFee], getMaxTicks[setFee], amount, amount0, amount1, address(this), new bytes(0));

        bytes[] memory params = new bytes[](2);

        params[0] = abi.encodeWithSelector(V4PositionManager(v4POSM).initializePool.selector, pool, sqrtPriceX96, new bytes(0));
        params[1] = abi.encodeWithSelector(
            V4PositionManager(v4POSM).modifyLiquidities.selector, abi.encode(actions, mintParams), block.timestamp + 60
        );

        V4PositionManager(v4POSM).multicall{value: amount0}(params);
    }

    // @dev    Helper function to launch the V4 pool.

    function _mintLiquidityParams(uint256 id, IUniswapV4PoolManager.PoolKey memory poolKey, int24 _tickLower,
        int24 _tickUpper, uint256 liquidity, uint256 amount0Max, uint256 amount1Max, address recipient,
        bytes memory hookData) internal returns (bytes memory, bytes[] memory) {
            bytes memory actions = abi.encodePacked(uint8(0x02), uint8(0x0d));

            bytes[] memory params = new bytes[](2);
            params[0] = abi.encode(poolKey, _tickLower, _tickUpper, liquidity, amount0Max, amount1Max, recipient, hookData);
            params[1] = abi.encode(poolKey.currency0, poolKey.currency1);

            _bondingCurve[id].uniV4Id = uint72(V4PositionManager(v4POSM).nextTokenId());

            return (actions, params);
    }   

    // @dev   If price manipulation is detected in every Uniswap pool
    // @dev   creation, this activates automatically and deactivates
    // @dev   the pool. FeeOwner Contract can withdraw the ETH and
    // @dev   the tokens to refund them.
    // @dev   This is way safer then waiting for an exploit to happen
    // @dev   like on four meme.
    function _activateEmergencyWithdrawal(uint256 id) internal {
        _bondingCurve[id].manipulation = true;
    }

    function emergencyWithdrawal(uint256 id, address to) public onlyFeeOwner {
        require(_bondingCurve[id].manipulation, "NO_MANIPULATION");

        uint256 balance0 = _bondingCurve[id].reserve0;
        uint256 balance1 = _bondingCurve[id].reserve1;

        require(IWETH9(_bondingCurve[id].token0).transfer(to, balance0));
        payable(to).transfer(balance1);
    }

    // @dev    Migration.
    // @dev    We launch the pools as:
    // @dev    I)
    // @dev    (i)  Uniswap V4 0.5% || ...to (iii)  Uniswap V4 0.25% in 0.05% steps
    // @dev    (iv) Uniswap V3 0.3% || (v)   Uniswap V3 1.0% || (vi)   Uniswap V2
    // @dev    in this order, where (iv) and (v) go to (i) and (ii) and bump the rest
    // @dev    up in the order if launch as V4 is deactivated.
    // @dev    The order of the V4 launch order can be switched around.
    // @dev    On First Launch we launch with: if V4: 0.5%, if V3: 0.3%
    // @dev    This is set already in the factory contract, so on creation of a 
    // @dev    bonding curve this will always launch as this pool, even if migration happens
    // @dev    in the far future.
    function _migrate(uint256 id) internal {
        BondingCurve storage bC = _bondingCurve[id];
        require(ERC20(bC.token0).transfer(feeOwner, 2000000 * 10 ** 18));
        bC.reserve0 -= 2000000 * 10 ** 18;

        // @dev Locks the reserved tokens into the FeeOwner Contract for
        // @dev 50% for 6 months and 50% for 1 year.
        FeeCollector(payable(feeOwner)).lockTokens(bC.token0);

        _migrationSplit(bC.reserve1 - bC.ethPool, bC.creator);

        // v4 pools work with 2500, 3000, 3500, 4000, 4500, 5000, 10000 fee levels
        // v3 with 3000 and 10000
        // *how to tell in which order we want to launch? -> array
        // make to eth required changeable to account for rising/falling prices for ETH.
        // make it only changeable every 1 month
        // only do the for loop if entering into v4 check
        //
        uint256 migratedTo;
        uint256 migratedFee;
        if (bC.launchAsV4) {
            // here we try to launch as V4 first, what we want to do is enter the first if and run the loop there,
            // if all are manipulated go to v3 launch
            // do an extra function for the loop.
            if (!_v4loop(id)) {
                _createV4pool(id, v4launcher);
                migratedTo = 4; migratedFee = v4launcher;
            }
            else if (!_checkPriceManipulationV3(id, 3000)) {
                _createV3pool(id, 3000);
                migratedTo = 3; migratedFee = 3000;
            }
            else if (!_checkPriceManipulationV3(id,10000)) {
                _createV3pool(id,10000);
                migratedTo = 3; migratedFee = 10000;
            }
            else if (!_checkPriceManipulationV2(id)) {
                _createV2pool(id);
                migratedTo = 2; migratedFee = 0;
            }
            else {
                _activateEmergencyWithdrawal(id);
                migratedTo = 0; migratedFee = 0;
            }
        } else {
            if (!_checkPriceManipulationV3(id, 3000)) {
                _createV3pool(id, 3000);
                migratedTo = 3; migratedFee = 3000;
            }
            else if (!_checkPriceManipulationV3(id, 10000)) {
                _createV3pool(id, 10000);
                migratedTo = 3; migratedFee = 10000;
            }
            else if (!_checkPriceManipulationV2(id)) {
                _createV2pool(id);
                migratedTo = 2; migratedFee = 0;
            }
            else if (!_v4loop(id)) {
                _createV4pool(id, v4launcher);
                migratedTo = 4; migratedFee = v4launcher;
            }
            else { // test
                _activateEmergencyWithdrawal(id);
                migratedTo = 0; migratedFee = 0;
            }
        }

        emit Migrate(id, bC.token0, address(0), minimumTokensTransfer, bC.ethPool, migratedTo, migratedFee);
    }

    function _v4loop(uint256 id) internal returns (bool manipulation) {
        manipulation = true;

        uint8 theLength = uint8(v4orderArray.length);

        for (uint8 i = 0; i < theLength; i++) {
            uint24 feeLevel = uint24(v4orderArray[i]) * uint24(100);
            // @dev    We need to multiply the v4orderArray by 100 as we use uint8.
            if (!_checkPriceManipulationV4(id, feeLevel)) {
                manipulation = false;
                v4launcher = feeLevel;
                break;
            }
        }

        // @dev    If for loop runs fully through manipulation = false
        // @dev    never gets set, i.e. true is returned.
        // @dev    If no manipulation is detected, break the loop and
        // @dev    return which fee level is ok to launch.
    }

    function v4order(uint8[7] memory instruction) public onlyFeeOwner {
        // check if every entry divided by 500 is 0 (a % b)
        for (uint256 i = 0; i < instruction.length; i++) {
            require(instruction[i] % 5 == 0, "WRONG_BPS");
            require(((instruction[i] >= 25) && (instruction[i] <= 50)) || (instruction[i] == 100), "WRONG_BPS");
            v4orderArray[i] = instruction[i];
        }
    }

    // @dev    This lets the FeeOwner Contract claim the collected fees on Uniswap V3/V4.
    // @dev    Reverts if a Uniswap V2 pool got created due price manipulations.
    function claim(uint256 id) external onlyFeeOwner returns (address _token0, address _token1) {
        BondingCurve storage bC = _bondingCurve[id];
        require(bC.migrated, "NOT_MIGRATED");
        require(!bC.v2pool, "IS_V2");

        // @dev   This claims on V4
        if (bC.uniV4Id > 0) {
            bytes memory hookData = new bytes(0);
            bytes memory actions = abi.encodePacked(uint8(0x01), uint8(0x11));
            bytes[] memory params = new bytes[](2);
            params[0] = abi.encode(bC.uniV4Id, 0, 0, 0, hookData);
            address currency0 = address(0);
            address currency1 = bC.token0;
            params[1] = abi.encode(currency0, currency1, feeOwner);

            uint256 deadline = block.timestamp + 60;

            V4PositionManager(v4POSM).modifyLiquidities(
                abi.encode(actions, params),
                deadline
            );

            _token1 = currency0;
            _token0 = currency1; // returns always token0 as the token, and 1 as eth/weth

        // @dev   This claims on V3
        } else {
            INonfungiblePositionManager.CollectParams memory params = INonfungiblePositionManager.CollectParams({
                    tokenId: bC.uniV3Id,
                    recipient: feeOwner,
                    amount0Max: type(uint128).max,
                    amount1Max: type(uint128).max
            });

            uint256 before = IWETH9(WETH).balanceOf(address(this));  
            v3positionManager.collect(params);
            uint256 aftr = IWETH9(WETH).balanceOf(address(this));
            uint256 WETHAmount = aftr - before;

            _token0 = bC.token0;
            _token1 = address(0);

            IWETH9(WETH).withdraw(WETHAmount);
            payable(feeOwner).transfer(WETHAmount);
            require(IWETH9(_token0).transfer(feeOwner, IWETH9(bC.token0).balanceOf(address(this))));
        }
    }
}
ReentrancyGuard.sol 87 lines
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
sqrtX96Math.sol 101 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import "./FullMath.sol";

library SqrtX96Math {
    // @dev    see https://github.com/Uniswap/v4-core/blob/main/src/libraries/FixedPoint96.sol
    uint256 internal constant Q96 = 0x1000000000000000000000000;

    // @dev    Calculation of the square rote price * 2^96
    function getSqrtPriceX96(uint256 amount0, uint256 amount1) public pure returns (uint160) {
        require(amount0 > 0, "Error: amount0 must be over 0");
        require(amount1 > 0, "Error: amount1 must be over 0");

        // @dev    We normalize the amounts with 1e15 to prevent overflows.
        uint256 amount00 = amount0 / 1e15;
        uint256 amount11 = amount1 / 1e15;

        uint256 ratio = (amount11 << 192) / amount00;
        uint256 sqrtPrice = sqrt(ratio);

        return uint160(sqrtPrice);
    }

    // @dev    Babylonian method for calculation of a square root
    function sqrt(uint256 b) internal pure returns (uint256 c) {
        if (b > 3) {
            uint256 a = b / 2 + 1;
            c = b; 
            while (a < c) {
                c = a;
                a = (b / a + a) / 2;
            }
        } else if (b != 0) {
            c = 1;
        }
    }

    // @dev    see https://github.com/Uniswap/v4-core/blob/main/src/libraries/TickMath.sol
    // @dev    Calculated directly to make it easier for us.
    function getSqrtX96ForMinMaxTicks(uint256 maxTick) internal pure returns (uint160 lowerX96, uint160 upperX96) {
        if (maxTick == 887200) {
            lowerX96 = 4310618291;
            upperX96 = 1456195216239875923660968522684675790921166487552;
        } else if (maxTick == 887220) {
            lowerX96 = 4306310043;
            upperX96 = 1457652066918736003311195112902404148109990952960;
        } else if (maxTick == 887250) {
            lowerX96 = 4299855742;
            upperX96 = 1459840076217215014784942191280287875428834607104;
        } else {
            revert("Error: Invalid tick provided for X96 math.");
        }
    }

    // @note   V4 pools will be activated as standard as soon as
    // @note   dex screeners support them widely, as they are
    // @note   way more gas friendly.

    // @dev    The following 3 functions are for calculation of the liquidity which we will add.
    // @dev    see https://github.com/Uniswap/v4-periphery/blob/main/src/libraries/LiquidityAmounts.sol

    function getLiquidityForAmount0(uint160 sqrtPriceLower, uint160 sqrtPriceUpper, uint256 amount0) internal pure returns (uint128 liquidity) {
        if (sqrtPriceLower > sqrtPriceUpper) (sqrtPriceLower, sqrtPriceUpper) = (sqrtPriceUpper, sqrtPriceLower);
        unchecked {
            if (sqrtPriceLower > sqrtPriceUpper) (sqrtPriceLower, sqrtPriceUpper) = (sqrtPriceUpper, sqrtPriceLower);
            uint256 intermediate = FullMath.mulDiv(sqrtPriceLower, sqrtPriceUpper, Q96);
            liquidity = uint128(FullMath.mulDiv(amount0, intermediate, sqrtPriceUpper - sqrtPriceLower));
        }
    }

    function getLiquidityForAmount1(uint160 sqrtPriceLower, uint160 sqrtPriceUpper, uint256 amount1) internal pure returns (uint128 liquidity) {
       unchecked {
            if (sqrtPriceLower > sqrtPriceUpper) (sqrtPriceLower, sqrtPriceUpper) = (sqrtPriceUpper, sqrtPriceLower);
            liquidity = uint128(FullMath.mulDiv(amount1, Q96, sqrtPriceUpper - sqrtPriceLower));
        }
    }

    function getLiquidityForAmounts(uint160 sqrtPriceX96, uint256 amount0, uint256 amount1, int24 maxTick) internal pure returns (uint128 liquidity) {
        // @dev    we only need to do this for the v4 pools,
        // @dev    loop migration
        // @dev    we in range of [-887272, 887272]
        // @dev    fixed value of SqrtX96 of lower tick on a 5000 pool
        // ticks 2500 pool: 50, 3000: 60, 3500: 70, 4000: 80, 4500: 90, 5000: 100
        // closest values are to min & max are min & max for this tick range
        (uint160 sqrtPriceAX96, uint160 sqrtPriceBX96) = getSqrtX96ForMinMaxTicks(uint256(int256(maxTick)));

        if (sqrtPriceAX96 > sqrtPriceBX96) (sqrtPriceAX96, sqrtPriceBX96) = (sqrtPriceBX96, sqrtPriceAX96);

        if (sqrtPriceX96 <= sqrtPriceAX96) {
            liquidity = getLiquidityForAmount0(sqrtPriceAX96, sqrtPriceBX96, amount0);
        } else if (sqrtPriceX96 < sqrtPriceBX96) {
            uint128 liquidity0 = getLiquidityForAmount0(sqrtPriceX96, sqrtPriceBX96, amount0);
            uint128 liquidity1 = getLiquidityForAmount1(sqrtPriceAX96, sqrtPriceX96, amount1);

            liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
        } else {
            liquidity = getLiquidityForAmount1(sqrtPriceAX96, sqrtPriceBX96, amount1);
        }
    }
}
feeAccount.sol 615 lines
/*                                                         
 *   :**-                                     :=*+:           
 *   +%%[))<=        :-+><][])*+=-:        =<])}@@=           
 *  :<@@@}*+<})*<][#@@@@@@@@@@@@@@@%#[)>*]#<+>#@@@*           
 *  :)@@@@@#]%@@@@@@@@@@@@@@@@@@@@@@@@@@@@#]%@@@@@>           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>           
 *  :>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *   *@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@+           
 *  :<@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  :)@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<           
 *  -[@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@}:          
 *  :=+>#@@@@#[<)[[[)<[@@@@%%@@@#))]}[)<]#@@@@@@@@@}=         
 *   =))#%]*<[[<)#@@@@@]<)[#})]<><)@@@@@@#))[@@@@@@@@[+       
 *    =]):++@< :)@@@@@@@@%][]#@: -%@@@@@@@@][@@@@@@@@@@#*:    
 *   =<=*#@[]= +%@@@@@@@@[<@@>}: <@@@@@@@@@=@@@}}@@@@@@@@%]+: 
 *  :+-)@@@#*<:>@@@@@@@@%[}@@+}: @@@@@@@@@@>@@@%>-])]#@@@@@@[:
 *   :)@@@@@*[-<@@@@@@@@[: += <> @@@@@@@@@*#@@@@@*:           
 *   *%@@[><%>[#@@@@@@@#=      <)@@@@@@@@<[%<<#@@%=           
 *  -]@[=-[@@}>)]}#}])><[%#[#%%]<))[##[])<#@%):+}@<           
 *  =}>:-}@[=*@@]+*--[@@@[:  ]@@@@]--+*#@@++}@]::)}:          
 *  -+ :]#* +@@}<]=*%@@@@%]><#@@@@@#++)<#@@=:<%<  *:          
 *     +[= :@@@%#>+#@@@@@@@@@@@@@@@@[=)#%@@#: *]=             
 *     >+  *@@@@}+]@@@@@@@@@@@@@@@@@@<>%@@@%=  >+             
 *    :-   <@@@@]+}@@@@@@@@@@@@@@@@@@[*#@@@%>  ::             
 *         <@%%@]<@@@@@@@@@@@@@@@@@@@@<}@#%%*                 
 *         >@=>@##@%@@@@@@@@@@@@@@@%%@#%@-=%=                 
 *         =[ :>@@}--<[%@@@@@@@@#]*-*%@%*  [:                 
 *          -   *#}-    -=***+=:    *%}=   =                  
 *               ->-                +>:                       
 *                                                            
 * LFG.CLUB FEEOWNER CONTRACT
 *
 */

// SPDX-License-Identifier: LicenseRef-LFG-Commercial
// Full license: https://github.com/lfgclub/lfgclub/blob/main/LICENSE
pragma solidity ^0.8.29;

import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./standardERC20.sol";
import "./factoryERC20.sol";
import "./pool.sol";
import "./FullMath.sol";
import "./sqrtX96Math.sol";
import "./depositContract.sol";

contract FeeCollector is ReentrancyGuard {

    address public feeOwner;
    address public immutable factoryAddress;
    address public specialAddress;

    uint256 public lastChangeAuthorization;

    mapping(address => bool) public authorized;

    address[] public authorizedArray;
    uint256[] public shareArray;

    mapping(address => uint256) public lockedTokensTotal;
    mapping(address => uint256) public lockedNativeTokensTotal;

    struct LockInfo {
        uint256 amount;
        uint256 unlockTime;
    }

    struct LockNativeInfo {
        uint256 amount;
        uint256 unlockTime;       
        address tokenOwner; 
    }

    uint256 public uniV3ID;
    address uniV3pool;

    address public immutable WETH;
    address nativeToken;

    uint256 lastETHChange;
    uint256 setLastOwnerChange;

    // @dev    As this contract is open source but with a modified license, 
    // @dev    usage and modification of this contract is only allowed if
    // @dev    at least 15% of all fees are forwarded to the contract creator.
    // @dev    Here the address is set.
    address constant alwaysInShare = 0x5C18eec0B15B962DAd08a4d2CBAF7C66eE98b93c;

    uint256 setFeeLast;
    uint256 setSplitLast;

    mapping(address => LockInfo[]) public locks;
    mapping(address => LockNativeInfo[]) public nativeLocks;

    event TokensLocked(address indexed token, uint256 amount, uint256 unlockTime);
    event TokensUnlocked(address indexed token, uint256 amount);
    event LockChange(address indexed token, uint256 amount, uint256 unlockTime, address newOwner); 

    Iv3Factory immutable v3Factory;
    INonfungiblePositionManager immutable v3positionManager;

    address immutable v3posm;

    bool v3added;

    constructor(address _factory, address _feeOwner, address _WETH, address v3factory_, address v3manager_) {
        feeOwner = _feeOwner;
        specialAddress = feeOwner;
        factoryAddress = _factory;

        setSharesOnInit();

        WETH = _WETH;

        v3posm = v3manager_;
        v3Factory = Iv3Factory(v3factory_);
        v3positionManager = INonfungiblePositionManager(v3manager_);
        setLastOwnerChange = block.timestamp;
    }

    receive() external payable {}
    fallback() external payable {}

    modifier onlyFeeOwner() {
        require(feeOwner == msg.sender, "NOT_OWNER");
        _;
    }

    modifier isInAuthorizedShare() {
        require(authorized[msg.sender], "NO_AUTH");     
        _;   
    }

    modifier isSpecialAddress() {
        require(specialAddress == msg.sender, "NO_AUTH");     
        _;   
    }

    function setSharesOnInit() internal {
        if (alwaysInShare == feeOwner) {
            authorized[feeOwner] = true;

            authorizedArray.push(feeOwner);
            shareArray.push(10000);
        } else {    
            // @dev    alwaysInShare needs to be position 1.
            authorized[alwaysInShare] = true;  
            authorizedArray.push(alwaysInShare);
            shareArray.push(1500);              

            authorized[feeOwner] = true;
            authorizedArray.push(feeOwner);
            shareArray.push(8500);            
        }
    }

    // @dev    This locks the token this contract receives during migration.
    // @dev    50% for 2.5 min on testnet, and 183 days on mainnet
    // @dev    50% for 10 min on testnet, and 366 days on mainnet
    function lockTokens(address token) external {
        require(msg.sender == payable(Factory(factoryAddress)._poolAddress()),"NOT_POOL");
        uint256 tokenBalance = ERC20(token).balanceOf(address(this));
        uint256 half = tokenBalance/2;

        uint256 unlockTime1 = 183 days; //---!! testnet: 2.5 min // mainnet: 183 days
        uint256 unlockTime2 = 366 days; //---!! testnet: 10 min // mainnet: 366 days

        locks[token].push(LockInfo(half, block.timestamp + unlockTime1));
        locks[token].push(LockInfo(tokenBalance - half, block.timestamp + unlockTime2));
        lockedTokensTotal[token] = tokenBalance;

        emit TokensLocked(token, half, block.timestamp + unlockTime1);
        emit TokensLocked(token, tokenBalance - half, block.timestamp + unlockTime2);
    }

    // @dev    Changes feeOwner Address. Cannot be called for 6 months after launch and after every change.
    function changeFeeOwner(address newOwner) public onlyFeeOwner {
        require((setLastOwnerChange + 180 days) <= block.timestamp, "WAIT_180_DAYS"); //---!! testnet 6 min // main: 180 days
        feeOwner = newOwner;
        setLastOwnerChange = block.timestamp;
    }

    // @dev    Unlocks tokens this contract received during migration to make them available for withdraw.
    function unlockTokens(address token) external isInAuthorizedShare {
        uint256 totalWithdrawable = 0;
        LockInfo[] storage tokenLocks = locks[token];

        for (uint256 i = 0; i < tokenLocks.length; i++) {
            if (tokenLocks[i].unlockTime <= block.timestamp && tokenLocks[i].amount > 0) {
                totalWithdrawable += tokenLocks[i].amount;
                tokenLocks[i].amount = 0; // Mark as unlocked
            }
        }

        require(totalWithdrawable > 0, "0_AVAILABLE");
        lockedTokensTotal[token] -= totalWithdrawable;
        emit TokensUnlocked(token, totalWithdrawable);
    }

    /**
     * LOCK FUNCTIONS FOR THE NATIVE TOKEN
     */

    // lock native token; this is apart from the lock function to not confuse the different locks.
    function lockTokensNative(address token, uint256 amount, uint256 unlockTime, bool holder) public onlyFeeOwner {
        require(nativeToken != address(0), "NATIVE_NOT_SET");

        if (holder) {
            // contract holds tokens already. Check it if it is true.
            uint256 tokenBalance = ERC20(token).balanceOf(address(this));
            require(amount <= tokenBalance,"AMOUNT>BALANCE");
        } else {
            // msg.sender holds tokens. Check if it is true.
            uint256 tokenBalance = ERC20(token).balanceOf(msg.sender);
            require(amount <= tokenBalance,"AMOUNT>BALANCE");

            // needs to be approved obviously
            require(IWETH9(token).transferFrom(msg.sender, address(this), amount));
        }

        nativeLocks[token].push(LockNativeInfo(amount, block.timestamp + unlockTime, msg.sender));
        lockedNativeTokensTotal[token] += amount;

        emit TokensLocked(token, amount, block.timestamp + unlockTime);
    }

    // transfer ownership of lock
    function transferNativeLock(address token, address target, uint256 lockId) external {
        require(nativeToken != address(0), "NATIVE_NOT_SET");
        require(nativeLocks[token][lockId].amount > 0, "NOT_EXIST");
        require(nativeLocks[token][lockId].tokenOwner == msg.sender, "NOT_OWNER");

        nativeLocks[token][lockId].tokenOwner = target;

        emit LockChange(token, nativeLocks[token][lockId].amount, nativeLocks[token][lockId].unlockTime, nativeLocks[token][lockId].tokenOwner);
    }

    // extend lock
    function extendNativeLock(address token, uint256 newUnlockTime, uint256 lockId) external {
        require(nativeToken != address(0), "NATIVE_NOT_SET");
        require(nativeLocks[token][lockId].amount > 0, "NOT_EXIST");
        require(nativeLocks[token][lockId].tokenOwner == msg.sender, "NOT_OWNER");
        require(nativeLocks[token][lockId].unlockTime < (block.timestamp + newUnlockTime), "OLD>NEW");

        nativeLocks[token][lockId].unlockTime = block.timestamp + newUnlockTime;

        emit LockChange(token, nativeLocks[token][lockId].amount, nativeLocks[token][lockId].unlockTime, nativeLocks[token][lockId].tokenOwner);
    }

    // unlock native tokens
    function unlockTokensNative(address token) external {
        require(nativeToken != address(0), "NATIVE_NOT_SET");
        uint256 totalWithdrawable = 0;
        LockNativeInfo[] storage tokenLocks = nativeLocks[token];

        for (uint256 i = 0; i < tokenLocks.length; i++) {
            if ((tokenLocks[i].unlockTime <= block.timestamp) && (tokenLocks[i].amount > 0) && (tokenLocks[i].tokenOwner == msg.sender)) {
                totalWithdrawable += tokenLocks[i].amount;
                tokenLocks[i].amount = 0; // Mark as unlocked
            }
        }

        require(totalWithdrawable > 0, "0_AVAILABLE");
        lockedTokensTotal[token] -= totalWithdrawable;
        emit TokensUnlocked(token, totalWithdrawable);
    }        

    // @dev     Withdraw native token.
    // @dev     To preserve the intended vesting percentage relative to the circulating supply,
    // @dev     a proportional burn is applied on withdrawal. Specifically, a fraction of the withdrawn
    // @dev     tokens—equivalent to the current burned ratio (i.e. the percentage of total supply
    // @dev     held by the burn address)—will be burned. This ensures that, for example, if 15% of
    // @dev     the total supply is burned at the time of withdrawal, then 15% of the tokens withdrawn
    // @dev     are burned as well. In turn, the relative proportion of tokens locked (e.g., 25%) remains
    // @dev     constant with respect to the circulating supply.
    function withdrawNativeTokens() public onlyFeeOwner {
        require(nativeToken != address(0), "NATIVE_NOT_SET");
        address token = nativeToken;
        
        uint256 tokenBalance = ERC20(token).balanceOf(address(this));
        require(tokenBalance > 0,"NO_BALANCE");

        // check if some of the tokens are locked

        if (lockedNativeTokensTotal[token] > 0) {
            uint256 lockedBalance = lockedNativeTokensTotal[token];

            tokenBalance -= lockedBalance;
            require(tokenBalance > 0, "0_AVAILABLE");
        }

        // calculate how much % is burned

        uint256 burnBalance = ERC20(nativeToken).balanceOf(0x000000000000000000000000000000000000dEaD);
        uint256 burnRatio = burnBalance * 1e35 / 1e27; // 1e27 = total supply with decimals, we need to multiply burnBalance with a multiplier.
        for (uint256 i = 0; i < authorizedArray.length; i++) {
            uint256 shareAmount = FullMath.mulDiv(tokenBalance, shareArray[i], 10000);
            // Calculate the burn portion based on burnRatio
            uint256 burnAmount = FullMath.mulDiv(shareAmount, burnRatio, 1e35);
            // Authorized recipients receive the remainder
            uint256 authorizedAmount = shareAmount - burnAmount;
            require(ERC20(token).transfer(authorizedArray[i], authorizedAmount));
            require(ERC20(token).transfer(0x000000000000000000000000000000000000dEaD, burnAmount));
        } 
    }

    // @dev    Withdraws the collected ETH, split up between the share holders.
    function withdrawETH() public isInAuthorizedShare {
        uint256 balanceContract = address(this).balance;
        require(balanceContract > 0,"0_ETH");

        uint256 theLength = authorizedArray.length;

        for (uint256 i = 0; i < theLength; i++) {
            uint256 amount = FullMath.mulDiv(balanceContract,shareArray[i],10000);
            payable(authorizedArray[i]).transfer(amount);
        }        

    }

    // @dev    Withdraws the collected token <tokenContract>, split up between the share holders.
    function withdrawTokens(address token) public isInAuthorizedShare {
        require(token != nativeToken,"WRONG");
        uint256 tokenBalance = ERC20(token).balanceOf(address(this));
        require(tokenBalance > 0,"0_TOKEN");

        // check if some of the tokens are locked

        if (lockedTokensTotal[token] > 0) {
            uint256 lockedBalance = lockedTokensTotal[token];

            tokenBalance -= lockedBalance;
            require(tokenBalance > 0, "0_AVAILABLE");
        }

        for (uint256 i = 0; i < authorizedArray.length; i++) {
            uint256 amount = FullMath.mulDiv(tokenBalance,shareArray[i],10000);
            require(ERC20(token).transfer(authorizedArray[i], amount));
        }  
    }

    // @dev    Modifies the share holders.
    // @dev    Syntax: [address1, addres2, address3, ...], [share1, share2, share3, ...]
    // @dev    Sum of share array must be exactly 10000.
    function changeAuthorization(address[] memory accounts, uint256[] memory shares) public onlyFeeOwner {
        // add that it can only be changed once weekly
        require((lastChangeAuthorization + 7 days) < block.timestamp, "WAIT_1_WEEK"); //---!! testnet: 10 min // main: 1 week
        
        require((accounts[0] == alwaysInShare) && (shares[0] >= 1500),"READ_LICENSE");
        uint256 totalSum;

        // first check that sum of shares is not >10000 and clear the maps
        for (uint i = 0; i < shares.length; i++) {
            totalSum += shares[i];
        }

        require(totalSum == 10000, "SUM_NEED_10000");

        uint256 theLength = authorizedArray.length;

        // first empty the mappings
        for (uint256 i = 0; i < theLength; i++) {
            authorized[authorizedArray[i]] = false;
        }
        // now clear the arrays
        delete authorizedArray;
        delete shareArray;

        // set the new array and maps

        for (uint256 i = 0; i < accounts.length; i++) {
            authorized[accounts[i]] = true;
            authorizedArray.push(accounts[i]);
            shareArray.push(shares[i]);
        }

        lastChangeAuthorization = block.timestamp;        
    }

    // @dev    Lets us update the metadata of tokens on the website.
    // @dev    Use only if shenanigans have happened.
    function updateMetadata(uint256 id, string[5] memory metad) public isInAuthorizedShare {
        Factory(factoryAddress)._updateMetadata(id, metad);
    }

    // @dev    Burns the address for deactivation of new token creations. 
    // @dev    This action is irrevocable.
    // -----------
    // @dev    On burning this address the creation of new tokens 
    // @dev    can no longer be activated/deactivated.
    // @dev    When calling this function <name> will become fully
    // @dev    decentralized and can no longer be censored. 
    // @dev    The deactivation function of new token creation is only 
    // @dev    implimented in case there is a critical bug. All deployed
    // @dev    tokens will continue be tradeable and migration can also
    // @dev    happen normally.
    // @dev    Why fully decentralized? Because <name> is NOT upgradable.
    // @dev    I.e. a new contract with bug fixes needs to be deployed.
    // @dev    This is intentional.
    function burnSpecialAddress() public isSpecialAddress {
        specialAddress = address(0);
    }

    // @dev    Deactivates creation of new tokens. Only call this if
    // @dev    a critical bug was detected.
    function factoryFlag(bool flag) public isSpecialAddress {
        Factory(factoryAddress).update(flag);
    }

    // @dev    Sets the fee level of buy/sell on the pool contract.
    // @dev    As only feeContract can change the fee we enforce the
    // @dev    1 day wait between fee changes here.
    function setFee(uint256 bps) public onlyFeeOwner {
        require((setFeeLast + 1 days) <= block.timestamp, "WAIT_1_DAY"); //---!! testnet: 2 min // main: 1 day
        address pool = Factory(factoryAddress)._poolAddress();
        ThePool(payable(pool)).setCurveFee(bps);
        setFeeLast = block.timestamp;
    }

    // @dev    Sets that all new migrations to launch as Uniswap V4 pool.
    function launchPoolAsV4(bool flag) public onlyFeeOwner {
        address pool = Factory(factoryAddress)._poolAddress();
        ThePool(payable(pool)).launchPoolAsV4(flag);        
    }

    // @dev    Changes the order of which the v4 pools will be checked and launched.
    function changeV4order(uint8[7] memory array) public onlyFeeOwner {
        address pool = Factory(factoryAddress)._poolAddress();
        ThePool(payable(pool)).v4order(array);
    }

    // @dev    Modifies the splitting between depositers and feeContract.
    // @dev    Can only be between 33% (lowest) and 85% (highest), denominated
    // @dev    as 3300 and 8500.
    function modifySplitting(uint256 number) public onlyFeeOwner {
        require((setSplitLast + 14 days) <= block.timestamp, "WAIT_14_DAYS"); //---!! testnet 2 min // main: 14 days
        address depositAddress = Factory(factoryAddress)._depositAddress();
        depositor(payable(depositAddress)).modifySplit(number);
        setSplitLast = block.timestamp;
    }
    // @dev    In case of any unforseen stuff.
    function _addDeposit() public payable onlyFeeOwner {
        address depositAddress = Factory(factoryAddress)._depositAddress();
        depositor(payable(depositAddress))._addETH{value: msg.value}();
    }

    // @dev    Sets the native token for the Deposit Contract.
    // @dev    This is needed for expansions to other networks, if deposits should be
    // @dev    activated.
    function setNativeOnDepositContract(address ca) public onlyFeeOwner {
        address depositAddress = Factory(factoryAddress)._depositAddress();
        depositor(payable(depositAddress)).setToken(ca, nativeToken);
    }

    // @dev    Updates the ETH needed for pool completion for new bonding curve launches.
    function updateETH(uint256 priceETH) public onlyFeeOwner {
        require((lastETHChange + 14 days) <= block.timestamp, "WAIT_14_DAYS"); //---!! testnet 2 min // main: 14 days
        address pool = Factory(factoryAddress)._poolAddress();
        ThePool(payable(pool))._updateRequirements(priceETH);
        lastETHChange = block.timestamp;
    }

    // @dev    Collects fees of the Uniswap pools.
    // @dev    If native tokens have been deposited the splitting is
    // @dev    between the feeCollector and depositers. This split can
    // @dev    be set between 33% and 66% of ETH. Only ETH is splitted,
    // @dev    while tokens get burned with the same split model. I.e.
    // @dev    if 33% of ETH goes to depositers, also 33% of tokens get
    // @dev    burned forever. If 50%, then 50% gets burned, etc.
    // --------
    // @dev    Team decides what to do with the rest of the tokens,
    // @dev    e.g. adding permanently to liquidity, burning, covering
    // @dev    expenses etc.
    function collect(uint256 tokenID) public isInAuthorizedShare nonReentrant {
        address pool = Factory(factoryAddress)._poolAddress();

        // @dev    token1 in bondingCurve is always WETH, so we only need token0.

        ThePool.BondingCurve memory bc = ThePool(payable(pool)).getBondingCurve(tokenID);
        address theToken0 = bc.token0;

        uint256 beforeETH = address(this).balance;
        uint256 beforeToken = ERC20(theToken0).balanceOf(address(this));

        address depositAddress = Factory(factoryAddress)._depositAddress();
        ThePool(payable(pool)).claim(tokenID);

        uint256 afterETH = address(this).balance;
        uint256 afterToken = ERC20(theToken0).balanceOf(address(this));        

        // @dev    First get split amount from the staking contract,
        // @dev    if it is 0 or if there are 0 stakers, don't do any split.
        // @dev    As eth & tokens are already in this contract we only need to perform
        // @dev    the split if requirements are met.

        if (depositAddress != address(0)) {
            uint256 splitAmt = depositor(payable(depositAddress)).getSplit();
            if ((depositor(payable(depositAddress)).totalDepositors() > 0) && (splitAmt > 0)) {
                uint256 receivedETH = afterETH - beforeETH;
                uint256 receivedTokens = afterToken - beforeToken;

                uint256 splittedETH = FullMath.mulDiv(receivedETH, splitAmt, 10000);
                uint256 splittedToken = FullMath.mulDiv(receivedTokens, splitAmt, 10000);
                (bool success, ) = payable(depositAddress).call{value: splittedETH}("");
                require(success, "TX_FAIL");
                require(ERC20(theToken0).transfer(0x000000000000000000000000000000000000dEaD, splittedToken));
            }
        }
    }

    // @dev    Withdraws everything from the bondingCurve of the associated tokenID. Can only be
    // @dev    called if an automatic manipulation during migration to Uniswap has been detected.
    // @dev    I.e. it cannot be set manually.
    function emergencyWithdraw(uint256 tokenID) public onlyFeeOwner {
        address pool = Factory(factoryAddress)._poolAddress();
        ThePool(payable(pool)).emergencyWithdrawal(tokenID, msg.sender);

        // @dev    ETH + tokens get send immidiately to feeOwner to not mix them up with other functions here
        // @dev    as refunds need to managed either way by the feeOwner and not by the contract.
    }

    
    // @dev    ADD LIQUIDITY FUNCTION FOR THE NATIVE TOKEN FOR UNISWAP V3.
    // @dev    This locks liquidity as NFT is transferred directly to the contract.
    // @dev    Only function available is claim.
    function addLiquidityNative(address token, uint256 amountToken) public payable nonReentrant onlyFeeOwner {
        require(!v3added, "ALREADY_EXIST");
        nativeToken = token;
        address token00 = nativeToken;
        address token11 = WETH;

        uint256 reserve0 = amountToken;
        uint256 reserve1 = msg.value;

        IWETH9(WETH).deposit{value: reserve1}();

        require(IWETH9(WETH).approve(v3posm, type(uint256).max));

        // @dev    Native token must be transfered to this contract first -> as we use transferFrom the caller needs to approve the tokens first
        // @dev    to this contract!
        require(IWETH9(token00).transferFrom(msg.sender, address(this), amountToken));
        require(IWETH9(token00).approve(v3posm, type(uint256).max));

        uniV3pool = v3Factory.createPool(token00, token11, 10000);
        address token0addr = Iv3Pool(uniV3pool).token0();

        uint160 sqrtX = (token0addr != token11) ? SqrtX96Math.getSqrtPriceX96(reserve0, reserve1) : SqrtX96Math.getSqrtPriceX96(reserve1, reserve0);
        Iv3Pool(uniV3pool).initialize(sqrtX);

        (uint256 tokenId, , , ) = v3positionManager.mint(
            INonfungiblePositionManager.MintParams({
                token0: (token0addr != token11) ? token00 : token11,
                token1: (token0addr != token11) ? token11 : token00,
                fee: 10000,
                tickLower: -887200,
                tickUpper: 887200,
                amount0Desired: (token0addr != token11) ? reserve0 : reserve1,
                amount1Desired: (token0addr != token11) ? reserve1 : reserve0,
                amount0Min: 0,
                amount1Min: 0,
                recipient: address(this),
                deadline: block.timestamp
            })
        );

        uniV3ID = tokenId;
        v3added = true;
    }

    // @dev    CLAIM FEE FOR THE NATIVE TOKEN FOR UNISWAP V3.
    // @dev    Fees from trading the native token is split up following:
    // @dev    -- Native Coin: ETH, BNB, etc., on claim():
    // @dev    100% of native coin goes to feeOwner Contract (no split up).
    // @dev    -- Native token for project, on claim():
    // @dev    75% of native token will be burned.
    // @dev    12.5% is transfered to feeOwner Contract and locked up for 6 months.
    // @dev    12.5% is transfered to feeOwner Contract and immediately available.
    function claimNativeFee() public nonReentrant onlyFeeOwner {
            require(v3added, "NO_NATIVE");
            INonfungiblePositionManager.CollectParams memory params = INonfungiblePositionManager.CollectParams({
                    tokenId: uniV3ID,
                    recipient: address(this),
                    amount0Max: type(uint128).max,
                    amount1Max: type(uint128).max
            });

            // @dev    As we are receiving here the native token, which could be deposited,
            // @dev    we need to take a snapshort before and after claim.
            // @dev    The native token, and weth before and after. Convert the weth to ETH.

            uint256 beforeETH = IWETH9(WETH).balanceOf(address(this));
            uint256 beforeNative = IWETH9(nativeToken).balanceOf(address(this));

            v3positionManager.collect(params);

            uint256 aftrETH = IWETH9(WETH).balanceOf(address(this));
            uint256 aftrNative = IWETH9(nativeToken).balanceOf(address(this));

            uint256 WETHAmount = aftrETH - beforeETH;
            uint256 NativeAmount = aftrNative - beforeNative;

            IWETH9(WETH).withdraw(WETHAmount);

            uint256 lockAmount = FullMath.mulDiv(NativeAmount, 12500, 100000);

            // @dev    Burn
            require(IWETH9(nativeToken).transfer(0x000000000000000000000000000000000000dEaD, lockAmount * 6));
            // @dev    Lock
            lockTokensNative(nativeToken, lockAmount, 184 days, true); // lock
    }

}
FullMath.sol 124 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = a * b
        // Compute the product mod 2**256 and mod 2**256 - 1
        // then 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(a, b, not(0))
            prod0 := mul(a, b)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            require(denominator > 0);
            assembly {
                result := div(prod0, denominator)
            }
            return result;
        }

        // Make sure the result is less than 2**256.
        // Also prevents denominator == 0
        require(denominator > prod1);

        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////

        // Make division exact by subtracting the remainder from [prod1 prod0]
        // Compute remainder using mulmod
        uint256 remainder;
        assembly {
            remainder := mulmod(a, b, denominator)
        }
        // Subtract 256 bit number from 512 bit number
        assembly {
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of denominator
        // Compute largest power of two divisor of denominator.
        // Always >= 1.
        uint256 twos = (~denominator + 1) & denominator;
        // Divide denominator by power of two
        assembly {
            denominator := div(denominator, twos)
        }

        // Divide [prod1 prod0] by the factors of two
        assembly {
            prod0 := div(prod0, twos)
        }
        // Shift in bits from prod1 into prod0. For this we need
        // to flip `twos` such that it is 2**256 / twos.
        // If twos is zero, then it becomes one
        assembly {
            twos := add(div(sub(0, twos), twos), 1)
        }
        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
        // correct for four bits. That is, denominator * inv = 1 mod 2**4
        uint256 inv = (3 * denominator) ^ 2;
        // Now use 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.
        inv *= 2 - denominator * inv; // inverse mod 2**8
        inv *= 2 - denominator * inv; // inverse mod 2**16
        inv *= 2 - denominator * inv; // inverse mod 2**32
        inv *= 2 - denominator * inv; // inverse mod 2**64
        inv *= 2 - denominator * inv; // inverse mod 2**128
        inv *= 2 - denominator * inv; // 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 precoditions 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 * inv;
        return result;
    }

    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param c The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function mulDivRoundingUp(
        uint256 a,
        uint256 c,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        result = mulDiv(a, c, denominator);
        if (mulmod(a, c, denominator) > 0) {
            require(result < type(uint256).max);
            result++;
        }
    }
}
metadata.sol 134 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

library Metadata {

    function combineInputs(
        string memory str0,
        string memory str1,
        string memory str2,
        string memory str3,
        string memory str4,
        string memory str5,
        string memory str6,
        uint256 number
    ) public pure returns (string memory) {
        return string(
            abi.encodePacked(
                "ID:",
                uintToString(number),
                "|Name:",
                str0,
                "|Symbol:",
                str1,
                "|Description:",
                str2,
                "|Image:",
                str3,
                "|Web:",
                str4,
                "|X:",
                str5,
                "|Telegram:",
                str6
            )
        );
    }

    function calculateHash(uint256 nmbr, string[7] memory input) public pure returns (bytes32 hash) {
        string memory tempVal = combineInputs(input[0], input[1], input[2], input[0], input[1], input[2], input[3], nmbr);
        hash = keccak256(abi.encode(tempVal));
    }

    function uintToString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0";
        }

        uint256 temp = value;
        uint256 digits;

        while (temp != 0) {
            digits++;
            temp /= 10;
        }

        bytes memory buffer = new bytes(digits);
        while (value != 0) {
            digits -= 1;
            buffer[digits] = bytes1(uint8(48 + value % 10));
            value /= 10;
        }

        return string(buffer);
    }

    /// @dev Base58 alphabet used by IPFS for CIDv0
    bytes constant ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

    /**
     * @notice Convert a 32-byte SHA-256 digest into an IPFS CIDv0 (Qm...) string
     * @param _digest The raw 32-byte SHA-256 hash
     * @return The CIDv0 string, e.g. "QmYwAPJz..."
     */
    function toBase58CIDv0(bytes32 _digest) public pure returns (string memory) {
        // 1) Prepend multihash prefix 0x12 0x20 (type=SHA-256, length=32)
        //    So now we have 34 bytes: [0x12, 0x20, digest...]
        bytes memory input = abi.encodePacked(hex"1220", _digest); // length = 34

        // 2) Base58-encode those 34 bytes
        //    We'll implement a modified "Big Integer" base conversion
        //    by treating each byte as part of a base-256 number.

        // Allocate a temporary array for base58 digits (max length ~50 for 34 input bytes)
        uint8[] memory digits = new uint8[](70);
        digits[0] = 0;
        uint256 digitLength = 1;

        for (uint256 i = 0; i < input.length; i++) {
            uint256 carry = uint256(uint8(input[i]));
            for (uint256 j = 0; j < digitLength; j++) {
                carry += (uint256(digits[j]) << 8); // Base256 shift
                digits[j] = uint8(carry % 58);      // Remainder in base58
                carry /= 58;
            }

            // If carry is still > 0, push new digits
            while (carry > 0) {
                digits[digitLength] = uint8(carry % 58);
                digitLength++;
                carry /= 58;
            }
        }

        // 3) Handle leading zeros in the input → which become '1' in Base58
        //    Count how many 0x00 bytes at the front of 'input'
        uint256 leadingZeros = 0;
        for (uint256 i = 0; i < input.length; i++) {
            if (input[i] == 0) {
                leadingZeros++;
            } else {
                break;
            }
        }

        // 4) Finally, prepare the output string
        //    - leading zeros turn into '1'
        //    - then the actual base58 digits in reverse order
        uint256 outputSize = leadingZeros + digitLength;
        bytes memory result = new bytes(outputSize);

        // Fill in '1' for each leading zero
        for (uint256 i = 0; i < leadingZeros; i++) {
            result[i] = ALPHABET[0]; // '1'
        }

        // Base58 digits (reverse order)
        for (uint256 i = 0; i < digitLength; i++) {
            // digits[digitLength-1 - i] gives us the correct reversed order
            result[i + leadingZeros] = ALPHABET[digits[digitLength - 1 - i]];
        }

        return string(result);
    }
}

Read Contract

_factoryAddress 0xf302315d → address
allowance 0xdd62ed3e → uint256
balanceOf 0x70a08231 → uint256
decimals 0x313ce567 → uint8
metadataHash 0xc5a1d7f0 → bytes32
name 0x06fdde03 → string
symbol 0x95d89b41 → string
tokenID 0xa5c42ef1 → uint256
totalSupply 0x18160ddd → uint256

Write Contract 3 functions

These functions modify contract state and require a wallet transaction to execute.

approve 0x095ea7b3
address spender
uint256 value
returns: bool
transfer 0xa9059cbb
address to
uint256 value
returns: bool
transferFrom 0x23b872dd
address from
address to
uint256 value
returns: bool

Recent Transactions

No transactions found for this address