MATIC Price: $1.02 (-2.67%)
Gas: 203 GWei
 

Overview

MATIC Balance

Polygon PoS Chain LogoPolygon PoS Chain LogoPolygon PoS Chain Logo0 MATIC

MATIC Value

$0.00

Token Holdings

Transaction Hash
Method
Block
From
To
Value
Approve551776642024-03-28 13:30:241 hr ago1711632624IN
Alethea AI: ALI Token
0 MATIC0.01000486206.35401361
Approve551754342024-03-28 12:06:342 hrs ago1711627594IN
Alethea AI: ALI Token
0 MATIC0.00556339114.718618
Transfer551752462024-03-28 11:59:542 hrs ago1711627194IN
Alethea AI: ALI Token
0 MATIC0.00772393135.89866404
Transfer551751742024-03-28 11:57:042 hrs ago1711627024IN
Alethea AI: ALI Token
0 MATIC0.00676855170.38947189
Transfer551751492024-03-28 11:55:562 hrs ago1711626956IN
Alethea AI: ALI Token
0 MATIC0.00890998144.67087722
Approve551747982024-03-28 11:42:582 hrs ago1711626178IN
Alethea AI: ALI Token
0 MATIC0.00920191188.95098935
Transfer551743582024-03-28 11:25:433 hrs ago1711625143IN
Alethea AI: ALI Token
0 MATIC0.00981822247.08630075
Transfer551743352024-03-28 11:24:533 hrs ago1711625093IN
Alethea AI: ALI Token
0 MATIC0.01678607272.5011878
Approve551707932024-03-28 9:05:355 hrs ago1711616735IN
Alethea AI: ALI Token
0 MATIC0.00968369199.77924164
Approve551702982024-03-28 8:45:455 hrs ago1711615545IN
Alethea AI: ALI Token
0 MATIC0.00566641116.87179774
Approve551696762024-03-28 8:21:366 hrs ago1711614096IN
Alethea AI: ALI Token
0 MATIC0.00976711200.30991858
Transfer551689752024-03-28 7:53:386 hrs ago1711612418IN
Alethea AI: ALI Token
0 MATIC0.0055130997
Transfer551686612024-03-28 7:40:567 hrs ago1711611656IN
Alethea AI: ALI Token
0 MATIC0.0052512392.41219797
Approve551679652024-03-28 7:13:047 hrs ago1711609984IN
Alethea AI: ALI Token
0 MATIC0.0018521258.99239291
Approve551670392024-03-28 6:38:048 hrs ago1711607884IN
Alethea AI: ALI Token
0 MATIC0.0036730875.85265873
Approve551663182024-03-28 6:10:048 hrs ago1711606204IN
Alethea AI: ALI Token
0 MATIC0.0026464154.56979286
Approve551640292024-03-28 4:42:459 hrs ago1711600965IN
Alethea AI: ALI Token
0 MATIC0.00554872113.79665969
Approve551637762024-03-28 4:32:4510 hrs ago1711600365IN
Alethea AI: ALI Token
0 MATIC0.01162898238.49444524
Transfer551612282024-03-28 2:58:0311 hrs ago1711594683IN
Alethea AI: ALI Token
0 MATIC0.00477944107.40326188
Transfer551610872024-03-28 2:52:3111 hrs ago1711594351IN
Alethea AI: ALI Token
0 MATIC0.0047731107.26074661
Approve551600522024-03-28 2:15:5112 hrs ago1711592151IN
Alethea AI: ALI Token
0 MATIC0.0075349154.53045136
Transfer551599562024-03-28 2:12:2712 hrs ago1711591947IN
Alethea AI: ALI Token
0 MATIC0.00668295117.5831558
Approve551559392024-03-27 23:49:2314 hrs ago1711583363IN
Alethea AI: ALI Token
0 MATIC0.0038458378.87274387
Approve551559182024-03-27 23:48:3714 hrs ago1711583317IN
Alethea AI: ALI Token
0 MATIC0.0024134283.62517362
Approve551558942024-03-27 23:47:4314 hrs ago1711583263IN
Alethea AI: ALI Token
0 MATIC0.0036080973.99707497
View all transactions

Parent Txn Hash Block From To Value
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
PolygonAliERC20v2

Compiler Version
v0.8.15+commit.e14f2714

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, MIT license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 10 : PolygonAliERC20v2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./AliERC20v2.sol";

/**
 * @title Artificial Liquid Intelligence ERC20 Token (With Polygon Support)
 *
 * @notice Polygon extension contract ads the functions required to bridge original
 *      ALI token on Ethereum L1; these are mint and burn functions executed
 *      when the tokens are deposited from from Ethereum L1 into Polygon L2 (mint),
 *      and when the tokens are withdrawn back from Polygon L2 into Ethereum L1 (burn).
 *
 * @notice Read more:
 *      https://docs.polygon.technology/docs/develop/ethereum-polygon/mintable-assets
 */
contract PolygonAliERC20v2 is AliERC20v2Base {

	/**
	 * @dev Constructs/deploys Polygon ALI instance,
	 *      assigns initial token supply to the address specified
	 */
	constructor() AliERC20v2Base(address(0), 0) {}

	/**
	 * @notice Executed by ChildChainManager when token is deposited on the root chain
	 *
	 * @dev Executable only by ChildChainManager which should be given the minting
	 *      permission as part of the smart contract deployment process;
	 *      handles the deposit by minting the required amount for user
	 *
	 * @param user user address for whom deposit is being done
	 * @param depositData abi encoded amount
	 */
	function deposit(address user, bytes calldata depositData) external {
		// extract the amount value to mint from the calldata
		uint256 amount = abi.decode(depositData, (uint256));

		// delegate to `mint`
		mint(user, amount);
	}

	/**
	 * @notice Executed by the tokens owner when they want to withdraw tokens back to the root chain
	 *
	 * @dev Burns user's tokens;
	 *      this transaction will be verified when exiting on the root chain
	 *
	 * @param amount amount of tokens to withdraw
	 */
	function withdraw(uint256 amount) external {
		// delegate to the `burn` function
		burn(msg.sender, amount);
	}
}

File 2 of 10 : EIP2612.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title EIP-2612: permit - 712-signed approvals
 *
 * @notice A function permit extending ERC-20 which allows for approvals to be made via secp256k1 signatures.
 *      This kind of “account abstraction for ERC-20” brings about two main benefits:
 *        - transactions involving ERC-20 operations can be paid using the token itself rather than ETH,
 *        - approve and pull operations can happen in a single transaction instead of two consecutive transactions,
 *        - while adding as little as possible over the existing ERC-20 standard.
 *
 * @notice See https://eips.ethereum.org/EIPS/eip-2612#specification
 */
interface EIP2612 {
	/**
	 * @notice EIP712 domain separator of the smart contract. It should be unique to the contract
	 *      and chain to prevent replay attacks from other domains, and satisfy the requirements of EIP-712,
	 *      but is otherwise unconstrained.
	 */
	function DOMAIN_SEPARATOR() external view returns (bytes32);

	/**
	 * @notice Counter of the nonces used for the given address; nonce are used sequentially
	 *
	 * @dev To prevent from replay attacks nonce is incremented for each address after a successful `permit` execution
	 *
	 * @param owner an address to query number of used nonces for
	 * @return number of used nonce, nonce number to be used next
	 */
	function nonces(address owner) external view returns (uint);

	/**
	 * @notice For all addresses owner, spender, uint256s value, deadline and nonce, uint8 v, bytes32 r and s,
	 *      a call to permit(owner, spender, value, deadline, v, r, s) will set approval[owner][spender] to value,
	 *      increment nonces[owner] by 1, and emit a corresponding Approval event,
	 *      if and only if the following conditions are met:
	 *        - The current blocktime is less than or equal to deadline.
	 *        - owner is not the zero address.
	 *        - nonces[owner] (before the state update) is equal to nonce.
	 *        - r, s and v is a valid secp256k1 signature from owner of the message:
	 *
	 * @param owner token owner address, granting an approval to spend its tokens
	 * @param spender an address approved by the owner (token owner)
	 *      to spend some tokens on its behalf
	 * @param value an amount of tokens spender `spender` is allowed to
	 *      transfer on behalf of the token owner
	 * @param v the recovery byte of the signature
	 * @param r half of the ECDSA signature pair
	 * @param s half of the ECDSA signature pair
	 */
	function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
}

File 3 of 10 : EIP3009.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title EIP-3009: Transfer With Authorization
 *
 * @notice A contract interface that enables transferring of fungible assets via a signed authorization.
 *      See https://eips.ethereum.org/EIPS/eip-3009
 *      See https://eips.ethereum.org/EIPS/eip-3009#specification
 */
interface EIP3009 {
	/**
	 * @dev Fired whenever the nonce gets used (ex.: `transferWithAuthorization`, `receiveWithAuthorization`)
	 *
	 * @param authorizer an address which has used the nonce
	 * @param nonce the nonce used
	 */
	event AuthorizationUsed(address indexed authorizer, bytes32 indexed nonce);

	/**
	 * @dev Fired whenever the nonce gets cancelled (ex.: `cancelAuthorization`)
	 *
	 * @dev Both `AuthorizationUsed` and `AuthorizationCanceled` imply the nonce
	 *      cannot be longer used, the only difference is that `AuthorizationCanceled`
	 *      implies no smart contract state change made (except the nonce marked as cancelled)
	 *
	 * @param authorizer an address which has cancelled the nonce
	 * @param nonce the nonce cancelled
	 */
	event AuthorizationCanceled(address indexed authorizer, bytes32 indexed nonce);

	/**
	 * @notice Returns the state of an authorization, more specifically
	 *      if the specified nonce was already used by the address specified
	 *
	 * @dev Nonces are expected to be client-side randomly generated 32-byte data
	 *      unique to the authorizer's address
	 *
	 * @param authorizer    Authorizer's address
	 * @param nonce         Nonce of the authorization
	 * @return true if the nonce is used
	 */
	function authorizationState(
		address authorizer,
		bytes32 nonce
	) external view returns (bool);

	/**
	 * @notice Execute a transfer with a signed authorization
	 *
	 * @param from          Payer's address (Authorizer)
	 * @param to            Payee's address
	 * @param value         Amount to be transferred
	 * @param validAfter    The time after which this is valid (unix time)
	 * @param validBefore   The time before which this is valid (unix time)
	 * @param nonce         Unique nonce
	 * @param v             v of the signature
	 * @param r             r of the signature
	 * @param s             s of the signature
	 */
	function transferWithAuthorization(
		address from,
		address to,
		uint256 value,
		uint256 validAfter,
		uint256 validBefore,
		bytes32 nonce,
		uint8 v,
		bytes32 r,
		bytes32 s
	) external;

	/**
	 * @notice Receive a transfer with a signed authorization from the payer
	 *
	 * @dev This has an additional check to ensure that the payee's address matches
	 *      the caller of this function to prevent front-running attacks.
	 * @dev See https://eips.ethereum.org/EIPS/eip-3009#security-considerations
	 *
	 * @param from          Payer's address (Authorizer)
	 * @param to            Payee's address
	 * @param value         Amount to be transferred
	 * @param validAfter    The time after which this is valid (unix time)
	 * @param validBefore   The time before which this is valid (unix time)
	 * @param nonce         Unique nonce
	 * @param v             v of the signature
	 * @param r             r of the signature
	 * @param s             s of the signature
	 */
	function receiveWithAuthorization(
		address from,
		address to,
		uint256 value,
		uint256 validAfter,
		uint256 validBefore,
		bytes32 nonce,
		uint8 v,
		bytes32 r,
		bytes32 s
	) external;

	/**
	 * @notice Attempt to cancel an authorization
	 *
	 * @param authorizer    Authorizer's address
	 * @param nonce         Nonce of the authorization
	 * @param v             v of the signature
	 * @param r             r of the signature
	 * @param s             s of the signature
	 */
	function cancelAuthorization(
		address authorizer,
		bytes32 nonce,
		uint8 v,
		bytes32 r,
		bytes32 s
	) external;
}

File 4 of 10 : ERC1363Spec.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "./ERC20Spec.sol";
import "./ERC165Spec.sol";

/**
 * @title ERC1363 Interface
 *
 * @dev Interface defining a ERC1363 Payable Token contract.
 *      Implementing contracts MUST implement the ERC1363 interface as well as the ERC20 and ERC165 interfaces.
 */
interface ERC1363 is ERC20, ERC165  {
	/*
	 * Note: the ERC-165 identifier for this interface is 0xb0202a11.
	 * 0xb0202a11 ===
	 *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
	 *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
	 *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
	 *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
	 *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
	 *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
	 */

	/**
	 * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @return true unless throwing
	 */
	function transferAndCall(address to, uint256 value) external returns (bool);

	/**
	 * @notice Transfer tokens from `msg.sender` to another address and then call `onTransferReceived` on receiver
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @param data bytes Additional data with no specified format, sent in call to `to`
	 * @return true unless throwing
	 */
	function transferAndCall(address to, uint256 value, bytes memory data) external returns (bool);

	/**
	 * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver
	 * @param from address The address which you want to send tokens from
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @return true unless throwing
	 */
	function transferFromAndCall(address from, address to, uint256 value) external returns (bool);


	/**
	 * @notice Transfer tokens from one address to another and then call `onTransferReceived` on receiver
	 * @param from address The address which you want to send tokens from
	 * @param to address The address which you want to transfer to
	 * @param value uint256 The amount of tokens to be transferred
	 * @param data bytes Additional data with no specified format, sent in call to `to`
	 * @return true unless throwing
	 */
	function transferFromAndCall(address from, address to, uint256 value, bytes memory data) external returns (bool);

	/**
	 * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
	 * and then call `onApprovalReceived` on spender.
	 * @param spender address The address which will spend the funds
	 * @param value uint256 The amount of tokens to be spent
	 */
	function approveAndCall(address spender, uint256 value) external returns (bool);

	/**
	 * @notice Approve the passed address to spend the specified amount of tokens on behalf of msg.sender
	 * and then call `onApprovalReceived` on spender.
	 * @param spender address The address which will spend the funds
	 * @param value uint256 The amount of tokens to be spent
	 * @param data bytes Additional data with no specified format, sent in call to `spender`
	 */
	function approveAndCall(address spender, uint256 value, bytes memory data) external returns (bool);
}

/**
 * @title ERC1363Receiver Interface
 *
 * @dev Interface for any contract that wants to support `transferAndCall` or `transferFromAndCall`
 *      from ERC1363 token contracts.
 */
interface ERC1363Receiver {
	/*
	 * Note: the ERC-165 identifier for this interface is 0x88a7ca5c.
	 * 0x88a7ca5c === bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
	 */

	/**
	 * @notice Handle the receipt of ERC1363 tokens
	 *
	 * @dev Any ERC1363 smart contract calls this function on the recipient
	 *      after a `transfer` or a `transferFrom`. This function MAY throw to revert and reject the
	 *      transfer. Return of other than the magic value MUST result in the
	 *      transaction being reverted.
	 *      Note: the token contract address is always the message sender.
	 *
	 * @param operator address The address which called `transferAndCall` or `transferFromAndCall` function
	 * @param from address The address which are token transferred from
	 * @param value uint256 The amount of tokens transferred
	 * @param data bytes Additional data with no specified format
	 * @return `bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))`
	 *      unless throwing
	 */
	function onTransferReceived(address operator, address from, uint256 value, bytes memory data) external returns (bytes4);
}

/**
 * @title ERC1363Spender Interface
 *
 * @dev Interface for any contract that wants to support `approveAndCall`
 *      from ERC1363 token contracts.
 */
interface ERC1363Spender {
	/*
	 * Note: the ERC-165 identifier for this interface is 0x7b04a2d0.
	 * 0x7b04a2d0 === bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))
	 */

	/**
	 * @notice Handle the approval of ERC1363 tokens
	 *
	 * @dev Any ERC1363 smart contract calls this function on the recipient
	 *      after an `approve`. This function MAY throw to revert and reject the
	 *      approval. Return of other than the magic value MUST result in the
	 *      transaction being reverted.
	 *      Note: the token contract address is always the message sender.
	 *
	 * @param owner address The address which called `approveAndCall` function
	 * @param value uint256 The amount of tokens to be spent
	 * @param data bytes Additional data with no specified format
	 * @return `bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))`
	 *      unless throwing
	 */
	function onApprovalReceived(address owner, uint256 value, bytes memory data) external returns (bytes4);
}

File 5 of 10 : ERC165Spec.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title ERC-165 Standard Interface Detection
 *
 * @dev Interface of the ERC165 standard, as defined in the
 *       https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * @dev Implementers can declare support of contract interfaces,
 *      which can then be queried by others.
 *
 * @author Christian Reitwießner, Nick Johnson, Fabian Vogelsteller, Jordi Baylina, Konrad Feldmeier, William Entriken
 */
interface ERC165 {
	/**
	 * @notice Query if a contract implements an interface
	 *
	 * @dev Interface identification is specified in ERC-165.
	 *      This function uses less than 30,000 gas.
	 *
	 * @param interfaceID The interface identifier, as specified in ERC-165
	 * @return `true` if the contract implements `interfaceID` and
	 *      `interfaceID` is not 0xffffffff, `false` otherwise
	 */
	function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

File 6 of 10 : ERC20Spec.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title EIP-20: ERC-20 Token Standard
 *
 * @notice The ERC-20 (Ethereum Request for Comments 20), proposed by Fabian Vogelsteller in November 2015,
 *      is a Token Standard that implements an API for tokens within Smart Contracts.
 *
 * @notice It provides functionalities like to transfer tokens from one account to another,
 *      to get the current token balance of an account and also the total supply of the token available on the network.
 *      Besides these it also has some other functionalities like to approve that an amount of
 *      token from an account can be spent by a third party account.
 *
 * @notice If a Smart Contract implements the following methods and events it can be called an ERC-20 Token
 *      Contract and, once deployed, it will be responsible to keep track of the created tokens on Ethereum.
 *
 * @notice See https://ethereum.org/en/developers/docs/standards/tokens/erc-20/
 * @notice See https://eips.ethereum.org/EIPS/eip-20
 */
interface ERC20 {
	/**
	 * @dev Fired in transfer(), transferFrom() to indicate that token transfer happened
	 *
	 * @param from an address tokens were consumed from
	 * @param to an address tokens were sent to
	 * @param value number of tokens transferred
	 */
	event Transfer(address indexed from, address indexed to, uint256 value);

	/**
	 * @dev Fired in approve() to indicate an approval event happened
	 *
	 * @param owner an address which granted a permission to transfer
	 *      tokens on its behalf
	 * @param spender an address which received a permission to transfer
	 *      tokens on behalf of the owner `_owner`
	 * @param value amount of tokens granted to transfer on behalf
	 */
	event Approval(address indexed owner, address indexed spender, uint256 value);

	/**
	 * @return name of the token (ex.: USD Coin)
	 */
	// OPTIONAL - This method can be used to improve usability,
	// but interfaces and other contracts MUST NOT expect these values to be present.
	// function name() external view returns (string memory);

	/**
	 * @return symbol of the token (ex.: USDC)
	 */
	// OPTIONAL - This method can be used to improve usability,
	// but interfaces and other contracts MUST NOT expect these values to be present.
	// function symbol() external view returns (string memory);

	/**
	 * @dev Returns the number of decimals used to get its user representation.
	 *      For example, if `decimals` equals `2`, a balance of `505` tokens should
	 *      be displayed to a user as `5,05` (`505 / 10 ** 2`).
	 *
	 * @dev Tokens usually opt for a value of 18, imitating the relationship between
	 *      Ether and Wei. This is the value {ERC20} uses, unless this function is
	 *      overridden;
	 *
	 * @dev NOTE: This information is only used for _display_ purposes: it in
	 *      no way affects any of the arithmetic of the contract, including
	 *      {IERC20-balanceOf} and {IERC20-transfer}.
	 *
	 * @return token decimals
	 */
	// OPTIONAL - This method can be used to improve usability,
	// but interfaces and other contracts MUST NOT expect these values to be present.
	// function decimals() external view returns (uint8);

	/**
	 * @return the amount of tokens in existence
	 */
	function totalSupply() external view returns (uint256);

	/**
	 * @notice Gets the balance of a particular address
	 *
	 * @param _owner the address to query the the balance for
	 * @return balance an amount of tokens owned by the address specified
	 */
	function balanceOf(address _owner) external view returns (uint256 balance);

	/**
	 * @notice Transfers some tokens to an external address or a smart contract
	 *
	 * @dev Called by token owner (an address which has a
	 *      positive token balance tracked by this smart contract)
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * self address or
	 *          * smart contract which doesn't support ERC20
	 *
	 * @param _to an address to transfer tokens to,
	 *      must be either an external address or a smart contract,
	 *      compliant with the ERC20 standard
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return success true on success, throws otherwise
	 */
	function transfer(address _to, uint256 _value) external returns (bool success);

	/**
	 * @notice Transfers some tokens on behalf of address `_from' (token owner)
	 *      to some other address `_to`
	 *
	 * @dev Called by token owner on his own or approved address,
	 *      an address approved earlier by token owner to
	 *      transfer some amount of tokens on its behalf
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * smart contract which doesn't support ERC20
	 *
	 * @param _from token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to an address to transfer tokens to,
	 *      must be either an external address or a smart contract,
	 *      compliant with the ERC20 standard
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return success true on success, throws otherwise
	 */
	function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);

