ETH Price: $1,932.07 (-4.09%)
Gas: 0.04 Gwei
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

TokenTracker

Lizcoin (LIZ) ($0.00)

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Approve245155462026-02-22 22:56:595 days ago1771801019IN
Lizard Labs: LIZ Token
0 ETH0.000003690.07574204
Approve244644832026-02-15 20:05:4712 days ago1771185947IN
Lizard Labs: LIZ Token
0 ETH0.000002160.04471967
Approve244619692026-02-15 11:40:5912 days ago1771155659IN
Lizard Labs: LIZ Token
0 ETH0.000001240.0469278
Approve244497732026-02-13 18:53:1114 days ago1771008791IN
Lizard Labs: LIZ Token
0 ETH0.000009190.18982315
Approve244166602026-02-09 3:55:3518 days ago1770609335IN
Lizard Labs: LIZ Token
0 ETH0.000001760.06667301
Approve244135882026-02-08 17:36:3519 days ago1770572195IN
Lizard Labs: LIZ Token
0 ETH0.000099442.05053958
Approve243933612026-02-05 21:38:3522 days ago1770327515IN
Lizard Labs: LIZ Token
0 ETH0.000462889.54486179
Approve242893242026-01-22 9:06:5936 days ago1769072819IN
Lizard Labs: LIZ Token
0 ETH0.000001480.05618091
Approve242647072026-01-18 22:43:1140 days ago1768776191IN
Lizard Labs: LIZ Token
0 ETH0.000006270.12949414
Transfer242631152026-01-18 17:24:1140 days ago1768757051IN
Lizard Labs: LIZ Token
0 ETH0.000001080.02723681
Approve241955392026-01-09 7:10:2349 days ago1767942623IN
Lizard Labs: LIZ Token
0 ETH0.000014050.53077425
Approve241791862026-01-07 0:21:2352 days ago1767745283IN
Lizard Labs: LIZ Token
0 ETH0.000005510.11312504
Approve241367362026-01-01 2:16:5957 days ago1767233819IN
Lizard Labs: LIZ Token
0 ETH0.000098372.02860207
Approve241344482025-12-31 18:37:5958 days ago1767206279IN
Lizard Labs: LIZ Token
0 ETH0.000101032.08811593
Approve241190212025-12-29 14:52:3560 days ago1767019955IN
Lizard Labs: LIZ Token
0 ETH0.000014930.5655039
Approve241155462025-12-29 3:14:4760 days ago1766978087IN
Lizard Labs: LIZ Token
0 ETH0.000001220.0463184
Approve241108982025-12-28 11:41:5961 days ago1766922119IN
Lizard Labs: LIZ Token
0 ETH0.000025550.52712911
Approve241108912025-12-28 11:40:3561 days ago1766922035IN
Lizard Labs: LIZ Token
0 ETH0.000025470.52610087
Approve241047822025-12-27 15:11:4762 days ago1766848307IN
Lizard Labs: LIZ Token
0 ETH0.000002290.04730071
Approve241018282025-12-27 5:18:2362 days ago1766812703IN
Lizard Labs: LIZ Token
0 ETH0.000025570.52728679
Approve240769822025-12-23 18:03:5966 days ago1766513039IN
Lizard Labs: LIZ Token
0 ETH0.000003330.06855119
Approve240743812025-12-23 9:20:4766 days ago1766481647IN
Lizard Labs: LIZ Token
0 ETH0.000014050.53203736
Approve240332672025-12-17 15:36:3572 days ago1765985795IN
Lizard Labs: LIZ Token
0 ETH0.000060461.24860203
Approve240332552025-12-17 15:34:1172 days ago1765985651IN
Lizard Labs: LIZ Token
0 ETH0.00004350.89833813
Approve240079722025-12-14 2:51:2375 days ago1765680683IN
Lizard Labs: LIZ Token
0 ETH0.000014130.53516209
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
LizcoinERC20

Compiler Version
v0.8.20+commit.a1b79de6

Optimization Enabled:
Yes with 200 runs

Other Settings:
paris EvmVersion, MIT license
File 1 of 11 : LizcoinERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@lazy-sol/advanced-erc20/contracts/token/AdvancedERC20.sol";

/**
 *                                                                                                               
 *                                            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑                                            
 *                                      ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑                                     
 *                                ↑↑↑↑↑↑↑↑↑↑↑↑↑                     ↑↑↑↑↑↑↑↑↑↑↑↑↑↑                               
 *                             ↑↑↑↑↑↑↑↑↑↑↑     ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑        ↑↑↑↑↑↑↑↑↑↑                            
 *                          ↑↑↑↑↑↑↑↑      ↑↑↑                          ↑↑↑      ↑↑↑↑↑↑↑↑                         
 *                       ↑↑↑↑↑↑↑↑     ↑↑      ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑     ↑↑     ↑↑↑↑↑↑↑↑                      
 *                     ↑↑↑↑↑↑↑           ↑↑↑↑↑↑                      ↑↑↑↑↑↑           ↑↑↑↑↑↑↑                    
 *                   ↑↑↑↑↑↑           ↑↑↑                                  ↑↑↑           ↑↑↑↑↑↑                  
 *                 ↑↑↑↑↑↑          ↑↑                                          ↑↑          ↑↑↑↑↑↑                
 *               ↑↑↑↑↑↑                                                                      ↑↑↑↑↑↑              
 *             ↑↑↑↑↑    ↑↑                                                                ↑↑    ↑↑↑↑             
 *            ↑↑↑↑   ↑↑↑                                                                    ↑↑    ↑↑↑↑           
 *           ↑↑↑↑  ↑     ↑                                                                ↑↑    ↑↑ ↑↑↑↑          
 *          ↑↑↑  ↑    ↑↑                                                                    ↑↑    ↑  ↑↑↑         
 *        ↑↑↑↑  ↑    ↑↑                                                                      ↑↑     ↑ ↑↑↑        
 *        ↑↑↑ ↑    ↑↑↑                                                                        ↑↑↑    ↑ ↑↑↑       
 *       ↑↑↑ ↑    ↑↑                             ↑↑↑↑↑↑↑↑↑↑                                     ↑↑    ↑ ↑↑↑      
 *      ↑↑↑ ↑    ↑↑                            ↑↑↑↑       ↑↑↑↑↑↑↑↑↑                              ↑↑    ↑ ↑↑↑     
 *     ↑↑↑ ↑    ↑↑                       ↑↑↑↑↑↑↑    ↑↑↑↑         ↑↑↑↑↑                            ↑↑    ↑ ↑↑↑    
 *     ↑↑↑      ↑                    ↑↑↑↑                 ↑↑↑↑↑↑↑↑  ↑↑↑                            ↑↑    ↑ ↑↑    
 *    ↑↑↑      ↑                   ↑↑↑    ↑↑↑↑↑↑↑↑↑↑↑   ↑↑↑   ↑   ↑↑↑ ↑↑↑                           ↑      ↑↑↑   
 *    ↑↑↑     ↑↑                  ↑↑  ↑↑↑↑      ↑↑↑↑↑↑ ↑↑  ↑↑↑   ↑  ↑↑  ↑↑↑↑                         ↑      ↑↑   
 *   ↑↑↑      ↑                  ↑  ↑                ↑ ↑↑ ↑       ↑ ↑↑    ↑↑↑↑                       ↑↑     ↑↑↑  
 *   ↑↑      ↑                  ↑↑     ↑             ↑  ↑ ↑↑  ↑↑ ↑↑ ↑↑  ↑↑   ↑↑↑                      ↑      ↑↑  
 *  ↑↑↑                         ↑   ↑ ↑  ↑↑          ↑↑  ↑↑  ↑↑↑↑  ↑↑ ↑↑↑↑↑↑↑  ↑↑↑                    ↑      ↑↑↑ 
 *  ↑↑↑                         ↑   ↑  ↑  ↑↑↑↑         ↑↑       ↑↑↑  ↑↑    ↑↑↑  ↑↑↑                   ↑      ↑↑↑ 
 *  ↑↑                          ↑↑   ↑  ↑↑   ↑↑↑↑       ↑↑↑↑       ↑↑↑       ↑↑↑  ↑↑                  ↑       ↑↑ 
 *  ↑↑                           ↑↑  ↑↑   ↑↑↑   ↑↑↑↑↑       ↑↑↑↑↑↑↑           ↑↑↑  ↑↑                 ↑↑      ↑↑ 
 *  ↑↑                            ↑↑  ↑↑↑   ↑↑↑↑↑    ↑↑↑↑↑↑↑↑↑                 ↑↑   ↑↑                ↑↑      ↑↑ 
 *  ↑↑                             ↑↑   ↑↑      ↑↑↑↑↑                           ↑↑   ↑↑               ↑       ↑↑ 
 *  ↑↑                              ↑↑↑  ↑↑↑        ↑↑↑↑↑↑↑↑↑↑↑↑↑                ↑↑↑  ↑↑              ↑       ↑↑ 
 *  ↑↑                               ↑↑↑   ↑↑↑                                    ↑↑↑  ↑↑             ↑       ↑↑ 
 *  ↑↑↑                                ↑↑↑   ↑↑                                    ↑↑   ↑↑                   ↑↑↑ 
 *  ↑↑↑     ↑ ↑                          ↑↑↑  ↑↑↑                                   ↑↑   ↑↑                  ↑↑  
 *   ↑↑↑    ↑  ↑                           ↑↑↑  ↑↑↑                                  ↑↑   ↑↑                 ↑↑  
 *   ↑↑↑     ↑ ↑                            ↑↑↑↑  ↑↑↑                                 ↑↑   ↑↑               ↑↑↑  
 *    ↑↑↑    ↑↑ ↑                             ↑↑↑↑  ↑↑↑                                ↑↑↑  ↑↑             ↑↑↑↑  
 *     ↑↑↑    ↑ ↑                               ↑↑↑↑  ↑↑↑↑                              ↑↑↑  ↑↑      ↑     ↑↑    
 *     ↑↑↑↑    ↑ ↑                                ↑↑↑↑  ↑↑↑↑                              ↑↑  ↑↑↑   ↑     ↑↑↑    
 *      ↑↑↑↑      ↑                                 ↑↑↑↑   ↑↑↑                             ↑↑↑  ↑↑       ↑↑↑↑    
 *       ↑↑↑↑      ↑                                   ↑↑↑   ↑↑↑                            ↑↑↑  ↑↑      ↑↑↑     
 *       ↑↑↑        ↑                                    ↑↑↑↑  ↑↑↑                            ↑↑↑↑     ↑↑↑↑      
 *        ↑↑↑↑       ↑                                     ↑↑↑↑ ↑↑↑                            ↑↑     ↑↑↑↑↑      
 *        ↑↑↑↑↑       ↑↑                                     ↑↑↑  ↑↑                                 ↑↑↑↑        
 *           ↑↑↑        ↑↑                                     ↑↑  ↑↑                               ↑↑↑          
 *           ↑↑↑↑        ↑↑↑                                    ↑↑  ↑↑                             ↑↑↑↑          
 *            ↑↑↑↑↑        ↑↑↑                                   ↑  ↑↑                           ↑↑↑↑↑           
 *             ↑↑↑↑↑↑         ↑↑↑                                ↑   ↑             ↑↑  ↑       ↑↑↑↑↑↑            
 *               ↑↑↑↑↑↑          ↑↑↑                             ↑  ↑↑          ↑↑           ↑↑↑↑↑↑              
 *                 ↑↑↑↑↑              ↑↑↑                       ↑↑  ↑↑      ↑↑    ↑↑↑     ↑↑↑↑↑↑                 
 *                   ↑↑↑↑↑        ↑↑      ↑↑↑                   ↑↑  ↑  ↑↑↑     ↑↑↑      ↑↑↑↑↑↑                   
 *                     ↑↑↑↑↑↑↑        ↑↑        ↑↑↑↑↑          ↑↑  ↑↑      ↑↑↑↑      ↑↑↑↑↑↑↑                     
 *                       ↑↑↑↑↑↑↑↑         ↑↑↑                   ↑↑↑↑   ↑↑↑       ↑↑↑↑↑↑↑↑↑                       
 *                          ↑↑↑↑↑↑↑↑             ↑↑↑↑↑↑↑↑↑↑    ↑↑↑↑           ↑ ↑↑↑↑↑↑↑                          
 *                             ↑↑↑↑↑↑↑↑↑↑↑                              ↑ ↑↑↑↑↑↑↑↑↑↑                             
 *                                ↑↑↑↑↑↑↑↑↑↑↑↑↑↑   ↑↑↑↑↑↑      ↑↑   ↑↑↑↑↑↑↑↑↑↑↑↑↑                                
 *                                       ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑                                      
 *                                             ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑                                            
 *                                                                                                               
 */

