GLMR Price: $0.020682 (-0.83%)

Contract

0x8fA7A3Cf8a2F6B5B4cf9E00083026F46bB1b81EB

Overview

GLMR Balance

Moonbeam Chain LogoMoonbeam Chain LogoMoonbeam Chain Logo0 GLMR

GLMR Value

$0.00

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Batch Withdraw D...30673872023-03-03 15:29:541060 days ago1677857394IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.06541703101.94
Batch Withdraw D...30538512023-03-01 17:25:001062 days ago1677691500IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04310938101.5
Batch Claim Rewa...30538492023-03-01 17:24:361062 days ago1677691476IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.06130691101.5
Batch Withdraw D...30528482023-03-01 13:56:301062 days ago1677678990IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.06860486101.5
Batch Claim Rewa...30528462023-03-01 13:56:061062 days ago1677678966IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.0734525101.5
Add Dai To Posit...30463872023-02-28 15:52:481063 days ago1677599568IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.06026329101.5
Create New Votin...30405782023-02-27 19:59:361064 days ago1677527976IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.05073467101.5
Create New Votin...30405582023-02-27 19:55:181064 days ago1677527718IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.08337676101.5
Add Dai To Posit...30392142023-02-27 15:18:541064 days ago1677511134IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04617864101.5
Add Dai To Posit...30392012023-02-27 15:16:181064 days ago1677510978IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.0541538101.5
Add Dai To Posit...30319172023-02-26 14:24:421065 days ago1677421482IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04081101.5
Add Dai To Posit...30319102023-02-26 14:23:181065 days ago1677421398IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04593808101.5
Add Dai To Posit...30311932023-02-26 11:56:481065 days ago1677412608IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.0465959101.5
Add Dai To Posit...30311492023-02-26 11:48:001065 days ago1677412080IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04608404101.5
Batch Withdraw D...30267432023-02-25 20:42:361066 days ago1677357756IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.06930826101.5
Batch Claim Rewa...30251562023-02-25 15:15:361066 days ago1677338136IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.11762548102
Add Dai To Posit...30106282023-02-23 13:48:541068 days ago1677160134IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.05189441101.5
Add Dai To Posit...30027552023-02-22 10:59:061069 days ago1677063546IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04334131101.5
Add Dai To Posit...29984312023-02-21 20:05:361070 days ago1677009936IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04850816101.8818
Create New Votin...29968482023-02-21 14:37:421070 days ago1676990262IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.05918779101.5
Batch Claim Rewa...29826132023-02-19 13:42:061072 days ago1676814126IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.04303874101.5
Add Zoo To Posit...29773662023-02-18 19:43:361073 days ago1676749416IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.01400872101.5
Add Zoo To Posit...29772982023-02-18 19:29:481073 days ago1676748588IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.01278016101.5
Add Zoo To Posit...29772972023-02-18 19:29:361073 days ago1676748576IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.01278016101.5
Add Dai To Posit...29771122023-02-18 18:51:001073 days ago1676746260IN
0x8fA7A3Cf...6bB1b81EB
0 GLMR0.03595505101.5
View all transactions

View more zero value Internal Transactions in Advanced View mode

Cross-Chain Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0x1FB81082...1e7d2Ef46
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
NftVotingPosition

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 1 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

pragma solidity 0.8.13;

// SPDX-License-Identifier: MIT

import "ERC721.sol";
import "IERC20.sol";
import "Ownable.sol";
import "NftBattleArena.sol";

/// @title NftVotingPosition
/// @notice contract for voters to interacte with BattleArena functions
contract NftVotingPosition is ERC721, Ownable
{
	event NftBattleArenaSet(address nftBattleArena);

	event ClaimedIncentiveRewardFromVoting(address indexed voter, address beneficiary, uint256 zooReward, uint256 votingPositionId);

	NftBattleArena public nftBattleArena;
	IERC20 public dai;
	IERC20 public zoo;

	constructor(string memory _name, string memory _symbol, address _dai, address _zoo) ERC721(_name, _symbol)
	{
		dai = IERC20(_dai);
		zoo = IERC20(_zoo);

	}

	modifier onlyVotingOwner(uint256 votingPositionId) {
		require(ownerOf(votingPositionId) == msg.sender, "Not the owner of voting");
		_;
	}

	function setNftBattleArena(address payable _nftBattleArena) external onlyOwner
	{
		require(address(nftBattleArena) == address(0));

		nftBattleArena = NftBattleArena(_nftBattleArena);

		emit NftBattleArenaSet(_nftBattleArena);
	}

	function createNewVotingPosition(uint256 stakingPositionId, uint256 amount) external
	{
		require(amount != 0, "zero vote not allowed");                                        // Requires for vote amount to be more than zero.
		require(nftBattleArena.getCurrentStage() == Stage.SecondStage, "Wrong stage!");
		dai.transferFrom(msg.sender, address(nftBattleArena), amount);                        // Transfers DAI to arena contract for vote.
		(,uint256 votingPositionId) = nftBattleArena.createVotingPosition(stakingPositionId, msg.sender, amount);
		_safeMint(msg.sender, votingPositionId);
	}

	function addDaiToPosition(uint256 votingPositionId, uint256 amount) external returns (uint256 votes)
	{
		dai.transferFrom(msg.sender, address(nftBattleArena), amount);                        // Transfers DAI to arena contract for vote.
		return nftBattleArena.addDaiToVoting(votingPositionId, msg.sender, amount, 0);               // zero for yTokens coz its not swap.
	}

	function addZooToPosition(uint256 votingPositionId, uint256 amount) external returns (uint256 votes) 
	{
		require(nftBattleArena.getCurrentStage() == Stage.FourthStage, "Wrong stage!");
		zoo.transferFrom(msg.sender, address(nftBattleArena), amount);                        // Transfers ZOO to arena contract for vote.
		return nftBattleArena.addZooToVoting(votingPositionId, msg.sender, amount);
	}

	function withdrawDaiFromVotingPosition(uint256 votingPositionId, address beneficiary, uint256 daiNumber) external onlyVotingOwner(votingPositionId)
	{
		nftBattleArena.withdrawDaiFromVoting(votingPositionId, msg.sender, beneficiary, daiNumber, false);
	}

	function withdrawZooFromVotingPosition(uint256 votingPositionId, uint256 zooNumber, address beneficiary) external onlyVotingOwner(votingPositionId)
	{
		nftBattleArena.withdrawZooFromVoting(votingPositionId, msg.sender, zooNumber, beneficiary);
	}

	function claimRewardFromVoting(uint256 votingPositionId, address beneficiary) external onlyVotingOwner(votingPositionId) returns (uint256)
	{
		return nftBattleArena.claimRewardFromVoting(votingPositionId, msg.sender, beneficiary);
	}

	/// @notice Function to move votes from one position to another.
	/// @notice If moving to nft not voted before(i.e. creating new position), then newVotingPosition should be zero.
	/// @param votingPositionId - Id of position votes moving from.
	/// @param daiNumber - amount of dai moving.
	/// @param newStakingPositionId - id of stakingPosition moving to.
	/// @param newVotingPosition - id of voting position moving to, if exist. If there are no such, should be zero.
	function swapVotesFromPosition(uint256 votingPositionId, uint256 daiNumber, uint256 newStakingPositionId, address beneficiary, uint256 newVotingPosition) external onlyVotingOwner(votingPositionId)
	{
		require(daiNumber != 0, "zero vote not allowed");                                                      // Requires for vote amount to be more than zero.
		require(newVotingPosition == 0 || ownerOf(newVotingPosition) == msg.sender, "Not the owner of voting");
		require(nftBattleArena.getCurrentStage() == Stage.FirstStage, "Wrong stage!");                         // Requires correct stage.

		(uint256 stakingPositionId, uint256 daiInvested,,,,,,,,,,) =  nftBattleArena.votingPositionsValues(votingPositionId);    // Gets id of staker position.

		if (daiNumber > daiInvested)                                                            // If swap amount more than invested.
		{
			daiNumber = daiInvested;                                                            // Set swap amount to maximum, same as in withdrawDai.
		}

		nftBattleArena.updateInfo(stakingPositionId);

		uint256 yTokens = nftBattleArena.tokensToShares(daiNumber);

		nftBattleArena.withdrawDaiFromVoting(votingPositionId, msg.sender, beneficiary, daiNumber, true);      // Calls internal withdrawDai.

		if (newVotingPosition == 0)                   // If zero, i.e. new position doesn't exist.
		{
			(, newVotingPosition) = nftBattleArena._createVotingPosition(newStakingPositionId, msg.sender, yTokens, daiNumber); // Creates new position to swap there.
			_safeMint(msg.sender, newVotingPosition);
		}
		else                                          // If position existing, swap to it.
		{
			(,,,,,,,uint256 endEpoch,,,,) = nftBattleArena.votingPositionsValues(newVotingPosition);
			require(endEpoch == 0, "unstaked");       // Requires for position to exist and still be staked.

			nftBattleArena.addDaiToVoting(newVotingPosition, msg.sender, daiNumber, yTokens);                // swap votes to existing position.
		}
	}

	/// @notice Claims rewards from multiple voting positions
	/// @param votingPositionIds array of voting positions indexes
	/// @param beneficiary address to transfer reward to
	function batchClaimRewardsFromVotings(uint256[] calldata votingPositionIds, address beneficiary) external returns (uint256 reward)
	{
		for (uint256 i = 0; i < votingPositionIds.length; i++)
		{
			require(msg.sender == ownerOf(votingPositionIds[i]), "Not the owner of voting");

			reward += nftBattleArena.claimRewardFromVoting(votingPositionIds[i], msg.sender, beneficiary);
		}
	}

	function batchWithdrawDaiFromVoting(uint256[] calldata votingPositionIds, address beneficiary, uint256 daiNumber) external
	{
		for (uint256 i = 0; i < votingPositionIds.length; i++)
		{
			require(msg.sender == ownerOf(votingPositionIds[i]), "Not the owner of voting");

			nftBattleArena.withdrawDaiFromVoting(votingPositionIds[i], msg.sender, beneficiary, daiNumber, false);
		}
	}

	function batchWithdrawZooFromVoting(uint256[] calldata votingPositionIds, uint256 zooNumber, address beneficiary) external
	{
		for (uint256 i = 0; i < votingPositionIds.length; i++)
		{
			require(msg.sender == ownerOf(votingPositionIds[i]), "Not the owner of voting");

			nftBattleArena.withdrawZooFromVoting(votingPositionIds[i], msg.sender, zooNumber, beneficiary);
		}
	}

	/// @notice Function to claim incentive reward for voting, proportionally of collection weight in ve-Model pool.
	function claimIncentiveVoterReward(uint256 votingPositionId, address beneficiary) external returns (uint256)
	{
		require(ownerOf(votingPositionId) == msg.sender, "Not the owner!");                     // Requires to be owner of position.

		uint256 reward = nftBattleArena.calculateIncentiveRewardForVoter(votingPositionId);

		zoo.transfer(beneficiary, reward);

		return reward;
	}

	function batchClaimIncentiveVoterReward(uint256[] calldata votingPositionIds, address beneficiary) external returns (uint256 reward)
	{
		for (uint256 i = 0; i < votingPositionIds.length; i++)
		{
			require(ownerOf(votingPositionIds[i]) == msg.sender, "Not the owner!");             // Requires to be owner of position.

			uint256 claimed = nftBattleArena.calculateIncentiveRewardForVoter(votingPositionIds[i]);
			reward += claimed;

			emit ClaimedIncentiveRewardFromVoting(msg.sender, beneficiary, reward, votingPositionIds[i]);
		}
		zoo.transfer(beneficiary, reward);
	}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/ERC721.sol)

pragma solidity ^0.8.0;

import "IERC721.sol";
import "IERC721Receiver.sol";
import "IERC721Metadata.sol";
import "Address.sol";
import "Context.sol";
import "Strings.sol";
import "ERC165.sol";

/**
 * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
 * the Metadata extension, but not including the Enumerable extension, which is available separately as
 * {ERC721Enumerable}.
 */
contract ERC721 is Context, ERC165, IERC721, IERC721Metadata {
    using Address for address;
    using Strings for uint256;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    // Mapping from token ID to owner address
    mapping(uint256 => address) private _owners;

    // Mapping owner address to token count
    mapping(address => uint256) private _balances;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0), "ERC721: balance query for the zero address");
        return _balances[owner];
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        address owner = _owners[tokenId];
        require(owner != address(0), "ERC721: owner query for nonexistent token");
        return owner;
    }

    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overriden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ERC721.ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            _msgSender() == owner || isApprovedForAll(owner, _msgSender()),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId), "ERC721: approved query for nonexistent token");

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        _setApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }

    /**
     * @dev See {IERC721-transferFrom}.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        //solhint-disable-next-line max-line-length
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");

        _transfer(from, to, tokenId);
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved");
        _safeTransfer(from, to, tokenId, _data);
    }

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * `_data` is additional data, it has no specified format and it is sent in call to `to`.
     *
     * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g.
     * implement alternative mechanisms to perform token transfer, such as signature-based.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer");
    }

    /**
     * @dev Returns whether `tokenId` exists.
     *
     * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
     *
     * Tokens start existing when they are minted (`_mint`),
     * and stop existing when they are burned (`_burn`).
     */
    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    /**
     * @dev Returns whether `spender` is allowed to manage `tokenId`.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId), "ERC721: operator query for nonexistent token");
        address owner = ERC721.ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender));
    }

    /**
     * @dev Safely mints `tokenId` and transfers it to `to`.
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    /**
     * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is
     * forwarded in {IERC721Receiver-onERC721Received} to contract recipients.
     */
    function _safeMint(
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _mint(to, tokenId);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    /**
     * @dev Mints `tokenId` and transfers it to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible
     *
     * Requirements:
     *
     * - `tokenId` must not exist.
     * - `to` cannot be the zero address.
     *
     * Emits a {Transfer} event.
     */
    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

        _beforeTokenTransfer(address(0), to, tokenId);

        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(address(0), to, tokenId);

        _afterTokenTransfer(address(0), to, tokenId);
    }

    /**
     * @dev Destroys `tokenId`.
     * The approval is cleared when the token is burned.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     *
     * Emits a {Transfer} event.
     */
    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721.ownerOf(tokenId);

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

        emit Transfer(owner, address(0), tokenId);

        _afterTokenTransfer(owner, address(0), tokenId);
    }

    /**
     * @dev Transfers `tokenId` from `from` to `to`.
     *  As opposed to {transferFrom}, this imposes no restrictions on msg.sender.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     *
     * Emits a {Transfer} event.
     */
    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner");
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId);
    }

    /**
     * @dev Approve `to` to operate on `tokenId`
     *
     * Emits a {Approval} event.
     */
    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(ERC721.ownerOf(tokenId), to, tokenId);
    }

    /**
     * @dev Approve `operator` to operate on all of `owner` tokens
     *
     * Emits a {ApprovalForAll} event.
     */
    function _setApprovalForAll(
        address owner,
        address operator,
        bool approved
    ) internal virtual {
        require(owner != operator, "ERC721: approve to caller");
        _operatorApprovals[owner][operator] = approved;
        emit ApprovalForAll(owner, operator, approved);
    }

    /**
     * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.
     * The call is not executed if the target address is not a contract.
     *
     * @param from address representing the previous owner of the given token ID
     * @param to target address that will receive the tokens
     * @param tokenId uint256 ID of the token to be transferred
     * @param _data bytes optional data to send along with the call
     * @return bool whether the call correctly returned the expected magic value
     */
    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.isContract()) {
            try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    /**
     * @dev Hook that is called before any token transfer. This includes minting
     * and burning.
     *
     * Calling conditions:
     *
     * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be
     * transferred to `to`.
     * - When `from` is zero, `tokenId` will be minted for `to`.
     * - When `to` is zero, ``from``'s `tokenId` will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)

pragma solidity ^0.8.0;

import "IERC165.sol";

/**
 * @dev Required interface of an ERC721 compliant contract.
 */
interface IERC721 is IERC165 {
    /**
     * @dev Emitted when `tokenId` token is transferred from `from` to `to`.
     */
    event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
     */
    event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);

    /**
     * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
     */
    event ApprovalForAll(address indexed owner, address indexed operator, bool approved);

    /**
     * @dev Returns the number of tokens in ``owner``'s account.
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    /**
     * @dev Returns the owner of the `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function ownerOf(uint256 tokenId) external view returns (address owner);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
     * are aware of the ERC721 protocol to prevent tokens from being forever locked.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Transfers `tokenId` token from `from` to `to`.
     *
     * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    /**
     * @dev Gives permission to `to` to transfer `tokenId` token to another account.
     * The approval is cleared when the token is transferred.
     *
     * Only a single account can be approved at a time, so approving the zero address clears previous approvals.
     *
     * Requirements:
     *
     * - The caller must own the token or be an approved operator.
     * - `tokenId` must exist.
     *
     * Emits an {Approval} event.
     */
    function approve(address to, uint256 tokenId) external;

    /**
     * @dev Returns the account approved for `tokenId` token.
     *
     * Requirements:
     *
     * - `tokenId` must exist.
     */
    function getApproved(uint256 tokenId) external view returns (address operator);

    /**
     * @dev Approve or remove `operator` as an operator for the caller.
     * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
     *
     * Requirements:
     *
     * - The `operator` cannot be the caller.
     *
     * Emits an {ApprovalForAll} event.
     */
    function setApprovalForAll(address operator, bool _approved) external;

    /**
     * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
     *
     * See {setApprovalForAll}
     */
    function isApprovedForAll(address owner, address operator) external view returns (bool);

    /**
     * @dev Safely transfers `tokenId` token from `from` to `to`.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `tokenId` token must exist and be owned by `from`.
     * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
     * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
     *
     * Emits a {Transfer} event.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 5 of 19 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.0;

/**
 * @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 `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol)

pragma solidity ^0.8.0;

import "IERC721.sol";

/**
 * @title ERC-721 Non-Fungible Token Standard, optional metadata extension
 * @dev See https://eips.ethereum.org/EIPS/eip-721
 */
interface IERC721Metadata is IERC721 {
    /**
     * @dev Returns the token collection name.
     */
    function name() external view returns (string memory);

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

    /**
     * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
     */
    function tokenURI(uint256 tokenId) external view returns (string memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

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

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

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

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

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

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

        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

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

        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

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

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

        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResult(success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            // Look for revert reason and bubble it up if present
            if (returndata.length > 0) {
                // The easiest way to bubble the revert reason is using memory via assembly

                assembly {
                    let returndata_size := mload(returndata)
                    revert(add(32, returndata), returndata_size)
                }
            } else {
                revert(errorMessage);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _HEX_SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "IERC165.sol";

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

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

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @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);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/Ownable.sol)

pragma solidity ^0.8.0;

import "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.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

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

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _transferOwnership(_msgSender());
    }

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

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing 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 {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _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);
    }
}