	/**
	 * @notice Approves address called `_spender` to transfer some amount
	 *      of tokens on behalf of the owner (transaction sender)
	 *
	 * @dev Transaction sender must not necessarily own any tokens to grant the permission
	 *
	 * @param _spender an address approved by the caller (token owner)
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens spender `_spender` is allowed to
	 *      transfer on behalf of the token owner
	 * @return success true on success, throws otherwise
	 */
	function approve(address _spender, uint256 _value) external returns (bool success);

	/**
	 * @notice Returns the amount which _spender is still allowed to withdraw from _owner.
	 *
	 * @dev A function to check an amount of tokens owner approved
	 *      to transfer on its behalf by some other address called "spender"
	 *
	 * @param _owner an address which approves transferring some tokens on its behalf
	 * @param _spender an address approved to transfer some tokens on behalf
	 * @return remaining an amount of tokens approved address `_spender` can transfer on behalf
	 *      of token owner `_owner`
	 */
	function allowance(address _owner, address _spender) external view returns (uint256 remaining);
}

File 7 of 10 : AddressUtils.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title Address Utils
 *
 * @dev Utility library of inline functions on addresses
 *
 * @dev Copy of the Zeppelin's library:
 *      https://github.com/gnosis/openzeppelin-solidity/blob/master/contracts/AddressUtils.sol
 */
library AddressUtils {

	/**
	 * @notice Checks if the target address is a contract
	 *
	 * @dev It is unsafe to assume that an address for which this function returns
	 *      false is an externally-owned account (EOA) and not a contract.
	 *
	 * @dev 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
	 *
	 * @param addr address to check
	 * @return whether the target address is a contract
	 */
	function isContract(address addr) internal view returns (bool) {
		// a variable to load `extcodesize` to
		uint256 size = 0;

		// XXX Currently there is no better way to check if there is a contract in an address
		// than to check the size of the code at that address.
		// See https://ethereum.stackexchange.com/a/14016/36603 for more details about how this works.
		// TODO: Check this again before the Serenity release, because all addresses will be contracts.
		// solium-disable-next-line security/no-inline-assembly
		assembly {
			// retrieve the size of the code at address `addr`
			size := extcodesize(addr)
		}

		// positive size indicates a smart contract address
		return size > 0;
	}
}

File 8 of 10 : ECDSA.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 *
 * @dev Copy of the Zeppelin's library:
 *      https://github.com/OpenZeppelin/openzeppelin-contracts/blob/b0cf6fbb7a70f31527f36579ad644e1cf12fdf4e/contracts/utils/cryptography/ECDSA.sol
 */
library ECDSA {
	/**
	 * @dev Returns the address that signed a hashed message (`hash`) with
	 * `signature`. This address can then be used for verification purposes.
	 *
	 * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
	 * this function rejects them by requiring the `s` value to be in the lower
	 * half order, and the `v` value to be either 27 or 28.
	 *
	 * IMPORTANT: `hash` _must_ be the result of a hash operation for the
	 * verification to be secure: it is possible to craft signatures that
	 * recover to arbitrary addresses for non-hashed data. A safe way to ensure
	 * this is by receiving a hash of the original message (which may otherwise
	 * be too long), and then calling {toEthSignedMessageHash} on it.
	 *
	 * Documentation for signature generation:
	 * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
	 * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
	 */
	function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
		// Divide the signature in r, s and v variables
		bytes32 r;
		bytes32 s;
		uint8 v;

		// Check the signature length
		// - case 65: r,s,v signature (standard)
		// - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
		if (signature.length == 65) {
			// ecrecover takes the signature parameters, and the only way to get them
			// currently is to use assembly.
			assembly {
				r := mload(add(signature, 0x20))
				s := mload(add(signature, 0x40))
				v := byte(0, mload(add(signature, 0x60)))
			}
		}
		else if (signature.length == 64) {
			// ecrecover takes the signature parameters, and the only way to get them
			// currently is to use assembly.
			assembly {
				let vs := mload(add(signature, 0x40))
				r := mload(add(signature, 0x20))
				s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
				v := add(shr(255, vs), 27)
			}
		}
		else {
			revert("invalid signature length");
		}

		return recover(hash, v, r, s);
	}

	/**
	 * @dev Overload of {ECDSA-recover} that receives the `v`,
	 * `r` and `s` signature fields separately.
	 */
	function recover(
		bytes32 hash,
		uint8 v,
		bytes32 r,
		bytes32 s
	) internal pure returns (address) {
		// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
		// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
		// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
		// signatures from current libraries generate a unique signature with an s-value in the lower half order.
		//
		// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
		// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
		// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
		// these malleable signatures as well.
		require(
			uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0,
			"invalid signature 's' value"
		);
		require(v == 27 || v == 28, "invalid signature 'v' value");

		// If the signature is valid (and not malleable), return the signer address
		address signer = ecrecover(hash, v, r, s);
		require(signer != address(0), "invalid signature");

		return signer;
	}

	/**
	 * @dev Returns an Ethereum Signed Message, created from a `hash`. This
	 * produces hash corresponding to the one signed with the
	 * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
	 * JSON-RPC method as part of EIP-191.
	 *
	 * See {recover}.
	 */
	function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
		// 32 is the length in bytes of hash,
		// enforced by the type signature above
		return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
	}

	/**
	 * @dev Returns an Ethereum Signed Typed Data, created from a
	 * `domainSeparator` and a `structHash`. This produces hash corresponding
	 * to the one signed with the
	 * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
	 * JSON-RPC method as part of EIP-712.
	 *
	 * See {recover}.
	 */
	function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
		return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
	}
}

File 9 of 10 : AliERC20v2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

import "../interfaces/ERC1363Spec.sol";
import "../interfaces/EIP2612.sol";
import "../interfaces/EIP3009.sol";
import "../utils/AccessControl.sol";
import "../lib/AddressUtils.sol";
import "../lib/ECDSA.sol";

/**
 * @title Artificial Liquid Intelligence ERC20 Token (Alethea, ALI)
 *
 * @notice ALI is the native utility token of the Alethea AI Protocol.
 *      It serves as protocol currency, participates in iNFTs lifecycle,
 *      (locked when iNFT is created, released when iNFT is destroyed,
 *      consumed when iNFT is upgraded).
 *      ALI token powers up the governance protocol (Alethea DAO)
 *
 * @notice Token Summary:
 *      - Symbol: ALI
 *      - Name: Artificial Liquid Intelligence Token
 *      - Decimals: 18
 *      - Initial/maximum total supply: 10,000,000,000 ALI
 *      - Initial supply holder (initial holder) address: 0x0738F702D1a7364d356729Cb8845701885C487A1 (configurable)
 *      - Not mintable: new tokens cannot be created
 *      - Burnable: existing tokens may get destroyed, total supply may decrease
 *      - DAO Support: supports voting delegation
 *
 * @notice Features Summary:
 *      - Supports atomic allowance modification, resolves well-known ERC20 issue with approve (arXiv:1907.00903)
 *      - Voting delegation and delegation on behalf via EIP-712 (like in Compound CMP token) - gives ALI token
 *        powerful governance capabilities by allowing holders to form voting groups by electing delegates
 *      - Unlimited approval feature (like in 0x ZRX token) - saves gas for transfers on behalf
 *        by eliminating the need to update “unlimited” allowance value
 *      - ERC-1363 Payable Token - ERC721-like callback execution mechanism for transfers,
 *        transfers on behalf and approvals; allows creation of smart contracts capable of executing callbacks
 *        in response to transfer or approval in a single transaction
 *      - EIP-2612: permit - 712-signed approvals - improves user experience by allowing to use a token
 *        without having an ETH to pay gas fees
 *      - EIP-3009: Transfer With Authorization - improves user experience by allowing to use a token
 *        without having an ETH to pay gas fees
 *
 * @dev Even though smart contract has mint() function which is used to mint initial token supply,
 *      the function is disabled forever after smart contract deployment by revoking `TOKEN_CREATOR`
 *      permission from the deployer account
 *
 * @dev Token balances and total supply are effectively 192 bits long, meaning that maximum
 *      possible total supply smart contract is able to track is 2^192 (close to 10^40 tokens)
 *
 * @dev Smart contract doesn't use safe math. All arithmetic operations are overflow/underflow safe.
 *      Additionally, Solidity 0.8.7 enforces overflow/underflow safety.
 *
 * @dev Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903) - resolved
 *      Related events and functions are marked with "arXiv:1907.00903" tag:
 *        - event Transfer(address indexed _by, address indexed _from, address indexed _to, uint256 _value)
 *        - event Approve(address indexed _owner, address indexed _spender, uint256 _oldValue, uint256 _value)
 *        - function increaseAllowance(address _spender, uint256 _value) public returns (bool)
 *        - function decreaseAllowance(address _spender, uint256 _value) public returns (bool)
 *      See: https://arxiv.org/abs/1907.00903v1
 *           https://ieeexplore.ieee.org/document/8802438
 *      See: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
 *
 * @dev Reviewed
 *      ERC-20   - according to https://eips.ethereum.org/EIPS/eip-20
 *      ERC-1363 - according to https://eips.ethereum.org/EIPS/eip-1363
 *      EIP-2612 - according to https://eips.ethereum.org/EIPS/eip-2612
 *      EIP-3009 - according to https://eips.ethereum.org/EIPS/eip-3009
 *
 * @dev ERC20: contract has passed
 *      - OpenZeppelin ERC20 tests
 *        https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC20/ERC20.behavior.js
 *        https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC20/ERC20.test.js
 *      - Ref ERC1363 tests
 *        https://github.com/vittominacori/erc1363-payable-token/blob/master/test/token/ERC1363/ERC1363.behaviour.js
 *      - OpenZeppelin EIP2612 tests
 *        https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC20/extensions/draft-ERC20Permit.test.js
 *      - Coinbase EIP3009 tests
 *        https://github.com/CoinbaseStablecoin/eip-3009/blob/master/test/EIP3009.test.ts
 *      - Compound voting delegation tests
 *        https://github.com/compound-finance/compound-protocol/blob/master/tests/Governance/CompTest.js
 *        https://github.com/compound-finance/compound-protocol/blob/master/tests/Utils/EIP712.js
 *      - OpenZeppelin voting delegation tests
 *        https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC20/extensions/ERC20Votes.test.js
 *      See adopted copies of all the tests in the project test folder
 *
 * @dev Compound-like voting delegation functions', public getters', and events' names
 *      were changed for better code readability (Alethea Name <- Comp/Zeppelin name):
 *      - votingDelegates           <- delegates
 *      - votingPowerHistory        <- checkpoints
 *      - votingPowerHistoryLength  <- numCheckpoints
 *      - totalSupplyHistory        <- _totalSupplyCheckpoints (private)
 *      - usedNonces                <- nonces (note: nonces are random instead of sequential)
 *      - DelegateChanged (unchanged)
 *      - VotingPowerChanged        <- DelegateVotesChanged
 *      - votingPowerOf             <- getCurrentVotes
 *      - votingPowerAt             <- getPriorVotes
 *      - totalSupplyAt             <- getPriorTotalSupply
 *      - delegate (unchanged)
 *      - delegateWithAuthorization <- delegateBySig
 * @dev Compound-like voting delegation improved to allow the use of random nonces like in EIP-3009,
 *      instead of sequential; same `usedNonces` EIP-3009 mapping is used to track nonces
 *
 * @dev Reference implementations "used":
 *      - Atomic allowance:    https://github.com/OpenZeppelin/openzeppelin-contracts
 *      - Unlimited allowance: https://github.com/0xProject/protocol
 *      - Voting delegation:   https://github.com/compound-finance/compound-protocol
 *                             https://github.com/OpenZeppelin/openzeppelin-contracts
 *      - ERC-1363:            https://github.com/vittominacori/erc1363-payable-token
 *      - EIP-2612:            https://github.com/Uniswap/uniswap-v2-core
 *      - EIP-3009:            https://github.com/centrehq/centre-tokens
 *                             https://github.com/CoinbaseStablecoin/eip-3009
 *      - Meta transactions:   https://github.com/0xProject/protocol
 *
 * @dev Includes resolutions for ALI ERC20 Audit by Miguel Palhas, https://hackmd.io/@naps62/alierc20-audit
 */
