Feature Tip: Add private address tag to any address under My Name Tag !
ERC-20
Overview
Max Total Supply
1,000,000,000 BUNNI
Holders
59
Market
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 18 Decimals)
Balance
390.000000000000026021 BUNNIValue
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Source Code Verified (Exact Match)
Contract Name:
BUNNI
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.4; import "./lib/XERC20.sol"; /// @title BUNNI /// @notice BUNNI is the governance token of Bunni. /// @author zefram.eth contract BUNNI is XERC20 { uint256 public constant MAX_SUPPLY = 1e27; // 1 billion tokens error BUNNI_InvalidLength(); constructor( address _initialOwner, uint256[] memory _minterLimits, uint256[] memory _burnerLimits, address[] memory _bridges ) XERC20(_initialOwner) { _mint(_initialOwner, MAX_SUPPLY); uint256 _bridgesLength = _bridges.length; if (_minterLimits.length != _bridgesLength || _burnerLimits.length != _bridgesLength) { revert BUNNI_InvalidLength(); } for (uint256 i; i < _bridgesLength; ++i) { _setLimits(_bridges[i], _minterLimits[i], _burnerLimits[i]); } } function name() public pure override returns (string memory) { return "Bunni"; } function symbol() public pure override returns (string memory) { return "BUNNI"; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.4 <0.9.0; import {Ownable} from "solady/auth/Ownable.sol"; import {IXERC20} from "../interfaces/IXERC20.sol"; import {ERC20Multicaller} from "./ERC20Multicaller.sol"; abstract contract XERC20 is ERC20Multicaller, Ownable, IXERC20 { /** * @notice The duration it takes for the limits to fully replenish */ uint256 private constant _DURATION = 1 days; /** * @notice Maps bridge address to bridge configurations */ mapping(address => Bridge) public bridges; /** * @notice Constructs the initial config of the XERC20 * * @param _initialOwner The initial owner of the contract */ constructor(address _initialOwner) { _initializeOwner(_initialOwner); } /** * @notice Mints tokens for a user * @dev Can only be called by a bridge * @param _user The address of the user who needs tokens minted * @param _amount The amount of tokens being minted */ function mint(address _user, uint256 _amount) public { _mintWithCaller(msg.sender, _user, _amount); } /** * @notice Burns tokens for a user * @dev Can only be called by a bridge * @param _user The address of the user who needs tokens burned * @param _amount The amount of tokens being burned */ function burn(address _user, uint256 _amount) public { if (msg.sender != _user) { _spendAllowance(_user, msg.sender, _amount); } _burnWithCaller(msg.sender, _user, _amount); } /** * @notice Updates the limits of any bridge * @dev Can only be called by the owner * @param _mintingLimit The updated minting limit we are setting to the bridge * @param _burningLimit The updated burning limit we are setting to the bridge * @param _bridge The address of the bridge we are setting the limits too */ function setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) external onlyOwner { _setLimits(_bridge, _mintingLimit, _burningLimit); } /** * @notice Complies with IXERC20 interface. Does not do anything since * a Lockbox is not used for BUNNI. */ function setLockbox(address) external {} /** * @notice Returns the max limit of a bridge * * @param _bridge the bridge we are viewing the limits of * @return _limit The limit the bridge has */ function mintingMaxLimitOf(address _bridge) public view returns (uint256 _limit) { _limit = bridges[_bridge].minterParams.maxLimit; } /** * @notice Returns the max limit of a bridge * * @param _bridge the bridge we are viewing the limits of * @return _limit The limit the bridge has */ function burningMaxLimitOf(address _bridge) public view returns (uint256 _limit) { _limit = bridges[_bridge].burnerParams.maxLimit; } /** * @notice Returns the current limit of a bridge * * @param _bridge the bridge we are viewing the limits of * @return _limit The limit the bridge has */ function mintingCurrentLimitOf(address _bridge) public view returns (uint256 _limit) { _limit = _getCurrentLimit( bridges[_bridge].minterParams.currentLimit, bridges[_bridge].minterParams.maxLimit, bridges[_bridge].minterParams.timestamp, bridges[_bridge].minterParams.ratePerSecond ); } /** * @notice Returns the current limit of a bridge * * @param _bridge the bridge we are viewing the limits of * @return _limit The limit the bridge has */ function burningCurrentLimitOf(address _bridge) public view returns (uint256 _limit) { _limit = _getCurrentLimit( bridges[_bridge].burnerParams.currentLimit, bridges[_bridge].burnerParams.maxLimit, bridges[_bridge].burnerParams.timestamp, bridges[_bridge].burnerParams.ratePerSecond ); } /** * @notice Uses the limit of any bridge * @param _bridge The address of the bridge who is being changed * @param _change The change in the limit */ function _useMinterLimits(address _bridge, uint256 _change) internal { uint256 _currentLimit = mintingCurrentLimitOf(_bridge); bridges[_bridge].minterParams.timestamp = block.timestamp; bridges[_bridge].minterParams.currentLimit = _currentLimit - _change; } /** * @notice Uses the limit of any bridge * @param _bridge The address of the bridge who is being changed * @param _change The change in the limit */ function _useBurnerLimits(address _bridge, uint256 _change) internal { uint256 _currentLimit = burningCurrentLimitOf(_bridge); bridges[_bridge].burnerParams.timestamp = block.timestamp; bridges[_bridge].burnerParams.currentLimit = _currentLimit - _change; } /** * @notice Updates the limit of any bridge * @dev Can only be called by the owner * @param _bridge The address of the bridge we are setting the limit too * @param _limit The updated limit we are setting to the bridge */ function _changeMinterLimit(address _bridge, uint256 _limit) internal { uint256 _oldLimit = bridges[_bridge].minterParams.maxLimit; uint256 _currentLimit = mintingCurrentLimitOf(_bridge); bridges[_bridge].minterParams.maxLimit = _limit; bridges[_bridge].minterParams.currentLimit = _calculateNewCurrentLimit(_limit, _oldLimit, _currentLimit); bridges[_bridge].minterParams.ratePerSecond = _limit / _DURATION; bridges[_bridge].minterParams.timestamp = block.timestamp; } /** * @notice Updates the limit of any bridge * @dev Can only be called by the owner * @param _bridge The address of the bridge we are setting the limit too * @param _limit The updated limit we are setting to the bridge */ function _changeBurnerLimit(address _bridge, uint256 _limit) internal { uint256 _oldLimit = bridges[_bridge].burnerParams.maxLimit; uint256 _currentLimit = burningCurrentLimitOf(_bridge); bridges[_bridge].burnerParams.maxLimit = _limit; bridges[_bridge].burnerParams.currentLimit = _calculateNewCurrentLimit(_limit, _oldLimit, _currentLimit); bridges[_bridge].burnerParams.ratePerSecond = _limit / _DURATION; bridges[_bridge].burnerParams.timestamp = block.timestamp; } /** * @notice Updates the current limit * * @param _limit The new limit * @param _oldLimit The old limit * @param _currentLimit The current limit * @return _newCurrentLimit The new current limit */ function _calculateNewCurrentLimit(uint256 _limit, uint256 _oldLimit, uint256 _currentLimit) internal pure returns (uint256 _newCurrentLimit) { uint256 _difference; if (_oldLimit > _limit) { _difference = _oldLimit - _limit; _newCurrentLimit = _currentLimit > _difference ? _currentLimit - _difference : 0; } else { _difference = _limit - _oldLimit; _newCurrentLimit = _currentLimit + _difference; } } /** * @notice Gets the current limit * * @param _currentLimit The current limit * @param _maxLimit The max limit * @param _timestamp The timestamp of the last update * @param _ratePerSecond The rate per second * @return _limit The current limit */ function _getCurrentLimit(uint256 _currentLimit, uint256 _maxLimit, uint256 _timestamp, uint256 _ratePerSecond) internal view returns (uint256 _limit) { _limit = _currentLimit; if (_limit == _maxLimit) { return _limit; } else if (_timestamp + _DURATION <= block.timestamp) { _limit = _maxLimit; } else if (_timestamp + _DURATION > block.timestamp) { uint256 _timePassed = block.timestamp - _timestamp; uint256 _calculatedLimit = _limit + (_timePassed * _ratePerSecond); _limit = _calculatedLimit > _maxLimit ? _maxLimit : _calculatedLimit; } } /** * @notice Internal function for burning tokens * * @param _caller The caller address * @param _user The user address * @param _amount The amount to burn */ function _burnWithCaller(address _caller, address _user, uint256 _amount) internal { uint256 _currentLimit = burningCurrentLimitOf(_caller); if (_currentLimit < _amount) revert IXERC20_NotHighEnoughLimits(); _useBurnerLimits(_caller, _amount); _burn(_user, _amount); } /** * @notice Internal function for minting tokens * * @param _caller The caller address * @param _user The user address * @param _amount The amount to mint */ function _mintWithCaller(address _caller, address _user, uint256 _amount) internal { uint256 _currentLimit = mintingCurrentLimitOf(_caller); if (_currentLimit < _amount) revert IXERC20_NotHighEnoughLimits(); _useMinterLimits(_caller, _amount); _mint(_user, _amount); } /** * @notice Sets the limits of any bridge * * @param _bridge The address of the bridge we are setting the limits too * @param _mintingLimit The updated minting limit we are setting to the bridge * @param _burningLimit The updated burning limit we are setting to the bridge */ function _setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) internal { if (_mintingLimit > (type(uint256).max / 2) || _burningLimit > (type(uint256).max / 2)) { revert IXERC20_LimitsTooHigh(); } _changeMinterLimit(_bridge, _mintingLimit); _changeBurnerLimit(_bridge, _burningLimit); emit BridgeLimitsSet(_mintingLimit, _burningLimit, _bridge); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Simple single owner authorization mixin. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol) /// /// @dev Note: /// This implementation does NOT auto-initialize the owner to `msg.sender`. /// You MUST call the `_initializeOwner` in the constructor / initializer. /// /// While the ownable portion follows /// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility, /// the nomenclature for the 2-step ownership handover may be unique to this codebase. abstract contract Ownable { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The caller is not authorized to call the function. error Unauthorized(); /// @dev The `newOwner` cannot be the zero address. error NewOwnerIsZeroAddress(); /// @dev The `pendingOwner` does not have a valid handover request. error NoHandoverRequest(); /// @dev Cannot double-initialize. error AlreadyInitialized(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ownership is transferred from `oldOwner` to `newOwner`. /// This event is intentionally kept the same as OpenZeppelin's Ownable to be /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173), /// despite it not being as lightweight as a single argument event. event OwnershipTransferred(address indexed oldOwner, address indexed newOwner); /// @dev An ownership handover to `pendingOwner` has been requested. event OwnershipHandoverRequested(address indexed pendingOwner); /// @dev The ownership handover to `pendingOwner` has been canceled. event OwnershipHandoverCanceled(address indexed pendingOwner); /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`. uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE = 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0; /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE = 0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d; /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`. uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE = 0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The owner slot is given by: /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`. /// It is intentionally chosen to be a high value /// to avoid collision with lower slots. /// The choice of manual storage layout is to enable compatibility /// with both regular and upgradeable contracts. bytes32 internal constant _OWNER_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927; /// The ownership handover slot of `newOwner` is given by: /// ``` /// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED)) /// let handoverSlot := keccak256(0x00, 0x20) /// ``` /// It stores the expiry timestamp of the two-step ownership handover. uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Override to return true to make `_initializeOwner` prevent double-initialization. function _guardInitializeOwner() internal pure virtual returns (bool guard) {} /// @dev Initializes the owner directly without authorization guard. /// This function must be called upon initialization, /// regardless of whether the contract is upgradeable or not. /// This is to enable generalization to both regular and upgradeable contracts, /// and to save gas in case the initial owner is not the caller. /// For performance reasons, this function will not check if there /// is an existing owner. function _initializeOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT if sload(ownerSlot) { mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`. revert(0x1c, 0x04) } // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } else { /// @solidity memory-safe-assembly assembly { // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Store the new value. sstore(_OWNER_SLOT, newOwner) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner) } } } /// @dev Sets the owner directly without authorization guard. function _setOwner(address newOwner) internal virtual { if (_guardInitializeOwner()) { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner)))) } } else { /// @solidity memory-safe-assembly assembly { let ownerSlot := _OWNER_SLOT // Clean the upper 96 bits. newOwner := shr(96, shl(96, newOwner)) // Emit the {OwnershipTransferred} event. log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner) // Store the new value. sstore(ownerSlot, newOwner) } } } /// @dev Throws if the sender is not the owner. function _checkOwner() internal view virtual { /// @solidity memory-safe-assembly assembly { // If the caller is not the stored owner, revert. if iszero(eq(caller(), sload(_OWNER_SLOT))) { mstore(0x00, 0x82b42900) // `Unauthorized()`. revert(0x1c, 0x04) } } } /// @dev Returns how long a two-step ownership handover is valid for in seconds. /// Override to return a different value if needed. /// Made internal to conserve bytecode. Wrap it in a public function if needed. function _ownershipHandoverValidFor() internal view virtual returns (uint64) { return 48 * 3600; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC UPDATE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Allows the owner to transfer the ownership to `newOwner`. function transferOwnership(address newOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { if iszero(shl(96, newOwner)) { mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`. revert(0x1c, 0x04) } } _setOwner(newOwner); } /// @dev Allows the owner to renounce their ownership. function renounceOwnership() public payable virtual onlyOwner { _setOwner(address(0)); } /// @dev Request a two-step ownership handover to the caller. /// The request will automatically expire in 48 hours (172800 seconds) by default. function requestOwnershipHandover() public payable virtual { unchecked { uint256 expires = block.timestamp + _ownershipHandoverValidFor(); /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to `expires`. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), expires) // Emit the {OwnershipHandoverRequested} event. log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller()) } } } /// @dev Cancels the two-step ownership handover to the caller, if any. function cancelOwnershipHandover() public payable virtual { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x20), 0) // Emit the {OwnershipHandoverCanceled} event. log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller()) } } /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`. /// Reverts if there is no existing ownership handover requested by `pendingOwner`. function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner { /// @solidity memory-safe-assembly assembly { // Compute and set the handover slot to 0. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) let handoverSlot := keccak256(0x0c, 0x20) // If the handover does not exist, or has expired. if gt(timestamp(), sload(handoverSlot)) { mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`. revert(0x1c, 0x04) } // Set the handover slot to 0. sstore(handoverSlot, 0) } _setOwner(pendingOwner); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PUBLIC READ FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the owner of the contract. function owner() public view virtual returns (address result) { /// @solidity memory-safe-assembly assembly { result := sload(_OWNER_SLOT) } } /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`. function ownershipHandoverExpiresAt(address pendingOwner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the handover slot. mstore(0x0c, _HANDOVER_SLOT_SEED) mstore(0x00, pendingOwner) // Load the handover slot. result := sload(keccak256(0x0c, 0x20)) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* MODIFIERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Marks a function as only callable by the owner. modifier onlyOwner() virtual { _checkOwner(); _; } }
// SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.4 <0.9.0; interface IXERC20 { /** * @notice Emits when a lockbox is set * * @param _lockbox The address of the lockbox */ event LockboxSet(address _lockbox); /** * @notice Emits when a limit is set * * @param _mintingLimit The updated minting limit we are setting to the bridge * @param _burningLimit The updated burning limit we are setting to the bridge * @param _bridge The address of the bridge we are setting the limit too */ event BridgeLimitsSet(uint256 _mintingLimit, uint256 _burningLimit, address indexed _bridge); /** * @notice Reverts when a user with too low of a limit tries to call mint/burn */ error IXERC20_NotHighEnoughLimits(); /** * @notice Reverts when caller is not the factory */ error IXERC20_NotFactory(); /** * @notice Reverts when limits are too high */ error IXERC20_LimitsTooHigh(); /** * @notice Contains the full minting and burning data for a particular bridge * * @param minterParams The minting parameters for the bridge * @param burnerParams The burning parameters for the bridge */ struct Bridge { BridgeParameters minterParams; BridgeParameters burnerParams; } /** * @notice Contains the mint or burn parameters for a bridge * * @param timestamp The timestamp of the last mint/burn * @param ratePerSecond The rate per second of the bridge * @param maxLimit The max limit of the bridge * @param currentLimit The current limit of the bridge */ struct BridgeParameters { uint256 timestamp; uint256 ratePerSecond; uint256 maxLimit; uint256 currentLimit; } /** * @notice Sets the lockbox address * * @param _lockbox The address of the lockbox */ function setLockbox(address _lockbox) external; /** * @notice Updates the limits of any bridge * @dev Can only be called by the owner * @param _mintingLimit The updated minting limit we are setting to the bridge * @param _burningLimit The updated burning limit we are setting to the bridge * @param _bridge The address of the bridge we are setting the limits too */ function setLimits(address _bridge, uint256 _mintingLimit, uint256 _burningLimit) external; /** * @notice Returns the max limit of a minter * * @param _minter The minter we are viewing the limits of * @return _limit The limit the minter has */ function mintingMaxLimitOf(address _minter) external view returns (uint256 _limit); /** * @notice Returns the max limit of a bridge * * @param _bridge the bridge we are viewing the limits of * @return _limit The limit the bridge has */ function burningMaxLimitOf(address _bridge) external view returns (uint256 _limit); /** * @notice Returns the current limit of a minter * * @param _minter The minter we are viewing the limits of * @return _limit The limit the minter has */ function mintingCurrentLimitOf(address _minter) external view returns (uint256 _limit); /** * @notice Returns the current limit of a bridge * * @param _bridge the bridge we are viewing the limits of * @return _limit The limit the bridge has */ function burningCurrentLimitOf(address _bridge) external view returns (uint256 _limit); /** * @notice Mints tokens for a user * @dev Can only be called by a minter * @param _user The address of the user who needs tokens minted * @param _amount The amount of tokens being minted */ function mint(address _user, uint256 _amount) external; /** * @notice Burns tokens for a user * @dev Can only be called by a minter * @param _user The address of the user who needs tokens burned * @param _amount The amount of tokens being burned */ function burn(address _user, uint256 _amount) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.4; import {LibMulticaller} from "multicaller/LibMulticaller.sol"; import {ERC20} from "./ERC20.sol"; /// @title ERC20Multicaller /// @notice An ERC20 token with Multicaller support. abstract contract ERC20Multicaller is ERC20 { /// @inheritdoc ERC20 function approve(address spender, uint256 amount) public virtual override returns (bool) { address msgSender = LibMulticaller.senderOrSigner(); /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, msgSender) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, msgSender, shr(96, mload(0x2c))) } return true; } /// @inheritdoc ERC20 function transfer(address to, uint256 amount) public virtual override returns (bool) { address msgSender = LibMulticaller.senderOrSigner(); _beforeTokenTransfer(msgSender, to, amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, msgSender) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, msgSender, shr(96, mload(0x0c))) } _afterTokenTransfer(msgSender, to, amount); return true; } /// @inheritdoc ERC20 function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address msgSender = LibMulticaller.senderOrSigner(); _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the allowance slot and load its value. mstore(0x20, msgSender) mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED)) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** * @title LibMulticaller * @author vectorized.eth * @notice Library to read the `msg.sender` of the multicaller with sender contract. * * @dev Note: * The functions in this library do NOT guard against reentrancy. * A single transaction can recurse through different Multicallers * (e.g. `MulticallerWithSender -> contract -> MulticallerWithSigner -> contract`). * * Think of these functions like `msg.sender`. * * If your contract `C` can handle reentrancy safely with plain old `msg.sender` * for any `A -> C -> B -> C`, you should be fine substituting `msg.sender` with these functions. */ library LibMulticaller { /** * @dev The address of the multicaller contract. */ address internal constant MULTICALLER = 0x0000000000002Bdbf1Bf3279983603Ec279CC6dF; /** * @dev The address of the multicaller with sender contract. */ address internal constant MULTICALLER_WITH_SENDER = 0x00000000002Fd5Aeb385D324B580FCa7c83823A0; /** * @dev The address of the multicaller with signer contract. */ address internal constant MULTICALLER_WITH_SIGNER = 0x000000000000D9ECebf3C23529de49815Dac1c4c; /** * @dev Returns the caller of `aggregateWithSender` on `MULTICALLER_WITH_SENDER`. */ function multicallerSender() internal view returns (address result) { return at(MULTICALLER_WITH_SENDER); } /** * @dev Returns the signer of `aggregateWithSigner` on `MULTICALLER_WITH_SIGNER`. */ function multicallerSigner() internal view returns (address result) { return at(MULTICALLER_WITH_SIGNER); } /** * @dev Returns the caller of `aggregateWithSender` on `MULTICALLER_WITH_SENDER`, * if the current context's `msg.sender` is `MULTICALLER_WITH_SENDER`. * Otherwise, returns `msg.sender`. */ function sender() internal view returns (address result) { return resolve(MULTICALLER_WITH_SENDER); } /** * @dev Returns the caller of `aggregateWithSigner` on `MULTICALLER_WITH_SIGNER`, * if the current context's `msg.sender` is `MULTICALLER_WITH_SIGNER`. * Otherwise, returns `msg.sender`. */ function signer() internal view returns (address) { return resolve(MULTICALLER_WITH_SIGNER); } /** * @dev Returns the caller or signer at `a`. * @param a The multicaller with sender / signer. */ function at(address a) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x00) if iszero(staticcall(gas(), a, codesize(), 0x00, 0x00, 0x20)) { revert(codesize(), codesize()) // For better gas estimation. } result := mload(0x00) } } /** * @dev Returns the caller or signer at `a`, if the caller is `a`. * @param a The multicaller with sender / signer. */ function resolve(address a) internal view returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, caller()) if eq(caller(), a) { if iszero(staticcall(gas(), a, codesize(), 0x00, 0x00, 0x20)) { revert(codesize(), codesize()) // For better gas estimation. } } result := mload(0x00) } } /** * @dev Returns the caller of `aggregateWithSender` on `MULTICALLER_WITH_SENDER`, * if the current context's `msg.sender` is `MULTICALLER_WITH_SENDER`. * Returns the signer of `aggregateWithSigner` on `MULTICALLER_WITH_SIGNER`, * if the current context's `msg.sender` is `MULTICALLER_WITH_SIGNER`. * Otherwise, returns `msg.sender`. */ function senderOrSigner() internal view returns (address result) { /// @solidity memory-safe-assembly assembly { mstore(0x00, caller()) let withSender := MULTICALLER_WITH_SENDER if eq(caller(), withSender) { if iszero(staticcall(gas(), withSender, codesize(), 0x00, 0x00, 0x20)) { revert(codesize(), codesize()) // For better gas estimation. } } let withSigner := MULTICALLER_WITH_SIGNER if eq(caller(), withSigner) { if iszero(staticcall(gas(), withSigner, codesize(), 0x00, 0x00, 0x20)) { revert(codesize(), codesize()) // For better gas estimation. } } result := mload(0x00) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {IERC20} from "../interfaces/IERC20.sol"; /// @notice Simple ERC20 + EIP-2612 implementation. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) /// /// @dev Note: /// - The ERC20 standard allows minting and transferring to and from the zero address, /// minting and transferring zero tokens, as well as self-approvals. /// For performance, this implementation WILL NOT revert for such actions. /// Please add any checks with overrides if desired. /// - The `permit` function uses the ecrecover precompile (0x1). /// /// If you are overriding: /// - NEVER violate the ERC20 invariant: /// the total sum of all balances must be equal to `totalSupply()`. /// - Check that the overridden function is actually used in the function you want to /// change the behavior of. Much of the code has been manually inlined for performance. abstract contract ERC20 is IERC20 { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The total supply has overflowed. error TotalSupplyOverflow(); /// @dev The allowance has overflowed. error AllowanceOverflow(); /// @dev The allowance has underflowed. error AllowanceUnderflow(); /// @dev Insufficient balance. error InsufficientBalance(); /// @dev Insufficient allowance. error InsufficientAllowance(); /// @dev The permit is invalid. error InvalidPermit(); /// @dev The permit has expired. error PermitExpired(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. uint256 internal constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. uint256 internal constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The storage slot for the total supply. uint256 internal constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c; /// @dev The balance slot of `owner` is given by: /// ``` /// mstore(0x0c, _BALANCE_SLOT_SEED) /// mstore(0x00, owner) /// let balanceSlot := keccak256(0x0c, 0x20) /// ``` uint256 internal constant _BALANCE_SLOT_SEED = 0x87a211a2; /// @dev The allowance slot of (`owner`, `spender`) is given by: /// ``` /// mstore(0x20, spender) /// mstore(0x0c, _ALLOWANCE_SLOT_SEED) /// mstore(0x00, owner) /// let allowanceSlot := keccak256(0x0c, 0x34) /// ``` uint256 internal constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20; /// @dev The nonce slot of `owner` is given by: /// ``` /// mstore(0x0c, _NONCES_SLOT_SEED) /// mstore(0x00, owner) /// let nonceSlot := keccak256(0x0c, 0x20) /// ``` uint256 internal constant _NONCES_SLOT_SEED = 0x38377508; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`. uint256 internal constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901; /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. bytes32 internal constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; /// @dev `keccak256("1")`. bytes32 internal constant _VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`. bytes32 internal constant _PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 METADATA */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the name of the token. function name() public view virtual returns (string memory); /// @dev Returns the symbol of the token. function symbol() public view virtual returns (string memory); /// @dev Returns the decimals places of the token. function decimals() public view virtual returns (uint8) { return 18; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the amount of tokens in existence. function totalSupply() public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := sload(_TOTAL_SUPPLY_SLOT) } } /// @dev Returns the amount of tokens owned by `owner`. function balanceOf(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`. function allowance(address owner, address spender) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x34)) } } /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens. /// /// Emits a {Approval} event. function approve(address spender, uint256 amount) public virtual returns (bool) { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c))) } return true; } /// @dev Transfer `amount` tokens from the caller to `to`. /// /// Requirements: /// - `from` must at least have `amount`. /// /// Emits a {Transfer} event. function transfer(address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(msg.sender, to, amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, caller()) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c))) } _afterTokenTransfer(msg.sender, to, amount); return true; } /// @dev Transfers `amount` tokens from `from` to `to`. /// /// Note: Does not update the allowance if it is the maximum uint256 value. /// /// Requirements: /// - `from` must at least have `amount`. /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the allowance slot and load its value. mstore(0x20, caller()) mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED)) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); return true; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EIP-2612 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev For more performance, override to return the constant value /// of `keccak256(bytes(name()))` if `name()` will never change. function _constantNameHash() internal view virtual returns (bytes32 result) {} /// @dev Returns the current nonce for `owner`. /// This value is used to compute the signature for EIP-2612 permit. function nonces(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the nonce slot and load its value. mstore(0x0c, _NONCES_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`, /// authorized by a signed approval by `owner`. /// /// Emits a {Approval} event. function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { // Revert if the block timestamp is greater than `deadline`. if gt(timestamp(), deadline) { mstore(0x00, 0x1a15a3cc) // `PermitExpired()`. revert(0x1c, 0x04) } let m := mload(0x40) // Grab the free memory pointer. // Clean the upper 96 bits. owner := shr(96, shl(96, owner)) spender := shr(96, shl(96, spender)) // Compute the nonce slot and load its value. mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX) mstore(0x00, owner) let nonceSlot := keccak256(0x0c, 0x20) let nonceValue := sload(nonceSlot) // Prepare the domain separator. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) mstore(0x2e, keccak256(m, 0xa0)) // Prepare the struct hash. mstore(m, _PERMIT_TYPEHASH) mstore(add(m, 0x20), owner) mstore(add(m, 0x40), spender) mstore(add(m, 0x60), value) mstore(add(m, 0x80), nonceValue) mstore(add(m, 0xa0), deadline) mstore(0x4e, keccak256(m, 0xc0)) // Prepare the ecrecover calldata. mstore(0x00, keccak256(0x2c, 0x42)) mstore(0x20, and(0xff, v)) mstore(0x40, r) mstore(0x60, s) let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20) // If the ecrecover fails, the returndatasize will be 0x00, // `owner` will be checked if it equals the hash at 0x00, // which evaluates to false (i.e. 0), and we will revert. // If the ecrecover succeeds, the returndatasize will be 0x20, // `owner` will be compared against the returned address at 0x20. if iszero(eq(mload(returndatasize()), owner)) { mstore(0x00, 0xddafbaef) // `InvalidPermit()`. revert(0x1c, 0x04) } // Increment and store the updated nonce. sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds. // Compute the allowance slot and store the value. // The `owner` is already at slot 0x20. mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender)) sstore(keccak256(0x2c, 0x34), value) // Emit the {Approval} event. log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero pointer. } } /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit. function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Grab the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) result := keccak256(m, 0xa0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL MINT FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Mints `amount` tokens to `to`, increasing the total supply. /// /// Emits a {Transfer} event. function _mint(address to, uint256 amount) internal virtual { _beforeTokenTransfer(address(0), to, amount); /// @solidity memory-safe-assembly assembly { let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT) let totalSupplyAfter := add(totalSupplyBefore, amount) // Revert if the total supply overflows. if lt(totalSupplyAfter, totalSupplyBefore) { mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`. revert(0x1c, 0x04) } // Store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter) // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c))) } _afterTokenTransfer(address(0), to, amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL BURN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Burns `amount` tokens from `from`, reducing the total supply. /// /// Emits a {Transfer} event. function _burn(address from, uint256 amount) internal virtual { _beforeTokenTransfer(from, address(0), amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, from) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Subtract and store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount)) // Emit the {Transfer} event. mstore(0x00, amount) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0) } _afterTokenTransfer(from, address(0), amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL TRANSFER FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Moves `amount` of tokens from `from` to `to`. function _transfer(address from, address to, uint256 amount) internal virtual { _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL ALLOWANCE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`. function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and load its value. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } } } /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`. /// /// Emits a {Approval} event. function _approve(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { let owner_ := shl(96, owner) // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED)) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HOOKS TO OVERRIDE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Hook that is called before any transfer of tokens. /// This includes minting and burning. function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /// @dev Hook that is called after any transfer of tokens. /// This includes minting and burning. function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity >=0.5.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. * Modified from OpenZeppelin's IERC20 contract */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @return The name of the token */ function name() external view returns (string memory); /** * @return The symbol of the token */ function symbol() external view returns (string memory); /** * @return The number of decimal places the token has */ function decimals() external view returns (uint8); function nonces(address account) external view returns (uint256); function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; function DOMAIN_SEPARATOR() external view returns (bytes32); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
{ "remappings": [ "create3-factory/=lib/create3-factory/", "ds-test/=lib/solmate/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "multicaller/=lib/multicaller/src/", "solady/=lib/solady/src/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 1000000 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "cancun", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"uint256[]","name":"_minterLimits","type":"uint256[]"},{"internalType":"uint256[]","name":"_burnerLimits","type":"uint256[]"},{"internalType":"address[]","name":"_bridges","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BUNNI_InvalidLength","type":"error"},{"inputs":[],"name":"IXERC20_LimitsTooHigh","type":"error"},{"inputs":[],"name":"IXERC20_NotFactory","type":"error"},{"inputs":[],"name":"IXERC20_NotHighEnoughLimits","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"NewOwnerIsZeroAddress","type":"error"},{"inputs":[],"name":"NoHandoverRequest","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[],"name":"Unauthorized","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":false,"internalType":"uint256","name":"_mintingLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_burningLimit","type":"uint256"},{"indexed":true,"internalType":"address","name":"_bridge","type":"address"}],"name":"BridgeLimitsSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_lockbox","type":"address"}],"name":"LockboxSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pendingOwner","type":"address"}],"name":"OwnershipHandoverRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_SUPPLY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"bridges","outputs":[{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"currentLimit","type":"uint256"}],"internalType":"struct IXERC20.BridgeParameters","name":"minterParams","type":"tuple"},{"components":[{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"currentLimit","type":"uint256"}],"internalType":"struct IXERC20.BridgeParameters","name":"burnerParams","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"burningCurrentLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"burningMaxLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"completeOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"mintingCurrentLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"}],"name":"mintingMaxLimitOf","outputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pendingOwner","type":"address"}],"name":"ownershipHandoverExpiresAt","outputs":[{"internalType":"uint256","name":"result","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":"deadline","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":[],"name":"renounceOwnership","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"requestOwnershipHandover","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_bridge","type":"address"},{"internalType":"uint256","name":"_mintingLimit","type":"uint256"},{"internalType":"uint256","name":"_burningLimit","type":"uint256"}],"name":"setLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"setLockbox","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","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":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"payable","type":"function"}]
Contract Creation Code
60806040523461036757611b96803803806100198161036b565b928339810160808282031261036757610031826103a4565b60208301516001600160401b03811161036757826100509185016103cf565b60408401519091906001600160401b03811161036757836100729186016103cf565b606085015190946001600160401b03821161036757019280601f850112156103675783516100a76100a2826103b8565b61036b565b9460208087848152019260051b82010192831161036757602001905b82821061034f575050506001600160a01b038116638b78c6d8198190555f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36805345cdf77eb68f44c546b033b2e3c9fd0803ce80000008101908110610342576805345cdf77eb68f44c556387a211a2600c525f526020600c206b033b2e3c9fd0803ce800000081540190556b033b2e3c9fd0803ce8000000602052600c5160601c5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a381519182825114801590610337575b610328575f5b8381106101b95760405161165b908161053b8239f35b6001600160a01b036101cb8284610422565b5116906101d88185610422565b51916101e48288610422565b51926001600160ff1b0381118015610318575b610309575f828152602081905260409081902060028101546003820154825460019384015493987f93f3bbfe8cfb354ec059175107653f49f6eb479a8622a7d83866ea015435c9449694909361026b939092610255929184906104ba565b90875f525f602052846002875f20015584610478565b5f868152602081905284812060030191909155838120620151808404908901558381204290558390206006810154600782015460048301546005909301546102ce936102b89284906104ba565b90875f525f602052836006875f20015583610478565b5f868152602081815285822060070192909255848120620151808404600590910155849020426004909101558351928352820152a2016101a3565b63f596480960e01b5f5260045ffd5b506001600160ff1b0384116101f7565b631a17785160e01b5f5260045ffd5b50828451141561019d565b63e5cfe9575f526004601cfd5b6020809161035c846103a4565b8152019101906100c3565b5f80fd5b6040519190601f01601f191682016001600160401b0381118382101761039057604052565b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361036757565b6001600160401b0381116103905760051b60200190565b9080601f830112156103675781516103e96100a2826103b8565b9260208085848152019260051b82010192831161036757602001905b8282106104125750505090565b8151815260209182019101610405565b80518210156104365760209160051b010190565b634e487b7160e01b5f52603260045260245ffd5b9190820391821161045757565b634e487b7160e01b5f52601160045260245ffd5b9190820180921161045757565b909190808311156104a75761048d919261044a565b808211156104a15761049e9161044a565b90565b50505f90565b61049e926104b49161044a565b9061046b565b9091939281948383145f146104cf5750505050565b620151808101808211610457574210806104ec5750929450505050565b6104f7575b50505050565b6105069192939495504261044a565b8181029181830414901517156104575761051f9161046b565b8181111561053357505b905f8080806104f1565b905061052956fe60806040526004361015610011575f80fd5b5f3560e01c806306fdde03146111db578063095ea7b31461114a5780630c05f82c146110e557806318160ddd146110a257806323b872dd14610fa05780632569296214610f39578063313ce56714610f0057806332cb6b0c14610ebc5780633644e51514610df257806340c10f1914610d07578063435350b714610cd057806354d1f13d14610c6e578063651fd26814610c2d57806370a0823114610bdd578063715018a614610b405780637ecebe0014610af05780638da5cb5b14610a8057806395d89b4114610a11578063998955d3146109c85780639dc29fac1461085e578063a08d5654146106a4578063a9059cbb146105ef578063c1eb71371461058a578063ced67f0c146104c5578063d505accf146102ce578063dd62ed3e14610272578063f04e283e14610207578063f2fde38b146101ac5763fee81cf414610158575f80fd5b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761018f61128a565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576101de61128a565b6101e661152e565b8060601b156101fa576101f890611565565b005b637448fbae5f526004601cfd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761023961128a565b61024161152e565b63389a75e1600c52805f526020600c209081544211610265575f6101f89255611565565b636f5e88185f526004601cfd5b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576102a961128a565b6102b16112ad565b602052637f5e9f20600c525f5260206034600c2054604051908152f35b346101a85760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761030561128a565b61030d6112ad565b6084359160643560443560ff851685036101a857610329611340565b60208101907f42756e6e690000000000000000000000000000000000000000000000000000008252519020908242116104b85773ffffffffffffffffffffffffffffffffffffffff80604051951695169565383775081901600e52855f5260c06020600c20958654957f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252602082019586528660408301967fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc688528b6060850198468a528c608087019330855260a08820602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9885252528688525260a082015220604e526042602c205f5260ff1660205260a43560405260c43560605260208060805f60015afa93853d51036104ab577f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259460209401905585777f5e9f200000000000000000000000000000000000000000176040526034602c2055a3005b63ddafbaef5f526004601cfd5b631a15a3cc5f526004601cfd5b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85773ffffffffffffffffffffffffffffffffffffffff61051161128a565b165f525f60205261010060405f206105886105376004610530846112d0565b93016112d0565b6105626040518094606080918051845260208101516020850152604081015160408501520151910152565b80516080840152602081015160a0840152604081015160c08401526060015160e0830152565bf35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85773ffffffffffffffffffffffffffffffffffffffff6105d661128a565b165f525f6020526020600660405f200154604051908152f35b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761062661128a565b6024356106316113f0565b916387a211a2600c52825f526020600c2080548084116106975783900390555f526020600c20818154019055602052600c5160601c907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3602060405160018152f35b63f4d678b85f526004601cfd5b346101a85760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576106db61128a565b6024356044356106e961152e565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82118015610835575b61080d577f93f3bbfe8cfb354ec059175107653f49f6eb479a8622a7d83866ea015435c944916040916107d673ffffffffffffffffffffffffffffffffffffffff861695865f525f6020526107876002865f20015461077183611364565b90895f525f602052866002895f200155866115e6565b5f88815260208190528681206003019190915585812062015180860460019091015585812042905585902060060154906107c0906113aa565b90875f525f602052836006875f200155836115e6565b5f868152602081815285822060070192909255848120620151808404600590910155849020426004909101558351928352820152a2005b7ff5964809000000000000000000000000000000000000000000000000000000005f5260045ffd5b507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111610713565b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761089561128a565b6024359073ffffffffffffffffffffffffffffffffffffffff811690813303610982575b826108c3336113aa565b1061095a576108ea836108d5336113aa565b335f525f60205242600460405f2001556114a1565b335f525f602052600760405f2001556387a211a2600c525f526020600c209182549283821161069757815f94039055806805345cdf77eb68f44c54036805345cdf77eb68f44c5582527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a3005b7f0b6842aa000000000000000000000000000000000000000000000000000000005f5260045ffd5b33602052637f5e9f20600c52805f526034600c208054600181016109a8575b50506108b9565b8085116109bb57849003905583806109a1565b6313be252b5f526004601cfd5b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576020610a09610a0461128a565b6113aa565b604051908152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610a7c610a4a611340565b7f42554e4e49000000000000000000000000000000000000000000000000000000602082015260405191829182611242565b0390f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610b2761128a565b6338377508600c525f52602080600c2054604051908152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610b7161152e565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755005b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610c1461128a565b6387a211a2600c525f52602080600c2054604051908152f35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576020610a09610c6961128a565b611364565b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576101f861128a565b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610d3e61128a565b6024359081610d4c33611364565b1061095a57610d7082610d5e33611364565b335f525f6020524260405f20556114a1565b335f525f602052600360405f2001556805345cdf77eb68f44c54828101908110610de5576805345cdf77eb68f44c556387a211a2600c525f526020600c20818154019055602052600c5160601c5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3005b63e5cfe9575f526004601cfd5b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857602060a0610e2c611340565b828101907f42756e6e690000000000000000000000000000000000000000000000000000008252519020604051907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252838201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015220604051908152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85760206040516b033b2e3c9fd0803ce80000008152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857602060405160128152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b346101a85760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610fd761128a565b610fdf6112ad565b60443590610feb6113f0565b8360601b90602052637f5e9f208117600c526034600c20908154916001830161108b575b506387a211a2915017600c526020600c2080548084116106975783900390555f526020600c2081815401905560205273ffffffffffffffffffffffffffffffffffffffff600c5160601c91167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3602060405160018152f35b8285116109bb57846387a211a2930390558561100f565b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85760206805345cdf77eb68f44c54604051908152f35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85773ffffffffffffffffffffffffffffffffffffffff61113161128a565b165f525f6020526020600260405f200154604051908152f35b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761118161128a565b60243561118c6113f0565b91602052637f5e9f20600c52815f52806034600c20555f52602c5160601c907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa3602060405160018152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610a7c611214611340565b7f42756e6e690000000000000000000000000000000000000000000000000000006020820152604051918291825b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101a857565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101a857565b906040516080810181811067ffffffffffffffff821117611313576040526060600382948054845260018101546020850152600281015460408501520154910152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604080519190820167ffffffffffffffff8111838210176113135760405260058252565b73ffffffffffffffffffffffffffffffffffffffff165f9081526020819052604090206003810154600282015482546001909301546113a79390929091906114ae565b90565b73ffffffffffffffffffffffffffffffffffffffff165f9081526020819052604090206007810154600682015460048301546005909301546113a79390929091906114ae565b335f526e2fd5aeb385d324b580fca7c83823a03314611448575b6dd9ecebf3c23529de49815dac1c4c3314611424575f5190565b60205f80806dd9ecebf3c23529de49815dac1c4c5afa15611444575f5190565b3838fd5b60205f80806e2fd5aeb385d324b580fca7c83823a05afa61140a573838fd5b9190820180921161147457565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9190820391821161147457565b9091939281948383145f146114c35750505050565b620151808101808211611474574210806114e05750929450505050565b6114eb575b50505050565b6114fa919293949550426114a1565b8181029181830414901517156114745761151391611467565b8181111561152757505b905f8080806114e5565b905061151d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361155857565b6382b429005f526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff16807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755565b90919080831115611612576115fb91926114a1565b8082111561160c576113a7916114a1565b50505f90565b6113a79261161f916114a1565b9061146756fea26469706673582212200035cdfcbdd492317a209c413128b0dbd74eef2a94d15c78fe007d91f8b4c57764736f6c634300081b00330000000000000000000000009a8fee232dcf73060af348a1b62cdb0a19852d13000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f3560e01c806306fdde03146111db578063095ea7b31461114a5780630c05f82c146110e557806318160ddd146110a257806323b872dd14610fa05780632569296214610f39578063313ce56714610f0057806332cb6b0c14610ebc5780633644e51514610df257806340c10f1914610d07578063435350b714610cd057806354d1f13d14610c6e578063651fd26814610c2d57806370a0823114610bdd578063715018a614610b405780637ecebe0014610af05780638da5cb5b14610a8057806395d89b4114610a11578063998955d3146109c85780639dc29fac1461085e578063a08d5654146106a4578063a9059cbb146105ef578063c1eb71371461058a578063ced67f0c146104c5578063d505accf146102ce578063dd62ed3e14610272578063f04e283e14610207578063f2fde38b146101ac5763fee81cf414610158575f80fd5b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761018f61128a565b63389a75e1600c525f52602080600c2054604051908152f35b5f80fd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576101de61128a565b6101e661152e565b8060601b156101fa576101f890611565565b005b637448fbae5f526004601cfd5b60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761023961128a565b61024161152e565b63389a75e1600c52805f526020600c209081544211610265575f6101f89255611565565b636f5e88185f526004601cfd5b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576102a961128a565b6102b16112ad565b602052637f5e9f20600c525f5260206034600c2054604051908152f35b346101a85760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761030561128a565b61030d6112ad565b6084359160643560443560ff851685036101a857610329611340565b60208101907f42756e6e690000000000000000000000000000000000000000000000000000008252519020908242116104b85773ffffffffffffffffffffffffffffffffffffffff80604051951695169565383775081901600e52855f5260c06020600c20958654957f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252602082019586528660408301967fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc688528b6060850198468a528c608087019330855260a08820602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9885252528688525260a082015220604e526042602c205f5260ff1660205260a43560405260c43560605260208060805f60015afa93853d51036104ab577f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259460209401905585777f5e9f200000000000000000000000000000000000000000176040526034602c2055a3005b63ddafbaef5f526004601cfd5b631a15a3cc5f526004601cfd5b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85773ffffffffffffffffffffffffffffffffffffffff61051161128a565b165f525f60205261010060405f206105886105376004610530846112d0565b93016112d0565b6105626040518094606080918051845260208101516020850152604081015160408501520151910152565b80516080840152602081015160a0840152604081015160c08401526060015160e0830152565bf35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85773ffffffffffffffffffffffffffffffffffffffff6105d661128a565b165f525f6020526020600660405f200154604051908152f35b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761062661128a565b6024356106316113f0565b916387a211a2600c52825f526020600c2080548084116106975783900390555f526020600c20818154019055602052600c5160601c907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3602060405160018152f35b63f4d678b85f526004601cfd5b346101a85760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576106db61128a565b6024356044356106e961152e565b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82118015610835575b61080d577f93f3bbfe8cfb354ec059175107653f49f6eb479a8622a7d83866ea015435c944916040916107d673ffffffffffffffffffffffffffffffffffffffff861695865f525f6020526107876002865f20015461077183611364565b90895f525f602052866002895f200155866115e6565b5f88815260208190528681206003019190915585812062015180860460019091015585812042905585902060060154906107c0906113aa565b90875f525f602052836006875f200155836115e6565b5f868152602081815285822060070192909255848120620151808404600590910155849020426004909101558351928352820152a2005b7ff5964809000000000000000000000000000000000000000000000000000000005f5260045ffd5b507f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8111610713565b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761089561128a565b6024359073ffffffffffffffffffffffffffffffffffffffff811690813303610982575b826108c3336113aa565b1061095a576108ea836108d5336113aa565b335f525f60205242600460405f2001556114a1565b335f525f602052600760405f2001556387a211a2600c525f526020600c209182549283821161069757815f94039055806805345cdf77eb68f44c54036805345cdf77eb68f44c5582527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a3005b7f0b6842aa000000000000000000000000000000000000000000000000000000005f5260045ffd5b33602052637f5e9f20600c52805f526034600c208054600181016109a8575b50506108b9565b8085116109bb57849003905583806109a1565b6313be252b5f526004601cfd5b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576020610a09610a0461128a565b6113aa565b604051908152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610a7c610a4a611340565b7f42554e4e49000000000000000000000000000000000000000000000000000000602082015260405191829182611242565b0390f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffff748739275473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610b2761128a565b6338377508600c525f52602080600c2054604051908152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610b7161152e565b5f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a35f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755005b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610c1461128a565b6387a211a2600c525f52602080600c2054604051908152f35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576020610a09610c6961128a565b611364565b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85763389a75e1600c52335f525f6020600c2055337ffa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c925f80a2005b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a8576101f861128a565b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610d3e61128a565b6024359081610d4c33611364565b1061095a57610d7082610d5e33611364565b335f525f6020524260405f20556114a1565b335f525f602052600360405f2001556805345cdf77eb68f44c54828101908110610de5576805345cdf77eb68f44c556387a211a2600c525f526020600c20818154019055602052600c5160601c5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3005b63e5cfe9575f526004601cfd5b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857602060a0610e2c611340565b828101907f42756e6e690000000000000000000000000000000000000000000000000000008252519020604051907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252838201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015220604051908152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85760206040516b033b2e3c9fd0803ce80000008152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857602060405160128152f35b5f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85763389a75e1600c52335f526202a30042016020600c2055337fdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d5f80a2005b346101a85760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610fd761128a565b610fdf6112ad565b60443590610feb6113f0565b8360601b90602052637f5e9f208117600c526034600c20908154916001830161108b575b506387a211a2915017600c526020600c2080548084116106975783900390555f526020600c2081815401905560205273ffffffffffffffffffffffffffffffffffffffff600c5160601c91167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3602060405160018152f35b8285116109bb57846387a211a2930390558561100f565b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85760206805345cdf77eb68f44c54604051908152f35b346101a85760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85773ffffffffffffffffffffffffffffffffffffffff61113161128a565b165f525f6020526020600260405f200154604051908152f35b346101a85760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a85761118161128a565b60243561118c6113f0565b91602052637f5e9f20600c52815f52806034600c20555f52602c5160601c907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa3602060405160018152f35b346101a8575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101a857610a7c611214611340565b7f42756e6e690000000000000000000000000000000000000000000000000000006020820152604051918291825b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602060409481855280519182918282880152018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101a857565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036101a857565b906040516080810181811067ffffffffffffffff821117611313576040526060600382948054845260018101546020850152600281015460408501520154910152565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b604080519190820167ffffffffffffffff8111838210176113135760405260058252565b73ffffffffffffffffffffffffffffffffffffffff165f9081526020819052604090206003810154600282015482546001909301546113a79390929091906114ae565b90565b73ffffffffffffffffffffffffffffffffffffffff165f9081526020819052604090206007810154600682015460048301546005909301546113a79390929091906114ae565b335f526e2fd5aeb385d324b580fca7c83823a03314611448575b6dd9ecebf3c23529de49815dac1c4c3314611424575f5190565b60205f80806dd9ecebf3c23529de49815dac1c4c5afa15611444575f5190565b3838fd5b60205f80806e2fd5aeb385d324b580fca7c83823a05afa61140a573838fd5b9190820180921161147457565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9190820391821161147457565b9091939281948383145f146114c35750505050565b620151808101808211611474574210806114e05750929450505050565b6114eb575b50505050565b6114fa919293949550426114a1565b8181029181830414901517156114745761151391611467565b8181111561152757505b905f8080806114e5565b905061151d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392754330361155857565b6382b429005f526004601cfd5b73ffffffffffffffffffffffffffffffffffffffff16807fffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927547f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a37fffffffffffffffffffffffffffffffffffffffffffffffffffffffff7487392755565b90919080831115611612576115fb91926114a1565b8082111561160c576113a7916114a1565b50505f90565b6113a79261161f916114a1565b9061146756fea26469706673582212200035cdfcbdd492317a209c413128b0dbd74eef2a94d15c78fe007d91f8b4c57764736f6c634300081b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000009a8fee232dcf73060af348a1b62cdb0a19852d13000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _initialOwner (address): 0x9a8FEe232DCF73060Af348a1B62Cdb0a19852d13
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000009a8fee232dcf73060af348a1b62cdb0a19852d13
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [3] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.