File 13 of 19 : NftBattleArena.sol
pragma solidity 0.8.13;

// SPDX-License-Identifier: MIT

import "IVault.sol";
import "IZooFunctions.sol";
import "ZooGovernance.sol";
import "ListingList.sol";
import "IERC20Metadata.sol";
import "Math.sol";

/// @notice Struct for stages of vote battle.
enum Stage
{
	FirstStage,
	SecondStage,
	ThirdStage,
	FourthStage,
	FifthStage
}

interface ControllerInterface
{
	function claimReward(uint8 rewardType, address holder) external;
}

/// @title NftBattleArena contract.
/// @notice Contract for staking ZOO-Nft for participate in battle votes.
contract NftBattleArena
{
	using Math for uint256;
	using Math for int256;

	IERC20Metadata public zoo;                                       // Zoo token interface.
	IERC20Metadata public dai;                                       // DAI token interface
	VaultAPI public vault;                                           // Yearn interface.
	ZooGovernance public zooGovernance;                              // zooGovernance contract.
	IZooFunctions public zooFunctions;                               // zooFunctions contract.
	ListingList public veZoo;
	ControllerInterface public tokenController;
	IERC20Metadata public well;

	/// @notice Struct with info about rewards, records for epoch.
	struct BattleRewardForEpoch
	{
		int256 yTokensSaldo;                                         // Saldo from deposit in yearn in yTokens.
		uint256 votes;                                               // Total amount of votes for nft in this battle in this epoch.
		uint256 yTokens;                                             // Amount of yTokens.
		uint256 tokensAtBattleStart;                                 // Amount of yTokens at battle start.
		uint256 pricePerShareAtBattleStart;                          // pps at battle start.
		uint256 pricePerShareCoef;                                   // pps1*pps2/pps2-pps1
	}

	/// @notice Struct with info about staker positions.
	struct StakerPosition
	{
		uint256 startEpoch;                                          // Epoch when started to stake.
		uint256 endEpoch;                                            // Epoch when unstaked.
		uint256 lastRewardedEpoch;                                   // Epoch when last reward were claimed.
		uint256 lastUpdateEpoch;                                     // Epoch when last updateInfo called.
		address collection;                                          // Address of nft collection contract.
		uint256 lastEpochOfIncentiveReward;
	}

	/// @notice struct with info about voter positions.
	struct VotingPosition
	{
		uint256 stakingPositionId;                                   // Id of staker position voted for.
		uint256 daiInvested;                                         // Amount of dai invested in voting position.
		uint256 yTokensNumber;                                       // Amount of yTokens got for dai.
		uint256 zooInvested;                                         // Amount of Zoo used to boost votes.
		uint256 daiVotes;                                            // Amount of votes got from voting with dai.
		uint256 votes;                                               // Amount of total votes from dai, zoo and multiplier.
		uint256 startEpoch;                                          // Epoch when created voting position.
		uint256 endEpoch;                                            // Epoch when liquidated voting position.
		uint256 lastRewardedEpoch;                                   // Epoch when last battle reward was claimed.
		uint256 lastEpochYTokensWereDeductedForRewards;              // Last epoch when yTokens used for rewards in battles were deducted from all voting position's yTokens
		uint256 yTokensRewardDebt;                                   // Amount of yTokens which voter can claim for previous epochs before add/withdraw votes.
		uint256 lastEpochOfIncentiveReward;
	}

	/// @notice Struct for records about pairs of Nfts for battle.
	struct NftPair
	{
		uint256 token1;                                              // Id of staker position of 1st candidate.
		uint256 token2;                                              // Id of staker position of 2nd candidate.
		bool playedInEpoch;                                          // Returns true if winner chosen.
		bool win;                                                    // Boolean, where true is when 1st candidate wins, and false for 2nd.
	}

	struct Debt
	{
		uint256 wells;
		uint256 glmrs;
	}

	/// @notice Event about staked nft.                         FirstStage
	event CreatedStakerPosition(uint256 indexed currentEpoch, address indexed staker, uint256 indexed stakingPositionId);

	/// @notice Event about withdrawed nft from arena.          FirstStage
	event RemovedStakerPosition(uint256 indexed currentEpoch, address indexed staker, uint256 indexed stakingPositionId);


	/// @notice Event about created voting position.            SecondStage
	event CreatedVotingPosition(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, uint256 daiAmount, uint256 votes, uint256 votingPositionId);

	/// @notice Event about liquidated voting position.         FirstStage
	event LiquidatedVotingPosition(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, address beneficiary, uint256 votingPositionId, uint256 zooReturned, uint256 daiReceived);

	/// @notice Event about recomputing votes from dai.         SecondStage
	event RecomputedDaiVotes(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, uint256 votingPositionId, uint256 newVotes, uint256 oldVotes);

	/// @notice Event about recomputing votes from zoo.         FourthStage
	event RecomputedZooVotes(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, uint256 votingPositionId, uint256 newVotes, uint256 oldVotes);


	/// @notice Event about adding dai to voter position.       SecondStage
	event AddedDaiToVoting(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, uint256 votingPositionId, uint256 amount, uint256 votes);

	/// @notice Event about adding zoo to voter position.       FourthStage
	event AddedZooToVoting(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, uint256 votingPositionId, uint256 amount, uint256 votes);


	/// @notice Event about withdraw dai from voter position.   FirstStage
	event WithdrawedDaiFromVoting(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, address beneficiary, uint256 votingPositionId, uint256 daiNumber);

	/// @notice Event about withdraw zoo from voter position.   FirstStage
	event WithdrawedZooFromVoting(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, uint256 votingPositionId, uint256 zooNumber, address beneficiary);


	/// @notice Event about claimed reward from voting.         FirstStage
	event ClaimedRewardFromVoting(uint256 indexed currentEpoch, address indexed voter, uint256 indexed stakingPositionId, address beneficiary, uint256 daiReward, uint256 votingPositionId);

	/// @notice Event about claimed reward from staking.        FirstStage
	event ClaimedRewardFromStaking(uint256 indexed currentEpoch, address indexed staker, uint256 indexed stakingPositionId, address beneficiary, uint256 yTokenReward, uint256 daiReward);


	/// @notice Event about paired nfts.                        ThirdStage
	event PairedNft(uint256 indexed currentEpoch, uint256 indexed fighter1, uint256 indexed fighter2, uint256 pairIndex);

	/// @notice Event about winners in battles.                 FifthStage
	event ChosenWinner(uint256 indexed currentEpoch, uint256 indexed fighter1, uint256 indexed fighter2, bool winner, uint256 pairIndex, uint256 playedPairsAmount);

	/// @notice Event about changing epochs.
	event EpochUpdated(uint256 date, uint256 newEpoch);

	uint256 public epochStartDate;                                                 // Start date of battle epoch.
	uint256 public currentEpoch = 1;                                               // Counter for battle epochs.

	uint256 public firstStageDuration;                                             // Duration of first stage(stake).
	uint256 public secondStageDuration;                                            // Duration of second stage(DAI)'.
	uint256 public thirdStageDuration;                                             // Duration of third stage(Pair).
	uint256 public fourthStageDuration;                                            // Duration fourth stage(ZOO).
	uint256 public fifthStageDuration;                                             // Duration of fifth stage(Winner).
	uint256 public epochDuration;                                                  // Total duration of battle epoch.

	uint256[] public activeStakerPositions;                                        // Array of ZooBattle nfts, which are StakerPositions.
	uint256 public numberOfNftsWithNonZeroVotes;                                   // Staker positions with votes for, eligible to pair and battle.
	uint256 public nftsInGame;                                                     // Amount of Paired nfts in current epoch.

	uint256 public numberOfStakingPositions = 1;
	uint256 public numberOfVotingPositions = 1;

	address public treasury;                                                       // Address of ZooDao insurance pool.
	//address public gasPool;                                                        // Address of ZooDao gas fee compensation pool.
	address public team;                                                           // Address of ZooDao team reward pool.
	address public xZoo;
	address public jackpotA;
	address public jackpotB;
	address payable public wGlmr;

	address public nftStakingPosition;
	address public nftVotingPosition;

	uint256 public baseStakerReward = 62500 * 10 ** 18;  // todo: check amount
	uint256 public baseVoterReward = 500000 * 10 ** 18;  // todo: check amount

	// epoch number => index => NftPair struct.
	mapping (uint256 => NftPair[]) public pairsInEpoch;                            // Records info of pair in struct per battle epoch.

	// epoch number => number of played pairs in epoch.
	mapping (uint256 => uint256) public numberOfPlayedPairsInEpoch;                // Records amount of pairs with chosen winner in current epoch.

	// position id => StakerPosition struct.
	mapping (uint256 => StakerPosition) public stakingPositionsValues;             // Records info about staker position.

	// position id => VotingPosition struct.
	mapping (uint256 => VotingPosition) public votingPositionsValues;              // Records info about voter position.

	// epoch index => collection => number of staked nfts.
	mapping (uint256 => mapping (address => uint256)) public numberOfStakedNftsInCollection;

	// collection => last epoch when was updated info about numberOfStakedNftsInCollection.
	mapping (address => uint256) public lastUpdatesOfStakedNumbers;

	// epoch => yvTokens
	mapping (uint256 => uint256) public xZooRewards;

	// epoch => yvTokens
	mapping (uint256 => uint256) public jackpotRewardsAtEpoch;

	// staker position id => epoch = > rewards struct.
	mapping (uint256 => mapping (uint256 => BattleRewardForEpoch)) public rewardsForEpoch;

	// epoch number => timestamp of epoch start
	mapping (uint256 => uint256) public epochsStarts;

	// epoch number => well claimed
	mapping (uint256 => uint256) public wellClaimedByEpoch;

	// epoch number => glmr claimed
	mapping (uint256 => uint256) public glmrClaimedByEpoch;

	// voting position id => debt
	mapping (uint256 => Debt) public debtOfPosition;

	// epoch => total active votes (in played nfts)
	mapping (uint256 => uint256) public totalActiveVotesByEpoch;

	modifier only(address who)
	{
		require(msg.sender == who);
		_;
	}

	/// @notice Contract constructor.
	/// @param _zoo - address of Zoo token contract.
	/// @param _dai - address of DAI token contract.
	/// @param _vault - address of yearn.
	/// @param _zooGovernance - address of ZooDao Governance contract.
	/// @param _treasuryPool - address of ZooDao treasury pool.
	// @param _gasFeePool - address of ZooDao gas fee compensation pool.
	/// @param _teamAddress - address of ZooDao team reward pool.
	constructor (
		IERC20Metadata _zoo,
		IERC20Metadata _dai,
		address _vault,
		address _zooGovernance,
		address _treasuryPool,
		//address _gasFeePool,
		address _teamAddress,
		address _nftStakingPosition,
		address _nftVotingPosition,
		address _veZoo,
		address _controller,
		IERC20Metadata _well)
	{
		zoo = _zoo;
		dai = _dai;
		vault = VaultAPI(_vault);
		zooGovernance = ZooGovernance(_zooGovernance);
		zooFunctions = IZooFunctions(zooGovernance.zooFunctions());
		veZoo = ListingList(_veZoo);

		treasury = _treasuryPool;
		//gasPool = _gasFeePool;
		team = _teamAddress;
		nftStakingPosition = _nftStakingPosition;
		nftVotingPosition = _nftVotingPosition;

		epochStartDate = block.timestamp; // Start date of 1st battle.
		epochsStarts[currentEpoch] = block.timestamp;
		tokenController = ControllerInterface(_controller);
		well = _well;
		(firstStageDuration, secondStageDuration, thirdStageDuration, fourthStageDuration, fifthStageDuration, epochDuration) = zooFunctions.getStageDurations();
	}

	function init(address _xZoo, address _jackpotA, address _jackpotB, address payable _wglmr) external
	{
		require(xZoo == address(0));

		xZoo = _xZoo;
		jackpotA = _jackpotA;
		jackpotB = _jackpotB;
		wGlmr = _wglmr;
	}

	receive() external payable { }

	/// @notice Function to get amount of nft in array StakerPositions/staked in battles.
	/// @return amount - amount of ZooBattles nft.
	function getStakerPositionsLength() public view returns (uint256 amount)
	{
		return activeStakerPositions.length;
	}

	/// @notice Function to get amount of nft pairs in epoch.
	/// @param epoch - number of epoch.
	/// @return length - amount of nft pairs.
	function getNftPairLength(uint256 epoch) public view returns(uint256 length)
	{
		return pairsInEpoch[epoch].length;
	}

	/// @notice Function to calculate amount of tokens from shares.
	/// @param sharesAmount - amount of shares.
	/// @return tokens - calculated amount tokens from shares.
	function sharesToTokens(uint256 sharesAmount) public returns (uint256 tokens)
	{
		return sharesAmount * vault.exchangeRateCurrent() / (10 ** 18);
	}

	/// @notice Function for calculating tokens to shares.
	/// @param tokens - amount of tokens to calculate.
	/// @return shares - calculated amount of shares.
	function tokensToShares(uint256 tokens) public returns (uint256 shares)
	{
		return tokens * (10 ** 18) / vault.exchangeRateCurrent();
	}

	/// @notice Function for staking NFT in this pool.
	/// @param staker address of staker
	/// @param token NFT collection address
	function createStakerPosition(address staker, address token) public only(nftStakingPosition) returns (uint256)
	{
		//require(getCurrentStage() == Stage.FirstStage, "Wrong stage!"); // Require turned off cause its moved to staker position contract due to lack of space for bytecode. // Requires to be at first stage in battle epoch.

		StakerPosition storage position = stakingPositionsValues[numberOfStakingPositions];
		position.startEpoch = currentEpoch;                                                     // Records startEpoch.
		position.lastRewardedEpoch = currentEpoch;                                              // Records lastRewardedEpoch
		position.collection = token;                                                            // Address of nft collection.
		position.lastEpochOfIncentiveReward = currentEpoch;

		numberOfStakedNftsInCollection[currentEpoch][token]++;                                  // Increments amount of nft collection.

		activeStakerPositions.push(numberOfStakingPositions);                                   // Records this position to stakers positions array.

		emit CreatedStakerPosition(currentEpoch, staker, numberOfStakingPositions);             // Emits StakedNft event.

		return numberOfStakingPositions++;                                                      // Increments amount and id of future positions.
	}

	/// @notice Function for withdrawing staked nft.
	/// @param stakingPositionId - id of staker position.
	function removeStakerPosition(uint256 stakingPositionId, address staker) external only(nftStakingPosition)
	{
		//require(getCurrentStage() == Stage.FirstStage, "Wrong stage!"); // Require turned off cause its moved to staker position contract due to lack of space for bytecode. // Requires to be at first stage in battle epoch.
		StakerPosition storage position = stakingPositionsValues[stakingPositionId];
		require(position.endEpoch == 0, "Nft unstaked");                                        // Requires token to be staked.

		position.endEpoch = currentEpoch;                                                       // Records epoch when unstaked.
		updateInfo(stakingPositionId);                                                          // Updates staking position params from previous epochs.

		if (rewardsForEpoch[stakingPositionId][currentEpoch].votes > 0)                         // If votes for position in current epoch more than zero.
		{
			for(uint256 i = 0; i < numberOfNftsWithNonZeroVotes; ++i)                           // Iterates for non-zero positions.
			{
				if (activeStakerPositions[i] == stakingPositionId)                              // Finds this position in array of active positions.
				{
					// Replace this position with another position from end of array. Then shift zero positions for one point.
					activeStakerPositions[i] = activeStakerPositions[numberOfNftsWithNonZeroVotes - 1];
					activeStakerPositions[--numberOfNftsWithNonZeroVotes] = activeStakerPositions[activeStakerPositions.length - 1];
					break;
				}
			}
		}
		else // If votes for position in current epoch are zero, does the same, but without decrement numberOfNftsWithNonZeroVotes.
		{
			for(uint256 i = numberOfNftsWithNonZeroVotes; i < activeStakerPositions.length; ++i)
			{
				if (activeStakerPositions[i] == stakingPositionId)                                     // Finds this position in array.
				{
					activeStakerPositions[i] = activeStakerPositions[activeStakerPositions.length - 1];// Swaps to end of array.
					break;
				}
			}
		}

		updateInfoAboutStakedNumber(position.collection);
		numberOfStakedNftsInCollection[currentEpoch][position.collection]--;
		activeStakerPositions.pop();                                                            // Removes staker position from array.

		emit RemovedStakerPosition(currentEpoch, staker, stakingPositionId);                    // Emits UnstakedNft event.
	}

	/// @notice Function for vote for nft in battle.
	/// @param stakingPositionId - id of staker position.
	/// @param amount - amount of dai to vote.
	/// @return votes - computed amount of votes.
	function createVotingPosition(uint256 stakingPositionId, address voter, uint256 amount) external only(nftVotingPosition) returns (uint256 votes, uint256 votingPositionId)
	{
		//require(getCurrentStage() == Stage.SecondStage, "Wrong stage!"); // Require turned off cause its moved to voting position contract due to lack of space for bytecode. // Requires to be at second stage of battle epoch.

		updateInfo(stakingPositionId);                                                          // Updates staking position params from previous epochs.

		dai.approve(address(vault), type(uint256).max);                                         // Approves Dai for yearn.
		uint256 yTokensNumber = vault.balanceOf(address(this));
		require(vault.mint(amount) == 0);                                                       // Deposits dai to yearn vault and get yTokens.

		(votes, votingPositionId) = _createVotingPosition(stakingPositionId, voter, vault.balanceOf(address(this)) - yTokensNumber, amount);// Calls internal create voting position.
	}

	/// @dev internal function to modify voting position params without vault deposit, making swap votes possible.
	/// @param stakingPositionId ID of staking to create voting for
	/// @param voter address of voter
	/// @param yTokens amount of yTokens got from Yearn from deposit
	/// @param amount daiVotes amount
	function _createVotingPosition(uint256 stakingPositionId, address voter, uint256 yTokens, uint256 amount) public only(nftVotingPosition) returns (uint256 votes, uint256 votingPositionId)
	{
		StakerPosition storage stakingPosition = stakingPositionsValues[stakingPositionId];
		require(stakingPosition.startEpoch != 0 && stakingPosition.endEpoch == 0, "Not staked"); // Requires for staking position to be staked.

		votes = zooFunctions.computeVotesByDai(amount);                                         // Calculates amount of votes.

		VotingPosition storage position = votingPositionsValues[numberOfVotingPositions];
		position.stakingPositionId = stakingPositionId;    // Records staker position Id voted for.
		position.daiInvested = amount;                     // Records amount of dai invested.
		position.yTokensNumber = yTokens;                  // Records amount of yTokens got from yearn vault.
		position.daiVotes = votes;                         // Records computed amount of votes to daiVotes.
		position.votes = votes;                            // Records computed amount of votes to total votes.
		position.startEpoch = currentEpoch;                // Records epoch when position created.
		position.lastRewardedEpoch = currentEpoch;         // Sets starting point for reward to current epoch.
		position.lastEpochOfIncentiveReward = currentEpoch;// Sets starting point for incentive rewards calculation.

		BattleRewardForEpoch storage battleReward = rewardsForEpoch[stakingPositionId][currentEpoch];

		if (battleReward.votes == 0)                                                            // If staker position had zero votes before,
		{
			for(uint256 i = 0; i < activeStakerPositions.length; ++i)                           // Iterate for active staker positions.
			{
				if (activeStakerPositions[i] == stakingPositionId)                              // Finds this position.
				{
					if (i > numberOfNftsWithNonZeroVotes)                      // if equal, then its already in needed place in array.
					{
						(activeStakerPositions[i], activeStakerPositions[numberOfNftsWithNonZeroVotes]) = (activeStakerPositions[numberOfNftsWithNonZeroVotes], activeStakerPositions[i]);                                              // Swaps this position in array, moving it to last point of non-zero positions.
					}
					numberOfNftsWithNonZeroVotes++;                                             // Increases amount of nft eligible for pairing.
					break;
				}
			}
		}

		battleReward.votes += votes;                                                            // Adds votes for staker position for this epoch.
		battleReward.yTokens += yTokens;                                                        // Adds yTokens for this staker position for this epoch.

		votingPositionId = numberOfVotingPositions;
		numberOfVotingPositions++;

		emit CreatedVotingPosition(currentEpoch, voter, stakingPositionId, amount, votes, votingPositionId);
	}

	/// @notice Function to recompute votes from dai.
	/// @notice Reasonable to call at start of new epoch for better multiplier rate, if voted with low rate before.
	/// @param votingPositionId - id of voting position.
	function recomputeDaiVotes(uint256 votingPositionId) public
	{
		require(getCurrentStage() == Stage.SecondStage, "Wrong stage!");              // Requires to be at second stage of battle epoch.

		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];

		_updateVotingRewardDebt(votingPositionId);

		uint256 stakingPositionId = votingPosition.stakingPositionId;
		updateInfo(stakingPositionId);                                                // Updates staking position params from previous epochs.

		uint256 daiNumber = votingPosition.daiInvested;                               // Gets amount of dai from voting position.
		uint256 newVotes = zooFunctions.computeVotesByDai(daiNumber);                 // Recomputes dai to votes.
		uint256 oldVotes = votingPosition.daiVotes;                                   // Gets amount of votes from voting position.

		require(newVotes > oldVotes, "Recompute to lower value");                     // Requires for new votes amount to be bigger than before.

		votingPosition.daiVotes = newVotes;                                           // Records new votes amount from dai.
		votingPosition.votes += newVotes - oldVotes;                                  // Records new votes amount total.
		rewardsForEpoch[stakingPositionId][currentEpoch].votes += newVotes - oldVotes;// Increases rewards for staker position for added amount of votes in this epoch.
		emit RecomputedDaiVotes(currentEpoch, msg.sender, stakingPositionId, votingPositionId, newVotes, oldVotes);
	}

	/// @notice Function to recompute votes from zoo.
	/// @param votingPositionId - id of voting position.
	function recomputeZooVotes(uint256 votingPositionId) public
	{
		require(getCurrentStage() == Stage.FourthStage, "Wrong stage!");              // Requires to be at 4th stage.

		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];

		_updateVotingRewardDebt(votingPositionId);

		uint256 stakingPositionId = votingPosition.stakingPositionId;
		updateInfo(stakingPositionId);

		uint256 zooNumber = votingPosition.zooInvested;                               // Gets amount of zoo invested from voting position.
		uint256 newZooVotes = zooFunctions.computeVotesByZoo(zooNumber);              // Recomputes zoo to votes.
		uint256 oldZooVotes = votingPosition.votes - votingPosition.daiVotes;         // Get amount of votes from zoo.

		require(newZooVotes > oldZooVotes, "Recompute to lower value");               // Requires for new votes amount to be bigger than before.

		votingPosition.votes += newZooVotes - oldZooVotes;                            // Add amount of recently added votes to total votes in voting position.
		rewardsForEpoch[stakingPositionId][currentEpoch].votes += newZooVotes - oldZooVotes; // Adds amount of recently added votes to reward for staker position for current epoch.

		emit RecomputedZooVotes(currentEpoch, msg.sender, stakingPositionId, votingPositionId, newZooVotes, oldZooVotes);
	}

	/// @notice Function to add dai tokens to voting position.
	/// @param votingPositionId - id of voting position.
	/// @param voter - address of voter.
	/// @param amount - amount of dai tokens to add.
	/// @param _yTokens - amount of yTokens from previous position when called with swap.
	function addDaiToVoting(uint256 votingPositionId, address voter, uint256 amount, uint256 _yTokens) public only(nftVotingPosition) returns (uint256 votes)
	{
		require(getCurrentStage() == Stage.SecondStage || _yTokens != 0, "Wrong stage!");// Requires to be at second stage of battle epoch.

		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];
		uint256 stakingPositionId = votingPosition.stakingPositionId;                 // Gets id of staker position.
		require(stakingPositionsValues[stakingPositionId].endEpoch == 0, "Position removed");// Requires to be staked.

		_updateVotingRewardDebt(votingPositionId);

		votes = zooFunctions.computeVotesByDai(amount);                               // Gets computed amount of votes from multiplier of dai.
		if (_yTokens == 0)                                                            // if no _yTokens from another position with swap.
		{
			_yTokens = vault.balanceOf(address(this));
			require(vault.mint(amount) == 0);                                                       // Deposits dai to yearn and gets yTokens.
			_yTokens = vault.balanceOf(address(this)) - _yTokens;
		}

		votingPosition.yTokensNumber = _calculateVotersYTokensExcludingRewards(votingPositionId) + _yTokens;// Adds yTokens to voting position.
		votingPosition.daiInvested += amount;                                         // Adds amount of dai to voting position.
		votingPosition.daiVotes += votes;                                             // Adds computed daiVotes amount from to voting position.
		votingPosition.votes += votes;                                                // Adds computed votes amount to totalVotes amount for voting position.
		votingPosition.startEpoch = currentEpoch;

		updateInfo(stakingPositionId);

		BattleRewardForEpoch storage battleReward = rewardsForEpoch[stakingPositionId][currentEpoch];

		battleReward.votes += votes;              // Adds votes to staker position for current epoch.
		battleReward.yTokens += _yTokens;         // Adds yTokens to rewards from staker position for current epoch.

		emit AddedDaiToVoting(currentEpoch, voter, stakingPositionId, votingPositionId, amount, votes);
	}

	/// @notice Function to add zoo tokens to voting position.
	/// @param votingPositionId - id of voting position.
	/// @param amount - amount of zoo tokens to add.
	function addZooToVoting(uint256 votingPositionId, address voter, uint256 amount) external only(nftVotingPosition) returns (uint256 votes)
	{
		//require(getCurrentStage() == Stage.FourthStage, "Wrong stage!"); // Require turned off cause its moved to voting position contract due to lack of space for bytecode. // Requires to be at 3rd stage.

		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];

		_updateVotingRewardDebt(votingPositionId);                                    // Records current reward for voting position to reward debt.

		votes = zooFunctions.computeVotesByZoo(amount);                               // Gets computed amount of votes from multiplier of zoo.
		require(votingPosition.zooInvested + amount <= votingPosition.daiInvested, "Exceed limit");// Requires for votes from zoo to be less than votes from dai.

		uint256 stakingPositionId = votingPosition.stakingPositionId;                 // Gets id of staker position.
		updateInfo(stakingPositionId);                                                // Updates staking position params from previous epochs.

		rewardsForEpoch[stakingPositionId][currentEpoch].votes += votes;              // Adds votes for staker position.
		votingPositionsValues[votingPositionId].votes += votes;                       // Adds votes to voting position.
		votingPosition.zooInvested += amount;                                         // Adds amount of zoo tokens to voting position.

		emit AddedZooToVoting(currentEpoch, voter, stakingPositionId, votingPositionId, amount, votes);
	}

	/// @notice Functions to withdraw dai from voting position.
	/// @param votingPositionId - id of voting position.
	/// @param daiNumber - amount of dai to withdraw.
	/// @param beneficiary - address of recipient.
	function withdrawDaiFromVoting(uint256 votingPositionId, address voter, address beneficiary, uint256 daiNumber, bool toSwap) public only(nftVotingPosition)
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];
		uint256 stakingPositionId = votingPosition.stakingPositionId;               // Gets id of staker position.
		updateInfo(stakingPositionId);                                              // Updates staking position params from previous epochs.

		require(getCurrentStage() == Stage.FirstStage || stakingPositionsValues[stakingPositionId].endEpoch != 0, "Wrong stage!"); // Requires correct stage or nft to be unstaked.
		require(votingPosition.endEpoch == 0, "Position removed");                  // Requires to be not liquidated yet.

		_updateVotingRewardDebt(votingPositionId);
		_subtractYTokensUserForRewardsFromVotingPosition(votingPositionId);

		if (daiNumber >= votingPosition.daiInvested)                                // If withdraw amount more or equal of maximum invested.
		{
			_liquidateVotingPosition(votingPositionId, voter, beneficiary, stakingPositionId, toSwap);// Calls liquidate and ends call.
			return;
		}

		uint256 shares = tokensToShares(daiNumber);                                 // If withdraw amount don't require liquidating, get amount of shares and continue.

		if (toSwap == false)                                                        // If called not through swap.
		{
			require(vault.redeem(shares) == 0);
			dai.transfer(voter, dai.balanceOf(address(this)));
		}
		BattleRewardForEpoch storage battleReward = rewardsForEpoch[stakingPositionId][currentEpoch];

		uint256 deltaVotes = votingPosition.daiVotes * daiNumber / votingPosition.daiInvested;// Gets average amount of votes withdrawed, cause vote price could be different.
		battleReward.yTokens -= shares;                                          // Decreases amount of shares for epoch.
		battleReward.votes -= deltaVotes;                                        // Decreases amount of votes for epoch for average votes.

		votingPosition.yTokensNumber -= shares;                                     // Decreases amount of shares.
		votingPosition.daiVotes -= deltaVotes;
		votingPosition.votes -= deltaVotes;                                         // Decreases amount of votes for position.
		votingPosition.daiInvested -= daiNumber;                                    // Decreases daiInvested amount of position.

		if (votingPosition.zooInvested > votingPosition.daiInvested)                // If zooInvested more than daiInvested left in position.
		{
			_rebalanceExceedZoo(votingPositionId, stakingPositionId, beneficiary);  // Withdraws excess zoo to save 1-1 dai-zoo proportion.
		}

		emit WithdrawedDaiFromVoting(currentEpoch, voter, stakingPositionId, beneficiary, votingPositionId, daiNumber);
	}

	/// @dev Function to liquidate voting position and claim reward.
	/// @param votingPositionId - id of position.
	/// @param voter - address of position owner.
	/// @param beneficiary - address of recipient.
	/// @param stakingPositionId - id of staking position.
	/// @param toSwap - boolean for swap votes, True if called from swapVotes function.
	function _liquidateVotingPosition(uint256 votingPositionId, address voter, address beneficiary, uint256 stakingPositionId, bool toSwap) internal
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];

		uint256 daiInvested = votingPosition.daiInvested;
		uint256 zooInvested = votingPosition.zooInvested;

		uint256 yTokens = votingPosition.yTokensNumber;

		if (toSwap == false)                                         // If false, withdraws tokens from vault for regular liquidate.
		{
			require(vault.redeem(yTokens) == 0);
			dai.transfer(beneficiary, dai.balanceOf(address(this))); // True when called from swapVotes, ignores withdrawal to re-assign them for another position.
		}

		_withdrawZoo(zooInvested, beneficiary);                      // Even if it is swap, withdraws all zoo.

		votingPosition.endEpoch = currentEpoch;                      // Sets endEpoch to currentEpoch.

		BattleRewardForEpoch storage battleReward = rewardsForEpoch[stakingPositionId][currentEpoch];
		battleReward.votes -= votingPosition.votes;                  // Decreases votes for staking position in current epoch.

		if (battleReward.yTokens >= yTokens)                         // If withdraws less than in staking position.
		{
			battleReward.yTokens -= yTokens;                         // Decreases yTokens for this staking position.
		}
		else
		{
			battleReward.yTokens = 0;                                // Or nullify it if trying to withdraw more yTokens than left in position(because of yTokens current rate)
		}

		// IF there is votes on position AND staking position is active
		if (battleReward.votes == 0 && stakingPositionsValues[stakingPositionId].endEpoch == 0)
		{
			// Move staking position to part, where staked without votes.
			for(uint256 i = 0; i < activeStakerPositions.length; ++i)
			{
				if (activeStakerPositions[i] == stakingPositionId)
				{
					(activeStakerPositions[i], activeStakerPositions[numberOfNftsWithNonZeroVotes - 1]) = (activeStakerPositions[numberOfNftsWithNonZeroVotes - 1], activeStakerPositions[i]);      // Swaps position to end of array
					numberOfNftsWithNonZeroVotes--;                                    // Decrements amount of non-zero positions.
					break;
				}
			}
		}

		emit LiquidatedVotingPosition(currentEpoch, voter, stakingPositionId, beneficiary, votingPositionId, zooInvested * 995 / 1000, daiInvested);
	}

	function _subtractYTokensUserForRewardsFromVotingPosition(uint256 votingPositionId) internal
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];

		uint256 yTokens = _calculateVotersYTokensExcludingRewards(votingPositionId);

		votingPosition.yTokensNumber = yTokens;
		votingPosition.lastEpochYTokensWereDeductedForRewards = currentEpoch;
	}

	/// @dev Calculates voting position's own yTokens - excludes yTokens that was used for rewards
	/// @dev yTokens must be substracted even if voting won in battle (they go to the voting's pending reward)
	/// @param votingPositionId ID of voting to calculate yTokens
	function _calculateVotersYTokensExcludingRewards(uint256 votingPositionId) internal view returns(uint256 yTokens)
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];
		uint256 stakingPositionId = votingPosition.stakingPositionId;

		yTokens = votingPosition.yTokensNumber;
		uint256 daiInvested = votingPosition.daiInvested;

		uint256 endEpoch = computeLastEpoch(votingPositionId);

		// From user yTokens subtract all tokens that go to the rewards
		// This way allows to withdraw exact same amount of DAI user invested at the start
		for (uint256 i = votingPosition.lastEpochYTokensWereDeductedForRewards; i < endEpoch; ++i)
		{
			if (rewardsForEpoch[stakingPositionId][i].pricePerShareCoef != 0)
			{
				yTokens -= daiInvested * 10**18 / rewardsForEpoch[stakingPositionId][i].pricePerShareCoef;
			}
		}
	}

	/// @dev function to withdraw Zoo number greater than Dai number to save 1-1 dai-zoo proportion.
	/// @param votingPositionId ID of voting to reduce Zoo number
	/// @param stakingPositionId ID of staking to reduce number of votes
	/// @param beneficiary address to withdraw Zoo
	function _rebalanceExceedZoo(uint256 votingPositionId, uint256 stakingPositionId, address beneficiary) internal
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];
		uint256 zooDelta = votingPosition.zooInvested - votingPosition.daiInvested;    // Get amount of zoo exceeding.

		_withdrawZoo(zooDelta, beneficiary);                                           // Withdraws exceed zoo.
		_reduceZooVotes(votingPositionId, stakingPositionId, zooDelta);
	}

	/// @dev function to calculate votes from zoo using average price and withdraw it.
	function _reduceZooVotes(uint256 votingPositionId, uint256 stakingPositionId, uint256 zooNumber) internal
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];
		uint256 zooVotes = votingPosition.votes - votingPosition.daiVotes;             // Calculates amount of votes got from zoo.
		uint256 deltaVotes = zooVotes * zooNumber / votingPosition.zooInvested;        // Calculates average amount of votes from this amount of zoo.

		votingPosition.votes -= deltaVotes;                                            // Decreases amount of votes.
		votingPosition.zooInvested -= zooNumber;                                       // Decreases amount of zoo invested.

		updateInfo(stakingPositionId);                                                 // Updates staking position params from previous epochs.
		rewardsForEpoch[stakingPositionId][currentEpoch].votes -= deltaVotes;          // Decreases amount of votes for staking position in current epoch.
	}

	/// @notice Functions to withdraw zoo from voting position.
	/// @param votingPositionId - id of voting position.
	/// @param zooNumber - amount of zoo to withdraw.
	/// @param beneficiary - address of recipient.
	function withdrawZooFromVoting(uint256 votingPositionId, address voter, uint256 zooNumber, address beneficiary) external only(nftVotingPosition)
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];
		_updateVotingRewardDebt(votingPositionId);

		uint256 stakingPositionId = votingPosition.stakingPositionId;                  // Gets id of staker position from this voting position.
		StakerPosition storage stakingPosition = stakingPositionsValues[stakingPositionId];
		require(getCurrentStage() == Stage.FirstStage || stakingPosition.endEpoch != 0, "Wrong stage!"); // Requires correct stage or nft to be unstaked.

		require(votingPosition.endEpoch == 0, "Position removed");                     // Requires to be not liquidated yet.

		uint256 zooInvested = votingPosition.zooInvested;

		if (zooNumber > zooInvested)                                                   // If trying to withdraw more than invested, withdraws maximum.
		{
			zooNumber = zooInvested;
		}

		_withdrawZoo(zooNumber, beneficiary);
		_reduceZooVotes(votingPositionId, stakingPositionId, zooNumber);

		emit WithdrawedZooFromVoting(currentEpoch, voter, stakingPositionId, votingPositionId, zooNumber, beneficiary);
	}


	/// @notice Function to claim reward in yTokens from voting.
	/// @param votingPositionId - id of voting position.
	/// @param beneficiary - address of recipient of reward.
	function claimRewardFromVoting(uint256 votingPositionId, address voter, address beneficiary) external only(nftVotingPosition) returns (uint256 daiReward)
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];

		require(getCurrentStage() == Stage.FirstStage || stakingPositionsValues[votingPosition.stakingPositionId].endEpoch != 0, "Wrong stage!"); // Requires to be at first stage or position should be liquidated.

		updateInfo(votingPosition.stakingPositionId);

		(uint256 yTokenReward, uint256 wells, uint256 glmrs) = getPendingVoterReward(votingPositionId); // Calculates amount of reward in yTokens.

		yTokenReward += votingPosition.yTokensRewardDebt;                                // Adds reward debt, from previous epochs.
		wells += debtOfPosition[votingPositionId].wells;
		glmrs += debtOfPosition[votingPositionId].glmrs;
		votingPosition.yTokensRewardDebt = 0;                                            // Nullify reward debt.
		debtOfPosition[votingPositionId] = Debt(0, 0);

		yTokenReward = yTokenReward * 950 / 975;

		require(vault.redeem(yTokenReward) == 0);                                                      // Withdraws dai from vault for yTokens, minus staker %.
		daiReward = dai.balanceOf(address(this));

		_daiRewardDistribution(beneficiary, votingPosition.stakingPositionId, daiReward);// Distributes reward between recipients, like treasury royalte, etc.

		BattleRewardForEpoch storage battleReward = rewardsForEpoch[votingPosition.stakingPositionId][currentEpoch];
		if (battleReward.yTokens >= yTokenReward)
		{
			battleReward.yTokens -= yTokenReward;                                        // Subtracts yTokens for this position.
		}
		else
		{
			battleReward.yTokens = 0;
		}

		votingPosition.lastRewardedEpoch = computeLastEpoch(votingPositionId);           // Records epoch of last reward claimed.
		well.transfer(beneficiary, wells);
		IERC20Metadata(wGlmr).transfer(beneficiary, glmrs);

		emit ClaimedRewardFromVoting(currentEpoch, voter, votingPosition.stakingPositionId, beneficiary, daiReward, votingPositionId);
	}


	/// @dev Updates yTokensRewardDebt of voting.
	/// @dev Called before every action with voting to prevent increasing share % in battle reward.
	/// @param votingPositionId ID of voting to be updated.
	function _updateVotingRewardDebt(uint256 votingPositionId) internal {
		(uint256 reward, uint256 wells, uint256 glmrs) = getPendingVoterReward(votingPositionId);

		if (reward != 0 || wells != 0 || glmrs != 0)
		{
			votingPositionsValues[votingPositionId].yTokensRewardDebt += reward;
			debtOfPosition[votingPositionId].wells += wells;
			debtOfPosition[votingPositionId].glmrs += glmrs;
		}

		votingPositionsValues[votingPositionId].lastRewardedEpoch = currentEpoch;
	}


	/// @notice Function to calculate pending reward from voting for position with this id.
	/// @param votingPositionId - id of voter position in battles.
	/// @return yTokens - amount of pending reward.
	function getPendingVoterReward(uint256 votingPositionId) public view returns (uint256 yTokens, uint256 wells, uint256 glmrs)
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];

		uint256 endEpoch = computeLastEpoch(votingPositionId);

		uint256 stakingPositionId = votingPosition.stakingPositionId;                  // Gets staker position id from voter position.

		for (uint256 i = votingPosition.lastRewardedEpoch; i < endEpoch; ++i)
		{
			int256 saldo = rewardsForEpoch[stakingPositionId][i].yTokensSaldo;         // Gets saldo from staker position for every epoch in range.

			//uint256 totalVotes = rewardsForEpoch[stakingPositionId][i].votes;          // Gets total votes from staker position.
			if (saldo > 0)
			{
				yTokens += uint256(saldo) * votingPosition.votes / rewardsForEpoch[stakingPositionId][i].votes;         // Calculates yTokens amount for voter.

				uint256 nominator = 965 * votingPosition.votes;
				uint256 denominator = totalActiveVotesByEpoch[i] * 1000;
				wells += wellClaimedByEpoch[i] * nominator / denominator; // 96.5%
				glmrs += glmrClaimedByEpoch[i] * nominator / denominator;
			}
		}

		return (yTokens, wells, glmrs);
	}

	/// @notice Function to claim reward for staker.
	/// @param stakingPositionId - id of staker position.
	/// @param beneficiary - address of recipient.
	function claimRewardFromStaking(uint256 stakingPositionId, address staker, address beneficiary) public only(nftStakingPosition) returns (uint256 daiReward)
	{
		StakerPosition storage stakerPosition = stakingPositionsValues[stakingPositionId];
		require(getCurrentStage() == Stage.FirstStage || stakerPosition.endEpoch != 0, "Wrong stage!"); // Requires to be at first stage in battle epoch.

		updateInfo(stakingPositionId);
		(uint256 yTokenReward, uint256 end) = getPendingStakerReward(stakingPositionId);
		stakerPosition.lastRewardedEpoch = end;                                               // Records epoch of last reward claim.

		require(vault.redeem(yTokenReward) == 0);                                                           // Gets reward from yearn.
		daiReward = dai.balanceOf(address(this));
		dai.transfer(beneficiary, daiReward);

		emit ClaimedRewardFromStaking(currentEpoch, staker, stakingPositionId, beneficiary, yTokenReward, daiReward);
	}

	/// @notice Function to get pending reward fo staker for this position id.
	/// @param stakingPositionId - id of staker position.
	/// @return stakerReward - reward amount for staker of this nft.
	function getPendingStakerReward(uint256 stakingPositionId) public view returns (uint256 stakerReward, uint256 end)
	{
		StakerPosition storage stakerPosition = stakingPositionsValues[stakingPositionId];
		uint256 endEpoch = stakerPosition.endEpoch;                                           // Gets endEpoch from position.

		end = endEpoch == 0 ? currentEpoch : endEpoch;                                        // Sets end variable to endEpoch if it non-zero, otherwise to currentEpoch.

		for (uint256 i = stakerPosition.lastRewardedEpoch; i < end; ++i)
		{
			int256 saldo = rewardsForEpoch[stakingPositionId][i].yTokensSaldo;                // Get saldo from staker position.

			if (saldo > 0)
			{
				stakerReward += uint256(saldo / 39);                                          // Calculates reward for staker: 2.5% == 25 / 975 == 1 / 39.
			}
		}
	}

	/// @notice Function for pair nft for battles.
	/// @param stakingPositionId - id of staker position.
	function pairNft(uint256 stakingPositionId) external
	{
		require(getCurrentStage() == Stage.ThirdStage, "Wrong stage!");                       // Requires to be at 3 stage of battle epoch.
		require(numberOfNftsWithNonZeroVotes / 2 > nftsInGame / 2, "No opponent");            // Requires enough nft for pairing.
		uint256 index1;                                                                       // Index of nft paired for.
		uint256 i;

		for (i = nftsInGame; i < numberOfNftsWithNonZeroVotes; ++i)
		{
			if (activeStakerPositions[i] == stakingPositionId)
			{
				index1 = i;
				break;
			}
		}

		require(i != numberOfNftsWithNonZeroVotes, "Wrong position");                         // Position not found in list of voted for and not paired.

		(activeStakerPositions[index1], activeStakerPositions[nftsInGame]) = (activeStakerPositions[nftsInGame], activeStakerPositions[index1]);// Swaps nftsInGame with index.
		nftsInGame++;                                                                         // Increases amount of paired nft.

		uint256 random = zooFunctions.computePseudoRandom() % (numberOfNftsWithNonZeroVotes - nftsInGame); // Get random number.

		uint256 index2 = random + nftsInGame;                                                 // Get index of opponent.
		uint256 pairIndex = getNftPairLength(currentEpoch);

		uint256 stakingPosition2 = activeStakerPositions[index2];                             // Get staker position id of opponent.
		pairsInEpoch[currentEpoch].push(NftPair(stakingPositionId, stakingPosition2, false, false));// Pushes nft pair to array of pairs.

		updateInfo(stakingPositionId);
		updateInfo(stakingPosition2);

		BattleRewardForEpoch storage battleReward1 = rewardsForEpoch[stakingPositionId][currentEpoch];
		BattleRewardForEpoch storage battleReward2 = rewardsForEpoch[stakingPosition2][currentEpoch];
		battleReward1.tokensAtBattleStart = sharesToTokens(battleReward1.yTokens);            // Records amount of yTokens on the moment of pairing for candidate.
		battleReward2.tokensAtBattleStart = sharesToTokens(battleReward2.yTokens);            // Records amount of yTokens on the moment of pairing for opponent.

		battleReward1.pricePerShareAtBattleStart = vault.exchangeRateCurrent();
		battleReward2.pricePerShareAtBattleStart = vault.exchangeRateCurrent();

		(activeStakerPositions[index2], activeStakerPositions[nftsInGame]) = (activeStakerPositions[nftsInGame], activeStakerPositions[index2]); // Swaps nftsInGame with index of opponent.
		nftsInGame++;                                                                         // Increases amount of paired nft.

		emit PairedNft(currentEpoch, stakingPositionId, stakingPosition2, pairIndex);
	}

	/// @notice Function to request random once per epoch.
	function requestRandom() public
	{
		require(getCurrentStage() == Stage.FifthStage, "Wrong stage!");                       // Requires to be at 5th stage.

		uint256 wellInitialBalance = well.balanceOf(address(this));

		tokenController.claimReward(0, address(this));
		tokenController.claimReward(1, address(this));
		wellClaimedByEpoch[currentEpoch] = well.balanceOf(address(this)) - wellInitialBalance;
		glmrClaimedByEpoch[currentEpoch] = address(this).balance;

		(bool sent, bytes memory data) = address(wGlmr).call{value: address(this).balance}("");
		require(sent, "Failed to send Glmr");

		IERC20Metadata(wGlmr).transfer(team, glmrClaimedByEpoch[currentEpoch] * 15 / 1000); // 1.5 % to team
		IERC20Metadata(wGlmr).transfer(treasury, glmrClaimedByEpoch[currentEpoch] / 50);    // 2% to treasury

		well.transfer(team, wellClaimedByEpoch[currentEpoch] * 15 / 1000);                  // 1.5 % to team
		well.transfer(treasury, wellClaimedByEpoch[currentEpoch] / 50);                     // 2% to treasury

		zooFunctions.requestRandomNumber();                                                 // Calls generate random number from chainlink or blockhash.
	}

	/// @notice Function for chosing winner for pair by its index in array.
	/// @notice returns error if random number for deciding winner is NOT requested OR fulfilled in ZooFunctions contract
	/// @param pairIndex - index of nft pair.
	function chooseWinnerInPair(uint256 pairIndex) external
	{
		require(getCurrentStage() == Stage.FifthStage, "Wrong stage!");                     // Requires to be at 5th stage.

		NftPair storage pair = pairsInEpoch[currentEpoch][pairIndex];

		require(pair.playedInEpoch == false, "Winner already chosen");                      // Requires to be not paired before.

		uint256 votes1 = rewardsForEpoch[pair.token1][currentEpoch].votes;
		uint256 votes2 = rewardsForEpoch[pair.token2][currentEpoch].votes;
		uint256 randomNumber = zooFunctions.getRandomResult();                              // Gets random number from zooFunctions.

		totalActiveVotesByEpoch[currentEpoch] += votes1 + votes2;
		pair.win = zooFunctions.decideWins(votes1, votes2, randomNumber);                   // Calculates winner and records it.
		pair.playedInEpoch = true;                                                          // Records that this pair already played this epoch.
		numberOfPlayedPairsInEpoch[currentEpoch]++;                                         // Increments amount of pairs played this epoch.

		// Getting winner and loser to calculate rewards
		(uint256 winner, uint256 loser) = pair.win? (pair.token1, pair.token2) : (pair.token2, pair.token1);
		_calculateBattleRewards(winner, loser);

		emit ChosenWinner(currentEpoch, pair.token1, pair.token2, pair.win, pairIndex, numberOfPlayedPairsInEpoch[currentEpoch]); // Emits ChosenWinner event.

		if (numberOfPlayedPairsInEpoch[currentEpoch] == pairsInEpoch[currentEpoch].length)
		{
			updateEpoch();                                                                  // calls updateEpoch if winner determined in every pair.
		}
	}

	/// @dev Contains calculation logic of battle rewards
	/// @param winner stakingPositionId of NFT that WON in battle
	/// @param loser stakingPositionId of NFT that LOST in battle
	function _calculateBattleRewards(uint256 winner, uint256 loser) internal
	{
		BattleRewardForEpoch storage winnerRewards = rewardsForEpoch[winner][currentEpoch];
		BattleRewardForEpoch storage loserRewards = rewardsForEpoch[loser][currentEpoch];

		BattleRewardForEpoch storage winnerRewards1 = rewardsForEpoch[winner][currentEpoch + 1];
		BattleRewardForEpoch storage loserRewards1 = rewardsForEpoch[loser][currentEpoch + 1];

		uint256 pps1 = winnerRewards.pricePerShareAtBattleStart;

		// Skip if price per share didn't change since pairing
		uint256 currentPps = vault.exchangeRateCurrent();
		if (pps1 == currentPps)
		{
			return;
		}

		winnerRewards.pricePerShareCoef = currentPps * pps1 / (currentPps - pps1);
		loserRewards.pricePerShareCoef = winnerRewards.pricePerShareCoef;

		// Income = yTokens at battle end - yTokens at battle start
		uint256 income1 = winnerRewards.yTokens - tokensToShares(winnerRewards.tokensAtBattleStart);
		uint256 income2 = loserRewards.yTokens - tokensToShares(loserRewards.tokensAtBattleStart);

		uint256 totalIncome = income1 + income2;
		uint256 xRewards = totalIncome * 15 / 1000;
		uint256 jackpotRewards = totalIncome / 200; // 0.5% == 5 / 1000 == 1 / 200
		vault.transfer(xZoo, xRewards);
		vault.transfer(jackpotA, jackpotRewards);
		vault.transfer(jackpotB, jackpotRewards);
		xZooRewards[currentEpoch] += xRewards;
		jackpotRewardsAtEpoch[currentEpoch] += jackpotRewards;
		winnerRewards.yTokensSaldo += int256(totalIncome - xRewards - 2 * jackpotRewards);
		loserRewards.yTokensSaldo -= int256(income2);

		winnerRewards1.yTokens = winnerRewards.yTokens + income2 - xRewards - 2 * jackpotRewards; // Add reward.
		loserRewards1.yTokens = loserRewards.yTokens - income2; // Withdraw reward amount.

		stakingPositionsValues[winner].lastUpdateEpoch = currentEpoch + 1;          // Update lastUpdateEpoch to next epoch.
		stakingPositionsValues[loser].lastUpdateEpoch = currentEpoch + 1;           // Update lastUpdateEpoch to next epoch.
		winnerRewards1.votes = winnerRewards.votes;      // Update votes for next epoch.
		loserRewards1.votes = loserRewards.votes;        // Update votes for next epoch.
	}

	/// @notice Function for updating position from lastUpdateEpoch, in case there was no battle with position for a while.
	function updateInfo(uint256 stakingPositionId) public
	{
		StakerPosition storage position = stakingPositionsValues[stakingPositionId];
		uint256 lastUpdateEpoch = position.lastUpdateEpoch;                         // Get lastUpdateEpoch for position.
		if (lastUpdateEpoch == currentEpoch)                                        // If already updated in this epoch - skip.
			return;

		for (; lastUpdateEpoch < currentEpoch; ++lastUpdateEpoch)
		{
			BattleRewardForEpoch storage rewardOfCurrentEpoch = rewardsForEpoch[stakingPositionId][lastUpdateEpoch + 1];
			BattleRewardForEpoch storage rewardOflastUpdateEpoch = rewardsForEpoch[stakingPositionId][lastUpdateEpoch];
			rewardOfCurrentEpoch.votes = rewardOflastUpdateEpoch.votes;                 // Get votes from lastUpdateEpoch.
			rewardOfCurrentEpoch.yTokens = rewardOflastUpdateEpoch.yTokens;             // Get yTokens from lastUpdateEpoch.
		}
		
		position.lastUpdateEpoch = currentEpoch;                                    // Set lastUpdateEpoch to currentEpoch.
	}

	/// @notice Function to increment epoch.
	function updateEpoch() public {
		require(getCurrentStage() == Stage.FifthStage, "Wrong stage!");             // Requires to be at fourth stage.
		require(block.timestamp >= epochStartDate + epochDuration || numberOfPlayedPairsInEpoch[currentEpoch] == pairsInEpoch[currentEpoch].length); // Requires fourth stage to end, or determine every pair winner.

		zooFunctions = IZooFunctions(zooGovernance.zooFunctions());                 // Sets ZooFunctions to contract specified in zooGovernance.

		epochStartDate = block.timestamp;                                           // Sets start date of new epoch.
		currentEpoch++;                                                             // Increments currentEpoch.
		epochsStarts[currentEpoch] = block.timestamp;                               // Records timestamp of new epoch start for ve-Zoo.
		nftsInGame = 0;                                                             // Nullifies amount of paired nfts.

		zooFunctions.resetRandom();     // Resets random in zoo functions.

		(firstStageDuration, secondStageDuration, thirdStageDuration, fourthStageDuration, fifthStageDuration, epochDuration) = zooFunctions.getStageDurations();

		emit EpochUpdated(block.timestamp, currentEpoch);
	}

	/// @notice Function to calculate incentive reward from ve-Zoo for voter.
	function calculateIncentiveRewardForVoter(uint256 votingPositionId) external only(nftVotingPosition) returns (uint256 reward)
	{
		VotingPosition storage votingPosition = votingPositionsValues[votingPositionId];
		uint256 stakingPositionId = votingPosition.stakingPositionId;

		address collection = stakingPositionsValues[stakingPositionId].collection;
		updateInfo(stakingPositionId);
		uint256 lastEpoch = computeLastEpoch(votingPositionId); // Last epoch

		veZoo.updateCurrentEpochAndReturnPoolWeight(collection);
		veZoo.updateCurrentEpochAndReturnPoolWeight(address(0));

		for (uint256 i = votingPosition.lastEpochOfIncentiveReward; i < lastEpoch; ++i) // Need different start epoch and last epoch.
		{
			uint256 endEpoch = veZoo.getEpochNumber(epochsStarts[i + 1]);
			if (endEpoch > veZoo.endEpochOfIncentiveRewards())
			{
				votingPosition.lastEpochOfIncentiveReward = currentEpoch;
				return reward;
			}

			uint256 startEpoch = veZoo.getEpochNumber(epochsStarts[i]);

			for (uint256 j = startEpoch; j < endEpoch; ++j)
			{
				if (veZoo.poolWeight(address(0), j) != 0 && rewardsForEpoch[stakingPositionId][i].votes != 0)
					reward += baseVoterReward * votingPosition.daiVotes * veZoo.poolWeight(collection, j) / veZoo.poolWeight(address(0), j) / rewardsForEpoch[stakingPositionId][i].votes;
			}
		}

		votingPosition.lastEpochOfIncentiveReward = currentEpoch;
	}

	/// @notice Function to calculate incentive reward from ve-Zoo for staker.
	function calculateIncentiveRewardForStaker(uint256 stakingPositionId) external only(nftStakingPosition) returns (uint256 reward)
	{
		StakerPosition storage stakingPosition = stakingPositionsValues[stakingPositionId];

		address collection = stakingPosition.collection;                              // Gets nft collection.
		updateInfo(stakingPositionId);                                                // Updates staking position params from previous epochs.
		updateInfoAboutStakedNumber(collection);                                      // Updates info about collection.
		veZoo.updateCurrentEpochAndReturnPoolWeight(collection);                      // Updates info in veZoo about collection.
		veZoo.updateCurrentEpochAndReturnPoolWeight(address(0));                      // Updates info in veZoo for all pools together.

		uint256 end = stakingPosition.endEpoch == 0 ? currentEpoch : stakingPosition.endEpoch;// Get recorded end epoch if it's not 0, or current epoch.

		for (uint256 i = stakingPosition.lastEpochOfIncentiveReward; i < end; ++i)
		{
			uint256 endEpoch = veZoo.getEpochNumber(epochsStarts[i + 1]);
			if (endEpoch > veZoo.endEpochOfIncentiveRewards())
			{
				stakingPosition.lastEpochOfIncentiveReward = currentEpoch;
				return reward;
			}

			uint256 startEpoch = veZoo.getEpochNumber(epochsStarts[i]);
			for (uint256 j = startEpoch; j < endEpoch; ++j)
			{
				if (veZoo.poolWeight(address(0), j) != 0)
					reward += baseStakerReward * veZoo.poolWeight(collection, j) / veZoo.poolWeight(address(0), j) / numberOfStakedNftsInCollection[i][collection];
			}
		}

		stakingPosition.lastEpochOfIncentiveReward = currentEpoch;

		return reward;
	}

	/// @notice Function to get last epoch.
	function computeLastEpoch(uint256 votingPositionId) public view returns (uint256 lastEpochNumber)
	{
		VotingPosition storage votingposition = votingPositionsValues[votingPositionId];
		//uint256 stakingPositionId = votingposition.stakingPositionId;  // Gets staker position id from voter position.
		uint256 lastEpochOfStaking = stakingPositionsValues[votingposition.stakingPositionId].endEpoch;        // Gets endEpoch from staking position.

		// Staking - finished, Voting - finished
		if (lastEpochOfStaking != 0 && votingposition.endEpoch != 0)
		{
			lastEpochNumber = Math.min(lastEpochOfStaking, votingposition.endEpoch);
		}
		// Staking - finished, Voting - existing
		else if (lastEpochOfStaking != 0)
		{
			lastEpochNumber = lastEpochOfStaking;
		}
		// Staking - exists, Voting - finished
		else if (votingposition.endEpoch != 0)
		{
			lastEpochNumber = votingposition.endEpoch;
		}
		// Staking - exists, Voting - exists
		else
		{
			lastEpochNumber = currentEpoch;
		}
	}

	function updateInfoAboutStakedNumber(address collection) public
	{
		uint256 lastUpdateEpoch = lastUpdatesOfStakedNumbers[collection];
		if (lastUpdateEpoch == currentEpoch)
			return;
		uint256 i = lastUpdateEpoch > 1 ? lastUpdateEpoch : 1;
		for (; i <= currentEpoch; ++i)
		{
			numberOfStakedNftsInCollection[i][collection] += numberOfStakedNftsInCollection[i - 1][collection];
		}

		lastUpdatesOfStakedNumbers[collection] = currentEpoch;
	}

	function _daiRewardDistribution(address beneficiary, uint256 stakingPositionId, uint256 daiReward) internal
	{
		//address collection = stakingPositionsValues[stakingPositionId].collection;
		//address royalteRecipient = veZoo.royalteRecipient(collection);

		dai.transfer(beneficiary, daiReward * 835 / 950);                             // Transfers voter part of reward.
		dai.transfer(treasury, daiReward / 10);                                 // Transfers treasury part. 9.5% = 95 / 950 = 1 / 10
		dai.transfer(team, daiReward * 15 / 950);                                     // Transfers team part.
		dai.transfer(veZoo.royalteRecipient(stakingPositionsValues[stakingPositionId].collection), daiReward * 5 / 950);
	}

	/// @notice Internal function to calculate amount of zoo to burn and withdraw.
	function _withdrawZoo(uint256 zooAmount, address beneficiary) internal
	{
		uint256 zooWithdraw = zooAmount * 995 / 1000; // Calculates amount of zoo to withdraw.
		//uint256 zooToBurn = zooAmount * 5 / 1000;     // Calculates amount of zoo to burn.

		zoo.transfer(beneficiary, zooWithdraw);                                           // Transfers zoo to beneficiary.
		// We can lock zoo at battle arena forever so we don't need to send zoo for burn to zero address
		//zoo.transfer(address(0), zooToBurn);
	}

	/// @notice Function to view current stage in battle epoch.
	/// @return stage - current stage.
	function getCurrentStage() public view returns (Stage)
	{
		uint256 time = epochStartDate + firstStageDuration;
		if (block.timestamp < time)
		{
			return Stage.FirstStage; // Staking stage
		}

		time += secondStageDuration;
		if (block.timestamp < time)
		{
			return Stage.SecondStage; // Dai vote stage.
		}

		time += thirdStageDuration;
		if (block.timestamp < time)
		{
			return Stage.ThirdStage; // Pair stage.
		}

		time += fourthStageDuration;
		if (block.timestamp < time)
		{
			return Stage.FourthStage; // Zoo vote stage.
		}
		else
		{
			return Stage.FifthStage; // Choose winner stage.
		}
	}
}

