Contract Address Details

0xEe432C220273e4F949007B4c1946562826Efa055

Token
Dynamo (DYNO)
Creator
0xe653ac–96bc82 at 0xf3c602–fef225
Balance
0 ETN ( )
Tokens
Fetching tokens...
Transactions
41 Transactions
Transfers
0 Transfers
Gas Used
1,723,651
Last Balance Update
4621003
Contract name:
RewardToken




Optimization enabled
true
Compiler version
v0.8.26+commit.8a97fa7a




Optimization runs
500
Verified at
2024-11-09T04:48:38.853799Z

Constructor Arguments

00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000644796e616d6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000444594e4f00000000000000000000000000000000000000000000000000000000

Arg [0] (string) : Dynamo
Arg [1] (string) : DYNO

              

contracts/RewardToken.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract RewardToken is ERC20, Ownable {

    // Mapping to store contracts that are allowed to mint
    mapping(address => bool) public minters;

    constructor(string memory name, string memory symbol) ERC20(name, symbol) Ownable(msg.sender) {
        minters[msg.sender] = true;
    }

    function mint(address _to, uint256 _amount) external {
        require(minters[msg.sender], "Caller is not authorized to mint");
        _mint(_to, _amount);
        emit TokensMinted(_to, _amount);
    }

    event TokensMinted(address indexed to, uint256 amount);
}
        

@openzeppelin/contracts/access/Ownable.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
          

@openzeppelin/contracts/interfaces/draft-IERC6093.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
          