abstract contract AliERC20v2Base is ERC1363, EIP2612, EIP3009, AccessControl {
	/**
	 * @dev Smart contract unique identifier, a random number
	 *
	 * @dev Should be regenerated each time smart contact source code is changed
	 *      and changes smart contract itself is to be redeployed
	 *
	 * @dev Generated using https://www.random.org/bytes/
	 */
	uint256 public constant TOKEN_UID = 0x8d4fb97da97378ef7d0ad259aec651f42bd22c200159282baa58486bb390286b;

	/**
	 * @notice Name of the token: Artificial Liquid Intelligence Token
	 *
	 * @notice ERC20 name of the token (long name)
	 *
	 * @dev ERC20 `function name() public view returns (string)`
	 *
	 * @dev Field is declared public: getter name() is created when compiled,
	 *      it returns the name of the token.
	 */
	string public constant name = "Artificial Liquid Intelligence Token";

	/**
	 * @notice Symbol of the token: ALI
	 *
	 * @notice ERC20 symbol of that token (short name)
	 *
	 * @dev ERC20 `function symbol() public view returns (string)`
	 *
	 * @dev Field is declared public: getter symbol() is created when compiled,
	 *      it returns the symbol of the token
	 */
	string public constant symbol = "ALI";

	/**
	 * @notice Decimals of the token: 18
	 *
	 * @dev ERC20 `function decimals() public view returns (uint8)`
	 *
	 * @dev Field is declared public: getter decimals() is created when compiled,
	 *      it returns the number of decimals used to get its user representation.
	 *      For example, if `decimals` equals `6`, a balance of `1,500,000` tokens should
	 *      be displayed to a user as `1,5` (`1,500,000 / 10 ** 6`).
	 *
	 * @dev NOTE: This information is only used for _display_ purposes: it in
	 *      no way affects any of the arithmetic of the contract, including balanceOf() and transfer().
	 */
	uint8 public constant decimals = 18;

	/**
	 * @notice Total supply of the token: initially 10,000,000,000,
	 *      with the potential to decline over time as some tokens may get burnt but not minted
	 *
	 * @dev ERC20 `function totalSupply() public view returns (uint256)`
	 *
	 * @dev Field is declared public: getter totalSupply() is created when compiled,
	 *      it returns the amount of tokens in existence.
	 */
	uint256 public override totalSupply; // is set to 10 billion * 10^18 in the constructor

	/**
	 * @dev A record of all the token balances
	 * @dev This mapping keeps record of all token owners:
	 *      owner => balance
	 */
	mapping(address => uint256) private tokenBalances;

	/**
	 * @notice A record of each account's voting delegate
	 *
	 * @dev Auxiliary data structure used to sum up an account's voting power
	 *
	 * @dev This mapping keeps record of all voting power delegations:
	 *      voting delegator (token owner) => voting delegate
	 */
	mapping(address => address) public votingDelegates;

	/**
	 * @notice Auxiliary structure to store key-value pair, used to store:
	 *      - voting power record (key: block.timestamp, value: voting power)
	 *      - total supply record (key: block.timestamp, value: total supply)
	 * @notice A voting power record binds voting power of a delegate to a particular
	 *      block when the voting power delegation change happened
	 *         k: block.number when delegation has changed; starting from
	 *            that block voting power value is in effect
	 *         v: cumulative voting power a delegate has obtained starting
	 *            from the block stored in blockNumber
	 * @notice Total supply record binds total token supply to a particular
	 *      block when total supply change happened (due to mint/burn operations)
	 */
	struct KV {
		/*
		 * @dev key, a block number
		 */
		uint64 k;

		/*
		 * @dev value, token balance or voting power
		 */
		uint192 v;
	}

	/**
	 * @notice A record of each account's voting power historical data
	 *
	 * @dev Primarily data structure to store voting power for each account.
	 *      Voting power sums up from the account's token balance and delegated
	 *      balances.
	 *
	 * @dev Stores current value and entire history of its changes.
	 *      The changes are stored as an array of checkpoints (key-value pairs).
	 *      Checkpoint is an auxiliary data structure containing voting
	 *      power (number of votes) and block number when the checkpoint is saved
	 *
	 * @dev Maps voting delegate => voting power record
	 */
	mapping(address => KV[]) public votingPowerHistory;

	/**
	 * @notice A record of total token supply historical data
	 *
	 * @dev Primarily data structure to store total token supply.
	 *
	 * @dev Stores current value and entire history of its changes.
	 *      The changes are stored as an array of checkpoints (key-value pairs).
	 *      Checkpoint is an auxiliary data structure containing total
	 *      token supply and block number when the checkpoint is saved
	 */
	KV[] public totalSupplyHistory;

	/**
	 * @dev A record of nonces for signing/validating signatures in EIP-2612 `permit`
	 *
	 * @dev Note: EIP2612 doesn't imply a possibility for nonce randomization like in EIP-3009
	 *
	 * @dev Maps delegate address => delegate nonce
	 */
	mapping(address => uint256) public override nonces;

	/**
	 * @dev A record of used nonces for EIP-3009 transactions
	 *
	 * @dev A record of used nonces for signing/validating signatures
	 *      in `delegateWithAuthorization` for every delegate
	 *
	 * @dev Maps authorizer address => nonce => true/false (used unused)
	 */
	mapping(address => mapping(bytes32 => bool)) private usedNonces;

	/**
	 * @notice A record of all the allowances to spend tokens on behalf
	 * @dev Maps token owner address to an address approved to spend
	 *      some tokens on behalf, maps approved address to that amount
	 * @dev owner => spender => value
	 */
	mapping(address => mapping(address => uint256)) private transferAllowances;

	/**
	 * @notice Enables ERC20 transfers of the tokens
	 *      (transfer by the token owner himself)
	 * @dev Feature FEATURE_TRANSFERS must be enabled in order for
	 *      `transfer()` function to succeed
	 */
	uint32 public constant FEATURE_TRANSFERS = 0x0000_0001;

	/**
	 * @notice Enables ERC20 transfers on behalf
	 *      (transfer by someone else on behalf of token owner)
	 * @dev Feature FEATURE_TRANSFERS_ON_BEHALF must be enabled in order for
	 *      `transferFrom()` function to succeed
	 * @dev Token owner must call `approve()` first to authorize
	 *      the transfer on behalf
	 */
	uint32 public constant FEATURE_TRANSFERS_ON_BEHALF = 0x0000_0002;

	/**
	 * @dev Defines if the default behavior of `transfer` and `transferFrom`
	 *      checks if the receiver smart contract supports ERC20 tokens
	 * @dev When feature FEATURE_UNSAFE_TRANSFERS is enabled the transfers do not
	 *      check if the receiver smart contract supports ERC20 tokens,
	 *      i.e. `transfer` and `transferFrom` behave like `unsafeTransferFrom`
	 * @dev When feature FEATURE_UNSAFE_TRANSFERS is disabled (default) the transfers
	 *      check if the receiver smart contract supports ERC20 tokens,
	 *      i.e. `transfer` and `transferFrom` behave like `transferFromAndCall`
	 */
	uint32 public constant FEATURE_UNSAFE_TRANSFERS = 0x0000_0004;

	/**
	 * @notice Enables token owners to burn their own tokens
	 *
	 * @dev Feature FEATURE_OWN_BURNS must be enabled in order for
	 *      `burn()` function to succeed when called by token owner
	 */
	uint32 public constant FEATURE_OWN_BURNS = 0x0000_0008;

	/**
	 * @notice Enables approved operators to burn tokens on behalf of their owners
	 *
	 * @dev Feature FEATURE_BURNS_ON_BEHALF must be enabled in order for
	 *      `burn()` function to succeed when called by approved operator
	 */
	uint32 public constant FEATURE_BURNS_ON_BEHALF = 0x0000_0010;

	/**
	 * @notice Enables delegators to elect delegates
	 * @dev Feature FEATURE_DELEGATIONS must be enabled in order for
	 *      `delegate()` function to succeed
	 */
	uint32 public constant FEATURE_DELEGATIONS = 0x0000_0020;

	/**
	 * @notice Enables delegators to elect delegates on behalf
	 *      (via an EIP712 signature)
	 * @dev Feature FEATURE_DELEGATIONS_ON_BEHALF must be enabled in order for
	 *      `delegateWithAuthorization()` function to succeed
	 */
	uint32 public constant FEATURE_DELEGATIONS_ON_BEHALF = 0x0000_0040;

	/**
	 * @notice Enables ERC-1363 transfers with callback
	 * @dev Feature FEATURE_ERC1363_TRANSFERS must be enabled in order for
	 *      ERC-1363 `transferFromAndCall` functions to succeed
	 */
	uint32 public constant FEATURE_ERC1363_TRANSFERS = 0x0000_0080;

	/**
	 * @notice Enables ERC-1363 approvals with callback
	 * @dev Feature FEATURE_ERC1363_APPROVALS must be enabled in order for
	 *      ERC-1363 `approveAndCall` functions to succeed
	 */
	uint32 public constant FEATURE_ERC1363_APPROVALS = 0x0000_0100;

	/**
	 * @notice Enables approvals on behalf (EIP2612 permits
	 *      via an EIP712 signature)
	 * @dev Feature FEATURE_EIP2612_PERMITS must be enabled in order for
	 *      `permit()` function to succeed
	 */
	uint32 public constant FEATURE_EIP2612_PERMITS = 0x0000_0200;

	/**
	 * @notice Enables meta transfers on behalf (EIP3009 transfers
	 *      via an EIP712 signature)
	 * @dev Feature FEATURE_EIP3009_TRANSFERS must be enabled in order for
	 *      `transferWithAuthorization()` function to succeed
	 */
	uint32 public constant FEATURE_EIP3009_TRANSFERS = 0x0000_0400;

	/**
	 * @notice Enables meta transfers on behalf (EIP3009 transfers
	 *      via an EIP712 signature)
	 * @dev Feature FEATURE_EIP3009_RECEPTIONS must be enabled in order for
	 *      `receiveWithAuthorization()` function to succeed
	 */
	uint32 public constant FEATURE_EIP3009_RECEPTIONS = 0x0000_0800;

	/**
	 * @notice Token creator is responsible for creating (minting)
	 *      tokens to an arbitrary address
	 * @dev Role ROLE_TOKEN_CREATOR allows minting tokens
	 *      (calling `mint` function)
	 */
	uint32 public constant ROLE_TOKEN_CREATOR = 0x0001_0000;

	/**
	 * @notice Token destroyer is responsible for destroying (burning)
	 *      tokens owned by an arbitrary address
	 * @dev Role ROLE_TOKEN_DESTROYER allows burning tokens
	 *      (calling `burn` function)
	 */
	uint32 public constant ROLE_TOKEN_DESTROYER = 0x0002_0000;

	/**
	 * @notice ERC20 receivers are allowed to receive tokens without ERC20 safety checks,
	 *      which may be useful to simplify tokens transfers into "legacy" smart contracts
	 * @dev When `FEATURE_UNSAFE_TRANSFERS` is not enabled addresses having
	 *      `ROLE_ERC20_RECEIVER` permission are allowed to receive tokens
	 *      via `transfer` and `transferFrom` functions in the same way they
	 *      would via `unsafeTransferFrom` function
	 * @dev When `FEATURE_UNSAFE_TRANSFERS` is enabled `ROLE_ERC20_RECEIVER` permission
	 *      doesn't affect the transfer behaviour since
	 *      `transfer` and `transferFrom` behave like `unsafeTransferFrom` for any receiver
	 * @dev ROLE_ERC20_RECEIVER is a shortening for ROLE_UNSAFE_ERC20_RECEIVER
	 */
	uint32 public constant ROLE_ERC20_RECEIVER = 0x0004_0000;

	/**
	 * @notice ERC20 senders are allowed to send tokens without ERC20 safety checks,
	 *      which may be useful to simplify tokens transfers into "legacy" smart contracts
	 * @dev When `FEATURE_UNSAFE_TRANSFERS` is not enabled senders having
	 *      `ROLE_ERC20_SENDER` permission are allowed to send tokens
	 *      via `transfer` and `transferFrom` functions in the same way they
	 *      would via `unsafeTransferFrom` function
	 * @dev When `FEATURE_UNSAFE_TRANSFERS` is enabled `ROLE_ERC20_SENDER` permission
	 *      doesn't affect the transfer behaviour since
	 *      `transfer` and `transferFrom` behave like `unsafeTransferFrom` for any receiver
	 * @dev ROLE_ERC20_SENDER is a shortening for ROLE_UNSAFE_ERC20_SENDER
	 */
	uint32 public constant ROLE_ERC20_SENDER = 0x0008_0000;

	/**
	 * @notice EIP-712 contract's domain typeHash,
	 *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
	 *
	 * @dev Note: we do not include version into the domain typehash/separator,
	 *      it is implied version is concatenated to the name field, like "AliERC20v2"
	 */
	// keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)")
	bytes32 public constant DOMAIN_TYPEHASH = 0x8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866;

	/**
	 * @notice EIP-712 contract's domain separator,
	 *      see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator
	 */
	bytes32 public immutable override DOMAIN_SEPARATOR;

	/**
	 * @notice EIP-712 delegation struct typeHash,
	 *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
	 */
	// keccak256("Delegation(address delegate,uint256 nonce,uint256 expiry)")
	bytes32 public constant DELEGATION_TYPEHASH = 0xff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a755;

	/**
	 * @notice EIP-712 permit (EIP-2612) struct typeHash,
	 *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
	 */
	// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")
	bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

	/**
	 * @notice EIP-712 TransferWithAuthorization (EIP-3009) struct typeHash,
	 *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
	 */
	// keccak256("TransferWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
	bytes32 public constant TRANSFER_WITH_AUTHORIZATION_TYPEHASH = 0x7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a2267;

	/**
	 * @notice EIP-712 ReceiveWithAuthorization (EIP-3009) struct typeHash,
	 *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
	 */
	// keccak256("ReceiveWithAuthorization(address from,address to,uint256 value,uint256 validAfter,uint256 validBefore,bytes32 nonce)")
	bytes32 public constant RECEIVE_WITH_AUTHORIZATION_TYPEHASH = 0xd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de8;

	/**
	 * @notice EIP-712 CancelAuthorization (EIP-3009) struct typeHash,
	 *      see https://eips.ethereum.org/EIPS/eip-712#rationale-for-typehash
	 */
	// keccak256("CancelAuthorization(address authorizer,bytes32 nonce)")
	bytes32 public constant CANCEL_AUTHORIZATION_TYPEHASH = 0x158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a1597429;

	/**
	 * @dev Fired in mint() function
	 *
	 * @param by an address which minted some tokens (transaction sender)
	 * @param to an address the tokens were minted to
	 * @param value an amount of tokens minted
	 */
	event Minted(address indexed by, address indexed to, uint256 value);

	/**
	 * @dev Fired in burn() function
	 *
	 * @param by an address which burned some tokens (transaction sender)
	 * @param from an address the tokens were burnt from
	 * @param value an amount of tokens burnt
	 */
	event Burnt(address indexed by, address indexed from, uint256 value);

	/**
	 * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903)
	 *
	 * @dev Similar to ERC20 Transfer event, but also logs an address which executed transfer
	 *
	 * @dev Fired in transfer(), transferFrom() and some other (non-ERC20) functions
	 *
	 * @param by an address which performed the transfer
	 * @param from an address tokens were consumed from
	 * @param to an address tokens were sent to
	 * @param value number of tokens transferred
	 */
	event Transfer(address indexed by, address indexed from, address indexed to, uint256 value);

	/**
	 * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903)
	 *
	 * @dev Similar to ERC20 Approve event, but also logs old approval value
	 *
	 * @dev Fired in approve(), increaseAllowance(), decreaseAllowance() functions,
	 *      may get fired in transfer functions
	 *
	 * @param owner an address which granted a permission to transfer
	 *      tokens on its behalf
	 * @param spender an address which received a permission to transfer
	 *      tokens on behalf of the owner `_owner`
	 * @param oldValue previously granted amount of tokens to transfer on behalf
	 * @param value new granted amount of tokens to transfer on behalf
	 */
	event Approval(address indexed owner, address indexed spender, uint256 oldValue, uint256 value);

	/**
	 * @dev Notifies that a key-value pair in `votingDelegates` mapping has changed,
	 *      i.e. a delegator address has changed its delegate address
	 *
	 * @param source delegator address, a token owner, effectively transaction sender (`by`)
	 * @param from old delegate, an address which delegate right is revoked
	 * @param to new delegate, an address which received the voting power
	 */
	event DelegateChanged(address indexed source, address indexed from, address indexed to);

	/**
	 * @dev Notifies that a key-value pair in `votingPowerHistory` mapping has changed,
	 *      i.e. a delegate's voting power has changed.
	 *
	 * @param by an address which executed delegate, mint, burn, or transfer operation
	 *      which had led to delegate voting power change
	 * @param target delegate whose voting power has changed
	 * @param fromVal previous number of votes delegate had
	 * @param toVal new number of votes delegate has
	 */
	event VotingPowerChanged(address indexed by, address indexed target, uint256 fromVal, uint256 toVal);

	/**
	 * @dev Deploys the token smart contract,
	 *      assigns initial token supply to the address specified
	 *
	 * @param _initialHolder owner of the initial token supply
	 * @param _initialSupply initial token supply value
	 */
	constructor(address _initialHolder, uint256 _initialSupply) {
		// verify initial holder address non-zero (is set) if there is an initial supply to mint
		require(_initialSupply == 0 || _initialHolder != address(0), "_initialHolder not set (zero address)");

		// build the EIP-712 contract domain separator, see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator
		// note: we specify contract version in its name
		DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("AliERC20v2")), block.chainid, address(this)));

		// if there is an initial supply to mint
		if(_initialSupply != 0) {
			// mint the initial supply
			mint(_initialHolder, _initialSupply);
		}
	}

	/**
	 * @inheritdoc ERC165
	 */
	function supportsInterface(bytes4 interfaceId) public pure override returns (bool) {
		// reconstruct from current interface(s) and super interface(s) (if any)
		return interfaceId == type(ERC165).interfaceId
		    || interfaceId == type(ERC20).interfaceId
		    || interfaceId == type(ERC1363).interfaceId
		    || interfaceId == type(EIP2612).interfaceId
		    || interfaceId == type(EIP3009).interfaceId;
	}

	// ===== Start: ERC-1363 functions =====

	/**
	 * @notice Transfers some tokens and then executes `onTransferReceived` callback on the receiver
	 *
	 * @inheritdoc ERC1363
	 *
	 * @dev Called by token owner (an address which has a
	 *      positive token balance tracked by this smart contract)
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * EOA or smart contract which doesn't support ERC1363Receiver interface
	 * @dev Returns true on success, throws otherwise
	 *
	 * @param _to an address to transfer tokens to,
	 *      must be a smart contract, implementing ERC1363Receiver
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return true unless throwing
	 */
	function transferAndCall(address _to, uint256 _value) public override returns (bool) {
		// delegate to `transferFromAndCall` passing `msg.sender` as `_from`
		return transferFromAndCall(msg.sender, _to, _value);
	}

	/**
	 * @notice Transfers some tokens and then executes `onTransferReceived` callback on the receiver
	 *
	 * @inheritdoc ERC1363
	 *
	 * @dev Called by token owner (an address which has a
	 *      positive token balance tracked by this smart contract)
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * EOA or smart contract which doesn't support ERC1363Receiver interface
	 * @dev Returns true on success, throws otherwise
	 *
	 * @param _to an address to transfer tokens to,
	 *      must be a smart contract, implementing ERC1363Receiver
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @param _data [optional] additional data with no specified format,
	 *      sent in onTransferReceived call to `_to`
	 * @return true unless throwing
	 */
	function transferAndCall(address _to, uint256 _value, bytes memory _data) public override returns (bool) {
		// delegate to `transferFromAndCall` passing `msg.sender` as `_from`
		return transferFromAndCall(msg.sender, _to, _value, _data);
	}

	/**
	 * @notice Transfers some tokens on behalf of address `_from' (token owner)
	 *      to some other address `_to` and then executes `onTransferReceived` callback on the receiver
	 *
	 * @inheritdoc ERC1363
	 *
	 * @dev Called by token owner on his own or approved address,
	 *      an address approved earlier by token owner to
	 *      transfer some amount of tokens on its behalf
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * EOA or smart contract which doesn't support ERC1363Receiver interface
	 * @dev Returns true on success, throws otherwise
	 *
	 * @param _from token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to an address to transfer tokens to,
	 *      must be a smart contract, implementing ERC1363Receiver
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return true unless throwing
	 */
	function transferFromAndCall(address _from, address _to, uint256 _value) public override returns (bool) {
		// delegate to `transferFromAndCall` passing empty data param
		return transferFromAndCall(_from, _to, _value, "");
	}

	/**
	 * @notice Transfers some tokens on behalf of address `_from' (token owner)
	 *      to some other address `_to` and then executes a `onTransferReceived` callback on the receiver
	 *
	 * @inheritdoc ERC1363
	 *
	 * @dev Called by token owner on his own or approved address,
	 *      an address approved earlier by token owner to
	 *      transfer some amount of tokens on its behalf
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * EOA or smart contract which doesn't support ERC1363Receiver interface
	 * @dev Returns true on success, throws otherwise
	 *
	 * @param _from token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to an address to transfer tokens to,
	 *      must be a smart contract, implementing ERC1363Receiver
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @param _data [optional] additional data with no specified format,
	 *      sent in onTransferReceived call to `_to`
	 * @return true unless throwing
	 */
	function transferFromAndCall(address _from, address _to, uint256 _value, bytes memory _data) public override returns (bool) {
		// ensure ERC-1363 transfers are enabled
		require(isFeatureEnabled(FEATURE_ERC1363_TRANSFERS), "ERC1363 transfers are disabled");

		// first delegate call to `unsafeTransferFrom` to perform the unsafe token(s) transfer
		unsafeTransferFrom(_from, _to, _value);

		// after the successful transfer - check if receiver supports
		// ERC1363Receiver and execute a callback handler `onTransferReceived`,
		// reverting whole transaction on any error
		_notifyTransferred(_from, _to, _value, _data, false);

		// function throws on any error, so if we're here - it means operation successful, just return true
		return true;
	}

	/**
	 * @notice Approves address called `_spender` to transfer some amount
	 *      of tokens on behalf of the owner, then executes a `onApprovalReceived` callback on `_spender`
	 *
	 * @inheritdoc ERC1363
	 *
	 * @dev Caller must not necessarily own any tokens to grant the permission
	 *
	 * @dev Throws if `_spender` is an EOA or a smart contract which doesn't support ERC1363Spender interface
	 *
	 * @param _spender an address approved by the caller (token owner)
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens spender `_spender` is allowed to
	 *      transfer on behalf of the token owner
	 * @return success true on success, throws otherwise
	 */
	function approveAndCall(address _spender, uint256 _value) public override returns (bool) {
		// delegate to `approveAndCall` passing empty data
		return approveAndCall(_spender, _value, "");
	}

	/**
	 * @notice Approves address called `_spender` to transfer some amount
	 *      of tokens on behalf of the owner, then executes a callback on `_spender`
	 *
	 * @inheritdoc ERC1363
	 *
	 * @dev Caller must not necessarily own any tokens to grant the permission
	 *
	 * @param _spender an address approved by the caller (token owner)
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens spender `_spender` is allowed to
	 *      transfer on behalf of the token owner
	 * @param _data [optional] additional data with no specified format,
	 *      sent in onApprovalReceived call to `_spender`
	 * @return success true on success, throws otherwise
	 */
	function approveAndCall(address _spender, uint256 _value, bytes memory _data) public override returns (bool) {
		// ensure ERC-1363 approvals are enabled
		require(isFeatureEnabled(FEATURE_ERC1363_APPROVALS), "ERC1363 approvals are disabled");

		// execute regular ERC20 approve - delegate to `approve`
		approve(_spender, _value);

		// after the successful approve - check if receiver supports
		// ERC1363Spender and execute a callback handler `onApprovalReceived`,
		// reverting whole transaction on any error
		_notifyApproved(_spender, _value, _data);

		// function throws on any error, so if we're here - it means operation successful, just return true
		return true;
	}

	/**
	 * @dev Auxiliary function to invoke `onTransferReceived` on a target address
	 *      The call is not executed if the target address is not a contract; in such
	 *      a case function throws if `allowEoa` is set to false, succeeds if it's true
	 *
	 * @dev Throws on any error; returns silently on success
	 *
	 * @param _from representing the previous owner of the given token value
	 * @param _to target address that will receive the tokens
	 * @param _value the amount mount of tokens to be transferred
	 * @param _data [optional] data to send along with the call
	 * @param allowEoa indicates if function should fail if `_to` is an EOA
	 */
	function _notifyTransferred(address _from, address _to, uint256 _value, bytes memory _data, bool allowEoa) private {
		// if recipient `_to` is EOA
		if (!AddressUtils.isContract(_to)) {
			// ensure EOA recipient is allowed
			require(allowEoa, "EOA recipient");

			// exit if successful
			return;
		}

		// otherwise - if `_to` is a contract - execute onTransferReceived
		bytes4 response = ERC1363Receiver(_to).onTransferReceived(msg.sender, _from, _value, _data);

		// expected response is ERC1363Receiver(_to).onTransferReceived.selector
		// bytes4(keccak256("onTransferReceived(address,address,uint256,bytes)"))
		require(response == ERC1363Receiver(_to).onTransferReceived.selector, "invalid onTransferReceived response");
	}

	/**
	 * @dev Auxiliary function to invoke `onApprovalReceived` on a target address
	 *      The call is not executed if the target address is not a contract; in such
	 *      a case function throws if `allowEoa` is set to false, succeeds if it's true
	 *
	 * @dev Throws on any error; returns silently on success
	 *
	 * @param _spender the address which will spend the funds
	 * @param _value the amount of tokens to be spent
	 * @param _data [optional] data to send along with the call
	 */
	function _notifyApproved(address _spender, uint256 _value, bytes memory _data) private {
		// ensure recipient is not EOA
		require(AddressUtils.isContract(_spender), "EOA spender");

		// otherwise - if `_to` is a contract - execute onApprovalReceived
		bytes4 response = ERC1363Spender(_spender).onApprovalReceived(msg.sender, _value, _data);

		// expected response is ERC1363Spender(_to).onApprovalReceived.selector
		// bytes4(keccak256("onApprovalReceived(address,uint256,bytes)"))
		require(response == ERC1363Spender(_spender).onApprovalReceived.selector, "invalid onApprovalReceived response");
	}
	// ===== End: ERC-1363 functions =====

	// ===== Start: ERC20 functions =====

	/**
	 * @notice Gets the balance of a particular address
	 *
	 * @inheritdoc ERC20
	 *
	 * @param _owner the address to query the the balance for
	 * @return balance an amount of tokens owned by the address specified
	 */
	function balanceOf(address _owner) public view override returns (uint256 balance) {
		// read the balance and return
		return tokenBalances[_owner];
	}

	/**
	 * @notice Transfers some tokens to an external address or a smart contract
	 *
	 * @inheritdoc ERC20
	 *
	 * @dev Called by token owner (an address which has a
	 *      positive token balance tracked by this smart contract)
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * self address or
	 *          * smart contract which doesn't support ERC20
	 *
	 * @param _to an address to transfer tokens to,
	 *      must be either an external address or a smart contract,
	 *      compliant with the ERC20 standard
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return success true on success, throws otherwise
	 */
	function transfer(address _to, uint256 _value) public override returns (bool success) {
		// just delegate call to `transferFrom`,
		// `FEATURE_TRANSFERS` is verified inside it
		return transferFrom(msg.sender, _to, _value);
	}

	/**
	 * @notice Transfers some tokens on behalf of address `_from' (token owner)
	 *      to some other address `_to`
	 *
	 * @inheritdoc ERC20
	 *
	 * @dev Called by token owner on his own or approved address,
	 *      an address approved earlier by token owner to
	 *      transfer some amount of tokens on its behalf
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * smart contract which doesn't support ERC20
	 *
	 * @param _from token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to an address to transfer tokens to,
	 *      must be either an external address or a smart contract,
	 *      compliant with the ERC20 standard
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @return success true on success, throws otherwise
	 */
	function transferFrom(address _from, address _to, uint256 _value) public override returns (bool success) {
		// depending on `FEATURE_UNSAFE_TRANSFERS` we execute either safe (default)
		// or unsafe transfer
		// if `FEATURE_UNSAFE_TRANSFERS` is enabled
		// or receiver has `ROLE_ERC20_RECEIVER` permission
		// or sender has `ROLE_ERC20_SENDER` permission
		if(isFeatureEnabled(FEATURE_UNSAFE_TRANSFERS)
			|| isOperatorInRole(_to, ROLE_ERC20_RECEIVER)
			|| isSenderInRole(ROLE_ERC20_SENDER)) {
			// we execute unsafe transfer - delegate call to `unsafeTransferFrom`,
			// `FEATURE_TRANSFERS` is verified inside it
			unsafeTransferFrom(_from, _to, _value);
		}
		// otherwise - if `FEATURE_UNSAFE_TRANSFERS` is disabled
		// and receiver doesn't have `ROLE_ERC20_RECEIVER` permission
		else {
			// we execute safe transfer - delegate call to `safeTransferFrom`, passing empty `_data`,
			// `FEATURE_TRANSFERS` is verified inside it
			safeTransferFrom(_from, _to, _value, "");
		}

		// both `unsafeTransferFrom` and `safeTransferFrom` throw on any error, so
		// if we're here - it means operation successful,
		// just return true
		return true;
	}

	/**
	 * @notice Transfers some tokens on behalf of address `_from' (token owner)
	 *      to some other address `_to` and then executes `onTransferReceived` callback
	 *      on the receiver if it is a smart contract (not an EOA)
	 *
	 * @dev Called by token owner on his own or approved address,
	 *      an address approved earlier by token owner to
	 *      transfer some amount of tokens on its behalf
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 *          * smart contract which doesn't support ERC1363Receiver interface
	 * @dev Returns true on success, throws otherwise
	 *
	 * @param _from token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to an address to transfer tokens to,
	 *      must be either an external address or a smart contract,
	 *      implementing ERC1363Receiver
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 * @param _data [optional] additional data with no specified format,
	 *      sent in onTransferReceived call to `_to` in case if its a smart contract
	 * @return true unless throwing
	 */
	function safeTransferFrom(address _from, address _to, uint256 _value, bytes memory _data) public returns (bool) {
		// first delegate call to `unsafeTransferFrom` to perform the unsafe token(s) transfer
		unsafeTransferFrom(_from, _to, _value);

		// after the successful transfer - check if receiver supports
		// ERC1363Receiver and execute a callback handler `onTransferReceived`,
		// reverting whole transaction on any error
		_notifyTransferred(_from, _to, _value, _data, true);

		// function throws on any error, so if we're here - it means operation successful, just return true
		return true;
	}

	/**
	 * @notice Transfers some tokens on behalf of address `_from' (token owner)
	 *      to some other address `_to`
	 *
	 * @dev In contrast to `transferFromAndCall` doesn't check recipient
	 *      smart contract to support ERC20 tokens (ERC1363Receiver)
	 * @dev Designed to be used by developers when the receiver is known
	 *      to support ERC20 tokens but doesn't implement ERC1363Receiver interface
	 * @dev Called by token owner on his own or approved address,
	 *      an address approved earlier by token owner to
	 *      transfer some amount of tokens on its behalf
	 * @dev Throws on any error like
	 *      * insufficient token balance or
	 *      * incorrect `_to` address:
	 *          * zero address or
	 *          * same as `_from` address (self transfer)
	 * @dev Returns silently on success, throws otherwise
	 *
	 * @param _from token sender, token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to token receiver, an address to transfer tokens to
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 */
	function unsafeTransferFrom(address _from, address _to, uint256 _value) public {
		// make an internal transferFrom - delegate to `__transferFrom`
		__transferFrom(msg.sender, _from, _to, _value);
	}

	/**
	 * @dev Powers the meta transactions for `unsafeTransferFrom` - EIP-3009 `transferWithAuthorization`
	 *      and `receiveWithAuthorization`
	 *
	 * @dev See `unsafeTransferFrom` and `transferFrom` soldoc for details
	 *
	 * @param _by an address executing the transfer, it can be token owner itself,
	 *      or an operator previously approved with `approve()`
	 * @param _from token sender, token owner which approved caller (transaction sender)
	 *      to transfer `_value` of tokens on its behalf
	 * @param _to token receiver, an address to transfer tokens to
	 * @param _value amount of tokens to be transferred,, zero
	 *      value is allowed
	 */
	function __transferFrom(address _by, address _from, address _to, uint256 _value) private {
		// if `_from` is equal to sender, require transfers feature to be enabled
		// otherwise require transfers on behalf feature to be enabled
		require(_from == _by && isFeatureEnabled(FEATURE_TRANSFERS)
		     || _from != _by && isFeatureEnabled(FEATURE_TRANSFERS_ON_BEHALF),
		        _from == _by? "transfers are disabled": "transfers on behalf are disabled");

		// non-zero source address check - Zeppelin
		// obviously, zero source address is a client mistake
		// it's not part of ERC20 standard but it's reasonable to fail fast
		// since for zero value transfer transaction succeeds otherwise
		require(_from != address(0), "transfer from the zero address");

		// non-zero recipient address check
		require(_to != address(0), "transfer to the zero address");

		// sender and recipient cannot be the same
		require(_from != _to, "sender and recipient are the same (_from = _to)");

		// sending tokens to the token smart contract itself is a client mistake
		require(_to != address(this), "invalid recipient (transfer to the token smart contract itself)");

		// according to ERC-20 Token Standard, https://eips.ethereum.org/EIPS/eip-20
		// "Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event."
		if(_value == 0) {
			// emit an ERC20 transfer event
			emit Transfer(_from, _to, _value);

			// don't forget to return - we're done
			return;
		}

		// no need to make arithmetic overflow check on the _value - by design of mint()

		// in case of transfer on behalf
		if(_from != _by) {
			// read allowance value - the amount of tokens allowed to transfer - into the stack
			uint256 _allowance = transferAllowances[_from][_by];

			// verify sender has an allowance to transfer amount of tokens requested
			require(_allowance >= _value, "transfer amount exceeds allowance");

			// we treat max uint256 allowance value as an "unlimited" and
			// do not decrease allowance when it is set to "unlimited" value
			if(_allowance < type(uint256).max) {
				// update allowance value on the stack
				_allowance -= _value;

				// update the allowance value in storage
				transferAllowances[_from][_by] = _allowance;

				// emit an improved atomic approve event
				emit Approval(_from, _by, _allowance + _value, _allowance);

				// emit an ERC20 approval event to reflect the decrease
				emit Approval(_from, _by, _allowance);
			}
		}

		// verify sender has enough tokens to transfer on behalf
		require(tokenBalances[_from] >= _value, "transfer amount exceeds balance");

		// perform the transfer:
		// decrease token owner (sender) balance
		tokenBalances[_from] -= _value;

		// increase `_to` address (receiver) balance
		tokenBalances[_to] += _value;

		// move voting power associated with the tokens transferred
		__moveVotingPower(_by, votingDelegates[_from], votingDelegates[_to], _value);

		// emit an improved transfer event (arXiv:1907.00903)
		emit Transfer(_by, _from, _to, _value);

		// emit an ERC20 transfer event
		emit Transfer(_from, _to, _value);
	}

	/**
	 * @notice Approves address called `_spender` to transfer some amount
	 *      of tokens on behalf of the owner (transaction sender)
	 *
	 * @inheritdoc ERC20
	 *
	 * @dev Transaction sender must not necessarily own any tokens to grant the permission
	 *
	 * @param _spender an address approved by the caller (token owner)
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens spender `_spender` is allowed to
	 *      transfer on behalf of the token owner
	 * @return success true on success, throws otherwise
	 */
	function approve(address _spender, uint256 _value) public override returns (bool success) {
		// make an internal approve - delegate to `__approve`
		__approve(msg.sender, _spender, _value);

		// operation successful, return true
		return true;
	}

	/**
	 * @dev Powers the meta transaction for `approve` - EIP-2612 `permit`
	 *
	 * @dev Approves address called `_spender` to transfer some amount
	 *      of tokens on behalf of the `_owner`
	 *
	 * @dev `_owner` must not necessarily own any tokens to grant the permission
	 * @dev Throws if `_spender` is a zero address
	 *
	 * @param _owner owner of the tokens to set approval on behalf of
	 * @param _spender an address approved by the token owner
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens spender `_spender` is allowed to
	 *      transfer on behalf of the token owner
	 */
	function __approve(address _owner, address _spender, uint256 _value) private {
		// non-zero spender address check - Zeppelin
		// obviously, zero spender address is a client mistake
		// it's not part of ERC20 standard but it's reasonable to fail fast
		require(_spender != address(0), "approve to the zero address");

		// read old approval value to emmit an improved event (arXiv:1907.00903)
		uint256 _oldValue = transferAllowances[_owner][_spender];

		// perform an operation: write value requested into the storage
		transferAllowances[_owner][_spender] = _value;

		// emit an improved atomic approve event (arXiv:1907.00903)
		emit Approval(_owner, _spender, _oldValue, _value);

		// emit an ERC20 approval event
		emit Approval(_owner, _spender, _value);
	}

	/**
	 * @notice Returns the amount which _spender is still allowed to withdraw from _owner.
	 *
	 * @inheritdoc ERC20
	 *
	 * @dev A function to check an amount of tokens owner approved
	 *      to transfer on its behalf by some other address called "spender"
	 *
	 * @param _owner an address which approves transferring some tokens on its behalf
	 * @param _spender an address approved to transfer some tokens on behalf
	 * @return remaining an amount of tokens approved address `_spender` can transfer on behalf
	 *      of token owner `_owner`
	 */
	function allowance(address _owner, address _spender) public view override returns (uint256 remaining) {
		// read the value from storage and return
		return transferAllowances[_owner][_spender];
	}

	// ===== End: ERC20 functions =====

	// ===== Start: Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903) =====

	/**
	 * @notice Increases the allowance granted to `spender` by the transaction sender
	 *
	 * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903)
	 *
	 * @dev Throws if value to increase by is zero or too big and causes arithmetic overflow
	 *
	 * @param _spender an address approved by the caller (token owner)
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens to increase by
	 * @return success true on success, throws otherwise
	 */
	function increaseAllowance(address _spender, uint256 _value) public returns (bool) {
		// read current allowance value
		uint256 currentVal = transferAllowances[msg.sender][_spender];

		// non-zero _value and arithmetic overflow check on the allowance
		unchecked {
			// put operation into unchecked block to display user-friendly overflow error message for Solidity 0.8+
			require(currentVal + _value > currentVal, "zero value approval increase or arithmetic overflow");
		}

		// delegate call to `approve` with the new value
		return approve(_spender, currentVal + _value);
	}

	/**
	 * @notice Decreases the allowance granted to `spender` by the caller.
	 *
	 * @dev Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903)
	 *
	 * @dev Throws if value to decrease by is zero or is greater than currently allowed value
	 *
	 * @param _spender an address approved by the caller (token owner)
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens to decrease by
	 * @return success true on success, throws otherwise
	 */
	function decreaseAllowance(address _spender, uint256 _value) public returns (bool) {
		// read current allowance value
		uint256 currentVal = transferAllowances[msg.sender][_spender];

		// non-zero _value check on the allowance
		require(_value > 0, "zero value approval decrease");

		// verify allowance decrease doesn't underflow
		require(currentVal >= _value, "ERC20: decreased allowance below zero");

		// delegate call to `approve` with the new value
		return approve(_spender, currentVal - _value);
	}

	// ===== End: Resolution for the Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903) =====

	// ===== Start: Minting/burning extension =====

	/**
	 * @dev Mints (creates) some tokens to address specified
	 * @dev The value specified is treated as is without taking
	 *      into account what `decimals` value is
	 *
	 * @dev Requires executor to have `ROLE_TOKEN_CREATOR` permission
	 *
	 * @dev Throws on overflow, if totalSupply + _value doesn't fit into uint256
	 *
	 * @param _to an address to mint tokens to
	 * @param _value an amount of tokens to mint (create)
	 */
	function mint(address _to, uint256 _value) public {
		// check if caller has sufficient permissions to mint tokens
		require(isSenderInRole(ROLE_TOKEN_CREATOR), "access denied");

		// non-zero recipient address check
		require(_to != address(0), "zero address");

		// non-zero _value and arithmetic overflow check on the total supply
		// this check automatically secures arithmetic overflow on the individual balance
		unchecked {
			// put operation into unchecked block to display user-friendly overflow error message for Solidity 0.8+
			require(totalSupply + _value > totalSupply, "zero value or arithmetic overflow");
		}

		// uint192 overflow check (required by voting delegation)
		require(totalSupply + _value <= type(uint192).max, "total supply overflow (uint192)");

		// perform mint:
		// increase total amount of tokens value
		totalSupply += _value;

		// increase `_to` address balance
		tokenBalances[_to] += _value;

		// update total token supply history
		__updateHistory(totalSupplyHistory, add, _value);

		// create voting power associated with the tokens minted
		__moveVotingPower(msg.sender, address(0), votingDelegates[_to], _value);

		// fire a minted event
		emit Minted(msg.sender, _to, _value);

		// emit an improved transfer event (arXiv:1907.00903)
		emit Transfer(msg.sender, address(0), _to, _value);

		// fire ERC20 compliant transfer event
		emit Transfer(address(0), _to, _value);
	}

	/**
	 * @dev Burns (destroys) some tokens from the address specified
	 *
	 * @dev The value specified is treated as is without taking
	 *      into account what `decimals` value is
	 *
	 * @dev Requires executor to have `ROLE_TOKEN_DESTROYER` permission
	 *      or FEATURE_OWN_BURNS/FEATURE_BURNS_ON_BEHALF features to be enabled
	 *
	 * @dev Can be disabled by the contract creator forever by disabling
	 *      FEATURE_OWN_BURNS/FEATURE_BURNS_ON_BEHALF features and then revoking
	 *      its own roles to burn tokens and to enable burning features
	 *
	 * @param _from an address to burn some tokens from
	 * @param _value an amount of tokens to burn (destroy)
	 */
	function burn(address _from, uint256 _value) public {
		// check if caller has sufficient permissions to burn tokens
		// and if not - check for possibility to burn own tokens or to burn on behalf
		if(!isSenderInRole(ROLE_TOKEN_DESTROYER)) {
			// if `_from` is equal to sender, require own burns feature to be enabled
			// otherwise require burns on behalf feature to be enabled
			require(_from == msg.sender && isFeatureEnabled(FEATURE_OWN_BURNS)
			     || _from != msg.sender && isFeatureEnabled(FEATURE_BURNS_ON_BEHALF),
			        _from == msg.sender? "burns are disabled": "burns on behalf are disabled");

			// in case of burn on behalf
			if(_from != msg.sender) {
				// read allowance value - the amount of tokens allowed to be burnt - into the stack
				uint256 _allowance = transferAllowances[_from][msg.sender];

				// verify sender has an allowance to burn amount of tokens requested
				require(_allowance >= _value, "burn amount exceeds allowance");

				// we treat max uint256 allowance value as an "unlimited" and
				// do not decrease allowance when it is set to "unlimited" value
				if(_allowance < type(uint256).max) {
					// update allowance value on the stack
					_allowance -= _value;

					// update the allowance value in storage
					transferAllowances[_from][msg.sender] = _allowance;

					// emit an improved atomic approve event (arXiv:1907.00903)
					emit Approval(msg.sender, _from, _allowance + _value, _allowance);

					// emit an ERC20 approval event to reflect the decrease
					emit Approval(_from, msg.sender, _allowance);
				}
			}
		}

		// at this point we know that either sender is ROLE_TOKEN_DESTROYER or
		// we burn own tokens or on behalf (in latest case we already checked and updated allowances)
		// we have left to execute balance checks and burning logic itself

		// non-zero burn value check
		require(_value != 0, "zero value burn");

		// non-zero source address check - Zeppelin
		require(_from != address(0), "burn from the zero address");

		// verify `_from` address has enough tokens to destroy
		// (basically this is a arithmetic overflow check)
		require(tokenBalances[_from] >= _value, "burn amount exceeds balance");

		// perform burn:
		// decrease `_from` address balance
		tokenBalances[_from] -= _value;

		// decrease total amount of tokens value
		totalSupply -= _value;

		// update total token supply history
		__updateHistory(totalSupplyHistory, sub, _value);

		// destroy voting power associated with the tokens burnt
		__moveVotingPower(msg.sender, votingDelegates[_from], address(0), _value);

		// fire a burnt event
		emit Burnt(msg.sender, _from, _value);

		// emit an improved transfer event (arXiv:1907.00903)
		emit Transfer(msg.sender, _from, address(0), _value);

		// fire ERC20 compliant transfer event
		emit Transfer(_from, address(0), _value);
	}

	// ===== End: Minting/burning extension =====

	// ===== Start: EIP-2612 functions =====

	/**
	 * @inheritdoc EIP2612
	 *
	 * @dev Executes approve(_spender, _value) on behalf of the owner who EIP-712
	 *      signed the transaction, i.e. as if transaction sender is the EIP712 signer
	 *
	 * @dev Sets the `_value` as the allowance of `_spender` over `_owner` tokens,
	 *      given `_owner` EIP-712 signed approval
	 *
	 * @dev Inherits the Multiple Withdrawal Attack on ERC20 Tokens (arXiv:1907.00903)
	 *      vulnerability in the same way as ERC20 `approve`, use standard ERC20 workaround
	 *      if this might become an issue:
	 *      https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit
	 *
	 * @dev Emits `Approval` event(s) in the same way as `approve` does
	 *
	 * @dev Requires:
	 *     - `_spender` to be non-zero address
	 *     - `_exp` to be a timestamp in the future
	 *     - `v`, `r` and `s` to be a valid `secp256k1` signature from `_owner`
	 *        over the EIP712-formatted function arguments.
	 *     - the signature to use `_owner` current nonce (see `nonces`).
	 *
	 * @dev For more information on the signature format, see the
	 *      https://eips.ethereum.org/EIPS/eip-2612#specification
	 *
	 * @param _owner owner of the tokens to set approval on behalf of,
	 *      an address which signed the EIP-712 message
	 * @param _spender an address approved by the token owner
	 *      to spend some tokens on its behalf
	 * @param _value an amount of tokens spender `_spender` is allowed to
	 *      transfer on behalf of the token owner
	 * @param _exp signature expiration time (unix timestamp)
	 * @param v the recovery byte of the signature
	 * @param r half of the ECDSA signature pair
	 * @param s half of the ECDSA signature pair
	 */
	function permit(address _owner, address _spender, uint256 _value, uint256 _exp, uint8 v, bytes32 r, bytes32 s) public override {
		// verify permits are enabled
		require(isFeatureEnabled(FEATURE_EIP2612_PERMITS), "EIP2612 permits are disabled");

		// derive signer of the EIP712 Permit message, and
		// update the nonce for that particular signer to avoid replay attack!!! --------->>> ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
		address signer = __deriveSigner(abi.encode(PERMIT_TYPEHASH, _owner, _spender, _value, nonces[_owner]++, _exp), v, r, s);

		// perform message integrity and security validations
		require(signer == _owner, "invalid signature");
		require(block.timestamp < _exp, "signature expired");

		// delegate call to `__approve` - execute the logic required
		__approve(_owner, _spender, _value);
	}

	// ===== End: EIP-2612 functions =====

	// ===== Start: EIP-3009 functions =====

	/**
	 * @inheritdoc EIP3009
	 *
	 * @notice Checks if specified nonce was already used
	 *
	 * @dev Nonces are expected to be client-side randomly generated 32-byte values
	 *      unique to the authorizer's address
	 *
	 * @dev Alias for usedNonces(authorizer, nonce)
	 *
	 * @param _authorizer an address to check nonce for
	 * @param _nonce a nonce to check
	 * @return true if the nonce was used, false otherwise
	 */
	function authorizationState(address _authorizer, bytes32 _nonce) public override view returns (bool) {
		// simply return the value from the mapping
		return usedNonces[_authorizer][_nonce];
	}

	/**
	 * @inheritdoc EIP3009
	 *
	 * @notice Execute a transfer with a signed authorization
	 *
	 * @param _from token sender and transaction authorizer
	 * @param _to token receiver
	 * @param _value amount to be transferred
	 * @param _validAfter signature valid after time (unix timestamp)
	 * @param _validBefore signature valid before time (unix timestamp)
	 * @param _nonce unique random nonce
	 * @param v the recovery byte of the signature
	 * @param r half of the ECDSA signature pair
	 * @param s half of the ECDSA signature pair
	 */
	function transferWithAuthorization(
		address _from,
		address _to,
		uint256 _value,
		uint256 _validAfter,
		uint256 _validBefore,
		bytes32 _nonce,
		uint8 v,
		bytes32 r,
		bytes32 s
	) public override {
		// ensure EIP-3009 transfers are enabled
		require(isFeatureEnabled(FEATURE_EIP3009_TRANSFERS), "EIP3009 transfers are disabled");

		// derive signer of the EIP712 TransferWithAuthorization message
		address signer = __deriveSigner(abi.encode(TRANSFER_WITH_AUTHORIZATION_TYPEHASH, _from, _to, _value, _validAfter, _validBefore, _nonce), v, r, s);

		// perform message integrity and security validations
		require(signer == _from, "invalid signature");
		require(block.timestamp > _validAfter, "signature not yet valid");
		require(block.timestamp < _validBefore, "signature expired");

		// use the nonce supplied (verify, mark as used, emit event)
		__useNonce(_from, _nonce, false);

		// delegate call to `__transferFrom` - execute the logic required
		__transferFrom(signer, _from, _to, _value);
	}

	/**
	 * @inheritdoc EIP3009
	 *
	 * @notice Receive a transfer with a signed authorization from the payer
	 *
	 * @dev This has an additional check to ensure that the payee's address
	 *      matches the caller of this function to prevent front-running attacks.
	 *
	 * @param _from token sender and transaction authorizer
	 * @param _to token receiver
	 * @param _value amount to be transferred
	 * @param _validAfter signature valid after time (unix timestamp)
	 * @param _validBefore signature valid before time (unix timestamp)
	 * @param _nonce unique random nonce
	 * @param v the recovery byte of the signature
	 * @param r half of the ECDSA signature pair
	 * @param s half of the ECDSA signature pair
	 */
	function receiveWithAuthorization(
		address _from,
		address _to,
		uint256 _value,
		uint256 _validAfter,
		uint256 _validBefore,
		bytes32 _nonce,
		uint8 v,
		bytes32 r,
		bytes32 s
	) public override {
		// verify EIP3009 receptions are enabled
		require(isFeatureEnabled(FEATURE_EIP3009_RECEPTIONS), "EIP3009 receptions are disabled");

		// derive signer of the EIP712 ReceiveWithAuthorization message
		address signer = __deriveSigner(abi.encode(RECEIVE_WITH_AUTHORIZATION_TYPEHASH, _from, _to, _value, _validAfter, _validBefore, _nonce), v, r, s);

		// perform message integrity and security validations
		require(signer == _from, "invalid signature");
		require(block.timestamp > _validAfter, "signature not yet valid");
		require(block.timestamp < _validBefore, "signature expired");
		require(_to == msg.sender, "access denied");

		// use the nonce supplied (verify, mark as used, emit event)
		__useNonce(_from, _nonce, false);

		// delegate call to `__transferFrom` - execute the logic required
		__transferFrom(signer, _from, _to, _value);
	}

	/**
	 * @inheritdoc EIP3009
	 *
	 * @notice Attempt to cancel an authorization
	 *
	 * @param _authorizer transaction authorizer
	 * @param _nonce unique random nonce to cancel (mark as used)
	 * @param v the recovery byte of the signature
	 * @param r half of the ECDSA signature pair
	 * @param s half of the ECDSA signature pair
	 */
	function cancelAuthorization(
		address _authorizer,
		bytes32 _nonce,
		uint8 v,
		bytes32 r,
		bytes32 s
	) public override {
		// derive signer of the EIP712 ReceiveWithAuthorization message
		address signer = __deriveSigner(abi.encode(CANCEL_AUTHORIZATION_TYPEHASH, _authorizer, _nonce), v, r, s);

		// perform message integrity and security validations
		require(signer == _authorizer, "invalid signature");

		// cancel the nonce supplied (verify, mark as used, emit event)
		__useNonce(_authorizer, _nonce, true);
	}

	/**
	 * @dev Auxiliary function to verify structured EIP712 message signature and derive its signer
	 *
	 * @param abiEncodedTypehash abi.encode of the message typehash together with all its parameters
	 * @param v the recovery byte of the signature
	 * @param r half of the ECDSA signature pair
	 * @param s half of the ECDSA signature pair
	 */
	function __deriveSigner(bytes memory abiEncodedTypehash, uint8 v, bytes32 r, bytes32 s) private view returns(address) {
		// build the EIP-712 hashStruct of the message
		bytes32 hashStruct = keccak256(abiEncodedTypehash);

		// calculate the EIP-712 digest "\x19\x01" ‖ domainSeparator ‖ hashStruct(message)
		bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, hashStruct));

		// recover the address which signed the message with v, r, s
		address signer = ECDSA.recover(digest, v, r, s);

		// return the signer address derived from the signature
		return signer;
	}

	/**
	 * @dev Auxiliary function to use/cancel the nonce supplied for a given authorizer:
	 *      1. Verifies the nonce was not used before
	 *      2. Marks the nonce as used
	 *      3. Emits an event that the nonce was used/cancelled
	 *
	 * @dev Set `_cancellation` to false (default) to use nonce,
	 *      set `_cancellation` to true to cancel nonce
	 *
	 * @dev It is expected that the nonce supplied is a randomly
	 *      generated uint256 generated by the client
	 *
	 * @param _authorizer an address to use/cancel nonce for
	 * @param _nonce random nonce to use
	 * @param _cancellation true to emit `AuthorizationCancelled`, false to emit `AuthorizationUsed` event
	 */
	function __useNonce(address _authorizer, bytes32 _nonce, bool _cancellation) private {
		// verify nonce was not used before
		require(!usedNonces[_authorizer][_nonce], "invalid nonce");

		// update the nonce state to "used" for that particular signer to avoid replay attack
		usedNonces[_authorizer][_nonce] = true;

		// depending on the usage type (use/cancel)
		if(_cancellation) {
			// emit an event regarding the nonce cancelled
			emit AuthorizationCanceled(_authorizer, _nonce);
		}
		else {
			// emit an event regarding the nonce used
			emit AuthorizationUsed(_authorizer, _nonce);
		}
	}

	// ===== End: EIP-3009 functions =====

	// ===== Start: DAO Support (Compound-like voting delegation) =====

	/**
	 * @notice Gets current voting power of the account `_of`
	 *
	 * @param _of the address of account to get voting power of
	 * @return current cumulative voting power of the account,
	 *      sum of token balances of all its voting delegators
	 */
	function votingPowerOf(address _of) public view returns (uint256) {
		// get a link to an array of voting power history records for an address specified
		KV[] storage history = votingPowerHistory[_of];

		// lookup the history and return latest element
		return history.length == 0? 0: history[history.length - 1].v;
	}

	/**
	 * @notice Gets past voting power of the account `_of` at some block `_blockNum`
	 *
	 * @dev Throws if `_blockNum` is not in the past (not the finalized block)
	 *
	 * @param _of the address of account to get voting power of
	 * @param _blockNum block number to get the voting power at
	 * @return past cumulative voting power of the account,
	 *      sum of token balances of all its voting delegators at block number `_blockNum`
	 */
	function votingPowerAt(address _of, uint256 _blockNum) public view returns (uint256) {
		// make sure block number is not in the past (not the finalized block)
		require(_blockNum < block.number, "block not yet mined"); // Compound msg not yet determined

		// `votingPowerHistory[_of]` is an array ordered by `blockNumber`, ascending;
		// apply binary search on `votingPowerHistory[_of]` to find such an entry number `i`, that
		// `votingPowerHistory[_of][i].k <= _blockNum`, but in the same time
		// `votingPowerHistory[_of][i + 1].k > _blockNum`
		// return the result - voting power found at index `i`
		return __binaryLookup(votingPowerHistory[_of], _blockNum);
	}

	/**
	 * @dev Reads an entire voting power history array for the delegate specified
	 *
	 * @param _of delegate to query voting power history for
	 * @return voting power history array for the delegate of interest
	 */
	function votingPowerHistoryOf(address _of) public view returns(KV[] memory) {
		// return an entire array as memory
		return votingPowerHistory[_of];
	}

	/**
	 * @dev Returns length of the voting power history array for the delegate specified;
	 *      useful since reading an entire array just to get its length is expensive (gas cost)
	 *
	 * @param _of delegate to query voting power history length for
	 * @return voting power history array length for the delegate of interest
	 */
	function votingPowerHistoryLength(address _of) public view returns(uint256) {
		// read array length and return
		return votingPowerHistory[_of].length;
	}

	/**
	 * @notice Gets past total token supply value at some block `_blockNum`
	 *
	 * @dev Throws if `_blockNum` is not in the past (not the finalized block)
	 *
	 * @param _blockNum block number to get the total token supply at
	 * @return past total token supply at block number `_blockNum`
	 */
	function totalSupplyAt(uint256 _blockNum) public view returns(uint256) {
		// make sure block number is not in the past (not the finalized block)
		require(_blockNum < block.number, "block not yet mined");

		// `totalSupplyHistory` is an array ordered by `k`, ascending;
		// apply binary search on `totalSupplyHistory` to find such an entry number `i`, that
		// `totalSupplyHistory[i].k <= _blockNum`, but in the same time
		// `totalSupplyHistory[i + 1].k > _blockNum`
		// return the result - value `totalSupplyHistory[i].v` found at index `i`
		return __binaryLookup(totalSupplyHistory, _blockNum);
	}

	/**
	 * @dev Reads an entire total token supply history array
	 *
	 * @return total token supply history array, a key-value pair array,
	 *      where key is a block number and value is total token supply at that block
	 */
	function entireSupplyHistory() public view returns(KV[] memory) {
		// return an entire array as memory
		return totalSupplyHistory;
	}

	/**
	 * @dev Returns length of the total token supply history array;
	 *      useful since reading an entire array just to get its length is expensive (gas cost)
	 *
	 * @return total token supply history array
	 */
	function totalSupplyHistoryLength() public view returns(uint256) {
		// read array length and return
		return totalSupplyHistory.length;
	}

	/**
	 * @notice Delegates voting power of the delegator `msg.sender` to the delegate `_to`
	 *
	 * @dev Accepts zero value address to delegate voting power to, effectively
	 *      removing the delegate in that case
	 *
	 * @param _to address to delegate voting power to
	 */
	function delegate(address _to) public {
		// verify delegations are enabled
		require(isFeatureEnabled(FEATURE_DELEGATIONS), "delegations are disabled");
		// delegate call to `__delegate`
		__delegate(msg.sender, _to);
	}

	/**
	 * @dev Powers the meta transaction for `delegate` - `delegateWithAuthorization`
	 *
	 * @dev Auxiliary function to delegate delegator's `_from` voting power to the delegate `_to`
	 * @dev Writes to `votingDelegates` and `votingPowerHistory` mappings
	 *
	 * @param _from delegator who delegates his voting power
	 * @param _to delegate who receives the voting power
	 */
	function __delegate(address _from, address _to) private {
		// read current delegate to be replaced by a new one
		address _fromDelegate = votingDelegates[_from];

		// read current voting power (it is equal to token balance)
		uint256 _value = tokenBalances[_from];

		// reassign voting delegate to `_to`
		votingDelegates[_from] = _to;

		// update voting power for `_fromDelegate` and `_to`
		__moveVotingPower(_from, _fromDelegate, _to, _value);

		// emit an event
		emit DelegateChanged(_from, _fromDelegate, _to);
	}

	/**
	 * @notice Delegates voting power of the delegator (represented by its signature) to the delegate `_to`
	 *
	 * @dev Accepts zero value address to delegate voting power to, effectively
	 *      removing the delegate in that case
	 *
	 * @dev Compliant with EIP-712: Ethereum typed structured data hashing and signing,
	 *      see https://eips.ethereum.org/EIPS/eip-712
	 *
	 * @param _to address to delegate voting power to
	 * @param _nonce nonce used to construct the signature, and used to validate it;
	 *      nonce is increased by one after successful signature validation and vote delegation
	 * @param _exp signature expiration time
	 * @param v the recovery byte of the signature
	 * @param r half of the ECDSA signature pair
	 * @param s half of the ECDSA signature pair
	 */
	function delegateWithAuthorization(address _to, bytes32 _nonce, uint256 _exp, uint8 v, bytes32 r, bytes32 s) public {
		// verify delegations on behalf are enabled
		require(isFeatureEnabled(FEATURE_DELEGATIONS_ON_BEHALF), "delegations on behalf are disabled");

		// derive signer of the EIP712 Delegation message
		address signer = __deriveSigner(abi.encode(DELEGATION_TYPEHASH, _to, _nonce, _exp), v, r, s);

		// perform message integrity and security validations
		require(block.timestamp < _exp, "signature expired"); // Compound msg

		// use the nonce supplied (verify, mark as used, emit event)
		__useNonce(signer, _nonce, false);

		// delegate call to `__delegate` - execute the logic required
		__delegate(signer, _to);
	}

	/**
	 * @dev Auxiliary function to move voting power `_value`
	 *      from delegate `_from` to the delegate `_to`
	 *
	 * @dev Doesn't have any effect if `_from == _to`, or if `_value == 0`
	 *
	 * @param _by an address which executed delegate, mint, burn, or transfer operation
	 *      which had led to delegate voting power change
	 * @param _from delegate to move voting power from
	 * @param _to delegate to move voting power to
	 * @param _value voting power to move from `_from` to `_to`
	 */
	function __moveVotingPower(address _by, address _from, address _to, uint256 _value) private {
		// if there is no move (`_from == _to`) or there is nothing to move (`_value == 0`)
		if(_from == _to || _value == 0) {
			// return silently with no action
			return;
		}

		// if source address is not zero - decrease its voting power
		if(_from != address(0)) {
			// get a link to an array of voting power history records for an address specified
			KV[] storage _h = votingPowerHistory[_from];

			// update source voting power: decrease by `_value`
			(uint256 _fromVal, uint256 _toVal) = __updateHistory(_h, sub, _value);

			// emit an event
			emit VotingPowerChanged(_by, _from, _fromVal, _toVal);
		}

		// if destination address is not zero - increase its voting power
		if(_to != address(0)) {
			// get a link to an array of voting power history records for an address specified
			KV[] storage _h = votingPowerHistory[_to];

			// update destination voting power: increase by `_value`
			(uint256 _fromVal, uint256 _toVal) = __updateHistory(_h, add, _value);

			// emit an event
			emit VotingPowerChanged(_by, _to, _fromVal, _toVal);
		}
	}

	/**
	 * @dev Auxiliary function to append key-value pair to an array,
	 *      sets the key to the current block number and
	 *      value as derived
	 *
	 * @param _h array of key-value pairs to append to
	 * @param op a function (add/subtract) to apply
	 * @param _delta the value for a key-value pair to add/subtract
	 */
	function __updateHistory(
		KV[] storage _h,
		function(uint256,uint256) pure returns(uint256) op,
		uint256 _delta
	) private returns(uint256 _fromVal, uint256 _toVal) {
		// init the old value - value of the last pair of the array
		_fromVal = _h.length == 0? 0: _h[_h.length - 1].v;
		// init the new value - result of the operation on the old value
		_toVal = op(_fromVal, _delta);

		// if there is an existing voting power value stored for current block
		if(_h.length != 0 && _h[_h.length - 1].k == block.number) {
			// update voting power which is already stored in the current block
			_h[_h.length - 1].v = uint192(_toVal);
		}
		// otherwise - if there is no value stored for current block
		else {
			// add new element into array representing the value for current block
			_h.push(KV(uint64(block.number), uint192(_toVal)));
		}
	}

	/**
	 * @dev Auxiliary function to lookup for a value in a sorted by key (ascending)
	 *      array of key-value pairs
	 *
	 * @dev This function finds a key-value pair element in an array with the closest key
	 *      to the key of interest (not exceeding that key) and returns the value
	 *      of the key-value pair element found
	 *
	 * @dev An array to search in is a KV[] key-value pair array ordered by key `k`,
	 *      it is sorted in ascending order (`k` increases as array index increases)
	 *
	 * @dev Returns zero for an empty array input regardless of the key input
	 *
	 * @param _h an array of key-value pair elements to search in
	 * @param _k key of interest to look the value for
	 * @return the value of the key-value pair of the key-value pair element with the closest
	 *      key to the key of interest (not exceeding that key)
	 */
	function __binaryLookup(KV[] storage _h, uint256 _k) private view returns(uint256) {
		// if an array is empty, there is nothing to lookup in
		if(_h.length == 0) {
			// by documented agreement, fall back to a zero result
			return 0;
		}

		// check last key-value pair key:
		// if the key is smaller than the key of interest
		if(_h[_h.length - 1].k <= _k) {
			// we're done - return the value from the last element
			return _h[_h.length - 1].v;
		}

		// check first voting power history record block number:
		// if history was never updated before the block of interest
		if(_h[0].k > _k) {
			// we're done - voting power at the block num of interest was zero
			return 0;
		}

		// left bound of the search interval, originally start of the array
		uint256 i = 0;

		// right bound of the search interval, originally end of the array
		uint256 j = _h.length - 1;

		// the iteration process narrows down the bounds by
		// splitting the interval in a half oce per each iteration
		while(j > i) {
			// get an index in the middle of the interval [i, j]
			uint256 k = j - (j - i) / 2;

			// read an element to compare it with the value of interest
			KV memory kv = _h[k];

			// if we've got a strict equal - we're lucky and done
			if(kv.k == _k) {
				// just return the result - pair value at index `k`
				return kv.v;
			}
			// if the value of interest is larger - move left bound to the middle
			else if (kv.k < _k) {
				// move left bound `i` to the middle position `k`
				i = k;
			}
			// otherwise, when the value of interest is smaller - move right bound to the middle
			else {
				// move right bound `j` to the middle position `k - 1`:
				// element at position `k` is greater and cannot be the result
				j = k - 1;
			}
		}

		// reaching that point means no exact match found
		// since we're interested in the element which is not larger than the
		// element of interest, we return the lower bound `i`
		return _h[i].v;
	}

	/**
	 * @dev Adds a + b
	 *      Function is used as a parameter for other functions
	 *
	 * @param a addition term 1
	 * @param b addition term 2
	 * @return a + b
	 */
	function add(uint256 a, uint256 b) private pure returns(uint256) {
		// add `a` to `b` and return
		return a + b;
	}

	/**
	 * @dev Subtracts a - b
	 *      Function is used as a parameter for other functions
	 *
	 * @dev Requires a ≥ b
	 *
	 * @param a subtraction term 1
	 * @param b subtraction term 2, b ≤ a
	 * @return a - b
	 */
	function sub(uint256 a, uint256 b) private pure returns(uint256) {
		// subtract `b` from `a` and return
		return a - b;
	}

	// ===== End: DAO Support (Compound-like voting delegation) =====
}