/**
 * @title Lizcoin ERC20
 *
 * @notice The ERC-20 token Lizcoin is the governance token for the Gaming Sub-DAO of Lizard Labs.
 *      While the studio focuses on immersive, interconnected gaming experiences, the token is primarily used for
 *      yield farming and revenue distribution for active participants, protocol governance, and liquidity staking.
 *
 * @notice Token Summary:
 *      - Symbol: LIZ
 *      - Name: Lizcoin
 *      - Decimals: 18
 *      - Initial total supply: 9B (1B to be minted as staking rewards)
 *      - Final total supply: 10B (not enforced by the token contract)
 *      - Initial supply holder (initial holder) address: 0xC93c904fFE3d55E15483eF37e38ECAF8Fe003Ba7
 *      - Mintability: configurable (initially enabled, but possible to revoke forever)
 *      - Burnability: configurable (initially enabled, but possible to revoke forever)
 *      - 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 the 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 Based on the https://github.com/lazy-sol/advanced-erc20 implementation
 *
 * @author Lizard Labs Core Contributors
 */
contract LizcoinERC20 is AdvancedERC20 {
	/**
	 * @dev Deploys the token smart contract,
	 *      sets token name, symbol, initial token supply
	 *
	 * @param _initialHolder initial holder of the token supply
	 */
	constructor(address _initialHolder) AdvancedERC20 (
		msg.sender, // _contractOwner smart contract owner (has minting/burning and all other permissions)
		"Lizcoin", // _name token name to set
		"LIZ", // _symbol token symbol to set
		_initialHolder, //  _initialHolder owner of the initial token supply
		// 9 bil + 15 mil + 16'358'635 + 147'227'717
		9_178_586_352 ether, // _initialSupply initial token supply (9.18 bil)
		0xFFFF // _initialFeatures RBAC features enabled initially
	) {}
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4; // custom errors (0.8.4)

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

/**
 * @title Initializable Role-based Access Control Core (I-RBAC-C)
 *
 * @notice Access control smart contract provides an API to check
 *      if a specific operation is permitted globally and/or
 *      if a particular user has a permission to execute it.
 *
 * @notice This contract is inherited by other contracts requiring the role-based access control (RBAC)
 *      protection for the restricted access functions
 *
 * @notice It deals with two main entities: features and roles.
 *
 * @notice Features are designed to be used to enable/disable public functions
 *      of the smart contract (used by a wide audience).
 * @notice User roles are designed to control the access to restricted functions
 *      of the smart contract (used by a limited set of maintainers).
 *
 * @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.
 *
 * @dev This is an initializable version of the RBAC, based on Zeppelin implementation,
 *      it can be used for EIP-1167 minimal proxies, for ERC1967 proxies, etc.
 *      see https://docs.openzeppelin.com/contracts/4.x/api/proxy#Clones
 *      see https://docs.openzeppelin.com/contracts/4.x/upgradeable
 *      see https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable
 *      see https://forum.openzeppelin.com/t/uups-proxies-tutorial-solidity-javascript/7786
 *      see https://eips.ethereum.org/EIPS/eip-1167
 *
 * @dev The 'core' version of the RBAC contract hides three rarely used external functions from the public ABI,
 *      making them internal and thus reducing the overall compiled implementation size.
 *      isFeatureEnabled() public -> _isFeatureEnabled() internal
 *      isSenderInRole() public -> _isSenderInRole() internal
 *      isOperatorInRole() public -> _isOperatorInRole() internal
 *
 * @custom:since 1.1.0
 *
 * @author Basil Gorin
 */
abstract contract InitializableAccessControlCore is Initializable {
	/**
	 * @dev Privileged addresses with defined roles/permissions
	 * @dev 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
	 *
	 * @dev We keep the mapping private to prevent direct writes to it from the inheriting
	 *      contracts, `getRole()` and `updateRole()` functions should be used instead
	 */
	mapping(address => uint256) private userRoles;

	/**
	 * @dev Empty reserved space in storage. The size of the __gap array is calculated so that
	 *      the amount of storage used by a contract always adds up to the 50.
	 *      See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
	 */
	uint256[49] private __gap;

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

	/**
	 * @notice Upgrade manager is responsible for smart contract upgrades,
	 *      see https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable
	 *      see https://docs.openzeppelin.com/contracts/4.x/upgradeable
	 *
	 * @dev Role ROLE_UPGRADE_MANAGER allows passing the _authorizeUpgrade() check
	 * @dev Role ROLE_UPGRADE_MANAGER has single bit at position 254 enabled
	 */
	uint256 public constant ROLE_UPGRADE_MANAGER = 0x4000000000000000000000000000000000000000000000000000000000000000;

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

	/**
	 * @notice Thrown when a function is executed by an account that does not have
	 *      the required access permission(s) (role)
	 *
	 * @dev This error is used to enforce role-based access control (RBAC) restrictions
	 */
	error AccessDenied();

	/**
	 * @dev Fired in updateRole() and updateFeatures()
	 *
	 * @param operator address which was granted/revoked permissions
	 * @param requested permissions requested
	 * @param assigned permissions effectively set
	 */
	event RoleUpdated(address indexed operator, uint256 requested, uint256 assigned);

	/**
	 * @notice Function modifier making a function defined as public behave as restricted
	 *      (so that only a pre-configured set of accounts can execute it)
	 *
	 * @param role the role transaction executor is required to have;
	 *      the function throws an "access denied" exception if this condition is not met
	 */
	modifier restrictedTo(uint256 role) {
		// verify the access permission
		_requireSenderInRole(role);

		// execute the rest of the function
		_;
	}

	/**
	 * @dev Creates/deploys the RBAC implementation to be used in a proxy
	 *
	 * @dev Note:
	 *      the implementation is already initialized and
	 *      `_postConstruct` is not executable on the implementation
	 *      `_postConstruct` is still available in the context of a proxy
	 *      and should be executed on the proxy deployment (in the same tx)
	 */
	constructor() initializer {}

	/**
	 * @dev Contract initializer, sets the contract owner to have full privileges
	 *
	 * @dev Can be executed only once, reverts when executed second time
	 *
	 * @dev IMPORTANT:
	 *      this function SHOULD be executed during proxy deployment (in the same transaction)
	 *
	 * @param _owner smart contract owner having full privileges, can be zero
	 * @param _features initial features mask of the contract, can be zero
	 */
	function _postConstruct(address _owner, uint256 _features) internal virtual onlyInitializing {
		// grant owner full privileges
		__setRole(_owner, FULL_PRIVILEGES_MASK, FULL_PRIVILEGES_MASK);
		// update initial features bitmask
		__setRole(address(this), _features, _features);
	}

	/**
	 * @dev Highest version that has been initialized.
	 *      Non-zero value means contract was already initialized.
	 * @dev see {Initializable}, {reinitializer}.
	 *
	 * @return highest version that has been initialized
	 */
	function getInitializedVersion() public view returns(uint64) {
		// delegate to `_getInitializedVersion`
		return _getInitializedVersion();
	}

	/**
	 * @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`
		return getRole(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 Reads the permissions (role) for a given user from the `userRoles` mapping
	 *      (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 Having a simple getter instead of making the mapping public
	 *      allows enforcing the encapsulation of the mapping and protects from
	 *      writing to it directly in the inheriting smart contracts
	 *
	 * @param operator address of a user to read permissions for,
	 *      or self address to read global features of the smart contract
	 */
	function getRole(address operator) public view returns(uint256) {
		// read the value from `userRoles` and return
		return userRoles[operator];
	}

	/**
	 * @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 self address 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
		_requireSenderInRole(ROLE_ACCESS_MANAGER);

		// evaluate the role and reassign it
		__setRole(operator, role, _evaluateBy(msg.sender, getRole(operator), role));
	}

	/**
	 * @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) internal view returns (uint256) {
		// read operator's permissions
		uint256 p = getRole(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 Ensures that the transaction sender has the required access permission(s) (role)
	 *
	 * @dev Reverts with an `AccessDenied` error if the sender does not have the required role
	 *
	 * @param required the set of permissions (role) that the transaction sender is required to have
	 */
	function _requireSenderInRole(uint256 required) internal view {
		// check if the transaction has the required permission(s),
		// reverting with the "access denied" error if not
		_requireAccessCondition(_isSenderInRole(required));
	}

	/**
	 * @notice Ensures that a specific condition is met
	 *
	 * @dev Reverts with an `AccessDenied` error if the condition is not met
	 *
	 * @param condition the condition that needs to be true for the function to proceed
	 */
	function _requireAccessCondition(bool condition) internal pure {
		// check if the condition holds
		if(!condition) {
			// revert with the "access denied" error if not
			revert AccessDenied();
		}
	}

	/**
	 * @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) internal 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) internal 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) internal view returns (bool) {
		// delegate call to `__hasRole`, passing operator's permissions (role)
		return __hasRole(getRole(operator), required);
	}

	/**
	 * @dev Sets the `assignedRole` role to the operator, logs both `requestedRole` and `actualRole`
	 *
	 * @dev Unsafe:
	 *      provides direct write access to `userRoles` mapping without any security checks,
	 *      doesn't verify the executor (msg.sender) permissions,
	 *      must be kept private at all times
	 *
	 * @param operator address of a user to alter permissions for,
	 *       or self address to alter global features of the smart contract
	 * @param requestedRole bitmask representing a set of permissions requested
	 *      to be enabled/disabled for a user specified, used only to be logged into event
	 * @param assignedRole bitmask representing a set of permissions to
	 *      enable/disable for a user specified, used to update the mapping and to be logged into event
	 */
	function __setRole(address operator, uint256 requestedRole, uint256 assignedRole) private {
		// assign the role to the operator
		userRoles[operator] = assignedRole;

		// fire an event
		emit RoleUpdated(operator, requestedRole, assignedRole);
	}

	/**
	 * @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) private pure returns (bool) {
		// check the bitmask for the role required and return the result
		return actual & required == required;
	}
}

// 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, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
}

// 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;
}

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

/**
 * @title Mintable ERC1363 Extension
 *
 * @notice Adds mint functions to the ERC1363 interface, these functions
 *      follow the same idea and logic as ERC1363 transferAndCall functions,
 *      allowing to notify the recipient ERC1363Receiver contract about the tokens received
 */
interface MintableERC1363 is ERC1363 {
	/**
	 * @notice Mint tokens to the receiver and then call `onTransferReceived` on the receiver
	 * @param to address The address which you want to mint to
	 * @param value uint256 The amount of tokens to be minted
	 * @return true unless throwing
	 */
	function mintAndCall(address to, uint256 value) external returns (bool);

	/**
	 * @notice Mint tokens to the receiver and then call `onTransferReceived` on the receiver
	 * @param to address The address which you want to mint to
	 * @param value uint256 The amount of tokens to be minted
	 * @param data bytes Additional data with no specified format, sent in call to `to`
	 * @return true unless throwing
	 */
	function mintAndCall(address to, uint256 value, bytes memory data) external returns (bool);
}

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

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

/**
 * @title Mintable/burnable ERC20 Extension
 *
 * @notice Adds mint/burn functions to the ERC20 interface;
 *      these functions are usually present in ERC20 implementations;
 *      they become a must for the bridged tokens since the bridge usually
 *      needs to have a way to mint tokens deposited from L1 to L2
 *      and to burn tokens to be withdrawn from L2 to L1
 */
interface MintableBurnableERC20 is ERC20 {
	/**
	 * @dev Mints (creates) some tokens to address specified
	 * @dev The value specified is treated as is without taking
	 *      into account what `decimals` value is
	 *
	 * @param to an address to mint tokens to
	 * @param value an amount of tokens to mint (create)
	 * @return success true on success, false otherwise
	 */
	function mint(address to, uint256 value) external returns (bool success);

	/**
	 * @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
	 *
	 * @param from an address to burn some tokens from
	 * @param value an amount of tokens to burn (destroy)
	 * @return success true on success, false otherwise
	 */
	function burn(address from, uint256 value) external returns (bool success);
}

File 8 of 11 : 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 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;
	}
}

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

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

import "@lazy-sol/access-control-upgradeable/contracts/InitializableAccessControlCore.sol";

