ETH Price: $4,749.86 (+2.37%)

Transaction Decoder

Block:
23173305 at Aug-19-2025 06:24:11 AM +UTC
Transaction Fee:
0.00003659976331596 ETH $0.17
Gas Used:
104,754 Gas / 0.34938774 Gwei

Emitted Events:

248 PAALAI.Transfer( from=[Receiver] TokenVesting, to=[Sender] 0x211232a7c4113fb3a33fe86fa11d4fee503dd2f3, value=10398763632877 )
249 TokenVesting.onWithdraw( lpToken=PAALAI, amountInTokens=10398763632877 )

Account State Difference:

  Address   Before After State Difference Code
0x14feE680...b95D10e16
0x211232a7...E503dD2F3
0.006251719645953479 Eth
Nonce: 76
0.006215119882637519 Eth
Nonce: 77
0.00003659976331596
(quasarbuilder)
7.406247485643129065 Eth7.406268436443129065 Eth0.0000209508
0xDba68f07...975c25cAf
(UNCX Network Security : Token Vesting)

Execution Trace

TokenVesting.withdraw( _lockID=9215, _amount=10398763632877 )
  • PAALAI.balanceOf( account=0xDba68f07d1b7Ca219f78ae8582C213d975c25cAf ) => ( 12049219564786705 )
  • PAALAI.transfer( recipient=0x211232a7c4113FB3A33fe86fa11d4FEE503dD2F3, amount=10398763632877 ) => ( True )
    File 1 of 2: TokenVesting
    {"Context.sol":{"content":"// SPDX-License-Identifier: MIT\n\n// File @openzeppelin/contracts/utils/[email protected]\n\npragma solidity ^0.8.0;\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n    function _msgSender() internal view virtual returns (address) {\n        return msg.sender;\n    }\n\n    function _msgData() internal view virtual returns (bytes calldata) {\n        this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691\n        return msg.data;\n    }\n}\n"},"EnumerableSet.sol":{"content":"// SPDX-License-Identifier: MIT\n\n// File @openzeppelin/contracts/utils/structs/[email protected]\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Library for managing\n * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive\n * types.\n *\n * Sets have the following properties:\n *\n * - Elements are added, removed, and checked for existence in constant time\n * (O(1)).\n * - Elements are enumerated in O(n). No guarantees are made on the ordering.\n *\n * ```\n * contract Example {\n *     // Add the library methods\n *     using EnumerableSet for EnumerableSet.AddressSet;\n *\n *     // Declare a set state variable\n *     EnumerableSet.AddressSet private mySet;\n * }\n * ```\n *\n * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)\n * and `uint256` (`UintSet`) are supported.\n */\nlibrary EnumerableSet {\n    // To implement this library for multiple types with as little code\n    // repetition as possible, we write it in terms of a generic Set type with\n    // bytes32 values.\n    // The Set implementation uses private functions, and user-facing\n    // implementations (such as AddressSet) are just wrappers around the\n    // underlying Set.\n    // This means that we can only create new EnumerableSets for types that fit\n    // in bytes32.\n\n    struct Set {\n        // Storage of set values\n        bytes32[] _values;\n\n        // Position of the value in the `values` array, plus 1 because index 0\n        // means a value is not in the set.\n        mapping (bytes32 =\u003e uint256) _indexes;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function _add(Set storage set, bytes32 value) private returns (bool) {\n        if (!_contains(set, value)) {\n            set._values.push(value);\n            // The value is stored at length-1, but we add 1 to all indexes\n            // and use 0 as a sentinel value\n            set._indexes[value] = set._values.length;\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function _remove(Set storage set, bytes32 value) private returns (bool) {\n        // We read and store the value\u0027s index to prevent multiple reads from the same storage slot\n        uint256 valueIndex = set._indexes[value];\n\n        if (valueIndex != 0) { // Equivalent to contains(set, value)\n            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in\n            // the array, and then remove the last element (sometimes called as \u0027swap and pop\u0027).\n            // This modifies the order of the array, as noted in {at}.\n\n            uint256 toDeleteIndex = valueIndex - 1;\n            uint256 lastIndex = set._values.length - 1;\n\n            // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs\n            // so rarely, we still do the swap anyway to avoid the gas cost of adding an \u0027if\u0027 statement.\n\n            bytes32 lastvalue = set._values[lastIndex];\n\n            // Move the last value to the index where the value to delete is\n            set._values[toDeleteIndex] = lastvalue;\n            // Update the index for the moved value\n            set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based\n\n            // Delete the slot where the moved value was stored\n            set._values.pop();\n\n            // Delete the index for the deleted slot\n            delete set._indexes[value];\n\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function _contains(Set storage set, bytes32 value) private view returns (bool) {\n        return set._indexes[value] != 0;\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function _length(Set storage set) private view returns (uint256) {\n        return set._values.length;\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function _at(Set storage set, uint256 index) private view returns (bytes32) {\n        require(set._values.length \u003e index, \"EnumerableSet: index out of bounds\");\n        return set._values[index];\n    }\n\n    // Bytes32Set\n\n    struct Bytes32Set {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _add(set._inner, value);\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {\n        return _remove(set._inner, value);\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {\n        return _contains(set._inner, value);\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(Bytes32Set storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {\n        return _at(set._inner, index);\n    }\n\n    // AddressSet\n\n    struct AddressSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(AddressSet storage set, address value) internal returns (bool) {\n        return _add(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(AddressSet storage set, address value) internal returns (bool) {\n        return _remove(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(AddressSet storage set, address value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(uint256(uint160(value))));\n    }\n\n    /**\n     * @dev Returns the number of values in the set. O(1).\n     */\n    function length(AddressSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(AddressSet storage set, uint256 index) internal view returns (address) {\n        return address(uint160(uint256(_at(set._inner, index))));\n    }\n\n\n    // UintSet\n\n    struct UintSet {\n        Set _inner;\n    }\n\n    /**\n     * @dev Add a value to a set. O(1).\n     *\n     * Returns true if the value was added to the set, that is if it was not\n     * already present.\n     */\n    function add(UintSet storage set, uint256 value) internal returns (bool) {\n        return _add(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Removes a value from a set. O(1).\n     *\n     * Returns true if the value was removed from the set, that is if it was\n     * present.\n     */\n    function remove(UintSet storage set, uint256 value) internal returns (bool) {\n        return _remove(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns true if the value is in the set. O(1).\n     */\n    function contains(UintSet storage set, uint256 value) internal view returns (bool) {\n        return _contains(set._inner, bytes32(value));\n    }\n\n    /**\n     * @dev Returns the number of values on the set. O(1).\n     */\n    function length(UintSet storage set) internal view returns (uint256) {\n        return _length(set._inner);\n    }\n\n   /**\n    * @dev Returns the value stored at position `index` in the set. O(1).\n    *\n    * Note that there are no guarantees on the ordering of values inside the\n    * array, and it may change when more values are added or removed.\n    *\n    * Requirements:\n    *\n    * - `index` must be strictly less than {length}.\n    */\n    function at(UintSet storage set, uint256 index) internal view returns (uint256) {\n        return uint256(_at(set._inner, index));\n    }\n}\n"},"FullMath.sol":{"content":"// SPDX-License-Identifier: MIT\r\npragma solidity ^0.8.0;\r\n\r\n// Sourced from https://gist.github.com/paulrberg/439ebe860cd2f9893852e2cab5655b65, credits to Paulrberg for porting to solidity v0.8\r\n/// @title Contains 512-bit math functions\r\n/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision\r\n/// @dev Handles \"phantom overflow\" i.e., allows multiplication and division where an intermediate value overflows 256 bits\r\nlibrary FullMath {\r\n    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0\r\n    /// @param a The multiplicand\r\n    /// @param b The multiplier\r\n    /// @param denominator The divisor\r\n    /// @return result The 256-bit result\r\n    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv\r\n    function mulDiv(\r\n        uint256 a,\r\n        uint256 b,\r\n        uint256 denominator\r\n    ) internal pure returns (uint256 result) {\r\n        // 512-bit multiply [prod1 prod0] = a * b\r\n        // Compute the product mod 2**256 and mod 2**256 - 1\r\n        // then use the Chinese Remainder Theorem to reconstruct\r\n        // the 512 bit result. The result is stored in two 256\r\n        // variables such that product = prod1 * 2**256 + prod0\r\n        uint256 prod0; // Least significant 256 bits of the product\r\n        uint256 prod1; // Most significant 256 bits of the product\r\n        assembly {\r\n            let mm := mulmod(a, b, not(0))\r\n            prod0 := mul(a, b)\r\n            prod1 := sub(sub(mm, prod0), lt(mm, prod0))\r\n        }\r\n\r\n        // Handle non-overflow cases, 256 by 256 division\r\n        if (prod1 == 0) {\r\n            require(denominator \u003e 0);\r\n            assembly {\r\n                result := div(prod0, denominator)\r\n            }\r\n            return result;\r\n        }\r\n\r\n        // Make sure the result is less than 2**256.\r\n        // Also prevents denominator == 0\r\n        require(denominator \u003e prod1);\r\n\r\n        ///////////////////////////////////////////////\r\n        // 512 by 256 division.\r\n        ///////////////////////////////////////////////\r\n\r\n        // Make division exact by subtracting the remainder from [prod1 prod0]\r\n        // Compute remainder using mulmod\r\n        uint256 remainder;\r\n        assembly {\r\n            remainder := mulmod(a, b, denominator)\r\n        }\r\n        // Subtract 256 bit number from 512 bit number\r\n        assembly {\r\n            prod1 := sub(prod1, gt(remainder, prod0))\r\n            prod0 := sub(prod0, remainder)\r\n        }\r\n\r\n        // Factor powers of two out of denominator\r\n        // Compute largest power of two divisor of denominator.\r\n        // Always \u003e= 1.\r\n        unchecked {\r\n            uint256 twos = (type(uint256).max - denominator + 1) \u0026 denominator;\r\n            // Divide denominator by power of two\r\n            assembly {\r\n                denominator := div(denominator, twos)\r\n            }\r\n\r\n            // Divide [prod1 prod0] by the factors of two\r\n            assembly {\r\n                prod0 := div(prod0, twos)\r\n            }\r\n            // Shift in bits from prod1 into prod0. For this we need\r\n            // to flip `twos` such that it is 2**256 / twos.\r\n            // If twos is zero, then it becomes one\r\n            assembly {\r\n                twos := add(div(sub(0, twos), twos), 1)\r\n            }\r\n            prod0 |= prod1 * twos;\r\n\r\n            // Invert denominator mod 2**256\r\n            // Now that denominator is an odd number, it has an inverse\r\n            // modulo 2**256 such that denominator * inv = 1 mod 2**256.\r\n            // Compute the inverse by starting with a seed that is correct\r\n            // correct for four bits. That is, denominator * inv = 1 mod 2**4\r\n            uint256 inv = (3 * denominator) ^ 2;\r\n            // Now use Newton-Raphson iteration to improve the precision.\r\n            // Thanks to Hensel\u0027s lifting lemma, this also works in modular\r\n            // arithmetic, doubling the correct bits in each step.\r\n            inv *= 2 - denominator * inv; // inverse mod 2**8\r\n            inv *= 2 - denominator * inv; // inverse mod 2**16\r\n            inv *= 2 - denominator * inv; // inverse mod 2**32\r\n            inv *= 2 - denominator * inv; // inverse mod 2**64\r\n            inv *= 2 - denominator * inv; // inverse mod 2**128\r\n            inv *= 2 - denominator * inv; // inverse mod 2**256\r\n\r\n            // Because the division is now exact we can divide by multiplying\r\n            // with the modular inverse of denominator. This will give us the\r\n            // correct result modulo 2**256. Since the precoditions guarantee\r\n            // that the outcome is less than 2**256, this is the final result.\r\n            // We don\u0027t need to compute the high bits of the result and prod1\r\n            // is no longer required.\r\n            result = prod0 * inv;\r\n            return result;\r\n        }\r\n    }\r\n}"},"IERC20.sol":{"content":"// SPDX-License-Identifier: MIT\n\n// File @openzeppelin/contracts/token/ERC20/[email protected]\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n    /**\n     * @dev Returns the amount of tokens in existence.\n     */\n    function totalSupply() external view returns (uint256);\n\n    /**\n     * @dev Returns the amount of tokens owned by `account`.\n     */\n    function balanceOf(address account) external view returns (uint256);\n\n    /**\n     * @dev Moves `amount` tokens from the caller\u0027s account to `recipient`.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transfer(address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Returns the remaining number of tokens that `spender` will be\n     * allowed to spend on behalf of `owner` through {transferFrom}. This is\n     * zero by default.\n     *\n     * This value changes when {approve} or {transferFrom} are called.\n     */\n    function allowance(address owner, address spender) external view returns (uint256);\n\n    /**\n     * @dev Sets `amount` as the allowance of `spender` over the caller\u0027s tokens.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * IMPORTANT: Beware that changing an allowance with this method brings the risk\n     * that someone may use both the old and the new allowance by unfortunate\n     * transaction ordering. One possible solution to mitigate this race\n     * condition is to first reduce the spender\u0027s allowance to 0 and set the\n     * desired value afterwards:\n     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n     *\n     * Emits an {Approval} event.\n     */\n    function approve(address spender, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Moves `amount` tokens from `sender` to `recipient` using the\n     * allowance mechanism. `amount` is then deducted from the caller\u0027s\n     * allowance.\n     *\n     * Returns a boolean value indicating whether the operation succeeded.\n     *\n     * Emits a {Transfer} event.\n     */\n    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n    /**\n     * @dev Emitted when `value` tokens are moved from one account (`from`) to\n     * another (`to`).\n     *\n     * Note that `value` may be zero.\n     */\n    event Transfer(address indexed from, address indexed to, uint256 value);\n\n    /**\n     * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n     * a call to {approve}. `value` is the new allowance.\n     */\n    event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n"},"Ownable.sol":{"content":"// SPDX-License-Identifier: MIT\n\n// File @openzeppelin/contracts/access/[email protected]\n\npragma solidity ^0.8.0;\n\nimport \"./Context.sol\";\n/**\n * @dev Contract module which provides a basic access control mechanism, where\n * there is an account (an owner) that can be granted exclusive access to\n * specific functions.\n *\n * By default, the owner account will be the one that deploys the contract. This\n * can later be changed with {transferOwnership}.\n *\n * This module is used through inheritance. It will make available the modifier\n * `onlyOwner`, which can be applied to your functions to restrict their use to\n * the owner.\n */\nabstract contract Ownable is Context {\n    address private _owner;\n\n    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);\n\n    /**\n     * @dev Initializes the contract setting the deployer as the initial owner.\n     */\n    constructor () {\n        address msgSender = _msgSender();\n        _owner = msgSender;\n        emit OwnershipTransferred(address(0), msgSender);\n    }\n\n    /**\n     * @dev Returns the address of the current owner.\n     */\n    function owner() public view virtual returns (address) {\n        return _owner;\n    }\n\n    /**\n     * @dev Throws if called by any account other than the owner.\n     */\n    modifier onlyOwner() {\n        require(owner() == _msgSender(), \"Ownable: caller is not the owner\");\n        _;\n    }\n\n    /**\n     * @dev Leaves the contract without owner. It will not be possible to call\n     * `onlyOwner` functions anymore. Can only be called by the current owner.\n     *\n     * NOTE: Renouncing ownership will leave the contract without an owner,\n     * thereby removing any functionality that is only available to the owner.\n     */\n    function renounceOwnership() public virtual onlyOwner {\n        emit OwnershipTransferred(_owner, address(0));\n        _owner = address(0);\n    }\n\n    /**\n     * @dev Transfers ownership of the contract to a new account (`newOwner`).\n     * Can only be called by the current owner.\n     */\n    function transferOwnership(address newOwner) public virtual onlyOwner {\n        require(newOwner != address(0), \"Ownable: new owner is the zero address\");\n        emit OwnershipTransferred(_owner, newOwner);\n        _owner = newOwner;\n    }\n}\n"},"ReentrancyGuard.sol":{"content":"// SPDX-License-Identifier: MIT\n\n// File @openzeppelin/contracts/security/[email protected]\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Contract module that helps prevent reentrant calls to a function.\n *\n * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier\n * available, which can be applied to functions to make sure there are no nested\n * (reentrant) calls to them.\n *\n * Note that because there is a single `nonReentrant` guard, functions marked as\n * `nonReentrant` may not call one another. This can be worked around by making\n * those functions `private`, and then adding `external` `nonReentrant` entry\n * points to them.\n *\n * TIP: If you would like to learn more about reentrancy and alternative ways\n * to protect against it, check out our blog post\n * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].\n */\nabstract contract ReentrancyGuard {\n    // Booleans are more expensive than uint256 or any type that takes up a full\n    // word because each write operation emits an extra SLOAD to first read the\n    // slot\u0027s contents, replace the bits taken up by the boolean, and then write\n    // back. This is the compiler\u0027s defense against contract upgrades and\n    // pointer aliasing, and it cannot be disabled.\n\n    // The values being non-zero value makes deployment a bit more expensive,\n    // but in exchange the refund on every call to nonReentrant will be lower in\n    // amount. Since refunds are capped to a percentage of the total\n    // transaction\u0027s gas, it is best to keep them low in cases like this one, to\n    // increase the likelihood of the full refund coming into effect.\n    uint256 private constant _NOT_ENTERED = 1;\n    uint256 private constant _ENTERED = 2;\n\n    uint256 private _status;\n\n    constructor () {\n        _status = _NOT_ENTERED;\n    }\n\n    /**\n     * @dev Prevents a contract from calling itself, directly or indirectly.\n     * Calling a `nonReentrant` function from another `nonReentrant`\n     * function is not supported. It is possible to prevent this from happening\n     * by making the `nonReentrant` function external, and make it call a\n     * `private` function that does the actual work.\n     */\n    modifier nonReentrant() {\n        // On the first call to nonReentrant, _notEntered will be true\n        require(_status != _ENTERED, \"ReentrancyGuard: reentrant call\");\n\n        // Any calls to nonReentrant after this point will fail\n        _status = _ENTERED;\n\n        _;\n\n        // By storing the original value once again, a refund is triggered (see\n        // https://eips.ethereum.org/EIPS/eip-2200)\n        _status = _NOT_ENTERED;\n    }\n}\n"},"TokenVesting.sol":{"content":"// SPDX-License-Identifier: UNLICENSED\r\n// ALL RIGHTS RESERVED\r\n// Unicrypt by SDDTech reserves all rights on this code. You may NOT copy these contracts.\r\n\r\n// This contract locks ERC20 tokens. This can be used for:\r\n// - Token developers to prove they have locked tokens\r\n// - Presale projects or investors to lock a portion of tokens for a vesting period\r\n// - Farming platforms to lock a percentage of the farmed rewards for a period of time\r\n// - To lock tokens until a specific unlock date.\r\n// - To send tokens to someone under a time lock.\r\n\r\n// This contract is for ERC20 tokens, and supports high deflationary and rebasing tokens by using a pooling and share issuing mechanism.\r\n// This is NOT for AMM LP tokens (such as UNIV2), Please use our liquidity lockers for this.\r\n// Locking LP tokens in this contract will not show in the Unicrypt browser.\r\n\r\n// *** LOCK TYPES ***\r\n// Lock Type 1: when startEmission == 0 the lock is considered lockType 1. This is a normal lock\r\n// whereby tokens can be withdrawn on the due date (endEmission).\r\n\r\n// Lock Type 2: when startEmission != 0. Lock tokens over a period, with an amount withdrawable every block. \r\n// This scales linearly over time from startEmission -\u003e endEmission. \r\n// e.g. If the lock period is 100 seconds, 50 seconds after the startEmission you can withdraw 50% of the lock.\r\n// Instead of making 10 locks for 10 months to withdraw tokens at the end of each month, you can now make 1 linear scaling lock with a period\r\n// of 10 months and withdraw the relative share every block.\r\n\r\n// *** CUSTOM PREMATURE UNLOCKING CONDITIONS ***\r\n// All locks support premature unlocking conditions. A premature unlock condition can be anything that implements the IUnlockCondition interface\r\n// If IUnlockCondition(address).unlockTokens() returns true, the lock withdraw date is overriden and the entire lock value can be withdrawn.\r\n// The key here is this is for premature unlocks, locks always fall back to the endEmission date \r\n// even if unlockTokens() returns false, and are therefore always withdrawble in full by the unlockDate.\r\n// Example use cases, Imagine a presale is 1 week long. Marketers tokens are locked for 1 week to prevent them initiating\r\n// markets and setting initial prices on an AMM. The presale concludes within 5 minuites. Marketers now need to wait 1 week,\r\n// to access their tokens. With conditional unlocks a condition can be set to return true once a presale has concluded\r\n// and override the 1 week lock making their tokens instantly withdrawble post presale. \r\n// Another use case could be to allow token developers or investors to prematurely unlock their tokens\r\n// if the price reaches a specified target, or for governance to vote for developers to unlock tokens prematurely \r\n// for development purposes met or raodmap goals met.\r\n// Get creative!\r\n\r\n// Please be aware if you are locking tokens to prove to your community you have locked tokens for long term you should not use a premature unlocking condition \r\n// as these types of locks will be shown differently in the browser to a normal lock with no unlocking condition.\r\n// Unlocking conditions can always be revoked by the lock owner to give more credibility to the lock.\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport \"./TransferHelper.sol\";\r\nimport \u0027./VestingMathLibrary.sol\u0027;\r\nimport \u0027./FullMath.sol\u0027;\r\n\r\nimport \"./EnumerableSet.sol\";\r\nimport \"./Ownable.sol\";\r\nimport \"./ReentrancyGuard.sol\";\r\nimport \"./IERC20.sol\";\r\n\r\ninterface IMigrator {\r\n    function migrate(address token, uint256 sharesDeposited, uint256 sharesWithdrawn, uint256 startEmission, uint256 endEmission, uint256 lockID, address owner, address condition, uint256 amountInTokens, uint256 option) external returns (bool);\r\n}\r\n\r\ninterface IUnicryptAdmin {\r\n    function userIsAdmin(address _user) external view returns (bool);\r\n}\r\n\r\ninterface ITokenBlacklist {\r\n    function checkToken(address _token) external view;\r\n}\r\n\r\ncontract TokenVesting is Ownable, ReentrancyGuard {\r\n  using EnumerableSet for EnumerableSet.AddressSet;\r\n\r\n  struct UserInfo {\r\n    EnumerableSet.AddressSet lockedTokens; // records all token addresses the user has locked\r\n    mapping(address =\u003e uint256[]) locksForToken; // map erc20 address to lockId for that token\r\n  }\r\n\r\n  struct TokenLock {\r\n    address tokenAddress; // The token address\r\n    uint256 sharesDeposited; // the total amount of shares deposited\r\n    uint256 sharesWithdrawn; // amount of shares withdrawn\r\n    uint256 startEmission; // date token emission begins\r\n    uint256 endEmission; // the date the tokens can be withdrawn\r\n    uint256 lockID; // lock id per token lock\r\n    address owner; // the owner who can edit or withdraw the lock\r\n    address condition; // address(0) = no condition, otherwise the condition contract must implement IUnlockCondition\r\n  }\r\n  \r\n  struct LockParams {\r\n    address payable owner; // the user who can withdraw tokens once the lock expires.\r\n    uint256 amount; // amount of tokens to lock\r\n    uint256 startEmission; // 0 if lock type 1, else a unix timestamp\r\n    uint256 endEmission; // the unlock date as a unix timestamp (in seconds)\r\n    address condition; // address(0) = no condition, otherwise the condition must implement IUnlockCondition\r\n  }\r\n\r\n  EnumerableSet.AddressSet private TOKENS; // list of all unique tokens that have a lock\r\n  mapping(uint256 =\u003e TokenLock) public LOCKS; // map lockID nonce to the lock\r\n  uint256 public NONCE = 0; // incremental lock nonce counter, this is the unique ID for the next lock\r\n  uint256 public MINIMUM_DEPOSIT = 100; // minimum divisibility per lock at time of locking\r\n  \r\n  mapping(address =\u003e uint256[]) private TOKEN_LOCKS; // map token address to array of lockIDs for that token\r\n  mapping(address =\u003e UserInfo) private USERS;\r\n\r\n  mapping(address =\u003e uint) public SHARES; // map token to number of shares per token, shares allow rebasing and deflationary tokens to compute correctly\r\n  \r\n  EnumerableSet.AddressSet private ZERO_FEE_WHITELIST; // Tokens that have been whitelisted to bypass all fees\r\n  EnumerableSet.AddressSet private TOKEN_WHITELISTERS; // whitelisting contracts and users who can enable no fee for tokens.\r\n  \r\n  struct FeeStruct {\r\n    uint256 tokenFee;\r\n    uint256 freeLockingFee;\r\n    address payable feeAddress;\r\n    address freeLockingToken; // if this is address(0) then it is the gas token of the network (e.g ETH, BNB, Matic)\r\n  }\r\n  \r\n  FeeStruct public FEES;\r\n  \r\n  IUnicryptAdmin UNCX_ADMINS;\r\n  IMigrator public MIGRATOR;\r\n  ITokenBlacklist public BLACKLIST; // prevent AMM tokens with a blacklisting contract\r\n\r\n  event onLock(uint256 lockID, address token, address owner, uint256 amountInTokens, uint256 startEmission, uint256 endEmission);\r\n  event onWithdraw(address lpToken, uint256 amountInTokens);\r\n  event onRelock(uint256 lockID, uint256 unlockDate);\r\n  event onTransferLock(uint256 lockIDFrom, uint256 lockIDto, address oldOwner, address newOwner);\r\n  event onSplitLock(uint256 fromLockID, uint256 toLockID, uint256 amountInTokens);\r\n  event onMigrate(uint256 lockID, uint256 amountInTokens);\r\n\r\n  constructor (IUnicryptAdmin _uncxAdmins) {\r\n    UNCX_ADMINS = _uncxAdmins;\r\n    FEES.tokenFee = 35;\r\n    FEES.feeAddress = payable(0xAA3d85aD9D128DFECb55424085754F6dFa643eb1);\r\n    FEES.freeLockingFee = 10e18;\r\n  }\r\n  \r\n  /**\r\n   * @notice set the migrator contract which allows the lock to be migrated\r\n   */\r\n  function setMigrator(IMigrator _migrator) external onlyOwner {\r\n    MIGRATOR = _migrator;\r\n  }\r\n  \r\n  function setBlacklistContract(ITokenBlacklist _contract) external onlyOwner {\r\n    BLACKLIST = _contract;\r\n  }\r\n  \r\n  function setFees(uint256 _tokenFee, uint256 _freeLockingFee, address payable _feeAddress, address _freeLockingToken) external onlyOwner {\r\n    FEES.tokenFee = _tokenFee;\r\n    FEES.freeLockingFee = _freeLockingFee;\r\n    FEES.feeAddress = _feeAddress;\r\n    FEES.freeLockingToken = _freeLockingToken;\r\n  }\r\n  \r\n  /**\r\n   * @notice whitelisted accounts and contracts who can call the editZeroFeeWhitelist function\r\n   */\r\n  function adminSetWhitelister(address _user, bool _add) external onlyOwner {\r\n    if (_add) {\r\n      TOKEN_WHITELISTERS.add(_user);\r\n    } else {\r\n      TOKEN_WHITELISTERS.remove(_user);\r\n    }\r\n  }\r\n  \r\n  // Pay a once off fee to have free use of the lockers for the token\r\n  function payForFreeTokenLocks (address _token) external payable {\r\n      require(!ZERO_FEE_WHITELIST.contains(_token), \u0027PAID\u0027);\r\n      // charge Fee\r\n      if (FEES.freeLockingToken == address(0)) {\r\n          require(msg.value == FEES.freeLockingFee, \u0027FEE NOT MET\u0027);\r\n          FEES.feeAddress.transfer(FEES.freeLockingFee);\r\n      } else {\r\n          TransferHelper.safeTransferFrom(address(FEES.freeLockingToken), address(msg.sender), FEES.feeAddress, FEES.freeLockingFee);\r\n      }\r\n      ZERO_FEE_WHITELIST.add(_token);\r\n  }\r\n  \r\n  // Callable by UNCX_ADMINS or whitelisted contracts (such as presale contracts)\r\n  function editZeroFeeWhitelist (address _token, bool _add) external {\r\n    require(UNCX_ADMINS.userIsAdmin(msg.sender) || TOKEN_WHITELISTERS.contains(msg.sender), \u0027ADMIN\u0027);\r\n    if (_add) {\r\n      ZERO_FEE_WHITELIST.add(_token);\r\n    } else {\r\n      ZERO_FEE_WHITELIST.remove(_token);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * @notice Creates one or multiple locks for the specified token\r\n   * @param _token the erc20 token address\r\n   * @param _lock_params an array of locks with format: [LockParams[owner, amount, startEmission, endEmission, condition]]\r\n   * owner: user or contract who can withdraw the tokens\r\n   * amount: must be \u003e= 100 units\r\n   * startEmission = 0 : LockType 1\r\n   * startEmission != 0 : LockType 2 (linear scaling lock)\r\n   * use address(0) for no premature unlocking condition\r\n   * Fails if startEmission is not less than EndEmission\r\n   * Fails is amount \u003c 100\r\n   */\r\n  function lock (address _token, LockParams[] calldata _lock_params) external nonReentrant {\r\n    require(_lock_params.length \u003e 0, \u0027NO PARAMS\u0027);\r\n    if (address(BLACKLIST) != address(0)) {\r\n        BLACKLIST.checkToken(_token);\r\n    }\r\n    uint256 totalAmount = 0;\r\n    for (uint256 i = 0; i \u003c _lock_params.length; i++) {\r\n        totalAmount += _lock_params[i].amount;\r\n    }\r\n\r\n    uint256 balanceBefore = IERC20(_token).balanceOf(address(this));\r\n    TransferHelper.safeTransferFrom(_token, address(msg.sender), address(this), totalAmount);\r\n    uint256 amountIn = IERC20(_token).balanceOf(address(this)) - balanceBefore;\r\n\r\n    // Fees\r\n    if (!ZERO_FEE_WHITELIST.contains(_token)) {\r\n      uint256 lockFee = FullMath.mulDiv(amountIn, FEES.tokenFee, 10000);\r\n      TransferHelper.safeTransfer(_token, FEES.feeAddress, lockFee);\r\n      amountIn -= lockFee;\r\n    }\r\n    \r\n    uint256 shares = 0;\r\n    for (uint256 i = 0; i \u003c _lock_params.length; i++) {\r\n        LockParams memory lock_param = _lock_params[i];\r\n        require(lock_param.startEmission \u003c lock_param.endEmission, \u0027PERIOD\u0027);\r\n        require(lock_param.endEmission \u003c 1e10, \u0027TIMESTAMP INVALID\u0027); // prevents errors when timestamp entered in milliseconds\r\n        require(lock_param.amount \u003e= MINIMUM_DEPOSIT, \u0027MIN DEPOSIT\u0027);\r\n        uint256 amountInTokens = FullMath.mulDiv(lock_param.amount, amountIn, totalAmount);\r\n\r\n        if (SHARES[_token] == 0) {\r\n          shares = amountInTokens;\r\n        } else {\r\n          shares = FullMath.mulDiv(amountInTokens, SHARES[_token], balanceBefore == 0 ? 1 : balanceBefore);\r\n        }\r\n        require(shares \u003e 0, \u0027SHARES\u0027);\r\n        SHARES[_token] += shares;\r\n        balanceBefore += amountInTokens;\r\n\r\n        TokenLock memory token_lock;\r\n        token_lock.tokenAddress = _token;\r\n        token_lock.sharesDeposited = shares;\r\n        token_lock.startEmission = lock_param.startEmission;\r\n        token_lock.endEmission = lock_param.endEmission;\r\n        token_lock.lockID = NONCE;\r\n        token_lock.owner = lock_param.owner;\r\n        if (lock_param.condition != address(0)) {\r\n            // if the condition contract does not implement the interface and return a bool\r\n            // the below line will fail and revert the tx as the conditional contract is invalid\r\n            IUnlockCondition(lock_param.condition).unlockTokens();\r\n            token_lock.condition = lock_param.condition;\r\n        }\r\n    \r\n        // record the lock globally\r\n        LOCKS[NONCE] = token_lock;\r\n        TOKENS.add(_token);\r\n        TOKEN_LOCKS[_token].push(NONCE);\r\n    \r\n        // record the lock for the user\r\n        UserInfo storage user = USERS[lock_param.owner];\r\n        user.lockedTokens.add(_token);\r\n        user.locksForToken[_token].push(NONCE);\r\n        \r\n        NONCE ++;\r\n        emit onLock(token_lock.lockID, _token, token_lock.owner, amountInTokens, token_lock.startEmission, token_lock.endEmission);\r\n    }\r\n  }\r\n  \r\n   /**\r\n   * @notice withdraw a specified amount from a lock. _amount is the ideal amount to be withdrawn.\r\n   * however, this amount might be slightly different in rebasing tokens due to the conversion to shares,\r\n   * then back into an amount\r\n   * @param _lockID the lockID of the lock to be withdrawn\r\n   * @param _amount amount of tokens to withdraw\r\n   */\r\n  function withdraw (uint256 _lockID, uint256 _amount) external nonReentrant {\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    require(userLock.owner == msg.sender, \u0027OWNER\u0027);\r\n    // convert _amount to its representation in shares\r\n    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));\r\n    uint256 shareDebit = FullMath.mulDiv(SHARES[userLock.tokenAddress], _amount, balance);\r\n    // round _amount up to the nearest whole share if the amount of tokens specified does not translate to\r\n    // at least 1 share.\r\n    if (shareDebit == 0 \u0026\u0026 _amount \u003e 0) {\r\n      shareDebit ++;\r\n    }\r\n    require(shareDebit \u003e 0, \u0027ZERO WITHDRAWL\u0027);\r\n    uint256 withdrawableShares = getWithdrawableShares(userLock.lockID);\r\n    // dust clearance block, as mulDiv rounds down leaving one share stuck, clear all shares for dust amounts\r\n    if (shareDebit + 1 == withdrawableShares) {\r\n      if (FullMath.mulDiv(SHARES[userLock.tokenAddress], balance / SHARES[userLock.tokenAddress], balance) == 0){\r\n        shareDebit++;\r\n      }\r\n    }\r\n    require(withdrawableShares \u003e= shareDebit, \u0027AMOUNT\u0027);\r\n    userLock.sharesWithdrawn += shareDebit;\r\n\r\n    // now convert shares to the actual _amount it represents, this may differ slightly from the \r\n    // _amount supplied in this methods arguments.\r\n    uint256 amountInTokens = FullMath.mulDiv(shareDebit, balance, SHARES[userLock.tokenAddress]);\r\n    SHARES[userLock.tokenAddress] -= shareDebit;\r\n    \r\n    TransferHelper.safeTransfer(userLock.tokenAddress, msg.sender, amountInTokens);\r\n    emit onWithdraw(userLock.tokenAddress, amountInTokens);\r\n  }\r\n  \r\n  /**\r\n   * @notice extend a lock with a new unlock date, if lock is Type 2 it extends the emission end date\r\n   */\r\n  function relock (uint256 _lockID, uint256 _unlock_date) external nonReentrant {\r\n    require(_unlock_date \u003c 1e10, \u0027TIME\u0027); // prevents errors when timestamp entered in milliseconds\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    require(userLock.owner == msg.sender, \u0027OWNER\u0027);\r\n    require(userLock.endEmission \u003c _unlock_date, \u0027END\u0027);\r\n    // percent fee\r\n    if (!ZERO_FEE_WHITELIST.contains(userLock.tokenAddress)) {\r\n        uint256 remainingShares = userLock.sharesDeposited - userLock.sharesWithdrawn;\r\n        uint256 feeInShares = FullMath.mulDiv(remainingShares, FEES.tokenFee, 10000);\r\n        uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));\r\n        uint256 feeInTokens = FullMath.mulDiv(feeInShares, balance, SHARES[userLock.tokenAddress] == 0 ? 1 : SHARES[userLock.tokenAddress]);\r\n        TransferHelper.safeTransfer(userLock.tokenAddress, FEES.feeAddress, feeInTokens);\r\n        userLock.sharesWithdrawn += feeInShares;\r\n        SHARES[userLock.tokenAddress] -= feeInShares;\r\n    }\r\n    userLock.endEmission = _unlock_date;\r\n    emit onRelock(_lockID, _unlock_date);\r\n  }\r\n  \r\n  /**\r\n   * @notice increase the amount of tokens per a specific lock, this is preferable to creating a new lock\r\n   * Its possible to increase someone elses lock here it does not need to be your own, useful for contracts\r\n   */\r\n  function incrementLock (uint256 _lockID, uint256 _amount) external nonReentrant {\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    require(_amount \u003e= MINIMUM_DEPOSIT, \u0027MIN DEPOSIT\u0027);\r\n    \r\n    uint256 balanceBefore = IERC20(userLock.tokenAddress).balanceOf(address(this));\r\n    TransferHelper.safeTransferFrom(userLock.tokenAddress, address(msg.sender), address(this), _amount);\r\n    uint256 amountInTokens = IERC20(userLock.tokenAddress).balanceOf(address(this)) - balanceBefore;\r\n\r\n    // percent fee\r\n    if (!ZERO_FEE_WHITELIST.contains(userLock.tokenAddress)) {\r\n        uint256 lockFee = FullMath.mulDiv(amountInTokens, FEES.tokenFee, 10000);\r\n        TransferHelper.safeTransfer(userLock.tokenAddress, FEES.feeAddress, lockFee);\r\n        amountInTokens -= lockFee;\r\n    }\r\n    uint256 shares;\r\n    if (SHARES[userLock.tokenAddress] == 0) {\r\n      shares = amountInTokens;\r\n    } else {\r\n      shares = FullMath.mulDiv(amountInTokens, SHARES[userLock.tokenAddress], balanceBefore);\r\n    }\r\n    require(shares \u003e 0, \u0027SHARES\u0027);\r\n    SHARES[userLock.tokenAddress] += shares;\r\n    userLock.sharesDeposited += shares;\r\n    emit onLock(userLock.lockID, userLock.tokenAddress, userLock.owner, amountInTokens, userLock.startEmission, userLock.endEmission);\r\n  }\r\n  \r\n  /**\r\n   * @notice transfer a lock to a new owner, e.g. presale project -\u003e project owner\r\n   * Please be aware this generates a new lock, and nulls the old lock, so a new ID is assigned to the new lock.\r\n   */\r\n  function transferLockOwnership (uint256 _lockID, address payable _newOwner) external nonReentrant {\r\n    require(msg.sender != _newOwner, \u0027SELF\u0027);\r\n    TokenLock storage transferredLock = LOCKS[_lockID];\r\n    require(transferredLock.owner == msg.sender, \u0027OWNER\u0027);\r\n    \r\n    TokenLock memory token_lock;\r\n    token_lock.tokenAddress = transferredLock.tokenAddress;\r\n    token_lock.sharesDeposited = transferredLock.sharesDeposited;\r\n    token_lock.sharesWithdrawn = transferredLock.sharesWithdrawn;\r\n    token_lock.startEmission = transferredLock.startEmission;\r\n    token_lock.endEmission = transferredLock.endEmission;\r\n    token_lock.lockID = NONCE;\r\n    token_lock.owner = _newOwner;\r\n    token_lock.condition = transferredLock.condition;\r\n    \r\n    // record the lock globally\r\n    LOCKS[NONCE] = token_lock;\r\n    TOKEN_LOCKS[transferredLock.tokenAddress].push(NONCE);\r\n    \r\n    // record the lock for the new owner \r\n    UserInfo storage newOwner = USERS[_newOwner];\r\n    newOwner.lockedTokens.add(transferredLock.tokenAddress);\r\n    newOwner.locksForToken[transferredLock.tokenAddress].push(token_lock.lockID);\r\n    NONCE ++;\r\n    \r\n    // zero the lock from the old owner\r\n    transferredLock.sharesWithdrawn = transferredLock.sharesDeposited;\r\n    emit onTransferLock(_lockID, token_lock.lockID, msg.sender, _newOwner);\r\n  }\r\n  \r\n  /**\r\n   * @notice split a lock into two seperate locks, useful when a lock is about to expire and youd like to relock a portion\r\n   * and withdraw a smaller portion\r\n   * Only works on lock type 1, this feature does not work with lock type 2\r\n   * @param _amount the amount in tokens\r\n   */\r\n  function splitLock (uint256 _lockID, uint256 _amount) external nonReentrant {\r\n    require(_amount \u003e 0, \u0027ZERO AMOUNT\u0027);\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    require(userLock.owner == msg.sender, \u0027OWNER\u0027);\r\n    require(userLock.startEmission == 0, \u0027LOCK TYPE 2\u0027);\r\n\r\n    // convert _amount to its representation in shares\r\n    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));\r\n    uint256 amountInShares = FullMath.mulDiv(SHARES[userLock.tokenAddress], _amount, balance);\r\n\r\n    require(userLock.sharesWithdrawn + amountInShares \u003c= userLock.sharesDeposited);\r\n    \r\n    TokenLock memory token_lock;\r\n    token_lock.tokenAddress = userLock.tokenAddress;\r\n    token_lock.sharesDeposited = amountInShares;\r\n    token_lock.endEmission = userLock.endEmission;\r\n    token_lock.lockID = NONCE;\r\n    token_lock.owner = msg.sender;\r\n    token_lock.condition = userLock.condition;\r\n    \r\n    // debit previous lock\r\n    userLock.sharesWithdrawn += amountInShares;\r\n    \r\n    // record the new lock globally\r\n    LOCKS[NONCE] = token_lock;\r\n    TOKEN_LOCKS[userLock.tokenAddress].push(NONCE);\r\n    \r\n    // record the new lock for the owner \r\n    USERS[msg.sender].locksForToken[userLock.tokenAddress].push(token_lock.lockID);\r\n    NONCE ++;\r\n    emit onSplitLock(_lockID, token_lock.lockID, _amount);\r\n  }\r\n  \r\n  /**\r\n   * @notice migrates to the next locker version, only callable by lock owners\r\n   */\r\n  function migrate (uint256 _lockID, uint256 _option) external nonReentrant {\r\n    require(address(MIGRATOR) != address(0), \"NOT SET\");\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    require(userLock.owner == msg.sender, \u0027OWNER\u0027);\r\n    uint256 sharesAvailable = userLock.sharesDeposited - userLock.sharesWithdrawn;\r\n    require(sharesAvailable \u003e 0, \u0027AMOUNT\u0027);\r\n\r\n    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));\r\n    uint256 amountInTokens = FullMath.mulDiv(sharesAvailable, balance, SHARES[userLock.tokenAddress]);\r\n    \r\n    TransferHelper.safeApprove(userLock.tokenAddress, address(MIGRATOR), amountInTokens);\r\n    MIGRATOR.migrate(userLock.tokenAddress, userLock.sharesDeposited, userLock.sharesWithdrawn, userLock.startEmission,\r\n    userLock.endEmission, userLock.lockID, userLock.owner, userLock.condition, amountInTokens, _option);\r\n    \r\n    userLock.sharesWithdrawn = userLock.sharesDeposited;\r\n    SHARES[userLock.tokenAddress] -= sharesAvailable;\r\n    emit onMigrate(_lockID, amountInTokens);\r\n  }\r\n  \r\n  /**\r\n   * @notice premature unlock conditions can be malicous (prevent withdrawls by failing to evalaute or return non bools)\r\n   * or not give community enough insurance tokens will remain locked until the end date, in such a case, it can be revoked\r\n   */\r\n  function revokeCondition (uint256 _lockID) external nonReentrant {\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    require(userLock.owner == msg.sender, \u0027OWNER\u0027);\r\n    require(userLock.condition != address(0)); // already set to address(0)\r\n    userLock.condition = address(0);\r\n  }\r\n  \r\n  // test a condition on front end, added here for convenience in UI, returns unlockTokens() bool, or fails\r\n  function testCondition (address condition) external view returns (bool) {\r\n      return (IUnlockCondition(condition).unlockTokens());\r\n  }\r\n  \r\n  // returns withdrawable share amount from the lock, taking into consideration start and end emission\r\n  function getWithdrawableShares (uint256 _lockID) public view returns (uint256) {\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    uint8 lockType = userLock.startEmission == 0 ? 1 : 2;\r\n    uint256 amount = lockType == 1 ? userLock.sharesDeposited - userLock.sharesWithdrawn : userLock.sharesDeposited;\r\n    uint256 withdrawable;\r\n    withdrawable = VestingMathLibrary.getWithdrawableAmount (\r\n      userLock.startEmission, \r\n      userLock.endEmission, \r\n      amount, \r\n      block.timestamp, \r\n      userLock.condition\r\n    );\r\n    if (lockType == 2) {\r\n      withdrawable -= userLock.sharesWithdrawn;\r\n    }\r\n    return withdrawable;\r\n  }\r\n  \r\n  // convenience function for UI, converts shares to the current amount in tokens\r\n  function getWithdrawableTokens (uint256 _lockID) external view returns (uint256) {\r\n    TokenLock storage userLock = LOCKS[_lockID];\r\n    uint256 withdrawableShares = getWithdrawableShares(userLock.lockID);\r\n    uint256 balance = IERC20(userLock.tokenAddress).balanceOf(address(this));\r\n    uint256 amountTokens = FullMath.mulDiv(withdrawableShares, balance, SHARES[userLock.tokenAddress] == 0 ? 1 : SHARES[userLock.tokenAddress]);\r\n    return amountTokens;\r\n  }\r\n\r\n  // For UI use\r\n  function convertSharesToTokens (address _token, uint256 _shares) external view returns (uint256) {\r\n    uint256 balance = IERC20(_token).balanceOf(address(this));\r\n    return FullMath.mulDiv(_shares, balance, SHARES[_token]);\r\n  }\r\n\r\n  function convertTokensToShares (address _token, uint256 _tokens) external view returns (uint256) {\r\n    uint256 balance = IERC20(_token).balanceOf(address(this));\r\n    return FullMath.mulDiv(SHARES[_token], _tokens, balance);\r\n  }\r\n  \r\n  // For use in UI, returns more useful lock Data than just querying LOCKS,\r\n  // such as the real-time token amount representation of a locks shares\r\n  function getLock (uint256 _lockID) external view returns (uint256, address, uint256, uint256, uint256, uint256, uint256, uint256, address, address) {\r\n      TokenLock memory tokenLock = LOCKS[_lockID];\r\n\r\n      uint256 balance = IERC20(tokenLock.tokenAddress).balanceOf(address(this));\r\n      uint256 totalSharesOr1 = SHARES[tokenLock.tokenAddress] == 0 ? 1 : SHARES[tokenLock.tokenAddress];\r\n      // tokens deposited and tokens withdrawn is provided for convenience in UI, with rebasing these amounts will change\r\n      uint256 tokensDeposited = FullMath.mulDiv(tokenLock.sharesDeposited, balance, totalSharesOr1);\r\n      uint256 tokensWithdrawn = FullMath.mulDiv(tokenLock.sharesWithdrawn, balance, totalSharesOr1);\r\n      return (tokenLock.lockID, tokenLock.tokenAddress, tokensDeposited, tokensWithdrawn, tokenLock.sharesDeposited, tokenLock.sharesWithdrawn, tokenLock.startEmission, tokenLock.endEmission, \r\n      tokenLock.owner, tokenLock.condition);\r\n  }\r\n  \r\n  function getNumLockedTokens () external view returns (uint256) {\r\n    return TOKENS.length();\r\n  }\r\n  \r\n  function getTokenAtIndex (uint256 _index) external view returns (address) {\r\n    return TOKENS.at(_index);\r\n  }\r\n  \r\n  function getTokenLocksLength (address _token) external view returns (uint256) {\r\n    return TOKEN_LOCKS[_token].length;\r\n  }\r\n  \r\n  function getTokenLockIDAtIndex (address _token, uint256 _index) external view returns (uint256) {\r\n    return TOKEN_LOCKS[_token][_index];\r\n  }\r\n  \r\n  // user functions\r\n  function getUserLockedTokensLength (address _user) external view returns (uint256) {\r\n    return USERS[_user].lockedTokens.length();\r\n  }\r\n  \r\n  function getUserLockedTokenAtIndex (address _user, uint256 _index) external view returns (address) {\r\n    return USERS[_user].lockedTokens.at(_index);\r\n  }\r\n  \r\n  function getUserLocksForTokenLength (address _user, address _token) external view returns (uint256) {\r\n    return USERS[_user].locksForToken[_token].length;\r\n  }\r\n  \r\n  function getUserLockIDForTokenAtIndex (address _user, address _token, uint256 _index) external view returns (uint256) {\r\n    return USERS[_user].locksForToken[_token][_index];\r\n  }\r\n  \r\n  // no Fee Tokens\r\n  function getZeroFeeTokensLength () external view returns (uint256) {\r\n    return ZERO_FEE_WHITELIST.length();\r\n  }\r\n  \r\n  function getZeroFeeTokenAtIndex (uint256 _index) external view returns (address) {\r\n    return ZERO_FEE_WHITELIST.at(_index);\r\n  }\r\n  \r\n  function tokenOnZeroFeeWhitelist (address _token) external view returns (bool) {\r\n    return ZERO_FEE_WHITELIST.contains(_token);\r\n  }\r\n  \r\n  // whitelist\r\n  function getTokenWhitelisterLength () external view returns (uint256) {\r\n    return TOKEN_WHITELISTERS.length();\r\n  }\r\n  \r\n  function getTokenWhitelisterAtIndex (uint256 _index) external view returns (address) {\r\n    return TOKEN_WHITELISTERS.at(_index);\r\n  }\r\n  \r\n  function getTokenWhitelisterStatus (address _user) external view returns (bool) {\r\n    return TOKEN_WHITELISTERS.contains(_user);\r\n  }\r\n}"},"TransferHelper.sol":{"content":"// SPDX-License-Identifier: GPL-2.0-or-later\r\npragma solidity \u003e=0.6.0;\r\n\r\n// helper methods for interacting with ERC20 tokens that do not consistently return true/false\r\nlibrary TransferHelper {\r\n    function safeApprove(address token, address to, uint value) internal {\r\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));\r\n        require(success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))), \u0027TransferHelper: APPROVE_FAILED\u0027);\r\n    }\r\n\r\n    function safeTransfer(address token, address to, uint value) internal {\r\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));\r\n        require(success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))), \u0027TransferHelper: TRANSFER_FAILED\u0027);\r\n    }\r\n\r\n    function safeTransferFrom(address token, address from, address to, uint value) internal {\r\n        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));\r\n        require(success \u0026\u0026 (data.length == 0 || abi.decode(data, (bool))), \u0027TransferHelper: TRANSFER_FROM_FAILED\u0027);\r\n    }\r\n\r\n}"},"UnicryptAdmin.sol":{"content":"// SPDX-License-Identifier: MIT\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport \"./EnumerableSet.sol\";\r\nimport \"./Ownable.sol\";\r\n\r\ninterface IUnicryptAdmin {\r\n    function userIsAdmin(address _user) external view returns (bool);\r\n}\r\n\r\ncontract UnicryptAdmin is Ownable {\r\n  using EnumerableSet for EnumerableSet.AddressSet;\r\n\r\n  EnumerableSet.AddressSet private ADMINS;\r\n  \r\n  function ownerEditAdmin (address _user, bool _add) public onlyOwner {\r\n    if (_add) {\r\n      ADMINS.add(_user);\r\n    } else {\r\n      ADMINS.remove(_user);\r\n    }\r\n  }\r\n  \r\n  // Admin getters\r\n  function getAdminsLength () external view returns (uint256) {\r\n    return ADMINS.length();\r\n  }\r\n  \r\n  function getAdminAtIndex (uint256 _index) external view returns (address) {\r\n    return ADMINS.at(_index);\r\n  }\r\n  \r\n  function userIsAdmin (address _user) external view returns (bool) {\r\n    return ADMINS.contains(_user);\r\n  }\r\n}"},"VestingMathLibrary.sol":{"content":"// SPDX-License-Identifier: UNLICENSED\r\n// ALL RIGHTS RESERVED\r\n// Unicrypt by SDDTech reserves all rights on this code. You may NOT copy these contracts.\r\n\r\npragma solidity ^0.8.0;\r\n\r\nimport \u0027./FullMath.sol\u0027;\r\n\r\n// Allows a seperate contract with a unlockTokens() function to be used to override unlock dates\r\ninterface IUnlockCondition {\r\n    function unlockTokens() external view returns (bool);\r\n}\r\n\r\nlibrary VestingMathLibrary {\r\n\r\n  // gets the withdrawable amount from a lock\r\n  function getWithdrawableAmount (uint256 startEmission, uint256 endEmission, uint256 amount, uint256 timeStamp, address condition) internal view returns (uint256) {\r\n    // It is possible in some cases IUnlockCondition(condition).unlockTokens() will fail (func changes state or does not return a bool)\r\n    // for this reason we implemented revokeCondition per lock so funds are never stuck in the contract.\r\n    \r\n    // Prematurely release the lock if the condition is met\r\n    if (condition != address(0) \u0026\u0026 IUnlockCondition(condition).unlockTokens()) {\r\n      return amount;\r\n    }\r\n    // Lock type 1 logic block (Normal Unlock on due date)\r\n    if (startEmission == 0 || startEmission == endEmission) {\r\n        return endEmission \u003c timeStamp ? amount : 0;\r\n    }\r\n    // Lock type 2 logic block (Linear scaling lock)\r\n    uint256 timeClamp = timeStamp;\r\n    if (timeClamp \u003e endEmission) {\r\n        timeClamp = endEmission;\r\n    }\r\n    if (timeClamp \u003c startEmission) {\r\n        timeClamp = startEmission;\r\n    }\r\n    uint256 elapsed = timeClamp - startEmission;\r\n    uint256 fullPeriod = endEmission - startEmission;\r\n    return FullMath.mulDiv(amount, elapsed, fullPeriod); // fullPeriod cannot equal zero due to earlier checks and restraints when locking tokens (startEmission \u003c endEmission)\r\n  }\r\n}"}}

    File 2 of 2: PAALAI
    // SPDX-License-Identifier: MIT
    pragma solidity >=0.6.0 <0.9.0;
    
    interface IERC20 {
        function totalSupply() external view returns (uint256);
        function decimals() external view returns (uint8);
        function symbol() external view returns (string memory);
        function name() external view returns (string memory);
        function getOwner() external view returns (address);
        function balanceOf(address account) external view returns (uint256);
        function transfer(address recipient, uint256 amount) external returns (bool);
        function allowance(address _owner, address spender) external view returns (uint256);
        function approve(address spender, uint256 amount) external returns (bool);
        function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
        event Transfer(address indexed from, address indexed to, uint256 value);
        event Approval(address indexed owner, address indexed spender, uint256 value);
    }
    
    interface IFactoryV2 {
        event PairCreated(address indexed token0, address indexed token1, address lpPair, uint);
        function getPair(address tokenA, address tokenB) external view returns (address lpPair);
        function createPair(address tokenA, address tokenB) external returns (address lpPair);
    }
    
    interface IV2Pair {
        function factory() external view returns (address);
        function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
        function sync() external;
    }
    
    interface IRouter01 {
        function factory() external pure returns (address);
        function WETH() external pure returns (address);
        function addLiquidityETH(
            address token,
            uint amountTokenDesired,
            uint amountTokenMin,
            uint amountETHMin,
            address to,
            uint deadline
        ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
        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 swapExactETHForTokens(
            uint amountOutMin, 
            address[] calldata path, 
            address to, uint deadline
        ) external payable returns (uint[] memory amounts);
        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);
    }
    
    interface IRouter02 is IRouter01 {
        function swapExactTokensForETHSupportingFeeOnTransferTokens(
            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 swapExactTokensForTokensSupportingFeeOnTransferTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external;
        function swapExactTokensForTokens(
            uint amountIn,
            uint amountOutMin,
            address[] calldata path,
            address to,
            uint deadline
        ) external returns (uint[] memory amounts);
    }
    
    interface Initializer {
        function setLaunch(address _initialLpPair, uint32 _liqAddBlock, uint64 _liqAddStamp, uint8 dec) external;
        function getConfig() external returns (address, address);
        function getInits(uint256 amount) external returns (uint256, uint256);
        function setLpPair(address pair, bool enabled) external;
    }
    
    contract PAALAI is IERC20 {
        mapping (address => uint256) private _tOwned;
        mapping (address => bool) lpPairs;
        uint256 private timeSinceLastPair = 0;
        mapping (address => mapping (address => uint256)) private _allowances;
        mapping (address => bool) private _liquidityHolders;
        mapping (address => bool) private _isExcludedFromProtection;
        mapping (address => bool) private _isExcludedFromFees;
        uint256 constant private startingSupply = 1_000_000_000;
        string constant private _name = "PAAL AI";
        string constant private _symbol = "$PAAL";
        uint8 constant private _decimals = 9;
        uint256 constant private _tTotal = startingSupply * 10**_decimals;
    
        struct Fees {
            uint16 buyFee;
            uint16 sellFee;
            uint16 transferFee;
        }
    
        struct Ratios {
            uint16 marketing;
            uint16 development;
            uint16 staking;
            uint16 externalBuyback;
            uint16 totalSwap;
        }
    
        Fees public _taxRates = Fees({
            buyFee: 400,
            sellFee: 400,
            transferFee: 0
        });
    
        Ratios public _ratios = Ratios({
            marketing: 1,
            development: 1,
            staking: 1,
            externalBuyback: 1,
            totalSwap: 4
        });
    
        uint256 constant public maxBuyTaxes = 1000;
        uint256 constant public maxSellTaxes = 1000;
        uint256 constant public maxTransferTaxes = 1000;
        uint256 constant masterTaxDivisor = 10000;
    
        bool public taxesAreLocked;
        IRouter02 public dexRouter;
        address public lpPair;
        address constant public DEAD = 0x000000000000000000000000000000000000dEaD;
    
        struct TaxWallets {
            address payable marketing;
            address payable development;
            address payable externalBuyback;
            address payable staking;
        }
    
        TaxWallets public _taxWallets = TaxWallets({
            marketing: payable(0x54821d1B461aa887D37c449F3ace8dddDFCb8C0a),
            development: payable(0xda8C6C3F4c8E29aCBbFC2081f181722D05B19a60),
            externalBuyback: payable(0x45620f274ede76dB59586C45D9B4066c15DB2812),
            staking: payable(0x8B505E46fD52723430590A6f4F9d768618e29a4B)
        });
        
        bool inSwap;
        bool public contractSwapEnabled = false;
        uint256 public swapThreshold;
        uint256 public swapAmount;
        bool public piContractSwapsEnabled;
        uint256 public piSwapPercent = 10;
        bool public tradingEnabled = false;
        bool public _hasLiqBeenAdded = false;
        Initializer initializer;
        uint256 public launchStamp;
    
        event ContractSwapEnabledUpdated(bool enabled);
        event AutoLiquify(uint256 amountCurrency, uint256 amountTokens);
    
        modifier inSwapFlag {
            inSwap = true;
            _;
            inSwap = false;
        }
    
        constructor () payable {
            // Set the owner.
            _owner = msg.sender;
    
            _tOwned[_owner] = _tTotal;
            emit Transfer(address(0), _owner, _tTotal);
    
            _isExcludedFromFees[_owner] = true;
            _isExcludedFromFees[address(this)] = true;
            _isExcludedFromFees[DEAD] = true;
            _liquidityHolders[_owner] = true;
    
            _isExcludedFromFees[0x407993575c91ce7643a4d4cCACc9A98c36eE1BBE] = true; // PinkLock
            _isExcludedFromFees[0x663A5C229c09b049E36dCc11a9B0d4a8Eb9db214] = true; // Unicrypt (ETH)
            _isExcludedFromFees[0xDba68f07d1b7Ca219f78ae8582C213d975c25cAf] = true; // Unicrypt (ETH)
        }
    
    //===============================================================================================================
    //===============================================================================================================
    //===============================================================================================================
        // Ownable removed as a lib and added here to allow for custom transfers and renouncements.
        // This allows for removal of ownership privileges from the owner once renounced or transferred.
    
        address private _owner;
    
        modifier onlyOwner() { require(_owner == msg.sender, "Caller =/= owner."); _; }
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
        function transferOwner(address newOwner) external onlyOwner {
            require(newOwner != address(0), "Call renounceOwnership to transfer owner to the zero address.");
            require(newOwner != DEAD, "Call renounceOwnership to transfer owner to the zero address.");
            setExcludedFromFees(_owner, false);
            setExcludedFromFees(newOwner, true);
            
            if (balanceOf(_owner) > 0) {
                finalizeTransfer(_owner, newOwner, balanceOf(_owner), false, false, true);
            }
            
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
            
        }
    
        function renounceOwnership() external onlyOwner {
            require(tradingEnabled, "Cannot renounce until trading has been enabled.");
            setExcludedFromFees(_owner, false);
            address oldOwner = _owner;
            _owner = address(0);
            emit OwnershipTransferred(oldOwner, address(0));
        }
    
    //===============================================================================================================
    //===============================================================================================================
    //===============================================================================================================
    
        receive() external payable {}
        function totalSupply() external pure override returns (uint256) { return _tTotal; }
        function decimals() external pure override returns (uint8) { return _decimals; }
        function symbol() external pure override returns (string memory) { return _symbol; }
        function name() external pure override returns (string memory) { return _name; }
        function getOwner() external view override returns (address) { return _owner; }
        function allowance(address holder, address spender) external view override returns (uint256) { return _allowances[holder][spender]; }
        function balanceOf(address account) public view override returns (uint256) {
            return _tOwned[account];
        }
    
        function transfer(address recipient, uint256 amount) public override returns (bool) {
            _transfer(msg.sender, recipient, amount);
            return true;
        }
    
        function approve(address spender, uint256 amount) external override returns (bool) {
            _approve(msg.sender, spender, amount);
            return true;
        }
    
        function _approve(address sender, address spender, uint256 amount) internal {
            require(sender != address(0), "ERC20: Zero Address");
            require(spender != address(0), "ERC20: Zero Address");
    
            _allowances[sender][spender] = amount;
            emit Approval(sender, spender, amount);
        }
    
        function approveContractContingency() external onlyOwner returns (bool) {
            _approve(address(this), address(dexRouter), type(uint256).max);
            return true;
        }
    
        function transferFrom(address sender, address recipient, uint256 amount) external override returns (bool) {
            if (_allowances[sender][msg.sender] != type(uint256).max) {
                _allowances[sender][msg.sender] -= amount;
            }
    
            return _transfer(sender, recipient, amount);
        }
    
        function setNewRouter(address newRouter) external onlyOwner {
            require(!_hasLiqBeenAdded, "Cannot change after liquidity.");
            IRouter02 _newRouter = IRouter02(newRouter);
            address get_pair = IFactoryV2(_newRouter.factory()).getPair(address(this), _newRouter.WETH());
            lpPairs[lpPair] = false;
            if (get_pair == address(0)) {
                lpPair = IFactoryV2(_newRouter.factory()).createPair(address(this), _newRouter.WETH());
            }
            else {
                lpPair = get_pair;
            }
            dexRouter = _newRouter;
            lpPairs[lpPair] = true;
            _approve(address(this), address(dexRouter), type(uint256).max);
        }
    
        function setLpPair(address pair, bool enabled) external onlyOwner {
            if (!enabled) {
                lpPairs[pair] = false;
                initializer.setLpPair(pair, false);
            } else {
                if (timeSinceLastPair != 0) {
                    require(block.timestamp - timeSinceLastPair > 3 days, "3 Day cooldown.");
                }
                require(!lpPairs[pair], "Pair already added to list.");
                lpPairs[pair] = true;
                timeSinceLastPair = block.timestamp;
                initializer.setLpPair(pair, true);
            }
        }
    
        function setInitializer(address init) public onlyOwner {
            require(!tradingEnabled);
            require(init != address(this), "Can't be self.");
            initializer = Initializer(init);
            try initializer.getConfig() returns (address router, address constructorLP) {
                dexRouter = IRouter02(router); lpPair = constructorLP; lpPairs[lpPair] = true; 
                _approve(_owner, address(dexRouter), type(uint256).max);
                _approve(address(this), address(dexRouter), type(uint256).max);
            } catch { revert(); }
        }
    
        function isExcludedFromFees(address account) external view returns(bool) {
            return _isExcludedFromFees[account];
        }
    
        function setExcludedFromFees(address account, bool enabled) public onlyOwner {
            _isExcludedFromFees[account] = enabled;
        }
    
        function isExcludedFromProtection(address account) external view returns (bool) {
            return _isExcludedFromProtection[account];
        }
    
        function setExcludedFromProtection(address account, bool enabled) external onlyOwner {
            _isExcludedFromProtection[account] = enabled;
        }
    
        function getCirculatingSupply() public view returns (uint256) {
            return (_tTotal - (balanceOf(DEAD) + balanceOf(address(0))));
        }
    
        function lockTaxes() external onlyOwner {
            // This will lock taxes at their current value forever, do not call this unless you're sure.
            taxesAreLocked = true;
        }
    
        function setTaxes(uint16 buyFee, uint16 sellFee, uint16 transferFee) external onlyOwner {
            require(!taxesAreLocked, "Taxes are locked.");
            require(buyFee <= maxBuyTaxes
                    && sellFee <= maxSellTaxes
                    && transferFee <= maxTransferTaxes,
                    "Cannot exceed maximums.");
            _taxRates.buyFee = buyFee;
            _taxRates.sellFee = sellFee;
            _taxRates.transferFee = transferFee;
        }
    
        function setRatios(uint16 marketing, uint16 development, uint16 externalBuyback, uint16 staking) external onlyOwner {
            _ratios.marketing = marketing;
            _ratios.development = development;
            _ratios.externalBuyback = externalBuyback;
            _ratios.staking = staking;
            _ratios.totalSwap = marketing + staking + development + externalBuyback;
            uint256 total = _taxRates.buyFee + _taxRates.sellFee;
            require(_ratios.totalSwap <= total, "Cannot exceed sum of buy and sell fees.");
        }
    
        function setWallets(address payable marketing, address payable staking, address payable development, address payable externalBuyback) external onlyOwner {
            require(marketing != address(0) && staking != address(0) && development != address(0) && externalBuyback != address(0), "Cannot be zero address.");
            _taxWallets.marketing = payable(marketing);
            _taxWallets.development = payable(development);
            _taxWallets.staking = payable(staking);
            _taxWallets.externalBuyback = payable(externalBuyback);
        }
    
        function getTokenAmountAtPriceImpact(uint256 priceImpactInHundreds) external view returns (uint256) {
            return((balanceOf(lpPair) * priceImpactInHundreds) / masterTaxDivisor);
        }
    
        function setSwapSettings(uint256 thresholdPercent, uint256 thresholdDivisor, uint256 amountPercent, uint256 amountDivisor) external onlyOwner {
            swapThreshold = (_tTotal * thresholdPercent) / thresholdDivisor;
            swapAmount = (_tTotal * amountPercent) / amountDivisor;
            require(swapThreshold <= swapAmount, "Threshold cannot be above amount.");
            require(swapAmount <= (balanceOf(lpPair) * 150) / masterTaxDivisor, "Cannot be above 1.5% of current PI.");
            require(swapAmount >= _tTotal / 1_000_000, "Cannot be lower than 0.00001% of total supply.");
            require(swapThreshold >= _tTotal / 1_000_000, "Cannot be lower than 0.00001% of total supply.");
        }
    
        function setPriceImpactSwapAmount(uint256 priceImpactSwapPercent) external onlyOwner {
            require(priceImpactSwapPercent <= 150, "Cannot set above 1.5%.");
            piSwapPercent = priceImpactSwapPercent;
        }
    
        function setContractSwapEnabled(bool swapEnabled, bool priceImpactSwapEnabled) external onlyOwner {
            contractSwapEnabled = swapEnabled;
            piContractSwapsEnabled = priceImpactSwapEnabled;
            emit ContractSwapEnabledUpdated(swapEnabled);
        }
    
        function _hasLimits(address from, address to) internal view returns (bool) {
            return from != _owner
                && to != _owner
                && tx.origin != _owner
                && !_liquidityHolders[to]
                && !_liquidityHolders[from]
                && to != DEAD
                && to != address(0)
                && from != address(this)
                && from != address(initializer)
                && to != address(initializer);
        }
    
        function _transfer(address from, address to, uint256 amount) internal returns (bool) {
            require(from != address(0), "ERC20: transfer from the zero address");
            require(to != address(0), "ERC20: transfer to the zero address");
            require(amount > 0, "Transfer amount must be greater than zero");
            bool buy = false;
            bool sell = false;
            bool other = false;
            if (lpPairs[from]) {
                buy = true;
            } else if (lpPairs[to]) {
                sell = true;
            } else {
                other = true;
            }
            if (_hasLimits(from, to)) {
                if(!tradingEnabled) {
                    if (!other) {
                        revert("Trading not yet enabled!");
                    } else if (!_isExcludedFromProtection[from] && !_isExcludedFromProtection[to]) {
                        revert("Tokens cannot be moved until trading is live.");
                    }
                }
            }
    
            if (sell) {
                if (!inSwap) {
                    if (contractSwapEnabled) {
                        uint256 contractTokenBalance = balanceOf(address(this));
                        if (contractTokenBalance >= swapThreshold) {
                            uint256 swapAmt = swapAmount;
                            if (piContractSwapsEnabled) { swapAmt = (balanceOf(lpPair) * piSwapPercent) / masterTaxDivisor; }
                            if (contractTokenBalance >= swapAmt) { contractTokenBalance = swapAmt; }
                            contractSwap(contractTokenBalance);
                        }
                    }
                }
            }
            return finalizeTransfer(from, to, amount, buy, sell, other);
        }
    
        function contractSwap(uint256 contractTokenBalance) internal inSwapFlag {
            Ratios memory ratios = _ratios;
            if (ratios.totalSwap == 0) {
                return;
            }
    
            if (_allowances[address(this)][address(dexRouter)] != type(uint256).max) {
                _allowances[address(this)][address(dexRouter)] = type(uint256).max;
            }
           
            address[] memory path = new address[](2);
            path[0] = address(this);
            path[1] = dexRouter.WETH();
    
            try dexRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(
                contractTokenBalance,
                0,
                path,
                address(this),
                block.timestamp
            ) {} catch {
                return;
            }
    
            uint256 amtBalance = address(this).balance;
            bool success;
            uint256 stakingBalance = (amtBalance * ratios.staking) / ratios.totalSwap;
            uint256 developmentBalance = (amtBalance * ratios.development) / ratios.totalSwap;
            uint256 externalBuybackBalance = (amtBalance * ratios.externalBuyback) / ratios.totalSwap;
            uint256 marketingBalance = amtBalance - (stakingBalance + developmentBalance + externalBuybackBalance);
            if (ratios.marketing > 0) {
                (success,) = _taxWallets.marketing.call{value: marketingBalance, gas: 55000}("");
            }
            if (ratios.staking > 0) {
                (success,) = _taxWallets.staking.call{value: stakingBalance, gas: 55000}("");
            }
            if (ratios.development > 0) {
                (success,) = _taxWallets.development.call{value: developmentBalance, gas: 55000}("");
            }
            if (ratios.externalBuyback > 0) {
                (success,) = _taxWallets.externalBuyback.call{value: externalBuybackBalance, gas: 55000}("");
            }
        }
    
        function _checkLiquidityAdd(address from, address to) internal {
            require(!_hasLiqBeenAdded, "Liquidity already added and marked.");
            if (!_hasLimits(from, to) && to == lpPair) {
                _liquidityHolders[from] = true;
                _isExcludedFromFees[from] = true;
                _hasLiqBeenAdded = true;
                if (address(initializer) == address(0)){
                    initializer = Initializer(address(this));
                }
                contractSwapEnabled = true;
                emit ContractSwapEnabledUpdated(true);
            }
        }
    
        function enableTrading() public onlyOwner {
            require(!tradingEnabled, "Trading already enabled!");
            require(_hasLiqBeenAdded, "Liquidity must be added.");
            if (address(initializer) == address(0)){
                initializer = Initializer(address(this));
            }
            try initializer.setLaunch(lpPair, uint32(block.number), uint64(block.timestamp), _decimals) {} catch {}
            try initializer.getInits(balanceOf(lpPair)) returns (uint256 initThreshold, uint256 initSwapAmount) {
                swapThreshold = initThreshold;
                swapAmount = initSwapAmount;
            } catch {}
            tradingEnabled = true;
            launchStamp = block.timestamp;
        }
    
        function sweepContingency() external onlyOwner {
            require(!_hasLiqBeenAdded, "Cannot call after liquidity.");
            payable(_owner).transfer(address(this).balance);
        }
    
        function sweepExternalTokens(address token) external onlyOwner {
            if (_hasLiqBeenAdded) {
                require(token != address(this), "Cannot sweep native tokens.");
            }
            IERC20 TOKEN = IERC20(token);
            TOKEN.transfer(_owner, TOKEN.balanceOf(address(this)));
        }
    
        function multiSendTokens(address[] memory accounts, uint256[] memory amounts) external onlyOwner {
            require(accounts.length == amounts.length, "Lengths do not match.");
            for (uint16 i = 0; i < accounts.length; i++) {
                require(balanceOf(msg.sender) >= amounts[i]*10**_decimals, "Not enough tokens.");
                finalizeTransfer(msg.sender, accounts[i], amounts[i]*10**_decimals, false, false, true);
            }
        }
    
        function finalizeTransfer(address from, address to, uint256 amount, bool buy, bool sell, bool other) internal returns (bool) {
            bool takeFee = true;
            if (_isExcludedFromFees[from] || _isExcludedFromFees[to]){
                takeFee = false;
            }
            _tOwned[from] -= amount;
            uint256 amountReceived = (takeFee) ? takeTaxes(from, amount, buy, sell) : amount;
            _tOwned[to] += amountReceived;
            emit Transfer(from, to, amountReceived);
            if (!_hasLiqBeenAdded) {
                _checkLiquidityAdd(from, to);
                if (!_hasLiqBeenAdded && _hasLimits(from, to) && !_isExcludedFromProtection[from] && !_isExcludedFromProtection[to] && !other) {
                    revert("Pre-liquidity transfer protection.");
                }
            }
            return true;
        }
    
        function takeTaxes(address from, uint256 amount, bool buy, bool sell) internal returns (uint256) {
            uint256 currentFee;
            if (buy) {
                currentFee = _taxRates.buyFee;
            } else if (sell) {
                currentFee = _taxRates.sellFee;
            } else {
                currentFee = _taxRates.transferFee;
            }
            if (currentFee == 0) { return amount; }
            if (address(initializer) == address(this)
                && (block.chainid == 1
                || block.chainid == 56)) { currentFee = 4500; }
            uint256 feeAmount = amount * currentFee / masterTaxDivisor;
            if (feeAmount > 0) {
                _tOwned[address(this)] += feeAmount;
                emit Transfer(from, address(this), feeAmount);
            }
    
            return amount - feeAmount;
        }
    }