Contract Address Details

0x2fDcB16a977921FaDac3A9E03D18aDa77B4aA3F5

Token
Tendies (TENDIES)
Creator
0x5045fb–3d3077 at 0x47549f–0deaf7
Balance
0.00000000000001 CRO ( )
Tokens
Fetching tokens...
Transactions
2,365 Transactions
Transfers
1,474 Transfers
Gas Used
235,414,959
Last Balance Update
20129247
Contract name:
Tendies




Optimization enabled
true
Compiler version
v0.8.6+commit.11564f7e




Optimization runs
20000
EVM Version
default




Verified at
2021-11-29T02:04:38.610454Z

Contract source code

// SPDX-License-Identifier: Unlicensed


/**
 * Henlo frens!
 * 
 * The Tendies contract itself has been heavily annotated to allow all frens
 * to understand what is going on. It is meant to be as transparent
 * as possible in explaining what each privileged function does, why it's needed,
 * the foreseen risks they present and the mitigation of those risks.
 * 
 * To that end, each privileged function is annotated in 4 parts as follows:
 * 
 * Function: [What the function does].
 * 
 * Justification: [Why it's in the contract].
 * 
 * Risk statement: [What the risks in principle are].
 * 
 * Mitigation: [How the risks are to be minimized].
 * 
 * The following global mitigation applies to the notion that the owner's private keys could 
 * be compromised and the malicious actor could call any number of the many privileged functions
 * or dump from the Fren Co. wallets:
 * 
 * "The owner recognises that its private keys are a potential single point of failure
 * since they could be compromised by a malicious actor. The owner is experienced in security 
 * and assures that the private keys will be carefully guarded and stored on an airgapped Qubes
 * machines. A multisig wallet could also be used in the future to provide additional assurance. 
 * A time-lock is unlikely to be added as the owner feels it would hamstring the project's agility,
 * and ability to resolve issues that may arise, such as an individual unable to withdraw tokens 
 * from a pool because it would violate the max Tx or max wallet rules. Being able to adjust the 
 * tokenomics on demand is a core tenet of the project. No privileged function has been added 
 * without careful consideration of its risk profile".
 */



// File: SafeMath.sol


pragma solidity 0.8.6;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking. Retained for backwards-compatibility.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}



// File: Context.sol


pragma solidity 0.8.6;

/*
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

}



// File: IDEXRouter02.sol


pragma solidity 0.8.6;

interface IDEXRouter02 {
    function factory() external pure returns (address);
    function WETH() external pure returns (address);

    function addLiquidity(address tokenA, address tokenB, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline) 
        external returns (uint amountA, uint amountB, uint liquidity);

    function addLiquidityETH(address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline) 
        external payable returns (uint amountToken, uint amountETH, uint liquidity);

    function removeLiquidity(address tokenA, address tokenB, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline) 
        external returns (uint amountA, uint amountB);

    function removeLiquidityETH(address token, uint liquidity, uint amountTokenMin, uint amountETHMin, address to, uint deadline) 
        external returns (uint amountToken, uint amountETH);

    function removeLiquidityWithPermit( address tokenA, address tokenB,uint liquidity,uint amountAMin,uint amountBMin,address to,uint deadline, bool approveMax, uint8 v, bytes32 r, bytes32 s) 
        external returns (uint amountA, uint amountB);
        
    function removeLiquidityETHWithPermit(address token, uint liquidity,uint amountTokenMin,uint amountETHMin,address to,uint deadline,bool approveMax, uint8 v, bytes32 r, bytes32 s) 
        external returns (uint amountToken, uint amountETH);

    function swapExactTokensForTokens(uint amountIn,uint amountOutMin,address[] calldata path,address to,uint deadline) external returns (uint[] memory amounts);

    function swapTokensForExactTokens(uint amountOut,uint amountInMax,address[] calldata path,address to,uint deadline) external returns (uint[] memory amounts);

    function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts);

    function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts);

    function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) external returns (uint[] memory amounts);

    function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) external payable returns (uint[] memory amounts);

    function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);

    function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);

    function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);

    function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);

    function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
    
        function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);

    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;

    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;

    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;

}



// File: IDEXFactory.sol


pragma solidity 0.8.6;

interface IDEXFactory {
    // Creates pair with CRO.
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);      

    // Gives a fee to the LP provider.
    function feeTo() external view returns (address);      
    // Gives a fee to the LP setter.
    function feeToSetter() external view returns (address);     

    // Gets the address of the LP token pair.
    function getPair(address tokenA, address tokenB) external view returns (address pair);  
    // Gets address of all pairs.
    function allPairs(uint) external view returns (address pair); 
    // Gets the length of pairs.
    function allPairsLength() external view returns (uint);     

    // Creates the pair.
    function createPair(address tokenA, address tokenB) external returns (address pair);    

    // Sets a fee to an address.
    function setFeeTo(address) external;  
    // Sets fee to the setter address.
    function setFeeToSetter(address) external;  

    function INIT_CODE_PAIR_HASH() external view returns (bytes32);
    
}



// File: ICRC20.sol


pragma solidity 0.8.6;

interface ICRC20 {

    /* Functions */
    
    // Returns the amount of tokens in existence.
    function totalSupply() external view returns (uint256); 

    // Returns the token decimals.
    function decimals() external view returns (uint8);  

    // Returns the token symbol.
    function symbol() external view returns (string memory); 

    // Returns the token name.
    function name() external view returns (string memory); 

    // Returns the token owner.
    function getOwner() external view returns (address); 

    // Returns the amount of tokens owned by `account`.
    function balanceOf(address account) external view returns (uint256);   
    
    // Transfers tokens to addr, emits a {Transfer} event.
    function transfer(address recipient, uint256 amount) external returns (bool);  

    // Returns remaining tokens that spender is allowed during {approve} or {transferFrom}.
    function allowance(address _owner, address spender) external view returns (uint256); 
    
    // Sets amount of allowance, emits {approval} event.
    function approve(address spender, uint256 amount) external returns (bool); 

    // Moves amount, then reduce allowance and emits a {transfer} event.
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 

    /* Events */

    // Emits when value tokens moved, value can be zero.
    event Transfer(address indexed from, address indexed to, uint256 value);    

    // Emits when allowance of spender for owner is set by a call to approve. Value is new allowance.
    event Approval(address indexed owner, address indexed spender, uint256 value);   

}



// File: Address.sol


pragma solidity 0.8.6;

/**
 * @dev Collection of functions related to the address type.
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize, which returns 0 for contracts in
        // construction, since the code is only stored at the end of the
        // constructor execution.

        uint256 size;
        // solhint-disable-next-line no-inline-assembly
        assembly { size := extcodesize(account) }
        return size > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
        (bool success, ) = recipient.call{ value: amount }("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain`call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
      return functionCall(target, data, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have a CRO balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        require(isContract(target), "Address: call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.call{ value: value }(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
        require(isContract(target), "Address: static call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.staticcall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
        require(isContract(target), "Address: delegate call to non-contract");

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return _verifyCallResult(success, returndata, errorMessage);
    }

    function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                // solhint-disable-next-line no-inline-assembly
                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// File: SafeCRC20.sol



pragma solidity 0.8.6;

/**
 * @title SafeCRC20
 * @dev Wrappers around CRC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeCRC20 for ICRC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeCRC20 {
    using Address for address;

    function safeTransfer(ICRC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(ICRC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {ICRC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(ICRC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        // solhint-disable-next-line max-line-length
        require((value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeCRC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(ICRC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(ICRC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeCRC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(ICRC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeCRC20: low-level call failed");
        if (returndata.length > 0) { // Return data is optional
            // solhint-disable-next-line max-line-length
            require(abi.decode(returndata, (bool)), "SafeCRC20: CRC20 operation did not succeed");
        }
    }
}



// File: Tendies.sol


pragma solidity 0.8.6;

contract Tendies is Context, ICRC20 {

    /* LIBRARIES */
    
    using SafeMath for uint256;
    using SafeCRC20 for ICRC20;
    using Address for address;

    /* BASIC TOKEN CONSTANTS */
    
    string private constant TOKEN_NAME = "Tendies";
    
    string private constant TOKEN_TICKER = "TENDIES";
    
    uint8 private constant TOKEN_DECIMALS = 9;
    
    uint256 private constant TOKEN_MAX_SUPPLY = 1000000 * 10**9;
    
    uint256 private constant MAXintNum = ~uint256(0);
    
    /* RELEASE TIME STAMP VARIABLE */
    
    uint256 deployedUNIXTimeStamp;
    
    /* TRACKING VARIABLES */
    
    mapping(address => mapping(address => uint256)) private allowanceAmount;

    mapping(address => uint256) private reflectTokensOwned;
    
    mapping(address => uint256) private totalTokensOwned;

    /* RFI VARIABLES */
    
    uint256 private _rTotal;
    
    uint256 private totalFeeAmount;

    uint256 public reflectFeePercent;
    uint256 private previousReflectFeePercent;

    uint256 public charityFeePercent;
    uint256 private previousCharityFeePercent;

    uint256 public burnFeePercent;
    uint256 private previousBurnFeePercent;

    uint256 public funFeePercent;
    uint256 private previousFunFeePercent;

    uint256 public liquidityFeePercent;
    uint256 private previousLiquidityFeePercent;

    IDEXRouter02 public DEXRouter;
    
    address public DEXPair;
    address private previousDEXPair;
    
    address public DEXRouterAddress;
    address private previousDEXRouterAddress;

    bool private inAutoLiquidity;
    bool public isAutoLiquidityEnabled;

    uint256 public maxTxAmount;
    uint256 private previousMaxTxAmount;
    
    uint256 public maxWallet;
    uint256 private previousMaxWallet;
    
    uint256 public autoLiquidityThreshold;
    uint256 private previousAutoLiquidityThreshold;

    /* KEY ADDRESS VARIABLES */
    
    address public constant deadAddress = 0x000000000000000000000000000000000000dEaD;
    
    address private ownerOfToken;
    address private previousOwnerOfToken;
    
    address public growthAddress;
    address private previousGrowthAddress;
    
    address public funAddress;
    address private previousFunAddress;
    
    address public charityAddress;
    address private previousCharityAddress;
    
    address public dexPadTokenLockerAddress;
    address public dexPadLPLockerAddress;
    address public dexPadAirdropperAddress;
 
    /* SPECIAL VARIABLES */
    
    mapping(address => bool) private isAddressExcludedFromReflections;
    address[] private excludedFromReflectionsAddresses;

    mapping(address => bool) private isAccountExcludedFromTax;
    
    mapping(address => bool) private isAccountExcludedFromMaxWallet;
    
    mapping(address => bool) private isAccountExcludedFromMaxTxAmount;

    /* EVENTS */
    
    /**
     * Here events are defined that will be called in privileged functions
     * so that the changes made are clearly broadcasted on the explorer
     * and cannot be obfuscated. The names of the events are intended
     * to make them self-explanatory.
     */
    
    event OwnershipTransferred(
        address indexed previousOwner, 
        address indexed newOwner
    );
    
    event AutoLiquidity(
        uint256 tokensSwapped,
        uint256 croReceived,
        uint256 tokensIntoLiquidity
    );
    
    event AutoLiquidityEnabledUpdated(bool enabled);
    
    event AutoLiquidityThresholdChanged(
        uint256 indexed previousAutoLiquidityThreshold, 
        uint256 indexed newAutoLiquidityThreshold
    );
    
    event GrowthAddressChanged(
        address indexed previousGrowthAddress, 
        address indexed newGrowthAddress
    );
    
    event FunAddressChanged(
        address indexed previousFunAddress, 
        address indexed newFunAddress
    );
    
    event CharityAddressChanged(
        address indexed previousCharityAddress, 
        address indexed newCharityAddress
    );
    
    event MaxTxAmountChanged(
        uint256 indexed previousMaxTxAmount, 
        uint256 indexed newMaxTxAmount
    );
    
    event MaxWalletChanged(
        uint256 indexed previousMaxWallet, 
        uint256 indexed newMaxWallet
    );
    
    event ReflectFeePercentChanged(
        uint256 indexed previousReflectFeePercent, 
        uint256 indexed newReflectFeePercent
    );
    
    event CharityFeePercentChanged(
        uint256 indexed previousCharityFeePercent, 
        uint256 indexed newCharityFeePercent
    );
    
    event BurnFeePercentChanged(
        uint256 indexed previousBurnFeePercent, 
        uint256 indexed newBurnFeePercent
    );
    
    event FunFeePercentChanged(
        uint256 indexed previousFunFeePercent, 
        uint256 indexed newFunFeePercent
    );
    
    event LiquidityFeePercentChanged(
        uint256 indexed previousLiquidityFeePercent, 
        uint256 indexed newLiquidityFeePercent
    );
    
    event RouterAddressChanged(
        address indexed previousRouterAddress,
        address indexed newRouterAddress
    );
    
    event PairAddressChanged(
        address indexed previousDEXPair,
        address indexed newDEXPair
    );
    
    event AddressExcludedFromReflections(
        address indexed excludedAddress
    );
    
    event AddressIncludedInReflections(
        address indexed includedAddress
    );
    
    event AddressWhitelistedFromTax(
        address indexed whitelistedAddress
    );
    
    event AddressIncludedInTax(
        address indexed includedAddress
    );
    
    event AddressWhitelistedFromMaxWallet(
        address indexed whitelistedAddress
    );
    
    event AddressIncludedInMaxWallet(
        address indexed includedAddress
    );
    
    event AddressWhitelistedFromMaxTx(
        address indexed whitelistedAddress
    );
    
    event AddressIncludedInMaxTx(
        address indexed addressIncludedInMaxTx
    );
    
    event TendiesRecovered(
        address indexed recipient,
        uint256 indexed amount
    );
    
    event CRC20Recovered(
        address indexed token,
        address indexed recipient,
        uint256 indexed amount
    );
    
    event CRORecovered(
        address indexed recipient,
        uint256 indexed amount
    );

    