/**
 * @title Advanced ERC20
 *
 * @notice Feature rich lightweight ERC20 implementation which is not built on top of OpenZeppelin ERC20 implementation.
 *      It uses some other OpenZeppelin code:
 *         - low level functions to work with ECDSA signatures (recover)
 *         - low level functions to work contract addresses (isContract)
 *         - OZ UUPS proxy and smart contracts upgradeability code
 *
 * @notice Token Summary:
 *      - Symbol: configurable (set on deployment)
 *      - Name: configurable (set on deployment)
 *      - Decimals: 18
 *      - Initial/maximum total supply: configurable (set on deployment)
 *      - Initial supply holder (initial holder) address: configurable (set on deployment)
 *      - Mintability: configurable (initially enabled, but possible to revoke forever)
 *      - Burnability: configurable (initially enabled, but possible to revoke forever)
 *      - 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 the 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,
 *        approvals, and restricted access mints (which are sometimes viewed as transfers from zero address);
 *        allows creation of smart contracts capable of executing callbacks - in response to token transfer, approval,
  *       and token minting - 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
 *
 * @notice This smart contract can be used as is, but also can be inherited and used as a template.
 *
 * @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 (Advanced ERC20 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 The code is based on Artificial Liquid Intelligence Token (ALI) developed by Alethea team
 * @dev Includes resolutions for ALI ERC20 Audit by Miguel Palhas, https://hackmd.io/@naps62/alierc20-audit
 *
 * @author Basil Gorin
 */