pragma solidity 0.8.13;
pragma experimental ABIEncoderV2;

// SPDX-License-Identifier: MIT

interface VaultAPI {
	function mint(uint256 mintAmount) external returns (uint256);

	function redeem(uint redeemTokens) external returns (uint256);

	function exchangeRateCurrent() external returns (uint256);

	function transfer(address who, uint256 amount) external returns (bool);

	function increaseMockBalance() external;

	function balanceOf(address who) external view returns (uint256);
}

pragma solidity 0.8.13;

// SPDX-License-Identifier: MIT

/// @title interface of Zoo functions contract.
interface IZooFunctions {

	/// @notice returns random number.
	function randomResult() external view returns(uint256 random);

	/// @notice sets random number in battles back to zero.
	function resetRandom() external;

	function randomFulfilled() external view returns(bool);

	/// @notice Function for choosing winner in battle.
	function decideWins(uint256 votesForA, uint256 votesForB, uint256 random) external view returns (bool);

	/// @notice Function for generating random number.
	function requestRandomNumber() external;

	/// @notice Function for getting random number.
	function getRandomResult() external returns(uint256);

	/// @notice Function for getting random number for selected epoch (historical).
	function getRandomResultByEpoch(uint256 epoch) external returns(uint256);

	function computePseudoRandom() external view returns (uint256);