constructor (){
    
        // This function is executed once when the contract is deployed
        // and it sets the values for important addresses and variables.
        
        // Sets Tendie Fren Co. wallets.
        growthAddress = 0x6c8fC09De5ff53dd0cE360574e3485aa734Fd125;
        funAddress = 0xD67c5FEC4c9B70Ce7045B181B542068220EBBB5E;
        charityAddress = 0x0c8F0ab88AC430b536BDA383a59d99FD48c5aF56;

        // Sets owner.
        ownerOfToken = _msgSender();
        emit OwnershipTransferred(address(0), _msgSender());
        
        // Sets _rTotal.
        _rTotal = (MAXintNum - (MAXintNum % TOKEN_MAX_SUPPLY));       
        
        // Sets initial max Tx which acts as anti-snipe.
        maxTxAmount = 0 * 10**9;
        previousMaxTxAmount = maxTxAmount;
    
        // Sets initial max wallet which will be changed to 10000 after initial liquidty
        // is added but before trading is enabled, where it will act as anti-whale.
        maxWallet = 1000000 * 10**9;
        previousMaxWallet = maxWallet;
        
        // Sets initial reflection tax %.
        reflectFeePercent = 7;
        previousReflectFeePercent = reflectFeePercent;
        
        // Sets initial auto-liquidity tax %.
        liquidityFeePercent = 3;
        previousLiquidityFeePercent = liquidityFeePercent;
        
        // Sets initial fun tax % for competitions/giveaways etc.
        funFeePercent = 2; 
        previousFunFeePercent = funFeePercent;
        
        // Sets initial charity tax %.
        charityFeePercent = 1; 
        previousCharityFeePercent = charityFeePercent;
        
        // Sets initial burn tax %.
        burnFeePercent = 1; 
        previousBurnFeePercent = burnFeePercent;
        
        // Enables auto-liquidity and sets initial LP conversion threshold.
        isAutoLiquidityEnabled = true; 
        autoLiquidityThreshold = 300 * 10**9;

        // Tracks minting of tokens.
        reflectTokensOwned[owner()] = _rTotal; 
        emit Transfer(address(0), owner(), TOKEN_MAX_SUPPLY);      

        // Sets router address
        DEXRouterAddress = 0xdADaae6cDFE4FA3c35d54811087b3bC3Cd60F348;
        
        // Gets the router.
        IDEXRouter02 DEXRouterLocal = IDEXRouter02(DEXRouterAddress);
        
        // Creates the WCRO-TENDIES pair on the DEX.
        DEXPair = IDEXFactory(
            DEXRouterLocal.factory()).createPair(address(this), 
            DEXRouterLocal.WETH()
        );
        
        // Sets the rest of the contract variables in the global router variable to the local one.
        DEXRouter = DEXRouterLocal;
        
        // Sets DexPad addresses to eliminate need to manually whitelist after deploying.
        dexPadTokenLockerAddress = 0x2EC4e8617AB86C05CB0Be6E303BB71eBaeDf0C3E;
        dexPadAirdropperAddress = 0xF85ED2faef90dB86935061016bdEE08C33017107;

        // Whitelists key addresses from tax.
        isAccountExcludedFromTax[owner()] = true; 
        isAccountExcludedFromTax[address(this)] = true; 
        isAccountExcludedFromTax[growthAddress] = true;  
        isAccountExcludedFromTax[funAddress] = true;  
        isAccountExcludedFromTax[charityAddress] = true;
        isAccountExcludedFromTax[deadAddress] = true;
        isAccountExcludedFromTax[dexPadTokenLockerAddress] = true;
        isAccountExcludedFromTax[dexPadAirdropperAddress] = true;
        
        // Whitelists key addresses from max wallet limit.
        isAccountExcludedFromMaxWallet[owner()] = true;
        isAccountExcludedFromMaxWallet[address(this)] = true;
        isAccountExcludedFromMaxWallet[growthAddress] = true;
        isAccountExcludedFromMaxWallet[funAddress] = true;
        isAccountExcludedFromMaxWallet[charityAddress] = true;
        isAccountExcludedFromMaxWallet[deadAddress] = true;
        isAccountExcludedFromMaxWallet[dexPadTokenLockerAddress] = true;
        isAccountExcludedFromMaxWallet[dexPadAirdropperAddress] = true;
        
        // Whitelists key addresses from max Tx limit.
        isAccountExcludedFromMaxTxAmount[owner()] = true;
        isAccountExcludedFromMaxTxAmount[address(this)] = true;
        isAccountExcludedFromMaxTxAmount[growthAddress] = true;
        isAccountExcludedFromMaxTxAmount[funAddress] = true;
        isAccountExcludedFromMaxTxAmount[charityAddress] = true;
        isAccountExcludedFromMaxTxAmount[deadAddress] = true;
        isAccountExcludedFromMaxTxAmount[dexPadTokenLockerAddress] = true;
        isAccountExcludedFromMaxTxAmount[dexPadAirdropperAddress] = true;
        
        // Gets the block timestamp of when the contract is deployed.
        deployedUNIXTimeStamp = block.timestamp;
    }
    
    
    /* CUSTOM TRANSFER FUNCTIONS */

    /** Transfer function that checks for whitelisting and processes transfers appropriately.
     *  It applies the max Tx and max wallet limit rules. It allows frens to send or sell 
     *  (but not buy or receive) TENDIES even if their balance exceeds the max wallet limit.
     *  It correctly exempts the owner from the max Tx limit in order to prevent Tx failures 
     *  if withdrawing large amounts from token lockers. It does not allow frens to withdraw 
     *  TENDIES from a pool if the withdrawal would violate the max Tx or max wallet limits.
     *  This is not deemed to be an issue because the max Tx and max wallet amounts will 
     *  gradually be raised after launch. As the market cap matures, it becomes less likely 
     *  that max Tx and max wallet amounts are exceeded. Any potential issues can be resolved 
     *  by the owner temporarily whitelisting an address from these limits.
     */
    function transferTokens(address sender, address recipient, uint256 transferAmount, bool takeFee) private {
        if (!takeFee) {
            removeAllFee();
        }

        (uint256 reflectAmount, uint256 reflectTransferAmount,uint256 reflectFee, uint256[6] memory reflectLiqCharityBurnFunFeeArray) = getTaxAndReflectionValues(transferAmount);

        if(isAddressExcludedFromReflections[sender]){
            totalTokensOwned[sender] = totalTokensOwned[sender].sub(transferAmount);
        }
        reflectTokensOwned[sender] = reflectTokensOwned[sender].sub(reflectAmount);

        if(isAddressExcludedFromReflections[recipient]){
            totalTokensOwned[recipient] = totalTokensOwned[recipient].add(reflectLiqCharityBurnFunFeeArray[5]);
        }
        reflectTokensOwned[recipient] = reflectTokensOwned[recipient].add(reflectTransferAmount);

        takeLiquidityFee(reflectLiqCharityBurnFunFeeArray[1]);   
        takeCharityFee(reflectLiqCharityBurnFunFeeArray[2]);      
        takeBurnFee(reflectLiqCharityBurnFunFeeArray[3]);      
        takeFunFee(reflectLiqCharityBurnFunFeeArray[4]);      
        takeReflectFee(reflectFee, reflectLiqCharityBurnFunFeeArray[0]);

        emit Transfer(sender, recipient, reflectLiqCharityBurnFunFeeArray[5]);

        if (!takeFee){
            restoreAllFee();
        } 
        
        if(!isAccountExcludedFromMaxTxAmount[sender] && recipient != ownerOfToken){ 
            require(transferAmount <= maxTxAmount, "Max Tx violation");
        }
        
        require(sender != address(0) && recipient != address(0), "Zero address");

        if(!isAccountExcludedFromMaxWallet[recipient]){ 
            require(balanceOf(recipient) < maxWallet, "Max wallet violation");
        }
  
    }

    /** Internal transfer function e.g. for executing recover functions. */
    function transferInternal(address senderAddr, address receiverAddr, uint256 amount) private {   
 
        require(senderAddr != address(0) && receiverAddr != address(0), "Zero address");
        require(amount > 0, "Transfer amount must be greater than 0");
        
        uint256 contractStoredReflectionTokenBalance = balanceOf(address(this));

        bool overMinContractStoredReflectionTokenBalance = false; 
        if(contractStoredReflectionTokenBalance >= autoLiquidityThreshold){
            overMinContractStoredReflectionTokenBalance = true;                        
        }

        if (overMinContractStoredReflectionTokenBalance && !inAutoLiquidity && senderAddr != DEXPair && isAutoLiquidityEnabled) {
            contractStoredReflectionTokenBalance = autoLiquidityThreshold;        
            autoLiquidity(contractStoredReflectionTokenBalance);   
        }

        bool takeFee = true;    
        if (isAccountExcludedFromTax[receiverAddr] || isAccountExcludedFromTax[senderAddr]) {   
            takeFee = false;    
        }


        transferTokens(senderAddr, receiverAddr, amount, takeFee); 
    }


    /* BASIC TRANSFER FUNCTIONS */

    /** Simple transfer to function with taxes applied. */
    function transfer(address recipient, uint256 amount) external override returns (bool) {
        transferInternal(_msgSender(), recipient, amount);
        return true;
    }

    /** Simple transfer from function with approval. */
    function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
        transferInternal(sender, recipient, amount); 
        approveInternal(sender, _msgSender(), allowanceAmount[sender][_msgSender()].sub(amount, "Transfer amount exceeds allowance"));
        return true;
    }

   
    /* LIQUIDITY FUNCTIONS */

    /**
     * Adds liquidity having approved both tokens. The LP generated stays in this contract address. 
     * As with the autoLiquidity function, it presents a centralization risk over time. Thus, the LP 
     * generated will be recovered and locked before built up enough to warrant sufficient concern. 
     */
    function addLiquidity(uint256 tokenAmount, uint256 croAmount) private {
        approveInternal(address(this), address(DEXRouter), tokenAmount);        
        DEXRouter.addLiquidityETH{value: croAmount}(address(this),tokenAmount, 0, 0, address(this), block.timestamp);     
    }
    
    /** Swaps tokens for CRO having grabbed the router address and approved both tokens. */
    function swapTokensForCRO(uint256 tokenAmount) private {
        address[] memory path = new address[](2);
        path[0] = address(this);      
        path[1] = DEXRouter.WETH();     
        approveInternal(address(this), address(DEXRouter), tokenAmount);        
        DEXRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(tokenAmount, 0, path, address(this), block.timestamp);     
    }

     /**
     * A typical auto-liquidity function often called swapAndLiquify.
     * It sells half of the TENDIES queued for LP conversion when the threshold is reached, triggered by sells.
     * It then grabs the amount of CRO the swap creates.
     * Finally it combines the CRO with TENDIES to add liquidity to the main WCRO-TENDIES pool to the DEX.
     * Over time, it will cause a build-up of TENDIES in this contract address.
     * This happens because swapping half the TENDIES into CRO reduces the price a bit.
     * In order to mitigate any centralization risk, TENDIES built up in the contract will be recovered and locked
     *  or distributed to the community.
     */
    function autoLiquidity(uint256 contractStoredReflectionTokenBalance) private {        
        inAutoLiquidity = true;
        uint256 half1 = contractStoredReflectionTokenBalance.div(2);
        uint256 half2 = contractStoredReflectionTokenBalance.sub(half1);
        uint256 initialBalance = address(this).balance;     
        swapTokensForCRO(half1); 
        uint256 newBalance = address(this).balance.sub(initialBalance);    
        addLiquidity(half2, newBalance);     
        emit AutoLiquidity(half1, newBalance, half2);
        inAutoLiquidity = false;
    }


    /* PRIVATE CALCULATION FUNCTIONS */

    /** Calculates tax from transfer amount. */
    function getTaxValues(uint256 transferAmount) private view returns (uint256[6] memory) {

        uint256[6] memory reflectLiqCharityBurnFunFeeArray;
        reflectLiqCharityBurnFunFeeArray[0] = transferAmount.mul(reflectFeePercent).div(10**2);    
        reflectLiqCharityBurnFunFeeArray[1] = transferAmount.mul(liquidityFeePercent).div(10**2);   
        reflectLiqCharityBurnFunFeeArray[2] = transferAmount.mul(charityFeePercent).div(10**2);   
        reflectLiqCharityBurnFunFeeArray[3] = transferAmount.mul(burnFeePercent).div(10**2);   
        reflectLiqCharityBurnFunFeeArray[4] = transferAmount.mul(funFeePercent).div(10**2);   
        reflectLiqCharityBurnFunFeeArray[5] = transferAmount.sub(reflectLiqCharityBurnFunFeeArray[0]).sub(reflectLiqCharityBurnFunFeeArray[1])
            .sub(reflectLiqCharityBurnFunFeeArray[2]).sub(reflectLiqCharityBurnFunFeeArray[3]).sub(reflectLiqCharityBurnFunFeeArray[4]);

        return (reflectLiqCharityBurnFunFeeArray);
    }

    /** Calculates reflections from transfer amount and tax fees. */
    function getReflectionValues(uint256 transferAmount, uint256 taxReflect, uint256 taxLiquidity, uint256 taxCharityFee, uint256 taxBurnFee, uint256 taxFunFee, uint256 currentRate) 
    private pure returns (uint256, uint256, uint256){
        uint256 reflectionAmount = transferAmount.mul(currentRate);
        uint256 reflectionFee = taxReflect.mul(currentRate);
        uint256 reflectionLiquidity = taxLiquidity.mul(currentRate);
        uint256 reflectionFeeCharity = taxCharityFee.mul(currentRate);
        uint256 reflectionFeeBurn = taxBurnFee.mul(currentRate);
        uint256 reflectionFeeFun = taxFunFee.mul(currentRate);
        uint256 reflectionTransferAmount = reflectionAmount.sub(reflectionFee).sub(reflectionLiquidity);
        reflectionTransferAmount = reflectionTransferAmount.sub(reflectionFeeCharity).sub(reflectionFeeBurn).sub(reflectionFeeFun);
        return (reflectionAmount, reflectionTransferAmount, reflectionFee);
    }

    /** Gets the total tax and reflection values from transfer amount. */
    function getTaxAndReflectionValues(uint256 tAmount) private view returns (uint256,uint256,uint256, uint256[6] memory) {

        (uint256[6] memory reflectLiqCharityBurnFunFeeArray) = getTaxValues(tAmount);
        (uint256 reflectAmount, uint256 reflectTransferAmount, uint256 reflectFee) = 
            getReflectionValues(tAmount, reflectLiqCharityBurnFunFeeArray[0], reflectLiqCharityBurnFunFeeArray[1], 
                reflectLiqCharityBurnFunFeeArray[2], reflectLiqCharityBurnFunFeeArray[3], reflectLiqCharityBurnFunFeeArray[4], getReflectRate());
        return (reflectAmount, reflectTransferAmount, reflectFee, reflectLiqCharityBurnFunFeeArray);

    }

    /** Gets the reflect rate by dividing the reflect supply by the total token supply. */
    function getReflectRate() private view returns (uint256) {
        (uint256 reflectSupply, uint256 tokenSupply) = getCurrentSupplyTotals();       
        return reflectSupply.div(tokenSupply);        
    }

    /** Subtracts the tax from the reflect totals and adds to the total tax amount. */
    function takeReflectFee(uint256 reflectFee, uint256 taxReflect) private {
        _rTotal = _rTotal.sub(reflectFee);      
        totalFeeAmount = totalFeeAmount.add(taxReflect);    
    }

    /** Takes the liquidity tax - used for calculating transactions. */
    function takeLiquidityFee(uint256 tLiquidity) private {
        uint256 currentRate = getReflectRate();
        uint256 rLiquidity = tLiquidity.mul(currentRate);
        reflectTokensOwned[address(this)] = reflectTokensOwned[address(this)].add(rLiquidity);
        if (isAddressExcludedFromReflections[address(this)]){
            totalTokensOwned[address(this)] = totalTokensOwned[address(this)].add(tLiquidity);
        }
    }

    /** Takes the charity tax - used for calculating transactions. */
    function takeCharityFee(uint256 taxCharityFee) private {
        uint256 currentRate = getReflectRate();
        uint256 rCharityTaxFee = taxCharityFee.mul(currentRate);
        reflectTokensOwned[charityAddress] = reflectTokensOwned[charityAddress].add(rCharityTaxFee); 
        if (isAddressExcludedFromReflections[charityAddress]){
            totalTokensOwned[charityAddress] = totalTokensOwned[charityAddress].add(taxCharityFee);
        }
    }

    /** Takes the burn tax - used for calculating transactions. */
    function takeBurnFee(uint256 taxBurnFee) private {
        uint256 currentRate = getReflectRate();
        uint256 rBurnTaxFee = taxBurnFee.mul(currentRate);
        reflectTokensOwned[deadAddress] = reflectTokensOwned[deadAddress].add(rBurnTaxFee); 
        if (isAddressExcludedFromReflections[deadAddress]){
            totalTokensOwned[deadAddress] = totalTokensOwned[deadAddress].add(taxBurnFee);
        }
    }

    /** Takes the fun tax - used for calculating transactions. */
    function takeFunFee(uint256 taxFunFee) private {
        uint256 currentRate = getReflectRate();
        uint256 rFunTaxFee = taxFunFee.mul(currentRate);
        reflectTokensOwned[funAddress] = reflectTokensOwned[funAddress].add(rFunTaxFee); 
        if (isAddressExcludedFromReflections[funAddress]){
            totalTokensOwned[funAddress] = totalTokensOwned[funAddress].add(taxFunFee);
        }
    }

    /** Removes all taxes - used to correctly process transactions for whitelisted addresses. */
    function removeAllFee() private {
        previousReflectFeePercent = reflectFeePercent;
        previousCharityFeePercent = charityFeePercent;
        previousBurnFeePercent = burnFeePercent;
        previousFunFeePercent = funFeePercent;
        previousLiquidityFeePercent = liquidityFeePercent;

        reflectFeePercent = 0;
        charityFeePercent = 0;
        burnFeePercent = 0;
        funFeePercent = 0;
        liquidityFeePercent = 0;
    }

    /** Restores all taxes - used to correctly process transactions for non-whitelisted addresses. */
    function restoreAllFee() private {
        reflectFeePercent = previousReflectFeePercent;
        charityFeePercent = previousCharityFeePercent;
        burnFeePercent = previousBurnFeePercent;
        funFeePercent = previousFunFeePercent;
        liquidityFeePercent = previousLiquidityFeePercent;
    }


    /* PUBLIC RFI FUNCTIONS */
    
    /** Returns the total TENDIES supply in uint256 format - 1000000000000000 i.e. 1,000,000 tokens. */
    function totalSupply() external pure override returns (uint256){
        return TOKEN_MAX_SUPPLY;   
    }
    
    /** Returns the number of decimals the token - 9. */
    function decimals() external pure override returns (uint8) {
        return TOKEN_DECIMALS;  
    }

    /** Returns the token ticker - TENDIES. */
    function symbol() external pure override returns (string memory) {
        return TOKEN_TICKER;   
    }

    /** Returns the token name - Tendies. */
    function name() external pure override returns (string memory) {
        return TOKEN_NAME;   
    }

    /** Returns the TENDIES balance of the address queried in uint256 format e.g. 5000000000000 = 5,000 tokens. */
    function balanceOf(address account) public view override returns (uint256) {
        if (isAddressExcludedFromReflections[account]) {   
            return totalTokensOwned[account];
        }
        return tokenFromReflection(reflectTokensOwned[account]);
    }
    
    /** Returns the current block timestamp in unix format e.g. 1635354360. 
     *  Use https://time.is/Unix_time_converter to convert to human time. 
     */
    function getNowBlockTime() external view returns (uint) {
        return block.timestamp;     
    }

    /** Returns the timestamp the contract was deployed in unix format as above. */
    function releaseUnixTimeDate() external view returns (uint256) {
        return deployedUNIXTimeStamp;
    }
    
        /** Returns the total amount of tokens taxed in uint256 format. */
    function totalFees() external view returns (uint256) {
        return totalFeeAmount;
    }

    /** Returns the total amount of reflect tokens in uint256 format from a transfer amount with or without tax fees deducted. */
    function reflectionFromToken(uint256 tAmount, bool deductTransferFee) external view returns (uint256) {
        require(tAmount <= TOKEN_MAX_SUPPLY, "Amount exceeds max supply");         
        (uint256 rAmount, uint256 rTransferAmount, , ) = getTaxAndReflectionValues(tAmount);
        if(deductTransferFee){
            return rTransferAmount;
        }
        else{
            return rAmount;
        }
    }

    /** Returns the amount of reflect tokens in uint256 format from the current reflect supply. */
    function tokenFromReflection(uint256 rAmount) public view returns (uint256){  
        require(rAmount <= _rTotal, "Amount exceeds total reflections");
        uint256 currentRate = getReflectRate();
        return rAmount.div(currentRate);
    }

    /** Gets the current supply totals by calculating from every address. */
    function getCurrentSupplyTotals() public view returns (uint256, uint256) { 

        uint256 rSupply = _rTotal;      
        uint256 tSupply = TOKEN_MAX_SUPPLY;       

        for (uint256 i = 0; i < excludedFromReflectionsAddresses.length; i++) {
            if ((reflectTokensOwned[excludedFromReflectionsAddresses[i]] > rSupply) || (totalTokensOwned[excludedFromReflectionsAddresses[i]] > tSupply)){
                return (_rTotal, TOKEN_MAX_SUPPLY);       
            } 
            rSupply = rSupply.sub(reflectTokensOwned[excludedFromReflectionsAddresses[i]]);  
            tSupply = tSupply.sub(totalTokensOwned[excludedFromReflectionsAddresses[i]]);    
            
        }

        if (rSupply < _rTotal.div(TOKEN_MAX_SUPPLY)){     
            return (_rTotal, TOKEN_MAX_SUPPLY);
        } 

        return (rSupply, tSupply);
    }
    

    /** Queries whether an address is excluded from receiving reflections and gives a boolean return. */
    function isExcludedFromReflections(address account) external view returns (bool) {
        return isAddressExcludedFromReflections[account];
    }
    
    /** Queries whether an address is whitelisted from tax fees and gives a boolean return. */
    function isExcludedFromTax(address account) external view returns (bool) {
        return isAccountExcludedFromTax[account];
    }
    
    /** Queries whether an address is whitelisted from the max wallet amount and gives a boolean return. */
    function isExcludedFromMaxWallet(address account) external view returns (bool) {
        return isAccountExcludedFromMaxWallet[account];
    }
    
    /** Queries whether an address is whitelisted from the max Tx amount and gives a boolean return. */
    function isExcludedFromMaxTxAmount(address account) external view returns (bool) {
        return isAccountExcludedFromMaxTxAmount[account];
    }

    
    /* FALLBACK AND RECEIVE FUNCTIONS */
    
    // To receive CRO from the router when swapping.
    receive() external payable {}
    fallback() external payable {}
   
   
    /* ACCESS CONTROL FUNCTIONS */

    /** Returns the address of the current owner. */
    function owner() public view returns (address) {
        return ownerOfToken;       
    }
    
    /** As above. */
    function getOwner() external view override returns (address) {
        return owner(); 
    }
    
    /** Enables use of privileged functions that can only be called by the owner. */
    modifier onlyOwner() {
        require(ownerOfToken == _msgSender(), "Requires owner");
        _;      
    }
    
    /** Allows the owner to change the owner address e.g. to a multisig wallet. */
    function transferOwnership(address _newAddress) external onlyOwner() {     
        require(_newAddress != address(0), "Zero address");   
        emit OwnershipTransferred(ownerOfToken, _newAddress);
        previousOwnerOfToken = ownerOfToken;
        ownerOfToken = _newAddress;
    }  
   
   
    /* ALLOWANCE FUNCTIONS */

    /** Returns number of tokens remaining in uint256 format that spender is allowed to approve or transfer. */
    function allowance(address ownerAddr, address spender) external view override returns (uint256) { 
        return allowanceAmount[ownerAddr][spender]; 
    }

    /** Approves token spend for the spender address. */
    function approve(address spender, uint256 amount) external override returns (bool){
        approveInternal(_msgSender(), spender, amount);     
        return true;
    }

    /** Increases the token spend allowance for the spender address. */
    function increaseAllowance(address spender, uint256 addedValue) external virtual returns (bool){
        approveInternal(_msgSender(), spender, allowanceAmount[_msgSender()][spender].add(addedValue));
        return true;
    }

    /** Decreases the token spend allowance for the spender address. */
    function decreaseAllowance(address spender, uint256 subtractedValue) external virtual returns (bool){
        approveInternal(_msgSender(),spender,allowanceAmount[_msgSender()][spender].sub(subtractedValue,"Cannot decrease allowance below 0."));
        return true;
    }
    
    /** Internal function to approve token spend. */
    function approveInternal(address ownerAddr, address spender, uint256 amount) private { 
        require(ownerAddr != address(0) && spender != address(0), "Zero address");
        allowanceAmount[ownerAddr][spender] = amount;
        emit Approval(ownerAddr, spender, amount);
    }

    
    /* PRIVILEGED DEX-UPDATING FUNCTIONS */

    /**
     * Function: Allows the owner to change the DEX router address the contract uses.
     *  
     * Justification: It would be called in the unlikely event that the DEX re-deploys its router 
     *  or if there is ever a need to desire to move over to a different DEX. This kind of Function
     *  would not be necessary on an established chain using an established and trusted DEX such as
     *  Uniswap, however the owner believes that having this function is necessary because the
     *  Cronos chain is new at the time this contract is deployed, and there will be no established
     *  DEX as a result.
     * 
     * Risk statement: Owner could change the router address maliciously to a honeypot.
     * 
     * Mitigation: Safeguard prevents router being set to the zero address. 
     * Function emits an event on the explorer when called, clearly broadcasting
     *  the new router address for everytone to see.
     */
    function setRouterAddress(address _newAddress) external onlyOwner() {
        require(_newAddress != address(0), "Zero address");
        emit RouterAddressChanged(DEXRouterAddress, _newAddress);
        previousDEXRouterAddress = DEXRouterAddress;
        DEXRouterAddress = _newAddress;
        IDEXRouter02 DEXRouterLocal = IDEXRouter02(DEXRouterAddress);      
        DEXPair = IDEXFactory(DEXRouterLocal.factory()).createPair(address(this), DEXRouterLocal.WETH());     
        DEXRouter = DEXRouterLocal;   
    }

    /**
     * Function: Allows the owner to change the liquidity pair the contract uses.
     *  
     * Justification: It would be called in the plausible event that was ever a need or desire
     *  to move over to a different DEX, which would have its own LP addresses. In a likely 
     *  situation where we have liquidity in more than one LP pair or on more than one DEX, 
     *  it could be called to change which pair automatic liquidity is added to.
     * 
     * Risk statement: Owner could change the LP address maliciously in order to change where
     *  liquidity is added. 
     * 
     * Mitigation: Function emits an event on the explorer when called, clearly broadcasting
     *  the new pair address for everyone to see.
     */
    function setPairAddress(address _newAddress) external onlyOwner() {
        require(_newAddress != address(0), "Zero address");
        emit PairAddressChanged(DEXPair, _newAddress);
        previousDEXPair = DEXPair;
        DEXPair = _newAddress;
    }
    
    
    /* PRIVILEGED PROJECT ADDRESS-UPDATING FUNCTIONS */

    /**
     * These allow the owner to change the Charity, Growth or Fun wallets to any address.
     *  
     * Justification: They would be called in the unlikely event that access to the original 
     *  wallets are lost, or if there is a plausible need to change them due to security concerns.
     * 
     * Risk statement: Owner could change them to different addresses to obfuscate where the 
     *  taxed tokens are sent and then dump them undetected.
     * 
     * Mitigation: Functions emit an event on the explorer when called, clearly broadcasting
     *  the change of addresses. Any such events will be made clear to the community.
     */
     
    function updateCharityAddress(address _newAddress) external onlyOwner() {     
        require(_newAddress != address(0), "Zero address");
        require(_newAddress != charityAddress, "Address already set");
        emit CharityAddressChanged(charityAddress, _newAddress);
        previousCharityAddress = charityAddress;
        charityAddress = _newAddress;
    }
 
    /** Allows the owner to change the Growth wallet address as above. */
     function updateGrowthAddress(address _newGrowthAddress) external onlyOwner() {     
        require(_newGrowthAddress != address(0), "Zero address"); 
        require(_newGrowthAddress != growthAddress, "Address already set");
        emit GrowthAddressChanged(growthAddress, _newGrowthAddress);
        previousGrowthAddress = growthAddress;
        growthAddress = _newGrowthAddress;
    }
    
    /* Allows the owner to change the Fun wallet address as above. */
       function updateFunAddress(address _newFunAddress) external onlyOwner() {     
        require(_newFunAddress != address(0), "Zero address");  
        require(_newFunAddress != funAddress, "Address already set");
        emit FunAddressChanged(funAddress, _newFunAddress);
        previousFunAddress = funAddress;
        funAddress = _newFunAddress;
    }  


    /* PRIVILEGED FLEXIBLE TOKENOMICS FUNCTIONS */

     /**
     * Function: Allows the owner to set the max Tx limit to any value above 1000.
     *  
     * Justification: This is an indispensible element of our flexible tokenomics.
     *  The max Tx amount is initially set at 0 as an anti-snipe measure while the owner
     *  sets the conditions for a fair launch. It will then be set to 1000 for launch,
     *  and is intended to even the playing field and reduce Tx failure due to price impact.
     *  The limit will then gradually be raised shortly after launch to 2500 and then 5000. 
     *  Any changes thereafter will be decided with the input of the FrenDAO.
     * 
     * Risk statement: Owner could change the max Tx limit to any value above 1000 at any
     *  time due to the absence of a time-lock.
     * 
     * Mitigation: Safeguard added so that the limit cannot be changed to below 1000.
     *  Function emits an event on the explorer when called, clearly broadcasting
     *  the limit change. Limit will not be changed after being raised to 5000 without
     *  the input of the FrenDAO. The owner, Charity, Growth and Fun addresses are
     *  whitelisted from this limit by default, therefore this function does not pose an
     *  additional risk to the community.
     */
    function setMaxTxAmount (uint256 _newValue) external onlyOwner() {
        require(_newValue >= 1000 * 10**9, "Min limit 1000 tokens");
        require(_newValue != maxTxAmount, "Value already set");
        emit MaxTxAmountChanged(maxTxAmount, _newValue);
        previousMaxTxAmount = maxTxAmount;
        maxTxAmount = _newValue;
    }
    
    /**
     * Function: Allows the owner to set the max wallet limit to any value above 10000.
     *  
     * Justification: This is an indispensible element of our flexible tokenomics.
     *  The max wallet is initially set at 1000000 so that the owner can add initial
     *  liquidity without the Tx failing. It will then be set to 10000 before trading
     *  is enabled and will function as an anti-whale measure. Owner will gradually
     *  increase the limit in 2500 or 5000 intervals up to 25000 with the input of the
     *  FrenDAO. These measures ensure that the launch is as fair as possible.
     * 
     * Risk statement: Owner could change the max wallet limit at any time to any value
     *  between 5-10% due to the absence of a time-lock.
     * 
     * Mitigation: Safeguard added so that the limit cannot be changed to below 10000.
     *  Function emits an event on the explorer when called, clearly broadcasting
     *  the limit change. Limit will not be raised without the input of the FrenDAO.
     */
    
    /** Allows the owner to adjust the max wallet to an amount that must be greater than 10,000 tokens. */
    function setMaxWallet (uint256 _newValue) external onlyOwner() {
        require(_newValue >= 10000 * 10**9, "Min limit 10000 tokens");
        require(_newValue != maxWallet, "Value already set");
        emit MaxWalletChanged(maxWallet, _newValue);
        previousMaxWallet = maxWallet;
        maxWallet = _newValue;
    }

    /**
     * Function: Allows the owner to set the reflection tax between 5-10%.
     *  
     * Justification: This is an indispensible element of our flexible tokenomics.
     *  The reflection tax may be adjusted at the will of the FrenDAO.
     * 
     * Risk statement: Owner could change the reflection tax at any time to any value
     *  between 5-10% due to the absence of a time-lock.
     * 
     * Mitigation: Reasonable lower and upper bounds of 5% and 10% were chosen to ensure 
     *  the tax cannot be raised to a prohibitively high or unfairly low value. Function emits 
     *  an event on the explorer when called, clearly broadcasting the tax change. Tax will not 
     *  be changed unilaterally without the input of the FrenDAO. The owner, Charity, Growth
     *  and Fun addresses are whitelisted from all taxes by default, therefore there is no
     *  financial incentive for a malicious actor to decrease them.
     */
    function setReflectFeePercent(uint256 _newPercent) external onlyOwner() {
        require(_newPercent >= 5 && _newPercent <= 10, "Reflect fee must be between 5-10");
        require(_newPercent != reflectFeePercent, "Value already set");
        emit ReflectFeePercentChanged(reflectFeePercent, _newPercent);
        previousReflectFeePercent = reflectFeePercent;
        reflectFeePercent = _newPercent;
    }

    /**
     * Function: Allows the owner to set the charity tax between 0-3%.
     *  
     * Justification: As above. The lower bound is 0% in case there is a need to simplify
     *  the tokenomics in the distant future as CEXes do not tend to comply with non-liquidity
     *  taxes. In this case, the charity mechanism could be achieved by mobilizing TENDIES or
     *  LP generated from the auto-liquidity function.
     * 
     * Risk statement: Owner could change the charity tax at any time to any value
     *  between 0-3% due to the absence of a time-lock.
     * 
     * Mitigation: As above, with 0-3% bounds.
     */
    function setCharityFeePercent(uint256 _newPercent) external onlyOwner() {
        require(_newPercent >= 0 && _newPercent <= 3, "Charity fee must be between 0-3");
        require(_newPercent != charityFeePercent, "Value already set");
        emit CharityFeePercentChanged(charityFeePercent, _newPercent);
        previousCharityFeePercent = charityFeePercent;
        charityFeePercent = _newPercent;
    }

    /**
     * Function: Allows the owner to set the burn tax between 0-5%.
     *  
     * Justification: As above. Changing the burn tax is potentially one of the most
     * fun and effective ways of altering the token's dynamics, therefore the upper
     * bound is higher at 5%. TENDIES can be deflationary or not - frens decide.
     * 
     * Risk statement: Owner could change the burn tax at any time to any value
     *  between 0-5% due to the absence of a time-lock.
     * 
     * Mitigation: As above, with 0-5% bounds.
     */
    function setBurnFeePercent(uint256 _newPercent) external onlyOwner() {
        require(_newPercent >= 0 && _newPercent <= 5, "Burn fee must be between 0-5");
        require(_newPercent != burnFeePercent, "Value already set");
        emit BurnFeePercentChanged(burnFeePercent, _newPercent);
        previousBurnFeePercent = burnFeePercent;
        burnFeePercent = _newPercent;
    }

    /**
     * Function: Allows the owner to set the fun tax between 0-3%.
     *  
     * Justification: As above. Frens can decide how much they want given back
     * to the community directly.
     * 
     * Risk statement: Owner could change the fun tax at any time to any value
     *  between 0-3% due to the absence of a time-lock.
     * 
     * Mitigation: As above, with 0-3% bounds.
     */
    function setFunFeePercent(uint256 _newPercent) external onlyOwner() {
        require(_newPercent >= 0 && _newPercent <= 3, "Fun fee must be between 0-3");
        require(_newPercent != funFeePercent, "Value already set");
        emit FunFeePercentChanged(funFeePercent, _newPercent);
        previousFunFeePercent = funFeePercent;
        funFeePercent = _newPercent;
    }

    /**
     * Function: Allows the owner to set the liquidity tax between 1-5%.
     *  
     * Justification: As above. Managing liquidity carefully is crucial and this
     *  is achieved by adjusting the liquidity tax along with the conversion threshold.
     *  If liquidity is particularly strong, the FrenDAO may wish to prioritize
     *  other aspects of the tokenomics.
     * 
     * Risk statement: Owner could change the liquidty tax at any time to any value
     *  between 1-5% due to the absence of a time-lock.
     * 
     * Mitigation: As above, with 1-5% bounds. The owner believes that there
     *  should always be some amount of auto-liquidity, therefore the lower bound
     *  is 1% rather than 0%.
     */
    function setLiquidityFeePercent(uint256 _newPercent) external onlyOwner() {
        require(_newPercent >= 1 && _newPercent <= 5, "Liquidity fee must be between 1-5");
        require(_newPercent != liquidityFeePercent, "Value already set");
        emit LiquidityFeePercentChanged(liquidityFeePercent, _newPercent);
        previousLiquidityFeePercent = liquidityFeePercent;
        liquidityFeePercent = _newPercent;
    }
    
    /**
     * Function: Allows the owner to toggle automatic liquidity on and off.
     *  
     * Justification: Though deemed unlikely, being able to momentarily disable 
     *  auto-liquidity is important to be able to resolve hypothetical issues
     *  that may arise, such as being unable to withdraw TENDIES from a token
     *  locker because the tokenomics cause the Tx to fail, or gas estimation
     *  issues on sells due to the additional gas burden auto-liquidity adds.
     * 
     * Risk statement: Owner could toggle auto-liquidity off for an extended period
     *  which would result in a significant build-up of TENDIES in this contract address
     *  due to the tax and reflections gained. The TENDIES could then be recovered from
     *  the contract by the owner and dumped.
     * 
     * Mitigation: Function emits an event on the explorer when called, clearly broadcasting
     *  when auto-liquidity is toggled on or off.
     */
    function setAutoLiquidityEnabled(bool enableAutoLiquidity) external onlyOwner() {     
        isAutoLiquidityEnabled = enableAutoLiquidity;   
        emit AutoLiquidityEnabledUpdated(enableAutoLiquidity);
    }

    /**
     * Function: Allows the owner to set the amount of TENDIES in the token contract
     *  that will trigger auto-liquidity betweeen 50-500.
     *  
     * Justification: Being able to adjust the auto-liquidity threshold is essential for
     *  a number of reasons: at launch when the market cap is low and the number of TENDIES
     *  traded is high, given that the initial liquidity tax is 3%, a max Tx of 5000 tokens
     *  would send roughly 150 tokens to the contract address. If the threshold was set too
     *  low, at 50 for example, TENDIES would build up over time and then as the market cap
     *  increases, the LP conversion would cause significant sell pressure. It is therefore
     *  appropriate that the threshold is set at a rate proportional to the max Tx initially,
     *  and then gradually reduced over time as the market cap increases and the number of
     *  TENDIES in the average Tx decreases relative to the max supply.
     * 
     * Risk statement: Owner could set threshold as low as possible in order to increase the
     *  number of TENDIES that accumulate in the TENDIES contract, then recover them from them
     *  contract and dump them.
     * 
     * Mitigation: Reasonable lower and upper bounds of 50 and 500 were chosen to minimize the
     *  potential impact that can be caused when the function is called. Function emits an event 
     *  on the explorer when called, clearly broadcasting when auto-liquidity is toggled on or off.
     */
    function setAutoLiquidityThreshold(uint256 _newValue) external onlyOwner() { 
        require(_newValue >= 50 * 10**9 && _newValue <= 500 * 10**9, "Threshold must be between 50-500");
        emit AutoLiquidityThresholdChanged(autoLiquidityThreshold, _newValue);
        previousAutoLiquidityThreshold = autoLiquidityThreshold;
        autoLiquidityThreshold = _newValue;
    }
    
    
    /* PRIVILEGED WHITELISTING FUNCTIONS */

    /**
     * Function: Allows the owner to exclude any address from receiving reflections.
     *  
     * Justification: The most important use of this function is to exclude LP addresses
     *  from reflections, otherwise they would swallow up the vast majority of them
     *  because they hold most of the TENDIES. The burn address is also excluded for The
     *  same reason. Finally, the function exists to deter anyone from attempting to
     *  circumvent the anti-whale measures by filling multiple wallets with the max amount
     *  on launch. It will only be used for this purpose if the abuser in question is
     *  deemed to threaten the future of the project by holding more TENDIES than intended,
     *  and will never be used without fair warning first.
     * 
     * Risk statement: Owner could toggle auto-liquidity off for an extended period
     *  which would result in a significant build-up of TENDIES in this contract address
     *  due to the tax and reflections gained. The TENDIES could then be recovered from
     *  the contract by the owner and dumped.
     * 
     * Mitigation: There is no mitigation for this in principle. The owner recognises that
     *  it is a powerful permission that must not ever be abused.
     */
    function excludeFromReflections(address _address) external onlyOwner() {
        require(_address != DEXRouterAddress, "Router exclusion disallowed");    
        require(!isAddressExcludedFromReflections[_address], "Address is already excluded");
        emit AddressExcludedFromReflections(_address);
        if (reflectTokensOwned[_address] > 0) {
            totalTokensOwned[_address] = tokenFromReflection(reflectTokensOwned[_address]);   
        }
        isAddressExcludedFromReflections[_address] = true;
        excludedFromReflectionsAddresses.push(_address);
    }

    /**
     * Function: Allows the owner to re-include any address in receiving reflections.
     *  
     * Justification: As above - this function could be called to re-include an abuser
     *  in reflections.
     * 
     * Risk statement: Owner could include the main TENDIES LP address in reflections to
     *  significantly reduce reflections earned by everyone else, but there would be no
     *  incentive for doing so.
     * 
     * Mitigation: There is no mitigation for this in principle. This include function is
     *  of much less importance than the exclude function.
     */
    function includeInReflections(address _address) external onlyOwner() {
        require(isAddressExcludedFromReflections[_address], "Address is already included");
        emit AddressIncludedInReflections(_address);
        for (uint256 i = 0; i < excludedFromReflectionsAddresses.length; i++) {
            if (excludedFromReflectionsAddresses[i] == _address) {
                excludedFromReflectionsAddresses[i] = excludedFromReflectionsAddresses[excludedFromReflectionsAddresses.length - 1];
                totalTokensOwned[_address] = 0;
                isAddressExcludedFromReflections[_address] = false;
                excludedFromReflectionsAddresses.pop();
                break;
            }
        }
    }

    /**
     * Function: Allows the owner to whitelist any address from all taxes.
     *  
     * Justification: This function exists primarily to whitelist TENDIES pools so that 
     *  frens do not get hammered by tax from staking and unstaking. It can also be used
     *  to whitelist the Fren Co. wallet addresses should they ever be changed. Moreover,
     *  to whitelist a zapper contract so that frens can use farms completely tax-free.
     * 
     * Risk statement: No additional risk posed due to the fact that the owner, Growth,
     *  Charity and Fun wallets are already whitelisted from taxes by default.
     * 
     * Mitigation: N/A.
     */
    function excludeFromTax(address _address) external onlyOwner() {
        isAccountExcludedFromTax[_address] = true;
        emit AddressWhitelistedFromTax(_address);
    }

    /**
     * Function: Allows the owner to re-include any address in all taxes.
     *  
     * Justification: As above - this function will rarely be called, in order to
     *  de-whitelist a wallet address from taxes after having been temporarily
     *  whitelisted.
     * 
     * Risk statement: Owner could de-whitelist the contract addresses of pools, farms
     *  or zappers, resulting in unexpected tax fees. The additional revenue could then
     *  be siphoned off, but there would be little incentive in doing so as a malicious
     *  actor could operate in simpler ways to have a greater impact.
     * 
     * Mitigation: N/A.
     */
    function includeInTax(address _address) external onlyOwner() {
        isAccountExcludedFromTax[_address] = false;
        emit AddressIncludedInTax(_address);
    }


    /**
     * Function: Allows the owner to whitelist any address from the max wallet limit.
     *  
     * Justification: This function is critical to be able to solve a number of potential
     *  issues: a fren may be unable to unstake TENDIES from a pool, harvest from a farm or 
     *  break LP if the Tx would take them over the max wallet limit; a fren may be unable 
     *  to receive TENDIES from giveaways and competitions. These isues are only expected to
     *  arise if at all shortly after launch before the market cap has matured. Thereafter,
     *  it is expected that the majority of wallets will not exceed the max wallet amount,
     *  which will have been raised since launch. It could also be used to whitelist new
     *  Fren Co. addresses and LP addresses if the need arises.
     * 
     * Risk statement: No additional risk posed due to the fact that the owner, Growth,
     *  Charity and Fun wallets are already whitelisted from the max wallet limit by default.
     * 
     * Mitigation: N/A.
     */
    function excludeFromMaxWallet(address _address) external onlyOwner() {
        isAccountExcludedFromMaxWallet[_address] = true;
        emit AddressWhitelistedFromMaxWallet(_address);
    }
    
    /**
     * Function: Allows the owner to re-include any address in the max wallet limit.
     *  
     * Justification: As above - this function will rarely be called, in order to
     *  de-whitelist a wallet address from the max wallet limit after having been 
     *  temporarily whitelisted.
     * 
     * Risk statement: Owner could de-whitelist the contract addresses for pools
     *  to break deposits but not withdrawals, but there would be little incentive
     *  to do so.
     * 
     * Mitigation: N/A.
     */
    function includeInMaxWallet(address _address) external onlyOwner() {
        isAccountExcludedFromMaxWallet[_address] = false;
        emit AddressIncludedInMaxWallet(_address);
    }
    
    /**
     * Function: Allows the owner to whitelist any address from the max Tx limit.
     *  
     * Justification: This function may be called to resolve issues where frens are unable
     *  to unstake TENDIES or break LP due to it violating the max Tx limit. It can also be used
     *  to whitelist new Fren Co. addresses should the need ever arise to change them.
     * 
     * Risk statement: No additional risk posed due to the fact that the owner, Growth,
     *  Charity and Fun wallets are already whitelisted from the max Tx limit by default.
     *  Only the owner address is able to evade the max Tx limit while swapping, whereas 
     *  all the above addresses can evade it while sending.
     * 
     * Mitigation: N/A.
     */
    function excludeFromMaxTxAmount(address _address) external onlyOwner() {
        isAccountExcludedFromMaxTxAmount[_address] = true;
        emit AddressWhitelistedFromMaxTx(_address);
    }
    
    /**
     * Function: Allows the owner to re-include any address in the max Tx limit.
     *  
     * Justification: As above - this function will rarely be called, in order to
     *  de-whitelist a wallet address from the max Tx limit after having been 
     *  temporarily whitelisted.
     * 
     * Risk statement: No additional risk posed due to the fact that the owner, Growth,
     *  Charity and Fun wallets are already whitelisted from the max Tx limit by default.
     * 
     * Mitigation: N/A.
     */
    function includeInMaxTxAmount(address _address) external onlyOwner() {
        isAccountExcludedFromMaxTxAmount[_address] = false;
        emit AddressIncludedInMaxTx(_address);
    }

    /* PRIVILEGED TOKEN RECOVER FUNCTIONS */
    
    /**
     * Function: Allows the owner to recover a specific amount of TENDIES from the TENDIES 
     *  contract.
     * 
     * Justification: Someone ending TENDIES to the contract address will almost certainly happen 
     *  at some point. This function allows the owner to recover the TENDIES lost so they can be 
     *  returned to the sender.
     * 
     * Risk statement: It is not expected that the contract will hold TENDIES unless someone
     *  accidentally sends them. Owner could choose not to return the TENDIES but there would
     *  be little incentive to do so.
     * 
     * Mitigation: N/A.
     */
    function recoverTendies(uint256 _amount) external onlyOwner() {
        require (_amount <= balanceOf(address(this)), "Not enough TENDIES in contract");
        emit TendiesRecovered(payableAddr(), _amount);
        transferInternal(address(this), payableAddr(), _amount);
    }
    
    /**
     * Function: Allows the owner to withdraw the total balance of a specific CRC20 token 
     *  from the TENDIES contract.
     *  
     * Justification: As above but for any token. The owner does not preclude the possibility of 
     *  taking some of this LP, breaking it, distributing the TENDIES back to the community via 
     *  the Growth wallet and keeping some CRO for personal use. This is hoped to be viewed as a 
     *  reasonable action due to the fact that 0% of the initial supply is held in reserve as an ROI. 
     * 
     * Risk statement: As above but for any token. Could be called to withdraw TENDIES awaiting
     *  LP conversion but this is redundant with the function below. Owner could withdraw all LP
     *  which would be of significant value once the market cap has matured and dump it.
     *  Clearly this presents a centralization risk and requires an amount of trust. It also 
     *  exists for if there arises a need or desire to add liquidity to another DEX before the LP
     *  from the initial liquidity has unlocked from the LP locker. 
     * 
     * Mitigation: Owner will regularly withdraw and lock or burn the accumulated LP
     *  to build trust and prevent it from creating a large centralization risk. Any intentions 
     *  to withdraw LP from the contract will be clearly announced and the input of the FrenDAO 
     *  will be welcomed.
     */
    function recoverCRC20(address _token) external onlyOwner() {
        emit CRC20Recovered(_token, payableAddr(), ICRC20(_token).balanceOf(address(this)));
        ICRC20(_token).safeTransfer(payableAddr(), ICRC20(_token).balanceOf(address(this)));
    }
    
    /**
     * Function: Allows the owner to withdraw all CRO from the TENDIES contract.
     *  
     * Justification: The reason this is called a recover function is that its intended
     *  use is to recover CRO sent accidentally to the TENDIES contract address. This is
     *  a common and often costly mistake that usually results in tokens being lost forever.
     *  Some CRO will build up in the contract over time as a byproduct of the autoLiquidity 
     *  function. This is because every time half the amount of the auto-liquidity threshold 
     *  in TENDIES is swapped for CRO and combined to form LP, the swap itself marginally reduces 
     *  the price of TENDIES. The autoLiquidity function does not account for this the next 
     *  time it is called.
     * 
     * Risk statement: The amount of CRO that builds up due to auto-liquidity is likely to
     *  become significant over time, and this presents a centralization risk. 
     * 
     * Mitigation: The CRO will most likely be locked or redistributed to the community, with a 
     *  reasonable amount that could be kept aside for the owner's personal use after consultation 
     *  with the FrenDAO.
     */
    function recoverCRO() external onlyOwner()  { 
        emit CRORecovered(payableAddr(), balanceOf(address(this)));
        payableAddr().transfer(address(this).balance);
    }
    
    /** Gets the address to which tokens can be recovered from the TENDIES contract - the owner wallet. */
    function payableAddr() private view returns (address payable) {
        address payable payableMsgSender = payable(owner());      
        return payableMsgSender;
    }
    
}
        

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[]},{"type":"event","name":"AddressExcludedFromReflections","inputs":[{"type":"address","name":"excludedAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AddressIncludedInMaxTx","inputs":[{"type":"address","name":"addressIncludedInMaxTx","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AddressIncludedInMaxWallet","inputs":[{"type":"address","name":"includedAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AddressIncludedInReflections","inputs":[{"type":"address","name":"includedAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AddressIncludedInTax","inputs":[{"type":"address","name":"includedAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AddressWhitelistedFromMaxTx","inputs":[{"type":"address","name":"whitelistedAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AddressWhitelistedFromMaxWallet","inputs":[{"type":"address","name":"whitelistedAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"AddressWhitelistedFromTax","inputs":[{"type":"address","name":"whitelistedAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"Approval","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"address","name":"spender","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"AutoLiquidity","inputs":[{"type":"uint256","name":"tokensSwapped","internalType":"uint256","indexed":false},{"type":"uint256","name":"croReceived","internalType":"uint256","indexed":false},{"type":"uint256","name":"tokensIntoLiquidity","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"AutoLiquidityEnabledUpdated","inputs":[{"type":"bool","name":"enabled","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"AutoLiquidityThresholdChanged","inputs":[{"type":"uint256","name":"previousAutoLiquidityThreshold","internalType":"uint256","indexed":true},{"type":"uint256","name":"newAutoLiquidityThreshold","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"BurnFeePercentChanged","inputs":[{"type":"uint256","name":"previousBurnFeePercent","internalType":"uint256","indexed":true},{"type":"uint256","name":"newBurnFeePercent","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"CRC20Recovered","inputs":[{"type":"address","name":"token","internalType":"address","indexed":true},{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"CRORecovered","inputs":[{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"CharityAddressChanged","inputs":[{"type":"address","name":"previousCharityAddress","internalType":"address","indexed":true},{"type":"address","name":"newCharityAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"CharityFeePercentChanged","inputs":[{"type":"uint256","name":"previousCharityFeePercent","internalType":"uint256","indexed":true},{"type":"uint256","name":"newCharityFeePercent","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"FunAddressChanged","inputs":[{"type":"address","name":"previousFunAddress","internalType":"address","indexed":true},{"type":"address","name":"newFunAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"FunFeePercentChanged","inputs":[{"type":"uint256","name":"previousFunFeePercent","internalType":"uint256","indexed":true},{"type":"uint256","name":"newFunFeePercent","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"GrowthAddressChanged","inputs":[{"type":"address","name":"previousGrowthAddress","internalType":"address","indexed":true},{"type":"address","name":"newGrowthAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"LiquidityFeePercentChanged","inputs":[{"type":"uint256","name":"previousLiquidityFeePercent","internalType":"uint256","indexed":true},{"type":"uint256","name":"newLiquidityFeePercent","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MaxTxAmountChanged","inputs":[{"type":"uint256","name":"previousMaxTxAmount","internalType":"uint256","indexed":true},{"type":"uint256","name":"newMaxTxAmount","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"MaxWalletChanged","inputs":[{"type":"uint256","name":"previousMaxWallet","internalType":"uint256","indexed":true},{"type":"uint256","name":"newMaxWallet","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"PairAddressChanged","inputs":[{"type":"address","name":"previousDEXPair","internalType":"address","indexed":true},{"type":"address","name":"newDEXPair","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"ReflectFeePercentChanged","inputs":[{"type":"uint256","name":"previousReflectFeePercent","internalType":"uint256","indexed":true},{"type":"uint256","name":"newReflectFeePercent","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"RouterAddressChanged","inputs":[{"type":"address","name":"previousRouterAddress","internalType":"address","indexed":true},{"type":"address","name":"newRouterAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"TendiesRecovered","inputs":[{"type":"address","name":"recipient","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"Transfer","inputs":[{"type":"address","name":"from","internalType":"address","indexed":true},{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"value","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"fallback","stateMutability":"payable"},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"DEXPair","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract IDEXRouter02"}],"name":"DEXRouter","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"DEXRouterAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"ownerAddr","internalType":"address"},{"type":"address","name":"spender","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"approve","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"autoLiquidityThreshold","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"balanceOf","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"burnFeePercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"charityAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"charityFeePercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"deadAddress","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"decreaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"subtractedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"dexPadAirdropperAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"dexPadLPLockerAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"dexPadTokenLockerAddress","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"excludeFromMaxTxAmount","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"excludeFromMaxWallet","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"excludeFromReflections","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"excludeFromTax","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"funAddress","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"funFeePercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"},{"type":"uint256","name":"","internalType":"uint256"}],"name":"getCurrentSupplyTotals","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"getNowBlockTime","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"getOwner","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"growthAddress","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"includeInMaxTxAmount","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"includeInMaxWallet","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"includeInReflections","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"includeInTax","inputs":[{"type":"address","name":"_address","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"increaseAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"addedValue","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isAutoLiquidityEnabled","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isExcludedFromMaxTxAmount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isExcludedFromMaxWallet","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isExcludedFromReflections","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"isExcludedFromTax","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"liquidityFeePercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxTxAmount","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"maxWallet","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"owner","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverCRC20","inputs":[{"type":"address","name":"_token","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverCRO","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"recoverTendies","inputs":[{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"reflectFeePercent","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"reflectionFromToken","inputs":[{"type":"uint256","name":"tAmount","internalType":"uint256"},{"type":"bool","name":"deductTransferFee","internalType":"bool"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"releaseUnixTimeDate","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAutoLiquidityEnabled","inputs":[{"type":"bool","name":"enableAutoLiquidity","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setAutoLiquidityThreshold","inputs":[{"type":"uint256","name":"_newValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBurnFeePercent","inputs":[{"type":"uint256","name":"_newPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setCharityFeePercent","inputs":[{"type":"uint256","name":"_newPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setFunFeePercent","inputs":[{"type":"uint256","name":"_newPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setLiquidityFeePercent","inputs":[{"type":"uint256","name":"_newPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMaxTxAmount","inputs":[{"type":"uint256","name":"_newValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setMaxWallet","inputs":[{"type":"uint256","name":"_newValue","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setPairAddress","inputs":[{"type":"address","name":"_newAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setReflectFeePercent","inputs":[{"type":"uint256","name":"_newPercent","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRouterAddress","inputs":[{"type":"address","name":"_newAddress","internalType":"address"}]},{"type":"function","stateMutability":"pure","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"tokenFromReflection","inputs":[{"type":"uint256","name":"rAmount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalFees","inputs":[]},{"type":"function","stateMutability":"pure","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"totalSupply","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transfer","inputs":[{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"sender","internalType":"address"},{"type":"address","name":"recipient","internalType":"address"},{"type":"uint256","name":"amount","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"_newAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateCharityAddress","inputs":[{"type":"address","name":"_newAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateFunAddress","inputs":[{"type":"address","name":"_newFunAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"updateGrowthAddress","inputs":[{"type":"address","name":"_newGrowthAddress","internalType":"address"}]},{"type":"receive","stateMutability":"payable"}]
            

Deployed ByteCode

0x6080604052600436106103f75760003560e01c806370a082311161020a578063b9d4400e11610119578063e17c4c74116100b2578063eefbbaab11610084578063f2fde38b1161006c578063f2fde38b14610cbe578063f67d22bf14610cde578063f8b45b0514610cfe57005b8063eefbbaab14610c8b578063f15c8b6514610cab57005b8063e17c4c7414610bf2578063e449aa8114610c12578063e6375d3e14610c32578063ec28438a14610c6b57005b8063ce5d47c6116100eb578063ce5d47c614610b33578063cea2695814610b53578063d3b64ecd14610b73578063dd62ed3e14610bac57005b8063b9d4400e14610aa4578063ba26b4a714610ac4578063cb4ca63114610ada578063ce5be4d214610b1357005b80638ee88c53116101a3578063a9059cbb11610175578063ae5a17a71161015d578063ae5a17a714610a44578063af41063b14610a64578063afcf2fc414610a8457005b8063a9059cbb14610a04578063ac22661c14610a2457005b80638ee88c531461095e57806395d89b411461097e578063a22d4832146109c4578063a457c2d7146109e457005b806384331512116101dc57806384331512146108ff578063893d20e8146109155780638c0b5e221461092a5780638da5cb5b1461094057005b806370a082311461087657806375c5f6e4146108965780637da4869f146108c95780637e3f5606146108df57005b8063313ce5671161030657806341cb87fc1161029f5780635c59523b1161027157806360d1259e1161025957806360d1259e146108075780636dd3d39f146108275780636e2310b21461086057005b80635c59523b146107c75780635d0044ca146107e757005b806341cb87fc146107515780634549b039146107715780634e0856a7146107915780635b700d91146107a757005b806339509351116102d857806339509351146106db5780633b540996146106fb5780633bb48ff7146107115780633f33e9091461073157005b8063313ce5671461065f5780633211a83d1461067b578063345a8b591461069b578063355326eb146106bb57005b806318160ddd1161039057806327c8f835116103625780632d8381191161034a5780632d838119146105ff5780632daa73f71461061f5780632f1191431461063f57005b806327c8f835146105bf578063285e801f146105d557005b806318160ddd1461054557806323b872dd1461055f57806325c5a5521461057f57806325f25f4d1461059f57005b806306fdde03116103c957806306fdde0314610479578063095ea7b3146104c857806313114a9d146104f8578063179795dd1461050d57005b8063044b5d4614610400578063056bd30a1461042057806305f5d3b31461044457806306ea73841461046457005b366103fe57005b005b34801561040c57600080fd5b506103fe61041b366004614591565b610d14565b34801561042c57600080fd5b506000545b6040519081526020015b60405180910390f35b34801561045057600080fd5b506103fe61045f3660046146ab565b610fa0565b34801561047057600080fd5b506103fe611099565b34801561048557600080fd5b5060408051808201909152600781527f54656e646965730000000000000000000000000000000000000000000000000060208201525b60405161043b919061474c565b3480156104d457600080fd5b506104e86104e3366004614645565b61117c565b604051901515815260200161043b565b34801561050457600080fd5b50600554610431565b34801561051957600080fd5b5060115461052d906001600160a01b031681565b6040516001600160a01b03909116815260200161043b565b34801561055157600080fd5b5066038d7ea4c68000610431565b34801561056b57600080fd5b506104e861057a366004614604565b611193565b34801561058b57600080fd5b50601f5461052d906001600160a01b031681565b3480156105ab57600080fd5b506103fe6105ba366004614591565b6111fd565b3480156105cb57600080fd5b5061052d61dead81565b3480156105e157600080fd5b506105ea611448565b6040805192835260208301919091520161043b565b34801561060b57600080fd5b5061043161061a3660046146ab565b6115dc565b34801561062b57600080fd5b506103fe61063a366004614591565b611646565b34801561064b57600080fd5b506103fe61065a366004614591565b611823565b34801561066b57600080fd5b506040516009815260200161043b565b34801561068757600080fd5b5060105461052d906001600160a01b031681565b3480156106a757600080fd5b5060245461052d906001600160a01b031681565b3480156106c757600080fd5b506103fe6106d6366004614591565b6118e4565b3480156106e757600080fd5b506104e86106f6366004614645565b611a74565b34801561070757600080fd5b5061043160085481565b34801561071d57600080fd5b506103fe61072c3660046146ab565b611aaa565b34801561073d57600080fd5b506103fe61074c366004614591565b611bed565b34801561075d57600080fd5b506103fe61076c366004614591565b611cae565b34801561077d57600080fd5b5061043161078c3660046146dd565b611fc9565b34801561079d57600080fd5b50610431600a5481565b3480156107b357600080fd5b506103fe6107c2366004614591565b61204b565b3480156107d357600080fd5b506103fe6107e2366004614591565b61210f565b3480156107f357600080fd5b506103fe6108023660046146ab565b61229f565b34801561081357600080fd5b506103fe610822366004614591565b6123da565b34801561083357600080fd5b506104e8610842366004614591565b6001600160a01b031660009081526029602052604090205460ff1690565b34801561086c57600080fd5b50610431600e5481565b34801561088257600080fd5b50610431610891366004614591565b61249e565b3480156108a257600080fd5b506014546104e8907501000000000000000000000000000000000000000000900460ff1681565b3480156108d557600080fd5b5061043160195481565b3480156108eb57600080fd5b506103fe6108fa366004614591565b6124fd565b34801561090b57600080fd5b50610431600c5481565b34801561092157600080fd5b5061052d6125c1565b34801561093657600080fd5b5061043160155481565b34801561094c57600080fd5b50601b546001600160a01b031661052d565b34801561096a57600080fd5b506103fe6109793660046146ab565b6125da565b34801561098a57600080fd5b5060408051808201909152600781527f54454e444945530000000000000000000000000000000000000000000000000060208201526104bb565b3480156109d057600080fd5b506103fe6109df366004614591565b612743565b3480156109f057600080fd5b506104e86109ff366004614645565b612875565b348015610a1057600080fd5b506104e8610a1f366004614645565b6128c4565b348015610a3057600080fd5b506103fe610a3f366004614671565b6128d1565b348015610a5057600080fd5b506103fe610a5f366004614591565b6129b0565b348015610a7057600080fd5b506103fe610a7f3660046146ab565b612b40565b348015610a9057600080fd5b5060215461052d906001600160a01b031681565b348015610ab057600080fd5b506103fe610abf3660046146ab565b612c76565b348015610ad057600080fd5b5061043160065481565b348015610ae657600080fd5b506104e8610af5366004614591565b6001600160a01b031660009081526028602052604090205460ff1690565b348015610b1f57600080fd5b506103fe610b2e3660046146ab565b612d78565b348015610b3f57600080fd5b5060255461052d906001600160a01b031681565b348015610b5f57600080fd5b506103fe610b6e3660046146ab565b612eae565b348015610b7f57600080fd5b506104e8610b8e366004614591565b6001600160a01b03166000908152602a602052604090205460ff1690565b348015610bb857600080fd5b50610431610bc73660046145cb565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b348015610bfe57600080fd5b506103fe610c0d366004614591565b612fe4565b348015610c1e57600080fd5b50601d5461052d906001600160a01b031681565b348015610c3e57600080fd5b506104e8610c4d366004614591565b6001600160a01b031660009081526026602052604090205460ff1690565b348015610c7757600080fd5b506103fe610c863660046146ab565b6130a5565b348015610c9757600080fd5b5060235461052d906001600160a01b031681565b348015610cb757600080fd5b5042610431565b348015610cca57600080fd5b506103fe610cd9366004614591565b6131df565b348015610cea57600080fd5b5060135461052d906001600160a01b031681565b348015610d0a57600080fd5b5061043160175481565b601b546001600160a01b03163314610d735760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e657200000000000000000000000000000000000060448201526064015b60405180910390fd5b6001600160a01b03811660009081526026602052604090205460ff16610ddb5760405162461bcd60e51b815260206004820152601b60248201527f4164647265737320697320616c726561647920696e636c7564656400000000006044820152606401610d6a565b6040516001600160a01b038216907fd687405d4d2679f959ddd9e06266152c7184c21950c54c2b8ec996407f7591f690600090a260005b602754811015610f9c57816001600160a01b031660278281548110610e3957610e3961497c565b6000918252602090912001546001600160a01b03161415610f8a5760278054610e649060019061489e565b81548110610e7457610e7461497c565b600091825260209091200154602780546001600160a01b039092169183908110610ea057610ea061497c565b600091825260208083209190910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03948516179055918416815260038252604080822082905560269092522080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001690556027805480610f2e57610f2e61494d565b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff00000000000000000000000000000000000000001690550190555050565b80610f94816148e5565b915050610e12565b5050565b601b546001600160a01b03163314610ffa5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b640ba43b74008110158015611014575064746a5288008111155b6110605760405162461bcd60e51b815260206004820181905260248201527f5468726573686f6c64206d757374206265206265747765656e2035302d3530306044820152606401610d6a565b6019546040518291907fefcf7b0651690fde95da14220fbfe2424088cf3728e0d4f4dfd6dcfb03e04d6590600090a360198054601a5555565b601b546001600160a01b031633146110f35760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6110fc3061249e565b611104613311565b6001600160a01b03167f24e1bea80368f2a20bddbf4391a480ca73f2cf7a85142b0baebc24a0f212f8ea60405160405180910390a3611141613311565b6001600160a01b03166108fc479081150290604051600060405180830381858888f19350505050158015611179573d6000803e3d6000fd5b50565b6000611189338484613326565b5060015b92915050565b60006111a08484846133f3565b6111f284336111ed856040518060600160405280602181526020016149cf602191396001600160a01b038a16600090815260016020908152604080832033845290915290205491906135cc565b613326565b5060015b9392505050565b601b546001600160a01b031633146112575760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6013546001600160a01b03828116911614156112b55760405162461bcd60e51b815260206004820152601b60248201527f526f75746572206578636c7573696f6e20646973616c6c6f77656400000000006044820152606401610d6a565b6001600160a01b03811660009081526026602052604090205460ff161561131e5760405162461bcd60e51b815260206004820152601b60248201527f4164647265737320697320616c7265616479206578636c7564656400000000006044820152606401610d6a565b6040516001600160a01b038216907f51c333fec5f0c391a6c8887cb7828501ba94881666ca33105f3f669f38dbe2ac90600090a26001600160a01b038116600090815260026020526040902054156113ac576001600160a01b038116600090815260026020526040902054611392906115dc565b6001600160a01b0382166000908152600360205260409020555b6001600160a01b0316600081815260266020526040812080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660019081179091556027805491820181559091527f98a476f1687bc3d60a2da2adbcba2c46958e61fa2fb4042cd7bc5816a710195b0180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169091179055565b600454600090819066038d7ea4c68000825b6027548110156115a35782600260006027848154811061147c5761147c61497c565b60009182526020808320909101546001600160a01b0316835282019290925260400190205411806114e757508160036000602784815481106114c0576114c061497c565b60009182526020808320909101546001600160a01b03168352820192909252604001902054115b156115015750506004549366038d7ea4c680009350915050565b611547600260006027848154811061151b5761151b61497c565b60009182526020808320909101546001600160a01b0316835282019290925260400190205484906135f8565b925061158f60036000602784815481106115635761156361497c565b60009182526020808320909101546001600160a01b0316835282019290925260400190205483906135f8565b91508061159b816148e5565b91505061145a565b506004546115b89066038d7ea4c68000613604565b8210156115d35750506004549266038d7ea4c6800092509050565b90939092509050565b60006004548211156116305760405162461bcd60e51b815260206004820181905260248201527f416d6f756e74206578636565647320746f74616c207265666c656374696f6e736044820152606401610d6a565b600061163a613610565b90506111f68382613604565b601b546001600160a01b031633146116a05760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038216906370a082319060240160206040518083038186803b1580156116f857600080fd5b505afa15801561170c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173091906146c4565b611738613311565b6001600160a01b0316826001600160a01b03167f97777e0d049e8c6ea21e08153c603fbd56f2276944e9fa416ae55f3e4997a36a60405160405180910390a4611179611782613311565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b038416906370a082319060240160206040518083038186803b1580156117da57600080fd5b505afa1580156117ee573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061181291906146c4565b6001600160a01b0384169190613633565b601b546001600160a01b0316331461187d5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b0381166000818152602a602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517f1fd984041c31c7563135d9eb2665164a9e4de93558028c6a587e208386aec99c9190a250565b601b546001600160a01b0316331461193e5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b0381166119945760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b601f546001600160a01b03828116911614156119f25760405162461bcd60e51b815260206004820152601360248201527f4164647265737320616c726561647920736574000000000000000000000000006044820152606401610d6a565b601f546040516001600160a01b038084169216907f774cfaaa1d8772aa9fafd8157e93b89d7a445fe0036d38364bc16cb72969be0990600090a3601f8054602080546001600160a01b038084167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092559091169216919091179055565b3360008181526001602090815260408083206001600160a01b038716845290915281205490916111899185906111ed90866136b8565b601b546001600160a01b03163314611b045760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b60058110158015611b165750600a8111155b611b625760405162461bcd60e51b815260206004820181905260248201527f5265666c65637420666565206d757374206265206265747765656e20352d31306044820152606401610d6a565b600654811415611bb45760405162461bcd60e51b815260206004820152601160248201527f56616c756520616c7265616479207365740000000000000000000000000000006044820152606401610d6a565b6006546040518291907fa5b3651733f3a2fb4a2b7238575ef825255db6aa5c70ef21b988189e5d96118490600090a36006805460075555565b601b546001600160a01b03163314611c475760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b03811660008181526029602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517f6d52ea90b53520ded80cbaff7471d8dc8fe1a0ec5bec867629926d04cfa6fa1b9190a250565b601b546001600160a01b03163314611d085760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b038116611d5e5760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b6013546040516001600160a01b038084169216907fba8909f452a8d4e389cf607ef89115b537d1aae7b3519cd1ee95100864b545e490600090a360138054601480546001600160a01b038084167fffffffffffffffffffffffff000000000000000000000000000000000000000092831617909255909116908316908117909155604080517fc45a01550000000000000000000000000000000000000000000000000000000081529051829163c45a0155916004808301926020929190829003018186803b158015611e2f57600080fd5b505afa158015611e43573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6791906145ae565b6001600160a01b031663c9c6539630836001600160a01b031663ad5c46486040518163ffffffff1660e01b815260040160206040518083038186803b158015611eaf57600080fd5b505afa158015611ec3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611ee791906145ae565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b1681526001600160a01b03928316600482015291166024820152604401602060405180830381600087803b158015611f4757600080fd5b505af1158015611f5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f7f91906145ae565b601180546001600160a01b039283167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216179091556010805493909216921691909117905550565b600066038d7ea4c680008311156120225760405162461bcd60e51b815260206004820152601960248201527f416d6f756e742065786365656473206d617820737570706c79000000000000006044820152606401610d6a565b60008061202e856136c4565b505091509150831561204357915061118d9050565b50905061118d565b601b546001600160a01b031633146120a55760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b03811660008181526029602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517fe5372373c5102b83bd2386a8de7611034806591422c8fe0c181f6126c03d6d0a9190a250565b601b546001600160a01b031633146121695760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b0381166121bf5760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b601d546001600160a01b038281169116141561221d5760405162461bcd60e51b815260206004820152601360248201527f4164647265737320616c726561647920736574000000000000000000000000006044820152606401610d6a565b601d546040516001600160a01b038084169216907ff1b735ce2abab56f4c686468c84ae4dd08008af9152a0b6611965342231079bd90600090a3601d8054601e80546001600160a01b038084167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092559091169216919091179055565b601b546001600160a01b031633146122f95760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6509184e72a00081101561234f5760405162461bcd60e51b815260206004820152601660248201527f4d696e206c696d697420313030303020746f6b656e73000000000000000000006044820152606401610d6a565b6017548114156123a15760405162461bcd60e51b815260206004820152601160248201527f56616c756520616c7265616479207365740000000000000000000000000000006044820152606401610d6a565b6017546040518291907f44d543cdf368504a8e1363a6f41fe63122b6d67c8cdb004b74d8c976439e416b90600090a36017805460185555565b601b546001600160a01b031633146124345760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b03811660008181526028602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f3c19df9711d1fdba3552022436b55d6e0339d170a371debb749841fa52ad7ede9190a250565b6001600160a01b03811660009081526026602052604081205460ff16156124db57506001600160a01b031660009081526003602052604090205490565b6001600160a01b03821660009081526002602052604090205461118d906115dc565b601b546001600160a01b031633146125575760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b0381166000818152602a602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001179055517f36ea1e97529f78154fb6f7657e5498c670ebbd8558ee1b0946c4d915b5f3c72a9190a250565b60006125d5601b546001600160a01b031690565b905090565b601b546001600160a01b031633146126345760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b60018110158015612646575060058111155b6126b85760405162461bcd60e51b815260206004820152602160248201527f4c697175696469747920666565206d757374206265206265747765656e20312d60448201527f35000000000000000000000000000000000000000000000000000000000000006064820152608401610d6a565b600e5481141561270a5760405162461bcd60e51b815260206004820152601160248201527f56616c756520616c7265616479207365740000000000000000000000000000006044820152606401610d6a565b600e546040518291907fa58cc3215f31d542b7fddb4ee6ca53ef679609fb6de3d9e3b96f5deefa2873bf90600090a3600e8054600f5555565b601b546001600160a01b0316331461279d5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b0381166127f35760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b6011546040516001600160a01b038084169216907f1866e0728e6e89c2ebd286a744b7f1c174b4fd145c7fa20b446ef15988c43edd90600090a360118054601280546001600160a01b038084167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092559091169216919091179055565b600061118933846111ed856040518060600160405280602281526020016149f0602291393360009081526001602090815260408083206001600160a01b038d16845290915290205491906135cc565b60006111893384846133f3565b601b546001600160a01b0316331461292b5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b601480548215157501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff9091161790556040517f6ba383421aa078dca1d49e105174c3bd84af39712fb6736f168781cd709a0e0d906129a590831515815260200190565b60405180910390a150565b601b546001600160a01b03163314612a0a5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b038116612a605760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b6021546001600160a01b0382811691161415612abe5760405162461bcd60e51b815260206004820152601360248201527f4164647265737320616c726561647920736574000000000000000000000000006044820152606401610d6a565b6021546040516001600160a01b038084169216907f5ab9b88456c3123d329ac95fb71a7ca2038d5c7d0d9d36de82f749cdee59118590600090a360218054602280546001600160a01b038084167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092559091169216919091179055565b601b546001600160a01b03163314612b9a5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6003811115612beb5760405162461bcd60e51b815260206004820152601f60248201527f4368617269747920666565206d757374206265206265747765656e20302d33006044820152606401610d6a565b600854811415612c3d5760405162461bcd60e51b815260206004820152601160248201527f56616c756520616c7265616479207365740000000000000000000000000000006044820152606401610d6a565b6008546040518291907f6152b0c8064ce64f7bc3efccccd98f4b15b3892d615ad7e81268e7e91b09e52690600090a36008805460095555565b601b546001600160a01b03163314612cd05760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b612cd93061249e565b811115612d285760405162461bcd60e51b815260206004820152601e60248201527f4e6f7420656e6f7567682054454e4449455320696e20636f6e747261637400006044820152606401610d6a565b80612d31613311565b6001600160a01b03167f184af76793da1243821c78b43dd8055354f3e3d370f6ee9a12c10fb3190a87f960405160405180910390a361117930612d72613311565b836133f3565b601b546001600160a01b03163314612dd25760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6003811115612e235760405162461bcd60e51b815260206004820152601b60248201527f46756e20666565206d757374206265206265747765656e20302d3300000000006044820152606401610d6a565b600c54811415612e755760405162461bcd60e51b815260206004820152601160248201527f56616c756520616c7265616479207365740000000000000000000000000000006044820152606401610d6a565b600c546040518291907f1ce17d37a178820c0f12cca4ef6918cd34f34433ed66a79877b3b6223d3a612390600090a3600c8054600d5555565b601b546001600160a01b03163314612f085760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6005811115612f595760405162461bcd60e51b815260206004820152601c60248201527f4275726e20666565206d757374206265206265747765656e20302d35000000006044820152606401610d6a565b600a54811415612fab5760405162461bcd60e51b815260206004820152601160248201527f56616c756520616c7265616479207365740000000000000000000000000000006044820152606401610d6a565b600a546040518291907fb1979b9f4b503526ad572434d093d9357dabe3e958522450a2bf1ed4af76bd0190600090a3600a8054600b5555565b601b546001600160a01b0316331461303e5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b03811660008181526028602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00169055517f94be775e0fd8ba1bd40feda9d3306b6c400606850637b93189fe956f3557bb299190a250565b601b546001600160a01b031633146130ff5760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b64e8d4a510008110156131545760405162461bcd60e51b815260206004820152601560248201527f4d696e206c696d6974203130303020746f6b656e7300000000000000000000006044820152606401610d6a565b6015548114156131a65760405162461bcd60e51b815260206004820152601160248201527f56616c756520616c7265616479207365740000000000000000000000000000006044820152606401610d6a565b6015546040518291907fd6f586005531e7d32112f5389278497d656f7ed9029f31f85494a3f58ca1833390600090a36015805460165555565b601b546001600160a01b031633146132395760405162461bcd60e51b815260206004820152600e60248201527f5265717569726573206f776e65720000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b03811661328f5760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b601b546040516001600160a01b038084169216907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3601b8054601c80546001600160a01b038084167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316179092559091169216919091179055565b60008061118d601b546001600160a01b031690565b6001600160a01b0383161580159061334657506001600160a01b03821615155b6133925760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b6001600160a01b0383161580159061341357506001600160a01b03821615155b61345f5760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b600081116134d55760405162461bcd60e51b815260206004820152602660248201527f5472616e7366657220616d6f756e74206d75737420626520677265617465722060448201527f7468616e203000000000000000000000000000000000000000000000000000006064820152608401610d6a565b60006134e03061249e565b9050600060195482106134f1575060015b808015613519575060145474010000000000000000000000000000000000000000900460ff16155b801561353357506011546001600160a01b03868116911614155b801561355a57506014547501000000000000000000000000000000000000000000900460ff165b1561356d57601954915061356d8261372e565b6001600160a01b03841660009081526028602052604090205460019060ff16806135af57506001600160a01b03861660009081526028602052604090205460ff165b156135b8575060005b6135c48686868461381c565b505050505050565b600081848411156135f05760405162461bcd60e51b8152600401610d6a919061474c565b505050900390565b60006111f6828461489e565b60006111f68284614826565b600080600061361d611448565b909250905061362c8282613604565b9250505090565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fa9059cbb000000000000000000000000000000000000000000000000000000001790526136b3908490613be0565b505050565b60006111f6828461480e565b60008060006136d1614573565b60006136dc86613cc5565b90506000808061371a89858360200201518660016020020151876002602002015188600360200201518960046020020151613715613610565b613d9c565b919950975095509293505050509193509193565b601480547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff1674010000000000000000000000000000000000000000179055600061377a826002613604565b9050600061378883836135f8565b90504761379483613e30565b60006137a047836135f8565b90506137ac8382613fc3565b60408051858152602081018390529081018490527fda7f053cd23e95a08eb319388087a2806a629265db68cc2f2043a1e1ea852e619060600160405180910390a15050601480547fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff169055505050565b8061385b5761385b6006805460075560088054600955600a8054600b55600c8054600d55600e8054600f55600094859055928490559083905582905555565b60008060008061386a866136c4565b6001600160a01b038c166000908152602660205260409020549397509195509350915060ff16156138d2576001600160a01b0388166000908152600360205260409020546138b890876135f8565b6001600160a01b0389166000908152600360205260409020555b6001600160a01b0388166000908152600260205260409020546138f590856135f8565b6001600160a01b03808a16600090815260026020908152604080832094909455918a1681526026909152205460ff161561396a5760a08101516001600160a01b038816600090815260036020526040902054613950916136b8565b6001600160a01b0388166000908152600360205260409020555b6001600160a01b03871660009081526002602052604090205461398d90846136b8565b6001600160a01b0388166000908152600260205260409020556139b6816001602002015161409c565b60408101516139c490614124565b60608101516139d2906141e3565b60808101516139e0906142f5565b80516139ed9083906143b4565b6001600160a01b038088169089167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8360056020020151604051613a3391815260200190565b60405180910390a384613a6357613a63600754600655600954600855600b54600a55600d54600c55600f54600e55565b6001600160a01b0388166000908152602a602052604090205460ff16158015613a9a5750601b546001600160a01b03888116911614155b15613af157601554861115613af15760405162461bcd60e51b815260206004820152601060248201527f4d61782054782076696f6c6174696f6e000000000000000000000000000000006044820152606401610d6a565b6001600160a01b03881615801590613b1157506001600160a01b03871615155b613b5d5760405162461bcd60e51b815260206004820152600c60248201527f5a65726f206164647265737300000000000000000000000000000000000000006044820152606401610d6a565b6001600160a01b03871660009081526029602052604090205460ff16613bd657601754613b898861249e565b10613bd65760405162461bcd60e51b815260206004820152601460248201527f4d61782077616c6c65742076696f6c6174696f6e0000000000000000000000006044820152606401610d6a565b5050505050505050565b6000613c35826040518060400160405280602081526020017f5361666543524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166143d89092919063ffffffff16565b8051909150156136b35780806020019051810190613c53919061468e565b6136b35760405162461bcd60e51b815260206004820152602a60248201527f5361666543524332303a204352433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401610d6a565b613ccd614573565b613cd5614573565b613cf56064613cef600654866143ef90919063ffffffff16565b90613604565b8152600e54613d0c90606490613cef9086906143ef565b6020820152600854613d2690606490613cef9086906143ef565b6040820152600a54613d4090606490613cef9086906143ef565b6060820152600c54613d5a90606490613cef9086906143ef565b608082018190526060820151604083015160208401518451613d919493613d8b939092849283919082908c906135f8565b906135f8565b60a082015292915050565b6000808080613dab8b866143ef565b90506000613db98b876143ef565b90506000613dc78b886143ef565b90506000613dd58b896143ef565b90506000613de38b8a6143ef565b90506000613df18b8b6143ef565b90506000613e0385613d8b89896135f8565b9050613e1582613d8b858185896135f8565b96995095975093955050505050509750975097945050505050565b6040805160028082526060820183526000926020830190803683370190505090503081600081518110613e6557613e6561497c565b6001600160a01b03928316602091820292909201810191909152601054604080517fad5c46480000000000000000000000000000000000000000000000000000000081529051919093169263ad5c4648926004808301939192829003018186803b158015613ed257600080fd5b505afa158015613ee6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f0a91906145ae565b81600181518110613f1d57613f1d61497c565b6001600160a01b039283166020918202929092010152601054613f439130911684613326565b6010546040517f791ac9470000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063791ac94790613f9590859060009086903090429060040161479d565b600060405180830381600087803b158015613faf57600080fd5b505af11580156135c4573d6000803e3d6000fd5b601054613fdb9030906001600160a01b031684613326565b6010546040517ff305d719000000000000000000000000000000000000000000000000000000008152306004820181905260248201859052600060448301819052606483015260848201524260a48201526001600160a01b039091169063f305d71990839060c4016060604051808303818588803b15801561405c57600080fd5b505af1158015614070573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906140959190614702565b5050505050565b60006140a6613610565b905060006140b483836143ef565b306000908152600260205260409020549091506140d190826136b8565b3060009081526002602090815260408083209390935560269052205460ff16156136b3573060009081526003602052604090205461410f90846136b8565b30600090815260036020526040902055505050565b600061412e613610565b9050600061413c83836143ef565b6021546001600160a01b031660009081526002602052604090205490915061416490826136b8565b602180546001600160a01b03908116600090815260026020908152604080832095909555925490911681526026909152205460ff16156136b3576021546001600160a01b03166000908152600360205260409020546141c390846136b8565b6021546001600160a01b0316600090815260036020526040902055505050565b60006141ed613610565b905060006141fb83836143ef565b61dead60005260026020527f6a9609baa168169acaea398c4407efea4be641bb08e21e88806d9836fd9333cc5490915061423590826136b8565b61dead6000527f6a9609baa168169acaea398c4407efea4be641bb08e21e88806d9836fd9333cc5560266020527f436f594ac5248b7e44d6a4b4c35ba3e500f642e681dd33b10ee0fd4f06d15f3b5460ff16156136b35761dead60005260036020527f262bb27bbdd95c1cdc8e16957e36e38579ea44f7f6413dd7a9c75939def06b2c546142c390846136b8565b61dead60005260036020527f262bb27bbdd95c1cdc8e16957e36e38579ea44f7f6413dd7a9c75939def06b2c55505050565b60006142ff613610565b9050600061430d83836143ef565b601f546001600160a01b031660009081526002602052604090205490915061433590826136b8565b601f80546001600160a01b03908116600090815260026020908152604080832095909555925490911681526026909152205460ff16156136b357601f546001600160a01b031660009081526003602052604090205461439490846136b8565b601f546001600160a01b0316600090815260036020526040902055505050565b6004546143c190836135f8565b6004556005546143d190826136b8565b6005555050565b60606143e784846000856143fb565b949350505050565b60006111f68284614861565b6060824710156144735760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608401610d6a565b843b6144c15760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610d6a565b600080866001600160a01b031685876040516144dd9190614730565b60006040518083038185875af1925050503d806000811461451a576040519150601f19603f3d011682016040523d82523d6000602084013e61451f565b606091505b509150915061452f82828661453a565b979650505050505050565b606083156145495750816111f6565b8251156145595782518084602001fd5b8160405162461bcd60e51b8152600401610d6a919061474c565b6040518060c001604052806006906020820280368337509192915050565b6000602082840312156145a357600080fd5b81356111f6816149ab565b6000602082840312156145c057600080fd5b81516111f6816149ab565b600080604083850312156145de57600080fd5b82356145e9816149ab565b915060208301356145f9816149ab565b809150509250929050565b60008060006060848603121561461957600080fd5b8335614624816149ab565b92506020840135614634816149ab565b929592945050506040919091013590565b6000806040838503121561465857600080fd5b8235614663816149ab565b946020939093013593505050565b60006020828403121561468357600080fd5b81356111f6816149c0565b6000602082840312156146a057600080fd5b81516111f6816149c0565b6000602082840312156146bd57600080fd5b5035919050565b6000602082840312156146d657600080fd5b5051919050565b600080604083850312156146f057600080fd5b8235915060208301356145f9816149c0565b60008060006060848603121561471757600080fd5b8351925060208401519150604084015190509250925092565b600082516147428184602087016148b5565b9190910192915050565b602081526000825180602084015261476b8160408501602087016148b5565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169190910160400192915050565b600060a082018783526020878185015260a0604085015281875180845260c086019150828901935060005b818110156147ed5784516001600160a01b0316835293830193918301916001016147c8565b50506001600160a01b03969096166060850152505050608001529392505050565b600082198211156148215761482161491e565b500190565b60008261485c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156148995761489961491e565b500290565b6000828210156148b0576148b061491e565b500390565b60005b838110156148d05781810151838201526020016148b8565b838111156148df576000848401525b50505050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156149175761491761491e565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6001600160a01b038116811461117957600080fd5b801515811461117957600080fdfe5472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636543616e6e6f7420646563726561736520616c6c6f77616e63652062656c6f7720302ea2646970667358221220f3a7c21d0c8d90ecfb501ee3d35f5ace43d8cbc7fb6998a4a6b4681dd180779664736f6c63430008060033