/**
 * @title Artificial Liquid Intelligence ERC20 Token (Alethea, ALI)
 *
 * @notice Ethereum mainnet implementation with the fixed supply 10,000,000,000 ALI (non-configurable)
 *
 * @author Basil Gorin
 */
contract AliERC20v2 is AliERC20v2Base {
	/**
	 * @dev Deploys the token smart contract,
	 *      assigns initial token supply to the address specified
	 *
	 * @param _initialHolder owner of the initial token supply
	 */
	constructor(address _initialHolder) AliERC20v2Base(_initialHolder, 10_000_000_000e18) {}
}

File 10 of 10 : AccessControl.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/**
 * @title Access Control List
 *
 * @notice Access control smart contract provides an API to check
 *      if specific operation is permitted globally and/or
 *      if particular user has a permission to execute it.
 *
 * @notice It deals with two main entities: features and roles.
 *
 * @notice Features are designed to be used to enable/disable specific
 *      functions (public functions) of the smart contract for everyone.
 * @notice User roles are designed to restrict access to specific
 *      functions (restricted functions) of the smart contract to some users.
 *
 * @notice Terms "role", "permissions" and "set of permissions" have equal meaning
 *      in the documentation text and may be used interchangeably.
 * @notice Terms "permission", "single permission" implies only one permission bit set.
 *
 * @notice Access manager is a special role which allows to grant/revoke other roles.
 *      Access managers can only grant/revoke permissions which they have themselves.
 *      As an example, access manager with no other roles set can only grant/revoke its own
 *      access manager permission and nothing else.
 *
 * @notice Access manager permission should be treated carefully, as a super admin permission:
 *      Access manager with even no other permission can interfere with another account by
 *      granting own access manager permission to it and effectively creating more powerful
 *      permission set than its own.
 *
 * @dev Both current and OpenZeppelin AccessControl implementations feature a similar API
 *      to check/know "who is allowed to do this thing".
 * @dev Zeppelin implementation is more flexible:
 *      - it allows setting unlimited number of roles, while current is limited to 256 different roles
 *      - it allows setting an admin for each role, while current allows having only one global admin
 * @dev Current implementation is more lightweight:
 *      - it uses only 1 bit per role, while Zeppelin uses 256 bits
 *      - it allows setting up to 256 roles at once, in a single transaction, while Zeppelin allows
 *        setting only one role in a single transaction
 *
 * @dev This smart contract is designed to be inherited by other
 *      smart contracts which require access control management capabilities.
 *
 * @dev Access manager permission has a bit 255 set.
 *      This bit must not be used by inheriting contracts for any other permissions/features.
 */