	/// @notice Function for calculating voting with Dai in vote battles.
	function computeVotesByDai(uint256 amount) external view returns (uint256);

	/// @notice Function for calculating voting with Zoo in vote battles.
	function computeVotesByZoo(uint256 amount) external view returns (uint256);

	function firstStageDuration() external view returns (uint256);

	function secondStageDuration() external view returns (uint256);

	function thirdStageDuration() external view returns (uint256);

	function fourthStageDuration() external view returns (uint256);

	function fifthStageDuration() external view returns (uint256);

	function getStageDurations() external view returns (uint256, uint256, uint256, uint256, uint256, uint256 epochDuration);
}

pragma solidity 0.8.13;

// SPDX-License-Identifier: MIT

import "IZooFunctions.sol";
import "Ownable.sol";

/// @title Contract ZooGovernance.
/// @notice Contract for Zoo Dao vote proposals.
contract ZooGovernance is Ownable {

	address public zooFunctions;                    // Address of contract with Zoo functions.

	/// @notice Contract constructor.
	/// @param baseZooFunctions - address of baseZooFunctions contract.
	/// @param aragon - address of aragon zoo dao agent.
	constructor(address baseZooFunctions, address aragon) {

		zooFunctions = baseZooFunctions;

		transferOwnership(aragon);                  // Sets owner to aragon.
	}

	/// @notice Function for vote for changing Zoo fuctions.
	/// @param newZooFunctions - address of new zoo functions contract.
	function changeZooFunctionsContract(address newZooFunctions) external onlyOwner
	{
		zooFunctions = newZooFunctions;
	}

}