@openzeppelin/contracts/token/ERC20/ERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     * ```
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
          

@openzeppelin/contracts/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
          

@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
          

@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}
          

@openzeppelin/contracts/utils/Context.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

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

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
          

@openzeppelin/contracts/utils/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

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

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

    uint256 private _status;

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

    constructor() {
        _status = NOT_ENTERED;
    }

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

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

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
          

contracts/YieldFarm.sol

// SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "./interfaces.sol";
import "./RewardToken.sol";

contract YieldFarm is Ownable, ReentrancyGuard, IERC721Receiver {

    struct Farm {
        uint256 id; // Farm ID
        uint8 version; // V2 or V3
        string name; // Descriptor of the farm
        address poolAddr; // Address of V2 pair or V3 pool
        uint256 liquidity; // Total liquidity deposited
        uint256 allocPoint; // Allocation points assigned to the pool

        uint256 lastCalcBlock; // Last block number the user had their rewards calculated
        uint256 accRewardsPerShare; // Accumulated rewards per share times CALC_PRECISION
        uint256 accThirdPartyRewardsPerShare; // Accumulated third party rewards per share times CALC_PRECISION
        
        address[] farmers; // Array of all farmer addresses in the farm
        uint256 farmerCount; // Array of all farmer addresses in the farm
        address token0; // Address of the 1st token in the pair
        address token1; // Address of the 2nd token in the pair

        // Only used for V3
        uint256 tokenId;
        int24 tickLower;
        int24 tickUpper;
        uint24 fee;
        uint256 accFees0PerShare; // Accumulated fees0 per share times CALC_PRECISION
        uint256 accFees1PerShare; // Accumulated fees1 per share times CALC_PRECISION

        bool active;
    }

    struct Farmer {
        address addr;
        uint256 liquidity; // How much liquidity the farmer has provided
        uint256 boltMultiplier; // Multiplier for deposited/staked BOLT, stored as percentage with two decimals
        uint256 boltDeposited; // Amount of BOLT depositted
        uint256 durationMultiplier; // Transient, populated by getFarmerByFarmIdAndAddress
        uint256 startingBlock; // Used in calculation of duration bonus multiplier

        uint256 rewards; // Transient
        uint256 rewardDebt; // The amount relative to farm.accRewardsPerShare the user can't get as reward
        uint256 thirdPartyRewards; // Transient
        uint256 thirdPartyRewardDebt; // The amount relative to farm.accThirdPartyRewardsPerShare the user can't get as reward

        // Only used for V3
        uint256 fees0; // Transient
        uint256 fees0Debt; // The amount relative to farm.accFees0PerShare the user can't claim
        uint256 fees1; // Transient
        uint256 fees1Debt; // The amount relative to farm.accFees1PerShare the user can't claim
    }

    struct ThirdPartyReward {
        address token;
        address tokenManager;
        uint256 tokensPerBlock;
        uint256 endBlock;
    }

    RewardToken public rewardToken; // Token to be minted as farming reward
    IElectroSwapV2Router internal v2Router; // For adding and removing V2 liquidity
    IElectroSwapV3Factory internal v3Factory; // Used to get V3 pool addresses
    INonfungiblePositionManager internal positionManager; // Used to get position info and add/remove liquidity
    IUncollectedFeeHelper internal feeHelper;
    address internal wetnAddress;
    address internal v2Factory; // Used to verify ElectroSwap V2 LP tokens
    address internal boltToken; // BOLT Token address

    uint256 public totalAllocPoint; // Tracks total allocation points across all pools
    uint256 public rewardPerBlock; // Number of rewards per block shared across all incentivized pools

    uint256 internal constant CALC_PRECISION = 1e18; // A big number to perform mul and div operations
    uint256 internal constant MAX_BLOCKS_FOR_BONUS = 6307200; // 1 year worth of blocks
    address internal constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;
    
    // Tracks state of current farms and farmers
    uint256 public farmCount;
    mapping(address => uint256[]) internal poolFarmIds;  // mapping(address => farmId[])
    mapping(uint256 => Farm) internal farmInfo;  // mapping(farmId => Farm))
    mapping(uint256 => mapping(address => Farmer)) internal farmerInfo; // mapping(farmId => mapping(farmerAddr => Farmer))
    mapping(uint256 => ThirdPartyReward) internal thirdPartyRewards;  // mapping(farmId => ThirdPartyReward))
    mapping(uint256 => uint256) internal boltMultipliers; // mapping(valid BOLT deposit => bonus multiplier padded by 10000X)

    constructor(
        string memory _rewardName, string memory _rewardSymbol, uint256 _rewardSupply, uint256 _rewardPerBlock, // Reward related params
        address _wetnAddress, address _boltToken, // WETN and BOLT address
        address _v2Factory, address _v2Router, // V2 params
        address _v3Factory, address _positionManager, address _feeHelper // V3 params
    ) Ownable(msg.sender) {

        rewardToken = new RewardToken(_rewardName, _rewardSymbol);
        rewardToken.mint(msg.sender, _rewardSupply);
        rewardToken.renounceOwnership();

        rewardPerBlock = _rewardPerBlock;

        boltToken = _boltToken;

        v2Factory = _v2Factory;
        v2Router = IElectroSwapV2Router(_v2Router);
        positionManager = INonfungiblePositionManager(_positionManager);
        v3Factory = IElectroSwapV3Factory(_v3Factory);
        feeHelper = IUncollectedFeeHelper(_feeHelper);
        
        wetnAddress = _wetnAddress;

        boltMultipliers[0] = 10000; // 1.00X Reward multiplier (not changeable)
        boltMultipliers[50000 * 1e18] = 10500; // 1.05X Reward multiplier
        boltMultipliers[100000 * 1e18] = 11500; // 1.15X Reward multiplier
    }

    /** ==================== ADMIN FUNCTIONS ==================== */

    // Add a new incentivized V2 farm by passing an existing V2 pair address. Can only be called by the owner (ElectroSwap).
    function adminCreateFarm_V2(string memory _name, address _pairAddr, uint256 _allocPoint) external onlyOwner nonReentrant {
        require(_pairAddr != address(0), "Cannot add zero address");
        require(poolFarmIds[_pairAddr].length == 0, "Pool already added");
        require(IElectroSwapV2Pair(_pairAddr).factory() == v2Factory, "Invalid LP Token");

        _updateAllFarmRewardsAndFees();

        totalAllocPoint += _allocPoint;

        IElectroSwapV2Pair pair = IElectroSwapV2Pair(_pairAddr);
        address token0 = pair.token0();
        address token1 = pair.token1();

        farmCount++;
        poolFarmIds[_pairAddr].push(farmCount);

        address[] memory farmerAddresses; // empty array
        farmInfo[farmCount] = Farm({
            id: farmCount,
            version: 2,
            name: _name,
            poolAddr: _pairAddr,
            liquidity: 0,
            allocPoint: _allocPoint,
            
            lastCalcBlock: block.number,
            accRewardsPerShare: 0,
            accThirdPartyRewardsPerShare: 0,

            farmers: farmerAddresses,
            farmerCount: 0,
            token0: token0,
            token1: token1,

            tokenId: 0,
            tickLower: 0,
            tickUpper: 0,
            fee: 0,
            accFees0PerShare: 0,
            accFees1PerShare: 0,

            active: true
        });

        emit FarmCreated(farmCount, _pairAddr, 2, 0, _allocPoint, totalAllocPoint);
    }


    // Add a new incentivized V3 farm by passing a position token ID, which requires prior approval. Can only be called by the owner (ElectroSwap).
    function adminCreateFarm_V3(string memory _name, uint256 _tokenId, uint256 _allocPoint) external onlyOwner nonReentrant {
        
        Position memory position = _getPosition(_tokenId);
        require(position.token0 != address(0) && position.token1 != address(0), "Invalid tokenId");
        positionManager.safeTransferFrom(msg.sender, address(this), _tokenId);

        address poolAddr = v3Factory.getPool(position.token0, position.token1, position.fee);

        _updateAllFarmRewardsAndFees();

        totalAllocPoint += _allocPoint;
        farmCount++;
        poolFarmIds[poolAddr].push(farmCount);

        address[] memory farmerAddresses = new address[](1);
        farmerAddresses[0] = msg.sender; 

        farmInfo[farmCount] = Farm({
            id: farmCount,
            version: 3,
            name: _name,
            poolAddr: poolAddr,
            liquidity: position.liquidity,
            allocPoint: _allocPoint,

            lastCalcBlock: block.number,
            accRewardsPerShare: 0,
            accThirdPartyRewardsPerShare: 0,

            farmers: farmerAddresses,
            farmerCount: 1,

            tokenId: _tokenId,
            token0: position.token0,
            token1: position.token1,
            tickLower: position.tickLower,
            tickUpper: position.tickUpper,
            fee: position.fee,
            accFees0PerShare: 0,
            accFees1PerShare: 0,
            
            active: true
        });

        farmerInfo[farmCount][msg.sender] = Farmer({
            addr: msg.sender,
            liquidity: position.liquidity,
            boltMultiplier: boltMultipliers[0], // 1.00X 
            boltDeposited: 0,
            durationMultiplier: 0,
            startingBlock: block.number,
            
            rewards: 0,
            rewardDebt: 0,
            thirdPartyRewards: 0,
            thirdPartyRewardDebt: 0,
            fees0: 0,
            fees0Debt: 0,
            fees1: 0,
            fees1Debt: 0
        });

        emit FarmCreated(farmCount, poolAddr, 3, _tokenId, _allocPoint, totalAllocPoint);
    }

    // Update the farm's allocation points. Can only be called by the owner (ElectroSwap).
    function adminUpdateFarmAllocPoints(uint256 _farmId, uint256 _allocPoint) external onlyOwner nonReentrant {
        Farm storage farm = farmInfo[_farmId];
        require(farm.poolAddr != address(0), "Invalid pool");

        _updateAllFarmRewardsAndFees();

        totalAllocPoint = totalAllocPoint - farm.allocPoint + _allocPoint;
        farm.allocPoint = _allocPoint;

        emit FarmAllocationUpdated(_farmId, farm.poolAddr, _allocPoint, totalAllocPoint);
    }

    // Update the farm's name. Can only be called by the owner (ElectroSwap).
    function adminUpdateFarmName(uint256 _farmId, string memory _name) external onlyOwner nonReentrant {
        Farm storage farm = farmInfo[_farmId];
        require(farm.poolAddr != address(0), "Invalid pool");

        farm.name = _name;

        emit FarmNameUpdated(_farmId, farm.poolAddr, _name);
    }

    // Update the number of rewards per block. Can only be called by the owner (ElectroSwap).
    function adminUpdateRewardsPerBlock(uint256 _rewardPerBlock) external onlyOwner nonReentrant {
        uint256 prevRewardPerBlock = rewardPerBlock;
        rewardPerBlock = _rewardPerBlock;

        _updateAllFarmRewardsAndFees();
        
        emit RewardsPerBlockUpdated(rewardPerBlock, prevRewardPerBlock);
    }

    // Update the valid BOLT deposit amounts and corresponding multipliers. Can only be called by the owner (ElectroSwap).
    function adminUpdateBoltMultiplier(uint256 _amountBolt, uint256 _multiplier) external onlyOwner nonReentrant {
        require(_multiplier >= 10000, "Multiplier must be >= 10000");
        require(_amountBolt > 0, "Amount must be > 0");

        boltMultipliers[_amountBolt] = _multiplier;

        emit BoltMultiplierUpdated(_amountBolt, _multiplier);
    }
    
    // Remove an incentivized farm. Still allows withdrawls, but no deposits. Can only be called by the owner (ElectroSwap).
    function adminDeactivateFarm(uint256 _farmId) external onlyOwner nonReentrant {

        Farm storage farm = farmInfo[_farmId];
        require(farm.poolAddr != address(0), "Nothing to deactivate");

        _updateAllFarmRewardsAndFees();

        totalAllocPoint -= farm.allocPoint;

        emit FarmDeactivated(_farmId, farm.poolAddr, farm.version, farm.tokenId);

        farm.active = false;
        farm.allocPoint = 0;
    }

    // Add third party rewards to a farm. Can only be called by the owner (ElectroSwap).
    function adminAddThirdPartyReward(uint256 _farmId, address _token, address _tokenManager) external onlyOwner nonReentrant {
        require(_token != address(0) && _tokenManager != address(0), "Cannot be zero address");

        thirdPartyRewards[_farmId] = ThirdPartyReward({
            token: _token,
            tokenManager: _tokenManager, // Address of user that can update the tokensPerBlock
            tokensPerBlock: 0,
            endBlock: 0
        });

        emit ThirdPartyRewardAdded(_farmId, _token, _tokenManager);
    }

    /** ==================== PRIMARY FUNCTIONS ==================== */

    // Deposit tokens into yield farm for rewards allocation.
    function deposit(uint256 _farmId, uint256 _amount0, uint256 _amount1, uint256 _amountBolt) payable external nonReentrant {
        
        Farm storage farm = farmInfo[_farmId];
        require(farm.active, "Inactive farm");
        
        _updateFarmRewardsAndFees(_farmId);

        Farmer storage farmer = farmerInfo[_farmId][msg.sender];

        bool token0Native = false;
        bool token1Native = false;
        
        // Wrap native ETN and transfer other token from msg.sender to the yield farming contract. Requires prior approval
        if(msg.value > 0){
            require(farm.token0 == wetnAddress || farm.token1 == wetnAddress, "Native ETN sent to unsupporting farm");

            IWETN(wetnAddress).deposit{value: msg.value}();

            if(farm.token0 == wetnAddress){
                _amount0 = msg.value;
                token0Native = true;
                IERC20(farm.token1).transferFrom(msg.sender, address(this), _amount1);
            } else {
                _amount1 = msg.value;
                token1Native = true;
                IERC20(farm.token0).transferFrom(msg.sender, address(this), _amount0);
            }
        } 
        // Transfer both tokens from msg.sender to the yield farming contract. Requires prior approval
        else {
            IERC20(farm.token0).transferFrom(msg.sender, address(this), _amount0);
            IERC20(farm.token1).transferFrom(msg.sender, address(this), _amount1);
        }

        require(_amount0 > 0 && _amount1 > 0, "Both amounts must be positive");

        (uint256 liquidityAdded, uint256 amount0Added, uint256 amount1Added) = farm.version == 2 ? 
            _depositV2(farm, _amount0, _amount1) :  
            _depositV3(farm, _amount0, _amount1);

        // Refund whatever was not added to the position to msg.sender
        _refundIfNeeded(farm.token0, token0Native, _amount0, amount0Added);
        _refundIfNeeded(farm.token1, token1Native, _amount1, amount1Added);
        
        farm.liquidity += liquidityAdded;
        
        // New farmer
        if (farmer.liquidity == 0) {
            farm.farmers.push(msg.sender); // Add farmer to the pool's farmer list
            farm.farmerCount += 1;

            farmer.addr = msg.sender;
            farmer.liquidity = liquidityAdded;
            farmer.boltMultiplier = boltMultipliers[0]; // Default value
            farmer.startingBlock = block.number;

            farmer.rewardDebt = liquidityAdded * farm.accRewardsPerShare / CALC_PRECISION;
            farmer.thirdPartyRewardDebt = liquidityAdded * farm.accThirdPartyRewardsPerShare / CALC_PRECISION;
            farmer.fees0Debt = liquidityAdded * farm.accFees0PerShare / CALC_PRECISION;
            farmer.fees1Debt = liquidityAdded * farm.accFees1PerShare / CALC_PRECISION;

            emit FarmDeposit(_farmId, msg.sender, amount0Added, amount1Added, liquidityAdded);
        } 
        else {

            // Adjust starting block based on amount of liquidity added to prevent unfair duration multiplier manipulation
            uint256 increaseOfTotal = (liquidityAdded * 1e18) / (farmer.liquidity + liquidityAdded);
            uint256 blocksServed = block.number - farmer.startingBlock;
            uint256 adjustedBlocksServed = blocksServed - ((blocksServed * increaseOfTotal) / 1e18); // Resolves to zero if negative
            farmer.startingBlock = block.number - adjustedBlocksServed;
            
            uint256 rewardsEarned = (farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION) - farmer.rewardDebt;
            uint256 thirdPartyRewardsEarned = (farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION) - farmer.thirdPartyRewardDebt;
            uint256 fees0Earned = (farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION) - farmer.fees0Debt;
            uint256 fees1Earned = (farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION) - farmer.fees1Debt;
            
            farmer.liquidity += liquidityAdded;
            
            farmer.rewardDebt = (farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION) - rewardsEarned; 
            farmer.thirdPartyRewardDebt = (farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION) - thirdPartyRewardsEarned; 
            farmer.fees0Debt = (farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION) - fees0Earned; 
            farmer.fees1Debt = (farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION) - fees1Earned; 

            emit FarmIncrease(_farmId, msg.sender, amount0Added, amount1Added, liquidityAdded, farmer.startingBlock);
        }

        if(_amountBolt > 0){
            uint256 totalBolt = _amountBolt + farmer.boltDeposited;
            require(boltMultipliers[totalBolt] != 0, "Invalid BOLT deposit");
            IERC20(boltToken).transferFrom(msg.sender, address(this), _amountBolt);
            
            farmer.boltDeposited = totalBolt;
            farmer.boltMultiplier = boltMultipliers[totalBolt];
        }
    }

    // Withdraw tokens AND rewards. If _asNative is true AND the pair contains WETN, native ETN will be sent.
    function withdraw(uint256 _farmId, uint256 _liquidityAmt, bool _asNative) external nonReentrant {

        Farm storage farm = farmInfo[_farmId];
        Farmer storage farmer = farmerInfo[_farmId][msg.sender];

        require(farmer.liquidity >= _liquidityAmt, "Cannot withdraw more than deposited");

        (uint256 rewardsMinted, uint256 fees0Collected, uint256 fees1Collected, uint256 thirdPartyRewardsCollected) = _collectRewardsAndFees(_farmId, _asNative);

        farmer.liquidity -= _liquidityAmt;

        farmer.rewardDebt = farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION;
        if(farm.version == 3){
            farmer.fees0Debt = farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION;
            farmer.fees1Debt = farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION;
        }

        if(thirdPartyRewards[_farmId].token != address(0)){
            farmer.thirdPartyRewardDebt = farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION;
        }

        // Just withdraw rewards and fees
        if(_liquidityAmt == 0){
            emit FarmWithdrawl(_farmId, msg.sender, 0, 0, rewardsMinted, fees0Collected, fees1Collected, thirdPartyRewardsCollected);
            return;
        }

        if(_asNative){
            require(farm.token0 == wetnAddress || farm.token1 == wetnAddress, "Unsupporting farm");
        }

        // Includes removed liquidity and fees
        (uint256 amount0, uint256 amount1) = farm.version == 2 ? _withdrawV2(farm, _liquidityAmt, _asNative) : _withdrawV3(farm, _liquidityAmt, _asNative);

        // Clear state on removal of all liquidity
        if (farmer.liquidity == 0) {

            // Withdraw all BOLT 
            if(farmer.boltDeposited > 0){
                IERC20(boltToken).transfer(msg.sender, farmer.boltDeposited);
            }
            delete farmerInfo[_farmId][msg.sender];
            _removeFarmer(_farmId, msg.sender);
        }

        farm.liquidity -= _liquidityAmt;

        emit FarmWithdrawl(_farmId, msg.sender, amount0, amount1, rewardsMinted, fees0Collected, fees1Collected, thirdPartyRewardsCollected);
    }


    /** ==================== INTERNAL FUNCTIONS ==================== */
    
    // Deposits tokens into V2 yield farm for rewards allocation.
    function _depositV2(Farm memory _farm, uint256 _amount0, uint256 _amount1) internal returns (uint256 liquidityAdded, uint256 amount0Added, uint256 amount1Added) {

        // Approve the router to spend the tokens
        IERC20(_farm.token0).approve(address(v2Router), _amount0);
        IERC20(_farm.token1).approve(address(v2Router), _amount1);

        (amount0Added, amount1Added, liquidityAdded) = v2Router.addLiquidity(
            _farm.token0,
            _farm.token1,
            _amount0,
            _amount1,
            1,
            1,
            address(this),
            block.timestamp
        );
    }

    // Withdraw tokens AND rewards.
    function _withdrawV2(Farm memory _farm, uint256 _liquidityAmt, bool _asNative) internal returns (uint256 amount0, uint256 amount1){
        // Transfers token0 and token1 back to this contract
        IERC20(_farm.poolAddr).approve(address(v2Router), _liquidityAmt);
        (amount0, amount1) = v2Router.removeLiquidity(_farm.token0, _farm.token1, _liquidityAmt, 1, 1, address(this), block.timestamp);

        _transferTokens(_farm, amount0, amount1, _asNative);
    }

    // Deposit tokens into V3 yield farm for rewards allocation.
    function _depositV3(Farm memory _farm, uint256 _amount0, uint256 _amount1) internal returns (uint256 liquidityAdded, uint256 amount0Added, uint256 amount1Added) {

        // Approve the positionManager to spend the tokens
        IERC20(_farm.token0).approve(address(positionManager), _amount0);
        IERC20(_farm.token1).approve(address(positionManager), _amount1);

        // Add liquidity to the existing position
        (liquidityAdded, amount0Added, amount1Added) = positionManager.increaseLiquidity(
            INonfungiblePositionManager.IncreaseLiquidityParams({
                tokenId: _farm.tokenId,
                amount0Desired: _amount0,
                amount1Desired: _amount1,
                amount0Min: 1,
                amount1Min: 1,
                deadline: block.timestamp
            })
        );
    }

    function _withdrawV3(Farm storage _farm, uint256 _liquidityAmt, bool _asNative) internal returns (uint256 amount0, uint256 amount1) {
        // Decreases available liquidity before collecting
        (amount0, amount1) = positionManager.decreaseLiquidity(
            INonfungiblePositionManager.DecreaseLiquidityParams({
                tokenId: _farm.tokenId,
                liquidity: uint128(_liquidityAmt),
                amount0Min: 1,
                amount1Min: 1,
                deadline: block.timestamp
            })
        );

        // Transfers token0 and token back to this contract
        _collectV3(_farm.tokenId);

        _transferTokens(_farm, amount0, amount1, _asNative);
    }

    function _transferTokens(Farm memory _farm, uint256 amount0, uint256 amount1, bool _asNative) internal {
        if(_asNative && _farm.token0 == wetnAddress){
            _withdrawNativeETN(amount0);
            IERC20(_farm.token1).transfer(msg.sender, amount1);
        } 
        else if(_asNative && _farm.token1 == wetnAddress){
            _withdrawNativeETN(amount1);
            IERC20(_farm.token0).transfer(msg.sender, amount0);
        } 
        else {
            IERC20(_farm.token0).transfer(msg.sender, amount0);
            IERC20(_farm.token1).transfer(msg.sender, amount1);
        }
    }

    function _refundIfNeeded(address token, bool tokenIsNative, uint256 amountDeposited, uint256 amountAdded) internal {
        if(amountDeposited > amountAdded){
            uint256 refundAmount = amountDeposited - amountAdded;
            if(tokenIsNative){
                _withdrawNativeETN(refundAmount);
            } else {
                IERC20(token).transfer(msg.sender, refundAmount);
            }
        }
    }

    function _withdrawNativeETN(uint256 amount) internal {
        IWETN(wetnAddress).withdraw(amount);
        (bool success, ) = payable(msg.sender).call{value: amount}("");
        require(success, "ETN transfer failed");
    }

    function _collectV3(uint256 tokenId) internal returns (uint256 amount0Collected, uint256 amount1Collected){
        (amount0Collected, amount1Collected) = positionManager.collect(
            INonfungiblePositionManager.CollectParams({
                tokenId: tokenId,
                recipient: address(this),
                amount0Max: type(uint128).max,
                amount1Max: type(uint128).max
            })
        );
    }

    function _collectRewardsAndFees(uint256 _farmId, bool _asNative) internal returns (uint256 rewardsCollected, uint256 fees0Collected, uint256 fees1Collected, uint256 thirdPartyRewardsCollected) {
        _updateFarmRewardsAndFees(_farmId);
        
        Farmer storage farmer = farmerInfo[_farmId][msg.sender];

        if(farmer.liquidity > 0){
            
            Farm storage farm = farmInfo[_farmId];

            uint256 newRewardDebt = farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION;
            uint256 rewardsEarned = newRewardDebt - farmer.rewardDebt;

            // boltMultiplier and durationMultiplier are both multiplied by 10000, so we divide by 100000000
            rewardsCollected = (rewardsEarned * farmer.boltMultiplier * _calculateDurationMultiplier(block.number - farmer.startingBlock)) / 100000000;
            if (rewardsCollected != 0) {
                rewardToken.mint(msg.sender, rewardsCollected);
            }

            if(farm.version == 3){
                uint256 newFee0Debt = farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION;
                fees0Collected = newFee0Debt - farmer.fees0Debt;

                uint256 newFee1Debt = farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION;
                fees1Collected = newFee1Debt - farmer.fees1Debt;

                _transferTokens(farm, fees0Collected, fees1Collected, _asNative);
            }

            ThirdPartyReward memory thirdPartyReward = thirdPartyRewards[_farmId];
            if(thirdPartyReward.token != address(0)){

                uint256 newThirdPartyDebt = farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION;
                thirdPartyRewardsCollected = newThirdPartyDebt - farmer.thirdPartyRewardDebt;

                if (thirdPartyRewardsCollected != 0) {
                    IERC20(thirdPartyReward.token).transfer(msg.sender, thirdPartyRewardsCollected);
                }
            }
        }
    }

    // Called prior to modifying totalAllocPoint or rewardPerBlock (by admin)
    function _updateAllFarmRewardsAndFees() private {
        for (uint256 i = 1; i <= farmCount; i++) {
            _updateFarmRewardsAndFees(i);
        }
    }

    // Sets accumulated amounts for rewards, third party rewards and fees by share
    function _updateFarmRewardsAndFees(uint256 _farmId) private {
        Farm storage farm = farmInfo[_farmId];
        
        if (farm.liquidity == 0 || !farm.active) {
            farm.lastCalcBlock = block.number;
            return;
        }

        uint256 blocksSinceLastCalc = block.number - farm.lastCalcBlock;
        uint256 rewards = blocksSinceLastCalc * ((rewardPerBlock * farm.allocPoint) / totalAllocPoint);
        farm.accRewardsPerShare += rewards * CALC_PRECISION / farm.liquidity;

        if(farm.version == 3){
            (uint256 fees0Collected, uint256 fees1Collected) = _collectV3(farm.tokenId);
            farm.accFees0PerShare += fees0Collected * CALC_PRECISION / farm.liquidity;
            farm.accFees1PerShare += fees1Collected * CALC_PRECISION / farm.liquidity;
        }

        ThirdPartyReward storage thirdPartyReward = thirdPartyRewards[_farmId];
        if(thirdPartyReward.token != address(0) && thirdPartyReward.tokensPerBlock > 0){
            uint256 thirdPartyRewardTokens;

            // Third party rewards ended between last calculation and now
            if(farm.lastCalcBlock < thirdPartyReward.endBlock && thirdPartyReward.endBlock <= block.number){
                uint256 blocksAbleToPay = thirdPartyReward.endBlock - farm.lastCalcBlock;
                thirdPartyRewardTokens = blocksAbleToPay * thirdPartyReward.tokensPerBlock;
                thirdPartyReward.tokensPerBlock = 0;
            } 
            else {
                thirdPartyRewardTokens = blocksSinceLastCalc * thirdPartyReward.tokensPerBlock;
            }

            farm.accThirdPartyRewardsPerShare += thirdPartyRewardTokens * CALC_PRECISION / farm.liquidity;
        }

        farm.lastCalcBlock = block.number;
    }

    // Internal function that calculates duration bonus multiplier
    function _calculateDurationMultiplier(uint256 blocksServed) internal view returns (uint256) {
        // Occurs when farmer is undefined
        if(blocksServed == block.number){
            return 10000;
        }
        // If blockCount is greater than or equal to maxBlockCount, return 25000
        else if (blocksServed >= MAX_BLOCKS_FOR_BONUS) {
            return 25000;
        }

        // Calculate the linear yield
        return 10000 + (15000 * blocksServed) / MAX_BLOCKS_FOR_BONUS;
    }

    struct Position {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint128 liquidity;
    }

    function _getPosition(uint256 _tokenId) internal view returns (Position memory) {
        (, ,address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint128 liquidity, , , ,) = positionManager.positions(_tokenId);

        return Position({
            token0: token0,
            token1: token1,
            fee: fee,
            tickLower: tickLower,
            tickUpper: tickUpper,
            liquidity: liquidity
        });
    }

    // Remove farmer address from a farm
    function _removeFarmer(uint256 _farmId, address _address) internal {

        Farm storage farm = farmInfo[_farmId];
        uint farmerCount = farm.farmers.length;
        uint indexToRemove = farmerCount;

        for (uint i = 0; i < farmerCount; i++) {
            if (farm.farmers[i] == _address) {
                indexToRemove = i;
                break;
            }
        }

        if (indexToRemove < farmerCount) {
            farm.farmers[indexToRemove] = farm.farmers[farmerCount - 1];
            farm.farmers.pop();
            farm.farmerCount -= 1;
        }
    }

    /** ==================== EXTERNAL HELPER FUNCTIONS ==================== */

    // Exposes function that calculates duration bonus multiplier - for gas savings
    function calculateDurationMultiplier(uint256 blocksServed) external view returns (uint256) {
        return _calculateDurationMultiplier(blocksServed);
    }

    // At least one farmer should be active when depositing, otherwise third party rewards go unaccounted for
    function depositThirdPartyReward(uint256 _farmId, uint256 _amountToken, uint256 _distributeOverBlockCount) external {

        ThirdPartyReward storage thirdPartyReward = thirdPartyRewards[_farmId];
        
        require(thirdPartyReward.token != address(0), "Third party reward not defined");
        require(owner() == msg.sender || thirdPartyReward.tokenManager == msg.sender, "Only token manager can deposit");
        
        _updateFarmRewardsAndFees(_farmId);

        IERC20(thirdPartyReward.token).transferFrom(msg.sender, address(this), _amountToken);
        
        uint256 blocksTillEnd = thirdPartyReward.endBlock >= block.number ? thirdPartyReward.endBlock - block.number : 0;
        uint256 unaccountedTokens = blocksTillEnd * thirdPartyReward.tokensPerBlock;

        thirdPartyReward.tokensPerBlock = (unaccountedTokens + _amountToken) / _distributeOverBlockCount;
        thirdPartyReward.endBlock = block.number + _distributeOverBlockCount;

        emit ThirdPartyRewardDeposited(_farmId, thirdPartyReward.token, thirdPartyReward.tokensPerBlock, thirdPartyReward.endBlock);
    }

    // Update the third party reward manager for a farm. Can only be called by the ElectroSwap or Third-Party token manager.
    function updateThirdPartyRewardManager(uint256 _farmId, address _tokenManager) external {
        require(_tokenManager != address(0), "Cannot be zero address");
        require(owner() == msg.sender || thirdPartyRewards[_farmId].tokenManager == msg.sender, "Only token manager can update");

        thirdPartyRewards[_farmId].tokenManager = _tokenManager;

        emit ThirdPartyRewardUpdated(_farmId, _tokenManager);
    }

    function getFarmById(uint256 _farmId) external view returns (Farm memory farm) {
        farm = farmInfo[_farmId];

        uint256 blocksSinceLastCalc = block.number - farm.lastCalcBlock;
        
        if(farm.liquidity > 0){
            uint256 rewards = blocksSinceLastCalc * ((rewardPerBlock * farm.allocPoint) / totalAllocPoint);
            farm.accRewardsPerShare += rewards * CALC_PRECISION / farm.liquidity;

            if(farm.version == 3){
                (uint256 uncollectedFee0, uint256 uncollectedFee1) = feeHelper.getUncollectedFees(farm.tokenId);
                farm.accFees0PerShare += uncollectedFee0 * CALC_PRECISION / farm.liquidity;
                farm.accFees1PerShare += uncollectedFee1 * CALC_PRECISION / farm.liquidity;
            }

            if(thirdPartyRewards[_farmId].token != address(0)){
                uint256 thirdPartyRewardTokens = blocksSinceLastCalc * thirdPartyRewards[_farmId].tokensPerBlock;
                farm.accThirdPartyRewardsPerShare += thirdPartyRewardTokens * CALC_PRECISION / farm.liquidity;
            }
        }
        
        // Prevent large responses by sending empty farmer array
        address[] memory emptyArr;
        farm.farmers = emptyArr;
    }

    function getFarmIdsByPoolAddress(address _poolAddr) external view returns (uint256[] memory) {
        return poolFarmIds[_poolAddr];
    }

    function getFarmerByFarmIdAndIndex(uint256 _farmId, uint256 _farmerIndex) external view returns (Farmer memory) {
        address farmerAddress = farmInfo[_farmId].farmers[_farmerIndex];
        return getFarmerByFarmIdAndAddress(_farmId, farmerAddress);
    }

    function getFarmerByFarmIdAndAddress(uint256 _farmId, address _farmerAddress) public view returns (Farmer memory farmer) {
        Farm memory farm = farmInfo[_farmId];
        farmer = farmerInfo[_farmId][_farmerAddress];
        farmer.durationMultiplier = _calculateDurationMultiplier(block.number - farmer.startingBlock);

        uint256 blocksSinceLastCalc = block.number - farm.lastCalcBlock;

        if(farm.liquidity > 0){
            uint256 rewards = blocksSinceLastCalc * ((rewardPerBlock * farm.allocPoint) / totalAllocPoint);
            farm.accRewardsPerShare += rewards * CALC_PRECISION / farm.liquidity;
        }
        
        uint256 rewardsEarned = (farmer.liquidity * farm.accRewardsPerShare / CALC_PRECISION) - farmer.rewardDebt;
        farmer.rewards = (rewardsEarned * farmer.boltMultiplier * farmer.durationMultiplier) / 100000000;

        ThirdPartyReward memory thirdPartyReward = thirdPartyRewards[_farmId];
        if(thirdPartyReward.token != address(0)){
            if(farm.liquidity > 0){
                
                uint256 thirdPartyRewardTokens;
                if(farm.lastCalcBlock < thirdPartyReward.endBlock && thirdPartyReward.endBlock <= block.number){
                    uint256 blocksAbleToPay = thirdPartyReward.endBlock - farm.lastCalcBlock;
                    thirdPartyRewardTokens = blocksAbleToPay * thirdPartyReward.tokensPerBlock;
                } 
                else {
                    thirdPartyRewardTokens = blocksSinceLastCalc * thirdPartyReward.tokensPerBlock;
                }
                farm.accThirdPartyRewardsPerShare += thirdPartyRewardTokens * CALC_PRECISION / farm.liquidity;
            }

            farmer.thirdPartyRewards = (farmer.liquidity * farm.accThirdPartyRewardsPerShare / CALC_PRECISION) - farmer.thirdPartyRewardDebt;
        }

        if(farm.version == 3){
            if(farm.liquidity > 0){
                (uint256 uncollectedFee0, uint256 uncollectedFee1) = feeHelper.getUncollectedFees(farm.tokenId);
                farm.accFees0PerShare += uncollectedFee0 * CALC_PRECISION / farm.liquidity;
                farm.accFees1PerShare += uncollectedFee1 * CALC_PRECISION / farm.liquidity;
            }
            
            farmer.fees0 = (farmer.liquidity * farm.accFees0PerShare / CALC_PRECISION) - farmer.fees0Debt;
            farmer.fees1 = (farmer.liquidity * farm.accFees1PerShare / CALC_PRECISION) - farmer.fees1Debt;
        }
    }

    function getThirdPartyRewardConfigByFarmId(uint256 _farmId) external view returns (ThirdPartyReward memory) {
        return thirdPartyRewards[_farmId];
    }


    /** ==================== EVENTS ==================== */
    
    event FarmCreated(uint256 indexed farmId, address indexed poolAddr, uint256 version, uint256 tokenId, uint256 allocPoints, uint256 totalAllocPoints);
    event FarmDeactivated(uint256 indexed farmId, address indexed poolAddr, uint256 version, uint256 tokenId);
    event FarmAllocationUpdated(uint256 indexed farmId, address indexed poolAddr, uint256 allocPoints, uint256 totalAllocPoints);
    event FarmNameUpdated(uint256 indexed farmId, address indexed poolAddr, string _name);

    event FarmDeposit(uint256 indexed farmId, address indexed farmer, uint256 amount0Added, uint256 amount1Added, uint256 liquidityAdded);
    event FarmIncrease(uint256 indexed farmId, address indexed farmer, uint256 amount0Added, uint256 amount1Added, uint256 liquidityAdded, uint256 adjustedStartingBlock);
    event FarmWithdrawl(uint256 indexed farmId, address indexed farmer, uint256 amount0Withdrawn, uint256 amount1Withdrawn, uint256 amountRewards, uint256 fees0Collected, uint256 fees1Collected, uint256 thirdPartyRewardsCollected);
    
    event ThirdPartyRewardAdded(uint256 indexed farmId, address indexed rewardToken, address indexed tokenManager);
    event ThirdPartyRewardUpdated(uint256 indexed farmId, address tokenManager);
    event ThirdPartyRewardDeposited(uint256 indexed farmId, address indexed rewardToken, uint256 tokensPerBlock, uint256 endBlock);

    event RewardsPerBlockUpdated(uint256 rewardPerBlock, uint256 prevRewardPerBlock);
    event BoltMultiplierUpdated(uint256 amountBolt, uint256 multiplier);

    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return this.onERC721Received.selector;
    }

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

contracts/interfaces.sol

// SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity ^0.8.0;

interface IWETN {
    function deposit() external payable;
    function withdraw(uint256 amount) external;
}

interface IElectroSwapV2Pair {
    function token0() external view returns (address);
    function token1() external view returns (address);
    function factory() external returns (address);
    function totalSupply() external returns (uint256);
}

interface IElectroSwapV2Router {
    function addLiquidity(address token0, address token1, uint amount0Desired, uint amount1Desired, uint amount0Min, uint amount1Min, address to, uint deadline) external returns (uint amount0, uint amount1, uint liquidity);
    function removeLiquidity(address token0, address token1, uint liquidity, uint amount0Min, uint amount1Min, address to, uint deadline) external returns (uint amount0, uint amount1);
    function WETH() external returns (address wetnAddress);
}

interface IElectroSwapV3Factory {
    function getPool(address, address, uint24) external view returns (address);
}

interface IUncollectedFeeHelper {
    function getUncollectedFees(uint256 positionTokenId) external view returns (uint256 uncollectedFee0, uint256 uncollectedFee1);
}

interface INonfungiblePositionManager {

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    function increaseLiquidity(IncreaseLiquidityParams calldata params) external payable returns (uint128 liquidity, uint256 amount0, uint256 amount1);

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    function decreaseLiquidity(DecreaseLiquidityParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

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

    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    function positions(uint256 tokenId) external view returns (
        uint96 nonce,
        address operator,
        address token0,
        address token1,
        uint24 fee,
        int24 tickLower,
        int24 tickUpper,
        uint128 liquidity,
        uint256 feeGrowthInside0LastX128,
        uint256 feeGrowthInside1LastX128,
        uint128 tokensOwed0,
        uint128 tokensOwed1
    );

    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function factory() external view returns (address factory);
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"string","name":"name","internalType":"string"},{"type":"string","name":"symbol","internalType":"string"}]},{"type":"error","name":"ERC20InsufficientAllowance","inputs":[{"type":"address","name":"spender","internalType":"address"},{"type":"uint256","name":"allowance","internalType":"uint256"},{"type":"uint256","name":"needed","internalType":"uint256"}]},{"type":"error","name":"ERC20InsufficientBalance","inputs":[{"type":"address","name":"sender","internalType":"address"},{"type":"uint256","name":"balance","internalType":"uint256"},{"type":"uint256","name":"needed","internalType":"uint256"}]},{"type":"error","name":"ERC20InvalidApprover","inputs":[{"type":"address","name":"approver","internalType":"address"}]},{"type":"error","name":"ERC20InvalidReceiver","inputs":[{"type":"address","name":"receiver","internalType":"address"}]},{"type":"error","name":"ERC20InvalidSender","inputs":[{"type":"address","name":"sender","internalType":"address"}]},{"type":"error","name":"ERC20InvalidSpender","inputs":[{"type":"address","name":"spender","internalType":"address"}]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"type":"address","name":"account","internalType":"address"}]},{"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":"OwnershipTransferred","inputs":[{"type":"address","name":"previousOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"TokensMinted","inputs":[{"type":"address","name":"to","internalType":"address","indexed":true},{"type":"uint256","name":"amount","internalType":"uint256","indexed":false}],"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":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"allowance","inputs":[{"type":"address","name":"owner","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":"value","internalType":"uint256"}]},{"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":"uint8","name":"","internalType":"uint8"}],"name":"decimals","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"mint","inputs":[{"type":"address","name":"_to","internalType":"address"},{"type":"uint256","name":"_amount","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"minters","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","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":"renounceOwnership","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"symbol","inputs":[]},{"type":"function","stateMutability":"view","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":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"transferFrom","inputs":[{"type":"address","name":"from","internalType":"address"},{"type":"address","name":"to","internalType":"address"},{"type":"uint256","name":"value","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"transferOwnership","inputs":[{"type":"address","name":"newOwner","internalType":"address"}]}]
            

Deployed ByteCode

0x608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146106c257508063095ea7b31461063c57806318160ddd1461061e57806323b872dd14610528578063313ce5671461050c57806340c10f19146103de57806370a08231146103a4578063715018a6146103505780638da5cb5b1461032957806395d89b4114610208578063a9059cbb146101d7578063dd62ed3e1461017f578063f2fde38b146100fd5763f46eccc4146100b957600080fd5b346100f85760203660031901126100f8576001600160a01b036100da6107de565b166000526006602052602060ff604060002054166040519015158152f35b600080fd5b346100f85760203660031901126100f8576001600160a01b0361011e6107de565b6101266108b9565b168015610169576001600160a01b03600554828219821617600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b631e4fbdf760e01b600052600060045260246000fd5b346100f85760403660031901126100f8576101986107de565b6001600160a01b036101a86107f4565b911660005260016020526001600160a01b03604060002091166000526020526020604060002054604051908152f35b346100f85760403660031901126100f8576101fd6101f36107de565b602435903361080a565b602060405160018152f35b346100f85760003660031901126100f85760405160006004548060011c9060018116801561031f575b60208310811461030b578285529081156102ef5750600114610298575b50819003601f01601f191681019067ffffffffffffffff8211818310176102825761027e82918260405282610795565b0390f35b634e487b7160e01b600052604160045260246000fd5b905060046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b6000905b8282106102d95750602091508201018261024e565b60018160209254838588010152019101906102c4565b90506020925060ff191682840152151560051b8201018261024e565b634e487b7160e01b84526022600452602484fd5b91607f1691610231565b346100f85760003660031901126100f85760206001600160a01b0360055416604051908152f35b346100f85760003660031901126100f8576103696108b9565b60006001600160a01b0360055481198116600555167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b346100f85760203660031901126100f8576001600160a01b036103c56107de565b1660005260006020526020604060002054604051908152f35b346100f85760403660031901126100f8576103f76107de565b6024359033600052600660205260ff60406000205416156104c8576001600160a01b031680156104b25760025482810180911161049c576002557f3f2c9d57c068687834f0de942a9babb9e5acab57d516d3480a3c16ee165a427360206000938385528482526040852081815401905583857fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef84604051858152a3604051908152a280f35b634e487b7160e01b600052601160045260246000fd5b63ec442f0560e01b600052600060045260246000fd5b606460405162461bcd60e51b815260206004820152602060248201527f43616c6c6572206973206e6f7420617574686f72697a656420746f206d696e746044820152fd5b346100f85760003660031901126100f857602060405160128152f35b346100f85760603660031901126100f8576105416107de565b6105496107f4565b604435906001600160a01b0383169283600052600160205260406000206001600160a01b0333166000526020526040600020546000198103610591575b506101fd935061080a565b8381106106015784156105eb5733156105d5576101fd94600052600160205260406000206001600160a01b0333166000526020528360406000209103905584610586565b634a1406b160e11b600052600060045260246000fd5b63e602df0560e01b600052600060045260246000fd5b8390637dc7a0d960e11b6000523360045260245260445260646000fd5b346100f85760003660031901126100f8576020600254604051908152f35b346100f85760403660031901126100f8576106556107de565b6024359033156105eb576001600160a01b03169081156105d557336000526001602052604060002082600052602052806040600020556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b346100f85760003660031901126100f85760006003548060011c9060018116801561078b575b60208310811461030b578285529081156102ef57506001146107345750819003601f01601f191681019067ffffffffffffffff8211818310176102825761027e82918260405282610795565b905060036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b6000905b8282106107755750602091508201018261024e565b6001816020925483858801015201910190610760565b91607f16916106e8565b91909160208152825180602083015260005b8181106107c8575060409293506000838284010152601f8019910116010190565b80602080928701015160408286010152016107a7565b600435906001600160a01b03821682036100f857565b602435906001600160a01b03821682036100f857565b6001600160a01b03169081156108a3576001600160a01b03169182156104b25760008281528060205260408120548281106108895791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b916064928463391434e360e21b8452600452602452604452fd5b634b637e8f60e11b600052600060045260246000fd5b6001600160a01b036005541633036108cd57565b63118cdaa760e01b6000523360045260246000fdfea2646970667358221220648a025d8089657b50380974a2009290b8e70c2a890a620dec571a608d62ef1764736f6c634300081a0033