contract AccessControl {
	/**
	 * @notice Access manager is responsible for assigning the roles to users,
	 *      enabling/disabling global features of the smart contract
	 * @notice Access manager can add, remove and update user roles,
	 *      remove and update global features
	 *
	 * @dev Role ROLE_ACCESS_MANAGER allows modifying user roles and global features
	 * @dev Role ROLE_ACCESS_MANAGER has single bit at position 255 enabled
	 */
	uint256 public constant ROLE_ACCESS_MANAGER = 0x8000000000000000000000000000000000000000000000000000000000000000;

	/**
	 * @dev Bitmask representing all the possible permissions (super admin role)
	 * @dev Has all the bits are enabled (2^256 - 1 value)
	 */
	uint256 private constant FULL_PRIVILEGES_MASK = type(uint256).max; // before 0.8.0: uint256(-1) overflows to 0xFFFF...

	/**
	 * @notice Privileged addresses with defined roles/permissions
	 * @notice In the context of ERC20/ERC721 tokens these can be permissions to
	 *      allow minting or burning tokens, transferring on behalf and so on
	 *
	 * @dev Maps user address to the permissions bitmask (role), where each bit
	 *      represents a permission
	 * @dev Bitmask 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
	 *      represents all possible permissions
	 * @dev 'This' address mapping represents global features of the smart contract
	 */
	mapping(address => uint256) public userRoles;

	/**
	 * @dev Fired in updateRole() and updateFeatures()
	 *
	 * @param _by operator which called the function
	 * @param _to address which was granted/revoked permissions
	 * @param _requested permissions requested
	 * @param _actual permissions effectively set
	 */
	event RoleUpdated(address indexed _by, address indexed _to, uint256 _requested, uint256 _actual);

	/**
	 * @notice Creates an access control instance,
	 *      setting contract creator to have full privileges
	 */
	constructor() {
		// contract creator has full privileges
		userRoles[msg.sender] = FULL_PRIVILEGES_MASK;
	}

	/**
	 * @notice Retrieves globally set of features enabled
	 *
	 * @dev Effectively reads userRoles role for the contract itself
	 *
	 * @return 256-bit bitmask of the features enabled
	 */
	function features() public view returns(uint256) {
		// features are stored in 'this' address  mapping of `userRoles` structure
		return userRoles[address(this)];
	}

	/**
	 * @notice Updates set of the globally enabled features (`features`),
	 *      taking into account sender's permissions
	 *
	 * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
	 * @dev Function is left for backward compatibility with older versions
	 *
	 * @param _mask bitmask representing a set of features to enable/disable
	 */
	function updateFeatures(uint256 _mask) public {
		// delegate call to `updateRole`
		updateRole(address(this), _mask);
	}

	/**
	 * @notice Updates set of permissions (role) for a given user,
	 *      taking into account sender's permissions.
	 *
	 * @dev Setting role to zero is equivalent to removing an all permissions
	 * @dev Setting role to `FULL_PRIVILEGES_MASK` is equivalent to
	 *      copying senders' permissions (role) to the user
	 * @dev Requires transaction sender to have `ROLE_ACCESS_MANAGER` permission
	 *
	 * @param operator address of a user to alter permissions for or zero
	 *      to alter global features of the smart contract
	 * @param role bitmask representing a set of permissions to
	 *      enable/disable for a user specified
	 */
	function updateRole(address operator, uint256 role) public {
		// caller must have a permission to update user roles
		require(isSenderInRole(ROLE_ACCESS_MANAGER), "access denied");

		// evaluate the role and reassign it
		userRoles[operator] = evaluateBy(msg.sender, userRoles[operator], role);

		// fire an event
		emit RoleUpdated(msg.sender, operator, role, userRoles[operator]);
	}

	/**
	 * @notice Determines the permission bitmask an operator can set on the
	 *      target permission set
	 * @notice Used to calculate the permission bitmask to be set when requested
	 *     in `updateRole` and `updateFeatures` functions
	 *
	 * @dev Calculated based on:
	 *      1) operator's own permission set read from userRoles[operator]
	 *      2) target permission set - what is already set on the target
	 *      3) desired permission set - what do we want set target to
	 *
	 * @dev Corner cases:
	 *      1) Operator is super admin and its permission set is `FULL_PRIVILEGES_MASK`:
	 *        `desired` bitset is returned regardless of the `target` permission set value
	 *        (what operator sets is what they get)
	 *      2) Operator with no permissions (zero bitset):
	 *        `target` bitset is returned regardless of the `desired` value
	 *        (operator has no authority and cannot modify anything)
	 *
	 * @dev Example:
	 *      Consider an operator with the permissions bitmask     00001111
	 *      is about to modify the target permission set          01010101
	 *      Operator wants to set that permission set to          00110011
	 *      Based on their role, an operator has the permissions
	 *      to update only lowest 4 bits on the target, meaning that
	 *      high 4 bits of the target set in this example is left
	 *      unchanged and low 4 bits get changed as desired:      01010011
	 *
	 * @param operator address of the contract operator which is about to set the permissions
	 * @param target input set of permissions to operator is going to modify
	 * @param desired desired set of permissions operator would like to set
	 * @return resulting set of permissions given operator will set
	 */
	function evaluateBy(address operator, uint256 target, uint256 desired) public view returns(uint256) {
		// read operator's permissions
		uint256 p = userRoles[operator];

		// taking into account operator's permissions,
		// 1) enable the permissions desired on the `target`
		target |= p & desired;
		// 2) disable the permissions desired on the `target`
		target &= FULL_PRIVILEGES_MASK ^ (p & (FULL_PRIVILEGES_MASK ^ desired));

		// return calculated result
		return target;
	}

	/**
	 * @notice Checks if requested set of features is enabled globally on the contract
	 *
	 * @param required set of features to check against
	 * @return true if all the features requested are enabled, false otherwise
	 */
	function isFeatureEnabled(uint256 required) public view returns(bool) {
		// delegate call to `__hasRole`, passing `features` property
		return __hasRole(features(), required);
	}

	/**
	 * @notice Checks if transaction sender `msg.sender` has all the permissions required
	 *
	 * @param required set of permissions (role) to check against
	 * @return true if all the permissions requested are enabled, false otherwise
	 */
	function isSenderInRole(uint256 required) public view returns(bool) {
		// delegate call to `isOperatorInRole`, passing transaction sender
		return isOperatorInRole(msg.sender, required);
	}

	/**
	 * @notice Checks if operator has all the permissions (role) required
	 *
	 * @param operator address of the user to check role for
	 * @param required set of permissions (role) to check
	 * @return true if all the permissions requested are enabled, false otherwise
	 */
	function isOperatorInRole(address operator, uint256 required) public view returns(bool) {
		// delegate call to `__hasRole`, passing operator's permissions (role)
		return __hasRole(userRoles[operator], required);
	}

	/**
	 * @dev Checks if role `actual` contains all the permissions required `required`
	 *
	 * @param actual existent role
	 * @param required required role
	 * @return true if actual has required role (all permissions), false otherwise
	 */
	function __hasRole(uint256 actual, uint256 required) internal pure returns(bool) {
		// check the bitmask for the role required and return the result
		return actual & required == required;
	}
}