pragma solidity 0.8.13;

// SPDX-License-Identifier: MIT

import "Ownable.sol";
import "IERC20.sol";
import "ERC721.sol";

/// @title ListingList
/// @notice Contract for recording nft contracts eligible for Zoo Dao Battles.
contract ListingList is Ownable, ERC721
{
	struct CollectionRecord
	{
		uint256 decayRate;
		uint256 rateOfIncrease;
		uint256 weightAtTheStart;
	}

	struct VePositionInfo
	{
		uint256 expirationDate;
		uint256 zooLocked;
		address collection;
		uint256 decayRate;
	}

	IERC20 public zoo;                                                               // Zoo collection interface.

	/// @notice Event records address of allowed nft contract.
	event NewContractAllowed(address indexed collection, address royalteRecipient);

	event ContractDisallowed(address indexed collection, address royalteRecipient);

	event RoyalteRecipientChanged(address indexed collection, address recipient);

	event VotedForCollection(address indexed collection, address indexed voter, uint256 amount, uint256 positionId);

	event ZooUnlocked(address indexed voter, address indexed collection, uint256 amount, uint256 positionId);

	mapping (address => uint256) public lastUpdatedEpochsForCollection;

	// Nft contract => allowed or not.
	mapping (address => bool) public eligibleCollections;

	// Nft contract => address recipient.
	mapping (address => address) public royalteRecipient;

	// collection => epoch number => Record
	mapping (address => mapping (uint256 => CollectionRecord)) public collectionRecords;

	mapping (uint256 => VePositionInfo) public vePositions;

	mapping (address => uint256[]) public tokenOfOwnerByIndex;

	uint256 public epochDuration;

	uint256 public startDate;

	uint256 public minTimelock;

	uint256 public maxTimelock;

	uint256 public vePositionIndex = 1;

	uint256 public endEpochOfIncentiveRewards;

	constructor(address _zoo, uint256 _duration, uint256 _minTimelock, uint256 _maxTimelock, uint256 _incentiveRewardsDuration) ERC721("veZoo", "VEZOO")
	{
		require(_minTimelock <= _duration, "Duration should be more than minTimeLock");

		zoo = IERC20(_zoo);
		startDate = block.timestamp;
		epochDuration = _duration;
		minTimelock = _minTimelock;
		maxTimelock = _maxTimelock;
		endEpochOfIncentiveRewards = _incentiveRewardsDuration / _duration + 1;
	}

	function getEpochNumber(uint256 timestamp) public view returns (uint256)
	{
		return (timestamp - startDate) / epochDuration + 1;// epoch numbers must start from 1
	}

	function getVectorForEpoch(address collection, uint256 epochIndex) public view returns (uint256)
	{
		require(lastUpdatedEpochsForCollection[collection] >= epochIndex, "Epoch record was not updated");

		return computeVectorForEpoch(collection, epochIndex);
	}

	// address(0) for total (collection sum)
	/// @notice Function to get ve-model pool weight for nft collection.
	function poolWeight(address collection, uint256 epochIndex) public view returns(uint256 weight)
	{
		require(lastUpdatedEpochsForCollection[collection] >= epochIndex, "Epoch and colletion records were not updated");

		return collectionRecords[collection][epochIndex].weightAtTheStart;
	}

	function updateCurrentEpochAndReturnPoolWeight(address collection) public returns (uint256 weight)
	{
		uint256 epochNumber = getEpochNumber(block.timestamp);
		uint256 i = lastUpdatedEpochsForCollection[collection];
		weight = poolWeight(collection, i);

		while (i < epochNumber)
		{
			CollectionRecord storage collectionRecord = collectionRecords[collection][i + 1];
			CollectionRecord storage collectionRecordOfPreviousEpoch = collectionRecords[collection][i];

			uint256 decreasingOfWeight = computeVectorForEpoch(collection, i) * epochDuration;
			if (collectionRecordOfPreviousEpoch.weightAtTheStart + collectionRecord.weightAtTheStart >= decreasingOfWeight)
				collectionRecord.weightAtTheStart = collectionRecord.weightAtTheStart + collectionRecordOfPreviousEpoch.weightAtTheStart - decreasingOfWeight;
			else
				collectionRecord.weightAtTheStart = 0;

			collectionRecord.decayRate += collectionRecordOfPreviousEpoch.decayRate;
			collectionRecord.rateOfIncrease += collectionRecordOfPreviousEpoch.rateOfIncrease;

			i++;
			weight = collectionRecord.weightAtTheStart;
		}

		lastUpdatedEpochsForCollection[collection] = epochNumber;
	}

/* ========== Eligible projects and royalte managemenet ===========*/

	/// @notice Function to allow new NFT contract into eligible projects.
	/// @param collection - address of new Nft contract.
	function allowNewContractForStaking(address collection, address _royalteRecipient) external onlyOwner
	{
		eligibleCollections[collection] = true;                                          // Boolean for contract to be allowed for staking.

		royalteRecipient[collection] = _royalteRecipient;                                // Recipient for % of reward from that nft collection.

		lastUpdatedEpochsForCollection[collection] = getEpochNumber(block.timestamp);

		emit NewContractAllowed(collection, _royalteRecipient);                                             // Emits event that new contract are allowed.
	}

	/// @notice Function to allow multiplie contracts into eligible projects.
	function batchAllowNewContract(address[] calldata tokens, address[] calldata royalteRecipients) external onlyOwner
	{
		for (uint256 i = 0; i < tokens.length; i++)
		{
			eligibleCollections[tokens[i]] = true;

			royalteRecipient[tokens[i]] = royalteRecipients[i];                     // Recipient for % of reward from that nft collection.

			lastUpdatedEpochsForCollection[tokens[i]] = getEpochNumber(block.timestamp);

			emit NewContractAllowed(tokens[i], royalteRecipients[i]);                                     // Emits event that new contract are allowed.
		}
	}

	/// @notice Function to disallow contract from eligible projects and change royalte recipient for already staked nft.
	function disallowContractFromStaking(address collection, address recipient) external onlyOwner
	{
		eligibleCollections[collection] = false;

		royalteRecipient[collection] = recipient;                                        // Recipient for % of reward from that nft collection.

		emit ContractDisallowed(collection, recipient);                                             // Emits event that new contract are allowed.
	}

	/// @notice Function to set or change royalte recipient without removing from eligible projects.
	function setRoyalteRecipient(address collection, address recipient) external onlyOwner
	{
		royalteRecipient[collection] = recipient;

		emit RoyalteRecipientChanged(collection, recipient);
	}

/* ========== Ve-Model voting part ===========*/
	
	function voteForNftCollection(address collection, uint256 amount, uint256 lockTime) public
	{
		require(eligibleCollections[collection], "NFT collection is not allowed");
		require(lockTime <= maxTimelock && lockTime >= minTimelock, "incorrect lockTime");

		zoo.transferFrom(msg.sender, address(this), amount);

		addRecordForNewPosition(collection, amount, lockTime, msg.sender, vePositionIndex);

		tokenOfOwnerByIndex[msg.sender].push(vePositionIndex);
		_mint(msg.sender, vePositionIndex++);
	}

	function unlockZoo(uint256 positionId) external
	{
		require(ownerOf(positionId) == msg.sender);
		VePositionInfo storage vePosition = vePositions[positionId];

		uint256 currentEpoch = getEpochNumber(block.timestamp);

		require(block.timestamp >= vePosition.expirationDate, "time lock doesn't expire");

		zoo.transfer(msg.sender, vePosition.zooLocked);
		_burn(positionId);

		emit ZooUnlocked(msg.sender, vePosition.collection, vePosition.zooLocked, positionId);
	}

	function prolongate(uint256 positionId, uint256 lockTime) external
	{
		require(lockTime <= maxTimelock && lockTime >= minTimelock, "incorrect lockTime");
		require(ownerOf(positionId) == msg.sender);

		VePositionInfo storage vePosition = vePositions[positionId];

		uint256 currentEpoch = getEpochNumber(block.timestamp);
		uint256 expirationEpoch = getEpochNumber(vePosition.expirationDate);
		address collection = vePosition.collection;
		uint256 decayRate = vePosition.decayRate;

		updateCurrentEpochAndReturnPoolWeight(collection);
		updateCurrentEpochAndReturnPoolWeight(address(0));

		if (vePosition.expirationDate > block.timestamp) // If position has not expired yet. We need to liquidate it and recreate.
		{
			collectionRecords[collection][expirationEpoch].rateOfIncrease -= decayRate;
			collectionRecords[collection][currentEpoch + 1].rateOfIncrease += decayRate;
			collectionRecords[address(0)][expirationEpoch].rateOfIncrease -= decayRate;
			collectionRecords[address(0)][currentEpoch + 1].rateOfIncrease += decayRate;
		}

		addRecordForNewPosition(collection, vePosition.zooLocked, lockTime, msg.sender, positionId);
	}

	function addRecordForNewPosition(address collection, uint256 amount, uint256 lockTime, address owner, uint256 positionId) internal
	{
		uint256 weight = amount * lockTime / maxTimelock;
		uint256 currentEpoch = getEpochNumber(block.timestamp);

		uint256 unlockEpoch = getEpochNumber(block.timestamp + lockTime);
		uint256 decay = weight / lockTime;
		vePositions[positionId] = VePositionInfo(block.timestamp + lockTime, amount, collection, decay);

		collectionRecords[address(0)][currentEpoch + 1].decayRate += decay;
		collectionRecords[address(0)][currentEpoch + 1].weightAtTheStart += weight;
		collectionRecords[collection][currentEpoch + 1].decayRate += decay;
		collectionRecords[collection][currentEpoch + 1].weightAtTheStart += weight;

		collectionRecords[address(0)][unlockEpoch].rateOfIncrease += decay;
		collectionRecords[collection][unlockEpoch].rateOfIncrease += decay;
		
		emit VotedForCollection(collection, msg.sender, amount, positionId);
	}

	function computeVectorForEpoch(address collection, uint256 epochIndex) internal view returns (uint256)
	{
		CollectionRecord storage collectionRecord = collectionRecords[collection][epochIndex];

		return collectionRecord.decayRate - collectionRecord.rateOfIncrease;
	}
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "IERC20.sol";

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a >= b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a / b + (a % b == 0 ? 0 : 1);
    }
}