contract AdvancedERC20 is MintableERC1363, MintableBurnableERC20, EIP2612, EIP3009, InitializableAccessControlCore {
	/**
	 * @notice Name of the 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 name;

	/**
	 * @notice Symbol of the token
	 *
	 * @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 symbol;

	/**
	 * @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 "AdvancedERC20v1"
	 */
	// keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)")
	bytes32 public constant DOMAIN_TYPEHASH = 0x8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866;

	/**
	 * @notice EIP-712 contract domain separator,
	 *      see https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator
	 *      note: we specify contract version in its name
	 */
	function DOMAIN_SEPARATOR() public view override returns(bytes32) {
		// 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
		return keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes("AdvancedERC20v1")), block.chainid, address(this)));
	}

	/**
	 * @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 contractOwner smart contract owner (has minting/burning and all other permissions)
	 * @param _name token name to set
	 * @param _symbol token symbol to set
	 * @param initialHolder owner of the initial token supply
	 * @param initialSupply initial token supply
	 * @param initialFeatures RBAC features enabled initially
	 */
	constructor(
		address contractOwner,
		string memory _name,
		string memory _symbol,
		address initialHolder,
		uint256 initialSupply,
		uint256 initialFeatures
	) {
		// delegate to the same `postConstruct` function which would be used
		// by all the proxies to be deployed and to be pointing to this impl
		postConstruct(contractOwner, _name, _symbol, initialHolder, initialSupply, initialFeatures);
	}

	/**
	 * @dev "Constructor replacement" for a smart contract with a delayed initialization (post-deployment initialization)
	 *
	 * @param contractOwner smart contract owner (has minting/burning and all other permissions)
	 * @param _name token name to set
	 * @param _symbol token symbol to set
	 * @param initialHolder owner of the initial token supply
	 * @param initialSupply initial token supply value
	 * @param initialFeatures RBAC features enabled initially
	 */
	function postConstruct(
		address contractOwner,
		string memory _name,
		string memory _symbol,
		address initialHolder,
		uint256 initialSupply,
		uint256 initialFeatures
	) public initializer {
		// verify name and symbol are set
		require(bytes(_name).length > 0, "token name is not set");
		require(bytes(_symbol).length > 0, "token symbol is not set");

		// assign token name and symbol
		name = _name;
		symbol = _symbol;

		// 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)");

		// if there is an initial supply to mint
		if(initialSupply != 0) {
			// mint the initial supply
			__mint(initialHolder, initialSupply);
		}

		// if initial contract owner or features are specified
		if(contractOwner != address(0) || initialFeatures != 0) {
			// initialize the RBAC module
			_postConstruct(contractOwner, initialFeatures);
		}
	}

	/**
	 * @inheritdoc ERC165
	 */
	function supportsInterface(bytes4 interfaceId) public pure virtual 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 the ERC1363Receiver interface
	 * @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 the ERC1363Receiver interface
	 * @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 the ERC1363Receiver interface
	 * @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 the ERC1363Receiver interface
	 * @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 true unless throwing
	 */
	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 true unless throwing
	 */
	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(to.code.length == 0) { // !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(spender.code.length > 0, "EOA spender"); // AddressUtils.isContract(_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");

		// according to the Ethereum ERC20 token standard, it is possible to transfer
		// tokens to oneself using the transfer or transferFrom functions.
		// In both cases, the transfer will succeed as long as the sender has a sufficient balance of tokens.
		// 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 improved transfer event (arXiv:1907.00903)
			emit Transfer(by, from, to, value);

			// 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 true unless throwing
	 */
	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 true unless throwing
	 */
	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 uint192
	 *
	 * @param to the destination address to mint tokens to
	 * @param value an amount of tokens to mint (create)
	 * @return success true on success, throws otherwise
	 */
	function mint(address to, uint256 value) public override virtual returns(bool success) {
		// check if caller has sufficient permissions to mint tokens
		require(_isSenderInRole(ROLE_TOKEN_CREATOR), "access denied");

		// delegate call to unsafe `__mint`
		__mint(to, value);

		// always return true
		return true;
	}

	/**
	 * @dev Mints (creates) some tokens and then executes `onTransferReceived` callback on the receiver,
	 *      passing zero address as the token source address `from`
	 * @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 uint192
	 * @dev Throws if the destination address `to` is a smart contract not supporting ERC1363Receiver interface
	 *
	 * @param to the destination address to mint tokens to, can be an EAO
	 *      or a smart contract, implementing the ERC1363Receiver interface
	 * @param value amount of tokens to mint (create)
	 * @return success true on success, throws otherwise
	 */
	function safeMint(address to, uint256 value, bytes memory data) public virtual returns(bool success) {
		// first delegate call to `mint` to perform regular minting
		mint(to, value);

		// after the successful minting - check if receiver supports
		// ERC1363Receiver and execute a callback handler `onTransferReceived`,
		// reverting whole transaction on any error
		_notifyTransferred(address(0), to, value, data, true);

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

	/**
	 * @dev Mints (creates) some tokens and then executes `onTransferReceived` callback on the receiver,
	 *      passing zero address as the token source address `from`
	 * @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 uint192
	 * @dev Throws if the destination address `to` is EOA or smart contract not supporting ERC1363Receiver interface
	 *
	 * @param to the destination address to mint tokens to,
	 *      must be a smart contract, implementing the ERC1363Receiver interface
	 * @param value amount of tokens to mint (create)
	 * @return success true on success, throws otherwise
	 */
	function mintAndCall(address to, uint256 value) public override virtual returns (bool success) {
		// delegate to `mintAndCall` passing empty data param
		return mintAndCall(to, value, "");
	}

	/**
	 * @dev Mints (creates) some tokens and then executes `onTransferReceived` callback on the receiver,
	 *      passing zero address as the token source address `from`
	 * @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 uint192
	 * @dev Throws if the destination address `to` is EOA or smart contract not supporting ERC1363Receiver interface
	 *
	 * @param to the destination address to mint tokens to,
	 *      must be a smart contract, implementing the ERC1363Receiver interface
	 * @param value amount of tokens to mint (create)
	 * @param data [optional] additional data with no specified format,
	 *      sent in onTransferReceived call to `to`
	 * @return success true on success, throws otherwise
	 */
	function mintAndCall(address to, uint256 value, bytes memory data) public override virtual returns (bool success) {
		// first delegate call to `mint` to perform regular minting
		mint(to, value);

		// after the successful minting - check if receiver supports
		// ERC1363Receiver and execute a callback handler `onTransferReceived`,
		// reverting whole transaction on any error
		_notifyTransferred(address(0), to, value, data, false);

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

	/**
	 * @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 Unsafe: doesn't verify the executor (msg.sender) permissions,
	 *      must be kept private at all times
	 *
	 * @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) private {
		// 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)
	 * @return success true on success, throws otherwise
	 */
	function burn(address from, uint256 value) public override virtual returns(bool success) {
		// 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);

		// always return true
		return true;
	}

	// ===== 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
	 *
	 * @dev Recovers the non-zero signer address from the signed message throwing on failure
	 *
	 * @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
	 * @return recovered non-zero signer address, unless throwing
	 */
	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);

		// according to the specs, zero address must be rejected when using ecrecover
		// this check already happened inside `ECDSA.recover`

		// 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 `holder`
	 *
	 * @param holder 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 holder) public view returns (uint256) {
		// get a link to an array of voting power history records for an address specified
		KV[] storage history = votingPowerHistory[holder];

		// 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 `holder` at some block `blockNum`
	 *
	 * @dev Throws if `blockNum` is not in the past (not the finalized block)
	 *
	 * @param holder 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 holder, uint256 blockNum) public view returns (uint256) {
		// make sure block number is in the past (the finalized block)
		require(blockNum < block.number, "block not yet mined"); // Compound msg not yet determined

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

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

	/**
	 * @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 holder delegate to query voting power history length for
	 * @return voting power history array length for the delegate of interest
	 */
	function votingPowerHistoryLength(address holder) public view returns(uint256) {
		// read array length and return
		return votingPowerHistory[holder].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 in the past (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 m = j - (j - i) / 2;

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

			// 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 = m;
			}
			// 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 = m - 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) =====
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized != type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

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

pragma solidity ^0.8.1;

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

        return account.code.length > 0;
    }

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

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

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

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

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

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

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

Settings
{
  "evmVersion": "paris",
  "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

API
[{"inputs":[{"internalType":"address","name":"_initialHolder","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccessDenied","type":"error"},{"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":"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":"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":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","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":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"requested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assigned","type":"uint256"}],"name":"RoleUpdated","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":"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":"ROLE_UPGRADE_MANAGER","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":[{"internalType":"bool","name":"success","type":"bool"}],"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":[],"name":"entireSupplyHistory","outputs":[{"components":[{"internalType":"uint64","name":"k","type":"uint64"},{"internalType":"uint192","name":"v","type":"uint192"}],"internalType":"struct AdvancedERC20.KV[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"features","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitializedVersion","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"getRole","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":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"success","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":"mintAndCall","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":"mintAndCall","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"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":"contractOwner","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"initialHolder","type":"address"},{"internalType":"uint256","name":"initialSupply","type":"uint256"},{"internalType":"uint256","name":"initialFeatures","type":"uint256"}],"name":"postConstruct","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":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"safeMint","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":"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":"votingDelegates","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","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":"holder","type":"address"}],"name":"votingPowerHistoryLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"votingPowerHistoryOf","outputs":[{"components":[{"internalType":"uint64","name":"k","type":"uint64"},{"internalType":"uint192","name":"v","type":"uint192"}],"internalType":"struct AdvancedERC20.KV[]","name":"","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"holder","type":"address"}],"name":"votingPowerOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]

60806040523480156200001157600080fd5b5060405162004aa638038062004aa6833981016040819052620000349162000a45565b33604051806040016040528060078152602001662634bd31b7b4b760c91b815250604051806040016040528060038152602001622624ad60e91b815250836b1da8594328c1fd53ddc0000061ffff60008060019054906101000a900460ff16159050808015620000ab5750600054600160ff909116105b80620000c75750303b158015620000c7575060005460ff166001145b6200011f5760405162461bcd60e51b815260206004820152602e602482015260008051602062004a4683398151915260448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff19166001179055801562000143576000805461ff0019166101001790555b801562000179576000805461ff00191690556040516001815260008051602062004a668339815191529060200160405180910390a15b506200018a86868686868662000197565b5050505050505062000c40565b600054610100900460ff1615808015620001b85750600054600160ff909116105b80620001d45750303b158015620001d4575060005460ff166001145b620002285760405162461bcd60e51b815260206004820152602e602482015260008051602062004a4683398151915260448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840162000116565b6000805460ff1916600117905580156200024c576000805461ff0019166101001790555b60008651116200029f5760405162461bcd60e51b815260206004820152601560248201527f746f6b656e206e616d65206973206e6f74207365740000000000000000000000604482015260640162000116565b6000855111620002f25760405162461bcd60e51b815260206004820152601760248201527f746f6b656e2073796d626f6c206973206e6f7420736574000000000000000000604482015260640162000116565b603362000300878262000b1c565b5060346200030f868262000b1c565b508215806200032657506001600160a01b03841615155b620003825760405162461bcd60e51b815260206004820152602560248201527f5f696e697469616c486f6c646572206e6f742073657420287a65726f20616464604482015264726573732960d81b606482015260840162000116565b82156200039557620003958484620003fd565b6001600160a01b038716151580620003ac57508115155b15620003be57620003be878362000662565b8015620003f4576000805461ff00191690556040516001815260008051602062004a668339815191529060200160405180910390a15b50505050505050565b6001600160a01b038216620004445760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b604482015260640162000116565b60355481810111620004a35760405162461bcd60e51b815260206004820152602160248201527f7a65726f2076616c7565206f722061726974686d65746963206f766572666c6f6044820152607760f81b606482015260840162000116565b6035546001600160c01b0390620004bc90839062000bfe565b11156200050c5760405162461bcd60e51b815260206004820152601f60248201527f746f74616c20737570706c79206f766572666c6f77202875696e743139322900604482015260640162000116565b806035600082825462000520919062000bfe565b90915550506001600160a01b038216600090815260366020526040812080548392906200054f90849062000bfe565b909155506200056e90506039620020a4620006ef60201b178362000706565b50506001600160a01b038083166000908152603760205260408120546200059a92339291168462000889565b6040518181526001600160a01b0383169033907f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f09060200160405180910390a36040518181526001600160a01b0383169060009033907fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f9060200160405180910390a46040518181526001600160a01b038316906000907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9060200160405180910390a35050565b600054610100900460ff16620006cf5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b606482015260840162000116565b620006de8260001980620009dd565b620006eb308280620009dd565b5050565b6000620006fd828462000bfe565b90505b92915050565b82546000908190156200075f5784548590620007259060019062000c14565b8154811062000738576200073862000c2a565b6000918252602090912001546801000000000000000090046001600160c01b031662000762565b60005b6001600160c01b031691506200077982848660201c565b855490915015801590620007c657508454439086906200079c9060019062000c14565b81548110620007af57620007af62000c2a565b6000918252602090912001546001600160401b0316145b156200082a57845481908690620007e09060019062000c14565b81548110620007f357620007f362000c2a565b9060005260206000200160000160086101000a8154816001600160c01b0302191690836001600160c01b0316021790555062000881565b604080518082019091526001600160401b0343811682526001600160c01b0380841660208085019182528954600181018b5560008b8152919091209451915190921668010000000000000000029216919091179101555b935093915050565b816001600160a01b0316836001600160a01b03161480620008a8575080155b620009d7576001600160a01b0383161562000942576001600160a01b0383166000908152603860209081526040822091908190620008f490849062000a37901b620020b0178662000706565b91509150856001600160a01b0316876001600160a01b031660008051602062004a86833981519152848460405162000936929190918252602082015260400190565b60405180910390a35050505b6001600160a01b03821615620009d7576001600160a01b038216600090815260386020908152604082209190819062000989908490620006ef901b620020a4178662000706565b91509150846001600160a01b0316876001600160a01b031660008051602062004a868339815191528484604051620009cb929190918252602082015260400190565b60405180910390a35050505b50505050565b6001600160a01b03831660008181526001602090815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000620006fd828462000c14565b60006020828403121562000a5857600080fd5b81516001600160a01b038116811462000a7057600080fd5b9392505050565b634e487b7160e01b600052604160045260246000fd5b600181811c9082168062000aa257607f821691505b60208210810362000ac357634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000b1757600081815260208120601f850160051c8101602086101562000af25750805b601f850160051c820191505b8181101562000b135782815560010162000afe565b5050505b505050565b81516001600160401b0381111562000b385762000b3862000a77565b62000b508162000b49845462000a8d565b8462000ac9565b602080601f83116001811462000b88576000841562000b6f5750858301515b600019600386901b1c1916600185901b17855562000b13565b600085815260208120601f198616915b8281101562000bb95788860151825594840194600190910190840162000b98565b508582101562000bd85787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052601160045260246000fd5b8082018082111562000700576200070062000be8565b8181038181111562000700576200070062000be8565b634e487b7160e01b600052603260045260246000fd5b613df68062000c506000396000f3fe608060405234801561001057600080fd5b506004361061043e5760003560e01c806387793f3e11610236578063b88d4fde1161013b578063d9169487116100c3578063e94a010211610087578063e94a010214610a22578063e98f5ba714610a5b578063ef55bec614610a65578063f63c2f8214610a78578063f9cf927114610a8057600080fd5b8063d91694871461097e578063dd62ed3e146109a5578063e3ee160e146109de578063e62cac76146109f1578063e7a324dc146109fb57600080fd5b8063c5ff500c1161010a578063c5ff500c14610928578063cae9ca5114610932578063d505accf14610945578063d5bb7f6714610958578063d8fbe9941461096b57600080fd5b8063b88d4fde146108e7578063bcc3f3bd146108fa578063c0d6568d1461090d578063c1d34b891461091557600080fd5b80639dc29fac116101be578063ae5b102e1161018d578063ae5b102e146108a1578063ae60bda4146108b4578063ae682e2e146108bf578063b3c65015146108ca578063b66dbdc5146108df57600080fd5b80639dc29fac14610841578063a0cc6a6814610854578063a457c2d71461087b578063a9059cbb1461088e57600080fd5b806394f4f9301161020557806394f4f930146107ed57806395d89b411461080057806397ba461114610808578063981b24d01461081b57806398de4ba31461082e57600080fd5b806387793f3e1461079f5780638832e6e3146107c85780638d4e57e6146107db5780638f6fba8c146107e557600080fd5b806339509351116103475780635c19a95c116102cf57806370a082311161029357806370a08231146107095780637815ef0c146107325780637ecebe00146107455780637f2eecc3146107655780637fd491b01461078c57600080fd5b80635c19a95c146106955780635e2dc2b7146106a857806364cb8b96146106b1578063653de620146106c65780636641d9a0146106cf57600080fd5b8063442767331161031657806344276733146106285780634721272d1461065157806350c29e371461065a57806359b961ef1461066f5780635a049a701461068257600080fd5b806339509351146105e75780633e9c5f7e146105fa5780634000aea01461060257806340c10f191461061557600080fd5b80631e0fa234116103ca5780632d4c39ea116103995780632d4c39ea1461058357806330adf81f1461058b578063313ce567146105b25780633177029f146105cc5780633644e515146105df57600080fd5b80631e0fa234146104f357806320606b701461053457806323b872dd1461055b5780632b5214161461056e57600080fd5b8063136d035f11610411578063136d035f146104a657806313873a24146104c457806318160ddd146104cc5780631993f554146104e35780631a0b04ea146104eb57600080fd5b806301ffc9a71461044357806306fdde031461046b578063095ea7b3146104805780631296ee6214610493575b600080fd5b610456610451366004613567565b610a93565b60405190151581526020015b60405180910390f35b610473610b1b565b60405161046291906135ca565b61045661048e3660046135f9565b610ba9565b6104566104a13660046135f9565b610bbf565b6104af61080081565b60405163ffffffff9091168152602001610462565b6104af608081565b6104d560355481565b604051908152602001610462565b6104af600481565b6104af600881565b61051c610501366004613623565b6037602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610462565b6104d57f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61045661056936600461363e565b610bd3565b306000908152600160205260409020546104d5565b6104af604081565b6104d57f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6105ba601281565b60405160ff9091168152602001610462565b6104566105da3660046135f9565b610c40565b6104d5610c5c565b6104566105f53660046135f9565b610cf8565b6104af602081565b61045661061036600461371c565b610da4565b6104566106233660046135f9565b610db2565b6104d5610636366004613623565b6001600160a01b031660009081526001602052604090205490565b6104af61020081565b61066d610668366004613772565b610e06565b005b61066d61067d36600461363e565b61106d565b61066d610690366004613819565b61107e565b61066d6106a3366004613623565b611129565b6104af61040081565b6106b961118c565b6040516104629190613867565b6104af61010081565b6106e26106dd3660046138c8565b611208565b604080516001600160401b0390931683526001600160c01b03909116602083015201610462565b6104d5610717366004613623565b6001600160a01b031660009081526036602052604090205490565b6106b9610740366004613623565b611243565b6104d5610753366004613623565b603a6020526000908152604090205481565b6104d57fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6106e261079a3660046135f9565b6112d5565b6104d56107ad366004613623565b6001600160a01b031660009081526038602052604090205490565b6104566107d636600461371c565b61131e565b6104af6201000081565b6104af600281565b6104d56107fb3660046135f9565b61133a565b6104736113a3565b61045661081636600461371c565b6113b0565b6104d56108293660046138c8565b6113cc565b61045661083c3660046135f9565b61141e565b61045661084f3660046135f9565b61143a565b6104d57f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104566108893660046135f9565b611894565b61045661089c3660046135f9565b611971565b61066d6108af3660046135f9565b61197e565b6104d5600160fe1b81565b6104d5600160ff1b81565b60005460405160ff9091168152602001610462565b6039546104d5565b6104566108f53660046138e1565b6119eb565b6104d5610908366004613623565b611a11565b6104af600181565b6104566109233660046138e1565b611a84565b6104af6204000081565b61045661094036600461371c565b611af5565b61066d610953366004613948565b611b64565b61066d6109663660046138c8565b611cb3565b61045661097936600461363e565b611cbd565b6104d57f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6104d56109b33660046139b2565b6001600160a01b039182166000908152603c6020908152604080832093909416825291909152205490565b61066d6109ec3660046139e5565b611cda565b6104af6202000081565b6104d57fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75581565b610456610a303660046135f9565b6001600160a01b03919091166000908152603b60209081526040808320938352929052205460ff1690565b6104af6208000081565b61066d610a733660046139e5565b611e35565b6104af601081565b61066d610a8e366004613a63565b611fb4565b60006001600160e01b031982166301ffc9a760e01b1480610ac457506001600160e01b031982166336372b0760e01b145b80610adf57506001600160e01b0319821663b0202a1160e01b145b80610afa57506001600160e01b03198216634ec7fbed60e11b145b80610b1557506001600160e01b03198216635ffa99dd60e11b145b92915050565b60338054610b2890613aa1565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5490613aa1565b8015610ba15780601f10610b7657610100808354040283529160200191610ba1565b820191906000526020600020905b815481529060010190602001808311610b8457829003601f168201915b505050505081565b6000610bb63384846120bc565b50600192915050565b6000610bcc338484611cbd565b9392505050565b6000610bdf60046121cd565b80610bf25750610bf283620400006121e9565b80610c045750610c046208000061220b565b15610c1957610c1484848461106d565b610c36565b610c34848484604051806020016040528060008152506119eb565b505b5060019392505050565b6000610bcc838360405180602001604052806000815250611af5565b604080518082018252600f81526e416476616e6365644552433230763160881b60209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f65218aa0cdc6725da873801e9daf9d54f230d8a515289f76c8bd31dd8648c50d81840152466060820152306080808301919091528351808303909101815260a0909101909252815191012090565b336000908152603c602090815260408083206001600160a01b03861684529091528120548281018110610d8e5760405162461bcd60e51b815260206004820152603360248201527f7a65726f2076616c756520617070726f76616c20696e637265617365206f722060448201527261726974686d65746963206f766572666c6f7760681b60648201526084015b60405180910390fd5b610d9c8461048e8584613af1565b949350505050565b6000610d9c33858585611a84565b6000610dc06201000061220b565b610dfc5760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b6044820152606401610d85565b610bb68383612217565b600054610100900460ff1615808015610e265750600054600160ff909116105b80610e405750303b158015610e40575060005460ff166001145b610ea35760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610d85565b6000805460ff191660011790558015610ec6576000805461ff0019166101001790555b6000865111610f0f5760405162461bcd60e51b81526020600482015260156024820152741d1bdad95b881b985b59481a5cc81b9bdd081cd95d605a1b6044820152606401610d85565b6000855111610f605760405162461bcd60e51b815260206004820152601760248201527f746f6b656e2073796d626f6c206973206e6f74207365740000000000000000006044820152606401610d85565b6033610f6c8782613b4a565b506034610f798682613b4a565b50821580610f8f57506001600160a01b03841615155b610fe95760405162461bcd60e51b815260206004820152602560248201527f5f696e697469616c486f6c646572206e6f742073657420287a65726f20616464604482015264726573732960d81b6064820152608401610d85565b8215610ff957610ff98484612217565b6001600160a01b03871615158061100f57508115155b1561101e5761101e878361243f565b8015611064576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b611079338484846124c2565b505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b03871691810191909152606081018590526000906110e2906080015b604051602081830303815290604052858585612a50565b9050856001600160a01b0316816001600160a01b0316146111155760405162461bcd60e51b8152600401610d8590613c09565b61112186866001612ab5565b505050505050565b61113360206121cd565b61117f5760405162461bcd60e51b815260206004820152601860248201527f64656c65676174696f6e73206172652064697361626c656400000000000000006044820152606401610d85565b6111893382612bc3565b50565b60606039805480602002602001604051908101604052809291908181526020016000905b828210156111ff57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b0316818301528252600190920191016111b0565b50505050905090565b6039818154811061121857600080fd5b6000918252602090912001546001600160401b0381169150600160401b90046001600160c01b031682565b6001600160a01b0381166000908152603860209081526040808320805482518185028101850190935280835260609492939192909184015b828210156112ca57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b03168183015282526001909201910161127b565b505050509050919050565b603860205281600052604060002081815481106112f157600080fd5b6000918252602090912001546001600160401b0381169250600160401b90046001600160c01b0316905082565b600061132a8484610db2565b50610c3660008585856001612c5f565b60004382106113815760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610d85565b6001600160a01b0383166000908152603860205260409020610bcc9083612da0565b60348054610b2890613aa1565b60006113bc8484610db2565b50610c3660008585856000612c5f565b60004382106114135760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610d85565b610b15603983612da0565b6000610bcc8383604051806020016040528060008152506113b0565b60006114486202000061220b565b61166b576001600160a01b03831633148015611469575061146960086121cd565b8061148d57506001600160a01b038316331480159061148d575061148d60106121cd565b6001600160a01b03841633146114d8576040518060400160405280601c81526020017f6275726e73206f6e20626568616c66206172652064697361626c656400000000815250611504565b60405180604001604052806012815260200171189d5c9b9cc8185c9948191a5cd8589b195960721b8152505b906115225760405162461bcd60e51b8152600401610d8591906135ca565b506001600160a01b038316331461166b576001600160a01b0383166000908152603c60209081526040808320338452909152902054828110156115a75760405162461bcd60e51b815260206004820152601d60248201527f6275726e20616d6f756e74206578636565647320616c6c6f77616e63650000006044820152606401610d85565b600019811015611669576115bb8382613c34565b6001600160a01b0385166000818152603c60209081526040808320338085529252909120839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76116118685613af1565b60408051918252602082018690520160405180910390a360405181815233906001600160a01b038616907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259060200160405180910390a35b505b816000036116ad5760405162461bcd60e51b815260206004820152600f60248201526e3d32b937903b30b63ab290313ab93760891b6044820152606401610d85565b6001600160a01b0383166117035760405162461bcd60e51b815260206004820152601a60248201527f6275726e2066726f6d20746865207a65726f20616464726573730000000000006044820152606401610d85565b6001600160a01b03831660009081526036602052604090205482111561176b5760405162461bcd60e51b815260206004820152601b60248201527f6275726e20616d6f756e7420657863656564732062616c616e636500000000006044820152606401610d85565b6001600160a01b03831660009081526036602052604081208054849290611793908490613c34565b9250508190555081603560008282546117ac9190613c34565b909155506117c0905060396120b084612f83565b50506001600160a01b038084166000908152603760205260408120546117eb923392911690856130ea565b6040518281526001600160a01b0384169033907fe8a89cc6e5096f9d9f43de82c077c1f4cfe707c0e0c2032176c68813b9ae6a5c9060200160405180910390a36040518281526000906001600160a01b038516903390600080516020613d818339815191529060200160405180910390a46040518281526000906001600160a01b03851690600080516020613da18339815191529060200160405180910390a350600192915050565b336000908152603c602090815260408083206001600160a01b0386168452909152812054826119055760405162461bcd60e51b815260206004820152601c60248201527f7a65726f2076616c756520617070726f76616c206465637265617365000000006044820152606401610d85565b828110156119635760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610d85565b610d9c8461048e8584613c34565b6000610bcc338484610bd3565b61198b600160ff1b613239565b6119e782826119e2336119b3876001600160a01b031660009081526001602052604090205490565b6001600160a01b0391909116600090815260016020526040902054600019808818821618908716919091171690565b61324a565b5050565b60006119f885858561106d565b611a06858585856001612c5f565b506001949350505050565b6001600160a01b0381166000908152603860205260408120805415611a715780548190611a4090600190613c34565b81548110611a5057611a50613c47565b600091825260209091200154600160401b90046001600160c01b0316611a74565b60005b6001600160c01b03169392505050565b6000611a9060806121cd565b611adc5760405162461bcd60e51b815260206004820152601e60248201527f45524331333633207472616e7366657273206172652064697361626c656400006044820152606401610d85565b611ae785858561106d565b611a06858585856000612c5f565b6000611b026101006121cd565b611b4e5760405162461bcd60e51b815260206004820152601e60248201527f4552433133363320617070726f76616c73206172652064697361626c656400006044820152606401610d85565b611b588484610ba9565b50610c368484846132a4565b611b6f6102006121cd565b611bbb5760405162461bcd60e51b815260206004820152601c60248201527f45495032363132207065726d697473206172652064697361626c6564000000006044820152606401610d85565b6001600160a01b0387166000908152603a602052604081208054611c4c917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b9187611c0b83613c5d565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016110cb565b9050876001600160a01b0316816001600160a01b031614611c7f5760405162461bcd60e51b8152600401610d8590613c09565b844210611c9e5760405162461bcd60e51b8152600401610d8590613c76565b611ca98888886120bc565b5050505050505050565b611189308261197e565b6000610d9c84848460405180602001604052806000815250611a84565b611ce56104006121cd565b611d315760405162461bcd60e51b815260206004820152601e60248201527f45495033303039207472616e7366657273206172652064697361626c656400006044820152606401610d85565b6000611d767f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760001b8b8b8b8b8b8b6040516020016110cb9796959493929190613ca1565b9050896001600160a01b0316816001600160a01b031614611da95760405162461bcd60e51b8152600401610d8590613c09565b864211611df25760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610d85565b854210611e115760405162461bcd60e51b8152600401610d8590613c76565b611e1d8a866000612ab5565b611e29818b8b8b6124c2565b50505050505050505050565b611e406108006121cd565b611e8c5760405162461bcd60e51b815260206004820152601f60248201527f4549503330303920726563657074696f6e73206172652064697361626c6564006044820152606401610d85565b6000611ed17fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860001b8b8b8b8b8b8b6040516020016110cb9796959493929190613ca1565b9050896001600160a01b0316816001600160a01b031614611f045760405162461bcd60e51b8152600401610d8590613c09565b864211611f4d5760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610d85565b854210611f6c5760405162461bcd60e51b8152600401610d8590613c76565b6001600160a01b0389163314611e115760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b6044820152606401610d85565b611fbe60406121cd565b6120155760405162461bcd60e51b815260206004820152602260248201527f64656c65676174696f6e73206f6e20626568616c66206172652064697361626c604482015261195960f21b6064820152608401610d85565b604080517fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75560208201526001600160a01b03881691810191909152606081018690526080810185905260009061206d9060a0016110cb565b905084421061208e5760405162461bcd60e51b8152600401610d8590613c76565b61209a81876000612ab5565b6110648188612bc3565b6000610bcc8284613af1565b6000610bcc8284613c34565b6001600160a01b0382166121125760405162461bcd60e51b815260206004820152601b60248201527f617070726f766520746f20746865207a65726f206164647265737300000000006044820152606401610d85565b6001600160a01b038381166000818152603c602090815260408083209487168084529482529182902080549086905582518181529182018690529392917fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a7910160405180910390a3826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516121bf91815260200190565b60405180910390a350505050565b30600090815260016020526040812054610b15905b8316831490565b6001600160a01b038216600090815260016020526040812054610bcc906121e2565b6000610b1533836121e9565b6001600160a01b03821661225c5760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b6044820152606401610d85565b603554818101116122b95760405162461bcd60e51b815260206004820152602160248201527f7a65726f2076616c7565206f722061726974686d65746963206f766572666c6f6044820152607760f81b6064820152608401610d85565b6035546001600160c01b03906122d0908390613af1565b111561231e5760405162461bcd60e51b815260206004820152601f60248201527f746f74616c20737570706c79206f766572666c6f77202875696e7431393229006044820152606401610d85565b80603560008282546123309190613af1565b90915550506001600160a01b0382166000908152603660205260408120805483929061235d908490613af1565b90915550612371905060396120a483612f83565b50506001600160a01b0380831660009081526037602052604081205461239b9233929116846130ea565b6040518181526001600160a01b0383169033907f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f09060200160405180910390a36040518181526001600160a01b038316906000903390600080516020613d818339815191529060200160405180910390a46040518181526001600160a01b03831690600090600080516020613da18339815191529060200160405180910390a35050565b600054610100900460ff166124aa5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610d85565b6124b7826000198061324a565b6119e730828361324a565b836001600160a01b0316836001600160a01b03161480156124e857506124e860016121cd565b806125155750836001600160a01b0316836001600160a01b031614158015612515575061251560026121cd565b846001600160a01b0316846001600160a01b031614612569576040518060400160405280602081526020017f7472616e7366657273206f6e20626568616c66206172652064697361626c6564815250612599565b604051806040016040528060168152602001751d1c985b9cd9995c9cc8185c9948191a5cd8589b195960521b8152505b906125b75760405162461bcd60e51b8152600401610d8591906135ca565b506001600160a01b03831661260e5760405162461bcd60e51b815260206004820152601e60248201527f7472616e736665722066726f6d20746865207a65726f206164647265737300006044820152606401610d85565b6001600160a01b0382166126645760405162461bcd60e51b815260206004820152601c60248201527f7472616e7366657220746f20746865207a65726f2061646472657373000000006044820152606401610d85565b306001600160a01b038316036126e25760405162461bcd60e51b815260206004820152603f60248201527f696e76616c696420726563697069656e7420287472616e7366657220746f207460448201527f686520746f6b656e20736d61727420636f6e747261637420697473656c6629006064820152608401610d85565b8060000361276f57816001600160a01b0316836001600160a01b0316856001600160a01b0316600080516020613d818339815191528460405161272791815260200190565b60405180910390a4816001600160a01b0316836001600160a01b0316600080516020613da18339815191528360405161276291815260200190565b60405180910390a3612a4a565b836001600160a01b0316836001600160a01b0316146128dd576001600160a01b038084166000908152603c6020908152604080832093881683529290522054818110156128085760405162461bcd60e51b815260206004820152602160248201527f7472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636044820152606560f81b6064820152608401610d85565b6000198110156128db5761281c8282613c34565b6001600160a01b038086166000818152603c60209081526040808320948b16808452949091529020839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76128768585613af1565b60408051918252602082018690520160405180910390a3846001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516128d291815260200190565b60405180910390a35b505b6001600160a01b0383166000908152603660205260409020548111156129455760405162461bcd60e51b815260206004820152601f60248201527f7472616e7366657220616d6f756e7420657863656564732062616c616e6365006044820152606401610d85565b6001600160a01b0383166000908152603660205260408120805483929061296d908490613c34565b90915550506001600160a01b0382166000908152603660205260408120805483929061299a908490613af1565b90915550506001600160a01b038084166000908152603760205260408082205485841683529120546129d292879281169116846130ea565b816001600160a01b0316836001600160a01b0316856001600160a01b0316600080516020613d8183398151915284604051612a0f91815260200190565b60405180910390a4816001600160a01b0316836001600160a01b0316600080516020613da1833981519152836040516121bf91815260200190565b50505050565b8351602085012060009081612a63610c5c565b60405161190160f01b60208201526022810191909152604281018390526062016040516020818303038152906040528051906020012090506000612aa9828888886133d0565b98975050505050505050565b6001600160a01b0383166000908152603b6020908152604080832085845290915290205460ff1615612b195760405162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b6044820152606401610d85565b6001600160a01b0383166000908152603b602090815260408083208584529091529020805460ff191660011790558015612b885760405182906001600160a01b038516907f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8190600090a3505050565b60405182906001600160a01b038516907f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a590600090a3505050565b6001600160a01b0380831660009081526037602081815260408084208054603684529190942054929091528484166001600160a01b0319821617909255911690612c0f848385846130ea565b826001600160a01b0316826001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a450505050565b836001600160a01b03163b600003612cb35780612cae5760405162461bcd60e51b815260206004820152600d60248201526c1153d0481c9958da5c1a595b9d609a1b6044820152606401610d85565b612d99565b604051632229f29760e21b81526000906001600160a01b038616906388a7ca5c90612ce89033908a9089908990600401613cdd565b6020604051808303816000875af1158015612d07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d2b9190613d1a565b90506001600160e01b03198116632229f29760e21b146111215760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e5472616e73666572526563656976656420726573706f6044820152626e736560e81b6064820152608401610d85565b5050505050565b81546000908103612db357506000610b15565b825482908490612dc590600190613c34565b81548110612dd557612dd5613c47565b6000918252602090912001546001600160401b031611612e325782548390612dff90600190613c34565b81548110612e0f57612e0f613c47565b600091825260209091200154600160401b90046001600160c01b03169050610b15565b8183600081548110612e4657612e46613c47565b6000918252602090912001546001600160401b03161115612e6957506000610b15565b82546000908190612e7c90600190613c34565b90505b81811115612f4c5760006002612e958484613c34565b612e9f9190613d37565b612ea99083613c34565b90506000868281548110612ebf57612ebf613c47565b6000918252602091829020604080518082019091529101546001600160401b038116808352600160401b9091046001600160c01b0316928201929092529150869003612f1d57602001516001600160c01b03169350610b1592505050565b80516001600160401b0316861115612f3757819350612f45565b612f42600183613c34565b92505b5050612e7f565b848281548110612f5e57612f5e613c47565b600091825260209091200154600160401b90046001600160c01b031695945050505050565b8254600090819015612fd05784548590612f9f90600190613c34565b81548110612faf57612faf613c47565b600091825260209091200154600160401b90046001600160c01b0316612fd3565b60005b6001600160c01b03169150612fec82848663ffffffff16565b855490915015801590613033575084544390869061300c90600190613c34565b8154811061301c5761301c613c47565b6000918252602090912001546001600160401b0316145b156130905784548190869061304a90600190613c34565b8154811061305a5761305a613c47565b9060005260206000200160000160086101000a8154816001600160c01b0302191690836001600160c01b031602179055506130e2565b604080518082019091526001600160401b0343811682526001600160c01b0380841660208085019182528954600181018b5560008b81529190912094519151909216600160401b029216919091179101555b935093915050565b816001600160a01b0316836001600160a01b03161480613108575080155b612a4a576001600160a01b038316156131a0576001600160a01b03831660009081526038602052604081209080613142836120b086612f83565b91509150856001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c18484604051613194929190918252602082015260400190565b60405180910390a35050505b6001600160a01b03821615612a4a576001600160a01b038216600090815260386020526040812090806131d6836120a486612f83565b91509150846001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c18484604051613228929190918252602082015260400190565b60405180910390a350505050505050565b6111896132458261220b565b613533565b6001600160a01b03831660008181526001602090815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000836001600160a01b03163b116132ec5760405162461bcd60e51b815260206004820152600b60248201526a22a7a09039b832b73232b960a91b6044820152606401610d85565b6040516307b04a2d60e41b81526000906001600160a01b03851690637b04a2d09061331f90339087908790600401613d59565b6020604051808303816000875af115801561333e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133629190613d1a565b90506001600160e01b031981166307b04a2d60e41b14612a4a5760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e417070726f76616c526563656976656420726573706f6044820152626e736560e81b6064820152608401610d85565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156134425760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202773272076616c756500000000006044820152606401610d85565b8360ff16601b148061345757508360ff16601c145b6134a35760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202776272076616c756500000000006044820152606401610d85565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156134f7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661352a5760405162461bcd60e51b8152600401610d8590613c09565b95945050505050565b8061118957604051634ca8886760e01b815260040160405180910390fd5b6001600160e01b03198116811461118957600080fd5b60006020828403121561357957600080fd5b8135610bcc81613551565b6000815180845260005b818110156135aa5760208185018101518683018201520161358e565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610bcc6020830184613584565b80356001600160a01b03811681146135f457600080fd5b919050565b6000806040838503121561360c57600080fd5b613615836135dd565b946020939093013593505050565b60006020828403121561363557600080fd5b610bcc826135dd565b60008060006060848603121561365357600080fd5b61365c846135dd565b925061366a602085016135dd565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126136a157600080fd5b81356001600160401b03808211156136bb576136bb61367a565b604051601f8301601f19908116603f011681019082821181831017156136e3576136e361367a565b816040528381528660208588010111156136fc57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561373157600080fd5b61373a846135dd565b92506020840135915060408401356001600160401b0381111561375c57600080fd5b61376886828701613690565b9150509250925092565b60008060008060008060c0878903121561378b57600080fd5b613794876135dd565b955060208701356001600160401b03808211156137b057600080fd5b6137bc8a838b01613690565b965060408901359150808211156137d257600080fd5b506137df89828a01613690565b9450506137ee606088016135dd565b92506080870135915060a087013590509295509295509295565b803560ff811681146135f457600080fd5b600080600080600060a0868803121561383157600080fd5b61383a866135dd565b94506020860135935061384f60408701613808565b94979396509394606081013594506080013592915050565b602080825282518282018190526000919060409081850190868401855b828110156138bb57815180516001600160401b031685528601516001600160c01b0316868501529284019290850190600101613884565b5091979650505050505050565b6000602082840312156138da57600080fd5b5035919050565b600080600080608085870312156138f757600080fd5b613900856135dd565b935061390e602086016135dd565b92506040850135915060608501356001600160401b0381111561393057600080fd5b61393c87828801613690565b91505092959194509250565b600080600080600080600060e0888a03121561396357600080fd5b61396c886135dd565b965061397a602089016135dd565b9550604088013594506060880135935061399660808901613808565b925060a0880135915060c0880135905092959891949750929550565b600080604083850312156139c557600080fd5b6139ce836135dd565b91506139dc602084016135dd565b90509250929050565b60008060008060008060008060006101208a8c031215613a0457600080fd5b613a0d8a6135dd565b9850613a1b60208b016135dd565b975060408a0135965060608a0135955060808a0135945060a08a01359350613a4560c08b01613808565b925060e08a013591506101008a013590509295985092959850929598565b60008060008060008060c08789031215613a7c57600080fd5b613a85876135dd565b955060208701359450604087013593506137ee60608801613808565b600181811c90821680613ab557607f821691505b602082108103613ad557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610b1557610b15613adb565b601f82111561107957600081815260208120601f850160051c81016020861015613b2b5750805b601f850160051c820191505b8181101561112157828155600101613b37565b81516001600160401b03811115613b6357613b6361367a565b613b7781613b718454613aa1565b84613b04565b602080601f831160018114613bac5760008415613b945750858301515b600019600386901b1c1916600185901b178555611121565b600085815260208120601f198616915b82811015613bdb57888601518255948401946001909101908401613bbc565b5085821015613bf95787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b602080825260119082015270696e76616c6964207369676e617475726560781b604082015260600190565b81810381811115610b1557610b15613adb565b634e487b7160e01b600052603260045260246000fd5b600060018201613c6f57613c6f613adb565b5060010190565b6020808252601190820152701cda59db985d1d5c9948195e1c1a5c9959607a1b604082015260600190565b9687526001600160a01b0395861660208801529390941660408601526060850191909152608084015260a083019190915260c082015260e00190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090613d1090830184613584565b9695505050505050565b600060208284031215613d2c57600080fd5b8151610bcc81613551565b600082613d5457634e487b7160e01b600052601260045260246000fd5b500490565b60018060a01b038416815282602082015260606040820152600061352a606083018461358456fed1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220f26bd73834e89ed93abb879fec1792bd3efc5337d6ff131af49d39b2b4f504e264736f6c63430008140033496e697469616c697a61626c653a20636f6e747261637420697320616c7265617f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb3847402498d1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c1000000000000000000000000c93c904ffe3d55e15483ef37e38ecaf8fe003ba7

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061043e5760003560e01c806387793f3e11610236578063b88d4fde1161013b578063d9169487116100c3578063e94a010211610087578063e94a010214610a22578063e98f5ba714610a5b578063ef55bec614610a65578063f63c2f8214610a78578063f9cf927114610a8057600080fd5b8063d91694871461097e578063dd62ed3e146109a5578063e3ee160e146109de578063e62cac76146109f1578063e7a324dc146109fb57600080fd5b8063c5ff500c1161010a578063c5ff500c14610928578063cae9ca5114610932578063d505accf14610945578063d5bb7f6714610958578063d8fbe9941461096b57600080fd5b8063b88d4fde146108e7578063bcc3f3bd146108fa578063c0d6568d1461090d578063c1d34b891461091557600080fd5b80639dc29fac116101be578063ae5b102e1161018d578063ae5b102e146108a1578063ae60bda4146108b4578063ae682e2e146108bf578063b3c65015146108ca578063b66dbdc5146108df57600080fd5b80639dc29fac14610841578063a0cc6a6814610854578063a457c2d71461087b578063a9059cbb1461088e57600080fd5b806394f4f9301161020557806394f4f930146107ed57806395d89b411461080057806397ba461114610808578063981b24d01461081b57806398de4ba31461082e57600080fd5b806387793f3e1461079f5780638832e6e3146107c85780638d4e57e6146107db5780638f6fba8c146107e557600080fd5b806339509351116103475780635c19a95c116102cf57806370a082311161029357806370a08231146107095780637815ef0c146107325780637ecebe00146107455780637f2eecc3146107655780637fd491b01461078c57600080fd5b80635c19a95c146106955780635e2dc2b7146106a857806364cb8b96146106b1578063653de620146106c65780636641d9a0146106cf57600080fd5b8063442767331161031657806344276733146106285780634721272d1461065157806350c29e371461065a57806359b961ef1461066f5780635a049a701461068257600080fd5b806339509351146105e75780633e9c5f7e146105fa5780634000aea01461060257806340c10f191461061557600080fd5b80631e0fa234116103ca5780632d4c39ea116103995780632d4c39ea1461058357806330adf81f1461058b578063313ce567146105b25780633177029f146105cc5780633644e515146105df57600080fd5b80631e0fa234146104f357806320606b701461053457806323b872dd1461055b5780632b5214161461056e57600080fd5b8063136d035f11610411578063136d035f146104a657806313873a24146104c457806318160ddd146104cc5780631993f554146104e35780631a0b04ea146104eb57600080fd5b806301ffc9a71461044357806306fdde031461046b578063095ea7b3146104805780631296ee6214610493575b600080fd5b610456610451366004613567565b610a93565b60405190151581526020015b60405180910390f35b610473610b1b565b60405161046291906135ca565b61045661048e3660046135f9565b610ba9565b6104566104a13660046135f9565b610bbf565b6104af61080081565b60405163ffffffff9091168152602001610462565b6104af608081565b6104d560355481565b604051908152602001610462565b6104af600481565b6104af600881565b61051c610501366004613623565b6037602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610462565b6104d57f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a86681565b61045661056936600461363e565b610bd3565b306000908152600160205260409020546104d5565b6104af604081565b6104d57f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b6105ba601281565b60405160ff9091168152602001610462565b6104566105da3660046135f9565b610c40565b6104d5610c5c565b6104566105f53660046135f9565b610cf8565b6104af602081565b61045661061036600461371c565b610da4565b6104566106233660046135f9565b610db2565b6104d5610636366004613623565b6001600160a01b031660009081526001602052604090205490565b6104af61020081565b61066d610668366004613772565b610e06565b005b61066d61067d36600461363e565b61106d565b61066d610690366004613819565b61107e565b61066d6106a3366004613623565b611129565b6104af61040081565b6106b961118c565b6040516104629190613867565b6104af61010081565b6106e26106dd3660046138c8565b611208565b604080516001600160401b0390931683526001600160c01b03909116602083015201610462565b6104d5610717366004613623565b6001600160a01b031660009081526036602052604090205490565b6106b9610740366004613623565b611243565b6104d5610753366004613623565b603a6020526000908152604090205481565b6104d57fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de881565b6106e261079a3660046135f9565b6112d5565b6104d56107ad366004613623565b6001600160a01b031660009081526038602052604090205490565b6104566107d636600461371c565b61131e565b6104af6201000081565b6104af600281565b6104d56107fb3660046135f9565b61133a565b6104736113a3565b61045661081636600461371c565b6113b0565b6104d56108293660046138c8565b6113cc565b61045661083c3660046135f9565b61141e565b61045661084f3660046135f9565b61143a565b6104d57f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226781565b6104566108893660046135f9565b611894565b61045661089c3660046135f9565b611971565b61066d6108af3660046135f9565b61197e565b6104d5600160fe1b81565b6104d5600160ff1b81565b60005460405160ff9091168152602001610462565b6039546104d5565b6104566108f53660046138e1565b6119eb565b6104d5610908366004613623565b611a11565b6104af600181565b6104566109233660046138e1565b611a84565b6104af6204000081565b61045661094036600461371c565b611af5565b61066d610953366004613948565b611b64565b61066d6109663660046138c8565b611cb3565b61045661097936600461363e565b611cbd565b6104d57f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742981565b6104d56109b33660046139b2565b6001600160a01b039182166000908152603c6020908152604080832093909416825291909152205490565b61066d6109ec3660046139e5565b611cda565b6104af6202000081565b6104d57fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75581565b610456610a303660046135f9565b6001600160a01b03919091166000908152603b60209081526040808320938352929052205460ff1690565b6104af6208000081565b61066d610a733660046139e5565b611e35565b6104af601081565b61066d610a8e366004613a63565b611fb4565b60006001600160e01b031982166301ffc9a760e01b1480610ac457506001600160e01b031982166336372b0760e01b145b80610adf57506001600160e01b0319821663b0202a1160e01b145b80610afa57506001600160e01b03198216634ec7fbed60e11b145b80610b1557506001600160e01b03198216635ffa99dd60e11b145b92915050565b60338054610b2890613aa1565b80601f0160208091040260200160405190810160405280929190818152602001828054610b5490613aa1565b8015610ba15780601f10610b7657610100808354040283529160200191610ba1565b820191906000526020600020905b815481529060010190602001808311610b8457829003601f168201915b505050505081565b6000610bb63384846120bc565b50600192915050565b6000610bcc338484611cbd565b9392505050565b6000610bdf60046121cd565b80610bf25750610bf283620400006121e9565b80610c045750610c046208000061220b565b15610c1957610c1484848461106d565b610c36565b610c34848484604051806020016040528060008152506119eb565b505b5060019392505050565b6000610bcc838360405180602001604052806000815250611af5565b604080518082018252600f81526e416476616e6365644552433230763160881b60209182015281517f8cad95687ba82c2ce50e74f7b754645e5117c3a5bec8151c0726d5857980a866818301527f65218aa0cdc6725da873801e9daf9d54f230d8a515289f76c8bd31dd8648c50d81840152466060820152306080808301919091528351808303909101815260a0909101909252815191012090565b336000908152603c602090815260408083206001600160a01b03861684529091528120548281018110610d8e5760405162461bcd60e51b815260206004820152603360248201527f7a65726f2076616c756520617070726f76616c20696e637265617365206f722060448201527261726974686d65746963206f766572666c6f7760681b60648201526084015b60405180910390fd5b610d9c8461048e8584613af1565b949350505050565b6000610d9c33858585611a84565b6000610dc06201000061220b565b610dfc5760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b6044820152606401610d85565b610bb68383612217565b600054610100900460ff1615808015610e265750600054600160ff909116105b80610e405750303b158015610e40575060005460ff166001145b610ea35760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610d85565b6000805460ff191660011790558015610ec6576000805461ff0019166101001790555b6000865111610f0f5760405162461bcd60e51b81526020600482015260156024820152741d1bdad95b881b985b59481a5cc81b9bdd081cd95d605a1b6044820152606401610d85565b6000855111610f605760405162461bcd60e51b815260206004820152601760248201527f746f6b656e2073796d626f6c206973206e6f74207365740000000000000000006044820152606401610d85565b6033610f6c8782613b4a565b506034610f798682613b4a565b50821580610f8f57506001600160a01b03841615155b610fe95760405162461bcd60e51b815260206004820152602560248201527f5f696e697469616c486f6c646572206e6f742073657420287a65726f20616464604482015264726573732960d81b6064820152608401610d85565b8215610ff957610ff98484612217565b6001600160a01b03871615158061100f57508115155b1561101e5761101e878361243f565b8015611064576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b611079338484846124c2565b505050565b604080517f158b0a9edf7a828aad02f63cd515c68ef2f50ba807396f6d12842833a159742960208201526001600160a01b03871691810191909152606081018590526000906110e2906080015b604051602081830303815290604052858585612a50565b9050856001600160a01b0316816001600160a01b0316146111155760405162461bcd60e51b8152600401610d8590613c09565b61112186866001612ab5565b505050505050565b61113360206121cd565b61117f5760405162461bcd60e51b815260206004820152601860248201527f64656c65676174696f6e73206172652064697361626c656400000000000000006044820152606401610d85565b6111893382612bc3565b50565b60606039805480602002602001604051908101604052809291908181526020016000905b828210156111ff57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b0316818301528252600190920191016111b0565b50505050905090565b6039818154811061121857600080fd5b6000918252602090912001546001600160401b0381169150600160401b90046001600160c01b031682565b6001600160a01b0381166000908152603860209081526040808320805482518185028101850190935280835260609492939192909184015b828210156112ca57600084815260209081902060408051808201909152908401546001600160401b0381168252600160401b90046001600160c01b03168183015282526001909201910161127b565b505050509050919050565b603860205281600052604060002081815481106112f157600080fd5b6000918252602090912001546001600160401b0381169250600160401b90046001600160c01b0316905082565b600061132a8484610db2565b50610c3660008585856001612c5f565b60004382106113815760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610d85565b6001600160a01b0383166000908152603860205260409020610bcc9083612da0565b60348054610b2890613aa1565b60006113bc8484610db2565b50610c3660008585856000612c5f565b60004382106114135760405162461bcd60e51b8152602060048201526013602482015272189b1bd8dac81b9bdd081e595d081b5a5b9959606a1b6044820152606401610d85565b610b15603983612da0565b6000610bcc8383604051806020016040528060008152506113b0565b60006114486202000061220b565b61166b576001600160a01b03831633148015611469575061146960086121cd565b8061148d57506001600160a01b038316331480159061148d575061148d60106121cd565b6001600160a01b03841633146114d8576040518060400160405280601c81526020017f6275726e73206f6e20626568616c66206172652064697361626c656400000000815250611504565b60405180604001604052806012815260200171189d5c9b9cc8185c9948191a5cd8589b195960721b8152505b906115225760405162461bcd60e51b8152600401610d8591906135ca565b506001600160a01b038316331461166b576001600160a01b0383166000908152603c60209081526040808320338452909152902054828110156115a75760405162461bcd60e51b815260206004820152601d60248201527f6275726e20616d6f756e74206578636565647320616c6c6f77616e63650000006044820152606401610d85565b600019811015611669576115bb8382613c34565b6001600160a01b0385166000818152603c60209081526040808320338085529252909120839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76116118685613af1565b60408051918252602082018690520160405180910390a360405181815233906001600160a01b038616907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259060200160405180910390a35b505b816000036116ad5760405162461bcd60e51b815260206004820152600f60248201526e3d32b937903b30b63ab290313ab93760891b6044820152606401610d85565b6001600160a01b0383166117035760405162461bcd60e51b815260206004820152601a60248201527f6275726e2066726f6d20746865207a65726f20616464726573730000000000006044820152606401610d85565b6001600160a01b03831660009081526036602052604090205482111561176b5760405162461bcd60e51b815260206004820152601b60248201527f6275726e20616d6f756e7420657863656564732062616c616e636500000000006044820152606401610d85565b6001600160a01b03831660009081526036602052604081208054849290611793908490613c34565b9250508190555081603560008282546117ac9190613c34565b909155506117c0905060396120b084612f83565b50506001600160a01b038084166000908152603760205260408120546117eb923392911690856130ea565b6040518281526001600160a01b0384169033907fe8a89cc6e5096f9d9f43de82c077c1f4cfe707c0e0c2032176c68813b9ae6a5c9060200160405180910390a36040518281526000906001600160a01b038516903390600080516020613d818339815191529060200160405180910390a46040518281526000906001600160a01b03851690600080516020613da18339815191529060200160405180910390a350600192915050565b336000908152603c602090815260408083206001600160a01b0386168452909152812054826119055760405162461bcd60e51b815260206004820152601c60248201527f7a65726f2076616c756520617070726f76616c206465637265617365000000006044820152606401610d85565b828110156119635760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610d85565b610d9c8461048e8584613c34565b6000610bcc338484610bd3565b61198b600160ff1b613239565b6119e782826119e2336119b3876001600160a01b031660009081526001602052604090205490565b6001600160a01b0391909116600090815260016020526040902054600019808818821618908716919091171690565b61324a565b5050565b60006119f885858561106d565b611a06858585856001612c5f565b506001949350505050565b6001600160a01b0381166000908152603860205260408120805415611a715780548190611a4090600190613c34565b81548110611a5057611a50613c47565b600091825260209091200154600160401b90046001600160c01b0316611a74565b60005b6001600160c01b03169392505050565b6000611a9060806121cd565b611adc5760405162461bcd60e51b815260206004820152601e60248201527f45524331333633207472616e7366657273206172652064697361626c656400006044820152606401610d85565b611ae785858561106d565b611a06858585856000612c5f565b6000611b026101006121cd565b611b4e5760405162461bcd60e51b815260206004820152601e60248201527f4552433133363320617070726f76616c73206172652064697361626c656400006044820152606401610d85565b611b588484610ba9565b50610c368484846132a4565b611b6f6102006121cd565b611bbb5760405162461bcd60e51b815260206004820152601c60248201527f45495032363132207065726d697473206172652064697361626c6564000000006044820152606401610d85565b6001600160a01b0387166000908152603a602052604081208054611c4c917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9918b918b918b9187611c0b83613c5d565b909155506040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810187905260e0016110cb565b9050876001600160a01b0316816001600160a01b031614611c7f5760405162461bcd60e51b8152600401610d8590613c09565b844210611c9e5760405162461bcd60e51b8152600401610d8590613c76565b611ca98888886120bc565b5050505050505050565b611189308261197e565b6000610d9c84848460405180602001604052806000815250611a84565b611ce56104006121cd565b611d315760405162461bcd60e51b815260206004820152601e60248201527f45495033303039207472616e7366657273206172652064697361626c656400006044820152606401610d85565b6000611d767f7c7c6cdb67a18743f49ec6fa9b35f50d52ed05cbed4cc592e13b44501c1a226760001b8b8b8b8b8b8b6040516020016110cb9796959493929190613ca1565b9050896001600160a01b0316816001600160a01b031614611da95760405162461bcd60e51b8152600401610d8590613c09565b864211611df25760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610d85565b854210611e115760405162461bcd60e51b8152600401610d8590613c76565b611e1d8a866000612ab5565b611e29818b8b8b6124c2565b50505050505050505050565b611e406108006121cd565b611e8c5760405162461bcd60e51b815260206004820152601f60248201527f4549503330303920726563657074696f6e73206172652064697361626c6564006044820152606401610d85565b6000611ed17fd099cc98ef71107a616c4f0f941f04c322d8e254fe26b3c6668db87aae413de860001b8b8b8b8b8b8b6040516020016110cb9796959493929190613ca1565b9050896001600160a01b0316816001600160a01b031614611f045760405162461bcd60e51b8152600401610d8590613c09565b864211611f4d5760405162461bcd60e51b81526020600482015260176024820152761cda59db985d1d5c99481b9bdd081e595d081d985b1a59604a1b6044820152606401610d85565b854210611f6c5760405162461bcd60e51b8152600401610d8590613c76565b6001600160a01b0389163314611e115760405162461bcd60e51b815260206004820152600d60248201526c1858d8d95cdcc819195b9a5959609a1b6044820152606401610d85565b611fbe60406121cd565b6120155760405162461bcd60e51b815260206004820152602260248201527f64656c65676174696f6e73206f6e20626568616c66206172652064697361626c604482015261195960f21b6064820152608401610d85565b604080517fff41620983935eb4d4a3c7384a066ca8c1d10cef9a5eca9eb97ca735cd14a75560208201526001600160a01b03881691810191909152606081018690526080810185905260009061206d9060a0016110cb565b905084421061208e5760405162461bcd60e51b8152600401610d8590613c76565b61209a81876000612ab5565b6110648188612bc3565b6000610bcc8284613af1565b6000610bcc8284613c34565b6001600160a01b0382166121125760405162461bcd60e51b815260206004820152601b60248201527f617070726f766520746f20746865207a65726f206164647265737300000000006044820152606401610d85565b6001600160a01b038381166000818152603c602090815260408083209487168084529482529182902080549086905582518181529182018690529392917fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a7910160405180910390a3826001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516121bf91815260200190565b60405180910390a350505050565b30600090815260016020526040812054610b15905b8316831490565b6001600160a01b038216600090815260016020526040812054610bcc906121e2565b6000610b1533836121e9565b6001600160a01b03821661225c5760405162461bcd60e51b815260206004820152600c60248201526b7a65726f206164647265737360a01b6044820152606401610d85565b603554818101116122b95760405162461bcd60e51b815260206004820152602160248201527f7a65726f2076616c7565206f722061726974686d65746963206f766572666c6f6044820152607760f81b6064820152608401610d85565b6035546001600160c01b03906122d0908390613af1565b111561231e5760405162461bcd60e51b815260206004820152601f60248201527f746f74616c20737570706c79206f766572666c6f77202875696e7431393229006044820152606401610d85565b80603560008282546123309190613af1565b90915550506001600160a01b0382166000908152603660205260408120805483929061235d908490613af1565b90915550612371905060396120a483612f83565b50506001600160a01b0380831660009081526037602052604081205461239b9233929116846130ea565b6040518181526001600160a01b0383169033907f9d228d69b5fdb8d273a2336f8fb8612d039631024ea9bf09c424a9503aa078f09060200160405180910390a36040518181526001600160a01b038316906000903390600080516020613d818339815191529060200160405180910390a46040518181526001600160a01b03831690600090600080516020613da18339815191529060200160405180910390a35050565b600054610100900460ff166124aa5760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b6064820152608401610d85565b6124b7826000198061324a565b6119e730828361324a565b836001600160a01b0316836001600160a01b03161480156124e857506124e860016121cd565b806125155750836001600160a01b0316836001600160a01b031614158015612515575061251560026121cd565b846001600160a01b0316846001600160a01b031614612569576040518060400160405280602081526020017f7472616e7366657273206f6e20626568616c66206172652064697361626c6564815250612599565b604051806040016040528060168152602001751d1c985b9cd9995c9cc8185c9948191a5cd8589b195960521b8152505b906125b75760405162461bcd60e51b8152600401610d8591906135ca565b506001600160a01b03831661260e5760405162461bcd60e51b815260206004820152601e60248201527f7472616e736665722066726f6d20746865207a65726f206164647265737300006044820152606401610d85565b6001600160a01b0382166126645760405162461bcd60e51b815260206004820152601c60248201527f7472616e7366657220746f20746865207a65726f2061646472657373000000006044820152606401610d85565b306001600160a01b038316036126e25760405162461bcd60e51b815260206004820152603f60248201527f696e76616c696420726563697069656e7420287472616e7366657220746f207460448201527f686520746f6b656e20736d61727420636f6e747261637420697473656c6629006064820152608401610d85565b8060000361276f57816001600160a01b0316836001600160a01b0316856001600160a01b0316600080516020613d818339815191528460405161272791815260200190565b60405180910390a4816001600160a01b0316836001600160a01b0316600080516020613da18339815191528360405161276291815260200190565b60405180910390a3612a4a565b836001600160a01b0316836001600160a01b0316146128dd576001600160a01b038084166000908152603c6020908152604080832093881683529290522054818110156128085760405162461bcd60e51b815260206004820152602160248201527f7472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636044820152606560f81b6064820152608401610d85565b6000198110156128db5761281c8282613c34565b6001600160a01b038086166000818152603c60209081526040808320948b16808452949091529020839055919250907fb3fd5071835887567a0671151121894ddccc2842f1d10bedad13e0d17cace9a76128768585613af1565b60408051918252602082018690520160405180910390a3846001600160a01b0316846001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925836040516128d291815260200190565b60405180910390a35b505b6001600160a01b0383166000908152603660205260409020548111156129455760405162461bcd60e51b815260206004820152601f60248201527f7472616e7366657220616d6f756e7420657863656564732062616c616e6365006044820152606401610d85565b6001600160a01b0383166000908152603660205260408120805483929061296d908490613c34565b90915550506001600160a01b0382166000908152603660205260408120805483929061299a908490613af1565b90915550506001600160a01b038084166000908152603760205260408082205485841683529120546129d292879281169116846130ea565b816001600160a01b0316836001600160a01b0316856001600160a01b0316600080516020613d8183398151915284604051612a0f91815260200190565b60405180910390a4816001600160a01b0316836001600160a01b0316600080516020613da1833981519152836040516121bf91815260200190565b50505050565b8351602085012060009081612a63610c5c565b60405161190160f01b60208201526022810191909152604281018390526062016040516020818303038152906040528051906020012090506000612aa9828888886133d0565b98975050505050505050565b6001600160a01b0383166000908152603b6020908152604080832085845290915290205460ff1615612b195760405162461bcd60e51b815260206004820152600d60248201526c696e76616c6964206e6f6e636560981b6044820152606401610d85565b6001600160a01b0383166000908152603b602090815260408083208584529091529020805460ff191660011790558015612b885760405182906001600160a01b038516907f1cdd46ff242716cdaa72d159d339a485b3438398348d68f09d7c8c0a59353d8190600090a3505050565b60405182906001600160a01b038516907f98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a590600090a3505050565b6001600160a01b0380831660009081526037602081815260408084208054603684529190942054929091528484166001600160a01b0319821617909255911690612c0f848385846130ea565b826001600160a01b0316826001600160a01b0316856001600160a01b03167f3134e8a2e6d97e929a7e54011ea5485d7d196dd5f0ba4d4ef95803e8e3fc257f60405160405180910390a450505050565b836001600160a01b03163b600003612cb35780612cae5760405162461bcd60e51b815260206004820152600d60248201526c1153d0481c9958da5c1a595b9d609a1b6044820152606401610d85565b612d99565b604051632229f29760e21b81526000906001600160a01b038616906388a7ca5c90612ce89033908a9089908990600401613cdd565b6020604051808303816000875af1158015612d07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d2b9190613d1a565b90506001600160e01b03198116632229f29760e21b146111215760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e5472616e73666572526563656976656420726573706f6044820152626e736560e81b6064820152608401610d85565b5050505050565b81546000908103612db357506000610b15565b825482908490612dc590600190613c34565b81548110612dd557612dd5613c47565b6000918252602090912001546001600160401b031611612e325782548390612dff90600190613c34565b81548110612e0f57612e0f613c47565b600091825260209091200154600160401b90046001600160c01b03169050610b15565b8183600081548110612e4657612e46613c47565b6000918252602090912001546001600160401b03161115612e6957506000610b15565b82546000908190612e7c90600190613c34565b90505b81811115612f4c5760006002612e958484613c34565b612e9f9190613d37565b612ea99083613c34565b90506000868281548110612ebf57612ebf613c47565b6000918252602091829020604080518082019091529101546001600160401b038116808352600160401b9091046001600160c01b0316928201929092529150869003612f1d57602001516001600160c01b03169350610b1592505050565b80516001600160401b0316861115612f3757819350612f45565b612f42600183613c34565b92505b5050612e7f565b848281548110612f5e57612f5e613c47565b600091825260209091200154600160401b90046001600160c01b031695945050505050565b8254600090819015612fd05784548590612f9f90600190613c34565b81548110612faf57612faf613c47565b600091825260209091200154600160401b90046001600160c01b0316612fd3565b60005b6001600160c01b03169150612fec82848663ffffffff16565b855490915015801590613033575084544390869061300c90600190613c34565b8154811061301c5761301c613c47565b6000918252602090912001546001600160401b0316145b156130905784548190869061304a90600190613c34565b8154811061305a5761305a613c47565b9060005260206000200160000160086101000a8154816001600160c01b0302191690836001600160c01b031602179055506130e2565b604080518082019091526001600160401b0343811682526001600160c01b0380841660208085019182528954600181018b5560008b81529190912094519151909216600160401b029216919091179101555b935093915050565b816001600160a01b0316836001600160a01b03161480613108575080155b612a4a576001600160a01b038316156131a0576001600160a01b03831660009081526038602052604081209080613142836120b086612f83565b91509150856001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c18484604051613194929190918252602082015260400190565b60405180910390a35050505b6001600160a01b03821615612a4a576001600160a01b038216600090815260386020526040812090806131d6836120a486612f83565b91509150846001600160a01b0316876001600160a01b03167fd1404f22081753a56b50e0d5ff5c9ed0e4a3a840e1171a443721a342e71bb5c18484604051613228929190918252602082015260400190565b60405180910390a350505050505050565b6111896132458261220b565b613533565b6001600160a01b03831660008181526001602090815260409182902084905581518581529081018490527fe9be537308880e0f56b7d7cfd7abf85f14c4934486d138f848b92a0cbaf659b4910160405180910390a2505050565b6000836001600160a01b03163b116132ec5760405162461bcd60e51b815260206004820152600b60248201526a22a7a09039b832b73232b960a91b6044820152606401610d85565b6040516307b04a2d60e41b81526000906001600160a01b03851690637b04a2d09061331f90339087908790600401613d59565b6020604051808303816000875af115801561333e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906133629190613d1a565b90506001600160e01b031981166307b04a2d60e41b14612a4a5760405162461bcd60e51b815260206004820152602360248201527f696e76616c6964206f6e417070726f76616c526563656976656420726573706f6044820152626e736560e81b6064820152608401610d85565b60007f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08211156134425760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202773272076616c756500000000006044820152606401610d85565b8360ff16601b148061345757508360ff16601c145b6134a35760405162461bcd60e51b815260206004820152601b60248201527f696e76616c6964207369676e6174757265202776272076616c756500000000006044820152606401610d85565b6040805160008082526020820180845288905260ff871692820192909252606081018590526080810184905260019060a0016020604051602081039080840390855afa1580156134f7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b03811661352a5760405162461bcd60e51b8152600401610d8590613c09565b95945050505050565b8061118957604051634ca8886760e01b815260040160405180910390fd5b6001600160e01b03198116811461118957600080fd5b60006020828403121561357957600080fd5b8135610bcc81613551565b6000815180845260005b818110156135aa5760208185018101518683018201520161358e565b506000602082860101526020601f19601f83011685010191505092915050565b602081526000610bcc6020830184613584565b80356001600160a01b03811681146135f457600080fd5b919050565b6000806040838503121561360c57600080fd5b613615836135dd565b946020939093013593505050565b60006020828403121561363557600080fd5b610bcc826135dd565b60008060006060848603121561365357600080fd5b61365c846135dd565b925061366a602085016135dd565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126136a157600080fd5b81356001600160401b03808211156136bb576136bb61367a565b604051601f8301601f19908116603f011681019082821181831017156136e3576136e361367a565b816040528381528660208588010111156136fc57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561373157600080fd5b61373a846135dd565b92506020840135915060408401356001600160401b0381111561375c57600080fd5b61376886828701613690565b9150509250925092565b60008060008060008060c0878903121561378b57600080fd5b613794876135dd565b955060208701356001600160401b03808211156137b057600080fd5b6137bc8a838b01613690565b965060408901359150808211156137d257600080fd5b506137df89828a01613690565b9450506137ee606088016135dd565b92506080870135915060a087013590509295509295509295565b803560ff811681146135f457600080fd5b600080600080600060a0868803121561383157600080fd5b61383a866135dd565b94506020860135935061384f60408701613808565b94979396509394606081013594506080013592915050565b602080825282518282018190526000919060409081850190868401855b828110156138bb57815180516001600160401b031685528601516001600160c01b0316868501529284019290850190600101613884565b5091979650505050505050565b6000602082840312156138da57600080fd5b5035919050565b600080600080608085870312156138f757600080fd5b613900856135dd565b935061390e602086016135dd565b92506040850135915060608501356001600160401b0381111561393057600080fd5b61393c87828801613690565b91505092959194509250565b600080600080600080600060e0888a03121561396357600080fd5b61396c886135dd565b965061397a602089016135dd565b9550604088013594506060880135935061399660808901613808565b925060a0880135915060c0880135905092959891949750929550565b600080604083850312156139c557600080fd5b6139ce836135dd565b91506139dc602084016135dd565b90509250929050565b60008060008060008060008060006101208a8c031215613a0457600080fd5b613a0d8a6135dd565b9850613a1b60208b016135dd565b975060408a0135965060608a0135955060808a0135945060a08a01359350613a4560c08b01613808565b925060e08a013591506101008a013590509295985092959850929598565b60008060008060008060c08789031215613a7c57600080fd5b613a85876135dd565b955060208701359450604087013593506137ee60608801613808565b600181811c90821680613ab557607f821691505b602082108103613ad557634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b80820180821115610b1557610b15613adb565b601f82111561107957600081815260208120601f850160051c81016020861015613b2b5750805b601f850160051c820191505b8181101561112157828155600101613b37565b81516001600160401b03811115613b6357613b6361367a565b613b7781613b718454613aa1565b84613b04565b602080601f831160018114613bac5760008415613b945750858301515b600019600386901b1c1916600185901b178555611121565b600085815260208120601f198616915b82811015613bdb57888601518255948401946001909101908401613bbc565b5085821015613bf95787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b602080825260119082015270696e76616c6964207369676e617475726560781b604082015260600190565b81810381811115610b1557610b15613adb565b634e487b7160e01b600052603260045260246000fd5b600060018201613c6f57613c6f613adb565b5060010190565b6020808252601190820152701cda59db985d1d5c9948195e1c1a5c9959607a1b604082015260600190565b9687526001600160a01b0395861660208801529390941660408601526060850191909152608084015260a083019190915260c082015260e00190565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090613d1090830184613584565b9695505050505050565b600060208284031215613d2c57600080fd5b8151610bcc81613551565b600082613d5457634e487b7160e01b600052601260045260246000fd5b500490565b60018060a01b038416815282602082015260606040820152600061352a606083018461358456fed1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa2646970667358221220f26bd73834e89ed93abb879fec1792bd3efc5337d6ff131af49d39b2b4f504e264736f6c63430008140033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000c93c904ffe3d55e15483ef37e38ecaf8fe003ba7

-----Decoded View---------------
Arg [0] : _initialHolder (address): 0xC93c904fFE3d55E15483eF37e38ECAF8Fe003Ba7

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000c93c904ffe3d55e15483ef37e38ecaf8fe003ba7


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

OVERVIEW

Lizard Labs is the studio behind Ethlizards, building mini-games and meta-games using Web3 and AI technologies.

Loading...
Loading
[ Download: CSV Export  ]

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