Settings
{
  "evmVersion": "london",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs",
    "useLiteralContent": true
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [],
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldValue","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"authorizer","type":"address"},{"indexed":true,"internalType":"bytes32","name":"nonce","type":"bytes32"}],"name":"AuthorizationUsed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Burnt","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"source","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"DelegateChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_by","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_requested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_actual","type":"uint256"}],"name":"RoleUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromVal","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toVal","type":"uint256"}],"name":"VotingPowerChanged","type":"event"},{"inputs":[],"name":"CANCEL_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DELEGATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_BURNS_ON_BEHALF","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_DELEGATIONS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_DELEGATIONS_ON_BEHALF","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_EIP2612_PERMITS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_EIP3009_RECEPTIONS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_EIP3009_TRANSFERS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_ERC1363_APPROVALS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_ERC1363_TRANSFERS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_OWN_BURNS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_TRANSFERS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_TRANSFERS_ON_BEHALF","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEATURE_UNSAFE_TRANSFERS","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RECEIVE_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_ACCESS_MANAGER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_ERC20_RECEIVER","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_ERC20_SENDER","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_TOKEN_CREATOR","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ROLE_TOKEN_DESTROYER","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN_UID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TRANSFER_WITH_AUTHORIZATION_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"remaining","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approveAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"approveAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_authorizer","type":"address"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"}],"name":"authorizationState","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_authorizer","type":"address"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"cancelAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"}],"name":"delegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"},{"internalType":"uint256","name":"_exp","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"delegateWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"bytes","name":"depositData","type":"bytes"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"entireSupplyHistory","outputs":[{"components":[{"internalType":"uint64","name":"k","type":"uint64"},{"internalType":"uint192","name":"v","type":"uint192"}],"internalType":"struct AliERC20v2Base.KV[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"target","type":"uint256"},{"internalType":"uint256","name":"desired","type":"uint256"}],"name":"evaluateBy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"features","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isFeatureEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isOperatorInRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"required","type":"uint256"}],"name":"isSenderInRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_exp","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_validAfter","type":"uint256"},{"internalType":"uint256","name":"_validBefore","type":"uint256"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"receiveWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_blockNum","type":"uint256"}],"name":"totalSupplyAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"totalSupplyHistory","outputs":[{"internalType":"uint64","name":"k","type":"uint64"},{"internalType":"uint192","name":"v","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupplyHistoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"transferFromAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFromAndCall","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_validAfter","type":"uint256"},{"internalType":"uint256","name":"_validBefore","type":"uint256"},{"internalType":"bytes32","name":"_nonce","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"transferWithAuthorization","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"unsafeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_mask","type":"uint256"}],"name":"updateFeatures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"uint256","name":"role","type":"uint256"}],"name":"updateRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userRoles","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"votingDelegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_of","type":"address"},{"internalType":"uint256","name":"_blockNum","type":"uint256"}],"name":"votingPowerAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"votingPowerHistory","outputs":[{"internalType":"uint64","name":"k","type":"uint64"},{"internalType":"uint192","name":"v","type":"uint192"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_of","type":"address"}],"name":"votingPowerHistoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_of","type":"address"}],"name":"votingPowerHistoryOf","outputs":[{"components":[{"internalType":"uint64","name":"k","type":"uint64"},{"internalType":"uint192","name":"v","type":"uint192"}],"internalType":"struct AliERC20v2Base.KV[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_of","type":"address"}],"name":"votingPowerOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60a06040523480156200001157600080fd5b5033600090815260208190526040812060001990558062000036565b60405180910390fd5b604080518082018252600a81526920b634a2a92199183b1960b11b60209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f5572cb272f139fecace047c0fa6b0771cef3e004fc181b85045ff215e5fcaa3c818401524660608201523060808083019190915283518083038201815260a0909201909352805191012090528015620000de57620000de8282620000e6565b505062000729565b620000f46201000062000397565b620001325760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b60448201526064016200002d565b6001600160a01b038216620001795760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b60448201526064016200002d565b60015481810111620001d85760405162461bcd60e51b815260206004820152602160248201527f7a65726f2076616c7565206f722061726974686d65746963206f766572666c6f6044820152607760f81b60648201526084016200002d565b6001546001600160c01b0390620001f1908390620006de565b1115620002415760405162461bcd60e51b815260206004820152601f60248201527f746f74616c20737570706c79206f766572666c6f77202875696e74313932290060448201526064016200002d565b8060016000828254620002559190620006de565b90915550506001600160a01b0382166000908152600260205260408120805483929062000284908490620006de565b90915550620002a39050600562002004620003ab60201b1783620003c0565b50506001600160a01b03808316600090815260036020526040812054620002cf92339291168462000543565b6040518181526001600160a01b0383169033907f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f09060200160405180910390a36040518181526001600160a01b0383169060009033907fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f9060200160405180910390a46040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b6000620003a5338362000697565b92915050565b6000620003b98284620006de565b9392505050565b8254600090819015620004195784548590620003df90600190620006f9565b81548110620003f257620003f262000713565b6000918252602090912001546801000000000000000090046001600160c01b03166200041c565b60005b6001600160c01b031691506200043382848660201c565b8554909150158015906200048057508454439086906200045690600190620006f9565b8154811062000469576200046962000713565b6000918252602090912001546001600160401b0316145b15620004e4578454819086906200049a90600190620006f9565b81548110620004ad57620004ad62000713565b9060005260206000200160000160086101000a8154816001600160c01b0302191690836001600160c01b031602179055506200053b565b604080518082019091526001600160401b0343811682526001600160c01b0380841660208085019182528954600181018b5560008b8152919091209451915190921668010000000000000000029216919091179101555b935093915050565b816001600160a01b0316836001600160a01b0316148062000562575080155b62000691576001600160a01b03831615620005fc576001600160a01b0383166000908152600460209081526040822091908190620005ae908490620006ba901b620020101786620003c0565b91509150856001600160a01b0316876001600160a01b03166000805160206200409e8339815191528484604051620005f0929190918252602082015260400190565b60405180910390a35050505b6001600160a01b0382161562000691576001600160a01b038216600090815260046020908152604082209190819062000643908490620003ab901b620020041786620003c0565b91509150846001600160a01b0316876001600160a01b03166000805160206200409e833981519152848460405162000685929190918252602082015260400190565b60405180910390a35050505b50505050565b6001600160a01b03821660009081526020819052604081205482168214620003b9565b6000620003b98284620006f9565b634e487b7160e01b600052601160045260246000fd5b60008219821115620006f457620006f4620006c8565b500190565b6000828210156200070e576200070e620006c8565b500390565b634e487b7160e01b600052603260045260246000fd5b6080516139526200074c6000396000818161060401526129ce01526139526000f3fe608060405234801561001057600080fd5b50600436106104495760003560e01c80637fd491b011610241578063c5ff500c1161013b578063e3ee160e116100c3578063ef55bec611610087578063ef55bec614610aa7578063f63c2f8214610aba578063f822d5aa14610ac2578063f9cf927114610ad5578063fcc2c07814610ae857600080fd5b8063e3ee160e14610a20578063e62cac7614610a33578063e7a324dc14610a3d578063e94a010214610a64578063e98f5ba714610a9d57600080fd5b8063d505accf1161010a578063d505accf14610987578063d5bb7f671461099a578063d8fbe994146109ad578063d9169487146109c0578063dd62ed3e146109e757600080fd5b8063c5ff500c14610944578063c688d6931461094e578063cae9ca5114610961578063cf2c52cb1461097457600080fd5b8063a0cc6a68116101c9578063b66dbdc51161018d578063b66dbdc5146108fb578063b88d4fde14610903578063bcc3f3bd14610916578063c0d6568d14610929578063c1d34b891461093157600080fd5b8063a0cc6a6814610890578063a457c2d7146108b7578063a9059cbb146108ca578063ae5b102e146108dd578063ae682e2e146108f057600080fd5b80638f6fba8c116102105780638f6fba8c1461082d57806394f4f9301461083557806395d89b4114610848578063981b24d01461086a5780639dc29fac1461087d57600080fd5b80637fd491b0146107c057806387793f3e146107d35780638a114e13146107fc5780638d4e57e61461082357600080fd5b80633644e515116103525780635e2dc2b7116102da578063725f36261161029e578063725f36261461073357806374d5e100146107465780637815ef0c146107665780637ecebe00146107795780637f2eecc31461079957600080fd5b80635e2dc2b7146106a957806364cb8b96146106b2578063653de620146106c75780636641d9a0146106d057806370a082311461070a57600080fd5b806340c10f191161032157806340c10f19146106545780634721272d1461066757806359b961ef146106705780635a049a70146106835780635c19a95c1461069657600080fd5b80633644e515146105ff57806339509351146106265780633e9c5f7e146106395780634000aea01461064157600080fd5b80631e0fa234116103d55780632d4c39ea116103a45780632d4c39ea1461058e5780632e1a7d4d1461059657806330adf81f146105ab578063313ce567146105d25780633177029f146105ec57600080fd5b80631e0fa234146104fe57806320606b701461053f57806323b872dd146105665780632b5214161461057957600080fd5b8063136d035f1161041c578063136d035f146104b157806313873a24146104cf57806318160ddd146104d75780631993f554146104ee5780631a0b04ea146104f657600080fd5b806301ffc9a71461044e57806306fdde0314610476578063095ea7b31461048b5780631296ee621461049e575b600080fd5b61046161045c36600461318e565b610afb565b60405190151581526020015b60405180910390f35b61047e610b83565b60405161046d91906131f8565b610461610499366004613227565b610b9f565b6104616104ac366004613227565b610bb5565b6104ba61080081565b60405163ffffffff909116815260200161046d565b6104ba608081565b6104e060015481565b60405190815260200161046d565b6104ba600481565b6104ba600881565b61052761050c366004613251565b6003602052600090815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161046d565b6104e07f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61046161057436600461326c565b610bc9565b306000908152602081905260409020546104e0565b6104ba604081565b6105a96105a43660046132a8565b610c36565b005b6104e07f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6105da601281565b60405160ff909116815260200161046d565b6104616105fa366004613227565b610c43565b6104e07f000000000000000000000000000000000000000000000000000000000000000081565b610461610634366004613227565b610c5f565b6104ba602081565b61046161064f366004613363565b610d0b565b6105a9610662366004613227565b610d19565b6104ba61020081565b6105a961067e36600461326c565b610f7c565b6105a96106913660046133ca565b610f8d565b6105a96106a4366004613251565b611038565b6104ba61040081565b6106ba611098565b60405161046d9190613418565b6104ba61010081565b6106e36106de3660046132a8565b611114565b604080516001600160401b0390931683526001600160c01b0390911660208301520161046d565b6104e0610718366004613251565b6001600160a01b031660009081526002602052604090205490565b6104616107413660046132a8565b61114f565b6104e0610754366004613251565b60006020819052908152604090205481565b6106ba610774366004613251565b611168565b6104e0610787366004613251565b60066020526000908152604090205481565b6104e07fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6106e36107ce366004613227565b6111fa565b6104e06107e1366004613251565b6001600160a01b031660009081526004602052604090205490565b6104e07f8d4fb97da97378ef7d0ad259aec651f42bd22c200159282baa58486bb390286b81565b6104ba6201000081565b6104ba600281565b6104e0610843366004613227565b611243565b61047e60405180604001604052806003815260200162414c4960e81b81525081565b6104e06108783660046132a8565b6112ac565b6105a961088b366004613227565b6112fe565b6104e07f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104616108c5366004613227565b61175c565b6104616108d8366004613227565b611839565b6105a96108eb366004613227565b611846565b6104e0600160ff1b81565b6005546104e0565b610461610911366004613479565b6118e9565b6104e0610924366004613251565b61190f565b6104ba600181565b61046161093f366004613479565b611982565b6104ba6204000081565b61046161095c366004613227565b6119f3565b61046161096f366004613363565b611a15565b6105a96109823660046134e0565b611a84565b6105a9610995366004613562565b611aa4565b6105a96109a83660046132a8565b611bf3565b6104616109bb36600461326c565b611bfd565b6104e07f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6104e06109f53660046135cc565b6001600160a01b03918216600090815260086020908152604080832093909416825291909152205490565b6105a9610a2e3660046135ff565b611c1a565b6104ba6202000081565b6104e07fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75581565b610461610a72366004613227565b6001600160a01b03919091166000908152600760209081526040808320938352929052205460ff1690565b6104ba6208000081565b6105a9610ab53660046135ff565b611d75565b6104ba601081565b6104e0610ad036600461367d565b611ed4565b6105a9610ae33660046136b0565b611eff565b610461610af63660046132a8565b611ff8565b60006001600160e01b031982166301ffc9a760e01b1480610b2c57506001600160e01b031982166336372b0760e01b145b80610b4757506001600160e01b0319821663b0202a1160e01b145b80610b6257506001600160e01b03198216634ec7fbed60e11b145b80610b7d57506001600160e01b03198216635ffa99dd60e11b145b92915050565b6040518060600160405280602481526020016138d96024913981565b6000610bac33848461201c565b50600192915050565b6000610bc2338484611bfd565b9392505050565b6000610bd5600461114f565b80610be85750610be883620400006119f3565b80610bfa5750610bfa62080000611ff8565b15610c0f57610c0a848484610f7c565b610c2c565b610c2a848484604051806020016040528060008152506118e9565b505b5060019392505050565b610c4033826112fe565b50565b6000610bc2838360405180602001604052806000815250611a15565b3360009081526008602090815260408083206001600160a01b03861684529091528120548281018110610cf55760405162461bcd60e51b815260206004820152603360248201527f7a65726f2076616c756520617070726f76616c20696e637265617365206f722060448201527261726974686d65746963206f766572666c6f7760681b60648201526084015b60405180910390fd5b610d0384610499858461371e565b949350505050565b6000610d0333858585611982565b610d2562010000611ff8565b610d415760405162461bcd60e51b8152600401610cec90613736565b6001600160a01b038216610d865760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b6044820152606401610cec565b60015481810111610de35760405162461bcd60e51b815260206004820152602160248201527f7a65726f2076616c7565206f722061726974686d65746963206f766572666c6f6044820152607760f81b6064820152608401610cec565b6001546001600160c01b0390610dfa90839061371e565b1115610e485760405162461bcd60e51b815260206004820152601f60248201527f746f74616c20737570706c79206f766572666c6f77202875696e7431393229006044820152606401610cec565b8060016000828254610e5a919061371e565b90915550506001600160a01b03821660009081526002602052604081208054839290610e8790849061371e565b90915550610e9b905060056120048361212d565b50506001600160a01b03808316600090815260036020526040812054610ec5923392911684612294565b6040518181526001600160a01b0383169033907f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f09060200160405180910390a36040518181526001600160a01b0383169060009033907fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f9060200160405180910390a46040518181526001600160a01b038316906000906000805160206138fd833981519152906020015b60405180910390a35050565b610f88338484846123e3565b505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b0387169181019190915260608101859052600090610ff1906080015b6040516020818303038152906040528585856129b1565b9050856001600160a01b0316816001600160a01b0316146110245760405162461bcd60e51b8152600401610cec9061375d565b61103086866001612a33565b505050505050565b611042602061114f565b61108e5760405162461bcd60e51b815260206004820152601860248201527f64656c65676174696f6e73206172652064697361626c656400000000000000006044820152606401610cec565b610c403382612b41565b60606005805480602002602001604051908101604052809291908181526020016000905b8282101561110b57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b0316818301528252600190920191016110bc565b50505050905090565b6005818154811061112457600080fd5b6000918252602090912001546001600160401b0381169150600160401b90046001600160c01b031682565b3060009081526020819052604081205482168214610b7d565b6001600160a01b0381166000908152600460209081526040808320805482518185028101850190935280835260609492939192909184015b828210156111ef57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b0316818301528252600190920191016111a0565b505050509050919050565b6004602052816000526040600020818154811061121657600080fd5b6000918252602090912001546001600160401b0381169250600160401b90046001600160c01b0316905082565b600043821061128a5760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610cec565b6001600160a01b0383166000908152600460205260409020610bc29083612bdd565b60004382106112f35760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610cec565b610b7d600583612bdd565b61130a62020000611ff8565b61152d576001600160a01b0382163314801561132b575061132b600861114f565b8061134f57506001600160a01b038216331480159061134f575061134f601061114f565b6001600160a01b038316331461139a576040518060400160405280601c81526020017f6275726e73206f6e20626568616c66206172652064697361626c6564000000008152506113c6565b60405180604001604052806012815260200171189d5c9b9cc8185c9948191a5cd8589b195960721b8152505b906113e45760405162461bcd60e51b8152600401610cec91906131f8565b506001600160a01b038216331461152d576001600160a01b0382166000908152600860209081526040808320338452909152902054818110156114695760405162461bcd60e51b815260206004820152601d60248201527f6275726e20616d6f756e74206578636565647320616c6c6f77616e63650000006044820152606401610cec565b60001981101561152b5761147d8282613788565b6001600160a01b0384166000818152600860209081526040808320338085529252909120839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76114d3858561371e565b60408051918252602082018690520160405180910390a360405181815233906001600160a01b038516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259060200160405180910390a35b505b8060000361156f5760405162461bcd60e51b815260206004820152600f60248201526e3d32b937903b30b63ab290313ab93760891b6044820152606401610cec565b6001600160a01b0382166115c55760405162461bcd60e51b815260206004820152601a60248201527f6275726e2066726f6d20746865207a65726f20616464726573730000000000006044820152606401610cec565b6001600160a01b03821660009081526002602052604090205481111561162d5760405162461bcd60e51b815260206004820152601b60248201527f6275726e20616d6f756e7420657863656564732062616c616e636500000000006044820152606401610cec565b6001600160a01b03821660009081526002602052604081208054839290611655908490613788565b92505081905550806001600082825461166e9190613788565b90915550611682905060056120108361212d565b50506001600160a01b038083166000908152600360205260408120546116ad92339291169084612294565b6040518181526001600160a01b0383169033907fe8a89cc6e5096f9d9f43de82c077c1f4cfe707c0e0c2032176c68813b9ae6a5c9060200160405180910390a36040518181526000906001600160a01b0384169033907fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f9060200160405180910390a46040518181526000906001600160a01b038416906000805160206138fd83398151915290602001610f70565b3360009081526008602090815260408083206001600160a01b0386168452909152812054826117cd5760405162461bcd60e51b815260206004820152601c60248201527f7a65726f2076616c756520617070726f76616c206465637265617365000000006044820152606401610cec565b8281101561182b5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610cec565b610d03846104998584613788565b6000610bc2338484610bc9565b611853600160ff1b611ff8565b61186f5760405162461bcd60e51b8152600401610cec90613736565b6001600160a01b03821660009081526020819052604090205461189490339083611ed4565b6001600160a01b03831660008181526020818152604091829020849055815185815290810193909352909133917f5a10526456f5116c0b7b80582c217d666243fd51b6a2d92c8011e601c2462e5f9101610f70565b60006118f6858585610f7c565b611904858585856001612dc0565b506001949350505050565b6001600160a01b038116600090815260046020526040812080541561196f578054819061193e90600190613788565b8154811061194e5761194e61379f565b600091825260209091200154600160401b90046001600160c01b0316611972565b60005b6001600160c01b03169392505050565b600061198e608061114f565b6119da5760405162461bcd60e51b815260206004820152601e60248201527f45524331333633207472616e7366657273206172652064697361626c656400006044820152606401610cec565b6119e5858585610f7c565b611904858585856000612dc0565b6001600160a01b03821660009081526020819052604081205482168214610bc2565b6000611a2261010061114f565b611a6e5760405162461bcd60e51b815260206004820152601e60248201527f4552433133363320617070726f76616c73206172652064697361626c656400006044820152606401610cec565b611a788484610b9f565b50610c2c848484612ef5565b6000611a92828401846132a8565b9050611a9e8482610d19565b50505050565b611aaf61020061114f565b611afb5760405162461bcd60e51b815260206004820152601c60248201527f45495032363132207065726d697473206172652064697361626c6564000000006044820152606401610cec565b6001600160a01b03871660009081526006602052604081208054611b8c917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b9187611b4b836137b5565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e001610fda565b9050876001600160a01b0316816001600160a01b031614611bbf5760405162461bcd60e51b8152600401610cec9061375d565b844210611bde5760405162461bcd60e51b8152600401610cec906137ce565b611be988888861201c565b5050505050505050565b610c403082611846565b6000610d0384848460405180602001604052806000815250611982565b611c2561040061114f565b611c715760405162461bcd60e51b815260206004820152601e60248201527f45495033303039207472616e7366657273206172652064697361626c656400006044820152606401610cec565b6000611cb67f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760001b8b8b8b8b8b8b604051602001610fda97969594939291906137f9565b9050896001600160a01b0316816001600160a01b031614611ce95760405162461bcd60e51b8152600401610cec9061375d565b864211611d325760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610cec565b854210611d515760405162461bcd60e51b8152600401610cec906137ce565b611d5d8a866000612a33565b611d69818b8b8b6123e3565b50505050505050505050565b611d8061080061114f565b611dcc5760405162461bcd60e51b815260206004820152601f60248201527f4549503330303920726563657074696f6e73206172652064697361626c6564006044820152606401610cec565b6000611e117fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860001b8b8b8b8b8b8b604051602001610fda97969594939291906137f9565b9050896001600160a01b0316816001600160a01b031614611e445760405162461bcd60e51b8152600401610cec9061375d565b864211611e8d5760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610cec565b854210611eac5760405162461bcd60e51b8152600401610cec906137ce565b6001600160a01b0389163314611d515760405162461bcd60e51b8152600401610cec90613736565b6001600160a01b03929092166000908152602081905260409020546000198084188216189216171690565b611f09604061114f565b611f605760405162461bcd60e51b815260206004820152602260248201527f64656c65676174696f6e73206f6e20626568616c66206172652064697361626c604482015261195960f21b6064820152608401610cec565b604080517fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75560208201526001600160a01b038816918101919091526060810186905260808101859052600090611fb89060a001610fda565b9050844210611fd95760405162461bcd60e51b8152600401610cec906137ce565b611fe581876000612a33565b611fef8188612b41565b50505050505050565b6000610b7d33836119f3565b6000610bc2828461371e565b6000610bc28284613788565b6001600160a01b0382166120725760405162461bcd60e51b815260206004820152601b60248201527f617070726f766520746f20746865207a65726f206164647265737300000000006044820152606401610cec565b6001600160a01b0383811660008181526008602090815260408083209487168084529482529182902080549086905582518181529182018690529392917fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a7910160405180910390a3826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161211f91815260200190565b60405180910390a350505050565b825460009081901561217a578454859061214990600190613788565b815481106121595761215961379f565b600091825260209091200154600160401b90046001600160c01b031661217d565b60005b6001600160c01b0316915061219682848663ffffffff16565b8554909150158015906121dd57508454439086906121b690600190613788565b815481106121c6576121c661379f565b6000918252602090912001546001600160401b0316145b1561223a578454819086906121f490600190613788565b815481106122045761220461379f565b9060005260206000200160000160086101000a8154816001600160c01b0302191690836001600160c01b0316021790555061228c565b604080518082019091526001600160401b0343811682526001600160c01b0380841660208085019182528954600181018b5560008b81529190912094519151909216600160401b029216919091179101555b935093915050565b816001600160a01b0316836001600160a01b031614806122b2575080155b611a9e576001600160a01b0383161561234a576001600160a01b038316600090815260046020526040812090806122ec836120108661212d565b91509150856001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c1848460405161233e929190918252602082015260400190565b60405180910390a35050505b6001600160a01b03821615611a9e576001600160a01b03821660009081526004602052604081209080612380836120048661212d565b91509150846001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c184846040516123d2929190918252602082015260400190565b60405180910390a350505050505050565b836001600160a01b0316836001600160a01b03161480156124095750612409600161114f565b806124365750836001600160a01b0316836001600160a01b0316141580156124365750612436600261114f565b846001600160a01b0316846001600160a01b03161461248a576040518060400160405280602081526020017f7472616e7366657273206f6e20626568616c66206172652064697361626c65648152506124ba565b604051806040016040528060168152602001751d1c985b9cd9995c9cc8185c9948191a5cd8589b195960521b8152505b906124d85760405162461bcd60e51b8152600401610cec91906131f8565b506001600160a01b03831661252f5760405162461bcd60e51b815260206004820152601e60248201527f7472616e736665722066726f6d20746865207a65726f206164647265737300006044820152606401610cec565b6001600160a01b0382166125855760405162461bcd60e51b815260206004820152601c60248201527f7472616e7366657220746f20746865207a65726f2061646472657373000000006044820152606401610cec565b816001600160a01b0316836001600160a01b0316036125fe5760405162461bcd60e51b815260206004820152602f60248201527f73656e64657220616e6420726563697069656e7420617265207468652073616d60448201526e6520285f66726f6d203d205f746f2960881b6064820152608401610cec565b306001600160a01b0383160361267c5760405162461bcd60e51b815260206004820152603f60248201527f696e76616c696420726563697069656e7420287472616e7366657220746f207460448201527f686520746f6b656e20736d61727420636f6e747261637420697473656c6629006064820152608401610cec565b806000036126c457816001600160a01b0316836001600160a01b03166000805160206138fd833981519152836040516126b791815260200190565b60405180910390a3611a9e565b836001600160a01b0316836001600160a01b031614612832576001600160a01b038084166000908152600860209081526040808320938816835292905220548181101561275d5760405162461bcd60e51b815260206004820152602160248201527f7472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636044820152606560f81b6064820152608401610cec565b600019811015612830576127718282613788565b6001600160a01b038086166000818152600860209081526040808320948b16808452949091529020839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76127cb858561371e565b60408051918252602082018690520160405180910390a3846001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161282791815260200190565b60405180910390a35b505b6001600160a01b03831660009081526002602052604090205481111561289a5760405162461bcd60e51b815260206004820152601f60248201527f7472616e7366657220616d6f756e7420657863656564732062616c616e6365006044820152606401610cec565b6001600160a01b038316600090815260026020526040812080548392906128c2908490613788565b90915550506001600160a01b038216600090815260026020526040812080548392906128ef90849061371e565b90915550506001600160a01b038084166000908152600360205260408082205485841683529120546129279287928116911684612294565b816001600160a01b0316836001600160a01b0316856001600160a01b03167fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f8460405161297691815260200190565b60405180910390a4816001600160a01b0316836001600160a01b03166000805160206138fd8339815191528360405161211f91815260200190565b835160208086019190912060405161190160f01b928101929092527f000000000000000000000000000000000000000000000000000000000000000060228301526042820181905260009182906062016040516020818303038152906040528051906020012090506000612a2782888888613015565b98975050505050505050565b6001600160a01b038316600090815260076020908152604080832085845290915290205460ff1615612a975760405162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b6044820152606401610cec565b6001600160a01b03831660009081526007602090815260408083208584529091529020805460ff191660011790558015612b065760405182906001600160a01b038516907f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8190600090a3505050565b60405182906001600160a01b038516907f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a590600090a3505050565b6001600160a01b0380831660009081526003602081815260408084208054600284529190942054929091528484166001600160a01b0319821617909255911690612b8d84838584612294565b826001600160a01b0316826001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a450505050565b81546000908103612bf057506000610b7d565b825482908490612c0290600190613788565b81548110612c1257612c1261379f565b6000918252602090912001546001600160401b031611612c6f5782548390612c3c90600190613788565b81548110612c4c57612c4c61379f565b600091825260209091200154600160401b90046001600160c01b03169050610b7d565b8183600081548110612c8357612c8361379f565b6000918252602090912001546001600160401b03161115612ca657506000610b7d565b82546000908190612cb990600190613788565b90505b81811115612d895760006002612cd28484613788565b612cdc9190613835565b612ce69083613788565b90506000868281548110612cfc57612cfc61379f565b6000918252602091829020604080518082019091529101546001600160401b038116808352600160401b9091046001600160c01b0316928201929092529150869003612d5a57602001516001600160c01b03169350610b7d92505050565b80516001600160401b0316861115612d7457819350612d82565b612d7f600183613788565b92505b5050612cbc565b848281548110612d9b57612d9b61379f565b600091825260209091200154600160401b90046001600160c01b031695945050505050565b833b612e085780612e035760405162461bcd60e51b815260206004820152600d60248201526c1153d0481c9958da5c1a595b9d609a1b6044820152606401610cec565b612eee565b604051632229f29760e21b81526000906001600160a01b038616906388a7ca5c90612e3d9033908a9089908990600401613857565b6020604051808303816000875af1158015612e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e809190613894565b90506001600160e01b03198116632229f29760e21b146110305760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e5472616e73666572526563656976656420726573706f6044820152626e736560e81b6064820152608401610cec565b5050505050565b823b612f315760405162461bcd60e51b815260206004820152600b60248201526a22a7a09039b832b73232b960a91b6044820152606401610cec565b6040516307b04a2d60e41b81526000906001600160a01b03851690637b04a2d090612f64903390879087906004016138b1565b6020604051808303816000875af1158015612f83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fa79190613894565b90506001600160e01b031981166307b04a2d60e41b14611a9e5760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e417070726f76616c526563656976656420726573706f6044820152626e736560e81b6064820152608401610cec565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156130875760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202773272076616c756500000000006044820152606401610cec565b8360ff16601b148061309c57508360ff16601c145b6130e85760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202776272076616c756500000000006044820152606401610cec565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa15801561313c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661316f5760405162461bcd60e51b8152600401610cec9061375d565b95945050505050565b6001600160e01b031981168114610c4057600080fd5b6000602082840312156131a057600080fd5b8135610bc281613178565b6000815180845260005b818110156131d1576020818501810151868301820152016131b5565b818111156131e3576000602083870101525b50601f01601f19169290920160200192915050565b602081526000610bc260208301846131ab565b80356001600160a01b038116811461322257600080fd5b919050565b6000806040838503121561323a57600080fd5b6132438361320b565b946020939093013593505050565b60006020828403121561326357600080fd5b610bc28261320b565b60008060006060848603121561328157600080fd5b61328a8461320b565b92506132986020850161320b565b9150604084013590509250925092565b6000602082840312156132ba57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126132e857600080fd5b81356001600160401b0380821115613302576133026132c1565b604051601f8301601f19908116603f0116810190828211818310171561332a5761332a6132c1565b8160405283815286602085880101111561334357600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561337857600080fd5b6133818461320b565b92506020840135915060408401356001600160401b038111156133a357600080fd5b6133af868287016132d7565b9150509250925092565b803560ff8116811461322257600080fd5b600080600080600060a086880312156133e257600080fd5b6133eb8661320b565b945060208601359350613400604087016133b9565b94979396509394606081013594506080013592915050565b602080825282518282018190526000919060409081850190868401855b8281101561346c57815180516001600160401b031685528601516001600160c01b0316868501529284019290850190600101613435565b5091979650505050505050565b6000806000806080858703121561348f57600080fd5b6134988561320b565b93506134a66020860161320b565b92506040850135915060608501356001600160401b038111156134c857600080fd5b6134d4878288016132d7565b91505092959194509250565b6000806000604084860312156134f557600080fd5b6134fe8461320b565b925060208401356001600160401b038082111561351a57600080fd5b818601915086601f83011261352e57600080fd5b81358181111561353d57600080fd5b87602082850101111561354f57600080fd5b6020830194508093505050509250925092565b600080600080600080600060e0888a03121561357d57600080fd5b6135868861320b565b96506135946020890161320b565b955060408801359450606088013593506135b0608089016133b9565b925060a0880135915060c0880135905092959891949750929550565b600080604083850312156135df57600080fd5b6135e88361320b565b91506135f66020840161320b565b90509250929050565b60008060008060008060008060006101208a8c03121561361e57600080fd5b6136278a61320b565b985061363560208b0161320b565b975060408a0135965060608a0135955060808a0135945060a08a0135935061365f60c08b016133b9565b925060e08a013591506101008a013590509295985092959850929598565b60008060006060848603121561369257600080fd5b61369b8461320b565b95602085013595506040909401359392505050565b60008060008060008060c087890312156136c957600080fd5b6136d28761320b565b955060208701359450604087013593506136ee606088016133b9565b92506080870135915060a087013590509295509295509295565b634e487b7160e01b600052601160045260246000fd5b6000821982111561373157613731613708565b500190565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b602080825260119082015270696e76616c6964207369676e617475726560781b604082015260600190565b60008282101561379a5761379a613708565b500390565b634e487b7160e01b600052603260045260246000fd5b6000600182016137c7576137c7613708565b5060010190565b6020808252601190820152701cda59db985d1d5c9948195e1c1a5c9959607a1b604082015260600190565b9687526001600160a01b0395861660208801529390941660408601526060850191909152608084015260a083019190915260c082015260e00190565b60008261385257634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061388a908301846131ab565b9695505050505050565b6000602082840312156138a657600080fd5b8151610bc281613178565b60018060a01b038416815282602082015260606040820152600061316f60608301846131ab56fe4172746966696369616c204c697175696420496e74656c6c6967656e636520546f6b656eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122028888ebb4f6293c3027e2bd106453c1def154fac001cd2af48019c23f294b6f564736f6c634300080f0033d1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c1

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106104495760003560e01c80637fd491b011610241578063c5ff500c1161013b578063e3ee160e116100c3578063ef55bec611610087578063ef55bec614610aa7578063f63c2f8214610aba578063f822d5aa14610ac2578063f9cf927114610ad5578063fcc2c07814610ae857600080fd5b8063e3ee160e14610a20578063e62cac7614610a33578063e7a324dc14610a3d578063e94a010214610a64578063e98f5ba714610a9d57600080fd5b8063d505accf1161010a578063d505accf14610987578063d5bb7f671461099a578063d8fbe994146109ad578063d9169487146109c0578063dd62ed3e146109e757600080fd5b8063c5ff500c14610944578063c688d6931461094e578063cae9ca5114610961578063cf2c52cb1461097457600080fd5b8063a0cc6a68116101c9578063b66dbdc51161018d578063b66dbdc5146108fb578063b88d4fde14610903578063bcc3f3bd14610916578063c0d6568d14610929578063c1d34b891461093157600080fd5b8063a0cc6a6814610890578063a457c2d7146108b7578063a9059cbb146108ca578063ae5b102e146108dd578063ae682e2e146108f057600080fd5b80638f6fba8c116102105780638f6fba8c1461082d57806394f4f9301461083557806395d89b4114610848578063981b24d01461086a5780639dc29fac1461087d57600080fd5b80637fd491b0146107c057806387793f3e146107d35780638a114e13146107fc5780638d4e57e61461082357600080fd5b80633644e515116103525780635e2dc2b7116102da578063725f36261161029e578063725f36261461073357806374d5e100146107465780637815ef0c146107665780637ecebe00146107795780637f2eecc31461079957600080fd5b80635e2dc2b7146106a957806364cb8b96146106b2578063653de620146106c75780636641d9a0146106d057806370a082311461070a57600080fd5b806340c10f191161032157806340c10f19146106545780634721272d1461066757806359b961ef146106705780635a049a70146106835780635c19a95c1461069657600080fd5b80633644e515146105ff57806339509351146106265780633e9c5f7e146106395780634000aea01461064157600080fd5b80631e0fa234116103d55780632d4c39ea116103a45780632d4c39ea1461058e5780632e1a7d4d1461059657806330adf81f146105ab578063313ce567146105d25780633177029f146105ec57600080fd5b80631e0fa234146104fe57806320606b701461053f57806323b872dd146105665780632b5214161461057957600080fd5b8063136d035f1161041c578063136d035f146104b157806313873a24146104cf57806318160ddd146104d75780631993f554146104ee5780631a0b04ea146104f657600080fd5b806301ffc9a71461044e57806306fdde0314610476578063095ea7b31461048b5780631296ee621461049e575b600080fd5b61046161045c36600461318e565b610afb565b60405190151581526020015b60405180910390f35b61047e610b83565b60405161046d91906131f8565b610461610499366004613227565b610b9f565b6104616104ac366004613227565b610bb5565b6104ba61080081565b60405163ffffffff909116815260200161046d565b6104ba608081565b6104e060015481565b60405190815260200161046d565b6104ba600481565b6104ba600881565b61052761050c366004613251565b6003602052600090815260409020546001600160a01b031681565b6040516001600160a01b03909116815260200161046d565b6104e07f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61046161057436600461326c565b610bc9565b306000908152602081905260409020546104e0565b6104ba604081565b6105a96105a43660046132a8565b610c36565b005b6104e07f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6105da601281565b60405160ff909116815260200161046d565b6104616105fa366004613227565b610c43565b6104e07f70bfa0aefbf99652a47eb777470e5bd1c1784af2530fc18dbab6886defcb04a581565b610461610634366004613227565b610c5f565b6104ba602081565b61046161064f366004613363565b610d0b565b6105a9610662366004613227565b610d19565b6104ba61020081565b6105a961067e36600461326c565b610f7c565b6105a96106913660046133ca565b610f8d565b6105a96106a4366004613251565b611038565b6104ba61040081565b6106ba611098565b60405161046d9190613418565b6104ba61010081565b6106e36106de3660046132a8565b611114565b604080516001600160401b0390931683526001600160c01b0390911660208301520161046d565b6104e0610718366004613251565b6001600160a01b031660009081526002602052604090205490565b6104616107413660046132a8565b61114f565b6104e0610754366004613251565b60006020819052908152604090205481565b6106ba610774366004613251565b611168565b6104e0610787366004613251565b60066020526000908152604090205481565b6104e07fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6106e36107ce366004613227565b6111fa565b6104e06107e1366004613251565b6001600160a01b031660009081526004602052604090205490565b6104e07f8d4fb97da97378ef7d0ad259aec651f42bd22c200159282baa58486bb390286b81565b6104ba6201000081565b6104ba600281565b6104e0610843366004613227565b611243565b61047e60405180604001604052806003815260200162414c4960e81b81525081565b6104e06108783660046132a8565b6112ac565b6105a961088b366004613227565b6112fe565b6104e07f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104616108c5366004613227565b61175c565b6104616108d8366004613227565b611839565b6105a96108eb366004613227565b611846565b6104e0600160ff1b81565b6005546104e0565b610461610911366004613479565b6118e9565b6104e0610924366004613251565b61190f565b6104ba600181565b61046161093f366004613479565b611982565b6104ba6204000081565b61046161095c366004613227565b6119f3565b61046161096f366004613363565b611a15565b6105a96109823660046134e0565b611a84565b6105a9610995366004613562565b611aa4565b6105a96109a83660046132a8565b611bf3565b6104616109bb36600461326c565b611bfd565b6104e07f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6104e06109f53660046135cc565b6001600160a01b03918216600090815260086020908152604080832093909416825291909152205490565b6105a9610a2e3660046135ff565b611c1a565b6104ba6202000081565b6104e07fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75581565b610461610a72366004613227565b6001600160a01b03919091166000908152600760209081526040808320938352929052205460ff1690565b6104ba6208000081565b6105a9610ab53660046135ff565b611d75565b6104ba601081565b6104e0610ad036600461367d565b611ed4565b6105a9610ae33660046136b0565b611eff565b610461610af63660046132a8565b611ff8565b60006001600160e01b031982166301ffc9a760e01b1480610b2c57506001600160e01b031982166336372b0760e01b145b80610b4757506001600160e01b0319821663b0202a1160e01b145b80610b6257506001600160e01b03198216634ec7fbed60e11b145b80610b7d57506001600160e01b03198216635ffa99dd60e11b145b92915050565b6040518060600160405280602481526020016138d96024913981565b6000610bac33848461201c565b50600192915050565b6000610bc2338484611bfd565b9392505050565b6000610bd5600461114f565b80610be85750610be883620400006119f3565b80610bfa5750610bfa62080000611ff8565b15610c0f57610c0a848484610f7c565b610c2c565b610c2a848484604051806020016040528060008152506118e9565b505b5060019392505050565b610c4033826112fe565b50565b6000610bc2838360405180602001604052806000815250611a15565b3360009081526008602090815260408083206001600160a01b03861684529091528120548281018110610cf55760405162461bcd60e51b815260206004820152603360248201527f7a65726f2076616c756520617070726f76616c20696e637265617365206f722060448201527261726974686d65746963206f766572666c6f7760681b60648201526084015b60405180910390fd5b610d0384610499858461371e565b949350505050565b6000610d0333858585611982565b610d2562010000611ff8565b610d415760405162461bcd60e51b8152600401610cec90613736565b6001600160a01b038216610d865760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b6044820152606401610cec565b60015481810111610de35760405162461bcd60e51b815260206004820152602160248201527f7a65726f2076616c7565206f722061726974686d65746963206f766572666c6f6044820152607760f81b6064820152608401610cec565b6001546001600160c01b0390610dfa90839061371e565b1115610e485760405162461bcd60e51b815260206004820152601f60248201527f746f74616c20737570706c79206f766572666c6f77202875696e7431393229006044820152606401610cec565b8060016000828254610e5a919061371e565b90915550506001600160a01b03821660009081526002602052604081208054839290610e8790849061371e565b90915550610e9b905060056120048361212d565b50506001600160a01b03808316600090815260036020526040812054610ec5923392911684612294565b6040518181526001600160a01b0383169033907f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f09060200160405180910390a36040518181526001600160a01b0383169060009033907fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f9060200160405180910390a46040518181526001600160a01b038316906000906000805160206138fd833981519152906020015b60405180910390a35050565b610f88338484846123e3565b505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b0387169181019190915260608101859052600090610ff1906080015b6040516020818303038152906040528585856129b1565b9050856001600160a01b0316816001600160a01b0316146110245760405162461bcd60e51b8152600401610cec9061375d565b61103086866001612a33565b505050505050565b611042602061114f565b61108e5760405162461bcd60e51b815260206004820152601860248201527f64656c65676174696f6e73206172652064697361626c656400000000000000006044820152606401610cec565b610c403382612b41565b60606005805480602002602001604051908101604052809291908181526020016000905b8282101561110b57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b0316818301528252600190920191016110bc565b50505050905090565b6005818154811061112457600080fd5b6000918252602090912001546001600160401b0381169150600160401b90046001600160c01b031682565b3060009081526020819052604081205482168214610b7d565b6001600160a01b0381166000908152600460209081526040808320805482518185028101850190935280835260609492939192909184015b828210156111ef57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b0316818301528252600190920191016111a0565b505050509050919050565b6004602052816000526040600020818154811061121657600080fd5b6000918252602090912001546001600160401b0381169250600160401b90046001600160c01b0316905082565b600043821061128a5760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610cec565b6001600160a01b0383166000908152600460205260409020610bc29083612bdd565b60004382106112f35760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610cec565b610b7d600583612bdd565b61130a62020000611ff8565b61152d576001600160a01b0382163314801561132b575061132b600861114f565b8061134f57506001600160a01b038216331480159061134f575061134f601061114f565b6001600160a01b038316331461139a576040518060400160405280601c81526020017f6275726e73206f6e20626568616c66206172652064697361626c6564000000008152506113c6565b60405180604001604052806012815260200171189d5c9b9cc8185c9948191a5cd8589b195960721b8152505b906113e45760405162461bcd60e51b8152600401610cec91906131f8565b506001600160a01b038216331461152d576001600160a01b0382166000908152600860209081526040808320338452909152902054818110156114695760405162461bcd60e51b815260206004820152601d60248201527f6275726e20616d6f756e74206578636565647320616c6c6f77616e63650000006044820152606401610cec565b60001981101561152b5761147d8282613788565b6001600160a01b0384166000818152600860209081526040808320338085529252909120839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76114d3858561371e565b60408051918252602082018690520160405180910390a360405181815233906001600160a01b038516907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259060200160405180910390a35b505b8060000361156f5760405162461bcd60e51b815260206004820152600f60248201526e3d32b937903b30b63ab290313ab93760891b6044820152606401610cec565b6001600160a01b0382166115c55760405162461bcd60e51b815260206004820152601a60248201527f6275726e2066726f6d20746865207a65726f20616464726573730000000000006044820152606401610cec565b6001600160a01b03821660009081526002602052604090205481111561162d5760405162461bcd60e51b815260206004820152601b60248201527f6275726e20616d6f756e7420657863656564732062616c616e636500000000006044820152606401610cec565b6001600160a01b03821660009081526002602052604081208054839290611655908490613788565b92505081905550806001600082825461166e9190613788565b90915550611682905060056120108361212d565b50506001600160a01b038083166000908152600360205260408120546116ad92339291169084612294565b6040518181526001600160a01b0383169033907fe8a89cc6e5096f9d9f43de82c077c1f4cfe707c0e0c2032176c68813b9ae6a5c9060200160405180910390a36040518181526000906001600160a01b0384169033907fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f9060200160405180910390a46040518181526000906001600160a01b038416906000805160206138fd83398151915290602001610f70565b3360009081526008602090815260408083206001600160a01b0386168452909152812054826117cd5760405162461bcd60e51b815260206004820152601c60248201527f7a65726f2076616c756520617070726f76616c206465637265617365000000006044820152606401610cec565b8281101561182b5760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610cec565b610d03846104998584613788565b6000610bc2338484610bc9565b611853600160ff1b611ff8565b61186f5760405162461bcd60e51b8152600401610cec90613736565b6001600160a01b03821660009081526020819052604090205461189490339083611ed4565b6001600160a01b03831660008181526020818152604091829020849055815185815290810193909352909133917f5a10526456f5116c0b7b80582c217d666243fd51b6a2d92c8011e601c2462e5f9101610f70565b60006118f6858585610f7c565b611904858585856001612dc0565b506001949350505050565b6001600160a01b038116600090815260046020526040812080541561196f578054819061193e90600190613788565b8154811061194e5761194e61379f565b600091825260209091200154600160401b90046001600160c01b0316611972565b60005b6001600160c01b03169392505050565b600061198e608061114f565b6119da5760405162461bcd60e51b815260206004820152601e60248201527f45524331333633207472616e7366657273206172652064697361626c656400006044820152606401610cec565b6119e5858585610f7c565b611904858585856000612dc0565b6001600160a01b03821660009081526020819052604081205482168214610bc2565b6000611a2261010061114f565b611a6e5760405162461bcd60e51b815260206004820152601e60248201527f4552433133363320617070726f76616c73206172652064697361626c656400006044820152606401610cec565b611a788484610b9f565b50610c2c848484612ef5565b6000611a92828401846132a8565b9050611a9e8482610d19565b50505050565b611aaf61020061114f565b611afb5760405162461bcd60e51b815260206004820152601c60248201527f45495032363132207065726d697473206172652064697361626c6564000000006044820152606401610cec565b6001600160a01b03871660009081526006602052604081208054611b8c917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b9187611b4b836137b5565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e001610fda565b9050876001600160a01b0316816001600160a01b031614611bbf5760405162461bcd60e51b8152600401610cec9061375d565b844210611bde5760405162461bcd60e51b8152600401610cec906137ce565b611be988888861201c565b5050505050505050565b610c403082611846565b6000610d0384848460405180602001604052806000815250611982565b611c2561040061114f565b611c715760405162461bcd60e51b815260206004820152601e60248201527f45495033303039207472616e7366657273206172652064697361626c656400006044820152606401610cec565b6000611cb67f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760001b8b8b8b8b8b8b604051602001610fda97969594939291906137f9565b9050896001600160a01b0316816001600160a01b031614611ce95760405162461bcd60e51b8152600401610cec9061375d565b864211611d325760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610cec565b854210611d515760405162461bcd60e51b8152600401610cec906137ce565b611d5d8a866000612a33565b611d69818b8b8b6123e3565b50505050505050505050565b611d8061080061114f565b611dcc5760405162461bcd60e51b815260206004820152601f60248201527f4549503330303920726563657074696f6e73206172652064697361626c6564006044820152606401610cec565b6000611e117fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860001b8b8b8b8b8b8b604051602001610fda97969594939291906137f9565b9050896001600160a01b0316816001600160a01b031614611e445760405162461bcd60e51b8152600401610cec9061375d565b864211611e8d5760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610cec565b854210611eac5760405162461bcd60e51b8152600401610cec906137ce565b6001600160a01b0389163314611d515760405162461bcd60e51b8152600401610cec90613736565b6001600160a01b03929092166000908152602081905260409020546000198084188216189216171690565b611f09604061114f565b611f605760405162461bcd60e51b815260206004820152602260248201527f64656c65676174696f6e73206f6e20626568616c66206172652064697361626c604482015261195960f21b6064820152608401610cec565b604080517fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75560208201526001600160a01b038816918101919091526060810186905260808101859052600090611fb89060a001610fda565b9050844210611fd95760405162461bcd60e51b8152600401610cec906137ce565b611fe581876000612a33565b611fef8188612b41565b50505050505050565b6000610b7d33836119f3565b6000610bc2828461371e565b6000610bc28284613788565b6001600160a01b0382166120725760405162461bcd60e51b815260206004820152601b60248201527f617070726f766520746f20746865207a65726f206164647265737300000000006044820152606401610cec565b6001600160a01b0383811660008181526008602090815260408083209487168084529482529182902080549086905582518181529182018690529392917fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a7910160405180910390a3826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258460405161211f91815260200190565b60405180910390a350505050565b825460009081901561217a578454859061214990600190613788565b815481106121595761215961379f565b600091825260209091200154600160401b90046001600160c01b031661217d565b60005b6001600160c01b0316915061219682848663ffffffff16565b8554909150158015906121dd57508454439086906121b690600190613788565b815481106121c6576121c661379f565b6000918252602090912001546001600160401b0316145b1561223a578454819086906121f490600190613788565b815481106122045761220461379f565b9060005260206000200160000160086101000a8154816001600160c01b0302191690836001600160c01b0316021790555061228c565b604080518082019091526001600160401b0343811682526001600160c01b0380841660208085019182528954600181018b5560008b81529190912094519151909216600160401b029216919091179101555b935093915050565b816001600160a01b0316836001600160a01b031614806122b2575080155b611a9e576001600160a01b0383161561234a576001600160a01b038316600090815260046020526040812090806122ec836120108661212d565b91509150856001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c1848460405161233e929190918252602082015260400190565b60405180910390a35050505b6001600160a01b03821615611a9e576001600160a01b03821660009081526004602052604081209080612380836120048661212d565b91509150846001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c184846040516123d2929190918252602082015260400190565b60405180910390a350505050505050565b836001600160a01b0316836001600160a01b03161480156124095750612409600161114f565b806124365750836001600160a01b0316836001600160a01b0316141580156124365750612436600261114f565b846001600160a01b0316846001600160a01b03161461248a576040518060400160405280602081526020017f7472616e7366657273206f6e20626568616c66206172652064697361626c65648152506124ba565b604051806040016040528060168152602001751d1c985b9cd9995c9cc8185c9948191a5cd8589b195960521b8152505b906124d85760405162461bcd60e51b8152600401610cec91906131f8565b506001600160a01b03831661252f5760405162461bcd60e51b815260206004820152601e60248201527f7472616e736665722066726f6d20746865207a65726f206164647265737300006044820152606401610cec565b6001600160a01b0382166125855760405162461bcd60e51b815260206004820152601c60248201527f7472616e7366657220746f20746865207a65726f2061646472657373000000006044820152606401610cec565b816001600160a01b0316836001600160a01b0316036125fe5760405162461bcd60e51b815260206004820152602f60248201527f73656e64657220616e6420726563697069656e7420617265207468652073616d60448201526e6520285f66726f6d203d205f746f2960881b6064820152608401610cec565b306001600160a01b0383160361267c5760405162461bcd60e51b815260206004820152603f60248201527f696e76616c696420726563697069656e7420287472616e7366657220746f207460448201527f686520746f6b656e20736d61727420636f6e747261637420697473656c6629006064820152608401610cec565b806000036126c457816001600160a01b0316836001600160a01b03166000805160206138fd833981519152836040516126b791815260200190565b60405180910390a3611a9e565b836001600160a01b0316836001600160a01b031614612832576001600160a01b038084166000908152600860209081526040808320938816835292905220548181101561275d5760405162461bcd60e51b815260206004820152602160248201527f7472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636044820152606560f81b6064820152608401610cec565b600019811015612830576127718282613788565b6001600160a01b038086166000818152600860209081526040808320948b16808452949091529020839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76127cb858561371e565b60408051918252602082018690520160405180910390a3846001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9258360405161282791815260200190565b60405180910390a35b505b6001600160a01b03831660009081526002602052604090205481111561289a5760405162461bcd60e51b815260206004820152601f60248201527f7472616e7366657220616d6f756e7420657863656564732062616c616e6365006044820152606401610cec565b6001600160a01b038316600090815260026020526040812080548392906128c2908490613788565b90915550506001600160a01b038216600090815260026020526040812080548392906128ef90849061371e565b90915550506001600160a01b038084166000908152600360205260408082205485841683529120546129279287928116911684612294565b816001600160a01b0316836001600160a01b0316856001600160a01b03167fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f8460405161297691815260200190565b60405180910390a4816001600160a01b0316836001600160a01b03166000805160206138fd8339815191528360405161211f91815260200190565b835160208086019190912060405161190160f01b928101929092527f70bfa0aefbf99652a47eb777470e5bd1c1784af2530fc18dbab6886defcb04a560228301526042820181905260009182906062016040516020818303038152906040528051906020012090506000612a2782888888613015565b98975050505050505050565b6001600160a01b038316600090815260076020908152604080832085845290915290205460ff1615612a975760405162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b6044820152606401610cec565b6001600160a01b03831660009081526007602090815260408083208584529091529020805460ff191660011790558015612b065760405182906001600160a01b038516907f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8190600090a3505050565b60405182906001600160a01b038516907f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a590600090a3505050565b6001600160a01b0380831660009081526003602081815260408084208054600284529190942054929091528484166001600160a01b0319821617909255911690612b8d84838584612294565b826001600160a01b0316826001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a450505050565b81546000908103612bf057506000610b7d565b825482908490612c0290600190613788565b81548110612c1257612c1261379f565b6000918252602090912001546001600160401b031611612c6f5782548390612c3c90600190613788565b81548110612c4c57612c4c61379f565b600091825260209091200154600160401b90046001600160c01b03169050610b7d565b8183600081548110612c8357612c8361379f565b6000918252602090912001546001600160401b03161115612ca657506000610b7d565b82546000908190612cb990600190613788565b90505b81811115612d895760006002612cd28484613788565b612cdc9190613835565b612ce69083613788565b90506000868281548110612cfc57612cfc61379f565b6000918252602091829020604080518082019091529101546001600160401b038116808352600160401b9091046001600160c01b0316928201929092529150869003612d5a57602001516001600160c01b03169350610b7d92505050565b80516001600160401b0316861115612d7457819350612d82565b612d7f600183613788565b92505b5050612cbc565b848281548110612d9b57612d9b61379f565b600091825260209091200154600160401b90046001600160c01b031695945050505050565b833b612e085780612e035760405162461bcd60e51b815260206004820152600d60248201526c1153d0481c9958da5c1a595b9d609a1b6044820152606401610cec565b612eee565b604051632229f29760e21b81526000906001600160a01b038616906388a7ca5c90612e3d9033908a9089908990600401613857565b6020604051808303816000875af1158015612e5c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e809190613894565b90506001600160e01b03198116632229f29760e21b146110305760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e5472616e73666572526563656976656420726573706f6044820152626e736560e81b6064820152608401610cec565b5050505050565b823b612f315760405162461bcd60e51b815260206004820152600b60248201526a22a7a09039b832b73232b960a91b6044820152606401610cec565b6040516307b04a2d60e41b81526000906001600160a01b03851690637b04a2d090612f64903390879087906004016138b1565b6020604051808303816000875af1158015612f83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612fa79190613894565b90506001600160e01b031981166307b04a2d60e41b14611a9e5760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e417070726f76616c526563656976656420726573706f6044820152626e736560e81b6064820152608401610cec565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156130875760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202773272076616c756500000000006044820152606401610cec565b8360ff16601b148061309c57508360ff16601c145b6130e85760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202776272076616c756500000000006044820152606401610cec565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa15801561313c573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661316f5760405162461bcd60e51b8152600401610cec9061375d565b95945050505050565b6001600160e01b031981168114610c4057600080fd5b6000602082840312156131a057600080fd5b8135610bc281613178565b6000815180845260005b818110156131d1576020818501810151868301820152016131b5565b818111156131e3576000602083870101525b50601f01601f19169290920160200192915050565b602081526000610bc260208301846131ab565b80356001600160a01b038116811461322257600080fd5b919050565b6000806040838503121561323a57600080fd5b6132438361320b565b946020939093013593505050565b60006020828403121561326357600080fd5b610bc28261320b565b60008060006060848603121561328157600080fd5b61328a8461320b565b92506132986020850161320b565b9150604084013590509250925092565b6000602082840312156132ba57600080fd5b5035919050565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126132e857600080fd5b81356001600160401b0380821115613302576133026132c1565b604051601f8301601f19908116603f0116810190828211818310171561332a5761332a6132c1565b8160405283815286602085880101111561334357600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561337857600080fd5b6133818461320b565b92506020840135915060408401356001600160401b038111156133a357600080fd5b6133af868287016132d7565b9150509250925092565b803560ff8116811461322257600080fd5b600080600080600060a086880312156133e257600080fd5b6133eb8661320b565b945060208601359350613400604087016133b9565b94979396509394606081013594506080013592915050565b602080825282518282018190526000919060409081850190868401855b8281101561346c57815180516001600160401b031685528601516001600160c01b0316868501529284019290850190600101613435565b5091979650505050505050565b6000806000806080858703121561348f57600080fd5b6134988561320b565b93506134a66020860161320b565b92506040850135915060608501356001600160401b038111156134c857600080fd5b6134d4878288016132d7565b91505092959194509250565b6000806000604084860312156134f557600080fd5b6134fe8461320b565b925060208401356001600160401b038082111561351a57600080fd5b818601915086601f83011261352e57600080fd5b81358181111561353d57600080fd5b87602082850101111561354f57600080fd5b6020830194508093505050509250925092565b600080600080600080600060e0888a03121561357d57600080fd5b6135868861320b565b96506135946020890161320b565b955060408801359450606088013593506135b0608089016133b9565b925060a0880135915060c0880135905092959891949750929550565b600080604083850312156135df57600080fd5b6135e88361320b565b91506135f66020840161320b565b90509250929050565b60008060008060008060008060006101208a8c03121561361e57600080fd5b6136278a61320b565b985061363560208b0161320b565b975060408a0135965060608a0135955060808a0135945060a08a0135935061365f60c08b016133b9565b925060e08a013591506101008a013590509295985092959850929598565b60008060006060848603121561369257600080fd5b61369b8461320b565b95602085013595506040909401359392505050565b60008060008060008060c087890312156136c957600080fd5b6136d28761320b565b955060208701359450604087013593506136ee606088016133b9565b92506080870135915060a087013590509295509295509295565b634e487b7160e01b600052601160045260246000fd5b6000821982111561373157613731613708565b500190565b6020808252600d908201526c1858d8d95cdcc819195b9a5959609a1b604082015260600190565b602080825260119082015270696e76616c6964207369676e617475726560781b604082015260600190565b60008282101561379a5761379a613708565b500390565b634e487b7160e01b600052603260045260246000fd5b6000600182016137c7576137c7613708565b5060010190565b6020808252601190820152701cda59db985d1d5c9948195e1c1a5c9959607a1b604082015260600190565b9687526001600160a01b0395861660208801529390941660408601526060850191909152608084015260a083019190915260c082015260e00190565b60008261385257634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061388a908301846131ab565b9695505050505050565b6000602082840312156138a657600080fd5b8151610bc281613178565b60018060a01b038416815282602082015260606040820152600061316f60608301846131ab56fe4172746966696369616c204c697175696420496e74656c6c6967656e636520546f6b656eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122028888ebb4f6293c3027e2bd106453c1def154fac001cd2af48019c23f294b6f564736f6c634300080f0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

OVERVIEW

Alethea AI is a research and development studio building at the intersection of two of the most transformative technologies of our time: Generative AI and Blockchain. It aims to use these technologies to enable decentralized ownership and democratic governance of AI.

Validator Index Block Amount
View All Withdrawals

Txn Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.