Settings
{
  "evmVersion": "istanbul",
  "optimizer": {
    "enabled": true,
    "runs": 1
  },
  "libraries": {
    "NftVotingPosition.sol": {}
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_dai","type":"address"},{"internalType":"address","name":"_zoo","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"voter","type":"address"},{"indexed":false,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"zooReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"votingPositionId","type":"uint256"}],"name":"ClaimedIncentiveRewardFromVoting","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"nftBattleArena","type":"address"}],"name":"NftBattleArenaSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"uint256","name":"votingPositionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addDaiToPosition","outputs":[{"internalType":"uint256","name":"votes","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"votingPositionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addZooToPosition","outputs":[{"internalType":"uint256","name":"votes","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"votingPositionIds","type":"uint256[]"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"batchClaimIncentiveVoterReward","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"votingPositionIds","type":"uint256[]"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"batchClaimRewardsFromVotings","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"votingPositionIds","type":"uint256[]"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"daiNumber","type":"uint256"}],"name":"batchWithdrawDaiFromVoting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"votingPositionIds","type":"uint256[]"},{"internalType":"uint256","name":"zooNumber","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"batchWithdrawZooFromVoting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"votingPositionId","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"claimIncentiveVoterReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"votingPositionId","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"claimRewardFromVoting","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"stakingPositionId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"createNewVotingPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dai","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nftBattleArena","outputs":[{"internalType":"contract NftBattleArena","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address payable","name":"_nftBattleArena","type":"address"}],"name":"setNftBattleArena","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"votingPositionId","type":"uint256"},{"internalType":"uint256","name":"daiNumber","type":"uint256"},{"internalType":"uint256","name":"newStakingPositionId","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"newVotingPosition","type":"uint256"}],"name":"swapVotesFromPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"votingPositionId","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"daiNumber","type":"uint256"}],"name":"withdrawDaiFromVotingPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"votingPositionId","type":"uint256"},{"internalType":"uint256","name":"zooNumber","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"withdrawZooFromVotingPosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"zoo","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

0x60806040523480156200001157600080fd5b506040516200303c3803806200303c83398101604081905262000034916200029b565b8351849084906200004d9060009060208501906200010b565b508051620000639060019060208401906200010b565b505050620000806200007a620000b560201b60201c565b620000b9565b600880546001600160a01b039384166001600160a01b0319918216179091556009805492909316911617905550620003669050565b3390565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b82805462000119906200032a565b90600052602060002090601f0160209004810192826200013d576000855562000188565b82601f106200015857805160ff191683800117855562000188565b8280016001018555821562000188579182015b82811115620001885782518255916020019190600101906200016b565b50620001969291506200019a565b5090565b5b808211156200019657600081556001016200019b565b634e487b7160e01b600052604160045260246000fd5b600082601f830112620001d957600080fd5b81516001600160401b0380821115620001f657620001f6620001b1565b604051601f8301601f19908116603f01168101908282118183101715620002215762000221620001b1565b816040528381526020925086838588010111156200023e57600080fd5b600091505b8382101562000262578582018301518183018401529082019062000243565b83821115620002745760008385830101525b9695505050505050565b80516001600160a01b03811681146200029657600080fd5b919050565b60008060008060808587031215620002b257600080fd5b84516001600160401b0380821115620002ca57600080fd5b620002d888838901620001c7565b95506020870151915080821115620002ef57600080fd5b50620002fe87828801620001c7565b9350506200030f604086016200027e565b91506200031f606086016200027e565b905092959194509250565b600181811c908216806200033f57607f821691505b6020821081036200036057634e487b7160e01b600052602260045260246000fd5b50919050565b612cc680620003766000396000f3fe608060405234801561001057600080fd5b50600436106101805760003560e01c806301ffc9a71461018557806306fdde03146101ad578063081812fc146101c2578063091903c9146101e2578063095ea7b3146101f7578063110f633f1461020a57806317f845521461022b57806323b872dd1461023e5780632cd779f3146102515780633837b833146102645780633f05b6bb1461027757806342842e0e1461028a5780635ec86c1c1461029d5780635fd717bb146102b05780636352211e146102c357806370a08231146102d6578063715018a6146102e95780637b6a8777146102f15780637cfab486146103045780638da5cb5b1461031757806395d89b411461031f5780639b6bdd8714610327578063a22cb4651461033a578063b88d4fde1461034d578063b8bb085614610360578063c87b56dd14610373578063d93652b114610386578063e985e9c514610399578063f2fde38b146103ac578063f4b9fa75146103bf578063f6277d1a146103d2578063f82e6029146103e5575b600080fd5b610198610193366004612267565b6103f8565b60405190151581526020015b60405180910390f35b6101b561044a565b6040516101a491906122dc565b6101d56101d03660046122ef565b6104dc565b6040516101a49190612308565b6101f56101f036600461231c565b610569565b005b6101f5610205366004612353565b61072b565b61021d61021836600461237f565b610836565b6040519081526020016101a4565b6101f56102393660046123af565b6108e8565b6101f561024c3660046123e7565b610988565b6007546101d5906001600160a01b031681565b61021d61027236600461231c565b6109b9565b6101f5610285366004612417565b610ab8565b6101f56102983660046123e7565b610b53565b61021d6102ab36600461247f565b610b6e565b6101f56102be3660046124d5565b610c7e565b6101d56102d13660046122ef565b61113c565b61021d6102e4366004612417565b6111b3565b6101f561123a565b6009546101d5906001600160a01b031681565b6101f561031236600461251e565b611275565b6101d561135d565b6101b561136c565b61021d61033536600461231c565b61137b565b6101f561034836600461258a565b6114d5565b6101f561035b3660046125ce565b6114e4565b6101f561036e3660046126ad565b61151c565b6101b56103813660046122ef565b611583565b61021d61039436600461247f565b61165a565b6101986103a73660046126db565b611837565b6101f56103ba366004612417565b611865565b6008546101d5906001600160a01b031681565b6101f56103e0366004612709565b611905565b61021d6103f336600461237f565b6119e8565b60006001600160e01b031982166380ac58cd60e01b148061042957506001600160e01b03198216635b5e139f60e01b145b8061044457506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606000805461045990612765565b80601f016020809104026020016040519081016040528092919081815260200182805461048590612765565b80156104d25780601f106104a7576101008083540402835291602001916104d2565b820191906000526020600020905b8154815290600101906020018083116104b557829003601f168201915b5050505050905090565b60006104e782611abd565b61054d5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b806000036105895760405162461bcd60e51b81526004016105449061279f565b6007546040805163eedbe31d60e01b815290516001926001600160a01b03169163eedbe31d9160048083019260209291908290030181865afa1580156105d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f791906127e4565b6004811115610608576106086127ce565b146106255760405162461bcd60e51b815260040161054490612805565b6008546007546040516323b872dd60e01b81526001600160a01b03928316926323b872dd9261065e92339290911690869060040161282b565b6020604051808303816000875af115801561067d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a1919061284f565b5060075460405163544bab9160e01b81526000916001600160a01b03169063544bab91906106d79086903390879060040161286c565b60408051808303816000875af11580156106f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610719919061288b565b9150506107263382611ada565b505050565b60006107368261113c565b9050806001600160a01b0316836001600160a01b0316036107a35760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610544565b336001600160a01b03821614806107bf57506107bf8133611837565b61082c5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776044820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b6064820152608401610544565b6107268383611af4565b600082336108438261113c565b6001600160a01b0316146108695760405162461bcd60e51b8152600401610544906128af565b600754604051630391d6f960e21b81526001600160a01b0390911690630e475be49061089d908790339088906004016128e0565b6020604051808303816000875af11580156108bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e091906128ff565b949350505050565b82336108f38261113c565b6001600160a01b0316146109195760405162461bcd60e51b8152600401610544906128af565b6007546040516231f64960e11b81526001600160a01b03909116906263ec9290610950908790339088908890600090600401612918565b600060405180830381600087803b15801561096a57600080fd5b505af115801561097e573d6000803e3d6000fd5b5050505050505050565b6109923382611b62565b6109ae5760405162461bcd60e51b815260040161054490612949565b610726838383611c24565b6008546007546040516323b872dd60e01b81526000926001600160a01b03908116926323b872dd926109f39233921690879060040161282b565b6020604051808303816000875af1158015610a12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a36919061284f565b5060075460405163388c01f960e01b81526001600160a01b039091169063388c01f990610a6e9086903390879060009060040161299a565b6020604051808303816000875af1158015610a8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab191906128ff565b9392505050565b33610ac161135d565b6001600160a01b031614610ae75760405162461bcd60e51b8152600401610544906129be565b6007546001600160a01b031615610afd57600080fd5b600780546001600160a01b0319166001600160a01b0383161790556040517f7a005c6c9516d93fdd58518d35c24fb68736a79353ade45cada8120e6e1f19c890610b48908390612308565b60405180910390a150565b610726838383604051806020016040528060008152506114e4565b6000805b83811015610c7657610b9b858583818110610b8f57610b8f6129f3565b9050602002013561113c565b6001600160a01b0316336001600160a01b031614610bcb5760405162461bcd60e51b8152600401610544906128af565b6007546001600160a01b0316630e475be4868684818110610bee57610bee6129f3565b9050602002013533866040518463ffffffff1660e01b8152600401610c15939291906128e0565b6020604051808303816000875af1158015610c34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5891906128ff565b610c629083612a1f565b915080610c6e81612a37565b915050610b72565b509392505050565b8433610c898261113c565b6001600160a01b031614610caf5760405162461bcd60e51b8152600401610544906128af565b84600003610ccf5760405162461bcd60e51b81526004016105449061279f565b811580610cec575033610ce18361113c565b6001600160a01b0316145b610d085760405162461bcd60e51b8152600401610544906128af565b6007546040805163eedbe31d60e01b815290516000926001600160a01b03169163eedbe31d9160048083019260209291908290030181865afa158015610d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7691906127e4565b6004811115610d8757610d876127ce565b14610da45760405162461bcd60e51b815260040161054490612805565b60075460405163cc1bb74960e01b81526004810188905260009182916001600160a01b039091169063cc1bb7499060240161018060405180830381865afa158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e179190612a50565b505050505050505050509150915080871115610e31578096505b6007546040516318b6dbf960e11b8152600481018490526001600160a01b039091169063316db7f290602401600060405180830381600087803b158015610e7757600080fd5b505af1158015610e8b573d6000803e3d6000fd5b505060075460405163f3044ac760e01b8152600481018b9052600093506001600160a01b03909116915063f3044ac7906024016020604051808303816000875af1158015610edd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0191906128ff565b6007546040516231f64960e11b81529192506001600160a01b0316906263ec9290610f39908c9033908b908e90600190600401612918565b600060405180830381600087803b158015610f5357600080fd5b505af1158015610f67573d6000803e3d6000fd5b5050505084600003610ffe57600754604051637c9d36f560e11b81526001600160a01b039091169063f93a6dea90610fa9908a90339086908e9060040161299a565b60408051808303816000875af1158015610fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610feb919061288b565b9550610ff990503386611ada565b611131565b60075460405163cc1bb74960e01b8152600481018790526000916001600160a01b03169063cc1bb7499060240161018060405180830381865afa158015611049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106d9190612a50565b50505050975050505050505050806000146110b55760405162461bcd60e51b81526020600482015260086024820152671d5b9cdd185ad95960c21b6044820152606401610544565b60075460405163388c01f960e01b81526001600160a01b039091169063388c01f9906110eb90899033908e90889060040161299a565b6020604051808303816000875af115801561110a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112e91906128ff565b50505b505050505050505050565b6000818152600260205260408120546001600160a01b0316806104445760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610544565b60006001600160a01b03821661121e5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610544565b506001600160a01b031660009081526003602052604090205490565b3361124361135d565b6001600160a01b0316146112695760405162461bcd60e51b8152600401610544906129be565b6112736000611dae565b565b60005b8381101561135657611295858583818110610b8f57610b8f6129f3565b6001600160a01b0316336001600160a01b0316146112c55760405162461bcd60e51b8152600401610544906128af565b6007546001600160a01b03166355652c958686848181106112e8576112e86129f3565b905060200201353386866040518563ffffffff1660e01b81526004016113119493929190612ad8565b600060405180830381600087803b15801561132b57600080fd5b505af115801561133f573d6000803e3d6000fd5b50505050808061134e90612a37565b915050611278565b5050505050565b6006546001600160a01b031690565b60606001805461045990612765565b60006003600760009054906101000a90046001600160a01b03166001600160a01b031663eedbe31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f691906127e4565b6004811115611407576114076127ce565b146114245760405162461bcd60e51b815260040161054490612805565b6009546007546040516323b872dd60e01b81526001600160a01b03928316926323b872dd9261145d92339290911690879060040161282b565b6020604051808303816000875af115801561147c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a0919061284f565b506007546040516340ae78bb60e01b81526001600160a01b03909116906340ae78bb90610a6e9086903390879060040161286c565b6114e0338383611e00565b5050565b6114ee3383611b62565b61150a5760405162461bcd60e51b815260040161054490612949565b61151684848484611eca565b50505050565b82336115278261113c565b6001600160a01b03161461154d5760405162461bcd60e51b8152600401610544906128af565b6007546040516355652c9560e01b81526001600160a01b03909116906355652c9590610950908790339088908890600401612ad8565b606061158e82611abd565b6115f25760405162461bcd60e51b815260206004820152602f60248201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60448201526e3732bc34b9ba32b73a103a37b5b2b760891b6064820152608401610544565b600061160960408051602081019091526000815290565b905060008151116116295760405180602001604052806000815250610ab1565b8061163384611efd565b604051602001611644929190612aff565b6040516020818303038152906040529392505050565b6000805b838110156117c1573361167c868684818110610b8f57610b8f6129f3565b6001600160a01b0316146116a25760405162461bcd60e51b815260040161054490612b2e565b6007546000906001600160a01b031663aae76c288787858181106116c8576116c86129f3565b905060200201356040518263ffffffff1660e01b81526004016116ed91815260200190565b6020604051808303816000875af115801561170c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173091906128ff565b905061173c8184612a1f565b9250337ff5488fb0cc1505f7ab04ca944cedbda860fd03eccadddedb24e31fc6002c890e8585898987818110611774576117746129f3565b905060200201356040516117a6939291906001600160a01b039390931683526020830191909152604082015260600190565b60405180910390a250806117b981612a37565b91505061165e565b5060095460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906117f49085908590600401612b56565b6020604051808303816000875af1158015611813573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c76919061284f565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b3361186e61135d565b6001600160a01b0316146118945760405162461bcd60e51b8152600401610544906129be565b6001600160a01b0381166118f95760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610544565b61190281611dae565b50565b60005b8381101561135657611925858583818110610b8f57610b8f6129f3565b6001600160a01b0316336001600160a01b0316146119555760405162461bcd60e51b8152600401610544906128af565b6007546001600160a01b03166263ec92868684818110611977576119776129f3565b9050602002013533868660006040518663ffffffff1660e01b81526004016119a3959493929190612918565b600060405180830381600087803b1580156119bd57600080fd5b505af11580156119d1573d6000803e3d6000fd5b5050505080806119e090612a37565b915050611908565b6000336119f48461113c565b6001600160a01b031614611a1a5760405162461bcd60e51b815260040161054490612b2e565b60075460405163155ced8560e31b8152600481018590526000916001600160a01b03169063aae76c28906024016020604051808303816000875af1158015611a66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8a91906128ff565b60095460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb906117f49086908590600401612b56565b6000908152600260205260409020546001600160a01b0316151590565b6114e0828260405180602001604052806000815250611ffd565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611b298261113c565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000611b6d82611abd565b611bce5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610544565b6000611bd98361113c565b9050806001600160a01b0316846001600160a01b03161480611c145750836001600160a01b0316611c09846104dc565b6001600160a01b0316145b806108e057506108e08185611837565b826001600160a01b0316611c378261113c565b6001600160a01b031614611c9b5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610544565b6001600160a01b038216611cfd5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610544565b611d08600082611af4565b6001600160a01b0383166000908152600360205260408120805460019290611d31908490612b6f565b90915550506001600160a01b0382166000908152600360205260408120805460019290611d5f908490612a1f565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b038681169182179092559151849391871691600080516020612c7183398151915291a4505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b031603611e5d5760405162461bcd60e51b815260206004820152601960248201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b6044820152606401610544565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611ed5848484611c24565b611ee184848484612030565b6115165760405162461bcd60e51b815260040161054490612b86565b606081600003611f245750506040805180820190915260018152600360fc1b602082015290565b8160005b8115611f4e5780611f3881612a37565b9150611f479050600a83612bee565b9150611f28565b6000816001600160401b03811115611f6857611f686125b8565b6040519080825280601f01601f191660200182016040528015611f92576020820181803683370190505b5090505b84156108e057611fa7600183612b6f565b9150611fb4600a86612c02565b611fbf906030612a1f565b60f81b818381518110611fd457611fd46129f3565b60200101906001600160f81b031916908160001a905350611ff6600a86612bee565b9450611f96565b6120078383612131565b6120146000848484612030565b6107265760405162461bcd60e51b815260040161054490612b86565b60006001600160a01b0384163b1561212657604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290612074903390899088908890600401612c16565b6020604051808303816000875af19250505080156120af575060408051601f3d908101601f191682019092526120ac91810190612c53565b60015b61210c573d8080156120dd576040519150601f19603f3d011682016040523d82523d6000602084013e6120e2565b606091505b5080516000036121045760405162461bcd60e51b815260040161054490612b86565b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506108e0565b506001949350505050565b6001600160a01b0382166121875760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610544565b61219081611abd565b156121dc5760405162461bcd60e51b815260206004820152601c60248201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b6044820152606401610544565b6001600160a01b0382166000908152600360205260408120805460019290612205908490612a1f565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386169081179091559051839290600080516020612c71833981519152908290a45050565b6001600160e01b03198116811461190257600080fd5b60006020828403121561227957600080fd5b8135610ab181612251565b60005b8381101561229f578181015183820152602001612287565b838111156115165750506000910152565b600081518084526122c8816020860160208601612284565b601f01601f19169290920160200192915050565b602081526000610ab160208301846122b0565b60006020828403121561230157600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6000806040838503121561232f57600080fd5b50508035926020909101359150565b6001600160a01b038116811461190257600080fd5b6000806040838503121561236657600080fd5b82356123718161233e565b946020939093013593505050565b6000806040838503121561239257600080fd5b8235915060208301356123a48161233e565b809150509250929050565b6000806000606084860312156123c457600080fd5b8335925060208401356123d68161233e565b929592945050506040919091013590565b6000806000606084860312156123fc57600080fd5b83356124078161233e565b925060208401356123d68161233e565b60006020828403121561242957600080fd5b8135610ab18161233e565b60008083601f84011261244657600080fd5b5081356001600160401b0381111561245d57600080fd5b6020830191508360208260051b850101111561247857600080fd5b9250929050565b60008060006040848603121561249457600080fd5b83356001600160401b038111156124aa57600080fd5b6124b686828701612434565b90945092505060208401356124ca8161233e565b809150509250925092565b600080600080600060a086880312156124ed57600080fd5b853594506020860135935060408601359250606086013561250d8161233e565b949793965091946080013592915050565b6000806000806060858703121561253457600080fd5b84356001600160401b0381111561254a57600080fd5b61255687828801612434565b9095509350506020850135915060408501356125718161233e565b939692955090935050565b801515811461190257600080fd5b6000806040838503121561259d57600080fd5b82356125a88161233e565b915060208301356123a48161257c565b634e487b7160e01b600052604160045260246000fd5b600080600080608085870312156125e457600080fd5b84356125ef8161233e565b935060208501356125ff8161233e565b92506040850135915060608501356001600160401b038082111561262257600080fd5b818701915087601f83011261263657600080fd5b813581811115612648576126486125b8565b604051601f8201601f19908116603f01168101908382118183101715612670576126706125b8565b816040528281528a602084870101111561268957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b6000806000606084860312156126c257600080fd5b833592506020840135915060408401356124ca8161233e565b600080604083850312156126ee57600080fd5b82356126f98161233e565b915060208301356123a48161233e565b6000806000806060858703121561271f57600080fd5b84356001600160401b0381111561273557600080fd5b61274187828801612434565b90955093505060208501356127558161233e565b9396929550929360400135925050565b600181811c9082168061277957607f821691505b60208210810361279957634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252601590820152741e995c9bc81d9bdd19481b9bdd08185b1b1bddd959605a1b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156127f657600080fd5b815160058110610ab157600080fd5b6020808252600c908201526b57726f6e672073746167652160a01b604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60006020828403121561286157600080fd5b8151610ab18161257c565b9283526001600160a01b03919091166020830152604082015260600190565b6000806040838503121561289e57600080fd5b505080516020909101519092909150565b6020808252601790820152764e6f7420746865206f776e6572206f6620766f74696e6760481b604082015260600190565b9283526001600160a01b03918216602084015216604082015260600190565b60006020828403121561291157600080fd5b5051919050565b9485526001600160a01b03938416602086015291909216604084015260608301919091521515608082015260a00190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008219821115612a3257612a32612a09565b500190565b600060018201612a4957612a49612a09565b5060010190565b6000806000806000806000806000806000806101808d8f031215612a7357600080fd5b8c519b5060208d01519a5060408d0151995060608d0151985060808d0151975060a08d0151965060c08d0151955060e08d015194506101008d015193506101208d015192506101408d015191506101608d015190509295989b509295989b509295989b565b9384526001600160a01b039283166020850152604084019190915216606082015260800190565b60008351612b11818460208801612284565b835190830190612b25818360208801612284565b01949350505050565b6020808252600e908201526d4e6f7420746865206f776e65722160901b604082015260600190565b6001600160a01b03929092168252602082015260400190565b600082821015612b8157612b81612a09565b500390565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b634e487b7160e01b600052601260045260246000fd5b600082612bfd57612bfd612bd8565b500490565b600082612c1157612c11612bd8565b500690565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612c49908301846122b0565b9695505050505050565b600060208284031215612c6557600080fd5b8151610ab18161225156feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122062d8b6c9487ced1b10246347d0f7e73ca09fcab961308090774f7adbf1e42e7864736f6c634300080d0033000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000322e86852e492a7ee17f28a78c663da38fb33bfb0000000000000000000000003907e6ff436e2b2b05d6b929fb05f14c0ee18d90000000000000000000000000000000000000000000000000000000000000000e7a566f746572506f736974696f6e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035a56500000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106101805760003560e01c806301ffc9a71461018557806306fdde03146101ad578063081812fc146101c2578063091903c9146101e2578063095ea7b3146101f7578063110f633f1461020a57806317f845521461022b57806323b872dd1461023e5780632cd779f3146102515780633837b833146102645780633f05b6bb1461027757806342842e0e1461028a5780635ec86c1c1461029d5780635fd717bb146102b05780636352211e146102c357806370a08231146102d6578063715018a6146102e95780637b6a8777146102f15780637cfab486146103045780638da5cb5b1461031757806395d89b411461031f5780639b6bdd8714610327578063a22cb4651461033a578063b88d4fde1461034d578063b8bb085614610360578063c87b56dd14610373578063d93652b114610386578063e985e9c514610399578063f2fde38b146103ac578063f4b9fa75146103bf578063f6277d1a146103d2578063f82e6029146103e5575b600080fd5b610198610193366004612267565b6103f8565b60405190151581526020015b60405180910390f35b6101b561044a565b6040516101a491906122dc565b6101d56101d03660046122ef565b6104dc565b6040516101a49190612308565b6101f56101f036600461231c565b610569565b005b6101f5610205366004612353565b61072b565b61021d61021836600461237f565b610836565b6040519081526020016101a4565b6101f56102393660046123af565b6108e8565b6101f561024c3660046123e7565b610988565b6007546101d5906001600160a01b031681565b61021d61027236600461231c565b6109b9565b6101f5610285366004612417565b610ab8565b6101f56102983660046123e7565b610b53565b61021d6102ab36600461247f565b610b6e565b6101f56102be3660046124d5565b610c7e565b6101d56102d13660046122ef565b61113c565b61021d6102e4366004612417565b6111b3565b6101f561123a565b6009546101d5906001600160a01b031681565b6101f561031236600461251e565b611275565b6101d561135d565b6101b561136c565b61021d61033536600461231c565b61137b565b6101f561034836600461258a565b6114d5565b6101f561035b3660046125ce565b6114e4565b6101f561036e3660046126ad565b61151c565b6101b56103813660046122ef565b611583565b61021d61039436600461247f565b61165a565b6101986103a73660046126db565b611837565b6101f56103ba366004612417565b611865565b6008546101d5906001600160a01b031681565b6101f56103e0366004612709565b611905565b61021d6103f336600461237f565b6119e8565b60006001600160e01b031982166380ac58cd60e01b148061042957506001600160e01b03198216635b5e139f60e01b145b8061044457506301ffc9a760e01b6001600160e01b03198316145b92915050565b60606000805461045990612765565b80601f016020809104026020016040519081016040528092919081815260200182805461048590612765565b80156104d25780601f106104a7576101008083540402835291602001916104d2565b820191906000526020600020905b8154815290600101906020018083116104b557829003601f168201915b5050505050905090565b60006104e782611abd565b61054d5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b806000036105895760405162461bcd60e51b81526004016105449061279f565b6007546040805163eedbe31d60e01b815290516001926001600160a01b03169163eedbe31d9160048083019260209291908290030181865afa1580156105d3573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105f791906127e4565b6004811115610608576106086127ce565b146106255760405162461bcd60e51b815260040161054490612805565b6008546007546040516323b872dd60e01b81526001600160a01b03928316926323b872dd9261065e92339290911690869060040161282b565b6020604051808303816000875af115801561067d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106a1919061284f565b5060075460405163544bab9160e01b81526000916001600160a01b03169063544bab91906106d79086903390879060040161286c565b60408051808303816000875af11580156106f5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610719919061288b565b9150506107263382611ada565b505050565b60006107368261113c565b9050806001600160a01b0316836001600160a01b0316036107a35760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610544565b336001600160a01b03821614806107bf57506107bf8133611837565b61082c5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776044820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b6064820152608401610544565b6107268383611af4565b600082336108438261113c565b6001600160a01b0316146108695760405162461bcd60e51b8152600401610544906128af565b600754604051630391d6f960e21b81526001600160a01b0390911690630e475be49061089d908790339088906004016128e0565b6020604051808303816000875af11580156108bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108e091906128ff565b949350505050565b82336108f38261113c565b6001600160a01b0316146109195760405162461bcd60e51b8152600401610544906128af565b6007546040516231f64960e11b81526001600160a01b03909116906263ec9290610950908790339088908890600090600401612918565b600060405180830381600087803b15801561096a57600080fd5b505af115801561097e573d6000803e3d6000fd5b5050505050505050565b6109923382611b62565b6109ae5760405162461bcd60e51b815260040161054490612949565b610726838383611c24565b6008546007546040516323b872dd60e01b81526000926001600160a01b03908116926323b872dd926109f39233921690879060040161282b565b6020604051808303816000875af1158015610a12573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a36919061284f565b5060075460405163388c01f960e01b81526001600160a01b039091169063388c01f990610a6e9086903390879060009060040161299a565b6020604051808303816000875af1158015610a8d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ab191906128ff565b9392505050565b33610ac161135d565b6001600160a01b031614610ae75760405162461bcd60e51b8152600401610544906129be565b6007546001600160a01b031615610afd57600080fd5b600780546001600160a01b0319166001600160a01b0383161790556040517f7a005c6c9516d93fdd58518d35c24fb68736a79353ade45cada8120e6e1f19c890610b48908390612308565b60405180910390a150565b610726838383604051806020016040528060008152506114e4565b6000805b83811015610c7657610b9b858583818110610b8f57610b8f6129f3565b9050602002013561113c565b6001600160a01b0316336001600160a01b031614610bcb5760405162461bcd60e51b8152600401610544906128af565b6007546001600160a01b0316630e475be4868684818110610bee57610bee6129f3565b9050602002013533866040518463ffffffff1660e01b8152600401610c15939291906128e0565b6020604051808303816000875af1158015610c34573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c5891906128ff565b610c629083612a1f565b915080610c6e81612a37565b915050610b72565b509392505050565b8433610c898261113c565b6001600160a01b031614610caf5760405162461bcd60e51b8152600401610544906128af565b84600003610ccf5760405162461bcd60e51b81526004016105449061279f565b811580610cec575033610ce18361113c565b6001600160a01b0316145b610d085760405162461bcd60e51b8152600401610544906128af565b6007546040805163eedbe31d60e01b815290516000926001600160a01b03169163eedbe31d9160048083019260209291908290030181865afa158015610d52573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d7691906127e4565b6004811115610d8757610d876127ce565b14610da45760405162461bcd60e51b815260040161054490612805565b60075460405163cc1bb74960e01b81526004810188905260009182916001600160a01b039091169063cc1bb7499060240161018060405180830381865afa158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e179190612a50565b505050505050505050509150915080871115610e31578096505b6007546040516318b6dbf960e11b8152600481018490526001600160a01b039091169063316db7f290602401600060405180830381600087803b158015610e7757600080fd5b505af1158015610e8b573d6000803e3d6000fd5b505060075460405163f3044ac760e01b8152600481018b9052600093506001600160a01b03909116915063f3044ac7906024016020604051808303816000875af1158015610edd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f0191906128ff565b6007546040516231f64960e11b81529192506001600160a01b0316906263ec9290610f39908c9033908b908e90600190600401612918565b600060405180830381600087803b158015610f5357600080fd5b505af1158015610f67573d6000803e3d6000fd5b5050505084600003610ffe57600754604051637c9d36f560e11b81526001600160a01b039091169063f93a6dea90610fa9908a90339086908e9060040161299a565b60408051808303816000875af1158015610fc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610feb919061288b565b9550610ff990503386611ada565b611131565b60075460405163cc1bb74960e01b8152600481018790526000916001600160a01b03169063cc1bb7499060240161018060405180830381865afa158015611049573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061106d9190612a50565b50505050975050505050505050806000146110b55760405162461bcd60e51b81526020600482015260086024820152671d5b9cdd185ad95960c21b6044820152606401610544565b60075460405163388c01f960e01b81526001600160a01b039091169063388c01f9906110eb90899033908e90889060040161299a565b6020604051808303816000875af115801561110a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061112e91906128ff565b50505b505050505050505050565b6000818152600260205260408120546001600160a01b0316806104445760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610544565b60006001600160a01b03821661121e5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610544565b506001600160a01b031660009081526003602052604090205490565b3361124361135d565b6001600160a01b0316146112695760405162461bcd60e51b8152600401610544906129be565b6112736000611dae565b565b60005b8381101561135657611295858583818110610b8f57610b8f6129f3565b6001600160a01b0316336001600160a01b0316146112c55760405162461bcd60e51b8152600401610544906128af565b6007546001600160a01b03166355652c958686848181106112e8576112e86129f3565b905060200201353386866040518563ffffffff1660e01b81526004016113119493929190612ad8565b600060405180830381600087803b15801561132b57600080fd5b505af115801561133f573d6000803e3d6000fd5b50505050808061134e90612a37565b915050611278565b5050505050565b6006546001600160a01b031690565b60606001805461045990612765565b60006003600760009054906101000a90046001600160a01b03166001600160a01b031663eedbe31d6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156113d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f691906127e4565b6004811115611407576114076127ce565b146114245760405162461bcd60e51b815260040161054490612805565b6009546007546040516323b872dd60e01b81526001600160a01b03928316926323b872dd9261145d92339290911690879060040161282b565b6020604051808303816000875af115801561147c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114a0919061284f565b506007546040516340ae78bb60e01b81526001600160a01b03909116906340ae78bb90610a6e9086903390879060040161286c565b6114e0338383611e00565b5050565b6114ee3383611b62565b61150a5760405162461bcd60e51b815260040161054490612949565b61151684848484611eca565b50505050565b82336115278261113c565b6001600160a01b03161461154d5760405162461bcd60e51b8152600401610544906128af565b6007546040516355652c9560e01b81526001600160a01b03909116906355652c9590610950908790339088908890600401612ad8565b606061158e82611abd565b6115f25760405162461bcd60e51b815260206004820152602f60248201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60448201526e3732bc34b9ba32b73a103a37b5b2b760891b6064820152608401610544565b600061160960408051602081019091526000815290565b905060008151116116295760405180602001604052806000815250610ab1565b8061163384611efd565b604051602001611644929190612aff565b6040516020818303038152906040529392505050565b6000805b838110156117c1573361167c868684818110610b8f57610b8f6129f3565b6001600160a01b0316146116a25760405162461bcd60e51b815260040161054490612b2e565b6007546000906001600160a01b031663aae76c288787858181106116c8576116c86129f3565b905060200201356040518263ffffffff1660e01b81526004016116ed91815260200190565b6020604051808303816000875af115801561170c573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061173091906128ff565b905061173c8184612a1f565b9250337ff5488fb0cc1505f7ab04ca944cedbda860fd03eccadddedb24e31fc6002c890e8585898987818110611774576117746129f3565b905060200201356040516117a6939291906001600160a01b039390931683526020830191909152604082015260600190565b60405180910390a250806117b981612a37565b91505061165e565b5060095460405163a9059cbb60e01b81526001600160a01b039091169063a9059cbb906117f49085908590600401612b56565b6020604051808303816000875af1158015611813573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c76919061284f565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b3361186e61135d565b6001600160a01b0316146118945760405162461bcd60e51b8152600401610544906129be565b6001600160a01b0381166118f95760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610544565b61190281611dae565b50565b60005b8381101561135657611925858583818110610b8f57610b8f6129f3565b6001600160a01b0316336001600160a01b0316146119555760405162461bcd60e51b8152600401610544906128af565b6007546001600160a01b03166263ec92868684818110611977576119776129f3565b9050602002013533868660006040518663ffffffff1660e01b81526004016119a3959493929190612918565b600060405180830381600087803b1580156119bd57600080fd5b505af11580156119d1573d6000803e3d6000fd5b5050505080806119e090612a37565b915050611908565b6000336119f48461113c565b6001600160a01b031614611a1a5760405162461bcd60e51b815260040161054490612b2e565b60075460405163155ced8560e31b8152600481018590526000916001600160a01b03169063aae76c28906024016020604051808303816000875af1158015611a66573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a8a91906128ff565b60095460405163a9059cbb60e01b81529192506001600160a01b03169063a9059cbb906117f49086908590600401612b56565b6000908152600260205260409020546001600160a01b0316151590565b6114e0828260405180602001604052806000815250611ffd565b600081815260046020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611b298261113c565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000611b6d82611abd565b611bce5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610544565b6000611bd98361113c565b9050806001600160a01b0316846001600160a01b03161480611c145750836001600160a01b0316611c09846104dc565b6001600160a01b0316145b806108e057506108e08185611837565b826001600160a01b0316611c378261113c565b6001600160a01b031614611c9b5760405162461bcd60e51b815260206004820152602560248201527f4552433732313a207472616e736665722066726f6d20696e636f72726563742060448201526437bbb732b960d91b6064820152608401610544565b6001600160a01b038216611cfd5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610544565b611d08600082611af4565b6001600160a01b0383166000908152600360205260408120805460019290611d31908490612b6f565b90915550506001600160a01b0382166000908152600360205260408120805460019290611d5f908490612a1f565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b038681169182179092559151849391871691600080516020612c7183398151915291a4505050565b600680546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b816001600160a01b0316836001600160a01b031603611e5d5760405162461bcd60e51b815260206004820152601960248201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b6044820152606401610544565b6001600160a01b03838116600081815260056020908152604080832094871680845294825291829020805460ff191686151590811790915591519182527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a3505050565b611ed5848484611c24565b611ee184848484612030565b6115165760405162461bcd60e51b815260040161054490612b86565b606081600003611f245750506040805180820190915260018152600360fc1b602082015290565b8160005b8115611f4e5780611f3881612a37565b9150611f479050600a83612bee565b9150611f28565b6000816001600160401b03811115611f6857611f686125b8565b6040519080825280601f01601f191660200182016040528015611f92576020820181803683370190505b5090505b84156108e057611fa7600183612b6f565b9150611fb4600a86612c02565b611fbf906030612a1f565b60f81b818381518110611fd457611fd46129f3565b60200101906001600160f81b031916908160001a905350611ff6600a86612bee565b9450611f96565b6120078383612131565b6120146000848484612030565b6107265760405162461bcd60e51b815260040161054490612b86565b60006001600160a01b0384163b1561212657604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290612074903390899088908890600401612c16565b6020604051808303816000875af19250505080156120af575060408051601f3d908101601f191682019092526120ac91810190612c53565b60015b61210c573d8080156120dd576040519150601f19603f3d011682016040523d82523d6000602084013e6120e2565b606091505b5080516000036121045760405162461bcd60e51b815260040161054490612b86565b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506108e0565b506001949350505050565b6001600160a01b0382166121875760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610544565b61219081611abd565b156121dc5760405162461bcd60e51b815260206004820152601c60248201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b6044820152606401610544565b6001600160a01b0382166000908152600360205260408120805460019290612205908490612a1f565b909155505060008181526002602052604080822080546001600160a01b0319166001600160a01b0386169081179091559051839290600080516020612c71833981519152908290a45050565b6001600160e01b03198116811461190257600080fd5b60006020828403121561227957600080fd5b8135610ab181612251565b60005b8381101561229f578181015183820152602001612287565b838111156115165750506000910152565b600081518084526122c8816020860160208601612284565b601f01601f19169290920160200192915050565b602081526000610ab160208301846122b0565b60006020828403121561230157600080fd5b5035919050565b6001600160a01b0391909116815260200190565b6000806040838503121561232f57600080fd5b50508035926020909101359150565b6001600160a01b038116811461190257600080fd5b6000806040838503121561236657600080fd5b82356123718161233e565b946020939093013593505050565b6000806040838503121561239257600080fd5b8235915060208301356123a48161233e565b809150509250929050565b6000806000606084860312156123c457600080fd5b8335925060208401356123d68161233e565b929592945050506040919091013590565b6000806000606084860312156123fc57600080fd5b83356124078161233e565b925060208401356123d68161233e565b60006020828403121561242957600080fd5b8135610ab18161233e565b60008083601f84011261244657600080fd5b5081356001600160401b0381111561245d57600080fd5b6020830191508360208260051b850101111561247857600080fd5b9250929050565b60008060006040848603121561249457600080fd5b83356001600160401b038111156124aa57600080fd5b6124b686828701612434565b90945092505060208401356124ca8161233e565b809150509250925092565b600080600080600060a086880312156124ed57600080fd5b853594506020860135935060408601359250606086013561250d8161233e565b949793965091946080013592915050565b6000806000806060858703121561253457600080fd5b84356001600160401b0381111561254a57600080fd5b61255687828801612434565b9095509350506020850135915060408501356125718161233e565b939692955090935050565b801515811461190257600080fd5b6000806040838503121561259d57600080fd5b82356125a88161233e565b915060208301356123a48161257c565b634e487b7160e01b600052604160045260246000fd5b600080600080608085870312156125e457600080fd5b84356125ef8161233e565b935060208501356125ff8161233e565b92506040850135915060608501356001600160401b038082111561262257600080fd5b818701915087601f83011261263657600080fd5b813581811115612648576126486125b8565b604051601f8201601f19908116603f01168101908382118183101715612670576126706125b8565b816040528281528a602084870101111561268957600080fd5b82602086016020830137600060208483010152809550505050505092959194509250565b6000806000606084860312156126c257600080fd5b833592506020840135915060408401356124ca8161233e565b600080604083850312156126ee57600080fd5b82356126f98161233e565b915060208301356123a48161233e565b6000806000806060858703121561271f57600080fd5b84356001600160401b0381111561273557600080fd5b61274187828801612434565b90955093505060208501356127558161233e565b9396929550929360400135925050565b600181811c9082168061277957607f821691505b60208210810361279957634e487b7160e01b600052602260045260246000fd5b50919050565b6020808252601590820152741e995c9bc81d9bdd19481b9bdd08185b1b1bddd959605a1b604082015260600190565b634e487b7160e01b600052602160045260246000fd5b6000602082840312156127f657600080fd5b815160058110610ab157600080fd5b6020808252600c908201526b57726f6e672073746167652160a01b604082015260600190565b6001600160a01b039384168152919092166020820152604081019190915260600190565b60006020828403121561286157600080fd5b8151610ab18161257c565b9283526001600160a01b03919091166020830152604082015260600190565b6000806040838503121561289e57600080fd5b505080516020909101519092909150565b6020808252601790820152764e6f7420746865206f776e6572206f6620766f74696e6760481b604082015260600190565b9283526001600160a01b03918216602084015216604082015260600190565b60006020828403121561291157600080fd5b5051919050565b9485526001600160a01b03938416602086015291909216604084015260608301919091521515608082015260a00190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b9384526001600160a01b039290921660208401526040830152606082015260800190565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60008219821115612a3257612a32612a09565b500190565b600060018201612a4957612a49612a09565b5060010190565b6000806000806000806000806000806000806101808d8f031215612a7357600080fd5b8c519b5060208d01519a5060408d0151995060608d0151985060808d0151975060a08d0151965060c08d0151955060e08d015194506101008d015193506101208d015192506101408d015191506101608d015190509295989b509295989b509295989b565b9384526001600160a01b039283166020850152604084019190915216606082015260800190565b60008351612b11818460208801612284565b835190830190612b25818360208801612284565b01949350505050565b6020808252600e908201526d4e6f7420746865206f776e65722160901b604082015260600190565b6001600160a01b03929092168252602082015260400190565b600082821015612b8157612b81612a09565b500390565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b634e487b7160e01b600052601260045260246000fd5b600082612bfd57612bfd612bd8565b500490565b600082612c1157612c11612bd8565b500690565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090612c49908301846122b0565b9695505050505050565b600060208284031215612c6557600080fd5b8151610ab18161225156feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122062d8b6c9487ced1b10246347d0f7e73ca09fcab961308090774f7adbf1e42e7864736f6c634300080d0033

Block Transaction Gas Used Reward
view all blocks collator

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.